/* ============================================================
   ACOSTA'S — SITE STYLES
   Rules:
   · No raw colors, sizes, or spacing. Tokens only.
   · Blocks never set their own section padding — .section owns it.
   · Vertical rhythm comes from .stack gaps, not element margins.
   · Reveal animations are progressive enhancement: content is
     visible by default; JS only hides what is below the fold.
   ============================================================ */

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

html { overflow-x: clip; scroll-behavior: smooth }

/* Belt-and-braces: root-level overflow-x clipping has engine quirks
   (iOS Safari can still let the viewport pan to content wider than the
   page even when html/body are clipped). Clipping at the two content
   roots guarantees nothing inside them can widen the page in ANY
   engine. The nav and drawer live outside these and carry their own
   containment. */
main, .footer { overflow-x: clip }

body {
  margin: 0;
  overflow-x: clip;
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-md, var(--text-base));
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100% }
img { height: auto }
a { color: inherit; text-decoration: none }
p  { margin: 0 }
h1, h2, h3 { margin: 0; font-weight: var(--weight-semibold) }


/* ═══════════════ LAYOUT PRIMITIVES ═══════════════
   These three own all spacing on the site. */

.section {
  padding-block: var(--space-section);
  padding-inline: var(--space-gutter);
  position: relative;
  /* Dark-section vignette default, driven by the master switch in
     tokens.css (currently off). Light and colored surfaces switch it
     off regardless; data-vignette overrides per instance. */
  --layer-vignette: var(--vignette-default);
  background-image: var(--layer-glow, none), var(--layer-vignette, none);
}
.section[data-density="compact"]  { padding-block: var(--space-section-compact) }
.section[data-density="spacious"] { padding-block: var(--space-section-spacious) }
.section[data-density="flush"]    { padding-block: 0 }

/* Surface option — the only way to set a section background.
   background-color, not the shorthand, so data-glow can layer an
   image on top without the two options fighting. */
.section[data-surface="alt"]   { background-color: var(--color-surface-alt) }
.section[data-surface="deep"]  { background-color: var(--color-surface-deep) }
.section[data-surface="light"] {
  background-color: var(--color-surface-light);
  color: var(--color-text-on-light);
  --layer-vignette: none;   /* vignette is a dark-surface treatment */
}
.section[data-surface="accent"] {
  background-color: var(--color-accent);
  color: var(--color-text-on-accent);
  --layer-vignette: none;
}
.section[data-surface="secondary"] {
  background-color: var(--color-secondary);
  color: var(--color-text-on-secondary);
  --layer-vignette: none;
}

/* Background treatments — glow and vignette are separate layers so
   they COMPOSE rather than overwrite each other, the same way
   data-surface composes with both. Not scoped to .section: these are
   surface treatments, so the nav and footer use the same options. */
[data-glow],
[data-vignette] {
  background-image: var(--layer-glow, none), var(--layer-vignette, none);
}

/* Glow — the value names the edge the light comes FROM. */
[data-glow="top"]    { --layer-glow: var(--glow-from-top) }
[data-glow="bottom"] { --layer-glow: var(--glow-from-bottom) }
[data-glow="left"]   { --layer-glow: var(--glow-from-left) }
[data-glow="right"]  { --layer-glow: var(--glow-from-right) }
[data-glow="both"]   { --layer-glow: var(--glow-from-top), var(--glow-from-bottom) }

/* Hero-geometry variants — offset and shallower. */
[data-glow="hero-top"]    { --layer-glow: var(--glow-hero-top) }
[data-glow="hero-bottom"] { --layer-glow: var(--glow-hero-bottom) }
[data-glow="hero-both"]   { --layer-glow: var(--glow-hero-top), var(--glow-hero-bottom) }

/* Vignette — radiates from all edges inward, or pools in the centre.
   "edge" is already the dark-section default; these are the per-
   instance overrides, including the opt-out. */
[data-vignette="edge"]    { --layer-vignette: var(--vignette-edge) }
[data-vignette="tight"]   { --layer-vignette: var(--vignette-edge-tight) }
[data-vignette="center"]  { --layer-vignette: var(--vignette-center) }
[data-vignette="corners"] { --layer-vignette: var(--vignette-corners) }
[data-vignette="none"]    { --layer-vignette: none }

/* Per-element intensity overrides, so one treatment can be stronger or
   softer without changing the site-wide default. */
[data-glow-strength="soft"]       { --glow-strength: var(--glow-strength-soft) }
[data-glow-strength="strong"]     { --glow-strength: var(--glow-strength-strong) }
[data-vignette-strength="soft"]   { --vignette-strength: var(--vignette-strength-soft) }
[data-vignette-strength="strong"] { --vignette-strength: var(--vignette-strength-strong) }

.container                      { max-width: var(--width-content); margin-inline: auto }
.container[data-width="prose"]  { max-width: var(--width-prose) }
.container[data-width="narrow"] { max-width: var(--width-narrow) }
.container[data-width="wide"]   { max-width: var(--width-wide) }

.stack                  { display: flex; flex-direction: column; gap: var(--space-stack) }
.stack[data-gap="tight"]{ gap: var(--space-stack-tight) }
.stack[data-gap="loose"]{ gap: var(--space-8) }
.stack[data-gap="xl"]   { gap: var(--space-10) }
.stack[data-align="center"] { align-items: center; text-align: center }
.stack[data-align="end"]    { align-items: flex-end;   text-align: right }
.stack[data-align="start"]  { align-items: flex-start }

/* Row — horizontal sibling of .stack, for inline groups of items. */
.row                    { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap }
.row[data-gap="tight"]  { gap: var(--space-2) }
.row[data-gap="loose"]  { gap: var(--space-6) }
.row[data-align="start"]{ align-items: flex-start }
.row[data-justify="between"] { justify-content: space-between }
.row[data-justify="center"]  { justify-content: center }

/* ── HEADING GROUP — block contract ────────────────────────────
   An eyebrow is a label attached to its heading, not a free-floating
   element. Grouping them here guarantees the pair stays tightly
   coupled and the larger gap falls between the cluster and the body
   copy — the same at every heading size, on every page.
   ─────────────────────────────────────────────────────────────── */
.heading-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-heading-label);
}

/* Intrinsic grids — reflow with no breakpoint */
.grid        { display: grid; gap: var(--space-grid) }
.grid[data-min="sm"] { grid-template-columns: repeat(auto-fit, minmax(min(var(--col-min-sm), 100%), 1fr)) }
.grid[data-min="md"] { grid-template-columns: repeat(auto-fit, minmax(min(var(--col-min-md), 100%), 1fr)) }
.grid[data-min="lg"] { grid-template-columns: repeat(auto-fit, minmax(min(var(--col-min-lg), 100%), 1fr)) }

/* ── SPLIT — block contract ────────────────────────────────────
   Guarantees:
   · Both columns are always equal height (align=stretch default).
   · Media height derives from an aspect token — never an invented
     min-height. Images crop via object-fit, never letterbox.
   · Reflows to one column at --split-min-column. No media query.
   Options: data-align="center" · data-reverse="true"
   ─────────────────────────────────────────────────────────────── */
.split {
  --split-gap:         var(--space-10);
  --split-min-column:  var(--col-min-lg);
  --split-align:       stretch;
  --split-media-ratio: var(--ratio-media-split);

  display: grid;
  gap: var(--split-gap);
  align-items: var(--split-align);
  grid-template-columns:
    repeat(auto-fit, minmax(min(var(--split-min-column), 100%), 1fr));
}
.split[data-align="center"] { --split-align: center }

/* Media ratio option — blocks declare their context, never a number. */
.split[data-media="hero"]   { --split-media-ratio: var(--ratio-media-hero) }
.split[data-media="full"]   { --split-media-ratio: var(--ratio-media-full) }
.split[data-media="square"] { --split-media-ratio: var(--aspect-square) }
.split[data-media="cinema"] { --split-media-ratio: var(--aspect-cinema) }

/* Each column is a query container, so type inside can size to the
   column rather than the viewport. */
.split > * { container-type: inline-size }

/* Media keeps its aspect ratio as the intrinsic height, which is what
   sizes the row. align-items:stretch then grows whichever column is
   shorter to match. No invented min-heights anywhere. */
.split > .media { aspect-ratio: var(--split-media-ratio) }
.split[data-reverse="true"] > :first-child { order: 2 }


/* ═══════════════ TYPOGRAPHY ═══════════════ */

/* Fluid so the longest eyebrow ("Hecho con amor · Made with love")
   stays on one line down to a 320px phone. 3.2vw is derived from that
   string measured at this weight and tracking; it caps at --text-sm
   above ~437px so desktop is unaffected. */
.eyebrow {
  font-size: clamp(0.6rem, 3.2vw, var(--text-sm));
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--color-secondary);
  line-height: var(--leading-snug);
  text-wrap: nowrap;
}
.eyebrow[data-tone="danger"] { color: var(--color-danger-on-dark) }

.display {
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: var(--tracking-tight);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-none);
  text-wrap: balance;
}
/* Explicit line grouping — each span is its own line, so phrases
   never break mid-thought regardless of viewport. */
.display[data-lines="true"] > span { display: block }

/* data-size="xl" is container-relative: it sizes off the width of its
   own column rather than the viewport, so it cannot overflow no matter
   what split ratio or gutter the block uses. 15.5cqi is derived from the
   longest line ("Puerto Rican") measured in Oswald 600 at this tracking. */
.display[data-size="xl"] { font-size: clamp(2.5rem, 15.5cqi, var(--display-xl)) }

/* Hero-scale headings carry the hard drop shadow from the original
   design (--shadow-text was defined for this but never wired). */
.display[data-size="xl"],
.display[data-size="lg"] { text-shadow: var(--shadow-text) }

/* Weathered texture — a grunge mask that erodes the letterforms.
   Driven by --texture-heading, so the whole site turns it off with one
   token. data-texture="none" opts a single heading out. */
.display {
  -webkit-mask-image: var(--texture-heading);
          mask-image: var(--texture-heading);
  -webkit-mask-size: var(--texture-scale);
          mask-size: var(--texture-scale);
}
.display[data-texture="none"] {
  -webkit-mask-image: none;
          mask-image: none;
}
.display[data-size="lg"] { font-size: var(--display-lg) }
.display[data-size="md"] { font-size: var(--display-md) }
.display[data-size="sm"] { font-size: var(--display-sm) }

/* Tone option — the only way to recolor a heading or eyebrow. */
.display[data-tone="accent"]    { color: var(--color-accent) }
.display[data-tone="secondary"] { color: var(--color-secondary) }
.display[data-tone="danger"]    { color: var(--color-danger) }
.eyebrow[data-tone="dim"]       { color: var(--color-text-dim) }
.eyebrow[data-tone="accent"]    { color: var(--color-accent) }

/* Inline emphasis inside a display heading. */
.display .tone-accent    { color: var(--color-accent);    font-style: normal }
.display .tone-secondary { color: var(--color-secondary); font-style: normal }
.display .tone-danger    { color: var(--color-danger);    font-style: normal }
.display .tone-base      { color: var(--color-text);      font-style: normal }

.lead {
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
  color: var(--color-text-muted);
  max-width: var(--width-prose);
  text-wrap: pretty;
}
.body {
  color: var(--color-text-muted);
  max-width: var(--width-prose);
  text-wrap: pretty;
}
/* Light-surface text colours. Anything that renders readable copy on a
   dark surface needs an equivalent here, or it inherits cream-on-cream
   and disappears. */
.on-light .lead,
.on-light .body,
.on-light .prose                  { color: var(--color-text-on-light-mut) }
.on-light .display                { color: var(--color-text-on-light) }
.on-light .prose strong           { color: var(--color-text-on-light) }
.on-light .link-plain             { color: var(--color-text-on-light); text-decoration: underline }
.on-light .link-plain:hover       { color: var(--color-danger-on-light) }
.on-light .prose a                { color: var(--color-text-on-light); text-decoration: underline }
.on-light .prose a:hover          { color: var(--color-danger-on-light) }
.on-light .info-row svg           { color: var(--color-danger-on-light) }
.on-light .eyebrow                { color: var(--color-secondary-deep) }
/* Ghost buttons draw their label in --color-accent, which is gold: 2.16:1
   on cream, unreadable. The filled variants are unaffected because they
   bring their own dark-on-gold pair. Border moves too — a 1.5px outline
   is non-text contrast and needs 3:1. */
.on-light .btn[data-variant="ghost"] {
  color: var(--color-accent-on-light);
  border-color: var(--color-accent-on-light);
}
.on-light .btn[data-variant="ghost"]:hover {
  border-color: var(--color-accent-on-light);
  background: var(--color-wash-on-light);
}

/* Cards and menu lists on a light surface. Without these the gold and
   cream text renders near-invisibly on cream. */
.on-light .card                   { border-color: var(--color-border-on-light) }
.on-light .card-title             { color: var(--color-accent-on-light) }
.on-light .card-body              { color: var(--color-text-on-light-mut) }
.on-light .card-tag               { color: var(--color-secondary-deep) }

.on-light .menu-group-title       { color: var(--color-secondary-deep) }
.on-light .menu-group-title svg   { stroke: var(--color-accent-on-light) }
.on-light .menu-sizes             { color: var(--color-text-on-light-mut) }
.on-light .menu-item-name         { color: var(--color-text-on-light) }
.on-light .menu-item-desc         { color: var(--color-text-on-light-mut) }
.on-light .menu-item-price        { color: var(--color-accent-on-light) }

/* Form controls on a light surface. The dark-surface defaults are cream
   text on a near-transparent white fill, which is invisible on cream. */
.on-light .field-label            { color: var(--color-text-on-light-mut) }
.on-light .field-input,
.on-light .field-textarea {
  background: var(--color-field-bg-on-light);
  border-color: var(--color-border-on-light);
  color: var(--color-text-on-light);
}
.on-light .field-input::placeholder,
.on-light .field-textarea::placeholder { color: var(--color-text-on-light-mut) }
.on-light .field-input:focus-visible,
.on-light .field-textarea:focus-visible { outline-color: var(--color-danger) }
.on-light .attribution            { color: var(--color-accent-on-light) }
.on-light .script                 { color: var(--color-text-on-light-mut) }
.on-light .divider-label          { color: var(--color-text-on-light-mut) }
.on-light .divider-label::before,
.on-light .divider-label::after   { background: var(--color-secondary-deep) }

/* The accent (gold) and feature (teal) card variants carry their own
   surface, so they must keep their own text colours. */
.on-light .card[data-variant="accent"] .card-title,
.on-light .card[data-variant="accent"] .card-body {
  color: var(--color-text-on-accent);
}
.on-light .card[data-variant="accent"] { border-color: transparent }


/* ═══════════════ BUTTON ═══════════════ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-target-min);
  padding: var(--button-padding-y) var(--button-padding-x);
  border-radius: var(--button-radius);
  font-size: var(--button-font-size);
  font-weight: var(--button-weight);
  border: var(--border-base) solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out);
}
.btn[data-variant="accent"]    { background: var(--color-accent); color: var(--color-text-on-accent) }
.btn[data-variant="accent"]:hover { background: var(--color-accent-hover) }
.btn[data-variant="danger"]    { background: var(--color-danger); color: var(--color-surface-lighter) }
.btn[data-variant="danger"]:hover { background: var(--color-danger-hover) }
.btn[data-variant="ghost"]     { color: var(--color-accent); border-color: var(--color-border-strong) }
.btn[data-variant="ghost"]:hover { border-color: var(--color-accent); background: var(--color-accent-subtle) }
.btn[data-variant="dark"]      { background: var(--color-text-on-accent); color: var(--color-accent) }


/* ═══════════════ NAV ═══════════════ */

.nav {
  position: sticky;
  top: 0;
  z-index: var(--layer-nav);
  min-height: var(--nav-height);
  display: flex;
  align-items: center;
  padding-inline: var(--space-gutter);
  background-color: var(--nav-bg);   /* not shorthand — data-glow layers an image */
  /* Nav is dark chrome — same default as dark sections, but the SHORT
     variant, since the standard ellipse leaves too little falloff to
     be seen at nav height. Driven by the same master switch. */
  --layer-vignette: var(--vignette-default-short);
  background-image: var(--layer-glow, none), var(--layer-vignette, none);
  backdrop-filter: blur(var(--nav-blur));
  box-shadow: 0 1px 0 var(--color-border);
}
.nav-inner {
  width: 100%;
  max-width: var(--width-wide);
  margin-inline: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
}
.brand { display: flex; align-items: center; gap: var(--space-4); min-width: 0 }
.brand img { width: var(--nav-logo-size); height: var(--nav-logo-size); flex: none }
.brand-name {
  font-family: var(--font-brand);
  font-size: var(--text-xl);
  letter-spacing: var(--tracking-normal);
  color: var(--color-accent);
  text-transform: uppercase;
  line-height: 1;
}
.brand-sub {
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-max);
  color: var(--color-secondary);
  text-transform: uppercase;
  white-space: nowrap;
  line-height: 1;
}
.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.nav-links a:not(.btn):hover { color: var(--color-accent) }
.nav-burger {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: var(--space-2);
  min-width: var(--tap-target-min);
  min-height: var(--tap-target-min);
  align-items: center;
  justify-content: center;
  color: var(--color-accent);
}
/* Icon sizes come from tokens — svg width/height attributes are only
   a no-CSS fallback. */
.nav-burger svg { width: var(--icon-lg); height: var(--icon-lg) }


/* ═══════════════ NAV DRAWER ═══════════════
   Sibling of .nav — never nested inside a sticky ancestor,
   which would break position:fixed containment. */

.drawer {
  display: none;
  position: fixed;
  inset: 0;
  z-index: var(--layer-drawer);
  pointer-events: none;
  overflow: clip;   /* off-screen panel can never create page overflow */
}
.drawer-scrim {
  position: absolute;
  inset: 0;
  background: var(--color-scrim);
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out);
}
.drawer-panel {
  position: absolute;
  inset-block: 0;
  right: 0;
  width: min(var(--drawer-width), 85vw);
  background: var(--color-surface-deep);
  border-left: var(--border-thin) solid var(--color-border);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  transform: translateX(105%);   /* geometry — own width + clearance, not a design value */
  transition: transform var(--duration-base) var(--ease-out);
}
.drawer[data-open="true"] { pointer-events: auto }
.drawer[data-open="true"] .drawer-scrim { opacity: 1 }
.drawer[data-open="true"] .drawer-panel { transform: none }
.drawer-link {
  padding: var(--space-4) var(--space-1);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  border-bottom: var(--border-thin) solid var(--color-hairline);
  min-height: var(--tap-target-min);
  display: flex;
  align-items: center;
}
.drawer-link:hover, .drawer-link[aria-current="page"] { color: var(--color-accent) }
.drawer-cta { margin-top: var(--space-5) }
.drawer-close {
  align-self: flex-end;
  width: var(--tap-target-min);
  height: var(--tap-target-min);
  border-radius: var(--radius-full);
  border: var(--border-base) solid var(--color-border-strong);
  background: none;
  color: var(--color-accent);
  font-size: var(--text-base);
  cursor: pointer;
  margin-bottom: var(--space-2);
}


/* ═══════════════ CARD ═══════════════ */

.card {
  padding: var(--card-padding);
  border: var(--card-border);
  border-radius: var(--card-radius);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.card-title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-tight);
  text-transform: uppercase;
  color: var(--color-accent);
  line-height: var(--leading-snug);
}
.card-body { font-size: var(--text-sm); color: var(--color-text-muted); flex: 1; text-wrap: pretty }
.card-tag {
  font-size: var(--text-xs);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-secondary);
}

.card[data-variant="accent"] {
  background: var(--color-accent);
  border-color: transparent;
  color: var(--color-text-on-accent);
  justify-content: center;
  gap: var(--space-4);
}
.card[data-variant="accent"] .card-title { color: var(--color-text-on-accent) }
.card[data-variant="accent"] .card-body  { color: var(--color-text-on-accent); font-weight: var(--weight-bold); flex: 0 }

.card-prose { line-height: var(--leading-relaxed); text-wrap: pretty }

/* Variants change surface and alignment only — padding and radius stay
   on the card contract so every card in a grid matches. */
.card[data-variant="feature"] {
  background: var(--color-secondary);
  border-color: transparent;
  color: var(--color-text-on-secondary);
  justify-content: center;
}


/* ── MENU LIST — block contract ────────────────────────────────
   A priced list inside a card. Guarantees:
   · name/description and price sit on one row, price never wraps
   · long names wrap without pushing the price off the card
   · the pair collapses to stacked rows when the column is narrow
   ─────────────────────────────────────────────────────────────── */
.menu-group     { gap: var(--space-5) }
.menu-group-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.menu-group-title {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-lg);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-secondary);
}
.menu-group-title svg { flex: none; width: var(--icon-sm); height: var(--icon-sm) }
.menu-sizes {
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  color: var(--color-text-faint);
  white-space: nowrap;
}

.menu-item {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.menu-item-main  { flex: 1 1 12rem; min-width: 0 }
.menu-item-name  { font-weight: var(--weight-extrabold); font-size: var(--text-base) }
.menu-item-desc  { font-size: var(--text-sm); color: var(--color-text-faint); margin-top: var(--space-1); text-wrap: pretty }
.menu-item-price {
  font-weight: var(--weight-extrabold);
  color: var(--color-accent);
  white-space: nowrap;
  font-size: var(--text-sm);
}


/* ── PROSE — long-form body copy ───────────────────────────────
   Constrained to the readable measure with consistent paragraph
   rhythm, so an essay never sets its own spacing. */
.prose {
  max-width: var(--width-prose);
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  color: var(--color-text-muted);
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
  text-wrap: pretty;
}
.prose[data-align="center"] { margin-inline: auto }
.attribution {
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wide);
  color: var(--color-accent);
  font-size: var(--text-sm);
  text-transform: uppercase;
}


/* ── FORM — block contract ─────────────────────────────────────
   Every control meets the tap-target minimum and inherits the type
   scale; no control sets its own size. */
.field { display: flex; flex-direction: column; gap: var(--space-2) }
.field-label {
  font-size: var(--text-xs);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-text-dim);
}
.field-input,
.field-textarea {
  width: 100%;
  min-height: var(--field-height);
  padding: var(--space-3) var(--field-padding-x);
  border: var(--field-border);
  border-radius: var(--field-radius);
  background: var(--field-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
}
.field-textarea { min-height: calc(var(--field-height) * 3); resize: vertical }
.field-input:focus-visible,
.field-textarea:focus-visible {
  outline: var(--border-base) solid var(--color-accent);
  outline-offset: var(--focus-ring-offset);
  border-color: transparent;
}
.field-input::placeholder,
.field-textarea::placeholder { color: var(--color-text-faint) }


/* data-size="flexible" makes Turnstile fill its column — but the widget
   documents a 300px MINIMUM, and a phone column here is ~276px. Some
   engines shrink it anyway (Chromium); some honor the minimum (iOS),
   which poked past the card and could widen the page. overflow-x:clip
   caps the iframe at the column; height stays free so an expanded
   interactive challenge is never cut off. An earlier transform-based
   fit was removed — it mis-reserved height and overlapped the button. */
.cf-turnstile { max-width: 100%; overflow-x: clip }

.form-status {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
  text-wrap: pretty;
}
.form-status[data-tone="ok"]    { color: var(--color-secondary) }
.form-status[data-tone="error"] { color: var(--color-danger-on-light) }


/* ── INFO ROW — icon + label + value ───────────────────────────*/
.info-row { display: flex; gap: var(--space-4); align-items: flex-start }
.info-row svg { flex: none; width: var(--icon-md); height: var(--icon-md); color: var(--color-accent) }


/* ═══════════════ MEDIA ═══════════════ */

.media {
  display: block;                  /* the class may sit on an <a>, which is
                                      inline by default — and aspect-ratio
                                      does not apply to inline elements */
  position: relative;
  overflow: hidden;
  margin: 0;                       /* figures ship with UA margin */
  border-radius: var(--radius-xl);
  aspect-ratio: var(--ratio-media-full);
  background: var(--color-surface-alt);
}
.media[data-ratio="wide"]     { aspect-ratio: var(--aspect-wide) }
.media[data-ratio="card"]     { aspect-ratio: var(--ratio-media-card) }
.media[data-ratio="square"]   { aspect-ratio: var(--aspect-square) }
.media[data-ratio="portrait"] { aspect-ratio: var(--aspect-portrait) }
.media[data-ratio="cinema"]   { aspect-ratio: var(--aspect-cinema) }
.media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--media-focal, var(--focal-center));
}
/* ── MEDIA ACTION — overlay CTA on a media block ───────────────
   The static-map / video-poster pattern: the WHOLE media is the
   link, and a button-styled label sits in a corner announcing what
   a click does. No third-party embed, no API key, no cookies.
   Options: data-corner="start" (default) · "end"                  */
.media-action {
  position: absolute;
  inset-block-end: var(--space-4);
  inset-inline-start: var(--space-4);
  z-index: var(--layer-base);
  box-shadow: var(--shadow-card);
}
.media-action[data-corner="end"] {
  inset-inline-start: auto;
  inset-inline-end: var(--space-4);
}
.media-action svg { width: var(--icon-sm); height: var(--icon-sm); flex: none }
/* The overlay is decorative markup inside the link — the anchor owns
   the interaction, so lift the whole media on hover, not just the pill. */
a.media:hover .media-action { background: var(--color-accent-hover) }

/* Focal point — named positions from the focal tokens. */
.media[data-focal="top"]    { --media-focal: var(--focal-top) }
.media[data-focal="bottom"] { --media-focal: var(--focal-bottom) }
.media[data-focal="left"]   { --media-focal: var(--focal-left) }
.media[data-focal="right"]  { --media-focal: var(--focal-right) }


/* ═══════════════ MISC BLOCKS ═══════════════ */

.rule { height: var(--border-thin); background: var(--color-border) }

/* Band is typography only — its surface comes from data-surface,
   like every other section. One way to set a background, not two. */
.band {
  font-weight: var(--weight-extrabold);
  font-size: var(--text-sm);
}
.band .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  flex-wrap: wrap;
}

.link-plain { color: var(--color-text) }
.link-plain:hover { color: var(--color-accent) }

.divider-label {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--text-xs);
  font-weight: var(--weight-extrabold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-text-dim);
}
.divider-label::before,
.divider-label::after {
  content: "";
  height: var(--border-base);
  flex: 1;
  max-width: var(--space-16);
  background: var(--color-secondary);
}

.script {
  font-family: var(--font-script);
  font-size: var(--display-sm);
  line-height: var(--leading-snug);
  color: var(--color-text-dim);
}


/* ═══════════════ FOOTER ═══════════════ */

/* Footer surface now comes from data-surface on its section, like
   every other block — nothing special-cased here. */
.footer-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-10);
  flex-wrap: wrap;
}
.footer-bottom {
  border-top: var(--border-thin) solid var(--color-hairline);
  padding-top: var(--space-5);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  flex-wrap: wrap;
  font-size: var(--text-sm);
  color: var(--color-text-dim);
}
.footer-bottom a:hover { color: var(--color-accent) }
.emblem {
  width: var(--emblem-size);
  height: auto;
  opacity: var(--emblem-opacity);
}

/* ── SOCIAL — block contract ───────────────────────────────────
   Guarantees:
   · No box or padding by default, so the link sits flush with
     whatever label is above it — a pill's inset breaks that.
   · ONE link keeps its handle visible to fill the space; TWO OR
     MORE drop to icons only so the row stays balanced. Handled
     automatically — the markup is the same either way.
   · Hidden handles stay in the accessibility tree, so icon-only
     links are still named for screen readers.
   Options: data-labels="always" · data-style="pill"
   ─────────────────────────────────────────────────────────────── */
.social-list {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  flex-wrap: wrap;
}
.social-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-target-min);
  color: var(--color-text-dim);
  font-weight: var(--weight-semibold);
  transition: color var(--duration-fast) var(--ease-out);
}
.social-link:hover { color: var(--color-accent) }
.social-link svg { flex: none; width: var(--icon-md); height: var(--icon-md) }

/* Two or more links → handles are visually hidden, icons only. */
.social-list:has(> :nth-child(2)) .social-label {
  position: absolute;
  width: 1px; height: 1px;   /* a11y-constant — visually-hidden idiom */
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.social-list[data-labels="always"] .social-label {
  position: static;
  width: auto; height: auto;
  clip-path: none;
}

/* Optional pill treatment, off by default. */
.social-link[data-style="pill"] {
  padding: var(--space-3) var(--space-5);
  border: var(--border-base) solid currentColor;
  border-radius: var(--radius-full);
}


/* ═══════════════ MOTION BASELINE ═══════════════
   Four layers, all driven by the motion tokens:
     1. ENTRANCE  — above-fold content on page load (CSS only)
     2. REVEAL    — below-fold content on scroll    (JS, fail-visible)
     3. MEDIA     — images fade in as they decode   (JS, fail-visible)
     4. NAVIGATION— cross-page view transitions     (CSS only)
   Every layer is off under prefers-reduced-motion.
   ═══════════════════════════════════════════════════════════════ */

@keyframes enter-rise {
  from { opacity: 0; transform: translateY(var(--motion-distance)) }
  to   { opacity: 1; transform: none }
}
@keyframes enter-fade {
  from { opacity: 0 }
  to   { opacity: 1 }
}

/* ── 1 · ENTRANCE ───────────────────────────────────────────────
   The first section of every page animates in on load. Uses a CSS
   animation rather than a JS-toggled class deliberately: an
   animation self-completes and leaves its final state, so it can
   never strand content invisible the way a missed transition can.
   Applied automatically to the first section — no markup needed. */
main > .section:first-of-type .stack,
main > .section:first-of-type .media {
  animation: enter-rise var(--motion-enter);
}
/* Stagger: each direct child of the hero starts slightly later. */
main > .section:first-of-type .split > *:nth-child(1) { animation-delay: 0ms }
main > .section:first-of-type .split > *:nth-child(2) { animation-delay: calc(var(--motion-stagger) * 2) }
main > .section:first-of-type .media { animation-name: enter-fade }


/* ── 2 · REVEAL ─────────────────────────────────────────────────
   Content is visible by default. The .js-reveal class is added by
   script ONLY to elements below the fold, so any JS failure leaves
   content visible rather than blank. */
.js-reveal {
  opacity: 0;
  transform: translateY(var(--motion-distance));
  transition: opacity var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-out);
}
.js-reveal[data-revealed="true"] { opacity: 1; transform: none }


/* ── 3 · MEDIA LOAD ─────────────────────────────────────────────
   Images fade in once decoded instead of popping. Fail-visible:
   the hidden state is only applied by JS to images that are not
   yet complete, so a JS failure leaves every image showing. */
img[data-loading] { opacity: 0 }
img[data-loaded]  { opacity: 1 }
img[data-loading], img[data-loaded] { transition: var(--motion-fade) }


/* ── 4 · NAVIGATION ─────────────────────────────────────────────
   Cross-document view transitions. Nav and footer are named so they
   persist across the change instead of flickering. Browsers without
   support simply navigate normally. */
@view-transition { navigation: auto }

.nav    { view-transition-name: site-nav }
.footer { view-transition-name: site-footer }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--duration-base);
  animation-timing-function: var(--ease-out);
}


/* ── Reduced motion — disables all four layers ─────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto }

  /* The standard reduced-motion reset. 0.01ms rather than 0 so
     animationend/transitionend still fire for any listener. */
  *, *::before, *::after {
    animation-duration: 0.01ms !important;   /* a11y-constant */
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;  /* a11y-constant */
  }
  .js-reveal, .js-reveal[data-revealed="true"] { opacity: 1; transform: none }
  img[data-loading] { opacity: 1 }

  @view-transition { navigation: none }
}


/* ═══════════════ BREAKPOINT — layout change only ═══════════════
   The only media query on the site. Everything else is fluid.
   768px = --bp-md (tablet). */

@media (max-width: 47.99rem) {
  .nav-links { display: none }
  .nav-burger { display: flex }
  .drawer { display: block }

  /* Footer contract: once the groups stack, every group centers on its
     own axis — including nested rows and stacks, and overriding any
     data-align set for the desktop layout. Small pairs like
     Privacy / Terms stay side by side because .row only wraps when it
     runs out of width. */
  .footer-top,
  .footer-bottom,
  .footer .row      { justify-content: center; text-align: center }
  .footer .stack,
  .footer .stack[data-align="end"] { align-items: center; text-align: center }

  .band .container { justify-content: center; text-align: center }

  /* Single column here, so `stretch` buys nothing — and Safari has
     resolved aspect-ratio inconsistently on stretched grid items,
     which can collapse the image to zero height. Opting out removes
     the ambiguity without changing how anything looks. */
  .split > .media { align-self: start }
}
