/* site.css · novaauteur.com v2 · layout only (K1). Tokens, fonts, base, type roles, buttons, nav, footer, cards,
   figures, timeline, the violet line and the focus ring come from /css/nova-ds.css (vendored nova-ds, nova-site-kit;
   v2.1 vendors every component except scrim, which no page uses). Change those in nova-ds and re-vendor; never here.
   Every svh value is declared in vh first, for browsers without svh (Safari / iOS 15.0-15.3, Chrome < 108, Firefox < 101). */

/* =========================================================
   HOME
   ========================================================= */

/* 1 · hero (Fida, 13 Oct 18:53): the film plays full-bleed; LABEL, the stacked H1, the sub and both buttons sit
   centred on one axis over it; the nav on top; the three figures in a quiet row along the bottom edge.
   Readable before the film or any script: the poster is the film's first frame, and the shading is plain css. */
.hero {
  position: relative; min-height: 100vh; min-height: 100svh; isolation: isolate; overflow: hidden; overflow: clip;  /* hidden: for browsers without clip (Safari 15) */
  display: grid; grid-template-rows: 1fr auto; row-gap: clamp(var(--s5), 5svh - 20px, var(--s6));
  padding: clamp(84px, 11svh, 124px) var(--gutter) clamp(20px, 3svh, 36px);
}
.hero__media { position: absolute; inset: 0; z-index: var(--z-media); }
.hero__media img, .hero__film { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; }
.hero__film { opacity: 0; transition: opacity var(--dur-fast) var(--ease); }
.hero__film.is-on { opacity: 1; }

/* the shading. One soft centred field (.hero__field), a sibling of the copy and sized from the copy block, plus two
   full-width edge bands (.hero__shade) for the nav and the figures. All of it only darkens (mix-blend darken) and is
   graded on the film's brightest frames: H1, label, figures, nav >= 4.5:1, the sub >= 7:1 at the worst pixel.
   The field is one continuous bell, not a plate: it falls off from the middle of the copy on a smoothstep over the
   whole radius (flat only over the inner 12%, zero slope at the rim), and it is at least 130vw wide and 130svh tall, so its rim lies
   past the screen's edges on every screen, wide, wide-short and very tall ones included: on a bright, even frame it
   reads as light falling off, never as a shape, a column or a dome (v2.1 reviews 1, 2 and 3). */
.hero__shade, .hero__field { position: absolute; z-index: var(--z-scrim); pointer-events: none; mix-blend-mode: darken; }
.hero__shade {
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(var(--canvas-rgb), 0.72) 0px, rgba(var(--canvas-rgb), 0.72) 76px, rgba(var(--canvas-rgb), 0.689) 95px, rgba(var(--canvas-rgb), 0.608) 114px, rgba(var(--canvas-rgb), 0.492) 133px, rgba(var(--canvas-rgb), 0.360) 152px, rgba(var(--canvas-rgb), 0.228) 171px, rgba(var(--canvas-rgb), 0.112) 190px, rgba(var(--canvas-rgb), 0.031) 209px, rgba(var(--canvas-rgb), 0) 228px),
    linear-gradient(to top, rgba(var(--canvas-rgb), 0.74) 0px, rgba(var(--canvas-rgb), 0.74) 112px, rgba(var(--canvas-rgb), 0.708) 134px, rgba(var(--canvas-rgb), 0.625) 156px, rgba(var(--canvas-rgb), 0.506) 178px, rgba(var(--canvas-rgb), 0.370) 200px, rgba(var(--canvas-rgb), 0.234) 222px, rgba(var(--canvas-rgb), 0.115) 244px, rgba(var(--canvas-rgb), 0.032) 266px, rgba(var(--canvas-rgb), 0) 288px);
}
.hero__core { position: relative; align-self: center; justify-self: center; }
.hero__field {
  /* centred on the copy: 110% of the copy block above and below or 65svh from its middle, whichever reaches further,
     and four copy-widths wide or 130vw, whichever is wider (the copy stops growing at the H1's cap, the screen does
     not). The copy's furthest corners sit near a third of the radius, where the bell is still ~0.7 */
  top: min(-110%, 50% - 65vh); top: min(-110%, 50% - 65svh); bottom: min(-110%, 50% - 65vh); bottom: min(-110%, 50% - 65svh); left: 50%;
  width: max(400%, 130vw); margin-left: calc(max(400%, 130vw) / -2);
  background: radial-gradient(closest-side, rgba(var(--canvas-rgb), 0.840) 0%, rgba(var(--canvas-rgb), 0.840) 12.0%, rgba(var(--canvas-rgb), 0.831) 17.5%, rgba(var(--canvas-rgb), 0.804) 23.0%, rgba(var(--canvas-rgb), 0.762) 28.5%, rgba(var(--canvas-rgb), 0.709) 34.0%, rgba(var(--canvas-rgb), 0.645) 39.5%, rgba(var(--canvas-rgb), 0.574) 45.0%, rgba(var(--canvas-rgb), 0.498) 50.5%, rgba(var(--canvas-rgb), 0.420) 56.0%, rgba(var(--canvas-rgb), 0.342) 61.5%, rgba(var(--canvas-rgb), 0.266) 67.0%, rgba(var(--canvas-rgb), 0.195) 72.5%, rgba(var(--canvas-rgb), 0.131) 78.0%, rgba(var(--canvas-rgb), 0.078) 83.5%, rgba(var(--canvas-rgb), 0.036) 89.0%, rgba(var(--canvas-rgb), 0.009) 94.5%, rgba(var(--canvas-rgb), 0) 100%);
}
.hero__stack { position: relative; display: flex; flex-direction: column; align-items: center; text-align: center; }
/* one H1 formula, sized from the height the stack has (measured), with no step at any height. The spacing round it
   stays tight until the H1 reaches its cap (about 870 px tall), then opens evenly over 880-1040 px, so on short
   laptops the height goes to the H1 and it stays the dominant mass */
.hero__label { margin-bottom: clamp(var(--s3), 2.5svh - 10px, var(--s4)); }
.hero__h1 { font-size: clamp(3.6rem, min(17vw, 35vh - 8.5rem), 10.5rem); font-size: clamp(3.6rem, min(17vw, 35svh - 8.5rem), 10.5rem); line-height: 0.84; letter-spacing: 0; overflow-wrap: normal; }
.hero__h1 > span { display: block; }  /* three stacked words; they never split mid-word */
.hero__sub { margin-top: clamp(var(--s4), 5svh - 28px, var(--s5)); max-width: 36ch; font-size: clamp(1rem, 1.25vw, 1.1875rem); line-height: 1.5; text-wrap: balance; }
.hero .cta-row { margin-top: clamp(var(--s5), 5svh - 20px, var(--s6)); justify-content: center; }
/* a value that holds var() can't fall back by declaring it twice (it is only rejected at computed-value time), so
   browsers without svh get these in vh here; placed before the media queries below, which still override them */
@supports not (height: 1svh) {
  .hero { row-gap: clamp(var(--s5), 5vh - 20px, var(--s6)); padding: clamp(84px, 11vh, 124px) var(--gutter) clamp(20px, 3vh, 36px); }
  .hero__label { margin-bottom: clamp(var(--s3), 2.5vh - 10px, var(--s4)); }
  .hero__sub { margin-top: clamp(var(--s4), 5vh - 28px, var(--s5)); }
  .hero .cta-row { margin-top: clamp(var(--s5), 5vh - 20px, var(--s6)); }
}
.hero__figs { position: relative; justify-self: center; width: min(100%, 60rem, 100% - 12rem); justify-content: space-between; }  /* 6rem each side stays clear for the pause control */  /* the row itself is nova-ds .figs */
.hero__figs .fig { align-items: center; text-align: center; }
.hero__figs .fig__n { font-size: clamp(1.25rem, 2vw, 1.75rem); }
.hero__figs .fig__t { max-width: none; }  /* a quiet row: each caption on one line where the row has room */
/* the hero film's pause control (WCAG 2.2.2; Cowork 15 Oct): small and quiet, Plex Mono, at the hero's bottom-right.
   Hidden until the film plays (site.js); never shown with JS off or under reduced motion, where only the poster shows.
   The label is the action it takes ("Pause" / "Play"); nothing is remembered between visits. */
.hero__pause {
  position: absolute; right: calc(var(--gutter) - var(--s2)); bottom: calc(clamp(20px, 3vh, 36px) + 4px); bottom: calc(clamp(20px, 3svh, 36px) + 4px);
  display: inline-flex; align-items: center; gap: var(--s2); min-height: 44px; padding: 0 var(--s2);
  font: 500 var(--t-label)/1 var(--f-mono); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink);
  background: none; border: 0; border-radius: var(--round); cursor: pointer;
  text-decoration-line: underline; text-decoration-color: transparent; text-decoration-thickness: 1px; text-underline-offset: 6px;
  transition: text-decoration-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.hero__pause[hidden] { display: none; }
.hero__pause svg { width: 10px; height: 10px; fill: currentColor; }
.hero__pause .i-play, .hero__pause[data-state="paused"] .i-pause { display: none; }
.hero__pause[data-state="paused"] .i-play { display: inline; }  /* one svg holds both glyphs; the state shows one */
@media (hover: hover) and (pointer: fine) { .hero__pause:hover { text-decoration-color: var(--accent); } }
.hero__pause:active { transform: scale(0.97); }

/* phones (Fida 15 Oct, option b): the film plays undimmed as a band directly under the nav, 35% of the screen tall (the low end of
   Fida's 35-40%, so the hero's Book a call reaches the first screen on the larger phones),
   cover-cropped on the middle of the frame; it rises out of the canvas over its top 8% and fades back into it over its
   lower half, both on a smoothstep, so neither edge reads as a line.
   The type sits below it on the canvas: same order and sizes, centred, no shading needed. Tablets and up unchanged. */
@media (max-width: 767px) {
  .hero { --band-top: 6.5rem; --band-h: clamp(180px, 35svh, 420px); display: block; min-height: 0; padding-top: var(--band-top); padding-bottom: var(--s6); }
  .hero__media { position: relative; inset: auto; z-index: auto; height: var(--band-h); margin: 0 calc(var(--gutter) * -1); }
  .hero__media::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(to bottom, rgba(var(--canvas-rgb), 1) 0%, rgba(var(--canvas-rgb), 0.957) 1%, rgba(var(--canvas-rgb), 0.844) 2%, rgba(var(--canvas-rgb), 0.684) 3%, rgba(var(--canvas-rgb), 0.5) 4%, rgba(var(--canvas-rgb), 0.316) 5%, rgba(var(--canvas-rgb), 0.156) 6%, rgba(var(--canvas-rgb), 0.043) 7%, rgba(var(--canvas-rgb), 0) 8%, rgba(var(--canvas-rgb), 0) 50%, rgba(var(--canvas-rgb), 0.043) 56.25%, rgba(var(--canvas-rgb), 0.156) 62.5%, rgba(var(--canvas-rgb), 0.316) 68.75%, rgba(var(--canvas-rgb), 0.5) 75%, rgba(var(--canvas-rgb), 0.684) 81.25%, rgba(var(--canvas-rgb), 0.844) 87.5%, rgba(var(--canvas-rgb), 0.957) 93.75%, rgba(var(--canvas-rgb), 1) 100%);
  }
  .hero__shade, .hero__field { display: none; }
  .hero__core { margin-top: var(--s3); }
  .hero__h1 { font-size: clamp(3rem, min(28vw, 44vh - 12.5rem), 7rem); font-size: clamp(3rem, min(28vw, 44svh - 12.5rem), 7rem); }  /* 28vw keeps TO SOLD. inside the gutters */
  .hero__label { margin-bottom: var(--s3); }
  .hero__sub { margin-top: var(--s4); }
  .hero .cta-row { margin-top: var(--s5); }
  .hero__figs { width: 100%; margin: var(--s6) auto 0; gap: var(--s3); }
  .hero__figs .fig__t { max-width: 13ch; font-size: 0.8125rem; text-wrap: balance; }  /* v2.3.1: "Site live / in a day", no lone word */
  .hero__pause { top: calc(var(--band-top) + var(--band-h) - 48px); bottom: auto; right: calc(var(--gutter) - var(--s2)); }
}
@media (min-width: 761px) and (max-width: 767px) { .hero { --band-top: 4.25rem; } }  /* 4px under the one-row nav, as under the two-row one */
@supports not (height: 1svh) { @media (max-width: 767px) { .hero { --band-h: clamp(180px, 35vh, 420px); } } }
/* landscape phones, 480-767px wide and under 500px tall: no 180px floor, so the band stays 35% and the H1 reaches the
   first screen. Narrower or portrait short screens (a phone at 150-200% zoom) keep the 180px band and the Pause inside it:
   there the centred label row has no room beside a Pause on the canvas (delta reviews 1 and 2) */
@media (min-width: 480px) and (max-width: 767px) and (max-height: 500px) and (orientation: landscape) { .hero { --band-h: 35svh; } }
/* there the band is too short for its fade to carry the Pause label (2.3:1 on the bright frames), so the control drops
   to the canvas just under the band's bottom-right corner, beside the centred label row */
@media (min-width: 480px) and (max-width: 767px) and (max-height: 500px) and (orientation: landscape) { .hero__pause { top: calc(var(--band-top) + var(--band-h)); } }
@supports not (height: 1svh) { @media (min-width: 480px) and (max-width: 767px) and (max-height: 500px) and (orientation: landscape) { .hero { --band-h: 35vh; } } }
@media (prefers-reduced-motion: reduce) { .hero__pause { display: none; } }

/* 2 · the problem */
.problem { padding: var(--pause) var(--gutter) var(--s9); display: grid; gap: var(--s6); }
.problem .lead:first-child { max-width: 20ch; }
.problem .lead + .lead { max-width: 26ch; justify-self: end; text-align: left; }

/* section heads */
.sec { padding-inline: var(--gutter); }
.sec__h { font-size: var(--t-display); }
.sec__intro { margin-top: var(--s4); max-width: 46ch; color: var(--ink); font-size: var(--t-xl); line-height: 1.4; }

/* 3 · the work: nine films (v2.3, BRIEF_v2 §5). Each poster is a link to its full film, so the reel works without JS.
   - Baseline (no JS, reduced motion, no WebGL, Save-Data), 761px and up: a 3x3 grid of posters with play marks.
   - Phones: a swipe rail of portrait cards, centred on the snap; reel.js gives them depth and plays the centred card's loop.
   - 761px and up with JS + WebGL 2 and motion allowed (.work--3d): the track pins a stage, reel.js draws the nine panels
     on the shared canvas and lays each panel's link (the hit area and focus ring) and caption over its panel.
   reel.js also opens the film player (a modal dialog) from any of them. */
.work { padding-top: var(--s7); }
.reel { margin-top: var(--s8); display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: var(--s5); row-gap: var(--s8); }
.panel { display: grid; gap: var(--s4); align-content: start; }
.panel__film { position: relative; display: block; border-radius: var(--radius); color: var(--ink); }
.panel__frame { display: block; aspect-ratio: 3 / 4; }
.panel__frame > img, .panel__loop { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.panel__loop { opacity: 0; transition: opacity var(--dur-fast) var(--ease); }
.panel__loop.is-on { opacity: 1; }
/* the play mark: a disc with a drawn triangle on every poster. Hover fills it with the accent (a physical change, the
   triangle stays cream at 4.9:1), press 0.97 */
.panel__play {
  position: absolute; left: var(--s4); bottom: var(--s4); z-index: 1; display: grid; place-items: center; width: 48px; height: 48px;
  border-radius: var(--round); background: rgba(var(--canvas-rgb), 0.72);
  transition: background-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease), opacity var(--dur-roll) var(--ease);
}
.panel__play::before { content: ""; width: 13px; height: 15px; margin-left: 3px; background: var(--ink); clip-path: polygon(0 0, 100% 50%, 0 100%); }
@media (hover: hover) and (pointer: fine) { .panel__film:hover .panel__play { background: var(--accent-fill); } }
.panel__film:active .panel__play { transform: scale(0.97); }
.panel__meta { display: grid; gap: var(--s2); }
.panel__title { font: 500 var(--t-xl)/1.2 var(--f-text); letter-spacing: -0.01em; }
.panel__line { color: var(--ink); max-width: 44ch; }
.reel__hint { display: none; }

/* phones: the swipe rail. Cards are 66vw, centred on the snap (17vw of padding each side lets the first and last centre) */
@media (max-width: 760px) {
  .reel {
    position: relative; display: flex; gap: var(--s4); overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior-x: contain;
    margin-inline: calc(var(--gutter) * -1); padding: var(--s2) 17vw var(--s3); scroll-padding-inline: 17vw; scrollbar-width: none;
  }
  .reel::-webkit-scrollbar { display: none; }
  .panel { flex: 0 0 66vw; scroll-snap-align: center; }
  .panel__frame { aspect-ratio: 9 / 16; }
  .reel__hint { display: block; margin-top: var(--s3); }
  /* depth (reel.js, motion allowed): each card turns and recedes with its distance from the centre (transform only), and
     its frame dims under a canvas veil; the captions stay at full contrast */
  .reel--depth .panel__film { will-change: transform; }
  .reel--depth .panel__frame::after { content: ""; position: absolute; inset: 0; z-index: 2; border-radius: inherit; background: var(--canvas); opacity: var(--veil, 0); pointer-events: none; }
}

/* the 3D reel (reel.js): the track is the scroll length (--reel-h, set from the stage height), the stage stays pinned
   while the camera travels; links and captions are positioned over their panels each frame (transform only) */
@media (min-width: 761px) {
  .work--3d .reel-track { position: relative; height: var(--reel-h, 560vh); margin-top: var(--s7); }
  .work--3d .reel {
    position: sticky; top: 0; display: block; height: 100vh; height: 100svh; margin: 0 calc(var(--gutter) * -1);
    overflow: hidden; overflow: clip; contain: layout paint;
  }
  .work--3d .stage3d { position: absolute; inset: 0; width: 100%; height: 100%; }
  .work--3d .panel { display: block; }
  .work--3d .panel__film { position: absolute; left: 0; top: 0; width: var(--pw, 300px); height: var(--ph, 534px); transform-origin: 0 0; will-change: transform; }
  .work--3d .panel__frame { height: 100%; aspect-ratio: auto; background: none; overflow: visible; }
  .work--3d .panel__frame > img { display: none; }  /* the canvas draws the poster; a lazy image that is never shown never loads */
  .work--3d .panel__play { opacity: 0; }
  .work--3d .panel.is-near .panel__play { opacity: 1; }
  .work--3d .panel__meta {
    position: absolute; left: 0; top: 0; z-index: 50; width: min(36ch, 42vw); pointer-events: none; will-change: transform;
    opacity: 0; translate: 0 10px; transition: opacity var(--dur-roll) var(--ease), translate var(--dur-roll) var(--ease);
  }
  .work--3d .panel.is-near .panel__meta { opacity: 1; translate: none; }
  .work--3d .panel.is-off .panel__film, .work--3d .reel:not(.is-ready) .panel__film { pointer-events: none; }
}

/* 4 · how it's made (v2.3, BRIEF_v2 §5b): the 7-Layer Engine film fills the band. 761px and up: the label over the
   left 8 columns, the film across them at 16:9, the body in the right 4 at the film's foot. Phones: the 9:16 film full
   width under the label, the body below. Pause / Play and Sound on / off sit under the film (site.js; WCAG 2.2.2); the
   row keeps its height before it appears, so nothing moves when it does */
.made {
  margin-top: var(--s9); padding: var(--s7) var(--gutter); border-block: 1px solid var(--hairline);
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--s5); row-gap: var(--s4); align-items: end;
}
.made__label { grid-column: 1 / -1; }
.made__film { grid-column: 1 / span 8; }
.made__frame { aspect-ratio: 16 / 9; background: var(--lq-land) center / cover no-repeat var(--surface); }  /* the LQIP sits in a custom property (one per shape), so nova-ds's [style*="background-image"] cover rule doesn't reach it */
.made__frame img, .made__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.made__video { opacity: 0; transition: opacity var(--dur-fast) var(--ease); }
.made__video.is-on { opacity: 1; }
.made__body { grid-column: 9 / span 4; margin-bottom: calc(44px + var(--s2)); font-size: var(--t-xl); line-height: 1.45; max-width: 46ch; }
.made__ctl { display: flex; justify-content: flex-end; gap: var(--s4); min-height: 44px; margin-top: var(--s2); }
.made__ctl[hidden] { display: flex; visibility: hidden; }
.made__btn {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: 44px; padding: 0 var(--s2);
  font: 500 var(--t-label)/1 var(--f-mono); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--ink);
  background: none; border: 0; border-radius: var(--round); cursor: pointer;
  text-decoration-line: underline; text-decoration-color: transparent; text-decoration-thickness: 1px; text-underline-offset: 6px;
  transition: text-decoration-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.made__btn svg { width: 10px; height: 10px; fill: currentColor; }
.made__btn .i-play, .made__btn[data-state="paused"] .i-pause { display: none; }
.made__btn[data-state="paused"] .i-play { display: inline; }
@media (hover: hover) and (pointer: fine) { .made__btn:hover { text-decoration-color: var(--accent); } }
.made__btn:active { transform: scale(0.97); }
/* the play button (reduced motion, Save-Data, or a refused autoplay): the reel's play mark, larger, at the frame's centre */
.made__play {
  position: absolute; left: 50%; top: 50%; z-index: 1; display: grid; place-items: center; width: 64px; height: 64px; margin: -32px 0 0 -32px;
  border: 0; border-radius: var(--round); background: rgba(var(--canvas-rgb), 0.72); cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.made__play[hidden] { display: none; }
.made__play::before { content: ""; width: 17px; height: 20px; margin-left: 4px; background: var(--ink); clip-path: polygon(0 0, 100% 50%, 0 100%); }
@media (hover: hover) and (pointer: fine) { .made__play:hover { background: var(--accent-fill); } }
.made__play:active { transform: scale(0.97); }
/* portrait tablets (761-959px): four columns are too narrow for the body, so it sits under the film, across 9 */
@media (min-width: 761px) and (max-width: 959px) {
  .made__film { grid-column: 1 / -1; }
  .made__body { grid-column: 1 / span 9; margin-bottom: 0; }
}
@media (max-width: 760px) {
  .made { grid-template-columns: minmax(0, 1fr); }
  .made__film, .made__body { grid-column: 1; }
  .made__frame { aspect-ratio: 9 / 16; background-image: var(--lq-port); }  /* size, position and repeat come from the rule above */
  .made__body { margin-bottom: 0; }
}

/* 5 · cost of waiting (v2.3: the band above no longer leaves a tall gap, so this pause starts shorter) */
.wait { padding: var(--s9) var(--gutter) var(--pause); display: grid; }
.wait .lead { justify-self: end; max-width: 22ch; }

/* 6 · how it works */
.how { padding-top: var(--s7); }
.how__grid { margin-top: var(--s8); display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: clamp(32px, 6vw, 112px); }
.how__col { display: grid; gap: var(--s5); align-content: start; }
.day { border-top: 1px solid var(--hairline); }
.day li { display: grid; grid-template-columns: 7ch 1fr; gap: var(--s4); align-items: baseline; padding: var(--s4) 0; border-bottom: 1px solid var(--hairline); font-size: var(--t-xl); }
.day time { font: 500 var(--t-sm)/1 var(--f-mono); font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.day li:last-child time { color: var(--ink); }
.big72 { font: 500 clamp(4.5rem, 10vw, 8.5rem)/0.9 var(--f-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.04em; }
.how__col > p:not(.label):not(.big72) { color: var(--ink); max-width: 40ch; font-size: 1.125rem; }  /* was p:last-child; v2.3.1 adds a second line under the timeline */
.how__note { margin-top: calc(var(--s3) * -1); }  /* sits close under the line above: one thought in two sentences (Fida, 15 Oct) */
@media (max-width: 860px) { .how__grid { grid-template-columns: 1fr; row-gap: var(--s9); } }

/* 7 · two ways in */
.ways { padding-top: var(--pause); }
.ways__label { margin-bottom: 0; }
.ways__cards { margin-top: var(--s5); display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s5); }
.offer { display: grid; gap: var(--s4); align-content: start; }  /* the surface, radius, padding and edge light are nova-ds .card */
.offer__h { font-size: min(var(--t-display-m), 13.5vw); margin-top: var(--s2); }  /* the vw cap only bites below ~284px (a phone at 200% zoom), where CINEMATIC would outgrow the card */
.offer p { max-width: 40ch; }
.offer .tlink { margin-top: var(--s3); justify-self: start; }
.excl { margin-top: var(--s7); display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: var(--s5); align-items: baseline; }
.excl p:last-child { font-size: var(--t-xl); line-height: 1.4; max-width: 44ch; }
@media (max-width: 860px) { .ways__cards { grid-template-columns: minmax(0, 1fr); } .excl { grid-template-columns: 1fr; gap: var(--s3); } }

/* 8 · close (Fida, 13 Oct 19:08): one centred component on Home, /creative and /cinematic-3d-websites, the bookend
   to the centred hero. The display line stacks on three lines; the line under it is centred, max ~40ch.
   The exclusivity line above it (offer pages) stays as built. */
.close { padding: var(--pause) var(--gutter) var(--s10); }
.close .excl { margin: 0 0 var(--s9); }
.close__end { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--s6); }
.close__h { font-size: clamp(2.6rem, 9.6vw, 8.5rem); line-height: 0.86; }
.close__h > span { display: block; }
.close__line { font-size: var(--t-xl); line-height: 1.4; max-width: 40ch; color: var(--ink); text-wrap: balance; }
@media (max-width: 760px) { .close__h { font-size: 13vw; } }

/* =========================================================
   FILM PLAYER (v2.3, reel.js): a modal dialog over a canvas veil. The film flies forward from the poster that was
   clicked (transform + clip-path), plays with its own controls and sound, and flies back on Esc, back or Close.
   ========================================================= */
.player { position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; background: transparent; color: var(--ink); overflow: hidden; overscroll-behavior: contain; }
.player::backdrop { background: transparent; }
.player__bg { position: absolute; inset: 0; background: rgba(var(--canvas-rgb), 0.94); }
.player__stage { position: absolute; left: 0; top: 0; transform-origin: 0 0; border-radius: var(--radius); overflow: hidden; background: var(--surface); will-change: transform, clip-path; }
.player__film { display: block; width: 100%; height: 100%; object-fit: contain; background: var(--canvas); }
.player__close {
  position: absolute; top: var(--s3); right: calc(var(--gutter) - var(--s2)); display: grid; place-items: center; width: 48px; height: 48px;
  border: 1px solid rgba(var(--ink-rgb), 0.55); border-radius: var(--round); background: rgba(var(--canvas-rgb), 0.6); color: var(--ink); cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.player__close::before, .player__close::after { content: ""; position: absolute; left: 50%; top: 50%; width: 18px; height: 1.5px; margin: -0.75px 0 0 -9px; background: currentColor; border-radius: 1px; rotate: 45deg; }
.player__close::after { rotate: -45deg; }
@media (hover: hover) and (pointer: fine) { .player__close:hover { background: var(--accent-fill); border-color: var(--accent-fill); } }
.player__close:active { transform: scale(0.97); }

/* =========================================================
   OFFER PAGES
   ========================================================= */
.ohero { position: relative; min-height: 88vh; min-height: 88svh; isolation: isolate; overflow: hidden; overflow: clip; display: grid; align-content: end; gap: var(--s5); padding: clamp(calc(6.5rem + 8px), 16svh, calc(6.5rem + 36px)) var(--gutter) clamp(32px, 5vw, 72px); }
@supports not (height: 1svh) { .ohero { padding: clamp(calc(6.5rem + 8px), 16vh, calc(6.5rem + 36px)) var(--gutter) clamp(32px, 5vw, 72px); } }  /* see the hero's svh note */
.ohero__media { position: absolute; inset: 0; z-index: var(--z-media); }
.ohero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 45%; }
.ohero__scrim { position: absolute; inset: 0; z-index: var(--z-scrim); pointer-events: none; background: linear-gradient(to top, rgba(var(--canvas-rgb), 1) 0%, rgba(var(--canvas-rgb), 0.72) 45%, rgba(var(--canvas-rgb), 0.45) 100%); }
/* desktop: a portrait film becomes a right-hand column that fades into the canvas, so its own lettering never sits under the H1 */
/* up to 860px: the portrait film is an in-flow band under the header and the copy follows it on canvas,
   so neither the nav nor the H1 ever sits on the can's lettering, at any height */
@media (max-width: 860px) {
  .ohero:has(.ohero__media--port) { min-height: 0; align-content: start; padding-top: calc(6.5rem + 8px); }
  .ohero__media--port { position: relative; inset: auto; z-index: 0; height: clamp(170px, 40vh, 420px); height: clamp(170px, 40svh, 420px); margin: 0 calc(var(--gutter) * -1) var(--s2);
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 62%, transparent 100%); mask-image: linear-gradient(to bottom, #000 0%, #000 62%, transparent 100%); }
  .ohero__media--port img { object-position: 50% 30%; }
  .ohero__media--port + .ohero__scrim { display: none; }
}
/* phones (up to 880px tall): the promise and CTA come first, the film band follows under them */
@media (max-width: 860px) and (max-height: 880px) {
  .ohero__media--port { order: 10; margin: var(--s3) calc(var(--gutter) * -1) 0;
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 22%, #000 70%, transparent 100%); mask-image: linear-gradient(to bottom, transparent 0%, #000 22%, #000 70%, transparent 100%); }
}
@media (min-width: 861px) {
  .ohero__media--port { left: auto; right: 0; width: min(46vw, 640px); -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 38%); mask-image: linear-gradient(to right, transparent 0%, #000 38%); }
  .ohero__media--port ~ .ohero__h1 { max-width: calc(100% - min(46vw, 640px) + 40px); }
  .ohero__media--port ~ .ohero__sub { max-width: min(40ch, calc(100% - min(46vw, 640px) + 40px)); }
  .ohero__media--port + .ohero__scrim { background: linear-gradient(to top, rgba(var(--canvas-rgb), 1) 0%, rgba(var(--canvas-rgb), 0) 34%), linear-gradient(to bottom, rgba(var(--canvas-rgb), 0.72) 0%, rgba(var(--canvas-rgb), 0) 24%); }  /* v2.4: top band 0.6/20% -> 0.72/24% so the nav's last link clears 4.5:1 on the film's brightest frame at 1024 (measured 4.47 -> 5.90) */
}
/* creative hero film (v2.4, BRIEF_v2 §6): the ICE hero film plays over its own first frame, in the column (desktop) or
   band (phones) the poster had, under the same scrim and masks. The film is .hero__film (site.js: after first paint, in
   view only, never under reduced motion or Save-Data); its Pause / Play is the Home hero's control (WCAG 2.2.2), here
   at the hero's bottom-right edge on desktop and at the band's foot on phones */
.ohero__film { object-position: 50% 45%; }
.ohero .hero__pause { top: auto; right: calc(var(--gutter) - var(--s2)); bottom: calc(clamp(32px, 5vw, 72px) - 6px); z-index: 1; }  /* above the phone film band (order: 10 paints it later than the button; review R1 B1) */
@media (max-width: 860px) {
  .ohero__media--port .ohero__film { object-position: 50% 30%; }
  .ohero .hero__pause { top: calc(6.5rem + 8px + clamp(170px, 40vh, 420px) - 52px); top: calc(6.5rem + 8px + clamp(170px, 40svh, 420px) - 52px); bottom: auto; }  /* band under the header */
}
@media (max-width: 860px) and (max-height: 880px) { .ohero .hero__pause { top: auto; bottom: calc(clamp(32px, 5vw, 72px) + var(--s1)); } }  /* band last, under the CTA */
@media (max-width: 300px) { .ohero .hero__pause { top: auto; bottom: calc(clamp(32px, 5vw, 72px) + var(--s1)); } }

.ohero__h1 { font-size: clamp(3.6rem, min(13vw, 19vh), 10rem); font-size: clamp(3.6rem, min(13vw, 19svh), 10rem); line-height: 0.84; }
.ohero__sub { font-size: var(--t-xl); line-height: 1.45; max-width: 40ch; }
.ohero > p, .ohero > h1 { justify-self: start; }
.ohero .label { margin-bottom: calc(var(--s3) * -1); }

/* creative · formats strip */
.formats { margin-top: var(--s9); padding: var(--s6) var(--gutter); border-block: 1px solid var(--hairline); overflow: hidden; }
.formats p { font-size: clamp(2rem, 5.6vw, 4.6rem); line-height: 1; color: var(--ink); }

/* rows (ways to start, sizes) */
.rows { margin-top: var(--s8); border-top: 1px solid var(--hairline); }
.row { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); gap: var(--s5); padding: var(--s6) 0; border-bottom: 1px solid var(--hairline); align-items: baseline; }
.row h3 { font: 500 var(--t-xl)/1.2 var(--f-text); letter-spacing: -0.01em; }
.row p { color: var(--ink); font-size: 1.125rem; max-width: 52ch; }
@media (max-width: 760px) { .row { grid-template-columns: 1fr; gap: var(--s2); padding: var(--s5) 0; } }

/* numbered steps (a real sequence) */
.steps { margin-top: var(--s8); display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s5); counter-reset: step; }
.steps li { display: grid; gap: var(--s3); align-content: start; padding-top: var(--s4); border-top: 1px solid var(--hairline); }
.steps li::before { counter-increment: step; content: counter(step); font: 500 var(--t-sm)/1 var(--f-mono); color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.steps h3 { font: 500 var(--t-xl)/1.2 var(--f-text); }
.steps p { color: var(--ink); }
@media (max-width: 1000px) { .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--s7); } }
@media (max-width: 560px) { .steps { grid-template-columns: 1fr; } }

/* creative · the work */
.cwork { margin-top: var(--s8); display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--s5); row-gap: var(--s7); }
.cwork .panel--ice { grid-column: 1 / span 5; grid-row: 1 / span 2; }
.cwork .panel--dirtea { grid-column: 6 / span 7; }
.cwork .panel--nb { grid-column: 6 / span 7; }
@media (max-width: 760px) {
  .cwork { grid-template-columns: 1fr; }
  .cwork .panel--ice, .cwork .panel--dirtea, .cwork .panel--nb { grid-column: 1; grid-row: auto; }
  .cwork .panel .media { aspect-ratio: 4 / 5; }  /* v2.3: was the Home reel's unscoped phone rule, which also shaped these tiles */
}

/* offer pages · film tiles (v2.4, tiles.js): each tile is a link to its full film with the reel's play mark. A muted
   preview (.panel__loop, as on Home) dissolves in over the poster; a click opens the film player (player.js) */
.tfilm { position: relative; display: block; border-radius: var(--radius); color: var(--ink); }
.tfilm .media { display: block; }
@media (hover: hover) and (pointer: fine) { .tfilm:hover .panel__play { background: var(--accent-fill); } }
.tfilm:active .panel__play { transform: scale(0.97); }
.tile .panel__play { left: var(--s3); bottom: var(--s3); }

/* creative · more work rail */
.more { margin-top: var(--pause); }
.more__line { margin-top: var(--s3); font-size: var(--t-xl); color: var(--ink); }
.rail {
  margin-top: var(--s6); display: flex; gap: var(--s4); overflow-x: auto; scroll-snap-type: x proximity; overscroll-behavior-x: contain;
  padding: 0 var(--gutter) var(--s4); scroll-padding-inline: var(--gutter); scrollbar-width: thin;
}
.tile { flex: 0 0 auto; display: grid; gap: var(--s3); scroll-snap-align: start; }
.tile .media { height: clamp(300px, 34vw, 440px); aspect-ratio: auto; }
.tile .media img { width: auto; height: 100%; }
.tile figcaption { font-size: var(--t-sm); color: var(--ink); }

/* websites · hero wireframe: the coded drawing, and the real-time scene's first frame (v2.4, sites3d.js draws the
   scene on .wire__gl, hidden until its first frame is on it, then the drawing hides: the swap doesn't show) */
.wire { position: absolute; inset: 0; z-index: var(--z-media); display: grid; place-items: center end; padding: 120px var(--gutter) 0; }
.wire svg { width: min(62vw, 900px); height: auto; opacity: 1; }
.wire .w-line { fill: none; stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.wire .w-soft { fill: none; stroke: var(--hairline); stroke-width: 1; vector-effect: non-scaling-stroke; }
.wire .w-fill { fill: var(--surface); }

.wire__gl { position: absolute; inset: 0; width: 100%; height: 100%; display: block; visibility: hidden; }
.wire--3d .wire__gl { visibility: visible; }
.wire--3d svg { visibility: hidden; }
/* fallback end state (PLAN_v2.4 §4, review R1 F3): reduced motion, Save-Data and no WebGL 2 (sites3d.js adds .wire--lit when it
   bails) and JS off (the page's noscript style, same rules) show the drawing assembled and lit, as the 3D scene rests: the
   blocks on canvas inside the surface panel, the penthouse frame in the hero block (its href is set by sites3d.js when it
   bails, so the 3D path never fetches it; JS off shows the lit panel without the frame), the headline bars in ink. Frame 0
   of the 3D path stays the bare drawing, so the swap to the canvas still shows nothing. */
.wire .w-img, .wire .w-shade { display: none; }
.wire--lit .w-img, .wire--lit .w-shade { display: inline; }
.wire .w-shade { fill: url(#w-scrim); }
.wire--lit rect.w-soft { fill: var(--canvas); }
.wire--lit .w-text { stroke: var(--ink); stroke-width: 3; }

/* websites · the day (v2.4, BRIEF_v2 §13 G: pinned scrollytelling). The track is nova-ds timeline. With JS and motion
   allowed, site.js adds .dayrun--live: the violet line runs along the track with the scroll, and each hour lands on its
   beat as the line reaches it (its time turns cream, its dot fills with the accent, its words rise 8px into place); the
   SITE DAYS label lands once the line has passed the last beat (a wipe down + a rise, not a fade). 861px and up
   the track and its copy stick a little above the middle of the screen for ~0.9 screens while the line runs (.dayrun--pin); phones fill the rail in place, the line keeping
   pace with a reading line at 62% of the screen. Reduced motion and JS off: the timeline as it was, every beat on.
   Unreached words stay ink-soft (5.2:1), never below the 4.5:1 floor. */
.dayrun--live .track { position: relative; border-top-color: var(--hairline); }
.dayrun--live .track::after { content: ""; position: absolute; left: 0; top: -1px; width: 100%; height: var(--line-w); background: var(--accent); transform-origin: 0 50%; transform: scaleX(var(--fill, 0)); }
.dayrun--live .track li::before { background: var(--canvas); box-shadow: inset 0 0 0 1px var(--ink-soft); transform: scale(0.78); transition: background-color var(--dur-roll) var(--ease), box-shadow var(--dur-roll) var(--ease), transform var(--dur-roll) var(--ease); }
.dayrun--live .track li.is-on::before { background: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); transform: none; }
.dayrun--live .track time { transition: color var(--dur-roll) var(--ease); }
.dayrun--live .track li.is-on time { color: var(--ink); }
.dayrun--live .track span { color: var(--ink-soft); transform: translateY(8px); transition: color 420ms var(--ease), transform 420ms var(--ease); }
.dayrun--live .track li.is-on span { color: var(--ink); transform: none; }
.dayrun--live .day-copy > .label { clip-path: inset(0 0 100% 0); transform: translateY(10px); transition: clip-path 520ms var(--ease), transform 520ms var(--ease); }
.dayrun--live .day-copy.is-on > .label { clip-path: inset(0 0 0 0); transform: none; }
@media (min-width: 861px) {
  .dayrun--pin .dayrun__pin { position: sticky; top: clamp(96px, 50vh - 10rem, 40vh); top: clamp(96px, 50svh - 10rem, 40svh); }  /* sticks a little above the middle of the screen */
  .dayrun--pin::after { content: ""; display: block; height: 90vh; height: 90svh; }  /* the pinned run (inside the section's content box, where sticky can use it) */
}
@media (max-width: 860px) {
  .dayrun--live .track { border-left-color: var(--hairline); }
  .dayrun--live .track::after { left: -1px; top: 0; width: var(--line-w); height: 100%; transform-origin: 50% 0; transform: scaleY(var(--fill, 0)); }
}

/* websites · the day's copy under the track */
.day-copy { margin-top: var(--s7); display: grid; grid-template-columns: minmax(0, 8fr) minmax(0, 4fr); gap: var(--s5); align-items: end; }
.day-copy p:first-child { font-size: var(--t-xl); line-height: 1.45; max-width: 48ch; }
.day-copy .label { justify-self: end; text-align: right; }
@media (max-width: 860px) {
  .day-copy { grid-template-columns: 1fr; }
  .day-copy .label { justify-self: start; text-align: left; }
}

/* websites · live by 5pm */
.live { padding-top: var(--pause); display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--s5); align-items: start; }
.live ul { border-top: 1px solid var(--hairline); }
.live li { padding: var(--s4) 0; border-bottom: 1px solid var(--hairline); font-size: var(--t-xl); line-height: 1.3; }
@media (max-width: 860px) { .live { grid-template-columns: 1fr; gap: var(--s7); } }

/* websites · the work (two portrait films, staggered) */
.swork { margin-top: var(--s8); display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: var(--s5); row-gap: var(--s8); }
.swork .panel--pent { grid-column: 2 / span 4; }
.swork .panel--ice { grid-column: 8 / span 4; margin-top: var(--s10); }
@media (max-width: 760px) {
  .swork { grid-template-columns: 1fr; }
  .swork .panel--pent, .swork .panel--ice { grid-column: 1; margin-top: 0; }
  .swork .media--port { aspect-ratio: 4 / 5; }
}

/* helpers used across offer pages */
.sec--pause { padding-top: var(--pause); }
.vline--gap { margin-top: var(--s9); }

/* short portrait phones (iPhone SE with Safari bars, small Android): the Home hero tightens so H1 and CTA share the first screen */
@media (max-width: 900px) and (max-height: 700px) {
  .hero { padding-bottom: var(--s4); row-gap: var(--s4); }  /* the top padding stays the nav's: 16px clear of it on phones */
  .hero__figs .fig__n { font-size: 1.25rem; }
  .hero__figs .fig__t { font-size: 0.75rem; }
  .hero__label { margin-bottom: var(--s2); }
  .hero__sub { margin-top: var(--s3); font-size: 0.9375rem; line-height: 1.45; }
  .hero .cta-row { margin-top: var(--s4); }
  .hero .btn--big { min-height: 48px; padding: 0 var(--s5); font-size: 1rem; }
}

/* ---------- websites hero: the wireframe keeps its own column, never behind the copy ---------- */
@media (min-width: 861px) {
  .ohero--sites .wire { left: auto; right: 0; width: min(46vw, 660px); padding: clamp(104px, 14svh, 140px) var(--gutter) clamp(32px, 5vw, 72px) 0; place-items: center end; }
  @supports not (height: 1svh) { .ohero--sites .wire { padding: clamp(104px, 14vh, 140px) var(--gutter) clamp(32px, 5vw, 72px) 0; } }
  .ohero--sites .wire svg { width: 100%; max-height: 100%; }
  .ohero--sites .ohero__h1 { max-width: calc(100% - min(46vw, 660px) - 24px); }
  .ohero--sites .ohero__sub { max-width: min(40ch, calc(100% - min(46vw, 660px) - 24px)); }
  .ohero--sites .ohero__scrim { background: linear-gradient(to top, rgba(var(--canvas-rgb), 1) 0%, rgba(var(--canvas-rgb), 0) 30%); }
}
@media (max-width: 860px) {
  .ohero--sites { min-height: 0; align-content: start; padding-top: calc(6.5rem + 8px); }
  .ohero--sites .wire { position: relative; inset: auto; z-index: 0; display: block; padding: 0; height: clamp(150px, 30vh, 300px); height: clamp(150px, 30svh, 300px); margin-bottom: var(--s2); }
  .ohero--sites .wire svg { width: auto; height: 100%; max-width: 100%; margin: 0 auto; }
  .ohero--sites .ohero__scrim { display: none; }
}
@media (max-width: 860px) and (max-height: 880px) {
  .ohero--sites .wire { order: 10; margin: var(--s3) 0 0; }
}

/* scrims only ever darken: where the film is already darker than canvas, it stays as dark as it was (the Home hero shading does the same) */
.ohero__scrim { mix-blend-mode: darken; }

/* websites hero, desktop: a slightly smaller H1 so CLOSE THE SALE. keeps two lines beside the wire column */
@media (min-width: 861px) { .ohero--sites .ohero__h1 { font-size: clamp(3.6rem, min(10vw, 19vh), 10rem); font-size: clamp(3.6rem, min(10vw, 19svh), 10rem); } }

/* a phone at 200% zoom (<= 300px): the nova-ds header joins the flow there, so the heroes drop their clearance for it */
@media (max-width: 300px) { .hero { --band-top: var(--s1); } .ohero { padding-top: var(--s5); } .ohero:has(.ohero__media--port) { padding-top: var(--s5); } }  /* two rules: a browser without :has() drops a whole list */
