/* ========== tokens.css ========== */
/* ==========================================================================
   VINTAGE ADDA — DESIGN TOKENS
   Every value here is taken directly from the Brand & Digital Foundation.
   Change a token, and it changes everywhere. Do not hard-code colours,
   sizes or spacing anywhere else in the stylesheet.
   ========================================================================== */

:root {
  /* --- Colour · the fixed tonal ladder -----------------------------------
     Seven steps generated on one hue (110°) at a constant 27% saturation.
     Any green in any Vintage Adda asset must be one of these. No eyeballed
     shades, no gradients between them. */
  --green-900: #192716; /* Late Green — backgrounds, night frames */
  --green-800: #1f3019;
  --green-700: #2a411f;
  --green-600: #365427;
  --green-500: #42673b; /* Adda Green — the primary */
  --green-400: #5c8f52;
  --green-300: #9cc294;
  --green-100: #dbe9d8; /* Verandah — tints, dividers, body copy on green */

  /* --- Colour · accent and neutrals -------------------------------------- */
  --gold: #fac148; /* Marquee Gold — highlight, never a field */
  --gold-deep: #8a5f03; /* Brass Deep — the accessible gold on light grounds */
  --parchment: #f6f0e2; /* primary light ground */
  --stone: #a89f8a; /* secondary text, rules, captions */
  --ink: #171512; /* text on gold, letterboards. Never pure black. */

  /* --- Semantic roles ----------------------------------------------------
     Approved pairings only (see the contrast table in the brand document):
       gold on Late Green  9.52:1   body + heads
       gold on Ink        11.10:1   body + heads
       Verandah on Adda    5.15:1   body + heads
       Adda on Parchment   5.71:1   body + heads
       gold on Adda Green  3.95:1   HEADS ONLY, 24px and above
       gold on white       1.64:1   never
       Adda Green on Ink   2.81:1   never */
  --bg-night: var(--green-900);
  --bg-green: var(--green-500);
  --bg-light: var(--parchment);
  --text-on-dark: #f2efe4;
  --text-on-green: var(--green-100);
  --text-on-light: #2c2a24;
  /* 0.66 was 6.7:1 on Late Green and sat behind the headings rather than
     under them. 0.78 is 8.6:1 and still clearly secondary. */
  --text-muted-dark: rgba(242, 239, 228, 0.78);
  /* Warmer and a good deal stronger than the #6b6558 that was here. That
     grey was a cool neutral on a warm ground, which is what made long
     paragraphs read as washed out next to the brass and the paper, and at
     5.1:1 it was only just clearing AA for the body sizes it is used at.
     This is the same hue family as the parchment itself, two-and-a-bit
     ratio points darker: 7.2:1. */
  --text-muted-light: #574e42;
  --accent: var(--gold);
  --accent-on-light: var(--gold-deep);
  /* The one stop of dim laid over a papered green wall, so the value lives
     with the palette rather than inside the ground rule. See .section--green
     in ornament.css for why it is a tint and not an eighth step on the ladder.
     Verandah on the dimmed wall measures 5.94:1, up from 5.15:1. */
  --green-dim: rgba(0, 0, 0, 0.36);
  --rule-dark: rgba(219, 233, 216, 0.16);
  --rule-light: rgba(23, 21, 18, 0.14);

  /* --- Type · three faces, one family each ------------------------------- */
  --font-display: "Fraunces", Georgia, "Times New Roman", serif;
  --font-text: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-kannada: "Anek Kannada", "Noto Sans Kannada", "Nirmala UI", sans-serif;
  /* The studio's mark, for the footer credit and nothing else. Subset to
     capitals only, so anything set in it must be uppercase. */
  --font-studio: "Josefin Sans", "Futura", "Century Gothic", var(--font-text);

  /* The scale is fixed. Every step is roughly 1.5x the one below it.
     Do not invent intermediate sizes — pick the nearest step.
     Fluid clamps keep the same relationships down to a 360px phone. */
  --fs-h1: clamp(2.5rem, 1.55rem + 4.2vw, 4rem); /*  40 → 64 */
  --fs-h2: clamp(1.75rem, 1.3rem + 2vw, 2.5rem); /*  28 → 40 */
  --fs-h3: 0.8125rem; /*  13, Archivo caps */
  --fs-body: 1rem; /*  16 */
  --fs-body-lg: clamp(1rem, 0.95rem + 0.25vw, 1.125rem); /*  16 → 18 */
  --fs-small: 0.84375rem; /*  13.5 — the floor */
  --fs-label: 0.6875rem; /*  11, Archivo caps */
  --fs-menu: 1.125rem; /*  18, Fraunces */
  --fs-price: 1rem; /*  16, tabular */

  --lh-h1: 1.02;
  --tr-h1: -0.025em;
  --lh-h2: 1.1;
  --tr-h2: -0.02em;
  --lh-h3: 1.3;
  --tr-h3: 0.12em;
  --lh-body: 1.6;
  --tr-body: 0;
  --lh-small: 1.55;
  --tr-small: 0.005em;
  --lh-label: 1.2;
  --tr-label: 0.18em;
  --lh-menu: 1.35;
  --tr-menu: -0.005em;
  --lh-marquee: 0.98;
  --tr-marquee: 0.04em;

  /* --- Space · 8pt scale. Nothing at 13px. ------------------------------- */
  --s-1: 0.5rem; /*   8 */
  /* A half-step. The scale jumped 8 to 16 with nothing between, which is a
     wide gap at the label tier — an eyebrow set 16px off its own heading
     reads as floating between that heading and whatever is above it, rather
     than as a label belonging to it. */
  --s-1h: 0.75rem; /*  12 */
  --s-2: 1rem; /*  16 */
  --s-3: 1.5rem; /*  24 */
  --s-4: 2.5rem; /*  40 */
  --s-5: 4rem; /*  64 */
  --s-6: 6.5rem; /* 104 */
  --section-y: clamp(var(--s-4), 3rem + 4vw, var(--s-6));

  /* --- Layout · 12 columns, margins never below 6% of the frame ---------- */
  --measure: 68ch;
  /* 78rem left about 330px of dead margin on either side of a 1900px screen,
     which is not restraint, it is waste — the grids inside were being squeezed
     while the room around them did nothing. 88rem still holds every measure
     that matters: prose is capped separately (34rem, 46ch, 70ch), so widening
     this only feeds the things that were actually short of room — the menu
     columns, the week's cards, the footer. */
  --container: 88rem; /* 1408px */
  --container-narrow: 52rem;
  /* And a little off the gutter at the top end, for the same reason. */
  --gutter: clamp(1.25rem, 5vw, 2.75rem);

  /* --- Surfaces ----------------------------------------------------------
     Photography carries no white border, no rounded corner and no drop
     shadow — a brand rule, and the reason --radius-photo is 0. Panels and
     chips may be rounded; images may not. */
  --radius-photo: 0px;
  --radius-panel: 4px;
  --radius-chip: 999px;
  --keyline: 1px solid rgba(250, 193, 72, 0.55);

  /* --- Elevation ---------------------------------------------------------
     Panels lie ON their wall rather than being printed into it. Each lift is
     three parts, and all three are needed on grounds this dark: a hairline of
     light along the top edge, where a raised edge would catch the room; a
     tight contact shadow that says the panel is only millimetres off the
     wall; and a wide, soft one that gives it somewhere to be. Drop the first
     and a panel on Late Green reads as a hole rather than a card, because a
     black shadow on a near-black ground carries almost no signal.

     Note this is a considered departure from the brand's "no drop shadow on
     photography" rule, made at the client's request. It is kept honest by
     never shadowing a bare photograph: what is lifted is always a panel or a
     frame, and the picture inside it still has no border, no radius and no
     shadow of its own. */
  --lift-1:
    0 1px 0 rgba(242, 239, 228, 0.055) inset, 0 1px 2px rgba(0, 0, 0, 0.3),
    0 8px 20px -12px rgba(0, 0, 0, 0.55);
  --lift-2:
    0 1px 0 rgba(242, 239, 228, 0.07) inset, 0 2px 4px rgba(0, 0, 0, 0.32),
    0 18px 40px -20px rgba(0, 0, 0, 0.66);
  /* The same idea on parchment, where the shadow has to be the ink's warm
     near-black rather than pure black or the paper turns grey under it. */
  --lift-light:
    0 1px 2px rgba(44, 42, 36, 0.14), 0 12px 28px -16px rgba(44, 42, 36, 0.3);

  /* --- Motion · unhurried by policy -------------------------------------- */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  /* Sine-like, symmetrical, no hard edge at either end. A filament warming and
     cooling has no corner in its curve, and --ease (which decelerates hard)
     gave the bulbs a visible "arrival" that read as a blink. */
  --ease-bulb: cubic-bezier(0.45, 0, 0.55, 1);
  --dur-fast: 180ms;
  --dur: 420ms;
  --dur-slow: 900ms;

  --header-h: 4.5rem;
}

@media (min-width: 60rem) {
  :root {
    --header-h: 5.5rem;
  }
}

/* Motion is a garnish, never the message. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ========== base.css ========== */
/* ==========================================================================
   BASE — reset, typography primitives, layout helpers, motion
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + var(--s-2));
}

body {
  margin: 0;
  background: var(--bg-night);
  color: var(--text-on-dark);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

body.is-locked { overflow: hidden; }

img, picture, video, svg { max-width: 100%; display: block; }
img { height: auto; border-radius: var(--radius-photo); }

/* --- Headings ------------------------------------------------------------
   Fraunces carries personality; Archivo carries information. The two never
   appear at the same size in the same block. */

h1, h2, h3, h4 { margin: 0; font-weight: 600; }

.h1, h1 {
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: var(--lh-h1);
  letter-spacing: var(--tr-h1);
  font-weight: 600;
  font-variation-settings: 'SOFT' 0, 'WONK' 1, 'opsz' 60;
}

.h2, h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  letter-spacing: var(--tr-h2);
  font-weight: 600;
  font-variation-settings: 'SOFT' 0, 'WONK' 1, 'opsz' 36;
}

.h3, h3 {
  font-family: var(--font-text);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  letter-spacing: var(--tr-h3);
  font-weight: 650;
  text-transform: uppercase;
}

/* The eyebrow / label. Archivo, condensed-ish, caps, tracked wide. */
.eyebrow {
  font-family: var(--font-text);
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  font-weight: 700;
  font-stretch: 88%;
  text-transform: uppercase;
  color: var(--accent);
  /* Tight to the heading below it. See --s-1h. */
  margin: 0 0 var(--s-1h);
  display: block;
}

.on-light .eyebrow, .section--light .eyebrow { color: var(--accent-on-light); }

/* Gold on Adda Green is 3.95:1 — legal for headlines at 24px and above, and
   not for an 11px label. On a green ground the eyebrow takes Parchment
   (5.71:1) and the gold stays where it belongs: on the bulb strip above it.
   Panels with their own Late Green ground keep the gold. */
.section--green .eyebrow { color: var(--parchment); }
.section--green .reserve .eyebrow,
.section--green .letterboard .eyebrow { color: var(--accent); }

/* --------------------------------------------------------------------------
   WHY THIS IS NOT JUSTIFIED
   Justified text was considered here and rejected, and it is worth writing down
   so it is not reconsidered from scratch every few months.

   Print justifies well because a typesetter breaks a paragraph as a whole,
   weighing every possible set of line breaks against each other. Browsers do
   not: they fill each line greedily, left to right, and once a line is set it
   is never revisited. The spare space on every line therefore lands in the word
   gaps of that line alone, which is what produces the loose lines and the
   vertical rivers that give web justification away.

   Hyphenation is the usual mitigation and it does not rescue it here. The
   measures on this site run from 24ch in a pillar to 56ch in a lede, and the
   narrower the measure the fewer words there are to absorb the slack, so the
   gaps get worse exactly where the columns are tightest. `hyphens: auto` would
   also start breaking words in a voice built on short, spoken sentences.

   Ragged right is the right answer, and it is also the honest one: a straight
   right edge on a page whose whole material story is torn paper would be the
   one hard edge in the building.

   What actually makes a paragraph look composed is the last line, and there is
   a real tool for that. `text-wrap: pretty` asks the engine to look ahead and
   avoid leaving one short word stranded on its own line, which is the thing
   that reads as untidy. It costs nothing where it is unsupported. */
p {
  margin: 0 0 var(--s-2);
  max-width: var(--measure);
  text-wrap: pretty;
}

/* Headings get `balance` instead: even line lengths matter more than the last
   line when there are only two or three lines and they are 40px tall. It is
   capped at a handful of lines by the spec, which is exactly the range every
   heading on this site falls in. */
h1, h2, h3, .h1, .h2 { text-wrap: balance; }
p:last-child { margin-bottom: 0; }

.lede {
  font-size: var(--fs-body-lg);
  color: var(--text-muted-dark);
  max-width: 56ch;
}
.section--light .lede { color: var(--text-muted-light); }
.section--green .lede { color: var(--text-on-green); }

.small {
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  letter-spacing: var(--tr-small);
}

a { color: inherit; text-decoration-color: rgba(250, 193, 72, 0.5); text-underline-offset: 0.2em; }
a:hover { text-decoration-color: var(--accent); }

/* --- Focus: visible, gold, never removed -------------------------------- */
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 2px;
}
.section--light :focus-visible { outline-color: var(--gold-deep); }

.skip-link {
  position: absolute; left: var(--s-2); top: -4rem; z-index: 999;
  background: var(--gold); color: var(--ink);
  padding: var(--s-1) var(--s-2); border-radius: var(--radius-panel);
  font-weight: 600; text-decoration: none;
  transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus { top: var(--s-1); }

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

/* --- Layout -------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: var(--container-narrow); }

.section { position: relative; padding-block: var(--section-y); }
.section--night { background: var(--bg-night); color: var(--text-on-dark); }
.section--green { background: var(--bg-green); color: var(--text-on-green); }
.section--light { background: var(--bg-light); color: var(--text-on-light); }
.section--light a { text-decoration-color: rgba(138, 95, 3, 0.5); }
.section--light a:hover { text-decoration-color: var(--gold-deep); }

.section__head { margin-bottom: var(--s-4); }
.section__head .h2 { max-width: 20ch; }

.stack > * + * { margin-top: var(--s-2); }
.stack-lg > * + * { margin-top: var(--s-4); }

.grid { display: grid; gap: var(--s-3); }

/* --- Buttons ------------------------------------------------------------
   Gold is a highlight, never a field. The gold button carries Ink text
   (11.10:1) and is used once or twice per screen, no more. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--font-text);
  font-size: var(--fs-small);
  font-weight: 650;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.95em 1.6em;
  border-radius: var(--radius-panel);
  border: 1px solid transparent;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: background-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:active { transform: translateY(1px); }

.btn--gold { background: var(--gold); color: var(--ink); }
.btn--gold:hover { background: #ffd171; }

.btn--ghost {
  background: transparent;
  color: var(--text-on-dark);
  border-color: rgba(242, 239, 228, 0.4);
}
.btn--ghost:hover { border-color: var(--gold); color: var(--gold); }

.section--light .btn--ghost { color: var(--text-on-light); border-color: rgba(23,21,18,0.28); }
.section--light .btn--ghost:hover { color: var(--gold-deep); border-color: var(--gold-deep); }

.btn--outline-gold { background: transparent; color: var(--gold); border-color: rgba(250,193,72,0.55); }
.btn--outline-gold:hover { background: var(--gold); color: var(--ink); border-color: var(--gold); }

.btn .arrow { transition: transform var(--dur) var(--ease); }
.btn:hover .arrow { transform: translateX(0.25em); }

/* A quieter text link with a gold rule that draws on hover. */
.link-arrow {
  display: inline-flex; align-items: center; gap: 0.5em;
  min-height: 2.75rem;
  font-size: var(--fs-small); font-weight: 650;
  letter-spacing: 0.09em; text-transform: uppercase;
  text-decoration: none; color: var(--accent);
  padding-bottom: 0.35em;
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px; background-repeat: no-repeat; background-position: 0 100%;
  transition: background-size var(--dur) var(--ease);
}
.link-arrow:hover { background-size: 100% 1px; }
.section--light .link-arrow { color: var(--accent-on-light); }

/* --- The bulb strip -----------------------------------------------------
   A row of gold dots. The one device that appears on every touchpoint. It
   says something is on tonight before a word is read. */
.bulbs {
  display: flex;
  gap: 0.55rem;
  align-items: center;
  margin-bottom: var(--s-1h);
}
/* Size, shape and rhythm live here; the filament's surface and its motion are
   in ornament.css, which owns everything to do with how the room is lit. */
.bulbs span {
  width: 0.3125rem; height: 0.3125rem;
  border-radius: 50%;
  background: var(--gold);
  opacity: 0.45;
}

/* --- The ledger rule ----------------------------------------------------
   Vertical gold hairlines with a bulb dot on alternating rows: old ruled
   ledger paper and matchbox label borders. A background field at low
   opacity, never behind body copy. */
.ledger {
  position: absolute; inset: 0;
  pointer-events: none;
  opacity: 0.14;
  background-image:
    repeating-linear-gradient(to right,
      rgba(250,193,72,0.9) 0 1px, transparent 1px 5.5rem);
  /* Masked away from the middle of the frame, where the copy lives. */
  --ledger-mask:
    linear-gradient(to bottom, transparent, #000 16%, #000 84%, transparent),
    linear-gradient(to right, #000 0 6%, transparent 16%, transparent 84%, #000 94% 100%);
  mask-image: var(--ledger-mask);
  -webkit-mask-image: var(--ledger-mask);
  mask-composite: intersect;
  -webkit-mask-composite: source-in;
}

/* --- Torn paper edge ----------------------------------------------------
   The transition between the night sections and the parchment ones, as in
   the approved layout. The band is a frame; what fills it is a real element
   carrying the neighbouring section's own ground class, clipped to the torn
   silhouette. See the long note in src/components/ornament.mjs for why it is
   built that way rather than as one SVG that fills a colour. */
.tear {
  position: absolute; left: 0; right: 0;
  height: clamp(1.5rem, 3vw, 2.75rem);
  z-index: 2;
  pointer-events: none;
}
.tear > * { position: absolute; inset: 0; }
.tear__fringe {
  background-repeat: repeat-x;
  background-size: 100% 100%;
}
.tear--down { bottom: -1px; }
.tear--up   { top: -1px; transform: scaleY(-1); }

/* --- Scroll reveal ------------------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(1.25rem);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
  will-change: opacity, transform;
}
.reveal.is-in { opacity: 1; transform: none; }
.reveal[data-delay="1"] { transition-delay: 90ms; }
.reveal[data-delay="2"] { transition-delay: 180ms; }
.reveal[data-delay="3"] { transition-delay: 270ms; }
.reveal[data-delay="4"] { transition-delay: 360ms; }
.reveal[data-delay="5"] { transition-delay: 450ms; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; }
}

/* Progressive enhancement: if JS never runs, nothing stays invisible. */
.no-js .reveal { opacity: 1; transform: none; }


/* ========== components.css ========== */
/* ==========================================================================
   COMPONENTS
   ========================================================================== */

/* --- The logo -----------------------------------------------------------
   Sized by height so the roundel is never cropped or boxed. The brand
   forbids drop shadows on the mark; the gold ring does the separating. */
.logo {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  text-decoration: none;
  line-height: 1;
}
.logo img {
  width: auto;
  height: 2.6rem;
  border-radius: 0;
  transition: transform var(--dur) var(--ease);
}
.logo:hover img {
  transform: rotate(-4deg) scale(1.04);
}

.logo__type {
  display: flex;
  flex-direction: column;
  gap: 0.18rem;
}
.logo__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.015em;
  color: var(--parchment);
  white-space: nowrap;
  font-variation-settings:
    "SOFT" 0,
    "WONK" 1,
    "opsz" 22;
}
.logo__desc {
  font-family: var(--font-text);
  font-weight: 700;
  font-stretch: 88%;
  font-size: 0.5rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
  white-space: nowrap;
}

/* The footer carries the full mark, above the 120px minimum. */
.logo--lg img {
  height: 8rem;
}
.logo--lg:hover img {
  transform: none;
}

@media (min-width: 62rem) {
  .logo img {
    height: 3rem;
  }
  .logo__name {
    font-size: 1.1875rem;
  }
  .logo--lg img {
    height: 9rem;
  }
}

/* --- Header -------------------------------------------------------------- */
.header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  height: var(--header-h);
  display: flex;
  align-items: center;
  transition:
    background-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    backdrop-filter var(--dur) var(--ease);
}
.header::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(25, 39, 22, 0.92),
    rgba(25, 39, 22, 0.55) 60%,
    transparent
  );
  opacity: 1;
  transition: opacity var(--dur) var(--ease);
  pointer-events: none;
}
.header.is-stuck {
  background: rgba(25, 39, 22, 0.72);
  backdrop-filter: saturate(140%) blur(10px);
  box-shadow: 0 1px 0 rgba(250, 193, 72, 0.18);
}
.header.is-stuck::after {
  opacity: 0;
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  width: 100%;
  position: relative;
  z-index: 1;
}

.nav {
  display: none;
}
.nav__list {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 2.25rem);
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav__link {
  font-family: var(--font-text);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text-on-dark);
  padding: 0.4rem 0;
  position: relative;
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  bottom: 0;
  height: 1px;
  background: var(--gold);
  transition: right var(--dur) var(--ease);
}
.nav__link:hover::after,
.nav__link[aria-current="page"]::after {
  right: 0;
}
.nav__link[aria-current="page"] {
  color: var(--gold);
}

.header__cta {
  display: none;
}

/* Mobile drawer */
.burger {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 2.75rem;
  height: 2.75rem;
  background: transparent;
  border: 1px solid rgba(242, 239, 228, 0.28);
  border-radius: var(--radius-panel);
  cursor: pointer;
  padding: 0 0.7rem;
}
.burger span {
  display: block;
  height: 1.5px;
  background: var(--parchment);
  transition:
    transform var(--dur) var(--ease),
    opacity var(--dur) var(--ease);
}
.burger[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(6.5px) rotate(45deg);
}
.burger[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}
.burger[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-6.5px) rotate(-45deg);
}

.drawer {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: var(--green-900);
  padding: calc(var(--header-h) + var(--s-4)) var(--gutter) var(--s-4);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-1rem);
  transition:
    opacity var(--dur) var(--ease),
    transform var(--dur) var(--ease),
    visibility var(--dur);
}
.drawer.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.drawer__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.drawer__list li + li {
  margin-top: var(--s-2);
  border-top: 1px solid var(--rule-dark);
  padding-top: var(--s-2);
}
.drawer__link {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 8vw, 2.5rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--parchment);
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}
.drawer__link span {
  font-family: var(--font-text);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  color: var(--gold);
}
.drawer__foot {
  border-top: 1px solid var(--rule-dark);
  padding-top: var(--s-3);
  color: var(--text-muted-dark);
}
.drawer__foot .btn {
  width: 100%;
  justify-content: center;
  margin-bottom: var(--s-2);
}

@media (min-width: 62rem) {
  .nav,
  .header__cta {
    display: block;
  }
  .burger {
    display: none;
  }
  .drawer {
    display: none;
  }
}

/* --- The header's booking button -----------------------------------------
   A full "BOOK A TABLE" in gold was the loudest thing in the header and it
   was competing with the four nav links for the same glance. At rest it is a
   brass coin with a date on it now, and it opens to the full label on hover or
   on keyboard focus — the affordance is there when you go for it, and out of
   the way of the masthead when you are not.

   The container holds the OPEN width and the button is right-aligned inside
   it, so the growth happens into space that was already reserved. Animating a
   width in a flex header without doing that would shove the navigation
   leftwards every time the pointer crossed the corner, which is the usual
   reason this pattern gets abandoned.

   `max-width` on the label rather than `width`, because the label's real width
   depends on the font and there is no number to hard-code; the transition runs
   to a ceiling the text never reaches, and the text sets its own size. */
.header__cta .btn--pill {
  padding: 0.8em 0.85em;
  gap: 0;
  border-radius: var(--radius-chip);
}
.btn--pill .btn__icon {
  display: flex;
  flex: none;
}
.btn--pill .btn__icon svg {
  width: 1.15rem;
  height: 1.15rem;
}
.btn--pill .btn__label {
  display: block;
  max-width: 0;
  opacity: 0;
  overflow: hidden;
  white-space: nowrap;
  transition:
    max-width var(--dur) var(--ease),
    opacity var(--dur-fast) var(--ease),
    margin-inline-start var(--dur) var(--ease);
}
.header__cta .btn--pill:hover .btn__label,
.header__cta .btn--pill:focus-visible .btn__label {
  max-width: 12rem;
  opacity: 1;
  margin-inline-start: 0.6em;
}
@media (min-width: 62rem) {
  .header__cta {
    display: flex;
    justify-content: flex-end;
    /* The open button measures 167px, so 11rem reserves it with a little to
       spare. Without this the header is `space-between` over three items and
       the navigation slides 61px left every time the pointer touches the
       corner. It also keeps the masthead's proportions exactly as they were
       when the button was full width: the coin sits on the same right edge the
       old one did, and the nav has not moved. */
    min-width: 11rem;
  }
}

/* --- Hero ---------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: min(94svh, 54rem);
  display: flex;
  align-items: flex-end;
  padding-top: calc(var(--header-h) + var(--s-4));
  padding-bottom: clamp(var(--s-5), 9vw, var(--s-6));
  overflow: hidden;
  background: var(--green-900);
}
.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}
/* <picture> is inline by default and would not give the <img> inside it a
   height to be 100% of. */
.hero__media picture {
  position: absolute;
  inset: 0;
  display: block;
}
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 46%;
  animation: hero-drift 30s var(--ease) infinite alternate;
}
/* The drift used to run 1.04 to 1.11 on top of a standing `scale(1.06)`, which
   compounded with what `cover` was already doing and put the frame at about
   1.7x for most of its cycle — the single biggest reason the hero read as
   zoomed in. There is no standing scale now, and the travel is a third of what
   it was: just enough that the room is never quite still. */
@keyframes hero-drift {
  from {
    transform: scale(1.012) translate3d(0, 0, 0);
  }
  to {
    transform: scale(1.048) translate3d(-0.7%, -0.8%, 0);
  }
}
/* Every wall in this venue carries large type of its own, so a photograph of
   one behind a headline is always type over type. On this frame it is the
   painted list — CAFÉ, PUB, RESTAURANT — which falls exactly where the H1 does
   and cannot be cropped away: the badge is dead centre in a symmetrical
   composition, so any crop tight enough to lose the list drags the badge in
   behind the headline as well. There is no framing that separates them.
   Geometry having failed, the overlay settles it instead.

   So the left third is not a wash over a photograph — it is a ground. Bright
   lettering survives a translucent scrim at almost any strength, because
   darkening everything uniformly leaves the local contrast that makes it
   legible; only something close to opaque removes it. From there the overlay
   opens quickly, and the two thirds that carry the badge, the water and the
   planting are barely touched, which is the whole reason to use this
   photograph. */
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(
      to top,
      rgba(25, 39, 22, 0.82) 0%,
      rgba(25, 39, 22, 0.52) 26%,
      rgba(25, 39, 22, 0.28) 62%,
      rgba(25, 39, 22, 0.6) 100%
    ),
    linear-gradient(
      to right,
      rgba(25, 39, 22, 1) 0%,
      rgba(25, 39, 22, 1) 34%,
      rgba(25, 39, 22, 0.86) 39%,
      rgba(25, 39, 22, 0.52) 47%,
      rgba(25, 39, 22, 0.26) 57%,
      rgba(25, 39, 22, 0.1) 76%,
      rgba(25, 39, 22, 0.16) 100%
    );
}

/* THE PHONE AND THE TABLET.
   Neither has a left third to put a ground in: the type runs the full width, so
   the picture is behind all of it and the only axis available is vertical. Both
   take the tall crop rather than the wide one, and that is the load-bearing
   part. The wide frame is 2.15:1, and in a box near enough square `cover` fills
   the height and throws away most of the width — so what survives is the middle,
   which on this symmetrical composition is the badge, sitting dead centre where
   the headline goes. The tall crop puts the badge in its top third and hands the
   rest of the frame to water and planting, which have nothing written on them.
   The fix here is therefore not the near-opaque panel above, which would black
   out most of the frame, but weight underneath the type and exposure taken off
   the whole thing.

   Both, not either. A scrim can only subtract uniformly, and pushing one hard
   enough to settle the lit badge flattened the water into a grey smear while
   still leaving the type sitting *on* the picture rather than above it. Taking
   the exposure and the saturation down first moves the entire frame back a
   plane; the scrim then only has to lift the type off it. The bottom two thirds
   are now near solid, which is what gives the headline somewhere to stand. */
@media (max-width: 61.99rem) {
  .hero__media img {
    filter: brightness(0.6) saturate(0.78);
    object-position: center 6%;
  }
  .hero__media::after {
    background: linear-gradient(
      to top,
      rgba(25, 39, 22, 0.99) 0%,
      rgba(25, 39, 22, 0.97) 34%,
      rgba(25, 39, 22, 0.86) 52%,
      rgba(25, 39, 22, 0.6) 70%,
      rgba(25, 39, 22, 0.42) 86%,
      rgba(25, 39, 22, 0.6) 100%
    );
  }
}

/* The tablet needs the scrim deeper still. The type block starts at about the
   same height in both, but the picture is twice as wide here for a hero of much
   the same height, so the badge lands lower in the frame and reaches further
   into the headline. Holding near-solid to nearly two thirds keeps it a lit
   shape above the type rather than lettering behind it. */
@media (min-width: 48rem) and (max-width: 61.99rem) {
  .hero__media::after {
    background: linear-gradient(
      to top,
      rgba(25, 39, 22, 0.99) 0%,
      rgba(25, 39, 22, 0.98) 44%,
      rgba(25, 39, 22, 0.93) 60%,
      rgba(25, 39, 22, 0.68) 78%,
      rgba(25, 39, 22, 0.44) 91%,
      rgba(25, 39, 22, 0.62) 100%
    );
  }
}

.hero__inner {
  position: relative;
  z-index: 1;
  width: 100%;
}
.hero__title {
  max-width: 15ch;
}
.hero__title .gold {
  color: var(--gold);
  display: block;
}
.hero__body {
  margin-top: var(--s-3);
  max-width: 46ch;
  color: rgba(242, 239, 228, 0.86);
  font-size: var(--fs-body-lg);
}
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-4);
}
.hero__badge {
  position: absolute;
  z-index: 2;
  right: var(--gutter);
  bottom: clamp(var(--s-5), 9vw, var(--s-6));
  display: none;
  width: 8.5rem;
  height: 8.5rem;
  border-radius: 50%;
  border: 1px solid rgba(250, 193, 72, 0.6);
  background: rgba(25, 39, 22, 0.72);
  backdrop-filter: blur(4px);
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 1rem;
}
.hero__badge b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.5rem;
  color: var(--gold);
  font-weight: 600;
  line-height: 1;
}
.hero__badge i {
  display: block;
  font-style: normal;
  font-size: 0.5625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-on-green);
  margin-top: 0.4rem;
}
.hero__badge em {
  font-style: normal;
  display: block;
  font-size: 0.5rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(219, 233, 216, 0.7);
  margin-bottom: 0.3rem;
}
@media (min-width: 62rem) {
  .hero__badge {
    display: flex;
  }
}

/* The sign breathes, because it is a lit sign and lit signs are never quite
   still. The same treatment on both of them — the disc in the hero and the
   stamp on the About collage are one sign printed twice, and they run off the
   same clock, so on a tall screen that shows both they warm and cool together
   rather than beating against each other.

   Deliberately NOT a bounce, a pop or a text zoom: those are the grammar of a
   notification badge, and a notification is the one thing a brass-and-
   parchment room should never look like. What is animated is the LIGHT — the
   same idea as the bulb strip, on the same --ease-bulb curve, which is
   sine-like and has no corner at either end, so the sign warms and cools
   rather than blinking.

   Two properties, both of which the compositor handles alone: the size of the
   disc and the opacity of a halo behind it. Nothing here triggers layout or
   repaints the text, so the whole effect costs one small layer each. 4.6s
   alternating is a nine-second round trip — slow enough to read as a room
   rather than as movement, and still caught in the corner of the eye on the
   way past, which is the whole job.

   `scale`, not `transform: scale()`. The stamp already carries a transform to
   centre itself, and an animation on the same property would win outright and
   drop it; the independent property composes with whatever `translate` is in
   force instead of replacing it. */
.hero__badge::before,
.about__stamp::before {
  content: "";
  position: absolute;
  inset: -0.4rem;
  border-radius: 50%;
  pointer-events: none;
  box-shadow:
    0 0 2.5rem rgba(250, 193, 72, 0.3),
    0 0 0.75rem rgba(250, 193, 72, 0.18);
  /* The resting value, and what a guest who has asked for reduced motion is
     left with: a sign that is lit, just not breathing. */
  opacity: 0.2;
}
.about__stamp {
  animation: sign-breathe 4.6s var(--ease-bulb) infinite alternate;
}
.about__stamp::before {
  animation: sign-glow 4.6s var(--ease-bulb) infinite alternate;
}
@media (min-width: 62rem) {
  .hero__badge {
    animation: sign-breathe 4.6s var(--ease-bulb) infinite alternate;
  }
  .hero__badge::before {
    animation: sign-glow 4.6s var(--ease-bulb) infinite alternate;
  }
}
@keyframes sign-breathe {
  from {
    scale: 1;
  }
  to {
    scale: 1.028;
  }
}
@keyframes sign-glow {
  from {
    opacity: 0.16;
  }
  to {
    opacity: 1;
  }
}

/* There was a pulsing hairline at the foot of the hero here, pointing down.
   It is gone on purpose: a scroll cue is an instruction to do the one thing
   every visitor already arrives knowing how to do, and on a phone — where the
   fold is obviously a fold — it read as a stray mark rather than as an
   invitation. */

/* --- Pillars ------------------------------------------------------------- */
.pillars {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
}
.pillar {
  padding: var(--s-3) var(--s-2);
  text-align: center;
  border-bottom: 1px solid var(--rule-light);
}
.pillar:nth-child(odd) {
  border-right: 1px solid var(--rule-light);
}

/* Two columns means the fifth pillar is alone on its row; let it take the
   whole width rather than drawing a rule against empty space. */
@media (max-width: 63.99rem) {
  .pillar:last-child:nth-child(odd) {
    grid-column: 1 / -1;
    border-right: 0;
  }
}
/* A 360px phone gives each of two columns 128px between the paddings, and
   A PROPER BAR needs 138 at the floor size. Six pixels either side is what
   the sign costs, and there is nothing else in the cell that wanted them. */
@media (max-width: 47.99rem) {
  .pillar {
    padding-inline: 0.5rem;
  }
}
.pillar__icon {
  display: flex;
  justify-content: center;
  margin-bottom: var(--s-2);
  color: var(--gold-deep);
}
.pillar__icon svg {
  width: 2.25rem;
  height: 2.25rem;
}
/* Out of the label tier and into the heading tier, which is where it always
   belonged. --fs-h3 is 13px — the Archivo-caps size the letterboard and the
   menu's section rules are set at — and the line underneath each pillar is
   --fs-small at 13.5px. The heading was smaller than its own body copy, so the
   two had nothing to separate them but weight and case, and they read as one
   block of small type rather than as a title and a sentence.

   The token stays where it is: the letterboard is a physical marquee and the
   menu's group rules are labels, and both genuinely want small caps. It is
   only the pillars that were sized as labels while doing a heading's job.

   Tracking comes DOWN as the size goes up. --tr-h3 is 0.12em, which exists to
   open out caps set at 13px; carried to 21px it would be two and a half pixels
   between every letter, and five two-word signs would stop being words. */
.pillar h3 {
  font-family: var(--font-text);
  /* 17px up to 1024, then 21px from 1440 on, which is where the 15-inch
     laptop lands. The ramp is not a taste judgement: five across means a
     column of (vw - 88) / 5 - 32, and the widest sign — GOOD COMPANY —
     measures 8.87x its own font size at this tracking. Those two lines cross
     at 17.5px on a 1024 frame, so the floor sits just under that and the slope
     is whatever reaches 21 by 1440 without crossing it on the way. Every one
     of the five stays on one line at every width. */
  font-size: clamp(1.0625rem, 0.45rem + 0.96vw, 1.3125rem);
  line-height: 1.18;
  font-weight: 650;
  letter-spacing: 0.045em;
  text-transform: uppercase;
  color: var(--text-on-light);
  margin-bottom: var(--s-1h);
}
.pillar p {
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: var(--text-muted-light);
  margin: 0 auto;
  max-width: 24ch;
}

/* Five across needs 1024, not 768. At 768 a fifth of the frame is a 150px
   column, which was wide enough for a 13px label and is not wide enough for a
   21px heading — four of the five wrapped to two lines, and a row of five
   signs where some are one line and some are two puts the sentence under each
   one at a different height. The two-column layout the phone gets holds up
   perfectly well across the tablet range, so it keeps it for longer. */
@media (min-width: 64rem) {
  .pillars {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
  .pillar {
    border-bottom: 0;
    border-right: 1px solid var(--rule-light);
    padding: var(--s-2) var(--s-2);
  }
  .pillar:last-child {
    border-right: 0;
  }
  .pillar:nth-child(odd) {
    border-right: 1px solid var(--rule-light);
  }
}

/* --- About collage ------------------------------------------------------- */
.about {
  display: grid;
  gap: var(--s-4);
  align-items: center;
}
.about__collage {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-1);
  position: relative;
}
.about__collage figure {
  margin: 0;
  overflow: clip;
  background: var(--green-700);
  aspect-ratio: 3 / 4;
}
.about__collage img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease);
}
.about__collage figure:hover img {
  transform: scale(1.05);
}
.about__stamp {
  position: absolute;
  left: 50%;
  bottom: -1.5rem;
  translate: -50% 0;
  width: 7.25rem;
  height: 7.25rem;
  border-radius: 50%;
  background: var(--green-500);
  border: 1px solid rgba(250, 193, 72, 0.6);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0.75rem;
}
.about__stamp em {
  font-style: normal;
  font-size: 0.5375rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
}
.about__stamp b {
  font-family: var(--font-display);
  font-size: 1.25rem;
  color: var(--gold);
  line-height: 1.1;
  margin: 0.15rem 0;
}
.about__stamp i {
  font-style: normal;
  font-size: 0.5375rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-on-green);
}

@media (min-width: 60rem) {
  .about {
    grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr);
    gap: var(--s-5);
  }
  /* Centred on the gap between the first two frames. It used to sit at 62%,
     which put it just inside the first frame with its edge crossing the
     second — fine over three bare photographs, but once each one has a mount
     a badge straddling a frame edge reads as a misalignment rather than as
     something placed there. On the gap it is unambiguously deliberate. */
  .about__stamp {
    left: auto;
    right: 59%;
    bottom: 12%;
    translate: none;
  }
}

/* --- Letterboard ---------------------------------------------------------
   The physical board beside the stage, reprinted. Ink ground, gold keyline,
   Archivo condensed caps — the marquee is the signature. */
.letterboard {
  background: var(--ink);
  border: 1px solid rgba(250, 193, 72, 0.55);
  border-radius: var(--radius-panel);
  padding: var(--s-3);
}
.letterboard h3 {
  font-family: var(--font-text);
  font-size: var(--fs-h3);
  letter-spacing: var(--tr-h3);
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 650;
  margin-bottom: var(--s-2);
}
.letterboard__row {
  display: grid;
  grid-template-columns: 3.25rem 1fr;
  gap: var(--s-2);
  padding: 0.7rem 0;
  border-top: 1px solid rgba(250, 193, 72, 0.16);
  font-family: var(--font-text);
  font-weight: 700;
  font-stretch: 66%;
  letter-spacing: var(--tr-marquee);
  line-height: var(--lh-marquee);
  text-transform: uppercase;
  font-size: 0.9375rem;
}
.letterboard__row:first-of-type {
  border-top: 0;
}
.letterboard__row dt {
  color: var(--gold);
}
.letterboard__row dd {
  margin: 0;
  color: var(--parchment);
}
.letterboard__note {
  font-size: var(--fs-small);
  color: var(--stone);
  margin-top: var(--s-2);
  font-style: italic;
}

/* --- Event cards --------------------------------------------------------- */
.events {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
}
@media (min-width: 40rem) {
  .events {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 68rem) {
  .events {
    grid-template-columns: repeat(4, 1fr);
  }
}

.event {
  position: relative;
  overflow: clip;
  background: var(--green-800);
  border: 1px solid rgba(219, 233, 216, 0.1);
  border-radius: var(--radius-panel);
  display: flex;
  flex-direction: column;
  transition:
    border-color var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}
.event:hover {
  border-color: rgba(250, 193, 72, 0.5);
  transform: translateY(-3px);
}
.event__media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: clip;
}
.event__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease);
}
.event:hover .event__media img {
  transform: scale(1.06);
}
.event__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgba(25, 39, 22, 0.85) 4%,
    rgba(25, 39, 22, 0.15) 55%,
    transparent
  );
}
.event__day {
  position: absolute;
  z-index: 2;
  top: var(--s-2);
  left: var(--s-2);
  background: rgba(25, 39, 22, 0.9);
  border: 1px solid rgba(250, 193, 72, 0.5);
  border-radius: 3px;
  padding: 0.45rem 0.6rem 0.4rem;
  text-align: center;
  line-height: 1;
}
.event__day b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--gold);
  font-weight: 600;
}
.event__day span {
  display: block;
  font-size: 0.5rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-on-green);
  margin-top: 0.2rem;
}
.event__tag {
  position: absolute;
  z-index: 2;
  top: var(--s-2);
  right: var(--s-2);
  font-size: 0.5625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--ink);
  background: var(--gold);
  padding: 0.3rem 0.55rem;
  border-radius: var(--radius-chip);
}
.event__body {
  padding: var(--s-2) var(--s-2) var(--s-3);
  flex: 1;
  display: flex;
  flex-direction: column;
}
.event__title {
  font-family: var(--font-display);
  font-size: 1.3125rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--parchment);
}
.event__artist {
  color: var(--gold);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.04em;
  margin-top: 0.2rem;
}
.event__time {
  font-size: var(--fs-small);
  letter-spacing: var(--tr-small);
  color: var(--text-on-green);
  margin-top: 0.35rem;
}
.event__blurb {
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: rgba(219, 233, 216, 0.72);
  margin-top: auto;
  padding-top: var(--s-2);
}

/* --- Moments strip ------------------------------------------------------- */
.moments {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s-1);
}
@media (min-width: 48rem) {
  .moments {
    grid-template-columns: repeat(5, 1fr);
  }
}
.moments figure {
  margin: 0;
  overflow: clip;
  position: relative;
  aspect-ratio: 1;
}
.moments img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition:
    transform var(--dur-slow) var(--ease),
    filter var(--dur) var(--ease);
}
.moments figure:hover img {
  transform: scale(1.07);
}

/* --- Gallery ------------------------------------------------------------- */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: var(--s-4);
}
.filter {
  font-family: var(--font-text);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: transparent;
  color: var(--text-muted-dark);
  border: 1px solid var(--rule-dark);
  border-radius: var(--radius-chip);
  padding: 0.55rem 1rem;
  cursor: pointer;
  transition:
    color var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    background-color var(--dur) var(--ease);
}
.filter:hover {
  color: var(--parchment);
  border-color: rgba(250, 193, 72, 0.45);
}
.filter[aria-pressed="true"] {
  background: var(--gold);
  color: var(--ink);
  border-color: var(--gold);
}

.masonry {
  columns: 1;
  column-gap: var(--s-2);
}
@media (min-width: 40rem) {
  .masonry {
    columns: 2;
  }
}
@media (min-width: 68rem) {
  .masonry {
    columns: 3;
  }
}
.masonry figure {
  break-inside: avoid;
  margin: 0 0 var(--s-2);
  position: relative;
  overflow: clip;
  background: var(--green-800);
  cursor: zoom-in;
}
.masonry figure[hidden] {
  display: none;
}
.masonry img {
  width: 100%;
  transition: transform var(--dur-slow) var(--ease);
}
.masonry figure:hover img {
  transform: scale(1.04);
}
.masonry figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--s-3) var(--s-2) var(--s-2);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: var(--parchment);
  background: linear-gradient(to top, rgba(25, 39, 22, 0.92), transparent);
  opacity: 0;
  transform: translateY(0.5rem);
  transition:
    opacity var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}
.masonry figure:hover figcaption,
.masonry figure:focus-within figcaption {
  opacity: 1;
  transform: none;
}

/* Lightbox */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(9, 14, 8, 0.96);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s-4) var(--s-2);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--dur) var(--ease),
    visibility var(--dur);
}
.lightbox.is-open {
  opacity: 1;
  visibility: visible;
}
.lightbox img {
  max-width: min(96vw, 68rem);
  max-height: 78svh;
  object-fit: contain;
}
.lightbox__cap {
  text-align: center;
  color: var(--text-on-green);
  font-size: var(--fs-small);
  margin-top: var(--s-2);
  max-width: 60ch;
}
.lightbox__fig {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.lightbox__btn {
  position: absolute;
  background: transparent;
  border: 1px solid rgba(242, 239, 228, 0.3);
  color: var(--parchment);
  width: 2.9rem;
  height: 2.9rem;
  border-radius: 50%;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition:
    border-color var(--dur) var(--ease),
    color var(--dur) var(--ease);
}
.lightbox__btn:hover {
  border-color: var(--gold);
  color: var(--gold);
}
.lightbox__close {
  top: var(--s-2);
  right: var(--s-2);
}
.lightbox__prev {
  left: var(--s-2);
  top: 50%;
  transform: translateY(-50%);
}
.lightbox__next {
  right: var(--s-2);
  top: 50%;
  transform: translateY(-50%);
}

/* --- Menu list ----------------------------------------------------------- */
.menu-cols {
  display: grid;
  gap: var(--s-4);
}
@media (min-width: 52rem) {
  .menu-cols {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-4);
  }
}
.menu-group h3 {
  color: var(--gold);
  margin-bottom: var(--s-2);
}
.section--light .menu-group h3 {
  color: var(--gold-deep);
}
.menu-item {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--rule-dark);
}
.section--light .menu-item {
  border-bottom-color: var(--rule-light);
}
.menu-item__name {
  font-family: var(--font-display);
  font-size: var(--fs-menu);
  line-height: var(--lh-menu);
  letter-spacing: var(--tr-menu);
  font-weight: 400;
}
.menu-item__leader {
  flex: 1;
  border-bottom: 1px dotted currentColor;
  opacity: 0.28;
  transform: translateY(-0.25em);
}
.menu-item__price {
  font-family: var(--font-text);
  font-size: var(--fs-price);
  font-weight: 500;
  letter-spacing: var(--tr-price, 0.02em);
  font-variant-numeric: tabular-nums;
}

/* --- Reservation form ---------------------------------------------------- */
.reserve {
  background: var(--green-900);
  border: 1px solid rgba(250, 193, 72, 0.35);
  border-radius: var(--radius-panel);
  padding: clamp(var(--s-3), 4vw, var(--s-4));
}
.reserve__grid {
  display: grid;
  gap: var(--s-2);
}
@media (min-width: 46rem) {
  .reserve__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
/* `align-items: start`, not `end`. Bottom-aligning the four fields used to be
   the way to land every input on one line, and it worked while each field was
   exactly a label and a control. The date field now carries a line of help
   underneath, which made it taller, and bottom alignment paid for that by
   lifting the whole field a line above its neighbours. Top alignment gives the
   same shared line for free, because these four labels are one line each at
   this width, and it lets the hint hang into the row's spare height instead of
   pushing anything around. */
@media (min-width: 68rem) {
  .reserve__grid {
    grid-template-columns: repeat(4, 1fr);
    align-items: start;
  }
}
/* The name takes two of the four columns. It is the longest thing anyone types
   into this form and the only free-text field on its row, so giving it the
   width of the two pickers beside it makes the row read as one line of a
   sentence rather than as three unrelated boxes. */
@media (min-width: 68rem) {
  .field--wide {
    grid-column: span 2;
  }
}
/* `min-width: 0` is load-bearing, not tidiness. A grid item defaults to
   `min-width: auto`, which means it refuses to shrink below its content, so the
   eight-card night strip inside one of these fields made the field 590px wide
   on a 390px phone and pushed the whole page sideways instead of scrolling
   inside its own box. */
.field {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  min-width: 0;
}
/* `.field__legend` is the same thing as a label for a row of radios, which has
   no single control to point `for` at. It names the group through
   `aria-labelledby` instead, and it has to look identical to the labels either
   side of it or the form reads as two kinds of field. */
/* `.field > label`, not `.field label`. The chips are <label> elements too, so
   the descendant selector was painting every unchecked chip gold — a field
   label's colour, applied to the field's contents. Only the direct child is
   the label of the field; anything deeper is one of its controls. */
.field > label,
.field__legend {
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  font-weight: 700;
  color: var(--gold);
}
.field input,
.field select,
.field textarea {
  font-family: var(--font-text);
  font-size: var(--fs-body);
  color: var(--parchment);
  background: rgba(219, 233, 216, 0.06);
  border: 1px solid rgba(219, 233, 216, 0.22);
  border-radius: var(--radius-panel);
  padding: 0.8rem 0.9rem;
  width: 100%;
  transition:
    border-color var(--dur) var(--ease),
    background-color var(--dur) var(--ease);
}
.field input::placeholder,
.field textarea::placeholder {
  color: rgba(219, 233, 216, 0.4);
}
.field input:focus,
.field select:focus,
.field textarea:focus {
  border-color: var(--gold);
  background: rgba(219, 233, 216, 0.1);
}
.field select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23dbe9d8' stroke-width='1.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 1.1rem;
  padding-right: 2.4rem;
}

/* The dropdown list itself is drawn by the operating system, not by this
   stylesheet, and it does not inherit the control's colours. It was painting
   the platform's default white panel and then taking `color: var(--parchment)`
   from the select for the option text: near-white on white, which is a list
   you cannot read. `color-scheme` is the fix rather than styling `option`
   directly — it tells the platform to render its own widget in its dark form,
   so the popup, its scrollbar, the highlight and the focus ring all arrive
   consistent instead of one part being repainted and the rest not. The
   `option` rules underneath are the belt to that braces, for engines that
   honour them (Windows Chrome does) and ignore the hint. */
.field select,
.field input[type="date"],
.field input[type="time"] {
  color-scheme: dark;
}
.field select option {
  background: var(--green-900);
  color: var(--parchment);
}
.field select option:checked {
  color: var(--gold);
}

/* --- The row of nights ---------------------------------------------------
   Eight cards, the whole booking window at a glance. It scrolls horizontally on
   a narrow screen and snaps, so a half-card at the edge is always the cue that
   there are more; on anything wider all eight fit and the scroll never engages.
   `scrollbar-width: none` because the cards themselves show there is more to
   the right, and a scrollbar under a row this short is louder than the row. */
.daypick {
  display: flex;
  min-width: 0;
  gap: 0.4rem;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  /* Room for the focus ring and the lifted state, which would otherwise be
     clipped by the scroll container. */
  padding: 0.25rem 0.25rem 0.4rem;
  margin: -0.25rem -0.25rem -0.4rem;
}
.daypick::-webkit-scrollbar {
  display: none;
}

/* One card, three rows. The night strip, the times and the party size are the
   same decision made three times, so they are the same object three times —
   declared once here rather than as three near-identical blocks that drift
   apart the first time one of them is touched. */
.daypick__day,
.chip {
  flex: 0 0 auto;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1rem;
  min-width: 3.75rem;
  padding: 0.55rem 0.5rem 0.6rem;
  border: 1px solid rgba(219, 233, 216, 0.22);
  border-radius: var(--radius-panel);
  background: rgba(219, 233, 216, 0.06);
  color: var(--text-on-green);
  cursor: pointer;
  font-family: var(--font-text);
  transition:
    border-color var(--dur-fast) var(--ease),
    background-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease);
}
.daypick__dow {
  font-size: 0.5625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 700;
  color: rgba(219, 233, 216, 0.6);
  white-space: nowrap;
}
.daypick__num {
  font-family: var(--font-display);
  font-size: 1.375rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.daypick__mon {
  font-size: 0.5rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(219, 233, 216, 0.5);
}
/* "Tonight" is a word where the others are three letters, so the first card is
   wider than the rest. Letting it be is the point: the night most people want
   is also the one that looks different. */
.daypick__day:first-child {
  min-width: 4.75rem;
}

.daypick__day:hover,
.chip:hover {
  border-color: rgba(250, 193, 72, 0.5);
  background: rgba(219, 233, 216, 0.1);
}
.daypick__day:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}
.daypick__day[aria-checked="true"] {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--green-900);
}
.daypick__day[aria-checked="true"] .daypick__dow,
.daypick__day[aria-checked="true"] .daypick__mon {
  color: rgba(25, 39, 22, 0.72);
}

/* --- The time and party-size rows ----------------------------------------
   The night strip scrolls sideways because eight dates are a sequence and you
   read along it. These two wrap instead: the whole reason they stopped being
   dropdowns is that a dropdown shows one value out of eight, and a row that
   scrolls would hide the last three the same way. Wrapping costs a second line
   on a phone and shows everything.

   No JavaScript anywhere in here. Real radios do the grouping, the arrow keys
   and the single tab stop; the label is the visible chip and the input behind
   it is only ever seen by the keyboard. */
.chips {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  min-width: 0;
  padding: 0.25rem;
  margin: -0.25rem; /* room for the focus ring */
}
/* Out of the layout but not out of the accessibility tree, and pinned inside
   the row's own positioning context so it can never widen the page. The
   explicit box is load-bearing twice over. `.field input` sets `width: 100%`,
   and a full-width absolutely positioned control is exactly what pushed the
   layout sideways the last time a control was hidden in this form. And the box
   is zero rather than the usual 1px, because a 1px input is a 1px TARGET as
   far as an audit is concerned — the thing you actually hit is the chip, which
   is 68px, and the input should not be measurable at all. It stays focusable:
   size has never gated that. */
.field .chips input {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  padding: 0;
  margin: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
.chip b {
  font-family: var(--font-display);
  font-size: 1.375rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.chip i {
  font-style: normal;
  font-size: 0.5rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(219, 233, 216, 0.5);
  white-space: nowrap;
}
/* Sized so eight of these break 4 + 4 on a 360px phone rather than 4 + 3 + 1,
   which left "Later" stranded on a line of its own looking like an accident.
   The numeral is a step under the night strip's, which is also the right
   hierarchy: the night is the decision, the time is the detail. */
.chips .chip {
  min-width: 4rem;
  padding-inline: 0.45rem;
}
.chips .chip b {
  font-size: 1.25rem;
}
/* "Later" is a word among numbers. Set down a size so it sits level with them
   instead of shouldering them aside. */
.chip--word b {
  font-size: 1rem;
  padding-block: 0.19rem;
}
/* Party size is one glyph and needs no caption, so those chips are square-ish
   and single-line. */
.chips--tight {
  gap: 0.35rem;
}
.chips--tight .chip {
  min-width: 2.9rem;
  padding: 0.7rem 0.4rem;
}
.chip--flat {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.chips input:checked + .chip {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--green-900);
}
.chips input:checked + .chip i {
  color: rgba(25, 39, 22, 0.72);
}
/* The ring belongs on the chip, because the input it belongs to is a pixel
   wide and invisible. `:focus-visible` and not `:focus`, so a mouse click does
   not leave a ring behind on a control that already shows its state in gold. */
.chips input:focus-visible + .chip {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

/* Under the date field: what the window actually is, said before the picker is
   opened rather than after a date is rejected. */
.field__hint {
  margin: 0.1rem 0 0;
  font-size: var(--fs-small);
  line-height: 1.4;
  color: rgba(219, 233, 216, 0.55);
}
/* The way out of the form, for the two cases it deliberately does not take:
   a night more than a week off, and a party bigger than the picker goes to. */
.reserve__aside {
  margin-top: var(--s-3);
  padding-top: var(--s-3);
  border-top: 1px solid var(--rule-dark);
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: rgba(219, 233, 216, 0.72);
}
.reserve__aside a {
  color: var(--gold);
  text-decoration: none;
  border-bottom: 1px solid rgba(250, 193, 72, 0.35);
}
.reserve__aside a:hover {
  border-bottom-color: var(--gold);
}
.reserve__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-top: var(--s-3);
  align-items: center;
}
.reserve__note {
  font-size: var(--fs-small);
  color: var(--stone);
  margin-top: var(--s-1h);
}
.reserve__full {
  grid-column: 1 / -1;
}

/* --- Opening hours ------------------------------------------------------
   One line, because there is one set of hours. It used to be a three-row table
   with an identical value in every row, which reads as a schedule you have to
   check rather than as the single fact it is. Setting the time large and the
   qualifier small says the same thing in one glance. */
.hours-one {
  margin: 0;
}
.hours-one b {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1.375rem, 1.1rem + 1.2vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  color: var(--text-on-light);
}
.hours-one span {
  display: block;
  margin-top: 0.35rem;
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  font-weight: 700;
  color: var(--text-muted-light);
}
.footer__hours {
  margin: 0;
}
.footer__hours b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
  color: var(--parchment);
}
.footer__hours span {
  display: block;
  margin-bottom: 0.3rem;
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  font-weight: 700;
  color: rgba(219, 233, 216, 0.72);
}

/* --- FAQ ----------------------------------------------------------------- */
.faq {
  border-top: 1px solid var(--rule-light);
}
.section--night .faq,
.section--green .faq {
  border-top-color: var(--rule-dark);
}
.faq details {
  border-bottom: 1px solid var(--rule-light);
}
.section--night .faq details,
.section--green .faq details {
  border-bottom-color: var(--rule-dark);
}
.faq summary {
  cursor: pointer;
  list-style: none;
  padding: var(--s-2) 2.5rem var(--s-2) 0;
  position: relative;
  font-family: var(--font-display);
  font-size: var(--fs-menu);
  font-weight: 600;
  letter-spacing: -0.005em;
}
.faq summary::-webkit-details-marker {
  display: none;
}
.faq summary::after {
  content: "";
  position: absolute;
  right: 0.4rem;
  top: 50%;
  width: 0.7rem;
  height: 0.7rem;
  margin-top: -0.45rem;
  border-right: 1.5px solid var(--gold-deep);
  border-bottom: 1.5px solid var(--gold-deep);
  transform: rotate(45deg);
  transition: transform var(--dur) var(--ease);
}
.section--night .faq summary::after,
.section--green .faq summary::after {
  border-color: var(--gold);
}
.faq details[open] summary::after {
  transform: rotate(-135deg);
}
.faq details p {
  padding-bottom: var(--s-2);
  color: var(--text-muted-light);
  max-width: 70ch;
}
.section--night .faq details p,
.section--green .faq details p {
  color: var(--text-muted-dark);
}

/* --- Info list (address, hours) ------------------------------------------ */
.info-grid {
  display: grid;
  gap: var(--s-4);
}
@media (min-width: 54rem) {
  .info-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
.info h3 {
  color: var(--gold);
  margin-bottom: var(--s-2);
}
.section--green .info h3 {
  color: var(--parchment);
}
.section--light .info h3 {
  color: var(--gold-deep);
}
.hours-row {
  display: flex;
  justify-content: space-between;
  gap: var(--s-2);
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rule-dark);
  font-size: var(--fs-small);
  letter-spacing: var(--tr-small);
}
.section--light .hours-row {
  border-bottom-color: var(--rule-light);
}
.hours-row b {
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.hours-row a {
  display: inline-block;
  padding-block: 0.25rem;
}

.map {
  border: 1px solid rgba(250, 193, 72, 0.3);
  border-radius: var(--radius-panel);
  overflow: hidden;
  background: var(--green-800);
  aspect-ratio: 16 / 9;
}
.map {
  position: relative;
}
.map iframe {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  filter: saturate(0.85) contrast(1.05);
  background: transparent;
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
/* Sits behind the embed, so a blocked or slow map still says something useful. */
.map__fallback {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  text-align: center;
  padding: var(--s-3);
  color: var(--text-on-green);
  background: var(--green-800);
}
.map__fallback svg {
  width: 1.75rem;
  height: 1.75rem;
  color: var(--gold);
}

/* --- Footer -------------------------------------------------------------- */
.footer {
  background: var(--green-900);
  color: var(--text-on-green);
  padding-block: var(--s-5) var(--s-3);
  position: relative;
}
.footer__grid {
  display: grid;
  gap: var(--s-4);
}
@media (min-width: 52rem) {
  .footer__grid {
    grid-template-columns: 1.6fr 1fr 1.4fr 1.2fr;
    gap: var(--s-4);
  }
}
.footer h4 {
  font-family: var(--font-text);
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 700;
  margin-bottom: var(--s-2);
}
.footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.footer li + li {
  margin-top: 0.1rem;
}
.footer a {
  text-decoration: none;
  font-size: var(--fs-small);
  color: var(--text-on-green);
  transition: color var(--dur-fast) var(--ease);
  /* A 13.5px line is a 15px tap target. WCAG 2.5.8 wants 24px, and a thumb
     wants more, so list links get padding rather than a bigger font. */
  display: inline-block;
  padding-block: 0.4rem;
  line-height: 1.35;
}
.footer a:hover {
  color: var(--gold);
}
.footer__blurb {
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: rgba(219, 233, 216, 0.72);
  max-width: 40ch;
}
.footer__kn {
  font-family: var(--font-kannada);
  font-size: 1.0625rem;
  color: var(--gold);
  margin-top: var(--s-2);
}
/* The studio's own mark, set in the studio's own face. Uppercase because that
   is the mark, and because the webfont is subset to capitals — lowercase would
   silently fall back to Archivo mid-word. Josefin Sans is geometric and runs
   small for its size, so it takes a size up and open letterspacing to sit level
   with the copyright line beside it. */
/* Scoped to `.footer` on purpose. `.footer a` is (0,1,1) and sets
   `display: inline-block` for the tap targets in the link lists; a bare
   `.credit` is (0,1,0) and loses to it, which quietly turned this back into an
   inline box. An inline non-replaced element ignores width and height, so the
   brass rule below collapsed to nothing and the gap and centring did nothing
   either. Two classes beat a class and a type. */
.footer .credit {
  display: inline-flex;
  align-items: center;
  gap: 1.35rem;
  /* Barely any above, since the row is top-aligned now and the rule above is
     already var(--s-3) away; the room is all needed below, between the mark
     and the bottom of the page. */
  padding-block: 0.15rem 1rem;
  padding-inline-start: 0.15rem;
  text-decoration: none;
  color: inherit;
}
/* A hairline of brass standing between the sentence and the mark, so the two
   read as a caption and a signature rather than as one long line of small
   type. It is the same gesture as the strip under the footer's own ledge. */
.credit::before {
  content: "";
  width: 1px;
  height: 3.5rem;
  background: linear-gradient(
    to bottom,
    transparent,
    rgba(250, 193, 72, 0.85),
    transparent
  );
  flex: none;
}
.credit__label {
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(219, 233, 216, 0.5);
  display: block;
  margin-bottom: 0.55rem;
}
/* Two lines, because that is how the studio sets its own name. Josefin Sans is
   geometric and runs small for its size, so it takes open letterspacing and a
   line height under 1 to stack without a gap opening between the words. */
.credit__mark {
  display: block;
  font-family: var(--font-studio);
  text-transform: uppercase;
  font-size: 1.0625rem;
  /* 0.94 was set to stop a gap opening between two words of a single mark, and
     it solved that by making them collide instead: two lines of caps at 0.94
     have their descender-free bodies almost touching, which reads as cramped
     rather than as tight. Caps need MORE leading than lowercase, not less,
     because there is nothing between the lines to breathe. */
  line-height: 1.34;
  letter-spacing: 0.19em;
  color: var(--text-on-green);
  transition: color var(--dur-fast) var(--ease);
}
.credit__mark span {
  display: block;
}
/* The second word is the longer one; tracking it a shade tighter squares the
   two lines off against each other instead of leaving a ragged right. */
.credit__mark span + span {
  letter-spacing: 0.075em;
}
.credit:hover .credit__mark {
  color: var(--gold);
}
.credit:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 4px;
  border-radius: 2px;
}

/* The city, in the script the city reads. Set at the address rather than in
   the middle of English copy: a place name is the one word that carries the
   same meaning either way, so it can sit beside its transliteration without
   either becoming decoration for the other. */
.footer__city-kn {
  font-family: var(--font-kannada);
  font-size: 1.05em;
  color: rgba(219, 233, 216, 0.9);
}

/* Top-aligned, not centred. The credit is a three-line lockup and the
   copyright is one line, so centring the row hung that one line halfway down a
   block four times its height — adrift between the rule above it and the
   bottom of the page, attached to neither. Aligning to the start puts it back
   where a colophon line belongs, directly under the rule that closes the
   footer, and lands it level with the credit's own caption on the other side
   so the two read as one row rather than as two stray sentences. */
.footer__bottom {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--rule-dark);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  justify-content: space-between;
  align-items: flex-start;
  font-size: var(--fs-small);
  color: rgba(219, 233, 216, 0.55);
}
.footer__bottom > p {
  padding-block: 0;
}
.socials {
  display: flex;
  gap: 0.6rem;
}
.socials a {
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  border: 1px solid var(--rule-dark);
  display: grid;
  place-items: center;
  color: var(--text-on-green);
  transition:
    border-color var(--dur) var(--ease),
    color var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}
.socials a:hover {
  border-color: var(--gold);
  color: var(--gold);
  transform: translateY(-2px);
}
.socials svg {
  width: 1.05rem;
  height: 1.05rem;
}

/* --- Story page ---------------------------------------------------------- */
.story-hero {
  display: grid;
  gap: var(--s-4);
  align-items: center;
  grid-template-columns: 1fr;
}
.story-hero figure img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
@media (min-width: 60rem) {
  .story-hero {
    grid-template-columns: 1.05fr 0.95fr;
    gap: var(--s-5);
  }
  .story-hero figure img {
    aspect-ratio: 5 / 4;
  }
}

.chapter {
  display: grid;
  gap: var(--s-4);
  align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 60rem) {
  .chapter {
    grid-template-columns: 1fr 1fr;
    gap: var(--s-5);
  }
  .chapter--flip > *:first-child {
    order: 2;
  }
}
.chapter figure img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.values {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
}
@media (min-width: 44rem) {
  .values {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (min-width: 72rem) {
  .values {
    grid-template-columns: repeat(4, 1fr);
  }
}
.value {
  border-top: 1px solid rgba(250, 193, 72, 0.35);
  padding-top: var(--s-2);
}
.value__num {
  font-family: var(--font-display);
  font-size: 1.75rem;
  color: var(--gold);
  font-weight: 600;
  display: block;
  line-height: 1;
}
.value__title {
  font-family: var(--font-display);
  font-size: var(--fs-menu);
  font-weight: 600;
  letter-spacing: -0.005em;
  text-transform: none;
  color: var(--parchment);
  margin: var(--s-1) 0 0.4rem;
}
.value p {
  font-size: var(--fs-small);
  line-height: var(--lh-small);
  color: var(--text-muted-dark);
}

/* --- Visit page ---------------------------------------------------------- */
.routes {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) {
  .routes {
    grid-template-columns: repeat(3, 1fr);
  }
}
.route {
  border-top: 1px solid rgba(250, 193, 72, 0.35);
  padding-top: var(--s-2);
}
.route__icon {
  color: var(--gold);
  margin-bottom: var(--s-1);
}
.route__icon svg {
  width: 1.75rem;
  height: 1.75rem;
}


/* ========== ornament.css ========== */
/* ==========================================================================
   ORNAMENT — paper, light and the drawn marks
   --------------------------------------------------------------------------
   Loaded last, so everything here is a deliberate refinement of what
   components.css already established rather than a competing rule.

   Two observations from the room drive this whole file.

   FIRST: nothing in Vintage Adda is a flat field. The dark walls are diagonal
   slate and monstera; the light surfaces are aged paper with a torn edge and a
   pen-drawn sprig in the corner. Flat #f6f0e2 and flat #192716 are the two
   colours the venue does not actually contain.

   SECOND, and it is the more important one: in the room, gold is never
   pigment. It is light. Every sign is halo-lit from behind, every ledge has a
   warm strip under it, every pendant is an amber globe. Gold-as-paint has to
   stay under a tenth of the frame or the brand turns into a fast-food sign.
   Gold-as-light does not, because light falls off — it is at four per cent by
   the time it has crossed a heading. That distinction is what lets this file
   add warmth everywhere without breaking 60 · 30 · 10.
   ========================================================================== */

:root {
  /* --- Paper --------------------------------------------------------------
     Every ground on the site is the same sheet of paper: a 400x400 seamless
     crop of the venue's own parchment plate, tiled. Nothing here is
     generated any more, and nothing is stretched — a fixed image scaled to a
     section is the one thing guaranteed to distort, since sections run from
     400px to 1300px tall. Tiling holds the grain at its true scale at every
     height, on every ground, and the whole site reads as one material.

     Both files are opaque mid-grey and composited with `overlay`, and that is
     what keeps the tokens honest rather than being a stylistic choice: a
     speckle laid on at 3% alpha over Late Green does not texture #192716, it
     *becomes* #202d1c — a green that is not on the eight-step ladder and that
     nobody signed off. Overlay against a 50% grey is a no-op, so the mean
     lands exactly on the token and only the paper's variance shows. Both
     tiles are recentred to that 50% grey mean in tools/process-textures.py
     for precisely this reason. */

  --parchment-photo: url('/textures/parchment-tile.webp');

  /* The same sheet, gained up for dark grounds. See the note in
     tools/process-textures.py for why it needs its own file rather than a
     CSS filter: overlay against a dark backdrop computes 2*b*s and so
     compresses the variance to about a fifth, meaning the expansion has to
     happen before the pixels ever reach CSS. */
  --parchment-photo-dark: url('/textures/parchment-tile-dark.webp');

  /* --- Wallpaper ----------------------------------------------------------
     The block-print repeat on the green walls, supplied as artwork and turned
     into an overlay tile in tools/process-textures.py. The repeat is set a
     little under half the source's own 700px so a motif lands at about 90px:
     large enough to read as a drawn pattern rather than as a moiré, small
     enough that a 400px-tall section still shows four rows of it and reads as
     papered wall rather than as one enormous stencil. */
  --wallpaper-green: url('/textures/wallpaper-green.webp');
  --wallpaper-size: clamp(300px, 26vw, 420px);

  /* --- Light -------------------------------------------------------------
     The halo behind the lettering, the strip under the ledge, the bloom off
     the pendants. Kept as tokens so the whole room can be dimmed in one edit. */
  --halo:       0 0 2.2rem rgba(250, 193, 72, 0.13), 0 0 5.5rem rgba(250, 193, 72, 0.07);
  --halo-lit:   0 0 1.6rem rgba(250, 193, 72, 0.30), 0 0 4.5rem rgba(250, 193, 72, 0.14);
  --spill:      linear-gradient(90deg, rgba(250,193,72,0.55), rgba(250,193,72,0.14) 46%, transparent 92%);

  /* The height of a torn edge, named so the padding rule below can be derived
     from it instead of guessing at it. */
  --tear-h: clamp(1.5rem, 3vw, 2.75rem);
}

/* --------------------------------------------------------------------------
   THE SHEET RULE
   A torn edge is drawn inside its own section, so every tear silently ate
   that much off the section's padding and pushed the first line of type up
   towards the join. Giving the section back exactly the height the tear took
   keeps the margins of a sheet equal to the margins of a section that has no
   tear at all — and because it is keyed off :has(), a section that gains or
   loses an edge later corrects itself.
   -------------------------------------------------------------------------- */
.tear { height: var(--tear-h); }

/* A band standing over its neighbour has to cover the whole of the strip that
   neighbour was stretched by, and the band is offset from it by the 1px that
   `.tear--up` / `.tear--down` use to guard against subpixel gaps. Two pixels of
   overshoot closes it. Without them there is a one-pixel line of the
   neighbour's ground left showing along the band's far edge, straight across
   the page — which is exactly the artefact this rule exists to kill, and it
   lives HERE rather than beside `.tear--over` in base.css because the `.tear`
   height above is the same specificity and would otherwise win on order. The
   overshoot goes into the band's own sheet, where two pixels of its own ground
   cannot be seen. */
.tear--over { height: calc(var(--tear-h) + 2px); }

.section:has(> .tear--up)   { padding-top:    calc(var(--section-y) + var(--tear-h)); }
.section:has(> .tear--down) { padding-bottom: calc(var(--section-y) + var(--tear-h)); }

/* --------------------------------------------------------------------------
   THE THREE GROUNDS
   Every surface on the site is one of three, and each is defined exactly once,
   here. The `.ground--*` alias in each selector list is what lets a torn edge
   dress its band in the neighbouring section's own material rather than in a
   hand-matched approximation of it: same colour, same paper, same wallpaper,
   same blend mode, from the same declaration. Nothing downstream can drift out
   of step with a section because there is no second copy to drift.

   A ground must therefore stay POSITION-INDEPENDENT — tiles and flat colour,
   no gradient sized to the element. A green section used to carry a warm wash
   down from its top edge, and it was the direct cause of the visible rule
   across the bottom of every tear: the band could reproduce the colour but not
   a gradient whose geometry depended on being 900px tall. Anything that has to
   fade across a whole section belongs on a child of that section, not in this
   rule.
   -------------------------------------------------------------------------- */

/* Parchment. The real paper, tiled — a 400x400 crop of the venue's own sheet
   with no corner ornament in it, overlaid rather than laid on, so the ground
   colour underneath is still exactly the Parchment token and every contrast
   figure in the brand document still holds. */
.section--light,
.ground--light {
  background-color: var(--bg-light);
  background-image: var(--parchment-photo);
  background-size: 400px 400px;
  background-blend-mode: overlay;
}

/* The dark rooms take the same sheet of paper. Tiled, never stretched — a
   fixed image sized to a section is the one thing guaranteed to distort,
   since section heights run from 400px to 1300px. At a 400px repeat the
   grain keeps its true scale everywhere and costs no second download, the
   light sections having already fetched the same sheet. */
.section--night,
.ground--night,
.footer,
.drawer {
  background-color: var(--bg-night);
  background-image: var(--parchment-photo-dark);
  background-size: 400px 400px;
  background-blend-mode: overlay;
}

/* The green walls are papered, not painted, and the paper is the only thing on
   them. The parchment grain that used to sit under this repeat has gone: two
   textures on one wall is one too many, and at this value the grain was
   contributing nothing but a slight muddiness under the motifs.

   The tile is opaque mid-grey artwork composited with `overlay`, which against
   a backdrop this dark is a straight multiply by twice the tile. A tile that
   averages 50% grey is therefore a no-op on the mean, and Adda Green survives
   the wallpaper exactly — measured, on the built file, in
   tools/process-textures.py. The alternative, a beige repeat at 8% alpha,
   cannot do that: it averages itself into the ground and moves the token.

   Note there is no `.ground--green` alias here, and there cannot be. See
   REPRODUCIBLE in ornament.mjs: a band can stand in for grain but not for
   motifs, so no band ever paints this ground. It is always the real wall. */
/* The room is dimmed one stop. Adda Green is a mid tone, and a mid tone is a
   difficult wall to read off: Verandah on it was 5.15:1, which clears AA but
   sits ON the wallpaper rather than in front of it, and the motifs were left
   with as much contrast against the ground as the paragraphs had. Ten per cent
   black over the whole ground takes the wall to 5.94:1 and, because the tint
   goes over the paper as well as under it, drops the motifs' contrast by the
   same tenth. The copy gains and the pattern recedes, from one value.

   It is a linear-gradient rather than a fourth token because it is lighting,
   not a colour: Adda Green is still the field, still declared, still the thing
   the tint is applied to. Two identical stops make the layer a flat fill at
   any size, so the ground stays POSITION-INDEPENDENT as the rule above
   requires, and a tear band could still reproduce it verbatim if one ever
   needed to. Layer order is top-first, so the tint sits above the paper and
   only the paper is blended with `overlay` against the colour. */
.section--green {
  background-color: var(--bg-green);
  background-image:
    linear-gradient(var(--green-dim), var(--green-dim)),
    var(--wallpaper-green);
  background-size: auto, var(--wallpaper-size) var(--wallpaper-size);
  background-blend-mode: normal, overlay;
}

/* --------------------------------------------------------------------------
   THE LAMP
   A warm pool of light that follows the pointer across the papered walls.

   The reference for this was a sci-fi pen: a cyan radial gradient chasing the
   cursor across a mesh of triangles on near-black. The MECHANIC is worth
   having; the look is the exact opposite of this brand, so none of it came
   across. What is here instead is the same mechanic read as a room — a lamp
   carried past a papered wall, warm, broad and slow, lifting the motifs where
   it falls and leaving them dim where it does not. It is the other half of
   --green-dim: the tint takes the whole wall down a stop so the copy reads,
   and the lamp gives that stop back locally, where the eye already is.

   Scoped to green walls on purpose. A light that also washed the parchment
   and the photographs would be a filter over the site; a light that only
   catches the wallpaper is a lamp in the room.

   Cost: one composited layer per green section, moved by `transform` alone.
   The `.lamp` wrapper does the clipping so no section has to change its own
   overflow, and it is the wrapper — not the moving part — that owns `inset`.
   Injected by main.js only on a fine pointer with motion allowed, so a phone
   never builds it and never pays for it. */
.section--green { position: relative; }

.lamp {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  /* Held back until the pointer has actually moved, so the lamp is never
     found parked in a corner on arrival. */
  opacity: 0;
  transition: opacity 900ms var(--ease);
}
.lamp.is-lit { opacity: 1; }

.lamp__pool {
  position: absolute;
  top: 0;
  left: 0;
  width: 46rem;
  height: 46rem;
  /* Half of its own size, so a transform of (x, y) puts the CENTRE of the
     pool on the pointer without a second translate to compose with. */
  margin: -23rem 0 0 -23rem;
  background: radial-gradient(closest-side,
    rgba(250, 193, 72, 0.115),
    rgba(250, 193, 72, 0.052) 42%,
    rgba(250, 193, 72, 0) 72%);
}
/* Promoted only once the lamp is actually alight. `will-change` holds a
   composited layer open for as long as it is declared, and a 736px square is
   about two megabytes of it — worth paying while the light is moving, not
   worth paying on a page nobody has pointed at yet. */
.lamp.is-lit .lamp__pool { will-change: transform; }

/* --- A tear standing over its neighbour ----------------------------------
   The other half of the rule in ornament.mjs. Where a band cannot reproduce
   what is on the far side of the torn line, it paints its own sheet on the
   near side and leaves the far side clear — and the neighbouring section is
   stretched by exactly the height of the band so that what shows through the
   gap is its real, correctly tiled background rather than an imitation of it.

   The stretch is a negative margin plus the padding to put the content back
   where it was, so nothing inside either section moves. `z-index` only appears
   in the `tear--up` case, where the section that has to cover the band comes
   FIRST in the document and would otherwise be painted over by it; a band
   itself sits at z-index 2 and still wins over both. */
.section:has(> .tear--down.tear--over) + .section {
  margin-top: calc(-1 * var(--tear-h));
  padding-top: calc(var(--section-y) + var(--tear-h));
}
.section:has(+ .section > .tear--up.tear--over) {
  position: relative;
  z-index: 1;
  margin-bottom: calc(-1 * var(--tear-h));
  padding-bottom: calc(var(--section-y) + var(--tear-h));
}

/* The cove light that used to sit in the rule above — a warm wash falling from
   the ceiling coffer onto the top of every green wall — is gone, and not only
   because it made the ground unreproducible. Its own geometry put it at its
   strongest exactly where a green section begins, and a green section begins
   at a torn edge. It was a gradient tuned to be invisible, positioned at the
   one place on the page where it could not be. The wallpaper carries that
   surface now, evenly, at every height. */

/* --------------------------------------------------------------------------
   THE BULB STRIP
   A row of warm points, and nothing else. There was a hairline "wire" running
   through them and overrunning to the right; it read as a stray rule rather
   than as a festoon, and at small sizes it just looked like an underline that
   had slipped. The bulbs carry the idea on their own.

   The motion is the whole detail here. A filament has thermal lag: it comes up
   slower than it goes down, it never fully extinguishes, and no two bulbs on a
   real string are ever in step. So each bulb gets its own period and its own
   phase, the curve is asymmetric (slow swell, quicker fall), and the trough
   sits well above zero. What it must never do is blink.
   -------------------------------------------------------------------------- */
.bulbs {
  position: relative;
  width: fit-content;
  /* The glow reaches past the dot itself; without room to bleed, the box
     clips it and every bulb gains a faint square edge. */
  padding: 0.35rem 0.4rem 0.35rem 0.1rem;
  margin-left: -0.1rem;
}

.bulbs span {
  position: relative;
  /* Slightly larger than before. At 5px the glow was doing all the work and
     the bulb itself had no substance; at 6px it reads as a source. */
  width: 0.375rem;
  height: 0.375rem;
  opacity: 0.42;
  /* A filament is not a flat disc — it is brightest off-centre, where the
     wire actually sits, and falls away to a warmer rim. */
  background: radial-gradient(circle at 38% 34%, #fff3d0 0%, var(--gold) 42%, #d99a1f 100%);
  will-change: opacity, box-shadow;
}

/* On Parchment the same dot in Brass Deep: gold on near-white is 1.64:1 and
   effectively invisible. */
.section--light .bulbs span {
  background: radial-gradient(circle at 38% 34%, #c08f2a 0%, var(--gold-deep) 55%, #6d4a02 100%);
  opacity: 0.62;
}

/* Every bulb runs a different period and starts at a different point in it, so
   the strip drifts permanently out of phase and never resolves into a pattern.
   Prime-ish durations keep the whole row from re-syncing on a common multiple. */
.bulbs--lit span { animation: bulb-warm 4.7s var(--ease-bulb) infinite; }
.bulbs--lit span:nth-child(1) { animation-duration: 4.7s;  animation-delay: -0.4s; }
.bulbs--lit span:nth-child(2) { animation-duration: 6.1s;  animation-delay: -2.9s; }
.bulbs--lit span:nth-child(3) { animation-duration: 5.3s;  animation-delay: -1.1s; }
.bulbs--lit span:nth-child(4) { animation-duration: 7.3s;  animation-delay: -4.6s; }
.bulbs--lit span:nth-child(5) { animation-duration: 5.9s;  animation-delay: -0.2s; }
.bulbs--lit span:nth-child(6) { animation-duration: 6.7s;  animation-delay: -3.3s; }
.bulbs--lit span:nth-child(7) { animation-duration: 5.1s;  animation-delay: -1.9s; }
.bulbs--lit span:nth-child(8) { animation-duration: 7.9s;  animation-delay: -5.2s; }
.bulbs--lit span:nth-child(9) { animation-duration: 6.3s;  animation-delay: -2.4s; }
.bulbs--lit span:nth-child(10){ animation-duration: 5.7s;  animation-delay: -3.8s; }
.bulbs--lit span:nth-child(11){ animation-duration: 7.1s;  animation-delay: -0.9s; }
.bulbs--lit span:nth-child(n+12) { animation-duration: 6.9s; animation-delay: -4.1s; }

/* Slow swell, quicker fall — the shape of something heating rather than
   switching. The trough never reaches zero, because a warm filament doesn't. */
@keyframes bulb-warm {
  0%   { opacity: 0.30; box-shadow: 0 0 3px -1px rgba(250,193,72,0.22); }
  55%  { opacity: 1;    box-shadow: 0 0 9px 0.5px rgba(250,193,72,0.62),
                                    0 0 20px 3px rgba(250,193,72,0.20); }
  100% { opacity: 0.30; box-shadow: 0 0 3px -1px rgba(250,193,72,0.22); }
}

/* On paper there is no bloom to give — ink does not glow — so the light
   sections animate opacity alone. */
.section--light .bulbs--lit span { animation-name: bulb-warm-ink; }
@keyframes bulb-warm-ink {
  0%   { opacity: 0.40; }
  55%  { opacity: 0.95; }
  100% { opacity: 0.40; }
}

/* --------------------------------------------------------------------------
   BACKLIT TYPE
   Every sign in the venue is halo-lit: the letters sit a few millimetres off
   the wall and the light comes round them. Two very wide, very low-alpha
   shadows reproduce it. They must stay wide — a tight shadow is a glow effect
   and reads as 2009; a 5rem one reads as a wall behind the letters.
   Only on dark grounds. Paper is lit from the front.
   -------------------------------------------------------------------------- */
.section--night .h2,
.section--night h2,
.section--green .h2,
.section--green h2,
.story-hero .h1,
.hero__title { text-shadow: var(--halo); }

/* The gold line of the headline is the lit one, as on the signboard. */
.hero__title .gold,
.value__num,
.event__day b { text-shadow: var(--halo-lit); }

/* Paper gets none of it, and says so explicitly so a nested heading in a
   light section cannot inherit a glow from a dark parent. */
.section--light .h2,
.section--light h2,
.section--light .h1 { text-shadow: none; }

/* The scroll cue used to end inside the torn edge, where it read as a speck
   of gold rather than as a line. It now stops clear of the tear. */


/* --------------------------------------------------------------------------
   THE LEDGE STRIP
   Under the signage in the room there is a warm LED in a routed channel: a
   hard bright line at one end, falling away to nothing. It appears here as the
   rule under a section heading, which is the same job — telling you where the
   block of type stops.
   -------------------------------------------------------------------------- */
.section__head { position: relative; }
.section__head::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: calc(var(--s-3) * -1);
  width: min(26rem, 62%);
  height: 1px;
  background: var(--spill);
  opacity: 0.42;
}
.section--light .section__head::after {
  background: linear-gradient(90deg, rgba(138,95,3,0.45), rgba(138,95,3,0.12) 46%, transparent 92%);
  opacity: 0.75;
}

/* --------------------------------------------------------------------------
   THE LETTERBOARD
   The physical board is lit from behind its frame. The rows already carry
   their own gold hairlines, so the light is the only thing missing — a routed
   groove drawn on a fixed pitch would have to guess at the row height, and a
   detail that only lines up at one font size is not a detail.
   -------------------------------------------------------------------------- */
.letterboard {
  position: relative;
  box-shadow: 0 0 3rem -0.5rem rgba(250, 193, 72, 0.16);
}
.letterboard::after {
  content: '';
  position: absolute;
  left: var(--s-3);
  right: var(--s-3);
  top: -1px;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(250,193,72,0.7), transparent);
}

/* --------------------------------------------------------------------------
   DOT LEADERS
   A dotted border draws square dots at whatever spacing the engine feels like.
   A repeated radial draws round ones at a spacing you chose, which is what a
   printed menu has.
   -------------------------------------------------------------------------- */
.menu-item__leader {
  align-self: center;
  height: 4px;
  transform: none;
  border-bottom: 0;
  opacity: 0.34;
  background-image: radial-gradient(circle at 50% 50%, currentColor 1px, transparent 1.4px);
  background-size: 7px 4px;
  background-repeat: repeat-x;
}

/* --------------------------------------------------------------------------
   WARMTH ON HOVER
   The cards and buttons previously answered a pointer by moving. Moving is
   loud. In a room lit like this one, the natural answer is that the thing gets
   a little more light on it.
   -------------------------------------------------------------------------- */
.event {
  transition: border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.event:hover,
.event:focus-within {
  transform: translateY(-2px);
  box-shadow: 0 0.75rem 2.5rem -1rem rgba(250, 193, 72, 0.28);
}

.btn--gold { box-shadow: 0 0 0 0 rgba(250, 193, 72, 0); }
.btn--gold:hover { box-shadow: 0 0 1.75rem -0.25rem rgba(250, 193, 72, 0.55); }
.btn--outline-gold:hover,
.btn--ghost:hover { box-shadow: 0 0 1.5rem -0.5rem rgba(250, 193, 72, 0.4); }

.reserve { box-shadow: 0 0 4rem -1.5rem rgba(250, 193, 72, 0.22); }

/* --------------------------------------------------------------------------
   THE BOTANICAL CORNER
   Two opposite corners, never four: four corners is a certificate. The mark is
   asymmetrical by drawing, so the second one is turned 180° and still does not
   read as a copy. Suppressed on small screens, where a 300px ornament on a
   360px page is not an ornament.
   -------------------------------------------------------------------------- */
.sprig {
  position: absolute;
  z-index: 0;
  width: clamp(9rem, 17vw, 15rem);
  height: auto;
  opacity: 0.6;
  pointer-events: none;
  /* Real ink on real paper, not a tinted vector, so it is left its own colour
     — a warm sepia close enough to Brass Deep that it never fights the token. */
}
.sprig--tl { top: 0; left: 0; }
/* The two corners are photographs of two different sprigs, not one mark
   rotated — turning this one 180deg would show it upside down. */
.sprig--br { bottom: 0; right: 0; }

@media (max-width: 47.99rem) {
  .sprig { width: 7rem; opacity: 0.36; }
  .sprig--br { display: none; }
}

/* --------------------------------------------------------------------------
   THE WALL PLATE
   The Vidhana Soudha is drawn on the wall of the room, beside the words this
   section reprints. It is the one mark that says which city this adda is in,
   so it gets a whole section and it gets it once — a watermark used twice is
   wallpaper.

   The composition is the one the supplied plate was drawn for: the building
   bleeding off the left edge, the type held on the clear right half.
   -------------------------------------------------------------------------- */
.plate {
  position: relative;
  overflow: clip;
}
.plate .soudha {
  position: absolute;
  z-index: 0;
  height: auto;
  pointer-events: none;
  /* On a phone there is no clear half to set the drawing beside the type, so
     it takes a band of its own underneath instead of hiding behind it at an
     opacity where it read as a smudge rather than a building. The section
     buys the room for it in the padding rule below. */
  left: 50%;
  transform: translateX(-50%);
  bottom: var(--tear-h);
  width: 108%;
  opacity: 0.62;
}

/* The band the drawing stands in. Its height is the drawing's: 108% of the
   frame wide, on a 1672x941 plate, is a shade under two-thirds of the frame
   in height. */
@media (max-width: 59.99rem) {
  /* `.section:has(> .tear--down)` above is (0,2,0), so a bare `.plate` loses
     to it and the band never appears. Matching that specificity is the whole
     point of the doubled selector. */
  .section.plate { padding-bottom: calc(var(--tear-h) + 16rem); }
}
.plate__inner { position: relative; z-index: 1; }

/* The city, in the script the city writes it in.
   This is the one place on the site where that word carries its full weight:
   the section exists because the Vidhana Soudha is painted on the wall, and the
   sentence under it says an adda is a Bengaluru habit before it is anything
   else. Set at display size rather than as a label, because a place name shrunk
   to eleven pixels is decoration, and decoration in a script most of the
   audience reads is the wrong way round.
   Brass Deep rather than the gold token: on Parchment the bright gold is
   1.6:1 and would not be readable at all. */
/* The city name is the plate's title, not its caption. At 18-24px it was
   sitting where an eyebrow sits and reading as one, which wastes the one word
   on the page that every person in this city recognises on sight. Set at the
   quote's own scale and a shade past it, it becomes the thing you see first
   and the quote becomes what it says underneath.

   Size does not invert the hierarchy here, because colour holds it: the city
   is brass and the quote is ink, so the big word reads as a mark stamped on
   the wall and the smaller one as the sentence. Line height goes up with the
   size — Kannada carries vowel signs above and below the base line, and the
   1.0 that was fine at 18px clips them at 48. */
.plate__city {
  font-family: var(--font-kannada);
  font-size: clamp(2rem, 1.3rem + 3vw, 3rem);
  font-weight: 600;
  line-height: 1.18;
  letter-spacing: 0.01em;
  color: var(--gold-deep);
  margin: 0 0 var(--s-2);
}

.plate__quote {
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 1.15rem + 2.6vw, 2.75rem);
  line-height: 1.08;
  letter-spacing: var(--tr-h2);
  font-weight: 600;
  font-variation-settings: 'SOFT' 0, 'WONK' 1, 'opsz' 40;
  max-width: 15ch;
}
/* The third line is the one on the wall in gold. Brass on Parchment is
   5.9:1 — the gold token itself would be 1.9:1 and unreadable. */
.plate__quote em { font-style: normal; color: var(--gold-deep); }

.plate__mark {
  display: block;
  margin-top: var(--s-3);
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  font-weight: 700;
  color: var(--text-muted-light);
}

/* Once there is a clear half to put it in, the building stops being a
   watermark and becomes the drawing it is: off the left edge, standing on the
   bottom of the section, with the type held clear of it on the right. That is
   the composition the supplied plate was drawn for. Nothing overlaps, so the
   line can carry four times the weight and actually be read. */
@media (min-width: 60rem) {
  .plate {
    /* Tall enough that the drawing clears both torn edges. The building is
       61% of the frame wide and the elevation is 1200x540, so it stands about
       27.5% of the frame high — the section has to hold that plus two tears
       plus the space that stops it looking wedged in. */
    min-height: 38rem;
    display: flex;
    align-items: center;
  }
  .plate .soudha {
    /* The drawing carries no paper of its own, so it is sized purely for the
       composition. The building occupies the left three-fifths of the file,
       so 66% of the frame puts its right edge and the trees comfortably clear
       of the type block, which starts at 54%. */
    left: -3%;
    /* Clear of the torn edge, not standing in it. At a flat 1.5rem the
       building's railings and forecourt were inside the tear band and got
       bitten off by it. */
    bottom: calc(var(--tear-h) + 0.75rem);
    width: 66%;
    opacity: 1;
    /* This has to be said out loud. The narrow rule above centres the drawing
       with `translateX(-50%)`, and overriding `left` here does not undo a
       transform: the two compose. The building was being carried a further
       528px to the left of where these numbers put it, which took it clean off
       the frame and left only the far trees showing near the left margin —
       looking for all the world like a texture that had failed to load. */
    transform: none;
    /* There is deliberately no mask here. The right-edge feather is baked
       into the file's own alpha in tools/process-textures.py, because a CSS
       mask on this element — inside a parent carrying background-blend-mode —
       silently paints nothing at all in at least one shipping engine. It cost
       an afternoon to find twice. An image that carries its own soft edge
       cannot be broken by whatever it is dropped into. */
  }
  .plate__inner { margin-left: auto; width: min(46%, 32rem); }
}

/* --------------------------------------------------------------------------
   THE LEDGER, SOFTENED
   It was a hard 5.5rem repeat at 14%, which counts as a stripe. Widening the
   gauge and dropping the opacity turns it back into what it is meant to be:
   ruled paper you notice on the second look.
   -------------------------------------------------------------------------- */
.ledger {
  opacity: 0.1;
  background-image:
    repeating-linear-gradient(to right,
      rgba(250,193,72,0.9) 0 1px, transparent 1px 7rem);
}

/* Not on the green walls any more. The ledger was standing in for a texture
   those sections did not have; now that they are papered, it is a second
   repeat crossing the first, and two patterns on one wall is one too many.
   The night rooms still have nothing but grain, so it stays there. */
.section--green .ledger { display: none; }

/* --------------------------------------------------------------------------
   THE BRASS STRIP AT THE FOOTER
   The last join on the page is Adda Green against Late Green, and unlike the
   one above it this one SHOULD be visible: the footer is a different kind of
   thing from the page, and saying so is the job of a rule.

   Drawn the way the room draws them. Nothing in this venue is edged with a
   painted line; every ledge has a lit brass strip under it, and light behaves
   in two parts — the filament and the bloom it throws. So this is a hairline
   that is brightest in the middle and gives out before either margin, over a
   soft warm fall beneath it. A flat 1px border across the full width would be
   a rule drawn on the page; this is a light left on in the room.
   -------------------------------------------------------------------------- */
.footer::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 4.5rem;
  pointer-events: none;
  background:
    linear-gradient(to right,
      transparent 0, rgba(250,193,72,0.42) 16%, rgba(250,193,72,0.88) 50%,
      rgba(250,193,72,0.42) 84%, transparent 100%) top left / 100% 1px no-repeat,
    linear-gradient(to bottom, rgba(250,193,72,0.085), transparent 72%);
}

/* Depth at the join — a sheet lying ON a dark ground rather than beside it —
   is drawn into the tear's own SVG now, not applied here as a filter. See the
   note in ornament.mjs for why: a drop shadow follows a clip polygon's closing
   edges as happily as its torn one, and drew a hairline across the page at the
   top and bottom of every band. */

/* --------------------------------------------------------------------------
   ELEVATION
   Everything on a wall is on it, not in it. The lifts are defined in
   tokens.css, including the note on why lifting a panel is not the same as
   breaking the brand's rule against shadowed photography — what carries the
   shadow here is always the frame, never the picture.
   -------------------------------------------------------------------------- */
.event,
.letterboard,
.map { box-shadow: var(--lift-1); }
.event:hover { box-shadow: var(--lift-2); }

/* The reserve panel is the one thing on the page being asked for something, so
   it gets the deepest lift and sits furthest off the wall. */
.reserve { box-shadow: var(--lift-2); }

/* --------------------------------------------------------------------------
   THE ABOUT WALL
   The one section that is purely the room describing itself, so it is the one
   that can afford furniture. Three pictures hung on a papered wall: each in a
   thin brass mount, each standing a little off it, with the light catching the
   top edge of the frame the way it catches every ledge downstairs.

   The mount is what makes this legitimate rather than decoration for its own
   sake — a photograph with a shadow on it is a photograph pretending to be an
   object, but a photograph in a frame IS an object, and the frame is the thing
   the shadow belongs to. The pictures themselves are still square-cornered and
   undressed inside it.
   -------------------------------------------------------------------------- */
/* The type on a papered wall. Verandah is a cool, desaturated green-white, and
   against a warm brass-motif ground it went grey and sat back — correct on a
   flat painted wall, underpowered on this one. Parchment is the same white the
   sheets downstairs are made of, and it is warm, so it belongs to the same room
   as the motifs rather than cooling them down. It also reads harder: 5.7:1
   against Adda Green where Verandah managed 5.2:1.

   The second line takes the gold, the way the hero's third line does. Brass on
   Adda Green is 3.95:1 — under AA for body copy, comfortably over the 3:1 that
   applies to display sizes, which is the only place it is ever used. */
.section--green h2,
.section--green .h2 { color: var(--parchment); }
.section--green h2 em { font-style: normal; color: var(--gold); }
.section--green .lede { color: rgba(246, 240, 226, 0.90); }

.about__collage figure {
  border: 1px solid rgba(250, 193, 72, 0.30);
  box-shadow: var(--lift-2);
  transition: box-shadow var(--dur-slow) var(--ease),
              border-color var(--dur-slow) var(--ease);
}
.about__collage figure:hover {
  border-color: rgba(250, 193, 72, 0.55);
}
/* A hair more air between the frames than between bare photographs, because
   three mounts touching read as one object with lines drawn on it. */
.about__collage { gap: var(--s-1h); }

/* --------------------------------------------------------------------------
   PARALLAX
   Scroll-driven, on the compositor, and declared entirely in CSS: there is no
   scroll handler anywhere in this project and this did not add one. Where
   `view()` timelines are not supported the page simply does not move, which is
   the correct fallback and needs no code of its own.

   Everything here animates `translate` and `scale` — the standalone
   properties, never `transform` — because the hover states on these same
   images are written in `transform`, and the two compose instead of one
   silently winning. That is also why the hero's slow Ken Burns loop can stay
   on the <img> while the recede rides on its wrapper.

   The amplitudes are small on purpose. The job is to separate the room from
   the words in front of it, not to announce itself; anything much over five
   per cent stops reading as depth and starts reading as a slide.

   ONE TRAP, and it cost most of the time spent here. `view()` measures its
   subject against the nearest SCROLL CONTAINER, and `overflow: hidden` makes
   an element one — so every one of these animations, sitting inside a figure
   or a section that clipped with `hidden`, was being measured against a box
   that by definition never scrolls. They attached, they reported themselves
   running, they even reported a plausible progress, and they were frozen at it
   forever. Everything on this page that only ever needed to clip now clips
   with `overflow: clip`, which does not create a scroll container. If a
   parallax here ever stops moving, that is the first thing to check.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {

    /* The room recedes as you leave it. Driven off the document scroll rather
       than the hero's own view timeline: the hero starts already in frame, so
       a view() range would begin halfway through itself and waste half the
       movement. Zero to one viewport is exactly the distance it is on screen. */
    .hero__media {
      animation: hero-recede linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100svh;
    }
    @keyframes hero-recede { to { translate: 0 9%; } }

    /* Photographs sit further back than the frames holding them. Entering from
       the bottom the image is lifted inside its frame and settles down through
       it as the frame rises, so it climbs the screen more slowly than the page
       does — which is the whole of the effect. The scale is the headroom that
       makes the travel possible without exposing an edge. */
    .about__collage img,
    .event__media img,
    .moments img,
    .chapter figure img,
    .story-hero figure img {
      scale: 1.115;
      animation: media-recede linear both;
      animation-timeline: view();
    }
    @keyframes media-recede {
      from { translate: 0 -4.6%; }
      to   { translate: 0 4.6%; }
    }

    /* The Soudha is painted on a wall behind the room, so it is the one thing
       on the page that should move least. Half the amplitude of a photograph,
       and on `translate` so the phone layout's `translateX(-50%)` centring
       survives underneath it. */
    .plate .soudha {
      animation: soudha-recede linear both;
      animation-timeline: view();
    }
    @keyframes soudha-recede {
      from { translate: 0 1.8%; }
      to   { translate: 0 -1.8%; }
    }
  }
}

/* The figures that had no reason to clip until now. The collage, the event
   cards and the moments strip already crop to a fixed ratio; these two size
   the image directly, so the parallax would hang over the edge of them. */
.chapter figure,
.story-hero figure { overflow: clip; }

/* --------------------------------------------------------------------------
   REDUCED MOTION
   The halo, the paper and the drawn marks are all static, so they stay. Only
   the drift and the filament stop. The parallax never starts: it is declared
   inside `prefers-reduced-motion: no-preference` rather than switched off
   again here, so there is no window in which it runs.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .hero__media img { animation: none; transform: scale(1.04); }
  .bulbs--lit span { animation: none; opacity: 0.75; }
}


