/* studio.css — split-rail layout.
   ------------------------------------------------------------------
   Structure borrowed from the reference the studio picked: a fixed
   project rail on the left, a column of rounded panels scrolling on the
   right, and a floating pill nav. Everything else is Tusc — the black
   ground, Urbanist display over Inter UI, and the three interactions
   worth keeping (weight-morph titles, cursor trail, anamorphic tunnel).

   The document scrolls, not the panel column. The rail is fixed rather
   than a scroll container, so ScrollTrigger's pin for the tunnel keeps
   working against the window with no scroller proxy. */

:root {
    /* --bg, --panel, --panel-solid, --panel-lift and --ink live in
       theme.css so both themes can move them together. */
    --hair: rgba(var(--ink-rgb), 0.10);
    --hair-strong: rgba(var(--ink-rgb), 0.20);
    --rail-w: 336px;
    --gap: 10px;
    /* Radius scale. Three sizes were in near-equal use with no rule behind
       them; these are the tiers, and nothing should invent a fourth. */
    --r-sm: 8px;      /* thumbnails, chips */
    --r-md: 12px;     /* cards, rows */
    --r-lg: 16px;     /* panels */
    --radius: var(--r-lg);

    /* Hairline that stays optically 1px on retina, where a real 1px border
       renders chunky. */
    --hairline: 1px;

    /* Elevation lives in theme.css: a dark ground and a light one need
       opposite shadow treatments. */
    /* The reference's expo-out. Long tail, no overshoot: things arrive
       quickly then settle, which is what makes the reveals feel calm. */
    --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* A real 1px border renders chunky on retina; halve it so hairlines stay
   optically hairline. Must come after :root or the base value wins. */
@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 1.5dppx) {
    :root { --hairline: 0.5px; }
}

* { box-sizing: border-box; }

html { background: var(--bg); }

body.studio {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font-family: 'Alte Haas Grotesk', sans-serif;
    overflow-x: hidden;
}

/* ── Opening curtain ───────────────────────────────────────────────
   Covers the page until the first paint is ready, then lifts. Without
   it the rail and panels pop in half-built while fonts swap. */
.veil {
    position: fixed;
    inset: 0;
    z-index: 90;
    background: var(--bg);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity 0.9s var(--ease), visibility 0.9s;
}
.veil.lifted { opacity: 0; visibility: hidden; pointer-events: none; }
.veil-mark {
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.4em;
    opacity: 0.5;
}

/* ── Left rail ─────────────────────────────────────────────────────── */
.rail {
    position: fixed;
    top: 0;
    left: 0;
    width: var(--rail-w);
    height: 100vh;
    height: 100svh;
    padding: var(--gap);
    display: flex;
    flex-direction: column;
    gap: var(--gap);
    z-index: 20;
    /* Slides in behind the veil; .ready is set once the intro runs. */
    transform: translateX(-102%);
    transition: transform 1.1s var(--ease);
}
body.ready .rail { transform: none; }

.rail-card {
    background: var(--panel);
    border-radius: var(--radius);
    flex: 0 0 auto;
}

/* Wordmark strip. The track is duplicated so the marquee can loop on a
   -50% translate with no visible seam. */
.rail-mark {
    height: 78px;
    overflow: hidden;
    display: flex;
    align-items: center;
}
.mark-track {
    display: flex;
    width: max-content;
    animation: mark-scroll 24s linear infinite;
}
.mark-track span {
    font-size: 2rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: -0.01em;
    padding-right: 1.6rem;
    white-space: nowrap;
}
@keyframes mark-scroll {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

.rail-all {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 62px;
    color: var(--ink);
    text-decoration: none;
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.82rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    transition: background 0.3s var(--ease);
}
.rail-all:hover { background: var(--panel-lift); }

/* The project list is the only thing in the rail that scrolls. */
.rail-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
    scrollbar-width: none;
}
.rail-list::-webkit-scrollbar { display: none; }

.rail-row {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 0.7rem;
    border-radius: var(--r-md);
    background: var(--panel);
    box-shadow: var(--e1);
    transition: background 0.4s var(--ease), box-shadow 0.4s var(--ease);
    text-decoration: none;
    color: var(--ink);
    flex: 0 0 auto;
    transition: background 0.35s var(--ease);
}
.rail-row:hover { background: var(--panel-lift); box-shadow: var(--e2); }
.rail-thumb {
    width: 62px;
    height: 44px;
    flex: 0 0 auto;
    border-radius: var(--r-sm);
    background-size: cover;
    background-position: center;
    background-color: var(--panel-lift);
    filter: grayscale(1) brightness(0.75);
    transition: filter 0.5s var(--ease);
}
.rail-row:hover .rail-thumb { filter: none; }
.rail-meta { min-width: 0; }
.rail-title {
    margin: 0;
    /* Caps in the source would fight screen readers and the <title> text, so
       the case lives here. Caps read larger at the same size, so the size
       drops a touch and picks up tracking to stay even against .rail-kind. */
    text-transform: uppercase;
    font-size: 0.86rem;
    letter-spacing: 0.05em;
    font-weight: 700;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: color 0.4s var(--ease);
}
.rail-row:hover .rail-title { font-weight: 700; }
.rail-kind {
    margin: 0.15rem 0 0;
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.62rem;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    opacity: 0.45;
}

.rail-foot {
    height: 54px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 0.3em;
    opacity: 0.4;
}
.rail-foot .arrow { animation: nudge 2.4s var(--ease) infinite; }
@keyframes nudge {
    0%, 100% { transform: translateY(0); opacity: 0.4; }
    50%      { transform: translateY(4px); opacity: 1; }
}

/* ── Panel column ──────────────────────────────────────────────────── */
.stage {
    margin-left: var(--rail-w);
    padding: var(--gap) var(--gap) 0 0;
    display: flex;
    flex-direction: column;
    gap: var(--gap);
}

.panel {
    position: relative;
    background: var(--panel);
    border-radius: var(--radius);
    box-shadow: var(--e1);
    overflow: hidden;
    padding: 5.5rem 4rem;
}
.panel--flush { padding: 0; }

/* Hero: media fills the panel, the statement sits over it. */
.panel--hero {
    height: calc(100vh - var(--gap) * 2);
    height: calc(100svh - var(--gap) * 2);
    min-height: 560px;
    display: flex;
    align-items: flex-end;
    padding: 3.5rem;
}
.hero-media {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.78;
    /* Settles from a slight push-in as the intro finishes. */
    transform: scale(1.06);
    transition: transform 2.4s var(--ease), opacity 1.6s var(--ease);
}
body.ready .hero-media { transform: scale(1); }
/* Fog on glass. A backdrop blur rather than a filter on the video itself:
   filtering the element feathers its own edges, while a covering layer blurs
   what is behind it and leaves the frame clean. Wipes clear as the pointer
   moves across the hero and settles back a few seconds after it stops. */
.hero-fog {
    position: absolute;
    inset: 0;
    -webkit-backdrop-filter: blur(9px) saturate(0.88);
            backdrop-filter: blur(9px) saturate(0.88);
    background: var(--media-scrim);
    opacity: 1;
    pointer-events: none;
    transition: opacity 1.9s var(--ease);
}
/* Clearing is quicker than fogging: a wipe is immediate, fog creeps back. */
.panel--hero.defogged .hero-fog {
    opacity: 0;
    transition: opacity 0.55s var(--ease);
}
/* No pointer to wipe with, and a full-screen backdrop blur is expensive on a
   phone. Touch devices get the plain hero. */
@media (hover: none), (max-width: 768px) {
    .hero-fog { display: none; }
    /* Without the fog to soften it, the raised opacity would leave the mobile
       hero far brighter than it was. Back to the level it always ran at. */
    .hero-media { opacity: 0.4; }
}
@media (prefers-reduced-motion: reduce) {
    .hero-fog { transition: none; }
}

.hero-shade {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(var(--shadow-rgb), 0.86), rgba(var(--shadow-rgb), 0.32) 60%);
    pointer-events: none;
}
.hero-copy { position: relative; z-index: 1; }
.hero-title {
    font-size: clamp(2.6rem, 6.2vw, 6.4rem);
    line-height: 0.92;
    letter-spacing: -0.025em;
    text-transform: uppercase;
    margin: 0;
}
.hero-sub {
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.24em;
    opacity: 0.6;
    margin: 1.6rem 0 0;
}

/* ── Section furniture ─────────────────────────────────────────────── */
.eyebrow {
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.26em;
    opacity: 0.42;
    margin: 0 0 2.2rem;
    display: flex;
    align-items: center;
    gap: 0.9rem;
}
.eyebrow::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--hair);
}
.display {
    font-size: clamp(2.1rem, 4.6vw, 4.4rem);
    line-height: 0.98;
    letter-spacing: -0.022em;
    margin: 0;
    max-width: 18ch;
}
.body-copy {
    font-size: clamp(1rem, 1.25vw, 1.15rem);
    line-height: 1.72;
    font-weight: 400;
    opacity: 0.72;
    max-width: 58ch;
    margin: 2rem 0 0;
}

.pill {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 2.6rem;
    padding: 0.85rem 1.7rem;
    border: 1px solid var(--hair-strong);
    border-radius: 999px;
    color: var(--ink);
    text-decoration: none;
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    transition: background 0.3s var(--ease), color 0.3s var(--ease);
}
.pill:hover { background: var(--ink); color: #000; }

/* ── Scroll reveals ────────────────────────────────────────────────── */
.rise {
    opacity: 0;
    transform: translateY(26px);
    transition: opacity 0.8s var(--ease), transform 0.9s var(--ease);
}
.rise.in { opacity: 1; transform: none; }

/* ── Capability columns ────────────────────────────────────────────── */
.caps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 2.6rem 3rem;
    margin-top: 3.2rem;
}
.cap h3 {
    font-size: 1.15rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    margin: 0 0 1.1rem;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--hair);
}
.cap ul { list-style: none; margin: 0; padding: 0; }
.cap li {
    font-size: 0.94rem;
    line-height: 2;
    font-weight: 400;
    opacity: 0.66;
    transition: opacity 0.3s var(--ease);
}
.cap li:hover { opacity: 1; }

/* ── Tunnel panel ──────────────────────────────────────────────────── */
.panel--tunnel { padding: 0; background: #000; }
.webgl-journey {
    position: relative;
    height: calc(100vh - var(--gap) * 2);
    width: 100%;
    overflow: hidden;
}
#webgl-canvas {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

/* ── Trusted by ────────────────────────────────────────────────────── */
/* ── Trusted By ────────────────────────────────────────────────────────
   A moving row rather than a static list. Two identical runs sit side by
   side and the track travels exactly one run's width before resetting, so
   the seam never lands on screen. Bleeds past the panel padding and fades
   at both edges so marks enter and leave rather than popping. */
.trust-marquee {
    position: relative;
    /* Cancel the panel's horizontal padding so the row runs edge to edge. */
    margin: 0 -4rem;
    padding: 0.5rem 0;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
            mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}
.trust-track {
    display: flex;
    width: max-content;
    animation: trust-scroll 42s linear infinite;
}
.trust-marquee:hover .trust-track { animation-play-state: paused; }
.trust-run {
    display: flex;
    align-items: center;
    gap: 5.5rem;
    padding-right: 5.5rem;   /* keeps spacing even across the seam */
    flex: 0 0 auto;
}
.trust-cell {
    display: flex;
    align-items: center;
    /* Was 0.55, which sank the lighter marks to the point of vanishing. */
    opacity: 0.72;
    transition: opacity 0.4s var(--ease);
}
.trust-cell:hover { opacity: 1; }

/* Optical normalisation.
   A single height for every logo ignores how much ink each one puts on the
   screen. Measured coverage across this set ran from 12% (UMG) to 60%
   (Hartsfield) with aspect ratios from 1.0 to 4.9, so a uniform height made
   the dense wide marks shout and the light ones disappear. Each height below
   is derived from that measured ink area, damped so the correction evens the
   row out without turning the sparse marks into billboards, then clamped so
   nothing runs away. */
.trust-logo {
    height: 34px;
    max-width: 190px;
    width: auto;
    object-fit: contain;
    display: block;
    /* Flattens any source mark to the same white so a mixed set reads
       as one row. */
    filter: var(--logo-filter);
}
.trust-logo.l-umg                { height: 46px; }   /* 12% ink, lightest */
.trust-logo.l-dude-perfect       { height: 46px; max-width: 74px; }  /* square */
.trust-logo.l-airia              { height: 36px; }
.trust-logo.l-high-museum        { height: 34px; }
.trust-logo.l-cluely             { height: 32px; }
.trust-logo.l-hartsfield-jackson { height: 29px; }
.trust-logo.l-cour               { height: 26px; }   /* 58% ink, densest */

@keyframes trust-scroll {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
}

@media (max-width: 900px) {
    .trust-marquee { margin: 0 -1.5rem; }
    .trust-run { gap: 3.25rem; padding-right: 3.25rem; }
    .trust-logo { height: 32px; max-width: 180px; }
    .trust-logo.mark { height: 46px; max-width: 70px; }
}

/* Motion is decoration here; the row still reads stopped. */
@media (prefers-reduced-motion: reduce) {
    .trust-track { animation: none; flex-wrap: wrap; gap: 2rem 3rem; }
}

/* ── Contact ───────────────────────────────────────────────────────── */
.contact-mail {
    display: inline-block;
    margin-top: 2.4rem;
    font-size: clamp(1.4rem, 3.4vw, 3rem);
    font-weight: 400;
    color: var(--ink);
    text-decoration: none;
    border-bottom: 1px solid var(--hair-strong);
    padding-bottom: 0.45rem;
    transition: color 0.4s var(--ease);
}
.contact-mail:hover { font-weight: 700; }

.colophon {
    display: flex;
    justify-content: space-between;
    gap: 2rem;
    flex-wrap: wrap;
    margin-top: 5rem;
    padding-top: 1.4rem;
    border-top: 1px solid var(--hair);
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.64rem;
    text-transform: uppercase;
    letter-spacing: 0.2em;
    opacity: 0.4;
}

/* ── Floating pill nav ─────────────────────────────────────────────── */
.pillnav {
    position: fixed;
    bottom: 18px;
    left: calc(var(--rail-w) + (100% - var(--rail-w)) / 2);
    transform: translateX(-50%) translateY(140%);
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px;
    border-radius: 999px;
    background: var(--chrome);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border: 1px solid var(--hair);
    z-index: 40;
    transition: transform 1s var(--ease) 0.35s;
}
body.ready .pillnav { transform: translateX(-50%) translateY(0); }
.pillnav a {
    padding: 0.6rem 1.15rem;
    border-radius: 999px;
    color: var(--ink);
    text-decoration: none;
    font-family: 'Alte Haas Grotesk', sans-serif;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    opacity: 0.65;
    white-space: nowrap;
    transition: background 0.3s var(--ease), opacity 0.3s var(--ease), color 0.3s var(--ease);
}
.pillnav a:hover { opacity: 1; background: rgba(var(--ink-rgb), 0.08); }
.pillnav a.here { background: var(--ink); color: var(--bg); opacity: 1; }

/* ── Narrow screens ────────────────────────────────────────────────
   The rail stops being a rail: it becomes a normal block at the top of
   the document and the panel column takes the full width. */
@media (max-width: 1024px) {
    /* Order matters more than position here. Left as-is the rail sits above
       the hero and eats ~250px, pushing the statement off the first screen.
       The hero leads and the rail becomes a project strip underneath it. */
    body.studio {
        display: flex;
        flex-direction: column;
    }
    .stage { order: 1; }
    .rail  { order: 2; }
    .rail {
        position: relative;
        width: auto;
        height: auto;
        transform: none;
        padding: 0 var(--gap) var(--gap);
    }
    .rail-mark { display: none; }
    .rail-list {
        flex-direction: row;
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        padding-bottom: 2px;
    }
    .rail-row { width: 74vw; scroll-snap-align: start; }
    .rail-foot { display: none; }
    .stage { margin-left: 0; padding: var(--gap); }
    .panel { padding: 3.5rem 1.6rem; }
    /* Taller hero and a deeper floor: the statement is three lines here, and
       the floating nav sits over the last one unless the panel pads past it. */
    .panel--hero {
        height: calc(100svh - var(--gap) * 2);
        min-height: 480px;
        padding: 2rem 1.6rem 5.5rem;
    }
    .hero-title { font-size: clamp(2rem, 9.5vw, 3.2rem); }
    .hero-sub { font-size: 0.66rem; letter-spacing: 0.18em; margin-top: 1rem; }
    .display { font-size: clamp(1.8rem, 8vw, 2.6rem); max-width: none; }
    /* The nav has to fit the viewport rather than scroll: a nav you have to
       discover by swiping is not a nav. */
    .pillnav {
        left: 50%;
        bottom: 12px;
        width: calc(100vw - 20px);
        justify-content: space-between;
        gap: 0;
    }
    .pillnav a {
        flex: 1 1 0;
        text-align: center;
        padding: 0.6rem 0.35rem;
        font-size: 0.6rem;
        letter-spacing: 0.08em;
    }
    .pillnav::-webkit-scrollbar { display: none; }
    .webgl-journey { height: 82svh; }
}

@media (prefers-reduced-motion: reduce) {
    .rail, .pillnav, .hero-media, .rise, .veil { transition: none; }
    .mark-track, .rail-foot .arrow { animation: none; }
    .rise { opacity: 1; transform: none; }
}

/* Emphasis word: an italic serif inside the grotesk line. Not uppercased,
   and optically sized up, because lowercase italic reads smaller than the
   caps around it. Carries emphasis that a static face cannot carry with
   weight. */
.em {
    font-family: 'PP Editorial New', Georgia, serif;
    font-style: italic;
    font-weight: 400;
    text-transform: none;
    letter-spacing: -0.01em;
    font-size: 1.14em;
    padding-right: 0.04em;
}


/* ── Interactive grid ───────────────────────────────────────────────────
   A fixed lattice behind the whole page, with a brighter patch that follows
   the pointer. Two layers rather than redrawing: the base grid is static, and
   the lit copy is masked to a radial around the cursor, so the only per-frame
   work is writing two custom properties. */
.grid-bg {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    --cell: 64px;
    --mx: 50%;
    --my: 40%;
    background-image:
        repeating-linear-gradient(90deg, var(--grid-line) 0 var(--hairline), transparent var(--hairline) var(--cell)),
        repeating-linear-gradient(0deg,  var(--grid-line) 0 var(--hairline), transparent var(--hairline) var(--cell));
}
.grid-bg::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image:
        repeating-linear-gradient(90deg, var(--grid-lit) 0 var(--hairline), transparent var(--hairline) var(--cell)),
        repeating-linear-gradient(0deg,  var(--grid-lit) 0 var(--hairline), transparent var(--hairline) var(--cell));
    -webkit-mask-image: radial-gradient(420px 420px at var(--mx) var(--my), #000 0%, rgba(var(--shadow-rgb), 0.45) 46%, transparent 78%);
            mask-image: radial-gradient(420px 420px at var(--mx) var(--my), #000 0%, rgba(var(--shadow-rgb), 0.45) 46%, transparent 78%);
}
/* No pointer to light it with; the base lattice stays. */
@media (hover: none) { .grid-bg::after { display: none; } }

/* The panels sit over the grid. Letting a little of it through is what keeps
   the lattice present the whole way down rather than only in the gutters. */
.stage > .panel, .panel { background: var(--panel); }
.panel--hero { background: var(--panel); }
