:root {
  color-scheme: light;
  --canvas: #f5eee4;
  --surface: #fffdf8;
  --on-surface: #1c1917;
  --on-surface-muted: #6e6259;
  --line: #e6dccd;
  --line-control: #8d867d;
  --accent: #b52020;
  --accent-deep: #79142a;
  --accent-bright: #c33838;
  /* Body-size link text. In light this IS the brand red (6.47:1 on surface).
     Dark needs its own value — see the dark blocks; measured, not chosen. */
  --link: #b52020;
  --on-accent: #fff6f2;
  --shadow: 0 24px 70px rgba(68, 48, 34, 0.16);
  --page-max: 1320px;
  --reading: 54ch;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* SCALES — one named step per value actually used below, so nobody has to
     invent a new number by eye. Add here first; never write a bare px/ms
     value in a rule below without checking this list. Named by their own
     value (--space-24 is 24px) so the number in the name always matches the
     number on screen — a photographer can read it without a lookup table. */

  /* Radius. 50% (circles) and 999px (pills) are shapes, not scale steps —
     left literal on purpose. 9px and 10px measured within 2px of each other
     and each appeared twice or fewer, so they share --radius-10 (owner-
     approved collapse; the one mobile 9px contact-frame corner is now 10px). */
  --radius-10: 10px;
  --radius-12: 12px;
  --radius-14: 14px;
  --radius-16: 16px;
  --radius-25: 25px;

  /* Spacing (gap/padding/margin). 7px and 8px measured within 2px of each
     other and each appeared twice or fewer, so they share --space-8
     (owner-approved collapse; two 7px spots are now 8px). */
  --space-3: 3px;
  --space-4: 4px;
  --space-8: 8px;
  --space-9: 9px;
  --space-10: 10px;
  --space-12: 12px;
  --space-13: 13px;
  --space-14: 14px;
  --space-16: 16px;
  --space-17: 17px;
  --space-18: 18px;
  --space-20: 20px;
  --space-21: 21px;
  --space-22: 22px;
  --space-24: 24px;
  --space-26: 26px;
  --space-28: 28px;
  --space-32: 32px;
  --space-36: 36px;
  --space-40: 40px;
  --space-44: 44px;
  --space-48: 48px;
  --space-52: 52px;
  --space-54: 54px;
  --space-56: 56px;
  --space-64: 64px;
  --space-72: 72px;
  --space-88: 88px;
  --space-92: 92px;
  --space-104: 104px;

  /* Type — the FIXED font-sizes only; the clamp() ramps are bespoke curves
     per display heading and stay exactly as written. 7px and 9px measured
     within 2px of each other and each appeared once, so they share --font-9
     (owner-approved collapse; the one 7px direction-mark label is now 9px). */
  --font-9: 9px;
  --font-11: 11px;
  --font-12: 12px;
  --font-13: 13px;
  --font-14: 14px;
  --font-15: 15px;
  --font-16: 16px;
  --font-17: 17px;
  --font-18: 18px;
  --font-20: 20px;
  --font-22: 22px;
  --font-24: 24px;
  --font-28: 28px;
  --font-48: 48px;
  --font-74: 74px;
  --font-122: 122px;

  /* Motion duration. 200/220/240/260ms were four names for the same "quick"
     state change (hover/focus color, opacity, small transforms) — owner
     approved collapsing all four into one token. 320ms and 520ms are their
     own slower beats (shape settle; the nav pill's big dock/undock slide)
     and keep their own tokens. */
  --duration-quick: 240ms;
  --duration-settle: 320ms;
  /* NOT part of the quick/settle/glide set and never merge it in: this is the
     duration a visitor gets when they have asked their OS for less motion. It
     is an accessibility accommodation, so it must not drift upward whenever a
     decorative transition is retuned. It was silently raised 200ms -> 240ms by
     the 2026-09-08 duration collapse; restored. */
  --duration-reduced-motion: 200ms;
  --duration-glide: 520ms;
  /* CORRECTED 2026-09-09 (owner report: the dock migration read as "not
     smooth"). --duration-glide (520ms) was tuned for the pill's older,
     shorter hop; against the current full-viewport-height journey it read
     as a slow, floaty drift. This is a dedicated, shorter step for that one
     full-height transform — kept separate from --duration-glide rather than
     retuning it, since nothing else in the sheet still uses --duration-glide
     and changing its value would be a silent, unrelated retune. */
  --duration-dock: 420ms;
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --canvas: #141210;
  --surface: #211d19;
  --on-surface: #f5eee4;
  --on-surface-muted: #b8aca2;
  --line: #3f3832;
  --line-control: #81786f;
  --accent: #bd3c42;
  --accent-deep: #6f1427;
  /* NOT --accent and NOT --accent-bright: measured on the dark surfaces they
     sit on, those give 3.10:1 and 3.86:1 against a 4.5:1 minimum for body
     text. This clears it — 4.99:1 on the legal callout, 5.58:1 on the page. */
  --link: #d96b6b;
  --accent-bright: #c95353;
  --on-accent: #fff6f2;
  --shadow: 0 24px 70px rgba(0, 0, 0, 0.36);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --canvas: #141210;
    --surface: #211d19;
    --on-surface: #f5eee4;
    --on-surface-muted: #b8aca2;
    --line: #3f3832;
    --line-control: #81786f;
    --accent: #bd3c42;
    --accent-deep: #6f1427;
    /* NOT --accent and NOT --accent-bright: measured on the dark surfaces they
       sit on, those give 3.10:1 and 3.86:1 against a 4.5:1 minimum for body
       text. This clears it — 4.99:1 on the legal callout, 5.58:1 on the page. */
    --link: #d96b6b;
    --accent-bright: #c95353;
    --shadow: 0 24px 70px rgba(0, 0, 0, 0.36);
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  /* The smooth value of scroll-behavior does NOT come back here. ScrollTrigger.refresh()
     has to scroll the page to measure every pinned section, then restore the
     scroll position it started from — with smooth scrolling on, that restore
     ANIMATES, so refresh() measures a scroll position that is still moving.
     Measured on this page, same session, same scroll(5000), same refresh():
     smooth ON gave pin.start 2407 against the pin-spacer's real document top
     7521 (desync -5114px, the pinned panels painting over the wrong section);
     smooth OFF gave pin.start 7521 against spacer 7521 (0 desync). Smooth
     scrolling for visitors now lives in motion.js as a delegated click
     handler on in-page anchors, so it never touches a ScrollTrigger measuring
     scroll. */
  scrollbar-color: var(--accent) var(--canvas);
  scrollbar-width: thin;
}

body {
  min-width: 320px;
  margin: 0;
  overflow-x: clip;
  background: var(--canvas);
  color: var(--on-surface);
  font-family: "Outfit", sans-serif;
  font-size: var(--font-17);
  font-weight: 400;
  line-height: 1.55;
  text-rendering: optimizeLegibility;
}

body::-webkit-scrollbar {
  width: 8px;
}

body::-webkit-scrollbar-track {
  background: var(--canvas);
}

body::-webkit-scrollbar-thumb {
  border: 2px solid var(--canvas);
  border-radius: 999px;
  background: var(--accent);
}

::selection {
  background: var(--accent);
  color: var(--on-accent);
}

img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

figure,
h1,
h2,
h3,
p {
  margin: 0;
}

a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

button,
a {
  -webkit-tap-highlight-color: transparent;
}

button {
  color: inherit;
  font: inherit;
}

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

/* The global accent ring reads at only ~1.2:1 on the partnership panels'
   maroon ground and ~2.7:1 inside the dark nav pill — both fail 3:1. Swap
   the ring colour to --on-accent in those two contexts only; it clears
   3:1 against both (measured ~5:1 / ~4.1:1 light/dark on partnership,
   ~16:1 on the nav pill). outline-offset stays the global 4px. */
.partnership :focus-visible,
.nav-pill :focus-visible {
  outline-color: var(--on-accent);
}

.skip-link {
  position: fixed;
  z-index: 200;
  top: 12px;
  left: 12px;
  padding: var(--space-10) var(--space-14);
  border-radius: 999px;
  background: var(--on-surface);
  color: var(--canvas);
  transform: translateY(-160%);
  transition: transform var(--duration-quick) var(--ease);
}

.skip-link:focus {
  transform: translateY(0);
}

.page-shell {
  width: min(calc(100% - 48px), var(--page-max));
  margin-inline: auto;
}

.section {
  padding-block: clamp(96px, 12vw, 176px);
}

h1,
h2 {
  font-weight: 400;
  letter-spacing: -0.035em;
  line-height: 0.96;
}

h2 {
  font-size: clamp(48px, 6.5vw, 96px);
}

h1 span,
h2 span {
  color: var(--on-surface-muted);
}

.button {
  display: inline-flex;
  min-height: 48px;
  align-items: center;
  justify-content: center;
  padding: var(--space-12) var(--space-22);
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: var(--font-14);
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  transition: transform var(--duration-quick) var(--ease), background-color var(--duration-quick) var(--ease), color var(--duration-quick) var(--ease), border-color var(--duration-quick) var(--ease);
}

.button:active {
  transform: scale(0.97);
}

.button:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

.button:disabled:active {
  transform: none;
}

.button--primary {
  background: var(--accent);
  color: var(--on-accent);
}

.button--quiet {
  border-color: var(--line-control);
  background: color-mix(in srgb, var(--surface) 78%, transparent);
  color: var(--on-surface);
}

.button--light {
  background: var(--on-accent);
  color: var(--accent-deep);
}

/* The fixed navigation stays a dark instrument on both page themes. */
.nav-wrap {
  position: fixed;
  z-index: 100;
  top: 12px;
  left: 50%;
  width: min(calc(100% - 32px), 372px);
  transform: translateX(-50%);
  /* CORRECTED 2026-09-09: was 520ms, tuned for the pill's older shorter hop.
     Now that .is-docked travels the FULL viewport height, 520ms read as a
     slow, floaty drift rather than a snap into place. Shortened so the same
     full-height journey completes with more urgency; the ease stays the same
     fast-out/settle curve so it still decelerates into the dock instead of
     stopping abruptly. */
  transition: transform var(--duration-dock) cubic-bezier(0.33, 0.9, 0.28, 1);
  will-change: transform;
}

/* ⚠ OWNER RULING 2026-09-02 — the pill MIGRATES, it does not hide.
   His words: "I wanted to go down, not up... The down part of the footer side
   of the scroll on mobile is supposed to go from up to down." So on the way
   down the page the pill travels from the top edge to the BOTTOM edge and
   docks there like a mobile bottom bar; on the way back up it travels home.
   It is never hidden — an earlier pass on 2026-09-02 tucked it upward out of
   view and that was wrong. This also REVERSES the 2026-08-28 "do not hide
   this" ruling in spirit: the bar is still always reachable, just not always
   at the top. See the long note in motion.js before changing any of it.

   Why this exact expression:
   - translateX(-50%) is REPEATED because this shorthand replaces the base
     transform outright — drop it and the pill jumps to the left edge mid-flight.
   - 100% in translateY resolves against the pill's OWN height.
   - CORRECTED 2026-09-08 (owner screenshot: pill floating mid-panel instead
     of docked at the bottom edge): this used `100svh` (the SMALL viewport
     height, i.e. the viewport as measured with the address bar fully
     expanded). On a real phone the address bar collapses as you scroll, so
     the visible viewport grows TALLER than `100svh` and the pill's offset
     under-shot the real bottom edge. `100dvh` (the DYNAMIC viewport height)
     tracks the viewport the reader can actually see right now, so the dock
     lands at the true bottom regardless of toolbar state.
   - Because the offset is measured from the element's own height, the docked
     pill is effectively pinned by its BOTTOM edge. When the menu opens and the
     pill grows taller, it therefore grows UPWARD into the page instead of
     pushing its panel off the bottom of the screen. That is load-bearing: the
     panel is markup that flows downward, and nothing else stops it escaping. */
.nav-wrap.is-docked {
  transform: translate(-50%, calc(100dvh - 100% - 24px));
}

/* ⚠ OWNER-REPORTED 2026-09-09: "when it is placed down at the footer side,
   then my navbar automatically go up, and then it come down."

   The docked position above is correct at rest, both open and shut — measured
   at 13px off the bottom either way. What he saw is the JOURNEY between them.
   The transform depends on `100%`, the wrap's own height, and the wrap grows
   ~340px the instant the panel is unhidden; but the transform is transitioned,
   so the position eased to its new value over 420ms while the panel was
   already there. The bar travelled the panel's whole height and back for no
   reason.

   A first attempt anchored the transform to the bar's height instead of the
   wrap's. That was WRONG and measuring caught it: it left the bar 44px BELOW
   the viewport once the panel opened, because the wrap's bottom no longer
   matched the bar's. Nothing about the resting layout needed changing — only
   the animating of a change that should be instantaneous. */
.nav-wrap.is-shifting {
  transition: none;
}

/* The panel opens upward when the pill is parked at the bottom — see the
   comment on .nav-pill. Never remove this without re-measuring the bar's y
   position before and after opening the menu at max scroll. */
.nav-wrap.is-docked .nav-pill {
  flex-direction: column-reverse;
}

/* ⚠ OWNER-REPORTED 2026-09-09: "try clicking menu when you are complete down
   there is an issue." Measured at the bottom of the page: tapping Menu moved
   the pill from y=656 to y=316 — 340px UP the screen, away from the thumb that
   just tapped it — because the panel stacks below the bar inside the pill, and
   the docked transform anchors the wrap's BOTTOM edge, so growing the panel
   pushed the bar upward.

   Reversing the stack when docked fixes it without moving anything else: the
   bar stays against the anchored bottom edge and the panel opens UPWARD above
   it, which is also how a phone expects a bottom-anchored menu to behave. At
   the top of the page the pill is anchored by its top edge, so the panel opens
   downward there exactly as before. */
.nav-pill {
  display: flex;
  flex-direction: column;
  width: min(100%, 372px);
  overflow: hidden;
  /* CORRECTED 2026-09-09 (owner report: "Design doesn't look popped up").
     Over a photograph the old shadow (0 12px 36px @ 0.24) and border
     (@ 0.14) read flat. Same object, more lift: a deeper, darker shadow so
     it separates from whatever is behind it, plus a brighter top edge so the
     border itself reads as a highlight catching light rather than a thin
     outline. Background, radius and width are untouched. */
  border: 1px solid rgba(255, 246, 242, 0.24);
  border-radius: var(--radius-25);
  background: #1c1917;
  color: #fff6f2;
  box-shadow: 0 20px 48px rgba(28, 25, 23, 0.4);
  transition: border-radius var(--duration-settle) var(--ease), box-shadow var(--duration-settle) var(--ease);
}

.nav-pill.is-open {
  border-radius: var(--radius-25) var(--radius-25) var(--radius-16) var(--radius-16);
  box-shadow: 0 24px 72px rgba(28, 25, 23, 0.32);
}

.nav-pill__bar {
  display: grid;
  min-height: 50px;
  grid-template-columns: minmax(0, 1fr) auto 54px;
  align-items: center;
  gap: var(--space-12);
  padding-inline: var(--space-16);
}

.nav-menu-button,
.theme-toggle {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  gap: var(--space-9);
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: var(--font-13);
  transition: color var(--duration-quick) var(--ease);
}

.nav-menu-button:active,
.theme-toggle:active {
  color: rgba(255, 246, 242, 0.5);
}

.nav-menu-button {
  justify-self: start;
  padding: 0;
}

.menu-mark {
  position: relative;
  width: 16px;
  height: 12px;
}

.menu-mark::before,
.menu-mark::after {
  position: absolute;
  left: 0;
  width: 16px;
  height: 1px;
  background: currentColor;
  content: "";
  transition: top var(--duration-quick) var(--ease), transform var(--duration-quick) var(--ease);
}

.menu-mark::before { top: 2px; }
.menu-mark::after { top: 9px; }
.nav-pill.is-open .menu-mark::before { top: 6px; transform: rotate(45deg); }
.nav-pill.is-open .menu-mark::after { top: 6px; transform: rotate(-45deg); }

.theme-toggle {
  justify-self: center;
  padding: 0;
}

/* ⚠ OWNER-REPORTED 2026-09-09: "when I select day time and night time it gets
   blend in gray tone... it should stay popped." It did: the track was a
   half-transparent white OUTLINE with a small dot inside, so both states read
   as the same faint grey ring on the dark pill, and neither said which one you
   were in.

   Both states are now a SOLID capsule, and the colours are fixed literals
   rather than theme tokens on purpose — the nav pill is always #1c1917 whatever
   theme the page is in, so a control that restyled itself alongside the thing
   it controls would keep drifting out of contrast. Measured against that pill:
   ivory 16.42:1, and #c33838 3.28:1 (the deeper brand #b52020 was tried first
   and REJECTED at 2.66:1 — under the 3:1 a non-text control needs). Thumb
   inside its own track: 16.42:1 on ivory, 5.00:1 on red. */
.theme-toggle__track {
  position: relative;
  width: 34px;
  height: 20px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: #c33838;
  transition: background var(--duration-quick) var(--ease),
              box-shadow var(--duration-quick) var(--ease);
}

/* The night state is the DEEP brand red the owner asked for — but #79142a on
   the pill measures 1.62:1, far under the 3:1 a non-text control needs, so the
   fill alone would be a black hole. The bright ring carries the shape instead:
   #c33838 against the pill is 3.28:1, and it reads as an outline of the same
   red family rather than the grey outline this replaced. Do not remove the ring
   and keep the dark fill — that is the invisible state all over again. */
.theme-toggle[aria-pressed="true"] .theme-toggle__track {
  background: #79142a;
  box-shadow: inset 0 0 0 1.5px #c33838;
}

.theme-toggle__thumb {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  /* Dark on the ivory track, ivory on the red one — the thumb always reads as
     the opposite of whatever it is sitting on. */
  background: #1c1917;
  transition: transform var(--duration-quick) var(--ease),
              background var(--duration-quick) var(--ease);
}

.theme-toggle[aria-pressed="true"] .theme-toggle__thumb {
  transform: translateX(14px);
  background: #c33838;
}

.nav-progress {
  justify-self: end;
  color: rgba(255, 246, 242, 0.72);
  font-size: var(--font-12);
  font-variant-numeric: tabular-nums;
}

/* The panel grows downward from the bar; transform-origin documents that behavior. */
.nav-panel {
  max-height: min(560px, calc(100svh - 92px));
  overflow-y: auto;
  padding: var(--space-12) var(--space-16) var(--space-18);
  border-top: 1px solid rgba(255, 246, 242, 0.14);
  transform-origin: top;
  transition: opacity var(--duration-quick) var(--ease), transform var(--duration-settle) var(--ease);
}

.nav-panel[hidden] {
  display: none;
}

.nav-links {
  display: grid;
}

.nav-links a {
  display: flex;
  min-height: 46px;
  align-items: center;
  border-bottom: 1px solid rgba(255, 246, 242, 0.12);
  font-size: var(--font-24);
  letter-spacing: -0.02em;
  text-decoration: none;
  transition: color var(--duration-quick) var(--ease);
}

.nav-links a[aria-current="page"] {
  /* the page you are on stays listed, so the menu keeps one shape everywhere,
     and says where you are instead of going quiet about it. */
  color: rgba(255, 246, 242, 0.5);
}

.nav-links a[aria-current="page"]::after {
  content: "";
  width: 5px;
  height: 5px;
  margin-left: var(--space-10);
  border-radius: 50%;
  background: currentColor;
}

.nav-links a:active {
  color: rgba(255, 246, 242, 0.5);
}

.nav-meta a {
  transition: color var(--duration-quick) var(--ease);
}

.nav-meta {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  padding-top: var(--space-16);
  color: rgba(255, 246, 242, 0.62);
  font-size: var(--font-11);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.nav-meta div {
  display: flex;
  gap: var(--space-16);
}

/* Hero */
.hero {
  position: relative;
  min-height: 150svh;
}

.hero__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  min-height: 700px;
  overflow: hidden;
  background: var(--canvas);
}

.contact-sheet {
  position: absolute;
  inset: 0;
}

/* ⚠ HERO BANNER — owner ruling 2026-08-31: "It should be a full-screen hero
   banner." Fills the entire first screen, edge to edge, top to bottom. It
   used to stop at 58% with the copy on cream below; he ruled against that
   twice. The copy now sits ON the photograph — see .hero__copy. */
.hero__banner {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.hero__banner picture,
.hero__banner img {
  display: block;
  width: 100%;
  height: 100%;
}

.hero__banner img {
  /* `cover` is right here and wrong for the Studio captures below: this is a
     photograph with room to lose at the edges, not a screen full of small
     text. Held slightly above centre so the models' heads stay in frame as
     the box gets shorter. */
  object-fit: cover;
  object-position: center 38%;
  /* Scroll-linked parallax headroom (2026-09-09): motion.js pans this image
     up to yPercent -7 as the reader scrolls the hero away, so the box is
     oversized by 16% and recentred with a matching negative margin — a
     STATIC crop change, not motion, so it applies even under reduced
     motion. -7% stays inside the 8% of slack this reserves on both edges,
     which is what keeps the pan from ever showing the canvas behind it. */
  height: 116%;
  margin-top: -8%;
  will-change: transform;
}

.contact-frame {
  position: absolute;
  overflow: hidden;
  border-radius: var(--radius-12);
  background: var(--surface);
  box-shadow: var(--shadow);
}

.contact-frame::after {
  position: absolute;
  inset: 0;
  border: 1px solid rgba(28, 25, 23, 0.08);
  border-radius: inherit;
  content: "";
  pointer-events: none;
}

.contact-frame--1 { top: 5%; left: 3.5%; width: 13%; aspect-ratio: 4 / 5; transform: rotate(-3deg); }
.contact-frame--2 { top: 7%; left: 22%; width: 12%; aspect-ratio: 1; transform: rotate(2deg); }
.contact-frame--3 { top: 9%; right: 4%; width: 13%; aspect-ratio: 4 / 5; transform: rotate(3deg); }
.contact-frame--4 { top: 4%; right: 21%; width: 11%; aspect-ratio: 4 / 5; transform: rotate(-2deg); }
.contact-frame--5 { top: 6%; left: 43%; width: 10%; aspect-ratio: 4 / 5; transform: rotate(2deg); }
.contact-frame--6 { top: 37%; right: 4%; width: 12%; aspect-ratio: 4 / 5; transform: rotate(-2deg); }
.contact-frame--7 { top: 35%; right: 19%; width: 10%; aspect-ratio: 4 / 5; transform: rotate(4deg); }

.direction-mark {
  position: absolute;
  top: 37%;
  left: 48%;
  width: 112px;
  height: 112px;
  color: var(--on-surface-muted);
  transform: translate(-50%, -50%);
}

.direction-mark__ring {
  position: absolute;
  inset: 12px;
  border: 1px solid currentColor;
  border-radius: 50%;
  opacity: 0.48;
}

.direction-mark__ring::after {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  content: "";
  transform: translate(-50%, -50%);
}

.direction-mark__line {
  position: absolute;
  top: 56px;
  left: -22px;
  width: 156px;
  height: 1px;
  background: currentColor;
  opacity: 0.35;
  transform: rotate(-18deg);
}

.direction-mark__label {
  position: absolute;
  top: 102px;
  left: 50%;
  width: max-content;
  font-size: var(--font-9);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transform: translateX(-50%);
}

.hero__copy {
  position: absolute;
  z-index: 3;
  bottom: clamp(28px, 5vh, 64px);
  left: 50%;
  transform: translateX(-50%);
}

.hero__copy > * {
  max-width: 760px;
}

/* ⚠ OWNER RULING 2026-08-31: "Remove the gradient. It is better to bring
   contrast to the typography." So there is NO scrim, wash or overlay on the
   photograph — the type carries its own contrast instead, in ivory against
   the desert frame, at full opacity rather than the muted tokens the rest of
   the page uses. These are fixed colours on purpose: they answer to the
   photograph underneath, not to the light/dark theme. Do not reintroduce a
   gradient here.
   ⚠ NARROWED 2026-09-02 — see the ruling above .hero__intro below. Measured
   contrast against the real photograph failed WCAG for the descriptor, the
   body copy and the quiet button (1.84:1 / 3.31:1 / 2.62:1, need 4.5:1);
   those three moved to cream and their ivory overrides are gone. h1 and
   .hero__position measured 2.46:1 and 3.56:1 against the large-text 3:1
   threshold — the owner kept them on the photograph, so they keep this
   rule. */
.hero .hero__copy,
.hero .hero__copy .hero__position,
.hero .hero__copy h1,
.hero .hero__copy h1 span {
  /* The `.hero` prefix is load-bearing: `.hero h1 span` further down this
     file paints the second line muted grey, and at equal specificity the
     later rule wins — which dropped "not generated." to low contrast against
     the lit sphere behind it. This outranks it instead of reordering. */
  color: #fffdf8;
}

.hero__descriptor,
.section-heading > p,
.inside__copy > p:first-child,
.founder__copy > p:first-child,
.outcome > div > p:first-child,
.proof-card__domain,
.proof-card__fact-label,
.proof-card__placeholder span {
  color: var(--on-surface-muted);
  font-size: var(--font-11);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.hero h1 {
  margin-top: var(--space-10);
  font-size: clamp(64px, 8vw, 116px);
}

.hero h1 span {
  color: var(--on-surface-muted);
}

.hero__position {
  margin-top: var(--space-17);
  font-size: clamp(20px, 2.1vw, 29px);
  letter-spacing: -0.02em;
}

.hero__body {
  max-width: 54ch;
  margin-top: var(--space-8);
  color: var(--on-surface-muted);
  font-size: var(--font-15);
}

.hero__actions {
  display: flex;
  gap: var(--space-10);
  margin-top: var(--space-20);
}

/* ⚠ OWNER RULING 2026-09-02 — measured contrast of the ivory hero copy
   against the real photograph failed WCAG for the small text: descriptor
   1.84:1, body 3.31:1, the quiet button 2.62:1 (all need 4.5:1); h1 and
   .hero__position measured 2.46:1 / 3.56:1 against the large-text 3:1
   threshold and were kept on the photograph — one of those two is the
   owner's accepted call, not a clean pass. He ruled twice (2026-08-31)
   against a scrim or gradient on the hero photograph, so the fix moves
   these three elements onto the page's own cream ground instead of
   dimming the image. His words, choosing this option: "The headline stays
   on the photograph where it belongs. The one-line label, the paragraph
   and the two buttons move onto clean cream just under it. Nothing is
   dimmed and no gradient appears." This PARTIALLY reverses the 2026-08-31
   ruling that put the whole hero copy block on the photograph (above):
   the headline and position line still are; these three no longer are.
   Sits on the page's own --canvas (body already paints it) — no
   background of its own needed. Padding matches the file's standard
   .section rhythm. */
.hero__intro {
  padding-block: clamp(96px, 12vw, 176px);
}

/* Story */
.statement {
  padding-top: clamp(88px, 10vw, 144px);
}

.statement__rule {
  height: 1px;
  background: var(--line);
}

.statement__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(280px, 0.6fr);
  align-items: end;
  gap: var(--space-48);
  padding-top: clamp(48px, 7vw, 88px);
}

.statement__grid p {
  max-width: 48ch;
  color: var(--on-surface-muted);
}

.work {
  overflow: hidden;
  background: var(--surface);
}

.section-heading {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  gap: var(--space-24);
}

.section-heading h1,
.section-heading h2 {
  max-width: 12ch;
}

/* range-title (gallery.html) and services-title (services.html) were
   promoted from h2 to h1 for heading-order correctness; the bare `h2`
   font-size rules above don't match h1, so mirror them here scoped to
   .section-heading so no other h1 on the site (e.g. the legal-page
   titles, which are intentionally unstyled by font-size) is affected. */
.section-heading h1 {
  font-size: clamp(48px, 6.5vw, 96px);
}

.editorial-gallery {
  display: grid;
  min-height: 760px;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-template-rows: repeat(10, 68px);
  gap: var(--space-12);
  margin-top: clamp(112px, 14vw, 190px);
}

.gallery-frame {
  overflow: hidden;
  border-radius: var(--radius-14);
}

.gallery-frame--wide { grid-area: 1 / 1 / 6 / 7; }
.gallery-frame--portrait { grid-area: 2 / 8 / 7 / 11; }
.gallery-frame--square { grid-area: 6 / 2 / 10 / 6; }
.gallery-frame--tall { grid-area: 5 / 10 / 11 / 13; }

.gallery-frame--banner {
  position: relative;
  background: var(--accent-bright);
}

.gallery-frame--banner img {
  object-position: 50% 50%;
  will-change: transform;
}

/* Scroll-linked settle-rate headroom (2026-09-09): motion.js gives these
   three non-banner frames slightly different upward drift rates as they
   pass through the viewport, so the group settles like a contact sheet
   instead of arriving as one block. Oversized and recentred the same way
   as .hero__banner img above, so the largest authored rate (-6%) never
   shows the frame's edge. */
.gallery-frame--portrait img,
.gallery-frame--square img,
.gallery-frame--tall img {
  height: 112%;
  margin-top: -6%;
  will-change: transform;
}

.gallery-note {
  grid-area: 7 / 7 / 10 / 10;
  align-self: center;
  color: var(--on-surface-muted);
  font-size: clamp(20px, 2.2vw, 32px);
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.value-rows {
  margin-top: clamp(64px, 8vw, 108px);
  border-bottom: 1px solid var(--line);
}

.value-rows article {
  display: grid;
  min-height: 128px;
  grid-template-columns: 80px minmax(180px, 0.45fr) minmax(0, 0.55fr);
  align-items: center;
  gap: var(--space-24);
  border-top: 1px solid var(--line);
}

.value-rows article > span {
  color: var(--on-surface-muted);
  font-size: var(--font-12);
  font-variant-numeric: tabular-nums;
}

.value-rows h3 {
  font-size: clamp(26px, 3vw, 42px);
  font-weight: 400;
  letter-spacing: -0.025em;
}

/* services.html's own section title (services-title) was promoted from h2
   to h1 for heading-order correctness (task/landing-review-fixes), which
   left its three item headings ("The whole catalogue"/"Lookbook"/"Campaign")
   one level too deep at h3 — a skip straight from h1 to h3. Promoting just
   those three to h2 fixes the skip; scoped to #services only so the other
   two .value-rows triplets on this page (still genuinely h3 under their own
   h2 section title) are untouched. Mirrors .value-rows h3 exactly so the
   visual result is identical. */
#services .value-rows h2 {
  font-size: clamp(26px, 3vw, 42px);
  font-weight: 400;
  letter-spacing: -0.025em;
  line-height: 1.55;
}

.value-rows p {
  max-width: 54ch;
  color: var(--on-surface-muted);
}

/* ⚠ OWNER RULINGS 2026-08-31 — the proof section: six domains plus campaign
   ("product 3, model 3, and then campaign"); the "before" frames are
   deliberate placeholders ("Just create a mock-up. We will do that later
   on."); accuracy is described in words with NO number ("Say it in words,
   no number"). */
.proof-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(48px, 6vw, 72px) clamp(24px, 3vw, 40px);
  margin-top: clamp(64px, 8vw, 108px);
}

/* The one body link to landing/gallery.html (see index.html's #proof
   section) — a plain quiet button, no new visual language. */
.proof-cta {
  margin-top: clamp(40px, 6vw, 64px);
}

.proof-card__domain {
  margin-bottom: var(--space-14);
}

.proof-card__frames {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-12);
  /* start, not stretch: the global `img { height: 100% }` would otherwise
     beat aspect-ratio inside a stretched row and crop the square frames. */
  align-items: start;
  align-self: start;
}

.proof-card__placeholder,
.proof-card__source,
.proof-card__result {
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-14);
  height: auto;
  object-fit: cover;
}

/* The domain label, the frames and the three lines line up ACROSS a row.
   Owner ruling 2026-09-08 ("4:5 ratio aligned"): every card in this section
   now shares the SAME 4:5 frame — jewellery and footwear no longer opt into
   a square crop (their source screenshots are 4:3; object-fit: cover above
   crops them into the shared frame rather than squashing them). Without this
   subgrid a taller card would push its own copy down and the row would read
   broken. The campaign card is its own full-width row and opts out. */
@supports (grid-template-rows: subgrid) {
  .proof-card:not(.proof-card--campaign) {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
    row-gap: 0;
  }
}

.proof-card__placeholder {
  display: grid;
  place-items: center;
  padding: var(--space-12);
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--canvas) 88%, var(--on-surface) 12%);
  text-align: center;
}

.proof-card__placeholder span {
  max-width: 12ch;
  line-height: 1.4;
}

.proof-card__facts {
  display: grid;
  gap: var(--space-14);
  margin-top: var(--space-20);
}

.proof-card__fact-label {
  margin-bottom: var(--space-4);
}

.proof-card__fact-text {
  font-size: var(--font-14);
  line-height: 1.5;
}

/* The campaign card gets its own full-width row: frames take the larger
   share, copy sits beside them rather than under them. */
/* RETIRED 2026-09-08 — owner ruling, "Remove this campaign from here." The
   proof section is back to his original six domains. This block and the
   :not(.proof-card--campaign) guard above are LEFT IN PLACE and unused, the
   same way the contact-frame hero was kept when it was retired: putting the
   card back is then a paste, not a rebuild. images/D1.jpg and D1-480.jpg are
   still on disk for the same reason. Delete all of it only when someone
   decides campaign is not coming back. */
.proof-card--campaign {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(240px, 1fr);
  grid-template-areas:
    "domain domain"
    "frames facts";
  align-items: center;
  gap: var(--space-20) clamp(32px, 4vw, 56px);
}

.proof-card--campaign .proof-card__domain { grid-area: domain; margin-bottom: 0; }
.proof-card--campaign .proof-card__frames { grid-area: frames; }
.proof-card--campaign .proof-card__facts { grid-area: facts; margin-top: 0; }

/* The range gallery carries all 55 of the owner's real AI-generated frames,
   in the fixed source order gallery.html lists them — grouped by product
   story (watches, sarees, black skirt, blue shirt, jewellery, beauty,
   interior pairs, mist) so the run reads in one scan, same idea as the old
   nine-frame version, just the full set.

   MIXED GRID, not uniform: every 5th frame (positions 1, 6, 11, 16 … 51 —
   `.range-gallery__frame--wide`) spans 2 of the 4 desktop columns; the other
   four in each block of five stay at 1 column. Because every frame keeps its
   own portrait aspect-ratio (set inline per image from its real file
   dimensions — 45 are ~1000×1339, 10 are the saree-teal/-maroon set at
   1000×1241, 9 of the mist set are 1000×1332-1334), a "wide" frame reads as
   a genuinely bigger photograph, not a wider crop of the same frame. At 4
   columns this tiles with zero gaps: a wide frame lands with two 1-col
   frames beside it, the next block's two 1-col frames land beside the block
   AFTER's wide frame, and the remaining four 1-col frames fill a clean row
   of their own — a 3-row cascade of [wide+small+small] / [small+small+wide]
   / [small+small+small+small] repeating every 10 frames. 55 is not a
   multiple of that, so the very last frame (55, itself a "wide" position)
   sits alone in a trailing half-width row — a normal masonry tail, not a
   hole in the middle of the grid.

   `align-items: start` is load-bearing here for the same reason it is on
   `.proof-card__frames` above — a row mixing a 2-col frame with 1-col
   frames of a different real aspect-ratio would otherwise let the global
   `img { height: 100% }` stretch the shorter ones to the row's height and
   crop them. Because height is not stretched, a row's shorter frames simply
   sit flush to its top with empty space below them at the row's shared
   height — the same accepted trade-off already documented on
   `.proof-card__frames`, not a defect.

   Responsive: 4 columns here is the "richer" desktop pattern (≥1024px, this
   rule). The `--wide` class needs no columns-specific override: at 2
   columns (768–1023px, below) span-2 already reads as "this frame gets its
   own full-width row, the rest pair up two-to-a-row" — simpler by
   construction, same markup. At 1 column (≤767px, further below) `--wide`
   is reset back to `span 1` so it doesn't force a phantom second grid
   track and overflow a narrow viewport; every frame just stacks full width. */
.range-gallery {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  align-items: start;
  gap: var(--space-16);
  margin-top: clamp(64px, 8vw, 108px);
}

.range-gallery__frame--wide {
  grid-column: span 2;
  /* ⚠ AND two ROWS. Measured 2026-09-07 at 1280px: a wide frame is 814px tall
     beside 396px neighbours, so spanning columns alone left ~418px of dead
     canvas under every small frame in its row — the grid filled only 74.2% of
     its own area and ran 11,434px long. Spanning two rows lets the next pair
     of small frames stack into that space: 93.8% filled, 9,046px, 2,388px of
     hole removed, and still zero cropped frames (each keeps its own inline
     aspect-ratio; `align-items: start` above stops a tall row stretching a
     short frame). Two stacked smalls (396+16+396=808) meet the wide's 814
     within 6px, so the seams read level. */
  grid-row: span 2;
}

.range-gallery__frame img {
  border-radius: var(--radius-14);
}

.range-gallery__note {
  max-width: 62ch;
  margin-top: clamp(28px, 4vw, 40px);
  color: var(--on-surface-muted);
  font-size: var(--font-15);
}

/* landing/gallery.html reuses the whole .range block above as the main
   content of its own standalone page (see landing/index.html for the
   version of this section that sits between #proof and .inside, with
   .section's shared padding-block on both sides). Off that page, .range
   is the FIRST thing under the fixed nav pill (top: 12px, ~74px tall),
   with no hero above it to clear — .section's own top padding (min 96px)
   would technically clear the pill but reads cramped as a page opener.
   This overrides just the top edge of .section's padding-block (a later,
   equal-specificity longhand wins the cascade for that edge; the bottom
   edge is untouched) with the same figures terms.html's .legal-main
   already uses for the same reason. */
.section--page-top {
  padding-top: clamp(120px, 18vw, 188px);
}

.inside {
  overflow: hidden;
  background: var(--on-surface);
  color: var(--canvas);
}

/* ⚠ OWNER RULING 2026-08-31: "all the screenshots in bigger size." The copy
   column gives up width to the capture rather than the section growing. */
.inside__grid {
  display: grid;
  grid-template-columns: minmax(220px, 0.3fr) minmax(0, 0.7fr);
  align-items: center;
  gap: clamp(36px, 5vw, 72px);
}

.inside__copy h2 {
  margin-top: var(--space-18);
  font-size: clamp(46px, 5.5vw, 78px);
}

.inside__copy h2 span,
.inside__copy > p:first-child,
.inside__copy > p:last-child {
  color: color-mix(in srgb, var(--canvas) 68%, transparent);
}

.inside__copy > p:last-child {
  max-width: 46ch;
  margin-top: var(--space-28);
}

.studio-proof {
  overflow: hidden;
  border-radius: var(--radius-16);
  background: #211d19;
  box-shadow: 0 28px 80px rgba(0, 0, 0, 0.4);
}

/* ⚠ OWNER RULING 2026-08-31: "Half of the frame screenshots are not visible.
   I want this thing to be visible." These are UI captures full of small text,
   not photographs — the global `img { object-fit: cover }` was cropping them
   to fill a box shaped differently from the file, and the phone rule below
   squeezed a 1600x885 screen into 4:5, throwing away most of the interface.
   `contain` plus the file's own ratio means the whole screen is on the page. */
.system-capture {
  background: #171514;
  object-fit: contain;
}

.studio-proof img {
  aspect-ratio: 1600 / 885;
}

.studio-proof figcaption {
  display: flex;
  justify-content: space-between;
  gap: var(--space-16);
  padding: var(--space-13) var(--space-16);
  color: #b8aca2;
  font-size: var(--font-11);
}

/* Three-stage partnership */
.partnership {
  position: relative;
  color: var(--on-accent);
  background: linear-gradient(180deg, var(--accent-bright), var(--accent-deep));
}

.partnership-anchor {
  height: 1px;
}

.partnership__stage {
  position: relative;
}

.partnership-panel {
  display: flex;
  min-height: 100svh;
  align-items: center;
  padding: clamp(84px, 9vw, 118px) var(--space-24) var(--space-44);
}

.partnership-panel__inner {
  position: relative;
  display: grid;
  /* ⚠ OWNER RULING 2026-08-31, same as .inside__grid: the Studio captures go
     bigger. Wider inner, and the capture column takes half instead of 0.44. */
  width: min(100%, 1320px);
  min-height: min(760px, calc(100svh - 132px));
  grid-template-columns: minmax(0, 0.5fr) minmax(320px, 0.5fr);
  grid-template-rows: auto auto auto 1fr;
  align-content: center;
  gap: var(--space-18) var(--space-72);
  margin-inline: auto;
}

.partnership-panel__number {
  position: absolute;
  z-index: 0;
  top: -36px;
  left: -4px;
  color: transparent;
  font-size: clamp(120px, 17vw, 238px);
  font-weight: 400;
  line-height: 0.8;
  letter-spacing: -0.04em;
  -webkit-text-stroke: 1px rgba(255, 246, 242, 0.22);
}

.partnership-panel__label,
.partnership-panel h2,
.partnership-panel__sentence,
.partnership-panel__details {
  position: relative;
  z-index: 1;
  grid-column: 1;
}

.partnership-panel__label {
  align-self: end;
  color: rgba(255, 246, 242, 0.72);
  font-size: var(--font-11);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.partnership-panel h2 {
  color: var(--on-accent);
  font-size: clamp(78px, 10vw, 150px);
}

.partnership-panel__sentence {
  max-width: 23ch;
  margin-top: var(--space-12);
  font-size: clamp(24px, 2.6vw, 38px);
  letter-spacing: -0.025em;
  line-height: 1.15;
}

.partnership-panel__details {
  display: flex;
  align-self: end;
  gap: var(--space-22);
  margin-top: var(--space-36);
  padding-top: var(--space-16);
  border-top: 1px solid rgba(255, 246, 242, 0.34);
  color: rgba(255, 246, 242, 0.72);
  font-size: var(--font-11);
}

.partnership-panel figure {
  position: relative;
  z-index: 1;
  grid-area: 1 / 2 / 5 / 3;
  align-self: center;
  overflow: hidden;
  border-radius: var(--radius-14);
  background: #211d19;
  box-shadow: 0 28px 80px rgba(61, 5, 20, 0.32);
}

.partnership-panel figure img {
  aspect-ratio: 16 / 10;
}

@media (min-width: 768px) {
  .partnership.is-enhanced .partnership__stage {
    height: 100svh;
    overflow: hidden;
  }

  .partnership.is-enhanced .partnership-panel {
    position: absolute;
    inset: 0;
    opacity: 0;
  }

  .partnership.is-enhanced .partnership-panel:first-child {
    opacity: 1;
  }
}

.founder {
  display: grid;
  grid-template-columns: minmax(0, 0.58fr) minmax(320px, 0.42fr);
  align-items: center;
  gap: clamp(56px, 8vw, 120px);
}

.founder__frames {
  position: relative;
  min-height: 720px;
}

.founder__frames figure {
  position: absolute;
  overflow: hidden;
  border-radius: var(--radius-14);
  box-shadow: var(--shadow);
}

.founder__frames figure:nth-child(1) { top: 0; left: 0; width: 54%; height: 80%; }
.founder__frames figure:nth-child(2) { top: 9%; right: 0; width: 38%; height: 47%; }
.founder__frames figure:nth-child(3) { right: 8%; bottom: 0; width: 33%; aspect-ratio: 1; }

.founder__copy h2 {
  margin-top: var(--space-20);
  font-size: clamp(46px, 5.5vw, 76px);
}

.founder__copy > p:not(:first-child) {
  max-width: 56ch;
  margin-top: var(--space-26);
  color: var(--on-surface-muted);
}

.outcome {
  padding-block: 0 clamp(96px, 12vw, 176px);
}

.outcome > div {
  padding: clamp(48px, 8vw, 104px);
  border-radius: var(--radius-16);
  background: var(--accent-deep);
  color: var(--on-accent);
}

.outcome h2 {
  max-width: 12ch;
  margin-top: var(--space-20);
}

.outcome h2 span,
.outcome > div > p:not(:first-child) {
  color: rgba(255, 246, 242, 0.7);
}

.outcome > div > p:not(:first-child) {
  max-width: 52ch;
  margin-top: var(--space-28);
}

.outcome .button {
  margin-top: var(--space-28);
}

.faq-list {
  margin-top: clamp(64px, 9vw, 112px);
  border-bottom: 1px solid var(--line);
}

.faq-list details {
  border-top: 1px solid var(--line);
}

.faq-list summary {
  position: relative;
  min-height: 76px;
  padding: var(--space-24) var(--space-52) var(--space-24) 0;
  cursor: pointer;
  font-size: var(--font-18);
  font-weight: 500;
  list-style: none;
  transition: color var(--duration-quick) var(--ease);
}

.faq-list summary::-webkit-details-marker { display: none; }

.faq-list summary::after {
  position: absolute;
  top: 23px;
  right: 4px;
  color: var(--accent);
  content: "+";
  font-size: var(--font-28);
  font-weight: 400;
  line-height: 1;
  transition: transform var(--duration-quick) var(--ease), color var(--duration-quick) var(--ease);
}

.faq-list details[open] summary::after { transform: rotate(45deg); }

.faq-list summary:active,
.faq-list summary:active::after {
  color: var(--accent-deep);
}

.faq-list details > p {
  max-width: var(--reading);
  padding: 0 var(--space-52) var(--space-28) 0;
  color: var(--on-surface-muted);
}

/* Outfit only loads 400/500 — never lean on a browser-faked bold. */
.faq-list details > p strong {
  color: var(--on-surface);
  font-weight: 500;
}

/* Enquiry form — posts to the existing anonymous /api/contacts/add (see
   motion.js's CONTACTS_API_URL and worker/routes/contacts.js). Built entirely
   from the page's own type scale, spacing rhythm and colour tokens; no new
   radius, colour or card style. `enquiry-input` per docs/design-landing.md
   §8: radius sm (10px), 1px line-control, ink border on focus, no glow ring
   — the ring comes from the page's own global :focus-visible outline. */
.enquiry-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-24) clamp(24px, 3vw, 40px);
  margin-top: clamp(64px, 8vw, 108px);
}

.enquiry-field--wide,
.enquiry-form__foot {
  grid-column: 1 / -1;
}

.enquiry-field {
  display: grid;
  gap: var(--space-8);
}

.enquiry-field label {
  color: var(--on-surface-muted);
  font-size: var(--font-11);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.enquiry-field label span {
  letter-spacing: normal;
  text-transform: none;
}

.enquiry-field input,
.enquiry-field textarea {
  width: 100%;
  padding: var(--space-14) var(--space-16);
  border: 1px solid var(--line-control);
  border-radius: var(--radius-10);
  background: var(--surface);
  color: var(--on-surface);
  font: inherit;
  font-size: var(--font-15);
  transition: border-color var(--duration-quick) var(--ease);
}

.enquiry-field textarea {
  min-height: 128px;
  resize: vertical;
}

.enquiry-field input:focus-visible,
.enquiry-field textarea:focus-visible {
  border-color: var(--on-surface);
}

.enquiry-field input[aria-invalid="true"] {
  border-color: var(--accent);
}

.enquiry-form__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-16);
  margin-top: var(--space-4);
}

.enquiry-status {
  color: var(--on-surface-muted);
  font-size: var(--font-13);
}

.enquiry-status[data-tone="error"] {
  color: var(--accent);
}

.enquiry-confirm {
  max-width: 54ch;
  margin-top: clamp(64px, 8vw, 108px);
}

.enquiry-confirm p {
  color: var(--on-surface-muted);
  font-size: var(--font-17);
}

.site-footer {
  margin-bottom: var(--space-24);
  padding: clamp(38px, 5vw, 64px);
  border-radius: var(--radius-16);
  background: var(--surface);
}

.site-footer__main {
  display: grid;
  grid-template-columns: minmax(180px, 1fr) minmax(220px, 1fr) auto;
  align-items: center;
  gap: var(--space-32);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: var(--space-12);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-decoration: none;
  transition: color var(--duration-quick) var(--ease);
}

.wordmark svg { color: var(--on-surface); transition: color var(--duration-quick) var(--ease); }

.site-footer__main > p {
  color: var(--on-surface-muted);
}

.site-footer__links,
.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-18);
  margin-top: var(--space-40);
  padding-top: var(--space-20);
  border-top: 1px solid var(--line);
  color: var(--on-surface-muted);
  font-size: var(--font-12);
}

/* Terms and Privacy moved out of the navigation row (owner ruling 2026-09-08,
   five links maximum) and down here, which is where legal links belong. */
.site-footer__legal-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-18);
}

.site-footer__links a {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: color var(--duration-quick) var(--ease);
}

/* Terms/Privacy rendered at 19px tall on phone (owner tap-target audit
   2026-09-08) — padding, not font-size, so the visible text position does
   not shift; matches the 44px target .site-footer__links a already uses. */
.site-footer__legal-links a {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

/* Owner ruling 2026-09-08: "I need in the bold." The one link in this row that
   goes somewhere other than this site, so it carries the weight the rest do
   not. 500 is the weight the buttons already use — not a new value. */
.footer-login-link {
  font-weight: 500;
}

.site-footer__legal {
  justify-content: space-between;
  margin-top: 0;
  border-top: 0;
  padding-top: var(--space-8);
}

@media (hover: hover) and (pointer: fine) {
  .button:hover { transform: translateY(-2px); }
  .button--primary:hover { background: var(--accent-deep); }
  .button--quiet:hover { border-color: var(--on-surface); }
  .button--light:hover { background: var(--canvas); }
  .nav-links a:hover { color: rgba(255, 246, 242, 0.62); }
  .nav-meta a:hover { color: rgba(255, 246, 242, 0.92); }
  .nav-menu-button:hover,
  .theme-toggle:hover { color: rgba(255, 246, 242, 0.7); }
  .faq-list summary:hover { color: var(--accent); }
  .faq-list summary:hover::after { color: var(--accent-deep); }
  .wordmark:hover,
  .wordmark:hover svg { color: var(--on-surface-muted); }
  .site-footer__links a:hover { color: var(--on-surface); }
}

@media (max-width: 1023px) {
  .contact-frame--1 { width: 18%; }
  .contact-frame--2 { left: 24%; width: 14%; }
  .contact-frame--3 { width: 16%; }
  .contact-frame--4 { right: 23%; width: 13%; }
  .contact-frame--5 { left: 45%; width: 12%; }
  .contact-frame--6 { width: 15%; }
  .contact-frame--7 { width: 13%; }

  .statement__grid { grid-template-columns: minmax(0, 1fr); }
  .section-heading { grid-template-columns: 120px minmax(0, 1fr); }

  .proof-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-40) var(--space-24); }
  .proof-card--campaign { grid-template-columns: minmax(0, 1fr); grid-template-areas: "domain" "frames" "facts"; }
  .proof-card--campaign .proof-card__facts { margin-top: var(--space-20); }

  /* Simpler tablet pattern, same markup: at 2 columns a `--wide` frame's
     span-2 already reads as its own full-width row, so every block of 5
     becomes one feature frame + two rows of two — no new rule needed. */
  .range-gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The 2-row span is a 4-column device only. Here a wide frame already
     fills the row, so keeping it would leave the hole it exists to remove. */
  .range-gallery__frame--wide { grid-row: span 1; }

  .inside__grid { grid-template-columns: minmax(0, 1fr); }
  .inside__copy { max-width: 720px; }
  .partnership-panel__inner { gap: var(--space-18) var(--space-40); }
  .founder { grid-template-columns: minmax(0, 1fr); }
  .founder__frames { width: min(100%, 760px); }
  .founder__copy { max-width: 720px; }
}

@media (max-width: 767px) {
  .page-shell { width: min(calc(100% - 28px), var(--page-max)); }
  .section { padding-block: var(--space-56); }
  /* hero__intro and .outcome carry the same 96px base clamp elsewhere in
     the file; mobile-only reduction here keeps them slightly more generous
     than a plain .section (owner ruling 2026-09-08: less empty space on phone). */
  .hero__intro { padding-block: var(--space-64); }
  .outcome { padding-block: 0 var(--space-64); }
  h2 { font-size: clamp(46px, 13vw, 68px); }
  .section-heading h1 { font-size: clamp(46px, 13vw, 68px); }

  /* PROBLEM 3, owner-reported 2026-09-08, two screenshots: the fixed pill
     sitting on top of the hero photograph, and sitting on top of a section
     heading (on pages like gallery/services where a `.section--page-top`
     heading is the very first thing on the page, with no hero above it).
     Owner's ruling: "Stop it covering the photo." The pill stays
     `position: fixed` at `top: 12px`, undocked, until NAV_DOCK_AFTER (80px
     of scroll) in motion.js — so at scroll 0, and for the first ~80px, it
     sits on whatever is directly under the top of the viewport. --nav-clear
     is the pill's own reserved footprint (12px offset + ~52px tall pill +
     a visible gap, using the same figures the footer fix above already
     uses for the pill's bottom footprint) so the page can push its first
     content below the pill instead of under it. */
  /* ⚠ OWNER-REPORTED 2026-09-09: "Manu bar has little gape still." It did.
     The pill is 12px from the top and about 52px tall, so it ends at 64px —
     but this reserved 100px, leaving a 36px band of bare page above the hero
     photograph. Now it reserves the pill's real footprint plus a 12px breath,
     matching the gap above it. Tightened again 2026-09-09 on his second look
     ("photo should be close to nav bar"): it now reserves the pill's exact
     footprint, 12 + 52 = 64px, so the photograph begins where the pill ends.
     Do not drop it below the
     pill's own bottom edge or the pill sits on the photograph again, which is
     the fault this clearance was added to fix in the first place. */
  :root { --nav-clear: calc(var(--space-12) + var(--space-52)); }

  .nav-wrap { top: 12px; width: min(calc(100% - 24px), 372px); }
  .theme-toggle__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .nav-panel { max-height: calc(100svh - 78px); }

  .hero { min-height: 112svh; margin-top: var(--nav-clear); }
  .hero__stage { min-height: 620px; }

  /* ⚠ OWNER-REPORTED 2026-09-08: "currently on the mobile is not looking good."
     Measured at 375x812: the banner box is 0.462 ratio against a 0.745 image,
     so `cover` was throwing away 38% OF THE WIDTH — the right-hand model was
     being sliced. The mobile crop is a good portrait frame; the box was simply
     far narrower than it. Anchoring at 42% across keeps both models in view
     instead of centring the cut, and the box gets a ceiling so it can never be
     taller than the frame it holds. */
  /* CORRECTED 2026-09-08 (owner screenshot: hero cropped down to the models'
     legs). object-position raised slightly further to keep both heads in
     frame across a range of band heights — measured at 620px, 480px, 380px
     and 320px stage heights in the Browser pane inspector; see the session
     report for the readings. */
  .hero__banner img { object-position: 42% 28%; }
  /* CORRECTED 2026-09-08: this used `86svh`, the SMALL viewport height (the
     viewport as measured with the address bar fully expanded). On a real
     phone the address bar collapses while scrolling, so the visible
     viewport grows TALLER than `86svh` — the cap held the stage far shorter
     than the screen it was actually filling, which drove `object-fit: cover`
     to crop both models' heads off, leaving only their legs (owner
     screenshot). `86dvh` tracks the viewport the reader can actually see. */
  .hero__stage { max-height: 86dvh; }
  .contact-frame { border-radius: var(--radius-10); }
  .contact-frame--1 { top: 9%; left: 5%; width: 32%; aspect-ratio: 4 / 5; transform: rotate(-3deg); }
  .contact-frame--2 { top: 8%; left: 48%; width: 28%; aspect-ratio: 1; transform: rotate(2deg); }
  .contact-frame--3 { top: 29%; right: auto; left: 9%; width: 25%; aspect-ratio: 4 / 5; transform: rotate(2deg); }
  .contact-frame--4 { top: 27%; right: 5%; width: 32%; aspect-ratio: 4 / 5; transform: rotate(-2deg); }
  .contact-frame:nth-child(n + 5) { display: none; }
  .direction-mark { top: 24%; left: 49%; width: 72px; height: 72px; }
  .direction-mark__ring { inset: 8px; }
  .direction-mark__line { top: 36px; left: -12px; width: 96px; }
  .direction-mark__label { top: 70px; font-size: var(--font-9); }

  .hero__copy { bottom: 20px; }
  .hero h1 { margin-top: var(--space-8); font-size: clamp(48px, 15vw, 64px); }
  .hero__position { margin-top: var(--space-12); font-size: var(--font-20); }
  .hero__body { max-width: 42ch; font-size: var(--font-13); line-height: 1.45; }
  .hero__actions { margin-top: var(--space-14); }
  .hero__actions .button { min-height: 44px; padding: var(--space-10) var(--space-14); font-size: var(--font-12); }

  .statement__grid { gap: var(--space-28); }
  .statement__grid p { font-size: var(--font-15); }
  .section-heading { grid-template-columns: minmax(0, 1fr); gap: var(--space-18); }

  .proof-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-40); margin-top: var(--space-64); }
  .proof-card__frames { gap: var(--space-8); }

  /* Plain one-column fallback: a mixed grid at 375px reads as broken, so
     `--wide` is reset to `span 1` here — without this a 2-col-implying span
     on a 1-col explicit grid would force a phantom second track and
     overflow the viewport. Every frame just stacks full width in order. */
  .range-gallery { grid-template-columns: minmax(0, 1fr); gap: var(--space-10); margin-top: var(--space-48); }
  .range-gallery__frame--wide { grid-column: span 1; grid-row: span 1; }
  .section--page-top { padding-top: max(var(--space-64), var(--nav-clear)); }

  .editorial-gallery { min-height: 980px; grid-template-rows: repeat(19, 48px); gap: var(--space-8); margin-top: var(--space-104); }
  .gallery-frame--wide { grid-area: 1 / 1 / 10 / 13; }
  .gallery-frame--banner { grid-area: 1 / 1 / 10 / 13; }
  .gallery-frame--banner img { object-position: 50% 43%; }
  .gallery-frame--portrait { grid-area: 11 / 1 / 16 / 6; }
  .gallery-frame--square { grid-area: 11 / 7 / 16 / 13; }
  .gallery-frame--tall { grid-area: 16 / 2 / 20 / 8; }
  .gallery-note { grid-area: 16 / 9 / 19 / 13; align-self: center; font-size: var(--font-16); }

  /* ⚠ OWNER RULING 2026-08-31: the phone rules used to force these UI
     captures into a 4:5 box and then pan across them with object-position,
     which is what made "half of the frame screenshots not visible." A wide
     screen full of small text cannot be cropped to a portrait box and still
     be a screenshot of anything. The whole frame shows now, letterboxed on
     its own dark ground; the panning rules are deliberately gone. */
  .studio-proof { aspect-ratio: auto; }
  /* Owner ruling 2026-09-02 ("fit the box to the picture"): the box is the
     screenshot's own 1600/885 shape. It was 4/5 from the days the capture was
     cropped with `cover`; once captures stopped being cropped (7d4b4c2a) that
     left dark bands above and below on phones. */
  .partnership-panel figure { aspect-ratio: 1600 / 885; }
  .partnership-panel figure img { aspect-ratio: auto; }

  .value-rows article { grid-template-columns: 42px minmax(0, 1fr); gap: var(--space-10) var(--space-14); padding-block: var(--space-22); }
  .value-rows article p { grid-column: 2; font-size: var(--font-14); }
  .value-rows h3 { font-size: var(--font-28); }

  .inside__grid { gap: var(--space-52); }
  .studio-proof figcaption { display: grid; gap: var(--space-3); }

  .partnership-panel { min-height: 92svh; padding: var(--space-92) var(--space-18) var(--space-54); }
  .partnership-panel__inner { min-height: 0; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto; gap: var(--space-16); }
  .partnership-panel__number { top: -24px; font-size: var(--font-122); }
  .partnership-panel__label,
  .partnership-panel h2,
  .partnership-panel__sentence,
  .partnership-panel__details,
  .partnership-panel figure { grid-column: 1; }
  .partnership-panel h2 { font-size: var(--font-74); }
  .partnership-panel__sentence { max-width: 27ch; font-size: var(--font-24); }
  .partnership-panel__details { flex-wrap: wrap; gap: var(--space-8) var(--space-18); margin-top: var(--space-14); }
  .partnership-panel figure { grid-area: auto; margin-top: var(--space-18); }

  .founder { gap: var(--space-56); }
  .founder__frames { min-height: 480px; }
  .founder__copy h2 { font-size: clamp(45px, 12vw, 64px); }

  .outcome > div { padding: var(--space-44) var(--space-24); }
  .outcome h2 { font-size: var(--font-48); }

  .faq-list summary { min-height: 70px; padding-block: var(--space-21); font-size: var(--font-16); }

  .enquiry-form { grid-template-columns: minmax(0, 1fr); gap: var(--space-20); margin-top: var(--space-48); }
  .enquiry-confirm { margin-top: var(--space-48); }

  .site-footer { padding: var(--space-28) var(--space-22); }
  .site-footer__main { grid-template-columns: minmax(0, 1fr); }
  .site-footer__main .button { justify-self: start; }

  /* ⚠ OWNER-REPORTED 2026-09-08, pointing at .site-footer__legal: "it's inside
     on phone." Measured at 375x812 in the browser pane: the docked nav pill
     (.nav-wrap.is-docked, motion.js) parks itself `calc(100svh - 100% - 24px)`
     from the top — i.e. 24px off the BOTTOM edge — and at this width it is
     52px tall, so it claims the bottom 76px of the viewport once the reader
     has scrolled all the way down. The footer's own margin-bottom was only
     var(--space-24) = 24px, so the legal row (Terms / Privacy / © SPANDA) sat
     underneath the pill with its lower half literally clipped by it in a
     screenshot at the page's max scroll position — the pill was never
     transparent, it was just docked exactly where the last line of the page
     lives. Not overflow, not a collision with the links beside it. Fix:
     reserve the pill's full footprint (76px) plus a visible gap below the
     footer instead of the flat 24px, only where the pill actually docks. */
  .site-footer { margin-bottom: calc(var(--space-52) + var(--space-24) + var(--space-24)); }
}

@media (max-width: 479px) {
  .nav-links a { font-size: var(--font-22); }
  .hero__actions { align-items: stretch; flex-direction: column; gap: var(--space-8); width: min(100%, 290px); }
  .hero__actions .button { width: 100%; }
  .founder__frames { min-height: 410px; }
  .site-footer__legal { flex-direction: column; }
}

@media (prefers-reduced-motion: reduce) {
  /* Both `scroll-behavior: auto` overrides that used to live here existed
     only to undo the base html{} rule's smooth scroll-behavior, which is
     gone (see the html{} rule up top) — `auto` is the platform default, so
     restating it did nothing even before this comment. */

  *,
  *::before,
  *::after {
    animation: none !important;
    transition-duration: var(--duration-reduced-motion) !important;
  }

  /* The nav tuck stays functional under reduced motion — it just stops
     sliding. Hiding/showing navigation is behaviour, not decoration. */
  .nav-wrap { transition: none !important; }

  .hero { min-height: 100svh; }
  .contact-frame { transform: none; }
  .button:active { transform: none; }
  .gallery-frame--banner img { transform: none !important; }
  .hero__banner img { transform: none !important; }
  .gallery-frame--portrait img,
  .gallery-frame--square img,
  .gallery-frame--tall img { transform: none !important; }
  .section-heading { transform: none !important; }

  .partnership.is-enhanced .partnership__stage {
    height: auto;
    overflow: visible;
  }

  .partnership.is-enhanced .partnership-panel {
    position: relative;
    inset: auto;
    opacity: 1;
  }
}
