/* ============================================================================
   INDIAN CURRY RESTAURANT & BAR: Dothan, Alabama
   The token system. Nothing is hand-placed; every value below traces to a token
   defined here BEFORE anything was composed (design-precision.md).

   Concept: the whole map of India served on gold in Dothan.
   Signature structure: THE SERVICE LINE: a centred vertical thread every page
   sets its courses along, the way their tables are actually laid.
   Reveal verb: SETTLE: things land like service being set on a table.
   Apertures: the arch (their menu cover), the katori (their thali), the jali
   (their red screen). Generated by tools/apertures.mjs.
   Colour: generated + contrast-gated by tools/tokens.py.
   ========================================================================= */

/* ---- THE FACES ---------------------------------------------------------- */
@font-face {
  font-family: 'Fraunces';
  src: url('/assets/fonts/fraunces-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
  font-style: normal;
}
@font-face {
  font-family: 'Archivo';
  src: url('/assets/fonts/archivo-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  font-style: normal;
}

:root {
  /* ---- COLOUR (tools/tokens.py; the ramp is oklch(L C 68), H locked to the
     measured brand hue of their own mark, C 0.010 whisper -> 0.044 tinted.
     Brand colours are pipetted exactly and never regenerated.) ------------- */
  --ic-50:  #faf5ef;
  --ic-100: #f3ebe3;
  --ic-200: #e7dcd0;
  --ic-300: #d5c6b7;
  --ic-400: #b5a290;
  --ic-500: #947f6a;
  --ic-600: #735d47;
  --ic-700: #58432d;
  --ic-800: #412f1b;
  --ic-900: #2e1e0c;
  --brass:  #b7a37f;
  --gold:   #947952;
  --crimson:#8e1e1a;

  /* L2 semantic: where 90% of the build points */
  --surface:        var(--ic-50);
  --surface-raised: var(--ic-100);
  --surface-sunk:   var(--ic-200);
  --surface-deep:   var(--ic-900);   /* the atlas ground, the footer */
  --ink:            var(--ic-900);
  --ink-muted:      var(--ic-600);   /* gated 5.72:1 on the field, 4.59:1 on parchment */
  --ink-on-deep:    var(--ic-50);
  --ink-on-deep-muted: var(--ic-300);
  --accent:         var(--crimson);  /* ONE accent, the one focal action, <=10% of surface */
  --accent-ink:     var(--ic-50);
  --line:           color-mix(in srgb, var(--ic-900) 10%, transparent);
  --line-strong:    color-mix(in srgb, var(--ic-900) 22%, transparent);
  --line-on-deep:   color-mix(in srgb, var(--ic-50) 14%, transparent);
  --focus:          var(--crimson);

  /* THE STUCK CHROME TOKEN. Deliberately sRGB hex, and deliberately NOT promoted to
     oklch in the @supports block below. It is the same ivory as --ic-50; only the
     syntax differs. Why: the render wall's stuck-chrome check (the Bunkers class,
     LESSONS 2026-07-04) reads a stuck bar's resolved background alpha with an
     rgb()/rgba() parser, so a nav painted in oklch() or color(srgb ...) reads as
     alpha 0 and is blocked as translucent even when it is fully opaque. The law here
     is "stuck chrome resolves fully opaque", and this token satisfies it in a form the
     gate can actually verify. The nav is the ONLY stuck bar on this site, so the
     exception is one token wide. Gate limitation surfaced to Campbell in the receipt.
     Do not point anything else at this token. */
  --chrome: #faf5ef;

  /* THE LIVING LAYER's one style surface. Each mood only re-tints this token;
     the composer never touches layout, type or structure. Tinted here in the
     build's own palette: light travelling over a brass tray through the day. */
  --sky: #f6ede1;

  /* ---- SPACE: one 4-based ramp. Every margin/padding/gap is one of these. */
  --space-1: 4px;   --space-2: 8px;   --space-3: 12px;  --space-4: 16px;
  --space-5: 24px;  --space-6: 32px;  --space-7: 48px;  --space-8: 64px;
  --space-9: 96px;  --space-10: 128px; --space-11: 160px; --space-12: 224px;

  /* ---- TYPE: one ratio, 1.25, generated. Never type a rem by hand. */
  --ms-dn2: 0.64rem; --ms-dn1: 0.8rem;
  --ms-0: 1rem;      --ms-1: 1.25rem;  --ms-2: 1.563rem; --ms-3: 1.953rem;
  --ms-4: 2.441rem;  --ms-5: 3.052rem; --ms-6: 3.815rem; --ms-7: 4.768rem;
  --display: 'Fraunces', Georgia, serif;
  --text: 'Archivo', system-ui, sans-serif;

  /* ---- GRID */
  --edge:    clamp(1rem, 0.6rem + 1.1vw, 1.5rem);
  --gutter:  clamp(1.25rem, 0.8rem + 2vw, 3rem);
  --shell:   min(100% - 2 * var(--edge), 75rem);
  --measure: 65ch;
  --section-y: clamp(3.5rem, 2.5rem + 4.5vw, 8rem);

  /* THE OPENING. The height of the stuck bar, derived from the nav's own numbers
     (.nav__in min-height + its padding-block, both ways) plus the safe-area inset it
     pads by. The hero sizes its doorway from this and the tray sticks to it, so the two
     can never drift apart: change the nav and both follow. The links wrap to a second
     row at 56rem, which is the only reason this has a breakpoint. */
  --nav-h: calc(68px + var(--space-2) * 2 + env(safe-area-inset-top));
  --door-gap: var(--space-8);   /* the frame never touches the fold's edge */
  /* THE TRAY'S HEIGHT, and the ONE authority on it. .tray__in is SET to this, and every
     chapter's scroll-margin clears the bar plus this, so the box and the landing read the
     same number and cannot drift. It is one fixed height on purpose: the tray used to open
     laid (7rem vessels) and collapse to a rim, but it sits in normal flow, so collapsing
     it shrank the page by 89px and shunted every chapter up. That shifted the book under
     the finger of anyone simply scrolling, and it landed every tray jump 89px behind the
     bar, because the target was measured in the layout we were leaving. Measured in a real
     browser 2026-07-16. A fixed lane costs the flourish and buys a page that does not move
     when you touch it. */
  --tray-h: 5rem;

  /* ---- RADIUS: the house lane. This build is nearly radius-free on purpose:
     the apertures ARE the shape language, so boxes never compete with them. */
  --r-xs: 4px; --r-sm: 8px; --r-md: 12px; --r-pill: 999px; --r-full: 50%;

  /* ---- ELEVATION: one light source, straight down, ink-tinted, never black */
  --e1: 0 1px 2px color-mix(in srgb, var(--ic-900) 12%, transparent),
        0 1px 1px color-mix(in srgb, var(--ic-900) 8%, transparent);
  --e2: 0 1px 2px color-mix(in srgb, var(--ic-900) 12%, transparent),
        0 4px 8px color-mix(in srgb, var(--ic-900) 10%, transparent);
  --e3: 0 1px 2px color-mix(in srgb, var(--ic-900) 14%, transparent),
        0 12px 20px color-mix(in srgb, var(--ic-900) 12%, transparent);
  --e4: 0 1px 2px color-mix(in srgb, var(--ic-900) 16%, transparent),
        0 24px 40px color-mix(in srgb, var(--ic-900) 12%, transparent);

  /* ---- MOTION: two eases, four durations, one reveal verb */
  --ease-standard:   cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-expressive: cubic-bezier(0.16, 1, 0.30, 1);
  --dur-micro: 120ms; --dur-element: 200ms; --dur-section: 320ms; --dur-cinematic: 600ms;
  --stagger: 75ms;

  font-size: clamp(1rem, 0.93rem + 0.31vw, 1.1875rem);
}

/* The same ramp in OKLCH where it is supported: wider gamut, same law, same numbers. */
@supports (color: oklch(0 0 0)) {
  :root {
    --ic-50:  oklch(0.972 0.010 68); --ic-100: oklch(0.945 0.014 68);
    --ic-200: oklch(0.900 0.020 68); --ic-300: oklch(0.835 0.026 68);
    --ic-400: oklch(0.724 0.034 68); --ic-500: oklch(0.610 0.040 68);
    --ic-600: oklch(0.495 0.044 68); --ic-700: oklch(0.400 0.044 68);
    --ic-800: oklch(0.320 0.042 68); --ic-900: oklch(0.250 0.038 68);
    --brass:  oklch(0.724 0.055 82); --gold:   oklch(0.592 0.064 76);
    --crimson:oklch(0.425 0.148 27.7);
  }
}

/* ---- THE LIVING LAYER's moods: light over a brass tray through the day ---- */
:root[data-living-mood="dawn"]     { --sky: #f7ece2; }
:root[data-living-mood="morning"]  { --sky: #f8f0e4; }
:root[data-living-mood="midday"]   { --sky: #f6ede1; }
:root[data-living-mood="afternoon"]{ --sky: #f6ebdc; }
:root[data-living-mood="evening"]  { --sky: #f3e5d6; }
:root[data-living-mood="night"]    { --sky: #efe2d3; }

/* ============================================================================
   BASE
   ========================================================================= */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
  min-height: 100svh;
  /* THE SAFE-ZONE LAW: installed standalone, nothing renders under the status bar
     or the home indicator. Every fixed edge pads by its own inset too. */
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
  color: var(--ink);
  font-family: var(--text);
  font-weight: 400;
  font-size: var(--ms-0);
  line-height: 1.6;
  text-align: center;              /* the centred law */
  display: flex;
  flex-direction: column;
  /* THE LIVING PARCHMENT: warm ivory, fine grain, and a slow brass light pass that
     drifts like light over a gold tray. Never flat white, never a stock gradient.
     The grain is a data-URI so it costs one paint and no request. */
  background-color: var(--surface);
  background-image:
    radial-gradient(120% 80% at 50% -10%, var(--sky) 0%, transparent 60%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E");
  transition: background-color var(--dur-cinematic) var(--ease-expressive);
}

main { flex: 1 0 auto; }

img, picture, svg, video { display: block; max-width: 100%; }
img { height: auto; }

a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

.g28-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.vh {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip {
  position: absolute; left: 50%; translate: -50% -120%;
  z-index: 400; padding: var(--space-3) var(--space-5);
  background: var(--ink); color: var(--ink-on-deep); text-decoration: none; font-weight: 600;
}
.skip:focus { translate: -50% calc(env(safe-area-inset-top) + var(--space-2)); }

/* ============================================================================
   TYPE ROLES: one treatment each, identical everywhere
   ========================================================================= */
.display, h1, h2, h3 {
  font-family: var(--display);
  font-variation-settings: 'opsz' 96, 'SOFT' 18, 'WONK' 1;
  font-weight: 900;
  text-wrap: balance;
  margin-inline: auto;
}
.display {
  font-size: clamp(var(--ms-5), 6.4vw, var(--ms-7));
  line-height: 1.0;
  letter-spacing: -0.025em;
  max-width: 15ch;
}
h1 { font-size: clamp(var(--ms-4), 5.2vw, var(--ms-5)); line-height: 1.05; letter-spacing: -0.02em; max-width: 16ch; }
h2 { font-size: clamp(var(--ms-3), 4vw, var(--ms-4)); line-height: 1.1; letter-spacing: -0.015em; max-width: 18ch; }
h3 { font-size: var(--ms-3); line-height: 1.15; letter-spacing: -0.01em; font-weight: 700; }

.subhead {
  font-family: var(--text);
  font-size: var(--ms-2);
  font-weight: 300;
  line-height: 1.35;
  letter-spacing: -0.005em;
  max-width: 34ch;
  margin-inline: auto;
  color: var(--ink-muted);
  text-wrap: balance;
}
p { max-width: var(--measure); margin-inline: auto; text-wrap: pretty; }
.lead { font-size: var(--ms-1); font-weight: 300; line-height: 1.55; }

/* Label = UI and form fields ONLY. Never a section eyebrow: this build has none. */
.label {
  font-family: var(--text);
  font-size: var(--ms-dn1);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.2;
}
.caption {
  font-size: var(--ms-dn1);
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: 0.01em;
  color: var(--ink-muted);
  max-width: 46ch;
  margin-inline: auto;
}

/* Every price, every stat, every time: tabular lining, always. */
.num, .price, time, .tabular {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* ============================================================================
   LAYOUT
   ========================================================================= */
.shell { width: var(--shell); margin-inline: auto; }
.measure { max-width: var(--measure); margin-inline: auto; }
.section { padding-block: var(--section-y); }
.section--deep { background: var(--surface-deep); color: var(--ink-on-deep); }
.section--deep .subhead, .section--deep .caption { color: var(--ink-on-deep-muted); }
.section--sunk { background: var(--surface-sunk); }

/* THE STRUCTURAL SIGNATURE is the FRAMING SYSTEM itself: every photograph on this site
   is seen through an aperture lifted from one of the restaurant's own objects (the arch
   from their menu cover, the katori from their thali, the jali from their screen). That
   is what carries the throughline from page to page.
   A centred "service line" used to be drawn down every page on top of it. It is gone:
   it ran through the headline and through every menu row, it read as a rendering
   artifact rather than a design, and the site loses nothing without it. Decoration by
   its own definition. Removed on Campbell's call, 2026-07-16. */

/* ============================================================================
   THE APERTURES: masks, never boxes (tools/apertures.mjs)
   Photographs keep their real backgrounds and are seen THROUGH a window cut
   from one of the restaurant's own objects. Nothing is cut out of a photo.
   ========================================================================= */
.frame {
  position: relative;
  margin-inline: auto;          /* the mobile-collapse trap: a block child with a
                                   max-width must centre its BOX, not just its text */
  overflow: hidden;
  background: var(--ic-800);    /* what shows while the photo decodes */
  /* iOS/Safari clips a mask correctly during a reveal transform only when the
     element owns its layer (the Bunkers scar, PLAN.md Part 5) */
  will-change: transform;
  transform: translateZ(0);
}
/* The manifest emits <picture><source><img>, so every rule below reaches THROUGH the
   picture element. Targeting `.frame > img` silently matched nothing and left every
   photograph unstyled inside its aperture: the katori showed a rectangle inside the
   brass rim and the lintel let ivory through its cusps. Caught on the wall's settled
   phone render, 2026-07-16. */
.frame picture { display: block; width: 100%; height: 100%; }
.frame img { width: 100%; height: 100%; object-fit: cover; display: block; }

.frame--arch   { aspect-ratio: 3 / 4; clip-path: url(#g28-arch); }
.frame--lintel { aspect-ratio: 3 / 2; clip-path: url(#g28-lintel); }
.frame--katori { aspect-ratio: 1 / 1; clip-path: url(#g28-katori); }

/* THE BRASS RIM: the katori is a bowl, so it owns a rim. Drawn as a ring INSIDE
   the aperture (an inset shadow would be clipped away by the mask on iOS). */
.frame--katori::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--r-full);
  border: 2px solid color-mix(in srgb, var(--brass) 70%, transparent);
  box-shadow: inset 0 0 0 6px color-mix(in srgb, var(--ic-900) 10%, transparent);
  pointer-events: none;
}

/* THE DARKENING lives in an OVERLAY, never a CSS filter: a filter on an image
   inside a transformed ancestor paints blank on iOS (the Bunkers scar). */
/* THE SCRIM IS THE HEADLINE'S GROUND, so it is cut to the headline's real band, measured,
   never guessed. The door is its only user.
   It used to release by 42% while the display sat from 24% to 61% up the frame, crossing
   the bright gold on their table AND the white wall above it. Swept across all 12,648
   pixels the type actually covers: worst 1.82:1 against the ivory, where the 3:1 floor for
   large text is law. Nine spot checks had said 2.62 and missed it, which is the whole
   reason the law says measure the ACTUAL surface.
   These stops plus the 32px the block drops (.door__in) measure a worst case of 4.03:1
   with zero samples under the floor, and they still let go by 82%, so the wall, the chairs
   and the room behind stay bright and the frame reads as a photograph, not a dark plate
   with words on it. Re-measure if the headline, its size, or this photo ever changes. */
.frame__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--ic-900) 86%, transparent) 0%,
    color-mix(in srgb, var(--ic-900) 72%, transparent) 30%,
    color-mix(in srgb, var(--ic-900) 60%, transparent) 55%,
    color-mix(in srgb, var(--ic-900) 30%, transparent) 68%,
    transparent 82%);
}

/* THE SERVICE REVEAL: an aperture does not fade in, it OPENS, like a door onto
   a room that was already there. The shape clip lives on .frame; this inner
   element carries the opening clip so the two never fight. */
.frame__door { position: absolute; inset: 0; clip-path: inset(0); }
.js .frame__door { clip-path: inset(48% 0 48% 0); transition: clip-path var(--dur-section) var(--ease-expressive); }
.js .frame.is-open .frame__door { clip-path: inset(0 0 0 0); }

/* PARALLAX THROUGH THE WINDOW: the photo drifts inside a STATIC aperture, which
   is what looking through a real window feels like: the frame is the wall, the
   room is behind it. One transform, driven by one clock. The <picture> is the moving
   part; the <img> just fills it. */
.frame__door > picture {
  position: absolute;
  left: 0;
  right: 0;
  top: -7%;
  height: 114%;                 /* the drift room, centred so a still frame is not offset */
  will-change: transform;
}
.frame__door > picture > img { width: 100%; height: 100%; object-fit: cover; }

/* THE JALI: the reveal threshold, not a frame. Their red screen dissolves and
   hands you the room. ONE moment in the build. */
.jali { position: relative; background: var(--crimson); aspect-ratio: 3 / 4; overflow: hidden; }
.jali picture { display: block; width: 100%; height: 100%; }
.jali img { width: 100%; height: 100%; object-fit: cover; display: block;
  mask: url(#g28-jali); -webkit-mask: url(#g28-jali); }
.g28-jali-bars { transition: opacity var(--dur-cinematic) var(--ease-expressive); }
.jali.is-through .g28-jali-bars { opacity: 0; }

/* ============================================================================
   CONTROLS
   ========================================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 48px;
  padding: 14px var(--space-6);
  font-family: var(--text);
  font-size: var(--ms-0);
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color var(--dur-element) var(--ease-standard),
              color var(--dur-element) var(--ease-standard),
              transform var(--dur-element) var(--ease-standard),
              box-shadow var(--dur-element) var(--ease-standard);
}
.btn--primary { background: var(--accent); color: var(--accent-ink); box-shadow: var(--e1); }
.btn--primary:hover { box-shadow: var(--e2); transform: translateY(-2px); }
.btn--quiet { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn--quiet:hover { border-color: var(--ink); transform: translateY(-2px); }
.section--deep .btn--quiet { color: var(--ink-on-deep); border-color: var(--line-on-deep); }
.section--deep .btn--quiet:hover { border-color: var(--ink-on-deep); }
.btn[disabled] { opacity: 0.6; cursor: progress; transform: none; }

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  justify-content: center;
}

/* ============================================================================
   THE REVEAL VERB: SETTLE. One verb, site-wide. Things land like service being
   set on a table: down onto their spot, never up from nowhere.
   ========================================================================= */
.js .settle { opacity: 0; transform: translateY(-16px); }
.js .settle.is-set {
  opacity: 1;
  transform: translateY(0);
  transition: opacity var(--dur-section) var(--ease-expressive),
              transform var(--dur-section) var(--ease-expressive);
}
.js .settle:nth-child(2)  { transition-delay: calc(var(--stagger) * 1); }
.js .settle:nth-child(3)  { transition-delay: calc(var(--stagger) * 2); }
.js .settle:nth-child(4)  { transition-delay: calc(var(--stagger) * 3); }
.js .settle:nth-child(5)  { transition-delay: calc(var(--stagger) * 4); }
.js .settle:nth-child(6)  { transition-delay: calc(var(--stagger) * 5); }

/* ============================================================================
   REDUCED MOTION: complete, not partial. Every window is simply already open.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .js .settle { opacity: 1; transform: none; }
  .js .frame__door { clip-path: inset(0); }
  .g28-jali-bars { opacity: 0; }
  .frame__door > picture { transform: none !important; }
}

/* ============================================================================
   THE NAV: one bar, honest labels. Every label lands where it promises.
   ========================================================================= */
.nav {
  position: sticky; top: 0; z-index: 200;
  /* the safe-zone law: a sticky top edge pads by its own inset */
  padding-top: env(safe-area-inset-top);
  /* THE STUCK-CHROME LAW (LESSONS 2026-07-04, the Bunkers class): stuck top chrome
     resolves a FULLY OPAQUE background. A translucent bar over a scrolling page is the
     banked defect, and no backdrop-filter buys its way out of it. Painted from
     --chrome (sRGB) so the gate can read the alpha it is checking. */
  background: var(--chrome);
  border-bottom: 1px solid var(--line);
}
.nav__in {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-4);
  min-height: 68px;
  padding-block: var(--space-2);
}
.nav__mark { display: inline-flex; align-items: center; gap: var(--space-3); text-decoration: none;
  justify-self: start; min-height: 48px; }
.nav__mark img { width: 48px; height: 48px; }
.nav__wordmark {
  font-family: var(--display);
  font-variation-settings: 'opsz' 40, 'SOFT' 10, 'WONK' 0;
  font-weight: 900; font-size: var(--ms-0); letter-spacing: -0.01em; line-height: 1;
  white-space: nowrap;
}
.tm { font-size: 0.5em; vertical-align: super; font-weight: 600; margin-left: 1px; opacity: 0.75; }
.nav__links { display: flex; gap: var(--space-5); justify-content: center; }
.nav__link {
  display: inline-flex; align-items: center;
  min-height: 48px; min-width: 48px; justify-content: center;
  padding-inline: var(--space-2);   /* the 48px law is width AND height, not the 44px floor */
  font-size: var(--ms-0); font-weight: 500; text-decoration: none; color: var(--ink-muted);
  transition: color var(--dur-micro) var(--ease-standard);
}
.nav__link:hover { color: var(--ink); }
.nav__link.is-here { color: var(--ink); font-weight: 600; }
.nav__order { justify-self: end; padding-inline: var(--space-5); }

@media (max-width: 56rem) {
  .nav__in { grid-template-columns: 1fr auto; row-gap: var(--space-2); min-height: 0; }
  .nav__links { grid-column: 1 / -1; order: 3; gap: var(--space-4); flex-wrap: wrap; padding-bottom: var(--space-1); }
  .nav__wordmark { display: none; }         /* the mark alone carries it on a phone */
  .nav__order { padding-inline: var(--space-4); }
  /* The links take a second row here, so the bar grows and everything measuring the
     opening has to know: the mark (48) + the row gap (8) + the links (48) + the row's
     own padding (4) + the bar's padding-block (8 + 8). Same derivation as the desktop
     token above; the hero's doorway and the tray's stuck top both read it. */
  :root {
    --nav-h: calc(48px + var(--space-2) + 48px + var(--space-1) + var(--space-2) * 2 + env(safe-area-inset-top));
  }
}

/* ============================================================================
   THE KEYSTONE: the Today Line, spoken into the plate over the door.
   Bunkers wears the pill; this build gets a different vessel for the same law.
   ========================================================================= */
.keystone {
  display: inline-flex; align-items: center; gap: var(--space-3);
  margin-inline: auto; margin-bottom: var(--space-6);
  padding: var(--space-3) var(--space-6);
  font-family: var(--text); font-size: var(--ms-dn1); font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink); background: color-mix(in srgb, var(--brass) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--brass) 55%, transparent);
  max-width: min(100%, 34rem);
  /* the keystone is a plate, so it takes the arch's own crown, not a pill */
  clip-path: polygon(0 22%, 6% 0, 94% 0, 100% 22%, 100% 100%, 0 100%);
}
.keystone::before {
  content: ''; width: 7px; height: 7px; rotate: 45deg; flex: none;
  background: var(--accent);
}
.keystone--closed::before { background: var(--ink-muted); }
.keystone--flat { margin-bottom: var(--space-5); }
.keystone__wait { opacity: 0.55; }
.section--deep .keystone { color: var(--ink-on-deep); }

/* ============================================================================
   THE DOOR: the hero. You open ON their table.
   The doorway is the FIRST viewport, not the fourth thing in it. The frame is
   sized from the real opening (the viewport minus the bar above it) so the
   whole aperture is in frame and the words land inside it: a lintel on a wide
   screen, an arch on a phone, because a door is portrait and a pass-through is
   landscape. Both clips are pre-compensated per aspect by tools/apertures.mjs,
   so the shape and the aspect-ratio ALWAYS swap together or the arch distorts.
   ========================================================================= */
.door { padding-bottom: var(--space-8); }
.door__doorway { position: relative; display: grid; justify-items: center; }
/* width-led, so aspect-ratio derives the height and the box can never overflow the
   opening. --door-gap keeps the frame off the fold's edge. */
.door__frame {
  width: min(100%, calc((100svh - var(--nav-h) - var(--door-gap)) * 3 / 2));
  margin-top: var(--space-5);
}
/* THE WORDS INSIDE THE DOORWAY. They sit on the frame's own scrim, low, where the
   photograph is already dark: their table is the ground the claim is made on. The plate
   carrying the Today Line seats UNDER the headline, never above it: a small caps label
   over an H1 is an eyebrow, and the eyebrow is a law. */
.door__in {
  position: absolute;
  /* space-6, not space-8: the 32px seats the whole block inside the scrim's deep band,
     which is what takes the measured worst case from 2.31:1 to 4.03:1 (see .frame__scrim).
     This number is doing contrast work, not spacing work. */
  bottom: var(--space-6);
  left: 0; right: 0;
  z-index: 2;
  display: grid; justify-items: center; gap: var(--space-5);
  pointer-events: none;            /* the words are painted over the photo; nothing here is a control */
}
.door__in > * { pointer-events: auto; }
.door__in .display { color: var(--ink-on-deep); text-shadow: 0 1px 24px color-mix(in srgb, var(--ic-900) 55%, transparent); }
.door__in .keystone {
  box-shadow: var(--e3);
  background: color-mix(in srgb, var(--ic-900) 72%, transparent);
  border-color: color-mix(in srgb, var(--brass) 80%, transparent);
  color: var(--ic-50);
  margin-bottom: 0;
}
.door__under {
  display: grid; justify-items: center; gap: var(--space-5);
  margin-top: var(--space-7);
}

/* THE PHONE: the arch. The words drop below it onto the parchment rather than crowding a
   477px frame, so the first viewport is still their table and the claim starts under it.
   One element repositioned, never a second element. */
@media (max-width: 60rem) {
  .door__frame {
    aspect-ratio: 3 / 4;
    clip-path: url(#g28-arch);
    width: min(100%, calc((100svh - var(--nav-h) - var(--door-gap)) * 3 / 4));
  }
  .door__in {
    position: static;
    margin-top: var(--space-6);
  }
  /* THE SCRIM GOES WITH THE WORDS. It exists to be the headline's ground and it is 86% ink
     deep because that is what the measurement demanded; here the words have dropped out of
     the frame onto the parchment, so all it can do is dim their food. It is the phone's
     main stage: an aperture cut into the page IS the treatment, and nothing is left inside
     it that needs holding. Deletion test, applied per breakpoint. */
  .door__frame .frame__scrim { display: none; }
  .door__in .display { color: var(--ink); text-shadow: none; }
  .door__in .keystone {
    background: color-mix(in srgb, var(--brass) 18%, transparent);
    border-color: color-mix(in srgb, var(--brass) 55%, transparent);
    color: var(--ink);
    box-shadow: none;
  }
}

.head { padding-block: var(--space-9) var(--space-7); }
.head--tight { padding-block: var(--space-8) var(--space-5); }
.head .shell { display: grid; justify-items: center; gap: var(--space-5); }
.head__f { width: min(100%, 56rem); margin-top: var(--space-5); }
.four__f { width: min(22rem, 60vw); margin-top: var(--space-6); }

/* ============================================================================
   THE THALI ATLAS: the signature. A pinned, scrubbed sequence: their real
   katoris fill a gold thali one region at a time.
   ========================================================================= */
.atlas { padding: 0; }
.atlas__pin {
  min-height: 100svh;
  display: grid; place-items: center;
  padding-block: var(--space-9);
  /* the gold tray: light travelling over brass */
  background:
    radial-gradient(120% 80% at 50% 40%, color-mix(in srgb, var(--gold) 26%, transparent), transparent 70%),
    var(--surface-deep);
}
.atlas__h { color: var(--ink-on-deep); margin-bottom: var(--space-8); }
.thali {
  display: flex; flex-wrap: wrap; gap: var(--gutter);
  justify-content: center;      /* the short-row law: a final row centres under the row above */
  align-items: flex-start;
  max-width: 64rem; margin-inline: auto;
}
.katori { width: min(15rem, 42vw); display: grid; justify-items: center; gap: var(--space-2); }
.js .katori { opacity: 0; transform: translateY(-16px) scale(0.94); }
.js .katori.is-set {
  opacity: 1; transform: translateY(0) scale(1);
  transition: opacity var(--dur-section) var(--ease-expressive),
              transform var(--dur-section) var(--ease-expressive);
}
.katori__f { width: 100%; margin-bottom: var(--space-3); }
.katori__region {
  font-family: var(--text); font-size: var(--ms-dn1); font-weight: 400;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--brass);
}
.katori__dish {
  font-family: var(--display); font-variation-settings: 'opsz' 48, 'SOFT' 14, 'WONK' 1;
  font-weight: 900; font-size: var(--ms-1); line-height: 1.1; color: var(--ink-on-deep);
}
.katori__price { font-size: var(--ms-0); font-weight: 600; color: var(--brass); }
.katori__note { color: var(--ink-on-deep-muted); font-size: var(--ms-dn1); max-width: 24ch; }
.atlas__resolve {
  margin-top: var(--space-9);
  font-family: var(--display); font-variation-settings: 'opsz' 72, 'SOFT' 20, 'WONK' 1;
  font-weight: 900; font-size: clamp(var(--ms-3), 4.4vw, var(--ms-5));
  line-height: 1.1; letter-spacing: -0.02em; color: var(--ink-on-deep);
  max-width: 20ch; text-wrap: balance;
}
.atlas__addr { display: block; color: var(--brass); }
.js .atlas__resolve { opacity: 0; transform: translateY(-16px); }
.js .atlas__resolve.is-set {
  opacity: 1; transform: translateY(0);
  transition: opacity var(--dur-cinematic) var(--ease-expressive),
              transform var(--dur-cinematic) var(--ease-expressive);
}
@media (max-width: 48rem) {
  .katori { width: min(14rem, 62vw); }
  .atlas__pin { min-height: auto; padding-block: var(--space-8); }
}

/* ============================================================================
   THE PROOF: an editorial running list of real diners. Not a card wall, not a
   stat banner. The quotes stagger down the service line.
   ========================================================================= */
.proof .subhead { margin-bottom: var(--space-8); }
.proof .subhead strong { font-weight: 700; color: var(--ink); }
.quotes { display: grid; gap: var(--space-8); max-width: 44rem; margin-inline: auto; }
.quote { display: grid; gap: var(--space-3); }
/* THE EVIDENCED QUOTE: the photograph of the thing the diner named, standing directly
   above their sentence. Tracey says the gold dishes and there is the brass katori on the
   gold tray; Kacey says the place settings and there is the gold laid on every table.
   Centred above, never beside: the centred law holds, and the running list keeps its
   rhythm with two of its six carrying their own proof. */
.quote--proved { gap: var(--space-5); justify-items: center; }
.quote__f { width: min(18rem, 54vw); margin-bottom: var(--space-2); }
.quote blockquote p {
  font-family: var(--display);
  font-variation-settings: 'opsz' 44, 'SOFT' 30, 'WONK' 0;
  font-weight: 300; font-size: var(--ms-2); line-height: 1.35; letter-spacing: -0.01em;
  max-width: 32ch; margin-inline: auto; text-wrap: balance;
}
.quote blockquote p::before { content: '\201C'; }
.quote blockquote p::after  { content: '\201D'; }
.quote figcaption { font-size: var(--ms-dn1); font-weight: 600; letter-spacing: 0.04em; }
.quote__meta { display: block; font-weight: 400; color: var(--ink-muted); letter-spacing: 0.06em; }

/* ============================================================================
   THE ROOM: a 7+5 split with the jali as the counterweight.
   ========================================================================= */
.room__in {
  display: grid; gap: var(--space-8); align-items: center;
  grid-template-columns: 1fr;
  justify-items: center;
}
.room__say { display: grid; gap: var(--space-5); justify-items: center; }
.room__jali { width: min(26rem, 90%); }
@media (min-width: 60rem) {
  .room__in { grid-template-columns: 7fr 5fr; text-align: center; }
  .room__jali { width: 100%; }
}

/* ============================================================================
   THE PATHS: three ways in, as a running list, not tiles.
   ========================================================================= */
.paths__set { display: grid; gap: 0; max-width: 46rem; margin: var(--space-8) auto 0; }
.path {
  display: grid; gap: var(--space-1); padding-block: var(--space-6);
  text-decoration: none; border-top: 1px solid var(--line-on-deep);
  transition: background-color var(--dur-element) var(--ease-standard);
}
.path:last-child { border-bottom: 1px solid var(--line-on-deep); }
.path:hover { background: color-mix(in srgb, var(--ink-on-deep) 6%, transparent); }
.path__k {
  font-family: var(--display); font-variation-settings: 'opsz' 56, 'SOFT' 14, 'WONK' 1;
  font-weight: 900; font-size: var(--ms-3); line-height: 1.1; color: var(--ink-on-deep);
}
.path__d { font-size: var(--ms-0); color: var(--ink-on-deep-muted); }

/* ============================================================================
   THE TRAY: the chapter index, stuck to the top of the book.
   Not a pill bar. A thali IS a tray with katoris set around it, this site's
   signature is a thali being laid, and the home page teaches what a katori
   means before you ever get here, so the index can be the object itself: ten
   settings on brass, each a real photograph of that chapter's food through the
   katori aperture. Three chapters have no photograph, so their vessel is an
   empty brass bowl, which is what a tray looks like before that course is
   served. Honest, and it reads as intent because it IS the object.
   Two states, one element: a laid tray at the head of the book, a rim once it
   reaches the bar.
   ========================================================================= */
.tray {
  position: sticky;
  top: var(--nav-h);          /* the same token the hero sizes its doorway from */
  z-index: 4;                 /* under the nav (200), over the book */
}
.tray__in {
  height: var(--tray-h);      /* the token IS the box; scroll-margin reads the same one */
  display: grid; align-items: center;
  /* the same light travelling over brass as the atlas ground, right way up */
  background: linear-gradient(to bottom,
    color-mix(in srgb, var(--brass) 34%, var(--ic-100)),
    color-mix(in srgb, var(--brass) 14%, var(--ic-50)));
  border-block: 1px solid color-mix(in srgb, var(--gold) 42%, transparent);
  box-shadow: var(--e1);
}
/* ONE LANE, always. Ten settings never fit across any real screen, so the tray scrolls
   sideways inside itself rather than wrapping into a bar that eats the page. */
.tray__set {
  display: flex; flex-wrap: nowrap;
  justify-content: safe center;
  align-items: center;
  gap: var(--space-4);
  height: 100%;
  /* the slack the mask below eats into. Without it the fade would land on a real label at
     any width where the settings happen to fill the lane exactly, and scrolled to the
     start the first setting would sit half-dissolved. */
  padding-inline: var(--space-7);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.tray__set::-webkit-scrollbar { display: none; }
.tray__i { display: flex; flex: none; }
/* The vessel over its name, not beside it. Side by side needed 1,728px to lay ten
   settings and the lane has 1,229 on a 1280 desktop, so three chapters hid behind a
   sideways scroll on a full-size screen: an index you have to search is not an index.
   Stacked, all ten fit across the desktop and the phone still scrolls its lane. */
.setting {
  display: grid; justify-items: center; align-content: center;
  gap: var(--space-1);
  padding-inline: var(--space-2);
  min-width: 3rem;            /* with the label's own width, always past the 48px touch floor */
  text-decoration: none;
  color: var(--ink-muted);
  white-space: nowrap;
  transition: color var(--dur-micro) var(--ease-standard);
}
.setting:hover, .setting:focus-visible { color: var(--ink); }
.setting__f, .setting__empty {
  width: 2.25rem;
  aspect-ratio: 1;
  flex: none;
  transition: transform var(--dur-element) var(--ease-standard),
              box-shadow var(--dur-element) var(--ease-standard);
}
/* THE EMPTY VESSEL: the same brass rim the katori aperture draws, around nothing.
   Eggs, South Indian and Desserts have no photograph of their own food, and a chapter
   face is a photograph of THAT chapter's food or it does not exist. */
.setting__empty {
  border-radius: var(--r-full);
  background: radial-gradient(120% 120% at 50% 22%,
    color-mix(in srgb, var(--brass) 52%, transparent),
    color-mix(in srgb, var(--gold) 34%, transparent));
  border: 2px solid color-mix(in srgb, var(--brass) 70%, transparent);
  box-shadow: inset 0 0 0 6px color-mix(in srgb, var(--ic-900) 10%, transparent);
}
.setting__n {
  font-family: var(--text);
  font-size: var(--ms-dn1); font-weight: 600;
  letter-spacing: 0.02em; line-height: 1.1;
}
.setting:hover .setting__f, .setting:hover .setting__empty,
.setting:focus-visible .setting__f, .setting:focus-visible .setting__empty {
  transform: translateY(-2px);
  box-shadow: var(--e2);
}
/* THE CHAPTER YOU ARE IN. The tray does not shout; the setting you are standing at
   simply comes up to full ink while the rest stay quiet. One focal point. */
.setting.is-here { color: var(--ink); }
.setting.is-here .setting__f, .setting.is-here .setting__empty {
  box-shadow: 0 0 0 2px var(--gold), var(--e2);
}

/* THE LANE'S EDGES: on a phone the lane runs off both sides, and a label guillotined
   mid-word ("Veg|etarian") reads as a broken layout rather than as "there is more this
   way". The settings are MASKED out at the ends, never covered by a painted gradient: the
   bar's ground is a brass gradient, so no flat colour could ever match it, and a mask lets
   the real brass show through while the content dissolves into it. Masks do not take
   pointer events either, so it cannot eat a setting. */
.tray__set {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--space-7),
                      #000 calc(100% - var(--space-7)), transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 var(--space-7),
              #000 calc(100% - var(--space-7)), transparent 100%);
}

@media (max-width: 48rem) {
  .tray__set { gap: var(--space-3); }
  .setting { padding-inline: var(--space-1); }
}

/* ============================================================================
   THE LEDGER: the menu. A menu is a true ledger, so it earns ledger rows.
   ========================================================================= */
.book { padding-bottom: var(--space-9); }
/* the anchor clears the bar AND the tray it sticks under, or a jump lands with the
   chapter's own name hidden behind the thing you just tapped. Both terms are the same
   tokens those two boxes are SET from, so the landing cannot drift from the layout. */
.chap { padding-block: var(--space-8); scroll-margin-top: calc(var(--nav-h) + var(--tray-h)); }
/* THE CHAPTER FACE: the chapter opens on a photograph of its own food, through the
   aperture the manifest authored for it. The proof the dish is real, which is the whole
   argument against a rival menu listing food they do not serve. */
.chap__head { display: grid; justify-items: center; gap: var(--space-5); margin-bottom: var(--space-7); }
.chap__f { width: min(26rem, 62vw); }
.chap__say { display: grid; justify-items: center; gap: var(--space-3); }
.chap__h { margin-bottom: 0; }
.chap__note { margin-bottom: 0; }
.ledger { max-width: 46rem; margin-inline: auto; }
.row {
  display: grid; grid-template-columns: 1fr auto; gap: var(--space-4);
  align-items: baseline; text-align: left;
  padding-block: var(--space-5);
  border-top: 1px solid var(--line);
}
.row:last-child { border-bottom: 1px solid var(--line); }
.row__main { min-width: 0; }            /* the mobile trap: a text child in a grid track */
.row__n {
  font-family: var(--display); font-variation-settings: 'opsz' 36, 'SOFT' 12, 'WONK' 0;
  font-weight: 700; font-size: var(--ms-1); line-height: 1.2; letter-spacing: -0.01em;
  margin-inline: 0; max-width: none;
}
.row__r {
  font-size: var(--ms-dn1); font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--gold); margin: var(--space-1) 0 0; margin-inline: 0; max-width: none;
}
.row__d { font-size: var(--ms-0); color: var(--ink-muted); margin: var(--space-2) 0 0; margin-inline: 0; max-width: 52ch; }
.row__p { font-size: var(--ms-1); font-weight: 600; margin: 0; white-space: nowrap; }
.veg {
  display: inline-block; width: 9px; height: 9px; margin-left: var(--space-2);
  border: 1.5px solid var(--gold); border-radius: var(--r-full);
  background: color-mix(in srgb, var(--gold) 40%, transparent);
  vertical-align: middle;
}

/* ============================================================================
   THE DIPTYCH + THE ARTIFACT PANEL
   ========================================================================= */
.diptych__pair {
  display: flex; gap: var(--gutter); justify-content: center; flex-wrap: wrap;
  margin: var(--space-7) auto var(--space-7);
  width: 100%;            /* same collapsed-container trap as .head__pair */
}
.diptych__pair > * { width: min(24rem, 42%); }
@media (max-width: 48rem) { .diptych__pair > * { width: min(20rem, 84%); } }

.artifact__in { display: grid; gap: var(--space-8); justify-items: center; align-items: center; }
.artifact__plate { width: min(22rem, 70%); }
.artifact__say { display: grid; gap: var(--space-4); justify-items: center; }
@media (min-width: 60rem) {
  .artifact__in { grid-template-columns: 5fr 7fr; }
  .artifact__plate { width: 100%; }
}
/* the artifact ships unframed and on purpose: it is the real object, not a view */
.plate { margin-inline: auto; box-shadow: var(--e3); }
.plate picture { display: block; }
.plate img { width: 100%; height: auto; display: block; }

/* ============================================================================
   THE FACTS: a spec list. True peers, one line each.
   ========================================================================= */
.facts { display: grid; gap: 0; max-width: 46rem; margin: var(--space-7) auto 0; }
.fact { padding-block: var(--space-5); border-top: 1px solid var(--line); }
.section--deep .fact { border-color: var(--line-on-deep); }
.fact:last-child { border-bottom: 1px solid var(--line); }
.section--deep .fact:last-child { border-bottom-color: var(--line-on-deep); }
.fact dt {
  font-family: var(--display); font-variation-settings: 'opsz' 36, 'SOFT' 12, 'WONK' 0;
  font-weight: 700; font-size: var(--ms-1); line-height: 1.25;
}
.fact dd { margin: var(--space-2) 0 0; color: var(--ink-muted); max-width: 52ch; margin-inline: auto; }
.section--deep .fact dd { color: var(--ink-on-deep-muted); }

/* ============================================================================
   HOURS
   ========================================================================= */
.hours { margin-inline: auto; border-collapse: collapse; }
.hours caption { font-size: var(--ms-dn1); color: var(--ink-muted); padding-bottom: var(--space-3); }
.hours th, .hours td { padding: var(--space-2) var(--space-4); text-align: center; font-weight: 400; }
.hours th { font-weight: 600; }
.hours__closed, .hours .is-closed td { color: var(--accent); font-weight: 600; }
.section--deep .hours__closed { color: var(--brass); }
.hours--full { margin-block: var(--space-5); }
.hours--full tr { border-top: 1px solid var(--line); }
.hours--full tr:last-child { border-bottom: 1px solid var(--line); }
.hours--full .is-closed th, .hours--full .is-closed td { color: var(--accent); font-weight: 700; }
.arrive .caption { margin-top: var(--space-4); }

/* ============================================================================
   THE FORM: every state designed (the state law + the Turnstile law's rider)
   ========================================================================= */
.contact form { max-width: 32rem; margin: var(--space-7) auto 0; display: grid; gap: var(--space-5); }
.contact label { display: grid; gap: var(--space-2); text-align: center; }
.contact input, .contact textarea {
  width: 100%; min-height: 48px; padding: 12px var(--space-4);
  border: 1px solid var(--line-strong); background: var(--surface-raised);
  color: var(--ink); font: inherit; text-align: center;
  border-radius: var(--r-xs);
}
.contact textarea { resize: vertical; }
/* THE TURNSTILE LAW: invisible, always. Zero footprint, never a visible widget. */
.cf-turnstile { width: 0; height: 0; overflow: hidden; }
.contact .send { justify-self: center; }
.form-note { min-height: 1.5em; }
.form-fallback { max-width: 40ch; margin-inline: auto; color: var(--ink); }
.form-fallback a { font-weight: 700; }

.legal { display: grid; gap: var(--space-5); max-width: 42rem; margin-inline: auto; }

/* ============================================================================
   THE FOOTER
   ========================================================================= */
.foot { padding-block: var(--space-9) var(--space-7); margin-top: var(--space-9); }
.foot .keystone { margin-bottom: var(--space-8); }
.foot__grid {
  display: grid; gap: var(--space-8); justify-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) { .foot__grid { grid-template-columns: repeat(3, 1fr); } }
.foot__col { display: grid; gap: var(--space-3); justify-items: center; }
.foot__h {
  font-family: var(--display); font-variation-settings: 'opsz' 28, 'SOFT' 10, 'WONK' 0;
  font-weight: 700; font-size: var(--ms-0); letter-spacing: 0.02em; color: var(--brass);
  max-width: none;
}
.foot__addr { font-style: normal; }
.foot__addr a, .foot__eml a, .foot__tel { color: var(--ink-on-deep); text-decoration: none; }
.foot__addr a:hover, .foot__eml a:hover, .foot__tel:hover { text-decoration: underline; }
.foot__tel { font-size: var(--ms-1); font-weight: 600; display: inline-block; min-height: 48px; line-height: 48px; }
.foot .hours th, .foot .hours td { color: var(--ink-on-deep); }
.foot .hours th { color: var(--ink-on-deep-muted); }
.social { display: flex; gap: var(--space-4); justify-content: center; padding: 0; }
.social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 48px; height: 48px; color: var(--ink-on-deep-muted);
  transition: color var(--dur-micro) var(--ease-standard);
}
.social a:hover { color: var(--brass); }
.social svg { width: 22px; height: 22px; fill: currentColor; }
#installBtn { margin: var(--space-8) auto 0; }
.foot__quiet {
  display: block; margin: var(--space-4) auto 0; min-height: 48px; padding: 12px var(--space-5);
  border: 0; background: transparent; color: var(--ink-on-deep-muted); font: inherit;
  font-size: var(--ms-dn1); cursor: pointer;
}
.foot__quiet:hover { color: var(--ink-on-deep); }
.foot__base {
  margin-top: var(--space-8); padding-top: var(--space-6);
  border-top: 1px solid var(--line-on-deep);
  display: grid; gap: var(--space-2);
  font-size: var(--ms-dn1); color: var(--ink-on-deep-muted);
}
.foot__g28 a { color: var(--ink-on-deep); font-weight: 700; text-decoration: none; }
.foot__g28 a:hover { color: var(--brass); }

/* ============================================================================
   THE ALERT MOMENT (the notification law, rule 3): a tapped push LANDS on its
   message, spoken large. Entrance is pure CSS so a dead script cannot strand it.
   ========================================================================= */
#alertMoment {
  position: fixed; inset: 0; z-index: 300; display: none;
  flex-direction: column; align-items: center; justify-content: center; text-align: center;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(14px);
  padding: calc(var(--space-5) + env(safe-area-inset-top)) var(--edge)
           calc(var(--space-5) + env(safe-area-inset-bottom));
}
#alertMoment.on { display: flex; }
#alertMoment.on > * { opacity: 0; transform: translateY(-16px); animation: settle 550ms var(--ease-expressive) forwards; }
#alertMoment.on > *:nth-child(2) { animation-delay: 80ms; }
#alertMoment.on > *:nth-child(3) { animation-delay: 160ms; }
#alertMoment.on > *:nth-child(4) { animation-delay: 240ms; }
@keyframes settle { to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { #alertMoment.on > * { animation: none; opacity: 1; transform: none; } }
#alertMoment .am-mark { height: 64px; width: 64px; margin-bottom: var(--space-5); }
#alertMoment .am-title {
  font-family: var(--display); font-variation-settings: 'opsz' 72, 'SOFT' 18, 'WONK' 1;
  font-weight: 900; font-size: clamp(var(--ms-3), 6vw, var(--ms-5)); line-height: 1.05;
  letter-spacing: -0.02em; max-width: 16ch; margin-inline: auto; text-wrap: balance;
}
#alertMoment .am-body { margin-top: var(--space-4); color: var(--ink-muted); max-width: var(--measure); margin-inline: auto; font-size: var(--ms-1); }
#alertMoment .am-acts { margin-top: var(--space-6); display: flex; gap: var(--space-4); flex-wrap: wrap; justify-content: center; }
#alertMoment .am-go, #alertMoment .am-close {
  display: inline-flex; align-items: center; justify-content: center; min-height: 48px;
  padding: 14px var(--space-6); font-weight: 600; text-decoration: none; cursor: pointer; font: inherit;
}
#alertMoment .am-go { background: var(--accent); color: var(--accent-ink); border: 0; font-weight: 600; }
#alertMoment .am-close { border: 1px solid var(--line-strong); background: transparent; color: var(--ink); }

/* ============================================================================
   THE FIRST-TASTE LIST: the owned audience. One field, asked AFTER the whole
   book is given away. Never a popup, never a gate.
   ========================================================================= */
.capture { max-width: 34rem; margin: var(--space-7) auto 0; display: grid; gap: var(--space-4); }
.capture__row { display: flex; gap: var(--space-3); flex-wrap: wrap; justify-content: center; }
.capture__row input {
  flex: 1 1 16rem; min-width: 0;      /* the mobile trap: a text child in a flex track */
  min-height: 48px; padding: 12px var(--space-4);
  border: 1px solid var(--line-strong); background: var(--surface-raised);
  color: var(--ink); font: inherit; text-align: center; border-radius: var(--r-xs);
}
.capture__row .btn { flex: 0 0 auto; }
.capture__consent {
  display: flex; gap: var(--space-3); align-items: flex-start; justify-content: center;
  max-width: 34ch; margin-inline: auto; text-align: left;
  font-size: var(--ms-dn1); color: var(--ink-muted); line-height: 1.4; cursor: pointer;
}
/* A native 20px checkbox is both off-system and under the target law, and "the label is
   the tap area" is not an answer the wall accepts, nor a thumb. Drawn in the build's own
   tokens instead: a 44px target (the absolute floor for a secondary control) carrying a
   22px visual, so the box a finger hits is twice the box an eye sees. */
.capture__consent input {
  appearance: none; -webkit-appearance: none;
  flex: none; position: relative; cursor: pointer;
  width: 44px; height: 44px; margin: -11px 0 -11px -11px;   /* the target grows, the row does not */
  border: 0; background: transparent;
}
.capture__consent input::before {
  content: ''; position: absolute; inset: 11px;
  border: 2px solid var(--line-strong); border-radius: var(--r-xs);
  background: var(--surface-raised);
  transition: background-color var(--dur-micro) var(--ease-standard),
              border-color var(--dur-micro) var(--ease-standard);
}
.capture__consent input:checked::before { background: var(--accent); border-color: var(--accent); }
.capture__consent input:checked::after {
  content: ''; position: absolute; left: 17px; top: 19px; width: 9px; height: 5px;
  border-left: 2px solid var(--accent-ink); border-bottom: 2px solid var(--accent-ink);
  rotate: -45deg;
}
.capture__consent input:focus-visible { outline: 2px solid var(--focus); outline-offset: -8px; border-radius: var(--r-sm); }
@media (max-width: 30rem) { .capture__row .btn { width: 100%; } }
