*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
    --ground:  #f7f4ef;
    --ink:     #141210;
    --ink-2:   #4a4642;
    --accent:  #d63b1f;
    --grain-opacity: 0;
    --measure: 1160px;
}

html { -webkit-text-size-adjust: 100%; }

body {
    font-family: 'Outfit', sans-serif;
    background: var(--ground);
    color: var(--ink);
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
    font-family: 'Bricolage Grotesque', sans-serif;
    font-weight: 600;
    letter-spacing: -0.03em;
    line-height: 0.95;
}

a { color: inherit; }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 999;
    background: var(--accent);
    color: #fff;
    padding: 13px 20px;
    font-size: 0.9rem;
}
.skip-link:focus { left: 0; }

/* Grain overlay. Opacity is driven by setPhase(). */
.grain {
    position: fixed;
    inset: 0;
    z-index: 100;
    pointer-events: none;
    opacity: var(--grain-opacity);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

.panel {
    position: relative;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 28px;
}

.inner {
    width: 100%;
    max-width: var(--measure);
    margin: 0 auto;
}

/* Lenis requirements */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

/* ── HERO ───────────────────────────────────────────── */

.hero { overflow: hidden; }

.hero-bg {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.hero-shot {
    position: absolute;
    width: 32vw;
    max-width: 460px;
    height: auto;
    border-radius: 6px;
    box-shadow: 0 24px 60px rgba(20, 18, 16, 0.18);
    opacity: 0.28;
}

.hero-shot-1 { top: 8%;  left: -4%;  transform: rotate(-4deg); }
.hero-shot-2 { bottom: 6%; right: -3%; transform: rotate(3deg); }
.hero-shot-3 { top: 44%; right: 12%;  width: 22vw; transform: rotate(-2deg); opacity: 0.18; }

.hero-inner { position: relative; z-index: 2; text-align: left; }

.eyebrow {
    font-size: 0.78rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: 26px;
}

.hero-title {
    font-size: clamp(2.6rem, 8.4vw, 7.2rem);
    margin-bottom: 30px;
}

.hero-title .line {
    display: block;
    overflow: hidden;
    padding-bottom: 0.06em;
}

.hero-title .line-in { display: block; }

.hero-sub {
    max-width: 46ch;
    font-size: clamp(1rem, 1.5vw, 1.22rem);
    line-height: 1.6;
    color: var(--ink-2);
    margin-bottom: 38px;
}

.hero-cta { display: flex; gap: 14px; flex-wrap: wrap; }

.btn {
    display: inline-block;
    padding: 15px 28px;
    border-radius: 2px;
    font-size: 0.95rem;
    font-weight: 500;
    text-decoration: none;
    transition: transform 0.25s ease, background-color 0.25s ease, color 0.25s ease;
}
.btn:hover { transform: translateY(-2px); }

.btn-solid { background: var(--accent); color: #fff; }
.btn-ghost { border: 1px solid currentColor; color: var(--ink); }

.scroll-hint {
    position: absolute;
    bottom: 34px;
    left: 50%;
    width: 1px;
    height: 54px;
    background: rgba(128, 122, 116, 0.35);
    overflow: hidden;
}
.scroll-hint span {
    position: absolute;
    inset: 0;
    background: var(--accent);
    transform: translateY(-100%);
    animation: hint 2.1s ease-in-out infinite;
}
@keyframes hint {
    0%   { transform: translateY(-100%); }
    55%  { transform: translateY(0); }
    100% { transform: translateY(100%); }
}

/* ── THE TURN ───────────────────────────────────────── */

.turn { height: 360vh; position: relative; }

.turn-pin {
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 28px;
}

.turn-line {
    font-family: 'Bricolage Grotesque', sans-serif;
    font-weight: 500;
    font-size: clamp(1.9rem, 5.4vw, 4.6rem);
    line-height: 1.14;
    letter-spacing: -0.03em;
    max-width: 18ch;
    text-align: center;
    color: var(--ink-2);
}

/* Each word wipes up out of its own clipping box. */
.ww { display: inline-block; overflow: hidden; vertical-align: bottom; }
.w  { display: inline-block; color: var(--ink); }

/* The doomed clause owns its own line, so when it clears the surviving line
   does not have to re-centre itself. */
.turn-keep, .turn-kill { display: block; }

/* The strike and the bars hug the words, not the full line box. */
.kill-inner { position: relative; display: inline-block; }

/* Comma and full stop share one slot, so the swap shifts nothing. The stop is
   aria-hidden: assistive tech should read the whole sentence, which is what a
   reduced-motion visitor sees too. */
.punc { position: relative; display: inline-block; }
.punc i { font-style: normal; }
.punc .dot { position: absolute; left: 0; top: 0; opacity: 0; }

.turn-strike {
    position: absolute;
    left: -0.04em;
    right: -0.04em;
    top: 52%;
    height: 0.055em;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left center;
    border-radius: 2px;
}

/* The template collapsing into the generic thing it always was. */
.turn-bars {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    gap: 0.16em;
    opacity: 0;
    pointer-events: none;
}

.turn-bars i {
    flex: 1;
    height: 0.46em;
    border-radius: 0.06em;
    background: var(--ink-2);
    transform: scaleX(0.4);
    transform-origin: left center;
}

.turn-bars i:nth-child(2) { flex: 1.6; }
.turn-bars i:nth-child(3) { flex: 0.7; }

@media (prefers-reduced-motion: reduce) {
    .turn { height: auto; }
    .turn-pin { position: static; height: auto; padding: clamp(120px, 18vh, 200px) 28px; }
    .w { transform: none !important; opacity: 1 !important; font-variation-settings: normal !important; }
    .turn-strike, .turn-bars { display: none; }
    .punc .dot { display: none; }
}

/* ── WORK RAIL ──────────────────────────────────────── */

.rail {
    height: 500vh;
    position: relative;
    /* One source of truth. The track's centring inset is derived from
       the card width, so the two cannot drift apart at any viewport.
       The height term matters: the pin is one viewport tall and clips,
       so a card sized on width alone pushes the eyebrow off the top and
       the progress rule and counter off the bottom on any short window
       (a 1440x900 laptop with a bookmarks bar, or a non-maximised one).

       The height term is the measured fit relation, not a bare fraction of
       the viewport. The pin's fixed furniture is 179px (head 85 + its 38px
       margin + foot 56), and a card is 162px of chrome, meta and link above
       a 3:2 shot, so a card of width w needs 341 + 2w/3 of pin height.
       Inverting that, with about 19px of slack for the card-sub wrapping to
       a second line, gives (100vh - 360px) * 1.5. A plain 70vh still clips
       at 1440x640, which is a 1366x768 laptop with browser chrome. */
    --card-w: clamp(300px, min(42vw, calc((100vh - 360px) * 1.5)), 620px);
    --rail-gutter: clamp(28px, 5vw, 80px);
}

.rail-pin {
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* clip, not hidden. `hidden` makes this a programmatically scrollable
       box, so tabbing onto an off-screen card link had the browser scroll
       the pin itself and drag the head, track and foot off with it.
       `clip` is not a scroll container, so focus cannot move it.
       The `hidden` first is the fallback for Safari 15 and older, which
       drop `clip` outright and would otherwise stop clipping at all. */
    overflow: hidden;
    overflow: clip;
}

.rail-head {
    padding: 0 clamp(28px, 5vw, 80px);
    margin-bottom: 38px;
}

.rail-title {
    font-size: clamp(1.6rem, 3.4vw, 2.9rem);
}

.rail-viewport { width: 100%; }

.rail-track {
    display: flex;
    align-items: center;
    gap: clamp(24px, 3vw, 48px);
    /* The left inset centres the first card at rail entry, so the rail
       opens on a composed frame, card 1 can reach full focus, and the
       counter can read 01. Never drops below the page gutter. */
    padding-left: max(var(--rail-gutter), calc((100vw - var(--card-w)) / 2));
    padding-right: max(var(--rail-gutter), calc((100vw - var(--card-w)) / 2));
    width: max-content;
}

.rail-card {
    width: var(--card-w);
    flex: 0 0 auto;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(164, 158, 152, 0.18);
    border-radius: 8px;
    overflow: hidden;
}

.card-chrome {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 10px 14px;
    border-bottom: 1px solid rgba(164, 158, 152, 0.15);
}

.dot { width: 9px; height: 9px; border-radius: 50%; background: rgba(164, 158, 152, 0.35); }

.card-url {
    margin-left: 12px;
    font-size: 0.72rem;
    letter-spacing: 0.02em;
    color: var(--ink-2);
}

.card-shot { aspect-ratio: 3 / 2; overflow: hidden; }

.card-shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
    display: block;
}

.card-meta {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    padding: 20px 22px 6px;
}

.card-name { font-size: 1.25rem; }

.card-sub {
    font-size: 0.85rem;
    color: var(--ink-2);
    margin-top: 6px;
}

.card-tag {
    flex: 0 0 auto;
    font-size: 0.68rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--accent);
    border: 1px solid currentColor;
    border-radius: 2px;
    padding: 5px 9px;
}

.card-link {
    display: inline-block;
    margin: 14px 22px 22px;
    font-size: 0.85rem;
    color: var(--accent);
    text-decoration: none;
    border-bottom: 1px solid currentColor;
    padding-bottom: 2px;
}

.rail-end {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 0 clamp(40px, 6vw, 90px);
    font-family: 'Bricolage Grotesque', sans-serif;
    font-size: clamp(1.1rem, 2vw, 1.7rem);
    text-decoration: none;
    white-space: nowrap;
}

.rail-end-arrow { color: var(--accent); }

.rail-foot {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 40px clamp(28px, 5vw, 80px) 0;
}

.rail-progress {
    flex: 1;
    height: 1px;
    background: rgba(164, 158, 152, 0.22);
    overflow: hidden;
}

.rail-bar {
    display: block;
    height: 100%;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left center;
}

.rail-count {
    font-size: 0.78rem;
    letter-spacing: 0.14em;
    color: var(--ink-2);
    font-variant-numeric: tabular-nums;
}

/* ── Rail fallback: stacked, no pin ──────────────────
   Below 900px, and for anyone who asked for less motion.

   The query is the exact negation of the JS context's
   `(min-width: 900px)`, not `max-width: 899px`. Viewport widths are
   fractional under browser zoom and Windows display scaling, and
   `max-width: 899px` versus `min-width: 900px` leaves the open interval
   (899, 900) matching neither: the rail would keep its 500vh pinned
   desktop geometry with nothing translating it, stranding five of six
   projects off screen. `not all and` cannot leave a gap at any width. */

@media not all and (min-width: 900px) {
    .rail { height: auto; }
    .rail-pin { position: static; height: auto; overflow: visible; padding: 110px 0; }
    .rail-track { flex-direction: column; width: 100%; gap: 44px; transform: none !important;
                  padding-left: var(--rail-gutter); padding-right: var(--rail-gutter); }
    .rail-card { width: 100%; }
    .rail-end { padding: 0; }
    .rail-foot { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .rail { height: auto; }
    .rail-pin { position: static; height: auto; overflow: visible; padding: 110px 0; }
    .rail-track { flex-direction: column; width: 100%; gap: 44px; transform: none !important;
                  padding-left: var(--rail-gutter); padding-right: var(--rail-gutter); }
    /* This block applies at any width, so without a cap a 1440px card
       renders 1296x1027, taller than the viewport. And the dark-ground
       frame is nearly invisible here: reduced motion never leaves paper,
       so the border needs an ink tint rather than a light one. */
    .rail-card { width: 100%; max-width: 620px; margin-inline: auto;
                 border-color: rgba(20, 18, 16, 0.22);
                 opacity: 1 !important; transform: none !important; }
    .rail-end { padding: 0; }
    .rail-foot { display: none; }
    .scroll-hint span { animation: none; }
}

/* ── No-script fallback ──────────────────────────────
   motion.js adds html.sn-static and bails out when GSAP is missing, so a
   CDN failure does not leave 500vh of pinned rail with an untranslated
   track (card 6 stranded at x=3658 behind the clip, four viewport heights
   of blank void). Same vertical stack the two paths above already use. */

html.sn-static .turn { height: auto; }
html.sn-static .turn-pin { position: static; height: auto; padding: clamp(120px, 18vh, 200px) 28px; }
html.sn-static .rail { height: auto; }
html.sn-static .rail-pin { position: static; height: auto; overflow: visible; padding: 110px 0; }
html.sn-static .rail-track { flex-direction: column; width: 100%; gap: 44px;
              padding-left: var(--rail-gutter); padding-right: var(--rail-gutter); }
html.sn-static .rail-card { width: 100%; max-width: 620px; margin-inline: auto;
              border-color: rgba(20, 18, 16, 0.22); }
html.sn-static .rail-end { padding: 0; }
html.sn-static .rail-foot { display: none; }
html.sn-static .process-svg { display: none; }
html.sn-static .step-node { display: none; }

/* ── SHARED REVEAL ──────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
    .reveal { opacity: 1 !important; transform: none !important; }
}

/* ── CAPABILITY ─────────────────────────────────────── */

.capability {
    padding: clamp(120px, 18vh, 220px) 28px;
}

.cap-list { list-style: none; margin-top: 46px; }

.cap-item {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: clamp(20px, 5vw, 70px);
    padding: 38px 0;
    border-top: 1px solid rgba(164, 158, 152, 0.18);
}

.cap-item:last-child { border-bottom: 1px solid rgba(164, 158, 152, 0.18); }

.cap-name { font-size: clamp(1.5rem, 3.4vw, 2.7rem); }

.cap-desc {
    font-size: clamp(0.95rem, 1.3vw, 1.1rem);
    line-height: 1.65;
    color: var(--ink-2);
    max-width: 46ch;
    align-self: center;
}

@media (max-width: 700px) {
    .cap-item { grid-template-columns: 1fr; gap: 14px; }
}

/* ── PROCESS ────────────────────────────────────────── */

.process { padding: clamp(120px, 18vh, 220px) 28px; }

.process-title {
    font-size: clamp(1.9rem, 4.6vw, 3.6rem);
    margin-bottom: clamp(60px, 9vw, 110px);
}

.process-track { position: relative; }

.process-svg {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 120px;
    overflow: visible;
}

.process-steps {
    list-style: none;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: clamp(24px, 4vw, 60px);
    padding-top: 120px;
}

.process-step { position: relative; }

.step-node {
    position: absolute;
    top: -126px;
    left: 0;
    width: 13px;
    height: 13px;
    border-radius: 50%;
    border: 2px solid var(--accent);
    background: var(--ground);
    transform: scale(0.75);
    opacity: 0.4;
    transition: transform 0.4s ease, opacity 0.4s ease;
}

.process-step.lit .step-node {
    transform: scale(1);
    opacity: 1;
    background: var(--accent);
}

.step-num {
    display: block;
    font-size: 0.75rem;
    letter-spacing: 0.16em;
    color: var(--accent);
    margin-bottom: 14px;
}

.step-name { font-size: clamp(1.4rem, 2.6vw, 2.1rem); margin-bottom: 12px; }

.step-desc {
    font-size: 0.98rem;
    line-height: 1.65;
    color: var(--ink-2);
    max-width: 34ch;
}

/* Exact negation of the JS context's `(min-width: 781px)`, for the same
   reason as the rail above: at a fractional width inside (780, 781) the
   curve would be neither hidden by CSS nor given its dash array by JS,
   so it would render permanently fully drawn with no node ever lighting. */
@media not all and (min-width: 781px) {
    .process-svg { display: none; }
    .process-steps { grid-template-columns: 1fr; gap: 44px; padding-top: 0; }
    .step-node { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .process-svg { display: none; }
    .step-node { display: none; }
}

/* ── CONTACT ────────────────────────────────────────── */

.contact { padding: clamp(120px, 18vh, 200px) 28px clamp(80px, 12vh, 140px); }

.contact-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    gap: clamp(40px, 6vw, 90px);
    align-items: start;
}

.contact-title { font-size: clamp(2.2rem, 6vw, 4.6rem); margin: 24px 0 20px; }

.contact-sub {
    font-size: clamp(1rem, 1.4vw, 1.15rem);
    line-height: 1.6;
    color: var(--ink-2);
    max-width: 34ch;
}

.contact-direct { margin-top: 22px; font-size: 0.95rem; color: var(--ink-2); }
.contact-direct a { color: var(--accent); }

.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

.field { margin-bottom: 18px; }

.field label {
    display: block;
    font-size: 0.78rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-2);
    margin-bottom: 8px;
}

.field input,
.field textarea {
    width: 100%;
    font-family: inherit;
    font-size: 1rem;
    color: var(--ink);
    background: transparent;
    border: 1px solid rgba(128, 122, 116, 0.35);
    border-radius: 2px;
    padding: 13px 15px;
    transition: border-color 0.25s ease;
}

.field input:focus,
.field textarea:focus { border-color: var(--accent); outline: none; }

/* A pointer focus gets the border tint alone; a keyboard focus gets the
   same accent outline as every other focusable thing on the page. */
.field input:focus-visible,
.field textarea:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.field textarea { resize: vertical; }

.form-submit {
    border: none;
    cursor: pointer;
    font-family: inherit;
    margin-top: 6px;
}
.form-submit:disabled { opacity: 0.55; cursor: default; transform: none; }

.form-error {
    margin-top: 14px;
    font-size: 0.9rem;
    color: var(--accent);
}

.success-msg {
    grid-column: 1 / -1;
    font-family: 'Bricolage Grotesque', sans-serif;
    font-size: clamp(1.2rem, 2.4vw, 1.8rem);
    line-height: 1.35;
    color: var(--accent);
}

/* ── FOOTER ─────────────────────────────────────────── */

.foot {
    padding: 34px 28px 44px;
    border-top: 1px solid rgba(128, 122, 116, 0.22);
    font-size: 0.85rem;
    color: var(--ink-2);
}

.foot-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
}

.foot-quiet {
    color: inherit;
    text-decoration: none;
    cursor: default;
}

.foot-nav { display: flex; gap: 22px; }
.foot-nav a { text-decoration: none; }
.foot-nav a:hover { color: var(--accent); }

@media (max-width: 820px) {
    .contact-inner { grid-template-columns: 1fr; }
    .field-row { grid-template-columns: 1fr; gap: 0; }
}

/* ── Touch tap targets ──────────────────────────────────────
   The inline text links sit at 18-22px tall, which is fine for a
   mouse and too small for a thumb. Grow the hit area to 44px with
   a transparent overlay rather than padding, so the underline, the
   baseline and every card height stay exactly where they are.
   None of these links has an interactive neighbour to steal from. */

@media (pointer: coarse), (max-width: 820px) {
    .card-link,
    .rail-end,
    .contact-direct a,
    .foot-nav a {
        position: relative;
    }

    .card-link::after,
    .rail-end::after,
    .contact-direct a::after,
    .foot-nav a::after {
        content: '';
        position: absolute;
        left: 0;
        right: 0;
        top: 50%;
        height: 44px;
        transform: translateY(-50%);
    }
}
