/* =============================================================================
   OCTARINE STUDIO
   One stylesheet. No JavaScript anywhere on this site — the menu, the project
   filter, the before/after slider, the counters and every scroll animation are
   pure CSS. Sections are ordered: tokens → base → layout → components → pages.
   ========================================================================== */

/* ----------------------------------------------------------------- 1. tokens */
/* Sampled from octarinestudio.com: cream page, near-black / lavender / white
   cards, 50px radius, Funnel Display. */
:root {
    --bg:            #08080b;
    --bg-raise:      #101018;
    --bg-raise-2:    #17171f;
    --ink:           #f5f3fb;      /* the high-contrast colour — light on this theme */
    --panel:         #150e26;      /* raised contrast card */
    --lav:           #8b3eff;

    --line:          rgba(255, 255, 255, .10);
    --line-strong:   rgba(255, 255, 255, .28);

    --text:          #e8e6f0;
    --text-dim:      #a3a1b4;
    --text-faint:    #726f84;

    --accent:        #8b3eff;
    --accent-deep:   #a97bff;
    --accent-soft:   rgba(139, 62, 255, .15);
    --accent-glow:   rgba(139, 62, 255, .6);

    --font-display: 'Funnel Display', 'Outfit', 'Segoe UI', system-ui, sans-serif;
    --font-mono:    'JetBrains Mono', ui-monospace, monospace;

    --wrap:   1320px;
    --gutter: clamp(20px, 4vw, 56px);
    --section-y: clamp(72px, 10vw, 150px);
    --r:      50px;

    --ease: cubic-bezier(.22, .61, .36, 1);
}

/* Blocks sitting on a lavender or light surface flip their own text. */
.on-dark { --text: #f5f3fb; --text-dim: rgba(245,243,251,.72); --text-faint: rgba(245,243,251,.5);
           --line: rgba(255,255,255,.16); --line-strong: rgba(255,255,255,.34); --accent: #c8bbf5; }
.on-lav  { --text: #16121f; --text-dim: rgba(22,18,31,.74); --text-faint: rgba(22,18,31,.55);
           --line: rgba(22,18,31,.18); --line-strong: rgba(22,18,31,.34);
           --accent: #2a1d55; --accent-deep: #2a1d55; --ink: #16121f; }

/* ------------------------------------------------------------------- 2. base */
*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-display);
    font-size: 16px;
    font-weight: 400;
    line-height: 1.62;
    -webkit-font-smoothing: antialiased;
}
/* NOTE: `overflow-x: hidden` must not go on body. It makes overflow-y compute
   to `auto`, which turns body into a scroll container — the viewport is what
   actually scrolls, so every scroll-driven timeline anchored inside body goes
   inactive (currentTime null) and the animations sit frozen. Sideways overflow
   is contained on the components that cause it instead. */

/* height:auto is load-bearing: every <img> carries width/height attributes (good
   for layout stability), and without this a CSS width leaves the attribute
   height in place and the picture stretches. Components that crop with
   object-fit set their own height and still win on specificity. */
img { max-width: 100%; height: auto; display: block; }
a   { color: inherit; text-decoration: none; }
ul  { margin: 0; padding: 0; list-style: none; }

h1, h2, h3, h4 { margin: 0; font-weight: 500; line-height: 1.02; letter-spacing: -.035em; }
p  { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

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

.skip {
    position: absolute; left: -9999px; top: 0; z-index: 100;
    background: var(--accent); color: #fff; padding: 12px 20px;
}
.skip:focus { left: 0; }

/* ------------------------------------------------------------ 3. type helpers */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }

.mono {
    font-family: var(--font-mono);
    font-weight: 400;
    letter-spacing: .16em;
    text-transform: uppercase;
}

.eyebrow {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .26em;
    text-transform: uppercase;
    color: var(--accent-deep);
    margin: 0 0 22px;
}
.on-dark .eyebrow, .on-lav .eyebrow { color: var(--accent); }

.h1  { font-size: clamp(2.6rem, 6.4vw, 5.6rem); }
.h2  { font-size: clamp(2rem, 4.4vw, 3.6rem); }
.h3  { font-size: clamp(1.35rem, 2.2vw, 1.9rem); }

.accent     { color: var(--accent); }
.lead       { font-size: clamp(1rem, 1.25vw, 1.125rem); color: var(--text-dim); max-width: 56ch; }
.dim        { color: var(--text-dim); }
.dot        { opacity: .4; margin-inline: .7em; }
.arrow      { display: inline-block; transition: transform .35s var(--ease); }

.section    { padding-block: var(--section-y); }
.section--tint { background: var(--bg-raise); }

.section-head { margin-bottom: clamp(40px, 5vw, 72px); }
.section-head--split {
    display: flex; flex-wrap: wrap; gap: 28px;
    align-items: end; justify-content: space-between;
}

.rule { height: 1px; background: var(--line); border: 0; margin: 0; }

/* --------------------------------------------------------------- 4. buttons */
.btn {
    border-radius: 999px;
    display: inline-flex; align-items: center; gap: .7em;
    font-family: var(--font-mono);
    font-size: 11px; font-weight: 500;
    letter-spacing: .2em; text-transform: uppercase;
    padding: 15px 26px;
    border: 1px solid var(--line-strong);
    position: relative;
    overflow: hidden;
    /* Own stacking context: keeps the z-index:-1 sweep above the button's own
       background but still behind the label. Without it, the sweep hides behind
       the white fill of .btn--solid and its hover state turns white-on-white. */
    isolation: isolate;
    /* The label waits for the fill to reach it, so it is never white-on-white. */
    transition: color .2s var(--ease) .18s, border-color .35s var(--ease), transform .35s var(--ease);
}
.btn::before {
    content: ''; position: absolute; inset: 0;
    background: var(--accent-deep);
    transform: scaleX(0); transform-origin: left;
    transition: transform .45s var(--ease);
    z-index: -1;
}
.btn:hover { border-color: var(--accent); transform: translateY(-2px); }
.btn:hover::before { transform: scaleX(1); }
.btn:hover .arrow { transform: translate(3px, -3px); }

.btn--solid  { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.btn--solid:hover { color: #fff; border-color: var(--accent-deep); }
.btn--outline { color: var(--text); }
.btn--ghost   { border-color: var(--line); color: var(--text-dim); }
.btn--ghost:hover { color: #fff; }

.btn-row { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 38px; }

/* ---------------------------------------------------------------- 5. header */
.nav-toggle { position: absolute; opacity: 0; pointer-events: none; }

.site-header {
    position: sticky; top: 0; z-index: 50;
    background: rgba(8, 8, 11, .78);
    backdrop-filter: blur(20px) saturate(150%);
    border-bottom: 1px solid var(--line);
}
.header-inner {
    display: flex; align-items: center; gap: clamp(14px, 2vw, 28px);
    min-height: 84px;
}

.brand { display: flex; align-items: center; margin-right: auto; }


.nav { display: flex; gap: 30px; }
.nav-link {
    font-family: var(--font-mono); font-size: 11px; font-weight: 500;
    letter-spacing: .2em; text-transform: uppercase;
    color: var(--text-dim);
    position: relative; padding-block: 6px;
    transition: color .3s var(--ease);
}
.nav-link::after {
    content: ''; position: absolute; left: 0; bottom: 0;
    width: 100%; height: 1px; background: var(--accent);
    transform: scaleX(0); transform-origin: right;
    transition: transform .4s var(--ease);
}
.nav-link:hover { color: var(--text); }
.nav-link:hover::after { transform: scaleX(1); transform-origin: left; }
.nav-link.is-active { color: var(--accent); }

.header-cta { padding: 12px 20px; }


.nav-drawer {
    position: fixed; inset: 66px 0 auto; z-index: 49;
    background: var(--bg-raise);
    border-bottom: 1px solid var(--line);
    display: grid; overflow: hidden;
    grid-template-rows: 0fr;
    transition: grid-template-rows .45s var(--ease);
}
.nav-drawer > nav { min-height: 0; display: grid; gap: 2px; padding-inline: var(--gutter); }
.drawer-link {
    font-size: 1.5rem; font-weight: 500; letter-spacing: -.02em;
    padding: 14px 0; border-bottom: 1px solid var(--line);
}
.drawer-link.is-active { color: var(--accent); }
.drawer-cta { margin: 24px 0 32px; justify-content: center; }

#nav-toggle:checked ~ .nav-drawer { grid-template-rows: 1fr; }

/* ------------------------------------------------------------------ 6. hero */
.hero {
    position: relative;
    min-height: min(92vh, 900px);
    display: flex; align-items: center;
    padding-block: clamp(80px, 12vh, 150px) clamp(40px, 6vh, 80px);
    overflow: hidden;
    perspective: 1000px;
}
.hero-media {
    position: absolute; inset: -6%;
    background: var(--hero-img) center/cover no-repeat;
    /* Slow push-in gives the still render depth without any script. */
    animation: hero-drift 26s var(--ease) infinite alternate;
    transform-origin: 60% 55%;
}
@keyframes hero-drift {
    from { transform: scale(1.02) translate3d(0, 0, 0); }
    to   { transform: scale(1.12) translate3d(-1.5%, -1.5%, 0); }
}
.hero-veil {
    position: absolute; inset: 0;
    background:
        linear-gradient(100deg, rgba(8,8,11,.94) 12%, rgba(8,8,11,.62) 48%, rgba(8,8,11,.35) 100%),
        linear-gradient(to top, var(--bg) 2%, rgba(8,8,11,0) 34%),
        radial-gradient(120% 90% at 78% 8%, var(--accent-soft), transparent 60%);
}
.hero-inner { position: relative; z-index: 2; width: 100%; }
.hero h1 { margin-bottom: 30px; max-width: 15ch; }
.hero .lead { max-width: 48ch; }

.hero-sub {
    font-family: var(--font-mono); font-size: 12px;
    letter-spacing: .26em; text-transform: uppercase;
    color: var(--text); margin-bottom: 20px;
}

/* Stats strip -------------------------------------------------------------- */
.stats {
    display: flex; flex-wrap: wrap; gap: clamp(24px, 5vw, 64px);
    border-top: 1px solid var(--line);
    margin-top: clamp(44px, 6vw, 76px); padding-top: 26px;
}
.stat { display: flex; align-items: baseline; gap: 10px; }
.stat-num {
    font-size: clamp(1.5rem, 2.6vw, 2.1rem); font-weight: 600;
    letter-spacing: -.04em; color: var(--text);
}
.stat-num sup { color: var(--accent); font-size: .7em; top: -.35em; }
.stat-label {
    font-family: var(--font-mono); font-size: 10px;
    letter-spacing: .22em; text-transform: uppercase; color: var(--text-faint);
}

/* Numbers count up in pure CSS via an animatable custom property. */
/* The per-stat @property rules and keyframes are generated from STATS in
   config.php and injected by head.php, so the animated target can never drift
   away from the number the site is configured with. */
/* inline-block + tabular figures so the reserved min-width generated in
   head.php actually applies and every digit occupies the same advance —
   without both, the number jitters as it counts. */
.count { font-variant-numeric: tabular-nums; display: inline-block; text-align: left; }
.count::after { content: counter(c); }

/* Browsers without @property fall back to the static number. The test used to
   be `@supports not (background: paint(x))`, which is the CSS Painting API - a
   Chrome-only feature that Safari has never shipped and that has nothing to do
   with @property, which Safari has supported since 16.4. So the rule fired on
   every iPhone and drew the number from the data-final attribute instead of the
   live counter: the value was updating correctly and Safari was painting a
   frozen string over it. That is why the count ran on desktop and not on phones.

   The real test is CSS.registerProperty, which is the same API as @property.
   footer.php adds `no-css-props` to <html> when it is missing; <noscript> below
   covers the no-JavaScript case, where there is no animation to show anyway. */
:root.no-css-props .count::after { content: attr(data-final); }

/* --------------------------------------------------------------- 7. marquee */
.marquee {
    border-block: 1px solid var(--line);
    background: var(--bg-raise);
    overflow: hidden;
    padding-block: 20px;
    --marquee-speed: 34s;
}
.marquee-track {
    display: flex; width: max-content;
    animation: marquee var(--marquee-speed) linear infinite;
}
.marquee-track > span {
    display: flex; align-items: center; gap: 34px;
    padding-right: 34px;
    font-family: var(--font-mono); font-size: clamp(13px, 1.6vw, 18px);
    letter-spacing: .22em; text-transform: uppercase; color: var(--text-dim);
    white-space: nowrap;
}
.marquee-track i {
    width: 6px; height: 6px; background: var(--accent);
    transform: rotate(45deg); flex: none;
}
@keyframes marquee { to { transform: translateX(-50%); } }

/* --------------------------------------------------- 8. sticky word statement */
.statement { position: relative; }
.statement-panel {
    position: relative;
    min-height: 100vh;
    display: flex; align-items: center;
    border-bottom: 1px solid var(--line);
    overflow: hidden;
}
.statement-panel .panel-media {
    position: absolute; inset: 0;
    background: var(--panel-img) center/cover no-repeat;
    opacity: .18;
    transform: scale(1.05);
}
.statement-panel .panel-veil {
    position: absolute; inset: 0;
    background: linear-gradient(to right, var(--bg) 8%, rgba(8,8,11,.55) 70%, rgba(8,8,11,.8));
}
.statement-inner { position: relative; z-index: 2; width: 100%; }
.statement-small {
    font-size: clamp(1.6rem, 3.4vw, 2.8rem);
    font-weight: 400; color: var(--text-dim); letter-spacing: -.03em;
    margin-bottom: 6px;
}
.statement-big {
    font-size: clamp(2.4rem, 7vw, 6rem);
    font-weight: 600; letter-spacing: -.04em; text-transform: uppercase;
    line-height: .98;
}
.statement-bars { display: flex; gap: 8px; margin-top: 44px; }
.statement-bars i { width: 46px; height: 2px; background: var(--line-strong); }
.statement-bars i.on { background: var(--accent); }

/* -------------------------------------------------------------- 9. services */
.svc-row {
    display: grid;
    grid-template-columns: 68px 1fr 52px;
    gap: clamp(16px, 3vw, 40px);
    align-items: center;
    padding-block: clamp(26px, 3.4vw, 40px);
    border-top: 1px solid var(--line);
    position: relative;
    transition: background .4s var(--ease), padding-left .4s var(--ease);
}
.svc-row:last-of-type { border-bottom: 1px solid var(--line); }
.svc-row::before {
    content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
    background: var(--accent);
    transform: scaleY(0); transform-origin: top;
    transition: transform .45s var(--ease);
}
.svc-row:hover { background: linear-gradient(90deg, var(--accent-soft), transparent 55%); padding-left: 18px; }
.svc-row:hover::before { transform: scaleY(1); }

.svc-num {
    font-family: var(--font-mono); font-size: 11px;
    letter-spacing: .2em; color: var(--text-faint);
}
.svc-title {
    display: block;
    font-size: clamp(1.5rem, 3.2vw, 2.5rem);
    font-weight: 500; letter-spacing: -.03em;
    transition: color .35s var(--ease);
}
.svc-row:hover .svc-title { color: #fff; }
.svc-desc { display: block; color: var(--text-dim); font-size: .95rem; margin-top: 6px; max-width: 62ch; }
.svc-go {
    width: 44px; height: 44px; display: grid; place-items: center;
    border: 1px solid var(--line); justify-self: end;
    transition: background .35s var(--ease), border-color .35s var(--ease), transform .35s var(--ease);
}
.svc-row:hover .svc-go { background: var(--accent); border-color: var(--accent); transform: translate(3px, -3px); }

/* ----------------------------------------------------- 10. before / after */
/* ------------------------------------------- before / after review slider
   Both halves are real, finished frames of the same camera - one graded for
   midday, one for dusk. The panel used to carry a blueprint filter
   (grayscale + sepia + hue-rotate), which existed because the left side was
   once a fake "line study"; on a real render it just looked broken, so it is
   gone. Dragging used to be the browser's native `resize` corner, which is
   invisible on a phone and unrecognisable on a desktop - it is now a proper
   handle on a full-width range input, so it drags anywhere, works on touch and
   is reachable from the keyboard. */
.compare {
    --pos: 50%;
    position: relative; overflow: hidden;
    border: 1px solid var(--line);
    aspect-ratio: 16 / 9;
    background: var(--after-img) center/cover no-repeat;
    container-type: size;        /* container units below measure against this box */
    user-select: none;
}
.compare-before {
    position: absolute; left: 0; top: 0; height: 100%;
    width: var(--pos);
    overflow: hidden;
}
.compare-before img {
    /* Locked to the OUTER box, not the shrinking panel, so both halves line up. */
    width: 100cqw; height: 100cqh; max-width: none;
    object-fit: cover;
}

/* The divider and its grab handle. Purely decorative - the range input over
   the top is what actually takes the drag. */
.compare-divider {
    position: absolute; top: 0; bottom: 0; left: var(--pos);
    width: 2px; margin-left: -1px; z-index: 3;
    background: linear-gradient(to bottom,
        rgba(255,255,255,.25), #fff 18%, #fff 82%, rgba(255,255,255,.25));
    pointer-events: none;
}
.compare-divider::after {
    content: ''; position: absolute; top: 50%; left: 50%;
    translate: -50% -50%;
    width: clamp(38px, 5vw, 52px); aspect-ratio: 1; border-radius: 50%;
    background: rgba(10, 8, 16, .58) center / 56% no-repeat
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 6l-5 6 5 6M15 6l5 6-5 6'/%3E%3C/svg%3E");
    border: 1.5px solid rgba(255,255,255,.9);
    box-shadow: 0 14px 34px -12px rgba(0,0,0,.9), 0 0 0 1px rgba(0,0,0,.25);
    backdrop-filter: blur(6px);
}

/* The real control. Invisible, full-bleed, so the whole frame is draggable.
   `touch-action: pan-y` keeps vertical scrolling working over it on a phone. */
.compare-range {
    position: absolute; inset: 0; z-index: 4;
    width: 100%; height: 100%; margin: 0; padding: 0;
    opacity: 0; cursor: ew-resize;
    touch-action: pan-y;
    -webkit-appearance: none; appearance: none; background: transparent;
}
.compare-range::-webkit-slider-thumb {
    -webkit-appearance: none; width: 54px; height: 100%; cursor: ew-resize;
}
.compare-range::-moz-range-thumb {
    width: 54px; height: 100%; border: 0; border-radius: 0; background: transparent; cursor: ew-resize;
}
.compare-range:focus-visible { outline: none; }
.compare-range:focus-visible ~ .compare-divider::after {
    box-shadow: 0 14px 34px -12px rgba(0,0,0,.9), 0 0 0 3px var(--accent);
}

.compare-tag {
    position: absolute; top: 18px; z-index: 3;
    font-family: var(--font-mono); font-size: 10px;
    letter-spacing: .2em; text-transform: uppercase;
    padding: 7px 12px; background: rgba(8,8,11,.66);
    border: 1px solid var(--line);
    pointer-events: none;
}
.compare-tag--l { left: 18px; }
.compare-tag--r { right: 18px; color: var(--accent); }
.compare-hint {
    font-family: var(--font-mono); font-size: 10px; letter-spacing: .2em;
    text-transform: uppercase; color: var(--text-faint); margin-top: 14px;
}

/* --------------------------------------------------------------- 11. process */
.process { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(30px, 5vw, 70px); align-items: start; }
.process-media { position: sticky; top: 110px; border: 1px solid var(--line); overflow: hidden; }
.process-media img { aspect-ratio: 4 / 5; object-fit: cover; width: 100%; }
.process-step {
    display: grid; grid-template-columns: 72px 1fr; gap: 22px;
    padding-block: clamp(26px, 3vw, 44px);
    border-top: 1px solid var(--line);
}
.process-step:last-child { border-bottom: 1px solid var(--line); }
.process-num {
    font-size: 1.9rem; font-weight: 600; letter-spacing: -.05em;
    color: var(--accent); line-height: 1;
}
.process-name {
    display: block;
    font-size: 1.15rem; font-weight: 600; letter-spacing: .04em;
    text-transform: uppercase; margin-bottom: 8px;
}

/* --------------------------------------------------------- 12. project cards */
.grid { display: grid; gap: clamp(18px, 2.2vw, 28px); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }

.card-3d { perspective: 1100px; }

.pcard {
    position: relative; display: block; overflow: hidden;
    border: 1px solid var(--line);
    background: var(--bg-raise);
    transform-style: preserve-3d;
    transition: transform .55s var(--ease), border-color .45s var(--ease), box-shadow .55s var(--ease);
}
.pcard:hover {
    /* The lift is a genuine 3D rotation, not a scale. */
    transform: rotateX(5deg) rotateY(-5deg) translateZ(26px);
    border-color: var(--line-strong);
    box-shadow: -22px 26px 60px rgba(0, 0, 0, .6), 0 0 60px -18px var(--accent-glow);
}
.pcard-img { display: block; position: relative; overflow: hidden; aspect-ratio: 4 / 3; background: var(--bg-raise-2); }
.pcard-img img { width: 100%; height: 100%; object-fit: cover; transition: transform .8s var(--ease), filter .6s var(--ease); }
.pcard:hover .pcard-img img { transform: scale(1.07); filter: saturate(1.12); }
.pcard-img::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(to top, rgba(8,8,11,.96) 0%, rgba(8,8,11,.78) 26%, rgba(8,8,11,.3) 58%, transparent 78%);
}
.pcard-body {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 22px; z-index: 2;
    transform: translateZ(40px);
}
.pcard-cat {
    display: block;
    font-family: var(--font-mono); font-size: 10px;
    letter-spacing: .22em; text-transform: uppercase; color: var(--accent);
    margin-bottom: 8px;
}
.pcard-title { display: block; font-size: 1.4rem; font-weight: 500; letter-spacing: -.02em; }
.pcard-meta { display: block; font-size: .82rem; color: var(--text-dim); margin-top: 4px; }
.pcard-count {
    display: block;
    position: absolute; top: 16px; right: 16px; z-index: 2;
    font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em;
    padding: 6px 10px; background: rgba(8,8,11,.7); border: 1px solid var(--line);
    transform: translateZ(30px);
}

/* -------------------------------------------------- 13. CSS-only project filter */
.filter-input { position: absolute; opacity: 0; pointer-events: none; }
.filter-row { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: clamp(30px, 4vw, 54px); }
.filter-chip {
    border-radius: 999px;
    font-family: var(--font-mono); font-size: 10px; font-weight: 500;
    letter-spacing: .2em; text-transform: uppercase;
    padding: 12px 20px; border: 1px solid var(--line);
    color: var(--text-dim); cursor: pointer;
    transition: color .3s var(--ease), border-color .3s var(--ease), background .3s var(--ease);
}
.filter-chip:hover { color: var(--text); border-color: var(--line-strong); }

/* A radio per category; :has() lets the checked one style its chip and hide the rest. */
#f-all:checked      ~ .filter-row [for="f-all"],
#f-residential:checked ~ .filter-row [for="f-residential"],
#f-commercial:checked  ~ .filter-row [for="f-commercial"],
#f-hospitality:checked ~ .filter-row [for="f-hospitality"],
#f-mixeduse:checked    ~ .filter-row [for="f-mixeduse"],
#f-interior:checked    ~ .filter-row [for="f-interior"] {
    color: #fff; border-color: var(--accent); background: var(--accent);
}
#f-residential:checked ~ .grid > :not([data-cat="Residential"]),
#f-commercial:checked  ~ .grid > :not([data-cat="Commercial"]),
#f-hospitality:checked ~ .grid > :not([data-cat="Hospitality"]),
#f-mixeduse:checked    ~ .grid > :not([data-cat="Mixed-Use"]),
#f-interior:checked    ~ .grid > :not([data-cat="Interior"]) { display: none; }

/* ------------------------------------------------------------- 14. galleries */
.gallery { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(12px, 1.6vw, 20px); }
.gallery figure { margin: 0; overflow: hidden; border: 1px solid var(--line); background: var(--bg-raise-2); }
.gallery img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; transition: transform .9s var(--ease); }
.gallery figure:hover img { transform: scale(1.04); }
.gallery figure:first-child { grid-column: 1 / -1; }
.gallery figure:first-child img { aspect-ratio: 16 / 9; }

/* ------------------------------------------------------------ 15. blog cards */
.post-card {
    display: grid; grid-template-rows: auto 1fr;
    border: 1px solid var(--line); background: var(--bg-raise);
    overflow: hidden; height: 100%;
    transition: border-color .4s var(--ease), transform .5s var(--ease);
}
.post-card:hover { border-color: var(--line-strong); transform: translateY(-6px); }
.post-card-img { display: block; overflow: hidden; aspect-ratio: 16 / 10; background: var(--bg-raise-2); }
.post-card-img img { width: 100%; height: 100%; object-fit: cover; transition: transform .9s var(--ease); }
.post-card:hover .post-card-img img { transform: scale(1.06); }
.post-card-body { padding: 26px; display: flex; flex-direction: column; gap: 12px; }
.post-meta {
    font-family: var(--font-mono); font-size: 10px;
    letter-spacing: .2em; text-transform: uppercase; color: var(--text-faint);
    display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}
.post-meta .cat { color: var(--accent); }
.post-card-title { display: block; font-size: 1.35rem; font-weight: 500; letter-spacing: -.02em; }
.post-card-ex { display: block; color: var(--text-dim); font-size: .93rem; }
.post-card-more {
    display: block;
    margin-top: auto; font-family: var(--font-mono); font-size: 10px;
    letter-spacing: .2em; text-transform: uppercase; color: var(--text);
}

/* Featured (first) post spans the row. */
.post-card--feature { grid-template-columns: 1.15fr 1fr; grid-template-rows: 1fr; grid-column: 1 / -1; }
.post-card--feature .post-card-img { aspect-ratio: auto; height: 100%; min-height: 320px; }
.post-card--feature .post-card-body { padding: clamp(28px, 4vw, 48px); justify-content: center; }
.post-card--feature .post-card-title { display: block; font-size: clamp(1.7rem, 3vw, 2.5rem); }

/* ---------------------------------------------------------- 16. article body */
.article { max-width: 720px; margin-inline: auto; }
/* Article type sits on the cream page, so it takes the page ink — not the
   near-white it used when this stylesheet was built for a dark background. */
.article-body { font-size: 1.06rem; line-height: 1.78; color: var(--text); }
.article-body h2 {
    font-size: clamp(1.5rem, 2.6vw, 2.05rem); font-weight: 600;
    margin: 2.2em 0 .7em; color: var(--ink); letter-spacing: -.03em;
}
.article-body p { margin-bottom: 1.35em; }
.article-body ul { margin: 0 0 1.6em; display: grid; gap: .8em; }
.article-body li { padding-left: 26px; position: relative; }
.article-body li::before {
    content: ''; position: absolute; left: 0; top: .62em;
    width: 7px; height: 7px; background: var(--accent); transform: rotate(45deg);
}
.article-body strong { color: var(--ink); font-weight: 600; }
.article-body em { color: var(--ink); }

.article-hero { aspect-ratio: 16 / 7; overflow: hidden; border: 1px solid var(--line); margin-bottom: clamp(34px, 5vw, 60px); }
.article-hero img { width: 100%; height: 100%; object-fit: cover; }

.article-nav { display: flex; justify-content: space-between; gap: 20px; margin-top: 60px; padding-top: 26px; border-top: 1px solid var(--line); }

/* ------------------------------------------------------------------ 17. forms */
.form { display: grid; gap: 20px; }
.field { display: grid; gap: 9px; }
.field label {
    font-family: var(--font-mono); font-size: 10px;
    letter-spacing: .22em; text-transform: uppercase; color: var(--text-faint);
}
.field input, .field select, .field textarea {
    font-family: inherit; font-size: 1rem; font-weight: 300; color: var(--text);
    background: var(--bg-raise); border: 1px solid var(--line);
    padding: 15px 16px; width: 100%;
    transition: border-color .3s var(--ease), background .3s var(--ease);
}
.field textarea { min-height: 150px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
    border-color: var(--accent); background: var(--bg-raise-2); outline: none;
}
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }

.notice { padding: 18px 20px; border: 1px solid var(--line); font-size: .95rem; }
.notice--ok   { border-color: rgba(80, 220, 160, .5); background: rgba(80, 220, 160, .08); }
.notice--bad  { border-color: rgba(255, 120, 120, .5); background: rgba(255, 120, 120, .08); }
.notice ul { margin-top: 8px; display: grid; gap: 4px; }

.honey { position: absolute; left: -9999px; }

.contact-grid { display: grid; grid-template-columns: 1.25fr .85fr; gap: clamp(34px, 5vw, 72px); align-items: start; }
.contact-list { display: grid; gap: 22px; }
.contact-item { border-top: 1px solid var(--line); padding-top: 18px; }
.contact-item .k { font-family: var(--font-mono); font-size: 10px; letter-spacing: .22em; text-transform: uppercase; color: var(--text-faint); margin-bottom: 6px; }
.contact-item .v { font-size: 1.05rem; }

/* --------------------------------------------------------------- 18. CTA band */
.cta-band { position: relative; overflow: hidden; padding-block: clamp(90px, 12vw, 170px); }
.cta-bg {
    position: absolute; inset: 0;
    background: var(--cta-bg) center/cover no-repeat;
    opacity: .34;
    animation: hero-drift 30s var(--ease) infinite alternate;
}
.cta-band::after {
    content: ''; position: absolute; inset: 0;
    background:
        linear-gradient(90deg, var(--bg) 10%, rgba(8,8,11,.5) 60%, rgba(8,8,11,.75)),
        linear-gradient(to bottom, var(--bg), transparent 22%, transparent 78%, var(--bg));
}
.cta-inner { position: relative; z-index: 2; }
.cta-meta { font-size: 10px; color: var(--text-faint); margin-top: 40px; }

/* ----------------------------------------------------------------- 19. footer */
.site-footer { border-top: 1px solid var(--line); background: var(--bg-raise); padding-top: clamp(56px, 7vw, 90px); }
.footer-grid { display: grid; grid-template-columns: 1.6fr 1fr 1fr; gap: clamp(30px, 5vw, 60px); padding-bottom: 60px; }
.footer-name { font-size: clamp(1.7rem, 3vw, 2.4rem); font-weight: 500; letter-spacing: -.03em; margin: 0 0 12px; }
.footer-tag { font-size: 10px; color: var(--accent); margin: 0 0 20px; }
.footer-blurb { color: var(--text-dim); max-width: 40ch; font-size: .95rem; }
.socials { display: flex; gap: 10px; margin-top: 26px; }
.socials a {
    width: 38px; height: 38px; display: grid; place-items: center;
    border: 1px solid var(--line); color: var(--text-dim);
    font-family: var(--font-mono); font-size: 10px; letter-spacing: .05em;
    transition: color .3s var(--ease), border-color .3s var(--ease), transform .3s var(--ease);
}
.socials a:hover { color: #fff; border-color: var(--accent); transform: translateY(-3px); }
.footer-links { display: grid; gap: 11px; }
.footer-links a, .footer-links span { color: var(--text-dim); font-size: .95rem; transition: color .3s var(--ease); }
.footer-links a:hover { color: var(--accent); }
.footer-cta { margin-top: 26px; }
.footer-base {
    display: flex; flex-wrap: wrap; justify-content: space-between; gap: 14px;
    border-top: 1px solid var(--line); padding-block: 26px;
    font-size: 10px; color: var(--text-faint);
}

/* ---------------------------------------------------- 20. scroll-in reveals */
/* Progressive enhancement: without scroll-driven animation support, content is
   simply visible. Nothing depends on these running. */
/* Scroll-driven reveals were removed. `animation-timeline` reports as
   supported but produces no effect in the browsers this site is being viewed
   in, which left every revealed element stuck at its `from` keyframe — folds
   flat, camera frozen, headings invisible. Content is now simply visible, and
   the motion that remains runs on timers and hover, which work everywhere. */

/* ------------------------------------------------------------- 21. responsive */
@media (max-width: 1180px) {
    .nav { display: none; }
    .grid--3 { grid-template-columns: repeat(2, 1fr); }
    .process { grid-template-columns: 1fr; }
    .process-media { position: static; }
    .contact-grid { grid-template-columns: 1fr; }
    .footer-grid { grid-template-columns: 1fr 1fr; }
    .footer-brand { grid-column: 1 / -1; }
    .post-card--feature { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
    .post-card--feature .post-card-img { min-height: 240px; aspect-ratio: 16 / 9; }
}

@media (max-width: 680px) {
    .grid--2, .grid--3 { grid-template-columns: 1fr; }
    .gallery { grid-template-columns: 1fr; }
    .gallery figure:first-child { grid-column: auto; }
    .svc-row { grid-template-columns: 44px 1fr; }
    .svc-go { display: none; }
    .field-row { grid-template-columns: 1fr; }
    .footer-grid { grid-template-columns: 1fr; }
    .statement-panel { min-height: 78vh; }
    .compare { aspect-ratio: 4 / 3; }
}

/* --------------------------------------------------------- 22. reduced motion */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
    .gem, .hero-media, .marquee-track, .cta-bg { animation: none !important; }
}

/* -------------------------------------------------------------- 23. printing */
@media print {
    .site-header, .nav-drawer, .cta-band, .site-footer, .btn { display: none !important; }
    body { background: #fff; color: #000; }
}

/* =============================================================================
   24. BRAND — the studio's real logo
   ========================================================================== */
.brand-logo  { display: block; width: auto; height: clamp(28px, 3.4vw, 40px); }
.footer-logo { display: block; width: 100%; max-width: 300px; height: auto; margin-bottom: 18px; }
.drawer-meta { color: var(--text-faint); font-size: 10px; line-height: 2; margin: 0 0 30px; }
.footer-addr { display: block; max-width: 30ch; line-height: 1.7; }

/* =============================================================================
   25. HERO SHOWREEL — real video if supplied, cross-fading renders otherwise
   ========================================================================== */
.hero--film { min-height: min(96vh, 940px); }

.hero-video {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    z-index: 0;
}

/* Six frames, each held then cross-faded, with a slow push-in. Reads as film. */
.hero-seq { position: absolute; inset: 0; overflow: hidden; }
.hero-seq img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    opacity: 0;
    animation: seq 36s var(--ease) infinite;
    animation-delay: calc(var(--i) * 6s);
}
@keyframes seq {
    0%    { opacity: 0; transform: scale(1.04); }
    2.5%  { opacity: 1; }
    16.6% { opacity: 1; }
    19%   { opacity: 0; }
    100%  { opacity: 0; transform: scale(1.16); }
}

/* Headlines get a little real depth rather than a flat drop shadow. */
.depth {
    text-shadow:
        0 1px 0 rgba(0,0,0,.5),
        0 2px 0 rgba(0,0,0,.35),
        0 3px 0 rgba(0,0,0,.22),
        0 18px 40px rgba(0,0,0,.55);
}

/* =============================================================================
   26. RENDER WALL — two counter-scrolling bands, tilted back in 3D
   ========================================================================== */
.render-wall-section { overflow: hidden; }
.render-wall {
    margin-top: clamp(30px, 4vw, 54px);
    perspective: 1400px;
    perspective-origin: 50% 40%;
    display: grid; gap: clamp(10px, 1.4vw, 18px);
}
.render-band { overflow: hidden; transform-style: preserve-3d; }
.render-band--a { transform: rotateX(9deg) rotateZ(-1.4deg); }
.render-band--b { transform: rotateX(-7deg) rotateZ(1.4deg); }

.render-track { display: flex; width: max-content; gap: clamp(10px, 1.4vw, 18px); }
.render-band--a .render-track { animation: wall-l 68s linear infinite; }
.render-band--b .render-track { animation: wall-r 82s linear infinite; }
@keyframes wall-l { to { transform: translateX(-50%); } }
@keyframes wall-r { from { transform: translateX(-50%); } to { transform: none; } }

.render-cell {
    display: block; flex: none;
    width: clamp(210px, 24vw, 340px);
    aspect-ratio: 3 / 2;
    overflow: hidden;
    border: 1px solid var(--line);
    background: var(--bg-raise-2);
    transition: transform .6s var(--ease), border-color .5s var(--ease);
}
.render-cell img { width: 100%; height: 100%; object-fit: cover; }
.render-cell:hover { transform: translateZ(60px) scale(1.03); border-color: var(--accent); }

/* =============================================================================
   27. ROTATING RENDER CUBE — six real frames on a CSS 3D solid
   ========================================================================== */
.cube-layout {
    display: grid; grid-template-columns: minmax(280px, .8fr) 1fr;
    gap: clamp(36px, 6vw, 90px); align-items: center;
}
.cube-stage {
    perspective: 1100px;
    display: grid; place-items: center;
    min-height: 380px;
}
.cube {
    position: relative;
    width: 220px; height: 220px;
    transform-style: preserve-3d;
    animation: cube-spin 26s linear infinite;
}
.cube-face {
    position: absolute; inset: 0;
    display: block;
    border: 1px solid rgba(139, 62, 255, .5);
    box-shadow: 0 0 40px -10px var(--accent-glow);
    overflow: hidden;
    backface-visibility: visible;
}
.cube-face img { width: 100%; height: 100%; object-fit: cover; opacity: .92; }
/* 220px cube => translateZ half of that */
.cube-face--0 { transform: translateZ(110px); }
.cube-face--1 { transform: rotateY( 90deg) translateZ(110px); }
.cube-face--2 { transform: rotateY(180deg) translateZ(110px); }
.cube-face--3 { transform: rotateY(-90deg) translateZ(110px); }
.cube-face--4 { transform: rotateX( 90deg) translateZ(110px); }
.cube-face--5 { transform: rotateX(-90deg) translateZ(110px); }
@keyframes cube-spin {
    0%   { transform: rotateX(-18deg) rotateY(0deg); }
    100% { transform: rotateX(-18deg) rotateY(360deg); }
}

/* Capabilities list ---------------------------------------------------------- */
.cap-list { display: grid; gap: 0; }
.cap {
    display: flex; align-items: baseline; gap: 18px;
    padding-block: 16px;
    border-top: 1px solid var(--line);
    transition: padding-left .4s var(--ease), color .4s var(--ease);
}
.cap:last-child { border-bottom: 1px solid var(--line); }
.cap:hover { padding-left: 12px; }
.cap-num  { color: var(--accent); font-size: 10px; }
.cap-name { font-size: clamp(1.05rem, 1.7vw, 1.3rem); font-weight: 400; }
.tools { margin-top: 26px; font-size: 10px; color: var(--text-faint); }

/* =============================================================================
   28. AUDIENCE CARDS — real 3D tilt on hover
   ========================================================================== */
.tilt-grid { perspective: 1200px; }
.aud {
    border: 1px solid var(--line);
    padding: clamp(24px, 3vw, 38px);
    display: grid; gap: 12px; align-content: start;
    background: var(--bg-raise);
    transform-style: preserve-3d;
    transition: transform .6s var(--ease), border-color .5s var(--ease), box-shadow .6s var(--ease);
}
.aud:hover {
    transform: rotateX(6deg) rotateY(-6deg) translateZ(30px);
    border-color: var(--line-strong);
    box-shadow: -20px 24px 54px rgba(0,0,0,.55), 0 0 50px -20px var(--accent-glow);
}

/* =============================================================================
   29. Q&A — native <details> accordion, no script
   ========================================================================== */
.faq-wrap { max-width: 860px; }
.faq {
    border-top: 1px solid var(--line);
    transition: background .4s var(--ease);
}
.faq:last-of-type { border-bottom: 1px solid var(--line); }
.faq[open] { background: linear-gradient(90deg, var(--accent-soft), transparent 60%); }

.faq-q {
    display: grid; grid-template-columns: 42px 1fr 22px;
    gap: 16px; align-items: center;
    padding-block: clamp(20px, 2.4vw, 28px);
    cursor: pointer; list-style: none;
    transition: padding-left .4s var(--ease);
}
.faq-q::-webkit-details-marker { display: none; }
.faq-q:hover { padding-left: 10px; }
.faq-num  { font-size: 10px; color: var(--accent); }
.faq-text { font-size: clamp(1.02rem, 1.9vw, 1.35rem); font-weight: 500; letter-spacing: -.02em; }

/* Plus that rotates into a minus. */
.faq-sign { position: relative; width: 16px; height: 16px; justify-self: end; }
.faq-sign::before, .faq-sign::after {
    content: ''; position: absolute; left: 0; top: 50%;
    width: 16px; height: 1.5px; background: var(--text);
    transition: transform .4s var(--ease), background .4s var(--ease);
}
.faq-sign::after { transform: rotate(90deg); }
/* Open: the vertical bar swings flat onto the horizontal one — a clean minus.
   (rotate(180deg) would leave it vertical and still read as a plus.) */
.faq[open] .faq-sign::before { background: var(--accent); }
.faq[open] .faq-sign::after  { transform: rotate(0deg); background: var(--accent); }

.faq-a {
    padding: 0 38px clamp(22px, 2.6vw, 30px) 58px;
    color: var(--text-dim); max-width: 68ch;
}
.faq-still {
    margin-top: clamp(46px, 6vw, 76px);
    border: 1px solid var(--line);
    padding: clamp(28px, 4vw, 46px);
    background: var(--bg-raise);
}

/* =============================================================================
   30. Responsive + motion for the new blocks
   ========================================================================== */
@media (max-width: 1000px) {
    .cube-layout { grid-template-columns: 1fr; }
    .cube-stage  { min-height: 300px; }
    .cube        { width: 170px; height: 170px; }
    .cube-face--0 { transform: translateZ(85px); }
    .cube-face--1 { transform: rotateY( 90deg) translateZ(85px); }
    .cube-face--2 { transform: rotateY(180deg) translateZ(85px); }
    .cube-face--3 { transform: rotateY(-90deg) translateZ(85px); }
    .cube-face--4 { transform: rotateX( 90deg) translateZ(85px); }
    .cube-face--5 { transform: rotateX(-90deg) translateZ(85px); }
}
@media (max-width: 680px) {
    .brand-logo { height: 28px; }
    .faq-q { grid-template-columns: 32px 1fr 18px; gap: 12px; }
    .faq-a { padding-left: 44px; padding-right: 8px; }
    .render-band--a, .render-band--b { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .hero-seq img { animation: none; }
    .hero-seq img:not(:first-child) { display: none; }
    .hero-seq img:first-child { opacity: 1; }
    .cube, .render-track { animation: none !important; }
    .hero-video { display: none; }
}

/* ------------------------------------------------------- 31. services detail */
.svc-detail {
    display: grid;
    grid-template-columns: 68px 1fr .85fr;
    gap: clamp(20px, 3.4vw, 48px);
    align-items: start;
    border-top: 1px solid var(--line);
    padding-block: clamp(34px, 4.5vw, 64px);
}
.svc-detail:last-of-type { border-bottom: 1px solid var(--line); }
.svc-detail-body h2 { margin-bottom: 10px; }
.svc-one { color: var(--text); font-size: 1.05rem; margin-bottom: 14px; }
.svc-list { display: grid; gap: 10px; margin-top: 22px; }
.svc-list li { position: relative; padding-left: 22px; color: var(--text-dim); font-size: .94rem; }
.svc-list li::before {
    content: ''; position: absolute; left: 0; top: .62em;
    width: 6px; height: 6px; background: var(--accent); transform: rotate(45deg);
}
.svc-shot {
    overflow: hidden; border: 1px solid var(--line);
    background: var(--bg-raise-2);
    transform-style: preserve-3d;
    transition: transform .6s var(--ease), border-color .5s var(--ease);
}
.svc-shot img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.svc-detail:hover .svc-shot { transform: perspective(900px) rotateY(-5deg) translateZ(24px); border-color: var(--accent); }

@media (max-width: 900px) {
    .svc-detail { grid-template-columns: 44px 1fr; }
    .svc-shot { grid-column: 1 / -1; }
}

/* ------------------------------------------------------------ 32. founder */
.founder { display: grid; grid-template-columns: .8fr 1fr; gap: clamp(30px, 5vw, 70px); align-items: center; }
.founder-shot { overflow: hidden; border: 1px solid var(--line); background: var(--bg-raise-2); }
.founder-shot img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
@media (max-width: 900px) { .founder { grid-template-columns: 1fr; } }

/* =============================================================================
   33. BRAND MOTION — the effects the studio's own site is built on.
   Everything below is scroll-driven CSS. Still zero JavaScript.
   ========================================================================== */

/* ---- 33.1 Minimal header: logo, "Say Hello" pill, circular burger --------- */
.hello-pill {
    display: inline-flex; align-items: center; gap: .6em;
    border: 1px solid var(--line-strong); border-radius: 999px;
    padding: 13px 22px; font-size: .95rem; font-weight: 500;
    transition: background .4s var(--ease), color .4s var(--ease), transform .4s var(--ease);
}
.hello-pill:hover { background: var(--ink); color: var(--bg); transform: translateY(-2px); }

.burger-round {
    display: none;
    width: 52px; height: 52px; border-radius: 50%;
    background: var(--accent); color: #fff;
    place-content: center; gap: 5px;
    cursor: pointer; flex: none;
    transition: transform .45s var(--ease), background .4s var(--ease);
}
.burger-round span { display: block; width: 19px; height: 1.6px; background: currentColor;
                     transition: transform .4s var(--ease), opacity .25s; }
.burger-round:hover { transform: rotate(90deg); background: var(--accent-deep); }
#nav-toggle:checked ~ .site-header .burger-round span:nth-child(1) { transform: translateY(6.6px) rotate(45deg); }
#nav-toggle:checked ~ .site-header .burger-round span:nth-child(2) { opacity: 0; }
#nav-toggle:checked ~ .site-header .burger-round span:nth-child(3) { transform: translateY(-6.6px) rotate(-45deg); }

/* ---- 33.2 Giant type that slides sideways as you scroll ------------------- */
.scroll-band {
    position: relative;
    padding-block: clamp(40px, 7vw, 90px);
    overflow: hidden;
}
.scroll-text {
    white-space: nowrap;
    font-size: clamp(3.2rem, 12vw, 11rem);
    font-weight: 500; letter-spacing: -.045em; line-height: 1;
    color: var(--ink);
    will-change: transform;
    /* Fallback everywhere: a slow continuous drift. */
    animation: band-drift 46s linear infinite;
}
@keyframes band-drift { from { transform: translateX(6%); } to { transform: translateX(-52%); } }

/* The studio's prism mark sits over the band; the type runs behind it. */
.band-mark {
    position: absolute; left: 50%; top: 50%;
    translate: -50% -50%;
    width: clamp(180px, 24vw, 330px); aspect-ratio: 1;
    border-radius: 50%;
    display: grid; place-items: center;
    background: var(--bg);
    box-shadow: 0 0 0 1px var(--line-strong), 0 0 80px -18px var(--accent-glow);
    z-index: 2;
    animation: mark-float 9s ease-in-out infinite alternate;
}
.band-mark img { width: 86%; height: auto; }
@keyframes mark-float {
    from { transform: translateY(-10px) rotate(-3deg) scale(.98); }
    to   { transform: translateY(10px)  rotate(3deg)  scale(1.02); }
}

/* ---- 33.3 Renders that float at different depths -------------------------- */
.float-row {
    position: relative;
    display: grid; grid-template-columns: repeat(3, 1fr);
    gap: clamp(16px, 3vw, 44px);
    align-items: center;
    padding-block: clamp(30px, 6vw, 80px);
    perspective: 1400px;
}
.float-card {
    border-radius: 26px; overflow: hidden;
    background: var(--bg-raise-2);
    box-shadow: 0 30px 60px -28px rgba(0,0,0,.42);
    transform-style: preserve-3d;
    transition: transform .7s var(--ease), box-shadow .7s var(--ease);
}
.float-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.float-card--1 { aspect-ratio: 3/4;  transform: translateY(-26px) rotate(-2deg); }
.float-card--2 { aspect-ratio: 4/3;  transform: translateY(34px)  rotate(1.4deg); }
.float-card--3 { aspect-ratio: 3/4;  transform: translateY(-14px) rotate(2.4deg); }
.float-card:hover { transform: translateY(-16px) rotateX(6deg) rotateY(-6deg) scale(1.03);
                    box-shadow: 0 46px 90px -30px rgba(0,0,0,.5); }

/* ---- 33.4 Rotating "scroll for more" badge -------------------------------- */
.scroll-badge {
    position: absolute; right: clamp(16px, 4vw, 60px); bottom: clamp(16px, 4vw, 48px);
    width: 128px; height: 128px; z-index: 3;
    display: grid; place-items: center;
}
.scroll-badge svg { position: absolute; inset: 0; width: 100%; height: 100%;
                    animation: badge-spin 18s linear infinite; }
.scroll-badge text { font-family: var(--font-mono); font-size: 9.2px; letter-spacing: .22em;
                     fill: var(--text-dim); text-transform: uppercase; }
.scroll-badge .spark { font-size: 30px; color: var(--accent); line-height: 1; }
@keyframes badge-spin { to { transform: rotate(360deg); } }

/* ---- 33.5 STACKING CARDS — the signature scroll effect -------------------- */
.stack { display: grid; gap: clamp(18px, 2.4vw, 30px); perspective: 1800px; }

.stack-card {
    position: sticky;
    top: calc(96px + var(--n) * 16px);
    border-radius: var(--r);
    padding: clamp(30px, 4.4vw, 68px);
    min-height: min(72vh, 620px);
    display: grid; align-content: start;
    transform-origin: 50% 0%;
    box-shadow: 0 -1px 0 rgba(255,255,255,.06), 0 34px 80px -40px rgba(0,0,0,.5);
    overflow: hidden;
}
.stack-card--ink   { background: var(--panel); }
.stack-card--lav   { background: var(--lav); }
.stack-card--paper { background: var(--bg-raise); }

.stack-head { display: flex; justify-content: space-between; align-items: start; gap: 24px; }
.stack-title { font-size: clamp(1.9rem, 4.4vw, 3.7rem); font-weight: 500; letter-spacing: -.04em; max-width: 18ch; }
.stack-num   { font-size: clamp(1.9rem, 4.4vw, 3.7rem); font-weight: 500; opacity: .42; letter-spacing: -.04em; }
.stack-copy  { max-width: 52ch; margin-top: 22px; font-size: 1.02rem; color: var(--text-dim); }
.stack-shots { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(14px, 1.8vw, 24px); margin-top: auto; padding-top: clamp(26px, 4vw, 48px); }
.stack-shots figure { margin: 0; border-radius: 22px; overflow: hidden; aspect-ratio: 16/10; background: rgba(0,0,0,.2); }
.stack-shots img { width: 100%; height: 100%; object-fit: cover; transition: transform .9s var(--ease); }
.stack-shots figure:hover img { transform: scale(1.06); }

/* ---- 33.6 Back to top ----------------------------------------------------- */
.to-top {
    position: fixed; right: clamp(14px, 2.4vw, 30px); bottom: clamp(14px, 2.4vw, 30px);
    width: 52px; height: 52px; border-radius: 50%; z-index: 40;
    display: grid; place-items: center;
    background: var(--bg-raise); color: var(--text);
    box-shadow: 0 0 0 1px var(--line), 0 14px 30px -14px rgba(0,0,0,.4);
    transition: transform .4s var(--ease), background .4s var(--ease), color .4s var(--ease);
}
.to-top:hover { transform: translateY(-4px); background: var(--accent); color: #fff; }

/* ---- 33.7 Scroll-driven versions (progressive enhancement) ---------------- */
/* Same reason as above: these were scroll-driven and never ran. The
   flythrough now flies on its own timer (see .fly-cam below), which reads the
   same on screen and works in every browser. */

/* ---- 33.8 Responsive ------------------------------------------------------ */
@media (max-width: 900px) {
    .float-row   { grid-template-columns: 1fr 1fr; }
    .float-card--3 { display: none; }
    .stack-shots { grid-template-columns: 1fr; }
    .stack-card  { min-height: auto; top: calc(84px + var(--n) * 10px); }
    .scroll-badge { width: 96px; height: 96px; }
}
@media (max-width: 680px) {
    :root { --r: 34px; }
    .float-row { grid-template-columns: 1fr; }
    .float-card--2 { display: none; }
    .stack-head { flex-direction: column; }
}
@media (prefers-reduced-motion: reduce) {
    .scroll-text, .band-mark, .scroll-badge svg { animation: none !important; }
}

/* =============================================================================
   34. HOMEPAGE HERO (brand layout) + number cards
   ========================================================================== */
.brand-hero { position: relative; padding-top: clamp(24px, 4vw, 54px); }

.brand-hero-title {
    font-size: clamp(1.55rem, 3.2vw, 2.7rem);
    font-weight: 500; letter-spacing: -.035em; line-height: 1.16;
    max-width: 22ch;
}
.brand-hero-title .dim { color: var(--text-faint); }

.brand-hero-bottom {
    position: relative;
    display: flex; justify-content: space-between; align-items: flex-end; gap: 30px;
    min-height: 150px;
}
.hero-services { display: grid; gap: 5px; }
.hero-services a {
    font-size: clamp(.98rem, 1.35vw, 1.12rem);
    color: var(--text); position: relative; display: inline-block;
    transition: color .3s var(--ease), transform .35s var(--ease);
}
.hero-services a::after {
    content: ''; position: absolute; left: 0; bottom: -2px; width: 100%; height: 1px;
    background: var(--accent-deep); transform: scaleX(0); transform-origin: right;
    transition: transform .4s var(--ease);
}
.hero-services a:hover { color: var(--accent-deep); transform: translateX(6px); }
.hero-services a:hover::after { transform: scaleX(1); transform-origin: left; }

/* Numbers laid over real renders, the way the studio does it. */
.numgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(14px, 1.8vw, 24px); }
.numcard {
    position: relative; overflow: hidden;
    border-radius: 28px; aspect-ratio: 4 / 5;
    display: grid; align-items: end;
    isolation: isolate;
    transform-style: preserve-3d;
    transition: transform .65s var(--ease), box-shadow .65s var(--ease);
}
.numcard-bg {
    position: absolute; inset: 0; z-index: -2;
    width: 100%; height: 100%; object-fit: cover;
    transition: transform 1s var(--ease);
}
.numcard::after {
    content: ''; position: absolute; inset: 0; z-index: -1;
    background: linear-gradient(to top, rgba(12,12,14,.92), rgba(12,12,14,.35) 55%, rgba(12,12,14,.15));
}
.numcard:hover { transform: perspective(1000px) rotateX(5deg) rotateY(-5deg) translateZ(24px); box-shadow: 0 40px 70px -34px rgba(0,0,0,.55); }
.numcard:hover .numcard-bg { transform: scale(1.08); }
.numcard-inner { padding: clamp(18px, 2vw, 28px); display: grid; gap: 4px; }
.numcard-n { font-size: clamp(2rem, 3.6vw, 3.3rem); font-weight: 500; letter-spacing: -.05em; color: #fff; font-variant-numeric: tabular-nums; }
.numcard-l { font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em; text-transform: uppercase; color: rgba(255,255,255,.8); }

@media (max-width: 900px) { .numgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .numgrid { grid-template-columns: 1fr; } .numcard { aspect-ratio: 16/10; } }

/* =============================================================================
   35. LIGHT-THEME CORRECTIONS to components originally built for a dark page
   ========================================================================== */
/* Section headings now sit on cream. */
.h1, .h2 { color: var(--ink); }
.on-dark .h1, .on-dark .h2, .on-lav .h1, .on-lav .h2 { color: var(--text); }

/* Cards and media get the brand radius. */
.pcard, .post-card, .compare, .process-media, .gallery figure,
.article-hero, .svc-shot, .founder-shot, .render-cell, .cube-face { border-radius: 26px; }
.pcard-img img, .post-card-img img { border-radius: 0; }

/* Hero image pages (project, about): keep white type, soften the veil. */
.hero-veil {
    background:
        linear-gradient(100deg, rgba(14,12,20,.86) 8%, rgba(14,12,20,.5) 52%, rgba(14,12,20,.28) 100%),
        linear-gradient(to top, var(--bg) 1%, rgba(8,8,11,0) 32%);
}
.hero .hero-inner { color: #fff; }
.hero .hero-inner .h1, .hero .hero-inner h1 { color: #fff; }
.hero .lead, .hero .hero-sub { color: rgba(255,255,255,.86); }
.hero .eyebrow { color: #c8bbf5; }
.hero .stat-num { color: #fff; }
.hero .stat-label { color: rgba(255,255,255,.68); }
.hero .btn--outline { color: #fff; border-color: rgba(255,255,255,.42); }
.hero .btn--solid { background: #fff; color: var(--bg); border-color: #fff; }

/* Marquee reads as a quiet rule on a light page. */
.marquee { background: transparent; border-color: var(--line); }
.marquee-track > span { color: var(--text-faint); }

/* CTA band becomes an ink card rather than a dark bleed. */
.cta-band { padding-block: clamp(30px, 5vw, 60px); }
.cta-band::after { display: none; }
.cta-bg { display: none; }
.cta-inner {
    background: var(--panel); border-radius: var(--r);
    box-shadow: inset 0 0 0 1px rgba(139,62,255,.3), 0 40px 90px -50px var(--accent-glow);
    padding: clamp(38px, 6vw, 96px);
    --text: #f6f4fb; --text-dim: rgba(246,244,251,.72); --text-faint: rgba(246,244,251,.5);
    --line: rgba(255,255,255,.16); --line-strong: rgba(255,255,255,.34);
    color: var(--text);
}
.cta-inner .eyebrow { color: #c8bbf5; }
.cta-inner .h1 { color: #fff; }
.cta-inner .accent { color: #c8bbf5; }
.cta-inner .btn--solid { background: #fff; color: var(--bg); border-color: #fff; }
.cta-inner .btn--ghost { color: rgba(255,255,255,.8); border-color: rgba(255,255,255,.28); }

/* Footer on cream. */
.site-footer { background: transparent; border-top: 1px solid var(--line); }
.socials a { border-radius: 50%; }
.socials a:hover { color: #fff; background: var(--accent); border-color: var(--accent); }

/* Statement panels (kept for other pages) need dark-on-light handling. */
.statement-panel { background: var(--ink); border-radius: var(--r); overflow: hidden; }
.statement-panel .statement-inner { color: #fff; }
.statement-big, .statement-small { color: #fff; }

/* Process + services rows on light. */
.svc-row:hover { background: linear-gradient(90deg, var(--accent-soft), transparent 55%); }
.svc-row:hover .svc-title { color: var(--accent-deep); }
.svc-go { border-radius: 50%; }
.svc-row:hover .svc-go { background: var(--accent-deep); border-color: var(--accent-deep); color: #fff; }
.process-num { color: var(--accent-deep); }
.faq-num, .cap-num { color: var(--accent-deep); }
.compare-tag { background: rgba(8,8,11,.74); color: var(--text); }
.compare-tag--r { color: var(--accent-deep); }
.field input, .field select, .field textarea { border-radius: 16px; background: var(--bg-raise); }
.notice { border-radius: 20px; }
.faq-still, .aud, .svc-detail .svc-shot { border-radius: 30px; }

/* The 3D cube glows lavender on cream. */
.cube-face { border-color: rgba(159,139,231,.7); box-shadow: 0 0 40px -12px var(--accent-glow); }


/* =============================================================================
   36. Menu button visibility — declared last so it wins the cascade over the
   base `.burger-round { display: none }` above.
   ========================================================================== */
@media (max-width: 1180px) {
    .burger-round { display: grid; }
}

/* =============================================================================
   37. OCTARINE — original 3D set pieces
   Organising idea: the studio's mark is light through a prism. Drawings go in,
   a spectrum comes out. Every piece below is that idea in motion.
   All pure CSS. Still no JavaScript anywhere on this site.
   ========================================================================== */

/* ---- 37.0 shared: the spectrum --------------------------------------------- */
:root {
    --spectrum: linear-gradient(90deg, #f5a524, #f2508f, #9f8be7, #4cc4f0, #6ee7c7);
    --spectrum-soft: linear-gradient(90deg, rgba(245,165,36,.16), rgba(242,80,143,.16),
                                      rgba(159,139,231,.2), rgba(76,196,240,.16), rgba(110,231,199,.16));
}
.rule-spectrum { height: 2px; border: 0; background: var(--spectrum); border-radius: 2px; opacity: .8; }

/* Headline word that carries the spectrum instead of a flat accent colour. */
.prism-word {
    background: var(--spectrum);
    -webkit-background-clip: text; background-clip: text;
    color: transparent;
    background-size: 220% 100%;
    animation: prism-shift 9s ease-in-out infinite alternate;
}
@keyframes prism-shift { to { background-position: 100% 0; } }

/* =============================================================================
   37.1 PRISM HERO — a real CSS-3D prism splitting a beam, drawings on one side,
   finished renders on the other.
   ========================================================================== */
.prism-hero {
    position: relative;
    display: grid; grid-template-columns: 1.05fr .95fr;
    gap: clamp(30px, 5vw, 80px); align-items: center;
    padding-block: clamp(40px, 7vw, 110px);
    min-height: min(88vh, 880px);
}
.prism-hero-copy { position: relative; z-index: 3; }
.prism-hero h1 {
    font-size: clamp(2.3rem, 5.2vw, 4.6rem);
    letter-spacing: -.045em; line-height: 1.02; max-width: 16ch;
    margin-bottom: 26px;
}

/* The stage: an incoming white beam, a rotating glass solid, a fanned spectrum. */
.prism-stage {
    position: relative; aspect-ratio: 1 / 1;
    perspective: 1200px; perspective-origin: 42% 50%;
    display: grid; place-items: center;
}
.prism-beam {
    position: absolute; left: -6%; top: 50%; width: 52%; height: 3px;
    translate: 0 -50%;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.22) 20%, rgba(255,255,255,.8));
    z-index: 1;
}
.prism-beam::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, #fff, transparent);
    animation: beam-run 3.4s var(--ease) infinite;
}
@keyframes beam-run { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

/* Fanned output: five coloured blades spreading out of the solid. */
.prism-fan { position: absolute; right: 2%; top: 50%; width: 56%; height: 60%; z-index: 1; }
.prism-fan i {
    position: absolute; left: 0; top: 50%; height: 2px; width: 100%;
    transform-origin: 0 50%;
    border-radius: 2px;
    opacity: .85;
    animation: fan-breathe 6s var(--ease) infinite alternate;
    animation-delay: calc(var(--i) * .28s);
}
.prism-fan i:nth-child(1) { background: linear-gradient(90deg, #f5a524, transparent); --a: -22deg; }
.prism-fan i:nth-child(2) { background: linear-gradient(90deg, #f2508f, transparent); --a: -11deg; }
.prism-fan i:nth-child(3) { background: linear-gradient(90deg, #9f8be7, transparent); --a:   0deg; }
.prism-fan i:nth-child(4) { background: linear-gradient(90deg, #4cc4f0, transparent); --a:  11deg; }
.prism-fan i:nth-child(5) { background: linear-gradient(90deg, #6ee7c7, transparent); --a:  22deg; }
@keyframes fan-breathe {
    from { transform: rotate(var(--a)) scaleX(.82); opacity: .55; }
    to   { transform: rotate(calc(var(--a) * 1.25)) scaleX(1); opacity: .95; }
}

/* The prism the beam strikes. Small, glassy, sitting where the beam ends and
   the spectrum begins - it is the hinge of the whole idea, not the subject. */
.prism-solid {
    position: absolute; left: 26%; top: 50%; translate: -50% -50%;
    width: clamp(58px, 7vw, 92px); aspect-ratio: 1; z-index: 1;
    transform-style: preserve-3d;
    animation: solid-turn 26s linear infinite;
}
.prism-solid span {
    position: absolute; inset: 0;
    border: 1px solid rgba(255,255,255,.14);
    background:
        linear-gradient(150deg, rgba(139,62,255,.45), rgba(76,196,240,.18) 58%, rgba(255,255,255,.07)),
        linear-gradient(0deg, rgba(8,8,11,.35), rgba(8,8,11,.35));
    box-shadow: inset 0 1px 0 rgba(255,255,255,.24);
}
.prism-solid span:nth-child(1) { transform: rotateY(  0deg) translateZ(30px); }
.prism-solid span:nth-child(2) { transform: rotateY( 90deg) translateZ(30px); }
.prism-solid span:nth-child(3) { transform: rotateY(180deg) translateZ(30px); }
.prism-solid span:nth-child(4) { transform: rotateY(270deg) translateZ(30px); }
@keyframes solid-turn { to { transform: rotateX(-14deg) rotateY(360deg); } }

/* The renders: what comes OUT of the prism.

   This was a six-panel carousel turning through a full 360deg inside the narrow
   grid column. Six cards on one circle inevitably crossed each other, ran into
   the glass solid and were clipped at the column edge, and each one had to stay
   small to fit - so the work itself was never actually legible. For a studio
   that sells renders, that was the wrong thing to optimise.

   It is now three cards, roughly twice the size, placed along the spectrum fan
   where the light lands. Nothing rotates through anything else, so nothing can
   collide or clip; each drifts on its own slow timer to keep the composition
   alive. */
.prism-orbit { position: absolute; inset: 0; z-index: 3; transform-style: preserve-3d; }
.prism-orbit figure {
    position: absolute; margin: 0;
    width: clamp(118px, 14.5vw, 196px); aspect-ratio: 4/3;
    border-radius: 18px; overflow: hidden;
    background: var(--bg-raise-2);
    box-shadow: 0 30px 60px -28px rgba(0,0,0,.85), 0 0 0 1px rgba(255,255,255,.08);
    will-change: transform;
}
/* Vertical spacing is what keeps these apart: they share horizontal space by
   design (that is the fan), so each band has ~40px of clearance above and below
   at the 1320px wrap - more than twice the drift amplitude, so no two can ever
   touch however the timers line up. */
.prism-orbit figure:nth-child(1) { right: 38%; top:  2%; animation: drift-a 13s var(--ease) infinite alternate; }
.prism-orbit figure:nth-child(2) { right:  4%; top: 38%; animation: drift-b 16s var(--ease) infinite alternate; z-index: 2; }
.prism-orbit figure:nth-child(3) { right: 32%; top: 70%; animation: drift-c 15s var(--ease) infinite alternate; }
.prism-orbit figure:nth-child(n+4) { display: none; }   /* the pool supplies more than are used */
.prism-orbit img { width: 100%; height: 100%; object-fit: cover; }

@keyframes drift-a {
    from { transform: perspective(1200px) rotateY( 9deg) translate3d(0,   0, 0); }
    to   { transform: perspective(1200px) rotateY( 5deg) translate3d(-8px, 10px, 26px); }
}
@keyframes drift-b {
    from { transform: perspective(1200px) rotateY(-7deg) translate3d(0,  0, 30px); }
    to   { transform: perspective(1200px) rotateY(-3deg) translate3d(10px, -11px, 52px); }
}
@keyframes drift-c {
    from { transform: perspective(1200px) rotateY( 7deg) translate3d(0,   0, 10px); }
    to   { transform: perspective(1200px) rotateY( 3deg) translate3d(-12px, -8px, 32px); }
}

@media (max-width: 900px) {
    .prism-hero { grid-template-columns: 1fr; }
    .prism-stage { aspect-ratio: 4/3; width: 100%; max-width: 460px; margin-inline: auto; }
}

/* =============================================================================
   37.2 FLYTHROUGH — scroll drives the camera through a corridor of renders.
   The showpiece: you travel into the work rather than scrolling past it.
   ========================================================================== */
.flythrough { position: relative; height: 400vh; }
.fly-stage {
    position: sticky; top: 0; height: 100vh;
    overflow: hidden;
    /* Own compositing layer: keeps the 3D camera off the page's main layer. */
    isolation: isolate;
    transform: translateZ(0);
    perspective: 900px; perspective-origin: 50% 46%;
    background: radial-gradient(120% 90% at 50% 40%, #17102b 0%, var(--bg) 62%);
}
.fly-cam {
    position: absolute; inset: 0; transform-style: preserve-3d;
}
.fly-cam figure {
    position: absolute; left: 50%; top: 50%; margin: 0;
    width: clamp(210px, 26vw, 400px); aspect-ratio: 3/2;
    border-radius: 22px; overflow: hidden;
    box-shadow: 0 40px 70px -34px rgba(0,0,0,.5);
    /* --x/--y place the frame off-axis so you fly between them, not at them. */
    transform:
        translate(-50%, -50%)
        translate3d(calc(var(--x) * 40vw), calc(var(--y) * 34vh), calc(var(--z) * -540px))
        rotateY(calc(var(--x) * -9deg));
}
.fly-cam img { width: 100%; height: 100%; object-fit: cover; }

.fly-copy {
    position: absolute; inset: auto 0 clamp(28px, 6vh, 70px); z-index: 5;
    text-align: center; pointer-events: none;
}
.fly-copy h2 { font-size: clamp(1.8rem, 4.4vw, 3.4rem); letter-spacing: -.04em; }
.fly-hint { font-family: var(--font-mono); font-size: 10px; letter-spacing: .24em;
            text-transform: uppercase; color: var(--text-faint); margin-top: 12px; }

/* =============================================================================
   37.3 SPECTRUM RING — renders on the face of a rotating 3D cylinder.
   ========================================================================== */
/* Nine panels, 40deg apart. The gap between neighbours is the ring's chord,
   2*R*sin(20deg) ~= 0.684*R, so the panel must stay well under that or the
   frames overlap and the ring reads as one stitched band. Panel is held at
   ~0.58*R, which leaves a visible gap at every viewport width:
     R 500 -> chord 342, panel 290, gap 52px
     R 37vw -> chord 25.3vw, panel 21vw, gap 4.3vw
     R 330 -> chord 226, panel 190, gap 36px */
.ring-wrap { perspective: 1500px; height: clamp(320px, 38vw, 460px); display: grid; place-items: center; overflow: hidden; }
.ring {
    position: relative; width: clamp(190px, 21vw, 290px); aspect-ratio: 4/3;
    transform-style: preserve-3d;
    animation: ring-turn 44s linear infinite;
}
.ring figure {
    position: absolute; inset: 0; margin: 0;
    border-radius: 18px; overflow: hidden;
    border: 1px solid rgba(255,255,255,.28);
    box-shadow: 0 26px 50px -26px rgba(0,0,0,.45);
    transform: rotateY(calc(var(--i) * 40deg)) translateZ(clamp(330px, 37vw, 500px));
    backface-visibility: hidden;
}
.ring img { width: 100%; height: 100%; object-fit: cover; }
@keyframes ring-turn { to { transform: rotateX(-6deg) rotateY(360deg); } }

/* =============================================================================
   37.4 FOLDING PANELS — services unfold from flat as they enter view.
   ========================================================================== */
.folds { perspective: 1500px; display: grid; gap: clamp(14px, 1.8vw, 22px); }
.fold {
    transform-origin: 50% 0%;
    border-radius: 30px;
    padding: clamp(20px, 2.2vw, 30px) clamp(22px, 2.6vw, 36px);
    display: grid; grid-template-columns: 58px 1fr auto; gap: clamp(16px, 2.4vw, 34px);
    align-items: center;
    background: var(--bg-raise);
    box-shadow: 0 22px 44px -30px rgba(0,0,0,.35);
    transition: transform .55s var(--ease), box-shadow .55s var(--ease), background .5s var(--ease);
}
.fold:hover {
    transform: perspective(1200px) rotateX(4deg) translateZ(26px) !important;
    box-shadow: 0 40px 70px -34px rgba(0,0,0,.6), 0 0 60px -26px var(--accent-glow);
    background: var(--bg-raise-2);
}
.fold-n { font-family: var(--font-mono); font-size: 11px; letter-spacing: .2em; color: var(--accent-deep); }
.fold-t { display: block; font-size: clamp(1.2rem, 2.3vw, 1.85rem); font-weight: 500; letter-spacing: -.03em; }
.fold-d { display: block; color: var(--text-dim); font-size: .95rem; margin-top: 6px; max-width: 60ch; }
.fold-shot { width: clamp(86px, 11vw, 150px); aspect-ratio: 4/3; border-radius: 16px; overflow: hidden; flex: none; }
.fold-shot img { width: 100%; height: 100%; object-fit: cover; transition: transform .8s var(--ease); }
.fold:hover .fold-shot img { transform: scale(1.1); }

/* =============================================================================
   37.5 FLIP MOSAIC — tiles that turn in 3D to a second render.
   ========================================================================== */
.mosaic { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(10px, 1.4vw, 18px); perspective: 1400px; }
.tile { position: relative; aspect-ratio: 1; transform-style: preserve-3d;
        transition: transform .8s var(--ease);
        will-change: transform; }
/* A tile turns only while the cursor is on that tile, and turns back when it
   leaves. No timer, no `!important`: nothing else writes to transform, so the
   transition is free to carry the whole movement smoothly in both directions. */
.tile:hover { transform: rotateY(180deg); }
.tile figure { position: absolute; inset: 0; margin: 0; border-radius: 20px; overflow: hidden; backface-visibility: hidden; }
.tile figure:last-child { transform: rotateY(180deg); }
.tile img { width: 100%; height: 100%; object-fit: cover; }
@keyframes tile-idle {
    0%, 42%  { transform: rotateY(0deg); }
    50%, 92% { transform: rotateY(180deg); }
    100%     { transform: rotateY(360deg); }
}
@media (max-width: 780px) { .mosaic { grid-template-columns: repeat(2, 1fr); } }

/* =============================================================================
   37.6 Scroll-driven layer — only where the browser supports it.
   ========================================================================== */
/* The last scroll-driven block (flythrough camera, fold unfolding, parallax)
   was removed with the others — it never ran. The flythrough now flies on a
   timer and the folds lift on hover. */

@media (prefers-reduced-motion: reduce) {
    .prism-solid, .prism-orbit, .prism-fan i, .prism-beam::after,
    .ring, .tile, .prism-word { animation: none !important; }
    .flythrough { height: auto; }
    .fly-stage { position: static; height: auto; padding-block: 60px; perspective: none; }
    .fly-cam { position: static; display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
    .fly-cam figure { position: static; transform: none !important; width: auto; }
}

/* ------------------------------------------------------- 38. founder block */
.founder { grid-template-columns: minmax(240px, .6fr) 1fr; align-items: start; }
.founder-shot { border-radius: 30px; max-width: 360px; box-shadow: 0 30px 60px -34px rgba(0,0,0,.4); }
.founder-shot img { aspect-ratio: 4 / 5; object-position: 50% 22%; }
.founder-name { font-size: clamp(1.8rem, 3.4vw, 2.8rem); font-weight: 500; letter-spacing: -.04em; line-height: 1.02; }
.founder-role { font-size: 10px; color: var(--accent-deep); margin-top: 10px; }

/* Membership / credential pill. Same mono-caps idiom as .filter-chip, but static. */
.founder-cred {
    display: inline-block; margin-top: 14px;
    border-radius: 999px; padding: 9px 16px;
    border: 1px solid var(--line); background: var(--accent-soft);
    font-size: 10px; font-weight: 500; letter-spacing: .18em; text-transform: uppercase;
    color: var(--text-dim);
}
.founder-words { display: grid; gap: 18px; }
.founder-lead { font-size: clamp(1.15rem, 1.9vw, 1.5rem); line-height: 1.45; letter-spacing: -.02em; color: var(--ink); margin: 0; }
.founder-words .dim { margin: 0; font-size: .98rem; }
@media (max-width: 900px) { .founder { grid-template-columns: 1fr; } }

/* Over a photographic hero the mid-tone spectrum loses contrast, so it switches
   to a brighter range and gains a shadow to hold against any image behind it. */
/* On black the whole spectrum runs bright — the mid-tone set was mixed for
   cream and goes muddy here. */
.prism-word { background-image: linear-gradient(90deg, #ffc978, #ff8ec0, #b79bff, #7fd7ff, #86f0d6); }
.on-lav .prism-word { background-image: linear-gradient(90deg, #6d3800, #94004f, #33197f, #004a6b, #075c46); }

/* =============================================================================
   39. THE FLOW — the studio's ribbon artwork, carried across every page.
   Colour stops sampled directly from the printed stationery.
   ========================================================================== */
:root {
    /* The brand ribbon, left to right, as a gradient. */
    --flow: linear-gradient(90deg,
        #f5c518  0%,   /* gold      */
        #ff7a1a 12%,   /* amber     */
        #ff2d55 26%,   /* red       */
        #e0006e 38%,   /* crimson   */
        #a3009e 50%,   /* magenta   */
        #7b1fd6 63%,   /* violet    */
        #3b3fd8 76%,   /* indigo    */
        #1f9fe8 88%,   /* blue      */
        #4fd6f5 100%   /* cyan      */
    );
    --flow-img: url('../img/brand/flow-b.jpg');
}

/* ---- 39.1 Ambient wash: the ribbon drifting behind every page ------------- */
/* The wash used to be a full-viewport fixed layer carrying mix-blend-mode AND a
   38px blur AND a looping scale/rotate. Chrome re-rasterised that every frame and,
   alongside the sticky 3D sections, would drop the layer and flash the page black.
   The blur is now baked into a 4KB image, the blend is gone (the artwork sits on
   black over a black page, so screen and normal are identical here), and the
   animation is a plain GPU translate. */
body::before {
    content: '';
    position: fixed; inset: -14% -18%;
    z-index: 0; pointer-events: none;
    background: url('../img/brand/flow-ambient.jpg') center / cover no-repeat;
    opacity: .22;
    transform: translateZ(0);
    will-change: transform;
    animation: flow-drift 60s linear infinite alternate;
}
@keyframes flow-drift {
    from { transform: translate3d(-3%, -1.5%, 0); }
    to   { transform: translate3d( 3%,  1.5%, 0); }
}
/* Content sits above the ambient wash. The header is deliberately left out:
   it is `position: sticky`, and setting `position: relative` here silently
   un-stuck it — which is why the nav scrolled away instead of pinning. */
main, .site-footer { position: relative; z-index: 1; }
.site-header { z-index: 50; }        /* already sticky; only the layer matters */
.to-top, .nav-drawer { z-index: 40; }

/* ---- 39.2 Ribbon band: a real slice of the artwork between sections ------- */
.flow-band {
    position: relative;
    height: clamp(120px, 17vw, 260px);
    overflow: hidden;
    margin-block: clamp(20px, 4vw, 50px);
}
.flow-band img {
    position: absolute; left: 50%; top: 50%;
    translate: -50% -50%;
    width: 132%; max-width: none;
    transform: translateZ(0);
    will-change: transform;
    animation: band-slide 34s linear infinite alternate;
}
@keyframes band-slide {
    from { transform: translateX(-6%) scale(1.02); }
    to   { transform: translateX(6%)  scale(1.1); }
}
.flow-band::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, var(--bg) 0%, transparent 14%, transparent 86%, var(--bg) 100%),
                linear-gradient(to bottom, var(--bg) 0%, transparent 22%, transparent 78%, var(--bg) 100%);
}
.flow-band-words {
    position: absolute; inset: 0; z-index: 2;
    display: flex; align-items: center; justify-content: center; gap: clamp(18px, 4vw, 60px);
    font-family: var(--font-mono); font-size: clamp(10px, 1.15vw, 13px);
    letter-spacing: .34em; text-transform: uppercase; color: #fff;
    text-shadow: 0 2px 18px rgba(0,0,0,.9);
}
.flow-band-words i { width: 5px; height: 5px; background: #fff; transform: rotate(45deg); flex: none; opacity: .8; }

/* ---- 39.3 Everything that was flat purple now carries the flow ------------ */
.rule-spectrum { background: var(--flow); }
.prism-word    { background-image: var(--flow); background-size: 260% 100%; }
.marquee       { border-image: var(--flow) 1; border-width: 1px 0; border-style: solid; }

/* Fan blades in the prism hero, matched to the ribbon. */
.prism-fan i:nth-child(1) { background: linear-gradient(90deg, #f5c518, transparent); }
.prism-fan i:nth-child(2) { background: linear-gradient(90deg, #ff2d55, transparent); }
.prism-fan i:nth-child(3) { background: linear-gradient(90deg, #a3009e, transparent); }
.prism-fan i:nth-child(4) { background: linear-gradient(90deg, #3b3fd8, transparent); }
.prism-fan i:nth-child(5) { background: linear-gradient(90deg, #4fd6f5, transparent); }

/* A hairline of the ribbon under the sticky header. */
.site-header::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
    background: var(--flow); background-size: 200% 100%;
    opacity: .9;
    animation: prism-shift 14s ease-in-out infinite alternate;
}

/* Cards pick up the ribbon on hover instead of a single purple. */
.pcard:hover, .fold:hover, .numcard:hover, .aud:hover {
    box-shadow: 0 40px 80px -40px rgba(0,0,0,.75), 0 0 70px -30px #7b1fd6, 0 0 90px -50px #1f9fe8;
}
.tile figure, .ring figure, .fly-cam figure, .float-card, .prism-orbit figure {
    box-shadow: 0 26px 50px -26px rgba(0,0,0,.7), 0 0 0 1px rgba(255,255,255,.08);
}

/* CTA card gets a ribbon edge. */
.cta-inner { position: relative; overflow: hidden; }
.cta-inner::before {
    content: ''; position: absolute; left: 0; right: 0; top: 0; height: 3px;
    background: var(--flow);
}
/* The ribbon bleeding off the card's bottom-right corner.
   `flow-b.jpg` is a JPEG whose artwork sits on solid black. Everywhere else on
   the site that is invisible, because the page behind it is black too — which
   is exactly why the blend mode was dropped from `body::before`. Here it sits
   on `--panel` (#150e26), so the black rectangle was showing through at .5
   opacity as a hard-edged box across the bottom of the card.
   `screen` drops the black out and keeps only the light of the ribbon, and the
   mask feathers the two inner edges so the artwork ends in a glow rather than
   at a straight line. */
.cta-inner::after {
    content: ''; position: absolute; right: -12%; bottom: -55%;
    width: 70%; aspect-ratio: 1800 / 695;
    background: var(--flow-img) center / cover no-repeat;
    mix-blend-mode: screen;
    opacity: .62;
    -webkit-mask-image: radial-gradient(75% 75% at 72% 68%, #000 38%, transparent 78%);
            mask-image: radial-gradient(75% 75% at 72% 68%, #000 38%, transparent 78%);
    pointer-events: none;
}

/* Footer sits on a soft ribbon glow. */
.site-footer { position: relative; overflow: hidden; }
.site-footer::before {
    content: ''; position: absolute; left: -10%; right: -10%; bottom: -60%;
    height: 150%;
    background: url('../img/brand/flow-glow.jpg') center / cover no-repeat;
    opacity: .3;                      /* pre-blurred asset, no runtime filter */
    pointer-events: none;
}
.site-footer > * { position: relative; z-index: 1; }

/* ---- 39.4 Scroll progress, drawn in the ribbon ---------------------------- */
.flow-progress {
    position: fixed; left: 0; top: 0; height: 3px; width: 100%;
    z-index: 60; transform-origin: 0 50%; transform: scaleX(0);
    background: var(--flow);
    pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
    body::before, .flow-band img, .site-header::after { animation: none !important; }
}
@media (max-width: 680px) {
    body::before { opacity: .13; }
    .flow-band-words { letter-spacing: .2em; gap: 14px; }
}

.hero-strap {
    font-family: var(--font-mono); font-size: 11px; letter-spacing: .3em;
    text-transform: uppercase; color: var(--text-dim); margin: -8px 0 22px;
}
.cta-motto { font-size: 11px; letter-spacing: .3em; color: #c8bbf5; margin-top: 18px; }

/* ------------------------------------------- 40. concept sheet → final visual */
.ba {
    display: grid; grid-template-columns: 1fr auto 1fr;
    gap: clamp(16px, 2.6vw, 34px); align-items: center;
    perspective: 1400px;
}
.ba-panel { margin: 0; display: grid; gap: 14px; transform-style: preserve-3d;
            transition: transform .6s var(--ease); }
.ba-panel img {
    width: 100%; border-radius: 24px; display: block;
    background: var(--bg-raise-2);
    box-shadow: 0 30px 60px -34px rgba(0,0,0,.8), 0 0 0 1px var(--line);
}
.ba-panel:first-child:hover  { transform: rotateY( 5deg) translateZ(24px); }
.ba-panel:last-child:hover   { transform: rotateY(-5deg) translateZ(24px); }
.ba-panel figcaption { color: var(--text-dim); font-size: .92rem; line-height: 1.6; }
.ba-panel figcaption .mono {
    display: block; font-size: 10px; letter-spacing: .22em;
    color: var(--accent-deep); margin-bottom: 5px;
}
.ba-arrow { color: var(--text-faint); display: grid; place-items: center; }
@media (max-width: 820px) {
    .ba { grid-template-columns: 1fr; }
    .ba-arrow { transform: rotate(90deg); justify-self: center; }
}

/* The concept panel is a blueprint, so it gets a cool rim rather than the
   neutral hairline the render panel uses. */
.ba-panel:first-child img {
    box-shadow: 0 30px 60px -34px rgba(0,0,0,.8),
                0 0 0 1px rgba(143, 216, 255, .28),
                0 0 60px -26px rgba(60, 140, 255, .55);
}
.ba-panel:first-child figcaption .mono { color: #8fd8ff; }

/* ------------------------------------------------ 41. the hand-drawn sheet */
.paper-note {
    display: grid; grid-template-columns: 1.15fr .85fr;
    gap: clamp(24px, 4vw, 56px); align-items: center;
    margin-top: clamp(46px, 7vw, 90px);
    perspective: 1400px;
}
.paper-shot { margin: 0; border-radius: 26px; overflow: hidden;
              transform-style: preserve-3d;
              transition: transform .7s var(--ease), box-shadow .7s var(--ease);
              box-shadow: 0 30px 60px -34px rgba(0,0,0,.85),
                          0 0 0 1px rgba(143,216,255,.26),
                          0 0 70px -30px rgba(60,140,255,.5); }
.paper-shot:hover { transform: rotateY(-4deg) rotateX(3deg) translateZ(24px); }
@media (max-width: 900px) { .paper-note { grid-template-columns: 1fr; } }


/* =============================================================================
   42. RENDERING BUDGET
   The page carries ~90 images inside permanently-animating 3D layers. Left
   unchecked the compositor holds every one resident, runs out of GPU memory,
   evicts layers and flashes the page black. Two rules stop that: decorative
   slots are served small images (see project_images()), and offscreen sections
   are not rendered at all.
   ========================================================================== */
/* `content-visibility: auto` was tried here and removed: it implies
   `contain: paint`, which breaks sticky positioning and scroll-driven
   animations in descendants. The rendering budget is met by serving the
   decorative slots 560px images instead (see project_images()), which is
   where the real cost was — 326MB of GPU texture down to 71MB. */

/* The mosaic used to flip 16 layers on a permanent timer, which is what the
   rendering budget above is about. Then it turned the whole grid on group
   hover — every tile spinning at once on a 14s loop, while the one under the
   cursor was yanked to 180deg by an `!important`. The two fought, and the
   result read as a glitch rather than a flip.
   Now: the tile you point at turns, and only that one. Devices with no cursor
   are handled in the `@media (hover: none)` block further down. */
.tile { animation: none; }

/* =============================================================================
   43. PHONE + LAPTOP
   The 3D set pieces are built for a pointer and a GPU. On a phone they get
   simplified rather than shrunk: fewer composited layers, no hover-only
   interactions, and nothing that needs a drag handle.
   ========================================================================== */

/* Sideways overflow is contained at the component that causes it, never on
   `html` or on every `.section`.
   `overflow-x: clip` there computes the other axis to `auto`, which turns the
   element into a scroll container — and that kills `position: sticky` and
   every `animation-timeline: scroll()/view()` measuring against it. That is
   what froze the flythrough and made the header disappear. */
.prism-hero { overflow: hidden; }

@media (max-width: 900px) {
    /* PHONE HERO — a moving strip of the work.

       The desktop composition is a fan: the renders spread horizontally away
       from the prism, which needs width a phone does not have. What reads well
       in a narrow column is a band travelling across it, so that is what the
       phone gets - the same renders, running as a continuous marquee.

       The set is emitted twice (see index.php). The track is therefore exactly
       two identical halves, so translating it by -50% lands the second copy
       precisely where the first began and the loop closes invisibly. No
       duplicate downloads: the two halves are the same three files.

       The beam, the spectrum blades and the glass solid are hidden here. Each
       needs horizontal room to read as what it is, and at 390px they only add
       noise. */
    .prism-stage {
        border-radius: 0;
        aspect-ratio: auto;
        /* explicit width: every child is absolutely positioned, so with the
           auto inline margins above the box would shrink-to-fit to zero */
        width: 100%;
        max-width: none;
        margin-inline: 0;
        height: clamp(200px, 58vw, 262px);
        perspective: none;
        margin-top: clamp(26px, 7vw, 46px);
        overflow: hidden;
        /* the strip fades out at both edges instead of being cut off, so it
           reads as continuing past the screen rather than stopping at it */
        -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
                mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
    }
    .prism-beam, .prism-fan, .prism-solid { display: none; }

    .prism-orbit {
        /* the desktop rule is `inset: 0`; that has to be cleared BEFORE top is
           set, or the shorthand wins and the track loses its vertical centring */
        inset: auto auto auto 0;
        position: absolute; top: 50%;
        display: flex; align-items: center; gap: clamp(10px, 3vw, 16px);
        width: max-content;
        animation: render-strip 26s linear infinite;
    }
    .prism-orbit figure {
        position: static;                /* out of the desktop absolute layout */
        flex: 0 0 auto;
        border-radius: 14px;
        animation: none;                 /* the track moves, not the cards */
        box-shadow: 0 18px 36px -16px rgba(0,0,0,.9), 0 0 0 1px rgba(255,255,255,.09);
    }
    .prism-orbit figure:nth-child(n+4) { display: block; }   /* the second half */

    /* The three cards are deliberately three different shapes. A row of
       identical tiles reads as a conveyor belt; a wide frame, an upright one
       and a letterbox read as a contact sheet someone laid out. It also crops
       far less: the renders are close to 2:1, so the desktop 4/3 box was
       throwing away a third of every image. The upright card is the one
       exception - it is cropped on purpose, the way a tall detail shot is.

       The pattern repeats every three, which is what keeps the two halves
       identical in width - that is the whole basis of the seamless -50% loop,
       so any change here must stay on 3n. */
    .prism-orbit figure:nth-child(3n+1) {
        width: clamp(150px, 46vw, 196px);
        aspect-ratio: 3 / 2;
        transform: translateY(-16px) rotate(-1.8deg);
    }
    .prism-orbit figure:nth-child(3n+2) {
        width: clamp(112px, 34vw, 146px);
        aspect-ratio: 3 / 4;
        transform: translateY(10px) rotate(1.4deg);
    }
    .prism-orbit figure:nth-child(3n+3) {
        width: clamp(138px, 42vw, 180px);
        aspect-ratio: 16 / 9;
        transform: translateY(-2px) rotate(.8deg);
        opacity: .94;
    }

    @keyframes render-strip {
        from { transform: translate(0, -50%); }
        to   { transform: translate(-50%, -50%); }
    }
    /* the gap doubles at the seam otherwise, which shows as a stutter */
    .prism-orbit figure:last-child { margin-right: clamp(10px, 3vw, 16px); }

    /* The flythrough keeps its 3D camera on phones — it is the signature
       moment of the site. It is made affordable instead of being removed:
       a shorter travel, a tighter perspective, and six of the fourteen
       frames dropped so the GPU holds far fewer layers. */
    .flythrough { height: 260vh; }
    .fly-stage  { perspective: 620px; }
    .fly-cam figure { width: clamp(150px, 46vw, 260px); }
    .fly-cam figure:nth-child(n+9) { display: none; }
    .fly-copy h2 { font-size: clamp(1.5rem, 7vw, 2.2rem); }

    /* Deliberately nothing here: a narrow *window* still has a cursor, so the
       tiles keep their hover behaviour. Genuine touch devices are handled by
       `@media (hover: none)`, which is the correct test for this. */
}

@media (max-width: 680px) {

    /* Big type needs to breathe less on a narrow screen. */
    .prism-hero h1 { font-size: clamp(2rem, 8.4vw, 2.8rem); }
    .fold { grid-template-columns: 44px 1fr; }
    .fold-shot { display: none; }
    .ring-wrap { height: 300px; }   /* the ring's own panel is 190px tall at its floor */
    .numgrid   { gap: 10px; }
    .flow-band { height: 96px; }

    /* Comfortable tap targets. */
    .nav-link, .footer-links a, .hero-services a, .drawer-link { min-height: 44px; display: flex; align-items: center; }
    .faq-q { min-height: 56px; }
}

/* Coarse pointer of any size: never rely on hover to reveal content. */
@media (hover: none) {
    .pcard-body, .post-card-body { opacity: 1 !important; }
    .fold:hover, .aud:hover, .pcard:hover, .numcard:hover { transform: none !important; }
}


/* =============================================================================
   44. NOTHING STARTS HIDDEN
   These classes used to be driven by scroll-linked animations. With those gone
   they must not carry an opacity:0 or a folded transform, or the content simply
   never appears.
   ========================================================================== */
.flow-progress { display: block; }


/* =============================================================================
   45. SCROLL-DRIVEN MOTION
   Restored. It had appeared dead, but the real faults were elsewhere: the
   header had `position: relative` overriding its `sticky`, and
   `html { overflow-x: clip }` turned the root into a scroll container, which
   detaches every scroll timeline. Both are fixed, so these run again.

   Everything here is inside @supports, and nothing starts invisible outside
   it — a browser without scroll timelines shows the finished state.
   ========================================================================== */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* Sections rise as they arrive. */
    .reveal, .reveal-stack > * {
        animation: reveal-in linear both;
        animation-timeline: view();
        animation-range: entry 6% cover 26%;
    }
    @keyframes reveal-in {
        from { opacity: 0; transform: translateY(34px); }
        to   { opacity: 1; transform: none; }
    }

    /* Service panels unfold from flat. */
    .fold {
        animation: fold-open linear both;
        animation-timeline: view();
        animation-range: entry 4% cover 22%;
    }
    @keyframes fold-open {
        from { opacity: 0; transform: perspective(1400px) rotateX(-62deg) translateY(36px); }
        to   { opacity: 1; transform: perspective(1400px) rotateX(0deg); }
    }

    /* Hero renders drift at three different rates. */
    .float-card { animation-timeline: view(); animation-range: cover 0% cover 100%;
                  animation-timing-function: linear; animation-fill-mode: both; }
    .float-card--1 { animation-name: floaty-1; }
    .float-card--2 { animation-name: floaty-2; }
    .float-card--3 { animation-name: floaty-3; }
    @keyframes floaty-1 { from { transform: translateY(80px)  rotate(-3deg); } to { transform: translateY(-80px) rotate(-1deg); } }
    @keyframes floaty-2 { from { transform: translateY(-45px) rotate(2deg);  } to { transform: translateY(55px)  rotate(0deg);  } }
    @keyframes floaty-3 { from { transform: translateY(105px) rotate(3deg);  } to { transform: translateY(-60px) rotate(1deg);  } }

    /* THE FLYTHROUGH — scroll drives the camera forward through the frames,
       so they grow and sweep past. `cover`, not `contain`: the subject is
       400vh and can never be contained by the viewport, which collapses a
       `contain` range to zero length and freezes the camera. */
    .flythrough { view-timeline-name: --fly; view-timeline-axis: block; }
    .fly-cam {
        animation: fly-in linear both;
        animation-timeline: --fly;
        animation-range: cover 0% cover 100%;
    }
    @keyframes fly-in {
        from { transform: translateZ(-500px); }
        to   { transform: translateZ(7400px); }
    }
    @media (max-width: 900px) {
        @keyframes fly-in {
            from { transform: translateZ(-260px); }
            to   { transform: translateZ(4200px); }
        }
    }
    .fly-copy {
        animation: fly-copy linear both;
        animation-timeline: --fly;
        animation-range: cover 2% cover 24%;
    }
    @keyframes fly-copy {
        from { opacity: 1; transform: none; }
        to   { opacity: 0; transform: translateY(26px); }
    }
  }
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .flow-progress {
        animation: flow-progress linear both;
        animation-timeline: scroll(root block);
    }
    @keyframes flow-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
  }
}


/* =============================================================================
   46. TOUCH: the wipe wipes itself
   The before/after is a range input now, which a touchscreen drags natively,
   so nothing here has to compensate for it.
   ========================================================================== */
@media (hover: none) {
    /* The slider takes touch directly now, so the frame no longer has to sweep
       on its own to prove it is a before/after. */
    .compare-divider::after { border-width: 1.5px; }

    /* No hover on a phone, so every tile turns rather than only the first four. */
    .mosaic .tile { animation: tile-idle 16s var(--ease) infinite;
                    animation-delay: calc(var(--i) * .9s); }
}


/* Small screens get the self-wipe too, not only devices that report no hover —
   a phone in desktop-site mode still can't drag a resize handle. */
@media (max-width: 680px) {
    .compare-hint { font-size: 9px; }
}

/* ------------------------------------------------- 47. project spec table */
.spec {
    margin: 0;
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 clamp(24px, 5vw, 72px);
    border-top: 1px solid var(--line);
}
.spec-row {
    display: grid; grid-template-columns: minmax(88px, 26%) 1fr; gap: 18px;
    padding-block: 15px; border-bottom: 1px solid var(--line);
    align-items: baseline;
}
.spec dt {
    font-family: var(--font-mono); font-size: 10px; letter-spacing: .2em;
    text-transform: uppercase; color: var(--text-faint);
}
.spec dd { margin: 0; font-size: .98rem; color: var(--text); }
@media (max-width: 780px) { .spec { grid-template-columns: 1fr; } }
