/* =====================================================================
   Beauty by Kocer — "The Powder Edit" (design 16)
   Mobile-first baseline = the complete static page: every photo and film
   visible, a CSS dust texture on the velvet, no canvas. That is what
   renders with no JavaScript, under prefers-reduced-motion and with the
   Motion toggle off. powder.js layers the living powder on top through
   three classes on <html>: .js (scripting exists), .powder-live (the
   canvas is running) and .reveals-on (GSAP reveals are armed).
   ===================================================================== */

/* ---------- Tokens ---------- */
:root {
  /* Palette (brief). Every colour on the page is one of these; powder.js
     reads the four powder tones from here to paint its particles. */
  --velvet: #120E14;
  --velvet-2: #1C1620;
  --pearl: #FFF5F0;
  --mist: #CBBFC6;
  --powder: #F7C6D0;
  --peach: #FFD9C2;
  --gold: #E7C47D;
  /* Translucent tints of the palette, named once so no rule spells a raw
     colour: header scrim, dialog backdrop, hairlines, frame glow, torch,
     glint and the static dust specks. */
  --velvet-scrim: rgba(18, 14, 20, .9);
  --velvet-clear: rgba(18, 14, 20, 0);
  --backdrop: rgba(12, 9, 14, .88);
  --hairline: rgba(255, 245, 240, .16);
  --hairline-strong: rgba(255, 245, 240, .38);
  --hover-fill: rgba(255, 245, 240, .08);
  --frame-glow: rgba(247, 198, 208, .22);
  --halo-line: rgba(231, 196, 125, .45);
  --sheen: rgba(247, 198, 208, .07);
  --torch-core: rgba(255, 217, 194, .13);
  --torch-edge: rgba(247, 198, 208, .05);
  --glint-soft: rgba(255, 245, 240, .1);
  --glint-bright: rgba(255, 236, 214, .34);
  --glint-speck: rgba(255, 245, 240, .75);
  --ripple: rgba(28, 22, 32, .5);
  --speck-powder: rgba(247, 198, 208, .55);
  --speck-peach: rgba(255, 217, 194, .45);
  --speck-gold: rgba(231, 196, 125, .5);
  --speck-pearl: rgba(255, 245, 240, .4);

  /* Type: Bellefair (display) + Albert Sans (text), with metric-similar
     system fallbacks so the page reads correctly before the fonts arrive. */
  --font-display: 'Bellefair', Georgia, 'Times New Roman', serif;
  --font-text: 'Albert Sans', 'Segoe UI', system-ui, sans-serif;
  --fs-xs: 13px;
  --fs-sm: 14px;
  --fs-md: 16px;
  --fs-lg: 18px;
  --fs-xl: 24px;
  --fs-h2: clamp(34px, 4.6vw, 60px);
  --fs-kicker: clamp(24px, 2.6vw, 36px);
  /* KOCER is ~3.6em wide in Bellefair and in Georgia, so 27% of its own
     column (cqi, the h1 is the query container) always fits the five
     capitals, whatever width the film beside it leaves. */
  --fs-word: min(27cqi, 150px);
  --fs-counter: clamp(72px, 8vw, 112px);

  /* Spacing: one 4px scale. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-8: 32px;
  --sp-10: 40px;
  --sp-12: 48px;
  --sp-16: 64px;
  --sp-24: 96px;

  /* Radii: one fixed set. */
  --r-sm: 8px;
  --r-md: 16px;
  --r-lg: 24px;
  --r-pill: 999px;

  /* Layout */
  --gutter: 16px;
  --maxw: 1320px;
  --header-h: 60px;
  --section-pad: clamp(96px, 16svh, 160px);
  /* The narrowest a text column beside a film may ever get. */
  --copy-min: 320px;

  /* Easing: every transition and keyframe names one of these. */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* clip (never hidden, and never on body): stops any stray decoration from
   widening the phone layout viewport without creating a scroll container. */
html { -webkit-text-size-adjust: 100%; overflow-x: clip; }
body {
  margin: 0;
  background: var(--velvet);
  color: var(--pearl);
  font: 400 var(--fs-md)/1.6 var(--font-text);
}
h1, h2, h3 { margin: 0; font-weight: 400; text-wrap: balance; }
p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img { display: block; max-width: 100%; height: auto; }
video { display: block; }
a { color: inherit; }
button { font: inherit; color: inherit; }
/* An author display value must never beat the hidden attribute (the media
   error lines are toggled with it). */
[hidden] { display: none !important; }

/* Gold on velvet is 11.5:1; every ring sits on velvet thanks to the offset. */
:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
/* main is focused by the skip link, not clicked: the visible ring belongs on
   whatever the visitor tabs to next. */
main:focus { outline: none; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: -80px;
  left: var(--sp-3);
  z-index: 20;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--sp-4);
  border-radius: var(--r-pill);
  background: var(--pearl);
  color: var(--velvet);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { top: var(--sp-3); }

/* ---------- Velvet: a fixed sheen, plus the static dust texture that stands
   in for the canvas whenever the powder is not running ---------- */
body::before,
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
body::before {
  background:
    radial-gradient(120% 70% at 50% -10%, var(--velvet-2) 0%, var(--velvet-clear) 62%),
    radial-gradient(70% 55% at 100% 100%, var(--sheen) 0%, var(--velvet-clear) 70%);
}
/* Twelve tiled layers of specks. Every tile is a different, non-square
   size (none smaller than 131px) with its speck off-centre, so the repeats
   never line up into rows or columns: it reads as loose, uneven powder. */
body::after {
  background-image:
    radial-gradient(circle at 17% 23%, var(--speck-powder) 0 1px, var(--velvet-clear) 1.7px),
    radial-gradient(circle at 71% 58%, var(--speck-peach) 0 1.3px, var(--velvet-clear) 2px),
    radial-gradient(circle at 42% 81%, var(--speck-gold) 0 .9px, var(--velvet-clear) 1.5px),
    radial-gradient(circle at 88% 12%, var(--speck-pearl) 0 1px, var(--velvet-clear) 1.8px),
    radial-gradient(circle at 29% 64%, var(--speck-powder) 0 .8px, var(--velvet-clear) 1.4px),
    radial-gradient(circle at 63% 37%, var(--speck-peach) 0 1.1px, var(--velvet-clear) 1.9px),
    radial-gradient(circle at 8% 91%, var(--speck-gold) 0 1.2px, var(--velvet-clear) 2px),
    radial-gradient(circle at 53% 6%, var(--speck-pearl) 0 .9px, var(--velvet-clear) 1.6px),
    radial-gradient(circle at 94% 77%, var(--speck-powder) 0 1px, var(--velvet-clear) 1.7px),
    radial-gradient(circle at 36% 47%, var(--speck-pearl) 0 .8px, var(--velvet-clear) 1.4px),
    radial-gradient(circle at 81% 33%, var(--speck-gold) 0 .9px, var(--velvet-clear) 1.6px),
    radial-gradient(circle at 12% 52%, var(--speck-peach) 0 1px, var(--velvet-clear) 1.7px);
  background-size:
    241px 173px, 317px 389px, 199px 263px, 431px 283px, 157px 337px, 373px 211px,
    283px 461px, 509px 293px, 227px 419px, 131px 229px, 347px 151px, 179px 307px;
  transition: opacity 700ms var(--ease-out);
}
html.powder-live body::after { opacity: 0; }

/* ---------- Decorative fixed layers: torch (#72) under the powder canvas
   (#111). Both ignore the pointer so every film and link stays clickable. */
.torch-layer {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 400ms var(--ease-out);
}
html.torch-on .torch-layer { opacity: 1; }
.torch {
  position: absolute;
  top: 0;
  left: 0;
  width: 640px;
  height: 640px;
  margin: -320px 0 0 -320px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--torch-core) 0%, var(--torch-edge) 38%, var(--velvet-clear) 70%);
  will-change: transform;
}
.powder-canvas {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
html.js .powder-canvas {
  display: block;
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
}
html.powder-live .powder-canvas { opacity: 1; }

/* ---------- Header: always fixed, always a sibling of main ---------- */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 10;
  height: var(--header-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: 0 var(--gutter);
  background: linear-gradient(to bottom, var(--velvet) 0%, var(--velvet-scrim) 55%, var(--velvet-clear) 100%);
}
/* Inline flow (not flex) so both parts share one baseline inside a 44px line. */
.wordmark {
  display: inline-block;
  min-height: 44px;
  font: 400 var(--fs-xl)/44px var(--font-display);
  letter-spacing: .04em;
  text-decoration: none;
  white-space: nowrap;
}
.wordmark-small {
  margin-right: var(--sp-1);
  font: 600 var(--fs-xs)/1 var(--font-text);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--mist);
}
.motion-toggle {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 0 var(--sp-4);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-pill);
  background: none;
  font: 600 var(--fs-xs)/1 var(--font-text);
  letter-spacing: .14em;
  text-transform: uppercase;
  cursor: pointer;
}
.motion-state { color: var(--gold); }
.motion-toggle:disabled { cursor: not-allowed; opacity: .6; }
/* The toggle does nothing without JS, so it only appears once scripting exists. */
html:not(.js) .motion-toggle { display: none; }

/* ---------- Shared controls ---------- */
/* Hover highlights are an opacity fade on a pseudo layer, and underlines a
   transform, so no colour or size ever animates. */
.btn::before,
.motion-toggle::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--hover-fill);
  opacity: 0;
  transition: opacity 200ms var(--ease-out);
}
.btn:hover::before,
.motion-toggle:not(:disabled):hover::before { opacity: 1; }
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 0 var(--sp-5);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-pill);
  background: none;
  font: 500 var(--fs-sm)/1 var(--font-text);
  letter-spacing: .06em;
  cursor: pointer;
}
/* The play button opens a JS dialog; without JS the film's own controls stand in. */
html:not(.js) .btn { display: none; }
.text-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  color: var(--gold);
  font: 500 var(--fs-sm)/1.3 var(--font-text);
  letter-spacing: .04em;
  text-decoration: none;
}
.text-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--sp-2);
  height: 1px;
  background: currentColor;
  transform: scaleX(.35);
  transform-origin: left center;
  transition: transform 300ms var(--ease-out);
}
.text-link:hover::after,
.text-link:focus-visible::after { transform: scaleX(1); }

.eyebrow {
  font: 600 var(--fs-xs)/1.4 var(--font-text);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--gold);
}

/* ---------- Media frames ---------- */
/* A frame clips its film (overflow + radius), which would also clip the
   film's own focus ring; the ring is drawn on the frame instead. */
.frame:has(:focus-visible) { outline: 2px solid var(--gold); outline-offset: 3px; }
.frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-md);
  background: var(--velvet-2);
  box-shadow: 0 0 0 1px var(--hairline), 0 32px 80px -40px var(--frame-glow);
}
.frame > img,
.frame > video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.media-error {
  position: absolute;
  right: var(--sp-3);
  bottom: var(--sp-3);
  left: var(--sp-3);
  z-index: 2;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  background: var(--backdrop);
  color: var(--pearl);
  font-size: var(--fs-sm);
}
.media-error a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--gold);
}
figcaption { color: var(--mist); font-size: var(--fs-sm); }

/* ---------- Sections ---------- */
/* padding-inline centres a max-width column without a wrapper element;
   overflow-x: clip keeps any scaled print or glint inside its section. */
.scene {
  position: relative;
  overflow-x: clip;
  padding-block: var(--section-pad);
  padding-inline: max(var(--gutter), (100% - var(--maxw)) / 2);
  scroll-margin-top: var(--header-h);
}
main,
.site-footer { position: relative; z-index: 3; }
.section-head {
  display: grid;
  justify-items: start;
  gap: var(--sp-4);
  max-width: 44rem;
  margin-bottom: var(--sp-12);
}
.section-head h2,
.film-head h2,
.contact-card h2 {
  font: 400 var(--fs-h2)/1.08 var(--font-display);
  letter-spacing: .01em;
}
.section-head p:not(.eyebrow),
.film-head p:not(.eyebrow):not(.counter),
.contact-card p:not(.eyebrow) {
  max-width: 40rem;
  color: var(--mist);
  font-size: var(--fs-lg);
}

/* ---------- 1. Hero ---------- */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: center;
  justify-items: start;
  gap: var(--sp-3);
  min-height: 100svh;
  padding-top: calc(var(--header-h) + var(--sp-4));
  padding-bottom: var(--sp-12);
}
/* UA h1 is 2em; the two children below carry explicit sizes instead. It
   stretches to its column and is the size container for the word's cqi. */
.hero-title {
  display: grid;
  justify-items: start;
  justify-self: stretch;
  gap: var(--sp-2);
  font-size: var(--fs-md);
  container-type: inline-size;
}
.hero-kicker {
  font: 400 var(--fs-kicker)/1.1 var(--font-display);
  letter-spacing: .08em;
}
/* The static (no-canvas) word is itself powdered: a speckled pearl-to-gold
   fill clipped to the letters, with a glowing dust line beneath. Under the
   canvas the powder draws the word and its line; this solid word only fades
   in under the settled powder (the middle path, see the pre-states below). */
.hero-word {
  display: block;
  padding-bottom: .16em;
  font: 400 var(--fs-word)/.9 var(--font-display);
  letter-spacing: .02em;
  text-transform: uppercase;
  background-image:
    radial-gradient(circle at 30% 40%, var(--speck-pearl) 0 1px, var(--velvet-clear) 1.6px),
    linear-gradient(100deg, var(--pearl) 10%, var(--powder) 45%, var(--peach) 70%, var(--gold) 95%);
  background-size: 9px 11px, 100% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: var(--velvet-clear);
}
.hero-word::after {
  content: "";
  display: block;
  height: 2px;
  margin-top: .08em;
  background: radial-gradient(ellipse at center, var(--gold) 0%, var(--powder) 40%, var(--velvet-clear) 72%);
}
/* The powder draws its own dust line: never two lines stacked. visibility
   (not display) keeps the word's box, so the powder's targets never move. */
html.powder-live .hero-word::after { visibility: hidden; }
.hero-film { justify-self: center; width: 100%; }
/* Phones: width-led so a 3:4 film is never wider than the column, which
   keeps it at 52svh or taller on every tested phone (see brief). */
.frame-hero {
  width: min(100%, 56svh * .75);
  margin-inline: auto;
  aspect-ratio: 3 / 4;
}
.hero-film figcaption { margin-top: var(--sp-3); text-align: center; }
.hero-lede { max-width: 34rem; color: var(--mist); font-size: var(--fs-lg); }
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-6);
}
/* Below 1024px the hero is one column, and the cue sits right under the
   title so it is on the first screen (768x1024, 390x844, 360x740) with the
   film still whole below it. Desktop places every item by grid area. */
.hero-eyebrow,
.hero-title { order: -2; }
.scroll-cue {
  order: -1;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--mist);
  font: 600 var(--fs-xs)/1.4 var(--font-text);
  letter-spacing: .16em;
  text-transform: uppercase;
}
.cue-grain {
  position: relative;
  width: 6px;
  height: 24px;
  border-radius: var(--r-pill);
  box-shadow: inset 0 0 0 1px var(--hairline);
}
/* A single grain falling down the cue: eight loops (about ten seconds), then
   it rests, so nothing keeps animating on an idle page. */
.cue-grain::before {
  content: "";
  position: absolute;
  top: 3px;
  left: 1px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--powder);
  animation: cue-fall 1200ms var(--ease-in-out) 8;
}
@keyframes cue-fall {
  0% { transform: translateY(0); opacity: 0; }
  25% { opacity: 1; }
  100% { transform: translateY(14px); opacity: 0; }
}

/* ---------- 2. Two clients ---------- */
.prints {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-16);
}
/* Both halves always visible: a square print in a square frame, contained. */
.frame-square { width: 100%; aspect-ratio: 1; }
/* While the reveals run, the square is see-through, so its loose powder
   shows before the print dissolves in over it. */
html.reveals-on .frame-square { background: none; }
.frame-square > img { object-fit: contain; }
/* 32px clears the powder outline (10px outside the frame) plus the text
   keep-clear zone, so the outline is never dimmed under the caption. */
.print figcaption {
  display: grid;
  justify-items: start;
  gap: var(--sp-1);
  margin-top: var(--sp-8);
}
.print h3 { color: var(--pearl); font: 400 var(--fs-xl)/1.2 var(--font-display); }
.print p { font-size: var(--fs-md); }

/* ---------- 3. Client three: film, counter, detail, stills ---------- */
.film-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-12);
}
.film-head {
  display: grid;
  justify-items: start;
  gap: var(--sp-4);
  max-width: 36rem;
}
/* Width-led on phones (56svh or taller there), capped on tablets so the
   single-column film never outgrows the screen. */
.frame-film { width: min(100%, 76svh * .75); aspect-ratio: 3 / 4; }
.film-main figcaption,
.film-detail figcaption { margin-top: var(--sp-8); }
.film-detail { width: 100%; }
.frame-detail { width: min(100%, 60svh * .75); aspect-ratio: 3 / 4; }

/* #134 sliding number counter: each digit is a reel of stacked numerals
   (one per 1em line) seen through a 1em window. The markup rests on the
   final value, 41, so the number is right with no JS at all. */
.counter {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-3);
  line-height: 1;
}
.counter-digits {
  display: inline-flex;
  font: 400 var(--fs-counter)/1 var(--font-display);
  color: var(--pearl);
}
.reel {
  display: inline-block;
  height: 1em;
  overflow: hidden;
}
.reel-strip {
  display: block;
  width: 1ch;
  text-align: center;
  word-break: break-all;
}
.reel-tens { transform: translateY(-4em); }
.reel-ones { transform: translateY(-41em); }
/* Lifts the small unit onto the numerals' baseline, which sits about 12%
   of the numeral size above the bottom of their 1em window. */
.counter-unit {
  padding-bottom: calc(var(--fs-counter) * .12);
  color: var(--gold);
  font: 600 var(--fs-xs)/1 var(--font-text);
  letter-spacing: .18em;
  text-transform: uppercase;
}

.stills { margin-top: var(--sp-24); }
/* 64px leaves the stills' powder trail a clear lane above the frames. */
.stills-title {
  margin-bottom: var(--sp-16);
  font: 400 var(--fs-xl)/1.2 var(--font-display);
}
/* Phones keep one column (brief), and every gallery zig-zags down it: each
   frame takes about two thirds of the width, alternating sides, so the
   trail, the vortex, the halos and the glint band stay visible in the open
   side of each row instead of hiding behind full-width photos. */
.stills-list,
.triptych,
.looks-grid,
.pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-10);
}
.stills-list > li,
.triptych > li,
.looks-grid > li,
.pair > .pair-item { width: 66%; }
.stills-list > li:nth-child(even),
.triptych > li:nth-child(even),
.looks-grid > li:nth-child(even),
.pair > .pair-item:nth-child(even) { justify-self: end; }
.frame-still { width: 100%; aspect-ratio: 3 / 4; }
.still figcaption { margin-top: var(--sp-8); }
.still-step {
  display: block;
  color: var(--gold);
  font: 600 var(--fs-xs)/1.4 var(--font-text);
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* ---------- 4. The artist at work ---------- */
.frame-tri { width: 100%; aspect-ratio: 3 / 4; }
.tri figcaption {
  display: grid;
  justify-items: start;
  margin-top: var(--sp-8);
}

/* ---------- 5. Her own looks ---------- */
.frame-oval {
  width: 100%;
  aspect-ratio: 3 / 4;
  border-radius: 50%;
  /* Static halo for the no-canvas page; the powder ring replaces it live. */
  outline: 1px solid var(--halo-line);
  outline-offset: 10px;
}
html.powder-live .frame-oval { outline-color: var(--velvet-clear); }
.look figcaption {
  display: flex;
  justify-content: center;
  margin-top: var(--sp-8);
}

/* ---------- 6. Her own transformation ---------- */
.frame-tall { width: 100%; aspect-ratio: 9 / 16; }
.frame-tall > img { object-fit: contain; }
.pair-item figcaption {
  margin-top: var(--sp-8);
  color: var(--gold);
  font: 600 var(--fs-xs)/1.4 var(--font-text);
  letter-spacing: .18em;
  text-align: center;
  text-transform: uppercase;
}
/* A translucent band of light with a few specks of powder in it: it passes
   OVER the portraits (screen blend), so both stay whole at every moment.
   powder.js moves it with translate3d only, in step with the canvas band. */
.glint {
  display: none;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 70%;
  pointer-events: none;
  background:
    radial-gradient(circle at 44% 22%, var(--glint-speck) 0 1px, var(--velvet-clear) 2px),
    radial-gradient(circle at 56% 61%, var(--glint-speck) 0 1.2px, var(--velvet-clear) 2.2px),
    radial-gradient(circle at 49% 84%, var(--glint-speck) 0 .8px, var(--velvet-clear) 1.8px),
    linear-gradient(105deg, var(--velvet-clear) 22%, var(--glint-soft) 40%, var(--glint-bright) 50%, var(--glint-soft) 60%, var(--velvet-clear) 78%);
  mix-blend-mode: screen;
  transform: translate3d(-110%, 0, 0);
  will-change: transform;
}
html.powder-live .glint { display: block; }

/* ---------- 7. Contact ---------- */
.contact {
  display: grid;
  place-items: center;
  min-height: 90svh;
}
.contact-card {
  display: grid;
  justify-items: center;
  gap: var(--sp-5);
  max-width: 36rem;
  text-align: center;
}
/* #110 liquid ripple button: ripples spawn at the press point underneath
   the label (isolation keeps them inside the pill), plus a spring press. */
.cta {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  min-height: 56px;
  margin-top: var(--sp-3);
  padding: 0 var(--sp-8);
  border-radius: var(--r-pill);
  background: linear-gradient(100deg, var(--powder), var(--peach));
  color: var(--velvet);
  font: 600 var(--fs-md)/1.2 var(--font-text);
  letter-spacing: .03em;
  text-decoration: none;
  /* Wraps inside the pill rather than overflowing a 360px column. */
  max-width: 100%;
  text-align: center;
  transition: transform 150ms var(--ease-out);
}
.cta:active { transform: scale(.97); }
.cta-label,
.cta-arrow { position: relative; z-index: 1; }
.cta-ripple {
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  background: var(--ripple);
  pointer-events: none;
  transform: scale(0);
  opacity: .6;
  animation: cta-ripple 750ms var(--ease-out) forwards;
}
@keyframes cta-ripple {
  to { transform: scale(4); opacity: 0; }
}

/* ---------- Footer ---------- */
.site-footer {
  display: grid;
  justify-items: center;
  gap: var(--sp-3);
  padding: var(--sp-12) var(--gutter) var(--sp-16);
  border-top: 1px solid var(--hairline);
  color: var(--mist);
  font-size: var(--fs-sm);
  text-align: center;
}
.site-footer p { max-width: 40rem; }
.footer-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 var(--sp-8);
}

/* ---------- Film dialog (native <dialog>) ---------- */
/* Nearly full width on phones so the film inside stays 52svh or taller
   (max-width: none lifts the UA's calc(100% - 2em) cap on modal dialogs). */
.film-dialog {
  width: min(96vw, 520px);
  max-width: none;
  max-height: 92dvh;
  padding: var(--sp-3);
  overflow: auto;
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  background: var(--velvet-2);
  color: var(--pearl);
}
.film-dialog[open] { animation: dialog-in 320ms var(--ease-out); }
@keyframes dialog-in {
  from { opacity: 0; transform: translateY(12px) scale(.98); }
}
.film-dialog::backdrop { background: var(--backdrop); }
.dialog-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}
.dialog-bar h2 { font: 400 var(--fs-xl)/1.2 var(--font-display); }
.dialog-close {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--hairline-strong);
  border-radius: 50%;
  background: none;
  cursor: pointer;
}
.dialog-close svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
}
.frame-dialog {
  width: min(100%, 70dvh * .75);
  margin-inline: auto;
  aspect-ratio: 3 / 4;
}
.dialog-source { margin-top: var(--sp-2); }

/* =====================================================================
   Tablet and up
   ===================================================================== */
@media (min-width: 768px) {
  :root { --gutter: 32px; }
  .hero { gap: var(--sp-4); }
  .frame-hero { width: min(100%, 62svh * .75); }
  .prints { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-10); }
  .stills-list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-12); }
  /* Both properties reset: an auto width with the phone's end alignment
     would shrink-wrap the frame instead of filling its column. */
  .stills-list > li,
  .stills-list > li:nth-child(even),
  .triptych > li,
  .triptych > li:nth-child(even),
  .looks-grid > li,
  .looks-grid > li:nth-child(even),
  .pair > .pair-item,
  .pair > .pair-item:nth-child(even) { width: auto; justify-self: stretch; }
  .looks-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-10) var(--sp-8); }
  /* Staggered heights give the powder trail a rising, falling path between
     the three frames instead of one flat line hidden behind them. */
  .stills-list > li:nth-child(2) { margin-top: var(--sp-16); }
  .stills-list > li:nth-child(3) { margin-top: var(--sp-8); }
  .triptych { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-12); }
  .triptych > li:nth-child(2) { margin-top: var(--sp-24); }
  .triptych > li:nth-child(3) { margin-top: var(--sp-10); }
  /* The pair is its own size container: each 9:16 frame is as tall as the
     viewport allows but never wider than half the column (768px tablets). */
  .pair {
    grid-template-columns: repeat(2, auto);
    justify-content: center;
    gap: var(--sp-12);
    container-type: inline-size;
  }
  .frame-tall { width: auto; height: min(76svh, 820px, (100cqi - var(--sp-12)) / 2 * 16 / 9); }
  .film-dialog { padding: var(--sp-4); }
}

/* =====================================================================
   1024px and wider, any shape: roomier gutters, a taller header, a larger
   hero word, and the lookbook as one staggered row.
   ===================================================================== */
@media (min-width: 1024px) {
  :root {
    --gutter: clamp(32px, 4.5vw, 72px);
    --header-h: 72px;
    --fs-word: min(27cqi, 200px);
  }
  .frame-square { width: min(100%, 76svh); }
  .looks-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-12); }
  .looks-grid > li:nth-child(even) { margin-top: var(--sp-16); }
}

/* =====================================================================
   Side by side, only on a landscape-shaped screen (4:3 or wider): the hero
   splits into copy + a tall film, and client three puts its film beside
   the story. A portrait or tall window (an iPad Pro, a tall desktop
   window) keeps the stacked layout that already works at 768x1024, where
   every film is capped by the column's width.
   Even here each svh height is also capped through container units (cqi,
   the section's own width), so no in-between shape can squeeze a text
   column below --copy-min or push a film off screen.
   ===================================================================== */
@media (min-width: 1024px) and (min-aspect-ratio: 4/3) {
  .hero {
    container-type: inline-size;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: minmax(0, 1fr) repeat(5, auto) minmax(0, 1fr);
    grid-template-areas:
      ". film"
      "eyebrow film"
      "title film"
      "lede film"
      "actions film"
      "cue film"
      ". film";
    column-gap: var(--sp-16);
    row-gap: var(--sp-5);
    align-content: stretch;
  }
  .hero-eyebrow { grid-area: eyebrow; }
  .hero-title { grid-area: title; }
  .hero-lede { grid-area: lede; }
  .hero-actions { grid-area: actions; }
  .scroll-cue { grid-area: cue; }
  /* min-content: the caption wraps under the film instead of widening it. */
  .hero-film { grid-area: film; align-self: center; width: min-content; }
  /* 74svh on a laptop, never taller than what the header leaves, and never
     so wide (3:4) that the copy beside it gets less than --copy-min. */
  .frame-hero {
    width: auto;
    height: min(74svh, 100svh - var(--header-h) - 96px, (100cqi - var(--sp-16) - var(--copy-min)) * 4 / 3);
  }

  .film-layout {
    container-type: inline-size;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "main head"
      "main detail";
    column-gap: var(--sp-16);
    row-gap: var(--sp-10);
    align-items: start;
  }
  .film-head { grid-area: head; }
  .film-main { grid-area: main; width: min-content; }
  /* The detail's figure measures its own column (it is the size container
     its frame's cqi reads), so the film always fits beside the main one. */
  .film-detail { grid-area: detail; width: auto; container-type: inline-size; }
  .frame-film {
    width: auto;
    height: min(68svh, (100cqi - var(--sp-16) - var(--copy-min)) * 4 / 3);
  }
  /* 50svh: a second film beside the story, never a thumbnail, and never
     wider than its column. */
  .frame-detail { width: auto; height: min(50svh, 100cqi * 4 / 3); }
}

/* =====================================================================
   Pre-materialise states — only when JS exists AND motion is allowed. Each
   carries a failsafe: if powder.js never takes over (script blocked, GSAP
   missing), the element reveals itself after 2.6s instead of staying hidden.
   ===================================================================== */
@media all {
  html.js:not(.motion-off) .hero-word {
    opacity: 0;
    animation: failsafe-show 300ms var(--ease-out) 2600ms forwards;
  }
  /* (0,4,1) beats the rule above (0,3,1): once the canvas runs, the
     failsafe is cancelled for good and the powder draws the word. */
  html.js.powder-live:not(.motion-off) .hero-word {
    animation: none;
    transition: opacity 600ms var(--ease-out);
  }
  /* The owner's middle path (powder.js, SOLID_WORD_UNDER_POWDER): the solid
     word fades in under the settled powder and out as it blows away. */
  html.js.powder-live.hero-word-solid:not(.motion-off) .hero-word { opacity: 1; }
  html.js:not(.motion-off):not(.reveals-on) .frame-hero {
    clip-path: circle(0% at 50% 50%);
    opacity: 0;
    animation: failsafe-iris 300ms var(--ease-out) 2600ms forwards;
  }
}
@keyframes failsafe-show { to { opacity: 1; } }
@keyframes failsafe-iris { to { opacity: 1; clip-path: circle(75% at 50% 50%); } }

/* =====================================================================
   Reduced motion / Motion off: the complete static page. The canvas and
   torch are gone, the dust texture stays, nothing loops.
   ===================================================================== */
/* Off on purpose: motion is always on, whatever the device's reduced-motion setting (owner's call, 2026-09-23). */
@media not all {
  .cue-grain::before,
  .film-dialog[open],
  .cta-ripple { animation: none; }
  .cta-ripple { display: none; }
  .text-link::after,
  .btn::before,
  .motion-toggle::before,
  .cta,
  body::after { transition: none; }
  .cta:active { transform: none; }
  .powder-canvas,
  .torch-layer { display: none; }
}
html.motion-off .cue-grain::before,
html.motion-off .film-dialog[open] { animation: none; }
html.motion-off .powder-canvas,
html.motion-off .torch-layer { display: none; }
html.motion-off .cta:active { transform: none; }

/* Phones and tablets: section eyebrows slid under the header's fading lower
   half and read through the wordmark. The scrim is solid for most of the
   header's height, and the header steps aside while reading down
   (initHeaderAway in powder.js). */
@media (max-width: 1023.98px) {
  .site-header {
    background: var(--velvet);
  }
  .site-header::after {
    content: ''; position: absolute; inset: 100% 0 auto 0; height: 20px; pointer-events: none;
    background: linear-gradient(to bottom, var(--velvet), var(--velvet-clear));
  }
  @media all { .site-header { transition: transform .35s var(--ease-out); } }
  html.header-away .site-header:not(:focus-within) { transform: translateY(-100%); }
}

/* Forced colours drop background-clip text; give the word a system colour. */
@media (forced-colors: active) {
  .hero-word { color: CanvasText; background: none; }
}
