/* ============================================================
   AŪRA STUDIOS — stylesheet
   Tokens extracted from Figma frames 02.07.2026 — see DESIGN-SYSTEM.md.
   Components reference tokens only. If a value looks wrong, fix the
   token (and the spec), never the component.
   ============================================================ */

/* ---------- Fonts (activate when files land in assets/fonts/) ---------- */
/* Trial files for build only — web licences required before launch (DESIGN-SYSTEM.md) */
@font-face {   /* 04.07 (Chiara): trialling PP Eiko Thin as the serif — photography page only */
  font-family: 'PP Eiko';
  src: url('/assets/fonts/PPEiko-Thin.woff2') format('woff2'), url('/assets/fonts/PPEiko-Thin.otf') format('opentype');
  font-weight: 100 300; font-style: normal; font-display: swap;
}
/* 14.07 (Chiara: "then we can do bold"): heavier Montreal cuts — drop the
   Pangram trials in assets/fonts/ with these names and they go live; until
   then weight 500 renders Regular and weight 700 would FAUX-BOLD (banned
   04.07) — so don't set 700 anywhere before the file lands. */
/* 14.07 (Chiara, MOF deck: "can we try staff grotesk"): R-Typography trial
   cuts — foundry ships them with the "Test" suffix, so the src URLs match
   that. Swap to the licensed filenames before launch. */
@font-face {
  font-family: 'Staff Grotesk';
  src: url('/assets/fonts/StaffGrotesk-Light.woff2') format('woff2')   /* 27.10: LICENSED web font (R-Typography, Aura Studios Creative Ltd) */;
  font-weight: 100 900;   /* 27.10 (Chiara: "can we not just get one weight?"): the site only ever SHOWS Light — one file serves every weight, so one licence */ font-style: normal; font-display: swap;
}
/* 14.07 (Chiara, MOF caption sample "SONEVA | RECLAIM…"): the deck caption
   register is a MONOSPACE, not a grotesk — typewriter voice. Trial arc:
   Roboto Mono → PP Neue Montreal Mono Book (swap the --font-mono token to
   change it back); IBMPlexMono + SpaceMono also on disk. */
@font-face {
  font-family: 'Roboto Mono';
  src: url('/assets/fonts/RobotoMono-VariableFont_wght.ttf') format('truetype');
  font-weight: 100 700; font-style: normal; font-display: swap;
}
/* 15.07 (Chiara: "could we try this font… and then this one" → "try the
   lighter font"): GT America Mono trial — the THIN cut serves the whole
   100–400 range (the register's rules all ask for 400), Regular parks at
   500 if a heavier step is ever wanted. Revert = swap the ranges. */
@font-face {
  font-family: 'GT America Mono';
  src: url('/assets/fonts/GT-America-Mono-Thin.woff2') format('woff2'), url('/assets/fonts/GT-America-Mono-Thin.otf') format('opentype')   /* 27.10: LICENSED (Grilli order 125988) */;
  font-weight: 100 900;   /* 27.10: Thin is the mono the whole site renders (1,670 labels); the 3 Regular column heads on /work join it — one licence */ font-style: normal; font-display: swap;
}
/* 10.07 type audit: retired faces DELETED (GT Era ×3, Montreal Text Book) —
   they were in no font stack; files stay in assets/fonts if ever re-trialled. */

/* ---------- Tokens (DESIGN-SYSTEM.md v1.0) ---------- */
:root {
  /* colour */
  --cream:   #FBF4E4;  /* 10.08 (Chiara: "can we try this as the background colour") — TRIAL: back to the 01.08 Alexis warm cream; supersedes the 04.08 cool stone-white #F5F5ED. NOTE: --cream is the system token, so text-on-dark + CTA fills shift with it — coherent by design; flag to Chiara if the warm/cool split should be two tokens. */
  --cream-2: var(--cream); /* DEPRECATED 02.07 (Chiara): #F3EFE8 too gray — one cream everywhere. Alias kept so nothing breaks. */
  --cream-warm: #FFF5E2; /* 02.07: warmer, yellower cream — the wordmark over film (reads golden against footage where --cream reads white) */
  --paper:   var(--cream);  /* 01.09 (W5 audit, ACTIONED): aliased to --cream, which it already equalled literally (#FBF4E4). One ground value, one source of truth — change --cream and the whole ground follows. Alias kept rather than sweeping ~200 usages, the --cream-2 precedent. */
  --beige:   var(--cream);  /* 01.09 (W5 audit, ACTIONED): aliased to --cream, same value, same reason as --paper above */
  /* --blush #F2E7DD RETIRED 08.08 — the mirror's own warm ground (14.07 v2) folds into the site-wide unification; .origin now sits on var(--beige) like everything else. Restore from git if the two-ground scheme returns. */
  --sage:    #E5E3D3;  /* lower section base */
  --green:   var(--brown);  /* 01.09 (W5 audit, ACTIONED): aliased to --brown, which it already equalled literally (#391913) — the dark grounds were unified on the one brown back on 01.08 and the second token never went away */   /* 01.08 (Chiara, Alexis-proposal palette): dark grounds unify on the one brown = --brown (was #241911, 04.07; deep green #3B4537 before that) */
  --ink:     #484536;   /* 30.09 TRIAL (Chiara: "#484536 can we try the font colour in this"): warm olive-charcoal ink, site-wide via the token. Was #16100D (19.08) — restore that value to revert. */ /* 19.08 TRIAL (Chiara: "#16100D — this is the colour we should use for font, can you try this"): deep warm near-black ink, site-wide. Was #434433 (13.08 olive/moss trial). */ /* 13.08 TRIAL (Chiara: "lets try this") — dark olive/moss: the 08.08 note's prediction realised ("if the sage temperature returns … a deep sibling as text ink"). Measures ~9:1 on the cream — AAA. Site-wide via the token: all type + the on-light logo. Was #3E2C22 (01.08 Alexis warm chocolate). NOTE: dark grounds (--brown/--green #391913) stay WARM — judge whether the two temperatures hold together or the grounds need to follow. */
  --brown:   var(--ground-3);  /* 25.10 (Chiara: "remove any use of this burnt amber colour… replace it with the dark brown we use for the backgrounds"): was #391913 — the red-brown under the header logo, the menu ground, every --green/--brown ground; all now the site dark #140E0C */
  --statement-ink: #211C19;   /* 12.10 (Chiara, gave the hex: "can the text font colour be #211C19"): the origin statement's ink on the light grounds. Its own token because --espresso is also the open-menu ground and she asked for the TEXT, not the menu. */
  --espresso: #150F0D;  /* 05.10 TRIAL (Chiara, gave the hex): near-black warm brown — the open menu ground */
  --stone:   #C8C1B4;  /* 05.10 TRIAL (Chiara, gave the hex): warm grey-stone — the type on the open menu */
  --terracotta: #C9764F;
  --sage-2: #AEB89C;   /* 18.08 v4 (Chiara: "maybe we go a sage green"): a mid botanical sage — the site's --sage (#E5E3D3) is too pale to read as a colour shift. TRIAL token. */
  /* THE GROUND DESCENT (18.08 v6, Chiara: sage "i like this… but then i think
     it needs to go into a deep chocolate… and the font cream"): three grounds,
     one per beat, driven by two scroll vars set in main.js updateOriginXfade —
       beat 1 claim      : beige (the film-ghost 88% alpha)
       beat 2 answer     : --ground-2, pale sage, ink text        (--xf  0→1 across swap 1)
       beat 3 credential : --ground-3, deep chocolate, cream text  (--xf2 0→1 across swap 2)
     The homepage deck's stage ground is --ground-3, so its window opens out of
     the chocolate. Header flips cream once --xf2 passes halfway (main.js). */
  --ground-2: var(--taupe-sub);   /* 19.08 v7 (Chiara: "more taupe maybe"): the brand's own warm taupe (#867567, Alexis palette) as the middle beat; type on it = --ground-2-text. v6 was the umber-charcoal color-mix(in srgb, var(--gallery) 82%, var(--ink) 18%) (Chiara: caramel "too warm"): the UMBER-CHARCOAL she already approved ("ooo i quite like the charcoal", ~#25211C) as the middle beat — beige → charcoal → #110B09, no mid-brown anywhere; cream type from here on. v5 was the hero's caramel glow #C8A778 (Chiara: the umber was "a bit pooey"): the hero film's GLOW — its sunlit caramel-gold (light warm mids average #C8A778; clusters #C0A070 / #D0A070). Ink type holds on it; the descent reads light → luminous → dark, the film's own arc. The other non-mud option from the frame is the deep espresso of its shadows, #48391F. v4 was the mid umber #7E6A47 (Chiara: "should it be warmer — take something from the tones of the hero video"): sampled from the showreel poster's mid-tones (warm mid range averages #836E46; densest clusters #807040 / #706040) — a golden olive-umber, a touch desaturated so cream type holds. Mid-lightness between the beige and #110B09, with the film's warmth instead of grey. v3 was the literal mean of #FAF4E3/#110B09 (#857F76 — "I don't think that colour is the middle"). v2 was the warm pull toward the brand brown: color-mix(in oklch, color-mix(in oklch, var(--beige) 50%, var(--ground-3) 50%) 55%, var(--brown) 45%) (Chiara: "is it?"): the true midpoint (color-mix(in oklch, beige 50%, ground-3 50%) — reads grey-taupe because #0E0806 carries almost no chroma) pulled 45% toward the brand brown — the same mid-lightness, WARM: the brown a designer means by "the middle of those two". v1 (Chiara + her designer: "we don't like the middle green… a gradient between the two, and the middle is the middle colour"): the PERCEPTUAL midpoint of beige → #0E0806 (OKLCH, so it stays a warm deep taupe rather than sRGB's flat grey). Text goes cream from this beat on. v5 was the pale sage color-mix(in srgb, var(--beige) 50%, var(--sage-2) 50%). */
  --fi-margin: 100px;   /* 12.10 (Chiara: "on the margins do 100"): the founder panel's outer margin, mirrored left and right */
  --method-dark: var(--ground-3);   /* 24.10 (Chiara, the Casole hero: "the scrim is a funny colour — make sure it's the dark background colour we have used on the rest of the site"): aliased to the site dark, was #191211. */ /* 12.10 (Chiara, gave the hex: "#191211 — needs to be this"): the Method page deck's dark ground and the hero film's melt into it. Method page only; the site dark stays --ground-3. */
  --ground-3: #140E0C;   /* 12.10 (Chiara, gave the hex: "#140E0C — making sure we are using this as the dark background colour"): the dark end, site-wide via the token (ledger dark, homepage dark beat). Was #110B09 (19.08 v12). */  /* was: #110B09;   /* 19.08 v12 (Chiara: "the in-between of #FAF4E3 and #110B09"): her dark end. v11 was #0E0806 ("that was fire"): her warm near-black. Alt kept: the umber-charcoal color-mix(in srgb, var(--gallery) 82%, var(--ink) 18%) (~#25211C). (Chiara: "more deep brown, more black… less red"): off the red-brown entirely — the gallery near-black warmed with a hint of the olive ink (~#25211C), a deep umber-charcoal. v7 was color-mix(brown 45%, gallery 55%) (~#2A1915, still red). */
  --ground-2-text: var(--cream);   /* 19.08 v2 (Chiara: "should this go cream?" — yes): one switch, ink on the light ground, cream from the first step down. v1 was #16100D (Chiara: "this is the colour we should use for font i think" — still a candidate for the site ink, her call) */
  --ground-3-text: var(--cream);
  /* v3 note — --ground-2: var(--brown) (Chiara: "deep green… or deep chocolate — what do we think" → chocolate): the brand's one dark ground, warm, in the gallery's family — beige → chocolate → the dark work is one descent. Text goes cream over it (.origin color on --xf), header flips cream (main.js). v2 was the apricot sand: color-mix(in srgb, var(--beige) 72%, var(--terracotta) 28%) — keep for a light-warm read. */   /* 18.08 v2 (Chiara: "warm summery… don't love the [clay]"): APRICOT SAND — beige pulled toward the terracotta accent (not the chocolate brown, which greyed it). The origin's second ground AND the homepage deck's stage ground, so the fade into the work is seamless. */   /* 18.08 (Chiara: "i want a warm summery colour palette"): the warm accent — a sunlit terracotta; first seat = the origin's second ground (mixed light), candidate for hovers/marks. Her refs: @twincityglobal, "luxury but alive". */
  --title-ink: var(--taupe-sub);   /* 10.09 (Chiara, the /work ledger screenshot: "I like the colour of the text — use this on the homepage" → the big serif titles on light grounds): the SERIF TITLE colour on cream/beige — the taupe, not the ink. Body copy + labels stay --ink; type on dark grounds stays cream. One token, so a re-rule is one line. */
  --taupe-sub: #867567;  /* 01.08 (Chiara, Alexis-proposal palette): warm taupe secondary — pairs with the chocolate ink (was #948A7F, 06.07) */
  --gallery: #1E1917;  /* 04.07 (Chiara): warm near-black gallery ground — photography (later /film); imagery carries the colour (reference palette #1C1717–#2E1616) */

  /* type — one ladder, 7 sizes */
  --font-serif: 'PP Eiko', 'Times New Roman', serif;  /* 04.07: PP Eiko Thin trial, site-wide */  /* 02.07: trial font lacks ?/&/! glyphs — Times fallback is far lighter than Georgia for the stand-ins; licensed GT Super fixes it properly */
  --font-body:  'Staff Grotesk', 'Helvetica Neue', Arial, sans-serif;  /* 14.07 TRIAL (Chiara: "try staff grotesk regular on the other font") — the whole sans layer flips to Staff when the R-Typography trial files land in assets/fonts/; renders Montreal until then. Staff Regular is a touch heavier than Montreal Light — in line with her 10.07 "more bold with fonts" instinct. Was: PP Neue Montreal (04.07). */
  --font-label: 'GT America Mono', 'Roboto Mono', monospace;  /* 15.07 TRIAL (Chiara): GT America Mono leads the label layer; Roboto Mono = the 14.07 pick, one token-edit to revert */
  --font-staff: 'Staff Grotesk', 'Helvetica Neue', Arial, sans-serif;  /* 14.07 TRIAL (MOF ref): Staff when the trial files land, Montreal until then — scoped to the CTA + deck ticker only */
  --font-mono:  'GT America Mono', 'Roboto Mono', monospace;  /* 15.07 TRIAL (Chiara): GT America Mono leads the mono register (was Roboto Mono, 14.07) */

  --type-display:   clamp(48px, 7vw, 80px); /* menu words · 16.07: coefficient raised (5.83 → 7vw) — headers hold presence below 1370px (Chiara, final sweep); max unchanged */
  --type-menu-word: clamp(min(38px, 11.5vw - 4px), 7vw, 80px);   /* 23.10 (Chiara: "reduce the font size slightly for Work / Method / Studio / Contact"): the phone floor 41 → 38 */   /* 20.10 (Chiara: "review the burger menu on mobile"): the menu words' own rung — the display floor (48) clipped "The Aura Method™" off the sheet at 390 (needs ≤41 inside the 16px gutters, measured). The floor is 41 = 10.5vw at 390 — the deck's own two-line-name coefficient (19.10) — and rides 11.5vw − 4px below that (the measured fit: the line runs 356px at 41px, so it needs ≤ (vw − 32px)/8.68) — a 375 and a 360 hold the line with 3px to spare. Coefficient and cap unchanged, so ≥586px nothing moves; a variant, not a display change — the founder statement and phase titles keep their 48 floor. */
  --type-phase:     clamp(40px, 5.6vw, 64px);   /* 16.07: 4.69 → 5.6vw, same sweep */
  --type-cta:       clamp(36px, 5vw, 57px);   /* 16.07: 4.17 → 5vw, same sweep */
  --type-statement: clamp(32px, 4.4vw, 50px);   /* 16.07: 3.65 → 4.4vw, same sweep */
  /* 14.07 (Chiara: "golden ratio… copy sizing is off" on the work deck):
     the deck tag rides a φ rung off the 123px title (title/φ³ ≈ 29px).
     A φ⁴ corner rung (18px) was trialled and REJECTED same day ("too
     big"); label 12px then too small — landed on 15px (her value). */
  --type-hw-tag:    clamp(22px, 2.5vw, 29px);   /* 16.07: 1.68 → 2.5vw, same sweep */
  --type-caption:   calc(var(--type-label) - 2px);   /* 18.08 (Chiara: "2px smaller" than the deck byline): the caption line under the What We Do hover previews — chained to the label token like hw-corner */
  --type-hw-corner: calc(var(--type-label) - 1px);   /* 08.08 (Chiara: "reduce this copy by 2px" — the Brands We've Built + Next: corners): 15 → 13, still chained to the label token. Arc: 12→18→15→16→14→16→15→13. */
  /* --type-mirror clamp(34px, 4.7vw, 54px) RETIRED 08.08 (origin-lab ruling) — its only seat (.origin-lead) moved to the monument rung below; restore from git if the two-sentence mirror lead ever returns */
  --wm-w: clamp(216px, 20.9vw, 286px);   /* 26.10 (Chiara: "should our logo be a bit bigger in the main nav?" → +10% across the whole ramp): 196/19vw/260 → 216/20.9vw/286 · 19.10 (Chiara, phone header: "aura logo a bit bigger"): floor 170 → 190 · 20.10 ("3% bigger on mobile"): 190 → 196; the 19vw ramp takes over from ~1000px, desktop max unchanged · 27.08: the NAV MARK's width (the header wordmark clamp, 13.08); its height is --wm-w × 0.0822 (the SVG's 308.78/3756.99) — the left cluster uses it to centre on the mark exactly */
  --wm-h: calc(var(--wm-w) * 0.0822);
  --type-header-cta: max(calc(var(--wm-h) * 1.12), 21px);   /* 20.10 (Chiara: "Let's Talk can be smaller by 2px" → then, with the mark at its true 196: "bump Let's Talk again"): 21 → 19 → 21 */   /* 19.10 later (Chiara: "Let's Talk looks v small — bump the text by 5px"): a 21px floor — 15.7 → 21 beside the 170px mark; the floor lets go at ~1200px where the ratio passes it, so desktop stays 24. */   /* 19.10 (Chiara, /work on a phone: "the hero is all wrong… it's cut off odd"): the header CTA was a FIXED 24px serif inside a line box of --wm-h — 21.4px on desktop (cap height fits), but 14px at 390 where the mark is 170w — and the roll's overflow:hidden cropped the tops of the L and T. The size now tracks the mark: 24px at ≥1368 (the 14.07 ratio, "same height as the logo… a bit smaller"), ~15.7px beside the 170px mark. Site-wide — every header. */
  --type-monument:  clamp(44px, 6vw, 88px);
  --type-ledger:    clamp(32px, 4.2vw, 72px);   /* 10.09 (Chiara: "make the fonts smaller on the work page — Luxury Hospitality Consulting is such a long word, needs to be on one line"): the /work ledger name rung — sized so the longest name holds ONE line in the centre column (3/5 of the measure) from 1100px up. Was --type-monument (88px cap). */   /* 08.08 (Chiara, origin-lab direction A "The Monument"): the claim's own display rung. Arc: 128 → 96 ("too big") → 86 (−10%) → 77 ("too big still") → 88 (13.08 v11, Chiara: "make it bigger?" — the centred two-line Vero composition carries more than the left-anchored Monument did). NOT consolidated onto --type-display: its 7vw coefficient renders BIGGER below 1370px — wrong direction. */
  /* --type-answer clamp(19px, 1.5vw, 24px) RETIRED 08.08 same day — born in the origin-lab A sans split, orphaned when Chiara moved the answer to Eiko and then "bigger": the line now rides the existing --type-serif-md rung. No twin tokens on the ladder. */
  --type-serif-lg:  clamp(30px, 3.8vw, 44px);   /* 16.07: 2.3 → 3.8vw, same sweep */   /* case-study rail title + spine (measured 44 @1928, Alex case frame 04.07) */
  --type-body-sm:   14px;   /* support copy (case rail) — Chiara 04.07: body 16 read too big beside the serif title */
  --type-serif-md:  clamp(24px, 3.1vw, 36px);
  --type-serif-md-up: clamp(26px, 3.1vw, 36px);   /* 23.10 v5 (Chiara: "drop the font size of that by 2px on the hero, on mobile only"): the hero line's phone rung 28 → 26; 3.1vw passes 26 at 839px, so tablet and desktop are untouched. v4 was 28 (24 "too small"), v3 24 shared with the origin statements, which now sit on serif-md. */
  --type-origin: clamp(34px, 4.2vw, 48px);   /* 01.09 v4 (Chiara: "I think reduce the font size too"): 55 → 48, one step down now the claim carries the whole statement on three lines. The origin pair's own rung; wider leading (1.22) stays. Arc: 42 → 46 → 64 → 55 → 48. */   /* 28.08 (Chiara: "bump this 5px", then "5 more" — the hero tagline: 46px at desktop): a variant rung, chained to serif-md so it scales with it */   /* 16.07: 1.88 → 3.1vw, same sweep */
  --type-origin-sm: clamp(32px, 3.94vw, 45px);   /* 26.10 (Chiara, "We came from the world we design for…": "reduce this font by 2–3 pixels"): the In Short statement's own rung — --type-origin scaled by 45/48 (−6%) so the curve keeps its shape; 48 → 45 on desktop. The origin trio keeps --type-origin. Phones/tablets unchanged (the ≤900 rule sets --type-serif-lg). */
  --type-serif-sm:  clamp(19px, 1.9vw, 22px);   /* 16.07: same sweep */  /* 02.07: small feature serif — byline, work change-lines */
  --type-body:      16px;  /* 05.07 (Chiara): set to a flat 16px (was clamp 17→19px) */
  --type-label:     14px;   /* 14.07 (Chiara: "( What We Do ) and BRANDS WE'VE BUILT should be the same size"): label layer lifted 12 → 14, the mono-register size */
  --type-label-lg:  18px;   /* 01.09 (W5 audit, ACTIONED): was 14px, i.e. IDENTICAL to --type-label — so .cta--large on the Enquire close rendered at exactly header-CTA size and the modifier did nothing. Given a real rung: the primary conversion now reads one step above every other CTA on the site. The 02.07 note said 'retire on next audit'; it earned a value instead. */
  --track-label:    normal;  /* 14.07: tracking retired with the grotesk — the mono carries its own rhythm (was 0.05em on Staff/Montreal) */
  --lh-body:        1.56;                       /* 25px @ 16px */

  /* MOTION — one signature, one ladder (01.09, W4 audit ACTIONED).
     The audit measured 149 transition/animation declarations: the house curve
     carried 42 of them and bare `ease` (the browser default, the one curve that
     reads as nobody chose it) carried 83. Alongside that, 28 distinct durations,
     seven of them clustered inside a single 300ms window where no viewer can tell
     them apart — the fossil record of iterative authoring.
     The rungs below were chosen to sit INSIDE that cluster, so the system gets one
     rhythm without the feel lurching: every swept value moves by at most ~100ms.
     --dur-quick stays at 0.3s deliberately — it was the site's dominant hover value
     (33 uses) and Chiara's taste is crisp response, not a slow one. */
  --ease-house:   cubic-bezier(0.22, 1, 0.36, 1);   /* THE signature. Expo-out: fast depart, long settle. Every brand gesture rides this. */
  --ease-ambient: cubic-bezier(0.37, 0, 0.63, 1);
  --ease-expo-inout: cubic-bezier(0.87, 0, 0.13, 1);   /* 10.09: Lolo's --expoInOut — the /work hover preview + cursor morph only (Chiara: "copy exactly how they have done it") */   /* true sinusoidal in-out — for the infinite ambient drifts (bob, float, marquee, pv-live) only, so their easing is a declared decision rather than six independent defaults */
  --dur-quick:    0.3s;    /* state + hover */
  --dur-base:     0.5s;    /* micro transitions, larger hovers */
  --dur-slow:     0.9s;    /* scroll reveals, ground shifts */
  --dur-cinema:   1.6s;
  --dur-ground:   1.8s;    /* 12.10 (Chiara: "too much for the eye, the background colours are changing — slow it all down so it feels smoother"): the ledger ground shifts, paper → taupe → dark; was --dur-slow 0.9s (Crown's 1s). The way out is slower still (2.6s, set on the paper state below). */    /* pins, masks, the big unmasks */

  /* layout */
  --header-h: 66px;
  --ledger-n: 4;   /* the number of stages — the stepping maths above needs it */
  --ledger-hold: 45svh;   /* 25.08 ("more smooth"): 30 → 45 — the return to cream has more scroll to drift across */   /* 25.08: the beat the closed ledger + closing card sit together before the stack releases */
  --ledger-rung: 88px;   /* 25.08: a fluid clamp(64px, 8vh, 88px) was tried in the responsive audit and reverted within the hour — in a short window it hit 64 and the 44px titles read "TOO TIGHT" (Chiara). Fixed 88; short viewports get less open room instead, which is the better trade. Earlier: 104 tried ("more breathing") and stepped back the same hour — at 104 the closed ledger pushed the work below the fold (Chiara: "too big — better when Websites stacked under it in the viewport"). Arc: 56 → 72 → 88 → 104 → 88 · earlier: 56 → 72 — the title sits on this line, so it is the air above it AND the closed-ledger spacing */  --ledger-last: 64svh;  --ledger-when: 8rem;   /* the weeks column — fixed so the rung and body grids align */   /* 25.08 Process ledger: the closed-rung height and the last card's hold — at :root because the CLOSING card is the sibling section, and custom props only inherit downward */   /* 25.08: the fixed header's real bottom edge at desktop (gutter 32 + the CTA pill, measured) — anything sticky seats BELOW this so the menu bar never overlays it (Chiara, Process ledger) */
  --inset-page: 4vw;   /* 09.09: the page's outer inline margin — the origin floats sit on it, the What We Do / testimonials sticky uses it (01.09 v12), and Meet the Founder now matches (Chiara: "match the padding of Meet the Founder with the padding of testimonials"). One token, so the page has one left/right edge. */
  --plate-w: min(33vw, 481px);   /* 10.09 (Chiara: "the hover images on the project page the same size as the hover images on the homepage"): ONE hover-plate width — the What We Do plate and the /work list plate, both 16:9 */
  --gutter: 32px;
  /* 21.10 (Chiara, crowncreative.com/studio → What We Do: "match the margins, the font sizing, the size of the images, and the way they animate"): CROWN'S OWN VALUES, lifted from their app.css (.work-component-text) and held as tokens so the section references them. */
  --measure-inset: max(2em, calc((100vw - 1440px) / 2 + 2em));   /* 21.10 (Chiara: "the same padding as What We Do… and maybe the process — bring everything closer"): THE MEASURE — Crown’s 1440 content width with 2em sides, as side padding so grounds stay full-bleed. Homepage: What We Do, the founder, the voices. Service pages: the process ledger, the closing cards, the FAQ. Heroes and footers stay full-bleed. */
  --type-disc: min(calc(3.4vh + 3.4vw), 66px);   /* their .works h3 */
  --type-cta-phone: var(--type-caption);   /* 25.10 v2 (Chiara: "every call to action should be 11 or 12… maybe 12"): 13 → caption (12), the one small mono size on the phone — labels, bylines, sign-off and CTAs all match. v1 (Chiara: "is View Our Work too big… reduce by 1 or 2 pixels… make sure the other CTAs are the same size"): every call to action on phones — View Our Work, Start a project, More about the studio, See where it takes you, the footer CTA. Was label 14 (spec); the spec row is amended to 13 */
  --type-disc-phone: 36px;   /* 25.10: the What We Do rows on phones — the spec’s display rung (_spec/responsive.html, table 5, 03) */
  --lh-disc: min(calc(4vh + 4vw), 66px);
  --team-seat-w: 406px;   /* 25.10 v3 (Chiara: "split the difference on the team image sizes"): 380 ↔ 432 → 406. v2: 350 → 380 */   /* 22.10: /studio team portrait seat width (3:4 → 350×467) — 300 "too small", the 4-up ~400 "too big" */
  --disc-plate-w: 40vw;   /* their .image-cont */
  --disc-plate-ratio: 691 / 469;   /* 26.10 (Chiara: "keep their relative size so nothing's cut off on the videos… at full screen on my 16-inch MacBook keep that dimension, just reduce the size of the box… don't crop it to a square"): the plate's SHAPE is locked to its 16" MacBook Pro full-screen size (1728×1117: 40vw × 42vh = 691 × 469) and only its size scales with the window width. Before, width (vw) and height (vh) moved independently, so the frame changed shape with the window and the cover-fit videos re-cropped. */
  --caption-gap: 18px;

  /* space scale */
  --space-xxs: 4px;  --space-xs: 8px;   --space-sm: 16px; --space-md: 24px;
  --space-lg: 40px;  --space-xl: 64px;  --space-2xl: 96px;
  --space-3xl: clamp(140px, 15vw, 210px);   /* reserved for special gaps only */
  --space-section: clamp(80px, 8.5vw, 120px); /* 02.07: section top/bottom rhythm (3xl was too wide as a default) */
  --space-cta: 14px;   /* THE RULE (02.07): any CTA sits exactly this far below its paragraph (tightened from 24) */

  /* THE PHONE SYSTEM (25.10, rollout Phase 1 — Chiara: "make sure all the tokens are set so the homepage sets the
     precedent for the rest of the pages"). Every decision the homepage settled on the phone, named once. Phase 2 points
     each page at these; nothing below is a new value — each one is lifted from the homepage rule that set it.
     Spec: _spec/responsive.html, section 8. */
  --type-intro: var(--type-serif-md-up);   /* the statement rung — hero line, both origin statements, footer ask: 26 on phones, 36 on desktop */
  --lh-intro: 1.22;                        /* its leading, every width — also the origin trio, In Short and the client quote */
  --type-deck-phone: 50px;                 /* the deck title on phones (LHC review, 25.10: 53 → 50) */
  --track-cta: 0.04em;                     /* CTA tracking — View Our Work set it, every phone CTA follows (size: --type-cta-phone, 12) */
  --measure-inset-phone: calc(1.5 * var(--gutter));   /* 24px each side on phones — where serif copy starts and stops (spec amended 23.10 from 40) */
  --measure-phone: calc(100% - 2 * var(--measure-inset-phone));   /* the same measure as a width */
  --inset-phone: calc(2 * var(--gutter));   /* 32 — the phone edge of the What We Do rows and the team; sections without a serif statement start here (25.10: How We Work joins it) */
  --space-section-phone: var(--space-2xl); /* 96 — section top and bottom on phones */
  --space-stage-phone: var(--space-lg);    /* 40 — inner clearance of a stage on phones */
  --frost: color-mix(in srgb, var(--cream) 58%, transparent);   /* THE FROSTED RULE — a title over imagery, cream at 58%; the base title sits UNDER the photo or the frost never reads */
  --frost-ink: color-mix(in srgb, var(--ink) 58%, transparent); /* its inverse, ink over light imagery */
  --read-rest: 0.4;                        /* the scroll read-on: unread words rest here and deepen to 1 (main.js reads this token) */
}

/* Mobile: tighter edge margin than the fixed desktop gutter */
@media (max-width: 700px) { :root { --gutter: 16px; } }
@media (max-width: 700px) { .site-header .cta--header { display: none !important; } }   /* 25.10 (Chiara: "Let's Talk in the nav keeps randomly appearing… hidden across everything, and not in the burger menu"): !important — the 0,3,1 guard below lost to `body:not(.page-interior) .site-header.on-light .cta--header` (0,4,1, display: inline-flex), so the link came back whenever the header went light on scroll. Previously: body .site-header.site-header .cta--header { display: none; } — 0,3,1, "the on-dark/on-light header rules (0,3,0) set inline-flex later in the sheet and were winning" */
@media (max-width: 700px) {
  .hero-intro .hero-intro-line { font-size: var(--type-intro); max-width: 22ch; }   /* 22.10 (Chiara, phone review: "maybe the tagline smaller font"): serif-lg → serif-md at phone widths (30 → 24px) */
  /* 23.10 (Chiara, phone review: "I don’t like the placement of the Strategy and creative line"): the cluster leaves the centre
     of the frame (the sun) for the LOWER-LEFT, the interior ride heroes’ seat — line + View Our Work left-aligned at the
     gutter, one lg above the wordmark on the frame’s foot. Desktop keeps the centred cluster. */
  .opening .hero-intro { place-content: center; justify-items: center; text-align: center; gap: var(--space-md); padding: 0 var(--gutter); }   /* 0,2,0 — outranks the base .hero-intro further down · 23.10 v4 (Chiara: "centre to the screen"): dead centre of the viewport, no wordmark offset */
  body.hero-pin .opening { height: 200svh; }   /* 23.10 (Chiara: "the video shouldn’t shrink as you scroll, it needs to swipe up"): THE PHONE SWIPE — hero-pin grammar at ≤700 (class set by main.js). svh, not vh: Safari's tall viewport would leave a dead band below the ride */
  body.hero-pin .origin { margin-top: -100svh; --origin-alpha: 100%; }   /* 23.10 (Chiara: "the background is too light opacity"): the cover is SOLID beige on the phone — the film does not ghost through the statement */
  body.hero-pin .opening .hero-intro { transform: none; }   /* 23.10 v12 (Chiara: "it should just fade"): the hero line holds still under the swipe and fades — no 28vh climb on phones */
  .origin-lead br:not(.br-phone) { display: none; }   /* 23.10 v2: the desktop breaks hide on phones… */
  .origin-lead br.br-phone { display: inline; }   /* …and the PHONE breaks show (Chiara, 23 Oct: "You've built something the / world should know about. / We make the brands and websites / that make sure it does.") */   /* 23.10 (Chiara: "make this smaller and line breaks less weird"): the desktop's pinned three lines wrapped mid-line on the phone — natural rag here */
  .origin .origin-lead { font-size: var(--type-intro); max-width: var(--measure-phone); }   /* the hero line’s rung (Chiara: "make the font the same size as the title" → 28px) at phone widths; one extra gutter each side (Chiara: 22ch was "margins are way too wide", gutter-to-gutter wanted "more margin" → 32px) · 0,2,0 outranks the base .origin-lead further down */
  .origin .origin-body p:not(.label), .origin .origin-answer { font-size: var(--type-intro); max-width: var(--measure-phone); }   /* 23.10 (Chiara, the credential: "same font size for this too"): the origin trio stays ONE size on the phone, as the 01.09 ruling has it on desktop */
  .origin .origin-body p:not(.label) { max-width: min(15.25em, var(--measure-phone)); }   /* 25.10 (Chiara, phone: "split the difference between the width of the text box here" → "wider" → "they aren’t the same size font"): the credential keeps the statement’s size (serif-md-up, the 23.10 match) and its measure is 15.25em (~396px), halfway between the 342 statement and the old ~455 set in a 540 window. On a 390 phone the 3-gutter cap holds it at 342, the old set — the first line "Strategy‑led. Design‑obsessed." is 331 at this size and cannot go narrower without splitting */
  .origin .origin-lead { max-width: min(15.25em, calc(100% - 2 * var(--gutter))); } .origin .origin-lead br { display: none; } .origin .origin-lead br.br-set { display: inline; }   /* 25.10 (Chiara: the credential at 15.25em "is perfect, the size and placement — match this for the" statement): the statement takes the same measure, 25.10 v2 (Chiara: "You’ve built something the world should know about. We / make the brands and websites that make sure it does."): four hand-set phone lines (widest 328px, so the screen cap is 2 gutters — they hold down to a 360 phone), "We" closing line two like "Design‑obsessed. We" in the credential. On a 390 phone it sets in five lines with "websites that" alone, as it already did under the hand-set breaks */
  .origin .origin-claim, .origin .origin-body { align-self: start; margin-top: calc(50svh - 4.88 * var(--type-intro) / 2); }   /* 25.10 v4 (Chiara: "the paragraph should be centred exactly to the viewport height, and the CTA will just be off-centre — move those paragraphs down slightly"): the seat now centres the four-line TEXT alone (4 × 1.22 leading), not text + gap + CTA — both statements drop ~39px and Start a project hangs below the centre    /* 25.10 v3 (Chiara: "the exact placement"): both layers hang from the SAME top — the seat a four-line block (4 × 1.22 leading + lg gap + the 14px CTA step + the 24px CTA) would take centred — instead of each centring on its own height, so the statement’s first line always lands on the credential’s, even where the credential runs to five lines (390) or six (360) */
  /* 23.10 v4 (Chiara: "maybe the images scroll left to right, like the Padel 22 one"): THE PHONE STRIP — The 22's Insider
     grammar. The five floats stand in ONE row behind the words and the row slides right → left as the pinned statement
     is scrolled (--strip-p, main.js: the origin's own p), first photo to last, so every image passes under the type and the
     cream twin does what it does on the desktop. The climbing seats (--oy0 / --oy) are retired here: static, one height.
     The ≤900 display:none is outranked (0,2,0). The stage spans the full width so the row clips at the SCREEN edge. */
  .origin .origin-stage { width: calc(100% + 2 * var(--gutter)); margin-inline: calc(-1 * var(--gutter)); }
  .origin .origin-floats { --strip-gap: 100vw; display: flex; gap: var(--strip-gap); inset: auto; left: 0; top: 50%; height: 84svh; width: max-content; translate: calc(100vw - var(--strip-p, 0) * 100%) -50%;   /* 23.10 v10 (Chiara: "a delay so the text appears, then the images slide on"): the row starts fully OFF the right edge and ends with its last image on it — travel = the row's own width; main.js drives --strip-p on The 22’s numbers: a 60%-screen hold, then 2.4px sideways per px scrolled, lerped */ will-change: translate; }   /* 23.10 v7 (Chiara: "a lot more space between them and space for the text to read"): the gap is SCREEN-scale — 80vw of empty ground between images, so the words read clear between each pass; four 46svh portraits + three gaps ≈ 2060px, travel ≈ the pin's own length */
  .origin .of { display: block; position: static; width: 48vw; height: auto; transform: none; opacity: 1; translate: 0 0; transition: none; }   /* 23.10 v12: no arrival fade/lift on the strip — the slide is the only motion · v13 (Chiara: "why are the images so small"): WIDTH-based now — the svh height shrank them in short windows · v14 (Chiara: "the mobile is too big now"): 60 → 48vw, 211 × 283 on a 440 phone */   /* 23.10 v9 (Chiara: "a bit smaller"): 46 → 40svh */
  .origin .of--1, .origin .of--4 { align-self: flex-start; }   /* 23.10 v5 (Chiara: "staggered, top and bottom vibes"): the row is 84svh tall and each image sits at its top or its foot, alternating in VISUAL order · v12 (Chiara: "swap the 3rd and 4th image"): of--4 takes the third seat (order), so the seats are named, not nth-child */
  .origin .of--2, .origin .of--3 { align-self: flex-end; }
  .origin .of--3 { order: 4; }
  .origin .of--4 { order: 3; }
  .origin .of--5 { display: none; }   /* 23.10 v6 (Chiara: "4 images only"): the fifth stays off the phone row */
  .origin .origin-claim { translate: 0 0; width: 100%; }   /* 23.10 v2: the claim takes the whole stage on phones (its desktop 86% width was shrinking the first statement's measure to 323 and dropping "websites" onto a line of its own) */   /* 23.10 (Chiara: "the paragraph be dead centre"): the −70px text parallax (--tp) is off on the phone — the claim holds the screen’s centre through its beat; the strip carries the motion */
  .origin .of img, .origin .of video { width: 100%; height: auto; }
}   /* 22.10 (Chiara, phone review: "maybe the tagline smaller font"): serif-lg → serif-md at phone widths (30 → 24px) */   /* 22.10 (Chiara, phone review: "hide the Let’s Talk for the menu nav so it’s just logo and burger"): the header CTA is OFF at phone widths site-wide — the ask lives in the burger menu + every footer */
@media (max-width: 760px) { :root { --type-body: 15px; } }
@media (max-width: 760px) { :root { --type-hw-corner: var(--type-caption); } }   /* 20.10 (Chiara: "( The Thinking ) — reduce all the ( ) headings by 1px"): the LABEL RUNG itself steps to the caption rung on the phone (13 → 12) — the token moves, so every bracketed heading follows whatever selector set it (the class-level override was outranked by the heading-specific rules) */   /* 19.10 (Chiara, Palmer's Design paragraph: "reduce the body copy font across the site on mobile by 1px"): 16 → 15 on phones, via the token — every body paragraph */

/* ---------- Base ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  font-family: var(--font-body);
  /* 10.07 type audit: the 03.07 "Thin 200 IS the body cut" ruling was made
     on GT Era, which had a 200 cut. PP Neue Montreal is owned in Light only
     (face declared 300–500), so every 200 request has silently rendered as
     300 since 04.07. RATIFIED: 300 is the body weight — the CSS now says
     what it renders. If a true Thin is ever wanted, buy the cut and re-open. */
  font-weight: 300;
  /* 04.07 (Chiara: "Thin doesn't look thin"): macOS subpixel rendering
     fattens strokes — grayscale antialiasing matches how the type renders
     in Figma. This is why the build read heavier than the design. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-size: var(--type-body);
  letter-spacing: 0.1px;
  line-height: 1.5;              /* 05.07 (Chiara): 24px at the 16px body (was 1.6) */
  color: var(--ink);
  background: var(--paper);
}
/* 04.07: the grayscale smoothing above is calibrated for the GT Era cuts —
   it also starves GT Super, which only exists here as the Light trial (no
   heavier file to reach for). The serif keeps macOS subpixel rendering
   until a heavier licensed cut lands. One list = every var(--font-serif)
   rule; keep in sync. */
.menu-primary a, .origin-lead, .origin-answer,   /* .quiet-link left 14.07 — mono register now */   /* .origin-body p left 08.08 — sans register now (origin-lab A); .origin-answer rejoined 08.08 v2 (back to Eiko) */
.origin-cost p:first-child, .declaration p:first-child,
.for-col p:last-child, .work-change, .work-item--quote blockquote,
.method-title, .method-deliv, .insight-line, .faq-list summary,
.footer-ask-statement, .interstitial-line {   /* 01.09: .enquire-statement renamed with the footer merge */
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
}

img, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }
body.menu-open { overflow: hidden; }

/* Label style — every caps label/subheading on the site.
   14.07 (Chiara: "all the sub headings like that moving forward"): the whole
   label layer joins the MONO REGISTER — Roboto Mono / 400 / 14px caps /
   untracked — one typewriter voice from the deck captions to every
   ( Section Label ). Supersedes the Staff/grotesk label trials and the
   03–04.07 weight rulings (Thin/Regular contrast retired with the grotesk). */
.label, .section-label {
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: var(--type-hw-corner);
  letter-spacing: normal;
  text-transform: uppercase;
  line-height: normal;
}

/* Logo as CSS mask — one SVG, any token colour via background-color.
   Longhand mask properties: the shorthand with /size fails silently in
   some Safari versions. */
.logo-mask {
  display: block;
  /* 03.07 (Chiara): tracking tightened — gaps closed to 55% of the
     original, the designer's kerning relationships kept (chosen over the
     optically-equalized cut, 03.07 — compare at /logo-lab.html; original
     + optical cuts kept in assets/logos/aura/ for revert) */
  /* 15.07 (Chiara: "replace the aura logo with this everywhere"): the TM
     horizontal lockup — one mask, so every instance (header, hero, footer,
     interior pages) swaps together. Was Aura_logo_tight.svg (kept on disk). */
  aspect-ratio: 3756.99 / 308.78;    /* Aura_logo_tm_horizontal.svg viewBox */
  -webkit-mask-image: url('/assets/shared/logos/aura-logo-horizontal.svg');
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
  -webkit-mask-size: contain;
  mask-image: url('/assets/shared/logos/aura-logo-horizontal.svg');
  mask-repeat: no-repeat;
  mask-position: center;
  mask-size: contain;
}

/* Type-only CTA (D1) — no pill exists in this design */
.cta {
  font-family: var(--font-mono);   /* 14.07 v2 (Chiara: "Start a project → should this be roboto mono"): CTAs join the mono register. Was --font-staff (Staff Light trial, same day). */
  font-weight: 400;   /* mono register weight — matches the caption register */
  font-size: var(--type-label);   /* 04.07 (Chiara): matched to the section labels (( Selected Work ) etc.) — the earlier step-up reverted */
  letter-spacing: normal;   /* mono carries its own rhythm — house track retired with the grotesk */
  text-transform: uppercase;
}

/* Scroll reveal (OH Architecture benchmark, 02.07) — content rises smoothly
   into place as it enters the viewport. JS tags elements with .rv and adds
   .in-view once; --rv-d staggers siblings. */
.rv {
  opacity: 0;
  transform: translateY(44px);
  transition: transform var(--dur-slow) var(--ease-house) var(--rv-d, 0ms),
              opacity var(--dur-slow) var(--ease-house) var(--rv-d, 0ms);
}
.rv.in-view { opacity: 1; transform: translateY(0); }

/* IMAGE REVEAL — 05.08 (Chiara: the fleava.com/works/ayana-residences laptop
   mockup). MEASURED, NOT GUESSED: the values below are lifted from fleava's
   own CSS, which is inlined in that page rather than sitting in the bundle.
   Their armed state is opacity 0 with scale 1.2 and a 1.5deg rotation, easing
   over 2s to scale 1 and 0deg, with opacity running on a much shorter 0.5s.
   The rotation settling out is the signature; the zoom on its own reads
   generic. Opacity lands about four times faster than the transform, so the
   long ease reads as a settle rather than a fade.
   FAIL-OPEN: the armed state is applied by JS only. Nothing here hides the
   picture, so if the script never runs the image is simply visible.
   Two earlier attempts shipped blank because the hidden state lived in this
   stylesheet, and one of those was really an unterminated comment swallowing
   the rules below it. Keep every comment closed, and never type brace or
   close-marker characters inside comment prose. */
.cs-parallax { overflow: hidden; }
.cs-parallax img { will-change: transform, opacity; }
.cs-parallax.reveal-armed img {
  opacity: 0;
  transform: scale(1.2) rotate(1.5deg);
  transition: transform var(--dur-cinema) var(--ease-house), opacity var(--dur-base) var(--ease-house);
}
.cs-parallax.reveal-armed.reveal-on img {
  opacity: 1;
  transform: scale(1) rotate(0deg);
}

/* Letter-roll hover (Superhaus reference, 02.07) — each character shutters
   up, its twin slides in from below, staggered left→right. JS splits any
   .roll element into .ch spans with an --i index. */
/* 05.07 (Chiara): gap restores the caps tracking — letter-spacing is ignored
   between flex items, so the split .ch chars lost the --track-label the .cta
   sets, reading tighter than the plain-text labels. gap tracks flex items. */
.roll { display: inline-flex; overflow: hidden; line-height: 1.4; gap: 0.05em; }   /* 14.07: was gap: var(--track-label) — pinned when the track token retired to `normal` (invalid as a gap) */
/* 06.07 (Chiara: "Start a Project looks too wide"): the CTA's rolled letters
   were double-tracked — the .cta's letter-spacing AND this gap both applied,
   ~1.15px. Modern browsers DO apply letter-spacing between the flex letters
   (the 05.07 gap was over-compensating for a bug that no longer bites), so the
   gap is dropped here and the CTA tracks on --track-label alone (~0.6px). */
.cta .roll, .cta.roll { gap: 0; }   /* 14.07 fix: the 06.07 de-double-tracking only matched a roll INSIDE a cta — the origin/enquire CTAs are one element with BOTH classes (class="cta roll") and stayed double-tracked (gap + letter-spacing) */
.roll .ch {
  position: relative;
  display: inline-block;
  transform: translateY(0);
  transition: transform var(--dur-base) cubic-bezier(0.65, 0, 0.35, 1);
  transition-delay: calc(var(--i) * 18ms);
}
.roll .ch::after {
  content: attr(data-ch);
  position: absolute;
  left: 0; top: 100%;
}
@media (hover: hover) and (pointer: fine) {
  .roll:hover .ch { transform: translateY(-100%); }
}   /* 01.09: gated — on touch the roll latched half-way and never came back */
@media (hover: none) { .roll .ch::after { content: none; } }   /* 25.10 (Chiara, phone: the doubled "Start the conversation"): the hover twin sits one line below each letter, and wherever a roll carries bottom padding (the footer CTA’s 0.3em underline gap) it peeked through as a ghost row. Touch never rolls, so the twin is not drawn there */
@media (prefers-reduced-motion: reduce) {
  .roll .ch { transition: none; }
  .roll:hover .ch { transform: none; }
  .roll:hover { opacity: 0.8; }
}

/* ============================================================
   HEADER — corner-anchored (Superhaus L1)
   ============================================================ */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;   /* above the menu overlay: the burger is also the close X */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: start;   /* 05.07 (Chiara): top-align so each item sits --gutter from the top edge, matching the left/right gutter — centring was dropping the shorter burger ~6px lower */
  padding: var(--gutter) var(--gutter) 0;
  color: var(--cream);            /* over the film at load (Chiara 02.07: #FFFAF1) */
  transition: color var(--dur-base) var(--ease-house), transform var(--dur-base) var(--ease-house);
}
/* 01.08 (Chiara: "the menu goes out of view so it's just clean images"):
   interior-page clean scroll — JS toggles .nav-away by scroll direction */
.site-header.nav-away { transform: translateY(-130%); }
body.menu-open .site-header { transform: none; }   /* the burger is the close X — never hide while the menu is open */
.site-header.on-light { color: var(--ink); }  /* charcoal once the film has parked */
.site-header .over-img, .site-header.on-light .over-img { color: var(--cream); }   /* 29.08 (Chiara): any header item sitting over a photograph goes cream — the origin twins' over-image rule, applied to the chrome (main.js samples per item on scroll) */
/* Clean scroll (02.07): nav fades out scrolling down, returns scrolling up */
/* Design pass 02.07: hiding exempts the CTA — Start a Project is genuinely
   persistent; burger/logo/sound still clear away for the clean scroll */
.header-left { display: flex; align-items: center; gap: var(--space-sm); height: var(--wm-h); }   /* 27.08 (Chiara: "the burger isn't centred to the logo" — measured 0.8px low): the cluster's box = the mark's height, so burger and sound centre on the mark's centre line, not on the 23px sound icon's */  /* 10.07 v2: burger + sound cluster left · 13.08 (Chiara: "icon a little closer to the burger"): md → sm (24 → 16) */
.site-header .menu-toggle, .site-header .sound-toggle, .site-header .cta {
  transition: opacity var(--dur-quick) var(--ease-house), transform var(--dur-base) var(--ease-house), color var(--dur-base) var(--ease-house);
}
/* Ground-aware header (design pass): cream over the green sections */
.site-header.on-dark { color: var(--cream); }
.site-header.on-dark .logo-mask { background-color: color-mix(in srgb, var(--cream) 70%, transparent); }   /* 13.08 (Chiara: "the logo should be a bit transparent… when something underneath it ie hero video"): the header mark joins the frosted-on-imagery rule. v2 (Chiara): 58 → 70% — the small mark takes a denser frost than the giant hero/footer marks (those stay 58%); at 58 the 21px letterforms lost too much body. on-dark IS the over-imagery state; over flat grounds (on-light) the mark stays solid ink. */
body:not(.menu-open) .site-header.on-dark .menu-toggle,
body:not(.menu-open):not(.work-dark) .site-header.on-dark .cta--header { color: color-mix(in srgb, var(--cream) 70%, transparent); transition: color var(--dur-quick) var(--ease-house); }   /* 26.10 (Chiara: "the burger menu and the Let's Talk should be frosted like the logo is"): the burger bars (currentColor) and the CTA text join the mark's frosted-on-imagery fill — the same 70% cream, only over media (.on-dark), so the three read as one frosted set. Not while the menu is open (the burger is the close X there), not on the dark /work index (its CTA already rests at 0.7 opacity, 10.09). */
body:not(.menu-open):not(.work-dark) .site-header.on-dark .cta--header:hover,
body:not(.menu-open) .site-header.on-dark .menu-toggle:hover { color: var(--cream); }   /* the hover lifts to full cream (the global rule: raise the alpha slightly on translucent controls, never a colour swap) */
.site-header.on-light .logo-mask { background-color: var(--ink); }   /* charcoal logo over the taupe origin (06.07) */
body.menu-open .site-header { color: var(--ink); }  /* menu overlay is cream — header goes charcoal */
body.menu-open .site-header.on-dark { color: var(--ink); }  /* 16.07 (Chiara: "the X and Let's Talk aren't visible"): outranks the .page-interior on-dark cream rule (1727) — the overlay is cream regardless of the ground beneath */
.cta--header::before { display: none !important; }  /* 10.07: pill skin retired site-wide */
.site-header .wordmark {
  grid-column: 2;                      /* 13.08 (Chiara): logo to the CENTRE cell — the 1fr auto 1fr grid seats its middle child at last; supersedes 10.07 "logo LEFT, Stud header". The hero hand-off is now axis-true: big centred mark retires, small centred mark arrives on the same vertical. */
  width: var(--wm-w);    /* 27.08: tokenised (--wm-w) so the left cluster can size to the mark · 13.08 (Chiara: "a bit bigger"): one step up from the left-seat 150/16.7vw/228 (~+14%) — the centre cell has no burger/CTA squeeze, so the 08.08 phone collision that forced the 150 floor can't return */
  opacity: 0;                          /* 03.07 (Chiara): hidden at rest — arrives on first scroll (the film + giant mark own the landing frame). 13.08: the masked-rise trial on this mark ("weird reveal effect") is RETIRED — Chiara ruled the arrival is an OPACITY change; the 03.07 fade grammar stands. */
  transform: translateY(0);
  transition: opacity var(--dur-base) var(--ease-house), transform var(--dur-base) var(--ease-house);
  will-change: opacity, transform;     /* own GPU layer — masked elements flicker when re-rasterised mid-scroll (02.07) */
  backface-visibility: hidden;
}
.site-header .logo-mask { background-color: var(--green); transition: background-color var(--dur-base) var(--ease-house); }



.site-header.wordmark-visible .wordmark { opacity: 1; }   /* 03.07: JS adds on first scroll */
/* 13.08: masked-rise arrival (hw-title roll on the header mark) TRIED +
   RETIRED same day — Chiara: "weird reveal effect". What she meant all along
   was the FROSTED fill ("masque effect" = the 58% frosted-on-imagery rule) —
   see the on-dark logo-mask rule above. Homepage arrival stays the original
   03.07 opacity transition on the scroll anchor; interior marks are simply
   present. */
/* hide-on-scroll must OUTRANK visible — it comes later deliberately (cascade fix 02.07).
   03.07 (Chiara, OH ref): the nav content JUMPS UP and out — burger and
   sound exit through the top edge together. The CTA is exempt: Start a
   Project is genuinely persistent. 10.07 (Chiara: "where's our logo gone?"):
   the WORDMARK is now exempt too — once it arrives with the curtain it
   stays for the whole page; only the origin-statement collision (.mark-clear)
   and the menu take it away. Supersedes the 03.07 "on the green only" rule. */
/* 10.07 v2 (Chiara: "the burger wasn't showing — bring that back in"):
   the BURGER is exempt from the clean-scroll exit (nav-hidden), same as the
   CTA — wayfinding stays reachable everywhere. 14.07 refinement (Chiara):
   during the origin-statement COLLISION only (.mark-clear) the burger DOES
   clear with the wordmark — nothing may overlay the mirror line — and
   returns as soon as the text scrolls past. */
/* 14.08 (Chiara: "make sure the sound icon appears on the menu nav"): while
   the OVERLAY IS OPEN the sound toggle stands with the X — the music keeps
   playing, so its control must stay reachable; menu-open outranks the
   clean-scroll exit below. */
body.menu-open .site-header .sound-toggle {
  opacity: 0.75 !important;
  transform: none !important;
  pointer-events: auto !important;
}
.site-header.mark-clear .menu-toggle,
.site-header.mark-clear .sound-toggle,   /* 19.08 (Chiara: "the sound icon disappears as you scroll"): the toggle leaves the nav-hidden (clean-scroll) group and joins the burger's — persistent, clearing only when text runs under the left lane */
.site-header.nav-hidden .header-descriptor,
.site-header.mark-clear .wordmark {
  opacity: 0;
  transform: translateY(-26px);
  pointer-events: none;
  /* crisp exit (03.07): opacity snaps out ahead of the flick — nothing
     is ever visibly crossed; the base rule's softer timing runs the return */
  transition: opacity 0.15s ease-out, transform var(--dur-base) cubic-bezier(0.5, 0, 0.1, 1), color var(--dur-base) var(--ease-house);
}
/* 03.07 (Chiara): inside the Selected Work pin the mark holds the centre
   seat — outranks nav-hidden (later in the cascade, higher specificity) */
.site-header.mark-center .wordmark {
  opacity: 1;
  transform: translateY(0);
}
/* 03.07 (Chiara): over the origin text the CTA clears too — nothing may
   sit above the story. nav-clear = the total exit; it rides WITH
   nav-hidden in the origin guard zone only (the CTA stays persistent
   everywhere else). */
.site-header.nav-clear .cta {
  opacity: 0;
  transform: translateY(-26px);
  pointer-events: none;
  transition: opacity 0.15s ease-out, transform var(--dur-base) cubic-bezier(0.5, 0, 0.1, 1), color var(--dur-base) var(--ease-house);  /* crisp exit, soft return (base rule) */
}
/* Left cell — burger + studio descriptor share the top-left (06.07 Chiara:
   moved from the right cell). align-items:flex-start tops both at the gutter;
   because the descriptor sits in a 13px box (= burger height), their optical
   centres both land on the burger's middle bar line. */
.site-header .header-left {
  justify-self: start;
  display: flex;
  align-items: center;    /* centre burger + (cap-trimmed) descriptor on one line */
  gap: var(--space-sm);   /* the column of air between burger and descriptor · 13.08: md → sm with the sound-icon gap ruling */
}
.site-header .header-right {
  grid-column: 3;   /* 10.07 v2 fix: homepage header has only two children (burger+logo cluster, CTA) since the wordmark moved into .header-left — auto-placement was dropping the CTA into the CENTRE column of the 1fr auto 1fr grid. Pin it to col 3 so Let's Talk sits 32px (--gutter) from the right edge, mirroring the burger. Interior pages (3 children) are unaffected. */
  justify-self: end;
  display: flex;
  align-items: center;
  gap: var(--space-md);   /* 03.07: air between sound + CTA — utility and conversion read as separate units (was 10px) */
}
/* Header CTA — 05.07 (Chiara): pill in the NAV only. Plain text (no roll, so
   the pill padding can't un-clip a duplicate row). Pill over dark grounds /
   full-bleed media; text-only over light backgrounds (cream sections + the
   menu overlay). The origin CTA stays text + letter-roll, no pill. */
.cta--header {
  /* 10.07 v2 (Chiara: "much bigger… Eiko… clear"): the serif voice at real
     scale — the Stud "Let's Talk" register. Pill retired site-wide. */
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-header-cta);   /* 19.10: was a fixed 24px — see the token. 14.07 v2 (Chiara: "same height as the logo… maybe a bit smaller to keep it balanced"): cap height ≈ 17px, sitting just under the wordmark's 18.3px letters (228w × 115/1432 viewBox) — level enough to share the line, small enough not to outweigh the mark. Still FIXED, never fluid ("it shouldn't shrink"). */
  letter-spacing: 0;
  text-transform: none;
  position: relative;
  line-height: max(var(--wm-h), 0.9em);   /* 19.10: the roll clips to this box — 0.9em keeps the caps and ascenders of the floored phone size inside it (desktop: 21.6 vs 21.4, nil) · 27.08 v2: the mark's own height (21.4 @ ≥1368) — burger, sound, mark, CTA all centre on one line · v1 was 23px (the sound icon's box) · (Chiara: "the logo looks higher than the items on the left… off balance" — measured: burger cy 43.5, sound 43.5, logo 42.7, Let's Talk 48.8): the 1.4 line box (33.6px) was seating the serif ~6px low; a 23px box = the sound icon's, so all four centres coincide on the gutter line */
}
.cta--header .roll { line-height: inherit; }   /* the roll's own 1.4 would re-open the box */
/* Pill FOOTPRINT — the CTA holds its pill dimensions over media grounds. On the
   homepage it KEEPS them over the taupe (on-light) too, so the fill can fade out
   IN PLACE with no text reflow as the ground flips. Inner pages (.page-interior)
   stay text-only — no footprint. */
.site-header.on-dark .cta--header,
body:not(.page-interior) .site-header.on-light .cta--header,
body.film-page .cta--header,
body.photo-page .cta--header {
  display: inline-flex; align-items: center;
  /* 10.07 v2 (Chiara): footprint padding retired WITH the pill skin — the
     old 5px 20px inset left the text 52px off the right edge / 5px low;
     now the text sits ON the gutter, mirroring the burger's left offset */
}
/* Pill SKIN — border + soft fill on a pseudo, so it fades on its OWN opacity,
   independent of the (charcoal) text. 06.07 (Chiara): over the taupe the pill
   must dissolve to nothing while Start a Project stays as charcoal text. The old
   skin lived on the element's border/background bound to .on-dark, so it snapped
   off + reflowed on the flip; now it just opacity-fades. */
.cta--header::before {
  content: '';
  position: absolute; inset: 0;
  border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
  border-radius: 200px;
  background: radial-gradient(120% 150% at 50% 50%, color-mix(in srgb, #000 30%, transparent), color-mix(in srgb, #000 9%, transparent) 66%, transparent);
  opacity: 0;                       /* hidden by default → fades in and out */
  transition: opacity var(--dur-base) var(--ease-house), border-color var(--dur-base) var(--ease-house);
  pointer-events: none;
}
/* Pill visible only once the roll is .ready over a media ground. Over the taupe
   (on-light) this never matches → the hero pill fades to nothing. */
.site-header.on-dark .cta--header.ready::before,
body.film-page .cta--header.ready::before,
body.photo-page .cta--header.ready::before { opacity: 1; }
.site-header.on-dark .cta--header.ready:hover::before,
body.film-page .cta--header.ready:hover::before,
body.photo-page .cta--header.ready:hover::before { border-color: color-mix(in srgb, currentColor 50%, transparent); }
/* Menu open (cream overlay) — pill hidden, text only. !important beats the
   .on-dark .ready pill that stays applied over the hero behind the menu. */
body.menu-open .cta--header::before { opacity: 0 !important; }
/* Menu open — the studio descriptor drops away (06.07 Chiara): the open menu
   owns the frame; only the burger→X and the CTA stay. */
body.menu-open .header-descriptor { opacity: 0; pointer-events: none; }
/* Scrolled — descriptor is a landing-only label; it fades the instant the page
   moves (06.07 Chiara), handing the header over to the arriving wordmark. */
.site-header.scrolled .header-descriptor { opacity: 0; pointer-events: none; }
/* invisible hit area — an overlay, NOT padding: padding un-clipped the
   letter-roll's duplicate row (02.07 regression fix) */
.cta--header::after {
  content: '';
  position: absolute;
  inset: -14px -10px -14px -14px;
}
/* Sound toggle — speaker icon (ref screenshot 02.07).
   Muted: speaker only. Playing: waves appear. */
.sound-toggle {
  background: none; border: 0; cursor: pointer;
  color: inherit;
  padding: 0;
  width: 23px; height: 23px;   /* 13.08 (Chiara: "a bit bigger") — 20 → 23, with the glyph also redrawn to fill its viewBox */
  opacity: 0.75;          /* 03.07: rests quieter than the CTA — utility, not action */
  transition: opacity var(--dur-quick) var(--ease-house);
}
.sound-toggle:hover { opacity: 1; }   /* wakes to full on hover (inverted 03.07 — was full resting, dim hover) */
.sound-toggle svg { width: 100%; height: 100%; display: block; }
.sound-toggle .wave { opacity: 0.4; transition: opacity var(--dur-quick) var(--ease-house); }   /* 13.08 (waves-only icon): arcs rest DIM, not hidden — with no speaker body the waves are the whole glyph, so 0 would leave a lone dot; .playing lifts them to full */
.sound-toggle.playing .wave-1 { opacity: 1; }
.sound-toggle.playing .wave-2 { opacity: 1; transition-delay: 0.1s; }
/* 19.08 (Chiara): the STRIKE — silent = struck. A single diagonal drawn across
   the arcs (crisp stroke draw, no fade): on at load (nothing is playing yet),
   wiped when the track starts, redrawn when she turns it off. */
.sound-toggle .strike { stroke-dasharray: 1; stroke-dashoffset: 1; transition: stroke-dashoffset var(--dur-quick) cubic-bezier(0.65, 0, 0.35, 1); }
.sound-toggle.off .strike { stroke-dashoffset: 0; }
.sound-toggle:not(.off) .strike { opacity: 0; }   /* 27.10 (Chiara, phone: "a dot next to ON"): a fully hidden dash still paints its round cap as a dot — it now hides outright when the sound is on */
.sound-toggle.off .wave { opacity: 1; }   /* 19.08 v2 (Chiara: "it needs to be full — show the icon and the stripe through it"): the arcs stay FULL when off; the strike alone says silent */

/* Hamburger — 01.08 (Chiara, header audit): TWO hairlines, 28px, 8px pitch —
   the quietest mark; the X morph is pure rotation, nothing has to vanish.
   Retired: 02.07 three-hairline 34px (middle bar drew in on hover); before
   that the measured 29×2px two-bar (read heavy). The .menu-toggle-bar span
   stays in the markup on every page — visually retired here. */
.menu-toggle {
  justify-self: start;
  background: none; border: 0; cursor: pointer;
  color: inherit;   /* buttons don't inherit colour — follow the header (cream ↔ charcoal) */
  width: 28px; height: 9px;
  position: relative;
}
.menu-toggle::before,
.menu-toggle::after {
  content: '';
  position: absolute; left: 0;
  width: 28px; height: 1px;
  background: currentColor;
  transition: width var(--dur-quick) var(--ease-house), transform var(--dur-base) cubic-bezier(0.65, 0, 0.35, 1),
              top var(--dur-base) cubic-bezier(0.65, 0, 0.35, 1), left var(--dur-base) cubic-bezier(0.65, 0, 0.35, 1),
              opacity var(--dur-quick) var(--ease-house);
}
.menu-toggle::before { top: 0; }
.menu-toggle::after { top: 8px; }
.menu-toggle .menu-toggle-bar { display: none; }
.menu-toggle:hover::after { width: 18px; }  /* bottom bar draws in on hover */

/* Burger ⇄ X morph — the two hairlines rotate about the shared centre;
   shorter arms (18px) centred in the 28px slot so the X holds the same
   visual size as the resting mark */
.menu-toggle.open::before { top: 4px; left: 5px; width: 18px; transform: rotate(45deg); }
.menu-toggle.open::after { top: 4px; left: 5px; width: 18px; transform: rotate(-45deg); }

/* ============================================================
   OPENING SEQUENCE — M1 (curtain reveal, 03.07)
   The film is pinned full-bleed and never moves. It holds untouched for
   the first ~120vh of scroll, then the green "How we got here" section
   slides up OVER it (.origin pulls itself into the pin range with
   margin-top: -100vh) — the film is fully covered exactly as the pin
   releases, so the hand-off is seamless.
   Height = 100vh view + 120vh hold + 100vh slide-over = 320vh. */
.opening { height: 210vh; position: relative; }   /* 13.08 v9 (Chiara: "the end scroll is still a bit sticky"): 230 → 210vh — the parked dwell shrinks to ~10vh, so the band releases almost as soon as it lands instead of holding the scroll pinned. Budget: 100 view + 100 shrink drive + 10 dwell. The origin's -76svh pull-up tracks the section end, so the band-to-claim seam stays flush at any height. */
.opening-sticky {
  --cover: 0;   /* 0 → 1 as the green section slides over (JS, scroll-synced) */
  --dark: 0;    /* 0 → 1 across the hold — the film darkens BEFORE the curtain arrives */
  position: sticky; top: 0;
  height: 100svh; overflow: hidden;   /* 22.10 (Chiara, phone review: "the vph needs to fit my mobile — the Aura logo is cut off on load and nothing is centred"): 100vh → 100svh. On iPhone Safari 100vh is the TALL viewport (bars hidden), so the frame overran the visible screen — the cluster centred low and the wordmark on the frame’s foot sat under the toolbar. svh is the visible height with the bars shown. */
  background: var(--beige);   /* 13.08 v2 (Chiara: "the background as the video shrinks needs to match the section below, not be dark"): the contraction reveals the ORIGIN's ground — the band parks on beige, Vero grammar. Supersedes the 16.07 --gallery inversion for the shrink phase; at rest the full-bleed film still owns the frame, so the resting look is unchanged. Header flips ink once the frame recedes (JS, .opening.shrunk). */
}

/* ---------- Arrival (03.07, crisp pass — Chiara: fades read PowerPoint,
   drops read gimmick). Burger, sound, logo and wordmark are simply THERE
   from the first paint — no entrance. The film develops behind them
   (it can't be instant; it waits on the first frame). The ONE gesture:
   Start a Project letter-rolls into place just after the film lands
   (JS adds .ready to it 350ms after body.arrived) — the eye lands there. */
/* body:not(.arrived) .hero-media-wrap { opacity: 0; }  RETIRED 25.10 (Chiara: "make the hero video load quicker… it looks off"): the frame was held invisible until canplay or 500ms, then faded over 0.9s — at least ~1.4s of empty dark on every load. The poster (preloaded in the head) now paints at once and the film takes over in place. */
.hero-media-wrap { transition: opacity var(--dur-slow) var(--ease-house); }
.site-header { transition: color var(--dur-base) var(--ease-house); }
.cta--header:not(.ready) .ch { transform: translateY(105%); transition: none; }
.cta--header.ready .ch { transform: translateY(0); }
.cta--header.ready:hover .ch { transform: translateY(-100%); }  /* hover outranks the entrance state */

/* Film wrapper — pinned full-bleed; the next section covers it.
   OH Architecture hand-off (03.07): as the curtain rises the film is
   dragged up at ~0.14× scroll (parallax recede) — it participates in
   the swipe instead of lying frozen under it. The curtain always moves
   faster than the film, so the gap opening beneath is never seen. */
.hero-media-wrap {
  /* 13.08 (Chiara, verostudio.com — "keep my existing hero as is but it has
     the shrink effect on scroll"): THE CONTRACTION. The wrap becomes the
     shrinking frame — full-bleed at rest, easing to Vero's measured park
     (84vw × 24svh, top-anchored, centred) as --shrink runs 0 → 1 (JS,
     easeOutCubic). The band then dwells and scrolls away with the sticky;
     the origin FOLLOWS it now instead of covering it. The dark sticky
     ground (--gallery, 16.07 inversion) frames the band while it parks. */
  position: absolute; inset: 0;
  /* 13.08 v7 (Chiara: "needs to get smoooother"): the band is now a CLIP
     WINDOW, not a resized box — width/height animation forced layout + video
     re-raster every frame (the clunk); clip-path inset paints on the
     compositor. Same geometry: top-anchored, centred, 84vw × 24svh at park.
     The footage stays put and gets windowed — closer to Vero's read too. */
  clip-path: inset(0 calc(var(--shrink, 0) * 8vw) calc(var(--shrink, 0) * 76svh) calc(var(--shrink, 0) * 8vw));
  will-change: clip-path;
  background: var(--ground-3);    /* stands in for footage until film lands — 25.10 (Chiara: "can the loading on the hero video be the dark black colour? at the moment it’s a lighter brown"): --green (= --brown #391913) → the site dark #140E0C */
  overflow: hidden;
}
.hero-media { width: 100%; height: 100%; object-fit: cover; background: transparent; }
/* 27.08 THE INTRO (Chiara: "the clip of the lovely sexy thing should land, then the tagline… a button that says View Our Work — the video behind switches to the showreel"): two films stacked in the frame — the fabric clip at rest, the reel underneath, silent and parked. The click is ONE CUT (0.25s), no fade-y crossover; the line + button leave with it. */
.hero-media--intro, .hero-media--reel { position: absolute; inset: 0; transition: opacity var(--dur-quick) var(--ease-house); }
/* 27.08 v10 THE PULL (film rides up with the mark) TRIED + REVERTED same hour (Chiara: "as it was before, and the logo was good too") — the films hold still; the mark flies alone. */
.hero-media--reel { opacity: 0; }
body.reel-on .hero-media--reel { opacity: 1; }
body.reel-on .hero-media--intro { opacity: 0; }
.hero-intro {
  position: absolute; inset: 0; z-index: 2;
  --hero-cta-h: 34px;   /* the bar's height — the centring below needs it */
  display: grid; place-content: center; justify-items: center; text-align: center; gap: var(--space-lg);
  padding: calc(var(--hero-cta-h) + var(--space-lg)) var(--gutter) 0;   /* v18 (Chiara: "I want this centred to the screen perfectly"): the cluster centres as a block, so the LINE sat above true centre by half the button + gap — a top inset of exactly button + gap moves the block's centre down by half that: the line's centre = the frame's centre, the button hangs below · v14 lower-third seat TRIED + REVERTED same minute ("back to middle") */
  color: var(--cream);
  opacity: calc(1 - var(--shrink, 0) * 3);   /* leaves in the first third of the contraction — the band is too short to hold it */
  pointer-events: none;
}
.hero-intro > * { pointer-events: auto; }
/* 27.08 v2 THE ARRIVAL (Chiara: "the logo should load… strategy and creative should load with the logo… way more smooth and considered, like an arrival moment"): ONE gesture, staged. The film develops (0.9s, existing); at 0.5s the wordmark and the line arrive TOGETHER on the house rise (a short 18px lift, 1.4s on the reveal curve, the .rv grammar at hero scale); View Our Work follows at 1.2s. No blur, no scale — presence, not a fade-in. Reduced motion: seated from first paint. */
/* 27.08 v3 (Chiara: "the video should load with Aura Studios big at the bottom, then Strategy and creative… should READ on with that reedy effect" — the origin claim's word-light): the wordmark rises with the film (0.5s); the line is seated from the start at rest-ink (0.22 per word, buildScrub) and READS ITSELF IN on a clock (main.js heroRead, ~1.7s from 1.2s) — the read IS its entrance, no rise; the button arrives when the read lands (body.intro-read). */
/* v5 (Chiara: "AURA STUDIOS should already be over the photo… with the video in the background"): the mark's own rise RETIRED — it sits inside the film frame and develops WITH the film (the wrap's 0.9s), already seated. */
.hero-intro-cta { opacity: 0; }
body.intro-read .hero-intro-cta { opacity: 1; }
/* 27.08 v28 (Chiara: "View Our Work should have that roll effect as it ENTERS, so your eyes are drawn to it"): the Let's Talk entrance — the letters rise out of the clip, staggered 18ms each, the moment the read lands (intro-read). The rise-and-fade entrance is retired; the roll IS the arrival. */
.hero-intro-cta .ch { transform: translateY(105%); transition: none; }
body.intro-read .hero-intro-cta .ch { transform: translateY(0); transition: transform var(--dur-base) var(--ease-house); transition-delay: calc(var(--i) * 22ms); }
body.intro-read .hero-intro-cta:hover .ch { transform: translateY(-100%); transition: transform var(--dur-base) cubic-bezier(0.65, 0, 0.35, 1); transition-delay: calc(var(--i) * 18ms); }   /* the hover roll outranks the entrance state, as on the header */
.hero-intro-line { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-lg);   /* 07.10 (Chiara: "does this need to be a bit smaller" → serif-md, then "should be a bit bigger I think" → serif-lg): the line lands on the serif-lg RUNG (30–44px), one step up from md and 2px under the retired --type-serif-md-plus bolt-on (+10px over md, used nowhere else, 46px at desktop). On a real rung now, not a one-off. */ line-height: 1.2; max-width: 40ch; margin: 0; text-wrap: balance; opacity: 0; }   /* v4: not on stage until the mark has jumped to the top — then it reads itself in */
.hero-intro-line { transform: translateY(14px); transition: opacity var(--dur-slow) var(--ease-house), transform var(--dur-slow) var(--ease-house); }   /* 27.10: cinema (1.6s) → slow (0.9s), it reads in step with the mark landing */   /* 05.10 (Chiara: "a nice fade"): one slow, soft fade-up of the whole line */
body.intro-line .hero-intro-line, body.intro-read .hero-intro-line { opacity: 1; transform: translateY(0); }   /* v6: on stage the instant the read starts — after the mark has flown */   /* v2 (Chiara: "a bit smaller"): serif-lg → serif-md · centred, two lines — the descriptor rung, not the claim's; the claim follows in the origin */
.hero-intro-cta { font-size: var(--type-caption); background: transparent; border: 0; border-radius: 0; padding: 0; height: var(--hero-cta-h); line-height: calc(var(--hero-cta-h) - 2px); letter-spacing: var(--track-cta); color: var(--cream); cursor: pointer; transition: background var(--dur-base) var(--ease-house), border-color var(--dur-base) var(--ease-house), opacity var(--dur-slow) var(--ease-house), transform var(--dur-cinema) var(--ease-house); }   /* v25 (Chiara: "how do we make the CTA look sexy and nice?"): built from the hero's own materials — the wordmark's FROST (the film blurs through it, 12% cream over), the Start-a-project HAIRLINE at 38%, the Let's Talk LETTER-ROLL on hover (.roll, JS), the ( bracket ) label grammar. Proportions as ruled: 34px, long, square. v11–v13 was the flat 82% ink block */
/* v27 (Chiara: "get rid of the box — just the label with a bracket"): no box at all — ( View Our Work ) as a bare mono label, the letter-roll is the hover; rests at 85% like the section labels, (the arrival rule owns opacity — rests full cream) */   /* v26 (Chiara: "I can't really see what the frosted glass does"): the frost is OUT — the footage is too soft for a blur to read; a plain hairline outline, brightening on hover */
.hero-intro-cta .roll { line-height: inherit; }
.hero-intro-cta { display: inline-flex; align-items: center; }
.hero-play { width: 0.55em; height: 0.66em; margin: 0 0.6em 0 0.75em; flex: none; }
.hero-intro-cta { overflow: hidden; }
.hero-play { transform: translateY(140%); transition: none; }
body.intro-read .hero-play { transform: translateY(0); transition: transform var(--dur-base) var(--ease-house) 330ms; }   /* 27.10 (Chiara: "does the play button do the animation on as well?"): it rises out of the clip with the letters, landing on the beat after "Showreel" (15 letters × 22ms) */   /* 27.10 v4 (Chiara: "put the play button next to Showreel"): ( WATCH SHOWREEL ▶ ) */   /* 27.10 v3 (Chiara: "the play button goes underneath it"): ( WATCH SHOWREEL ) with the play mark centred below */   /* 27.10 v2 (Chiara: "to the right of it, in the bracket"): ( VIEW OUR WORK ▶ ) */   /* 27.10 (Chiara: "a lot of people are going to miss the showreel… a small play button", desktop and phone): View Our Work cuts to the reel, so it now says so — a small solid triangle in the button's own colour */
.hero-scroll-cue { position: absolute; left: 50%; bottom: var(--space-md); transform: translateX(-50%); margin: 0; z-index: 2; color: var(--cream); opacity: 0; transition: opacity var(--dur-slow) var(--ease-house) 0.9s; pointer-events: none; }   /* 28.08: the scroll cue — sits under the big mark's seat once it has flown; arrives 0.9s after the click (reel-on), fades on the first real scroll (body.has-scrolled) */
body.reel-on .hero-scroll-cue { opacity: 0.7; }   /* 28.08 v2 (Chiara: "scroll should come only when someone clicks View Our Work"): the cue belongs to the REEL — once the film is playing and the line/button have left, ( Scroll ) is the only thing on the frame; it arrives 0.9s after the cut */
body.has-scrolled .hero-scroll-cue { opacity: 0 !important; transition-delay: 0s; transition-duration: 0.25s; }   /* has-scrolled = a real first scroll (main.js) */
@media (max-width: 900px) { .hero-scroll-cue { display: none; } }   /* 25.10 (Chiara: "only on desktop"): phones and tablets play the reel in the lightbox — no cue there */
body.reel-on .hero-intro { opacity: 0; pointer-events: none; transition: opacity var(--dur-quick) var(--ease-house); }
body.reel-on .hero-intro > * { pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .hero-intro-cta { opacity: 1; transform: none; transition: none; } }
/* 13.08 v5: the BAND MARK (Vero-style logo on the parked band) TRIED + REMOVED
   same hour — Chiara: "it should only appear again after the video is off
   screen". The band runs bare; the logo's only return is the header mark's
   arrival, now gated on the opening leaving the viewport (updateHeaderGround). */
/* 06.08: letterbox-on-narrow TRIED + REVERTED same hour (Chiara: "video
   needs to fill the height still") — contain below 4:3 kept the reel's
   composed frames intact but broke the full-bleed hero. Cover everywhere;
   the edge crop on narrow screens is the accepted trade-off. If specific
   reel frames lose their titles on mobile, the fix is in the EDIT (safe-area
   the reel's composed frames), not in the CSS. */

/* Scrim — the film runs bright; matches the design's multiply overlay
   (measured rgba(0,0,0,0.41) on the CTA image; lighter here for the hero).
   03.07 (Chiara, Rat & Boa ref): plus a top shadow so the nav pops over
   any footage — darker at the very top, gone by ~22% down the frame. */
.hero-media-wrap::after {
  content: '';
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, calc(0.20 + var(--dark, 0) * 0.25));   /* 14.07 (Chiara: "the hero section can be darker" — MOF cinematic-dark tone): deepened from 0.30; the solid-white wordmark reads brighter against it */
                                     /* 05.08 (Chiara): "reduce the opacity of the overlay ... on the hero" → 0.30, then her call: 0.20. The 0.45 was set when the seat held no footage and a solid-white mark; with the real film in and the mark frosted, it read murky. */
                                     /* 13.08 (Chiara: "the Studio section needs to come UP OVER the hero video"): the --dark hook RECONNECTED — the JS has computed it since 03.07 but its consumer was lost somewhere along the way. At rest the wash stays her 0.20; as the curtain enters it builds to 0.45, so the film visibly dims-and-parks while the cream sheet rises over it. Without this, 88% cream arriving over the SAME film had no legible edge — the slide-over gesture read as a fade. */
  pointer-events: none;
}

/* Hero wordmark — the original logo, huge across the bottom of the frame.
   It stays put; the green section slides up over it (03.07). The header
   wordmark takes over once the frame is covered.
   Arrival (03.07, MOF pass): the slow develop — no movement, a long
   quiet dissolve after the film lands. Luxury as stillness. */
/* Studio descriptor — tracked micro-caps in the header's right cell, left of
   the sound + CTA cluster (06.07 Chiara: "keep it top right"). PP Neue Montreal
   + --track-label, so its letter-spacing matches Start a Project exactly.
   Vertical alignment: a 13px box (= the burger's height) top-anchored to the
   same gutter, text centred inside it — so its optical centre lands on the
   burger's middle bar (gutter + 6.5px), NOT on the taller pill's centre. That
   is what "centre it to the burger's middle line" means here. */
.header-descriptor {
  display: inline-block;
  font-family: var(--font-label);
  font-size: var(--type-label);    /* 12px — the caps-label step */
  line-height: 1;
  /* Tracking matched to Start a Project's RENDERED spacing (~1.15px). Note:
     the CTA is split into per-letter spans with a flex gap ON TOP OF its
     letter-spacing, so it tracks ~2× the --track-label token (0.6px). To
     look identical we match the rendered value, not the token. (If we'd
     rather normalise, the real fix is dropping the CTA's double-count.) */
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  white-space: nowrap;
  color: inherit;                  /* follows the header cream ↔ charcoal */
  transition: opacity var(--dur-quick) var(--ease-house);   /* fades out when the menu opens */
  /* Optical vertical centring: trim the line box down to the cap height so
     the parent's align-items:center lands the CAPS — not the empty line box —
     on the burger's middle bar. Verified: caps ink centre = 38.5px = bar
     centre. Line-box centring alone left the caps ~4.3px high. */
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}
/* Narrow desktop + down: the extra line crowds the wordmark/CTA — hold it
   until there's room (needs its own designed mobile placement) */
@media (max-width: 1024px) { .header-descriptor { display: none; } }
/* 06.08 (Chiara: "load with the logo in the bottom third, then it shrinks
   into the menu nav"): the mark FLIES to the header on scroll. Layout moved
   from left:50% + translateX(-50%) to explicit gutters — the box is
   identical, but `transform` is freed for the flight and origin 0 0 lets
   the JS map this box straight onto the header mark's measured box (same
   aspect-ratio mask, so a translate + uniform scale is an exact fit). */
.hero-wordmark {
  /* 13.08 (Vero port, Chiara: "I love how the Vero logo shrinks too into the
     menu nav"): the mark now lives INSIDE the shrinking frame and rides it —
     width interpolates from the full gutter span down to the header mark's
     own rung (the centred header seat), so the giant mark and the nav mark
     read as one object changing scale. Supersedes the gutter-anchored seat
     and the 06.08 quiet exit. */
  position: absolute;
  left: 50%;
  /* 19.08 (Chiara: "check the smoothness of the video shrinking the text"):
     the contraction moved off LAYOUT (bottom + width interpolated per frame —
     a re-layout and a mask re-raster every frame, and it kept costing after
     the mark had faded) onto the COMPOSITOR: fixed full width, then one
     transform — translateY for the bottom's travel (gutter → 14px) and scale
     for the width's (full span → the header rung; the ratio --wm-s is set by
     JS from the same two measures, since CSS can't divide lengths). Same
     start and end geometry, no layout. */
  bottom: var(--gutter);
  width: calc(100vw - var(--gutter) * 2);
  --wmp: max(var(--shrink, 0), var(--wmf, 0));   /* 27.08 v6 (Chiara: "the logo at the bottom shrinks up to the main nav… where it is when we scroll down"): the flight runs on EITHER driver — the scroll contraction (--shrink) or the timed intro flight (--wmf, main.js) — same geometry, same landing on the header seat */
  transform: translateX(-50%) translateX(calc(var(--wm-dx, 0px) * var(--wmp))) translateY(calc(var(--wm-dy, calc(var(--gutter) - 14px)) * var(--wmp))) scale(calc(1 - (1 - var(--wm-s, 0.18)) * var(--wmp)));   /* 03.09: --wm-dx joins dy — the header seat is top-LEFT now, so the shrink travels on both axes (JS measures it) */   /* 27.08 v7: --wm-dy = the measured travel to the nav seat (JS setWmScale) — the mark rises AND shrinks into the header, and the header mark takes over at that exact spot */
  transform-origin: 50% 100%;
  z-index: 1;                /* over the frame's scrim */
  /* 13.08 v3 (Chiara: "needs to disappear like it did before"): the
     end-of-shrink dissolve trial RETIRED same hour — the 06.08 quiet exit
     is back (wm-hidden on first scroll, JS). The width interpolation below
     stays harmlessly: the mark is gone before it matters. */
  will-change: transform;
  z-index: 1;                            /* above the scrim */
  /* 03.07 (Chiara): frost REMOVED — backdrop-filter isn't reliably
     clipped by the mask, so the blur painted a visible block across the
     element box. Cream letters, glassy, footage showing through.
     Dial: the alpha %.
     05.08 (Chiara: "the logo overlay needs the frosted effect"): matched to
     the site's frosted-on-imagery rule — 58% cream, the same value as
     .cs-intro-lead on the case-study heroes. Was 80% here, and dead anyway:
     the 14.07 solid-#fff rule further down was overriding it.
     06.08: alpha now rides --wm-fill (JS, 58 → 100 across the flight) so
     the mark is solid cream when it lands on the header seat — the swap to
     the real header mark is invisible. Resting value unchanged: 58%. */
  /* 01.09 (Chiara: "the big logo going to the menu nav has a weird jittering effect — like it's
     repeating the logo loads"): the fill was a color-mix driven by --wm-fill, recomputed EVERY
     frame of the flight — a full repaint of a MASKED layer per scroll tick, which re-rasters the
     SVG mask and shimmers on the thin serif strokes. The mark is now solid cream and the same
     46% → 100% ramp runs on OPACITY, which is compositor-only: no repaint, no re-raster.
     (JS still sets --wm-fill; harmless, and it keeps the old value documented.) */
  background-color: var(--cream);
  opacity: calc(0.46 + 0.54 * var(--wmp));
  will-change: transform, opacity;
}
/* 09.09 THE CUT (Chiara: "the current animation up looks a bit weird… it doesn't look luxurious, it looks a
   bit PowerPoint"). Behind body.intro-cut (the intro lab first; index when ruled). The FLIGHT — one object
   shrinking across the screen from the frame's foot to the header seat — is the PowerPoint move however well
   it is eased: the eye follows a journey. The cut is two gestures in place, and nothing travels.
   v2 (Chiara, on seeing the down-wipe: "take the effect we've used on the case study text, the way it swipes
   up, up, up, and put that on the logo"): THE DECK'S WORD-ROLL, on the mark. The big mark ROLLS UP out of a
   fixed window exactly as a case-study title does (.hw-title--roll: translateY 112%, --dur-slow, house curve),
   a beat of nothing, then the header mark rolls UP into its seat the same way. One element carries both the
   window and the move: the clip's top inset grows by the same distance the mark travels, so in page space the
   top edge holds still while the letters leave through it — the roll, without a wrapper. Not a fade: her rule.
   The scroll contraction (--shrink/--wmp) is untouched — this only runs on the clock. */
body.intro-cut .hero-wordmark {
  clip-path: inset(-0.3em 0 -0.3em 0);   /* rest: open, with breath for the serif overshoot */
  transition: clip-path var(--dur-slow) var(--ease-house), transform var(--dur-slow) var(--ease-house);
}
body.intro-cut.wm-exit .hero-wordmark {
  clip-path: inset(112% 0 -0.3em 0);
  transform: translateX(-50%) translateY(-112%);   /* up and out through a top edge that does not move */
}
/* v3 (Chiara: "should I swipe down… like disappear down"): the DOWN variant, for the comparison — the same roll,
   the mark leaving through the window's BOTTOM edge (bottom inset grows by the travel, so that edge holds still).
   The header mark still rolls up. Lab: ?down. One of these two gets ruled; the other is deleted. */
body.intro-cut.intro-down.wm-exit .hero-wordmark {
  clip-path: inset(-0.3em 0 112% 0);
  transform: translateX(-50%) translateY(112%);
}
body.intro-cut .site-header .wordmark {
  opacity: 1;   /* presence is carried by the clip, not opacity — the arrival is a roll, not a fade */
  clip-path: inset(0 0 100% 0);
  transform: translateY(112%);
  transition: clip-path var(--dur-slow) var(--ease-house), transform var(--dur-slow) var(--ease-house);
}
body.intro-cut .site-header.wordmark-visible .wordmark { clip-path: inset(-0.3em 0 -0.3em 0); transform: translateY(0); }
body.intro-cut.menu-open .site-header .wordmark { opacity: 0; }   /* the menu-foot logo takes over, as on index */
@media (prefers-reduced-motion: reduce) {
  body.intro-cut .hero-wordmark, body.intro-cut .site-header .wordmark { transition: none; }
}
/* 06.08 v3 (Chiara): the FLIP flight retired same hour for the calmer read —
   "the logo disappears, and then on scroll appears in nav", and the exit
   "should be clean". So: a fast plain opacity out, NO drift, no scale — the
   mark is simply gone; the header mark then makes its own house arrival
   when the work deck reaches the fold. JS drives wm-hidden only. */
body.wm-hidden .hero-wordmark {
  opacity: 0 !important;   /* 01.09: outranks the flight ramp above */
  pointer-events: none;
  transition: opacity 0.25s ease-out;
}
/* 06.08 v4 (Chiara): centre seat RETIRED ("on the left where it was before")
   and the arrival re-anchored — the nav mark appears only when the work deck
   reaches the fold ("it only comes on when you get to the case study pages"),
   via the wordmark-visible toggle in updateHeaderGround. No CSS override
   needed; wm-landed no longer exists. wm-hidden (big-mark exit) remains. */

/* Hero line + support — placement TBC with Alex (not in frames yet) */


.scroll-cue {
  position: absolute; bottom: var(--space-md); left: 50%;
  width: 1px; height: 40px;
  background: var(--paper);
  opacity: 0.7;
  animation: cue 2.2s var(--ease-ambient) infinite;
}
@keyframes cue { 0%,100% { transform: scaleY(0.4); } 50% { transform: scaleY(1); } }

/* ============================================================
   MENU OVERLAY — light (measured: cream bg, 112px words, hairlines)
   ============================================================ */
.menu-overlay {
  position: fixed; inset: 0; z-index: 50;
  background: var(--paper);
  color: var(--ink);
  padding: 127px var(--gutter) 0;   /* measured: hairline at y127 */
  /* 03.07 (Chiara): fade read gimmicky — now a strong swipe. The panel
     wipes DOWN from the top edge (clip-path: content holds still, the
     edge sweeps). Close retracts back up, quicker. */
  visibility: hidden;
  clip-path: inset(0 0 100% 0);
  transition: clip-path var(--dur-quick) cubic-bezier(0.65, 0, 0.35, 1), visibility 0s 0.35s;
}
.menu-overlay.open {
  visibility: visible;
  clip-path: inset(0);
  /* 03.07: QUICK — the luxury-reference swipe is decisive, not languid */
  transition: clip-path var(--dur-base) var(--ease-house), visibility 0s;
}
/* 03.07 (Chiara): the open menu carries the mark — the header wordmark
   shows whenever the overlay is up (it's otherwise hidden until the
   opening is scrolled past), green on the cream sheet. Outranks the
   nav-hidden state (extra element in the selector). */
body.menu-open .site-header .wordmark { opacity: 0; pointer-events: none; }   /* 16.07 (Chiara): the logo moved to the menu foot — the header mark stands down while the overlay is open */
body.menu-open .site-header .logo-mask { background-color: var(--green); }  /* stays green even if on-dark set it cream */
/* 14.07 (Chiara, Matter of Form ref): menu foot — descriptor left, contact
   right, "the line above it" = the hairline rule. Anchored to the sheet's
   bottom edge inside the gutters. */
.menu-foot {
  position: absolute;
  left: var(--gutter); right: var(--gutter); bottom: var(--space-md);
  padding-top: var(--space-md);   /* 13.08: Domingo foot-tighten (md → sm) TRIED + REVERTED same hour (Chiara) */
  border-top: 1px solid color-mix(in srgb, currentColor 22%, transparent);
  display: flex; justify-content: space-between; align-items: flex-end; gap: var(--space-lg);
  font-size: var(--type-body-sm);
}
.menu-foot-desc {
  max-width: 34ch; opacity: 0.75;
  font-size: var(--type-body);   /* 14.07 (Chiara): 16px — "bigger" than the 14px foot default, but serif-sm's ~22px was "too big"; body rung splits it */
}
.menu-foot-logo { width: clamp(300px, 26vw, 400px); background-color: var(--ink); }   /* 16.07 v2 (Chiara: "logo should be bigger") — up from the header's 228px rung */
/* 13.08: foot-logo masked rise TRIED + REMOVED same day — a misread of
   "the logo in the menu nav" (Chiara meant the HEADER mark's fade). The
   foot logo is simply present with the overlay. */
.menu-foot-contact { text-align: right; display: grid; gap: 8px; justify-items: end; }
.menu-foot-contact a { border-bottom: 1px solid color-mix(in srgb, currentColor 35%, transparent); }
/* 14.07 (Chiara): IG + LinkedIn above the email — quiet line icons, no underline */
/* 12.10 (Chiara: the icons "a bit tacky" → "the wording in mono and then an arrow at 45 degrees"): the two socials are
   hairline ROWS — mono label left, a thin 45° arrow right — the grammar of her reference screenshot. */
/* v2 same evening (Chiara: "review the size of the images now we have added IG and LinkedIn to the footer — or does everything
   go side by side?"): SIDE BY SIDE. Stacked rows made the foot three lines tall under a preview panel whose height is
   fixed between the hairlines, and on a short laptop the rows climbed into the image. One row — Instagram ↗, LinkedIn ↗,
   then the email — keeps the foot one line and the image its full height. */
.menu-foot-contact { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: flex-end; gap: var(--space-xs) var(--space-lg); }
.menu-foot-social { display: flex; gap: var(--space-lg); }
.menu-foot-social a { display: inline-flex; align-items: center; gap: var(--space-xs); border-bottom: 0; opacity: 0.75; transition: opacity var(--dur-quick) var(--ease-house); }
.menu-foot-social a:hover { opacity: 1; }
.menu-foot-social .label { margin: 0; }
.menu-foot-arrow { width: 14px; height: 14px; display: block; flex: none; transition: transform var(--dur-quick) var(--ease-house); }
.menu-foot-social a:hover .menu-foot-arrow { transform: translate(2px, -2px); }
@media (max-width: 700px) {
  /* 20.10 (Chiara, the phone menu foot, settled on the third pass: "Instagram left, LinkedIn left, and then Hello Aura
     aligned right. And make the font what they were, size-wise."): the socials anchor the LEFT gutter, the email pushes
     to the RIGHT via margin-left auto — which also holds the email on the right edge if a narrow phone wraps it to its
     own line. Font sizes untouched (an 11px shrink was tried + reverted within the hour — "Why is the font so small"). */
  .menu-foot { flex-direction: row; }
  .menu-foot-contact { width: 100%; justify-content: flex-start; flex-wrap: wrap; }
  .menu-foot-contact > a[href^="mailto:"] { margin-left: auto; }
}
.menu-overlay::before {
  content: '';
  position: absolute; left: var(--gutter); right: var(--gutter);
  height: 1px; background: var(--ink); opacity: 0.25;
  top: 127px;
}
/* 14.07 (Chiara: "double lines — remove one"): the overlay's OWN bottom
   hairline (::after at bottom 90–150px) retired — the menu-foot's border-top
   is the one bottom rule now, attached to the content it frames. */
/* Menu items arrive with the house rise (M4) when the overlay opens —
   staggered, classy, once per open (02.07) */
.menu-primary li, .menu-secondary li {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity var(--dur-base) var(--ease-house), transform var(--dur-slow) var(--ease-house);
}
.menu-overlay.open .menu-primary li, .menu-overlay.open .menu-secondary li {
  opacity: 1;
  transform: translateY(0);
}
.menu-overlay.open .menu-primary li:nth-child(1) { transition-delay: 0.05s; }
.menu-overlay.open .menu-primary li:nth-child(2) { transition-delay: 0.13s; }
.menu-overlay.open .menu-primary li:nth-child(3) { transition-delay: 0.21s; }
.menu-overlay.open .menu-primary li:nth-child(4) { transition-delay: 0.29s; }
.menu-overlay.open .menu-secondary li:nth-child(1) { transition-delay: 0.38s; }
.menu-overlay.open .menu-secondary li:nth-child(2) { transition-delay: 0.44s; }
.menu-overlay.open .menu-secondary li:nth-child(3) { transition-delay: 0.50s; }
.menu-overlay.open .menu-secondary li:nth-child(4) { transition-delay: 0.56s; }
.menu-overlay.open .menu-secondary li:nth-child(5) { transition-delay: 0.62s; }
@media (prefers-reduced-motion: reduce) {
  .menu-primary li, .menu-secondary li { opacity: 1; transform: none; transition: none; }
  .menu-overlay { clip-path: none; transition: none; }   /* RM: instant show/hide, no wipe */
}

/* Secondary hover (02.07): soft nudge + brackets breathe in — the house
   ease, nothing sudden */
.menu-secondary a {
  display: inline-block;
  transition: color var(--dur-quick) var(--ease-house);
}
.menu-secondary a:hover, .menu-secondary a:focus-visible {
  color: var(--green);   /* 02.07: movement removed — brackets + colour carry the hover */
}
/* Spotlight (02.07): hovering one item softens the rest — the list focuses */
.menu-secondary:hover a { opacity: 0.4; transition: opacity var(--dur-quick) var(--ease-house), color var(--dur-quick) var(--ease-house); }
.menu-secondary:hover a:hover { opacity: 1; }
.menu-secondary a { transition: opacity var(--dur-quick) var(--ease-house), color var(--dur-quick) var(--ease-house); }

/* Secondary hover: index brackets appear around the hovered item (02.07) */
.menu-secondary .tm { font-size: 0.65em; vertical-align: super; }
.menu-secondary a::before { content: '( '; opacity: 0; transition: opacity var(--dur-quick) var(--ease-house); }
.menu-secondary a::after { content: ' )'; opacity: 0; transition: opacity var(--dur-quick) var(--ease-house); }
@media (hover: none) { .menu-secondary a::before, .menu-secondary a::after { content: none; } }   /* 20.10 (the phone menu review): the brackets are hover furniture, but at opacity 0 they still RESERVE their width — on touch every link carried ~1ch of phantom space each side, part of why the Services row read ragged. No hover, no brackets, no ghost gaps. */
.menu-secondary a:hover::before, .menu-secondary a:hover::after,
.menu-secondary a:focus-visible::before, .menu-secondary a:focus-visible::after { opacity: 1; }

.menu-primary { padding-top: var(--space-md); position: relative; z-index: 2; }   /* 13.08 (Chiara: "reduce the margin in the menu nav from the top a little"): one token step down, lg → md (40 → 24) — the overlay's own 127px hairline seat untouched */
.menu-primary a {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-menu-word);   /* 20.10: display → the menu-word variant (41px floor) — see the token */
  line-height: 1.35;               /* opened for PP Eiko taller metrics (was 1.18 for GT Super) — stops the roll clipping descenders + more air between words */
  text-transform: none;             /* source text is already Title case — `capitalize` + letter spans caused the all-caps glitch */
}
.menu-primary a.roll { display: inline-flex; line-height: 1.35; }
.menu-primary .tm { font-size: 0.45em; }   /* 20.10 (Chiara: "can the tm be smaller"): the Method's ™ ran at the browser's sup default (~0.83em = 34px on the phone words) — a second word. 0.45em sits it as a mark, not a letter; also buys the Method line ~12px of room on narrow phones. */
.menu-primary a:hover { color: var(--green); }

/* Hover preview — right side (measured frame: 580×326 @ ~50%+15px).
   Items crossfade per hovered word; tinted until real images land. */
.menu-preview {
  /* 03.07: bigger, flush to the right gutter, centred on the word block */
  /* 03.07 final: the image IS the right panel — full height between the
     two hairlines, flush to the gutter. A two-column spread, not a card. */
  position: absolute;
  right: var(--gutter);
  top: var(--mp-top, calc(127px + var(--gutter)));   /* 21.10 v4 (Chiara: "can it match the height of the content"): main.js measures the menu’s link block on open and hands its top and height over as --mp-top / --mp-h; the width follows the 4:5 ratio. Fallbacks = the v3 gutter insets */
  height: var(--mp-h, auto); bottom: calc(var(--space-md) * 3 + var(--gutter));
  width: auto; aspect-ratio: 4 / 5; max-width: clamp(408px, 32.3vw, 663px);   /* 21.10 v4: width from the content height at 4:5, capped at the v2 clamp */
  pointer-events: none;
  opacity: 0;
  /* 15.07 (Chiara: "menu overlay should have the same too"): the Crown
     fade-tile arrival — soft 150px rise + fade, plain ease, matches the
     router's disc-preview */
  transform: translateY(150px);
  transition: opacity var(--dur-slow) var(--ease-house), transform var(--dur-slow) var(--ease-house);
  z-index: 1;
}
.menu-preview.active { opacity: 1; transform: translateY(0); }
@media (max-width: 700px), (hover: none) { .menu-preview { display: none; } }   /* 25.10 (Chiara: "on mobile, hide the images on the burger menu dropdown"): the previews are hover furniture — on touch a TAP fires the mouseenter, so the panel flashed up as the page left and nothing ever cleared it (no mouseleave on touch). Off on phones and every touch screen; desktop unchanged */
.menu-preview-item {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-house);
  background-size: cover; background-position: center;
}
.menu-preview-item.active { opacity: 1; }
.menu-preview-item { inset: 0; overflow: hidden; }   /* 21.10 v4: the image fills the panel again — the panel itself now takes the content’s height (above). The square/4:5 centring of v1–v3 retires */   /* 21.10 v2 (Chiara: "make that smaller again, it’s too tall still"): 4:5 → square */
.menu-preview-item video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }   /* 21.10 (Chiara: "can film on the burger menu be a preview"): the Film row carries the film hero sequence; main.js plays it while its row is lit */   /* 21.10 (Chiara: "reduce the height of the images on the menu, they are too tall"): the image is a 4:5 portrait centred in the panel’s run, no longer the full height between the hairlines; the panel box (and its arrival rise) is unchanged */
/* 12.10: the menu previews briefly carried the homepage panes' films (same evening, Chiara: "scrap that") — back to the background stills above. */
.menu-preview-item--strategy { background-color: color-mix(in srgb, var(--green) 55%, var(--cream)); background-image: url('../assets/images/menu/strategy.jpg'); }
.menu-preview-item--identity { background-color: color-mix(in srgb, var(--sage) 85%, var(--ink)); background-image: url('/assets/branding/one-and-only-brochures.jpg');   /* 21.10 (Chiara: "branding image on the menu — can it be the one&only brochure"): the branding hero's own frame, so the menu preview and the page it opens share one image */; }   /* 13.10 (Chiara: "i dont like the images for branding we have used on the burger menu"): Hustl's mark in the sand → the Palmer brand guidelines layout — the whole identity system in warm terracotta, green and cream, not a single mark */   /* 12.10 (Chiara: "pick better images"): Hustl's mark in the sand — the brand the homepage Branding hover plays */   /* 13.08 v2 (Chiara: "try this for brand guideline"): the Zambala guidelines spreads laid flat — supersedes the same-day LHC identity spread (that one's a candidate for /branding). Web-optimised 1600w from her master; the 58MB PNG master is parked OUTSIDE the repo in aura-rebrand/design-masters/ (GitHub rejects >100MB files — the Websites master was 103MB). Feeds BOTH the router row and the menu overlay (shared class by design). */
.menu-preview-item--digital  { background-color: color-mix(in srgb, var(--brown) 45%, var(--cream)); background-image: url('/assets/web/palmer-laptop-terracotta-sofa.jpg'); }   /* 21.10 (Chiara: "web design — can it be the hero in the menu too… orange sofa"): the web hero's own frame, as the branding preview now carries the branding hero */   /* 12.10: the Beyond by Kim site on the laptop — the homepage Web Design hover's subject */   /* 29.08 (Chiara: "the image we have used on the palmer case study page — use that one on the homepage too, the colour is nice"): the Websites hover/menu preview takes the Palmer case-study hero (the poolside parasols); the 13.08 laptop-on-sofa mockup retires here */   /* 13.08 (Chiara: "this for website"): laptop-on-sofa homepage mockup, web-optimised from her master (design-masters/, as above) */
.menu-preview-item--photography { background-color: color-mix(in srgb, var(--green) 30%, var(--sage)); background-image: url('/assets/photography/chef-counter-window.jpg'); }   /* 21.10 v2 (Chiara: "photography image needs to match the hero"): the chef at the window, the page’s own hero, as branding and web carry theirs. The tea master (library 08) was here for an hour. */   /* 21.10 (Chiara: "the hover over image should be the japan man on the menu"): the tea master, library frame 08 — the menu panel is portrait, so the full 2:3 frame sits as shot (face, spoon, pot). The lake retires here and on the homepage pane. */   /* 12.10 v2: her Popup 2 — the lake under the peaks, matching the homepage Photography hover */   /* 18.08 (Chiara): her IMG_0405 — the Can Ferrereta (Santanyí, Mallorca) spa pool, baked to the frame ratio. v1 same hour was the safari shot (IMG_0927 → aura-studios-photography-safari-elephant.jpg, kept in assets). The menu/photography.jpg it pointed at before never existed; feeds the menu preview too (same class). */
.menu-preview-item--film { background-color: color-mix(in srgb, var(--brown) 30%, var(--sage)); background-image: url('/assets/shared/menu/palmer-lake.jpg'); }   /* 12.10: a frame from the Palmer film (the lake at dawn) — the homepage Film hover's footage */   /* 09.09: the kitesurf still was deleted in c9b4b4c but this rule still asked for it — a 404 on every page load. Points at the film page's own hero for now; Chiara to pick the menu's film still. */   /* 18.08 (Chiara: "video hover — crop some of the bottom left so less boat, more focus on the filming"): her IMG_2017 baked to the frame's 580:326 at (880,960 · 2100×1180) — kite, cameraman on the Ronin, kitesurfer; console + throttle cut. menu/film.jpg never existed. */
.menu-secondary { margin-top: var(--space-xl); display: flex; gap: var(--space-md); flex-wrap: wrap; }   /* 13.08: Domingo rhythm test (xl → lg) TRIED + REVERTED same hour (Chiara) — the 24px pull-up didn't register at overlay scale */
.menu-secondary a {
  font-family: var(--font-label);
  font-weight: 400;   /* 04.07 (Chiara): was inheriting the body's Thin — joins the Regular wayfinding caps */
  font-size: var(--type-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

/* ============================================================
   ORIGIN — the deep, grounded moment (brand guidelines: colour
   "deep and grounded rather than safe and neutral"). Cream type
   on the green ground; editorial, text-led, generous air.
   ============================================================ */
/* Matched to the For/Not-for section's structure (02.07): same centred
   container, same left-aligned columns, same measures and rhythm — one
   feature-serif system across origin + for-whom. */
.origin {
  /* 18.08 (Chiara: "should the background do a fade thing into a different
     colour? maybe would look cool"): THE GROUND SHIFT. Beat one sits on
     beige; through the crossfade the ground warms to --ground-2 (beige pulled
     toward the brand brown — a warm clay) under the credential beat, staging
     the descent into the dark deck. --xf is scroll-driven (main.js
     updateOriginXfade, 0 → 1 across the swap), so it is reversible and never
     a timed fade. Swap --ground-2 for var(--sage) for the cooler read. */
  background: color-mix(in srgb, var(--ground-3) calc(var(--xf2, 0) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--xf, 0) * 100%), color-mix(in srgb, var(--beige) var(--origin-alpha, 88%), transparent)));   /* 23.10: the beige's alpha is a dial (--origin-alpha) — the phone ride-over sets it solid */   /* v7b: sRGB throughout, so the live drift passes through exactly the stops a Figma gradient would */   /* v6/v7: beige(88%) → ground-2 on --xf → ground-3 on --xf2 — one perceptual gradient (OKLCH) light → dark across the three beats; lands SOLID so the seam into the deck's ground is exact */
  color: color-mix(in srgb, var(--ground-3-text) calc(var(--xf2, 0) * 100%), color-mix(in srgb, var(--ground-2-text) calc(var(--xf, 0) * 100%), var(--ink)));   /* v10: ink → #16100D on the taupe (swap 1) → cream on the near-black (swap 2) */   /* 13.08 (Chiara: "a bit of lighter opacity and then video underneath it … just the showreel behind it"): the ground goes 88% — the still-pinned hero showreel ghosts through for the section's whole ride (film + section scroll in lockstep after cover). Was opaque var(--beige) (08.08). KNOWN OPEN: scrub words rest at 0.22 ink over moving footage — judge, then raise the rest opacity (main.js buildScrub) or retire the scrub here if mushy. */
  /* color: var(--ink) (08.08) SUPERSEDED 18.08 v3 by the ink → cream drift declared above with the ground */
  /* 03.07 (Chiara): label cut — the section opens straight on the big
     statement, centred in the green frame (symmetric padding + the auto
     margins below). The arriving header wordmark owns the top zone. */
  padding: 0 var(--gutter);   /* 13.08 v2 (lab match): the two beats each own a full viewport — block padding retires, the min-heights carry the rhythm */
  /* Curtain reveal (03.07): rides up into the opening's pin range and
     covers the still-pinned film — must stay -100vh, matched to the
     .opening height budget (100 view + 120 hold + 100 slide-over).
     min-height pays the borrowed 100vh back in full: any shorter and a
     strip of film peeks out between this section and the next. */
  margin-top: -76svh;   /* 13.08 v8 (Chiara: "wayy too much blank space between the hero and the next section"): the sticky stage is 100svh but the parked band fills only its top 24svh — this pulls the origin up over the empty 76svh so its top edge sits FLUSH with the band's bottom (230vh section end − 76svh = the band's document bottom). Beige over beige, invisible seam; the claim rises in beneath the parked band, Vero's own choreography. Supersedes the v2 "normal flow" ruling. */
  min-height: 100vh;
  position: relative;
  z-index: 1;
  /* 13.08 (Vero port, Chiara: "love this bit" — the lab's centred statements):
     the origin moves to the CENTRE AXIS — tag, claim, answer, credential, CTA
     all centred, the MoF/Vero belief grammar. Supersedes origin-lab A "The
     Monument" (claim at the gutter, cluster at the golden section). */
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
/* Soft leading edge (03.07, Alex: the straight green line was too sharp).
   A gradient apron rides ABOVE the section's top edge, so the curtain has
   no line — the taupe condenses out of the footage as it rises.
   06.07 (Chiara: "glitchy, harsh line on the right — make it seamless").
   Rebuilt as a TRUE vertical dissolve. Two rules that make it luxe:
   (1) the RGB is constant (239,230,217 = #EFE6D9) and ONLY the alpha
       ramps — so there is zero hue/luminance drift through the fade. The
       old version faded to `transparent` (= black·0α), which greyed the
       midtones into a dirty band. Constant-RGB, alpha-only = no mud.
   (2) an eased, many-stop toe — the top ~third barely lifts (0→0.08α), so
       the fade has no perceptible onset edge; it accelerates to solid low
       down, where the join to the section is invisible (both solid taupe).
   The radial was dropped entirely: an off-centre ellipse draws CURVED
   iso-lines, and that arc clipping the right of the frame WAS the "harsh
   line". A vertical ramp has no such contour. */
/* .origin::before apron DELETED 10.07 v2 (Chiara: the wash "looks cheap")
   — the origin now covers the pinned film with a HARD EDGE, same grammar as
   the Method cover and the film-page swipes. */
/* (13.08: the ?film / ?src URL-toggle trials RETIRED same day — the film-through
   ground shipped as the default on .origin above; the Odisi films went to the
   work deck, not the hero/origin. See git for the trial rules.) */

/* 10.07 v2 (Chiara: "margins are off"): the mirror beat composed — cluster
   vertically centred in the viewport, statement left, guide right-below;
   the old auto-margins (cover-era) left a dead half-screen under the CTA. */
.origin { justify-content: flex-start; gap: 0; }   /* 13.08 v2 (lab match): the section is TWO full-viewport beats — claim screen, then stack screen — exactly the opening-lab rhythm ("this needs to match the opening lab example") */
/* 18.08 (Chiara: "the first bit fades out as you scroll and the second bit
   fades in — in its place"): THE CROSSFADE. The two beats no longer stack
   down the page — they share ONE seat. .origin-stage pins for a viewport
   (sticky, 100svh) while the section runs --xf-budget longer; the claim
   scrubs out across the first ~40% of that ride, the answer stack scrubs in
   across the last ~40%, both scroll-driven and reversible (main.js
   updateOriginXfade — no timed fades). Supersedes the 14.08 "own viewport"
   claim beat: the seat (26svh) is now the STAGE's, shared by both layers. */
.origin { --xf-budget: 220svh;   /* 28.08 v3: 160svh TRIED + UNDONE at once (Chiara: "no undo") ·   /* 28.08 v2 (Chiara: "the second batch of images need to be way lower down"): 180 → 220 — the ride needs the room for the late pair to climb fully in before the release · 28.08: TWO beats (claim+answer · credential) — 240 → 180 */ /* v5→v10: 140 → … → 240 — THREE beats in one seat, each with a hold: claim alone · answer alone · credential + CTA */ min-height: calc(100svh + var(--xf-budget)); }   /* 18.08 v4 (Chiara: "should we slow it down?"): 120 → 140 — the answer's arrival + word-read had ~36svh of scroll, quicker than reading pace; everything scales with this one dial. Past ~150 the pin starts to feel sticky. */
.origin-stage {
  position: sticky; top: 0;
  height: 100svh; width: 100%;
  display: grid;
  justify-items: center; align-items: center;   /* 18.08 (Chiara: "this should be centred to the section"): the seat is the viewport's CENTRE — both layers share it. Supersedes the 13.08 v6 upper-third (26svh) seat. */
}
.origin-claim, .origin-mid, .origin-body { grid-area: 1 / 1; will-change: transform, opacity; }
.origin-stage { overflow: hidden; }   /* 28.08: the floats travel through the stage and are clipped at its edges */
/* 28.08 THE FLOATS: absolute in the stage; --ox/--oy0/--ow are each figure's seat
   (inline, per figure), --oy is the ride (JS). Rates < -1 mean they climb faster
   than the page — the "float". z-index 0 = under the type layers. */
.origin-floats { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.of { position: absolute; left: var(--ox); top: 0; width: var(--ow); margin: 0; transform: translateY(calc(var(--oy0) + var(--oy, 0px))); will-change: transform; }
/* 01.09 (Chiara: "let them load on"): each float ARRIVES rather than being simply present —
   a short rise + fade as it reaches the frame, once, on the house curve. The rise uses the
   `translate` property, NOT transform: transform is already carrying the parallax ride, and
   the two would fight. JS adds .in via an IntersectionObserver with a rootMargin so the move
   starts just before the figure enters. Reduced motion: seated, no move. */
.of { opacity: 0; translate: 0 42px; transition: opacity var(--dur-slow) var(--ease-house), translate var(--dur-cinema) var(--ease-house); }
.of.in { opacity: 1; translate: 0 0; }
@media (prefers-reduced-motion: reduce) { .of { opacity: 1; translate: 0 0; transition: none; } }
.of img, .of video { display: block; width: 100%; height: auto; animation: of-bob var(--bob, 9s) var(--ease-ambient) var(--bob-d, 0s) infinite alternate; will-change: transform; }   /* 28.08 v2 (Chiara: "super floaty"): a slow idle BOB on each image, own clock + phase per figure, so they float even when the page is still — under the scroll ride */
.of--1 img, .of--1 video, .of--2 video, .of--2 img, .of--3 img, .of--3 video { aspect-ratio: 881 / 1181; object-fit: cover; }   /* 12.10 (Chiara: "swap the curtain and elephant"): floats 1 and 3 both take img OR video now, so a seat swap is markup-only. Earlier 12.10: .of--3 video joined — the curtain film took the boat's seat and keeps its 3:4 box (the .of--3 img rule below is the boat's, idle until a still returns) */   /* 07.10: .of--1 joins — the elephant moved to the FIRST seat (swapped with the sofa), and keeps its 3:4 box there; the sofa plate is natively 881×1181 so the rule is a no-op on seat 2 */   /* 01.10: img joins — float 2 is the elephant photograph now, held in the same 3:4 box the film had */
/* 12.10: the deck-anchored crop below was the BOAT's (object-position 50% 100%); the elephant sits in seat 3 now and keeps its centred crop, so the rule is retired — restore it with the boat. */ /* .of--3 img { aspect-ratio: 881 / 1181; object-fit: cover; object-position: 50% 100%; } */   /* 10.09 (Chiara: "crop into the image of the ladies on the boat — show less of the white space on the top" · 10.09 later: swapped to the first seat and back — the boat is in the THIRD seat, rule follows it): the 2:3 frame goes 3:4 like its neighbours, anchored to the deck so the whole trim comes off the sail canopy */   /* 10.09: the film float sits in the first float's frame (3:4); the 9:16 footage is cover-cropped into it */
/* 10.09: 20px CORNERS on every framed seat TRIED + REVERTED same hour (Chiara: "hate it — undo the corners"). Hard edges stand; the softness in this section is the motion. */
@keyframes of-bob { from { transform: translateY(-9px); } to { transform: translateY(9px); } }   /* 10.09: the ±0.35° rotate RETIRED — the over-image twin is clipped by an axis-aligned inset, so a rotated image always left a sliver of cream text at the corners (the "funny outline"). The bob is the drift alone now. */
/* 28.08 v2 (Chiara: "the text has a parallax behind it"): the type layers counter-drift slowly (--tp, JS: a gentle 70px climb across the ride) while the floats climb fast — two speeds = depth. `translate` composes with the layers' own transforms. */
.origin-claim { translate: 0 var(--tp, 0px); }   /* 29.08 (Chiara: the credential + CTA "not centred… vertical"): the −70px text parallax now rides the CLAIM only — the credential layer sits at the stage's exact centre, para + CTA as one group */
.origin-claim, .origin-mid, .origin-body { position: relative; z-index: 1; }
.origin-claim--over { color: var(--cream) !important;   /* 29.08 (Chiara: "when the image goes over the text it should be cream not grey"): the 58% frost read as grey — solid cream */ pointer-events: none; z-index: 2; }   /* 28.08 v3: the over-image twin — the case-study frosted-title rule (58% cream), clipped per frame to its float (main.js) */
.origin-claim--over .origin-lead span { opacity: 1 !important; }   /* the twin's words are always lit — the frost itself is the tone */
@media (max-width: 900px) { .origin-floats { display: none; } }   /* phones: the type owns the screen */
@media (prefers-reduced-motion: reduce) { .origin-floats { display: none; } }
   /* same cell = same seat; JS drives opacity + the drift (18.08 v3 motion continuity; v10: three layers) */
.origin-mid { opacity: 0; text-align: center; }
.origin-claim {
  display: flex; flex-direction: column;
  align-items: center; justify-content: flex-start;
  gap: var(--space-lg);
}
.origin-tag { opacity: .65; }   /* 08.08 (origin-lab A): the section names itself — deck "( … )" grammar; type comes from .label */
.origin-lead { margin-top: 0; }
.origin-body { margin-top: 0; margin-bottom: 0; min-height: 0; align-content: start; }   /* 14.08 v2 padding-bottom 3xl retired 18.08 — the stage owns the height now */
.origin-body { opacity: 0; pointer-events: none; }             /* JS drives opacity; the hidden layer must not catch the CTA click */

/* THE INVERT (02.09, Chiara: "the third and fourth images that scroll over 'Strategy Led,
   Design Obsessed' need to have the effect on them — when the image passes, the text inverts
   the colour of the text to the opposite of the photo").

   mix-blend-mode: difference computes |backdrop − source| per pixel, so with a WHITE source the
   text renders as the exact photographic negative of whatever sits behind it: dark over a light
   frame, light over a dark one, and it tracks the image as it drifts. No JS, no overlap test, no
   per-image wiring — which is why it self-selects to floats 3 and 4: they are simply the two that
   pass this paragraph.

   Why it can blend at all: .origin-floats sits at z-index 0 and .origin-body at z-index 1 in the
   same stacking context, so the photos are genuinely this element's backdrop. It needs opacity to
   reach exactly 1 (anything less isolates the element and the blend dies against transparency) —
   main.js seat() holds xfBody at 1 and never dissolves it, so that holds.

   THE ONE TRADE: difference needs a white source to invert properly, so on the plain ground the
   credential no longer paints in --ink (#16100D, warm) but in its mathematical inverse of the
   beige, ~#040B1B — a touch darker and cooler. It also now follows the ground descent for free.
   If that coolness reads wrong, the alternative is the .origin-claim--over treatment: a clipped
   twin registered to each image box, which keeps the brand colours exactly but needs JS per image. */
.origin-body p:not(.label) {
  mix-blend-mode: normal;   /* 21.10 (Chiara: "why is it blue?"): the 28.08 difference blend RETIRED — on the beige ground it painted the paragraph in the negative of beige, a cold navy, and over the warm frames a blue. The clipped twins (.origin-body--over, cream) carry the inversion instead. */
  color: inherit;
}
.origin-body .origin-cta { mix-blend-mode: normal; }   /* the CTA keeps its own colour — inverting a link reads as a bug, not an effect */
@media (prefers-reduced-motion: reduce) { .origin-body p:not(.label) { mix-blend-mode: normal; color: inherit; } }
.origin.xf-body .origin-body { pointer-events: auto; }
.origin.xf-body .origin-claim { pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  /* no scrub → no pin: the two beats simply stack, both full ink */
  .origin { min-height: 0; }
  .origin-body { color: inherit; }   /* 29.08: the static stack sits on the beige — cream would vanish */
  .origin-stage { position: static; height: auto; display: flex; flex-direction: column; align-items: center; gap: var(--space-2xl); padding-bottom: var(--space-3xl); }
  .origin-body, .origin-mid { opacity: 1; pointer-events: auto; }
}
/* Quiet text links — the one wayfinding style. 14.07 v3 (Chiara: "remove the
   underline from the CTA and it needs the same animation as the lets talk
   cta"): mono register, NO underline — the whole-word roll (same .roll
   mechanic as Let's Talk) is the only affordance. */
.quiet-link {
  display: block;
  width: fit-content;
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: var(--type-label);
  text-transform: uppercase;
  letter-spacing: normal;
}
.interstitial .quiet-link { margin: 0 auto; }
/* (origin-services disciplines caption — tried + removed same hour 14.07;
   the "we haven't said what we do" gap stays OPEN for the wireframe pass) */
.origin-cta {
  /* 14.07 (Chiara's pick): QUIET-LINK serif — .quiet-link carries the type
     (Eiko serif-sm, soft underline); this rule keeps only the entrance fade
     and the seat. The 03.07 caps letter-roll treatment is retired here. */
  /* opacity 0 → 1 fade RETIRED 18.08 (Chiara: "it should animate in with the ripple effect it has on hover — in both instances"): the fade was masking the letter-roll — the letters now shutter up from under the rule in the clear, staggered left → right, exactly the hover ripple */
  transition: border-color var(--dur-quick) var(--ease-house);
  justify-self: center;   /* 13.08 v4 (Chiara: "Start a project should be in the centre"): start → center — the left seat belonged to the Monument cluster; the centred stack centres its CTA */
  margin-top: var(--space-cta);
  color: inherit;   /* 18.08 v3: follows the section's ink → cream drift (was var(--ink)) */
  /* 08.08 (Chiara: "start a project needs underline"): the soft rule returns —
     SCOPED here; Let's Talk and the other quiet-links stay bare. Supersedes the
     14.07 v3 "no underline, the roll is the only affordance" ruling for this
     seat only. The letter-roll continues above the rule unaffected. */
  border-bottom: 1px solid color-mix(in srgb, currentColor 35%, transparent);   /* 13.08 v6 (Chiara: "underline is very thick"): full-ink rule → the house soft rule (35%, same as the menu-foot links) — the hairline reads finer at the mono's weight */
  padding-bottom: 3px;
}
/* entrance: letters start below the clip and roll up, staggered, on ready */
.origin-cta:not(.ready) .ch { transform: translateY(105%); transition: none; }
.origin-cta.ready .ch { transform: translateY(0); }
.origin-cta.ready:hover .ch { transform: translateY(-100%); }  /* hover outranks the entrance state (03.07) */

/* Left-aligned at the gutter (02.07): label, then the cost line directly
   beneath it — no centring container */
.origin-cost { margin-bottom: var(--space-2xl); }
/* 08.08 (origin-lab direction A): the MONUMENT claim — Eiko at its own display
   rung, natural rag (text-wrap:balance dropped — the approved lab render wraps
   "You've built / something the world / should know about." on the raw flow).
   The 14.07 mirror-era spec (60px rung, 720px box, logo-seat alignment) is
   fully superseded; the font-synthesis italic guard went with the <em> device. */
.origin-lead {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-origin);   /* 28.08 v3 (Chiara: "46px" — the hero tagline's rung, so the opening's three statements share ONE size) · earlier that day: phase → cta (36–57) · 18.08 v12 (Chiara: "should we choose a consistent font size? is it weird the first statement is bigger?" — yes, one voice): the claim joins the answer + credential on the phase rung; its presence is the solo seat + the hold, not the point size. --type-monument (88) is now unseated — retire on the next audit unless it finds a home. Alt ruled-out: all three on --type-display (80). */
  line-height: var(--lh-intro);   /* 29.08: opened with the size step down */   /* 13.08 v2 (lab match): 1.04 → 1.12 — the lab claim breathes more; the tight Monument leading went with the left-anchored composition */
  letter-spacing: -0.02em;
  max-width: 33ch;      /* 29.08 v3 (Chiara: "Luxury Travel Advisors. Hospitality Brands. — one line"): 30 → 33ch, the least that holds the 43-char fragment line; the sentence lines are PINNED with <br>s to the 30ch rag so the block reads the credential's width · v2 (Chiara: the credential's width "is perfect — can the first one match it"): back to 30ch, the credential's measure — ONE measure for both beats; the fragments take a line each · 29.08: 38ch TRIED for the one-line Title Case fragments, superseded · 13.08 v6 (Chiara: "on 2 lines"): opened for the hard-break wrap — the <br> owns the line turn now, not the measure */
  text-align: center;   /* 13.08 v2 (lab match): the Monument's left rag retires — every line centres, per the ruled lab render */
  margin-bottom: 0;
}
/* 29.08 (Chiara's 10.49 screenshot: "when the text changes it goes to the same colour
   as the background so you can't read it… the font colour is always the opposite to
   the background"): THE COLOUR SPLIT. The section rule above drifts the type ink →
   cream on the SAME --xf dial as the ground's beige → taupe, so halfway both sit at
   mid-tone and the leaving claim disappears. The two layers never share a ground:
   the claim exits over beige → taupe (ink holds on taupe), the credential arrives
   over taupe → chocolate (cream holds on both). So each layer takes ONE colour and
   nothing crosses mid-fade. The over-image twins stay cream (their own rule). */
.origin-claim { color: var(--statement-ink); }   /* 12.10: → --statement-ink #211C19 (Chiara gave the hex) */   /* 05.10 (Chiara: "be darker, the same as the background of the menu"): --title-ink → --espresso (#150F0D) */   /* 10.09: the claim rests in the taupe on the light grounds */
.origin.type-cream .origin-claim { color: var(--cream); }   /* …and cream from the dark beat on (the 09.09 flip, stated explicitly now the claim carries its own colour) */
.origin.type-cream .origin-body p:not(.label) { color: color-mix(in srgb, var(--cream) 78%, var(--ground-3)); }   /* 10.09 (Chiara: the credential "needs to be a few shades darker"): on the dark beat the Strategy-led line rests a few shades under full cream — the claim above it keeps the cream, so the two read as lead and support */
.origin-mid { color: var(--cream); }   /* 01.09 (the three-beat restore): the answer arrives on the taupe — cream, like the credential; the claim alone stays ink on the beige */
.origin-body { color: var(--cream); }
.origin-body { display: grid; gap: var(--space-lg); justify-items: center; }   /* 13.08 (Vero port): answer, credential and CTA centre under the claim — gap lg, the lab's stack step */
@media (min-width: 900px) {
  /* 08.08 v2 (origin-lab A ruled): the rail era is over — no centred 1360/1100
     rail, no logo-seat lead margin. The claim anchors the gutter. */
  /* 08.08 v4 (Chiara: "way too much gap between text on left and right"): the
     cluster leaves the far-right gutter — her 680px box restored. */
  /* 08.08 v5 (Chiara: "use the golden alignment"): the cluster's left edge sits
     at the GOLDEN SECTION of the content width (1/φ = 61.8%) — between the
     50%-line and right-docked extremes she tried; the claim holds the major
     interval, the response the minor. */
  /* .origin-body { max-width: 680px } RETIRED 18.08 — the answer now sits on the monument rung and the 680 box was folding it to five lines; each item carries its own measure (answer 30ch, credential 44ch, CTA fit-content). */
  .origin-body { margin-inline: auto; }
}
.origin-cost p:first-child {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.45;
  max-width: 44ch;    /* same optical measure as the For/Not-for columns */
}
/* 08.08 (origin-lab A): the response cluster leaves the serif register — the
   split IS the design: Eiko speaks only the claim, Staff Grotesk answers.
   Supersedes the shared serif-md rule above for .origin-body p and the 13.07
   680px box (the cluster caps at 400px in the desktop MQ instead). */
.origin-body p {
  /* 18.08 (Chiara: "should this just be all Eiko now" — yes): the credential
     joins the serif register — claim, answer, credential are ONE voice in one
     seat (the MoF/Vero belief grammar); the 08.08 sans "response cluster"
     split belonged to the off-axis composition. Small feature-serif rung, not
     body 16 — the credential must not read as serif body copy. Revert: swap
     the family/size back to --font-body / --type-body. */
  font-family: var(--font-serif);
  font-weight: 300;
  /* v2 same hour (Chiara: "as in all the same font size"): the credential
     takes the ANSWER's rung, leading, tracking and measure — the stack is one
     continuous statement in one size, not a headline with a caption. */
  font-size: var(--type-origin);   /* 28.08 v3 (Chiara: "46px"): the credential on the hero tagline's rung — was var(--type-phase) */
  line-height: var(--lh-intro);
  letter-spacing: -0.02em;
  opacity: 1;
  max-width: 30ch;
}
.origin-answer {   /* 18.08 v7: lives in .origin-claim now (was .origin-body) */
  font-family: var(--font-serif);   /* 08.08 v2 (Chiara: "make this eiko font") — the answer joins the claim's register; only the credential + CTA stay sans/mono. Supersedes the same-day full register split. */
  /* 18.08 (Chiara: "this maybe needs to be bigger now"): the answer takes the
     CLAIM's seat in the crossfade, so it takes the claim's rung — peers in one
     seat. Arc: md → lg (13.08 v11) → monument ("too big") → phase: the rung
     between the old lg (44) and the claim (88) — the claim's RESPONSE, not its
     twin. Same leading/tracking/measure as .origin-lead. */
  font-size: var(--type-origin);   /* 01.09 (Chiara: "make sure all 3 use it"): the answer joins the claim and the credential on the origin trio's shared rung — three beats, ONE size, which is the 18.08 v12 ruling ("is it weird the first statement is bigger?" — yes). Was serif-lg (44), from the 28.08 two-beat layout where the answer sat UNDER the claim on the same screen · v11 (Chiara: "this should be the same size as [the credential]"): answer + credential shared the phase rung (64) — the claim alone holds the monument. Arc: lg → monument → phase → monument (v8, paired beat) → phase. */
  line-height: var(--lh-intro);
  letter-spacing: -0.02em;
  max-width: 30ch;
  opacity: 1;
}

/* ============================================================
   FOOTER — giant wordmark (Colour Transition frame: full width
   minus gutters, legal line + three links below)
   ============================================================ */
body.home .site-footer .footer-video { --footer-bright: 0.68; }   /* 25.10: the Caruso terrace (homepage footer) is paler than the films the 0.55 default was set for — 0.68 on desktop; phones darken it further in the ≤900 block */
body.home { background: var(--ground-3); }   /* 25.10 (Chiara, phone: "why is there a white band when you reveal the footer?"): the page under <main> was cream — wherever Safari showed it (the fixed footer catching up as its bars slide away, the bounce past the bottom or top) it read as a white band. <main> carries its own paper, so the canvas beneath can be the site dark: the hero above and the footer below are both dark, and every gap now matches them */
.above-footer {
  position: relative; z-index: 1; background: var(--paper);
  /* Soft reveal edge (04.07, Chiara: "the fade comes in immediately — never
     a harsh line over the film"). The page's own bottom edge dissolves via
     mask, so the very first pixel of the footer reveal is already soft —
     v1 was a skirt hanging below the edge, which kept the seam solid until
     ~20vh of footer was exposed. 16vh depth stays inside the enquire
     section's bottom padding; deepen with care or the mantra starts fading. */
  /* 03.09 (Chiara: "the reveal needs to be asymmetric and more of a wash — a harsh line, too PowerPoint,
     needs to feel wafty"): two changes to the same mask. (1) EASED — the old ramp was a single linear
     stop, so the dissolve began with a kink, and a kink across a full-width edge reads as a rule. Now it
     starts slow and breathes out. (2) The ASYMMETRY lives in .footer-scrim, not here — a tilted mask was
     tried first and pulled: an angled ramp cannot reach zero along a rectangular bottom edge, so one side
     of the page stayed ~20% painted and drew a NEW line. A mask must end at exactly 0 on its edge. */
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 36vh), rgba(0,0,0,0.90) calc(100% - 30vh), rgba(0,0,0,0.68) calc(100% - 22vh), rgba(0,0,0,0.42) calc(100% - 14vh), rgba(0,0,0,0.18) calc(100% - 6vh), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 36vh), rgba(0,0,0,0.90) calc(100% - 30vh), rgba(0,0,0,0.68) calc(100% - 22vh), rgba(0,0,0,0.42) calc(100% - 14vh), rgba(0,0,0,0.18) calc(100% - 6vh), transparent);   /* 24.08 v2 (Chiara: "it needs to wash up more"): 24vh → 36vh — the film now ghosts a third of a viewport up into the page's edge. 04.07's caution is live at this depth: if the mantra's last line starts fading at the release position, this is the value to pull back. */
}

/* 01.09 — ONE token for the stage height, used by BOTH the spacer and the fixed
   stage. They must stay equal or the reveal lands in the wrong place, and they were
   two independent 86vh literals waiting to drift apart. Raised to a full screen now
   that the ask lives on the film (the stage carries statement + CTA + mark + links +
   legal), and moved off vh onto svh — the W5 finding: as mobile Safari's URL bar
   collapses, a vh stage and a vh spacer drift apart and the reveal seam moves. */
:root { --footer-h: 100dvh; }   /* 25.10 v2: dvh, not lvh — lvh put the footer’s legal links behind Safari’s bottom bar while it showed; dvh is always the visible screen. v1 (Chiara, phone: "why is there a cream gap at the top here?"): svh is the screen WITH Safari’s bars — once they collapse the screen grows and the fixed footer stopped short, leaving the cream page ground in a band above it. lvh is the full screen; identical on desktop */
.footer-spacer { height: var(--footer-h); }
.site-footer--reveal {
  position: fixed;
  inset: auto 0 0 0;
  height: var(--footer-h);
  z-index: 0;
  background: var(--ground-3);   /* 03.09: was --paper. With a dark film the stage should fail DARK — a cream flash before the video paints is now the jarring state, not the safe one. The seam with the page above is the scrim's job, not this. */
  color: var(--cream);
}
.footer-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  filter: brightness(var(--footer-bright, 0.55)) contrast(1.12) saturate(1.06);   /* 03.09 (Chiara: "the video needs a consistent overlay — what did you do on Palmer? do the same"): ONE grade for every footer film, 0.55, the value Palmer settled on. The per-film tuning below is retired — the films are chosen to sit at this level rather than the level tuned per film. */
  /* 03.09: the darkening is a TOKEN now, because the right value depends on the film. Measured
     mean luminance: lounge-39 = 64.9, lounge-37 = 52.9, so 37 is ~18% darker before any grade.
     Reusing 39's 0.58 on it would have crushed the lamp-lit mids. Both are tuned to land at the
     same effective level (~38/255), which is where the frosted cream wordmark reads cleanly:
        lounge-37 (live) -> 0.71
        lounge-39        -> 0.58
        mountain-dusk    -> no brightness at all; it was already dark
     Swap the film, set --footer-bright to match. */
}
.footer-scrim {
  position: absolute; inset: 0;
  --melt: var(--paper);   /* 03.09: the colour the film washes UP into. It has to match whatever section sits above the footer or a hard seam appears, and that differs per page — so it is a variable now, not a hardcoded --paper. */
  /* dock.cool blend (03.07, Chiara): the drone film's sky melts UP into
     the cream page above — no hard top edge on the footage. A whisper of
     shadow at the base keeps the legal line + links legible.
     05.07 (Chiara): fade refined — eased multi-stop melt (was a single
     linear ramp ending hard at 38%, which showed a faint band edge). */
  /* 14.07 (Chiara: "the line on the video reveal needs to be blended"): the
     melt now fades to --paper, matching the page edge above — it was still
     on --cream (near-white) from before the 10.07 palette settle, and that
     half-step tonal mismatch WAS the visible line. */
  /* 09.08 (Chiara: "the reveal needs a less harsh line"): the melt lengthened
     + re-eased for the bone ground — the film's warm sky against the cooler
     #F5F5ED made the old 86%-release band visible. More stops, gentler
     mid-section, and the tail now breathes out to 97% instead of releasing
     at 86% — no perceptible edge at any reveal position. */
  /* 03.09 ("make this dark"): the melt now RELEASES BY 44% instead of 68%. The long ramp was
     built to hide a seam against a dark plate; against the bright lounge interior it was veiling
     the top half of the frame in cream. The seam still cannot show — the first 6% is solid paper
     and the steps are gentle — it simply stops holding on. The base shadow deepens 0.5 -> 0.68
     and reaches further up (34% -> 52%) so the legal line, links and wordmark have real ground
     under them now the film beneath them is warm rather than near-black. */
  background:
    /* 03.09 ("asymmetric … wafty"): an off-centre wash of the melt colour, heaviest top-left, on top of the
       level ramp below. The two edges no longer coincide anywhere across the width, so there is no single
       height at which the film "starts" — it clears on the right first and lingers up the left. */
    radial-gradient(110% 62% at 18% -6%, var(--melt) 0%, color-mix(in srgb, var(--melt) 55%, transparent) 38%, color-mix(in srgb, var(--melt) 18%, transparent) 62%, transparent 80%),
    linear-gradient(to bottom,
      var(--melt) 0%,
      var(--melt) 6%,
      color-mix(in srgb, var(--melt) 82%, transparent) 13%,
      color-mix(in srgb, var(--melt) 58%, transparent) 20%,
      color-mix(in srgb, var(--melt) 36%, transparent) 27%,
      color-mix(in srgb, var(--melt) 18%, transparent) 34%,
      color-mix(in srgb, var(--melt) 6%, transparent) 40%,
      transparent 44%),
    /* 03.09 (Chiara: "there is a darker overlay on the bottom of the footer video than the middle — can it
       just be a flat overlay, not tapered"): THE BASE SHADOW IS GONE. It was linear-gradient(to top,
       rgba(0,0,0,0.68), transparent 52%) — black at 68% on the bottom edge easing out by half the height,
       there since 03.07 to hold the legal line and links. The frame now carries only the flat wash below
       (and the per-page brightness grade), so the film is one level from the melt down to the edge. If the
       legal line needs help again, raise the flat wash — not a gradient. */
    /* 03.09 (Chiara: "the overlay on the video is black 15% so the text is more legible"): a flat wash
       across the whole frame, under the melt and the base shadow. Those two are directional and leave
       the middle of the film — exactly where the ask now sits — unprotected. */
    linear-gradient(rgba(0, 0, 0, var(--footer-wash, 0.15)), rgba(0, 0, 0, var(--footer-wash, 0.15)));   /* 03.09: the wash is a token now — Odisi runs it at 0 (Chiara: "remove the dark overlay completely on the odisi one"), set inline on the page's .footer-scrim alongside --footer-bright: 1 on its video */
  /* 24.08 v4 (Chiara: "the video just doesn't work — the colours are off"): the footage
     itself is warm and on-palette (orange-peach sky, plum ridges — verified from frames);
     the OFF colour was the melt still veiling the mountains with 30–45% cream at mid-frame,
     washing them pink-grey. The ramp is now long-and-gentle in the sky (no band edge — max
     step ~18% per 8% of frame) but fully released by 68%: the entire mountain band runs at
     native grade + the contrast lift. History: v2 released at 56% w/ steep steps (band edge),
     v3 at 86% (mountains veiled), 09.08 original at 97% (whole film veiled). */
  /* 24.08 v2 (Chiara: "the footer video looks too faded… the tone is odd — same colour
     density as the opening video"): the WASH was the paper melt reaching 97% — the whole
     film sat behind a paper veil — plus the same-hour flat black veil, which greyed the
     warm footage (both retired). The melt now releases by 56%: the top seam stays soft,
     the lower film runs at native density. Base shadow (0.5/34%) stays for the small
     print. The film itself gets a light contrast lift (.footer-video filter) to sit at
     the hero's density. */
}
.footer-stage {
  position: relative;
  height: 100%;
  overflow: hidden;
  /* 03.07 (Chiara): mark ABOVE the policy links/legal — the small print
     closes the page at the base, inside the frame margin */
  padding: var(--gutter);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
}
.footer-wordmark {
  width: calc(100vw - (var(--gutter) * 2));
  /* 09.08 (Chiara): the hero mark's frosted effect, ported — the site's
     frosted-on-imagery rule (58% cream, same value as .hero-wordmark and
     .cs-intro-lead), mountain footage glowing through the letters. */
  background-color: var(--frost);
  /* 03.09 (Chiara: "it needs a mask on it and frosted, like we have used on the hero image"):
     back to THE HOUSE FROSTED RULE — cream at 58%, the same value as .hero-wordmark, .cs-intro-lead
     and the deck titles. The letters are a cream VEIL the full-bleed film glows through, not a
     window filled with film. The video-inside-the-letterforms experiment is retired with this: at a
     12:1 lockup it only ever showed an 11% slice of the frame, which is why it read as odd.
     The local scrim below stays — at 58% the letters are genuinely translucent, and this film is
     busier behind the mark than the hero's is. */
}/* 03.09: THE MARK'S OWN GROUND. A soft LOCAL scrim travelling with the mark, so 58% frosted cream
   holds against the film's busiest band without darkening the whole lower frame to achieve it. */
.footer-wordmark-link::before {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  width: 130vw; height: 260%;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.46) 0%, rgba(0, 0, 0, 0.28) 45%, transparent 72%);
  pointer-events: none;
  z-index: -1;
}
.footer-wordmark-link {
  width: calc(100vw - (var(--gutter) * 2));
  display: block;
  position: relative; z-index: 2;   /* 03.07 fix: it was painting UNDER the scrim — the wordmark must be the brightest thing on the stage */
  order: 1;                         /* the mark leads; links + legal close the page beneath it */
  margin-bottom: var(--space-md);
}
.footer-wordmark-link .footer-wordmark { width: 100%; transition: opacity var(--dur-quick) var(--ease-house); }
.footer-wordmark-link:hover .footer-wordmark { opacity: 0.85; }
.footer-mail {
  position: absolute;
  top: calc(var(--gutter) + 44px); left: var(--gutter);   /* 20.10: a right-align of this mail was applied and REVERTED within the hour (Chiara: "i wasnt talking about the footer — put that back") — the mail keeps its top-left seat pairing the socials top-right. */
  text-transform: lowercase; letter-spacing: normal;   /* 15.07 (Chiara: "can the email not have weird spacing"): the 0.04em pre-mono tracking gapped the mono out */
  /* 25.09 (Chiara: "remove the underline on my email in the footer"): the 45% cream rule + its hover-to-solid RETIRED — the address sits bare in the mono register like the socials beside it. Was: border-bottom 1px color-mix(cream 45%), padding-bottom 3px, hover → solid cream. */
}
.footer-socials {
  position: absolute;
  top: calc(var(--gutter) + 44px); right: var(--gutter);
  display: flex; gap: var(--space-md);
}
.footer-socials a { display: inline-flex; align-items: center; gap: var(--space-xxs); transition: opacity var(--dur-quick) var(--ease-house); }   /* 12.10 (Chiara: "in the footer too, can Instagram and LinkedIn have the arrows"): the menu's 45° arrow, same glyph, label rung */
.footer-socials .menu-foot-arrow { width: 12px; height: 12px; }
.footer-socials a:hover { opacity: 0.7; }
/* Footer content rows — in flow above the wordmark (03.07, was corner-
   anchored absolute): links row, then the legal line, then the mark. */
.footer-legal {
  order: 3;                            /* the last line on the page */
  position: relative; z-index: 2;      /* above the scrim, like the wordmark */
  text-align: center;
  white-space: nowrap;
  opacity: 0.75;
  font-size: 10px;                     /* smaller than the label step (03.07) */
  letter-spacing: var(--track-label);  /* 03.07 audit: joins the one caps tracking */
}
.footer-links {
  order: 2;
  position: relative; z-index: 2;      /* above the scrim, like the wordmark */
  width: 100%;
  margin-bottom: 0;   /* 03.09 (Chiara: "shift the footer and terms / privacy / cookie to the bottom, with a little bit of padding, not too much"): the 16px that held the legal line off the links is gone with the legal line; the links now sit on the stage's own 32px frame padding — the same inset as the corner labels at the top. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
}
.footer-links a { font-size: 11px; }   /* 03.09 (Chiara: "reduced by 5px, across all footers" → 13px measured, so 8px; then "bump privacy footer text by 3px, all of them" → 11px, one under the old 12px legal-line register). Below the label rung on purpose — the small print closing the page, where the 10px legal line used to sit. */
.footer-links a:nth-child(2) { justify-self: center; }
.footer-links a:nth-child(3) { justify-self: end; }

/* ============================================================
   BELOW THE INTRO — not built yet (scoped: intro only)
   ============================================================ */
.unbuilt { display: none; }

/* ============================================================
   SECTIONS BELOW THE FOLD — bones build 02.07. Faithful to the
   token system; Alex's treatments (colour transitions, scroll
   choreography) layer on in the design pass.
   ============================================================ */

/* Placeholder media — tinted block until the real asset lands */
.ph-media {
  background: linear-gradient(160deg, color-mix(in srgb, var(--green) 18%, var(--cream)), color-mix(in srgb, var(--sage) 60%, var(--cream)));
}

/* Origin — cost of the gap, signed */
.origin-cost { max-width: 44ch; margin-bottom: var(--space-2xl); }
.origin-cost p:first-child {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.45;
}
.origin-sign { opacity: 0.65; margin-top: var(--space-sm); }

/* Clients — quiet recognition row (text placeholders until SVGs land) */
/* 01.09 (W5): the first .clients + .client-logos declarations deleted — .client-logos has zero markup in any page in this repo, and this .clients padding was already being overridden by the later duplicate at equal specificity, so the gutter never applied. The live wall rules are the marquee block below and the ground gradient near the foot of this file. */
.clients .section-label { opacity: 0.78; margin-bottom: var(--space-xl); }

/* Real client logos (02.07) — every mark masked to the one ink, so mixed
   brand colours arrive uniform. Height fixed, width from each aspect-ratio. */
.client-logo {
  display: block;
  height: 28px;   /* all five identical, pixel-measured (02.07) */
  background-color: var(--ink);
  /* mask-image is set inline per logo — a url() inside a CSS variable
     resolves against the stylesheet in Safari and 404s (02.07 bug) */
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-position: center;
  -webkit-mask-size: contain;
  mask-repeat: no-repeat;
  mask-position: center;
  mask-size: contain;
}

/* Declaration — the trademark, at volume */
.declaration { padding: var(--space-section) var(--gutter); text-align: center; }
.declaration p:first-child {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-display);
  line-height: 1.05;
  color: var(--ink);
}
.declaration sup { font-size: 0.25em; vertical-align: super; }
.declaration-sign { display: block; margin-top: var(--space-md); opacity: 0.65; }
.declaration-sub {
  max-width: 52ch;
  margin: var(--space-lg) auto 0;
  font-size: var(--type-body);
}

/* Who this is for — two quiet columns */
.for-whom {
  /* 14.08 (method reveal-from-under): the For/Not sheet is the LIFTING COVER —
     solid paper ground + a stacking seat above the pinned Method card beneath */
  position: relative; z-index: 2; background: var(--paper);
  /* 02.07: consistent margins all the way down — gutter-anchored like every
     other section; extra air below the beat */
  /* 14.07 v3 (Chiara: "spacing is off on the right margin"): the right inset
     now MIRRORS the left — both hang from the wordmark anchor (gutter +
     burger 34 + cluster gap 24). The v2 "Let's Talk"-derived right edge
     (gutter + 112px) read as a hole on the right. */
  padding: calc(var(--space-section) * 1.5) calc(var(--gutter) + 34px + var(--space-md)) calc(var(--space-section) * 2.25) calc(var(--gutter) + 34px + var(--space-md));
  display: grid;
  /* 03.07: even halves (the edge-anchored version read as a hole in the
     middle — reverted same day). The 44ch caps on the paragraphs below
     still protect the measure on very wide screens. */
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2xl);
}
.for-col .section-label { opacity: 0.65; margin-bottom: var(--space-md); display: block; }
.for-col p:last-child {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.45;
  max-width: calc(44ch - 50px);   /* 14.07 (Chiara: "reduce by 50px") — the 03.07 44ch measure, pulled in; still scales with the type */
}

/* Selected Work — pinned horizontal gallery (Lando mechanic).
   Vertical scroll drives .work-track sideways (JS, smoothed); cards
   alternate upper/lower at three weights per the Colour Transition frame. */
.work-pin { height: 860vh; position: relative; }  /* nine case studies, three quotes (02.07); +100vh = the read-Palmer-first hold (10.07 Chiara) */
.work-sticky {
  position: sticky; top: 0;
  height: 100vh; overflow: hidden;
  display: flex; align-items: center;
}
.work-label {
  position: absolute;
  /* 03.07 (Chiara): the section takes the header's own line — label sits
     exactly where the burger lives, CTA exactly where the header CTA
     lives; the header chrome clears inside the pin.
     05.07 (Chiara): the +3px optical nudge dropped — the section line now
     sits flush at --gutter, matching the nav's top-aligned header. */
  top: var(--gutter);
  left: var(--gutter);
  opacity: 0.78;
}
/* Section-owned CTA (03.07) — the corner pattern replicated: label left,
   Start a Project right, ON THE HEADER'S OWN LINE (top: gutter — where
   the burger/CTA sit). The header chrome clears inside the pin, and the
   section takes over its exact positions. */
.work-cta {
  position: absolute;
  top: var(--gutter);   /* matches .work-label — flush at the gutter (05.07: +3px nudge dropped) */
  right: var(--gutter);
  z-index: 5;
  /* 03.07 (Chiara): arrives as the mark drops in, with the SAME letter-roll
     entrance as the header CTA on page load — the clip hides the letters
     until .ready (JS, ~250ms after pin entry), then they roll up staggered.
     Re-arms on every entry so the reveal is always seen. */
  pointer-events: none;
}
.work-pin.pin-active .work-cta { pointer-events: auto; }
.work-cta:not(.ready) .ch,
.method-cta:not(.ready) .ch { transform: translateY(105%); transition: none; }
.work-cta.ready .ch,
.method-cta.ready .ch { transform: translateY(0); }
.work-cta.ready:hover .ch,
.method-cta.ready:hover .ch { transform: translateY(-100%); }  /* hover outranks the entrance state */
.work-track {
  display: flex;
  align-items: center;
  gap: clamp(90px, 9vw, 180px);   /* 02.07: tightened — imagery closer */
  padding: 0 var(--gutter);
  will-change: transform;
}
.work-item { flex: 0 0 auto; }
.work-item a { display: flex; flex-direction: column; gap: var(--space-sm); }
.work-item--cap-above a { flex-direction: column-reverse; }
/* 02.07: the name+details lockup always anchors to the image edge — on
   caption-above tiles the caption's internal order mirrors (tagline on top,
   lockup nearest the image) */
.work-item--cap-above .work-caption { display: flex; flex-direction: column-reverse; }
.work-item--cap-above .work-caption .label { margin-bottom: 0; margin-top: var(--space-md); }

.work-item--sm .work-media { width: clamp(260px, 20vw, 390px); aspect-ratio: 4 / 3; }
.work-item--md .work-media { width: clamp(300px, 23vw, 440px); aspect-ratio: 1 / 1; }
.work-item--lg .work-media {
  /* 03.07 (Chiara): budget opened up — the nav clears entirely inside the
     pin, so only the section label needs air. 82vh height budget with an
     HONEST caption reserve: the serif change-line wraps to two lines at
     desktop sizes (~210px with the lockup), the old 130px was measured
     before the tagline was stepped up and let cap-above tiles overflow
     the frame bottom. */
  width: min(clamp(480px, 46vw, 900px), calc((86vh - 210px) / 0.96));   /* 03.07: label moved to the header line — tiles claim the freed headroom (fits 800px-tall laptops with the 100px clearance) */
  aspect-ratio: 1 / 0.96;
}
/* Pixel-perfect pass (03.07): every lg caption reserves the SAME height
   (the 210px budget minus the 16px lockup gap), so one-line change-lines
   can't float their card higher than two-line ones — all tiles share
   identical bounds and the equal-air formula holds for every card. */
.work-item--lg .work-caption { min-height: 194px; }

/* Large tiles alternate high/low; quotes sit centre-line between them.
   Symmetric buffers (02.07): same air top and bottom. */
/* 03.07 (Chiara): EQUAL AIR — the gap from the label line down to a
   tile's top edge equals the gap from its bottom edge to the viewport
   base, at every screen size. Derivation: tiles total 86vh (caption 210
   + media budget), label line bottom ≈ 58px (top gutter 40 + 12px caps),
   so gap G = (100vh − 86vh − 58px) / 2 = 7vh − 29px.
   up-tiles:   top margin = 58px + G = 7vh + 29px
   down-tiles: bottom margin = G      = 7vh − 29px  */
.work-item--up { align-self: flex-start; margin-top: calc(7vh + 29px); }
.work-item--down { align-self: flex-end; margin-bottom: calc(7vh - 29px); }

/* Caption hierarchy (02.07): client name = exactly the Start a Project spec
   (12.6px · Regular · 0.09em · caps, full ink); disciplines step back in a
   muted ink; the serif change-line follows with clear air. */
.work-caption .label {
  display: block;
  color: color-mix(in srgb, var(--ink) 76%, transparent);  /* contrast floor 02.07 */
  margin-bottom: var(--space-xs);
}
.work-caption .label { line-height: 1.3; }
.work-caption .label br { display: none; }  /* 02.07 fix: the block <strong> already breaks the line — the <br> was adding a phantom empty row */
.work-caption .label { margin-bottom: var(--space-md); }  /* tagline steps further down (02.07) */

/* Media swap (Zambala test, 02.07): video with the focus held left;
   hovering crossfades to the still (the bag shot) */
.work-media--swap { position: relative; overflow: hidden; }
.work-media--swap video,
.work-media--swap .swap-base,
.work-media--swap .swap-hover {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
/* Per-tile focal points (02.07): each client's footage crops differently */
.work-media--focus-left video {
  width: 200%;                 /* Zambala: the leftmost slice, magnified (02.07) */
  left: 0; right: auto;
  object-position: left center;
}
.work-media--focus-topright video {
  object-position: right top;  /* Odisi: focus on the top right */
}
.work-media--focus-topright .swap-base {
  object-position: right center;  /* 02.07: wing-view photo, focal point right */
}
.work-media--swap .swap-hover { opacity: 0; transition: opacity var(--dur-base) var(--ease-house); object-position: center 78%; /* focus low in the frame, on the bag (02.07) */ }
/* Scrim over the tile video so the client logo reads (02.07);
   lifts away on hover with the footage */
.work-media--swap::after {
  content: '';
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.28);   /* 03.07: tile grain undone — back to the plain scrim; the site-wide whisper grain still covers everything */
  z-index: 1;
  transition: opacity var(--dur-base) var(--ease-house);
}
.work-media--swap .swap-hover { z-index: 1; }
/* 10.07 (Chiara): no scrim on tiles whose photo carries its own wordmark
   (Palmer umbrellas) — the darkening exists only to float cream logos */
.work-media--noscrim::after { content: none; }
.work-item a:hover .work-media--swap::after { opacity: 0; }
.work-item a:hover .swap-hover { opacity: 1; }

/* Client logomark over the tile video (Zambala 02.07) — centred, cream,
   fades away on hover so the bag shot reads clean */
.work-media-logo {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  opacity: 0;                              /* fades in as the tile arrives (02.07) */
  transition: opacity var(--dur-slow) var(--ease-house) 0.15s;
  width: clamp(154px, 17vw, 277px);   /* 02.07: −5% again */
  height: auto;
  z-index: 2;
  filter: brightness(0) invert(1) opacity(0.95);  /* renders the mark cream-white over footage */
  transition: opacity var(--dur-base) var(--ease-house);
}
.work-media-logo.in-view { opacity: 1; }
.work-item a:hover .work-media-logo.in-view { opacity: 0; transition-delay: 0s; }  /* hover still clears it */
.work-media-logo--brand {
  filter: none;   /* Odisi ships its own Temple Stone colour — no cream override */
  width: clamp(116px, 11.9vw, 192px);   /* 03.07: +5% back */
}
.work-caption .label strong {
  display: block;
  color: var(--ink);
  font-weight: 400;   /* 03.07: the title runs Regular against the Thin disciplines line */
  font-size: var(--type-label);
  margin-bottom: 0;                 /* brackets sit directly beneath the name (02.07) */
}
.work-change {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);   /* 02.07: stepped up (was serif-sm) — the tagline carries the card */
  line-height: 1.3;
  max-width: 24ch;
  margin-bottom: var(--space-xxs);
}
/* metric removed 02.07 (Chiara) — hard numbers live on the case-study pages */

.work-item--quote { max-width: 52ch; }  /* wider text box (02.07) */
.work-item--quote blockquote {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.45;    /* matched to the For-section serif (02.07) — one shared style */
  margin-bottom: var(--space-md);
}
.work-quote-name { opacity: 0.78; }

@media (max-width: 760px), (prefers-reduced-motion: reduce) {
  .work-pin { height: auto; }
  .work-sticky { position: static; height: auto; overflow: visible; display: block; padding: var(--space-2xl) 0; }
  .work-label { position: static; display: block; margin: 0 var(--gutter) var(--space-xl); }
  .work-track { flex-direction: column; align-items: flex-start; gap: var(--space-2xl); transform: none !important; }
  .work-item--up, .work-item--down { align-self: auto; margin: 0; }
  .work-item--sm .work-media, .work-item--md .work-media, .work-item--lg .work-media { width: 100%; }
  .work-item--lg .work-caption { min-height: 0; }   /* stacked layout — no reserved height */
}

/* The Method — CHAPTER CARD (09.07 Chiara: the bottom-left Monolog beat read
   as a void — "starts randomly, loads of space"). The method's name held
   dead-centre in the viewport, lead beneath; scroll hands off to Phase 1. */
.method-intro {
  position: relative;
  height: 100svh;   /* 16.07 (Chiara: "it shouldn't scroll up over the bottom") — the pin-cover retired; the hero scrolls away like any service hero */
}
.method-intro-sticky {
  position: relative;   /* 16.07: no longer pinned */
  height: 100vh;
  display: flex;
  /* 16.07 (Chiara): the service-hero grammar — supersedes the 14.07
     bottom-right cluster. Copy block spans the base; title left, lead right. */
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-end;
  text-align: left;
  overflow: hidden;
  background: var(--gallery);        /* 10.07 (Chiara, colour-lab pick): warm near-black — brown #241911 read too heavy; still FPO until Alex's still lands */
  color: var(--cream);
}
.method-intro-media {
  position: absolute; inset: 0; margin: 0;
  overflow: hidden;
}
.method-intro-media video, .method-intro-media img { width: 100%; height: 100%; object-fit: cover; display: block; }   /* 12.10: img joins — the elephant hero */   /* 14.08 (Chiara: "try the curtain video under the aura method"): the Zambala curtain film fills the seat under the 0.3 scrim */
.method-intro-media::after {         /* even scrim — centred copy needs a full-field hold, not a bottom gradient */
  content: '';
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.3);
}
.method-intro-copy {
  position: relative; z-index: 1;
  padding: var(--gutter);
  display: flex; flex-direction: column;   /* 16.07: the svc-hero column — eyebrow, title, right-aligned lead */
}
.method-intro-title {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-phase);      /* same ladder as the phase titles — the card is slide 0 of the system */
  line-height: 1.05;
}
.method-intro-title .tm { font-size: 0.3em; vertical-align: super; letter-spacing: 0; margin-left: 4px; }
.method-intro .svc-title .tm { font-size: 0.25em; vertical-align: super; letter-spacing: 0; margin-left: 4px; }   /* 22.10 (Chiara: "™ on the Method page is very big — reduce by 50%"): the /the-aura-method hero title is an .svc-title, not .method-intro-title, so the mark ran at the browser sup default (~0.83em); half of that */   /* 19.08: 0.4 → 0.3em — at the monument rung the mark read too large */
.method-intro-lead {
  /* 15.07 (Chiara: "the aura method needs to be eiko for the body copy
     there too"): back to the serif — the 14.07 mono trial on this lead is
     retired. Bottom-right seat stays. */
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.32;
  max-width: 44ch;
  margin: var(--space-md) 0 0 auto;
}

/* The Method — pinned slide template (Chiara frame 02.07).
   Scroll flicks through five phases: image + title + value + deliverables
   hard-cut per phase (09.07: crossfade dropped — house motion rule, no
   dissolves; the cut on the counter tick IS the gesture). Title runs across
   the portrait image, ink outside, cream where it crosses (the knockout,
   via a clipped duplicate). */
.method-pin { height: 560vh; position: relative; }  /* 16.07: the -100vh hero cover retired — the hero scrolls away, then the deck locks */
.method-sticky {
  position: sticky; top: 0;
  height: 100vh; overflow: visible;   /* 16.07 (Chiara): "remove the overflow: hidden" */
  background: var(--paper);
  /* 04.07 (Chiara: "centre this, all breakpoints"): one source of truth for
     the slide image box. The title used to sit at a magic top: 44vh measured
     off the 16:9 design frame — on any other viewport shape the image centre
     moves and the lockup drifts. Title + knockout now derive from these. */
  --method-media-w: clamp(240px, 24vw, 400px);   /* 25.10 (Chiara: "the same as How We Get You There on the homepage — maybe a bit bigger"): the homepage seat's 3:4 at ~20% larger (346×461 at 1440; the homepage is 288×384) — the phases' image IS the content. Was min(22vw + 20px, 440) at 534:905 */   /* 22.10 (Chiara: "the method images are too big on the page"): ~20% down — was min(27.8vw + 20px, 554px), measured 534×905 */
  --method-media-h: min(calc(var(--method-media-w) * 4 / 3), 70vh);   /* 25.10: 3:4, the homepage frame's shape (was the 534:905 strip) */   /* 22.10: height cap 82vh → 64vh with it */
  --method-title-top: var(--space-2xl);   /* 12.10 (Chiara: "can this sit higher up the image on all of them" → "like at the top" → "undo that"): the top-of-image seat was built, seen, and REVERSED within the hour; the token stays idle so the three rules below can be flipped back in one move if she returns to it. */
}
.method-label .tm { font-size: 0.65em;  /* 10.07: label-band ™ unified at 0.65em */ vertical-align: super; letter-spacing: 0; margin-left: 2px; }
/* 03.07 (Chiara): the Method takes the header line, same as Selected Work —
   title + counter cluster in the burger seat, mark centre, CTA right */
.method-line {
  position: absolute;
  top: var(--gutter);   /* the header line, flush at the gutter (05.07: +3px nudge dropped) */
  left: var(--gutter);
  display: flex; align-items: baseline; gap: var(--space-md);
  z-index: 4;
}
.method-label { opacity: 0.8; }
.method-counter { opacity: 0.8; }   /* rides beside the title; .label styling untouched */
.method-cta {
  position: absolute;
  top: var(--gutter); right: var(--gutter);   /* 05.07: +3px nudge dropped — flush at the gutter */
  z-index: 5;
  pointer-events: none;             /* letters hidden below the roll clip until .ready */
}
.method-pin.pin-active .method-cta { pointer-events: auto; }

/* 16.07 v2 (Chiara): the pinned flick restored — one locked screen, the
   wording cuts. The seat joins the canvas grammar; label centred in the
   header line (clear of burger + Let's Talk); counter rides above the
   title inside each slide, so it cuts with the wording. */
.method-label--center { position: absolute; top: 0; left: 0; right: 0; text-align: center; padding: var(--space-md) var(--gutter); z-index: 2; }
.method-pin .method-media {
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  /* re-pinned: the photography-frames .ph-media { position: relative } lands
     later in the sheet and outranks .method-media's centring */
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: var(--method-media-w);
  height: var(--method-media-h);
}
.method-pin .method-media:has(img, video)::after { content: none; }   /* 12.10: a seat that holds media drops the placeholder caption (Immersion landed first) */
.method-pin .method-media::after {
  content: '(\2003Imagery\2003)';
  position: absolute; inset: 0;
  display: flex; align-items: flex-end; justify-content: center;
  padding-bottom: var(--space-md);
  font-family: var(--font-mono); font-size: var(--type-label);
  text-transform: uppercase; opacity: 0.45;
}
.method-pin .method-counter {
  position: absolute; left: 0; right: 0;
  bottom: calc(50% + (var(--type-phase) / 2) + var(--space-md));   /* one step above the title's cap line */
  text-align: center; z-index: 2;
  opacity: 0.65;
}
.method-slide {
  position: absolute; inset: 0;
  opacity: 0;
  visibility: hidden;
  /* no transition — slides hard-cut on the scroll tick (09.07: the 0.5s
     opacity crossfade was the "PowerPoint" dissolve the motion rules ban) */
}
.method-slide.active { opacity: 1; visibility: visible; }

.method-media {
  /* 04.07 (Chiara): lockup centres in the viewport — on the 16:9 design
     frame this equals the old bottom: 9vh (82vh image, equal air), but on
     taller windows the air now splits evenly instead of pooling on top */
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: var(--method-media-w);
  height: var(--method-media-h);      /* 82vh cap keeps 9vh air top + bottom on short viewports */
}
/* 10.07 (Chiara): Postcards FPO pulls removed — slots stay placeholder until
   Alex's method stills land. Tint shifts keep the five slides distinct. */
.method-slide .ph-media--m2 { filter: hue-rotate(15deg); }
.method-slide .ph-media--m3 { filter: hue-rotate(-12deg) brightness(1.04); }
.method-slide .ph-media--m4 { filter: hue-rotate(30deg) brightness(0.97); }
.method-slide .ph-media--m5 { filter: hue-rotate(-25deg); }

.method-title {
  position: absolute; left: 0; right: 0;
  top: 50%;                            /* 04.07: image centres at 50vh, so the line centres with it — exact on every viewport shape */   /* 12.10: a top-of-image seat (calc(50% − media-h/2 + --method-title-top), transform none) was tried and undone the same hour */
  transform: translateY(-50%);
  z-index: 2;
  text-align: center;
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-phase);
  line-height: 1;
  color: var(--ink);
  white-space: nowrap;
}
/* The knockout: duplicate title clipped to the image box, in cream */
.method-clip {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: var(--method-media-w);
  height: var(--method-media-h);
  overflow: hidden;
  z-index: 3;
  pointer-events: none;
}
.method-media img { width: 100%; height: 100%; object-fit: cover; display: block; }   /* 12.10: the first phase still (Immersion, DSC07741) fills the seat like the method-intro video rule */
.method-slide[data-phase="5"] .method-clip .method-title { color: var(--ink); }   /* 12.10: Immersion carries the light Luvia tablet shot — ink over it too */   /* 12.10 v3: only Sustain carries a still now (the white studio, ink over it); the other four are back on their tinted placeholders, where the cream over-title is moot */   /* 12.10: Sustain (DSC07666) is the same white studio — ink over it too */   /* 12.10: the Immersion still is a white studio — cream over it vanished in the render, so this slide's over-image title stays ink (the cream flip below is for dark imagery; set per slide as the stills land) */
.method-clip .method-title {
  left: 50%; bottom: 50%; top: auto;   /* clip box = image box, so 50% up the clip = image centre — exact registration with the base line */
  transform: translate(-50%, 50%);
  width: 100vw;
  color: var(--cream);
}

.method-value::before {
  content: '( The Shift )';           /* 02.07 — left column: the value; FPO, Hannah may re-voice */
  display: block;
  font-family: var(--font-label);
  font-weight: 400;
  font-size: var(--type-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.76;
  margin-bottom: var(--space-sm);
}
.method-deliv::before {
  content: '( The Work )';            /* right column: how we get there */
  display: block;
  font-family: var(--font-label);
  font-weight: 400;
  font-size: var(--type-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink);
  opacity: 0.76;
  margin-bottom: var(--space-sm);
}
.method-value {
  position: absolute; left: var(--gutter); bottom: 9vh;   /* matches the work section's buffer rhythm */
  max-width: 34ch;
  display: grid; gap: var(--space-md);
  z-index: 4;
  font-weight: 300;   /* Thin (Chiara 02.07) — the slide value copy specifically */
}
/* "You walk out with" / "You become" as scannable label lines (02.07 —
   inline green italics were hard to read): each beat = small caps label
   on its own line, then the content in body type */
.method-value em {
  display: block;
  font-style: normal;
  font-family: var(--font-label);
  font-size: var(--type-label);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--green);
  margin-bottom: 4px;
}

.method-deliv {
  position: absolute; right: var(--gutter); bottom: 9vh;
  text-align: right;
  z-index: 4;
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.5;
  color: var(--ink);
}

@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  /* 20.10 v3 (Chiara: "i hate the design — align it to the desktop version as much as possible but optimised"):
     the phone deck is THE DESKTOP FLICK, scaled. Two mobile layouts died today: the 04.07 static stack (its
     overrides never out-ranked the pin's absolutes — five images piled mid-page) and a sticky card stack (built
     on her "they should stack?", rejected on sight). Now: one locked 100svh seat, centred portrait, title knocked
     out over it, counter above, value bottom-left, work bottom-right — the desktop composition re-proportioned.
     The JS flick driver's ≤900 gate is lifted (main.js, same date) so scroll hard-cuts the five phases here too.
     The intro stays static flow; the pinned CTA stays off (the footer ask carries conversion on phones). */
  .method-intro { height: auto; }
  .method-intro-sticky { position: static; height: auto; min-height: 100svh; }
  .method-pin { height: 400svh; }   /* 5 cuts in ~4 screens of scroll — the desktop's 560vh is a long thumb-ride at 390 */
  .method-sticky {
    height: 100svh;
    --method-media-w: min(61vw, 300px);   /* 25.10: the homepage phone seat (51vw) +20% → 238px at 390 */   /* desktop formula gives 128px at 390 — a stamp, not a seat. 20.10: 64vw → 54vw (Chiara: "can the image be smaller?") */
    --method-media-h: calc(var(--method-media-w) * 4 / 3);   /* 25.10: 3:4, as desktop and the homepage */   /* 20.10 (Chiara, the Sustain still: "a weird shape, can we make it shorter"): the desktop's 534:905 portrait reads like a strip at phone scale — the seat steps to 4:5 (263px tall at 390), the crop grammar of the founder plate and the stacked case frames. Clip + title registration ride the same vars. */
  }
  .method-cta { display: none; }
  /* 20.10 v4 (Chiara: "the titles, Design etc., should sit under the number tags — make more use of the space above
     the image for the title instead of having it over the image"): the phone HEADER CLUSTER — counter at 12svh,
     title directly under it, both centred on the ground in the running type colour; the knockout overlay is OFF on
     phones (it stays the desktop signature) and the image sits at 47svh, centring in the room between the title
     cluster and the corner texts. Supersedes the 42svh over-image lockup from earlier today. */
  .method-pin .method-counter { bottom: auto; top: 8svh; }
  .method-title { top: calc(8svh + 28px + var(--space-xs)); transform: none; }
  .method-clip { display: none; }
  /* 25.10 (Chiara, phone: "do we need to move it up a little bit?"): the phase cluster rises ~5svh (≈40px at 844) — counter 12 → 8svh, title with it, the photo 47 → 42svh and the Shift / Work columns follow the photo — into the empty top room; the header logo keeps its clearance */
  .method-pin .method-media { top: 42svh; }
  /* the deck's centred THE AURA METHOD™ marquee repeated the hero title the reader just left — one label register
     per seam on a phone; the counter + phase titles carry the deck. */
  .method-label--center { display: none; }
  /* 20.10 (Chiara, three passes: tags "look big" → copy "so small" → "match the case study page"): the corners wear
     the CASE-STUDY register — copy at --type-body like a story row's paragraph, tags at --type-hw-corner like its
     (   ) labels. One register across the site's reading layer, nothing bespoke to remember. */
  /* 20.10 (the spacing review, Chiara: "make sure everything's in a good position"): the two corner groups were
     BOTTOM-anchored, so their ( ) tags landed at different heights whenever the columns differed in length — ragged
     on every phase. They now hang from ONE top line, a lg below the image seat: the tags align like a case story
     row's labels and the columns rag downward into the bottom air. */
  .method-value { max-width: 38vw; bottom: auto; top: calc(42svh + (var(--method-media-h) / 2) + var(--space-lg)); font-size: var(--type-body); gap: var(--space-sm); }
  .method-deliv { max-width: 54vw; bottom: auto; top: calc(42svh + (var(--method-media-h) / 2) + var(--space-lg)); font-size: calc(var(--type-serif-md) - 4px); line-height: 1.4; }   /* 20.10 final: serif-md read "a bit big" at 390 — one notch under the rung (20px), still serif, still above body */   /* 20.10 (Chiara pasted the work list): at body size the serif sat below its own floor — the one place on the site Eiko ran under --type-serif-md. Back on the serif rung (24 at 390), as on desktop; the long items wrap inside 54vw and the value column narrows to 38vw to keep the corners clear of each other. */
  .method-value::before, .method-deliv::before { font-size: var(--type-hw-corner); }
  /* 27.10 (Chiara, phone: "this looks bad… nothing's aligned, the positioning"): the two columns become an even 50/50 pair —
     both LEFT-aligned, the right one starting at the centre line, labels on one line, copy starting at one line (the value
     column's grid gap had dropped its copy 16px below the list's). */
  .method-value { left: var(--gutter); right: auto; width: calc(42% - var(--gutter) - var(--space-xs)); max-width: none; gap: 0; }
  .method-deliv { left: calc(42% + var(--space-sm)); right: var(--gutter); width: auto; max-width: none; text-align: left; align-items: flex-start; font-size: calc(var(--type-serif-md) - 6px); }   /* 42/58 so each deliverable holds one line */
  .method-deliv li { text-align: left; line-height: 1.2; margin-bottom: var(--space-xs); }   /* a long item (Brand Strategy Workshop) may wrap — the item gap keeps each one reading as one */
}

/* Meet the Founder (14.08, Chiara: "meet the founder and its photo of me —
   50/50 text to image"). Takes the hidden Insight Report's seat and its
   grid bones: half text, half portrait, centred against each other. Copy
   FPO – Hannah; the media is a ph placeholder until the portrait is shot. */
/* MEET THE FOUNDER — rebuilt 01.09 (Chiara "do it" on the audit).
   W5 S1: the section's largest element was an EMPTY figure painted with the .ph-media
   gradient — half a screen of placeholder, and nothing around it could read as luxury
   while it shipped. W5 S3: the block aligned to no axis on the page — a 50/50 grid with
   the copy centred inside its own column and the media at 86% centred inside the other,
   so it touched neither gutter and shared no edge with the router rows or the section
   labels. It was the one section that could have belonged to any website.

   NOTE — THIS REVERSES A 14.08 CALL OF CHIARA'S ("centre this text", then "make the
   photo to the centre too"). Today's approved build order says full bleed, on the page
   axis, which is later and more specific, but the earlier instruction was explicit and
   this comment exists so the reversal is visible rather than silent. Restore the 14.08
   centred pair from git if she wants it back.

   The layout is now SELF-GATING on whether a real portrait exists:
     .founder-media:empty  -> the figure does not render at all, and the section is a
                              left-anchored typographic block on the gutter axis. No
                              gradient rectangle ships while the shoot is outstanding.
     figure has an <img>   -> the :has() rule below flips it to the full-bleed treatment.
   So the portrait lands by dropping one <img> into the figure. No CSS change needed. */
/* 09.09 (Chiara: "the text should be right-aligned, and the image of me would be on the left"): TWO COLUMNS, the
   portrait's seat on the left, the copy in the right column and ranged right. The seat stays empty (and unpainted)
   until the shoot lands; the copy already sits where it will sit beside it. */
.founder {
  padding: var(--space-section) var(--inset-page);   /* 09.09: the testimonials' inset — measured 32px vs their 58px before; one edge now */
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-2xl);
  align-items: center;
}
.founder-copy {
  grid-column: 2;
  display: grid;
  gap: var(--space-lg);
  justify-items: end;
  text-align: right;
  max-width: 52ch;
  margin-inline: auto 0;
}
.founder-name {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-display);   /* 12.10: the founder STATEMENT, on the display rung — every reference she likes sets it there; was --type-serif-lg ("Our founder."). Survives the option B retire. */
  line-height: 1.1;
  text-wrap: balance;
  color: var(--title-ink);   /* 10.09: serif title on a light ground → taupe */
}
.founder-byline {   /* 09.09: the byline under the founder title — the deck's .hw-byline rung (serif-md, 0.85), ranged with the copy */
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-md); line-height: 1.25;
  opacity: 0.85; margin: calc(-1 * var(--space-md)) 0 0;   /* sits a step closer to the title than the grid's lg gap */
  text-wrap: balance;
}
.founder-credit { opacity: 0.85; margin: calc(-1 * var(--space-md)) 0 0; }   /* 10.09: name + role under the title, on the mono label rung (.label); the byline's tighter seat under the title */
.founder-line {
  font-family: var(--font-body); font-weight: 300;
  font-size: var(--type-body); line-height: var(--lh-body);
  opacity: 0.85;
}

/* THE PORTRAIT SEAT. Empty until the shoot lands, and an empty seat renders nothing. */
.founder-media { margin: 0; }
.founder-media:empty { display: none; }
.founder-media img,
.founder-media video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* FULL BLEED — arms itself the moment a real asset is in the figure. */
/* 09.09: with a portrait it is a SPLIT, not an overlay — image left at full column height, copy right, ranged
   right, ink on the section's own ground (the 01.09 full-bleed-with-scrim treatment retires with this). */
/* 12.10: OPTION B TRIED AND RETIRED THE SAME DAY. Rendered three compositions (A copy-over-film, B statement +
   5/2/5 grid with a 4:5 plate, C statement row + film band); Chiara picked B, it was built, and on seeing it:
   "not sure on the title of that and format" → "don't love it" → "all of the above" to every fix offered. Back to
   this split. What B leaves behind: the title on --type-display (was serif-lg), the copy cut 115 → 58 words, and
   the title as a statement rather than "Our founder." The B rules are in git under 12.10 if wanted again. */
.founder:has(.founder-media:not(:empty)) {
  padding: 0 var(--gutter) 0 0;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-2xl);
  position: relative;
  min-height: 100svh;        /* svh, not vh — the W5 finding: a vh full-screen block drifts as mobile Safari's URL bar collapses */
}
.founder:has(.founder-media:not(:empty)) .founder-media {
  grid-column: 1;
  width: 100%;
  height: 100svh;
  overflow: hidden;          /* the clip window for the mask-grow, when that gets wired to the asset */
}
.founder:has(.founder-media:not(:empty)) .founder-copy {
  grid-column: 2;
  align-self: center;
  z-index: 2;
  padding: var(--gutter) 0;
}
/* the base shadow that keeps copy legible over a portrait, the footer-scrim grammar */
.founder:has(.founder-media:not(:empty))::after { display: none; }   /* 09.09: no scrim — the copy no longer sits over the portrait */
.founder-retired-scrim::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.55), transparent 46%);
  z-index: 1;
  pointer-events: none;
}
/* TODO when the portrait lands: wire the deck's mask-grow (.hw--grow, the clip-window
   expansion at ~.hw-pin) onto .founder-media. W4's point stands — it is the best gesture
   on the site and it currently fires once, above the fold. Reusing it here hands the
   bottom third a bespoke moment for almost no new code. Not wired now because animating
   an element with no content is speculative work. */

/* Insight Report — the warm door */
.insight {
  padding: var(--space-lg) var(--gutter);   /* 14.07 v2 (Chiara: "too much padding around it") — was space-section; the frame now hugs the viewport */
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2xl);
  align-items: center;
}
.insight-media {
  aspect-ratio: 4 / 5;
  /* 14.07 v2 (Chiara: "bigger on the left"): fills the viewport height bar
     a space-lg breath top and bottom, anchored to the left gutter (was
     centred in its column behind section-scale padding). Section still
     caps at ~one screen. */
  height: calc(100vh - 2 * var(--space-lg));
  max-width: 100%;
  justify-self: start;
}
.insight-body { max-width: 52ch; }   /* 03.07 measure audit: one box for label + line + copy + form (was uncapped — ~125ch on wide screens) */
.insight-body .section-label { opacity: 0.65; display: block; margin-bottom: var(--space-lg); }
.insight-line {
  font-family: var(--font-serif);
  font-size: var(--type-serif-md);
  line-height: 1.35;
  max-width: 30ch;   /* 03.07 measure audit: feature line, was uncapped in its grid column */
  margin-bottom: var(--space-md);
}
/* 02.07: name + email, stacked short lines (was one very long email field) */
.insight-form {
  display: grid;
  gap: var(--space-md);
  margin-top: var(--space-lg);
  max-width: 360px;
}
.insight-form input[type="email"],
.insight-form input[type="text"]:not(.hp) {
  background: none;
  border: 0; border-bottom: 1px solid color-mix(in srgb, var(--ink) 40%, transparent);  /* hairline feel (03.07 — full ink read thick next to the light type) */
  padding: var(--space-xs) 0;
  font-family: var(--font-body);
  font-weight: 300;              /* 03.07: inputs don't inherit weight — match the body's light cut */
  font-size: var(--type-body);
  letter-spacing: 0.1px;
  color: var(--ink);
}
.insight-form input::placeholder { font-weight: 300; color: color-mix(in srgb, var(--ink) 55%, transparent); }
.insight-form input:focus-visible { outline: 2px solid var(--green); outline-offset: 4px; border-bottom-color: var(--green); }
.insight-form .cta { justify-self: start; background: none; border: 0; cursor: pointer; color: var(--green); padding: 0; }
.insight-form .hp { position: absolute; left: -9999px; }
.insight-form .cta { background: none; border: 0; cursor: pointer; color: var(--green); }

/* ============================================================
   CLIENT APPLICATION (/apply/, 12.10 · v2 same evening, Chiara: "way too
   messy") — one calm column. Question on the FAQ's serif rung, answer
   beneath on a single hairline, choices as tappable text (inputs visually
   hidden, labels carry the state), no counters, no native circles.
   ============================================================ */
/* 13.10 v3 (Chiara: "hate the contact page" → "a very clean contact form layout" → "a clean floating image
   to the left … then simple contact form"): TWO COLUMNS — a still plate floats left (sticky under the header,
   4:5), head + form on the right. Questions drop from the serif rung to mono labels; name/email pair up. */
.apply { display: grid; grid-template-columns: 5fr 7fr; gap: var(--space-2xl); align-items: start; padding: var(--space-xl) var(--gutter) var(--space-section); }
.apply-photo { position: sticky; top: calc(var(--header-h) + var(--space-lg)); margin: 0; aspect-ratio: 4 / 5; max-height: calc(100svh - var(--header-h) - 2 * var(--space-lg)); overflow: hidden; }
.apply-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.apply-main { max-width: 640px; padding-top: var(--space-lg); }
.apply-head { margin-bottom: var(--space-xl); }
.apply-title { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.15; letter-spacing: -0.01em; text-wrap: balance; color: var(--title-ink); margin: var(--space-sm) 0 var(--space-sm); }
.apply-head .svc-lead { max-width: 52ch; text-align: left; margin: 0; font-family: var(--font-body); font-size: var(--type-body); line-height: var(--lh-body); }
.apply-form { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-lg) var(--space-md); }
.apply-row { grid-column: 1 / -1; display: grid; gap: var(--space-xs); }
.apply-row--half { grid-column: auto; }
.apply-q { margin: 0; opacity: 0.65; }
.apply-a { display: grid; gap: var(--space-sm); }
.apply-row:has(input[name="business"]) .apply-a { grid-template-columns: 1fr 1fr; gap: var(--space-md); }
.apply-form input[type="text"]:not(.hp), .apply-form input[type="email"], .apply-form textarea {
  width: 100%; background: none; border: 0; border-bottom: 1px solid color-mix(in srgb, var(--ink) 30%, transparent);
  padding: var(--space-xs) 0; font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: var(--lh-body); color: var(--ink); border-radius: 0;
}
.apply-form textarea { resize: vertical; min-height: 3.2em; }
.apply-form input::placeholder, .apply-form textarea::placeholder { font-weight: 300; color: color-mix(in srgb, var(--ink) 50%, transparent); }
.apply-form input:focus-visible, .apply-form textarea:focus-visible { outline: none; border-bottom-color: var(--ink); }
/* choices: a row of text, the chosen one lit and underlined */
.apply-a:has(.apply-opt) { display: flex; flex-wrap: wrap; gap: var(--space-xs) var(--space-md); padding-top: var(--space-xxs); }
.apply-opt { position: relative; cursor: pointer; }
.apply-opt input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.apply-opt-text { display: inline-block; margin: 0; padding: var(--space-xxs) 0; opacity: 0.5; border-bottom: 1px solid transparent; transition: opacity var(--dur-quick) var(--ease-house), border-color var(--dur-quick) var(--ease-house); }
.apply-opt:hover .apply-opt-text { opacity: 0.85; }
.apply-opt input:checked + .apply-opt-text { opacity: 1; border-bottom-color: currentColor; }
.apply-opt input:focus-visible + .apply-opt-text { outline: 2px solid var(--green); outline-offset: 4px; }
.apply-submit { grid-column: 1 / -1; padding-top: var(--space-sm); }
/* 27.10 (Chiara: "i dont like the design", ref crowncreative.com/contact → "tidy what's there"): the form reads
   like a form. Larger serif title; questions in sentence case on the body face (not mono caps); fields as soft
   filled boxes, not hairlines; choices as clear pills that fill when picked. Tokens only. */
.apply-title { font-size: var(--type-serif-lg); }
.apply-form { gap: var(--space-lg) var(--space-md); }   /* 27.10 (Chiara: "form fields better spaced"): 40 between questions, 24 between columns — matches the business pair so the boxes line up */
.apply-q { opacity: 1; font-family: var(--font-body); font-size: var(--type-body-sm); line-height: var(--lh-body); color: color-mix(in srgb, var(--ink) 80%, transparent); }
.apply-form input[type="text"]:not(.hp), .apply-form input[type="email"], .apply-form textarea {
  background: color-mix(in srgb, var(--ink) 6%, transparent); border-bottom: 0; padding: var(--space-xs) var(--space-sm);
}
.apply-form input:focus-visible, .apply-form textarea:focus-visible { outline: 1px solid color-mix(in srgb, var(--ink) 45%, transparent); outline-offset: 0; }
.apply-a:has(.apply-opt) { gap: var(--space-xs); padding-top: 0; }
.apply-opt-text { opacity: 1; padding: var(--space-xs) var(--space-sm); border: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); border-radius: 200px;
  font-family: var(--font-body); font-size: var(--type-body-sm); line-height: 1.2; transition: background var(--dur-quick) var(--ease-house), color var(--dur-quick) var(--ease-house), border-color var(--dur-quick) var(--ease-house); }
.apply-opt:hover .apply-opt-text { opacity: 1; border-color: color-mix(in srgb, var(--ink) 60%, transparent); }
.apply-opt input:checked + .apply-opt-text { background: var(--ink); color: var(--cream); border-color: var(--ink); }
/* 27.10 (Chiara: "pill style form fields"): the text fields take the pill shape of the choices; the textarea keeps a soft rounded box (a multi-line pill can't hold) */
.apply-form input[type="text"]:not(.hp), .apply-form input[type="email"] { border-radius: 200px; padding: var(--space-xs) var(--space-md); }
.apply-form textarea { border-radius: var(--space-lg); padding: var(--space-sm) var(--space-md); resize: none; }   /* 27.10 (Chiara: "curve the pill corners"): 24 → 40; resize off — the grip sat in the round corner */
.apply-ai-q { margin-top: var(--space-sm); gap: var(--space-xs); }
.apply-ai-q[hidden] { display: none; }
.apply-alt { margin: var(--space-sm) 0 0; font-family: var(--font-body); font-size: var(--type-body-sm); color: color-mix(in srgb, var(--ink) 80%, transparent); }
.apply-li { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; display: inline-flex; align-items: center; gap: var(--space-xxs); }
.apply-li .menu-foot-arrow { width: 12px; height: 12px; }
/* 27.10 (Chiara: "we need a hero like the rest"): /apply gets the interior hero; the form sits below in one centred column. */
.apply--single { grid-template-columns: minmax(0, 720px); justify-content: center; padding-top: var(--space-2xl); }
.apply--single .apply-main { max-width: none; padding-top: 0; }
/* 27.10 (Chiara: "remove the scrim… cream background"): /apply stays on cream (body.close-paper, no drift); the dark treatment + hero fade retired */
.apply-hero .svc-hero-bg::after { background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 70%, rgba(0, 0, 0, 0.55) 100%), linear-gradient(to bottom, rgba(0, 0, 0, 0.22), transparent 22%), rgba(0, 0, 0, var(--hero-scrim, 0.38)); }   /* 27.10 (Chiara: "needs overlay like we have on the method page"): the Studio/Method hero scrim — dark foot, top band, 38% flat */
.apply-hero-row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-end; gap: var(--space-md) var(--space-lg); }   /* 27.10 (Chiara: "do we have a cta button that links to the contact form?"): title left, the CTA bottom right on its baseline; wraps under the title on phones */
.apply-hero-cta { color: var(--cream); margin-bottom: var(--space-xs); }
.apply--single { --ink: var(--ground-3); color: var(--ink); }   /* 27.10 (Chiara: "the colours look off too"): the olive ink went muddy-green in the field fills and pill lines on the warm cream; the form takes the warm near-black instead, so every tint stays warm */
.apply--single .apply-intro { margin-bottom: var(--space-lg); text-wrap: balance; }   /* 27.10 (Chiara: "review the line spacing, it looks off"): the gap under the intro takes the 40 question rhythm; balanced so no short orphan line */
.apply--single .apply-alt--after { margin: var(--space-xl) 0 0; }
.apply-hero :is(.svc-title, .svc-eyebrow) { color: var(--frost); opacity: 1; }   /* 27.10 (Chiara: "a byline above Tell us what you've built"): seated like /studio and /the-aura-method */
.apply-intro { margin: 0 0 var(--space-xs); font-family: var(--font-body); font-size: var(--type-body); line-height: var(--lh-body); }

.apply-form .cta { background: none; border: 0; cursor: pointer; padding: 0; color: inherit; }
.apply-form .hp { position: absolute; left: -9999px; }
/* 27.10 (Chiara: "take inspo from the process page stack" + "the form fields fit on the viewport, no scrolling" + "concise and easy to see
   what they need to do"): THE LEDGER FORM. The /branding process grammar — full-width hairlines, a small mono number, the answer column
   starting at 40% — one row per question, question left, answer right, so the whole form sits in one desktop screen. One field language:
   soft filled pills for inputs, choices and Submit alike; a choice deepens when picked. Phones stack each row. */
.apply--ledger { display: block; min-height: 100svh; padding: calc(var(--header-h) + var(--space-md)) var(--gutter) var(--space-xl); }
.apply--ledger .apply-main { max-width: none; padding: 0; }
.apply--ledger :is(.apply-head-row, .apply-row, .apply-submit) { display: grid; grid-template-columns: 2fr 3fr; column-gap: var(--space-lg); align-items: center;
  padding: var(--space-sm) 0; border-top: 1px solid color-mix(in srgb, var(--ink) 18%, transparent); }
.apply--ledger .apply-head-row { border-top: 0; padding: 0 0 var(--space-md); align-items: baseline; }
.apply--ledger .apply-tag { margin: 0; opacity: 0.8; }
.apply--ledger .apply-intro { margin: 0; max-width: 56ch; text-wrap: pretty; }
.apply--ledger .apply-form { display: block; }
.apply--ledger .apply-row { grid-column: auto; }
.apply--ledger .apply-q { display: flex; align-items: baseline; margin: 0; padding-right: var(--space-lg); color: var(--ink); font-size: var(--type-body); line-height: 1.35; }
.apply--ledger .apply-n { flex: none; min-width: 3.2em; font-family: var(--font-mono); font-size: var(--type-caption); letter-spacing: 0.06em; opacity: 0.55; vertical-align: 0.1em; }
.apply--ledger .apply-a { grid-column: 2; }
.apply--ledger .apply-a.apply-a--pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-sm); }
.apply--ledger .apply-ai-q { grid-column: 2; margin-top: var(--space-xs); }
.apply--ledger .apply-ai-q .apply-q { font-size: var(--type-body-sm); padding: 0; }
.apply--ledger .apply-form textarea { min-height: 0; height: calc(3 * 1.5em + 2 * var(--space-sm)); }
.apply--ledger .apply-a:has(.apply-opt) { gap: var(--space-xs); }
.apply--ledger .apply-opt-text { border-color: transparent; background: color-mix(in srgb, var(--ink) 6%, transparent); }
.apply--ledger .apply-opt:hover .apply-opt-text { border-color: transparent; background: color-mix(in srgb, var(--ink) 11%, transparent); }
.apply--ledger .apply-opt input:checked + .apply-opt-text { background: color-mix(in srgb, var(--ink) 82%, transparent); color: var(--cream); border-color: transparent; }
.apply--ledger .apply-submit { padding-top: var(--space-md); border-bottom: 0; }
.apply--ledger .apply-submit .apply-send { grid-column: 2; justify-self: start; border-color: transparent; background: color-mix(in srgb, var(--ink) 82%, transparent); color: var(--cream); }
.apply--ledger .apply-submit .apply-send:hover { border-color: transparent; background: var(--ink); }
.apply--ledger .apply-alt--after { margin: var(--space-md) 0 0; padding-left: calc((100% - var(--space-lg)) * 0.4 + var(--space-lg)); }
/* 27.10 (Chiara: "dark background with lighter for this"): the ledger form on the site dark — --ink flips to cream inside the
   section, so every tint (hairlines, fills, labels) lightens with it; the hero photo melts into the same dark. */
html body.apply-page, body.apply-page main { background: var(--ground-3); }
.apply--ledger { --ink: var(--cream); color: var(--cream); background: var(--ground-3); }
.apply--ledger .apply-opt input:checked + .apply-opt-text, .apply--ledger .apply-submit .apply-send { color: var(--ground-3); }
.apply--ledger .apply-submit .apply-send:hover { background: var(--cream); }
body.apply-page .apply-hero .svc-hero-bg::after { background: linear-gradient(to bottom, transparent 55%, color-mix(in srgb, var(--ground-3) 45%, transparent) 78%, var(--ground-3) 100%), linear-gradient(to bottom, rgba(0, 0, 0, 0.22), transparent 22%), rgba(0, 0, 0, var(--hero-scrim, 0.38)); }
@media (max-width: 900px) {
  .apply--ledger { min-height: 0; }
  .apply--ledger :is(.apply-head-row, .apply-row, .apply-submit) { grid-template-columns: 1fr; row-gap: var(--space-xs); padding: var(--space-md) 0; }
  .apply--ledger :is(.apply-a, .apply-ai-q, .apply-submit .apply-send) { grid-column: 1; }
  .apply--ledger .apply-a.apply-a--pair { grid-template-columns: 1fr; gap: var(--space-xs); }
  .apply--ledger .apply-alt--after { padding-left: 0; }
  .apply--ledger .apply-a:has(.apply-opt) { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-xs); }   /* 27.10 (Chiara: "optimise for mobile"): choices as an even two-up grid — no orphan pill, bigger tap targets */
  .apply--ledger .apply-opt-text { display: block; text-align: center; }
  .apply--ledger .apply-submit .apply-send { width: 100%; }   /* a full-width Submit under the thumb */
}
body.page-interior.close-dark.apply-page .above-footer::after { background: var(--ground-3); }   /* the rider strip under the form matched --gallery (a lighter band); the form ground runs to the footer */
body.page-interior.close-dark.apply-page .footer-scrim { --melt: var(--ground-3); }
.apply-form .apply-send { display: inline-block; text-align: center; min-width: 154px; height: 40px; line-height: 38px; overflow: hidden; padding: 0 var(--space-md); border-radius: 200px; background: none; border: 1px solid color-mix(in srgb, var(--ink) 45%, transparent); color: var(--ink); transition: border-color var(--dur-quick) var(--ease-house); }   /* 27.10 (Chiara: "dark pill looks weird"): outlined, the answer pills' grammar, one step firmer */   /* 27.10 (Chiara: "change to Submit and make it a button"): the one filled pill on the page, the global CTA pill size (154 × 40) */
.apply-form .apply-send:hover { border-color: var(--ink); }
.apply-sent { padding-top: var(--space-lg); border-top: 1px solid color-mix(in srgb, currentColor 25%, transparent); }
@media (max-width: 900px) {   /* layout flip only: the plate stacks above, cropped short; the form goes single-column */
  .apply { grid-template-columns: 1fr; gap: var(--space-lg); padding-top: calc(var(--header-h) + var(--space-sm)); }
  .apply-photo { position: static; aspect-ratio: 4 / 3; max-height: none; }
  .apply-main { padding-top: 0; }
  .apply-form, .apply-row:has(input[name="business"]) .apply-a { grid-template-columns: 1fr; }
}

/* ============================================================
   THE FOUNDER CHAPTER (12.10, Alex's layout from khanhnguyen.design) —
   one screen on a two-column grid: chapter mark + credit in the left column
   (top and bottom), the intro block, quote, link and portrait in the right.
   The portrait wipes open left → right on arrival (the reference's reveal).
   ============================================================ */
.fi { position: relative; height: 100svh; min-height: 640px; padding: calc(var(--space-lg) + var(--header-h, 0px)) var(--gutter) var(--space-2xl); display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--space-xl); grid-template-rows: auto 1fr auto; }   /* 12.10: bottom padding lg → 2xl so the name clears the fixed sound toggle in the corner (her 22.07 screenshot: "OFF" over "Chiara") */
.fi-chapter { grid-column: 1; grid-row: 1; margin: 0; font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); text-transform: uppercase; letter-spacing: 0.02em; line-height: 1; color: var(--title-ink); }
.fi-who { grid-column: 1; grid-row: 3; align-self: start; margin: 0; display: grid; gap: var(--space-xs); justify-self: center; width: calc(min(66svh, 658px) * 0.75); max-width: 100%; }   /* 12.10 (Chiara: "move this aligned with the image"): the name block takes the portrait's width and centring, so its left edge sits on the photo's left edge */   /* 12.10 v2 (Chiara: "maybe Chiara and Founder & Creative Director goes to the bottom"): bottom-left, the reference's "beyond design" slot; the top-left is empty air */
.fi-name { margin: 0; font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.1; color: var(--title-ink); }   /* 12.10 (Chiara: "the Eiko could be a bit smaller"): serif-lg → serif-md, one rung */
/* 12.10 (Chiara: "move the name closer to the image"): the portrait now sits at the FOOT of its rows (align-self end) with a 16px gap, and the name block sits at the TOP of the row below — caption-tight under the photo */
.fi-role { margin: 0; opacity: 0.65; }
.fi-intro { grid-column: 2; grid-row: 1; display: grid; gap: var(--space-md); }
.fi-label { margin: 0; opacity: 0.65; }
.fi-statement { margin: 0; font-family: var(--font-serif); font-weight: 300; font-size: var(--type-origin-sm); line-height: var(--lh-intro); letter-spacing: -0.02em; color: var(--title-ink); max-width: 34ch; text-wrap: balance; }   /* 22.10 (Chiara: "check the line breaks here"): "now a" was stranded on its own line because multi‑disciplinary (non-breaking hyphen) could not follow it; balance evens the six lines out */
.fi-quote { grid-column: 2; grid-row: 2; align-self: start; margin: var(--space-xl) 0 0; opacity: 0.8; line-height: 1.5; }
.fi-link { grid-column: 2; grid-row: 2; align-self: start; justify-self: start; margin-top: var(--space-lg); display: inline-flex; align-items: center; gap: var(--space-xs); border-bottom: 0.5px solid currentColor; padding-bottom: 2px;   /* 22.10 (Chiara: "more about the studio should be underlined"): back on, at the footer CTA’s 0.5px rest weight — the 21.10 "so thick" rule was the old 1px */ }   /* 12.10: in the quote's old seat under the statement, underlined (Chiara) */   /* 22.10 (Chiara: "this feels random placement?"): the link leaves the far-right seat and sits on the column’s left edge under the sign-off, one lg below — label, statement, sign-off, link, one stack */
body.home .fi-link { margin-top: var(--space-xl); }   /* 25.10 (Chiara: "increase the spacing between Chiara Colella and More about the studio"): lg → xl, 47 → ~71px on the homepage desktop */
.fi-link .menu-foot-arrow { width: 14px; height: 14px; transition: transform var(--dur-quick) var(--ease-house); }
.fi-link:hover .menu-foot-arrow { transform: translate(2px, -2px); }   /* the menu foot's nudge */
.fi-photo { grid-column: 1; grid-row: 1 / 3; justify-self: center; align-self: end; margin: 0 0 var(--space-sm); height: min(66svh, 658px); width: auto; max-width: 100%; aspect-ratio: 3 / 4; overflow: hidden; }   /* 12.10 v8 (Chiara: "move the image to the right more"): justify-self start → center in the left column · v7 (Chiara: "another 10% bigger"): 60svh/598px × 1.1 = 66svh/658px · v6 "15% bigger" · v5 "needs to be smaller" */   /* 12.10 v4 (Chiara: "add my photo on the left — left top"): the portrait top-left, stretched to the two rows above the name; the width follows the 3:4 */
.fi-photo img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 100%; display: block; }
.fi-photo--fpo { background: color-mix(in srgb, var(--ink) 7%, transparent); display: flex; align-items: center; justify-content: center; }   /* 21.10: the empty portrait seat on /photography (the Alex block) — same tint as .svc-founder-photo; the label sits centred at 0.45 */
.fi-photo--fpo .label { margin: 0; opacity: 0.45; }
/* the reveal: the house rise for the type (REVEAL list in main.js), a left → right wipe for the portrait */
.fi-photo.rv { opacity: 1; transform: none; --rv-d: 0ms; }   /* no sibling stagger on the portrait */
.fi-photo, .fi-who { translate: 0 var(--fiy, 0px); will-change: translate; }
body.home .fi-photo, .chapter-scroll .fi-photo { aspect-ratio: 3 / 4; grid-row: 1 / 4; align-self: center; height: min(82svh, 820px); margin: 0; }   /* 12.10 (Chiara: "and maybe image bigger?"): the name is off the photo now, so the portrait takes the whole left column — 66svh → 82svh, centred */
.fi-sign { margin: var(--space-md) 0 0; opacity: 0.65; }
@media (min-width: 901px) {
  body.home .fi, .chapter-scroll .fi { padding-left: var(--fi-margin); padding-right: var(--fi-margin); }   /* 12.10: mirrored outer margins, 100px both sides (was 110 left / 32 right) */
  body.home .fi-photo, .chapter-scroll .fi-photo { justify-self: start; }
}   /* 12.10 (Chiara: "place it as I've shared with you"): the homepage portrait keeps the file's own 2:3 — no crop — so the frame takes its shape; the caption width follows */
body.home .fi-who { width: calc(min(66svh, 658px) * 0.6667); }   /* 12.10 (Chiara: "a parallax light movement on the image so it lightly floats"): main.js drifts the portrait ±40px against the scroll; `translate` (not transform) so it never fights the reveal or the breathe */
@media (max-width: 900px), (prefers-reduced-motion: reduce) { .fi-photo, .fi-who { translate: none; } }
.fi-photo.rv img { transform: scale(1.06); transition: transform var(--dur-cinema) var(--ease-house); }   /* 12.10 v3 (Chiara: "I don't like the swipe"): the left→right wipe is GONE — the photo is simply there, settling gently from 1.06 as the section arrives, then breathing */
.fi-photo.rv.in-view img { transform: scale(1); animation: fi-breathe 24s var(--ease-ambient) var(--dur-cinema) infinite alternate; }   /* after the settle: a slow breathe, 1 → 1.05 over 24s and back, on the ambient curve (the What We Do panes' rhythm) */
@keyframes fi-breathe { from { transform: scale(1); } to { transform: scale(1.05); } }
@media (prefers-reduced-motion: reduce) { .fi-photo.rv img, .fi-photo.rv.in-view img { transform: none; animation: none; transition: none; clip-path: none; } }
@media (max-width: 900px) {
  .fi { grid-template-columns: 1fr; grid-template-rows: none; row-gap: var(--space-lg); min-height: 0; padding-top: var(--space-section-phone); }
  .fi-who, .fi-intro, .fi-quote, .fi-link, .fi-photo { grid-column: 1; grid-row: auto; }
  .fi-link { margin-top: 0; }   /* 23.10 (Chiara, phone In Short: the link "is random" — it floated 80px under the byline): the grid’s row gap (lg) is the whole distance; the desktop’s own lg top margin was doubling it */
  body.home .fi-link { margin-top: calc(var(--space-xl) - var(--space-lg)); }   /* 25.10 (Chiara: "increase the spacing between Chiara Colella and More about the studio"): the row gap (lg 40) + 24 = the xl step, 47 → ~71px sign-off → link on phones */
  .fi-sign { font-size: var(--type-caption); }   /* 25.10 (Chiara: "Chiara Colella, Founder & Creative Director — font can be smaller… the Aura Method text is a good size, the section above should be the same for consistency"): 14 → caption (12), the Method lead’s size */   /* 23.10 (Chiara, the sign + "More about the studio": "balance this font out"): 12 → the label rung (14), the same size as the link under it */
  .fi-statement { font-size: var(--type-serif-lg); }   /* 23.10 (Chiara, "We came from the world we design for…": "reduce this font a little bit"): origin (34) → serif-lg (30) on phones */
  .fi-intro, .fi-link { margin-inline: var(--measure-inset-phone); }   /* 23.10 (Chiara, the In Short text: "move this to the middle… not centred, the margins are weird"): the block stays LEFT-ALIGNED but sits in from the edges — 40px side margins (gutter + 24), the origin statement's phone measure */
  .method-intro--teaser.method-intro--teaser .method-intro-title.mi-lockup { z-index: 3; }   /* 23.10 (Chiara: "The Aura Method™ — can this go over the image"): on phones the title paints ABOVE the photo outright; the clipped cream twin (.mi-clip) that carries the letters over the image on desktop was not landing here */
  .method-intro--teaser .mi-clip { display: none; }   /* 23.10: the twin would double the line now the title is on top */
  @media (min-width: 375px) { .method-intro--teaser.method-intro--teaser .mi-clip { display: block; } .method-intro--teaser.method-intro--teaser .method-intro-title.mi-lockup.mi-lockup:not(.mi-over) { z-index: 0; } }   /* 25.10 (Chiara: "add the text effect over the centre of the text"): the cream twin returns on phones from 375 up, where The Aura Method sets on ONE line and the twin registers to the pixel (measured 390/430: identical boxes); the base title drops under the twin so the cream shows where it crosses the image. Below 375 the title wraps and the twin stays off */
  body.home .method-intro--teaser.method-intro--teaser .mi-clip .method-intro-title.mi-over, body.home.close-cream .method-intro--teaser.method-intro--teaser .mi-clip .method-intro-title.mi-over { color: var(--frost); text-shadow: none; }   /* 25.10 v8 (Chiara: "do it like the case study images — why are you adding a different effect?"): the case-study title treatment, exactly — cream at 58% so the photograph shows through the letters. It never read before because the solid base title was painted ON TOP of the photo, under the twin; the base title now sits UNDER the image on phones (z-index rule above), so over the photo only the 58% letters show. v3–v7 (ink, haze, glow, ink 60%) retired */
  body.home .site-footer .footer-video { --footer-bright: 0.5; }   /* 25.10 (Chiara, phone: "the footer needs a bit of a darker overlay — it's too busy"): the Caruso terrace darkens to 0.5 on phones (the whole photo is in view there — chairs, table, both figures) */
  .method-intro--teaser.method-intro--teaser .method-intro-lead { font-size: var(--type-caption); max-width: var(--measure-phone); }   /* 23.10 (Chiara: "check the margins, more margin on the side" / "reduce the font size a little"): label (14) → caption (12); 40px side margins like the origin statement */
  /* THE PHONE CTA (25.10, rollout Phase 1): one size (--type-cta-phone, 12) and one tracking (--track-cta) for every CTA on the phone. Phase 2 adds each page's CTAs to these two lists — never a new size. */
  .opening .hero-intro-cta, .origin .origin-cta, #founder .fi-link, #founder .founder-link, .method-intro--teaser .method-intro-link, body .svc-proof-head .cta { font-size: var(--type-cta-phone); }   /* 25.10 (Chiara: "View More button is too big — the same size as the other CTA"): the work-section head link (Branding View More, Web All Projects) joins the list */   /* 25.10 (Chiara: "just need to make sure they’re equal"): back to 14, every phone CTA on one size (spec: label 14) */   /* 23.10 (Chiara: "See where it takes you — reduce the font size a little"): 14 → 12 with the lead */   /* 25.10 (Chiara: "make sure they’re equal"): View Our Work 12 → 14 on phones, with every other CTA */   /* 25.10 v2 (Chiara: "View Our Work too big — reduce by 1 or 2px, and make sure the other CTAs are the same size"): every phone CTA reads ONE token now */
  body .origin .origin-cta, body #founder .fi-link, body #founder .founder-link, body .method-intro--teaser .method-intro-link, body .site-footer .footer-ask .cta, body .svc-proof-head .cta { letter-spacing: var(--track-cta); gap: 0; }   /* 25.10 (Chiara: "View Our Work looks bigger than Start a project — make sure whatever you've used for View Our Work, we do the same"): same 12px, but View Our Work tracks 0.04em with no letter gap — the others ran 0.05em gap / no tracking (7.45 vs 7.73px a letter). Every phone CTA now tracks like View Our Work */
  body.home .fi-photo { height: min(60svh, 600px); width: min(calc(min(60svh, 600px) * 0.75), 100%); height: auto; justify-self: center; }   /* 25.10 (W5 audit S1): the 60svh-tall portrait was wider than its column on every phone (380 in a 358 column at 390×844) and gave the whole page a sideways scroll — the width now caps at the column and the 3:4 sets the height */   /* 23.10 (Chiara: "move the photo of me to the middle, centred"): the portrait sits centred in its column on phones */   /* 23.10: width follows the 3:4 (the column was stretching it to full width and the cover-crop went square) */   /* 23.10 (Chiara: "reduce the size of the image of me, looks way too big"): 82svh → 60svh on phones (601 → 440px tall at 733) */
  body.home .fi-photo { overflow: hidden; } body.home .fi-photo img { height: 112%; translate: 0 var(--fipy, -5%); will-change: translate; }   /* 25.10 (Chiara: "a slight parallax… on the image of me"): 12% of spare height for the drift (main.js --fipy, 0 → −10%) */
  .disc.disc.disc--voices { padding-top: var(--space-section-phone); padding-bottom: var(--space-xl); }   /* 23.10 (Chiara: "reduce the space above What Our Clients Say" / "above Trusted By"): 3xl/3xl → 2xl/xl on phones */
  .disc.disc--voices .disc-sticky { padding-top: 0; padding-bottom: 0; }   /* 25.10 (Chiara: "reduce the space above Trusted By… match what’s below it"): the bottom 80 goes too — arrows → label was 276 against 191 under the logos */   /* 23.10: the 80px header clearance is a desktop-sticky thing — the stage is static on phones */
  .disc.disc--voices .disc-voice { margin-top: 0; }   /* 23.10: the 2xl desktop offset — 316px sat above the label, now the section's 2xl alone */
  .disc-voice .voice { transition: opacity var(--dur-cinema) var(--ease-house), transform var(--dur-cinema) var(--ease-house); }   /* 25.10 (Chiara, phone: "What our clients say should nicely fade on the testimonials… triggered when it comes onto screen"): the first quote rises and fades in slowly as the section arrives */
  .disc-voice.voices-started .voice, .disc-voice.voices-started .voice.out { transition: none; }   /* 25.10 (Chiara: "the other ones should just cleanly change — it shouldn't re-fade, it's a bit too much"): after the entrance, arrows and the timer swap quotes with a clean cut */
  .disc.disc.disc--crown { padding-top: var(--space-section-phone); padding-bottom: var(--space-section-phone); }   /* 23.10 (Chiara: "reduce the spacing above the What We Do gap a bit — and below"): 3xl (140) → 2xl (96) both ends on phones */
  .disc.disc--crown .disc-sticky { padding-top: var(--space-stage-phone); padding-bottom: var(--space-stage-phone); }   /* 23.10: the stage's 80px desktop clearance → lg (40) — 220 → 136 above the label and below the last row */
  .disc.disc .disc-row sup { font-size: var(--type-label); margin-right: var(--space-sm); line-height: 0; vertical-align: baseline; position: relative; top: -1.1em; }   /* 23.10 v3 (Chiara: "the lines aren't equidistant between the words… move Strategy & Branding closer to the number"): the super-aligned index was growing each row's first line box by 4px, so the words sat low between the hairlines — line-height 0 + a relative lift keeps the index out of the line box; 24 → 16px to the word */   /* 23.10 v2 (Chiara: "01 02 03 04 — increase by 2px"): caption (12) → label (14) */   /* (0,3,1) 23.10 (Chiara: "bump 01 02 03 04 font size"): the mono index was 0.18em of the row (≈6px on a phone) — it takes the caption rung (12px), the phone’s bracket-heading size */
  .disc.disc--crown .disc-row { font-size: var(--type-disc-phone); line-height: 1.08; padding: var(--space-md) 0; text-box: trim-both cap alphabetic; }   /* 25.10 (Chiara: "the lines still aren't equidistant between the text and the numbers… the numbers aren't centred to those words"): the spec row (03 What We Do: display 36, line 1.08) replaces the vh+vw formula (37.0 on a 390×844, a different size on every phone height); the line box is trimmed to cap height and baseline so the md padding IS the gap — rule → cap top and baseline → rule both 24, where the font's own ascender left 25.5 above and 24 below */
  .disc.disc--crown .disc-row sup { top: calc((0.73em - 0.743 * var(--type-disc-phone)) / 2); }   /* 25.10: the index CENTRES on its word — the midpoint of the 14px mono figures (cap 0.73em) sits on the midpoint of the Eiko caps (0.743 of 36). Was top −1.1em, which hung the figures 7px above the word's centre */
  .site-header .wordmark { transition: translate var(--dur-base) var(--ease-house); }
  body.home.logo-away:not(.menu-open) .site-header .wordmark { translate: 0 calc(-100% - var(--space-lg)); }   /* 25.10 (Chiara: "the Aura logo on mobile shouldn't cover any of the text"): the wordmark slides up out of the frame while scrolling down (main.js .logo-away), back on scroll up — 'translate', so the homepage's own logo hand-off transforms are untouched */
  .disc.disc.disc--crown .disc-main { opacity: 1; transform: none; transition: none; }   /* 25.10 (Chiara, phone: "they need to stagger load in, so the text reveals… the lines need to grow"): the whole-block 1.5s fade retires on phones — each row carries its own entrance now */
  .disc.disc--crown .disc-row { padding: calc(var(--space-lg) - 2px) 0; }   /* 25.10 v2 (Chiara: "reduce the line spacing between What We Do very slightly, maybe 2 pixels"): 40 → 38 rule-to-type. v1 (Chiara: "space those out a bit more"): md (24) → lg (40) rule-to-type on phones — the desktop row step */
  .disc.disc--crown.disc--crown .disc-row::before, .disc.disc--crown.disc--crown .disc-row:last-child::after { transform: scaleX(0); transition: transform var(--dur-cinema) var(--ease-house); transition-delay: 0s; }
  .disc.disc--crown.disc--crown .disc-row.row-in::before, .disc.disc--crown.disc--crown .disc-row.row-in:last-child::after { transform: scaleX(1); }   /* 25.10: each hairline GROWS from the left as its row scrolls in (main.js .row-in, one row at a time), on the cinema rung; the closing rule draws with the last row */
  .disc.disc--crown .dr-in { display: block; padding-block: 0.5em; margin-block: -0.5em; clip-path: inset(0 -0.3em); }   /* 25.10 v4 (Chiara, phone: "the g's are being cut off in What We Do"): the mask's room above and below 0.3 → 0.5em — Safari draws Eiko's descenders deeper than Chrome (Chrome had 6px spare at 0.3em; Safari clipped the g tails). The matching negative margin keeps the row spacing exactly as it was. v3 (Chiara: "I don't like that reveal-up — it needs to be like the hover on View Our Work, that effect on the words"): the box is the roll's MASK now (padded 0.3em so descenders sit inside it, the negative margin keeps the spacing); the v1/v2 wipe-up retires */
  .disc.disc--crown .dr-in .ch { display: inline-block; transform: translateY(115%); will-change: transform; transition: transform var(--dur-slow) var(--ease-house) calc(0.15s + var(--i, 0) * 22ms); }   /* 25.10 v4 (Chiara: "the way they're loading is a bit jolty — make it nice and smooth"): the in-out curve (slow start, snap) → the house EASE-OUT on the slow rung, letters 22ms apart — each one leaves at once and settles; main.js also queues the rows ≥160ms apart */
  .disc.disc--crown .dr-in .ch { padding-block: 0.3em; margin-block: -0.3em; }   /* 27.10 (Chiara, phone again: "the g's are cut off in What We Do"): iOS Safari crops each composited letter at its own box — the descender of g/y fell outside it. The letter box now carries the room, and the layer is released once seated. */
  .disc.disc--crown .disc-row.row-in .dr-in .ch { transform: translateY(0); will-change: auto; }   /* 25.10 v3: THE LETTER-ROLL — each letter rises into place one after another (View Our Work's curve and 18ms step), a beat after the row's hairline starts to grow (main.js splits the words on phones) */
  @media (prefers-reduced-motion: reduce) { .disc.disc--crown .dr-in .ch { transform: none; transition: none; } }
  body.home .fi-statement { color: inherit; }   /* 25.10 (W5 audit S2): the In Short statement held --title-ink while the ground under it darkened to near-black (≈1.2:1 mid-scroll) — it now follows the close-rider's ink → cream flip like its labels */
  body.home .hw-slide .hw-title { font-size: var(--type-deck-phone); line-height: 1.08; top: calc(50% - 14px); }   /* 25.10 (Chiara, LHC on her phone: "font way too big — reduce the case study hero text by 3px"): 53 → 50; the bylines re-seated below for the 12px gap */   /* 23.10 v4 (Chiara, LHC: "improve the line spacing"): the three stacked lines were set solid (1.0) → 1.08 · the title+byline PAIR re-centred on the slide: title centre 14px above the middle = half of (12px gap + 16px byline) */   /* 23.10 v3 (Chiara: "Palmer… 3px"): 50 → 53; the pair re-centred */   /* 23.10 (Chiara, phone deck: "the main header, like 4px more" → "header 2px more"): the clamp’s 44px floor + 6 · "centralise the title and byline": the PAIR is centred on the slide — the title rides 24px above the centre line, the byline 26px below it (half the pair’s height) */
  body.home .hw-slide .hw-byline { font-size: var(--type-caption); top: calc(50% + 25px); }   /* 25.10: −14 + 27 (half of 50 × 1.08) + 12 */   /* 23.10 v4: 12px under a one-line 53px title at 1.08 (title centre −14 + 28.6 + 12) */
  body.home .hw-slide .t-full { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; } body.home .hw-slide .t-short { display: inline; }   /* 25.10 (Chiara: "LHC — can we change it to LHC? It just looks too big"): on phones the title and its Next: read LHC (one line, so its byline takes the shared 25px seat — the three-line 79px override retires); the full name stays for screen readers and desktop */
  body.home .hw-title sup { font-size: var(--type-caption); opacity: 0.9; }   /* 25.10 (Chiara: "the numbers on the case study are too small, I can't read them"): 0.2em (10px) → caption 12, a touch stronger */
  body.home .hw-slide.hw-slide .hw-next { bottom: var(--space-lg); }   /* 25.10 (Chiara: "Next is cut off, too close to the bottom of the iPhone — more padding, everything moves up"): 19 → 40 off the bottom edge */
  body .sound-toggle { bottom: var(--space-lg); }   /* 25.10: the sound toggle rides the same 40px seat as Next */
  body.home .hw-slide .hw-tag { font-size: var(--type-serif-sm); bottom: calc(var(--space-xl) + var(--space-lg)); padding-inline: var(--gutter); box-sizing: border-box; text-wrap: balance; }   /* 25.10 (Chiara: "everything moves up"): 80 → 104 off the bottom, riding up with Next    /* 25.10 (Chiara: "the byline — Strategy, Brand Identity, Website, Film — also needs to be smaller"): 21 → serif-sm (19 on phones), the ladder rung    /* 25.10 (W5 audit S1/S3): the services line ran the full screen width with no margin — Zambala/LHC at 360 and Odisi at 390 sat 0.1–0.5px from the edges, and "Film )" was left alone on its line; gutter both sides and a balanced rag */   /* 23.10 v2 (Chiara: "the strategy, brand identity line — 3px bigger, and move it up"): label-lg (18) + 3; foot seat xl (64) → 80, 24px of air over Next */   /* 23.10 (Chiara, the services line: "the bylines too big… smaller… move it up a bit"): 22 → the label rung (14) → "increase by 4px" → label-lg (18); it seats at xl (64) from the foot instead of lg (40) */
  body.home .hw-slide .hw-next { bottom: calc(var(--space-sm) + 3px); top: auto; }   /* 23.10 v2 (Chiara: "Next: Odisi — move inline with the On button"): the sound toggle is fixed 16px from the foot and 23px tall; Next (16px line) seats so the two share a centre line */   /* 23.10 (Chiara: "Next: Odisi — one line and towards the bottom"): seats at lg (40) from the foot, under the services line */
  body.home .hw-slide .hw-next br { display: none; }   /* one line: "Next: Odisi" */
  .fi-quote { margin-top: 0; }
  .fi-photo { width: 100%; height: auto; justify-self: stretch; }
  .fi { height: auto; }
}

/* ============================================================
   THE CHAPTER SCROLLER (/studio, 12.10) — the homepage founder chapter as
   panel one, then the story travelling in from the right. A pinned
   viewport, a horizontal track, the page's vertical scroll turned sideways
   by main.js (--ch-max is the track's overflow; the section's height is
   one screen plus that travel). Phones + reduced motion: unpinned, stacked.
   ============================================================ */
.chapter-scroll { position: relative; height: calc(100svh + var(--ch-max, 0px)); }   /* 12.10: was .ch — which collided with the letter-roll's per-character spans (class "ch"), giving every rolling letter a full-screen height */
.ch-vp { position: sticky; top: 0; height: 100svh; overflow: hidden; }
.ch-track { display: flex; height: 100%; width: max-content; will-change: transform; }
.ch-panel { flex: none; height: 100%; }
.ch-panel--chapter { width: 100vw; }
.ch-panel--chapter .fi { height: 100%; min-height: 0; }
.ch-panel--text { width: min(60vw, 820px); padding: calc(var(--space-lg) + var(--header-h, 0px)) var(--gutter) var(--space-2xl); display: grid; align-content: center; gap: var(--space-md); }
.ch-panel--text .label { margin: 0 0 var(--space-sm); opacity: 0.65; }
.ch-panel--text p:not(.label) { margin: 0; font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: 1.6; max-width: 48ch; }
.ch-panel--text .ch-quote { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-lg); line-height: 1.25; max-width: 24ch; color: var(--title-ink); }
.fi-cue { grid-column: 2; grid-row: 2; align-self: start; justify-self: end; margin: var(--space-xl) 0 0; opacity: 0.6; }   /* 12.10: in the link's seat, right-aligned as on the homepage */
.ch-end-link { margin-top: var(--space-lg); grid-column: auto; grid-row: auto; }   /* 12.10: the homepage scroller ends on the studio link */   /* the chapter's scroll cue, in the link's old seat */
@media (prefers-reduced-motion: reduce) {   /* 25.10: reduced motion only — phones ride the track now (the /studio phone block sizes it); was (max-width: 900px), (prefers-reduced-motion: reduce) */
  .chapter-scroll { height: auto; }
  .ch-vp { position: static; height: auto; overflow: visible; }
  .ch-track { display: block; width: auto; transform: none !important; }
  .ch-panel, .ch-panel--chapter, .ch-panel--text { width: auto; height: auto; }
  .ch-panel--chapter .fi { height: auto; }
  .fi-cue { display: none; }
}

/* THE BLEED GROW (/studio, 12.10) — the homepage deck's mask-grow on a single image: a 200svh runway, a sticky screen,
   the clip window opening from a framed plate (top 8svh, sides 30vw, bottom 30svh at --grow 0) to the full screen. */
.bleed-grow { position: relative; height: 200svh; margin: var(--space-section) 0 0; }
.bleed-grow-pin { position: sticky; top: 0; height: 100svh; overflow: hidden;
  clip-path: inset(calc((1 - var(--grow, 0)) * 8svh) calc((1 - var(--grow, 0)) * 30vw) calc((1 - var(--grow, 0)) * 30svh)); will-change: clip-path; }
.bleed-grow-pin img, .bleed-grow-pin video { width: 100%; height: 100%; object-fit: cover; object-position: 50% 80%; display: block; transform: scale(calc(1.1 - var(--grow, 0) * 0.1)); }   /* 80%: her hand on the laptop in frame at full grow (60% cut at the waist) */
@media (max-width: 900px), (prefers-reduced-motion: reduce) {
  .bleed-grow { height: auto; }
  .bleed-grow-pin { position: static; height: 76svh; clip-path: none; }
  .bleed-grow-pin img, .bleed-grow-pin video { transform: none; }
}

/* 22.10 (the Venice window hero, rendered): the case-study FROST on the title and the taupe lead went grey over the pale
   lagoon and the foot fade — on this page the hero type runs solid cream, the origin’s 29.08 ruling. */
body.studio-page .svc-hero--media :is(.svc-title, .svc-eyebrow, .svc-lead) { color: var(--frost); opacity: 1; }

.chapter-scroll .fi-photo { height: min(72svh, 720px, calc((50vw - 2 * var(--fi-margin) - var(--space-lg)) * 4 / 3)); }   /* 27.10 (Chiara's screenshot: the portrait running into "We came from the world"): the photo was sized by the window's HEIGHT only, so on narrower or taller laptop windows (920–1160 wide) it grew past the half-way line into the statement, up to 194px over. Capped by the width too: it always stops one lg short of the text column. */
.ch-panel--chapter .fi { grid-template-rows: 1fr auto auto 1fr; padding-top: var(--space-2xl); padding-bottom: var(--space-2xl); }   /* 22.10 (Chiara’s 15.01 screenshot: "centre this to the section height"): the old auto/1fr/auto rows pinned the statement to the top and left the lower half empty — two flexible rows now bracket the statement + cue, so the text centres on the panel; the portrait spans every row and centres on the same height; equal 2xl above and below */
.ch-panel--chapter .fi-intro { grid-row: 2; gap: var(--space-lg); }   /* 25.10 (Chiara, the studio In Short: "can you add more spacing"): the label, statement and sign-off step md (24) → lg (40) apart on /studio only; the homepage keeps md */
.ch-panel--chapter .fi-cue { grid-row: 3; }
@media (min-width: 901px) { .ch-panel--chapter .fi-photo { position: absolute; top: calc(50% + var(--space-lg)); left: var(--fi-margin); translate: 0 -50%; grid-row: auto; align-self: auto; margin: 0; } }   /* 22.10 (Chiara: "can you make it middle"): OUT of the grid — the portrait is pinned to the panel’s vertical centre line, independent of rows, padding or arrival state; the text column keeps the grid. 22.10 v2 ("too high now", after a 96 drop was "move it up"): one lg (40) below the centre line — the optical seat between the two. */

/* THE REEL LIGHTBOX (22.10, phones — see index.html): full-screen dark box, the showreel as a 16:9 film with native controls. */
.reel-lightbox { position: fixed; inset: 0; z-index: 1000; background: var(--ground-3); color: var(--cream); display: grid; place-items: center; padding: var(--gutter); }
.reel-lightbox[hidden] { display: none; }
.reel-lightbox-video { width: 100%; max-height: calc(100svh - 2 * var(--gutter)); aspect-ratio: 16 / 9; background: #000; display: block; }
.reel-lightbox-frame { position: relative; width: 100%; }   /* 23.10: the film and its close share one frame, so the × sits ABOVE THE VIDEO’s top-right corner, not the screen’s */
.reel-lightbox-close { position: absolute; top: calc(-1 * (40px + var(--space-xs))); right: calc(-1 * var(--space-xs)); width: 40px; height: 40px; display: grid; place-items: center; margin: 0; background: transparent; border: 0; padding: 0; color: var(--cream); font-size: var(--type-label-lg); line-height: 1; cursor: pointer; }   /* 23.10 (Chiara: "a smaller × on the top right above the video"): a 40px touch target, the × glyph at the label-lg rung, its right edge on the video’s */
body.reel-open { overflow: hidden; }

/* THE TEAM (22.10, /studio — Chiara: "and I need a team section"): the ledger’s statement head, then four portrait
   seats in a row — 3:4 frame, serif name, mono role, one line of copy. Empty seats take the /photography tint until
   the portraits land. Ground + type inherit the page ground (it sits between the How We Work ledger and What We Do). */
body.studio-page .team { padding: var(--space-section) var(--measure-inset) 0; background: var(--ground-3); color: var(--cream); }   /* 25.10 (Chiara: "the margins the same as What We Do on the homepage"): gutter → --measure-inset, the homepage 1440 column with 2em sides (32 at 1440 and on phones, centred on wider screens — 272 at 1920) */   /* 22.10 v3 ("spacing is off on the bottom too"): no bottom padding — What We Do’s own 140 top carries the seam, as the ledger’s last rung → team is 120 */
body.studio-page .svc-ledger-stage { --ledger-last: 40svh; }   /* 22.10 (Chiara: "review the spacing — some gaps are big, some are small"): the last rung’s open room, 64svh sitewide, left 346px of empty dark under 003’s copy before the Team label (every other seam is 120). 40svh keeps the pinned beat (room for the three rungs + copy) and halves the tail. */
body.studio-page .svc-ledger-stage + .team { padding-top: var(--space-xl); }   /* and the Team meets it one step closer — the ledger’s tail already carries the air */
body.studio-page .team .cs-stack-text { padding: 0; }   /* the statement head carries a full section padding of its own (.cs-stack-text, v7) — inside the team block that doubled to 284 above the label and inset it a second gutter; the block’s padding is the padding */
@media (min-width: 901px) { body.studio-page .team + .disc--split { margin-top: 0; } }   /* the -18svh tuck is the HOMEPAGE origin-tail rule — under the team it rode 162px up over the last cards */   /* 22.10 v2 (Chiara: "it’s not changing colour"): the closing-ride paint (.close-rider, later in the sheet, same weight) was winning and holding the block on paper until the final 3vh of the page — the team’s own dark now outranks it, as What We Do’s router-dark does */   /* 22.10 (Chiara: "something weird is happening with the background"): the ledger stage holds ITS dark to its end and What We Do is router-dark — the team sat between them on the page’s cream, a cream band between two darks. It takes the same dark, so the run is one surface ledger → team → What We Do. */
.team-grid { list-style: none; margin: var(--space-xl) 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, var(--team-seat-w)); justify-content: space-between; gap: var(--space-lg) var(--space-lg); }   /* 25.10 v2 (Chiara: "the three images are too big now"): back to the seat token, 380 (was 1fr ≈ 432), spread edge to edge so the two gaps stay equal */   /* 25.10 (Chiara: "three images equidistant apart, bigger images"): three equal columns across the full measure (350 → ~432px each at 1440), one lg gap between — was auto-fill at the 350 seat token */   /* 22.10 (Chiara: "reduce the size of the headshots" → "too big" at the 4-up ~400): the seats take a fixed width token, --team-seat-w (300 → a 300×400 portrait), and flow left — the dial is the token */
.team-card { margin: 0; }
.team-seat { margin: 0; aspect-ratio: 3 / 4; overflow: hidden; }
.team-seat img { width: 100%; height: 118%; margin-top: -9%; object-fit: cover; object-position: 50% 20%; display: block; translate: 0 var(--wpy, 0px); will-change: translate; }   /* 25.10 (Chiara: "a float — they move up and down slightly within the frame"): 118% headroom and the --wpy drift (main.js updateWorkParallax), the homepage case-card grammar */
.team-seat--fpo { background: color-mix(in srgb, currentColor 7%, transparent); display: flex; align-items: center; justify-content: center; }
.team-seat--fpo .label { margin: 0; opacity: 0.45; }
.team-name { margin: var(--space-md) 0 0; font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.1; letter-spacing: -0.02em; }
.team-role { margin: var(--space-xs) 0 0; opacity: 0.65; }
.team-copy { margin: var(--space-sm) 0 0; max-width: 26ch; }
@media (max-width: 900px) {
  .team-grid { grid-template-columns: 1fr; gap: var(--space-xl); }   /* 25.10: phones stack the three full-width, xl apart (the 2-up left one portrait alone on its row) */
  body.studio-page .team { padding-left: var(--inset-phone); padding-right: var(--inset-phone); }   /* 25.10: phones pin the What We Do edge (32) — --measure-inset is 2em, and this section's smaller type made it 30 */
  .team-copy { max-width: none; }
  /* 25.10 THE TEAM RIDE (Chiara, phone: "our photos should be smaller, and maybe they're scrolling across — the image of me is
     two-thirds, and then you can see the next person… as you're scrolling it goes across, like what we've done on the homepage").
     Base (no script, reduced motion): one row you can swipe, portraits two-thirds of the screen, the next one peeking at the edge.
     With main.js (.team--ride): the section becomes a runway (--team-travel, measured), one sticky screen holds the label and the
     row, and the row slides left as you scroll down (--team-p 0 → 1) — the origin strip's grammar. */
  body.studio-page .team-grid { display: flex; gap: var(--space-md); overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; margin-right: calc(-1 * var(--inset-phone)); padding-right: var(--inset-phone); }
  body.studio-page .team-grid::-webkit-scrollbar { display: none; }
  body.studio-page .team-card { flex: none; width: 66vw; scroll-snap-align: start; }
  body.studio-page .team.team--ride, body.studio-page .svc-ledger-stage + .team.team--ride { height: calc(100svh + var(--team-travel, 0px)); padding-top: 0; padding-bottom: 0; }   /* the runway is exactly the travel — the section's 64s of air would have shortened the pinned run */
  body.studio-page .team--ride .team-pin { position: sticky; top: 0; height: 100svh; display: flex; flex-direction: column; justify-content: flex-start; padding-top: calc(var(--header-h) + var(--space-sm)); box-sizing: border-box; margin-inline: calc(-1 * var(--inset-phone)); padding-inline: var(--inset-phone); overflow: hidden; }   /* full-bleed so the row clips at the SCREEN edge, not the margin — and never widens the page */   /* v2 (Chiara: "too much of a gap before the team"): label + row seat at the TOP of the pinned screen, under the header (as How We Work's label pins), not centred — 338 → ~200 from the last card's copy to ( The Team ) */
  body.studio-page .team--ride .team-grid { overflow: visible; scroll-snap-type: none; margin-right: 0; padding-right: 0; translate: calc(-1 * var(--team-p, 0) * var(--team-travel, 0px)) 0; will-change: translate; }
}

/* 22.10 FULL-BLEED METHOD DECK (Chiara: "let’s just scrap the small image thing and do full bleed images on the page"):
   each phase still fills the pinned stage, a scrim holds the type, and the type runs cream in every ground state. The
   seated-portrait lockup and its clipped twin (.method-clip) are retired — not deleted — above. All widths. */
/* .method-pin .method-media { position: absolute; inset: 0; left: 0; top: 0; width: 100%; height: 100%; transform: none; margin: 0; }  RETIRED 25.10 (Chiara: "the head is normal, and then it's the Immersion divider that has the image of the text"): the phases return to the seated lockup — the still FRAMED in the middle (--method-media-w/h), the title crossing it, the frosted twin (.method-clip) over the photo */
.method-pin .method-media::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  background: color-mix(in srgb, var(--ground-3) 20%, transparent); }   /* 25.10: the framed seat takes the homepage frame's light tint (the full-bleed 70/38/48 wash was for type over a whole-screen photo) */   /* 24.10 (Chiara, the brighter Belmond set: "images need a dark overlay on them"): black → the site dark, and every stop deeper — 0.58/0.22/0.32 → 0.70/0.38/0.48 */   /* 22.10: centre band 0.12 → 0.22 so white holds on the dunes and the stone */
.method-pin .method-media img { width: 100%; height: 100%; object-fit: cover; display: block; filter: none; }
/* .method-pin .method-clip { display: none; }  RETIRED 25.10 — the frosted title twin returns with the framed seat */
body.method-page .method-sticky, body.method-page .method-pin.mg-light .method-sticky, body.method-page .method-pin.mg-dark .method-sticky { color: var(--cream); }
.method-pin .method-title, .method-slide .method-title { color: inherit; }
.method-slide, body.method-page .method-sticky, body.method-page .method-pin.mg-light .method-sticky, body.method-page .method-pin.mg-dark .method-sticky { color: var(--cream); }   /* 27.10 (Chiara: "make sure the font is the cream colour on the aura method page"): white → cream */   /* 22.10 (Chiara: "change the colour of the text" → "needs to be WHITE on the images so you can read it"): pure white, every phase — an ink pass on the two pale frames was tried and reversed within minutes */

/* 22.10 (Chiara: "can you do the swipe effect like how we have the case studies — so it’s consistent"): the deck WIPES UP,
   the case deck’s .hw--up mechanic verbatim — clip opens from the bottom edge on the 0.9s cubic-bezier(0.76, 0, 0.19, 1),
   the still pre-drags 10% from below and settles from 1.16 → 1.06. Slides at or before the current one carry .shown and
   stay open underneath, so scrolling back closes the top one over the previous. .active still gates the counter. */
.method-pin .method-slide { opacity: 1; visibility: visible; clip-path: inset(100% 0 0 0); transition: clip-path var(--dur-slow) cubic-bezier(0.76, 0, 0.19, 1); will-change: clip-path; }
.method-pin .method-slide.shown, .method-pin .method-slide.active { clip-path: inset(0 0 0 0); }
.method-pin .method-media img { transform: translateY(10%) scale(1.16); transition: transform var(--dur-slow) cubic-bezier(0.76, 0, 0.19, 1); will-change: transform; }
.method-pin .method-slide.shown .method-media img, .method-pin .method-slide.active .method-media img { transform: translateY(0) scale(1.06); }
@media (prefers-reduced-motion: reduce) { .method-pin .method-slide { transition: none; } .method-pin .method-media img { transform: none; transition: none; } }

/* 22.10 (Chiara: "remove the footer on this page — the reveal"): /the-aura-method/ runs a PLAIN footer. The fixed stage, the
   36vh + xl clearance band and main's bottom mask-melt are off here (the /work gallery’s no-reveal pattern); the footer
   sits in flow straight after the deck’s dark close, same stage + ask, same height. */
body.page-interior.method-page:not(.case-study) .above-footer { margin-bottom: 0; padding-bottom: 0; mask-image: none; -webkit-mask-image: none; }   /* 0,4,1 — outranks the page-interior clearance rules (0,3,1) further down the sheet */
body.page-interior.method-page:not(.case-study) .above-footer::after { display: none; }
body.page-interior.method-page .site-footer--reveal, body.page-interior.method-page .footer-spacer { display: none; }   /* 22.10 later (Chiara: "remove the footer on the Aura Method page I think"): the footer is OFF the page — it ends on Sustain’s dark close, as the /work gallery ends on its deck. The in-flow footer (one step before) was `position: relative; inset: auto; z-index: 1` on the footer + the spacer hidden. */
body.page-interior.work-list .site-footer--reveal, body.page-interior.work-list .footer-spacer { display: none; }   /* 25.10 (Chiara: "the list view should have no footer on the Work page"): in list view the reveal footer and its spacer are off (gallery view keeps them) — the Method page's rule, keyed to body.work-list (work.js) */
body.page-interior.work-list:not(.case-study) .above-footer { margin-bottom: 0; padding-bottom: 0; mask-image: none; -webkit-mask-image: none; }
body.page-interior.work-list:not(.case-study) .above-footer::after { display: none; }

/* THE METHOD PAGE GROUND (12.10, Chiara: "load dark, and as you scroll it changes to the cream — when it reaches full
   vph it becomes cream and stays cream"): the phase deck arrives on the dark ground and crossfades to paper as its top
   climbs the viewport; main.js latches --mg at 1 once the deck fills the screen, so scrolling back up never returns it to
   dark. Type follows the ground (cream on dark, ink on paper); the clipped over-image titles keep their own colour. */
body.method-page .method-sticky { background: color-mix(in srgb, var(--ground-3) calc(var(--mg2, 0) * 100%), color-mix(in srgb, var(--paper) calc(var(--mg, 0) * 100%), var(--method-dark))); color: var(--cream); transition: color var(--dur-base) var(--ease-house); }   /* 20.10 v3 (Chiara: "the background transition needs to be super smooth, like the home page"): the dark close is SCRUBBED, not cut — --mg2 (main.js, 0→1 across the deck's last scroll segment) mixes the latched paper down into --ground-3 the way --mg mixed the dark up into paper at the deck's start. Reversible with the thumb, like every homepage ground. */
body.method-page .method-pin.mg-light .method-sticky { color: var(--ink); }
body.method-page .method-pin, body.method-page .method-pin .method-sticky, body.method-page .method-pin.mg-light .method-sticky, body.method-page .method-pin.mg-dark .method-sticky { background: var(--ground-3); color: var(--cream); }   /* 25.10 (Chiara: "make sure the background is the correct colour — the dark colour"): the phases sit on the site dark throughout; the paper / method-dark ground mix (--mg / --mg2) no longer shows */
/* 20.10 (Chiara: "5 should change to a dark background so that the bottom bit blends", then "there's a line on the
   last image before the footer", then "super smooth, like the home page"): SUSTAIN CLOSES DARK, ON A SCRUB — the
   seat ends on --ground-3 (the melt's own dark; the warmer method dark drew a tone line at the film's top edge),
   and it arrives via the --mg2 mix above, scrubbed across the last fifth of the pin instead of snapping with the
   cut. The type flips with the ground via .mg-dark (main.js, mg2 ≥ 0.5), outranking .mg-light's ink below. */
body.method-page .method-pin.mg-dark .method-sticky { color: var(--cream); }
/* 12.10 (Chiara: Immersion "needs to be the opposite of the image, so you can read it"): over the Luvia tablet the
   title INVERTS per pixel — white type on a difference blend reads dark over the linen and light over the tablet
   screen. Immersion only; the other phases keep their set colour. */
/* 22.10 (Chiara: "the text over the image needs the effect we have used on the intro section"): the difference blend
   RETIRED here as on the origin (21.10) — it painted the over-image title in the negative of the frame, cold over
   warm. Immersion and Define now carry the same solid cream clipped twin as the intro and phases 3–4. Sustain's
   white studio keeps its ink twin (cream vanished over it, 12.10). */
.method-slide:is([data-phase="1"], [data-phase="2"]) .method-clip { mix-blend-mode: normal; }
/* 12.10 (Chiara: "blur the line of the video too so it all melts into the section below"): the hero film's foot fades
   into the deck's dark ground — a gradient over the bottom third to --ground-3, under the existing 0.3 scrim, so the
   film and the dark section below are one surface with no edge. */
body.method-page .method-intro-media::after { background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 45%, color-mix(in srgb, var(--method-dark) 70%, transparent) 78%, var(--method-dark) 100%), rgba(0, 0, 0, 0.3); }   /* v2: a longer, softer fade (from 45%) */
/* 12.10 v2 (Chiara: "blur the edge of the video with a fade on it"): the film's foot also BLURS out — a backdrop blur
   over the bottom 40%, masked so it grows from nothing to full toward the edge, under the colour fade above. */
body.method-page .method-intro-media::before { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 40%; z-index: 1; pointer-events: none;
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 85%); mask-image: linear-gradient(to bottom, transparent, #000 85%); }
body.method-page .method-intro-media::after { z-index: 2; }
body.method-page .method-intro-copy { position: relative; z-index: 3; }
/* 22.10: the Method hero is the RIDE HERO now (see the-aura-method/index.html) — the same foot fade + blur into the deck's
   dark ground, retargeted from .method-intro-media to the ride's .svc-hero-bg; the type layers already sit above (z 1). */
body.method-page .svc-hero-bg::before { content: none; }
body.method-page .svc-hero::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 40vh; z-index: 2; pointer-events: none; background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--ground-3) 65%, transparent) 55%, var(--ground-3)); }   /* 25.10 v2 (Chiara: "you can do a fade between the hero and the Method section"): a plain colour fade — the photo dissolves into the site dark over its last third, so the hero hands over to the dark phases with no edge; no blur. v1 removed the 22.10 frosted band (Chiara: "why does the bottom of the image have that weird blur on the Aura Method hero?"): the 22.10 frosted band (backdrop blur 18px over the bottom 40%, masked in) RETIRED — it was the melt into the deck's dark ground, which the hero no longer does */
body.method-page .svc-hero-bg::after { z-index: 2; }   /* 25.10 (Chiara: "are we overkilling the scrim? do what we've done for all the case studies"): the Method's own three-layer wash (bottom fade to the site dark at 100%, top band, 30% flat) RETIRED — the hero now takes the default .svc-hero-bg::after, which is the case-study hero scrim verbatim (20% flat, 38% top band, 46% foot, 18% base). Was: background: linear-gradient(to bottom, transparent 45%, ground-3 70% at 78%, ground-3 100%), linear-gradient(to bottom, ground-3 22%, transparent 22%), ground-3 30% */
body.method-page .svc-hero--media .svc-title .tm { font-size: 0.25em; }   /* the 22.10 half-size ™ follows the title into the ride hero · later that day ("™"): 0.4 → 0.25em */
body.method-page .svc-hero--media, body.method-page .svc-hero--media .svc-hero-overlay { color: var(--cream); }
/* body.method-page .svc-hero--ride .svc-hero-line { max-width: 37ch; }  RETIRED 25.10 ("exact same as Studio") */   /* 22.10 (Chiara, the shorter overview: "then the box can be more narrow" → "split the difference"): 30ch was tight, the site line is 44 — 37ch */   /* 22.10 (Chiara: "can’t read it — needs to be lighter"): the hero is now a .close-rider (last two children of main) and the ride paints its riders INK — the overview rode up in ink over the palms. The hero’s cream outranks it here. */
body.method-page .svc-hero--media :is(.svc-title, .svc-eyebrow, .svc-lead) { color: var(--frost); opacity: 1; }
body.method-page .svc-hero--media.svc-hero--ride > .svc-hero-first, body.method-page .svc-hero--media.svc-hero--ride > .svc-hero-overlay { z-index: 3; }   /* 22.10 (Chiara: "text has a weird effect — can’t read it"): the foot fade (::after, z 2) and the blur (::before, z 1) were painting OVER the ride’s text layers (z 1) — the overview rode up through the blur and under the dark. Same lift the 12.10 .method-intro-copy had (z 3). */   /* 22.10 (rendered): the 58% frost went grey over the dark carriage, as it did on /studio — solid cream here too */
   /* 12.10 (Chiara: "make sure the text is the top layer over the blur — the hero and byline"): the title, eyebrow and lead sit above the blur (1) and the colour fade (2) */   /* the TYPE steps (cream → ink at the halfway point, a 0.5s ease) while the ground blends — a mixed type colour washed out mid-fade */
body.method-page .method-pin .method-title, body.method-page .method-pin .method-value, body.method-page .method-pin .method-value p, body.method-page .method-pin .method-value::before,
body.method-page .method-pin .method-deliv, body.method-page .method-pin .method-deliv li, body.method-page .method-pin .method-deliv::before, body.method-page .method-pin .method-counter, body.method-page .method-pin .method-label { color: inherit; }

/* 12.10 (Chiara: "same for the homepage — the Luxury Hospitality Consulting project only — the same fade edge into the
   brown"): the deck's LAST slide melts into the voices section's dark ground below it, as the Method hero does — a
   masked blur over the bottom 40% and a colour fade to --ground-3. Titles (z 2) and the pin's ticker stay above it. */
body.home .hw-slide[data-project="lhc"]::before { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 40%; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, color-mix(in srgb, var(--ground-3) 70%, transparent) 60%, var(--ground-3) 100%);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 70%); mask-image: linear-gradient(to bottom, transparent, #000 70%); }

/* 12.10 (Chiara, /studio: "the same as what we've done on the Aura Method page with the hero and the fade"): the hero
   image's foot blurs and fades into the section below — here the cream chapter panel, so the fade lands on --paper.
   The title already carries the case-study frost (.svc-hero--media .svc-title); the copy sits above both layers. */
body.studio-page .svc-hero-bg::before { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 14%;   /* 22.10 v2: 40% → 14%, with the fade */ z-index: 1; pointer-events: none;
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 85%); mask-image: linear-gradient(to bottom, transparent, #000 85%); }
body.studio-page .svc-hero-bg::after, body.method-page .svc-hero-bg::after { z-index: 2;   /* 25.10: the Method hero shares the Studio scrim exactly (Chiara: "do the exact same for the Aura Method header hero") */
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 70%, rgba(0, 0, 0, 0.55) 100%),   /* 22.10 v3 (Chiara: "the studio shadow needs to be dark not light"): the foot fades to a dark shade, not to paper */
              linear-gradient(to bottom, rgba(0, 0, 0, 0.22), transparent 22%),
              rgba(0, 0, 0, var(--hero-scrim, 0.38)); }   /* 22.10 v2: the paper fade pulled to the foot (from 80%, was 50%) — on the dark Venice still the half-height fade sat cream behind the cream title and ghosted it */   /* 22.10 (Chiara, the Venice window still: "can we go dark scrim them if we use that image"): the dark wash is BACK — the service heroes’ 0.20 + 0.18 as one dial, --hero-scrim, so the pale sky holds the cream title */   /* v4: the light fabric film — the dark wash layers (0.20 + 0.18) are gone, they greyed its cream; only a soft top shade stays for the header */
body.studio-page .svc-hero--media > *:not(.svc-hero-bg) { z-index: 3; }
/* the copy sits in the bottom band, which is CREAM now — the type turns ink there (cream-on-cream vanished in the render); the title keeps the frost grammar, in ink */
/* v2 (Chiara: "it needs to be dark and fade to cream"): the hero melts to #191211 like the Method hero (cream type stays), and the chapter section below
   opens on that dark and crossfades to cream as it fills the screen, latched — main.js --sg on .chapter-scroll. */
/* v3 (Chiara: "change the colour of the studio background to cream — not the dark fade, I'll find a new hero"): the chapter
   section is CREAM from the start; the hero melts to cream and its copy is ink over that band. The dark→cream script is idle. */
body.studio-page .chapter-scroll .ch-vp { background: var(--paper); color: var(--ink); }
/* 12.10 (Chiara, gave the hex: "the font colour should be #887869 on this page"): the studio page's type — the ink and title-ink
   TOKENS are re-pointed on this page only, so every rule that uses them (statement, ledger, services rows, FAQ, hero copy) follows. */
body.studio-page { --ink: #887869; --title-ink: #887869; }
body.studio-page .svc-ledger-stage .svc-process--ledger > .cs-stack-text:first-child { padding-top: var(--space-section); }   /* 22.10 (Chiara: "more space above the How We Work section"): back on the section rhythm (120) — the 13.10 xl (64) was the reduction */   /* 13.10 (Chiara: "can you reduce the space after film"): the How We Work intro sits one rung closer to the showreel — section rhythm (120) → --space-xl (64) */
body.studio-page .svc-ledger-stage .svc-process--ledger { padding-top: var(--space-section); } body.studio-page .svc-ledger-stage .svc-process--ledger > .cs-stack-text:first-child { padding-top: 0; padding-bottom: var(--space-sm); position: sticky; top: calc(var(--header-h) + var(--space-xs)); z-index: 3; background-color: inherit; } body.studio-page .svc-ledger-stage .svc-process--ledger { --ledger-top: calc(var(--header-h) + var(--space-lg) + var(--space-xs)); }   /* 25.10 (Chiara: "( How We Work ) needs to appear on top of the line — it loses its context when it hits the sticky stack"): the studio joins the service pages' rule — the label RIDES THE STACK, pinned just under the header above rung 01 while 01–03 collect beneath it; the section’s 120 of top air moves onto the section so the pinned box is just the label. v2: the label carries the section ground (background inherit, cream) so the cards slide UNDER it as they leave instead of through its text, and rung 01 pins 8px lower (ledger-top + xs) so it clears the label. Phones keep the static flow */   /* 25.10: every width — the phone stack is back (see THE PROCESS LEDGER, phone block) */
body.studio-page .svc-hero-first { color: var(--cream); }   /* 22.10 (the Venice window still): the 13.10 ink first-screen (set for the light fabric film) goes CREAM — dark frame, dark scrim */
body.studio-page .svc-hero-first .svc-title { color: var(--frost); }   /* 27.10: frosted with every media hero */   /* 22.10: solid, not the 80% ink — over the lagoon it rendered as a grey ghost */
body.studio-page .chapter-scroll :is(.fi-statement, .fi-label, .fi-sign, .fi-cue, .ch-quote, .ch-panel--text p, .ch-panel--text .label) { color: inherit; }
/* 25.10 /STUDIO ON THE PHONE (Chiara: "why is it so contrasty? On mobile, can the background fade from brown to white, like we do?
   The image of me is way too big, and I think the text needs to come first… can the show reel be like 16:9?"). Phones only.
   RETIRED same day (v2, "it just needs to transition"): the dark → cream scroll ride on .ch-vp (color-mix on main.js --sg, type
   crossing at sg-light) — the section is cream from the start again and the hero fades into it instead. */
@media (max-width: 900px) {
  body.studio-page .svc-hero-bg::after, body.method-page .svc-hero-bg::after { background: linear-gradient(to bottom, transparent 50%, color-mix(in srgb, var(--ground-3) 6%, transparent) 60%, color-mix(in srgb, var(--ground-3) 18%, transparent) 69%, color-mix(in srgb, var(--ground-3) 36%, transparent) 77%, color-mix(in srgb, var(--ground-3) 58%, transparent) 85%, color-mix(in srgb, var(--ground-3) 80%, transparent) 92%, color-mix(in srgb, var(--ground-3) 94%, transparent) 97%, var(--ground-3) 100%), linear-gradient(to bottom, rgba(0, 0, 0, 0.22), transparent 22%), rgba(0, 0, 0, var(--hero-scrim, 0.38)); }   /* v3 (Chiara: "why is there a weird fade? It needs to be going into dark, and then the background's dark, and then it changes to white"): the photo's foot melts to the site dark (the Method hero's grammar) — the section below opens on that dark. Retired same day: v2's cream band on the hero runway's end ("weird fade") */   /* v4 (Chiara: "make sure the fade of the hero into the body copy is not a harsh line — a nice merge"): the melt runs the bottom HALF (was the last 30%) on an eased curve — eight stops, slow to build, soft to land — so neither the start nor the join reads as an edge */
  body.studio-page .chapter-scroll { --chg: var(--ground-3); --chg-fg: var(--cream); }
  body.studio-page .chapter-scroll.ch-light { --chg: var(--paper); --chg-fg: var(--ink); }   /* main.js: once the portrait is halfway up the screen — a STATE, eased on its own clock (the ledger's ground shift), not scrubbed by scroll (v1's scrub read muddy mid-tone) */
  body.studio-page .chapter-scroll::before { content: ''; position: absolute; left: 0; right: 0; bottom: 100%; height: calc(var(--space-section-phone) + var(--space-xl) + 1px); background-color: var(--chg); transition: background-color var(--dur-ground) var(--ease-house); }   /* paints the portrait's room (the section's top margin) with the same ground */
  body.studio-page .chapter-scroll .ch-vp { background-color: var(--chg); color: var(--chg-fg); transition: background-color var(--dur-ground) var(--ease-house), color var(--dur-base) var(--ease-house); }
  body.studio-page .ch-panel--chapter .fi { grid-template-rows: none; }
  /* v3 THE CHAPTER TRACK ON THE PHONE (Chiara: "by the time it gets to In Short it's back to a light background… In Short sticks,
     then the story, both sides… start scrolling from the right. It shouldn't be a gap underneath — In Short should swipe to the left,
     and then those things move like on desktop"). The desktop track itself (main.js, --ch-max): one pinned screen, four full-width
     panels — In Short, The Story, Both Sides of the Door, What You Get — sliding left as you scroll. The portrait (v2: photo first,
     two-thirds wide) hangs just ABOVE the pinned screen: it scrolls in before the pin and is off the top while the panels ride.
     The section's top padding is the room it hangs in. Every panel's copy starts on one line under the header. */
  body.studio-page .chapter-scroll { --ch-ph: calc(66vw * 4 / 3); height: calc(100svh + var(--ch-max, 0px)); margin-top: calc(var(--space-section-phone) + var(--space-xl)); }   /* v5 (Chiara's 21.38 screenshot: "stop the In Short with a bit of my image above it still… then the text changes — otherwise it feels like too much moving"): the portrait now lives INSIDE the pinned screen, its top one xl (64) off the screen's edge; the section's margin is just the step above it */   /* the portrait's room is MARGIN, outside the section box — main.js measures the ride from the section's top, so the slide starts exactly as the screen pins */
  body.studio-page .ch-vp { position: sticky; top: 0; height: 100svh; overflow: clip visible; }   /* x clipped so the off-screen panels never widen the page; y open so the portrait can hang above */
  body.studio-page .ch-track { display: flex; height: 100%; width: max-content; }
  body.studio-page .ch-panel, body.studio-page .ch-panel--chapter, body.studio-page .ch-panel--text { flex: none; width: 100vw; height: 100%; }
  body.studio-page .ch-panel--chapter { position: relative; }
  /* v6 (Chiara: "move it down a bit — too close to the image, too much space at the bottom; centre it so the biggest one is centred"): main.js measures the tallest panel (In Short) and the room between the portrait's foot and the screen's foot ON THE PHONE IN HAND, and sets --ch-pad so that panel sits dead centre in it; every panel shares the one top, so the labels still line up as the text slides. The calc is the fallback */
  body.studio-page .ch-panel--chapter .fi { display: block; height: 100%; padding: var(--ch-pad, calc(var(--ch-ph) - var(--space-xl) + var(--space-lg))) var(--inset-phone) 0; box-sizing: border-box; }   /* v5: every panel's copy starts under the portrait's foot (photo bottom at ph − 64, then one xl) */
  body.studio-page .ch-panel--chapter .fi-photo { position: absolute; left: 17vw; top: calc(-1 * var(--space-xl)); bottom: auto; width: 66vw; height: auto; margin: 0; translate: var(--ch-x, 0px) 0; z-index: 1; }   /* v5: pinned WITH the screen and held still while the track slides — main.js hands over the track's offset (--ch-x) and the portrait cancels it */   /* two-thirds of the screen, centred, its foot on the pinned screen's top edge — fully off the top while the panels ride */
  body.studio-page .ch-panel--chapter .fi-intro { margin: 0; gap: var(--space-md); }   /* v5: the copy starts one lg (40) under the portrait's foot, and label / statement / sign-off step md (24) — the pinned screen holds portrait + In Short with air at the foot (desktop keeps its lg steps) */
  body.studio-page .ch-panel--text { box-sizing: border-box; padding: var(--ch-pad, calc(var(--ch-ph) - var(--space-xl) + var(--space-lg))) var(--inset-phone) 0; align-content: start; }
  body.studio-page .bleed-grow { height: auto; margin-top: calc(var(--space-section-phone) - var(--ch-tail, 0px)); }   /* v4 (Chiara: "the showreel doesn't work — needs to be small 16:9"): the grow retires on phones; the reel is a still, full-width 16:9 frame again. Kept from v3 ("fill the gap"): main.js measures the last chapter panel's empty foot (--ch-tail) and the reel lands one section step (96) under the copy */
  body.studio-page .svc-ledger-stage { position: relative; }   /* 25.10 (Chiara's 21.33 screenshot: "what's this gap below the showreel"): the reel rides up into the chapter screen's empty foot (--ch-tail) and everything after it rides up too — How We Work started ~200px INSIDE the chapter section's tail, where the pinned chapter screen (sticky = positioned) painted over it: a cream band between reel and dark. Positioned here, the stage paints after (above) the chapter screen and covers that tail */
  body.studio-page .bleed-grow-pin { position: static; height: auto; aspect-ratio: 16 / 9; clip-path: none; }   /* v4: small 16:9, full width (390 × 219), no pin, no clip — v3's grow from a 16:9 band to full screen retired */
  body.studio-page .ch-panel--chapter .fi-cue { display: none; }   /* (Chiara: "remove the Scroll in the bracket — do we need that?"): not on the phone — scrolling IS the swipe here */
}

/* FAQ + price floor */
.faq { padding: var(--space-section) var(--gutter); margin: 0 auto; }  /* 12.10 (Chiara: "can the lines be wider?"): the 950px box goes — the rows run gutter to gutter like the ledger above them (Stud's full-width rows) */  /* 02.07: centred again, per Chiara — the 950px box retired 12.10 */
.faq > .section-label { opacity: 0.78; display: block; margin-bottom: var(--space-xl); }
.faq-list details { border-top: 1px solid color-mix(in srgb, currentColor 25%, transparent); }   /* 12.10: currentColor, not --ink — the branding FAQ now sits on the held dark ground (cream type), and ink hairlines vanished there; identical on cream */
.faq-list details:last-child { border-bottom: 1px solid color-mix(in srgb, currentColor 25%, transparent); }
/* 12.10 (Chiara, the Stud FAQ format — "the lines and numbers"): heading in her words, a mono number column (001…),
   the question beside it, a + that turns to × when open, the answer under the question column. Numbers are counters. */
.faq-title { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-lg); line-height: 1.15; text-wrap: balance; max-width: 18ch; margin: 0 0 var(--space-xl) calc(var(--faq-n) + var(--space-lg)); color: inherit; }   /* 12.10: the heading sits over the question column, as on Stud */
.faq > .faq-title { --faq-n: clamp(96px, 20vw, 280px); margin-left: 0; font-size: var(--type-phase); }   /* 20.10 (Chiara: "Good questions. We've got the answers. — something's not sitting right… can you refine it?"): the heading was the one section title on the site floating OFF the gutter — indented to the question column at barely a rung above the rows, it read as a stray row, not the opener. It now anchors the left edge like every section title and steps serif-lg → phase; the numbered rows indent beneath it, Stud's own hierarchy. */
.faq-list { counter-reset: faq; --faq-n: clamp(96px, 20vw, 280px); }   /* 12.10: with full-width rows the number column widens toward Stud's left third, so the questions start where the ledger titles do */
.faq-list details { counter-increment: faq; }
.faq-list summary {
  cursor: pointer;
  list-style: none;
  padding: var(--space-md) 0;
  display: grid; grid-template-columns: var(--faq-n) 1fr auto; column-gap: var(--space-lg); align-items: baseline;
  font-family: var(--font-serif);
  font-size: var(--type-serif-md);
  font-weight: 300;
}
.faq-list summary::before { content: "0" counter(faq, decimal-leading-zero); font-family: var(--font-mono); font-size: var(--type-label); letter-spacing: normal; opacity: 0.5; }
.faq-list summary::after { content: "+"; font-family: var(--font-mono); font-weight: 300; font-size: var(--type-serif-md); line-height: 1; opacity: 0.6; justify-self: end; display: inline-block; transition: transform var(--dur-quick) var(--ease-house), opacity var(--dur-quick) var(--ease-house); }
.faq-list summary:hover::after { opacity: 1; }
.faq-list details[open] summary::after { transform: rotate(45deg); }
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list details p { padding: 0 0 var(--space-md); padding-left: calc(var(--faq-n) + var(--space-lg)); max-width: calc(80ch + var(--faq-n) + var(--space-lg)); }   /* 25.08: 80ch measure; 12.10: sits under the question column */
@media (max-width: 760px) {
  .faq-list, .faq > .faq-title { --faq-n: 40px; }   /* the heading indents with the same column on the phone */
  .faq-list summary { column-gap: var(--space-sm); }
  .faq-list details p { padding-left: calc(var(--faq-n) + var(--space-sm)); }
  .faq > .faq-title { margin-left: 0; font-size: var(--type-serif-lg); }   /* 20.10: gutter-anchored on the phone too · 27.10 (Chiara: "Good questions, we've got the answers looks very big on mobile"): phase (40px) → serif-lg (30px) on phones */
  body .faq { padding-left: var(--inset-phone); padding-right: var(--inset-phone); }   /* 25.10 (Chiara, stud.house/studio/web-development FAQ: "I like the margin that you've used on the process for the FAQs"): the How We Work edge, 32 (was the 16 gutter) */
  .faq-list summary::before { font-size: var(--type-caption); }   /* ("the numbers look too big on the left"): label 14 → caption 12 — Stud drops the number on mobile altogether; ours stays, quieter */
  .faq-list summary::after { font-family: var(--font-serif); font-weight: 300; opacity: 0.5; }   /* ("the arrows look a bit thick"): the + in Eiko's hairline (was the mono at 300) and quieter, after Stud's thin 30% plus — it still turns to a thin × when open */
}   /* 25.08 (Chiara: "too narrow — make them wider"): 58 → 80ch, most of the 950px column */
.faq-personal { margin-top: var(--space-xl); }
.faq-personal a { color: var(--green); border-bottom: 0.5px solid currentColor;   /* 21.10 (Chiara: "make sure the underlines are all the same as the footer ones"): the footer CTA’s 0.5px rest weight */ }

/* THE RUNWAY + THE ASK (01.09 — Chiara "do it" on the audit's biggest move).
   section.enquire is retired. It was three lines of type and a pill on a flat cream
   ground, sitting directly in front of the footer reveal — which is the best moment on
   the site. The page read as: flat type, then the good bit. The ask now lives ON the
   film and the cream that remains is simply the melt's runway.
   RETIRED WITH IT: the 1.4s/1.2s reveal override + 0/150/300/450ms stagger (the CTA
   finished arriving 1.7s after the section appeared, via an opacity fade — the
   "PowerPoint" failure, on the one element the page exists to deliver), and the
   lit-front scrub on the statement (js/main.js scrubTargets), which cannot work on a
   position:fixed element whose rect never changes with scroll. That is a real trade:
   the scrub was a nice device. What replaces it is the page peeling off the film. */
.footer-runway {
  height: 46vh;                 /* the footer scrim melts 36vh up into whatever sits above it; this is that headroom plus a breath. It is the same run of cream that already existed below the old CTA. */
  background: var(--paper);     /* the melt fades TO --paper, so this must stay paper or a hard seam appears at the top of the film */
}

.footer-ask {
  order: 0;                     /* leads the stage: ask, then the mark, then links + legal */
  position: relative; z-index: 2;   /* above the scrim, like the wordmark — the scrim would otherwise veil it */
  text-align: center;
  /* 03.09 (Chiara: "centre it to the space" + "too close to the footer logo"): the ask FILLS the stage above the wordmark and centres in it, instead of hanging a fixed 96px off the mark. The gap to the logo now scales with the viewport (~230px at 1440×900). */
  flex: 1 1 auto;
  display: grid; align-content: center; justify-items: center;
  padding-top: var(--space-xl);   /* 03.09 (Chiara: "move this down slightly"): the ask sat dead-centre in the space; this drops the centre by half the token (32px) so it leans toward the mark. */
  margin-bottom: 0;
  max-width: 100%;
}
/* 03.09 (Chiara: "maybe the fade reveals the video in the footer and THEN the text reads on and is
   triggered … so there is a reveal of the video and then text"): the ask no longer sits pre-painted on the
   stage. The film reveals first; once the page's bottom edge has cleared the statement's top (main.js,
   .footer-ask.is-on) the statement and the CTA rise into place — the .rv grammar (44px, --dur-slow, house
   ease), CTA a beat behind. The .rv class itself cannot be used here: the stage is position:fixed, so an
   IntersectionObserver sees it "in view" from the first frame, behind the page. One-shot per page load. */
.footer-ask-statement, .footer-ask .cta {
  opacity: 0;
  transform: translateY(44px);
  transition: transform var(--dur-slow) var(--ease-house), opacity var(--dur-slow) var(--ease-house);
}
.footer-ask.is-on .footer-ask-statement, .footer-ask.is-on .cta { opacity: 1; transform: translateY(0); }
.footer-ask.is-on .cta { transition-delay: 140ms; }
/* 01.10 (Chiara: "make it super clear to click on"): the footer CTA keeps the type-only register (no pill — the
   site-local CTA rule) but gains two plain affordances: a trailing arrow in the label and a hairline under it that
   thickens on hover. The letter-roll still runs on pointer devices. */
.footer-ask .cta { padding-bottom: var(--space-xxs); border-bottom: 1px solid currentColor; }
.footer-ask .cta:hover, .footer-ask .cta:focus-visible { border-bottom-width: 2px; }
/* 01.10: the plain next-step line under the footer CTA — body register, quieter than the statement, rises a beat after the CTA */
.footer-ask-sub { margin: var(--space-sm) 0 var(--space-md); max-width: 34ch; font-size: var(--type-label); line-height: 1.5; color: var(--cream); opacity: 0; transform: translateY(44px); transition: transform var(--dur-slow) var(--ease-house), opacity var(--dur-slow) var(--ease-house); transition-delay: 70ms; }
.footer-ask.is-on .footer-ask-sub { opacity: 0.85; transform: translateY(0); }
.footer-ask-sub a { color: inherit; text-decoration: underline; text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
@media (prefers-reduced-motion: reduce) { .footer-ask-sub { opacity: 0.8; transform: none; transition: none; } }
@media (prefers-reduced-motion: reduce) {
  .footer-ask-statement, .footer-ask .cta { opacity: 1; transform: none; transition: none; }
}
.footer-ask-statement {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-phase);   /* 01.09 (W5 hierarchy finding): one rung UP, --type-cta (57px) -> --type-phase (64px). Descending the zone the display ladder read 74 (router rows) -> 64 (Method) -> 57 (the close), so the eye was walked DOWN in scale exactly where it should be walked up. The audit's alternative, --type-monument (88px), was not taken: on this stage the statement sits directly above the full-width wordmark, and at 88 the two fight. 64 lifts the close level with the Method without unseating the mark. */
  line-height: 1.15;
  margin: 0 auto var(--space-lg);   /* 03.09 (Chiara: "increase spacing after last line and cta"): off the 14px --space-cta paragraph rule onto the lg rung — this is a 44–64px display statement, not body copy, and the CTA needs to sit clear of its descenders. */
  color: var(--cream);
  /* the site's frosted-on-imagery rule is deliberately NOT used here: this is the
     conversion and it should be the brightest, most legible thing on the stage. The
     scrim's base shadow (rgba 0.5 → transparent at 34%) carries it over the footage. */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 2px 18px rgba(0, 0, 0, 0.18);   /* legibility insurance over a moving mountain ridge, not a style */   /* 20.10 (Chiara, Necker on the phone: "the harsh line here is wrong — soften it"): the 40px/45% shadow fused into a solid slab behind the two lines on a bright sky. A tight 2px lift plus a faint 18px haze holds the letters without a plate. */
}
.footer-ask .cta--large {
  font-size: var(--type-label-lg);   /* now a real 18px rung — it was 14px, identical to --type-label, so this modifier did nothing at all */
  display: inline-block;
  color: var(--cream);
  /* 21.10 (Chiara: "i dont like the line under start the conversation" → minutes later: "reduce the thickness of the
     line before the hover… post hover is the thickness it is now"): the 03.09 underline STAYS but breathes — 0.5px is
     already one device pixel, so the rest state thins through ALPHA (35% cream, reads as a finer rule) and hover
     brings it to full currentColor. House curve on the change. */
  border-bottom: 0.5px solid currentColor;   /* 21.10 v5 (Chiara, after 1px read "too thick"): 0.5px full-strength at rest — one device pixel on a 2× screen, the floor — rising to 1px on hover, the one visible step above it */
  padding-bottom: 0.3em;
  transition: border-bottom-width var(--dur-quick) var(--ease-house);
}
.footer-ask .cta--large:hover, .footer-ask .cta--large:focus-visible { border-bottom-width: 1px; }


/* Small screens: single-column fallbacks until the mobile design pass */
@media (max-width: 760px) {
  .for-whom, .work-card a, .work-card:nth-child(odd) a, .insight, .founder { grid-template-columns: 1fr; }
  .founder-copy { grid-column: 1; justify-items: start; text-align: left; margin-inline: 0; }   /* 09.09: one column on small screens — the right-ranging is a desktop composition */
  /* 07.10: the ARMED seat on small screens. The :has() split (0,2,0) outranks the one-column rule above (0,1,0), so
     the first real image in the figure would have held two columns on a phone. Layout flip only: image first,
     full-bleed, at its 4/5 plate (not 100svh — that is the desktop column height); copy below on the page inset. */
  .founder:has(.founder-media:not(:empty)) { grid-template-columns: 1fr; padding: 0 0 var(--space-section); min-height: 0; }
  .founder:has(.founder-media:not(:empty)) .founder-media { grid-column: 1; height: auto; }
  .founder:has(.founder-media:not(:empty)) .founder-copy { grid-column: 1; padding: var(--space-lg) var(--inset-page) 0; }
  .work-card:nth-child(odd) .work-media { order: 0; }
  .method-phase { padding-left: var(--space-lg); }
}

/* Pin-exit CTAs (design pass 02.07) */
.interstitial {
  padding: var(--space-section) var(--gutter);
  text-align: center;
}
.interstitial-line {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  max-width: 40ch;   /* 03.07 measure audit: was uncapped — full page width on desktop */
  margin-left: auto; margin-right: auto;   /* stays centred under its cap */
  margin-bottom: var(--space-cta);
}
.sr-only {
  position: absolute; width: 1px; height: 1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* Film grain (03.07) — the measured design's full-frame texture layer.
   SVG turbulence, fixed, whisper opacity. Delete this block to remove. */
body::after {
  content: '';
  position: fixed; inset: 0;
  z-index: 100;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  .hero-wordmark { display: none; }
  .opening { height: auto; }
  .opening-sticky { position: relative; }
  .origin { margin-top: 0; min-height: 0; }   /* no pin to ride over — normal flow */
  .site-header .wordmark { opacity: 1; }
  html { scroll-behavior: auto; }
}

/* ============================================================
   WORK INDEX — /work (Alex's Case Studies Hub frames, 04.07)
   One DOM list, two layouts. The toggle FLIPs the caption lines
   between them (superhaus.co/projects reference); images cut on
   exit and wipe in on return — the menu's clip language, no
   crossfades. List rows hover a floating preview (menu-preview
   mechanic: panes swap, the box glides between rows).
   ============================================================ */

/* Interior pages: cream ground from first paint — header is ink and the
   wordmark is present (no film to own the landing frame) */
.page-interior .site-header { color: var(--ink); }
.page-interior .site-header .wordmark { opacity: 1; }   /* 13.08: load-fade trial REMOVED same day — "I want the opacity to change" meant the FROSTED fill (58% over imagery, see the on-dark logo-mask rule), not an entrance. Interior mark is simply present, per the house motion rule. */
/* 10.07: interior pages went beige, so full-bleed dark grounds (film scenes)
   flip the header cream via on-dark — this must outrank the ink rule above */
.page-interior .site-header.on-dark { color: var(--cream); }
.page-interior .site-header.on-dark .logo-mask { background-color: color-mix(in srgb, var(--cream) 70%, transparent); }   /* 13.08: interior pages carry the same frosted-over-imagery rule (case heroes, film scenes) — 70%, matching the homepage header mark */

.work-main {
  /* 16.07: the eyebrow + display-scale title need real clearance under the
     fixed header — the 06.07 2×gutter left the view toggle UNDER Let's Talk
     (unclickable). space-2xl clears it and matches the service-page rhythm. */
  /* 10.09 (the Lolo ledger): the top clears the header AND the centred pill that now sits under it */
  padding: calc(var(--header-h) + var(--space-xl)) var(--gutter) 0;   /* 10.09 (the Lolo ledger): the top clears the header; the pill lives on the header line now, so xl not 2xl. Bottom: see the .work-main.above-footer rule near the close system */
}
/* 10.09 THE PILL (Chiara: "List / Gallery in the top nav… exactly how Lolo have it"): Lolo's .case_toggle in the house
   register — 150px, hairline pill, two mono halves, the KNOB slides to the pressed half on the house curve. Fixed and
   centred under the header so it rides both views; cream over the gallery deck. */
/* 19.10: a type-only toggle ( LIST )  GALLERY was tried for an hour and REVERTED (Chiara: "I like the top list gallery as it was") — the pill stays. */
.view-toggle {
  position: absolute; top: calc((var(--header-h) - 32px) / 2 + 2px); left: 50%; transform: translateX(-50%); z-index: 61;   /* 10.09 v4: a direct child of <body> (see the markup note), above the header's 60 — clicks reach it */   /* 10.09 v2: ABSOLUTE, not fixed — Lolo's is `absolute top-30`; fixed, it rode over the names once the list scrolled under it · v3 (Chiara: "does the menu bar look messy on this page" — it did: header, pill, heads = three tiers): the pill sits ON THE HEADER LINE, centred between the wordmark and Let's Talk (the header's centre is empty on interior pages), so the top is two tiers: chrome, then the column heads */
  display: flex; width: 150px; height: 32px; padding: 2px; box-sizing: border-box;
  border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 200px;
  color: var(--ink); transition: color var(--dur-base) var(--ease-house), border-color var(--dur-base) var(--ease-house);
}
.view-toggle::before {   /* the knob */
  content: ''; position: absolute; left: 2px; top: 2px; width: calc(50% - 2px); height: calc(100% - 4px);
  border-radius: 200px; background: color-mix(in srgb, currentColor 9%, transparent);
  transition: transform var(--dur-base) var(--ease-house);
}
.view-toggle:has(.view-btn[data-view="grid"][aria-pressed="true"])::before { transform: translateX(100%); }
.view-btn {
  position: relative; z-index: 1; flex: 1 1 0; background: none; border: 0; cursor: pointer; padding: 0;
  font-family: var(--font-label); font-weight: 400; font-size: 11px; letter-spacing: var(--track-label); text-transform: uppercase;
  color: inherit; opacity: 0.45;
  transition: opacity var(--dur-quick) var(--ease-house);
}
.view-btn[aria-pressed="true"] { opacity: 0.85; }
.view-btn:hover, .view-btn:focus-visible { opacity: 0.85; }
body:has(.work-index:not(.is-list)) .view-toggle { color: var(--cream); }
/* 10.09 THE DARK INDEX (Chiara: "make the background dark and the text light I think"): body.work-dark — the whole
   /work page sits on the near-black close ground with cream type. Names rest frosted (0.65, the house recessive rung)
   and go full cream under the cursor; labels + heads cream; the pill and the cursor invert; the header runs cream
   (main carries data-header-tone="dark"). One class on <body> — remove it and the page is cream again. */
body.work-dark { background: var(--ground-3); color: var(--cream); }
body.work-dark .work-main { background: var(--ground-3); color: var(--cream); }
body.work-dark .work-index.is-list .wi-name { color: var(--cream); opacity: 0.65; transition: opacity var(--dur-quick) var(--ease-house); }
body.work-dark .work-index.is-list .wi-item a:hover .wi-name { color: var(--cream); opacity: 1; }
body.work-dark .work-index.is-list .wi-disc, body.work-dark .work-index.is-list .wi-sector { color: var(--cream); opacity: 0.7; }
body.work-dark .work-index.is-list .wi-head .label { color: var(--cream); }
body.work-dark .view-toggle { color: var(--cream); }
body.work-dark .wi-cursor { background: var(--cream); color: var(--ink); }
body.work-dark .wi-cursor.view { background: color-mix(in srgb, var(--cream) 20%, transparent); color: var(--cream); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }   /* 10.09 (Chiara: "the view cursor button should have an opacity of 2" → read as 0.2): the VIEW pill's fill at 20% cream, frosted, the label full cream so it still reads; the resting dot stays solid */
body.work-dark .site-header { color: var(--cream); }
body.work-dark .site-header .cta--header { opacity: 0.7; transition: opacity var(--dur-quick) var(--ease-house); }   /* 10.09 (Chiara: "add lighter opacity to the CTA button"): Let's Talk rests at 0.7 on the dark index — full cream read heavier than the frosted names beside it */
body.work-dark .site-header .cta--header:hover, body.work-dark .site-header .cta--header:focus-visible { opacity: 1; }
/* 10.09 (Chiara's screenshot: an empty grey box at the reveal): body.close-dark paints the rider + melt in --gallery (#1E1917, the DECK's end colour); the dark LIST ends on --ground-3 (#110B09), so that block read as a lighter panel. Both follow the list ground now — the rider dissolves invisibly into the film. */
body.page-interior.close-dark.work-dark .above-footer::after { background: var(--ground-3); }
body.page-interior.close-dark.work-dark .footer-scrim { --melt: var(--ground-3); }
/* 10.09 (Chiara: "& have bold — change that"): the mono's own ampersand is a heavy glyph even in the Thin cut, so the
   row labels set it in the SERIF — the editorial ampersand, weight 300, a touch larger, sat on the mono's baseline. */
.wi-disc .amp, .wi-sector .amp { font-family: var(--font-serif); font-weight: 300; font-size: 1.25em; line-height: 0; vertical-align: -0.04em; }
/* rows without a case page: not a link, no view cursor, no frame */
.wi-item--nolink a { cursor: default; }
/* 10.09 THE CURSOR (Lolo's .cursor): a 12px ink square that replaces the pointer inside the list and swells into a
   VIEW ↗ pill over a row. Position from --cx/--cy (work.js, compositor-only). Hover-capable pointers only. */
.wi-cursor {
  position: fixed; left: 0; top: 0; z-index: 60; pointer-events: none;
  width: 12px; height: 12px; border-radius: 50%; background: var(--ink); color: var(--cream);
  transform: translate3d(calc(var(--cx, -100px) - 50%), calc(var(--cy, -100px) - 50%), 0);
  display: flex; align-items: center; justify-content: center; overflow: hidden; opacity: 0;
  transition: opacity var(--dur-quick) var(--ease-house), width 0.4s var(--ease-expo-inout), height 0.4s var(--ease-expo-inout), border-radius 0.4s var(--ease-expo-inout);   /* Lolo: expo.inOut .4 on the morph */
}
.wi-cursor.on { opacity: 1; }
.wi-cursor.view { width: 78px; height: 28px; border-radius: 200px; }   /* 19.10 (Chiara: "should the hover view have a pill shape like the list gallery up top?"): yes — the VIEW state is now the same 200px pill as the toggle, and the resting dot is round, so the morph runs dot → pill. Was Lolo's 5px rounded rectangle on a 2px square (10.09). The .4s expo in-out morph is unchanged. */
.wi-cursor-label { font-family: var(--font-label); font-size: 11px; text-transform: uppercase; letter-spacing: var(--track-label); white-space: nowrap; opacity: 0; transition: opacity 0.15s ease-out; }
.wi-cursor.view .wi-cursor-label { opacity: 1; transition-delay: 0.1s; }
@media (hover: hover) and (pointer: fine) and (min-width: 901px) { .work-index.is-list { cursor: none; } }   /* 19.10: native cursor off only where the VIEW cursor runs — the desktop ledger */
@media (prefers-reduced-motion: reduce) { .wi-cursor { display: none; } .work-index.is-list { cursor: auto; } }

/* Page head — 16.07 v3 (Chiara): ( Our Work ) rung + toggle share the top
   line; the title sits below on its own */
/* 10.09: the page head (.work-head / .work-title, the dark "Brands We've Built" block) RETIRED — Chiara: "remove any black". The h1 is sr-only. */
/* 10.09: the 16.07 corner toggle rules RETIRED — the pill above owns .view-toggle / .view-btn now */

/* ---------- Grid layout (default) ---------- */
.work-index {
  /* 03.08 v2 (Chiara: "let's copy how it is on the homepage" + her 13.34
     screenshot of the Palmer deck slide): the IMAGE view = the homepage deck
     grammar, stacked — each project one full-viewport full-bleed frame, the
     name centred in the frosted serif, the disciplines in serif brackets at
     the base, deck scrim over the image. First frame bleeds to the very top;
     the page head (eyebrow + toggle) rides over it in cream and the big
     serif title retires in this view. LIST view untouched. */
  display: block;
}
.work-index:not(.is-list) { margin-inline: calc(-1 * var(--gutter)); }   /* full bleed out of work-main's padding */
.wi-item { margin: 0; }
.wi-item a { display: block; }
.work-index:not(.is-list) .wi-item a { position: relative; height: 100svh; overflow: hidden; }
.wi-media {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  overflow: hidden;
  margin: 0;
}
.work-index:not(.is-list) .wi-media::after {   /* 03.08 v2 (Chiara: "amend it so the text all reads well"): the case-hero scrim formula — flat dim + top/bottom gradients so the cream head, frosted name and base disciplines all hold on any image */
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(rgba(0, 0, 0, 0.20), rgba(0, 0, 0, 0.20)),
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%);
}
.work-index:not(.is-list) .wi-name {
  position: absolute; left: var(--gutter); right: var(--gutter); top: 50%;
  transform: translateY(-50%); text-align: center; z-index: 1; margin: 0;
  font-size: clamp(44px, 8vw, 92px); line-height: 1;   /* the deck's stepped scale — uniform so LHC's long name matches its siblings */
  color: var(--frost);   /* the frosted title rule */
}
.work-index:not(.is-list) .wi-disc {
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: var(--space-lg);
  text-align: center; z-index: 1; margin: 0;
  font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-sm);
  color: var(--cream);
  text-transform: none; letter-spacing: normal;   /* 03.08 v2: the .label mono grammar (uppercase/tracked) doesn't ride into the serif tag — Title Case like the homepage deck */
}
.work-index:not(.is-list) .wi-disc::before { content: '(\2003'; }
.work-index:not(.is-list) .wi-disc::after { content: '\2003)'; }
/* The page head rides OVER the first frame in image view; :has() flips it
   back to the in-flow ink head when the list is active. */
.work-main { position: relative; }
.work-main:has(.work-index:not(.is-list)) { padding-top: 0; }
.work-main:has(.work-index:not(.is-list)) .work-head { display: none; }   /* 03.08 v5: the deck view carries no page title — the head only returns with the list */
/* 03.08 v5 (Chiara): the corner chrome lives in body-level .work-corners —
   .above-footer's reveal MASK makes it a containing block, so fixed elements
   inside main pinned to the mask box, not the viewport (the v3/v4 rules
   rendered at the top; lesson learned). ( Our Work ) bottom-left; the toggle
   bottom-right with the two buttons STACKED (her call). Colour flips by view
   via body:has() — cream over the image deck, ink on the list's paper. */
/* 10.09: .work-corners RETIRED (eyebrow + corner toggle) — see the pill at .view-toggle */
.wi-media img, .wi-media video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Soft shadow/scrim over the footage so the cream mark reads (05.07, Chiara) —
   sits above the video, below the logo. */
.wi-media--video::after {
  content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(rgba(0,0,0,0.16), rgba(0,0,0,0.16)),   /* 05.07 (Chiara): slight shadow over the video */
    radial-gradient(ellipse at center, rgba(0,0,0,0.28), rgba(0,0,0,0) 66%);
}
/* Zambala tile: brand mark centred over the footage (05.07, Chiara).
   Scoped as .wi-media .wi-media-logo so it outranks `.wi-media img`
   (which would otherwise stretch the logo to 100%/cover the tile). */
.wi-media .wi-media-logo {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 44%; height: auto; object-fit: contain; z-index: 2; pointer-events: none;
  filter: brightness(0) invert(1) opacity(0.95);   /* renders the mark cream-white over footage */
}
.wi-name {   /* 16.07: names join the serif card register (svc-work-name), out of the mono caption */
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-md); line-height: 1.15;
  margin: 0 0 var(--space-xxs);
}
.wi-name strong { font-weight: 400; }     /* caption spec (legacy — strong tags removed 16.07) */
.wi-disc {
  font-weight: 300;
  margin-top: 2px;
  color: var(--taupe-sub);          /* 06.07 (Chiara): disciplines line in taupe */
  opacity: 1;                        /* colour now carries the recede — no dimming (kept 0.75 muddied the taupe) */
}
.wi-sector { display: none; font-weight: 300; opacity: 0.8; }

/* Images wipe back in when returning to grid (JS adds .wiping for a beat) */
.work-index.wiping .wi-media {
  animation: wi-wipe var(--dur-base) var(--ease-house) backwards;
  animation-delay: calc(var(--i) * 45ms);
}
@keyframes wi-wipe {
  from { clip-path: inset(0 0 100% 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* ---------- List layout ---------- */
.work-index.is-list { display: block; }
.work-index.is-list .wi-item { margin-bottom: 0; }   /* 03.08: the image-row spacing must not gap the list's hairline rows */
.work-index.is-list .wi-item a {
  display: grid;
  grid-template-columns: minmax(0, 24fr) minmax(0, 21fr) minmax(0, 55fr);  /* measured column starts: 0 / 23.7% / 45% */
  align-items: baseline;
  padding: var(--space-md) 0;
  border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
  transition: opacity var(--dur-quick) var(--ease-house);
}
.work-index.is-list .wi-item:last-child a { border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.work-index.is-list .wi-media { display: none; }
.work-index.is-list .wi-sector { display: block; }
/* 19.10 (Chiara: "when the page loads could they all stack in chronological order"): THE ARRIVAL — the head lands
   first, then every row rises in, top to bottom, one after another. --i is re-indexed to DOM order in the markup
   (it had drifted with the 10.09–01.10 reorders, so any stagger keyed on it would have landed rows out of sequence).
   Retires the 16.07 sector-only wi-rise, which fired every label on one shared delay — nothing read as a stack. */
.work-index.is-list .wi-head { --i: -1; }
.work-index.is-list .wi-head,
.work-index.is-list .wi-item {
  animation: wi-land var(--dur-base) var(--ease-house) backwards;
  animation-delay: calc(0.15s + var(--i) * 45ms);
}
@keyframes wi-land {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.work-index.is-list .wi-disc { margin-top: 0; }
/* Spotlight (the menu-secondary pattern): hovering one row softens the rest · 19.10: desktop ledger only */
@media (min-width: 901px) {
  .work-index.is-list:hover .wi-item a { opacity: 0.45; }
  .work-index.is-list:hover .wi-item a:hover { opacity: 1; }
}

/* 10.09 THE INDEX AS A LEDGER (Chiara: "the our work page should load on list view" + her reference screenshot — a
   three-column list, the project name huge and centred in the serif, services left, industry right, column heads
   above, no rules): the list view REBUILT on that grammar. Hairline rows retire; each row is one big name flanked
   by its two mono labels. Names rest in the taupe (the disciplines' colour) and go ink under the cursor. The DOM
   order (name, sector, disc) is unchanged — `order` seats them. The head block + the corner toggle stay as they were. */
.work-index.is-list .wi-head {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 3fr) minmax(0, 1fr); align-items: baseline;
  margin: 0; padding: 0 0 var(--space-lg); opacity: 1;
}
.work-index.is-list .wi-head .label { margin: 0; font-weight: 500; color: var(--ink); }   /* 10.09 (Chiara: "the subtitles for the sections need to be bolder — Services, Project, Industry"): the mono's REGULAR face (500 — 400 is the Thin cut on this site), solid ink */
.work-index.is-list .wi-head .label:nth-child(2) { text-align: center; }
.work-index.is-list .wi-head .label:nth-child(3) { text-align: right; }
.work-index:not(.is-list) .wi-head { display: none; }
.work-index.is-list .wi-item a {
  grid-template-columns: minmax(0, 1fr) minmax(0, 3fr) minmax(0, 1fr);
  align-items: center; column-gap: var(--space-md);
  padding: var(--space-sm) 0;   /* 10.09 (Chiara: "shall we reduce the spacing too"): md → xs, then ("spread the line height spacing out a bit"): xs → sm — the middle rung, stacked but not touching */
  border-top: 0;
}
.work-index.is-list .wi-item:last-child a { border-bottom: 0; }
.work-index.is-list .wi-disc { order: 1; text-align: left; }
.work-index.is-list .wi-name {
  order: 2; text-align: center; margin: 0;
  font-size: var(--type-ledger); line-height: 1; white-space: nowrap;   /* 10.09: one line, always — the rung is sized for it */
  color: var(--taupe-sub);
  transition: color var(--dur-quick) var(--ease-house);
}
.work-index.is-list .wi-sector { order: 3; text-align: right; opacity: 1; animation: none; }
.work-index.is-list .wi-disc, .work-index.is-list .wi-sector { color: var(--taupe-sub); font-size: var(--type-caption); font-weight: 400; }   /* 25.10 (Chiara: "the service and industry font, the mono — a bit smaller, too big"): label 14 → caption 12 on desktop, the phone size    /* 10.09 (Chiara: "services need to be more punchy" → then "the actual project lines need to be normal weight"): the side labels step up to the label rung (14) but stay in the light cut — only the column heads carry the Regular (500) */
.work-index.is-list .wi-item a:hover .wi-name { color: var(--ink); }
/* 27.10 (Alex, via Chiara: the centred list "looks a bit messy… laid out more like this" + the /branding process
   screenshot): DESKTOP LIST = THE PROCESS LEDGER. Hairline rows, services left, the name LEFT-aligned in the serif at
   the process title rung starting at 40%, industry right — the svc-step grid (2fr 3fr auto) so /work and the service
   pages read as one system. The centred 10.09 ledger retires on desktop; phones keep the 19.10 phone ledger. */
@media (min-width: 901px) {
  .work-index.is-list .wi-head, .work-index.is-list .wi-item a { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr) minmax(0, 1fr); column-gap: var(--space-lg); }
  .work-index.is-list .wi-head { padding: 0 0 var(--space-sm); }
  .work-index.is-list .wi-head .label:nth-child(2) { text-align: left; }
  .work-index.is-list .wi-item a { align-items: center; min-height: var(--ledger-rung); padding: var(--space-sm) 0; border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
  .work-index.is-list .wi-item:last-child a { border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
  body.work-dark .work-index.is-list .wi-item a { border-color: color-mix(in srgb, var(--cream) 16%, transparent); }
  .work-index.is-list .wi-name { text-align: left; font-size: min(var(--type-serif-lg), 3.35vw); line-height: 1.1; }
}
/* 19.10 THE PHONE LEDGER (Chiara: "I don't like the list view on mobile — it loses all the structure"): the one-column
   centred stack (heads hidden, no rules, industry dropped) RETIRED. Under 900px the ledger keeps its grammar in two
   columns — PROJECT / INDUSTRY heads, hairline rows, the name left at the serif-md rung with its services line
   beneath, the industry right-aligned on the name's baseline. Tap = the row. Layout-only; the stagger stays. */
@media (max-width: 900px) {
  .work-index.is-list .wi-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; padding: 0 0 var(--space-sm); }
  .work-index.is-list .wi-head .label:nth-child(1) { display: none; }   /* Services has no column here — it sits under the name */
  .work-index.is-list .wi-head .label:nth-child(2) { text-align: left; }
  .work-index.is-list .wi-head .label:nth-child(3) { text-align: right; }
  .work-index.is-list .wi-item a {
    grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name name" "disc sector";   /* v2 same hour: the industry beside the name took 42% and wrapped "Wagner Bespoke Travel" and the services — the name now owns the full row, the two mono labels pair up beneath it (services left, industry right) */
    column-gap: var(--space-sm); row-gap: calc(var(--space-xs) + 2px); align-items: baseline; text-align: left;   /* (Chiara: "increase the line spacing between Necker Island and Production & Film Editing" → "bump it again — by 2px"): xxs → xs → xs + 2 = 10px between the name and its labels */
    padding: var(--space-md) 0; border-top: 0;   /* → "reduce the space between each project": back to md, 48px row to row */   /* v3 (Chiara: "maybe remove the lines?" → "spread it out?" → "too much space, split the difference"): no hairlines, as on desktop; rows step on the midpoint of md and lg (32px) */
  }
  .work-index.is-list .wi-item:last-child a { border-bottom: 0; }
  .work-index.is-list .wi-name { grid-area: name; font-size: calc(var(--type-serif-md) + 3px); line-height: 1.15; white-space: normal; text-align: left; }   /* (Chiara: "project title 3px bigger"): serif-md + 3 = 27px at 390 */
  .work-index.is-list .wi-disc { grid-area: disc; text-align: left; }
  .work-index.is-list .wi-disc, .work-index.is-list .wi-sector { font-size: calc(var(--type-label) - 2px); }
  .work-index.is-list .wi-disc .amp, .work-index.is-list .wi-sector .amp { font-family: var(--font-serif); font-weight: 300; font-size: 1em; line-height: 0; vertical-align: -0.02em; }   /* 20.10 ("the ampersand is still bold… can we review"): the mono's own & is a heavy glyph against its caps; the serif & at the SAME size (not desktop's 1.25em) reads as a light mark between the words */   /* 20.10 (Chiara: "the ampersand is bold"): the serif & at 1.25em reads heavy against the 12px mono on a phone — it takes the mono */
  /* (Chiara: "do we need the view button? the hover doesn't work on mobile"): there is no hover on touch, so the VIEW ↗
     cursor and the Lolo preview never fire — the row had no affordance at all. The cursor's own glyph, ↗, sits at the
     right of the name line in the phone label mono: "this opens". Storia (no page) does not get one. */
  .work-index.is-list .wi-name { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-sm); }
  .work-index.is-list .wi-name::after { content: 'View \2197'; font-family: var(--font-mono); font-size: calc(var(--type-label) - 2px); text-transform: uppercase; opacity: 0.7; flex: 0 0 auto; }   /* (Chiara: "and say View — or something, not too much"): the cursor's word, VIEW ↗, in the label mono at the row's right */
  .work-index.is-list .wi-item--nolink .wi-name::after { content: none; }   /* the phone label size (gallery bylines, case meta): the pair fits one line on every row at 390 */
  .work-index.is-list .wi-sector { grid-area: sector; display: block; text-align: right; align-self: baseline; }   /* display: the older ≤900 two-column rule below hides the industry; the ledger needs it */
}

/* ---------- List hover preview ---------- */
/* 10.09 LOLO'S PREVIEW (Chiara: "copy exactly how they have done it — the images and animation movement"):
   .cases_list-hover-parent → .wi-preview-parent (fixed, full-screen, centred, scale 0 → 1 in .8s expoInOut while
   the list is hovered); .cases_list-hover → .wi-preview (317×198, offset from the pointer, eased follow);
   .cases_preview-img → .pv-pane (stacked layers, opacity .4s expoInOut, z-index counter brings the hovered one up). */
.wi-preview-parent {
  position: fixed; inset: 0; height: 100dvh; z-index: 5; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  transform: scale(0); transition: transform 0.8s var(--ease-expo-inout);   /* Lolo: .8s var(--expoInOut) */
}
.wi-preview-parent.on { transform: scale(1); }
.wi-preview {
  position: relative; width: calc(var(--plate-w) * 1.25);   /* 22.10 (Chiara: "on the list of projects can we make the pop-ups bigger — for the images"): the ledger’s plate a quarter up on the shared token — 33vw/481 → ~41vw/601 */ aspect-ratio: 16 / 9; overflow: hidden;   /* 10.09 (Chiara: "the same size as the hover images on the homepage"): the What We Do plate's width + 16:9 — one token. Was Lolo's 317×198, then +5% +5% */
  transform: translate3d(var(--pv-x, 0px), var(--pv-y, 0px), 0); will-change: transform;
}
.pv-pane { position: absolute; inset: 0; opacity: 0; transition: opacity 0.4s var(--ease-expo-inout); }   /* Lolo: .cases_preview-img */
.pv-pane.active { opacity: 1; }
.pv-pane img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 1023px) { .wi-preview-parent { display: none; } }   /* Lolo: l:hidden — under 1024 the list has no hover plate */

/* ---------- Interior footer band (L1 corner anchoring) ---------- */
.work-footer {
  display: flex; justify-content: space-between; align-items: flex-end;
  padding: var(--space-2xl) var(--gutter) var(--space-lg);
}
.work-footer .label strong { font-weight: 400; }
.work-footer-mail { display: inline-block; margin-top: var(--space-xxs); font-weight: 300; text-transform: none; letter-spacing: normal; }   /* 15.07: pre-mono tracking retired here too */
.work-footer .footer-socials { position: static; display: flex; gap: var(--space-md); }  /* the home footer's absolute placement doesn't apply in the band */

/* ---------- Breakpoints: layout flips only ---------- */
@media (max-width: 1100px) {
  .work-index { grid-template-columns: repeat(2, 1fr); }
}
/* 19.10: the legacy ≤900 list rule (two columns, industry hidden) RETIRED — it sat later in the file and overrode the
   phone ledger above (empty Industry column). The ledger block owns every width under 900 now. */
@media (max-width: 700px) {
  .work-index { grid-template-columns: 1fr; }
  .work-head { flex-direction: column; align-items: flex-start; gap: var(--space-md); }
  .work-index.is-list .wi-item a { grid-template-columns: 1fr; gap: var(--space-xxs); }
}
/* No hover, no preview — touch devices go straight through to the case study */
@media (hover: none) {
  .wi-preview { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .wi-preview { transition: none; }
  .work-index.wiping .wi-media, .work-index.is-list .wi-head, .work-index.is-list .wi-item { animation: none; }
  .work-index.is-list:hover .wi-item a { opacity: 1; }
}

/* ============================================================
   WEB DESIGN PAGE — /web (WEB-PAGE-FLOW.md, 04.07)
   Crown Creative funnel shape, Aura register. Reuses the
   work-index grid, enquire close and footer band; below is
   only what's new: lead, why-custom strip, process rows.
   ============================================================ */

/* 01 · Arrival lead — serif statement under the page head */
.web-lead {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.35;
  max-width: 30ch;
  margin-bottom: var(--space-3xl);
}

/* 02 · Why custom — manifesto right-set (the origin's editorial asymmetry) */
.web-why { margin-bottom: var(--space-3xl); }
.web-why-body p {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.45;
  max-width: 44ch;
}
@media (min-width: 900px) {
  .web-why-body { margin-left: 50%; }
}
/* The self-proof strip — quiet caps band between hairlines; the site
   states how it's built. Load-time slot stays FPO until measured. */
.web-strip {
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-sm) var(--space-md);
  margin-top: var(--space-2xl);
  padding: var(--space-md) 0;
  border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
  font-weight: 400;   /* wayfinding caps — the 04.07 ruling */
}
.web-strip span { opacity: 0.8; }

/* 03 · Web work — section label + reused grid + quiet link out */
.web-work { margin-bottom: var(--space-3xl); }
.web-work .section-label { display: block; margin-bottom: var(--space-lg); }
.web-work .quiet-link { margin-top: var(--space-xl); }

/* 04 · Process — numbered hairline rows: number | title | body */
.web-process .section-label { display: block; margin-bottom: var(--space-lg); }
.wp-list { counter-reset: none; }
.wp-row {
  display: grid;
  grid-template-columns: minmax(0, 24fr) minmax(0, 21fr) minmax(0, 55fr);  /* the work-list column grid — one family */
  align-items: baseline;
  padding: var(--space-lg) 0;
  border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
}
.wp-row:last-child { border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.wp-num { opacity: 0.65; }
.wp-title {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.2;
}
.wp-body { max-width: 44ch; }
/* Process hover preview (06.07, Chiara) — same wipe-open mechanic as the
   work/case-study list view; the panel follows the hovered row. */
.wp-preview {
  position: fixed; right: 8vw; top: 0;
  width: min(27vw, 480px); aspect-ratio: 4 / 3;
  overflow: hidden; z-index: 5; pointer-events: none;
  clip-path: inset(50% 0 50% 0);
  transform: translateY(calc(var(--pv-y, 50vh) - 50%));
  transition: clip-path var(--dur-base) var(--ease-house), transform var(--dur-base) var(--ease-house);
}
.wp-preview.show { clip-path: inset(0 0 0 0); }
.wp-pane { position: absolute; inset: 0; display: none; }
.wp-pane.on { display: block; }
.wp-pane img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 900px), (hover: none), (prefers-reduced-motion: reduce) { .wp-preview { display: none; } }

/* 06 · Enquire close — homepage grammar, tightened for an interior page */
.web-enquire { padding: var(--space-3xl) 0 var(--space-2xl); }
.web-enquire .cta { display: inline-block; margin-top: var(--space-cta); }

@media (max-width: 900px) {
  .wp-row { grid-template-columns: minmax(0, 1fr) minmax(0, 3fr); }
  .wp-body { grid-column: 2; margin-top: var(--space-sm); }
}
@media (max-width: 700px) {
  .wp-row { grid-template-columns: 1fr; gap: var(--space-xs); }
  .wp-body { grid-column: 1; margin-top: 0; }
  .web-strip { justify-content: flex-start; }
}

/* ============================================================
   CASE STUDY TEMPLATE + FILMSTRIP (P1, ruled 04.07)
   The horizontal mechanic shared by case-study work reels and
   photography editions (js/strip.js). Zambala is the pilot —
   duplicate work/zambala/ per client, swap content.
   ============================================================ */

/* Filmstrip — vertical scroll drives horizontal drift */
.hstrip-pin { position: relative; }
.hstrip-sticky { position: sticky; top: 0; height: 100vh; overflow: hidden; display: flex; align-items: center; }
.hstrip-track { display: flex; align-items: center; gap: clamp(40px, 6vw, 120px); padding: 0 var(--gutter); will-change: transform; }
.hstrip-item { flex: 0 0 auto; }
.hstrip-item img, .hstrip-item video { display: block; width: auto; max-width: 78vw; height: min(62vh, 640px); object-fit: cover; }
.hstrip-item .frame-cap { margin-top: var(--space-sm); }
.hstrip-label { position: absolute; top: calc(var(--gutter) + 3px); left: var(--gutter); z-index: 2; }
@media (prefers-reduced-motion: reduce), (max-width: 700px) {
  /* no pin: native horizontal scroller — the strip is still a strip */
  .hstrip-pin { height: auto !important; }
  .hstrip-sticky { position: static; height: auto; overflow-x: auto; padding: var(--space-2xl) 0; -webkit-overflow-scrolling: touch; }
  .hstrip-track { transform: none !important; }
  .hstrip-item img, .hstrip-item video { height: min(52vh, 480px); }
}

/* Frame caption (shared with the photography editions) */
.frame-cap { font-weight: 300; opacity: 0.65; }
.frame-cap strong { font-weight: 400; opacity: 1; }


/* ---------- Case study (Alex's frames 04.07 + superhaus hide-words) ----------
   Words view: fixed text rail left, panels drift horizontally past it.
   Hide-words view (body.words-off): rail slides out, a rotated spine
   title holds the edge, panels grow. One decisive gesture, no fades. */
/* 05.07 (Chiara): panels span the rail — top aligns with the title (Zambala),
   bottom reaches the disc line / project nav. Height = viewport minus the
   rail's top inset (gutter+90) and bottom inset (gutter+20). */
.case-study { --cs-rail-w: clamp(280px, 19vw, 380px); --cs-panel-h: calc(100vh - (2 * var(--gutter)) - 146px); }   /* leaves a footer band below the panels for the disc + nav arrows */
.case-study.words-off { --cs-panel-h: calc(100vh - (2 * var(--gutter)) - 40px); }   /* rail gone — panels grow toward the base */
/* Top-align the reel with the rail title instead of centring it */
.case-study .hstrip-sticky { align-items: flex-start; padding-top: calc(var(--gutter) + 90px); }

.cs-rail {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: calc(var(--cs-rail-w) + var(--gutter) + clamp(24px, 2vw, 40px));
  padding: calc(var(--gutter) + 90px) clamp(24px, 2vw, 40px) var(--gutter) var(--gutter);  /* disc bottom-aligns with the nav arrows (05.07) */
  background: var(--paper);   /* 04.07 (Chiara): solid ground — the reel scrolls BEHIND the words */
  z-index: 3;
  display: flex; flex-direction: column;
  transition: transform var(--dur-base) var(--ease-house), visibility 0.6s;
}
.words-off .cs-rail { transform: translateX(calc(-100% - var(--gutter) - 10px)); visibility: hidden; }
.cs-title { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-lg); line-height: 1.05; }   /* Title Case in content (Chiara 04.07) — no transform */
.cs-sector { margin-top: var(--space-xs); opacity: 0.75; font-weight: 400; }
.cs-copy { margin-top: var(--space-lg); }
.cs-copy p { font-size: var(--type-body-sm); line-height: 1.65; max-width: 40ch; }   /* 04.07 (Chiara): support tier, not body tier */
.cs-copy p + p { margin-top: var(--space-md); }
.cs-copy .cs-more { margin-top: var(--space-md); }   /* gap before the revealed paragraphs */
.cs-read {
  background: none; border: 0; padding: 0; cursor: pointer; color: inherit;
  display: block; width: fit-content; margin-top: var(--space-md);
  font-family: var(--font-label); font-weight: 400; font-size: var(--type-label);
  letter-spacing: var(--track-label); text-transform: uppercase;
  opacity: 0.5; transition: opacity var(--dur-quick) var(--ease-house);   /* 05.07 (Chiara): quiet, no underline — not too prominent */
}
.cs-read:hover { opacity: 0.85; }
/* 06.07 (Chiara): disc lifts to the image's bottom edge (was pinned to the very
   base). The prev/next nav takes the bottom corners in its place. */
.cs-disc { position: absolute; left: var(--gutter); bottom: calc(var(--gutter) + 56px); font-weight: 400; }
/* Hide-words / fold control retired 05.07 (Chiara) — the rail stays put. */

/* The reel — panels start beyond the rail; words-off reclaims the space */
.cs-track {
  gap: var(--space-sm);   /* 04.07 (Chiara): "15px between each" — on-token 16 */
  padding-left: calc(var(--cs-rail-w) + var(--gutter) + clamp(24px, 2vw, 40px) + var(--space-sm));
  transition: padding-left var(--dur-base) var(--ease-house);
}
.words-off .cs-track { padding-left: calc(var(--gutter) + 60px); }
.words-off .cs-rail { pointer-events: none; }
.cs-track .hstrip-item img, .cs-track .hstrip-item video, .cs-track .hstrip-item .ph-media {
  height: var(--cs-panel-h); width: auto;
  transition: height var(--dur-base) var(--ease-house);
}
/* 05.07 (Chiara): case-study panels keep their true aspect — override the
   base strip's object-fit:cover + 78vw cap, which crop-zoomed the wide
   Zambala mockups (read as "stretched"). width:auto sets the box to the
   image ratio, so there is nothing to crop. */
.cs-track .hstrip-item img, .cs-track .hstrip-item video { max-width: none; object-fit: contain; }
.cs-track .ph-media { display: block; }
/* Brand mark centred over the lead photo (05.07, Chiara). Higher specificity
   than the panel-img sizing rule so it stays logo-sized, not panel-height. */
.hstrip-item--logo { position: relative; }
.cs-track .hstrip-item .cs-panel-logo {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 62%; max-width: 340px; height: auto; z-index: 3; pointer-events: none;
  filter: brightness(0) invert(1) opacity(0.95);   /* cream-white over the photo */
  transition: none;
}
/* Project nav (05.07, Chiara) — one quiet "next project" in the bottom-right,
   Eiko serif lowercase, aligned to the disc baseline. Subtle, not a button. */
.cs-next, .cs-prev {
  position: absolute; bottom: var(--gutter); z-index: 5;
  display: inline-flex; align-items: center; gap: 0.45em;
  font-family: var(--font-serif); font-weight: 300; text-transform: none;
  font-size: var(--type-serif-sm); line-height: 1; letter-spacing: 0;
  color: inherit; opacity: 0.55; transition: opacity var(--dur-quick) var(--ease-house);
}
.cs-next { right: var(--gutter); }
.cs-prev { left: var(--gutter); }
.cs-next:hover, .cs-prev:hover { opacity: 1; }
.cs-next-arrow { width: 0.62em; height: 0.62em; margin-top: 0.16em; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; transition: transform var(--dur-quick) var(--ease-house); }
.cs-next:hover .cs-next-arrow { transform: translateX(4px); }
.cs-prev:hover .cs-next-arrow { transform: translateX(-4px); }
.words-off .cs-next, .words-off .cs-prev { color: var(--cream); }   /* panels cover the band when words are hidden */
@media (max-width: 700px), (prefers-reduced-motion: reduce) {
  .cs-next, .cs-prev { position: static; display: inline-flex; margin: var(--space-md) var(--gutter) 0; }
}

/* Reel finale — the conversion, not a next-project detour (Chiara 04.07) */
.cs-cta-panel { display: grid; align-content: center; justify-items: center; text-align: center; gap: var(--space-cta); min-width: 28vw; padding: 0 var(--gutter); height: var(--cs-panel-h); }  /* 05.07 (Chiara): content centred in the panel space */
.cs-cta-line { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-lg); line-height: 1.15; max-width: 14ch; }

@media (max-width: 700px), (prefers-reduced-motion: reduce) {
  /* stacked: rail in flow above a native horizontal scroller; no hide-words */
  .cs-rail { position: static; width: auto; padding: calc(var(--gutter) + 90px) var(--gutter) 0; }
  .cs-hide, .cs-spine, .cs-show { display: none; }
  .cs-disc { margin-top: var(--space-lg); }
  .cs-track { padding-left: var(--gutter); }
}

/* ============================================================
   PHOTOGRAPHY (/photography) — stacked destination editions.
   Ruled 04.07: Filmstrip, one grammar site-wide (MOTION.md P1).
   Arrival mirrors the /work interior (hairline + serif title);
   each edition is a shared .hstrip filmstrip driven by js/strip.js.
   ============================================================ */
.photo-head { padding: calc(var(--gutter) + 90px) var(--gutter) 0; }   /* sits high — clears the fixed header, no hairline */
.photo-title { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-statement); line-height: 1; }
.photo-lede {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-md); line-height: 1.2;
  max-width: 42ch; margin-top: var(--space-md);   /* a touch wider (was 30ch) */
}

/* Editions — the filmstrips, stacked with a breath of ground between */
.photo-editions { margin-top: var(--space-2xl); }   /* first strip sits up closer under the arrival */
.photo-edition + .photo-edition { margin-top: var(--space-2xl); }

/* Frame-size rhythm (04.07): three sizes sequenced against the story beats —
   the opener lands big, the close-up details pull you in, the rest hold the
   line. Breaks the metronome of equal frames. Desktop only; the mobile
   native scroller keeps its own height. */
@media (min-width: 701px) {
  .photo-page .f-hero img,     .photo-page .f-hero video     { height: min(76vh, 860px); }   /* 05.07 (Chiara): down from 84vh — the hero was reaching the header/logo when centred in the pin */
  .photo-page .f-std img,      .photo-page .f-std video      { height: min(66vh, 740px); }
  .photo-page .f-intimate img, .photo-page .f-intimate video { height: min(48vh, 540px); }
}

/* Edition masthead — the first car of each filmstrip: number, place,
   one line. Sits in the track like a frame, reads like a chapter head. */
.hstrip-mast { display: grid; align-content: center; width: fit-content; max-width: min(44vh, 74vw); padding-right: var(--space-md); }   /* hugs its text so the first frame sits close */
.hstrip-mast .section-label { display: block; margin-bottom: var(--space-sm); }
.edition-title { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-lg); line-height: 1; }
.edition-line {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-sm); line-height: 1.25;
  max-width: 24ch; margin-top: var(--space-sm);
}

/* THE SIGNATURE — soft float. The brief (Chiara 04.07): make the destination
   feel felt, alive. Each frame breathes on a slow vertical drift, staggered
   out of step so the strip levitates rather than bobbing in unison. Ambient
   motion, not a fade; composes over the horizontal drift (translateY on the
   image, translateX on the track). The photo lifts above its caption. */
@media (prefers-reduced-motion: no-preference) {
  .photo-edition .hstrip-item img,
  .photo-edition .hstrip-item video {   /* stills and live frames float alike */
    animation: photo-float var(--float-dur, 8s) var(--ease-ambient) infinite;
    animation-delay: var(--float-delay, 0s);
  }
  .photo-edition .hstrip-item:nth-child(4n+1) img, .photo-edition .hstrip-item:nth-child(4n+1) video { --float-dur: 8.4s; --float-delay: -2.1s; }
  .photo-edition .hstrip-item:nth-child(4n+2) img, .photo-edition .hstrip-item:nth-child(4n+2) video { --float-dur: 7.1s; --float-delay: -0.6s; }
  .photo-edition .hstrip-item:nth-child(4n+3) img, .photo-edition .hstrip-item:nth-child(4n+3) video { --float-dur: 9.6s; --float-delay: -3.4s; }
  .photo-edition .hstrip-item:nth-child(4n)   img, .photo-edition .hstrip-item:nth-child(4n)   video { --float-dur: 6.7s; --float-delay: -1.3s; }
}
@keyframes photo-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-12px); }
}

/* Ground (04.07): trying cream + dark text (Chiara). The dark gallery variant
   is parked in git history — set body.photo-page background to var(--gallery)
   + colour var(--cream) and restore the header/logo cream rules to bring it back. */
/* 10.07 (Chiara): light warm beige ground — the dark brown read too heavy
   site-wide; imagery carries the colour. Dark variants parked in git. */
body.photo-page { background: var(--beige); color: var(--ink); --font-serif: 'PP Eiko', 'Times New Roman', serif; }

/* Service close (04.07) — the postcards prove the craft; this beat makes it
   commissionable. Centred, same grammar as the homepage enquire; the scope
   line is what turns a gallery into an offer. */
.photo-service { padding: var(--space-section) var(--gutter) var(--space-2xl); text-align: center; }
.photo-service-eyebrow { display: block; margin-bottom: var(--space-lg); opacity: 0.55; }
.photo-service-statement {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-statement); line-height: 1.15;
  max-width: 24ch; margin: 0 auto var(--space-xl);
}
.photo-service-scope {
  list-style: none; display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--space-sm) var(--space-lg); margin: 0 auto var(--space-xl); max-width: 54ch;
}
.photo-service-scope li {
  font-family: var(--font-label); font-weight: 400; font-size: var(--type-label);
  letter-spacing: var(--track-label); text-transform: uppercase; opacity: 0.7;
}
.photo-service .cta--large { font-size: var(--type-label-lg); }  /* 05.07 (Chiara): underline removed to match the enquire CTA */
/* Hero commission cue — a quiet text link, bookends the close's "Commission the lens" */
.photo-head-cta { display: inline-block; margin-top: var(--space-lg); }


/* Micro-craft (site-wide) */
::selection { background: var(--green); color: var(--cream); }
:focus-visible { outline: 2px solid currentColor; outline-offset: 4px; }
.ph-media { position: relative; }
.ph-media::after {   /* pending imagery reads as pending, not broken */
  content: '( Imagery to follow )';
  position: absolute; inset: 0; display: grid; place-items: center;
  font-family: var(--font-label); font-weight: 300;
  font-size: var(--type-label); letter-spacing: var(--track-label);
  text-transform: uppercase; opacity: 0.45;
}


/* Selected clients — auto-scrolling marquee (04.07, Raben Rifaie ref). Two
   copies of the set; -50% loops seamlessly. Pauses on hover; off for reduced
   motion. Marks kept small (Chiara: were a touch big). Swap text for logo SVGs. */
.clients { padding: var(--space-2xl) 0; overflow: hidden;
  /* 24.08 v19 (Chiara: "the Method doesn't need to cycle to lighter… as you scroll down it fades to lighter"): the wall rides the teaser's --mx vars (main.js) — the return to paper happens across THIS hand-off, both grounds identical at every instant, the logo masks (currentColor) arriving cream and settling to ink. At rest (--mx 0) this is paper, as before. */
  /* 02.09 v5: the ground moved OUT of this rule and into the shared descent block near the foot of this file (search THE DESCENT). The wall no longer paints its own ground — it is one link in a three-section run, which is the only way the edges can disappear. */
}   /* v5: back in normal flow after the Method (the v3/v4 reveal frame retired) */   /* 19.08 v3 (Chiara: "it shouldn't scroll up — the page should already be there and appear"): the wall sits UNDER the Method's last frame (−100vh), centred in it; the Method's ground fades to transparent over it, so the wall is revealed in place, then scrolls on normally */
.clients-label { text-align: center; margin-bottom: var(--space-lg); opacity: 0.5; }
.marquee { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.marquee-track { display: flex; width: max-content; gap: clamp(40px, 6vw, 96px); margin: 0; padding: 0; list-style: none; animation: marquee 45s linear infinite; }
/* 01.09 (Chiara: "the logo carousel is too weird… it needs to do what happens further up the page"):
   SCROLL-DRIVEN, like the drift and the floats above it. The 45s timer animation is off; the track
   is moved by the page instead (--marq, set by main.js from the wall's own crossing progress), so
   it advances only while you scroll and holds still when you stop. Reduced motion: seated. */
.clients .marquee-track { animation: none; transform: translateX(var(--marq, 0px)); will-change: transform; }
.marquee-track li { font-family: var(--font-serif); font-weight: 300; font-size: clamp(20px, 1.8vw, 28px); line-height: 1; white-space: nowrap; opacity: 0.5; display: flex; align-items: center; }   /* 18.08: items centre their logo marks on one optical baseline */
/* 18.08 (Chiara): the client marks — colour-controllable masks (currentColor),
   each sized by its own optical height (--lh) so wordmarks and stacked marks
   sit at one visual weight; --logo-scale steps the whole wall at once. */
.client-logo {
  display: block;
  --logo-scale: 1;
  height: calc(var(--lh, 32px) * var(--logo-scale));
  width: calc(var(--lh, 32px) * var(--logo-scale) * var(--ar, 3));   /* --ar = the mark's own w/h, so the box IS the mark — no contain-shrink on the wide wordmarks */
  background: currentColor;
  -webkit-mask: var(--logo) center / contain no-repeat;
          mask: var(--logo) center / contain no-repeat;
}
@media (max-width: 900px) { .client-logo { --logo-scale: 0.8; } }
/* .marquee:hover pause RETIRED 18.08 (Chiara: "it shouldn't pause on hover") — the wall keeps moving */
@keyframes marquee { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; flex-wrap: wrap; justify-content: center; width: auto; }
  .marquee-track li[aria-hidden="true"] { display: none; }
}
/* ============================================================
   FILM (/film) — full-screen hero films, scroll-driven zoom + swipe.
   Each scene pins; the frame zooms slowly; the next scene's sticky
   scrolls up over it (a swipe). Poster + lazy video; one plays at a time.
   ============================================================ */
body.film-page { background: var(--beige); color: var(--ink); --font-serif: 'PP Eiko', 'Times New Roman', serif; }  /* 10.07 (Chiara): beige, matching photography */

/* Arrival — mirrors the photography head */
.film-head { position: relative; min-height: 82vh; display: flex; flex-direction: column; justify-content: center; padding: calc(var(--gutter) + 90px) var(--gutter) var(--space-2xl); }
.film-title { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-statement); line-height: 1; }
.film-lede { font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: 1.5; max-width: 42ch; margin-top: var(--space-lg); opacity: 0.9; }
.film-head-cta { display: inline-block; margin-top: var(--space-lg); opacity: 0.55; transition: opacity var(--dur-quick) var(--ease-house); }  /* 05.07 (Chiara): faded, quiet */
.film-head-cta:hover { opacity: 1; }
.film-scroll-cue { position: absolute; left: var(--gutter); bottom: var(--space-xl); opacity: 0.5; }

/* Reel — pin + swipe stack */
.film-reel { position: relative; }
.film-scene { height: 150vh; position: relative; }   /* 05.07 (Chiara): shorter hold — snappier, smoother swipes */
.film-sticky { position: sticky; top: 0; height: 100vh; overflow: hidden; background: var(--green); }
.film-video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  background: var(--green);
}
.film-scrim {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to top, rgba(0,0,0,0.55), transparent 36%),
    linear-gradient(to bottom, rgba(0,0,0,0.22), transparent 20%);
}
/* Client logo centred over the film (05.07, Chiara). Placeholder = client
   wordmark; drop a real logo SVG into .film-logo (as <img>) to swap. */
.film-logo { position: absolute; inset: 0; z-index: 2; display: grid; place-items: center; pointer-events: none; padding: 0 var(--gutter); }
.film-logo span { font-family: var(--font-serif); font-weight: 300; font-size: clamp(38px, 6vw, 92px); line-height: 1; color: var(--cream); letter-spacing: 0.04em; text-transform: uppercase; text-align: center; }
.film-logo img { width: min(42vw, 480px); height: auto; filter: brightness(0) invert(1) opacity(0.96); }
/* small, pre-coloured client mark (e.g. Palmer icon in Sand) — no white filter */
.film-logo--mark img { width: min(13vw, 140px); filter: none; }

/* Caption — client + services, lower-left third */
.film-caption { position: absolute; left: var(--gutter); right: var(--gutter); bottom: var(--space-xl); z-index: 2; color: var(--cream); }
.film-caption figcaption { margin: 0; }
.film-client { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.05; }
.film-services { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-xs) 0; margin-top: var(--space-sm); list-style: none; padding: 0; opacity: 0.85; }
.film-services li:not(:last-child)::after { content: '·'; margin: 0 var(--space-sm); opacity: 0.5; }

/* Sound toggle */
.film-sound {
  position: fixed; bottom: var(--space-xl); right: var(--gutter); z-index: 3;   /* 10.07: was absolute per-scene — rode up into the header during scene exits */
  background: color-mix(in srgb, #000 30%, transparent); color: var(--cream);   /* clean solid fill */
  border: 1px solid color-mix(in srgb, var(--cream) 35%, transparent); border-radius: 200px;
  padding: 7px 16px; cursor: pointer;
  font-family: var(--font-label); font-size: var(--type-label); letter-spacing: var(--track-label); text-transform: uppercase;
  transition: background var(--dur-quick) var(--ease-house), color var(--dur-quick) var(--ease-house), border-color var(--dur-quick) var(--ease-house);
}
.film-sound:hover { background: color-mix(in srgb, #000 44%, transparent); }
.film-sound[aria-pressed="true"] { background: var(--paper); color: var(--ink); border-color: var(--cream); }

@media (max-width: 760px), (prefers-reduced-motion: reduce) {
  /* stacked native — no pin/zoom */
  .film-scene { height: auto; }
  .film-sticky { position: static; height: 78vh; }
  .film-video { transform: none !important; }
  .film-sound { top: auto; bottom: calc(var(--space-xl) + 96px); }
  .film-scene + .film-scene { margin-top: var(--space-xs); }
}

/* ============================================================
   VIRTUOSO / TRAVEL ADVISORS LANDING (/virtuoso) — 09.07
   Campaign page for VTW 2026. Composes existing components
   (work-head, web-why, wp-list, work-index, insight, enquire);
   only the page-specific pieces live here. Tokens only.
   ============================================================ */
/* 03.08 (Chiara: "margins totally off"): web-why/web-process were designed
   inside .work-main, which supplied the gutter — bare on /virtuoso they ran
   edge-to-edge. .vtw-pad restores the page rhythm; hero clearance below. */
.vtw-pad { padding-inline: var(--gutter); }
.vtw-hero { color: var(--cream); height: 100svh; }   /* 07.08 (Hannah doc): mirror screen cut — the base .cs-hero 200svh would leave a blank second viewport, so the campaign hero is single-screen */
.vtw-aud { opacity: 0.78; margin-bottom: var(--space-md); }   /* 03.08: the hybrid hero missed svc-hero--media's cream cascade — lead + title ran ink over the image */
.vtw-hero .cs-hero-screen--title {   /* landing arrangement on the case-hero mechanics: title low-left, hook right — the svc grammar */
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: calc(var(--space-section) * 0.75) var(--gutter) var(--space-xl);
}
/* 03.08: the What This Is beat — label + left-set body, one measure throughout */
.cs-hero + .vtw-what { padding-top: var(--space-3xl); }
.vtw-what { margin-bottom: var(--space-3xl); }
.vtw-what .section-label { display: block; opacity: 0.65; margin-bottom: var(--space-lg); }
.vtw-what-body p {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-md); line-height: 1.45;
  max-width: 44ch;
}
.vtw-what-body p + p { margin-top: var(--space-md); }
/* 14.08 v2 (Chiara: "too much eiko font"): only the FIRST paragraph of a
   What-This-Is body holds the serif claim — every following paragraph drops
   to the sans body register (the serif-claim / sans-support grammar the
   homepage origin ruled today). */
.vtw-what-body p + p {
  font-family: var(--font-body); font-weight: 300;
  font-size: var(--type-body); line-height: var(--lh-body);
  opacity: 0.85; max-width: 52ch;
}
/* 14.08 v2 vertical rail TRIED + RETIRED same hour → v3 (Chiara: "use the
   alexis-landrum proposal structure almost"): the HORIZONTAL timeline, ported
   to site grammar — the hairline draws left→right on arrival (.in via IO),
   week-stops stagger in as columns beneath it. Ink only; the proposal's
   sand→clay→umber colour ramp stays with its own palette. */
.vtw-htl { position: relative; margin-top: var(--space-xl); }
.vtw-htl-line { position: absolute; top: 5px; left: 0; right: 0; height: 1px; background: color-mix(in srgb, var(--ink) 25%, transparent); }
.vtw-htl-line::after {
  content: ''; position: absolute; inset: 0; background: var(--ink);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur-cinema) var(--ease-house) 0.3s;
}
.vtw-htl.in .vtw-htl-line::after { transform: scaleX(1); }
.vtw-htl-stops { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr; row-gap: var(--space-lg); }
.vtw-htl-stop {
  position: relative; padding-top: var(--space-lg);
  opacity: 0; transform: translateY(14px);
  transition: opacity var(--dur-slow) var(--ease-house), transform var(--dur-slow) var(--ease-house);
}
.vtw-htl.in .vtw-htl-stop { opacity: 1; transform: none; }
.vtw-htl-stop:nth-child(1) { transition-delay: 0.35s; }
.vtw-htl-stop:nth-child(2) { transition-delay: 0.75s; }
.vtw-htl-stop:nth-child(3) { transition-delay: 1.15s; }
.vtw-htl-stop:nth-child(4) { transition-delay: 1.55s; }
.vtw-htl-stop::before {
  content: ''; position: absolute; top: 0; left: 0;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--paper); border: 1px solid var(--ink);
}
.vtw-htl-stop:last-child::before { background: var(--ink); }   /* launch lands solid */
.vtw-htl-stop .wk { display: block; margin-bottom: var(--space-sm); opacity: 0.65; }
.vtw-htl-stop strong { display: block; font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-sm); margin-bottom: var(--space-xs); }
.vtw-htl-stop p { font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: var(--lh-body); opacity: 0.85; margin: 0; max-width: 30ch; }
@media (max-width: 959px) { .vtw-htl-line { display: none; } .vtw-htl-stop { padding-left: var(--space-md); } .vtw-htl-stop::before { top: calc(var(--space-lg) + 5px); } }
@media (min-width: 960px) { .vtw-htl-stops { grid-template-columns: repeat(4, 1fr); column-gap: var(--space-xl); } }
@media (prefers-reduced-motion: reduce) {
  .vtw-htl-line::after { transition: none; transform: scaleX(1); }
  .vtw-htl-stop { opacity: 1; transform: none; transition: none; }
}
.web-process.vtw-pad { margin-bottom: var(--space-3xl); }

/* 14.08 SPARSE CUT (Chiara, Crown/MOF/Domingo standard, spec approved):
   ruled discipline rows + full-bleed image bands. */
.vtw-rows { margin-top: var(--space-xl); }
.vtw-rows .disc-row { cursor: default; }
.vtw-rows .disc-row:hover { transform: none; }   /* rows, not links — no nudge */
.vtw-rows .disc-row::before,
.vtw-rows .disc-row:last-child::after { transform: scaleX(1); transition: none; }   /* hairlines seated — the draw-in belongs to the homepage router (.disc.lines-in) */
.vtw-row-note {
  font-family: var(--font-body); font-weight: 300;
  font-size: var(--type-body); line-height: var(--lh-body);
  opacity: 0.7; margin-left: var(--space-md);
}
.vtw-band { width: 100%; height: min(62vw, 72svh); overflow: hidden; margin: var(--space-3xl) 0; }
.vtw-band img, .vtw-band video { width: 100%; height: 100%; object-fit: cover; display: block; }

.vtw-proof { padding: 0 var(--gutter); margin-bottom: var(--space-2xl); }   /* 03.08 five-beat merge: tighter hand-off into the carousel — same chapter */
.vtw-proof > .section-label { display: block; margin-bottom: var(--space-lg); }
.vtw-proof .work-index { margin-top: var(--space-xl); }
.vtw-proof .quiet-link { display: inline-block; margin-top: var(--space-lg); }

/* Proof lead — the aspiration paragraph before the quote */
.vtw-proof-lead p {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.3;
  max-width: 30ch;
  margin-bottom: var(--space-xl);
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
}

/* 03.08: testimonial carousel over a full-bleed image (Chiara). Scroll-snap,
   manual — crisp swipes, no autoplay, no fades. Logo seats until assets land. */
.vtw-cases { position: relative; overflow: hidden; min-height: 100svh; display: flex; flex-direction: column; justify-content: flex-end;   /* 03.08 (Chiara): testimonial bottom-left aligned — hero-lead posture */ padding: var(--space-2xl) 0; color: var(--cream); margin-bottom: var(--space-3xl); }   /* 03.08 (Chiara): "testimonials full vph and vpw" — full-viewport section, quote centred over the image */
.vtw-cases-bg { position: absolute; inset: 0; background: var(--gallery); display: grid; place-items: center; }   /* 03.08: canvas seat while imageless */
.vtw-cases-bg img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vtw-cases-bg::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(rgba(0, 0, 0, 0.20), rgba(0, 0, 0, 0.20)),
              rgba(0, 0, 0, 0.18);   /* the hero scrim's base layers — quotes need the same legibility */
}
.vtw-carousel {
  position: relative; z-index: 1;
  display: flex; gap: var(--space-2xl);
  overflow-x: auto; scroll-snap-type: x mandatory;
  padding: 0 var(--gutter);
  scrollbar-width: none;
}
.vtw-carousel::-webkit-scrollbar { display: none; }
.vtw-carousel .vtw-t-slide { flex: 0 0 100%; max-width: none; scroll-snap-align: center; margin: 0; display: flex; flex-direction: column; min-height: 62svh; }   /* 03.08: logo top, quote at the base — the scene reads top-to-bottom */
.vtw-carousel .vtw-t-slide .vtw-t-logo { margin-bottom: auto; }   /* 03.08 (Chiara): ONE testimonial per slide — scoped selector outranks .vtw-quote's later 62ch cap, which was re-shrinking the slides */
.vtw-t-slide > p { max-width: 62ch; }   /* 03.08 (Chiara): left-aligned — the auto-centring dropped */
.vtw-cases-head { position: relative; z-index: 1; display: block; padding: 0 var(--gutter); margin-bottom: var(--space-xl); opacity: 0.78; }
.vtw-carousel .vtw-quote + .vtw-quote { margin-top: 0; }   /* the stacked-quote rule doesn't apply on the rail */
.vtw-carousel .vtw-quote p:first-child {
  font-size: var(--type-serif-md);   /* 03.08 (Chiara: "what's happening to the font"): the on-paper quote size read wrong at full-viewport scale */
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;   /* the on-paper subpixel smoothing fringes cream text over imagery — antialiased like the heroes */
}
.vtw-car-nav { position: relative; z-index: 1; display: flex; justify-content: flex-end; gap: var(--space-sm); padding: var(--space-lg) var(--gutter) 0; }
.vtw-car-btn {
  width: 44px; height: 44px; border-radius: 50%;
  background: none; border: 1px solid color-mix(in srgb, var(--cream) 55%, transparent);
  color: var(--cream); font-size: 16px; cursor: pointer;
  display: grid; place-items: center;
  transition: background var(--dur-quick) var(--ease-house);
}
.vtw-car-btn:hover { background: color-mix(in srgb, var(--cream) 12%, transparent); }
.vtw-t-client { display: flex; align-items: center; gap: var(--space-md); margin-top: var(--space-lg); flex-wrap: wrap; }
.vtw-t-client .vtw-quote-name { margin-top: 0; }   /* 03.08: .vtw-t-client wrapper retired from the carousel markup — logo rides the top of the slide now */
.vtw-t-logo { opacity: 0.65; }
.vtw-t-logo img { height: 40px; width: auto; display: block; }   /* when the logo files land */

/* Named quote — the work-track quote grammar, standing alone on paper */
/* 03.08: awards strip — recognition held by the advisors we've built for (their laurels, honestly framed) */
.vtw-afters { list-style: none; margin: 0 0 var(--space-xl); padding: 0; max-width: 62ch; }
.vtw-afters li { padding: var(--space-sm) 0; border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.vtw-afters li:last-child { border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.vtw-proof-lead p:last-of-type { margin-bottom: var(--space-xl); }

.vtw-awards-head { margin-top: var(--space-lg); }
/* 03.08 (Chiara): the roster as image tiles — see the work per advisor */
.vtw-adv-grid { list-style: none; margin: var(--space-sm) 0 var(--space-xl); padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-lg) var(--gutter); }
.vtw-adv-grid a { display: block; }
.vtw-adv-grid .wi-name { text-transform: none; }   /* 03.08 (Chiara: "i dont like the all caps"): advisor names in sentence case; the mono register stays */
.vtw-adv-grid .wi-media { position: relative; aspect-ratio: 8 / 5; overflow: hidden; margin: 0 0 var(--caption-gap); width: auto; height: auto; }
.vtw-adv-grid .wi-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vtw-adv-seat { background: color-mix(in srgb, var(--ink) 10%, var(--cream)); display: grid; place-items: center; }   /* 03.08 (Chiara): grey squares for missing images — reads as a seat, not a broken tile */
.vtw-adv-seat .label { opacity: 0.6; }
@media (max-width: 900px) { .vtw-adv-grid { grid-template-columns: repeat(2, 1fr); } }
.vtw-awards { display: flex; flex-wrap: wrap; gap: var(--space-sm) var(--space-lg); list-style: none; padding: 0; margin-block: var(--space-sm) var(--space-xl); }
.vtw-awards .label { opacity: 0.65; }

.vtw-quote { max-width: 62ch; }
.vtw-quote + .vtw-quote { margin-top: var(--space-xl); } /* 03.08: two stacked quotes (Eli, Corey) */
.vtw-quote p:first-child {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-sm);   /* lead carries serif-md; the quote steps down */
  line-height: 1.3;
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
}
.vtw-quote-name { margin-top: var(--space-md); opacity: 0.65; }

/* Offer — insight grammar carries it; only the off-ramp line is new */
/* 03.08: the three disciplines under the plain statement — the what-we-do outline */
/* 03.08 (Chiara: "3 next to each"): the disciplines as three columns — number,
   name, note stacked under a shared hairline. Stacks to one column on mobile. */
.vtw-disciplines { list-style: none; margin: var(--space-xl) 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-lg) var(--gutter); }
.vtw-disciplines li { padding-top: var(--space-md); border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.vtw-disciplines .label { display: block; opacity: 0.65; margin-bottom: var(--space-sm); }
.vtw-disciplines strong { display: block; font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-sm); margin-bottom: var(--space-xs); }
.vtw-d-note { display: block; opacity: 0.8; max-width: 32ch; }
@media (max-width: 900px) { .vtw-disciplines { grid-template-columns: 1fr; } }
.vtw-included { list-style: none; margin: var(--space-lg) 0 var(--space-xl); padding: 0; }
.vtw-included li { padding: var(--space-sm) 0; border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.vtw-included li:last-child { border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }

.vtw-alt { margin-top: var(--space-lg); font-size: var(--type-body-sm); opacity: 0.8; }
.vtw-alt .quiet-link { color: var(--green); }

.vtw-enquire { padding-top: var(--space-section); }


/* ============================================================
   HOMEPAGE v2 (10.07) — swipe-stack works + disciplines router
   ============================================================ */
/* THE WORK — one-screen swipe deck (10.07 v2.1, Stud layout): slides stack
   in one pinned viewport; scroll ADVANCES with a discrete upward wipe on the
   house curve — never a scrubbed position, never a fade. */
.hw { height: 600vh; position: relative; }   /* 24.08 (Chiara ruled the deck to FOUR — Palmer, Odisi, LHC, Zambala; Hustl cut): 750 → 600, same ~150vh budget per slide. /work's 7-slide 1050vh override is untouched. */   /* 14.07: was 5 slides (LHC + Hustl added) */
.hw-pin { position: sticky; top: 0; height: 100vh; overflow: hidden; background: var(--gallery); }
body.home .hw-pin { background: var(--ground-3); }   /* 09.09 (the ground scan): the pin's --gallery (#1E1917) sat between two --ground-3 (#110B09) sections — a 24-unit edge above the deck for a step and BELOW it for ~800px of scroll while the voices arrived. On the homepage the pin's ground is only ever seen in the swipe gaps and at the stage edges, so it takes the section's own colour. /work keeps --gallery (its close spacer is matched to it). */
/* 13.08 (Chiara: "the palmer hero image needs to grow — the inverted version
   of the hero video shrinking"): THE EXPANSION. The homepage deck's stage
   opens from a contained centred frame (40vw × 40svh) to full-bleed across
   the pin's first viewport of scroll (--grow, JS, easeOutCubic — the same
   clip-window mechanic as the hero's contraction, inverted). Text entrances
   (hw-inview) hold until the frame is open; slide advance starts after the
   entry budget. Vero bookend: the film contracts out at the top, the work
   expands back in below. */
/* 18.08 (Chiara: "the mockups shouldn't shrink and grow — a parallax scroll
   and mask over the image that reveals, rather than the image changing size";
   ref fleava.com/works/ayana-residences, and "the effect you have used on the
   hero"): the 14.08 COUNTER-SCALE is RETIRED — the image never resizes. The
   window is a pure MASK over full-bleed footage, exactly the hero's clip-
   window grammar; beneath it the slide LAGS (translateY, 22svh → 0 as the
   window opens) so the closed frame shows the composition's centre instead
   of a raw top-strip crop, and the mask edges and the image travel at
   different rates — Fleava's parallax reveal, scroll-scrubbed. */
.hw--grow { height: 540vh; }   /* 02.09 (Chiara: "why do we have to do 2 scrolls to go through the case studies… can we just do one scroll and it changes"): 850 → 540vh. The maths: main.js takes total = height − 100vh, then spends the first 100vh on the grow entry, and divides what is LEFT across the slides. At 850vh that was (850−100−100)/4 = 162vh per slide, i.e. ~1.6 viewports — hence two gestures each. At 540vh it is (540−100−100)/4 = 85vh, just under one viewport, so one decisive flick lands the next case study. The entry budget is untouched, so the frame still opens before the swipes begin. /work keeps its own 1050vh (7 slides at the old 150vh budget) — say the word and it gets the same treatment. */
.hw--grow { background: var(--ground-3); }   /* 18.08 (Chiara: "the background of the case study needs to match that so the fade is seamless"): the clip-path window clips the PIN (its gallery ground included), so what shows around the opening window is the SECTION's ground — now the origin's apricot sand, not the body beige: the window opens out of the very ground the credential sat on. The gallery ground stays inside the window, behind the slides. */
.hw--grow .hw-pin {
  /* 13.08 v12 (Chiara: "palmer studio image should grow underneath start a
     project"): the window anchors near the stage TOP (was centred) — so the
     contained frame surfaces beneath the stack's CTA while approaching, then
     expands mostly downward + outward to full-bleed as the pin catches.
     Start: 40vw × 40svh, 8svh from the stage top. */
  clip-path: inset(calc((1 - var(--grow, 1)) * 8svh) calc((1 - var(--grow, 1)) * 30vw) calc((1 - var(--grow, 1)) * 52svh));
  will-change: clip-path;
}
/* 18.08 THE LAG (replaces the 14.08 counter-scale on .hw-pin > *): the closed
   window's centre sits at 28svh, the image's at 50svh — the slide rides 22svh
   high at g=0 and settles to 0 as the mask opens. Slide-level (not the media)
   so it scrubs crisply with the mask; the media's own drift rides on top. */
.hw--grow .hw-slide { transform: translateY(calc((1 - var(--grow, 1)) * -22svh)); will-change: transform; }
/* 14.08 v2: the slide's cinematic scrim compressed into a black cap inside
   the small frame — it fades in WITH the growth, so the contained image
   reads clean and the full-bleed deck keeps its held edges. */
.hw--grow .hw-slide::after { opacity: var(--grow, 1); }
.hw-slide {
  position: absolute; inset: 0;
  clip-path: inset(0 0 0 100%);   /* 10.07 v2.2 (Chiara, stud.house ref): LEFT swipe — the incoming slide enters at the right edge and its reveal sweeps left (was the upward wipe) */
  transition: clip-path var(--dur-slow) cubic-bezier(0.76, 0, 0.19, 1);   /* 14.07 v2 (Chiara: "a bit slow"): 1.2s → 0.9s — keeps the camera-move curve, loses the drag */
  will-change: clip-path;
}
.hw-slide.shown { clip-path: inset(0 0 0 0); }
/* 22.10 (Chiara: "the same swipe effect we have currently, just the direction is bottom to top, not left to right" → "try that"):
   .hw--up flips the deck's wipe axis. The clip opens from the BOTTOM edge (top inset 100% → 0) on the same 0.9s
   cubic-bezier(0.76, 0, 0.19, 1), and the media's 10% pre-drag comes from below instead of the right, so the camera
   settle is vertical too. Title roll, byline, Next cue, the view pill — all untouched. Drop the modifier to go back. */
.hw--up .hw-slide { clip-path: inset(100% 0 0 0); }
.hw--up .hw-slide.shown { clip-path: inset(0 0 0 0); }
.hw--up .hw-slide .hw-media { transform: translateY(10%) scale(1.16); }
.hw--up .hw-slide.shown .hw-media { transform: translateX(0) translateY(var(--drift, 0%)) scale(1.06); }
/* 14.07 (Chiara: "smoother and more luxurious… very cinematic light"): the
   slide text DEVELOPS after the sweep — the hero's arrival grammar ("the
   slow develop… luxury as stillness"): a long quiet dissolve, a small drift,
   and the title clearing from soft focus like light resolving. Title first,
   corner furniture a beat behind. Retiring .shown resets it, so a
   back-scroll replays the entrance. */
/* 14.07 v2 (Chiara: "glitches… a bit slow"): the blur-develop is CUT —
   blur + mix-blend-mode re-rasterises blended text mid-animation and
   shimmers on some GPUs — and every duration/delay tightens. The develop
   is now opacity + rise + the light-swell alone. */
.hw-slide .hw-title { opacity: 0; transform: translateY(calc(-50% + 24px)); }
.hw-slide .hw-tag,
.hw-slide .hw-byline,
.hw-slide .hw-next { opacity: 0; transform: translateY(14px); }
.hw-slide .hw-title, .hw-slide .hw-tag, .hw-slide .hw-byline, .hw-slide .hw-next {
  transition: opacity var(--dur-slow) var(--ease-house), transform var(--dur-slow) var(--ease-house);
}
/* 19.10 THE ORDER (Chiara: "the timing feels off — the titles should change and then the ( ) bottom bits; at the moment
   it's the other way round"): the TITLE LEADS, both ways. On exit the word rolled out on a 0.35s delay while the tag
   and byline faded at once — so the brackets left first. On entry the two were 0.2s apart under a 0.9s roll, which
   read as one beat. Now: exit — word rolls at 0s, brackets fade 0.25s after; entry — word rolls at 0.2s, brackets
   land at 0.75s, when the roll is ~80% settled. Deck-wide (.hw), so the homepage deck follows. */
.hw-slide .hw-tag, .hw-slide .hw-byline, .hw-slide .hw-next { transition-delay: 0.25s; }
/* 14.07 (Chiara): BRANDS WE'VE BUILT has NO entrance — it's section furniture,
   simply there; every slide carries the identical copy at the identical spot,
   so the wipe passes over it invisibly. */
.hw-slide .hw-brand { opacity: 0.85; transform: none; transition: none; }
.hw-slide.shown .hw-title { opacity: 1; transform: translateY(-50%); transition-delay: 0.35s; }
/* 15.07 (Chiara: "could this case study name appear with the same animation
   as lets talk — but just the first case study"): the title word-ROLLS
   into place (the Let's Talk entrance) instead of the opacity develop. The
   h2 holds its centred seat; the inner word rolls up through the clip.
   13.08 (Chiara: "make sure it's consistent across all of them"): the roll
   is now DECK-WIDE — every .hw-title carries --roll; the opacity develop
   remains only for .hw-tag / .hw-byline / .hw-next. */
.hw-slide .hw-title--roll { opacity: 1; transform: translateY(-50%); transition: none; }
.hw-title--two { font-size: clamp(36px, 6.4vw, 74px); }   /* 21.10 (Chiara: "Luxury Hospitality Consulting is in a really big font compared to everything else"): the one deck name that breaks to two lines takes 80% of the title token so its block weighs the same as the one-liners */
.hw-title-clip { display: block; overflow: hidden; padding-bottom: 0.22em; margin-bottom: -0.22em; }   /* breath for Eiko overshoot · 13.08 (Chiara: "the g in consulting is cut off"): 0.08 → 0.22em — the old breath was tuned on descender-less names (Palmer/Odisi/Zambala/Hustl); at line-height 1 the LHC "Consulting" g needs the full descender depth. Same negative margin keeps the seat identical. */
.hw-title--roll .hw-title-word {
  display: block;
  transform: translateY(112%);
  transition: transform var(--dur-slow) var(--ease-house) 0s;   /* 19.10: exit rolls at once (was 0.35s) — the title leads */
  will-change: transform;
}
.hw.hw-inview .hw-slide.shown .hw-title--roll .hw-title-word { transform: translateY(0); transition-delay: 0.2s; }   /* 15.07: gated on the deck's arrival (.hw-inview from JS) — slide 0 is .shown from load, so ungated the roll played off-screen */
.hw.hw-inview .hw-slide.shown .hw-tag,
.hw.hw-inview .hw-slide.shown .hw-byline { opacity: 0.85; transform: translateY(0); transition-delay: 0.75s; }   /* 13.08 (Chiara: "can this animate on like the other text" — Palmer's byline): gated on .hw-inview like the title roll (15.07 fix) — slide 0 is .shown from load, so ungated its entrance played off-screen and the byline just sat there */
.hw.hw-inview .hw-slide.shown .hw-next { opacity: 1; transform: translateY(0); transition-delay: 0.75s; }
/* 14.07 v2 (Chiara: "should it sit under the project name" — yes): one line
   of story per slide, a caption directly beneath the title. Offset = half
   the title's height (it's centred at 50%) + a breath; LHC's two-line
   title gets its own deeper offset. */
.hw-byline {
  position: absolute; left: var(--gutter); right: var(--gutter);
  top: calc(50% + clamp(22px, 4vw, 46px) + 28px);   /* 12.10: 50% + half of the 92 step + gap (was half of the 123 rung) */
  text-align: center;
  /* 14.07 v6 (Chiara, MOF sample screenshot — "SONEVA | RECLAIM ITS
     POSITION…"): the caption register is a MONOSPACE. One caption voice
     across the whole frame — 14px / 400 / caps / mono — wording as
     [SUBJECT] | [THE WORK], flat and composed.
     13.08: sans-body trial TRIED + REVERTED same day (Chiara: "not sure i
     like the other font") — the byline stays mono even with Hannah's
     spoken-line copy in the slot. */
  font-family: var(--font-mono); font-weight: 400;
  font-size: var(--type-label);   /* 13.08 (Chiara: "bumped 1 or 2 px"): the byline leaves the corner rung (13px, shared with Brands We've Built / Next:) and joins the LABEL rung (14px) — one token step, the corners stay untouched */
  text-transform: uppercase;
  letter-spacing: normal;   /* mono carries its own rhythm; MOF sample runs untracked */
  line-height: 1.4;
  color: var(--cream);          /* 14.07 v3: inversion off */
  z-index: 2;
}
.hw-slide[data-project="lhc"] .hw-byline { top: calc(50% + clamp(44px, 8vw, 92px) + 28px); }   /* half of the 2-line block = one title size; 12.10: on the 92 step like the rest */
/* 14.07 (cinematic light): the footage drags in WITH the sweep and the
   LIGHT SWELLS as the frame parks. 15.07 v3 (Chiara: "more of a movement
   when it loads — too subtle"): the drag doubled (10% travel, 1.16 scale,
   deeper 0.68 lift) over a longer 1.6s settle — a real camera move now. */
.hw-slide .hw-media {
  transform: translateX(10%) scale(1.16);
  filter: brightness(0.68);
  transition: transform var(--dur-cinema) var(--ease-house), filter var(--dur-cinema) var(--ease-house);
  will-change: transform, filter;
}
.hw-slide.shown .hw-media {
  /* 14.07 (stud.house ref): parked media holds a 6% scale headroom and
     drifts vertically on --drift (JS-lerped scroll progress) — the "light
     movement" on the imagery; the 1.2s transition above smooths each step */
  transform: translateX(0) translateY(var(--drift, 0%)) scale(1.06);
  filter: brightness(1);
}
/* 19.08 (Chiara: "for the Zambala video, increase the saturation slightly — a bit
   more vibrant and a bit deeper"): a GRADE on the slide, not a re-encode —
   +18% saturation, a touch of contrast for depth; the entrance keeps its lift. */
.hw-slide[data-project="zambala"] .hw-media { filter: brightness(0.68) saturate(1.18) contrast(1.05); }
/* 19.08 (Chiara: "does the Palmer image look too saturated? more natural, or a darker overlay?"): a light grade, not a heavy overlay — saturation −12%, and its flat wash deepens a touch (below) */
/* 29.08 (Chiara: "can the Palmer photo on the homepage be the same we used on the case study page"): it IS the same file — the difference was this grade (saturate 0.88) + the heavier Palmer-only washes. RETIRED: the slide now takes the deck's default scrim, the same one the case-study hero holds, so the colour matches. The three retired rules, for the record:
   .hw-slide[data-project="palmer"] .hw-media { filter: brightness(0.68) saturate(0.88); }
   .hw-slide[data-project="palmer"].shown .hw-media { filter: brightness(1) saturate(0.88); }
   .hw-slide[data-project="palmer"]::after { background: linear-gradient(rgba(0,0,0,.16), rgba(0,0,0,.16)), linear-gradient(to bottom, rgba(0,0,0,.38), transparent 26%), linear-gradient(to top, rgba(0,0,0,.46), transparent 34%), rgba(0,0,0,.18); } */
.hw-slide[data-project="zambala"].shown .hw-media { filter: brightness(1) saturate(1.18) contrast(1.05); }
/* 10.07 v2.2 (Chiara): services TICKER — one persistent ( disciplines ) line
   on the pin; each word sits in a slot that clips one line and rolls like a
   split-flap when the slide changes (shared words hold still). JS builds it
   from the slide tags and adds .hw--ticker; the per-slide .hw-tag stays as
   the static/mobile fallback. */
.hw--ticker .hw-slide .hw-tag { display: none; }
.hw-ticker {
  position: absolute; left: 0; right: 0; bottom: var(--space-lg);
  display: flex; justify-content: center; align-items: center; gap: 0.08em;   /* 15.07 (Chiara: "reduce the spacing between words"): the slots' 6px clip guard + this sliver ≈ one true word space after each comma */
  opacity: 0.85; z-index: 2;
  font-family: var(--font-serif); font-weight: 300;   /* 14.07 v4: back to Eiko with .hw-tag */
  font-size: var(--type-hw-tag);
  line-height: 1.4;
  color: var(--cream);          /* 14.07 v3: inversion off */
}
/* 15.07: wide-bracket house style — 0.92 top-up + 0.08 gap = the full em
   of air inside the parens, whatever the word gap does. */
.hw-ticker > span:first-child { margin-right: 0.92em; }
.hw-ticker > span:last-child { margin-left: 0.92em; }
.tick-slot {
  display: block; overflow: hidden; height: 1.4em;
  transition: width var(--dur-base) var(--ease-house);
}
.tick-col { display: flex; flex-direction: column; transition: transform var(--dur-base) var(--ease-house); }
.tick-col span { display: block; width: max-content; line-height: 1.4; white-space: nowrap; }   /* 14.07 clip fix: intrinsic width — column-flex children were stretching to a too-narrow slot, so re-measuring returned the clipped width */
.hw-media { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* (14.07 focal-point nudge for the portrait Parkhotel crop removed same day —
   the slide now runs the landscape palmer-hero-deck.jpg, native centre) */
/* 14.07 (Chiara: "just palmer studio photo should show — remove the others"):
   every other slide holds the empty gallery ground; their media (Zambala film,
   Odisi, LHC, the Hustl trial) stays wired in the markup — flip a slide back
   on by removing it from this :not() when its imagery is approved.
   (15.07: scoped to .hw-pin — the LHC case-study hero borrows the slide
   grammar outside the deck and must keep its image.) */
/* (13.08: the approved-imagery GATE is RETIRED — Hustl's Necker Island image was
   the last homepage slide to land, so all five show media. The 14.07 gate
   grammar lives in git if a slide ever needs benching again: three :not()
   rules hiding .hw-media, setting the --gallery ground, and dropping ::after.
   One survivor, scoped: Alquemie on the work index has no imagery yet —
   it keeps the empty-ground treatment explicitly below.) */
.hw-pin .hw-slide[data-project="alquemie"] { background: var(--gallery); }
.hw-pin .hw-slide[data-project="alquemie"]::after { display: none; }

/* ============================================================
   CASE STUDY — CONTINUOUS-SCROLL FORMAT (15.07, Chiara,
   stud.house/works/grid ref). First page: /work/lhc/.
   Hero = the homepage deck slide grammar, then meta + intro,
   then a flush full-bleed image stack, then the next-project
   handoff. Video slots drop into the stack when films land.
   ============================================================ */
/* 15.07 v2 (Chiara, stud grid structure + "full width immersive"): the hero
   is TWO viewports on one sticky image. Screen 1 = the deck slide verbatim;
   screen 2 = the intro paragraph scrolls up over the held image in the right
   column, and the meta rungs close the hero as a horizontal mono row. */
.cs-hero {
  position: relative;
  height: 200svh;
  background: var(--gallery);
}
.cs-hero-media {
  position: sticky; top: 0;
  height: 100svh;
  overflow: hidden;
}
.cs-hero-media::after {   /* the deck scrim, verbatim — held for both screens */
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(rgba(0, 0, 0, 0.20), rgba(0, 0, 0, 0.20)),   /* 16.07 (Chiara): darker — 10% → 20%; this layer is the dial */
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%),
              rgba(0, 0, 0, 0.18);
}
/* 25.09 (Chiara, Palmer: "pull the overlay on the actual case study — make sure it's the same image"): the
   comment above says "the deck scrim, verbatim", but the flat layer was dialled 0.10 → 0.20 on 16.07 and the
   deck's stayed at 0.10 — so the same photograph read darker the moment the view transition landed. --deck
   restores the slide's exact stack for a hero that shares its slide's image. Palmer only for now: LHC and
   Zambala's slides carry a deeper 0.25 wash (+ LHC's centre band), so parity there is a separate rule. */
/* 01.10 (Chiara, Alquemie: "the hero is a little hard to read"): --deep = the hero scrim with a heavier
   flat layer (0.20 → 0.34), for bright, detailed frames where the title sits over cloud or rock. */
.cs-hero-media--deep::after {
  background: linear-gradient(rgba(0, 0, 0, 0.34), rgba(0, 0, 0, 0.34)),
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%),
              rgba(0, 0, 0, 0.18);
}
.cs-hero-media--deck::after {
  background: linear-gradient(rgba(0, 0, 0, 0.10), rgba(0, 0, 0, 0.10)),
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%),
              rgba(0, 0, 0, 0.18);
}
.cs-hero-screen {
  position: absolute; left: 0; right: 0;
  height: 100svh;
  z-index: 1;
}
.cs-hero-screen--title { top: 0; }
/* 01.08 (Chiara: "the text goes smaller… make the title the same size as on
   the home page"): the blanket case-hero step-down RETIRED — single-line case
   titles ride the full deck scale (base .hw-title clamp), so the click-through
   from the deck holds size. Two-LINE titles (LHC, Beyond by Kim) keep the
   92px step via .hw-title--step below — same as their deck treatment. */
.cs-hero .hw-title--step { font-size: clamp(44px, 8vw, 92px); }
/* 19.10 (Chiara: "make sure on the case study page they are matching the size on mobile"): the case hero name takes the
   phone deck's sizes — 11vw (43 at 390) for one-line names, 10.5vw (41) for the two stacked ones, which the hero
   sections carry as data-project. Desktop unchanged (the clamps above). */
@media (max-width: 760px) {
  .cs-hero .hw-title.hw-title { font-size: 11vw; }
  .cs-hero .cs-hero-meta .label, .cs-hero .cs-hero-meta dt, .cs-hero .cs-hero-meta dd { font-size: calc(var(--type-label) - 2px); }
  .cs-hero .cs-intro-lead { font-size: var(--type-serif-md); line-height: 1.35; color: color-mix(in srgb, var(--cream) 72%, transparent); }
  .cs-stack-item--frame.cs-stack-item--frame video:not(.cs-swap-b) { height: auto; aspect-ratio: 16 / 9; }
  .cs-stack-item--swap.cs-stack-item--swap { aspect-ratio: 16 / 9; overflow: hidden; width: auto; margin-inline: var(--gutter); }
  .cs-stack-item--pair-second { display: none; }   /* 20.10 (duplicate-scene pairs — Hustl, Zambala): the second frame lives inside the first as its swap layer on the phone */
  /* 20.10 (Chiara: "( Our Approach ) / The Aura Method™ — can this all follow suit to the case study pages on
     mobile"): the Method hero wears the case-hero phone treatment — title on the stacked-name rung (10.5vw = 41 at
     390; display's 48 floor outsizes every case hero), the eyebrow at the hero-meta's 12px, the lead with the case
     intro's tighter leading and 72% cream. Same register the reader just left on /work/<case>. */
  /* 20.10 (Chiara: "the intro text and the heroes are the same" as the case pages): every service hero joins the
     case-hero phone rung — title at 11vw (the one-line case-name size; the Method's longer title keeps its 10.5
     below), lead with the case intro's leading and 72% cream. Eyebrow and lead size already matched. */
  .svc-hero .svc-title { font-size: 11vw; }
  .svc-hero .svc-lead { line-height: 1.35; color: color-mix(in srgb, var(--cream) 72%, transparent); }
  .method-intro .svc-title { font-size: 10.5vw; }
  .method-intro .svc-eyebrow.svc-eyebrow { font-size: calc(var(--type-label) - 2px); }
  .method-intro .svc-lead { font-size: var(--type-serif-md); line-height: 1.35; color: color-mix(in srgb, var(--cream) 72%, transparent); }
  .cs-stack-item--pair .cs-swap-b--phone { display: block; }   /* 20.10 (Alquemie: "the second photo has no border — both images need to be the same"): EVERY swap seat, framed or not, sits inside the gutters at 16:9 like the stills around it */   /* 20.10 (Chiara: "there needs to be a side margin on the LMDM first image under The Thinking"): the seat sits inside the gutters like every other phone media (358 wide), not full bleed */   /* 20.10 (Chiara, LMDM: "the video overlay doesn't fully cover the image"): the 16:9 rule had sized the overlay film to the width while the still underneath kept its taller native height. The swap seat is one 16:9 box; both layers cover it. */
  .cs-stack-item--swap.cs-stack-item--swap > img, .cs-stack-item--swap.cs-stack-item--swap > video, .case-study .cs-stack-item--swap img.cs-swap-b.cs-swap-b, .case-study .cs-stack-item--swap video.cs-swap-b.cs-swap-b { position: absolute; inset: 0; left: 0; width: 100%; height: 100%; object-fit: cover; }   /* .cs-swap-b doubled: the 30.09 rule insets the swap layer by a gutter each side (a desktop composition); on the phone the seat is edge to edge */   /* 19.10 (Chiara: "needs the 16:9 dimension") — class doubled to outrank the base 56.25vw rule later in the file: the film frame's height came from the viewport (56.25vw) while the element sits inside the gutters — 358 × 219; now 16:9 of its own width like every other phone media (358 × 201) */   /* + (Chiara: "make the opacity a bit stronger on that text"): the frost lifts 58 → 72% on the phone, where the smaller size over the film needs more ink; desktop keeps 58 */   /* 19.10 (Chiara, Necker's "A film built around salt…": "reduce font — relative to title"): 30 → 24px at 390, the serif-md rung, so the lead sits clearly under the 41–43px name (0.56 of it; was 0.7). Desktop unchanged. */   /* 19.10 later (Chiara: "( Industry ) / ( Scope ) lines — same size as the bylines on the gallery"): the phone byline's label − 2 = 12px; desktop stays on the 14px label rung */
  .cs-hero[data-project="lhc"] .hw-title.hw-title,
  .cs-hero[data-project="wagner-bespoke-travel"] .hw-title.hw-title { font-size: 10.5vw; }
}
/* 19.10: a one-screen phone hero with the intro moved OUT onto the dark ground was built on Necker + Palmer (gated
   body.cs-phone-v2) and REVERTED the same hour — Chiara: "I don't like this, not over the image". The two-screen
   hero stays on the phone: the intro screen glides over the sticky film as on desktop; its meta is the stacked
   right-edge column (the ≤900 rule above). */
/* 15.07 v2 (stud.house line reveal): the title lines rise from overflow masks
   on arrival — a seat, not a fade. JS stamps .shown on .cs-hero one frame in. */
.cs-line { display: block; overflow: hidden; padding-bottom: 0.38em; margin-bottom: -0.38em; }   /* 27.10 (Chiara, phone: "the G and the Y on Photography are getting cut off — check the other service pages"): 0.25 → 0.38em, iOS Safari draws Eiko descenders deeper than Chrome; every masked title (service, case, method) takes it */   /* 15.07 (Chiara: "g on consulting is cut off"): 0.08em covers Eiko overshoot but not a descender — the mask needs ~a quarter-em below the baseline */
.cs-line-inner {
  display: block; position: relative;   /* positioned so the index sup travels with line 1 */
  transform: translateY(135%);   /* past the mask's padded window (125%), or a sliver shows pre-reveal */
  transition: transform var(--dur-slow) var(--ease-house) 0.35s;   /* 15.07 v2 (Chiara: "the title reanimates on like the lets talk animation"): the deck's word-roll timing verbatim — same curve, same 0.35s beat after arrival */
}
.cs-line:nth-child(2) .cs-line-inner { transition-delay: 0.45s; }   /* line 2 rides one decisive beat behind line 1 */
.cs-hero.shown .cs-line-inner { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .cs-line-inner { transform: none; transition: none; }
}
/* 15.07 (Chiara: "seamless transition from the homepage to the case study"):
   cross-document View Transitions — the clicked slide's media carries a
   view-transition-name that matches the case-study hero's, so the image
   HOLDS while the pages swap beneath it and the title re-rolls in fresh.
   Chrome/Edge/Safari 18+; others get a normal navigation.
   30.09: "the clicked slide's" is now literally true — the deck images carry
   the name in data-vt and main.js applies it on the click (it was inline and
   permanent, so LHC's snapshot rode every navigation off the deck). */
@view-transition { navigation: auto; }
/* 01.08 (Chiara: "the image flickers… a weird thing"): three artefacts fixed.
   (1) The default old/new CROSS-FADE on the held media dipped brightness
   mid-swap (two near-identical frames at partial opacity) — both snapshots
   now run static at full opacity: the new frame simply takes over the moment
   it has pixels, the old holds beneath until then. A true hold.
   (2) LHC's name sat on the raw img, so its snapshot missed the hero scrim
   (Palmer's wrapper included it) — name moved to the wrapper in the markup.
   (3) Root swap tightened 0.5s → 0.3s: the page beneath reads as a decisive
   cut under the stationary image, not a drifting fade. The deck img's parked
   scale(1.06) eases into the hero frame via the group's geometry tween — the
   one settle gesture — and the title word-roll re-enters 0.35s later. */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.3s; }
::view-transition-old(cs-media-palmer), ::view-transition-new(cs-media-palmer),
::view-transition-old(cs-media-lhc),    ::view-transition-new(cs-media-lhc) { animation: none; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}
/* 01.08: single-screen hero variant (.cs-hero--single / .cs-hero-intro-after)
   TRIED + REVERTED same hour (Chiara: "the hero image was perfect before —
   I just wanted the text to have the frosted effect"). Don't re-propose;
   the ask was only the frosted colour on .cs-intro-lead below. */
.cs-hero-screen--intro {
  top: 100svh;
  display: grid;
  grid-template-columns: 1fr 2fr;   /* stud offset: left third empty, copy rides the right */
  align-items: center;
  padding: 0 var(--gutter);
}
.cs-intro-lead {
  grid-column: 2;
  justify-self: end;       /* 15.07 (Chiara): ragged-left against the right gutter */
  text-align: right;
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-lg);
  line-height: 1.35;
  max-width: 34ch;
  text-wrap: pretty;   /* 25.09 (Chiara, Palmer lead: "it shouldn't be on its own line"): at 1440 and wider the paragraph ended on a one-word line — "it." alone. pretty lets the browser rebalance the last lines so a single word never orphans. A &nbsp; would not survive: the word-light splits on \s+, which includes it. Chrome 117+ / Safari 17.5+; others keep the natural rag. */
  color: var(--frost);   /* 01.08 (Chiara: "same effect as the title in terms of the colour"): the frosted title rule on the hero lead — was solid cream. The word-light scrub still breathes opacity over it; lit words land at the frosted 58%. */
}
.cs-hero-meta {   /* the stud info rung: labels + values in the mono register, one row */
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: var(--space-lg);
  margin: 0;
  display: flex;
  gap: var(--space-2xl);
  color: var(--cream);
}
.cs-hero-meta .label { font-size: var(--type-label); }   /* 15.07 (Chiara: "can it be 14px"): the rung sits on the 14px label token, below the 15px corners */
.cs-hero-meta dt { opacity: 0.65; margin-bottom: var(--space-xxs); }
/* 19.10: the ( ) around the hero meta labels moved OUT of the markup (41 <dt>s across the case pages) into these
   pseudo-elements, so the phone can drop them (its two-voice meta) while desktop keeps the house brackets verbatim. */
.cs-hero-meta dt::before { content: '(\2003'; }
.cs-hero-meta dt::after { content: '\2003)'; }
.cs-hero-meta dd { margin: 0; }
.cs-hero-scroll { margin-left: auto; align-self: end; opacity: 0.65; }
/* 01.10 (Necker Island): CREDITS at the foot of a film case — the hero meta's dt/dd grammar, in flow, under a ( Credits ) label. Rows wrap on narrow screens. */
.cs-credits { margin: var(--space-md) 0 0; display: flex; flex-wrap: wrap; justify-content: center; text-align: center; gap: var(--space-md) var(--space-2xl); }   /* 19.10 (Chiara: "centre this — desktop too"): the credits row centred under the strip; each pair centred on itself */
.cs-credits > div { display: flex; flex-direction: column; align-items: center; }
.cs-credits dt { opacity: 0.65; margin-bottom: var(--space-xxs); }
.cs-credits dd { margin: 0; }
/* 15.07 v3 (Chiara: "articulate the strategic thinking… the case study copy
   needs to be strong"): stud works__text grammar — narrative rows between the
   stack images. Label rung left, paragraph rides the right column, same
   offset as the hero intro. */
.cs-stack-text {   /* 15.07 v9 (Chiara: "back to left/right alignment"): rung left, paragraph right — the offset grid. (Layout arc: offset → centred → offset → centred → OFFSET; v7 full-section padding holds.) */
  display: grid;
  grid-template-columns: 1fr 2fr;
  align-items: start;
  padding: var(--space-section) var(--gutter);   /* v7 (Chiara: "more padding top and bottom") */
}
.cs-stack-text .label { opacity: 0.65; }
.cs-stack-text p:not(.label) + p:not(.label) { margin-top: var(--space-md); }   /* 19.10 (Chiara: "on desktop split the Thinking para too"): a second paragraph in a story row gets a full md above it on desktop (the phone rule, more specific, keeps its own sum to the same 24px) */
.case-study .cs-stack-text p:not(.label) { max-width: 68ch; }   /* 25.09 (Chiara, Palmer Thinking again: "should this be a slightly wider width box? Maybe a little bit extra"): 62 → 68ch, the same ~10% step as last time. Ladder: 52 → 62 → 68. · 03.08 (Chiara, Palmer Thinking: "make the width of this box wider"): case-study narrative rows carry longer copy now — 62ch here, service pages hold the 52ch base */
.cs-stack-text p:not(.label) {
  grid-column: 2;
  justify-self: end;
  text-align: right;
  margin: 0;
  font-family: var(--font-body);
  font-weight: 300;
  font-size: var(--type-body);
  line-height: 1.6;
  max-width: 52ch;
}
/* 01.08: left-align trial on service-page stack-text TRIED + REVERTED same day
   (Chiara: "right align") — the right-justified offset grammar is the call,
   services and case studies alike. Don't re-propose. */
@media (max-width: 900px) {
  .cs-stack-text { grid-template-columns: 1fr; gap: var(--space-sm); }
  .cs-stack-text p:not(.label) { grid-column: 1; }
}
.cs-stack { display: block; }
/* 03.08 (Chiara): breathing room between consecutive mockups in the story stack —
   the gutter value, so the vertical rhythm matches the side gutters. Text blocks
   keep their own spacing; only media-to-media pairs get the gap. */
.cs-stack :is(.cs-stack-item, .cs-placeholder, .cs-split) + :is(.cs-stack-item, .cs-placeholder, .cs-split) { margin-top: var(--gutter); }
.cs-stack-item { margin: 0; }
.cs-stack-item img, .cs-stack-item video { display: block; width: 100vw; height: auto; }
/* 13.08 (Chiara, Odisi seaplane pair: "loaded in the space where the first image
   is and it swaps — instead of them being stacked"): one seat, two images. Image
   A defines the frame's height; B sits over it (cover-cropped to A's aspect) and
   crossfades in when the frame's centre passes the viewport centre (updateSwaps,
   main.js). Scroll back up and it swaps back. */
.cs-stack-item--swap { position: relative; }
.svc-bleed { margin: var(--space-section) 0; overflow: hidden; }   /* 25.08: the /web full-bleed band before the FAQ — section rhythm above and below; the frame clips the parallax growth */
.svc-bleed img { aspect-ratio: 2400 / 1351; object-fit: cover; transform-origin: 50% 50%; will-change: transform; }   /* 25.08 (Chiara: "grow with a parallax"): main.js updateBleed scales 1 → 1.12 and drifts the image as the band crosses the viewport — raw scroll, the hero-pan family */
@media (prefers-reduced-motion: reduce) { .svc-bleed img { transform: none !important; } }
.cs-stack-item--swap .cs-swap-b { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity var(--dur-slow) var(--ease-house); }
.cs-stack-item--swap.swapped .cs-swap-b { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .cs-stack-item--swap .cs-swap-b { transition: none; } }
/* 20.07: .cs-stack-item--stick (150svh sticky hold on the website frames)
   TRIED + RETIRED same day (Chiara) — the mockups scroll plainly; on the
   Lenis glide the stack reads guided without holds. */
/* 20.07 (Palmer beat 4, Chiara: "fit into the frame so can crop it"): a
   full-bleed video held to the house frame band — cover crops the excess */
.cs-stack-item--frame { position: relative; display: block; font-size: 0; line-height: 0; }   /* 20.07 (Chiara: "gap between the video and the brand guideline layout") — kills the inline-descender gap under the video */
.cs-stack-item--frame video { display: block; width: 100vw; height: min(56.25vw, 810px); object-fit: cover; }
.cs-stack-item--frame::after {
  content: ""; position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.05);   /* 20.07 (Chiara): 5% black scrim — settles the footage under the logo */
  pointer-events: none;
}
/* 20.07 (Chiara: "add this logo over the hero video at the bottom"): the
   client logotype floats centred on the footage — the Group-84 grammar.
   v2 ("can just see the o"): the export's internal fills sank into the
   footage — now the house logo-mask trick: the SVG is a MASK, cream pours
   through, every letterform solid whatever the film behind is doing. */
.cs-stack-item .cs-stack-logo {   /* doubled class beats the base `.cs-stack-item img { width:100vw }` rule — as an <img> the logo was rendering FULL-BLEED, which is why every width tweak "did nothing" */
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(24.7%, 304px);   /* 01.08: RESTORED to the 20.07 v11 approved value. Chiara's "70% smaller" was measured against the full-bleed specificity bug (see the gutter rule note) — 304px ≈ 78% smaller than what she was seeing. The interim ×0.30 (91px) value was sized against the bug, not the design. */
  /* per-client widths below MATCH ON HEIGHT: each logotype has its own aspect,
     so equal width would render them different visual sizes (Chiara 20.07:
     "reduce odisi logo to same size as palmer") */
  z-index: 1;   /* above the 5% scrim */
  opacity: 0;   /* 20.07 v11 (Chiara: "fade on entrance"): develops in when the frame enters view — JS adds .shown */
  transition: opacity var(--dur-cinema) var(--ease-house);
}
.cs-stack-item .cs-stack-logo.shown { opacity: 1; }
.cs-stack-item .cs-stack-logo--odisi { width: min(17.5%, 215px); }   /* Odisi mark is 906×235 (Palmer 2022×371) — 215px wide lands the SAME cap height as Palmer's 304px */
/* 03.09 (Chiara, on the wing-view / ocean frame: "make the Odisi logo frosted and a bit greeny slightly"):
   the mark is a mask fill, so "frosted" is alpha on the fill — the site's frosted-on-imagery rule (58%),
   here at 62% so it still carries over the bright window light — not backdrop-filter, which does not
   survive a mask. "Greeny" is a 22% pull of --sage-2 into the cream: enough to read as tinted against a
   plain cream mark, not enough to become a colour. Only this frame's mark; the deck's stays cream. */
.cs-stack-item .cs-stack-logo--odisi {
  background-color: color-mix(in srgb, color-mix(in srgb, var(--sage-2) 22%, var(--cream)) 62%, transparent);
}
/* 20.07 (Chiara): the wordmark on the green panel matches the one over the
   film. VIEWPORT units, not %: the panel is 50vw, so a percentage would
   render half the size — min(17.5vw, 215px) is the same on-screen size. */
.cs-split-logotype.cs-split-logotype--odisi { width: min(17.5vw, 215px); }
/* 20.07 (Beyond by Kim): deep green ground + mark widths matched on cap height */
.cs-split-left.cs-split--beyond-green,
.cs-split-right.cs-split--beyond-green {
  background: #46533F url("/assets/work/beyond-by-kim/pattern-ground.jpg") center / cover no-repeat;   /* 20.07 (Chiara): the monogram-pattern ground replaces flat green; colour stays as the fallback */
}
.cs-split-logotype.cs-split-logotype--beyond { width: min(20vw, 250px); }
.cs-split-logotype.cs-split-logotype--zambala { width: min(25.5vw, 330px); }   /* 01.08 v5 (Chiara: "the curtain logo size — use on the first one"): ONE size for both film marks. Arc: 410 → 369 → 258 → 271 → 330 settled. */
.cs-stack-item .cs-stack-logo--beyond { width: min(7%, 92px); }
/* 20.07 (Chiara: "more full width images"): a full-bleed colour band that
   holds a centred mark — the split's green panel, promoted to its own beat */
.cs-stack-item--band { position: relative; width: 100vw; height: min(56.25vw, 810px); }
.cs-stack-item .cs-stack-logo--beyond-wide { width: min(24%, 300px); }
.cs-split-mark.cs-split-mark--beyond { width: min(7vw, 96px); }   /* Frame 8 monogram on the green panel — 20.07 (Chiara): matched to the balcony-film mark; ladder was 190 → 171 → 96px */
.cs-split-mark.cs-split-mark--beyond-film { width: min(7vw, 96px); }   /* 20.07 (Chiara: "ee over the curtain video is too big"): the monogram on the balcony film sits much smaller — the panel is a tall vertical crop, so it needs its own size */
/* 20.07 (Chiara): the mark over the greenery film — stone, not white, at 80% */
.brand-mark-el--stone { background-color: #D6D1C4; opacity: 0.8; }
@media (prefers-reduced-motion: reduce) {
  .cs-stack-item .cs-stack-logo { opacity: 1; transition: none; }
}
/* 15.07 (Chiara): brand-guidelines flick-through — the pin holds one viewport
   while the scroll turns the pages. v2 same hour ("much smoother"): the hard
   swap becomes the homepage deck's clip-path sweep — the incoming page wipes
   UP over the last (a turn, not a fade). Pages ≤ active stack shown, so
   scrolling back sweeps them off in reverse. */
.cs-deck { height: calc(100svh + var(--deck-pages, 3) * 126.667svh); position: relative; }   /* 30.09: was a flat 480svh, which is this formula at three pages (LHC). LMDM's deck turns FOUR (the pressed logo + three guideline spreads), so the runway is now one viewport of pin + 126.667svh per page — the same dwell per turn at any count. Set --deck-pages on the section. */   /* 20.07 v3 (Chiara: "one scroll should trigger the change — first change very delayed"): ~127svh per page, one committed scroll per turn. The 850svh "longer hold" (v2, same day) over-corrected — right for slow scrolling, stuck at real speed */
.cs-deck-pin { position: sticky; top: 0; height: 100svh; overflow: hidden; }
.cs-deck-page {
  position: absolute; inset: 0; margin: 0;
  clip-path: inset(100% 0 0 0);   /* 22.10 (Chiara, after choosing the up-wipe on the homepage deck: "same for the case study pages"): the case deck’s flick now wipes from the BOTTOM edge like the homepage deck (.hw--up); same cinema duration and curve */   /* 15.07 v3 (Chiara: "left-right not up-down"): the homepage deck's LEFT swipe — enters at the right edge, sweeps left */
  transition: clip-path var(--dur-cinema) cubic-bezier(0.6, 0.04, 0.16, 1);   /* 20.07 v3: 2.6s → 1.6s — the sweep must land inside its shortened dwell or fast scrolling stacks mid-flight turns (the "glitchy" feel); the soft gather/settle curve stays */
  will-change: clip-path;
}
.cs-deck-page.shown { clip-path: inset(0 0 0 0); }
/* 20.10 (LHC on the phone): the brand-guidelines flick — a 100svh pin over ~4000px of scroll — becomes the same
   full-width 16:9 reel as the split: the pages stacked, gutter gaps, no pin. Desktop keeps the flick. */
@media (max-width: 760px) {
  .cs-deck { height: auto; }
  .cs-deck .cs-deck-pin, .cs-deck.cs-deck--framed .cs-deck-pin { position: static; height: auto; top: auto; overflow: visible; display: grid; gap: var(--gutter); padding: 0 var(--gutter); }   /* .cs-deck.cs-deck--framed outranks the variant's fixed pin height, which sits later in the file */
  .cs-deck .cs-deck-page, .cs-deck.cs-deck--framed .cs-deck-page { position: relative; inset: auto; clip-path: none; aspect-ratio: 16 / 9; margin: 0; overflow: hidden; transition: none; }
  .cs-deck-page img { width: 100%; height: 100%; object-fit: cover; }
  /* 20.10 (LMDM's two website frames — "these need to stack"): a --pair deck is ONE seat on the phone: page 1 sits in
     flow, page 2 lies over it and fades in when the seat passes mid-screen (the generic .cs-stack-item--swap driver
     toggles .swapped on the pin). Same frame, the screen changes — the sofa-photo mechanic. */
}
.cs-deck-page img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 20.10 (Chiara: "on desktop the two Zambala laptop mockups need to be stacked" → every duplicate pair): a --pair deck is
   ONE seat at every width — page 1 in flow, page 2 over it, fading in when the seat passes mid-screen (the generic
   .cs-stack-item--swap driver). The desktop two-page flick on these decks is retired. LMDM's laptops. Moved out of
   the phone query. */
.cs-deck.cs-deck--pair { height: auto; }
.cs-deck.cs-deck--pair .cs-deck-pin { display: block; position: relative; height: auto; top: auto; overflow: visible; padding: 0; }
.cs-deck.cs-deck--pair .cs-deck-page { position: relative; inset: auto; clip-path: none; margin: 0; overflow: hidden; transition: none; aspect-ratio: 16 / 9; }
.cs-deck.cs-deck--pair .cs-deck-page:nth-child(2) { position: absolute; top: 0; left: 0; right: 0; bottom: auto; width: auto; height: auto; aspect-ratio: 16 / 9; opacity: 0; transition: opacity var(--dur-slow) var(--ease-house); }
.cs-deck.cs-deck--pair .cs-deck-pin.swapped .cs-deck-page:nth-child(2) { opacity: 1; }
/* 30.09 (Chiara, LMDM: "change the full screen of the images swiping — needs to have the border on it"):
   --framed holds the deck inside the page gutters like every other stack frame — the pages keep the
   side gutters and the image sits at its own 16:9, centred in the pin, the page ground showing above
   and below. The sweep, the dwell and the JS are untouched. LHC's deck stays full-bleed (no modifier). */
/* 30.09 later (Chiara: "spacing after design is off"): the first cut centred the image in a full 100svh pin,
   so the pin carried a band of empty ground above and below the frame and the gaps to its neighbours
   grew with the screen. The PIN is now exactly the frame's height (gutter-width at 16:9, capped at the
   viewport) and sticks centred; above and below it the normal stack spacing applies. */
.cs-deck--framed { --deck-h: min(100svh, calc((100vw - var(--gutter) * 2) * 0.5625)); }
.cs-deck--framed .cs-deck-pin { height: var(--deck-h); top: calc((100svh - var(--deck-h)) / 2); }
.cs-deck--framed .cs-deck-page { inset: 0 var(--gutter); }
.cs-deck--framed + .cs-stack-item { margin-top: var(--gutter); }   /* the deck is not in the stack's sibling-gap rule; its follower gets the same gutter gap every frame pair has */
@media (prefers-reduced-motion: reduce) {
  .cs-deck-page { transition: none; }
}
/* 15.07 (Chiara): UX Strategy & Website — three page wireframes as pinned
   blueprints, top-aligned, cropped at a shared height (the full frames run
   ~1:3.2). Hairline keeps the cream frames off the paper ground.
   15.07 v2: strip PARKED behind .cs-placeholder notes while the mockups are
   art-directed — rules kept for the return. */
.cs-wireframes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gutter);
  align-items: start;
  padding: 0 var(--gutter) var(--space-section);
}
.cs-wireframes figure {
  margin: 0;
  max-height: 80vh;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
}
.cs-wireframes img { display: block; width: 100%; height: auto; }
@media (max-width: 900px) {
  .cs-wireframes { grid-template-columns: 1fr; }
}
/* 15.07 (bones template): hero-media seat note — the gallery ground holds
   the screen, the mono note marks the missing image. Sits above the scrim,
   clear of the centred title. */
.cs-media-note {
  position: absolute; top: 24%; left: 0; right: 0;
  text-align: center;
  color: var(--cream); opacity: 0.4;
  z-index: 1;
}
/* ============================================================
   SERVICE PAGE — /branding/ (15.07, stud.house/studio/branding
   structure: Overview → Process → FAQs → Other services). Bones.
   ============================================================ */
.svc-hero {
  min-height: 72svh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: calc(var(--space-section) * 0.75) var(--gutter) var(--space-xl);
}
.svc-eyebrow { opacity: 0.65; margin-bottom: var(--space-md); }   /* 24.08 (Chiara, /web hero: "reduce spacing here"): lg → md, one rung down the ladder — eyebrow sits closer over its title on every service hero */
.svc-title {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-display);
  line-height: 1;
  margin: 0 0 var(--space-lg);
}
.svc-lead {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.4;
  max-width: 40ch;
  margin: 0;
  align-self: flex-end;   /* 16.07 (Chiara): the lead sits right-aligned against the right gutter, case-study intro grammar */
  text-align: right;
}
/* 16.07 (Chiara): photo hero — full-bleed image, deck scrim, cream text */
.svc-hero--media { position: relative; min-height: 100svh; color: var(--cream); overflow: hidden; }   /* 16.07 (Chiara): full viewport height */
.svc-hero-bg { position: absolute; inset: 0; background: var(--gallery); }   /* 16.07 (Chiara): dark box canvas — image removed for Alex */
.svc-hero-bg img, .svc-hero-bg video { width: 100%; height: 100%; object-fit: cover; display: block; }   /* 12.10: video joins — the branding hero runs the Hustl film */
.svc-hero-bg::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(rgba(0, 0, 0, 0.20), rgba(0, 0, 0, 0.20)),   /* 16.07 (Chiara): the darker-hero layer, matching the case-study heroes; this layer is the dial */
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%),
              rgba(0, 0, 0, 0.18);
}
.svc-hero--media > *:not(.svc-hero-bg) { position: relative; z-index: 1; }
.svc-hero--media :is(.svc-title, .svc-eyebrow, .svc-lead) { color: var(--frost); opacity: 1; }   /* 27.10 (Chiara: "all of these titles have the frosted effect… on all hero images"): every media hero's eyebrow, title and lead take --frost */
.svc-hero--media .hw-tag { position: absolute; }   /* the case-hero byline seat — bottom-centre */
.svc-hero--media .svc-lead { margin-bottom: var(--space-2xl); transform: translateY(10vh); }   /* the lead steps up so the byline owns the base line · 24.08 (Chiara, /web hero seated: "move the text down by 5%… another 5%"): +10vh via transform (layout untouched) — all media heroes share the seat, one grammar */
.svc-hero .cs-line-inner,
.method-intro .cs-line-inner {   /* self-starting: no .shown stamp on service pages — the seat runs as a load animation. 16.07: the Method hero joins */
  animation: svc-seat var(--dur-slow) var(--ease-house) 0.2s forwards;
}
@keyframes svc-seat { to { transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .svc-hero .cs-line-inner, .method-intro .cs-line-inner { animation: none; }
}
.svc-process { padding-bottom: var(--space-xl); }
.svc-phases {
  list-style: none; margin: 0; padding: 0 var(--gutter);
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--space-xl) var(--space-2xl);   /* 24.08 (Chiara: "increase the spacing between 01 02 03 — the text boxes are too wide now"): column gap gutter → 2xl (96) — the wider gutters narrow each column back to a comfortable measure now the 38ch cap is gone */
}
.svc-phases li .label { display: block; opacity: 0.65; margin-bottom: var(--space-md); }
.svc-phases--culture { row-gap: var(--space-2xl); }   /* 16.07 (About): six commitments over two rows · 05.08: 2×2 for four · 07.08 (Hannah doc): The Bar cut, THREE convictions — back on the base 3-col grid, one equal row. */
/* 12.10 — STUDIO PAGE, stud.house/studio grammar (Chiara: "I really like this"). Three additions, all on existing tokens:
   .svc-bleed — the full-bleed photograph between chapters. .svc-conv — the Culture rows: index | title + body | image, a
   hairline per row (the .svc-others rule colour). The services list is the homepage router (.disc--split), lifted as-is. */
.svc-bleed { margin: var(--space-2xl) 0 var(--space-xl); }
.svc-bleed img { display: block; width: 100%; height: 76svh; object-fit: cover; object-position: 50% 72%; }
.svc-conv { list-style: none; margin: var(--space-xl) 0 0; padding: 0 var(--gutter); }
.svc-conv-row {
  display: grid; grid-template-columns: 1fr 2.2fr minmax(180px, 320px); gap: var(--space-xl); align-items: start;
  padding: var(--space-lg) 0;
  border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
}
.svc-conv-row:last-child { border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.svc-conv-index { margin: 0; opacity: 0.65; }
.svc-conv-title { margin: 0 0 var(--space-md); font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-lg); line-height: 1.1; }
.svc-conv-body p { margin: 0; font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: 1.6; max-width: 46ch; }
.svc-conv-media { margin: 0; aspect-ratio: 4 / 5; overflow: hidden; }
.svc-conv-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 900px) {
  .svc-conv-row { grid-template-columns: 1fr; gap: var(--space-md); }
  .svc-conv-media { aspect-ratio: 16 / 10; }
  .svc-bleed img { height: 56svh; }
}
.svc-phases li p:not(.label) {
  margin: 0;
  font-family: var(--font-body); font-weight: 300;
  font-size: var(--type-body); line-height: 1.6;
  /* 24.08 (Chiara: "big gap on the right"): the 38ch cap left the third column's
     trailing space against the page edge — the COLUMN is the measure now (the
     3-col grid caps lines at a readable ~50ch anyway) */
}
/* THE PROCESS LEDGER (25.08, /web — stud.house/studio/ui-ux-design ref, Chiara: "takes you
   through the process as you scroll"): pinned sticky stack. Card i pins at base + i × rung;
   card i+1 pins one rung lower, so it covers everything of card i except its rung line —
   the "collapse" is occlusion, a hard cut on scroll position, no JS, no fades. The cream
   ground on each card is what does the covering. Phones: static flow (house grammar). */
.svc-process--ledger { padding-bottom: 0; --rung: var(--ledger-rung); --ledger-top: calc(var(--header-h) + var(--space-lg)); }   /* 21.10: md → lg under the header — the room the pinned ( The Process ) label now rides in, see below */
/* 21.10 (Chiara: "( The Process ) is this floating a bit random — does it need to be in view when the 01 02 03 stack? and closer to the section"): YES — the label RIDES THE STACK. It pins just under the header, in the room above rung 01, for as long as the ledger holds, then leaves with it; at rest it sits one rung closer to the first hairline (md → sm). Service pages only — the studio's block carries a statement paragraph that must not pin. */
body:not(.studio-page) .svc-process--ledger { padding-top: var(--space-section); }   /* the statement block's 120 of top air moves up onto the SECTION, so the pinned box below is just the label */
body:not(.studio-page) .svc-process--ledger > .cs-stack-text { padding-top: 0; padding-bottom: var(--space-sm); }
@media (min-width: 901px) { body:not(.studio-page) .svc-process--ledger > .cs-stack-text { position: sticky; top: calc(var(--header-h) + var(--space-xs)); z-index: 3; } }   /* 21.10 (the phone UX review): the pin rides the STACK — ≤900 the rungs are a plain list, and a label pinning alone over it read as a stray (S3). Desktop-only. */   /* the block's 120 of bottom air was the rest-state gap to rung 01 (the ≤900 rule never reached desktop) — sm now, "closer to the section" */   /* 25.08 (Chiara: "no lines overlay the menu bar"): the stack seats under the fixed header + one rung of air. No bottom padding: the next section is the ledger's closing card (below) */
.svc-ledger { list-style: none; margin: 0; padding: 0 var(--gutter); }
.svc-step {
  position: sticky; top: calc(var(--ledger-top) + var(--i) * var(--rung));
  min-height: calc(var(--ledger-last) + (var(--ledger-n) - 1 - var(--i)) * var(--ledger-rung));   /* 25.08 (Chiara: "after Testing & Launch all of that section should scroll — at the moment 04 overlays"): every card's PINNED BOTTOM on one line so the stack releases as ONE — each card is one rung taller than the one below… */
  background: var(--cream);
  border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);   /* the FAQ hairline — one rule family */
}
.svc-step + .svc-step { margin-top: calc(-1 * (var(--ledger-n) - var(--i)) * var(--ledger-rung)); }   /* …and each card is pulled up by exactly that surplus, so every stage still ARRIVES after the same scroll (equal open room — the 'Build has a smaller bottom margin' complaint). Heights step, arrivals don't. Arc: 60svh → 48svh → stepped → equal+peel → this. */
/* 25.08 (Chiara: "number 4 should stack too"): the last stage can only collapse if something lands under it — so the
   NEXT section (Websites We've Built, the process's outcome) is the closing card: it rides up over 04's body on a
   negative top margin sized to 04's height, lands exactly one rung below rung 04 (its own hairline reads as the fifth
   line), and at that instant the ledger's containing block ends and the whole stack scrolls off together. */
/* 25.08 THE GROUND SHIFT v6 (Chiara: "like crowncreative.com — how the text changed, more smooth"):
   STATE-based, not scroll-scrubbed. Crown's body carries `transition: background-color var(--dur-slow) var(--ease-house),
   color var(--dur-quick) var(--ease-house)` and the page swaps a colour state at thresholds — the ground eases on its own
   clock whatever the scroll speed, the type crosses in 0.3s so it never lingers mid-tone. Same
   here: main.js updateLedgerGround stamps data-ground = paper | taupe | dark on the stage at
   thresholds (and back to paper across the hold); this rule does the rest. Colours = the homepage
   arc (--ground-2 taupe, --ground-3 chocolate, cream type on both). The scroll-scrubbed color-mix
   version (v1–v5, same day) was stepped and washed the type mid-crossover. */
/* 25.08 PAGE-LEVEL GROUND (photography, Chiara: "the background needs to be dark" for the Library):
   Crown's model proper — the BODY carries the colour state, so the dark can run from the ledger
   through the Library and lift as the FAQ arrives with no section edge anywhere. A stage marked
   .page-ground paints body's variables (the cards still occlude); main.js stamps body instead of
   the stage on those pages. Sections with their own grounds (hero image, footer) are unaffected. */
body { --pg-bg: var(--cream); --pg-fg: var(--ink); }
body[data-ground="taupe"] { --pg-bg: var(--ground-2); --pg-fg: var(--cream); }
body[data-ground="dark"]  { --pg-bg: var(--ground-3); --pg-fg: var(--cream); }
body.has-page-ground { background-color: var(--pg-bg); color: var(--pg-fg); transition: background-color var(--dur-ground) var(--ease-house), color var(--dur-base) var(--ease-house); }   /* 12.10: same slower rungs as the stage */
body.has-page-ground:not([data-ground]) { transition-duration: 2.6s, 0.8s; }   /* the way out is slower than the way in, as on the ledger */
body.has-page-ground .above-footer { background: var(--pg-bg); color: var(--pg-fg); transition: background-color var(--dur-ground) var(--ease-house), color var(--dur-base) var(--ease-house); }   /* the page wrapper paints --paper over the body — on page-ground pages it paints the page variable instead */
body.has-page-ground:not([data-ground]) .above-footer { transition-duration: 2.6s, 0.8s; }
.svc-ledger-stage.page-ground, .svc-ledger-stage.page-ground[data-ground] { --lg-bg: var(--pg-bg); --lg-fg: var(--pg-fg); }
.svc-ledger-stage { --lg-bg: var(--cream); --lg-fg: var(--ink); }
.svc-ledger-stage[data-ground="taupe"] { --lg-bg: var(--ground-2); --lg-fg: var(--cream); }
.svc-ledger-stage[data-ground="dark"]  { --lg-bg: var(--ground-3); --lg-fg: var(--cream); }
/* 25.08: a taupe state for the portfolio once it filled the view was tried and cut within the hour — a section-level ground has a bottom edge, and the FAQ below must sit on cream (Chiara: "there should never be a harsh line here" / "it should be cream at this point"). If the portfolio ever needs its own ground it has to be PAGE-level (body), Crown-style, so the colour has no edge. */
.svc-ledger-stage, .svc-process--ledger, .svc-step, .svc-ledger-close {   /* 25.10 (Chiara, /studio: "a weird line under How We Work before Meet the Team — make sure the section backgrounds are all merged, no blocks changing at different rates"): the STAGE wrapper joins — its own ground stayed page-cream, so its tail and gaps showed as a cream band against the darkening cards and the dark Team below. One surface, one transition. */   /* the SECTION carries the ground too — else the gutters and the strip under the header stay cream around dark cards */
  background: var(--lg-bg); color: var(--lg-fg);
  transition: background-color var(--dur-ground) var(--ease-house), color var(--dur-base) var(--ease-house);   /* 12.10: 0.9s / 0.3s (Crown's) → --dur-ground 1.8s / --dur-base 0.5s, per Chiara ("slow it all down") */
  border-top-color: color-mix(in srgb, currentColor 25%, transparent);
}
.svc-ledger-stage .svc-proof-head { border-top-color: color-mix(in srgb, currentColor 25%, transparent); }
/* 25.08 (Chiara: "does the end of Launch change too quickly?"): the way OUT is slower than the way in — a transition reads its duration from the destination state, so the paper state carries a longer ease: the return from chocolate drifts over 2s, the type over 0.6s. Descents keep Crown's 1s / 0.3s. */
.svc-ledger-stage:not([data-ground]), .svc-ledger-stage:not([data-ground]) :is(.svc-process--ledger, .svc-step, .svc-ledger-close) { transition-duration: 2.6s, 0.8s; }
.svc-ledger-stage.close-rider { background: var(--lg-bg); color: var(--lg-fg); }   /* 25.10: the stage is ALSO tagged .close-rider (the end-of-page dark run, main.js) and that rule's paper↔dark mix was painting it on a different clock from its cards — the stage follows the ledger ground like its children, one surface */   /* 12.10: 2s / 0.6s → 2.6s / 0.8s with the slower descent */
@media (prefers-reduced-motion: reduce) { .svc-ledger-stage, .svc-process--ledger, .svc-step, .svc-ledger-close { transition: none; } }
.svc-ledger-hold { height: calc(var(--ledger-last) + var(--ledger-hold)); }   /* 25.08 (Chiara: "where is the Testing & Launch text?"): the spacer = 04's OWN open room + the hold beat. Sized to the beat alone, the closing card's overlap put it one rung under 04 from the start and covered its copy for good. */
.svc-ledger-stage-tail { height: var(--ledger-hold); }
.svc-ledger-close {
  position: sticky; top: calc(var(--header-h) + var(--space-md) + var(--ledger-n) * var(--ledger-rung)); z-index: 4;   /* 21.10: 1 → 4 (photography): the pinned ( The Process ) label sits at z 3 and showed THROUGH the arriving closing card — the close now paints above it, so the label is covered as the card lands and leaves with the ledger */   /* background = the ground shift rule above (a cream override here was hiding the dark end) */
  margin-top: calc(-1 * var(--ledger-last) - var(--ledger-hold) + var(--ledger-rung));   /* 25.08 (Chiara: "Testing & Launch isn't stacking"): lands one rung under 04 a HOLD before 04 releases, PINS there (the stage wrapper is its containing block; the tail = the hold), and lets go on the same scroll pixel as 04 — the full closed ledger + the work sit together for the beat, then leave as one */
}   /* 25.08 (Chiara: "a harsh line under Launch"): the closing card's own hairline retired — the four rungs keep theirs, the portfolio just lands. · the hold: the last stage is taller, so the complete ledger sits pinned for a beat before the stack leaves together (an ol tail was tried — it left a void AFTER the stack, not a hold) */
/* 25.08 v5 (Chiara: "not sure about the titles"): the stud shape — the serif title lives ON the rung line
   (number left · title · weeks right), so a closed rung still names its phase; the claim titles retired.
   The body is the copy alone, in the title's column. One grid for rung and body so they align. */
.svc-step-rung, .svc-step-body { display: grid; grid-template-columns: 2fr 3fr var(--ledger-when); column-gap: var(--space-lg); align-items: center; }   /* 25.08 (Chiara: "titles aligned, the text more in the centre, still left-aligned"): the title/copy column starts at 40%; the weeks column is a FIXED width so rung and body split identically — with `auto` the empty body cell shifted the copy 24px off the title. A left-packed variant was tried and undone the same hour. */
.svc-step-rung { height: var(--rung); }
.svc-step-rung .label { margin: 0; opacity: 0.65; }
.svc-step-when { text-align: right; }
.svc-step-title { margin: 0; font-family: var(--font-serif); font-weight: 300; font-size: min(var(--type-serif-lg), 3.35vw); line-height: 1.1; }   /* 20.10 (Chiara, /studio 002: "The founder, on your project — can this be just one line always?"): serif-lg's 3.8vw outruns the rung's column in the fluid band. MEASURED at 950px: the column is 42.8% of the viewport and this title (28ch, the longest ledger title site-wide) needs 12.57px per font-size px — exact fit 3.4vw, 3.35 leaves slack. One line at every width; ≤900 keeps its serif-md override; the 44 cap returns from ~1315px; the 900–1000 sliver just over the phone breakpoint runs a narrower column still and steps to serif-md below. */
@media (max-width: 1000px) { .svc-step-title { font-size: var(--type-serif-md); } }   /* 25.08 (Chiara: "slightly bigger?"): md → lg once the rung went to 72px */
.svc-step-body { padding: var(--space-md) 0 var(--space-xl); align-items: start; }   /* 25.08: sm → md under the title */
.svc-step-text { grid-column: 2; }
.svc-step-copy { max-width: 52ch; }   /* 25.08: the measure is on the copy only — the tag row below runs the full column so five items sit on one line */
.svc-step-copy { margin: 0; font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: 1.6; }
.svc-step-gets { display: block; margin: var(--space-md) 0 0; opacity: 0.65; }   /* 25.08: one mono line, dots between — bracket items and hairline rounded tags were both tried and reverted the same hour (Chiara: "go back to just the words with dots") */
@media (max-width: 900px) {
  .footer-legal { white-space: normal; text-align: center; padding: 0 var(--gutter); }   /* 25.08 mobile audit: the nowrap legal line ran 483px wide on a 390px screen, 47px off both edges */
  .svc-ledger-close { position: static; margin-top: 0; top: auto; }   /* 25.10: top: auto — /photography's close (.cs-hscroll, position: relative from its own rule) kept the desktop sticky top (354px) as a RELATIVE offset on phones, pushing The Library 354 down over what follows */
  .svc-ledger-hold, .svc-ledger-stage-tail { height: 0; }
  /* 25.08 review: on phones the rung stacks — label + weeks on one mono line, the title full-width beneath (the three-cell rung crushed "Visual Design & Copy" into four lines at 390px) */
  .svc-step-rung { display: grid; grid-template-columns: 1fr auto; column-gap: var(--space-sm); height: auto; padding: var(--space-lg) 0 var(--space-sm); }   /* 25.08 (Chiara: "lines are quite tight — more breathing space"): md → lg above, xs → sm below */
  .svc-step-rung .label { grid-row: 1; }
  .svc-step-when { grid-column: 2; }
  .svc-step-title { grid-column: 1 / -1; grid-row: 2; margin-top: var(--space-sm); }
  /* stud's compact step: title at the medium serif, the copy indented into the right column at
     the small body size, deliverables off (the wrapping mono lines were the messiest thing on the phone) */
  .svc-step-title { font-size: calc(var(--type-serif-md) + 4px); }   /* 21.10 (Chiara, phone: "the titles a bit bigger"): 24 → 28 at 390 */
  /* 20.10 (Chiara: "the branding + service pages should follow the case study pages — the paragraphs' width isn't
     the same"): the two-column step body is RETIRED on phones — it squeezed the copy to a 235px right column beside
     a near-empty left one, while a case story paragraph runs the full measure from the gutter. One column, label
     above copy, the case pages' body rung. */
  .svc-step-body { display: grid; grid-template-columns: 1fr; row-gap: var(--space-xs); padding: var(--space-md) 0 var(--space-xl); }
  .svc-step-text { grid-column: 1; max-width: none; }
  .svc-step-copy { font-size: var(--type-body); line-height: 1.6; max-width: none; }
  .svc-step-gets { display: none; }
  .svc-process--ledger .cs-stack-text { padding-bottom: var(--space-sm); }   /* 21.10: md → sm, "closer to the section" */
  /* 25.10 THE STUD STACK — /studio first (Chiara, stud.house/studio on mobile: "I quite like that it's stacked… can you do the layout
     exactly like how Stud does it… the 01, 02, the line, everything… and the spacing" → "let's do it on the studio page first, let me
     check I'm happy, and then you can roll it out"). Stud's own mobile values, read from their CSS: every card sticky, each one a rung
     lower (theirs 10svh, ours the 88 ledger rung), every card's FOOT on one line at 90svh so the stack releases as one; a hairline
     on top; the number mono at 50% in the top-right corner; the serif title (their 30px = our serif-lg on phones) top-left; the
     copy 15svh below it at 70% width and the small body size. Side padding: their 20px → our phone measure (24). Their copy runs
     at 50% opacity — ours stays solid: the studio ink (#887869) at 50% on cream would read about 1.8:1. Other pages keep the flat
     list until this is approved. */
  /* ROLLED OUT 25.10 (Chiara: "love the placement of the How We Work section now and that formatting — roll that out across the other
     pages where it uses it"): every ledger on the phone — Branding, Web, Photography, Film. Retired here: the flat list
     (.svc-step { position: static; min-height: 0 } · .svc-step + .svc-step { margin-top: 0 } · .svc-process--ledger { padding-bottom: var(--space-section) }).
     The closing card and the hold stay off on phones (below) — the stack releases as one when the last card lands, as on /studio. */
  body .svc-ledger { padding: 0; }   /* v3 (Chiara: "a funny glitch with the background under How We Work — margin on the left and right of the white background"): the cards are FULL WIDTH again so no edge between card and section can ever show (an inset card read as a white panel with strips either side whenever the two grounds were a beat apart); the 32 margin moves INSIDE the card, and the hairline is drawn margin to margin (below) */
  body .svc-ledger-stage.svc-ledger-stage { --ledger-last: 45svh; }   /* v2 ("the spacing looks a little bit off — before the Strategy there's a big gap"): Stud's 90svh foot gave each card a different empty tail (350 / 262 / 174 at 390); the desktop maths (below) gives every card the SAME open room — this is the last card's, and the room every card keeps under its copy */
  body .svc-step { position: sticky; top: calc(var(--ledger-top) + var(--i) * var(--rung)); min-height: calc(var(--ledger-last) + (var(--ledger-n) - 1 - var(--i)) * var(--ledger-rung)); padding: var(--space-sm) var(--inset-phone) 0; box-sizing: border-box; border-top: 0; background-image: linear-gradient(color-mix(in srgb, currentColor 25%, transparent), color-mix(in srgb, currentColor 25%, transparent)); background-size: calc(100% - 2 * var(--inset-phone)) 1px; background-position: center top; background-repeat: no-repeat; }   /* the hairline as a 1px image, margin to margin, on a full-width card */
  body .svc-step + .svc-step { margin-top: calc(-1 * (var(--ledger-n) - var(--i)) * var(--ledger-rung)); }   /* v2: the desktop occlusion maths at phone width — pinned feet on one line, equal open room, equal arrival */
  body .svc-step-rung { display: block; position: relative; height: auto; padding: 0; }
  body .svc-step-rung .label:not(.svc-step-when) { position: absolute; top: 0; right: 0; opacity: 0.5; line-height: var(--lh-intro); }
  body .svc-step-rung .label .lb, body .svc-step-rung .label .ph { display: none; }   /* the brackets — Stud's corner reads the bare number (service pages: the phase name too — the title already names the phase) */
  body .svc-step-rung .svc-step-when { display: block; position: absolute; left: 0; top: calc(100% + 15svh); translate: 0 calc(-100% - var(--space-sm)); margin: 0; opacity: 0.5; text-align: left; }   /* v2 (Chiara: "where should Weeks 1–2 go — above the paragraph?"): yes — the weeks sit one sm (16) above the copy, in Stud's open room between title and copy, brackets off like the corner number. The closed tabs read the title alone, as on /studio (v1 put the weeks under the title, inside every tab) */
  body .svc-step-title { font-size: var(--type-serif-md); line-height: 1.1; margin: 0; padding-right: var(--space-lg); white-space: nowrap; }   /* v2 (Chiara: "it shouldn't go onto two lines — just one line"): the longest title sets the size — "The founder, on your project" is 377 wide at 30, 302 at serif-md (24); the room beside the number is 304 at 390. One line, nowrap. (At 360 it would still crowd the number — a shorter title fixes that.) */
  body .svc-step-body { display: block; padding: 15svh 0 0; }
  body .svc-process--ledger { padding-bottom: 0; }
  body .svc-ledger-stage .svc-process--ledger.svc-process--ledger { padding-top: var(--space-section-phone); --ledger-top: calc(var(--header-h) + var(--space-lg) + var(--space-xs)); }   /* v3 (Chiara: "needs more space at the top"): the phone section rhythm (96, was the 80 floor of --space-section) */
  body:not(.studio-page) .svc-process--ledger > .cs-stack-text { position: sticky; top: calc(var(--header-h) + var(--space-xs)); z-index: 3; background-color: inherit; }   /* service pages: the label pins on phones too (it was desktop-only while the phone list was flat) */
  body .svc-process--ledger > .cs-stack-text { padding-left: var(--inset-phone); padding-right: var(--inset-phone); }   /* v3 ("too far left… align with the team — the bracket is not aligned"): label, hairlines and team all start at 32 (were 16 / 24 / 32) */
  body .svc-process--ledger > .cs-stack-text:first-child { margin-bottom: calc(var(--ledger-last) + (var(--ledger-n) - 1) * var(--ledger-rung) + var(--space-xs)); }
  body .svc-ledger { margin-top: calc(-1 * (var(--ledger-last) + (var(--ledger-n) - 1) * var(--ledger-rung) + var(--space-xs))); }   /* v3 ("How We Work doesn't stick — it kind of overlaps as you scroll"): the label's sticky range is its SECTION, which ran ~560px past the moment the cards let go, so it floated alone over the team. A bottom margin the length of the stack's pinned run (cancelled by the list's equal negative margin, so nothing moves in the flow) ends its range on the cards' release line — label and stack leave together */   /* v2: the last card already keeps the open room under its copy — the section's own bottom padding doubled it (200 → 118 + the team's top) */
  body .svc-step-copy { font-size: var(--type-body-sm); width: 70%; }
}

/* 16.07 (Chiara): Build + Elevate ride the grid as ghosts — dimmed, linked;
   the "first half" story told by the layout. The old trailing paragraph
   (.svc-method-link) retired. */
.svc-phase-ghost { opacity: 0.45; transition: opacity var(--dur-quick) var(--ease-house); }
.svc-phase-ghost:hover { opacity: 0.8; }
.svc-phase-ghost .label { display: block; opacity: 0.65; margin-bottom: var(--space-md); }
.svc-phase-ghost p:not(.label) {
  margin: 0;
  font-family: var(--font-body); font-weight: 300;
  font-size: var(--type-body); line-height: 1.6;
  max-width: 38ch;
}
.svc-phase-more { align-self: end; justify-self: start; }
/* 16.07 (Chiara: "more bold… very text-heavy"): the argument rows speak at
   statement scale — same serif rung as the case-study intro, not body 16. */
.cs-stack-text--statement p:not(.label) {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.35;
  max-width: 40ch;
  /* 01.08: left-align trial REVERTED same day (Chiara: "right align") — the
     statement rides the base right-justified grammar like every stack row. */
}
/* What's Inside — hairline rows, term serif left, the change right */
.svc-inside { list-style: none; margin: 0; padding: 0 var(--gutter); }
.svc-inside li {
  display: grid; grid-template-columns: 1fr 2fr; gap: var(--space-lg);
  align-items: baseline;
  padding: var(--space-md) 0;
  border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
}
.svc-inside li:last-child { border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.svc-inside-term { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.15; }
.svc-inside li p { margin: 0; font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: 1.6; max-width: 44ch; }
@media (max-width: 900px) {
  .svc-inside li { grid-template-columns: 1fr; gap: var(--space-xxs); }
}
/* Proof cards (16.07 v3): six cards; head row carries the rung + All Projects */
.svc-proof-head {
  display: flex; justify-content: space-between; align-items: baseline;   /* 21.10 v3 (Chiara: "these two need to go left- and right-aligned, back to how they were — under the process and under the weeks"): the label on the ledger’s left edge, View More on its right edge where the weeks column ends; the centred stack lasted an hour */
  padding: var(--space-2xl) var(--gutter) var(--space-lg);   /* 21.10 (Chiara: "a bit more space between the end of Launch and Websites We’ve Built"): xl → 2xl above the head */   /* 21.10 (Chiara's 12.52 screenshot: "at full vpw the brands we've built should sit in the frame with the titles"): section (120) → xl (64) above, xl (64) → lg (40) below — the head, a full card row AND its names now hold one viewport at desktop widths. A 676 → 560 frame flatten was tried alongside and reverted within minutes ("dont love the image cropping") — the padding alone buys the fold back. */
}
.svc-proof-head .label { opacity: 0.65; }
.svc-work {
  list-style: none; margin: 0; padding: 0 var(--gutter);
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--gutter);
}
.svc-work figure { margin: 0 0 var(--space-sm); position: relative; aspect-ratio: 1200 / 676; overflow: hidden; }   /* 24.08 THE 4/3 CULPRIT (higher specificity than .svc-work-seat — it was forcing every EMPTY seat tall while :has-media seats escaped): aligned to the card-film frame, all service pages */
.svc-work img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 20.10 (Chiara: the hover zoom "looks a bit tacky… maybe the images change and it reveals some of the branding"):
   the 1.04 scale is RETIRED — on hover the card HARD-CUTS to a frame of the brand itself (wordmark, stationery,
   the site on a screen), the case pages' own swap grammar: the cut IS the gesture, no fade, reversible. Touch
   never half-triggers it (hover-gated); the second frame lazy-loads with the card. */
.svc-work figure .svc-work-swap { position: absolute; left: 0; right: 0; top: 0; height: 100%; opacity: 0; transition: opacity 0.35s var(--ease-house); }   /* (0,2,0) — must outrank .svc-work-seat img/video's height: 100% */   /* 21.10 (Chiara, Alquemie + LMDM: "the images jump when you zoom, they don't look smooth when you change"): the hover layer sat at 100% while the cover ran 118% for the drift — every crossfade was also a zoom step, obvious between two near-identical mockups. Same height, same seat, same drift: the two frames now register exactly. */   /* 21.10: the Crown crossfade — the one sanctioned fade on the site, by Chiara's explicit reference ("use the same") */
@media (hover: hover) and (pointer: fine) {
  .svc-work a:focus-visible .svc-work-swap { opacity: 1; }   /* keyboard gets the reveal directly; pointer hover is the JS alternation above (main.js) */
}
/* 21.10 (Chiara, crowncreative.com/design: "the mask over the image moves ever so slightly with a light parallax"):
   the base media runs 12% tall inside the masked frame and drifts on --wpy (main.js, scroll-scrubbed ±6px, the
   ground-run grammar — a raw position write per tick, no transition). Reduced motion never sets the var. */
.svc-work figure > img:first-child, .svc-work figure > video:first-child { height: 100%; position: relative; margin-top: 0; }   /* 25.10 (Chiara: "the proportions of the second images of everything are being cut off — what's the actual frame of that image at its correct proportion? make sure all of the boxes are that size"): the frame IS the images' own proportion (1200:676 = 16:9, the size the mockups and guideline spreads are exported at); the 118% drift headroom was over-scaling both layers ~18% and slicing ~8% off each side of every mockup. Both layers now draw at exactly the frame — still registered, so the crossfade never zooms (21.10); the card drift retires with the headroom. Was: height: 118%; margin-top: -9%; translate: 0 var(--wpy) on both layers */   /* 21.10 (Chiara: "when I hover they jump out of line"): the base sat top-anchored (its top 85% showing) while the swap sat centred (top: -9%) — a 9% framing shift under the crossfade; both layers now centre the same */
/* 21.10: a hover-settle (both layers easing their drift to 0 on hover, to centre the films) lived here for an hour and was removed — "the images are moving on hover, they need to fade nicely like the crown creative example". The hover is a pure dissolve; the two layers share one drift so they register. */
.svc-work li { margin-bottom: var(--space-xl); }   /* 6 cards = 2 rows */
.svc-work-name { margin: 0 0 var(--space-xxs); font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.15; }   /* 16.07 v3 (Chiara): "the name should be bigger" — sm → md */
.svc-work-tag { margin: 0; font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: 1.5; opacity: 0.75; }
.svc-work-seat .label { opacity: 0.45; }
/* 24.08 v4 RULED (Chiara: "i clearly said i want the cards to be the size of the videos"):
   a seat holding media takes the FILE'S OWN frame — aspect auto, height auto, zero crop.
   (Both card films are 1200×674, so the boxes match each other naturally.) Only EMPTY
   placeholder seats (Zambala, until its film lands) hold the same 1200/676 frame. */
.svc-work-seat { aspect-ratio: 1200 / 676; }   /* 24.08 (Chiara: "make zambala the same size"): the EXACT card-film frame (both encodes are 1200×676) — the empty seat is pixel-identical to the video boxes */
/* 20.10 (Chiara: "can we make all of the photos the same dimensions?"): the 24.08 :has escape — media-filled seats
   ran at their NATIVE ratio — is retired. It was invisible while every seat held a 1200×676 film; the case hero
   stills arrive in mixed ratios and the grid went ragged (BBK's portrait letter ran 346px tall beside 172px frames).
   Every seat now locks to the card frame and the media cover-crops into it, films included (they are natively
   1200×676, so they lose nothing). */
.svc-work-seat video, .svc-work-seat img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pv-seat { width: 100%; height: 100%; background: color-mix(in srgb, var(--ink) 12%, transparent); }   /* 16.07: hover panes hold tinted seats until service imagery lands */
@media (max-width: 900px) { .svc-work { grid-template-columns: 1fr; } }
/* Proof roster — every case study, name + credit on hairline rows */
.svc-proof-list { list-style: none; margin: 0; padding: 0 var(--gutter); }
.svc-proof-list li {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-lg);
  border-top: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
}
.svc-proof-list li:last-child { border-bottom: 1px solid color-mix(in srgb, var(--ink) 25%, transparent); }
.svc-proof-list a {
  display: block; padding: var(--space-md) 0;
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-md); line-height: 1.15;
}
.svc-proof-list .label { opacity: 0.65; text-align: right; }
@media (max-width: 900px) {
  .svc-proof-list li { flex-direction: column; align-items: flex-start; gap: 0; padding-bottom: var(--space-md); }
  .svc-proof-list a { padding-bottom: var(--space-xxs); }
}
.svc-others { padding: 0 calc(var(--gutter) + 58px) var(--space-section); }   /* 16.07 v5 (Chiara: "narrower, so it aligns with Aura's logo — just for the Also section"): left edge sits at the wordmark's 90px (gutter 32 + burger cluster 58); if the header cluster changes, re-measure */
.svc-others > .label { display: block; opacity: 0.8; margin-bottom: var(--space-lg); }
.svc-others ul { list-style: none; margin: 0; padding: 0; }
.svc-others li { border-top: 1px solid color-mix(in srgb, currentColor 25%, transparent); }   /* 12.10: as the FAQ — reads on the held dark */
.svc-others li:last-child { border-bottom: 1px solid color-mix(in srgb, currentColor 25%, transparent); }
.svc-others a {
  display: block;
  padding: var(--space-md) 0;   /* the router's row step */
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-lg);   /* 16.07 v4 (Chiara: "just the margins — the font size was fine") — the 89px router-rung trial reverted */
  line-height: 1.1;
}
@media (max-width: 900px) {
  .svc-phases { grid-template-columns: 1fr; }
}
/* 16.07 (Film): full-bleed film rows — seat edge to edge, client + credits
   on the gutter line beneath */
.svc-films { padding-bottom: var(--space-section); }
.svc-film { margin-bottom: var(--space-xl); }
.svc-film-seat {
  position: relative;   /* 16.07 v2 (Chiara: "brand and tags over the hero video"): meta rides ON the film — the house text-over-media grammar */
  margin: 0;
  width: 100%; aspect-ratio: 21 / 9;
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.svc-film-seat video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }   /* 20.10 (Chiara: "the case study images in the image spot for all of them"): the four seats carry their case pages' own cuts — Necker, Palmer, Sevven, One Bellot */
.svc-film-seat .svc-film-note, .svc-film-seat .svc-film-meta { position: relative; z-index: 2; }   /* the meta rides ON the film, above the scrim */
/* 21.10 (Chiara: "roll this out across the other service pages — taking branding as the benchmark"): on phones the
   film seats take the BRANDING CARD GRAMMAR — cover, then name, then credits beneath — because a 21:9 seat at 358px
   is 153px tall and the name, two lines of credits and the ( Film ) note were piling over the footage. The film runs
   16:9 at the top of the figure, the meta flows under it in the card's own type, the scrim and the note stand down. */
@media (max-width: 900px) {
  .svc-film .svc-film-seat { aspect-ratio: auto; display: block; background: none; overflow: visible; }
  .svc-film .svc-film-seat video { position: static; width: 100%; aspect-ratio: 16 / 9; height: auto; object-fit: cover; display: block; }
  .svc-film .svc-film-seat::after { display: none; }
  .svc-film .svc-film-note { display: none; }
  .svc-film .svc-film-seat .svc-film-meta { position: static; display: block; padding: var(--space-sm) var(--gutter) 0; text-align: left; }   /* the seat stays full-bleed (its own 16.07 grammar); the name + credits sit on the gutter line like a card's */
  .svc-film .svc-film-client { margin: 0 0 var(--space-xxs); font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.15; }
  .svc-film .svc-film-meta .label { text-align: left; opacity: 0.65; }
}
.svc-film-seat::after {   /* the deck scrim — near-invisible on the seat tint */
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.35), transparent 40%);
  opacity: 1;   /* 20.10: the films landed — scrim on, as this comment always promised */
}
.svc-film-note { opacity: 0.4; }
.svc-film-meta {   /* 16.07 v3 (Chiara: "not centred — how you had it before"): client left, credits right, on the gutter line — just riding ON the film now */
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: var(--space-lg);
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-lg);
  z-index: 1;
}
.svc-film-client {
  margin: 0;
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-lg); line-height: 1.15;
  color: color-mix(in srgb, var(--ink) 88%, transparent);   /* swap to the 58% cream rule when footage lands */
}
.svc-film-meta .label { opacity: 0.65; text-align: right; }
@media (max-width: 900px) {
  .svc-film-meta { flex-direction: column; align-items: flex-start; gap: var(--space-xxs); }
  .svc-film-meta .label { text-align: left; }
}

/* 21.10 (Chiara, the Films We've Made stack: "how can we improve this, it looks messy"): the four full-width 21:9
   seats with the name and credits floating over the footage behind a scrim retire. The films take the Brands We've
   Built card grammar at every width — a two-up grid of 16:9 covers, the name and credits beneath in the card's own
   type, no scrim, no ( Film ) note. The phone rules above already said this; now the desktop does too. */
.svc-films { display: grid; grid-template-columns: repeat(2, 1fr); column-gap: var(--gutter); row-gap: var(--space-xl); padding: 0 var(--gutter) var(--space-section); }
.svc-films .cs-stack-text { grid-column: 1 / -1; padding-left: 0; padding-right: 0; }
.svc-film { margin: 0; }
.svc-film .svc-film-seat { aspect-ratio: auto; display: block; background: none; overflow: visible; }
.svc-film .svc-film-seat video { position: static; width: 100%; aspect-ratio: 16 / 9; height: auto; object-fit: cover; display: block; }
.svc-film .svc-film-seat::after { display: none; }
.svc-film .svc-film-note { display: none; }
.svc-film .svc-film-seat .svc-film-meta { position: static; display: block; padding: var(--space-sm) 0 0; text-align: left; }
.svc-film .svc-film-client { margin: 0 0 var(--space-xxs); font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.15; color: inherit; }
.svc-film .svc-film-meta .label { text-align: left; opacity: 0.65; }
@media (max-width: 900px) { .svc-films { grid-template-columns: 1fr; row-gap: var(--space-lg); } }

/* 21.10 (Chiara: "too much going on — one viewport, the arrow system"): the FILM STAGE. One film lit at a time in a
   section that is one screen tall; the deck's counter + pill arrows in the head row; a hard cut between films. */
.svc-films--stage { display: grid; grid-template-columns: 2fr 3fr var(--ledger-when); grid-template-rows: none; column-gap: var(--space-lg); align-items: center; top: 0; height: 100svh; min-height: 0; padding: var(--space-2xl) var(--gutter); box-sizing: border-box; }   /* 21.10 v17 (Chiara: "can it stick top and bottom so it’s centred and sticks as you scroll through the videos"): the stage pins at the TOP of the screen, one screen tall, the film centred in it — it rides up over the held rungs instead of seating under them. Padding symmetric again (2xl), the film centred by the grid. v11–v16 (the under-rung seat, the 3xl top) in git. */
.svc-films--stage { background: var(--pg-bg); transition: background-color var(--dur-ground) var(--ease-house), color var(--dur-base) var(--ease-house); }   /* 21.10 v4 (Chiara: "changed my mind, switch it back"): the cream experiment (v3) reverted — the body’s ground token on the body’s transition, so the stage hides the held rungs and moves in step with the FAQ */
.svc-films-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: var(--space-sm) var(--space-lg); }   /* 21.10 (Chiara: "move this closer to the video"): the label holds the left edge, the counter and pill sit at the right end of the number column, one lg gap off the film */   /* 21.10 (Chiara: "move the arrows closer to the Films We’ve Made"): the counter and pill sit one lg step after the label, not at the far gutter */
.svc-films-head .label { margin: 0; opacity: 0.65; }
.svc-films-ctl { display: flex; align-items: center; gap: var(--space-sm); translate: -5px 0; }   /* 21.10 (Chiara: "02 / 04 — move this 5px to the left"): the counter + pill group nudged off the film’s edge */   /* 21.10 (Chiara: "move the arrows left, reduce the gap on the left"): md → xs between the counter and the pill */
.svc-film-stage { display: grid; align-items: center; grid-column: 2 / -1; }   /* 21.10 v9 (Chiara: "should reach more of the right, increase the size"): back across the weeks column, the frame at 88% of that span, left edge still on the titles */   /* 21.10 v7 (Chiara: "too big"): the title column only — the weeks column stays clear */
.svc-films-head { grid-column: 1; }
.svc-film-stage .svc-film { grid-area: 1 / 1; margin: 0; opacity: 0; visibility: hidden; }
.svc-film-stage .svc-film.is-on { opacity: 1; visibility: visible; }
.svc-film-stage .svc-film-seat { position: relative; }
.svc-film-stage .svc-film-seat video { display: block; }
.svc-film-logo { position: absolute; left: 50%; top: 50%; translate: -50% -50%; width: 206px; height: 78px; background: var(--cream); -webkit-mask: var(--logo) center / contain no-repeat; mask: var(--logo) center / contain no-repeat; pointer-events: none; z-index: 2; }   /* 21.10 v2 (Chiara: "smaller, and all a consistent size"): one fixed 150×56 box for every mark, contain-fit inside it, dead centre of the film */   /* 21.10: the client’s mark centred over the film, cream, via mask so any single-colour SVG reads the same */
.svc-film-logo--sevven { scale: 1.4; }   /* 21.10: Sevven +10% three times over the shared box (1.1 → 1.21 → 1.33) */
.svc-film-logo--palmer { scale: 1.21; }   /* 21.10 (Chiara: "increase 10% sevven and palmer") */
.svc-film-logo--roam { scale: 0.6; }   /* 21.10 (Chiara: "make the Roam logo much smaller so it’s the same size as the other ones"): the ROAM wordmark is four tall letters, so contain-fit filled the box’s height and read huge — 0.6 brings its cap height in line with the other marks */
.svc-film-stage .svc-film-frame { position: relative; width: 100%; margin: 0; }   /* 21.10 v14 (Chiara: "undo that"): v13’s height-led fit reverted — full span again */
.svc-film-stage .svc-film-seat video { width: 100%; }   /* the film fits the stage with room for the name and credits under it */
.svc-film-stage .svc-film-seat .svc-film-meta { width: 100%; margin: 0; padding-top: var(--space-lg); display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-lg); }
@media (max-width: 900px) {
  .svc-films--stage { height: auto; min-height: 0; grid-template-columns: 1fr; row-gap: var(--space-md); }
  .svc-film-stage, .svc-films-head { grid-column: 1; }
  .svc-film-stage .svc-film-frame, .svc-film-stage .svc-film-seat .svc-film-meta { width: 100%; }
  .svc-film-logo { width: 151px; height: 55px; }
  .svc-film-stage .svc-film-seat .svc-film-meta { flex-direction: column; align-items: flex-start; gap: var(--space-xxs); }
}

/* 16.07 (Photography): the library — a curated-collection grid of seats;
   mixed ratios give the gallery rhythm until Alex's curation lands */
/* 16.07 v3 (Chiara): the library is three POSTCARD STACKS — fanned piles,
   hover fans further + straightens the top card. Pure CSS; no fades. */
.svc-stacks {
  list-style: none; margin: 0; padding: var(--space-xl) var(--gutter) var(--space-section);
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: var(--gutter);
}
.svc-stack-cards { position: relative; aspect-ratio: 3 / 2; margin-bottom: var(--space-md); }
.svc-stack-card {
  position: absolute; inset: 0;
  background: color-mix(in srgb, var(--ink) 7%, var(--paper));
  border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  display: flex; align-items: center; justify-content: center;
  transition: transform var(--dur-base) var(--ease-house);
}
.svc-stack-card:nth-child(1) { transform: rotate(-4deg) translateY(2%); }
.svc-stack-card:nth-child(2) { transform: rotate(2.5deg) translateY(-1%); }
.svc-stack-card:nth-child(3) { transform: rotate(-1deg); }
.svc-stack:hover .svc-stack-card:nth-child(1) { transform: rotate(-8deg) translate(-6%, 3%); }
.svc-stack:hover .svc-stack-card:nth-child(2) { transform: rotate(6deg) translate(6%, -2%); }
.svc-stack:hover .svc-stack-card:nth-child(3) { transform: rotate(0); }
.svc-stack-card .label { opacity: 0.4; }
.svc-stack-name {
  margin: 0 0 var(--space-xxs);
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-md); line-height: 1.15;
}
.svc-stack-count { opacity: 0.65; }
@media (max-width: 900px) { .svc-stacks { grid-template-columns: 1fr; row-gap: var(--space-2xl); } }
@media (prefers-reduced-motion: reduce) { .svc-stack-card { transition: none; } }
/* the opened collection — the stack fans out into a masonry grid below.
   16.07 (Chiara: "landscape and portrait — don't look gimmicky"): photos
   sit at natural ratios in the columns; the fan is the only flourish. */
.svc-stack { cursor: pointer; }
.svc-stack[aria-expanded="true"] .svc-stack-card:nth-child(1) { transform: rotate(-8deg) translate(-6%, 3%); }
.svc-stack[aria-expanded="true"] .svc-stack-card:nth-child(2) { transform: rotate(6deg) translate(6%, -2%); }
.svc-stack[aria-expanded="true"] .svc-stack-card:nth-child(3) { transform: rotate(0); }
.svc-collection-panel { padding: var(--space-xl) var(--gutter) 0; }
.svc-library-grid {
  list-style: none; margin: 0; padding: 0;
  columns: 3; column-gap: var(--gutter);
}
.svc-library-grid li {
  break-inside: avoid;
  margin-bottom: var(--gutter);
  aspect-ratio: 3 / 2;   /* landscape postcards; real photos keep natural ratios */
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  display: flex; align-items: center; justify-content: center;
}
.svc-library-grid li.lib-tall { aspect-ratio: 4 / 5; }   /* the portrait frames in the mix */
.svc-library-grid li .label { opacity: 0.4; }
@media (max-width: 900px) { .svc-library-grid { columns: 2; } }

/* THE LIBRARY FILMSTRIP (25.08, photography — Chiara's reference screenshot): one row of portrait
   frames, edge to edge, scroll-snapped; ( Back ) / ( Next ) top corners; the last frame peeks past
   the right edge. The frame width is the one dial. */
.svc-strip-nav { display: flex; justify-content: space-between; padding: var(--space-md) var(--gutter); }
.svc-strip-btn { background: none; border: 0; padding: 0; margin: 0; cursor: pointer; color: inherit; opacity: 0.65; transition: opacity var(--dur-quick) var(--ease-house); }   /* no `font: inherit` — the button's .label class sets the mono register (Chiara: "back and next need to be the mono font") */
.svc-strip-btn:hover { opacity: 1; }
.svc-strip { overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; scroll-padding-inline: var(--gutter); scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.svc-strip::-webkit-scrollbar { display: none; }
.svc-strip-track { display: flex; gap: var(--space-sm); width: max-content; }
.svc-library .svc-strip-track { column-gap: var(--space-lg); }   /* 21.10 (Chiara: "more space between the images to let them breathe on the carousel"): the library's frames step from the shared sm (16) to lg (40); Necker's making-of keeps the tight step */
.svc-library .svc-strip-frame { height: min(58svh, 620px); width: auto; aspect-ratio: 2 / 3; }   /* 21.10: 72svh overran the seat (card lands ~234px down on desktop) — 58svh keeps label + frames inside the screen */   /* 21.10 (Chiara: "a lot of dead space — lets make the images bigger"): the library frames fill the pinned screen — 72svh tall at the same 2:3, from the shared ~456px; the label and air above/below take what remains. Phones (static strip) inherit the same height. */
/* 21.10 (Chiara: "the library can stop at the same spot process does on the other pages"): the pinned library seats
   at --ledger-top — header + lg, where every process stack pins — instead of centring in the screen; label first,
   then the frames, the air falls below. */
.svc-library .cs-hscroll-pin { justify-content: flex-start; padding-top: var(--space-md); }   /* the closing card's own sticky seat (under the stacked rungs) already places the pin — only a md inside it, or the label sat twice as low */
@media (max-width: 900px) {
  .svc-library .svc-strip-frame { height: auto; width: 74vw; }   /* 25.10 (Chiara, /photography on the phone: "the images are slightly smaller — show one full image and then a tiny bit of the next one so you know you scroll"): 78 → 74vw (304 → 289 at 390) on an md (24) gap — the next frame shows ~45px at the right edge (was 6) */
  .svc-library .svc-strip-track { column-gap: var(--space-md); }
  .svc-library .svc-strip-track::before, .svc-library .svc-strip-track::after { flex-basis: calc(var(--inset-phone) - var(--space-md)); }   /* the first frame starts on the page's 32 edge (was 40) */
  .svc-library .cs-hscroll-pin { justify-content: center; padding-top: 0; gap: var(--space-lg); }   /* v2 ("move it up a bit — the margin on the bottom and the margin on top are not equal"): centred on the WHOLE screen, equal air above and below as the eye reads it (v1 centred below the header: 211 above vs 144 below from the screen edges) · v1 ("that whole section needs to be centred on the page"): label + strip centre in the pinned screen (was top-loaded: 96 + the label block's own padding, frames ending 92px off the foot) */
  .svc-library .cs-hscroll-pin > .cs-stack-text { padding: 0 var(--inset-phone); }   /* the label on the 32 edge with the frames, no section padding of its own inside the pin */
}   /* was: .svc-library .svc-strip-frame { height: auto; width: 78vw; } .svc-library .cs-hscroll-pin { padding-top: var(--space-2xl); } */   /* phones: the static strip keeps its 78vw frames; the desktop height rule would have overrun the screen width */
.svc-strip-frame { position: relative; margin: 0; flex: 0 0 auto; width: clamp(240px, 24vw, 420px); aspect-ratio: 2 / 3; scroll-snap-align: start;   /* 25.08: 2:3 — the Japan exports' own ratio, no crop */ background: color-mix(in srgb, var(--ink) 7%, transparent); }
.svc-strip-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.svc-strip-frame figcaption {   /* 19.10 (Chiara: "make sure the Img. tag is the inverse colour of the image so it shows up"): it WAS the inverse (white, difference) — which is exactly why it vanished on mid-tone sand and sky. Now the frosted pill the VIEW cursor and the deck arrows use: cream on 28% ink, blurred. Reads on any frame. */
  position: absolute; top: var(--space-sm); left: var(--space-sm); margin: 0;
  color: var(--cream); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 0 12px rgba(0, 0, 0, 0.35);   /* 20.10 (Chiara, Necker's behind-the-scenes: "remove this pill background"): the 19.10 frosted pill is OUT — the tag is bare cream type again, held on any frame by a soft shadow rather than a box (the pre-pill white/difference blend vanished on sand and sky) */
  padding: 0; background: none; backdrop-filter: none; -webkit-backdrop-filter: none; border-radius: 0;
}   /* difference: reads on a white-bordered frame and a dark one alike */
.svc-strip-track::before, .svc-strip-track::after { content: ''; flex: 0 0 calc(var(--gutter) - var(--space-sm)); }   /* gutter spacers at both ends (padding collapses inside a scroller): the first frame starts on the gutter line, the last can snap fully in */
@media (max-width: 900px) { .svc-strip-frame { width: 78vw; } .svc-strip-nav { padding-top: var(--space-lg); } }
/* 01.10 (Necker Island, The Making Of): a LANDSCAPE strip — phone photographs are 4:3, so the frames go 4:3 and wider; everything else about the Library strip holds. The case-study gutter rules do not reach .svc-strip, so it runs edge to edge like /photography. */
.svc-strip--landscape .svc-strip-frame { width: clamp(320px, 36vw, 640px); aspect-ratio: 4 / 3; }
/* 01.10 (Necker, "like the Domingo reference"): a PINNED HORIZONTAL RUN — the section is one viewport tall
   plus the track's overflow (--hs-h, main.js), the inner stage sticks, and the page scroll slides the track. */
.cs-hscroll { position: relative; height: var(--hs-h, 300vh); }
.cs-hscroll-pin { position: sticky; top: 0; height: 100svh; display: flex; flex-direction: column; justify-content: center; gap: var(--space-md); overflow: hidden; }
.cs-section-label { margin: 0; padding: 0 var(--gutter); opacity: 0.65; }
.cs-section-label + .cs-section-title { margin-top: calc(var(--space-sm) - var(--space-md)); }   /* the pin's flex gap is md (24); label → title should be the story rows' sm (16), so the title pulls up by the difference */   /* 20.10: the mono tag over the serif section title — the story rows' label-then-text grammar */
.cs-section-title { margin: 0; padding: 0 var(--gutter); font-family: var(--font-serif); font-weight: 300; font-size: var(--type-phase); line-height: 1.05; color: var(--title-ink); }
.cs-hscroll-viewport { overflow: hidden; }
.cs-hscroll .svc-strip-track { will-change: transform; }
.cs-hscroll-nav { padding-top: 0; padding-bottom: 0; }
/* 12.10 (Chiara: credits "more secondary under the making of"): one line under the strip at the caption rung; roles muted, names at full ink. Necker only for now. */
.cs-credits-line { margin: 0; padding: 0 var(--gutter); font-size: var(--type-caption); opacity: 0.65; }
.cs-credits-line span { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .cs-hscroll { height: auto; }
  .cs-hscroll-pin { position: static; height: auto; padding: var(--space-2xl) 0; }
  .cs-hscroll-viewport { overflow-x: auto; scrollbar-width: none; }
}
@media (max-width: 900px) { .svc-strip--landscape .svc-strip-frame { width: 84vw; } .svc-strip-frame figcaption.label { font-size: calc(var(--type-label) - 2px); } }   /* 19.10 (Chiara: "can the Img. 01 / 02 be smaller"): the frame captions on the phone label size */

/* 16.07 (About): founder section — headshot seat left, first-person copy right */
.svc-founder {
  display: grid; grid-template-columns: 1fr 2fr;
  gap: var(--gutter);
  align-items: start;
  padding: var(--space-section) var(--gutter);
}
.svc-founder-photo {
  margin: 0; aspect-ratio: 4 / 5;
  background: color-mix(in srgb, var(--ink) 7%, transparent);
  display: flex; align-items: center; justify-content: center;
}
.svc-founder-photo .label { opacity: 0.45; }
.svc-founder-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }   /* 12.10: the headshot landed (DSC07705); fills the 4/5 seat */
.svc-founder-copy .label { display: block; opacity: 0.65; margin-bottom: var(--space-md); }
.svc-founder-quote {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-md); line-height: 1.35;
  max-width: 26ch;
  margin: 0 0 var(--space-lg);
}
.svc-founder-copy p:not(.label) {
  margin: 0 0 var(--space-md);
  font-family: var(--font-body); font-weight: 300;
  font-size: var(--type-body); line-height: 1.6;
  max-width: 52ch;
}
/* 12.10: the quote rule above (one class) was losing to this paragraph rule (two classes + :not) — the serif line has
   rendered as body text since the studio founder block was built. Restated at matching specificity, after it. */
.svc-founder-copy .quiet-link { border-bottom: 0.5px solid currentColor; padding-bottom: 2px;   /* 21.10: footer CTA’s 0.5px rest weight */ }   /* 12.10 (Chiara: "make sure More About The Studio is underlined") */
.svc-founder-copy p.svc-founder-quote { font-family: var(--font-serif); font-size: var(--type-serif-md); line-height: 1.35; max-width: 26ch; margin: 0 0 var(--space-lg); color: var(--title-ink); }
@media (max-width: 900px) {
  .svc-founder { grid-template-columns: 1fr; }
}
/* 16.07 (LHC, Chiara): the identity split — Group 79 as live elements.
   Left: full-bleed photo, monogram centred. Right: LHC espresso ground,
   inset photo ~5:6 at 62%, logotype spanning ~80% over it. */
.cs-split {
  position: relative;   /* 20.07 v3 (Chiara: "it needs to stick"): the RUNWAY — ~150svh of dwell per state, the deck's budget */
  /* 25.09 (Chiara, after Palmer + Odisi: "apply the same on the other case study pages"): ONE FLICK PER STATE
     is now the site-wide budget. --split-dwell is the travel per state; the base is a three-state deck
     (LHC, Beyond by Kim) and the variants below only change the multiplier. Was 550svh (≈183svh/state). */
  --split-dwell: 75svh;
  width: 100%; height: calc(100svh + 3 * var(--split-dwell));
}
.cs-split-pin {
  position: sticky; top: calc((100svh - min(56.25vw, 810px)) / 2);   /* the frame holds centred mid-viewport while the runway scrolls */
  height: min(56.25vw, 810px);
  overflow: hidden;
}
/* 20.07 (Palmer): --single — a one-state split rides in normal flow; no
   runway, no pin (nothing to flick through until more states land) */
.cs-split--single { height: min(56.25vw, 810px); }
.cs-split--single .cs-split-pin { position: static; top: auto; }
/* 01.08 (Palmer, Chiara: LHC's scroll-cut splits "for Palmer with the first
   mock-up"): --two — a TWO-state pinned split (~same per-state dwell as the
   550svh/3 base). Unused since the 01.08 v2 three-beat resequence but kept
   as the generic two-state size. */
.cs-split--two {   /* 25.09: two-state deck (Palmer). First page on the one-flick prototype; the budget is site-wide now (see .cs-split) */
  height: calc(100svh + 2 * var(--split-dwell));
}
.cs-split--three {   /* 25.09: three-state deck (Odisi) — same as the base; kept so the count is legible in the markup */
  height: calc(100svh + 3 * var(--split-dwell));
}
.cs-split--four { height: calc(100svh + 4 * var(--split-dwell)); }   /* 01.08 (Zambala): FOUR-beat runway · 25.09: on the one-flick budget */
.cs-split--five { height: calc(100svh + 5 * var(--split-dwell)); }   /* 25.09: Zambala has carried FIVE beats since the 03.09 rebuild but was still sized as four — its last cut sat 25svh from the release. Now five */
/* 01.08 v2 (Palmer beat 2, Chiara: "the full-width green image with the
   logo"): --full — a state whose single panel spans BOTH columns; reveal
   rides the existing is-stateN-ground/right rules, one clean cut. */
.cs-split-state--full .cs-split-right { grid-column: 1 / -1; }
/* 01.08 v3 (Palmer, Chiara: "smooth out the transition between the change of
   the images"): --soft — the cuts become quick dissolves. TRIED on Palmer +
   Beyond, RETIRED same evening (Chiara on Beyond: "the animation is off —
   replicate what we've used on the other pages") — every case deck rides the
   LHC hard cut. Rules kept; re-adding the class on a section restores the
   dissolve if it's ever wanted. */
.cs-split--soft .cs-split-state--2 .cs-split-right,
.cs-split--soft .cs-split-state--2 .cs-split-left,
.cs-split--soft .cs-split-state--3 .cs-split-right,
.cs-split--soft .cs-split-state--3 .cs-split-left,
.cs-split--soft .cs-split-state--2 .cs-split-right .cs-split-inset,
.cs-split--soft .cs-split-state--4 .cs-split-right,
.cs-split--soft .cs-split-state--4 .cs-split-left,
.cs-split--soft .cs-split-state--5 .cs-split-right,
.cs-split--soft .cs-split-state--5 .cs-split-left {   /* 25.09: states 4–5 join (Zambala) — the dissolve was written for LHC's three beats */
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-house), visibility 0s 0.5s;
}
.cs-split--soft.is-state2-ground .cs-split-state--2 .cs-split-right,
.cs-split--soft.is-state2-right .cs-split-state--2 .cs-split-right .cs-split-inset,
.cs-split--soft.is-state2-left .cs-split-state--2 .cs-split-left,
.cs-split--soft.is-state3-right .cs-split-state--3 .cs-split-right,
.cs-split--soft.is-state3-left .cs-split-state--3 .cs-split-left,
.cs-split--soft.is-state4-right .cs-split-state--4 .cs-split-right,
.cs-split--soft.is-state4-left .cs-split-state--4 .cs-split-left,
.cs-split--soft.is-state5-right .cs-split-state--5 .cs-split-right,
.cs-split--soft.is-state5-left .cs-split-state--5 .cs-split-left { opacity: 1; }
.cs-split-right > img.cs-split-fill,
.cs-split-right > video.cs-split-fill { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }   /* generic panel-fill (the state-3 LHC rule, generalised for Palmer's colour panel · 03.08: video joins — the Palmer cocktail film runs full) */
.cs-split-state {
  position: absolute; inset: 0;
  display: grid; grid-template-columns: 1fr 1fr;
  z-index: 1;   /* 03.09 (Chiara, Beyond by Kim: "logo on left should disappear after the first video"): each
                   state is its OWN stacking context. Without this, .cs-split-logotype's z-index: 5 escaped
                   its state and resolved against the pin — so state 1's mark painted OVER states 2 and 3 and
                   the Beyond logotype sat on the brand letter and the business cards for the whole section.
                   Every state gets 1: later states already win on DOM order, and the marks now stay home. */
}
/* 16.07 v7 (Chiara): hard cuts PACED BY SCROLL — the scroll itself is the
   trigger: right half cuts at one depth, left half a beat deeper. Fully
   reversible, no timers (the method-deck grammar: the cut IS the gesture). */
/* 20.07 v5 (Chiara: "i dont like the swipe effect but like everything else"):
   the sweep retires — back to the HARD CUT (16.07 v7: "the cut IS the
   gesture"), but the MOF orchestration stays: scroll fires the round, and
   the cuts land as a staggered MONTAGE via transition-delay on visibility
   (ground cuts, frame cuts half a beat later, far panel closes the phrase).
   Delays live on the ACTIVE rules only, so reverse-scroll unwinds instantly. */
.cs-split-state--2, .cs-split-state--3, .cs-split-state--4 { pointer-events: none; }   /* 01.08: --4 joins (Zambala's four-beat deck) */
.cs-split-state--2 .cs-split-right, .cs-split-state--2 .cs-split-left,
.cs-split-state--3 .cs-split-right, .cs-split-state--3 .cs-split-left,
.cs-split-state--4 .cs-split-right, .cs-split-state--4 .cs-split-left,
.cs-split-state--2 .cs-split-right .cs-split-inset {
  visibility: hidden;
  transition: visibility 0s;
}
/* 20.07 v9 (Chiara: "images changing need to be staggered left / right"):
   two moments per round — the RIGHT panel cuts (ground + inset as one),
   the LEFT follows 0.35s behind. A quick one-two, not five pops (that
   version retired v6) and not simultaneous (retired v9). Delays sit on
   the ACTIVE rules only, so reverse-scroll unwinds instantly. */
.cs-split.is-state2-ground .cs-split-state--2 .cs-split-right { visibility: visible; }
.cs-split.is-state2-right .cs-split-state--2 .cs-split-right .cs-split-inset { visibility: visible; }
.cs-split.is-state2-left .cs-split-state--2 .cs-split-left { visibility: visible; transition-delay: 0.35s; }
.cs-split.is-state3-right .cs-split-state--3 .cs-split-right { visibility: visible; }
.cs-split.is-state3-left .cs-split-state--3 .cs-split-left { visibility: visible; transition-delay: 0.35s; }
.cs-split.is-state4-right .cs-split-state--4 .cs-split-right { visibility: visible; }   /* 01.08: the fourth round, same one-two */
.cs-split.is-state4-left .cs-split-state--4 .cs-split-left { visibility: visible; transition-delay: 0.35s; }
.cs-split-state--5 { pointer-events: none; }   /* 03.09: the fifth beat (Zambala's rebuild to the LHC structure) */
.cs-split-state--5 .cs-split-right, .cs-split-state--5 .cs-split-left { visibility: hidden; transition: visibility 0s; }
.cs-split.is-state5-right .cs-split-state--5 .cs-split-right { visibility: visible; }
.cs-split.is-state5-left .cs-split-state--5 .cs-split-left { visibility: visible; transition-delay: 0.35s; }
/* 20.07 v11: the 1.05→1 settle TRIED + RETIRED same hour (Chiara: "i dont
   like the zoom fade") — the split is pure hard cuts again, right then left
   0.35s apart. The full retired-motion ledger for this section: sweep (v3),
   five-beat stagger (v4), simultaneous cut (v6), panel parallax (v6), inset
   drift (v8), settle (v10). The cut + one-two stagger IS the gesture. */
.cs-split-state--3 .cs-split-right > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }   /* 20.07: the flat Group-84 crop fills the panel */
@media (prefers-reduced-motion: reduce) {
  .cs-split-state--2 .cs-split-right, .cs-split-state--2 .cs-split-left,
  .cs-split-state--3 .cs-split-right, .cs-split-state--3 .cs-split-left,
  .cs-split-state--2 .cs-split-right .cs-split-inset { transition: none; }   /* delays vanish — each round lands as one clean cut */
}
.cs-split-left--seat {
  background: color-mix(in srgb, var(--ink) 7%, transparent);
}
.cs-split-right.cs-split-right--cream {
  background: #EAE6DB;   /* 16.07 (Chiara): state-2 right ground · 20.07: selector doubled — the base .cs-split-right espresso is declared later and was winning the cascade, so the yoga panel never went cream */
  color: var(--ink);
}
.cs-split-right.cs-split-right--palmer {
  background: #FFEACF;   /* 20.07 (Chiara): Palmer beat-3 ground — brand cream */
  color: var(--ink);
}
.cs-split-right--palmer .cs-split-inset { width: min(59.9%, 435px); }   /* 01.08 (Chiara): cocktail-bar film +5% — Palmer's inset only; the shared inset (LHC) holds min(57%, 414px) */
.cs-split-left.cs-split--odisi-green,
.cs-split-right.cs-split--odisi-green { background: #2E4A33; }   /* 20.07: Odisi deep green ground (from Chiara's Figma preview) */
/* 20.07 (Chiara's Odisi split previews): a brand SVG floated on a panel as a
   LIVE element, poured in cream through a mask so it reads on any ground.
   Pass the file via --m; aspect-ratio keeps the box true to the artwork. */
.brand-mark-el {
  background-color: var(--cream);
  -webkit-mask: var(--m) center / contain no-repeat;
  mask: var(--m) center / contain no-repeat;
}
.cs-split-left > video, .cs-split-right > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }   /* 20.07 (Chiara): film can fill a split panel like a photo */
.cs-split-inset--free { aspect-ratio: auto; background: none; }   /* an inset that keeps its image's own proportions (the emboss composition) */
.cs-split-inset--free img { position: static; width: 100%; height: auto; }
.cs-split-inset video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }   /* 20.07: film in the inset frame — same fill as the img rule */

.cs-split-left {
  position: relative; overflow: hidden;
  background: color-mix(in srgb, var(--ink) 7%, transparent);   /* seat until the photo lands */
  display: flex; align-items: flex-end; justify-content: center; padding-bottom: var(--space-md);
}
.cs-split-left > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* THE FLICK — PARKED 02.09 ("no undo that go back to the palm video"). Kept whole: re-add the
   <img class="cs-flick"> frames to a .cs-split-left and it works again, no other change needed.
   (02.09, Chiara: "how about we flick through the photos on the left") — a stack of
   frames in one pane, advanced on a timer by js/main.js (csFlick).
   A WIPE, not a crossfade: her standing rule is no fades ("PowerPoint"), one decisive gesture,
   and the clip-path unmask is already the site's own device (.disc-preview, .hw-slide).
   Each frame KEEPS .is-on once it has been shown and is given a rising z-index, so the incoming
   frame simply covers the one before it. Nothing clips closed mid-wipe (which would flash the
   pane's ground through the un-wiped half) and the wrap back to frame 1 needs no special case. */
/* 03.09 (Chiara: "why do the images have a weird swipe on them" → "they should be the same as the other
   case studies"): THE WIPE IS RETIRED. The frames were revealed by a clip-path opening right-to-left over
   0.9s — a hard-edged bar sweeping across a still. That is the same effect Chiara retired from the split
   cuts on 20.07 ("i dont like the swipe effect but like everything else"), which left this section running
   two grammars at once. Now it is the split's: a HARD CUT, the incoming frame simply on top. The cut IS
   the gesture. (The clip-path arc is in git if a reveal is ever wanted here again.) */
.cs-split-left > .cs-flick, .cs-split-right > .cs-flick { visibility: hidden; }   /* 03.09: right panes flick too (Zambala state 1) */
.cs-split-left > .cs-flick.is-on, .cs-split-right > .cs-flick.is-on { visibility: visible; }
.cs-split-left > .cs-split-logotype { z-index: 5; }
/* 03.09 (Chiara, Zambala: "logo on left should disappear after the first video"): in a flick seat the
   mark belongs to the OPENING frame — it fades out as the seat wipes to the stills and returns when the
   film comes round again. Only applies where JS has flagged a flick pane, so single-image seats (Palmer,
   LHC) are untouched. Fades on the house curve rather than cutting: the wipe is the gesture, not this. */
.cs-split-left.is-flick-seat > .cs-split-logotype { opacity: 0; transition: opacity var(--dur-base) var(--ease-house); }
.cs-split-left.is-flick-seat.is-flick-first > .cs-split-logotype { opacity: 1; }
.cs-split-left--scrim::after { content: ''; position: absolute; inset: 0; background: rgba(0, 0, 0, 0.3); z-index: 4; pointer-events: none; }   /* 03.09 (Chiara, Zambala: "add a dark overlay" to the beach film under the logotype): a flat 30% wash between the frames and the mark — same grammar as the deck slides' flat layer, no gradient. z-index 4: above every flick frame (they stack from 2 upward), below the mark at 5, so the wash is the same on the film and on the stills that follow it. */   /* the mark stays above every frame */
@media (prefers-reduced-motion: reduce) {
  .cs-split-left > .cs-flick:not(.is-on), .cs-split-right > .cs-flick:not(.is-on) { visibility: hidden; }
}
.cs-split-left .cs-media-note { position: static; opacity: 0.4; color: inherit; }
.cs-split-mark {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(15.3%, 112px);   /* 16.07 v7: −10% (Chiara) */
  display: flex; align-items: center; justify-content: center;
}
.cs-split-mark img { width: 100%; height: auto; display: block; }
.cs-split-mark .label { opacity: 0.4; white-space: nowrap; }
.cs-split-right {
  position: relative; overflow: hidden;
  background: #4D3B37;   /* LHC espresso */
  color: var(--cream);
}
.cs-split-inset {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(57%, 414px); aspect-ratio: 5 / 6;   /* 16.07 (Chiara): −8% */
  margin: 0;
  background: color-mix(in srgb, #ffffff 8%, transparent);
  display: flex; align-items: flex-end; justify-content: center; padding-bottom: var(--space-md);
}
.cs-split-inset img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cs-split-inset .label { opacity: 0.45; }
.cs-split-logotype {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(72%, 522px);   /* 16.07 v7: −10% (Chiara) */
  display: flex; align-items: center; justify-content: center;
}
.cs-split-logotype img { width: 100%; height: auto; display: block; }
.cs-split-logotype .label { opacity: 0.6; }
@media (max-width: 900px) {
  /* 19.10 (Chiara, Palmer on the phone: "what's happening to the hero here?"): the 20.07 rule made the pin `static`, so
     the absolutely-positioned states lost their container, the section collapsed to 0px and its layers painted over
     the TOP OF THE PAGE — the green panel and laptop sat on the hero. The pin stays a positioned box; the states come
     out of the absolute stack and sit in flow, one under the other, every pane visible (the scroll-cut visibility
     rules are desktop choreography). */
  .cs-split { height: auto; position: relative; }
  .cs-split-pin { position: relative; height: auto; top: auto; overflow: visible; display: grid; gap: var(--gutter); }   /* (Chiara: "match the image spacing with the section under Design"): the reel's seams are the stills' gap — the gutter, 16px — not 2px */
  .cs-split-pin .cs-split-state { position: relative; inset: auto; }
  .cs-split-pin .cs-split-state .cs-split-left, .cs-split-pin .cs-split-state .cs-split-right { visibility: visible; opacity: 1; transition: none; }
  /* 20.10 (Chiara, LHC: "the mockups look weird"): the desktop INSET — a 5:6 framed picture floating in a coloured half —
     is a two-up composition; in a full-width 16:9 phone pane it read as a small portrait with a logotype across it. On
     the phone every inset FILLS its pane, and the flick-hidden panes (the desktop's scroll-cut choreography) are shown. */
  .cs-split-pin .cs-split-state .cs-split-inset:not(.cs-split-inset--free) { position: absolute; inset: 0; transform: none; width: 100%; height: 100%; aspect-ratio: auto; background: none; visibility: visible; opacity: 1; padding: 0; }
  /* 20.10 (Chiara, Odisi: "what's going on — can we do something about that?"): the --free inset (the embossed mark, a
     card that keeps its own proportions over the palm leaf) was caught by the fill rule and stretched into a 2:1 strip.
     It stays a centred card on the phone, 62% of the pane wide, its image at native shape. */
  .cs-split-pin .cs-split-state .cs-split-inset--free { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); height: 78%; width: auto; aspect-ratio: auto; background: none; visibility: visible; opacity: 1; padding: 0; margin: 0; display: block; }   /* sized by HEIGHT — the emboss is portrait, so a width-set card overflowed the 16:9 pane */
  .cs-split-pin .cs-split-state .cs-split-inset--free img { position: static; height: 100%; width: auto; object-fit: unset; display: block; }   /* opacity: the state-2 inset fades in on the desktop cut; on the phone it is simply there */
  .cs-split-pin .cs-split-state .cs-split-inset img, .cs-split-pin .cs-split-state .cs-split-left > img, .cs-split-pin .cs-split-state .cs-split-right > img { opacity: 1; visibility: visible; clip-path: none; transform: none; }
  .cs-split-pin .cs-split-state .cs-split-right .cs-split-inset.cs-split-inset, .cs-split-pin .cs-split-state .cs-split-left .cs-split-inset.cs-split-inset { opacity: 1; transition: none; }   /* outranks the cut choreography's state-scoped opacity: 0 */
  /* 20.10 (LHC: "can we zoom out on any of the images?"): true-proportion panes were tried ("too big"), then 5:4, then
     Chiara ruled "16:9" — the reel stays on the one window everywhere. The --natural variant is retired. */
  .cs-split-pin .cs-split-state .cs-flick, .cs-split-pin .cs-split-state .cs-flick:not(.is-on) { visibility: visible; }
  .cs-split-pin .cs-split-state .cs-split-inset .label { display: none; }
  .cs-split-pin .cs-split-state { pointer-events: auto; }
  .cs-split-left, .cs-split-right, .cs-split-state--full .cs-split-right { aspect-ratio: 16 / 9; }   /* → "dimensions should be 16:9 for all": every pane in the phone reel, full-width 16:9 (was 1:1 squares and a 2:1 band) */   /* 19.10 (Chiara, Palmer: "the mockup video needs to be the same size as the one below it"): the full-width video as a 2:1 band */
  .cs-split-pin .cs-split-state { grid-template-columns: 1fr; gap: var(--gutter); }   /* → "do we stack them?": yes — two 179px squares side by side are stamps on a phone. Every pane full width: a vertical reel */
  .cs-split-pin .cs-split-state--full .cs-split-right { grid-column: 1; }
  /* 20.10 (Zambala: "the cropping is all wrong… keep the split screen on mobile for this one… for the first section"
     → then "image 1 and 2 could stay as full"): a --pairs two-up was built for state 1 and RETIRED within the hour —
     every Zambala pane runs full width in the reel, like every other split on the phone. */
  /* → "and then the yellow and footprints 1/2": state 2 (the repeated wordmark on yellow beside the wet footprints) is the
     first pair kept — then extended to every state after the first. */
  .cs-split-pin .cs-split-logotype.cs-split-logotype--zambala { width: min(26.8vw, 330px); }   /* 20.10 (Chiara: "Zambala logo 5% bigger on mobile"): 25.5 → 26.8vw on the phone (99 → 104px at 390); the 330px cap keeps desktop where it was */
  .cs-split--pairs .cs-split-pin .cs-split-state:not(.cs-split-state--1) { grid-template-columns: 1fr 1fr; gap: 0; }   /* → "remove the line space between the ½ photos so they touch": no CENTRE seam — the two squares meet; above and below, each pair keeps the page's 16px gutter gap like every other media (a pulled-back row gap was tried and reverted: "top and bottom need equal gap, matching the rest of the pages") */
  .cs-split--pairs .cs-split-pin .cs-split-state:not(.cs-split-state--1) .cs-split-left, .cs-split--pairs .cs-split-pin .cs-split-state:not(.cs-split-state--1) .cs-split-right { aspect-ratio: 1 / 1; }
  /* 19.10 (Palmer: "split the video — sofa and text on green?"): a full-frame film whose composition is a 2:1 split
     becomes two 16:9 panes on the phone — the page carries a second <video> of the same file in .cs-split-left--phone-half,
     cropped to the left half; the original crops to the right half. */
  .cs-split-pin .cs-split-left--phone-half { display: block; order: 2; }   /* outranks the desktop none below · (Chiara: "open with the video of the laptop"): the green still steps SECOND — the film's laptop half leads the reel */
  /* → "we've repeated the same asset": object-position only SHIFTED a 2:1 frame inside a 16:9 pane (each pane still showed
     ~90% of it). True halves: the film is laid at 200% of the pane's width, and each pane windows onto its half. */
  .cs-split-pin .cs-split-left--phone-half, .cs-split-pin .cs-split-state--full .cs-split-left--phone-half + .cs-split-right { overflow: hidden; }
  .cs-split-pin .cs-split-left--phone-half img.cs-split-fill { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }   /* the green pane is a still now (Chiara's artwork), not a half of the film */
  .cs-split-pin .cs-split-state--full .cs-split-left--phone-half + .cs-split-right video.cs-split-fill { position: absolute; top: 0; bottom: 0; right: auto; width: 200%; max-width: none; height: 100%; object-fit: cover; object-position: 50% 50%; left: 0; }   /* max-width: none — the global media cap would hold it at 100% · descendant, not child: main.js wraps each pane's media in a .cs-flick frame at load */
  .cs-split-pin .cs-split-state--full .cs-split-left--phone-half + .cs-split-right video.cs-split-fill { left: -100%; }
  /* 20.10 (Palmer: "the two sofa photos — stack on top, as you scroll the image replaces it"): the laptop frame carries
     the phone still as its swap layer on the phone; state 2's separate phone pane hides. */
  .cs-split-pin .cs-swap-b--phone, .case-study .cs-split-pin .cs-stack-item--swap img.cs-swap-b--phone { display: block; position: absolute; inset: 0; left: 0; width: 100%; height: 100%; object-fit: cover; z-index: 2; opacity: 0; transition: opacity var(--dur-slow) var(--ease-house); }
  .cs-split-pin .cs-stack-item--swap.swapped .cs-swap-b--phone, .case-study .cs-split-pin .cs-stack-item--swap.swapped img.cs-swap-b--phone { opacity: 1; }   /* the second selector matches the gutter-fix rule's specificity (.case-study + img), which had been re-pinning 0 after the swap */
  .cs-split[data-project="palmer"] .cs-split-state--2 .cs-split-left { display: block; position: relative; overflow: hidden; visibility: visible; opacity: 1; }   /* 27.10 (Chiara, phone: "the laptop, then the green image, then a phone mockup, then the green image"): the phone mockup returns as the third pane (was hidden) */
  .cs-split[data-project="palmer"] .cs-split-state--2 .cs-split-left img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
  .cs-split[data-project="palmer"] .cs-split-pin .cs-swap-b--phone, .case-study .cs-split[data-project="palmer"] .cs-split-pin .cs-stack-item--swap img.cs-swap-b--phone, .case-study .cs-split[data-project="palmer"] .cs-split-pin .cs-stack-item--swap.swapped img.cs-swap-b--phone { display: none; }   /* the phone mockup has its own pane now — pane 1 stays the laptop */
  .case-study .cs-split[data-project="palmer"] .cs-split-state--1 .cs-split-right, .cs-split[data-project="palmer"] .cs-split-state--1 .cs-split-right { margin-inline: 0; }   /* 27.10 ("the first image isn't the same width as the others"): the laptop pane carried the swap seat's 16px inset — it now runs the same 358 as the greens */
}
.cs-split-left--phone-half, .cs-swap-b--phone { display: none; }   /* desktop: the single full-frame film, no phone swap layer */
/* 20.10 (Chiara: "on desktop the two Zambala laptop mockups need to be stacked"): the duplicate-scene pair is a swap seat at EVERY width now — the first frame changes to the second as it passes the middle; the standalone second figure stays hidden. Hustl's pair takes the same. (The phone-only version lived here from this morning.) */
.cs-stack-item--pair { position: relative; }
.cs-stack-item--pair .cs-swap-b--phone { display: block; }
.cs-stack-item--pair-second { display: none; }

/* 15.07: placeholder seat — a mono note holding a parked block's place */
.cs-placeholder {
  margin: 0 var(--gutter) var(--space-lg);
  padding: var(--space-2xl) var(--gutter);
  text-align: center;
  border: 1px dashed color-mix(in srgb, var(--ink) 25%, transparent);
}
.cs-placeholder .label { opacity: 0.5; }
/* 03.09 (Chiara's 10.05 screenshot: "make this dark" -> "not cream"): a case study now ENDS DARK.
   The handoff and the CTA were cream, so the page ran light straight into a dark lounge film and the
   wordmark, the closing line and Start a Project were all light-grey-on-light. Dark ground, cream type,
   and the footer melt is retargeted to match so the film washes up into the same near-black instead of
   into paper. The homepage is untouched: its runway is still cream and its --melt still resolves to
   --paper, so nothing there moves. */
body.case-study .cs-handoff { background: var(--ground-3); color: var(--cream); }   /* .cs-cta retired from case studies 03.09 — the ask lives on the film now */
body.case-study .footer-scrim { --melt: var(--ground-3); }
/* 12.10 (Chiara, Necker: "this section of the footer should be cream background with dark text, just on this case study page"): body.close-paper is the
   mirror of close-dark — the page opts OUT of the descent and stays on paper: no drift (main.js skips the run, so --cx1/--cx2 keep their
   paper defaults), ink type, and the film melts up into paper the way the homepage did before 03.09. Necker only: its making-of strip is
   already cream, so a dark handoff was a hard flip after the lightest section on the page. */
body.case-study.close-paper .cs-handoff { background: var(--paper); color: var(--ink); }
body.case-study.close-paper .footer-scrim { --melt: var(--paper); }
body.case-study.close-paper .footer-mail, body.case-study.close-paper .footer-socials { color: var(--ink); }   /* 20.10 (the footer review): the mail + socials pin to the TOP of the film, which on the paper close is the melt's solid cream — they were cream-on-cream (dim before today's deeper melt, gone after it). Ink, like the handoff above; the legal links at the frame's dark base stay cream. */
/* 20.10 (Chiara, Necker on the phone: "the footer image line — can we fix"): the melt releases at 44%, and Necker's
   poster puts its sea horizon at ~30% with bright sky above — the paper let go exactly where the frame turned from
   pale to dark, so the two edges read as one cut. First pass held the paper to 6% and released at 58%; the horizon
   still showed ("the image on necker still has that harsh line"). MEASURED: object-fit scales the 3:2 poster to the
   viewport HEIGHT on a phone, so the horizon lands at ~19% of the frame (160px at 844) and at ~17% on a 16:10
   desktop — both well below the 6% solid band, under a 70–85% veil that cannot hide a pale-to-dark cut.
   20.10 v2 (Chiara: "increase the opacity of the footer scrim"): the paper is now SOLID to 22% — through the
   horizon on every aspect — and steps down to release by 66%; the radial lobe lengthens to match. The house
   (roof ~45–50%) sits under a 32–50% veil, the pool and the point run clear. Necker only. */
body.case-study.close-paper .footer-scrim {   /* 27.10 (Chiara: "the curve of the reveal on Necker looks weird — a straight line, ending at its highest point"): the off-centre radial lobe is gone; one level fade */
  background:
    linear-gradient(to bottom,
      var(--melt) 0%,
      var(--melt) 22%,
      color-mix(in srgb, var(--melt) 88%, transparent) 29%,
      color-mix(in srgb, var(--melt) 70%, transparent) 36%,
      color-mix(in srgb, var(--melt) 50%, transparent) 43%,
      color-mix(in srgb, var(--melt) 32%, transparent) 50%,
      color-mix(in srgb, var(--melt) 16%, transparent) 57%,
      color-mix(in srgb, var(--melt) 6%, transparent) 62%,
      transparent 66%),
    linear-gradient(rgba(0, 0, 0, var(--footer-wash, 0.15)), rgba(0, 0, 0, var(--footer-wash, 0.15)));
}
/* 03.09 (Chiara: "there is a gap under the testimonial that needs to be filled"): the closing run
   — testimonial, handoff, CTA — is now all one dark ground, so their section paddings stopped
   reading as separation between blocks and started stacking into dead space: 120px off the bottom
   of .cs-voice plus 90px off the top of .cs-handoff = ~210px of empty near-black between Liv's
   name and "NEXT". Collapse the join. They are the same colour; nothing needs to be held apart. */
/* 03.09 (Chiara's 10.13 screenshot: "add a margin to the bottom of this image"): inside .cs-stack,
   consecutive media get margin-top: var(--gutter). .cs-voice sits OUTSIDE that container, so it
   never picked the rule up and butted straight against the last image. Same value, so the break
   matches every other media-to-media gap on the page. */
body.case-study .cs-stack + .cs-voice { margin-top: var(--gutter); }
body.case-study .cs-voice { padding-bottom: var(--space-section); }   /* 25.09 (Chiara: "move these [prev/next] down a bit — a bit more space after the testimonial"): 2xl (96) → section (120 at desktop), the next rung up — and now the same value as the voice's own top padding, so the block sits symmetric. Ladder: 64 → 96 → 120. · 03.09 (Chiara, after the collapse: "more space after the testimonial before the next/previous project"): xl (64) was too tight; 2xl is the middle rung between the 64 collapse and the 210 dead space */
body.case-study .cs-voice + .cs-handoff { padding-top: var(--space-lg); }   /* 25.09 (Chiara, second ask in the hour: "can we move these down a bit?" after 96 → 120 on the voice): the join takes 40 back — 120 + 40 = 160 between the client's name and PREVIOUS/NEXT. The next token rung on the voice alone would be 3xl (210), the ~210 dead band she asked to fill on 03.09, so the step is a second token at the join, not a leap. Ladder: 64 → 96 → 120 → 160. */
body.case-study .cs-voice { box-shadow: 0 1px 0 var(--ground-3); }
/* 03.09 — THE CLOSE'S DESCENT, SITE-WIDE (Chiara: "the video always needs to be revealed in the footer by
   a dark fade of the section above"). The homepage wall's device on every interior page's close: the run
   above the footer rides ONE scroll-driven ground (main.js sets --cx1/--cx2 on <body>), paper → taupe →
   near-black, so every element in it is the same colour at every instant and no seam can exist — and it
   always ENDS on the dark the film melts up into. Defaults are 1 (paper): right before the first scroll.
   History: Palmer first (close-light, static paper, for an hour; then close-drift, opt-in); Odisi likewise;
   then the rule. The opt-in classes are gone — this is what a close IS now. body.close-dark opts a page OUT
   (a close that is already dark, e.g. a full-bleed deck): no drift, dark clearance, dark melt. */
body.case-study .cs-stack, body.case-study .cs-voice, body.case-study .cs-handoff,
.close-rider,
body.page-interior:not(.case-study) .above-footer::after, body.home .above-footer::after {
  background: color-mix(in srgb, var(--paper) calc(var(--cx2, 1) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--cx1, 1) * 100%), var(--ground-3)));
  color: color-mix(in srgb, var(--ink) calc(var(--cx2, 1) * 100%), var(--cream));
}
body.page-interior .footer-scrim, body.home .footer-scrim { --melt: var(--ground-3); }   /* every close ends dark, so every film melts up into the dark */
/* the reveal clearance (36vh + xl of empty ground the mask dissolves through) must ride the run too — on a
   case study it is the nav's own padding; elsewhere it moves off the page wrapper (fixed paper) onto a spacer */
body.page-interior:not(.case-study) .above-footer, body.home .above-footer { padding-bottom: 0; }
body.page-interior .work-main.above-footer { padding-bottom: var(--space-lg); }   /* 10.09 (Chiara: "a lot more [room] further down" → then "reduce the spacing after Storia"): the grey block she saw was the reveal rider, now invisible on the dark ground — so the list needs only the lg rung of its own before the rider's 36vh clearance carries it into the film */
body.page-interior:not(.case-study) .above-footer::after, body.home .above-footer::after { content: ''; display: block; height: calc(36vh + var(--space-xl)); }
body.page-interior.close-dark .above-footer::after { background: var(--gallery); }   /* /work/: the deck ends on --gallery (#1E1917), not --ground-3 — measured. Specificity matched to the rider rule above so this wins. */
body.page-interior.close-dark .footer-scrim { --melt: var(--gallery); }
/* case studies: no fixed-colour strip anywhere between the last frame and the nav — the 32px gutter gap
   that sat OUTSIDE both sections (a margin, showing the wrapper's paper) becomes padding INSIDE the stack,
   the fractional-pixel seam cover is retired (the sections are one colour) and the nav overlaps the voice by 1px */
body.case-study .cs-stack { padding-bottom: var(--gutter); }
body.case-study .cs-stack + .cs-voice { margin-top: 0; box-shadow: none; }
body.case-study .cs-voice + .cs-handoff { margin-top: -1px; }
/* riders whose hairlines were drawn in fixed ink would vanish on the dark — draw them in the current colour */
.close-rider .faq-list details, .close-rider .faq-list details:last-child { border-color: color-mix(in srgb, currentColor 25%, transparent); }
.close-rider .method-title:not(.method-title--over), .close-rider .method-deliv,
.close-rider .method-value::before, .close-rider .method-deliv::before { color: inherit; }   /* /method/: the pinned panel's title and deliverables list were set in fixed ink — they follow the run now; the clipped over-image title keeps its cream */

/* 03.09 (Chiara: "there is a gap that appears here"): .above-footer masks its own bottom 36vh to
   transparent so the footer film never arrives behind a hard edge. On the homepage that band falls
   inside the empty runway. On a case study the CTA is the LAST thing in <main>, so its line and
   Start a Project sat inside the dissolve and faded into the film. Clear the mask depth so only
   empty ground fades. */
body.case-study .cs-handoff { padding-bottom: calc(36vh + var(--space-xl)); }

/* 03.09 (Chiara: "why is this so big — revert it to the size it was before"): the ask inherited the
   HOMEPAGE's sizing when it moved onto the film. There the statement is three deliberate lines at
   --type-phase (64px) and the CTA takes the --large rung; here it is one line that used to sit at
   --type-serif-lg (44px) with the standard 14px CTA. Back to the case-study values. */
body.page-interior .footer-ask-statement { font-size: var(--type-serif-lg); max-width: 780px; }   /* 03.09 (Chiara: "rolled out across everything"): was body.case-study — every interior page now carries the ask on the film at the case-study sizing; only the homepage keeps its 64px three-liner */   /* 03.09 (Chiara: "cap the text box at 600" → "780, needs to be 2 lines"): was uncapped, running the full gutter width (1351px at 1440) on one line; 780 puts it on two centred lines. CASE STUDIES ONLY — the homepage statement carries hard breaks at 64px and a cap there split its three lines into five. */
body.page-interior .footer-ask .cta--large { font-size: var(--type-label); }   /* 03.09: body.case-study -> body.page-interior with the roll-out */

/* 03.09: a hairline between the nav and the CTA was tried here and pulled straight back out
   (Chiara: "hmm maybe that doesnt work"). Kept in the session history, not in the file. */
/* CASE-STUDY NAVIGATION (03.09, Chiara: "do we need a clearer next case study / previous case
   study button") — PREV + NEXT, replacing a single centred "Next" that had no arrow, no hover
   affordance and no previous. Two links on the page's own gutter axis, so they land on the same
   edges as everything above them rather than floating in the centre. */
.cs-handoff {
  padding: calc(var(--space-section) * 0.75) var(--gutter);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-lg);
  align-items: start;
}
.cs-nav-link { display: grid; gap: var(--space-xs); align-content: start; }
.cs-nav-link--prev { justify-items: start; text-align: left; }
.cs-nav-link--next { justify-items: end; text-align: right; }
.cs-nav-dir {
  display: inline-flex; align-items: center; gap: var(--space-xs);
  opacity: 0.6;
  transition: opacity var(--dur-quick) var(--ease-house);
}
.cs-nav-name {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-lg);
  line-height: 1.1;
  text-wrap: balance;
}
/* the arrow is the affordance — it travels on hover, which is the one decisive gesture rather than a colour change */
.cs-nav-arrow { display: inline-block; transition: transform var(--dur-base) var(--ease-house); }
@media (hover: hover) and (pointer: fine) {
  .cs-nav-link:hover .cs-nav-dir { opacity: 1; }
  .cs-nav-link--next:hover .cs-nav-arrow { transform: translateX(6px); }
  .cs-nav-link--prev:hover .cs-nav-arrow { transform: translateX(-6px); }
}
.cs-nav-link:focus-visible { outline: 1px solid currentColor; outline-offset: 6px; }
/* 19.10: a NEXT FRAME close (the next project's deck image full bleed, name centred) was built on Necker's phone page
   and REVERTED within the hour — Chiara: "hate the footer with Palmer and Tom — revert to a clean design".
   20.10 (Chiara, with the gallery's bottom row as the reference: "make the next/previous very neat at the bottom —
   like how we've done this style"): on the phone the close is ONE ROW in the gallery's grammar — ( All Work ) as a
   quiet mono line, then two pills on the gutter in the toggle recipe: ← Previous left, Next → right. The project
   names are off on the phone; the arrows carry it. Desktop keeps the typographic prev/next. */
@media (max-width: 700px) {
  .cs-handoff.cs-handoff { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: var(--space-sm); padding: var(--space-2xl) var(--gutter); }   /* three columns: prev | ( All Projects ) centred | next — grid, after flex mis-measured the pills and pushed Next off the edge */   /* class doubled: the older ≤700 `grid-template-columns: 1fr` later in the file stacked the row */
  .cs-handoff .cs-nav-all { order: 1; grid-column: 2; justify-self: center; text-align: center; font-size: calc(var(--type-label) - 2px); white-space: nowrap; }   /* 20.10 (Chiara: "does it look like testimonial-next, not project-next?"): the link sits BETWEEN the pills and reads ( All Projects ) — the word in the middle names what the row moves between. Markup on all 13 pages: All Work → All Projects. */   /* grid-column: 2 — the desktop rule spans it 1 / -1, which gave it its own row */   /* .cs-handoff prefix: the base grid-column: 1 / -1 sits later in the file */
  .cs-handoff .cs-nav-link--prev { order: 0; grid-column: 1; }
  .cs-handoff .cs-nav-link { display: inline-flex; justify-self: start; align-items: center; height: 26px; padding: 0 14px; box-sizing: border-box;
    border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 200px; background: color-mix(in srgb, currentColor 9%, transparent); }
  .cs-handoff .cs-nav-link--next { order: 2; grid-column: 3; justify-self: end; justify-items: unset; text-align: left; }   /* outranks the older ≤700 `order: -1` that put Next first */
  .cs-nav-name { display: none; }
  .cs-nav-dir { opacity: 0.85; font-size: 11px; gap: var(--space-xs); }
}
@media (max-width: 700px) {
  /* two long project names side by side get very narrow on a phone — stack, prev under next */
  .cs-handoff { grid-template-columns: 1fr; gap: var(--space-xl); }
  .cs-nav-link--next { order: -1; justify-items: start; text-align: left; }
}

/* 15.07 (Chiara): the pre-footer ask — serif line + the type-only CTA */
.cs-cta {
  padding: calc(var(--space-section) * 0.75) var(--gutter);
  text-align: center;
}
.cs-cta-line {
  font-family: var(--font-serif);
  font-weight: 300;
  font-size: var(--type-serif-md);
  line-height: 1.4;
  max-width: 26ch;   /* 03.09 (Chiara: "this is too wide too"): 34ch -> 26ch, a tighter stack under the FAQ */
  margin: 0 auto var(--space-lg);
}
@media (max-width: 900px) {
  .cs-hero-screen--intro { grid-template-columns: 1fr; }
  .cs-intro-lead { grid-column: 1; }
  .cs-hero-meta { flex-wrap: wrap; gap: var(--space-md) var(--space-lg); }
  .cs-hero-scroll { display: none; }   /* the row wraps on small screens — the cue loses its right-edge seat */
  /* 19.10 (Chiara, Necker on the phone: "placement of ( Industry ) / ( Scope ) / ( Visit ) looks weird — how do we
     balance it?" · "The Thinking — do we right-align everything?"): ONE AXIS per screen. The hero intro screen is
     right-aligned — the lead already is (15.07), so the meta stacks as one column on the same right edge instead of
     wrapping as an uneven row. The narrative rows go to one column, label above a LEFT-aligned paragraph: right-set
     body copy is hard to read at phone widths; right alignment is for short leads. */
  /* 19.10 later (Chiara: "looks basic — can we change"): the intro screen as ONE BLOCK, not a lead at centre and a mono
     stack in the corner. Lead and meta sit together in the lower third on the right edge; the meta is a credits list
     — label and value on one line, the label dimmed — an end card over the film. */
  .cs-hero-screen--intro { display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-end; gap: 0; padding: 10px var(--gutter) calc(3svh + 10px); top: calc(100svh - var(--space-lg)); height: calc(100svh + var(--space-lg) - 20px); }   /* v5 (Chiara: "reduce the padding top and bottom of that section — by 10px"): the full second screen stays (v5 content-height version tried and reverted in the same minute); the box is 20px shorter and the lead's centring room shrinks 10px each side · v4 (Chiara: "move it to the centre of that section, between the title above and the tabs"): the lead takes margin auto, so it CENTRES in the room between the screen's head (the credits line sits just above the seam) and the meta, which now ends the flow 3% above the foot */
  .cs-hero .cs-intro-lead { margin: auto 0; }   /* v3 ("split the difference" on the lead's placement): the block now hangs from the TOP of the second screen — 72px under it, i.e. ~155px under the credits line on every page (the midpoint of the 112 and 196 Chiara saw) — instead of floating up from the foot, where a shorter lead moved it. The meta stays pinned 3% above the foot. */   /* foot padding = the meta's 3% offset + 3xl, so the lead clears the pinned meta by ~a md */
  .cs-hero { height: 200svh; }   /* (Chiara: "too much space between the ( … ) line and the lead — reduce the spacing"): the second screen starts at 65svh instead of 100 (60 tried: "a bit more space"; 70: "split the difference"), so the intro block (seated at its foot) begins ~200px under the credits line; the runway shortens with it — 200 → 170svh */
  /* (Chiara: "should we align these?"): yes — END-CREDITS columns. Labels right-aligned to a shared gutter, values
     left-aligned from it, the block anchored right. display: contents lets the dl's <div> pairs sit on one grid. */
  /* → "this looks off": the end-credit columns REVERTED — over an image the shared gutter sat mid-frame and both edges
     went ragged. Each pair on one line, label dimmed, the block flush RIGHT so it shares the lead's edge. */
  /* → "how can we improve": TWO VOICES, as the deck — the value in the serif at the lead's small rung, the label a tiny
     dimmed mono caption without the brackets (a headline idiom, not a caption one). Quieter: facts under a thought. */
  .cs-hero-meta { position: static; margin-top: 0; display: flex; flex-direction: column; align-items: flex-end; text-align: right; gap: var(--space-sm); }   /* (Chiara: "these can sit at the bottom of the hero image, offset from the bottom by 3%"): the meta pins to the foot of the intro screen, 3% of the viewport up; the lead keeps its seat above (its 140px foot padding clears the block) */
  .cs-hero-meta > div { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
  .cs-hero-meta dt { margin: 0; font-size: 10px; letter-spacing: 0.08em; opacity: 0.55; }
  .cs-hero-meta dt::before, .cs-hero-meta dt::after { content: none; }
  .cs-hero .cs-hero-meta dd.label { color: var(--cream); }   /* → (Chiara: "the font can stay the same — the monos"): the serif value REVERTED; values stay in the 12px label mono, caps. Only the structure (label over value, no brackets) and the dimmed caption label are kept. */
  .cs-hero .cs-hero-meta dd a { color: inherit; }
  /* the making-of credits take the same columns, left-anchored under the strip (Necker) */
  .cs-credits { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--space-sm); margin: var(--space-xl) var(--gutter) 0; font-size: calc(var(--type-label) - 2px); }   /* (Chiara: "adjust the position" → "centre it?"): CENTRED under the strip, the hero meta's stacked grammar — role dimmed above the name */
  .cs-credits > div { display: flex; flex-direction: column; align-items: center; gap: 2px; }
  .cs-credits dt { opacity: 0.55; }
  .cs-hscroll-nav { display: none; }   /* (Chiara: "Back and Next can be smaller" → "on mobile remove the Back and Next buttons"): the strip swipes natively on the phone; the buttons were desktop furniture */
  .cs-credits dt { margin: 0; }
  .cs-hero-meta > div.cs-hero-scroll { display: none; }   /* the credits-row rule above outranked the cue's hide */
  /* (Chiara: "increase the opacity on the hero image"): the phone hero takes the --deep wash (0.20 → 0.34 flat) on every
     case, plus a deeper foot — the lead and credits sit in the lower third over sand, sea, sky; desktop keeps its dial */
  .cs-hero-media.cs-hero-media::after {
    background: linear-gradient(rgba(0, 0, 0, 0.34), rgba(0, 0, 0, 0.34)),
                linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
                linear-gradient(to top, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.3) 30%, transparent 55%),
                rgba(0, 0, 0, 0.18);
  }
  .cs-stack-text { grid-template-columns: 1fr; row-gap: var(--space-sm); }
  body.case-study .cs-stack-text p { text-align: left; }
  body.case-study .cs-stack-text p:not(.label) + p:not(.label) { margin-top: var(--space-xs); }   /* 19.10 (Chiara, Palmer's Thinking: "paragraph break? text looks heavy"): the paragraphs already carry a sm; + xs = a full md (24px) between them on the phone */   /* the paragraph carries its own right-set from the desktop offset grid (a :not(.label) rule) — body.case-study outranks it */
}
/* 14.07: Hustl has no photography yet — plain near-black ground until real
   frames land (swap for an <img> then). Logo removed same day (Chiara) —
   the title already names it. */
.hw-media--hustl { background: var(--gallery); }
/* 10.07 v2.2 (Chiara: "can we add an overlay"): scrim deepened — a flat wash
   plus a bottom gradient so the title AND the corner furniture (brand label,
   tag, NEXT) always sit on solid ground, whatever the footage. */
.hw-slide::after {
  content: ''; position: absolute; inset: 0;
  /* 14.07 v2 (Chiara: "do we add a darker overlay top and bottom"): a top
     gradient joins the bottom one — header + title sit on held ground at
     both edges; the flat wash eases back so the image mid-field stays alive */
  background: linear-gradient(rgba(0, 0, 0, 0.10), rgba(0, 0, 0, 0.10)),   /* 16.07 (Chiara: "darker overlay on the case study on the homepage"): +10% flat over the deck slides; this layer is the dial */
              /* per-slide deepening: see the LHC rule after this block */
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%),
              rgba(0, 0, 0, 0.18);
}
/* 13.08 (Chiara: "For the finest hotels of the world — this image needs a darker
   overlay", then "same for zambala"): LHC + Zambala's flat wash deepens
   0.10 → 0.25; gradients + base unchanged. Bright mid-frames (hearth glow /
   Lembongan sky) sit right behind the frosted title + byline. */
.hw-slide[data-project="zambala"]::after,
.hw-slide[data-project="lhc"]::after {
  background: linear-gradient(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.25)),
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%),
              rgba(0, 0, 0, 0.18);
}
/* 13.08 v2 (Chiara: "lhc needs a darker overlay in the centre so we can read
   the byline", "same for hustl"): a CENTRE BAND joins the stack on these two —
   feathered hold across the title + byline zone (solid 46–66% of the frame,
   the byline seats at ~58–62%), easing out before the edge gradients take
   over. Supersedes "the mid-field stays alive" for these slides only; LHC
   keeps its 0.25 flat wash, Hustl inherits it here too. */
.hw-slide[data-project="lhc"]::after,
.hw-slide[data-project="hustl"]::after {
  background: linear-gradient(rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.25)),
              linear-gradient(to bottom, transparent 30%, rgba(0, 0, 0, 0.28) 46%, rgba(0, 0, 0, 0.28) 66%, transparent 82%),   /* the centre hold — this layer is the dial */
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%),
              rgba(0, 0, 0, 0.18);
}
.hw-title {
  position: absolute; left: var(--gutter); right: var(--gutter);
  top: 50%;   /* 14.07 v2 (Chiara, Stud "Shoutt" screenshot: "this placement"): dead centre — supersedes the same-day 38.2% golden-section trial */
  transform: translateY(-50%); text-align: center; z-index: 2;
  font-family: var(--font-serif); font-weight: 300;
  font-size: clamp(44px, 8vw, 92px);   /* 12.10 (Chiara, on /work/?view=grid: "I like the size of the font of the client names on this page"): the homepage deck takes the gallery's 92px step, so the two decks match. Was clamp(56px, 10.7vw, 123px) (v2 display ceiling · 16.07: 9 → 10.7vw). */
  line-height: 1;
  color: var(--cream);          /* 14.07 v3 (Chiara: "turn off the effect on the text inverting i dont like it"): solid cream on the scrim — difference blend retired on the deck */
}
/* 14.07 v4 (Chiara: "add a transparency to the palmer studio text like a
   frosted"): translucent cream fill on the title over imagery — the photo
   ghosts through the strokes, frosted-glass read. Scoped to Palmer (the one
   slide with a photo); empty-ground slides keep the solid cream. */
.hw-slide[data-project="palmer"] .hw-title,
.hw-slide[data-project="lhc"] .hw-title,
.hw-slide[data-project="odisi"] .hw-title,   /* 13.08 (Chiara: "Palmer's got the inverted thing"): Odisi + Zambala joined the frosted rule when their films landed — per the 16.07 ruling, frosted wherever a title sits on imagery. */
.hw-slide[data-project="zambala"] .hw-title,
.hw-slide[data-project="hustl"] .hw-title,   /* 13.08 (Chiara: "Hustl title needs more of the text effect"): joined when the Necker image landed — all five deck titles now frosted */
.hw-slide[data-project="beyond-by-kim"] .hw-title,   /* 13.08: work-index deck mirror — Beyond sits on imagery too. Alquemie stays solid cream (no media yet). */
.cs-hero .hw-title,
.cs-hero .svc-title,   /* 03.08: the /virtuoso landing hero — svc-title on cs-hero mechanics */
.svc-hero--media .svc-title,
.method-intro .svc-title {   /* 12.10 (Chiara: "The Aura Method™ — the image underneath needs to show more, like the case study images"): the Method hero joins the frost */
  color: var(--frost);   /* 16.07 (Chiara: "I love the text colour over Palmer — apply to all hero image titles"): the frosted rule goes site-wide wherever a title sits on imagery */
}
/* 14.07 (Chiara: "the project name isn't centred"): the index is OUT OF FLOW
   so the name alone defines the centre — the sup was adding width on the
   right and shoving the name half its width off-axis. top ≈ the cap line. */
/* 14.07: the 001/grotesque production-log treatment was tried on the deck
   indices and REVERTED same hour (Chiara: "the case studies should stay as
   they were") — deck sups are 01–05 in the title's own Eiko. The ROUTER
   keeps the 001/grotesque treatment. */
.hw-title sup { font-size: 0.2em; position: absolute; top: 0.3em; margin-left: 12px; opacity: 0.75; }
.t-short { display: none; }   /* 25.10: the phone-only short case-study names (LHC) — shown in the ≤700 block */
/* 14.07 v3 (Chiara: "this font is bigger than the rest"): LHC keeps its TWO
   explicit lines, but steps down so the block's optical mass matches the
   one-line siblings — at the full 123px, "Luxury Hospitality" ran ~40% wider
   than "Palmer Studio" AND doubled in height. 92px puts its longest line at
   roughly Palmer's width. (Index rides line 1's cap via the inline sup.) */
.hw-slide[data-project="lhc"] .hw-title,
.hw-slide[data-project="beyond-by-kim"] .hw-title { font-size: clamp(44px, 8vw, 92px); }   /* 16.07: the sweep · 03.08 (Chiara: case-page size is the baseline) — Beyond's two-line deck slide steps down to match its case hero, same as LHC */
/* 10.07 v2 (Chiara): the disciplines tag speaks in the serif voice now —
   Eiko at serif-sm (was the 13px Neue label); the &ensp; in the markup opens
   the air inside the brackets. */
.hw-tag {
  position: absolute; left: 0; right: 0; bottom: var(--space-lg); text-align: center;
  opacity: 0.85; z-index: 2;
  /* 14.07 v4 (Chiara: "back to eiko font and bigger"): the mono trial on the
     services line reverted — Eiko at the φ rung again; the corners + labels
     stay mono. The &emsp; in the markup keeps the wide-bracket house style. */
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-hw-tag);
  color: var(--cream);          /* 14.07 v3: inversion off, matches the title */
}
/* 10.07 v2 (Chiara): bottom-left corner furniture — BRANDS WE'VE BUILT in the
   Neue label voice; mirrors the NEXT thumb's seat on the right. Lives in every
   slide so the wipe carries it invisibly (same text, same spot). */
.hw-brand {
  position: absolute; left: var(--gutter); bottom: var(--space-lg);
  color: var(--cream); opacity: 0.85; z-index: 2;
  font-family: var(--font-mono);   /* 15.07 (Chiara): corners join the GT America Mono trial via the token (arc: Roboto → PP Mono Book "thinner" → GT) */
  font-weight: 400;
  font-size: var(--type-hw-corner);
  letter-spacing: normal;   /* mono carries its own rhythm — the 0.09em grotesk tracking gaps it out; MOF sample runs untracked */
}
.hw-next {
  position: absolute; right: var(--gutter); bottom: var(--space-lg); z-index: 2;
  display: flex; flex-direction: column; gap: 8px; color: var(--cream);
  font-family: var(--font-mono);   /* 15.07: GT trial via the token, matches .hw-brand */
  font-weight: 400;
  font-size: var(--type-hw-corner);   /* 14px, matches .hw-brand */
  letter-spacing: normal;
}
.hw-next img { width: 110px; height: 74px; object-fit: cover; border-radius: 6px; display: block; }
.hw-next a { color: var(--cream); border-bottom: 1px solid color-mix(in srgb, var(--cream) 45%, transparent); }
.hw-link { position: absolute; inset: 0; z-index: 1; }
/* 15.07 (Chiara: "make the case studies link"): the title/tag layers sit at
   z2 ABOVE the full-slide link and were eating clicks dead-centre — they're
   text, not controls; clicks fall through to the link. */
.hw-slide .hw-title, .hw-slide .hw-tag, .hw-ticker { pointer-events: none; }
@media (max-width: 760px), (prefers-reduced-motion: reduce) {
  .hw { height: auto; }
  .hw-pin { position: static; height: auto; overflow: visible; }
  .hw-slide { position: relative; height: 82vh; clip-path: none; transition: none; }
  /* static mode: the text is simply there — no entrance to wait on */
  .hw-slide .hw-title { opacity: 1; transform: translateY(-50%); filter: none; transition: none; }
  .hw-slide .hw-tag,
  .hw-slide .hw-byline,
  .hw-slide .hw-brand { opacity: 0.85; transform: none; transition: none; }
  .hw-slide .hw-next { opacity: 1; transform: none; transition: none; }
  .hw-slide .hw-media { transform: none; filter: none; transition: none; }
  .hw-title--roll .hw-title-word { transform: none; transition: none; }   /* 15.07: the word-roll entrance is motion — static mode shows it seated */
  .hw-ticker { display: none; }
  .hw--ticker .hw-slide .hw-tag { display: block; }
}
/* 23.10 THE PHONE HOME DECK (Chiara: "Brands We’ve Built needs the grow effect you have on desktop, to bring it to full
   screen, and it should be the vph"): the homepage deck LEAVES static mode on phones — pinned at 100svh, the mask grows
   from the contained frame to full-bleed on approach (--grow, main.js), then the four up-wipes on the same scroll budget
   as the desktop (5.4 screens). The static block above still seats the text (no entrances to wait on). /work's phone
   deck is untouched. */
@media (max-width: 760px) and (prefers-reduced-motion: no-preference) {
  body.home .hw.hw--grow { height: calc(100svh * 5.4); }
  body.home .hw--grow .hw-pin { position: sticky; top: 0; height: 100dvh; overflow: hidden; }   /* 25.10 v2 (Chiara: "the case studies aren’t centred and the information at the bottom is cut off"): lvh made the pin TALLER than the visible screen while Safari’s bars were showing — the centred title sat low and the services line + Next went behind the bottom bar. dvh tracks the screen as it actually is, bars or no bars: centred, nothing cut, no strip. v1: 100svh is the screen WITH Safari’s toolbars — once they collapse on scroll the screen grows and a dark band opened under the slide (the sound toggle sat in it). lvh is the full screen with the bars away, so the slide always reaches the bottom; with the bars showing, the overhang sits behind them */
  body.home .hw--grow .hw-slide { position: absolute; inset: 0; height: auto; clip-path: inset(100% 0 0 0); transition: clip-path var(--dur-slow) cubic-bezier(0.76, 0, 0.19, 1); }
  body.home .hw--grow .hw-slide.shown { clip-path: inset(0 0 0 0); }
}
/* 19.10 THE PHONE DECK (Chiara: "mobile looks so bad for gallery" → "full bleed… and maybe we have arrows" → "build"):
   the /work gallery under 760px is a native horizontal SWIPE — one full-bleed frame per project, scroll-snap, the
   OS's own momentum; the same left-swipe "deck" as the desktop, by thumb. A mono counter ( 01 / 13 ) bottom-left is
   the discoverability; two mono arrows bottom-right are secondary furniture for people who tap rather than swipe,
   and they mirror the desktop deck's NEXT corner. The frame is sized to the phone, so the name and the ( ) line
   fit; the services line may wrap. The captioned stack tried earlier today (image, words under it) is RETIRED —
   Chiara: "that looks terrible". The homepage deck keeps its stacked static frames. work.js drives the counter and
   the arrows. The scroll-driven desktop engine stays off here (main.js staticMode). */
@media (max-width: 760px) {
  .work-main .hw.hw { height: auto; padding: 0; margin-inline: calc(-1 * var(--gutter)); position: relative; }   /* .hw.hw outranks 5425's 2100vh run is the desktop scroll budget — not on a phone */
  .work-main .hw-pin {
    position: relative; height: 100svh; overflow-x: auto; overflow-y: hidden;
    display: flex; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; overscroll-behavior-x: contain;
  }
  .work-main .hw-pin::-webkit-scrollbar { display: none; }
  .work-main .hw-slide { position: relative; flex: 0 0 100%; width: 100%; height: 100%; overflow: hidden; scroll-snap-align: start; scroll-snap-stop: always; clip-path: none; }
  /* 19.10 later (Chiara: "the text to have the same animation as on desktop"): the static-mode rules above seat every
     title/byline/tag permanently; here they are released so the desktop entrance runs on ARRIVAL — work.js marks
     the snapped frame .shown (and un-marks the rest, so a swipe back replays it). The swipe itself stays native: no
     slide transition, the frame moves under the thumb. Reduced motion keeps the static seat (the block below). */
  .work-main .hw-slide .hw-title--roll .hw-title-word { transform: translateY(112%); transition: transform var(--dur-slow) var(--ease-house) 0s; }
  .work-main .hw-slide.shown .hw-title--roll .hw-title-word { transform: translateY(0); transition-delay: 0.2s; }
  .work-main .hw-slide .hw-tag, .work-main .hw-slide .hw-byline { opacity: 0; transform: translateY(14px); transition: opacity var(--dur-slow) var(--ease-house) 0.25s, transform var(--dur-slow) var(--ease-house) 0.25s; }
  .work-main .hw-slide.shown .hw-tag, .work-main .hw-slide.shown .hw-byline { opacity: 0.85; transform: translateY(0); transition-delay: 0.75s; }
  /* 19.10 later (Chiara: "feels a bit flat on mobile"): the desktop's CAMERA SETTLE on arrival — the frame parks a
     touch tight and dim, then eases out to full over the cinema rung as the title rolls. Phone-scaled: 1.08 not 1.16,
     no lateral drag (the swipe already supplied the movement). */
  .work-main .hw-slide .hw-media { transform: scale(1.08); filter: brightness(0.7); transition: transform var(--dur-cinema) var(--ease-house), filter var(--dur-cinema) var(--ease-house); }
  .work-main .hw-slide.shown .hw-media { transform: scale(1); filter: brightness(1); }
  /* 19.10 later (Chiara: "bump the client name font… 10px more"): the phone names run on vw so they hold on a 360
     as on a 430 — 14.3vw = 56px at 390 (was the 46px floor). Measured one-line ceilings at 390 cap the four that
     cannot take 56: Beyond by Kim 52, LMDM Retreats 49, Wagner Bespoke (two lines) 46, Tom Private Chef 46; LHC's
     two-line name tops out at 41. Each slide is seen alone, so the sizes may differ — the desktop deck already does. */
  .work-main .hw-slide[data-project] .hw-title.hw-title { font-size: 11vw; }   /* → "happy medium" → "Wagner and LHC use 41 and the others are 2px bigger": one-line names 43px at 390, the two stacked names 41 (the ceiling for "Luxury Hospitality" on one line) */
  .work-main .hw-slide[data-project="wagner-bespoke-travel"] .hw-title.hw-title,
  .work-main .hw-slide[data-project="lhc"] .hw-title.hw-title { font-size: 10.5vw; }   /* 19.10 later (Chiara: "they should all be the same"): ONE size — 46px at 390, the largest at which every one-line name holds (Tom Private Chef is the limit). Arc today: 56 → 51 → 47 → 46-for-all; the per-project caps are retired. */

  .work-main .hw-slide .hw-byline { font-size: calc(var(--type-label) - 2px); }
  .work-main .hw-slide[data-project] .hw-byline.hw-byline { top: calc(50% + clamp(22px, 4vw, 46px) + 28px + var(--space-xs)); }   /* (Chiara: "increase the line space between the project name and the bylines"): the phone byline seats 8px lower under the one-line names — 36px below the title's centre-line offset */
  .work-main .hw-slide[data-project="wagner-bespoke-travel"] .hw-byline.hw-byline,
  .work-main .hw-slide[data-project="lhc"] .hw-byline.hw-byline { top: calc(50% + clamp(44px, 8vw, 92px) + 28px + var(--space-xs)); }   /* the two-line names, same +8 */   /* 19.10 later (Chiara, on Odisi's "A new frontier of island travel": "reduce this by 1px… or maybe 2px"): the phone byline at label − 2 = 12px, every slide alike (hw-corner's own label − 1 formula, one step further) */
  .work-main .hw-slide .hw-tag { bottom: calc(var(--space-lg) + 44px); padding: 0 var(--space-xs); font-size: var(--type-serif-sm); line-height: 1.3; }   /* seats above the nav row · 19.10 later (Chiara: "( ) these bits — smaller font so they sit on one line"): the sm rung (19px) on an xs inset; the two longest credit lines (Tom, Beyond) still take two lines at 390 */
  .work-main .hw-slide.hw-slide::after {   /* .hw-slide doubled — the desktop scrim near the file end shares the specificity · the phone's bottom row (counter, pill, arrows) sits on the lowest 60px — over sand or sea it vanished; the foot of the scrim deepens and climbs so the controls hold on any frame. Flat wash, top and centre unchanged. */
    background: linear-gradient(rgba(0, 0, 0, 0.36), rgba(0, 0, 0, 0.36)),
                linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
                linear-gradient(to top, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.3) 22%, transparent 42%),
                rgba(0, 0, 0, 0.18);
  }
  /* THE ASK FRAME (19.10, Chiara: "what about the CTA?"): the 14th phone frame — the footer's film still, statement and
     CTA centred in the frame. Hidden everywhere else (rule after the block). The CTA's letter-roll arrives on the
     main.js intersection gate, as every rolled CTA does. */
  .work-main .hw-frame--ask { display: flex; position: relative; flex: 0 0 100%; width: 100%; height: 100%; overflow: hidden; scroll-snap-align: start; scroll-snap-stop: always; }
  .work-main .hw-frame--ask::after { content: ''; position: absolute; inset: 0; background: rgba(0, 0, 0, 0.42); }   /* a flat wash — the statement and CTA sit on the film still */
  /* (Chiara: "the hover-over VIEW button won't work on mobile — let's change that"): the frames are links, but touch has
     no cursor to say so. A mono "View project ↗" under the byline — the cursor's words, seated in the frame — enters
     with the byline. Decorative (CSS content); the full-frame link already carries its label for assistive tech. */
  .work-main .hw-slide .hw-byline::after { content: 'View project \2197'; display: block; margin-top: var(--space-sm); font-size: calc(var(--type-label) - 2px); opacity: 0.7; }
  .work-main .hw-ask {
    position: relative; z-index: 2; margin: auto; padding: 0 var(--gutter); text-align: center;
    display: flex; flex-direction: column; align-items: center; gap: var(--space-md);
  }
  .work-main .hw-ask-statement { margin: 0; font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.25; color: var(--cream); text-wrap: balance; }
  .work-main .hw-ask .cta { color: var(--cream); }
  .work-main .hw-nav {
    position: absolute; left: var(--gutter); right: var(--gutter); bottom: var(--gutter); z-index: 3;   /* 19.10 later (Chiara: "move the 01/13 and arrows down"): on the gutter — the same 16px inset as the header corners */
    display: flex; align-items: center; justify-content: space-between; color: var(--cream);
  }
}
@media (max-width: 760px) and (prefers-reduced-motion: reduce) {
  .work-main .hw-slide .hw-title--roll .hw-title-word, .work-main .hw-slide .hw-tag, .work-main .hw-slide .hw-byline { transform: none; opacity: 0.85; transition: none; }
  .work-main .hw-slide .hw-title--roll .hw-title-word { opacity: 1; }
  .work-main .hw-slide .hw-media { transform: none; filter: none; transition: none; }
}
/* 19.10 later (Chiara: "remove the footer on mobile gallery view — it shouldn't scroll"): the phone gallery is ONE
   screen — the swipe is the only movement. The page locks to the viewport, the reveal footer and its spacer are
   out, and work-main drops the list's clearances. List view keeps the footer and scrolls as before. */
@media (max-width: 760px) {
  body:has(.work-main .work-index:not(.is-list)) { height: 100svh; overflow: hidden; }
  body:has(.work-main .work-index:not(.is-list)) .site-footer,
  body:has(.work-main .work-index:not(.is-list)) .footer-spacer { display: none; }
  body:has(.work-main .work-index:not(.is-list)) .work-main { padding: 0; margin: 0; min-height: 0; }
  body:has(.work-main .work-index:not(.is-list)) .work-main .hw.hw { margin-inline: 0; }   /* main has no padding here, so the full-bleed pull-out would push the deck 16px off the left edge and 32px too wide — the nav and the ( ) line lost their gutter */
  html:has(.work-main .work-index:not(.is-list)) { height: 100svh; overflow: hidden; }   /* iOS needs the root locked too, or it rubber-bands */
  body:has(.work-main .work-index:not(.is-list)) .above-footer { margin-bottom: 0; padding-bottom: 0; }   /* the reveal's clearance under main */
  body:has(.work-main .work-index:not(.is-list)) .above-footer::after { display: none; }
  body:has(.work-main .work-index:not(.is-list)) .work-main.above-footer { mask-image: none; -webkit-mask-image: none; }   /* the reveal MELT — main's bottom ~300px fade to let the footer through; on the one-screen gallery that is the counter, arrows and the ( ) line (sampled at ~15%). No footer here, no melt. */
  /* 19.10 later (Chiara: "the top looks very crowded"): in the phone gallery the List/Gallery pill leaves the header
     stack and joins the bottom row — centred between the counter and the arrows, thumb-reach, one tier at the top
     (the header) and one at the bottom (the controls). List view keeps it under the mark, where the row heads need it. */
  /* 27.10 (Chiara: "bad design for the list/gallery toggle to jump from top to bottom… it needs to stay where it is"): RETIRED — the phone gallery keeps the list view's seat at the top. Was: body:has(.work-main .work-index:not(.is-list)) .view-toggle { top: auto; bottom: calc(var(--gutter) + 5px); } */
}
.hw-frame--ask { display: none; }   /* the ask frame is the phone deck's close; the desktop deck closes on the footer */
/* The nav row exists only in the phone deck; desktop and the homepage never show it */
.hw-nav { display: none; }
.hw-count { opacity: 0.85; }
/* 19.10 later (Chiara: "the arrow circles are quite big — could they match the List/Gallery pill?"): ONE pill, the
   toggle's exact skin — 26px tall, hairline at 25% currentColor, 200px radius, 2px inset — holding the two arrows
   as its two halves, 11px mono like the toggle's labels. The 36px circles are retired. Each half keeps a 44px
   touch halo (::before) so the small pill is still easy to hit. */
.hw-arrows {
  display: flex; height: 26px; padding: 2px; box-sizing: border-box;
  border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-radius: 200px;
  background: color-mix(in srgb, currentColor 9%, transparent);
}
.hw-arrow {
  position: relative; background: none; border: 0; padding: 0 14px; cursor: pointer; color: inherit;
  font-family: var(--font-label); font-weight: 400; font-size: 11px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  opacity: 0.85; transition: opacity var(--dur-quick) var(--ease-house);
}
.hw-arrow::before { content: ''; position: absolute; inset: -9px 0; }   /* 44px touch target */
.hw-arrow:hover, .hw-arrow:focus-visible { opacity: 1; }
.hw-arrow[disabled] { opacity: 0.3; cursor: default; }
}

/* ============================================================
   28.08 THE BRIEF ORIGIN + THE DRIFT (homepage)
   ============================================================ */
/* The origin, one screen (Chiara: "reduce the font size… make that one paragraph"):
   the hero now carries the monument beat, so the claim, the answer and the credential
   compress into ONE paragraph on the serif-lg rung — a statement, not body copy — with
   Start a project and Crown's ( Scroll ) cue at its foot. Paper ground, not pinned; the
   Monument (08.08) survives behind ?monument. */
.origin-brief { position: relative; z-index: 2; background: var(--paper); color: var(--ink); }
.origin-slide { position: relative; min-height: 100svh; display: grid; align-content: center; justify-items: start; gap: var(--space-lg); padding: var(--space-section) var(--gutter); }   /* 28.08 v2 (Chiara: "2 scroll screens"): each paragraph owns a screen — slide one the claim + answer, slide two the credential + Start a project */
.origin-brief-text { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-lg); line-height: 1.25; letter-spacing: -0.005em; max-width: 30ch; margin: 0; text-wrap: pretty; }
.origin-brief .origin-cta { opacity: 1; }   /* the roll arms on visibility (main.js gatedCtas) — no crossfade layer to wait for here */
.origin-brief-cue { position: absolute; left: var(--gutter); bottom: var(--space-lg); margin: 0; opacity: 0.6; }
/* THE DRIFT: four photographs floating up past each other. A 12-column stage;
   each figure owns a column span and a top offset so no two share an edge, and
   rides the raw scroll on --drift-y (main.js updateDrift: rate = data-drift ×
   the figure's distance from the viewport centre). The rise (.rv) is its
   arrival; the drift is its life. */
.drift { position: relative; z-index: 2; background: var(--paper); color: var(--ink); padding: var(--space-section) var(--gutter) var(--space-3xl); display: grid; grid-template-columns: repeat(12, 1fr); column-gap: var(--space-md); row-gap: 0; }
.drift-label { grid-column: 1 / -1; margin: 0 0 var(--space-xl); opacity: 0.8; }
.drift-fig { margin: 0; overflow: hidden; transform: translateY(var(--drift-y, 0px)); will-change: transform; }
.drift-fig.rv { transform: translateY(calc(var(--drift-y, 0px) + 44px)); }   /* the house rise rides ON the drift — .rv's own transform would otherwise cancel it */
.drift-fig.rv.in-view { transform: translateY(var(--drift-y, 0px)); }
.drift-fig img { display: block; width: 100%; height: 100%; object-fit: cover; }
.drift-fig--1 { grid-column: 1 / 8; aspect-ratio: 3 / 2; }
.drift-fig--2 { grid-column: 9 / 13; aspect-ratio: 2 / 3; margin-top: 40vh; }
.drift-fig--3 { grid-column: 2 / 6; aspect-ratio: 2 / 3; margin-top: -10vh; }
.drift-fig--4 { grid-column: 7 / 12; aspect-ratio: 4 / 5; margin-top: 30vh; }
@media (max-width: 900px) {
  .origin-slide { min-height: auto; }
  .origin-brief-text { max-width: none; }
  .drift { display: grid; grid-template-columns: 1fr; row-gap: var(--space-xl); }
  .drift-fig, .drift-fig--1, .drift-fig--2, .drift-fig--3, .drift-fig--4 { grid-column: 1 / -1; margin-top: 0; transform: none; }   /* phones: one column, the rise only */
}
@media (prefers-reduced-motion: reduce) { .drift-fig { transform: none !important; } }

/* THE FOUR DISCIPLINES — ruled router rows */
.disc { padding: var(--space-3xl) var(--gutter); }
.disc-label { margin-bottom: var(--space-lg); opacity: 0.8; }   /* type comes from the .section-label mono register */
/* (15.07: the Crown fade/drift trials on the router TEXT are RETIRED —
   Chiara: "the text position was all fine… nothing is happening to the
   images, that's the thing i wanted changed". The Crown move lives on the
   pop-up preview IMAGES only.) */
.disc-row {
  display: block;
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-display);   /* 19.08 v7 (Chiara: "the font's too small… make that a bit bigger"): phase → display (80). Arc: clamp(44,7.7vw,89) → display (v3) → phase (v5, "a bit smaller") → display. */
  line-height: 1.1;
  padding: var(--space-md) 0;
  position: relative;
  transition: transform var(--dur-quick) var(--ease-house);
}
/* 14.08 (Chiara: "the lines for the what we do section animate in — softly
   reveal"): the hairlines are now DRAWN, not borders — each scales open from
   the left on the house curve, staggered a beat per row, when the router
   arrives (.lines-in, IO in main.js). RM sees them seated. */
.disc-row::before,
.disc-row:last-child::after {
  content: ''; position: absolute; left: 0; right: 0; height: 1px;
  background: color-mix(in srgb, currentColor 25%, transparent);   /* 24.08 (Chiara: "as we are scrolling the lines need to go to the cream colour"): was var(--ink) — currentColor rides the ground fade's ink → cream with the type (same value on paper, where color IS ink) */
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform var(--dur-slow) var(--ease-house);
}
.disc-row::before { top: 0; }
.disc-row:last-child::after { bottom: 0; transition-delay: 0.36s; }
.disc-row:nth-child(2)::before { transition-delay: 0.09s; }
.disc-row:nth-child(3)::before { transition-delay: 0.18s; }
.disc-row:nth-child(4)::before { transition-delay: 0.27s; }
.disc.lines-in .disc-row::before,
.disc.lines-in .disc-row:last-child::after { transform: scaleX(1); }
@media (prefers-reduced-motion: reduce) {
  .disc-row::before, .disc-row:last-child::after { transition: none; transform: scaleX(1); }
}
@media (hover: hover) and (pointer: fine) {
  .disc-row:hover { transform: translateX(12px); }   /* one decisive nudge — no fades */
}   /* 01.09: gated — the nudge latched on tap */
/* 14.07 (Chiara): editorial numbering on the router rows — the deck's
   superscript-index grammar. v2 same day: index on the LEFT of the word
   (Stud "01 / Journal" chapter grammar) with a generous column of air. */
.dr-in { display: contents; }   /* 25.10: the phone letter-roll wrapper (index.html) is layout-transparent on desktop — the index and the words stay direct flex items of the row, exactly as before; the ≤900 block makes it the roll mask */
.disc-row sup {
  font-size: 0.18em; vertical-align: super; margin-right: clamp(20px, 2.5vw, 44px); opacity: 0.6;
  font-family: var(--font-mono); font-weight: 400; letter-spacing: normal;   /* 14.07 v2 (Chiara): 001-format indices go Roboto Mono — spec-plate voice, matches the caption register */
}
/* 13.07 (Chiara): hovering a row floats that discipline's image over the
   right end of the rows — the menu-preview grammar reused (same assets,
   same crossfade); the .menu-preview-item--* classes carry the imagery. */
.disc { position: relative; }
.disc-preview {
  position: absolute;
  /* 15.07 (Chiara: "the images are close to the edge — centred to the
     space"): the box centres in the right half's air (between the page
     midline and the right gutter) instead of hugging the gutter */
  /* 19.08 v10 (Chiara: "the image goes in the gap, the testimonials underneath"): the preview is IN FLOW inside the voice column — a full-width seat between the timer line and the quote, always showing an image (rest = Branding's; the last hovered stays). The v3–v9 absolute seats and the story-fade retire. */
  --pv-w: 100%;
  position: relative; left: auto; right: auto; top: auto;
  max-width: min(100%, 44vh);   /* 27.08 (Chiara: "condense everything"): 64 → 44vh so the voice column (now with the pull-line) fits under the chrome line with the rows · v12/v16 (Chiara: "the image bigger?"): fills the column on normal screens; only short viewports shrink it (aspect kept) so the column still fits under the header line */
  margin: 0;   /* 24.08 (Chiara ruled the fix for the dead middle): the column's slack moves ABOVE the quote (.voices-seat margin-top: auto) — quote + image now close together at the foot, the air sits under the timer line. v13b's auto margin moved up a child; the seat's lg bottom margin is the fixed gap to this frame. */
  /* 27.08 (Chiara: "the hover over should show the images only… too much where they are"): the v10 always-on seat RETIRED — hidden at rest (opacity 0 below), Crown rise on hover, drops when the pointer leaves the rows (main.js). The seat still holds its space so the column doesn't jump. */
  /* 15.07 (Chiara, crowncreative.com/studio "What We Do" ref): the pop-up
     box arrives with Crown's fade-tile move — a long soft RISE (their spec:
     opacity 0 + translateY(150px) → seat, plain ease ~1.5s; ours runs 1.1s
     so the hover still feels answered). The -50% keeps the JS row-centring. */
  transform: translateY(calc(-50% + 150px));
  width: var(--pv-w);                      /* 15.07 (Chiara): +5% on the 14.07 "bigger" values */
  aspect-ratio: 580 / 326;   /* the measured menu frame's ratio */
  pointer-events: none;
  opacity: 0;
  /* 14.07 (Chiara: "move to the lines they are on"): top glides between rows */
  transition: opacity var(--dur-slow) var(--ease-house), transform var(--dur-slow) var(--ease-house), top var(--dur-base) var(--ease-house);
  z-index: 1;
}
.disc-preview.active { opacity: 1; transform: translateY(-50%); }
.disc-preview-item {
  position: absolute; inset: 0;
  opacity: 0;   /* 25.09: a 0.01 floor (pre-rasterise the waiting panes) was TRIED for an hour and reverted — the box itself is opacity 0 at rest, so the panes were never painted early anyway; the decode fix lives in main.js primeDiscPreview + the <img> panes */
  transition: opacity var(--dur-base) var(--ease-house);
  background-size: cover; background-position: center;
}
/* 15.07 (Chiara: "nothing is happening to the IMAGES — that's the thing i
   wanted changed"): the image is never still while it's up — a slow Crown
   drift through its frame (scale + rise inside the box), restarting on
   each activation. The box's own 150px arrival stays. */
.disc-preview-item.active { opacity: 1; }
/* 12.10 (Chiara, Safari: the What We Do hover "shows wrong video"): three of the four panes are <video> now, and WebKit
   keeps a video's compositing layer painted under an opacity-0 parent, so the top-most film showed on every row. Waiting
   panes are now also visibility:hidden (switched after the fade, so the crossfade still overlaps) and the active pane
   is lifted above its siblings. Chrome was already right; main.js pauses every film but the hovered row's. */
.disc-preview-item { visibility: hidden; transition: opacity var(--dur-base) var(--ease-house), visibility 0s linear var(--dur-base); }
.disc-preview-item.active { visibility: visible; transition-delay: 0s, 0s; z-index: 1; }
/* 15.07 v3 (Chiara: "too jumpy"): the zoom no longer restarts per row — ALL
   panes breathe in sync while the box is up (14s out, 14s back, forever),
   so row-switches are pure crossfades between identically-scaled images.
   Slow + noticeable without a single jump. */
.disc-preview.active .disc-preview-item {
  animation: pv-live 28s var(--ease-ambient) infinite alternate;
}
@keyframes pv-live {
  from { transform: scale(1); }
  to   { transform: scale(1.2); }
}
@media (prefers-reduced-motion: reduce) {
  .disc-preview-item.active { animation: none; }
}
/* 18.08 (Chiara): the caption under the frame — project name (label rung) +
   its deck byline (2px smaller), mono caps, ink on paper; each caption is a
   [data-preview-for] sibling of the panes so the same toggle crossfades it. */
/* v2 same hour (Chiara: "the overlay text needs to sit on the image — over
   image in cream"): the caption moves INSIDE the frame, bottom-left, cream —
   the deck's own caption seat. The frame clips (the panes' slow zoom was
   spilling past its edges), and a quiet foot scrim (the deck-slide grammar)
   holds the cream against the brighter Palmer image. */
.disc-preview { overflow: hidden; }
.disc-preview::after {
  content: ''; position: absolute; inset: auto 0 0 0; height: 48%;
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.42));
  pointer-events: none;
}
.pv-cap {
  position: absolute; left: var(--space-sm); right: var(--space-sm); bottom: calc(var(--space-sm) - 2px);   /* v6 (Chiara: "more padding now"): back to the sm rung (16px), optical −2px on the bottom kept */   /* v4/v5 (Chiara: "bring it further down… closer to edges but equal distance"): md → sm → xs inset; the −2px on the bottom cancels the line box's half-leading under the last baseline so the OPTICAL gap to the frame's bottom edge equals the gap to its left edge */
  pointer-events: auto;   /* 24.08 (the never-cross-content audit): the caption must be VISIBLE to the header's collision sampler — elementsFromPoint skips pointer-events:none nodes, so the caption was riding under the mark uncleared as the router scrolled out. Sits over the preview image, blocks nothing that matters. */
  margin: 0; z-index: 2;
  display: grid; gap: 0;   /* v3 (Chiara: "less line spacing") — the two lines sit on the mono's own leading */
  font-family: var(--font-mono); font-weight: 400;
  font-size: var(--type-caption);   /* v3 (Chiara: "the title and tag line should be the smaller font and same") — both lines on the 12px caption rung */
  text-transform: uppercase; letter-spacing: normal; line-height: 1.3;
  color: var(--cream);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-house);
}
.pv-film { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.pv-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.pv-flick .pv-img { opacity: 0; }   /* 12.10: the Photography pane is a FLICK — three stills stacked, one lit at a time, cut by main.js while the row is hovered */
.pv-flick .pv-img.on { opacity: 1; }   /* 25.09: the still panes join the film's fill rule — <img> instead of background-image so they can be pre-decoded (the hover jolt was JPEG decode on first paint) */   /* 18.08: the Film pane's clip — rides the pane's slow zoom like the stills */
.pv-cap.active { opacity: 1; }
.pv-cap-line { opacity: 0.85; }
@media (max-width: 900px) { .disc-preview { display: none; } }   /* hover-only furniture */

/* Method teaser variant (home) — plain full-height card, no cover choreography */
.method-intro--teaser { height: auto; }
.method-intro--teaser .method-intro-sticky { position: static; height: 100vh; }
.method-intro-link { margin: var(--space-lg) 0 0 auto; color: var(--cream); }   /* 14.07 v2: rides the block's right edge (was centred) */
/* 15.07 (Chiara: "and the reveal too"): the method CTA letter-rolls INTO
   place when the card arrives — the Let's Talk entrance, same as the
   origin CTA's scroll-gated .ready */
.method-intro-link:not(.ready) .ch { transform: translateY(105%); transition: none; }
.method-intro-link.ready .ch { transform: translateY(0); }
.method-intro-link.ready:hover .ch { transform: translateY(-100%); }
/* 15.07 v2 (Chiara: "scroll trigger entrance on ALL cta"): every rolled CTA
   site-wide letter-rolls into place when it scrolls into view (IO in
   main.js adds .ready). Header CTA keeps its page-arrival gate; work/method
   pin CTAs keep their own choreographed gates. */
.cta.roll:not(.cta--header):not(.ready) .ch { transform: translateY(105%); transition: none; }
.cta.roll:not(.cta--header).ready .ch { transform: translateY(0); }
.cta.roll:not(.cta--header).ready:hover .ch { transform: translateY(-100%); }
@media (prefers-reduced-motion: reduce) {
  .method-intro-link:not(.ready) .ch,
  .cta.roll:not(.ready) .ch { transform: none; }
}

/* HEADER BLEND (14.07 "let's use the effect") DELETED same day (Chiara:
   "turn off that effect across the whole site — doesnt work"). The JS
   ground-detection colour system (on-dark/on-light/menu-open) rules again.
   Kept from that pass: the solid-white footer wordmark. */
/* 05.08 (Chiara: "the logo overlay needs the frosted effect"): the hero
   wordmark's solid #fff override DELETED — it was masking the frosted rule
   in the base .hero-wordmark block, which now carries the 58% cream. One
   rule for the hero mark's fill, nothing fighting it. */
/* 15.07 (Chiara: "a note in american font on the hero that just says
   showreel here"): production placeholder — GT America Mono via the label
   register, dead-centre of the hero. Remove with the structure pass. */
.hero-note {
  position: absolute; left: 0; right: 0; top: 50%;
  transform: translateY(-50%);
  text-align: center;
  font-family: var(--font-mono); font-weight: 400;
  font-size: var(--type-label);
  text-transform: uppercase;
  color: var(--cream); opacity: 0.6;   /* 16.07 (Chiara): offwhite over the inverted dark hero */
}
/* 09.08 (Chiara: "the hero video text logo effect over this"): the 14.07
   solid-#fff footer override DELETED — the footer mark joins the hero's
   frosted rule (58% cream, footage glowing through the letters), set in the
   base .footer-wordmark block. Same consolidation the hero mark got 05.08. */

/* ============================================================
   CARD-STACK PINS (14.07, Chiara: "what we do needs to be sticky
   top and bottom… the aura method too") — the Mad Nice stack on
   the house cover grammar: each frame pins full-screen for 100vh
   while the next section rides up over it (opening→origin pattern:
   200vh outer, sticky 100vh inner, follower at margin-top −100vh).
   ============================================================ */
/* 14.07 v2 (Chiara: "it sticks to the top… after a few scrolls it stays and
   then releases — the who we're for section doesn't scroll over it, it
   should just continue the scroll"): pin + DWELL + RELEASE, no covers.
   The outer section is taller than the viewport; the inner frame sticks
   for the extra scroll, then the page simply moves on. Supersedes the
   same-day -100vh cover version. */
.disc { padding: 0; height: 200vh; position: relative; z-index: 2; }   /* 100 in view + 100vh dwell — 19.08 v5 (Chiara: "a big gap before the Aura Method appears"): 140 → 100 dwell */   /* 18.08 z-index 2 (Chiara's 17.00 screenshot: the curtain film showing through under the Photography row): the Method card pins a viewport EARLY (margin-top -100vh) and its box overlaps the router's last ~200px — without a stacking seat the film painted over the paper there. Same seat as .for-whom (which follows in DOM, so it still lifts OVER the router). */
.disc-sticky {
  position: sticky; top: 0; height: 100vh;
  /* 19.08 v3 (the merge): two columns — the client-voice SCREEN left, the rows right */
  --dv-w: clamp(320px, 30vw, 440px);
  display: grid; grid-template-columns: 3fr 2fr; column-gap: var(--space-2xl); align-content: center; align-items: start;   /* 27.08 (Chiara: "centre what we do and what our clients say"): the condensed block centres in the space under the chrome line again — it fits now, so centring no longer pushes the labels under the header */   /* v13b: both columns stretch to the rows' height so the image seat can close on the rows' last rule · v9 (Chiara: "maybe more 3/5 – 2/5"): 3fr 2fr · v8 was 2fr 1fr · v5 was half-half · v6 ("the lines are out of line… in line with the top"): both columns START on one line — the two labels share the top edge; the pair is centred in the viewport as a block */   /* v4 (Chiara: "should what we do be the left and right client feedback — does that make sense from a UX perspective?" — yes: claim → proof, left to right; the rows keep their wordmark anchor) */
  /* 15.07 (Chiara: "this should align under aura logo i think — not the
     burger"): left edge = the wordmark's start (gutter + burger 34 +
     cluster gap 24), the same anchor as for-whom */
  padding: calc(var(--gutter) + 80px) calc(var(--gutter) + 34px + var(--space-md)) var(--gutter) calc(var(--gutter) + 34px + var(--space-md));   /* v16 (Chiara: "margins are off"): the RIGHT inset mirrors the left (both hang from the wordmark anchor: gutter + burger 34 + cluster gap 24) — the 14.07 for-whom ruling · 19.08 v12 (Chiara: "re-evaluate the margins… the burger and the sound are missing from that section"): the pinned screen respects the CHROME LINE — content starts below the header (gutter + 72), a gutter at the base, and the block centres in what's left; on ~760px laptops the labels were sitting under the header, which cleared the burger/sound/mark */
  /* 24.08 (Chiara: "as the vph leaves What We Do that's when the background goes taupe, then dark — never a harsh line"): the router's ground rides the SAME --mx vars as the Method teaser (main.js sets them on .disc too) — both grounds are the identical colour at every instant of the hand-off, so the teaser's rising edge has nothing to contrast against: the fade happens across the transition with no seam. At rest (--mx 0) this is var(--paper), exactly as before. Type inherits the same cream ride. */
  background: color-mix(in srgb, var(--ground-3) calc(var(--mx2, 0) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--mx1, 0) * 100%), var(--paper)));
  color: color-mix(in srgb, var(--cream) calc(var(--mx1, 0) * 100%), var(--ink));
}
/* the Method card gets the same stick-dwell-release grammar */
/* 14.08 (Chiara: "the aura method section should reveal up — the section
   above lifts to reveal the curtain video"): THE REVEAL-FROM-UNDER. The
   teaser's pin starts a full viewport EARLY (margin-top -100vh) so the
   curtain film is already pinned beneath the Who We're For sheet; that
   section lifts away on its solid paper ground and exposes the card.
   Budget: 260vh = 100 borrowed + 100 reveal ride + 60 dwell. */
/* 01.09 (Chiara ruled it: keep the Method on the homepage, but "shrink it"): THE FLAT BAND.
   It was a 240vh pinned hold — 2.4 screens to deliver a lockup, one line and a link, the most
   expensive section per word on the page, and its dark dip was what made the grounds flicker
   between the paper Founder above and the paper Enquire below. Now one screen, in normal flow:
   the lockup, the lead, the CTA. The pin, the hold and the ground drift retire with it; the
   --mx vars keep being set (main.js) but nothing rides them here any more.
   REVERT: height 240vh + position sticky on .method-intro-sticky, below. */
.method-intro--teaser { height: auto; min-height: 88svh; margin-top: 0; }
.method-intro--teaser .method-intro-sticky { position: static; height: auto; min-height: 88svh; background: var(--ground-3); color: var(--cream); }   /* v6 (Chiara: "needs to stay there for another beat before it scrolls off"): 200 → 240 — the dark lockup holds ~40vh longer at rest · v5 (Chiara: "the end of that needs to HOLD before it scrolls to the next bit"): 140 → 200 — the dark lockup holds ~40vh at rest before it dissolves and the light returns (no dead paper: the exit is the last ~10%) */   /* v3 (Chiara: "boom into the Aura Method… and a massive gap"): 200 → 160 — the ground now fades on APPROACH (main.js), the copy arrives at the pin, 60vh hold */   /* 18.08 v2 (Chiara: "the background just fades into the aura method like it does on the homepage"): the REVEAL-FROM-UNDER retires (the −100vh borrow + the sheet lift) — the section arrives in normal flow on the paper it shares with Who We're For, pins, and its ground FADES to the near-black. Budget: 100 in view + 100 hold. */
/* 01.09: the sticky teaser variant retires with the pin (see THE FLAT BAND above) */
/* 18.08 (Chiara: "repeats the colour background change instead of the video"):
   THE RETURN TO LIGHT — the origin's descent in reverse. The card is revealed
   from under the For/Not sheet already on the charcoal (--ground-3, the ground
   the origin ended on and the deck sat in); as it holds it lightens to the pale
   sage (--ground-2) and the copy turns cream → ink. --mxf 0→1 is scroll-driven
   (main.js), so it is reversible; header flips ink past halfway. */
.method-intro--teaser .method-intro-sticky {
  /* v2: paper → --ground-3 (the origin's near-black), ink → cream — the
     origin's own descent, once more, in the same direction. --mxf 0→1 across
     the first half of the pinned hold (main.js), reversible. */
  /* v13 (Chiara: "scroll off to the lighter… fading through those colours again, and the next section scrolling up with it"): the STATEMENT'S palette, out and back — paper → taupe (--mx1) → near-black (--mx2), hold, then back down through taupe to paper; the wall follows in normal flow. Type ink → cream from the taupe on (the statement's rule). The v12 reveal-from-under (transparent exit, wall beneath) is retired. */
  background: color-mix(in srgb, var(--ground-3) calc(var(--mx2, 0) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--mx1, 0) * 100%), var(--paper)));
  color: color-mix(in srgb, var(--cream) calc(var(--mx1, 0) * 100%), var(--ink));
}
/* v3 (Chiara: "the text needs a fade on it"): the copy SURFACES as the ground
   darkens — a scroll-driven fade + short rise on --mtx (main.js), a beat
   behind the ground so the title arrives onto the near-black, not the paper. */
.method-intro--teaser .method-intro-copy { will-change: opacity, transform; }   /* v4: opacity + rise + word-read set inline by main.js seat() — the credential's arrival; with no JS/RM the block is simply there */
/* 13.08 CENTRED TRIAL (Chiara: "not sure on this section layout — should it
   be centred?" — the morning's MoF centre-axis question, back after seeing it
   in situ): the teaser's copy block moves to the centre axis, mid-frame —
   the MoF belief/IP grammar (their Brand Interactions™ section). The
   service-hero seat (base-anchored, title left / lead right) is the revert:
   delete this block. */
.method-intro--teaser .method-intro-sticky { justify-content: center; text-align: center; }
.method-intro--teaser .method-intro-lead { margin: var(--space-xl) auto 0; }   /* 19.08 v2: md → xl under the lockup — the stack fills the frame: label · xl · lockup · xl · lead · xl · CTA */
/* 14.08: .method-intro-body (serif/body split on the teaser lead) TRIED +
   REVERTED same hour — one lead line stands */
.method-intro--teaser .method-intro-link {
  margin: var(--space-xl) auto 0;   /* 19.08 (Chiara: "bump the spacing very slightly"): lg → xl (64) · 18.08 v2 (Chiara: "more space between the last line and this cta"): cta rung (14) → lg (40) — the lead here is a full-width serif statement, not a body paragraph, so the CTA needs the block gap, not the paragraph gap */
  color: inherit;   /* rides the block's ink → cream drift (was fixed cream) */
  border-bottom: 1px solid color-mix(in srgb, currentColor 35%, transparent);   /* "with the underline too" — the origin CTA's soft hairline */
  padding-bottom: 3px;
}
@media (max-width: 900px) {
  /* static flow on small screens — same rule as every other pin */
  .disc { height: auto; padding: var(--space-3xl) var(--gutter); }
  .disc-sticky { position: static; height: auto; padding: 0; background: none; }
  .method-intro--teaser { height: auto; margin-top: 0; }   /* static flow (the v2 fade never runs — --mxf stays 0: paper + ink) */
  .method-intro--teaser .method-intro-sticky { position: static; }
  .for-whom { padding-left: var(--gutter); padding-right: var(--gutter); }   /* the header-furniture insets are a desktop composition */
}

/* ============================================================
   ⚠ TEMPORARY — STRUCTURE PASS v3 (14.07, Chiara: "I want them to
   be empty"). All REAL imagery (photos + films) hidden; the tinted
   PLACEHOLDER boxes she asked back stay — menu previews, router
   hover preview, insight figure. Markup untouched. DELETE THIS
   BLOCK to restore the media.
   ============================================================ */
/* 16.07 (Chiara: "what's happened to the image in the middle?"): the list
   below SHRANK — the structure-pass hid all media, but the method seats
   (image + knockout title), the ph placeholder tints and the approved
   footer drone film are live again. Still hidden: the opening film (awaits
   Maria's showreel) and the work-index tiles. */
/* 05.08 (Chiara): .hero-media-wrap OUT of this list — the opening film is
   live again (LHC film seated as a stand-in until Maria's showreel). The
   footer drone film and work tiles stay hidden per her earlier calls. */
/* 09.08 (Chiara's footer screenshot): .footer-video + .footer-scrim OUT of
   this list — the drone film returns (licensed mountain-dusk master, web
   encode), superseding the 16.07 "remove" call. Work tiles stay hidden. */
.work-media {              /* work index tiles */
  display: none !important;
}
/* 14.07 later: DECK media restored (Chiara placed the Parkhotel shot on the
   Palmer slide — "image is not showing"); .hw-media, .hw-slide::after and
   the empty-ground override left this block. Rest of the pass stays. */
/* 09.08: the 15.07 ink-footer override DELETED with the film's return, as its
   own note instructed — the footer runs cream over the footage again. */
/* 05.08: the ink-header override DELETED with the film's return, as its own
   note instructed — the homepage top ground is dark footage again (0.45 scrim),
   so the header's cream default is correct from first paint. */


/* ============================================================
   /work — FULL-BLEED ROWS (20.07, Chiara: "i hate the layout…
   needs to feel impressive when someone lands on it")
   One project per screen: image edge-to-edge, name huge over it,
   index and disciplines on the gutter line. The grid/list toggle
   and list hover-preview are retired with this layout.
   ============================================================ */
.work-index--rows { display: block; margin: 0; padding: 0; list-style: none; }
.work-index--rows .wi-item { margin: 0; }
.work-index--rows .wi-item a {
  position: relative; display: block;
  height: 82svh; min-height: 460px;
  overflow: hidden;
  color: var(--cream);
}
.work-index--rows .wi-media {
  position: absolute; inset: 0; margin: 0; width: 100%; height: 100%;
}
.work-index--rows .wi-media img,
.work-index--rows .wi-media video {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transform: scale(1.04);
  transition: transform var(--dur-cinema) var(--ease-house);   /* a slow settle on hover — the house curve */
}
.work-index--rows .wi-item a:hover .wi-media img,
.work-index--rows .wi-item a:hover .wi-media video { transform: scale(1); }
.work-index--rows .wi-media::after {           /* scrim: the names have to hold on any frame */
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(0,0,0,0.28) 0%, rgba(0,0,0,0.08) 45%, rgba(0,0,0,0.42) 100%);
}
.work-index--rows .wi-name {
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: calc(var(--gutter) + 2.4em);
  margin: 0; z-index: 1;
  font-size: clamp(2.4rem, 6.4vw, 6.4rem); line-height: 0.94; letter-spacing: -0.01em;
  color: var(--cream);
}
.work-index--rows .wi-sector,
.work-index--rows .wi-disc {
  position: absolute; bottom: var(--gutter); margin: 0; z-index: 1;
  color: var(--cream); opacity: 0.8;
}
.work-index--rows .wi-sector { left: var(--gutter); }
.work-index--rows .wi-disc { right: var(--gutter); text-align: right; }
@media (max-width: 700px) {
  .work-index--rows .wi-item a { height: 68svh; min-height: 380px; }
  .work-index--rows .wi-disc { display: none; }   /* the credits line crowds the name at phone widths */
}
@media (prefers-reduced-motion: reduce) {
  .work-index--rows .wi-media img,
  .work-index--rows .wi-media video { transform: none; transition: none; }
}


/* ============================================================
   CASE STUDIES — gutter margins (21.07, Chiara: "add margin on the
   left and right — the width of the space between the burger and the
   left, and Let's Talk on the right"). That space is --gutter (32px
   desktop / 16px mobile), so the imagery now aligns to the same
   vertical as the header items instead of running edge to edge.
   Text rows already carry --gutter padding, so they're untouched.
   The HERO and the reveal FOOTER stay full-bleed — the immersive
   open and close are the frame the margins sit inside.
   ============================================================ */
.case-study .cs-stack-item { padding-left: var(--gutter); padding-right: var(--gutter); }
/* 01.08: a --stack-max-h height cap on stack imagery was TRIED + REVERTED
   same hour (Chiara: "the image needs to be big… undo") — stack imagery runs
   full gutter-width at natural ratio. Don't re-propose. */
.case-study .cs-stack-item img:not(.cs-stack-logo),
.case-study .cs-stack-item video,
.case-study .cs-stack-item--frame video { width: 100%; }   /* 01.08: :not(.cs-stack-logo) — this 21.07 gutter rule outranked the logo's width and blew the film logo up to FULL-BLEED (the exact failure the 20.07 doubled-class comment warned about). The logo keeps its own width rule. */
.case-study .cs-stack-item--band { width: auto; overflow: hidden; }
.case-study .cs-stack-item--band img { height: 100%; object-fit: cover; }
/* 30.09 (Chiara, LMDM, with a Figma frame: "on the jacket can you mockup split screen then left is the video"):
   a band frame cut into TWO HALVES — film left with the monogram over it, product right. A static
   layout, not the pinned .cs-split (no runway, no states): the band keeps its height, a grid halves it.
   The monogram is a .cs-stack-logo, so it centres in its own half and develops in like every film logo;
   the 5% scrim is the film-logo rule (.cs-stack-item--frame::after). Phones stack the halves. */
.case-study .cs-stack-item--halves { display: grid; grid-template-columns: 1fr 1fr; }
.case-study .cs-stack-item--halves .cs-half { position: relative; min-width: 0; height: 100%; overflow: hidden; }
.case-study .cs-stack-item--halves .cs-half > video,
.case-study .cs-stack-item--halves .cs-half > img:not(.cs-stack-logo) { display: block; width: 100%; height: 100%; object-fit: cover; }
.case-study .cs-stack-item--halves .cs-half > img.cs-stack-logo { height: auto; object-fit: contain; }   /* 30.09 (Chiara: "the logo loads weird… on the video of the drink"): the band rule above (.cs-stack-item--band img { height: 100%; object-fit: cover }) was catching the MONOGRAM too — stretched to the full height of the half and cover-cropped. The mark keeps its own proportions now. */
.case-study .cs-stack-item--halves .cs-half--film::after { content: ""; position: absolute; inset: 0; background: rgba(0, 0, 0, 0.05); pointer-events: none; }
@media (max-width: 760px) {
  .case-study .cs-stack-item--halves { grid-template-columns: 1fr; height: auto; }
  .case-study .cs-stack-item--halves .cs-half { aspect-ratio: 8 / 9; }
}
/* 13.08 (Chiara, Odisi wing view: "should be the same format as the seaplane
   one with the border"): swap image B pinned to inset: 0 = the PADDING box,
   so it escaped the 21.07 gutter frame image A sits in — full-bleed over a
   bordered seat. Anchor B inside the gutters; the swap now holds one format. */
.case-study .cs-stack-item--swap img.cs-swap-b,
.case-study .cs-stack-item--swap video.cs-swap-b { left: var(--gutter); width: calc(100% - var(--gutter) * 2); }   /* 30.09: video.cs-swap-b added — LMDM's opening swaps a photograph to a FILM in the same seat */
/* (the 14.08 note continues) */   /* 14.08 (Chiara: "the right-hand side goes to the edge"): img added to the selector (the 21.07 width:100% rule was winning on order) and the width COMPUTED — an absolutely-positioned img with width:auto falls back to intrinsic width (left+right anchors don't stretch replaced elements), which verified at a 207px right inset. Explicit calc = both gutters hold exactly. */   /* 01.08 (Chiara, Palmer logotype): a band image cover-fills the frame — the band height (min(56.25vw, 810px)) is the SAME cap the splits ride, so band + split sections sit at one height */
.case-study .cs-split { width: auto; margin-left: var(--gutter); margin-right: var(--gutter); }
.case-study .cs-placeholder { margin-left: var(--gutter); margin-right: var(--gutter); }

/* ============================================================
   /WORK DECK (03.08, Chiara: "make the images stack like the homepage")
   The real homepage .hw engine runs on /work with 7 slides; the
   .work-index markup is now the LIST view only. Appended at file end
   so the media override outranks the homepage only-Palmer/LHC rule.
   ============================================================ */
.work-main .hw { height: 2100vh; margin-inline: calc(-1 * var(--gutter)); }   /* 05.10: 13 slides + the loop twin = 14 × the ~150vh budget (was 7 → 1050vh) — the deck mirrors the list now; full bleed out of main's padding */
.work-main .hw-pin .hw-slide:not(.no-media) .hw-media { display: block; }   /* every /work slide shows its media (the homepage hides all but Palmer/LHC) */
.work-main .work-index:not(.is-list) { display: none; }   /* /work only — the virtuoso rail reuses nothing of this */
body:has(.work-index.is-list) .hw { display: none; }   /* list view replaces the deck */
.work-main .hw-next { display: none; }   /* 03.08 v2 (Chiara: "remove the next"): the /work deck drops the NEXT corner — the toggle owns bottom-right; the homepage deck keeps its NEXT */
body:has(.work-main) .site-header.nav-away { transform: none; }   /* 03.08 (Chiara: "logo and menu nav should stay consistent"): /work keeps its header put like the homepage deck — the interior hide-on-scroll doesn't apply here */

/* VTW proof rail (03.08): its own little grid — the old .work-index grid
   styles retired when /work became the deck, so the campaign rail carries
   its own tiles now. */
.vtw-rail { list-style: none; margin: var(--space-xl) 0 0; padding: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-lg) var(--gutter); }
.vtw-rail a { display: block; }
.vtw-rail .wi-media { position: relative; aspect-ratio: 8 / 5; overflow: hidden; margin-bottom: var(--caption-gap); width: auto; height: auto; }
.vtw-rail .wi-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 900px) { .vtw-rail { grid-template-columns: repeat(2, 1fr); } }

/* ============================================================
   WHAT CLIENTS SAY (19.08 · v3 = THE MERGE) — the client-voice SCREEN in the
   router's left column. Structure: bymonolog.com (hairline timer, arrows,
   counter); sans quote, mono name + role; one voice at a time, paging.
   The hover preview shares this seat (see .disc-preview) — .previewing on
   .disc-sticky fades the story while an image is up.
   ============================================================ */
.disc-voice { position: relative; display: flex; flex-direction: column; gap: 0; transition: opacity var(--dur-base) var(--ease-house); order: 2; }   /* v13b: a flex column — the image seat takes margin-top:auto and closes level with the rows */   /* v4: the voice screen sits RIGHT · v8 (Chiara: "the loading line needs to be aligned with the line on top of Branding"): the grid gap goes — the label's own margin (the same lg as .disc-label) puts the timer line exactly on the first row's rule; the rest of the stack carries explicit margins */
.voices-top { margin-top: var(--space-sm); margin-bottom: var(--space-md); }   /* 24.08 (Chiara: "the testimonial is close to the arrow"): a fixed md below the controls = the GUARANTEED minimum gap — the seat's auto top margin collapses to 0 when a long quote leaves the column no slack (flex margins don't collapse, so this always holds) */
.voices-seat { margin-top: auto; margin-bottom: var(--space-lg); }   /* 24.08 (Chiara): auto top — the quote settles to the column's foot against the image, the dead middle becomes air under the timer line · v11 · v13b: lg below, the fixed gap to the image seat */
/* .disc-sticky.previewing .disc-voice fade RETIRED v10 — the image no longer covers the story */
.voices-bar { position: relative; height: 1px; background: color-mix(in srgb, currentColor 20%, transparent); }   /* 24.08: currentColor (was --ink) — the timer line rides the hand-off fade to cream with everything else */
.voices-fill { position: absolute; inset: 0 auto 0 0; width: 0; background: currentColor; }
.voices-fill.run { width: 100%; transition: width var(--voice-hold, 8s) linear; }   /* the Monolog "loading" line — fills across the hold, restarts per voice */
.voices-top { display: flex; justify-content: space-between; align-items: center; }
.voices-nav { display: flex; gap: var(--space-md); }
.voices-btn { font-family: var(--font-mono); font-size: var(--type-label); background: none; border: 0; padding: 0; color: inherit; cursor: pointer; opacity: 0.65; transition: opacity var(--dur-quick) var(--ease-house), transform var(--dur-quick) var(--ease-house); }
.voices-btn:hover { opacity: 1; transform: translateX(var(--nudge, 0)); }
.voices-btn[data-dir="-1"] { --nudge: -4px; }
.voices-btn[data-dir="1"] { --nudge: 4px; }
.voices-count { margin: 0; opacity: 0.65; font-variant-numeric: tabular-nums; }
.voices-label { margin: 0 0 var(--space-lg); opacity: 0.8; }   /* v13: leads the column again — same rung + gap as .disc-label, so the timer line sits on the first row's rule */
.voices-seat { display: grid; }
.voice { grid-area: 1 / 1; margin: 0; opacity: 0; transform: translateY(24px); pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-house), transform var(--dur-slow) var(--ease-house); }
.voice.in { opacity: 1; transform: translateY(0); pointer-events: auto; }
.voice.out { opacity: 0; transform: translateY(-20px); transition: opacity var(--dur-quick) var(--ease-house), transform var(--dur-base) cubic-bezier(0.5, 0, 0.1, 1); }
.voice-lead { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.18; letter-spacing: -0.01em; max-width: 20ch; margin: 0 0 var(--space-md); text-wrap: balance; }   /* 27.08 (Chiara: the voices "a bit boring where they are" → "try the pull-line"): each voice LEADS with its one killer sentence on the serif-md rung — the wow is the pull-line, the warmth stays in the full quote below, nothing re-trimmed */
.voice-quote { font-family: var(--font-body); font-weight: 300; font-size: var(--type-body); line-height: var(--lh-body); max-width: 52ch; margin: 0; }   /* v15 (Chiara: "a little narrower"): 52ch — between the old 44 and the full column */
.voice-mark { display: block; height: 22px; width: auto; margin-bottom: var(--space-md); }   /* the client's logo mark above the quote — once the marks land */
.voice-mark:empty, .voice-mark:not([src]) { display: none; }
.voice-who { display: grid; gap: var(--space-xxs); margin-top: var(--space-md); }   /* v11: tighter — the column must sit level with the rows */
.voice-role, .voice-proof { opacity: 0.65; }
.voice-proof:empty { display: none; }

/* CASE-STUDY TESTIMONIAL (03.09) — the homepage voices' treatment, on a case study.
   Deliberately reuses .voice-quote/.voice-who/.voice-name/.voice-role above rather than
   restyling them, so a client's words look the same wherever a reader meets them. The
   carousel's own .voice class is NOT used: it carries grid-area + opacity:0 for the
   crossfade and would render a static quote invisible. */
.cs-voice {
  background: var(--ground-3);
  color: var(--cream);
  padding: var(--space-section) var(--gutter);
}
.cs-voice--light { background: var(--paper); color: var(--ink); }   /* 03.09 (Chiara: "on the palmer page the background needs to be white with dark text"): per-page ground for the voice — paper + ink, the page's own pair, not pure white. Set with data-header-tone="light" on the section so the header flips with it. */
.cs-voice-label { opacity: 0.65; margin: 0 0 var(--space-xl); }   /* 20.10 (Chiara: "is this the same size as ( Website )?"): size, weight and tracking already matched (13px/400); the opacity was 0.8 against the story labels' 0.65 — now the same */
.cs-voice-quote { margin: 0; max-width: none; }   /* 03.09 (Chiara: "wider", after 62ch -> 80ch was still not enough): the character cap comes off entirely. The quote now runs the full width of the section, so the only thing setting its measure is the page gutter — the same edge the rest of the case study aligns to. Safe here because it is Eiko at the serif rung, not sans body copy: a display serif holds a long line, and this is five sentences on a dark ground rather than a page of reading. */   /* 03.09 (Chiara: "make this box wider"): 62ch -> 80ch. It reads wider than a body measure would normally allow, but this is Eiko at the serif-md rung rather than sans body copy, and a display serif carries a longer line comfortably. Capped at 100% so it never outruns the section gutters. */
.cs-voice .voice-quote { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.32; letter-spacing: -0.005em; }   /* 03.09 (Chiara: "make this eiko"): the quote moves off the body sans onto the serif display rung. It is the only client voice on the page rather than one of seven in a carousel, so it can carry the section at the serif-md size the case-study leads use. */   /* one rung up from homepage body: this quote stands alone rather than sharing a carousel, so it can carry the section */
.cs-voice .voice-role { opacity: 0.65; }
@media (max-width: 900px) {
  .disc-sticky { display: block; }
  .disc-voice { margin-bottom: var(--space-2xl); }
}
@media (prefers-reduced-motion: reduce) { .voice, .voice.out, .voices-fill.run { transition: none; } }

/* 19.08 TRIAL SWITCH (Chiara: "should it be on a darker background and white text?"):
   load the page with ?routerdark to see the router on the near-black with cream
   type — JS adds .router-dark to the section. Judge in situ; delete this block
   (and the JS hook) once ruled. */
.disc.router-dark { background: var(--ground-3); }   /* 30.08 (Chiara's 14.57 screenshot — a cream stripe between "Our Work" and the deck): the dark ground was painted on the STICKY only, so wherever the section is taller than its sticky child the paper body showed through as a band. The section carries the ground now; the sticky keeps it too (it needs its own while pinned). */
.disc.router-dark .disc-sticky { background: var(--ground-3); color: var(--cream); }
.disc.router-dark .disc-row::before, .disc.router-dark .disc-row:last-child::after,
.disc.router-dark .voices-bar { background: color-mix(in srgb, var(--cream) 25%, transparent); }
.disc.router-dark .voices-fill { background: var(--cream); }

/* ── THE DESCENT ─────────────────────────────────────────────────────────────
   02.09 (Chiara: "what background effect do we have on the intro to the dark?
   can you apply that to testimonial to logo bit" + "the colours jarring against
   the next section").

   THE ORIGIN'S DEVICE. Not a gradient: one flat ground whose colour is color-mix'd
   live from a scroll variable. The reason the origin reads seamless is NOT its
   palette — it is that the sections around it ride the SAME variable, so adjacent
   grounds are the identical colour at every instant and no edge can exist.

   The wall spent three attempts failing because it was running the descent alone,
   wedged between a permanently dark voices section and a permanently cream founder.
   One band cannot reconcile two fixed neighbours. So all three now share one
   timeline (--gx1/--gx2, set by main.js drawMarq off the wall's crossing):

     voices ── dark, held while the quotes are still being read
     wall   ── descends dark → taupe → paper as it crosses
     founder ─ paper, arrived

   Defaults differ ON PURPOSE: voices and wall default to 0 (dark) so they are
   correct before you have scrolled to them; the founder defaults to 1 (paper) so a
   direct link or a reload deep in the page never lands it on a dark ground. */
.disc--voices.router-dark,
.disc--voices.router-dark .disc-sticky,
.clients {
  background: color-mix(in srgb, var(--paper) calc(var(--gx2, 0) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--gx1, 0) * 100%), var(--ground-3)));
  color: color-mix(in srgb, var(--ink) calc(var(--gx2, 0) * 100%), var(--cream));
}
/* 04.09 (Chiara: "there is a weird background effect on it" → "it has a weird fade on it"): the Founder's
   scripted ground is GONE, not just unhooked. Yesterday it left the wall's --gx run in JS, but the section
   kept this rule, so its background was still a live color-mix over variables that other code sets — and
   with `transition: all` inherited from the page-ground rules, every stray change to those vars animated
   as a slow wash across the whole section. That lag was the fade. Meet the Founder is flat paper now: one
   colour, no variables, nothing to transition. */
.founder, #founder { background: var(--paper); color: var(--ink); transition: none; }   /* 12.10: #founder joins — the homepage block is .svc-founder now (the studio composition), keyed by its id so the ground run survives the class change */
/* 04.09: on the homepage the Founder JOINS THE CLOSE RUN (see the JS note) — it takes the run's ground so
   there is no edge between it and the Method. Still no transition property: the colour is driven by scroll
   position, so animating it would only add lag on top of the movement. */
body.home .founder.close-rider, body.home #founder.close-rider {
  /* 09.09 (the ground scan): the paper term is the WALL'S ground, not flat --paper — main.js sets --gx1/--gx2 on the
     Founder too, so while the wall is still descending the Founder's top is the identical colour, and once the wall
     has landed on paper (gx 1/1) this collapses to plain paper and the close run takes over as before. The two
     timelines never overlap: the wall finishes at q .55, the close starts when the Founder's top has left the screen. */
  background: color-mix(in srgb, color-mix(in srgb, var(--paper) calc(var(--gx2, 1) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--gx1, 1) * 100%), var(--ground-3))) calc(var(--cx2, 1) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--cx1, 1) * 100%), var(--ground-3)));
  color: color-mix(in srgb, var(--ink) calc(var(--cx2, 1) * 100%), var(--cream));
}

/* 19.08 — THE METHOD TEASER LOCKUP (Chiara, Monolog ref; the /method phase-card
   grammar): the title runs across a centred portrait — "The Aura" · image ·
   "Method™" — inline, the portrait overshooting the line top and bottom. Sits on
   the teaser's paper → near-black fade; colour follows the section's drift. */
/* v2: the STAGE — image behind, title across, clipped frosted duplicate over the image box */
.mi-stage { position: relative; display: grid; place-items: center; width: 100%; --mi-w: clamp(220px, 20vw, 340px);   /* 25.10 v2 (Chiara: "a bit smaller so that the Method overhangs either side of it — too big now"): 24vw → 20vw (346 → 288px wide at 1440), between the old 259 and v1 346. v1: "on desktop it needs to be bigger as well" */ min-height: calc(var(--mi-w) * 4 / 3); }   /* 25.10 v5 (Chiara: "bring How We Get You There closer to the image — reduce the spacing"): the extra lg clear above and below retires (104 → ~64px); */   /* 25.10 v4 (Chiara: "now maybe too tall — make sure of the composition"): 3:4, between 4:5 and the photo’s 2:3 — the crop takes 11% of empty ceiling off the top and keeps the whole arch, window, column and sea. v3 (Chiara: "is it too wide? should it keep the proportions of the image?"): the seat takes the photo’s own 2:3 (was 4:5 — the arch lost 15% of its height to the crop); same width, taller. v2 (Chiara: "the spacing is too big on How We Get You There — squish it together a little"): xl → lg (64 → 40) clear above and below the seat. v1: xl so the bigger seat no longer touched ( How We Get You There ) */
.mi-media { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(var(--mi-grow, 1)); width: var(--mi-w); aspect-ratio: 3 / 4; overflow: hidden; z-index: 1; }   /* v4 (Chiara: "'Aura Me' needs the opacity overlay on it"): the image sits ABOVE the base title — so where the title crosses it, only the frosted twin shows (over the photo), not solid cream under a frosted copy */   /* 24.08 (Chiara: "some kind of smooth interaction — it grows slightly"): --mi-grow rides the section's lerped timeline (updateMethodGround) — box scales 1 → 1.08 across the ride, compositor-only; the clip window scales WITH it and its title counter-scales (below), so the frosted letters stay pixel-registered — the deck's counter-scaled clip-window trick */
.mi-media img, .mi-media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.mi-media::after { content: ""; position: absolute; inset: 0; background: color-mix(in srgb, var(--ground-3) 20%, transparent); pointer-events: none; }   /* 25.10 (Chiara: "a slightly darker overlay on the image so it pops?"): the case-study photographs carry a dark tint, which is what lets their 58% cream titles read — the pale Caruso arches get the same: the site dark at 20%, under the title twin (.mi-clip sits above .mi-media) */
.mi-media img { height: 108%; margin-top: -4%;   /* 25.10 (Chiara: "why is it zoomed in?"): headroom 118 → 108% at every size — the drift amplitude follows it */ translate: 0 var(--wpy, 0px); }   /* 22.10 (Chiara: "a light parallax scroll on it, like the service page case-study boxes"): the /svc-work grammar — 18% headroom, ±24px drift on the translate property (main.js updateWorkParallax), so it rides under the 24s breathe (transform) and the box's own --mi-grow (parent) without touching either */
.mi-media video { animation: pv-live 24s var(--ease-ambient) infinite alternate; }   /* 01.10: img joins — the Method seat holds a photograph now (the elephant at sunset), and her 24.08 "grow ever so slightly" drift applies to it the same */   /* 24.08 (Chiara: "can it grow ever so slightly or glide or parallax or something"): the disc-preview's slow drift (pv-live, scale 1 → 1.2) INSIDE the frame — always alive, even at rest; the box clips it so the frosted title's registration is untouched. Rides on top of the scroll-driven --mi-grow box swell. */   /* 25.10: the photograph no longer breathes to 1.2× (Chiara: "why is it zoomed in?") — the film keeps it */
@media (prefers-reduced-motion: reduce) { .mi-media video, .mi-media img { animation: none; } }
.method-intro--teaser .method-intro-title.mi-lockup {
  position: relative; z-index: 0; margin: 0; text-align: center; white-space: nowrap;   /* v4: base title UNDER the image */
  font-size: var(--type-monument);   /* the unseated monument rung finds its home: the lockup is the page's biggest line */
  line-height: 1; letter-spacing: -0.02em;
}
.mi-clip { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(var(--mi-grow, 1)); width: var(--mi-w); aspect-ratio: 3 / 4; overflow: hidden; z-index: 2; pointer-events: none; }
.mi-clip .mi-over { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(calc(1 / var(--mi-grow, 1))); width: 100vw; color: var(--cream); transition: color 0.15s ease-out; }   /* 10.09 (Chiara: "the text effect is wrong on the Aura Method section — the text over it needs to be inverted"): the FROST RETIRES here too. Over the bright curtain film the 58% cream twin simply vanished. The twin is now the INVERSE of the base title, the origin twins' rule (5556): cream over the image while the base is ink (paper), ink over the image once the base has gone cream (close-cream). Solid, not frosted. */
body.home.close-cream .mi-clip .mi-over { color: var(--ink); }
/* 05.10 (Chiara: "the text over the image should have that inverted opacity effect we have used elsewhere"): THE FROST
   RETURNS — the site's frosted rule (cream at 58%, the deck-title value) on the letters that cross the photograph, in both
   ground states, so the elephant ghosts through them. It was retired 10.09 only because it vanished over the bright curtain
   film; the elephant frame is darker and warmer. If it disappears again, the 10.09 solid invert above is the fallback. */
.mi-clip .mi-over, body.home.close-cream .mi-clip .mi-over { color: var(--cream); }   /* 22.10 (Chiara: "and the Aura Method on the homepage too — the text treatment"): the 58% frost retired for the intro's SOLID cream — the same call as the origin on 29.08 (the frost read as grey over the photo) */   /* v3 (Chiara: "on the brown, the text over the image needs the effect so it looks different to the text over the brown"): the SITE'S frosted rule — cream 58%, the deck-title value — the photo ghosts through the crossing letters; solid cream elsewhere. Registered to the base title: both centre on the stage's centre */
@media (max-width: 900px) {
  .method-intro--teaser .method-intro-title.mi-lockup { white-space: normal; }
  .mi-clip { display: none; }   /* a wrapped title can't register — the base title alone over the image */
  .mi-stage { --mi-w: 51vw; }   /* 25.10 (Chiara: "a bit smaller on the Method on mobile — shrink it by 5%"): 54 → 51vw (211 → 199px wide at 390) */ .mi-stage .mi-media img { height: 108%; margin-top: -4%; animation: none; }   /* 25.10 (Chiara, the Vespa in How We Get You There: "make the image bigger, I want to see more of it — why is it zoomed in?"): the seat 44 → 64vw (172 → 250px wide on a 390), the drift headroom 118 → 108% (the drift amplitude follows the headroom, main.js), and the 24s breathing zoom (pv-live, to 1.2×) off on phones — 92% of the frame’s photo is on screen at any moment instead of ~70%  */
}
/* 19.08 (Chiara: the lead "might need to be in the body font or… the mono font, maybe"): the teaser lead goes to the MONO caption register — the deck's byline voice under the lockup; trial, the serif-md rule stays beneath for the revert. */
.method-intro--teaser .method-intro-lead { font-family: var(--font-mono); font-weight: 400; font-size: var(--type-label); text-transform: uppercase; letter-spacing: normal; line-height: 1.7; max-width: 52ch; opacity: 0.85; }
.method-intro--teaser .method-intro-label { margin: 0 auto var(--space-xl); opacity: 0.8; }   /* 19.08 v2 (Chiara: "optimise the spacing between these to fill the space"): lg → xl to the lockup */
/* 19.08 (Chiara: "can the text have the effect you used on the image on the case
   studies?"): the FLEAVA REVEAL (05.08, measured from their CSS) on the whole
   lockup — armed at opacity 0 / scale 1.2 / rotate 1.5deg, settling to 1 / 0deg
   over 2s with the opacity landing in 0.5s; the rotation settling out is the
   signature. JS adds .on as the Method arrives (updateMethodGround). */
/* 19.08 v2 (Chiara: "why is the image zooming in? I don't like that"): the scale/rotate reveal RETIRED — the lockup arrives with the block (the seat's rise + fade); only the frosted-type-over-image treatment remains. .on is now inert. */
.mi-stage { opacity: 1; transform: none; }
.founder-sign { margin: var(--space-md) 0 0; opacity: 0.8; }   /* 29.08: the sign-off — mono label under the copy, before the link */
.founder-link { border-bottom: 1px solid color-mix(in srgb, currentColor 35%, transparent); padding-bottom: 3px; }   /* 19.08 (Chiara: "More about the studio should have an underline"): the Start a project soft rule */
/* 19.08 (Chiara: "more spacing between 01–04 so the bottom line aligns with the bottom of the image"): the rows SPREAD to the column's height — both columns stretch to the taller one, the rows distribute evenly, so the last rule always closes on the image's bottom edge. */
.disc-main { display: flex; flex-direction: column; height: 100%; }
.disc-rows { flex: 1; display: flex; flex-direction: column; }
.disc-row { flex: 0 0 auto; display: flex; align-items: center; }   /* 27.08 (Chiara: "the line spacing is massive — condense everything"): the 19.08 stretch-to-the-image RETIRED — rows sit at their own md rhythm; the pull-line had grown the voice column and the rows spread with it */
@media (max-width: 900px) { .disc-main { height: auto; display: block; } .disc-rows { display: block; } .disc-row { display: block; } }

/* 24.08: THE LOADER block removed same day (Chiara: "remove it") — the full arc is in git (v201069–201077) and loader-lab.html stays on disk. */

/* ============================================================
   24.08 FINISH PASS — small closures
   ============================================================ */
/* Header CTA overflow at ≤430px (flagged 15.07: "Let's Talk" spilled ~9px at 390
   — the 13.08 wordmark bump to a 170px floor re-created the squeeze its comment
   said couldn't return, via the 1fr side cells). The CTA never shrinks (ruled);
   the wordmark floor gives back the room instead: 170 → 150 (its pre-13.08 floor)
   only under 430px. */
/* 20.10: the ≤430 `.site-header .wordmark { width: 150px }` override RETIRED — it predated the --wm-w token and was
   silently cancelling the phone floor (Chiara's "logo a bit bigger" / "3% bigger" never reached a real phone: the mark
   measured 150 at 390). The token's floor (196) rules; the header still clears at 390 (196 + 92 + 22 + two 16 gaps = 342). */
/* Insight Report: the report doesn't exist yet — the menu link dead-ends (the
   homepage section is hidden; interior pages no-op). HIDDEN site-wide until the
   report ships; delete this rule to restore. (:has needs a modern browser; the
   harmless fallback is the old dead link.) */
.insight[hidden] { display: none; }   /* 05.10: the Insight Report section is parked until it exists */
/* 05.10 SITE MAP REVIEW: the small menu row is the SERVICES group — a mono label, then the four disciplines. */
.menu-secondary { align-items: baseline; }
.menu-secondary-label { opacity: 0.55; margin-right: var(--space-sm); }
/* the way back up from a case study: ( All Work ), centred above previous / next */
.cs-nav-all { grid-column: 1 / -1; justify-self: center; opacity: 0.75; transition: opacity var(--dur-quick) var(--ease-house); }
.cs-nav-all:hover, .cs-nav-all:focus-visible { opacity: 1; }

/* CONTINUE READING (24.08, Chiara — crowncreative.com ref): overview stands
   alone, the depth arrives on the reader's ask; the link is consumed by the
   click (Crown's one-way read). Button carries the quiet-link skin. */
.svc-more-toggle { background: none; border: 0; padding: 0; cursor: pointer; color: inherit; font-family: var(--font-mono); font-size: var(--type-label); text-transform: uppercase; margin-top: var(--space-md); border-bottom: 1px solid color-mix(in srgb, currentColor 35%, transparent); padding-bottom: 3px; }
.svc-more { margin-top: var(--space-md); }
.svc-more p { margin: 0 0 var(--space-sm); opacity: 0; transform: translateY(14px); transition: opacity var(--dur-slow) var(--ease-house), transform var(--dur-slow) var(--ease-house); }
.svc-more.open p { opacity: 1; transform: none; }
.svc-more.open p + p { transition-delay: 0.12s; }
@media (prefers-reduced-motion: reduce) { .svc-more p { transition: none; transform: none; opacity: 1; } }

/* 24.08: the /web hero experiments (cs-hero port, extra pan, lead word-read) all REVERTED same hour — Chiara: "go back to how it was but with image". The seated image + the existing 16.07 updateSvcHero pan ARE the hero. Arc in git v201085. */

/* /web HERO RIDE — THE CASE MECHANIC (25.08, Chiara: "the iteration where the second paragraph
   was under the first" = v201092/v201098): ONLY the image pins (the sticky holds the bg alone);
   screen 1 (top: 0) and the ( Overview ) layer (top: 100svh) are absolute layers the scroll
   itself moves — the title rides off, the Overview rides in over the held image in the
   case-intro seat (right gutter, ragged-left), rides off, and a THIRD beat of clean full-bleed
   image pans to its base before the release into Process. No fades. 300svh. The v7 in-place
   lead swap retired (its two-lead seat grid with it). */
.svc-hero--ride { display: block; height: 300svh; min-height: 0; padding: 0; overflow: visible; }   /* 25.08 v2: a 250svh cut was TRIED and reverted within the hour — the Overview layer (top: 100svh) only clears the viewport at 2vh of scroll, so any clean beat needs ≥ 300svh; below that the Overview is still riding out as the image releases. 300 = one clean viewport. */
.svc-hero--ride .svc-hero-sticky { position: sticky; top: 0; height: 100svh; overflow: hidden; }
/* 25.08 (Chiara: "make it identical to the LHC example", reaffirmed after the aspect note): LHC's
   hearth is PORTRAIT — cover leaves ~990px of hidden image at 1440×900, so its 30% drift travels
   ~296px. The sofa is landscape (2400×1566) — cover leaves 127px, the same drift moves 38px, i.e.
   nothing. To reproduce LHC's travel the box carries its own headroom: 135% tall, and the ride
   pans the WHOLE hidden band (0 → −35/135 of its height ≈ 296px). Cost, stated and accepted:
   screen 1 is ~1.18× the approved cover crop. --svc-ride-head is the one dial. Phones keep the
   flat cover crop (the ride is static there). */
.svc-hero--ride { --svc-ride-head: 20; }   /* 25.08 v6 (Chiara: "why is this image soooo zoomed in — show more width"): 50 → 20. Width and pan travel are the SAME dial: the taller box is cover-scaled wider too. At 20 the frame shows ~96% of the render's width (wardrobe + side table back) and the pan drops to ~80px + the release. Arc: 35 → 22 → 30 → 40 → 50 → 20. v5: 40 → 50 — ~200px of reveal at 1440×900, screen 1 ≈ 1.3× the flat crop; the ceiling before the sides visibly crop. v4: 30 → 40 — ~160px of reveal at 1440×900, screen 1 ≈ 1.21× the flat crop. v3: 22 → 30 — the ride now starts centred (screen 1 = the approved crop) and pans only the lower half, so the band grows to keep ~120px of travel; screen 1 ≈ 1.12× the flat cover crop */   /* 25.08 v2: 35 → 22 — the pan settles with the laptop still in frame (it was ending on cushion), and screen 1 sits ~1.12× the approved crop instead of 1.18× */
.svc-hero--ride .svc-hero-bg img, .svc-hero--ride .svc-hero-bg video { height: calc((100 + var(--svc-ride-head)) * 1%); will-change: transform; }
.svc-hero--ride .svc-hero-bg img, .svc-hero--ride .svc-hero-bg video { transform: translateY(calc(var(--svc-ride-head) / (100 + var(--svc-ride-head)) * -50%)); }   /* 25.08 v3: the centred rest state before JS runs — no jump on first paint */
.svc-hero-bg picture { display: contents; }
body.method-page .svc-hero--ride { --svc-ride-start: 0.85; }   /* 25.10 (Chiara: "more focus on the lower bit, a little bit more"): the Method hero's ride starts 85% down the band (was centred) — the loggia's chairs and floor in view from the first screen; the pan still ends at the base */   /* 25.10: a <picture> wrapper (the Method hero's phone portrait source) must not become a box — the img keeps sizing against .svc-hero-bg */
/* body.method-page .svc-hero--ride { --svc-ride-head: 4; }  RETIRED 25.10 (Chiara: "do the exact same as the Studio header hero"): the Method hero takes the standard 20 ride, as /studio */   /* 25.10 (Chiara, the Caruso arches as the Method hero: "can we zoom out of it any more?"): the ride headroom 20 → 4 on this page — ~1.04× instead of ~1.2×, so the desktop shows the slice nearly whole; the ride still drifts a touch */
.svc-hero--media.svc-hero--ride > .svc-hero-first {   /* 0,3,0 — must beat the 16.07 `> *:not(.svc-hero-bg) { position: relative }` rule (0,3,0, earlier) or the layer drops into flow */
  position: absolute; top: 0; left: 0; right: 0; height: 100svh; z-index: 1;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-start;
  padding: calc(var(--space-section) * 0.75) var(--gutter) var(--space-xl);
}
.svc-hero--ride .svc-lead { transform: none; margin-bottom: 0; align-self: flex-end; }   /* 25.08: a serif-lg / 32ch step-up to match the Overview line was tried and reverted within the hour (Chiara: "reduce it to what it was") — the lead stays on --type-serif-md / 40ch */   /* the +10vh seat nudge is the classic hero's; the layer's own padding seats the lead here */
.svc-hero--media.svc-hero--ride > .svc-hero-overlay {   /* 0,3,0 — same specificity war as the first layer: the 16.07 `.svc-hero--media > *:not(.svc-hero-bg) { position: relative }` rule outguns a lone class and would force this into flow (the 24.08 cream-block bug) */
  position: absolute; top: 100svh; left: 0; right: 0; height: 100svh; z-index: 1; pointer-events: none;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-end;   /* 25.08 v2: the LEAD'S seat — base-right, same bottom padding — so the Overview arrives exactly where the lead rode out: a hand-off, not two paragraphs stacked mid-ride */
  gap: var(--space-md); padding: 0 var(--gutter) var(--space-xl); text-align: right;
}
.svc-hero-overlay .label { opacity: 0.8; }
.svc-hero-line { font-family: var(--font-serif); font-weight: 300; font-size: var(--type-serif-md); line-height: 1.35; max-width: 44ch; margin: 0; }   /* 21.10 (Chiara: "is the overview font a bit too big? should it match the intro text?"): serif-lg → serif-md, the case pages' intro-lead rung — one statement register site-wide; the measure widens a notch so the line count holds */
@media (prefers-reduced-motion: reduce) {
  .svc-hero--ride { height: auto; min-height: 100svh; }
  .svc-hero--ride .svc-hero-sticky { position: relative; }
  .svc-hero--media.svc-hero--ride > .svc-hero-first, .svc-hero--media.svc-hero--ride > .svc-hero-overlay { position: static; height: auto; }
}

/* 24.08 MOBILE PASS: the /web hero ride goes STATIC under 900px — the house
   grammar (router/Method/deck all drop their pins on phones). The overlay
   flows as a normal block after the hero. */
@media (max-width: 900px) {
  /* 21.10 (Chiara, the phone branding review: "( Overview ) … this needs to be like the case study page over the hero
     image"): the 25.08 cream block is RETIRED — on phones the ride runs as on desktop, shortened to two screens: the
     image holds (sticky), screen one is eyebrow + title + lead, screen two is the Overview arriving over the same
     image in the lead's seat, cream on the scrim. Then the release. No headroom pan on phones. */
  .svc-hero--ride { height: 200svh; min-height: 0; }
  .svc-hero--ride .svc-hero-bg img { height: 100%; transform: none; }
  .svc-hero--media.svc-hero--ride > .svc-hero-overlay { align-items: flex-start; text-align: left; }
  .svc-hero--ride .svc-hero-line { font-size: var(--type-serif-md); line-height: 1.35; max-width: none; }
}

/* ============================================================
   28.08 THE SPLIT (Chiara: "what we do should have its own section… similar
   to this layout but alternate up and down offset… in the new style [lighter]…
   then client testimonials should be a new section… when you hover over the
   item it shows the image"; screenshot 16.24 = a four-column services row,
   index above, serif title below). The 19.08 merge (voices left / rows right,
   pinned) is UNPICKED: What We Do is a four-column row on paper — columns 2
   and 4 drop one step (--space-2xl) for the up/down rhythm — the hover image
   rises BEHIND the whole row (full section width, titles go cream over it);
   the client voices follow as their own full-width section. Static flow, no
   pin. The .disc/.disc-sticky/.disc-rows/.disc-row/.disc-preview class names
   STAY so main.js (hover previews, ground vars, header chrome, collision,
   voice pause-on-row-hover) runs untouched — every override is scoped to
   .disc--split. REVERT = delete this block + drop the modifier + move the
   preview back under the voice label.
   ============================================================ */
.disc.disc--split { height: auto; }
/* 30.08 (Chiara: "the cards for 01–04 are cut off" → "change the cards back"): THE OVERLAP RETIRED.
   Pulling the tiles up into the origin's empty tail (-52svh) with the origin painting on top
   (z-index 3) closed the gap but the origin's layer clipped the tiles' upper edge. A modest
   negative margin only — no stacking — so nothing overlaps anything. */
@media (min-width: 901px) and (prefers-reduced-motion: no-preference) {
  .disc--split:not(.disc--voices) { margin-top: -18svh; }
}   /* 30.08 (Chiara's 13.03 screenshot: "reduce the gap"): the origin stage centres its content, so after the pin releases its lower half scrolls past EMPTY — the tiles section rides up into that tail (both grounds are --ground-3, so the overlap is invisible). 34svh ≈ the empty half minus the CTA's clearance. */
.disc--split .disc-sticky {
  position: relative; height: auto; display: block;
  padding: var(--space-section) var(--inset-page);   /* 09.09: tokenised · 01.09 v12 (Chiara: "I want the What We Do section to have that margin too"): 4vw inline — the same outer margin the origin floats sit on, so the page has one left/right edge · v11 gave up the wordmark inset */
}
/* --- What We Do: THE ROWS (01.09 — Alex: the tile grid "too messy"; back to lines + titles,
   image on hover). The base .disc-row rules (drawn hairlines, serif, mono index, the 12px
   nudge) all still apply; this block only seats them on the dark split section and puts the
   Crown pop-up tile over the right-hand air. The 30.08 tile CSS is gone — git has it. --- */
.disc--split .svc { position: relative; display: flex; flex-direction: column; justify-content: center; min-height: 100svh; padding: var(--space-xl) 0; box-sizing: border-box; }
.disc--split .svc-label { margin: 0 0 var(--space-md); text-align: left; position: relative; z-index: 2; opacity: 0.8; }   /* 01.09 v4 (renamed off .svc-title: the service-page rule at ~3388 is a big serif and was outranking .section-label): the mono label register (type comes from .section-label) · 01.09 v5 (Chiara: "what we do title is too close to the line"): xs → md, the house rung for a label over the thing it names (.svc-eyebrow) — the first hairline was 8px under the label */
.disc--split .disc-rows { position: relative; z-index: 2; display: block; max-width: none; }   /* 01.09 v12 (Chiara: "the lines need to be longer"): the cap is GONE — the hairlines (.disc-row::before, left:0/right:0) were stopping where the measure stopped. The 82% existed to keep the longest title out from under a plate PINNED RIGHT; since v17 the plate follows the pointer, so the cap was guarding against a layout that no longer exists. Lines now run the full section width. */   /* 01.09 v11 (Chiara flagged "Website Design & Development" — the longest title ran under the plate): the rows stop short of the plate's column so every title reads whole · v2 was full width */
.disc--split .disc-row { font-size: clamp(34px, 5.3vw, 74px); padding: var(--space-lg) 0; white-space: nowrap; letter-spacing: -0.015em; opacity: 0.65; transition: opacity var(--dur-base) var(--ease-house), transform var(--dur-quick) var(--ease-house); }   /* 01.09 v12 (Chiara: "maybe it should be frosted and on hover it goes full opacity" → "the text i mean"): the rows now REST frosted at 0.65 (the house recessive rung — .svc-eyebrow, .pv-cap) instead of full; hovering the list deepens the unhovered ones to 0.32 and the hovered line resolves to 1. Two stages, not one. */   /* 01.09 v13: 64 → 74 — the raise Chiara asked for twice, finally affordable: "Website Design & Development" was the single title pinning the nowrap measure, and it retired this pass. Longest is now "Brand Strategy & Design" (23 ch). No plate narrowing or tracking tricks needed to pay for it. */   /* 01.09 v11 (Chiara: "a bit bigger" again): 58 → 64, paid for by pulling the section's left inset back to the plain gutter, tightening the index gap and a touch of tracking — the longest title still holds one line · v10: 54 → 58, with the measure opened to 79% and the plate narrowed to make the room · v9: 54 + nowrap — the largest that keeps "Website Design & Development" on ONE line inside the measure, so every row is a single line and the index rhythm holds (v8's 62 wrapped it) */   /* 01.09 v7 (Chiara: "more"): md → lg — back to the 40px rung the rows opened on · v6 was sm → md — half a step back from the tight index · v5 was lg → sm ("closer together") · v2: serif-lg (44) → display (80) */
.disc--split .disc-preview {
  /* 01.09 v4 (Chiara: "the hover could be more luxury editorial — it looks a bit BTEC"): THE PLATE.
     A PORTRAIT frame (the magazine crop, not a 16:9 card), seated to the right and tracking the
     hovered line (JS sets --pv-top), revealed by an UNMASK from the base rather than a pop-and-fade,
     with the caption OUTSIDE the frame beneath it like a photo credit. The corner caption box and
     its scrim are gone. */
  position: absolute; left: 0; right: auto; top: 0; z-index: 3;   /* 10.09 (Chiara: "a bit jolty… make sure it's reaaaallly smooth"): the plate no longer sits on top/left. Both were LAYOUT properties under a CSS transition that was retargeted on every mousemove — a reflow per frame plus a 0.5s expo-out restarting from wherever it had got to, which reads as a soft stutter. It now rides ONE compositor transform (--pv-x/--pv-y, below) that main.js lerps toward the pointer every frame: no layout, no retargeting, one continuous curve. */   /* 01.09 v17 (Chiara: "the placement of the image hover should be different"): the plate now follows the POINTER horizontally (clamped to the section, JS sets --pv-x) while still tracking the hovered row vertically — the Lolo behaviour. Was flush to the section's right edge. */
  width: var(--plate-w);   /* 10.09: tokenised — shared with the /work plate · 01.09 v16 (Chiara: "10% here"): another 10% — 30vw/437 → 33vw/481 */ max-width: none; aspect-ratio: 16 / 9; margin: 0;   /* 01.09 v15 (Chiara: "and more 16:9"): the measured menu frame (580/326 ≈ 1.78 but a touch taller) gives way to a true 16:9 · v14 "bigger by 15%": 26vw/380 → 30vw/437 · v13 (Chiara: "the image hover over — I preferred the horizontal images"): back to LANDSCAPE; it sits inside one row band, so the plate no longer spans two lines · v10 was the 4/5 portrait plate */
  transform: translate3d(var(--pv-x, 60%), var(--pv-y, 50%), 0) translate(-50%, -50%);
  will-change: transform, clip-path;
  opacity: 1;
  clip-path: inset(100% 0 0 0);
  transition: clip-path var(--dur-slow) var(--ease-house);   /* 10.09: ONLY the unmask transitions — position is the JS lerp (was: top + left on the base rung, retargeted per move — the 09.09 "jolty" fix that moved the stutter rather than removing it) */   /* 09.09 (Chiara: "jolty"): left joins — the plate was stepping to every mousemove raw while top glided, so a diagonal move between rows read as a glide in one axis and a stutter in the other. Retargeted on each move, the base rung reads as a soft follow. */
  overflow: visible;
}
.disc--split .disc-preview.active { clip-path: inset(0 0 0 0); }
.disc--split .disc-preview::after { display: none; }             /* the foot scrim retires with the corner caption */
.disc--split .disc-preview .disc-preview-item { clip-path: inset(0); }
.disc--split .pv-cap {
  position: absolute; left: 0; right: auto; top: calc(100% + var(--space-sm)); bottom: auto;
  color: inherit; opacity: 0; white-space: nowrap;
  transition: opacity var(--dur-base) var(--ease-house) 0.15s;
}
.disc--split .pv-cap.active { opacity: 0.75; }

/* ============================================================
   21.10 — WHAT WE DO IN CROWN CREATIVE'S GRAMMAR (Chiara: "match the margins of how Crown Creative have done
   it, the font sizing, the size of the images, and the way that they animate — pull how they've done it").
   Their /studio "What We Do" (.work-component-text), read from app.css: a 1440px container with 2em sides;
   stacked rows, 25px top/bottom, a hairline each; titles capped at 66px, -2px tracking, leading = size; on
   hover the title slides 40px right over .6s, and a 40vw × 42vh image rises behind it (opacity 0 → 1, scale
   .8 → 1, .4s), seated right 2em; the whole block fades up 20px over 1.5s on arrival; under 1024 the titles
   cap at 45px, rows 20px, no image. The raw Crown durations and bare ease are DELIBERATE here — her brief was
   their motion, not ours. Modifier on the section; the split block above still seats the rest.
   ============================================================ */
.disc--crown .disc-sticky { padding: var(--space-section) var(--measure-inset); box-sizing: border-box; }
/* 21.10: the service pages take the measure (see --measure-inset). The ledger's rungs, the closing cards' heads and grids, the film stage and the FAQ all inset to it; the ground runs stay full-bleed because it is padding. */
@media (min-width: 901px) {
  .faq { padding-left: var(--measure-inset); padding-right: var(--measure-inset); }   /* 21.10 v2 (Chiara: "the process and Brands We’ve Built should take up the width of the website; Good Questions can be narrower — leave that"): only the FAQ keeps the measure; the ledger, the closing cards and the film stage go back to the gutter */
}
   /* 21.10 v3 (Chiara: "the margins were good — it was more the background"): the 1440 measure comes back as PADDING, not a max-width — the section stays full-bleed so its ground reaches the edges, while the content sits inside Crown’s 1440 with 2em sides */   /* 21.10 v2 (Chiara, the founder block: "the sides are lighter"): Crown’s 1440 cap DROPPED on all three sections — the founder paints its own ground on the scroll run, so a capped section left the body showing lighter either side on wide screens. 2em sides, full width, like the rest of the site */
.disc--voices .disc-sticky { padding-left: var(--measure-inset); padding-right: var(--measure-inset); box-sizing: border-box; }
@media (min-width: 901px) { body.home .fi { padding-left: var(--measure-inset); padding-right: var(--measure-inset); box-sizing: border-box; } }   /* 21.10 (Chiara, "review the layout of the founder section against the new margins" → "run it"): the founder block joins the 1440 container with 2em sides, so What We Do, the founder and the voices share one edge; the 100px --fi-margin seat retires here (it still serves /studio). Composition unchanged. */   /* 21.10 (Chiara: "can the testimonial section have the same width margins as the What We Do section"): the same 1440 container, 2em sides */
.disc--crown .svc { min-height: 0; padding: 0; }
.disc--crown .svc-label { margin: 0 0 35px; }
.disc--crown .disc-row { font-size: var(--type-disc); line-height: var(--lh-disc); letter-spacing: -2px; padding: var(--space-lg) 0;   /* 21.10 v2 (Chiara: "a bit more breathing space"): Crown’s 25px → our lg (40px) per row */ opacity: 1; white-space: normal; transition: transform 0.6s ease; }
@media (hover: hover) and (pointer: fine) { .disc--crown .disc-row:hover { transform: translateX(16px); opacity: 1; } }   /* 21.10 v4 (Chiara: "still quite jumpy — they jump out quite a lot when you hover"): Crown’s 40px slide → 16px on the same 0.6s, so the title leans rather than lunges */
.disc--crown .disc-rows { z-index: 2; }
.disc--crown .pv-cap { display: none; }   /* 21.10 (Chiara: "remove the tags on the images that pop up"): no caption under the plate — Crown’s tiles carry none */
.disc--crown .disc-preview {
  left: auto; right: 2em; top: 0; z-index: 3;   /* 21.10 v2 (Chiara: "the images are too covered by the lines"): the plate rides ABOVE the rows and their hairlines (Crown seats theirs beneath, z -1); the titles are short enough that it clears them */
  width: var(--disc-plate-w); height: auto; aspect-ratio: var(--disc-plate-ratio); max-width: none;   /* 26.10: one shape at every window size — see --disc-plate-ratio */
  transform: translate3d(0, var(--pv-y, 50%), 0) translateY(-50%); scale: 0.8;
  opacity: 0; clip-path: none; will-change: transform, opacity, scale;
  transition: opacity 0.4s ease, scale 0.4s ease;   /* 21.10 v3 (Chiara: "it jumps quite a lot when you hover, a bit jolty — smooth that"): the 0.4s TRANSFORM transition was fighting main.js, which lerps the plate’s y toward the hovered row every frame — each frame’s write restarted the transition, so the plate stuttered instead of gliding. The .8 → 1 now rides the separate scale property; transform is left to the lerp alone */
}
.disc--crown .disc-preview.active { opacity: 1; clip-path: none; scale: 1; }
.disc--crown .disc-preview.active .disc-preview-item { animation: none; }
.disc--crown .disc-main { opacity: 0; transform: translateY(20px); transition: opacity var(--dur-cinema) var(--ease-house), transform var(--dur-cinema) var(--ease-house); }   /* 25.10 (Chiara: "jolty — smooth on desktop too"): bare `ease` 1.5s → the house curve on the cinema rung, so the block's arrival and the hairline draw share one motion signature */
.disc--crown.lines-in .disc-main { opacity: 1; transform: translateY(0); }
@media (max-width: 1024px) {
  .disc--crown .disc-row { font-size: min(calc(3vh + 3vw), 45px); line-height: min(calc(3vh + 3vw), 45px); padding: 20px 0; }
  .disc--crown .disc-preview { display: none; }
}
@media (prefers-reduced-motion: reduce) { .disc--crown .disc-main { opacity: 1; transform: none; transition: none; } }
/* the list dims so the hovered line is the one you read — the Lolo device */
/* 01.09 (W4 audit, ACTIONED): 53 hover rules on this site and not one @media (hover:hover)
   gate. On a touch device a tapped element LATCHES its hover state until something else is
   tapped, so on an iPad tapping a discipline row left the whole list dimmed to 0.32 with a
   preview plate stranded on screen, and the letter-roll CTA latched half-rolled. These are the
   three the audit named as priority — the visible ones. */
@media (hover: hover) and (pointer: fine) {
  .disc--split .disc-rows:hover .disc-row { opacity: 0.32; transition: opacity var(--dur-base) var(--ease-house); }
  .disc--split .disc-rows:hover .disc-row:hover { opacity: 1; }
}
@media (max-width: 900px) {
  .disc--split .disc-sticky { padding: var(--space-section) var(--gutter); }
  .disc--split .disc-rows { max-width: none; }
}
/* --- the voices: THE REVELATIO SEAT (01.09, Chiara: "I like their testimonial section
   [revelatio.studio] — could we do this but more Crown Creative style, so no headshot of the
   client, and then a line that loads as a counting-down timer till the next one, like we had").
   Their composition: label top-left, ONE quote large in the middle of the frame, attribution
   small at the foot, Previous/Next bottom-right. Ours keeps the hairline that fills across the
   hold as the countdown (the Monolog device we already had) and drops the headshot — we have
   no portraits, and a face beside a quote was the 19.08 confusion anyway.
   The 30.08 two-column spread (pull-line left / quote right) is retired; the pull-lines stay in
   the markup, hidden, since they are Chiara's 27.08 ruling and may return as an eyebrow. --- */
.disc--split .disc-voice { margin-top: var(--space-2xl); order: 0; min-height: 82svh; display: flex; flex-direction: column; }
.disc--split .voices-label { text-align: left; margin: 0 0 var(--space-md); order: 0; }
/* 01.09 (Chiara, the Avec chrome screenshot: "let's do it like this"): the counter and the arrows
   move ABOVE the quote — ( 02 – 06 ) top-left with the total dimmed, round outline arrows top-right,
   and the countdown hairline running underneath them, filling across the hold. */
.disc--split .voices-top { order: 4; margin: var(--space-xl) 0 0; justify-content: space-between; align-items: center; gap: var(--space-lg); }   /* 01.09 v2 (Chiara: "should the load line and number arrows go to the bottom?"): the COUNTER + ARROWS drop to the foot so no chrome precedes the quote; the countdown hairline stays up top as the rule under the label */
.disc--split .voices-count { order: -1; margin: 0; font-variant-numeric: tabular-nums; }
.disc--split .voices-total { opacity: 0.45; }
.disc--split .voices-nav { gap: var(--space-md); }   /* 23.10: bare glyphs need the md gap the rings did not */
.disc--split .voices-btn {
  width: auto; height: auto; border-radius: 0; border: 0; padding: var(--space-xs); margin: calc(-1 * var(--space-xs));   /* 23.10 (Chiara: "make the arrows the same as on the case study page — and on mobile"): the 44px ring RETIRED — bare ← → glyphs like .cs-nav-arrow; the hover nudge (±4px, .voices-btn) is the affordance */
  display: grid; place-items: center; opacity: 0.85;
  transition: opacity var(--dur-quick) var(--ease-house), border-color var(--dur-quick) var(--ease-house), transform var(--dur-base) var(--ease-house);
}
.disc--split .voices-btn:hover { opacity: 1; }   /* 23.10: the ring is gone — the nudge carries the hover */
.disc--split .voices-bar { order: 1; width: 100%; }
.disc--split .voices-seat { order: 2; margin: var(--space-2xl) 0 0; flex: 1; align-content: center; }
/* 05.10 (Chiara: "when you click through the testimonials the text shouldn't jump — it should just transition seamlessly"):
   the voices share one grid cell, so the seat is already as tall as the longest quote — but align-content: center then sat
   each SHORTER quote lower down, so the headline jumped up and down between clients. Every voice now anchors to the TOP of
   the seat: the headline lands in the same place every time, and only the length below it changes. The swap itself is a
   shorter, smaller move (8px, not 24/20) so it reads as one surface changing, not a block moving. */
.disc--split .voices-seat .voice { align-self: start; }
.voice { transform: translateY(8px); }
.voice.out { transform: translateY(-6px); }
.disc--split .voice { display: flex; flex-direction: column; text-align: left; }
.disc--split .voice-lead { display: none; }                    /* the QUOTE is the statement now · 26.10: a lead-as-headline version was tried and reverted (Chiara: "doesn't feel very premium, feels a bit sassy") — the leads read as sales headlines */
.disc--split .voice-quote {
  font-family: var(--font-serif); font-weight: 300;
  font-size: clamp(22px, 2.6vw, 36px); line-height: var(--lh-intro); letter-spacing: -0.01em;
  max-width: 44ch; margin: 0;   /* 01.09 (Chiara: "the testimonials need to be wider — Mousumi Das is too long"): 30ch → 44ch and 40 → 36px, so the longest voice sits in the same frame as the shortest instead of running to nine lines */
}
/* 01.09 (Chiara: "maybe we need a mix of fonts" + "name / brand needs to be differentiated
   somehow, they look too same-same"): the attribution carries the mix — the PERSON in Eiko,
   sentence case, at reading size; the COMPANY in mono caps at the caption rung, dimmed. Two
   registers, so a name reads as a name and a brand reads as a label. */
.disc--split .voice-who { display: flex; flex-direction: column; gap: var(--space-sm); align-items: flex-end; align-self: flex-end; text-align: right; margin-top: var(--space-2xl); }   /* 01.09 (Chiara: "swap the side"): the attribution moves to the RIGHT, aligned right — the "Kind words" composition · gap xxs → sm ("more spacing between the name and brand") */   /* 01.09 (Chiara: "can the brand name go above the name"): stacked, company first */
.disc--split .voice-role { order: -1; }   /* the brand leads, the person signs beneath it */
.disc--split .voice-name {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-sm, 22px); line-height: 1.1; letter-spacing: -0.01em;
  text-transform: none; opacity: 1;
}
.disc--split .voice-role::before { content: "\2013\00a0"; }   /* 01.09 (Chiara: "have a – WAGNER BESPOKE"): an en dash leads the company line */
.disc--split .voice-role {
  font-family: var(--font-mono); font-weight: 400;
  font-size: var(--type-caption); text-transform: uppercase; letter-spacing: 0.04em;
  opacity: 0.55;
}
@media (max-width: 900px) {
  .disc--split .disc-voice { min-height: 0; }
  .disc--split .voice-quote { max-width: none; }
  .disc--split .voice-who { flex-direction: column; gap: var(--space-xxs); }
}
/* THE WALL'S GROUND (30.08): the wall enters from under the dark voices and LIGHTENS as it rises —
   --wf 0 → 1 across its climb (main.js), dark → paper, cream marks → ink — so the Method below
   arrives on paper as before and there is no colour edge anywhere in the chapter. */
.clients { padding-top: var(--space-lg); padding-bottom: var(--space-2xl); }   /* 12.10 (Chiara's 19.47 screenshot: the empty taupe under the logos): bottom 3xl → 2xl now the founder block below is in flow with its own section padding — the 3xl was holding air for the old split's 100svh image */   /* 01.09 v2 (Chiara: "i dont like the colour fade of the our clients work banner"): THE STATIC GRADIENT IS GONE. It was linear-gradient(180deg, ground-3 0%, ground-3 62%, ground-2 80%, paper 100%) — a fade painted DOWN the band and fixed in place, so scrolling past it walked your eye down a gradient instead of moving the colour. The band is now ONE flat ground crossfading dark → taupe → paper as it crosses the viewport: the opening statement's device (--cw1/--cw2 rule above). Her 01.09 padding stays: top lg ("can we bring the logos up"), bottom 3xl. */
.clients .client-logo { background-color: currentColor; }

/* 30.08: THE METHOD'S EXIT — whatever section FOLLOWS the Method rides its exit ground vars
   (main.js sets --mx1/--mx2 on the Method's next sibling) so the dark → paper return has no
   hard line. Was a .founder rule (29.08); the Founder now sits above the Method, so it's
   the Enquire. Generic, so the next reorder costs nothing. */
.method-intro--teaser + * {   /* 01.09: was `+ section`. The Enquire section retired into the footer and the element that follows the Method is now a div (.footer-runway), so the sibling selector silently stopped matching and the Method's dark → paper return would have ended on a hard line — the exact thing Chiara has flagged repeatedly. `+ *` matches whatever is there, which is also what main.js already does (mxWall = mxEl.nextElementSibling). */
  background: color-mix(in srgb, var(--ground-3) calc(var(--mx2, 0) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--mx1, 0) * 100%), var(--paper)));
  color: color-mix(in srgb, var(--cream) calc(var(--mx1, 0) * 100%), var(--ink));
}

/* 30.08: THE DECK'S HEADING — the What We Do title treatment, ahead of the pinned case-study deck.
   Same ground as the deck (--ground-3) so there is no edge between the heading and the pin. */
.work-head {
  background: var(--ground-3); color: var(--cream);
  padding: var(--space-2xl) var(--gutter) var(--space-lg);   /* 21.10: symmetric sides, so the centred label is truly centred (the burger-cluster inset only mattered while it was left-aligned) */   /* 30.08: lg below the title — the deck follows immediately */
  margin-bottom: 0;   /* 30.08 (Chiara's 15.10 screenshot — the cream stripe): the heading was inheriting a 64px bottom margin from the generic section rule, and the paper <main> showed through it. The deck must butt straight up against the heading. */
  position: relative; z-index: 2;
}
.work-head-label { margin: 0 auto; text-align: center; opacity: 0.8; }   /* 21.10 (Chiara: "( Brands We’ve Built ) is not going to the centre of the section — I want it in the middle"): the HOMEPAGE deck head, not the service pages’ (those were centred earlier) */   /* 01.09: mono label (type from .section-label) — was the Eiko phase-rung .svc-title */
@media (max-width: 900px) { .work-head { padding: var(--space-xl) var(--gutter) var(--space-lg); } }

/* 01.09 (Chiara, loloagency.com/work: "maybe we need a cursor View with arrow on hover over too"):
   THE VIEW CURSOR. A small cream pill that follows the pointer while it is over anything that
   opens a case study — the deck's slide links and the work-index rows. Pointer devices only, and
   it never appears for reduced motion or touch. Add data-cursor="view" to opt an element in. */
.cursor-view {
  position: fixed; left: 0; top: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center;
  width: 78px; height: 28px; padding: 0; border-radius: 200px;
  /* 21.10 (Chiara: "could the view buttons on the homepage case studies match the ones on the work list page"): the
     deck's VIEW cursor takes the work index's .wi-cursor pill exactly — 78 × 28, solid ink, cream label, 11px label
     face, uppercase on the label tracking. The 01.09 frosted chip (22% cream wash, blur, hairline, the ↗) retires. */
  background: var(--ink); border: 0; color: var(--cream);
  font-family: var(--font-label); font-size: 11px; letter-spacing: var(--track-label); text-transform: uppercase; white-space: nowrap;
  pointer-events: none; opacity: 0; transform: translate(-50%, -50%) scale(0.86);
  transition: opacity var(--dur-quick) var(--ease-house), transform var(--dur-base) var(--ease-house);
  will-change: transform, opacity;
}
.cursor-view.on { opacity: 1; transform: translate(-50%, -50%) scale(1); }
@media (hover: none), (prefers-reduced-motion: reduce) { .cursor-view { display: none; } }
.disc--split .disc-row sup { margin-right: 24px; }   /* 01.09 v11: the index column tightens to buy the rows room */

/* 03.09 — THE HEADER LOCKUP (Chiara: "on the homepage the burger menu on the right with Let's Talk next to
   it … the logo zooms up to the top left … and then that rolls out across all of the cases" → "all of the
   pages"): IDENTITY LEFT, ACTIONS RIGHT. The mark leaves the centre cell for column 1 and the burger leaves
   column 1 to join the CTA in column 3, so the corner reads "Let's Talk · ☰" as one control group instead
   of two actions split either side of the wordmark. Site-wide, deliberately: the header is fixed and
   persists across navigation, so a homepage-only lockup would make the furniture jump on the first click.
   The homepage keeps its ARRIVAL (the mark fades up on first scroll, .wordmark-visible); interior pages
   simply start with it in place. The 1fr auto 1fr grid is unchanged — only the seats move. */
.site-header { grid-template-columns: 1fr auto auto; column-gap: var(--space-md); }   /* was 1fr auto 1fr with the mark in the middle cell; now: mark | CTA | burger, the last two adjacent at the right edge */
.site-header .wordmark { grid-column: 1; justify-self: start; }
.site-header .header-right { grid-column: 2; }
.site-header .header-left { grid-column: 3; justify-self: end; }

/* 03.09 — THE HEADER'S SCROLL STATES (Chiara, Palmer ref). .is-hidden lifts it out on the way down;
   .is-float brings it back on a frosted ground on the way up. Palmer's bar is a centred pill; this is a
   full-bleed band instead — Aura retired pills in July and the CTA here is type-only, so a rounded panel
   would reintroduce the one shape the design does not use. Tint follows the header's own tone class, so it
   frosts dark over film and light over paper. Say the word and it becomes an inset pill like Palmer's. */
.site-header.is-hidden { transform: translateY(-140%); }
.site-header.is-float {
  padding-bottom: var(--space-sm);
  background: color-mix(in srgb, var(--paper) 72%, transparent);
  backdrop-filter: blur(18px) saturate(1.1);
  -webkit-backdrop-filter: blur(18px) saturate(1.1);
  border-bottom: 0.5px solid color-mix(in srgb, var(--ink) 12%, transparent);
}
.site-header.is-float.on-dark {
  background: color-mix(in srgb, var(--ground-3) 62%, transparent);
  border-bottom-color: color-mix(in srgb, var(--cream) 14%, transparent);
}
@media (prefers-reduced-motion: reduce) { .site-header { transition: none; } }

/* 03.09 (Chiara: "maybe the sound icon can go bottom right"): the sound toggle leaves the header cluster
   for its own corner — it is an ambient control, not navigation, and in the new lockup it was the third
   thing in a corner that now holds two. Fixed, so it never rides the header's hide/float. */
.sound-toggle {
  position: fixed;
  left: var(--gutter);            /* 03.09 v2 (Chiara chose option 1): bottom-LEFT. Bottom-right sat straight on top of "Cookie Policy" once the footer arrived; the left corner clears the reading column and the policy row is handled by the fade below. */
  bottom: var(--gutter);
  z-index: 61;
  transition: opacity var(--dur-base) var(--ease-house), visibility var(--dur-base), color var(--dur-base) var(--ease-house);
  /* 10.09 (Chiara: "add the music button to the homepage video and the showreel video"): the button was THERE — and
     invisible. Leaving the header on 03.09 also left the header's on-dark cream behind: `color: inherit` now took the
     body's ink, so over both hero films (fabric + reel) the toggle painted ink on near-black. It now carries its own
     ground colour: ink at rest, cream while imagery or a dark ground sits under its box (.on-dark, main.js samples
     the corner on the same scroll tick as the footer parking). */
  color: var(--ink);
}
.sound-toggle.on-dark { color: var(--cream); }
/* 12.10 (Chiara: sound on / sound off as TEXT): the toggle is a mono label now, not a 23px glyph. Width follows the
   words; the two states swap by the .playing class main.js already sets. The .wave/.strike rules above are idle. */
.sound-toggle { width: auto; height: auto; white-space: nowrap; display: inline-flex; align-items: center; gap: var(--space-xxs); }   /* 12.10 v4 (Chiara: "the icon with ON next to it — no brackets"): glyph + bare state word in one row · v5 ("reduce the spacing between the sound and on text"): xs → xxs, one token step */
.sound-toggle svg { width: 23px; height: 23px; flex: none; display: block; }
.sound-toggle .st { display: none; margin: 0; }
.sound-toggle:not(.playing) .st-off, .sound-toggle.playing .st-on { display: block; }
/* it steps aside for the close: the footer's own furniture owns the bottom edge there, and an ambient
   control has nothing to say over the ask. JS adds .sound-parked as the reveal begins. */
body.sound-parked .sound-toggle { opacity: 0; visibility: hidden; pointer-events: none; }
/* 10.09: and for the MENU. In the corner the toggle lands exactly on the overlay's foot wordmark (both sit at the
   left gutter, bottom edge) — ink on ink, a dead click on the mark. It steps aside while the overlay is open, as it
   does for the footer; the music keeps playing and the control is one close away. The 14.08 "sound icon on the menu
   nav" ruling was written for the header seat beside the X — flagged to Chiara for a re-rule if she wants it back
   in the overlay, which would mean giving it its own seat there. */
body.menu-open .sound-toggle { opacity: 0; visibility: hidden; pointer-events: none; }
/* 10.09 (Chiara: "when the logo loads on the very first landing the toggle shouldn't be visible"): the landing frame is
   the film and the big mark alone — the toggle arrives with the header chrome once the mark has landed (intro-flown,
   main.js landCut/land). Reduced motion never runs the intro, so it is simply present there. */
body.home:not(.intro-flown) .sound-toggle { opacity: 0; visibility: hidden; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { body.home:not(.intro-flown) .sound-toggle { opacity: 0.75; visibility: visible; pointer-events: auto; } }

/* 03.09 (Chiara: "Let's Talk should be aligned with the burger menu — it's much lower down, can you shift
   it up" · "and the left logo should be aligned too"): ONE LINE FOR ALL THREE. .header-left already had
   height: var(--wm-h) with its items centred (27.08, when the burger sat off the mark); the CTA never got
   the same treatment and the mark itself sits on the grid's start edge, so the three landed on three
   different baselines. Same box, same centring, for each. */
.site-header .wordmark, .site-header .header-right, .site-header .header-left { grid-row: 1; }   /* 03.09: without this the auto-placer put the mark and the CTA on a SECOND row — the burger is first in the DOM and claims column 3, so the cursor had already passed columns 1–2. That phantom row was the 21px drop Chiara saw between "Let's Talk" and the burger. */
.site-header .header-right, .site-header .wordmark { height: var(--wm-h); align-self: start; }
.site-header .header-right { align-items: center; }
/* start, not centre: the grid row is taller than the mark (the burger cluster sets it), so centring dropped
   the mark and the CTA 21px below the burger — the very gap Chiara was pointing at. All three now hang from
   the same top edge and share one 21px box, so their midlines agree. */

/* 03.09 (Chiara: "make sure the left and right margins and the top margin match" → "so move it up more —
   the menu items"): the header's four insets already resolved to --gutter exactly (measured 32/32/32/32),
   but they MEASURE equal and READ unequal: the wordmark's glyphs sit inside their box with leading above
   them, so the optical top gap was ~8px deeper than the sides. The row lifts by that much — an optical
   correction, not a new value; the sides and the sound toggle stay on --gutter, which is what the eye
   compares them against. */
.site-header { padding-top: calc(var(--gutter) - 8px); }
/* 19.10 THE PHONE HEADER (Chiara, /work gallery on a phone: "check the padding above the menu nav… needs more space" ·
   "Let's Talk looks v small, bump the text by 5px" · "the List/Gallery buttons are big, make them smaller, and move
   them down"): at the 16px phone gutter the rule above left 8px over the wordmark — desktop's 24 on the md rung
   instead. The pill was seated off --header-h (66, the desktop line) while the real phone header is ~22px tall, so
   it rode the wordmark row; now it hangs from the header's own stack (pad + mark + sm) and steps down to 124×26.
   The CTA size floor and its line-box guard live on the token / .cta--header rules. */
@media (max-width: 700px) {
  .site-header { padding-top: var(--gutter); }   /* 19.10 later (Chiara: "match the padding left and right to the top — 24 is too much"): the 16px gutter on all three sides */
  /* 19.10 later (Chiara: "the burger needs to be more elegant on mobile… reduce the line… and Let's Talk closer"): the
     mark steps from 28 × 1 lines 8px apart to 22 × 1 lines 6px apart; the gap to the CTA from md (24) to sm (16). Phone only. */
  .menu-toggle { width: 22px; height: 7px; opacity: 0.7; }   /* 20.10 (Chiara: "change the burger colour to match the text — is it the opacity?"): yes — the CTA sits at 0.7, the mark at 1; the burger's two full-strength lines read brighter than the words. 0.7, as the CTA. */
  .menu-toggle::before, .menu-toggle::after { width: 22px; }
  .menu-toggle::after { top: 6px; }
  .menu-toggle:hover::after { width: 14px; }
  .menu-toggle.open::before, .menu-toggle.open::after { top: 3px; left: 3px; width: 16px; }   /* the X re-centred for the 22 × 7 mark */
  .site-header { column-gap: var(--space-sm); }   /* the header is a grid [mark | CTA | burger]; its column gap IS the CTA-to-burger distance — md → sm */
  /* 20.10 (Chiara: "the services look off"): the Services ROW is a desktop line — at 390 it wrapped after WEB DESIGN
     into a ragged two-line cluster, the label reading as a fifth word. Layout flip only: label on its own line, the
     four links a left-aligned mono column on the sm rung — the menu foot's row grammar, stood upright. Type untouched. */
  .menu-secondary { flex-direction: column; align-items: flex-start; gap: var(--space-sm); }
  .menu-secondary-label { margin: 0 0 var(--space-xs); }
  .view-toggle { top: calc((var(--gutter) + var(--wm-h) + var(--header-h) + var(--space-xl)) / 2 - 13px); width: 124px; height: 26px; }   /* 19.10 later (Chiara, screenshot: "the pill sits central to this"): CENTRED between the header line (gutter + mark) and the column heads (work-main's top = header-h + xl) — half the span, minus half the pill. Arc: sm → md → midpoint → centred → +8 tried and reverted same minute: measured glyph-to-glyph (logo art bottom 28 → PROJECT cap top 133 at 390) true centre is 40 / 39, so the formula stands. */
}

/* 04.09: the frost applied to .hero-intro-cta here was REVERTED within the hour — Chiara meant the case-study
   cursor/hover "View Work" chip, not the hero's ( View Our Work ) label. The hero label stays bare (v27). */

/* 04.09 (Chiara: "when you scroll up the menu bar comes up — I don't like that design, hide that for now"):
   THE FROSTED BAR IS OFF. The header still lifts out on the way down and still returns on the way up, but
   it returns as it lives at the top — no ground, no blur, no rule. The .is-float class is still set by JS
   so the treatment can come back in one rule if it is ever wanted; it simply paints nothing now. */
.site-header.is-float,
.site-header.is-float.on-dark {   /* 04.09 (Chiara: "there is a weird menu bar that appears when you scroll up"): the DARK variant had to be
     named too. The first attempt only reset .is-float (0,2,0) while .is-float.on-dark (0,3,0) kept painting
     a 62% near-black band over film — which is exactly the bar she was still seeing. */
  padding-bottom: 0;
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: 0;
}

/* 04.09 (Chiara: "the text in the footer is too big — can it be the same size as the footer text we've used
   across the case studies"): the ask's serif rung is now ONE value site-wide. It was --type-phase (64px)
   on the homepage and --type-serif-lg (44px) everywhere else, a split that only existed because the
   homepage got the treatment first. 44px + the 780 cap, as on every case study. */
.footer-ask-statement { font-size: var(--type-serif-lg); max-width: 780px; }
/* 20.10 (Chiara, the footer on a phone: "reduce the font of this, looks too big… also reduce the hello@ and IG and LinkedIn"):
   the ask statement steps lg → md (30 → 24 at 390), the CTA to the phone label, the mail / socials / legal to the caption rung. */
@media (max-width: 760px) {
  .site-footer.site-footer--reveal .footer-ask .footer-ask-statement, .site-footer .footer-ask-statement.footer-ask-statement { font-size: var(--type-intro); line-height: var(--lh-intro); max-width: none; }   /* 25.10 (Chiara: "the footer line looks big — make it the same size as the intro text"): 28 → serif-md-up (26) at 1.22, the origin statements' size and leading    /* 20.10 (Chiara: "make footer 24" → "sorry, bigger" → "28?"): 28px on the phone. Arc: 22 → 23 → 25 → 23 → 25 → 26 → 24 → 26 → 28, three lines on a 390 phone — "running / ahead of your brand – you / already know." The <br> after "ahead" stays in the markup for desktop; here it is neutralised so the phone wraps naturally. Arc: 22 → 23 → 25 → 23 → 25 — NOTE the first line can no longer hold "…running ahead" at 358px, so it breaks after "running" (23px was the ceiling for the "ahead" break; measured). 22px on the full 358 the line breaks after "ahead" — "If your reputation is running ahead / of your brand – you already know." (24 broke after "running"; measured) */
  .site-footer .footer-ask-statement br { display: none; } .site-footer .footer-ask-statement.footer-ask-statement { text-wrap: balance; }   /* 25.10 (W5 audit S2): the desktop's hand-set breaks left "up" alone on its line at 390 and six lines at 360 — on phones the line flows and balances */
  .site-footer.site-footer--reveal .footer-ask .cta.cta--large, .site-footer .footer-ask .cta { font-size: var(--type-cta-phone); }   /* 25.10 (Chiara: "make sure they’re equal"): 12 → 14, the phone CTA size */
  .footer-mail, .footer-socials a, .footer-links a { font-size: 11px; }
  /* 20.10 (Chiara: "your reputation deserves on the 2nd line"): the break after "brand" holds on the PHONE too — the first line fits at 26px, so the forced break reads the same at every width. (The old .desk-br hide, from the "running ahead" line, is retired.) */
  .cs-voice .cs-voice-label { margin-bottom: var(--space-sm); }   /* 20.10 (Chiara: "should What Our Client Says be the same distance from the testimonial as the ( ) tags to their paragraphs?"): yes — the story rows sit 16px under their label on the phone; the testimonial was 64 */   /* the forced break after "ahead" is desktop-only (the markup carries a real space after it, so hiding the <br> never fuses the words); the phone wraps the 26px line naturally */
}

/* 04.09 (Chiara: "the Aura method: 5 phases, 1 standard — can't read it"): the teaser lead sits over the
   portrait lockup, so on a light frame it was cream on cream. The site's legibility insurance (the same
   shadow the footer ask carries over moving footage) rather than a scrim — the lockup's imagery stays
   untouched and the line holds on any frame. */
.method-intro--teaser .method-intro-lead { text-shadow: 0 1px 40px rgba(0, 0, 0, 0.55); }
/* 04.09: the dashed "Portrait to come" placeholder is RETIRED (Chiara: "that looks weird"). Its rules are
   gone rather than parked — an empty .founder-media already collapses, and the section stands on its copy. */
/* 04.09 — MEET THE FOUNDER, TWO COLUMNS (Chiara: "it needs to be a stand-alone photo on the right"): copy
   left, portrait right, the portrait a plain standing image rather than a full-bleed band. Arms itself the
   moment a real asset is in the figure, so nothing changes until the photo lands. */
/* 07.10: the first real image armed this and the 04.09 `order: 2` + 1fr/0.85fr columns were still live here, UNDER the
   09.09 split (portrait LEFT, copy right) — the figure has grid-column: 1, so `order: 2` moved it to a SECOND ROW
   under the copy and the section ran 1582px tall. Both retired; the 09.09 block above is the one composition. */
.founder:has(.founder-media:not(:empty)) { align-items: center; }   /* 12.10: back to center with the split (option B's top-align went with it) */
.founder:has(.founder-media:not(:empty)) .founder-media img,
.founder:has(.founder-media:not(:empty)) .founder-media video { aspect-ratio: 4 / 5; object-fit: cover; }   /* 11.10: video joins — the seat holds the founder film now; without this the 9:16 clip ran full height on a phone */

/* 04.09 — THE HOMEPAGE'S CLOSE. It has its own .footer-runway (the empty band the reveal mask dissolves
   through) where the interior pages use the ::after spacer, and that runway was painted --paper. So the
   Method went dark, the runway went cream underneath it, and the film washed up into cream — the one page
   whose close did not match. The runway takes the close ground; the spacer stays off here so the clearance
   is not counted twice. */
body.home .above-footer::after { display: none; }
/* the runway takes the close ground from the rider rule now, not a flat dark — same timeline as the Method above it */

/* 04.09 (Chiara: "the Aura method needs to go to dark again and then it reveals the footer"): the teaser
   ground is HELD DARK on the homepage. Its colour is driven by --mx1/--mx2 (main.js), and those are not
   being set on this page at all — measured empty at every scroll position — so the section has been sitting
   on its 0/0 default, which is paper. That is why the close showed a cream band above the dark runway. This
   pins the end state the copy was written for; the scripted paper → taupe → dark ARRIVAL is a separate,
   real bug (the driver never runs here) and wants its own pass, not a patch inside this one. */
/* 04.09 v2 (Chiara: "the background for Aura needs to fade in and out … Meet Chiara goes, Aura starts on
   light, it moves to dark, it reveals the footer"): the teaser's ground is DRIVEN BY THE CLOSE RUN now,
   instead of being pinned dark (v1) or sitting on its dead default (paper). Its own two vars are mapped
   onto the close's: --cx2 1→0 is paper→taupe, --cx1 1→0 is taupe→dark, and the Method reads them the other
   way up, so mx1 = 1 - cx2 and mx2 = 1 - cx1. One timeline for the Method, the runway and the melt: the
   section lightens in, travels to near-black, and the film comes up out of the same colour. */
body.home .method-intro--teaser .method-intro-sticky {
  --mx1: calc(1 - var(--cx2, 1));
  --mx2: calc(1 - var(--cx1, 1));
}
body.home .footer-runway.close-rider {   /* the runway carries its own --paper further down the sheet; matched specificity so it rides the run like everything else */
  background: color-mix(in srgb, var(--paper) calc(var(--cx2, 1) * 100%), color-mix(in srgb, var(--ground-2) calc(var(--cx1, 1) * 100%), var(--ground-3)));
}

/* 04.09 — THE WALL, TWO ROWS (Chiara: "can we add another strip of logos"). The second row travels the
   opposite way, which is what stops two parallel lines reading as one block sliding sideways. Row B is
   offset half a step so the marks never line up into columns. */
.clients .marquee-track--b { margin-top: var(--space-lg); }
.clients .marquee-track--b { transform: translateX(calc(var(--marq-b, 0px))); }

/* 04.09: .hw-brand and .hw-next are no longer in the homepage deck (Chiara: "keep it clean"). Their rules
   stay in place — the /work index still uses the same slide grammar — but nothing on the homepage matches. */

/* 04.09 (Chiara: "the Start a Project needs to be smaller"): the footer CTA drops to the standard label
   rung everywhere. The homepage was the only page still on --type-label-lg (18px), a rung invented on
   01.09 when this was the site's one big conversion; now that the ask is 44px on every page, an 18px CTA
   under it read heavy. 14px, as the case studies have carried all along. */
.footer-ask .cta--large { font-size: var(--type-label); }

/* ============================================================
   09.09 TYPE FLIPS, IT DOES NOT CROSSFADE (Chiara, on Trusted By: "once it goes to the taupe background,
   all of the text and all of the logos completely disappear… whatever the background is, it's still got
   to have the inverted colour of the content"). MEASURED: contrast 1.39 at the middle of the wall's
   taupe → paper half. Every scroll-driven ground on the site fades on two vars, and the TYPE was fading
   with it on the same vars — so ground and type passed through the same mid-tone at the same instant.
   The ground keeps its fade. The type now SWITCHES, once, 15% into the taupe side of each run (JS sets
   .type-ink on the wall's run and body.close-cream on the close run), on the 0.15s micro-fade (the one
   sanctioned exception to the rungs) so the switch itself never shows a mid-tone. Flip point 8%: taupe is
   where cream and ink contrast are equal (~4:1), so the switch sits on the crossover. Ink on the light side, cream on the dark side, never a mix. The logos are
   currentColor masks, so they follow.
   ============================================================ */
.clients { color: var(--cream); transition: color 0.15s ease-out; }
.clients.type-ink { color: var(--ink); }
.clients .client-logo { transition: background-color 0.15s ease-out; }
.close-rider { color: var(--ink); transition: color 0.15s ease-out; }
body.close-cream .close-rider { color: var(--cream); }
body.home .founder.close-rider, body.home #founder.close-rider { color: var(--ink); transition: color 0.15s ease-out; }
body.home.close-cream .founder.close-rider, body.home.close-cream #founder.close-rider { color: var(--cream); }
body.home .founder.close-rider, body.home #founder.close-rider { transition: color var(--dur-slow) var(--ease-house); }   /* 25.10 (Chiara: "from We came from the world we design for to the next bit there's a funny switch of font colours — it's glitchy, smooth that"): the In Short type (statement, labels, sign-off, link) crosses ink → cream over the slow rung instead of snapping in 0.15s, so it eases with the darkening ground */
/* 12.10 (Chiara, branding, 17.45 screenshot: the FAQ as a cream box on the held dark — "should be no cream background"):
   on a page whose ground is HELD (body carries data-ground), the close riders (the last two sections of main) take the
   page's ground and type instead of starting their paper → dark run from cream. The run's end colour is --ground-3
   either way, so the melt into the footer film is unchanged. */
body.has-page-ground[data-ground] .close-rider { background: var(--pg-bg); color: var(--pg-fg); transition: background-color var(--dur-ground) var(--ease-house), color var(--dur-base) var(--ease-house); }
body.home .method-intro--teaser .method-intro-sticky { color: var(--ink); transition: color 0.15s ease-out; }
body.home.close-cream .method-intro--teaser .method-intro-sticky { color: var(--cream); }
body.home .method-intro--teaser .method-intro-title { color: var(--title-ink); transition: color 0.15s ease-out; }   /* 10.09: the lockup title in the taupe while the ground is paper… */
body.home.close-cream .method-intro--teaser .method-intro-title { color: inherit; }
/* 22.10 (Chiara: "the bit over the image needs the text treatment" / "not working"): the two title rules above (0,3,1)
   OUTRANKED .mi-clip .mi-over (0,2,0) — the clipped twin painted in the title's own taupe, so nothing happened where the
   letters cross the photo. The twin now carries its own rule at the same weight: solid cream over the frame in both
   ground states (the intro's treatment). */
body.home .method-intro--teaser .method-intro-title.mi-over, body.home.close-cream .method-intro--teaser .method-intro-title.mi-over { color: var(--frost); }   /* 22.10 v4 (Chiara: "Aura Me needs a lighter invert"): the site’s FROST — cream at 58%, the deck-title value — on the letters that cross the train window, both grounds; solid cream read heavy over the frame */   /* 22.10 v3 (the E&O train still, Chiara: "the text over the image needs to be lighter — the opposite of the image"): the frame is a dark interior, so the twin is CREAM on both grounds; the v2 ink twin vanished into it */   /* …cream with the rest once the ground has gone dark */
/* the origin statement rides the same rule, and its over-image twins carry the INVERSE — ink over a
   photograph while the type is cream, cream while it is ink — so a float sliding under a word never
   takes it with it (Chiara: "the text over the images is white and it disappears — it needs to invert") */
.origin { color: var(--ink); transition: color 0.15s ease-out; }
.origin.type-cream { color: var(--cream); }
.origin-claim--over, .origin-body--over { color: var(--frost) !important; pointer-events: none; user-select: none; -webkit-user-select: none; z-index: 2; transition: color 0.15s ease-out; }   /* 12.10 (Chiara: "the text hover effect I'm talking about is what we used for Palmer Studio" → the deck's 16.07 frost, cream at 58% over imagery): the twins take the SAME frost instead of solid cream. For the frost to read, the ink original must not paint underneath it — main.js clipTwins now cuts the float boxes OUT of the original (evenodd polygon), which also removes the dark anti-alias fringe she saw on "You've" and "know" over the elephant (cream glyph painted exactly over an ink glyph leaves the ink edge pixels showing). Supersedes 29.08 "cream not grey" — that was solid-cream-vs-grey; this is the Palmer frost she has ruled on twice since. */   /* 12.10 (Chiara pasted the statement six times over: "check the text effect over this"): the twins are paint, so they leave the selection — copying the claim now yields ONE copy, not the original plus five clones */
.origin-claim--over, .origin-body--over { color: var(--cream) !important; mix-blend-mode: normal; }   /* 21.10 v2 (Chiara: "should be inverted in beige tones — why is it blue?"): difference is a per-pixel negative, and the negative of a warm frame is BLUE. Back to the clipped-twin treatment, but at full cream rather than the 58% wash — the text reads as cream wherever a photograph passes beneath, ink everywhere else. The hour of difference on the claim is in git. */
.origin.type-cream .origin-claim--over, .origin.type-cream .origin-body--over { color: var(--frost-ink) !important; }   /* 12.10: the inverse frost on the dark beat — ink at 58% over the light floats */
@media (max-width: 700px) { .origin.type-cream .origin-claim--over, .origin.type-cream .origin-body--over { color: var(--ink) !important; } }   /* 23.10 (Chiara, phone: "Strategy-led over [the image] needs to have an invert"): on phones the strip passes straight behind the credential, so the over-image twin is SOLID ink in the dark beat — the full invert of the cream paragraph, not the desktop's 58% frost (which read as nothing over the photos: "Start a project I can’t read") */
.origin-body--over span, .origin-body--over .origin-cta { opacity: 1 !important; }   /* the twin's words are always lit — the image itself is the tone */
.origin-body--over .origin-cta .ch { transform: translateY(0); }   /* 23.10 (Chiara, phone: "Start a project over the white video can’t read"): the twin is cloned before the original’s .ready lands, so its letters sat rolled out of view (translateY 105%) — where the strip crossed the link, the original was holed out and the twin showed nothing. The twin’s letters are simply in place */
/* 09.09: THE INVERT (02.09) RETIRES. mix-blend-mode: difference on the credential gave a per-pixel negative
   in Chromium, but for Chiara (Safari) the blend was not applied at all — the text painted in its white source
   colour and vanished over the light floats ("the text over the images is white and it disappears"). Safari
   drops blends when the blended layer AND the backdrop are both composited, which is exactly this stage (the
   floats and the type layers all carry will-change + transforms). The credential now uses the CLAIM'S device,
   which she has seen working since 28.08: solid type on the flip colour, and clipped twins in the inverse
   over each float. One mechanism for both layers. Caveat: a twin is one colour per image, so it reads on the
   light interiors this set uses; a near-black float would need a cream twin — pick floats accordingly. */
.origin-body p:not(.label) { mix-blend-mode: normal; color: inherit; }   /* the white blend source retires with the blend */
.origin-body { color: inherit; }
.origin-body--over p:not(.label) { mix-blend-mode: normal; color: inherit; }
.origin-body--over .rv, .origin-claim--over .rv { opacity: 1 !important; transition: none !important; }   /* transform is synced from the original per frame (main.js clipTwins) */   /* 09.09: a twin is cloned with the house-rise class but never receives its .in — it must sit exactly where the original has risen to, not 44px below it */

/* ============================================================
   09.09 THE PINNED HERO (Chiara: "something about our hero shrinking doesn't work for me" · OH Architecture
   and Backhouse refs: "the next section scrolls up and over the hero"). Behind body.hero-pin, so the Vero
   contraction (13.08) is one class away. MEASURED on oharchitecture.com.au: the hero image is pinned at 115%
   and does not move for two screens; the hero copy drifts up at ~0.28× scroll; the next section is plain
   flow with a higher z-index, pulled up one screen, and simply rides over. That is the 03.07 curtain
   slide-over this page used to have — restored on the same hooks (--dark, the origin's 88% ground so the
   film ghosts through). Nothing shrinks. The header flips to ink when the origin's ground passes it (the
   existing rect logic), not when the frame recedes.
   ============================================================ */
body.hero-pin .opening { height: 200vh; }                                   /* one screen to look, one screen of ride-over */
body.hero-pin .opening-sticky { background: var(--ground-3); }              /* the film's own dark stands in behind it — no beige to reveal, nothing shrinks */
body.hero-pin .hero-media-wrap { clip-path: none; will-change: auto; }      /* full-bleed, pinned, still */
body.hero-pin .hero-media { transform: scale(1.06); }                       /* OH's 1.15 read as a crop on our 16:9 reel; a hair of scale hides the edge as the cover's shadow lands */
body.hero-pin .hero-intro { opacity: calc(1 - var(--ride, 0) * 2); transform: translateY(calc(var(--ride, 0) * -28vh)); will-change: transform, opacity; }   /* the copy drifts up at ~0.28× under the cover, gone by half the ride */
body.hero-pin .origin { margin-top: -100vh; position: relative; z-index: 2; }   /* the ride-over: the statement climbs the pinned film on its 88% ground */
body.hero-pin .hero-wordmark { --wmp: var(--wmf, 0); }                      /* the scroll contraction of the mark retires with the shrink — the intro cut owns the mark now */
@media (min-width: 701px) and (max-width: 900px) { body.hero-pin .opening { height: auto; } body.hero-pin .origin { margin-top: 0; } }   /* static flow on tablets, as every pin · 23.10: phones LEFT this rule — main.js puts body.hero-pin on at ≤700 (Chiara: "the video shouldn’t shrink as you scroll, it needs to swipe up"), so the phone gets the ride-over: the film pinned and still, the origin climbs over it */

/* ============================================================
   LEGAL PAGES — /terms, /privacy, /cookies
   25.09 (Chiara: "apply this to the footers — create footer pages based on our old website"). The three
   footer links had pointed at /terms, /privacy and /cookies since 24.08 with nothing behind them.
   v2 same day (Chiara: "quite narrow text… can we improve it in any way?"): the v1 sticky title + one long
   column left a dead left third and a 644px strip with cream to its right. Now the LEDGER GRAMMAR the site
   already uses for its rows (drawn hairlines, ruled-row router): the head is rung + title LEFT, the opening
   paragraph RIGHT as a small-serif lead; then every section is a ruled row — hairline across the page, the
   section name in the mono register in the left column, its clauses in the right at a 72ch measure. The
   running headings give the left column a job and make a 14-section notice scannable. Tokens only: the
   offset grid (1fr 2fr), --type-serif-lg / -sm / -body / -label, the space ladder, the 25% currentColor
   hairline from the ledger and FAQ rules. Phones: rows stack, heading above clauses.
   ============================================================ */
.legal { padding: calc(var(--header-h) + var(--space-xl)) var(--gutter) var(--space-section); }
.legal-head, .legal-sec {
  display: grid;
  grid-template-columns: 1fr 2fr;
  column-gap: var(--space-lg);
  align-items: start;
}
.legal-head { padding-bottom: var(--space-xl); }
.legal-head .label { opacity: 0.65; margin: 0 0 var(--space-md); }
.legal-title {
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-lg);
  line-height: 1.1;
  margin: 0;
  text-wrap: balance;
}
.legal-lead {   /* the old "BACKGROUND" paragraph, set as a lead — the case-study intro's small-serif rung */
  margin: 0;
  max-width: 44ch;   /* 34ch at this 22px rung ran ten lines tall; 44 sits it at about seven */
  font-family: var(--font-serif); font-weight: 300;
  font-size: var(--type-serif-sm);
  line-height: 1.4;
  text-wrap: pretty;
}
.legal-sec {
  padding: var(--space-lg) 0;
  border-top: 1px solid color-mix(in srgb, currentColor 25%, transparent);   /* the ledger / FAQ hairline */
}
.legal-sec:last-child { border-bottom: 1px solid color-mix(in srgb, currentColor 25%, transparent); }
.legal-sec h2 {
  font-family: var(--font-mono); font-weight: 400;
  font-size: var(--type-label);
  text-transform: uppercase; letter-spacing: normal;
  opacity: 0.65;
  margin: 0;
  line-height: 1.6;   /* sits on the first clause's baseline */
  padding-right: var(--space-lg);
}
.legal-sec-body {
  max-width: 72ch;
  font-family: var(--font-body); font-weight: 300;
  font-size: var(--type-body);
  line-height: 1.6;
}
.legal-sec-body > :last-child { margin-bottom: 0; }
.legal-sec-body p, .legal-sec-body li { margin: 0 0 var(--space-sm); }
.legal-sec-body ul, .legal-sec-body ol { padding-left: 1.25em; margin: 0 0 var(--space-sm); }
.legal-sec-body ul { list-style: disc; }   /* the site's global reset strips markers — a rights list needs them back */
.legal-sec-body ol { list-style: decimal; }
.legal-sec-body ol[type="a"] { list-style: lower-alpha; }
.legal-sec-body ul ul, .legal-sec-body ol ol { margin-top: var(--space-xs); }
.legal-sec-body dl { margin: 0; }
.legal-sec-body dt { font-weight: 400; margin: var(--space-md) 0 var(--space-xs); }
.legal-sec-body dt:first-child { margin-top: 0; }
.legal-sec-body dd { margin: 0 0 var(--space-sm); }
.legal-sec-body a, .legal-lead a { color: inherit; text-decoration: underline; text-underline-offset: 0.15em; text-decoration-thickness: 1px; }
@media (max-width: 900px) {
  .legal-head, .legal-sec { grid-template-columns: 1fr; row-gap: var(--space-md); }
  .legal-lead { max-width: none; }
  .legal-sec { padding: var(--space-md) 0; }
  .legal-sec h2 { padding-right: 0; }
}

/* 05.10 (Chiara, on the restructured menu: "let's move the logo to top left"): with the menu open the mark now stays in its
   HEADER seat, top left — the 16.07 move to the menu foot is retired, so the logo is in the same place whether the menu is
   open or closed. The header mark is forced present (it can still be mid-arrival on the homepage) and inks to the paper
   sheet; the large foot logo is gone and the foot's contact block keeps the right edge. Placed last so it outranks the
   earlier menu-open rules (lines ~834, ~891). */
body.menu-open .site-header .wordmark,
body.intro-cut.menu-open .site-header .wordmark { opacity: 1; pointer-events: auto; clip-path: none; transform: none; transition: none; }
body.menu-open .site-header .logo-mask { background-color: var(--ink); }
.menu-foot-logo { display: none; }
.menu-foot { justify-content: flex-end; }

/* 05.10 TRIAL (Chiara, on the open menu: "the colours are off — do we go dark with light?"): the menu sheet goes DARK —
   ink ground, cream type — so the menu reads as its own room instead of a paler copy of the page. Everything that sits on
   the sheet follows: header mark, CTA, burger X, hairlines. Hover can no longer be the dark accent (invisible on ink), so
   it lifts to the warm cream. To revert, delete this block. */
.menu-overlay { background: var(--espresso); color: var(--stone); }   /* 05.10 v2 (Chiara: "lets go the dark brown colour"): ink → brown · v3 same hour ("#150F0D"): brown → --espresso */
.menu-overlay::before { background: currentColor; }
body.menu-open .site-header, body.menu-open .site-header.on-dark { color: var(--stone); }
body.menu-open .site-header .logo-mask { background-color: var(--stone); }
.menu-primary a:hover, .menu-secondary a:hover, .menu-secondary a:focus-visible { color: var(--cream); }   /* hover lifts stone → cream */

/* 05.10 (Chiara, on the Services previews: "I don't like the way the images move up on hover — can they do something
   else"): the 150px rise + fade is retired. The preview now holds still and is WIPED open from its top edge — the same
   swipe the menu sheet itself opens with — so the image never travels, only its edge does. */
/* v2 same day (Chiara: "I don't like the swipe down"): the wipe is retired too — the image is simply PRESENT on hover, no travel, no edge. */
.menu-preview { opacity: 0; transform: none; transition: none; }
.menu-preview.active { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .menu-preview { transition: none; } }

/* 05.10 (Chiara, on the link block: "can we centre this a bit to the lines"): the links + Services row are centred
   VERTICALLY between the menu's two hairlines (top rule at 127px, the foot's rule below) instead of hanging from the top
   one. The sheet becomes a flex column; the bottom padding reserves the foot's seat (the same clamp the preview panel
   already stops at), so "centre" means centre of the space between the lines. Preview, foot and rule are absolute and
   unaffected. */
.menu-overlay { display: flex; flex-direction: column; justify-content: center; padding-bottom: clamp(90px, 11vh, 150px); }   /* 23.10 (Chiara: "equal distance between the lines… the top line the same distance from the text as the bottom line"): 12vh → 11vh. Measured at 430×932: the reserve ran 18px past the foot's rule, so the lists rode high — top rule → first capitals 139, last baseline → bottom rule 148. At 11vh both are ~144. */
.menu-primary { padding-top: 0; }

/* 05.10 (Chiara: "the list / gallery toggle needs to stay at the top when you switch between the two"): in the GALLERY
   the pill is FIXED — the deck is 13 slides long now, and an absolute pill scrolled away with slide one, leaving no way
   back to the list without scrolling all the way up. Same seat, same header line. The list keeps the 10.09 absolute
   seat (fixed, it rode over the project names once the rows scrolled under it). */
body:has(.work-index:not(.is-list)) .view-toggle { position: fixed; }

/* 05.10 (Chiara, on the /work gallery: "can the images have more of an overlay on them"): the gallery deck's flat wash
   deepens 0.10 → 0.36 on every slide (the level LHC + Zambala already needed, a touch more) — thirteen different
   photographs now sit behind the same frosted title, so the ground is held evenly. Gradients + base unchanged. The
   homepage deck keeps its own lighter wash. This layer is the dial. */
/* 12.10 (Chiara: "the homepage case study images — are they missing the darker overlay?"): yes — the homepage deck sat at the
   10% wash (LHC + Zambala 25%) while the gallery went to 36% on 05.10. The homepage deck takes the gallery's wash too. */
body.home .hw-slide::after,
.work-main .hw-slide::after {
  background: linear-gradient(rgba(0, 0, 0, 0.36), rgba(0, 0, 0, 0.36)),   /* v2 same hour ("darker so the text pops"): 0.28 → 0.36 */
              linear-gradient(to bottom, rgba(0, 0, 0, 0.38), transparent 26%),
              linear-gradient(to top, rgba(0, 0, 0, 0.46), transparent 34%),
              rgba(0, 0, 0, 0.18);
}

/* 05.10 (Chiara, on the /work gallery: "we need the text to have that opacity background effect on it"): the frosted
   title rule (58% cream, the photo ghosting through the strokes) was listed project by project, so the seven slides that
   joined the deck today sat in solid cream. Every /work slide carries imagery now, so every title is frosted. */
.work-main .hw-slide .hw-title { color: var(--frost); }

/* 05.10 (Chiara, on the /work gallery: "it's glitchy"): ONE TITLE SIZE, ONE BYLINE SEAT. LHC and Beyond by Kim still
   carried the step-down made for their old two-line titles (92px against 123px), and LHC's byline sat 28px lower for
   the same reason — so the type jumped in size and position as those two slides swept through. LHC is one short word
   now and Beyond fits one line, so on /work both return to the deck's own title rung and byline seat. */
.work-main .hw-slide[data-project="lhc"] .hw-title { font-size: clamp(35px, 6.4vw, 74px); }   /* v3 same day (Chiara: "LHC — could it be the full name but smaller for that one"): the gallery slide spells out Luxury Hospitality Consulting on ONE line; 6.4vw holds the 29 characters to roughly three-quarters of the frame, about Palmer's optical width */
/* v2 same day (Chiara: "can the font size of the gallery version match the size we have used on the case studies"): each
   gallery title now sits at ITS case hero's size, so the click-through holds. Eleven case heroes ride the full rung
   (above); Beyond by Kim and Wagner Bespoke Travel use the 92px step on their case pages (.hw-title--step), so they take
   it here too. If a case page changes rung, change it here. */
.work-main .hw-slide[data-project="beyond-by-kim"] .hw-title,
.work-main .hw-slide[data-project="wagner-bespoke-travel"] .hw-title { font-size: clamp(46px, 8vw, 92px); }


/* 05.10: the deck is SNAPPED while work.js re-measures it on the list → gallery switch (see setView) — no sweep, no text
   entrance delay, so the gallery opens settled on the first project instead of un-sweeping twelve slides. */
.hw.hw-snap .hw-slide, .hw.hw-snap .hw-slide * { transition: none !important; }

/* 05.10 v4 (Chiara: "the titles all look different sizes"): ONE SIZE FOR ALL THIRTEEN. The day's per-project rungs (123
   for most, 92 for Wagner + Beyond, 74 for LHC's full name) are overruled on /work: every gallery title sits on the 92px
   step — the largest rung that still holds "Luxury Hospitality Consulting" on one line inside the gutters — and the
   byline takes the matching seat (half of the 92 block + the same 28px gap). Placed last so it outranks the rules above. */
.work-main .hw-slide[data-project] .hw-title { font-size: clamp(46px, 8vw, 92px); }   /* 19.10 later (Chiara: "bump the project title by 5px" — the phone): floor 44 → 46, not 49 — measured at 390, 47+ wraps "Tom Private Chef" and 48+ breaks "Wagner Bespoke" onto a third line; 8vw passes 46 at ~575px, so tablet and desktop are untouched */
.work-main .hw-slide[data-project] .hw-byline { top: calc(50% + clamp(22px, 4vw, 46px) + 28px); }
/* 12.10 (Chiara: Wagner + LHC "two lines on the case study slide" → "then the byline needs to go lower down"): the byline
   seat is 50% + HALF THE TITLE BLOCK + 28px, and a two-line block at line-height 1 is twice the font-size — so each
   takes its own title size as the offset (both on the 92px step every gallery title takes). Placed AFTER the all-slides seat above, which has the same specificity and was winning. */
.work-main .hw-slide[data-project="wagner-bespoke-travel"] .hw-byline { top: calc(50% + clamp(44px, 8vw, 92px) + 28px); }
.work-main .hw-slide[data-project="lhc"] .hw-byline { top: calc(50% + clamp(44px, 8vw, 92px) + 28px); }   /* LHC rides the 92 step on /work too (the rule above), so its two-line block is the same height as Wagner's */

/* 05.10 THE GALLERY LOOP (Chiara: "a continuous project loop… we don't have the CTA reveal"): in gallery view on desktop
   the footer and its reveal spacer stand down — the deck wraps back to project one instead of ending (work.js). The
   list view keeps the footer. On phones / reduced motion the deck is a plain stack, so the twin hides and the footer stays. */
@media (min-width: 761px) and (prefers-reduced-motion: no-preference) {
  body:has(.work-index:not(.is-list)) .site-footer,
  body:has(.work-index:not(.is-list)) .footer-spacer { display: none; }
  body.page-interior:has(.work-index:not(.is-list)) .work-main.above-footer { margin-bottom: 0; padding-bottom: 0; }
  body.page-interior:not(.case-study):has(.work-index:not(.is-list)) .work-main.above-footer::after { display: none; }   /* the reveal rider — no footer to reveal, so no tail under the twin */
}
@media (max-width: 760px), (prefers-reduced-motion: reduce) { .hw-slide[data-loop] { display: none; } }

.br-phone { display: none; }   /* 23.10: phone-only line breaks (index.html, .origin-lead) — shown inside the ≤700 block */


/* 25.10 (Chiara, the Aura Method phases: "make the background cream for the first one, Immersion" + "add a frosted
   colour over the text on Immersion, like we have for the case study"). Each slide now carries its OWN ground, so the
   up-wipe changes the colour with the slide (a slide is a full-screen layer — a transparent one would show Immersion's
   cream through the next wipe): Immersion cream with ink type, phases 2–5 the site dark. The title takes the
   case-study FROST where it crosses the photo — the twin in .method-clip at cream 58% — and the solid title sits UNDER
   the photo (media z 2 over title z 1), else the frost is painted over solid type and never reads. */
body.method-page .method-pin .method-slide { background: var(--ground-3); }
/* body.method-page .method-pin .method-slide[data-phase="1"] { background: var(--cream); color: var(--ink); }  — tried 25.10 and reverted the same hour (Chiara: "maybe it does look better dark") */
body.method-page .method-pin .method-media { z-index: 2; }
body.method-page .method-pin .method-title:not(.method-title--over) { z-index: 1; }
@media (min-width: 901px) { body.method-page .method-pin .method-clip { display: block; z-index: 3; } }   /* desktop only — on phones the title sits ABOVE the photo, so the twin stays off (the ≤900 rule) */
body.method-page .method-pin .method-clip .method-title { color: color-mix(in srgb, var(--cream) 78%, transparent); text-shadow: 0 0 22px color-mix(in srgb, var(--ground-3) 40%, transparent); }   /* 25.10 v2 (Chiara: "make sure the text over the image on Immersion can still be read"): the frost 58 → 78% with a soft dark haze behind the letters — the Reid’s sky and sun-flecked water are bright, and 58% washed out on them; still translucent, still the case-study family 

/* 25.10 (Chiara: "add 01, 02, 03 to the images, with the little line thing" → the What We Do style): each phase's
   counter is its index (01–05, was "( 1/5 )"), set as the What We Do index — mono label — with a hairline running the
   frame's width, seated one step above the image's top edge. Phones: centred over the title with a short rule. */
body.method-page .method-pin .method-counter { display: flex; align-items: center; gap: var(--space-sm); opacity: 0.8; }
body.method-page .method-pin .method-counter::after { content: ''; flex: 1; height: 1px; background: color-mix(in srgb, currentColor 35%, transparent); }
@media (min-width: 901px) {
  body.method-page .method-pin .method-counter { left: calc(50% - var(--method-media-w) / 2); right: auto; width: var(--method-media-w); bottom: calc(50% + var(--method-media-h) / 2 + var(--space-sm)); text-align: left; }
}
@media (max-width: 900px) {
  body.method-page .method-pin .method-counter { justify-content: center; }
  body.method-page .method-pin .method-counter::after { flex: 0 0 40px; }
}

/* 25.10 v2 (Chiara: "not above it — 01, 02, 03 next to the word, like the case studies — the line looks weird"): the
   phase index rides the title as the case-study deck's superscript (.hw-title sup grammar: small, top-right, 0.75);
   the counter-with-hairline above the frame retires. The twin in .method-clip carries the same sup so it registers. */
body.method-page .method-pin .method-counter { display: none; }
body.method-page .method-title .mt-num { position: absolute; top: 0.3em; margin-left: 12px; font-size: 0.2em; opacity: 0.75; font-family: var(--font-serif); font-weight: 300; letter-spacing: 0; }
@media (max-width: 900px) { body.method-page .method-title .mt-num { font-size: var(--type-caption); margin-left: 8px; top: 0.25em; } }
@media (min-width: 901px) {
  body.method-page .method-title .mt-num { font-size: max(0.2em, 14px); opacity: 0.9; }   /* 25.10 v3: the index straddles the frame edge on long words (Immersion: 884–896 against the photo's 893) — 14px and 0.9 so it reads on both sides of the edge */
  body.method-page .method-pin .method-clip .method-title .mt-num { text-shadow: none; color: var(--cream); }   /* no haze round the small figures, and solid cream inside the frame so the two halves match */
}
@media (min-width: 901px) {
  /* 25.10 (Chiara: "maybe the word is a third of the way down instead of half on the Method images"): the title's centre
     sits at one third of the frame — the frame is centred at 50%, so a third down is 50% − h/6 — and the frosted twin
     inside the frame-sized clip sits at 33.333% of the clip, which is the same line: they register. */
  body.method-page .method-pin .method-title:not(.method-title--over) { top: calc(50% - var(--method-media-h) / 6); transform: translateY(-50%); }
  body.method-page .method-pin .method-clip .method-title { top: 33.333%; bottom: auto; transform: translate(-50%, -50%); }
}
@media (min-width: 901px) {
  /* 25.10 v2 (Chiara: "maybe it should go above it, and the number a bit bigger"): the title leaves the photograph —
     its foot sits one md step above the frame's top edge (the phone layout's order), so the frosted twin has nothing
     to frost and stands down; the index steps up 14 → ~0.3em (≈19px). Supersedes the third-down seat above. */
  body.method-page .method-pin .method-title:not(.method-title--over) { top: auto; bottom: calc(50% + var(--method-media-h) / 2 + var(--space-md)); transform: none; }
  body.method-page .method-pin .method-clip { display: none; }
  body.method-page .method-title .mt-num { font-size: max(0.3em, 16px); opacity: 0.85; top: 0.2em; }
}
@media (max-width: 900px) { body.method-page .method-title .mt-num { font-size: 15px; } }   /* phones: 12 → 15, the bigger index */
body.method-page .method-pin .method-media img, body.method-page .method-pin .method-slide.shown .method-media img, body.method-page .method-pin .method-slide.active .method-media img { transform: none; transition: none; will-change: auto; }   /* 25.10 (Chiara: "I like the crispness of the first image — 2, 3 and 4 look like they've got a fuzzy opacity"): it was the slide's ZOOM (1.16 waiting → 1.06 shown, on a will-change layer) — the photo was rasterised once and then stretched, so every slide after the first rendered soft. The framed seat drops the zoom: each still draws at its true size, crisp; the up-wipe is untouched */
@media (min-width: 901px) {
  /* 25.10 v3 (Chiara: "the name goes over the image in the top third, but the number goes ABOVE the word — 01, then
     the word underneath"): back over the photograph at one third down (supersedes v2 "above the frame"), the frosted
     twin returns, and the index stacks centred over the word instead of riding its shoulder. */
  body.method-page .method-pin .method-title:not(.method-title--over) { top: calc(50% - var(--method-media-h) / 6); bottom: auto; transform: translateY(-50%); }
  body.method-page .method-pin .method-clip { display: block; }
  body.method-page .method-pin .method-clip .method-title { top: 33.333%; bottom: auto; transform: translate(-50%, -50%); }
}
body.method-page .method-title .mt-num { position: absolute; left: 50%; bottom: 100%; top: auto; transform: translateX(-50%); margin: 0 0 var(--space-xs); font-size: 16px; line-height: 1; opacity: 0.85; }   /* the index sits ABOVE the word, centred on it, one xs step over its cap line — desktop and phones alike */
@media (max-width: 900px) { body.method-page .method-title .mt-num { font-size: 15px; } }
@media (min-width: 901px) { body.method-page .method-title .mt-num { font-size: 18px; opacity: 1; } body.method-page .method-pin .method-clip .method-title .mt-num { color: var(--cream); text-shadow: 0 0 14px color-mix(in srgb, var(--ground-3) 45%, transparent); } }   /* 25.10: the stacked index reads over the busy water — 18px, full cream, a light haze */
@media (min-width: 901px) { body.method-page .method-pin .method-title:not(.method-title--over) { top: calc(50% - var(--method-media-h) / 4); } body.method-page .method-pin .method-clip .method-title { top: 25%; } }   /* 25.10 v4 (Chiara: "move the titles up a bit more — there is space up there"): one third → one quarter down the frame (50% − h/4 is the quarter line of a frame centred at 50%; the twin at 25% of the clip registers) */
body.method-page .method-title .mt-num { font-family: var(--font-mono); font-weight: 400; font-size: var(--type-caption); letter-spacing: 0.06em; line-height: 1; opacity: 0.85; margin-bottom: 10px; }   /* 25.10 v5 (Chiara: "the placement looks weird — 01 too far away or too big, half on half off — do something that looks good"): the index becomes a LABEL — the site's mono at the caption size, tight over the word (10px) — instead of a small serif fragment floating above it; title and index sit together inside the photograph, cover-style */
@media (min-width: 901px) { body.method-page .method-title .mt-num { font-size: var(--type-label); opacity: 0.9; } }
body.method-page .method-pin .method-slide:is([data-phase="2"], [data-phase="3"], [data-phase="4"]) .method-media::after { background: none; }   /* 25.10 (Chiara: "images 3 and 4 — remove whatever effect…" → "2, 3, 4 need removing overlay, leave 1 as is"): no tint on Define, Design and Build; Build's file is re-exported ungraded (as supplied) */
body.method-page .method-pin .method-slide[data-phase="5"] .method-media::after { content: ""; background: linear-gradient(to bottom, color-mix(in srgb, var(--ground-3) 45%, transparent), transparent 45%); }   /* 27.10 (Chiara: "Sustain over the lady's head, can't read it"): a soft top shade under the cream title on the pale sky */
/* 27.10 (Chiara: "Sustain over the lady's head, can't read it"): the 25.10 dark-at-62% phase-5 title RETIRED; Sustain now takes the same cream + haze as the other four. */
body.studio-page .team { padding-bottom: var(--space-xl); }   /* 25.10 (Chiara: "a bit of spacing after the team, before What We Do"): + xl (64) under the cards — 96 → ~160 to the ( What We Do ) label */

/* 27.10 (Chiara, phone: "when the video loads there's a play button on it — I want it to autoplay"): iOS paints its own
   start-playback button when it blocks autoplay (Low Power Mode, data saver). Never show the native control layer; the
   poster holds the frame, and main.js starts every in-view film on the first touch. */
video::-webkit-media-controls-start-playback-button { display: none !important; -webkit-appearance: none; }
video::-webkit-media-controls, video::-webkit-media-controls-panel, video::-webkit-media-controls-overlay-play-button { display: none !important; -webkit-appearance: none; }
body.nav-stays.work-dark .site-header.is-float, body.nav-stays.work-dark .site-header.is-float.on-dark { padding-bottom: var(--space-sm); background: color-mix(in srgb, var(--ground-3) 92%, transparent); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border-bottom: 0; }   /* 27.10 (nav review): /work's always-on header gets the page's own dark behind it once scrolled, so the project names pass under it instead of through the logo */
/* 27.10 (Chiara, phone: "( Strategy, Brand Identity, Website, Film ) a touch on the edge… a bit smaller by 2px"): the byline ran
   to 15–16px of the screen edge on the case heroes (22px) and the home deck (19px). Two pixels off each, and the gutter held. */
@media (max-width: 700px) {
  .cs-hero .hw-tag { font-size: calc(var(--type-hw-tag) - 2px); padding-inline: var(--space-sm); box-sizing: border-box; text-wrap: balance; }
  body.home .hw-slide .hw-tag { font-size: calc(var(--type-serif-sm) - 2px); }
}
/* 27.10 (Chiara, phone, Palmer / Wagner / LHC: "big gaps at the bottom of the hero"): the pinned hero photo was 100svh — the
   height WITH Safari's toolbar showing — so when the toolbar shrank on scroll, a dark band opened under it. lvh is the
   height with the toolbar gone; the photo now always reaches the bottom of the screen. */
@media (max-width: 700px) {
  .cs-hero-media, .svc-hero--ride .svc-hero-sticky { height: 100lvh; }
}
/* 27.10 (Chiara, phone: "the Aura Studios logo keeps appearing over the text when you scroll up and down"): on dark pages the
   returning header had no ground (the 04.09 bar was retired as "weird"), so the byline and lead slid straight through the
   logo. Phones now get a soft dark FADE behind it, not a bar: the page's own dark at the top, gone by the foot. */
@media (max-width: 700px) {
  body .site-header.is-float.on-dark { padding-bottom: var(--space-lg); background: linear-gradient(to bottom, color-mix(in srgb, var(--ground-3) 97%, transparent) 0%, color-mix(in srgb, var(--ground-3) 85%, transparent) 50%, transparent); backdrop-filter: none; -webkit-backdrop-filter: none; border-bottom: 0; }
}
/* 27.10 (Chiara: "the footer looks off — Let's build the brand… does the text need to be darker?"): the cream line sat over the
   brightest, busiest middle of each footer film. Not darker type: a soft dark GLOW behind the words only, so the film
   still reads everywhere else. */
.footer-ask::before { content: ''; position: absolute; left: 50%; top: 55%; width: min(900px, 120%); height: 75%; translate: -50% -50%; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--ground-3) 62%, transparent), color-mix(in srgb, var(--ground-3) 30%, transparent) 60%, transparent); }
/* 27.10 (Chiara, phone: "terms, privacy and cookie are getting cut off slightly — move them up, and the logo up, so the curve
   of the phone doesn't cut it off"): one md more room at the foot of the footer on phones, plus the home-indicator inset. */
@media (max-width: 700px) {
  .footer-stage { padding-bottom: calc(var(--gutter) + var(--space-md) + env(safe-area-inset-bottom, 0px)); }
}
/* 27.10 THE PHONE PILLS — built and REMOVED the same hour (Chiara: "I don't like the frosted thing… absolutely not"). On phones
   the returning header carries no ground of any kind. */
@media (max-width: 700px) {
  body .site-header.is-float, body .site-header.is-float.on-dark, body.nav-stays.work-dark .site-header.is-float { background: none; backdrop-filter: none; -webkit-backdrop-filter: none; border-bottom: 0; padding-bottom: 0; }
}
/* 27.10 (Chiara, phone, Studio In Short: "the logo's over my face… everything's too close to my image… move it down so it's
   centred to the gap"): the portrait started 64px ABOVE the pinned screen, under the header. It now starts just below the
   logo line and runs 54vw wide (was 66), so the statement gets the room — main.js still centres it in what's left. */
@media (max-width: 700px) {
  body.studio-page .chapter-scroll { --ch-ph: calc(54vw * 4 / 3); }
  body.studio-page .ch-panel--chapter .fi-photo { top: calc(var(--header-h) + var(--space-sm)); left: 23vw; width: 54vw; }
}
/* 27.10 (Chiara, phone: "does the contact form look overwhelming? does Apply Here look too big? should it be right-aligned?"):
   Apply Here drops to the eyebrow's size and sits bottom-right, as on desktop; the form tightens — rows closer, pills smaller. */
@media (max-width: 700px) {
  .apply-hero-row .apply-hero-cta { font-size: var(--type-caption); margin-left: auto; }
  .apply-hero .svc-hero-first { padding-left: 0; padding-right: 0; }   /* 27.10 (Chiara: "Apply Here needs to align with it now"): the hero's eyebrow, title and Apply Here share the form's 32px edge */
  .apply--ledger :is(.apply-head-row, .apply-row, .apply-submit) { padding: var(--space-sm) 0; }
  .apply--ledger .apply-q { font-size: var(--type-body-sm); }
  .apply--ledger .apply-opt-text { font-size: 13px; padding: 6px var(--space-sm); }
  .apply--ledger .apply-form input[type="text"]:not(.hp), .apply--ledger .apply-form input[type="email"] { padding: 8px var(--space-md); }
}
/* 27.10 (Chiara, phone: "a big gap above The Application… more gap between the titles and the pills" → centred tried and
   reverted: "I don't think it looks good centred… left-aligned, more breathing room between the lines and the text, and the
   sections"): left-aligned; each row one md clear of its hairline above and below, the question one sm above its answers. */
@media (max-width: 700px) {
  .apply--ledger { padding-top: var(--space-lg); }
  .apply--ledger :is(.apply-head-row, .apply-row, .apply-submit) { padding: var(--space-md) 0; row-gap: var(--space-sm); }
  .apply--ledger .apply-head-row { padding-top: 0; }
  .apply--ledger { padding-left: var(--inset-phone); padding-right: var(--inset-phone); }   /* 27.10 (Chiara: "the margins are really narrow on the contact page"): 16 → 32, the site's phone inset for sections like this (What We Do, the team, How We Work) */
}
@media (max-width: 700px) { .apply--ledger .apply-row .apply-a.apply-a--pair, .apply--ledger .apply-row:has(input[name="business"]) .apply-a.apply-a--pair { gap: var(--space-xs); } }   /* both field pairs one even 8px step */
/* 27.10 (Chiara, phone: "are the titles a little big — reduce by 2px on all pages?"): every phone hero title steps 2px down —
   service + apply heroes, the Method, every case study, the home deck cards. Desktop unchanged. */
@media (max-width: 700px) {
  .svc-hero .svc-title { font-size: calc(11vw - 2px); }
  .method-intro .svc-title { font-size: calc(10.5vw - 2px); }
  .cs-hero .hw-title.hw-title { font-size: calc(11vw - 2px); }
  .cs-hero[data-project="wagner-bespoke-travel"] .hw-title.hw-title { font-size: calc(10.5vw - 2px); }
  body.home .hw-slide .hw-title { font-size: calc(var(--type-deck-phone) - 2px); }
}
/* 27.10 ONE phone hero margin — first set at 32 (the phone inset), then REVERTED same evening (Chiara: "too big now… they
   need to match the menu margins, the same width as the burger"): every hero's copy sits on the 16px gutter, the logo's
   and the burger's edge. The ride heroes carry no outer gutter, so they take it themselves. */
@media (max-width: 700px) {
  .svc-hero .svc-hero-first, .apply-hero .svc-hero-first { padding-left: 0; padding-right: 0; }
  body .svc-hero.svc-hero--ride .svc-hero-first.svc-hero-first { padding-left: var(--gutter); padding-right: var(--gutter); }
  .cs-hero .cs-hero-screen--intro { padding-left: var(--gutter); padding-right: var(--gutter); }
}
/* 27.10 (Chiara, phone, the Method steps: "more space between the title, the image and the text… the title's too big"): the
   frame is centred at 42svh; the title now hangs one lg ABOVE its top edge (it was riding the edge on taller phones), steps
   down to 34px, and the two columns start one xl under the frame (was lg). */
@media (max-width: 700px) {
  body.method-page .method-pin .method-title:not(.method-title--over) { top: calc(42svh - var(--method-media-h) / 2 - var(--space-lg)); bottom: auto; transform: translateY(-100%); font-size: 34px; line-height: 1.05; }
  body.method-page .method-pin .method-clip { display: none; }
  .method-value, .method-deliv { top: calc(42svh + (var(--method-media-h) / 2) + var(--space-xl)); }
}
/* 27.10 v2 (Chiara: "move it down more — everything's too together, there's a big gap at the bottom… left and right margins
   the same, split the difference"): the cluster drops (frame centre 42 → 46svh) and opens up (title xl above the frame,
   columns xl under it); both columns sit inside one 32px inset each side with an equal split. */
@media (max-width: 700px) {
  .method-pin .method-media { top: 46svh; }
  body.method-page .method-pin .method-title:not(.method-title--over) { top: calc(46svh - var(--method-media-h) / 2 - var(--space-lg)); }
  .method-value, .method-deliv { top: calc(46svh + (var(--method-media-h) / 2) + var(--space-xl)); }
  .method-value { left: var(--inset-phone); width: calc((100% - 2 * var(--inset-phone) - var(--space-md)) * 0.45); }
  .method-deliv { left: calc(var(--inset-phone) + (100% - 2 * var(--inset-phone) - var(--space-md)) * 0.45 + var(--space-md)); right: var(--inset-phone); }
}
/* 27.10 v3 ("the right margin's bigger still"): the list's lines are short, so a left-anchored column left a ragged 60px+ on
   the right. The list now HUGS its longest line and is anchored to the right 32px edge (lines still read left-aligned),
   a notch smaller so "Brand Strategy Workshop" holds one line — both outer margins measure 32. */
@media (max-width: 700px) {
  .method-deliv { left: auto; right: var(--inset-phone); width: max-content; max-width: calc(58% - var(--inset-phone)); font-size: calc(var(--type-serif-md) - 8px); }
  .method-value { width: calc(100% - 2 * var(--inset-phone) - var(--space-md) - min(58% - var(--inset-phone), 190px)); }
}
.consent-ctl { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-xs) var(--space-md); margin: var(--space-sm) 0 0; }
.consent-ctl button { background: none; border: 0; padding: 0; cursor: pointer; color: inherit; font: inherit; text-transform: uppercase; font-family: var(--font-label); font-size: var(--type-label); text-decoration: underline; text-underline-offset: 4px; }   /* 27.10: the cookie choice, on the Cookie Policy page only */
/* 27.10 Odisi: the window photograph fills the green panel, the logotype rides over it. A soft green wash keeps the panel's colour. */
.cs-split--odisi-photo { position: relative; overflow: hidden; }
.cs-split--odisi-photo > img.cs-split-fill { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cs-split--odisi-photo::after { content: ''; position: absolute; inset: 0; background: rgba(0, 0, 0, 0.22); pointer-events: none; }
.cs-split--odisi-photo .cs-split-logotype { position: relative; z-index: 2; }
/* 27.10 (Chiara, Hustl: "on desktop they should just be stacked, not swipe through each other"): desktop shows both stills,
   one under the other (HUSTL on the chair first); phones keep the single seat that swaps. */
@media (min-width: 761px) {
  .cs-stack-item--pair.cs-pair--desk-stack .cs-swap-b--phone { display: none; }
  .cs-stack-item--pair-second.cs-pair--desk-stack { display: block; }
}
/* 27.10 v4 (Chiara: "the Method — move The Shift / The Work down a bit more, and a narrower margin to align with the menu"):
   the columns drop to 2xl under the frame (was xl) and sit on the 16px gutter both sides, the logo's and the burger's edge. */
@media (max-width: 700px) {
  .method-value, .method-deliv { top: calc(46svh + (var(--method-media-h) / 2) + var(--space-2xl)); }
  .method-value { left: var(--gutter); width: calc(100% - 2 * var(--gutter) - var(--space-md) - min(58% - var(--gutter), 200px)); }
  .method-deliv { right: var(--gutter); max-width: calc(58% - var(--gutter)); }
}
/* 27.10 (Chiara: "zoom out the photo of Alex"): the team seats run their photos at 118% for the scroll drift, which cropped
   Alex's 3:4 frame (already the seat's own shape) by ~15%. His sits at 100%: the whole photograph, no drift. */
.team-seat img[src*="team-alex"] { height: 100%; margin-top: 0; translate: none; }
img[src*="team-alex"] { scale: -1 1; }   /* 27.10 (Chiara: "can we flip Alex?"): mirrored so he faces in — toward Chiara's card on /studio, toward his own quote on /photography */
/* 27.10 (Chiara, phone: "the sound on/off is covering over stuff… what do we do with that?"): on phones it lives on the hero
   only — it fades out once the hero is behind you and back in at the top. The music keeps playing. Desktop unchanged. */
@media (max-width: 700px) {
  .sound-toggle { transition: opacity var(--dur-base) var(--ease-house); }
  body.past-hero .sound-toggle { opacity: 0; pointer-events: none; }
}
body.close-paper .footer-ask::before { content: none; }   /* 27.10: the dark glow is for film footers — on the paper melt (Necker) it read as a smudge */
/* 27.10 v5 (Chiara, phone: "The Shift text is getting cut off… make the image a bit smaller, the title a bit smaller"): the
   frame now sizes from the screen's HEIGHT as well as its width (never taller than 36% of the screen), the title steps
   34 → 30px, and the columns sit one xl under the frame — so every step's text clears the foot of a small iPhone. */
@media (max-width: 700px) {
  .method-pin { --method-media-w: min(56vw, 27svh); }
  .method-pin .method-media { top: 42svh; }
  body.method-page .method-pin .method-title:not(.method-title--over) { top: calc(42svh - var(--method-media-h) / 2 - var(--space-md)); font-size: 30px; }
  .method-value, .method-deliv { top: calc(42svh + (var(--method-media-h) / 2) + var(--space-xl) + var(--space-xs)); }   /* v6 ("move The Shift / The Work down more"): lg → xl + xs (72px under the frame) */
}
@media (max-width: 700px) and (max-height: 760px) {   /* short phones (SE / mini, or Safari's bars down): a smaller frame, a step up the screen */
  .method-pin { --method-media-w: min(52vw, 22svh); }
  .method-pin .method-media { top: 38svh; }
  body.method-page .method-pin .method-title:not(.method-title--over) { top: calc(38svh - var(--method-media-h) / 2 - var(--space-sm)); font-size: 26px; }
  .method-value, .method-deliv { top: calc(38svh + (var(--method-media-h) / 2) + var(--space-md)); }
}
.fi-statement { text-wrap: pretty; }   /* 27.10 (Chiara: "the line breaks look weird"): the glued "a decade" / "a multi-disciplinary" / "a handful" forced short ragged lines ("travel, now"); the glue is gone (only the last "at a time" stays together) and the browser balances the rag */
/* 27.10 ASK AI ABOUT AURA (Chiara, from Monolog's menu): one quiet mono line in the menu foot — the lead, then three links that
   open ChatGPT, Claude and Perplexity with a question about Aura already typed. Same register as the socials. */
.menu-ask { flex-basis: 100%; display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: baseline; gap: var(--space-xs) var(--space-md); margin: 0 0 var(--space-xs); }
.menu-ask .menu-ask-lead { opacity: 0.55; }
.menu-ask a { border-bottom: 0; opacity: 0.75; transition: opacity var(--dur-quick) var(--ease-house); }
.menu-ask a:hover { opacity: 1; }
@media (max-width: 700px) { .menu-ask { justify-content: flex-start; } .menu-ask .menu-ask-lead { flex-basis: 100%; text-align: left; } }   /* phones: the lead on its own line, the three names in one row under it */
.cs-voice[hidden] { display: none; }   /* 27.10: a case study can park its testimonial until the quote exists (Alquemie) */
.team-card--text { display: flex; flex-direction: column; justify-content: flex-end; }   /* 27.10: a portrait-less card — its name sits on the same line as the others' */
@media (max-width: 700px) { body.studio-page .team-card--text { justify-content: flex-end; padding-bottom: 0; } }
.cs-stack-item img[src*="website-tablet-sofa"] { object-position: 50% 58%; }   /* 27.10: the portrait mockup in a landscape seat — keep the tablet centred in the crop */
