/* ═══════════════════════════════════════════════════════════════════════════
   VIVERE COLORADO — shared stylesheet
   215 Ute Street, Delta CO · "to live Colorado"

   MOBILE-FIRST. Base styles are the phone. Larger screens arrive via
   min-width only. The previous build was the inverse — 7 max-width queries
   and 0 min-width — so every phone parsed and then overrode a desktop
   layout it never used.

   ONE stylesheet, not three. The rebuild plan specced base/components/motion
   as separate files; they are folded into one long-cached request here
   because three files is three round trips on rural mobile data to save
   nothing — @layer already gives the separation the split was for.

   Layer order is the cascade contract. Anything later wins regardless of
   specificity, so components never has to out-specify base, and the five
   pages can't start a specificity war with each other.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer tokens, base, components, motion, utilities;

/* ───────────────────────────────────────────────────────────── TOKENS ─── */
@layer tokens {
  :root {
    --navy:       #183C6B;   /* Delta Roadhouse navy - the campus container */
    /* Palette derived from the emblem: ember flame, brass inner tongue,
       charcoal ground, bone type. Never write a raw hex below this block. */
    --ember:      #A34F22;   /* Delta Roadhouse 'Gray Brown' - art-verified #A34F21 */
    --ember-dk:   oklch(from var(--ember) calc(l - .12) c h);
    --ember-lt:   oklch(from var(--ember) calc(l + .14) calc(c - .04) h);
    /* --ember reaches only 4.43:1 against white, which fails WCAG AA for
       normal-size text. Any SOLID FILL carrying white text uses this
       darker step. Use --ember for accents, borders and glows only.
       (Re-measured 2026-08-20 after --ember was corrected to the art
       value #C9531F; it was 3.80:1 at the old #E4531F, so this step is
       still required, just less so.) */
    --ember-fill: #A34F22;   /* white on rust = 5.68:1, passes AA normal.
       The old darker step existed because #C9531F only reached 4.43:1;
       the Roadhouse rust is darker and no longer needs it. */
    --ember-fill-hover: oklch(from var(--ember-fill) calc(l - .07) c h);

    --brass:      #B57E28;   /* Delta Roadhouse 'Gray Orange' - art-verified #B57F29 */
    --brass-lt:   oklch(from var(--brass) calc(l + .16) c h);
    --brass-dk:   oklch(from var(--brass) calc(l - .18) c h);

    --char:       #14335A;   /* campus navy, mid - panels */
    --char-2:     oklch(from var(--char) calc(l + .05) c h);
    --ink:        #0C1F39;   /* campus navy, deep - the ground */
    --bone:       #E5D6BD;   /* Delta Roadhouse 'Light Orange' - art-verified #E6D6BD */
    --cream:      #EFE4D0;
    --paper:      #F7F1E6;
    --muted:      oklch(48% 0.02 60);
    --rule:       oklch(86% 0.02 78);
    --ok:         oklch(58% 0.13 150);

    --f-display: 'Anton', Impact, 'Haettenschweiler', sans-serif;
    --f-body:    'Barlow', system-ui, -apple-system, 'Segoe UI', sans-serif;

    /* Fluid scale. Lower bounds are the phone value — this is where
       mobile-first actually pays: no query needed to size type.

       RESCALED 2026-08-17. The first pass topped out at a 112px h1, a
       23px lede and 23px menu-item names — measured, not estimated. That
       reads as large-print: it fills the screen without conveying
       importance, because when everything is big, nothing is emphasised.
       The ratios between steps are what create hierarchy, not the
       absolute sizes, so the whole scale came down and the RATIOS were
       kept. Display steps fell hardest (a 112px headline is a poster,
       not a page); body text barely moved, since that was already in a
       comfortable range and is what people actually read. */
    /* Deliberately NOT reduced as hard as the display steps. The
       complaint was oversized headings, not undersized captions — and
       this step carries menu descriptions and card body copy, which are
       read, not scanned. Shrinking it in sympathy with the h1 would
       trade one readability problem for another. */
    --step--1: clamp(.85rem, .82rem + .14vw, .91rem);
    --step-0:  clamp(.97rem, .94rem + .17vw, 1.04rem);
    --step-1:  clamp(1.1rem, 1.03rem + .34vw, 1.28rem);
    --step-2:  clamp(1.5rem, 1.28rem + .95vw, 2rem);
    --step-3:  clamp(2rem, 1.55rem + 2.1vw, 3.1rem);
    --step-4:  clamp(2.35rem, 1.75rem + 3.6vw, 4.25rem);

    /* ── Spacing scale ───────────────────────────────────────────────
       Audited 2026-08-17 and rebuilt. The page had SIX different grid
       gaps in play — .7 / .9 / 1.1 / 1.3 / 1.4rem plus the button row —
       chosen ad hoc as each component was written. Nothing was
       individually wrong; collectively there was no system, which is
       what "the spacing needs fixed" actually looks like. These six
       steps now cover every gap and stack on the page, so two things
       that should feel related are spaced identically rather than
       coincidentally.  */
    --sp-2xs: .4rem;
    --sp-xs:  .65rem;
    --sp-sm:  .9rem;
    --sp-md:  1.25rem;
    --sp-lg:  1.9rem;
    --sp-xl:  2.75rem;

    /* Section rhythm. Was clamping to 7.5rem — 98px of padding top AND
       bottom, so ~196px of dead air between every section on desktop.
       That reads as a stretched page rather than a generous one, and it
       was a large part of why the site felt like it was scrolling
       forever. ~76px a side is the corrected value. */
    --sp-fluid: clamp(2.75rem, 1.9rem + 3.6vw, 5.5rem);
    --gutter:   1.25rem;
    --measure:  64ch;

    --r: 14px; --r-lg: 22px; --r-pill: 999px;
    --sh-1: 0 1px 2px oklch(17% .014 55/.07), 0 4px 12px oklch(17% .014 55/.07);
    --sh-2: 0 2px 6px oklch(17% .014 55/.09), 0 12px 30px oklch(17% .014 55/.11);
    --sh-3: 0 4px 14px oklch(17% .014 55/.11), 0 26px 60px oklch(17% .014 55/.16);
    /* Warm, ember-tinted shadow ramp — for anything floating on the dark
       canvas (.home). A neutral grey shadow under a light card on a warm
       dark ground reads as pasted-on; a shadow tinted toward the ground's
       own hue reads as the card actually sitting in the scene. */
    --sh-warm-1: 0 2px 4px oklch(21% .05 38/.28), 0 6px 16px oklch(21% .05 38/.22);
    --sh-warm-2: 0 6px 14px oklch(21% .05 38/.32), 0 18px 44px oklch(21% .05 38/.28);
    --sh-warm-3: 0 10px 24px oklch(21% .05 38/.36), 0 34px 80px oklch(21% .05 38/.32);

    /* Published by JS from the nav's measured height, for scroll offsets
       ONLY. Nothing that affects the nav's own box may read it — see the
       growth-loop note in components. */
    --nav-h: 60px;
  }
}

/* ─────────────────────────────────────────────────────────────── BASE ─── */
@layer base {
  *, *::before, *::after { box-sizing: border-box; }
  * { margin: 0; padding: 0; }

  html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--nav-h) + 12px);
  }

  /* clip, NOT hidden. `hidden` makes <body> a scroll container, which
     silently breaks position:sticky on the nav. `clip` contains horizontal
     overflow without creating one. */
  /* 1.55, not 1.62 — the old value was tuned for a smaller body size.
     Stacked on top of the (previously oversized) type it was what made
     the page feel double-spaced as well as large-print. */
  body {
    font-family: var(--f-body);
    font-size: var(--step-0);
    line-height: 1.55;
    color: var(--ink);
    background: var(--cream);
    overflow-x: clip;
  }

  h1, h2, h3, h4 {
    font-family: var(--f-display);
    font-weight: 400;
    line-height: .95;
    letter-spacing: .01em;
    text-transform: uppercase;
    text-wrap: balance;
  }
  p { text-wrap: pretty; }
  a { color: var(--ember-dk); }
  img, svg, video { display: block; max-width: 100%; }
  button, input, select, textarea { font: inherit; }
  button { cursor: pointer; }

  :focus-visible { outline: 3px solid var(--ember); outline-offset: 3px; border-radius: 4px; }

  /* The viewport is viewport-fit=cover, so the page paints under the display
     cutout. Backgrounds bleed there; content must not. Padding (not margin)
     keeps section backgrounds full-bleed while holding text clear in
     landscape. env() resolves to 0 on every device without a cutout. */
  .wrap {
    width: min(1120px, 100% - (var(--gutter) * 2));
    margin-inline: auto;
    padding-inline: env(safe-area-inset-left) env(safe-area-inset-right);
  }
  .wrap-narrow { width: min(760px, 100% - (var(--gutter) * 2)); }

  .sr {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .skip {
    position: absolute; left: -999px; top: 0; z-index: 999;
    background: var(--ember-fill); color: #fff; padding: .7em 1.2em;
  }
  .skip:focus { left: 0; }

  /* ── The dark canvas (homepage only) ────────────────────────────────
     The first build alternated opaque paper/cream/bone/char section
     backgrounds down the page — every scroll boundary was a flat ruled
     line where one solid colour stopped and another started. body.home
     replaces that with ONE continuous warm-dark ground for the whole
     page; sections stop carrying their own opaque fills (see .sched,
     .campus below) and instead let content float on this ground as
     paper/glass cards. book/index.html, privacy.html and 404.html don't
     carry this class and keep the light utility-page treatment — this
     is specifically the "first contact" page's atmosphere, not a
     site-wide repaint. */
  body.home {
    background: var(--char);
    color: oklch(92% .018 78 / .88);
  }

  /* ── Ambient layer ───────────────────────────────────────────────────
     Three large, heavily blurred colour fields drifting on independent,
     very slow timers (55-80s). This is what makes the dark canvas read
     as one continuous atmosphere rather than a flat fill — it is NOT
     the "nothing loops but the status pulse" rule being broken (see the
     motion layer): that rule is about foreground UI competing for
     attention. This sits behind every card, is never sharp, and moves
     too slowly to consciously perceive as animation — only as the
     scene feeling alive. Fixed, so it doesn't repaint or scroll-jank;
     one shared instance for the whole page rather than per-section. */
  .ambient {
    position: fixed; inset: 0; z-index: -1;
    overflow: hidden; pointer-events: none;
  }
  .ambient .orb {
    position: absolute; border-radius: 50%; filter: blur(90px);
    opacity: .55; will-change: transform;
  }
  .ambient .orb-a {
    width: 62vw; height: 62vw; top: -22vw; left: -14vw;
    background: radial-gradient(circle, oklch(from var(--ember) l c h / .5), transparent 68%);
    animation: drift-a 62s ease-in-out infinite alternate;
  }
  .ambient .orb-b {
    width: 54vw; height: 54vw; bottom: -20vw; right: -16vw;
    background: radial-gradient(circle, oklch(from var(--brass) l c h / .4), transparent 70%);
    animation: drift-b 78s ease-in-out infinite alternate;
  }
  .ambient .orb-c {
    width: 40vw; height: 40vw; top: 38%; left: 50%;
    background: radial-gradient(circle, oklch(from var(--ember-fill) l c h / .32), transparent 72%);
    animation: drift-c 70s ease-in-out infinite alternate;
  }
  @keyframes drift-a { from { transform: translate(0, 0); } to { transform: translate(6vw, 8vw); } }
  @keyframes drift-b { from { transform: translate(0, 0); } to { transform: translate(-7vw, -6vw); } }
  @keyframes drift-c { from { transform: translate(-50%, 0); } to { transform: translate(-46%, -10vh); } }

  /* ── Background layer 2: ember field ────────────────────────────────
     A slow upward drift of small warm points — embers off a griddle.
     It's the subject rendered as atmosphere rather than decoration,
     which is the difference between an effect that belongs and one
     that's just switched on.

     Built as two repeating radial-gradient sheets rather than DOM nodes:
     ~30 points cost two painted layers instead of 30 elements, and the
     whole thing animates on `transform` alone (compositor-only, no
     layout, no paint per frame). Each sheet is exactly one tile tall
     and translates by exactly its own tile height, so the loop is
     seamless with no visible reset. */
  .ambient .embers {
    position: absolute; inset: -10% -5% 0;
    opacity: .5; will-change: transform;
    background-repeat: repeat;
  }
  .ambient .embers-near {
    background-image:
      radial-gradient(1.6px 1.6px at 12% 18%, oklch(75% .17 45 / .85), transparent 60%),
      radial-gradient(1.3px 1.3px at 68% 42%, oklch(80% .14 70 / .7), transparent 60%),
      radial-gradient(1.9px 1.9px at 34% 72%, oklch(72% .19 40 / .8), transparent 60%),
      radial-gradient(1.2px 1.2px at 87% 88%, oklch(78% .12 78 / .6), transparent 60%),
      radial-gradient(1.5px 1.5px at 52% 6%,  oklch(74% .18 44 / .75), transparent 60%);
    background-size: 420px 420px;
    animation: ember-rise 34s linear infinite;
  }
  /* Farther layer: smaller, dimmer, slower — parallax depth, so the
     field reads as a volume of air rather than a flat texture. */
  .ambient .embers-far {
    background-image:
      radial-gradient(1.1px 1.1px at 26% 30%, oklch(70% .15 50 / .5), transparent 60%),
      radial-gradient(.9px .9px  at 78% 64%, oklch(74% .12 76 / .42), transparent 60%),
      radial-gradient(1.2px 1.2px at 44% 90%, oklch(68% .16 42 / .48), transparent 60%);
    background-size: 300px 300px;
    opacity: .34;
    animation: ember-rise-far 58s linear infinite;
  }
  @keyframes ember-rise     { to { transform: translate3d(14px, -420px, 0); } }
  @keyframes ember-rise-far { to { transform: translate3d(-10px, -300px, 0); } }

  /* ── Background layer 3: vignette ───────────────────────────────────
     Darkens the far edges so the eye settles toward the middle where the
     content is. Static — a moving vignette would fight the orbs.
     `screen`-free, pure multiply-ish darkening via a transparent-centre
     radial, so it never lightens anything. */
  .ambient .vignette {
    position: absolute; inset: 0;
    background: radial-gradient(ellipse 78% 64% at 50% 42%,
      transparent 0%, transparent 52%, oklch(12% .01 55 / .55) 100%);
  }

  /* ── Background layer 4: grain ──────────────────────────────────────
     The page already had .grain, but only on two sections — so the
     texture stopped and started mid-scroll. This is one fixed sheet over
     the whole canvas instead. Deliberately NOT animated: moving grain
     is film, and at this size it reads as noise on a broken screen. */
  .ambient .grain-layer {
    position: absolute; inset: 0; opacity: .055; mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='4'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
  }

  /* ── Scroll reactivity ──────────────────────────────────────────────
     The whole field drifts and warms slightly as you move down the page,
     so the background is a participant in the scroll rather than a
     static wallpaper behind it. Scroll-driven: no listener, no JS, and
     absent entirely where unsupported. */
  @supports (animation-timeline: scroll()) {
    .ambient {
      animation: ambient-scroll linear;
      animation-timeline: scroll(root);
    }
    @keyframes ambient-scroll {
      from { transform: translateY(0)     scale(1);    filter: hue-rotate(0deg)  saturate(1); }
      to   { transform: translateY(-40px) scale(1.08); filter: hue-rotate(-12deg) saturate(1.25); }
    }
  }
}

/* ───────────────────────────────────────────────────────── COMPONENTS ─── */
@layer components {

  /* ── Nav ─────────────────────────────────────────────────────────── */
  .nav {
    position: sticky; top: 0; z-index: 60;
    background: oklch(from var(--char) l c h / .94);
    backdrop-filter: blur(14px) saturate(1.3);
    border-bottom: 3px solid var(--ember);
  }
  @supports not (backdrop-filter: blur(1px)) { .nav { background: var(--char); } }

  /* Height is a LITERAL, never var(--nav-h). --nav-h is measured FROM this
     element, so consuming it here creates a ResizeObserver feedback loop
     that grows the nav by its border width every tick. */
  .nav-in {
    display: flex; align-items: center; justify-content: space-between;
    gap: .5rem; height: 60px;
  }
  .brand { display: flex; align-items: center; text-decoration: none; }
  /* Height-driven so the 60px row stays the authority and the image can
     never push the nav taller. Intrinsic art is 1368x548. */
  .brand img { height: 21px; width: auto; }

  /* Type lockup - stands in for a mark that does not exist yet.
     Deliberate: a wordmark set well in the campus frame reads as intentional;
     a rushed logo reads as amateur and then has to be unpicked. */
  .brand .wm { display: flex; flex-direction: column; line-height: 1; }
  .brand .wm b {
    font-family: var(--f-display); font-weight: 400;
    font-size: 1.05rem; letter-spacing: .015em;
    color: var(--bone); text-transform: uppercase;
  }
  .brand .wm i {
    font-style: normal; font-size: .5rem; letter-spacing: .3em;
    text-transform: uppercase; color: var(--brass); margin-top: .22rem;
  }
  @media (min-width: 48rem) {
    .brand .wm b { font-size: 1.3rem; }
    .brand .wm i { font-size: .56rem; }
  }

  .nav-links { display: none; }
  .nav-links a {
    color: oklch(92% .018 78/.86); text-decoration: none;
    font-weight: 600; font-size: var(--step--1);
    padding: .75rem; border-radius: var(--r-pill); text-align: center;
    transition: background .2s, color .2s;
  }
  .nav-links a:hover, .nav-links a[aria-current="page"] {
    background: oklch(100% 0 0/.12); color: #fff;
  }
  /* Mobile drawer is the BASE state — no query needed to get it. */
  .nav-links.open {
    display: flex; flex-direction: column; align-items: stretch; gap: .2rem;
    position: absolute; inset: 100% 0 auto 0;
    background: oklch(from var(--ink) l c h / .98);
    border-bottom: 3px solid var(--ember);
    padding: .7rem calc(var(--gutter) + env(safe-area-inset-right)) 1.1rem
                   calc(var(--gutter) + env(safe-area-inset-left));
  }

  /* 44px minimum. This is the only route to the menu on a phone. */
  .burger {
    display: grid; place-items: center;
    min-width: 44px; min-height: 44px;
    background: none; border: 0; color: #fff;
    font-size: 1.5rem; line-height: 1; border-radius: var(--r-pill);
  }

  .order-btn {
    background: var(--ember-fill); color: #fff; border: 0;
    border-radius: var(--r-pill); padding: .55rem .9rem;
    font-weight: 700; font-size: var(--step--1);
    display: flex; align-items: center; gap: .5rem;
    transition: background .2s, transform .15s;
  }
  .order-btn:hover { background: var(--ember-fill-hover); }
  .cart-count {
    background: #fff; color: var(--ember-dk); border-radius: var(--r-pill);
    min-width: 1.35em; height: 1.35em; display: grid; place-items: center;
    font-size: .76rem; font-weight: 700; font-variant-numeric: tabular-nums;
  }
  /* Hide the badge entirely when the cart is empty. */
  .order-btn:has(.cart-count[data-n="0"]) .cart-count { display: none; }

  @media (min-width: 56rem) {
    .nav-in { gap: 1rem; }
    .brand img { height: 26px; }
    .burger { display: none; }
    .nav-links { display: flex; flex-direction: row; align-items: center; gap: .15rem; }
    .nav-links a { padding: .45rem .8rem; }
    .order-btn { padding: .55rem 1.15rem; }
  }

  /* ── Status rail — the first thing on the page ───────────────────── */
  .status {
    background: var(--ink); color: var(--bone);
    font-size: var(--step--1);
    border-bottom: 1px solid oklch(100% 0 0/.1);
  }
  .status-in {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: .5rem 1.4rem; padding: .6rem 0;
  }
  .pill { display: inline-flex; align-items: center; gap: .45rem; font-weight: 600; }
  .led {
    width: 8px; height: 8px; border-radius: 50%; background: var(--ok);
    box-shadow: 0 0 0 0 oklch(58% .13 150/.6);
    animation: ping 2.4s ease-out infinite;
  }
  .led.off { background: var(--muted); animation: none; box-shadow: none; }
  @keyframes ping {
    0%        { box-shadow: 0 0 0 0 oklch(58% .13 150/.55); }
    70%, 100% { box-shadow: 0 0 0 9px oklch(58% .13 150/0); }
  }
  .status .left { color: var(--brass-lt); font-variant-numeric: tabular-nums; }
  .status .left b { font-family: var(--f-display); font-size: 1.15rem; color: var(--ember-lt); }
  @media (min-width: 40rem) { .status .left { margin-left: auto; } }

  /* ── Hero ────────────────────────────────────────────────────────── */
  /* No base fill — body.home already provides it, and this section sits
     first, exactly where the ambient orbs are largest. An opaque rect
     here would blot out the one place the effect is most visible. */
  .hero {
    position: relative; overflow: hidden;
    color: var(--bone);
    padding: calc(var(--sp-fluid) * 1.25) 0 calc(var(--sp-fluid) * 1.1);
  }
  .hero::before {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background:
      radial-gradient(ellipse 70% 55% at 50% -10%, oklch(from var(--ember) l c h / .34), transparent 70%),
      repeating-linear-gradient(114deg, oklch(100% 0 0/.03) 0 2px, transparent 2px 17px);
  }
  /* Emblem watermark. Decorative only — the wordmark is already in the nav,
     so this carries no information and stays out of the accessibility tree.
     Brought inboard and up in opacity from .07: at 420px/-14% offset it
     was mostly off-canvas, so the right third of the hero read as empty
     space rather than as composition. Now it anchors that column. */
  .hero-mark {
    position: absolute; right: 2%; top: 50%; translate: 0 -50%;
    width: min(52vw, 340px); opacity: .1; pointer-events: none;
    animation: mark-breathe 26s ease-in-out infinite alternate;
  }
  @media (min-width: 60rem) { .hero-mark { right: 6%; width: 400px; opacity: .13; } }
  @keyframes mark-breathe {
    from { transform: scale(1) rotate(0deg); }
    to   { transform: scale(1.06) rotate(3deg); }
  }
  .hero-in { position: relative; z-index: 1; max-width: 46rem; }
  .eyebrow {
    display: inline-flex; align-items: center; gap: .55rem;
    font-size: .72rem; font-weight: 700; letter-spacing: .2em;
    text-transform: uppercase; color: var(--brass-lt); margin-bottom: 1.1rem;
  }
  .eyebrow::before { content: ""; width: 28px; height: 2px; background: var(--ember); }
  /* 18ch, not 14ch. At the old 112px size, 14ch forced this headline onto
     three lines with an orphaned "&". At the corrected size 18ch lets it
     break in two, which is both calmer and faster to read. */
  .hero h1 { font-size: var(--step-4); color: #fff; margin-bottom: .3em; max-width: 18ch; }
  .hero h1 em { font-style: normal; color: var(--ember); }
  .hero .lede {
    font-size: var(--step-1); max-width: 44ch;
    color: oklch(92% .018 78/.85); margin-bottom: 2em;
  }
  .hero-foot {
    margin-top: 2.8rem; padding-top: 1.5rem;
    border-top: 1px solid oklch(92% .018 78/.18);
    font-size: var(--step--1); color: oklch(92% .018 78/.72);
  }
  .hero-foot b { color: #fff; }

  /* ── Buttons ─────────────────────────────────────────────────────── */
  .btns { display: flex; flex-wrap: wrap; gap: var(--sp-sm); }
  .btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
    padding: .9em 1.7em; border-radius: var(--r-pill);
    text-decoration: none; font-weight: 700; font-size: var(--step--1);
    border: 0; min-height: 44px;
    transition: transform .15s, box-shadow .15s, background .2s;
  }
  .btn-p { background: var(--ember-fill); color: #fff; }
  .btn-p:hover { background: var(--ember-fill-hover); }
  .btn-s { background: oklch(100% 0 0/.07); color: #fff; border: 1.5px solid oklch(92% .018 78/.4); }
  .btn-s:hover { background: oklch(100% 0 0/.14); }
  .btn:hover { transform: translateY(-2px); box-shadow: var(--sh-3); }

  /* ── Sections ────────────────────────────────────────────────────── */
  section { scroll-margin-top: calc(var(--nav-h) + 8px); }
  .sec { padding: var(--sp-fluid) 0; }
  .sec-h { font-size: var(--step-2); margin-bottom: var(--sp-xs); }
  .sec-sub { color: var(--muted); max-width: 58ch; margin-bottom: var(--sp-lg); }
  /* No fill on .on-dark itself — on the homepage that fill IS body.home's
     --char, and painting the same colour again as an opaque section
     rectangle would block the ambient layer sitting behind it (that
     layer is what the fill used to BE, before the whole page went dark;
     see body.home above). .on-dark now only carries the text-colour
     contract; .method and .quote-section keep their own local tint
     because they still want a slightly distinct glow, not the section
     boundary that caused the original complaint. */
  .on-dark { color: var(--bone); }
  .on-dark .sec-h { color: #fff; }
  .on-dark .sec-sub { color: oklch(92% .018 78/.78); }

  /* ── Floating glass panel (Order Ahead CTA) ─────────────────────────
     True glassmorphism — translucent, blurred, bordered in light rather
     than colour, so the ambient orbs read as glowing through it rather
     than a flat card dropped on top of a flat background. */
  /* Centred, and centred on the page — the deliberate break in a layout
     that was otherwise flush-left in all six sections. Alignment change
     is doing real work here rather than decoration: this is the page's
     single primary call to action, and centring is what separates it
     from the reading columns above and below it. */
  .cta-panel {
    background: oklch(100% 0 0 / .05);
    border: 1px solid oklch(100% 0 0 / .12);
    backdrop-filter: blur(18px) saturate(1.4);
    border-radius: var(--r-lg);
    padding: clamp(1.8rem, 1.4rem + 2vw, 3rem);
    box-shadow: var(--sh-warm-2);
    max-width: 44rem; margin-inline: auto; text-align: center;
  }
  .cta-panel .sec-sub { margin-inline: auto; }

  /* ── How-to-order steps — floating numbered glass cards ─────────────
     Was a plain <ol>. The numerals reuse the Method section's oversized-
     ghost-digit treatment so the two "process" sections read as one
     family rather than two different UI patterns. */
  /* Second deliberate alignment break — a centred heading over a four-up
     row reads as a process/diagram, which is what this section is,
     versus the left-aligned reading columns around it. */
  .howto .sec-h, .howto .sec-sub { text-align: center; }
  .howto .sec-sub { margin-inline: auto; }
  .howto-steps {
    list-style: none; display: grid; grid-template-columns: 1fr;
    gap: var(--sp-md); margin-top: var(--sp-lg); counter-reset: howto;
  }
  @media (min-width: 30rem) {
    .howto-steps { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
  }
  .howto-steps li {
    position: relative; counter-increment: howto;
    background: oklch(100% 0 0 / .045); border: 1px solid oklch(100% 0 0 / .1);
    backdrop-filter: blur(14px);
    border-radius: var(--r-lg); padding: 1.5rem 1.5rem 1.4rem;
    display: flex; flex-direction: column; gap: .6rem;
    transition: transform .3s cubic-bezier(.2,.7,.3,1), background .3s, border-color .3s;
  }
  .howto-steps li:hover {
    transform: translateY(-5px);
    background: oklch(100% 0 0 / .08); border-color: oklch(from var(--ember) l c h / .4);
  }
  .howto-steps b {
    font-family: var(--f-display); font-size: 2.6rem; line-height: 1;
    color: var(--ember-lt); font-variant-numeric: tabular-nums;
  }
  .howto-steps span { font-size: var(--step--1); color: oklch(92% .018 78/.85); }
  .howto-steps em { font-style: normal; color: #fff; }

  /* ── Reserved slots — the swap-in contract ───────────────────────── */
  /* Every unknown ships as a box with a fixed aspect-ratio and a data-slot
     attribute, filled today by type or vector. When the real value lands it
     is an insertion, not a redesign, and layout shift is zero. */
  [data-slot] { display: inline-block; }
  .tbd {
    display: inline-block; font-size: .68rem; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase;
    padding: .16em .6em; border-radius: var(--r-pill);
    background: repeating-linear-gradient(45deg,
      oklch(96% .03 85), oklch(96% .03 85) 6px,
      oklch(92% .05 85) 6px, oklch(92% .05 85) 12px);
    color: oklch(45% .08 82); border: 1px solid var(--brass);
    vertical-align: .1em;
  }

  /* ── Schedule ────────────────────────────────────────────────────── */
  /* No section fill any more — the day chips are their own floating
     glass cards on the dark canvas, spaced apart rather than packed
     edge to edge inside a bordered paper band. */
  .days { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-sm); margin-bottom: var(--sp-lg); }
  @media (min-width: 25rem) { .days { grid-template-columns: repeat(4, 1fr); } }
  @media (min-width: 40rem) { .days { grid-template-columns: repeat(7, 1fr); } }
  .day {
    border: 1.5px solid transparent; border-radius: var(--r-lg);
    padding: 1.1rem .5rem; text-align: center; background: var(--paper);
    box-shadow: var(--sh-warm-1);
    transition: transform .28s cubic-bezier(.2,.7,.3,1), box-shadow .28s;
  }
  .day:hover { transform: translateY(-4px); box-shadow: var(--sh-warm-2); }
  /* "Standing" days are a warm-lit glass card rather than a near-black
     fill, which nearly vanished once the page around it went dark too —
     the ring is what reads as "selected", not a flat block. */
  .day.on {
    background: oklch(from var(--ember-fill) l c h / .16);
    border-color: oklch(from var(--ember) l c h / .55);
    color: #fff;
    backdrop-filter: blur(10px);
  }
  .day.avail {
    border-style: dashed; border-color: oklch(from var(--brass) l c h / .55);
    background: oklch(100% 0 0 / .04); backdrop-filter: blur(10px);
  }
  .day dt {
    font-family: var(--f-display); font-size: .95rem;
    letter-spacing: .06em; margin-bottom: .3rem;
  }
  .day.on dt { color: #fff; }
  .day.avail dt { color: var(--bone); }
  .day dd { font-size: .71rem; line-height: 1.3; color: var(--muted); }
  .day.on dd { color: oklch(92% .018 78/.85); }
  .day.avail dd { color: var(--brass-lt); }
  .day .dot {
    display: block; width: 7px; height: 7px; border-radius: 50%;
    background: var(--ember); margin: 0 auto .5rem;
    box-shadow: 0 0 8px 1px oklch(from var(--ember) l c h / .6);
  }
  .day.avail .dot { background: var(--brass); box-shadow: 0 0 8px 1px oklch(from var(--brass) l c h / .55); }
  /* Set by schedule.js from the real America/Denver date — not a guess. */
  .day.today { outline: 2px solid var(--ember); outline-offset: 3px; }

  /* Explicit dark text, not inherited — this is a light card and its
     nearest ancestor with an opinion (.on-dark) sets a LIGHT text colour
     for the dark canvas around it. Without this override the card would
     render light text on its own light background. */
  .notice {
    display: flex; gap: .8rem; align-items: flex-start;
    background: oklch(96% .04 85); color: var(--ink); border: 1.5px solid var(--brass);
    border-left-width: 5px; border-radius: var(--r);
    padding: .9rem 1.1rem; font-size: var(--step--1);
  }
  /* This is the ONE place staff free-text reaches the public page. An
     unbroken string — a pasted URL, or a phone in a pocket — has no wrap
     opportunity and blows the layout out horizontally. Verified: a
     160-char unbroken note produced 1043px of content in a 314px box.
     min-width:0 is needed too, because a flex child's default
     min-width:auto refuses to shrink below its content. */
  .notice p { overflow-wrap: anywhere; min-width: 0; }
  .notice[hidden] { display: none; }

  /* ── Menu ────────────────────────────────────────────────────────── */
  .menu-cat {
    font-size: var(--step-1); letter-spacing: .06em;
    margin-top: 2.4rem; padding-bottom: .4rem;
    border-bottom: 2px solid var(--rule); color: var(--ink);
  }
  .menu-cat:first-of-type { margin-top: 1.4rem; }
  .on-dark .menu-cat { color: #fff; border-color: oklch(92% .018 78/.22); }
  /* One card per row below 34rem, deliberately. Two-up was tried and
     measured: a 390px phone gives the grid a 350px container, so two
     cards plus the gap land at ~164px each — and these cards carry a
     dish name, a description, a size picker and an Add button. That is
     a cramped card, not a denser menu. The scroll length is instead
     addressed by tightening card padding on narrow screens below. */
  .menu-grid {
    display: grid; grid-template-columns: 1fr; gap: var(--sp-md);
    margin-top: var(--sp-lg); align-items: stretch;
  }
  @media (min-width: 34rem) {
    .menu-grid { grid-template-columns: repeat(auto-fit, minmax(272px, 1fr)); }
  }
  /* Flex column + margin-top:auto on the footer aligns every card's price
     row without subgrid, which would need the parent to declare matching
     rows that auto-fit columns can't guarantee. */
  /* color:var(--ink) is explicit, not incidental. This card's background
     is always light paper, but its section's ambient text colour now
     varies (light-on-dark on the homepage, dark-on-light nowhere else
     that uses .item) — without its own colour the card would inherit
     whatever the section around it decided, and light text on a light
     card is invisible. Every light card component below repeats this
     same fix for the same reason (.item, .day, .camp). */
  .item {
    container-type: inline-size;
    background: var(--paper); color: var(--ink); border: 1px solid var(--rule);
    border-radius: var(--r-lg); box-shadow: var(--sh-warm-1);
    display: flex; flex-direction: column;
    /* Tighter on phones — with one card per row, padding is pure scroll
       length. Full padding returns once cards sit side by side.
       Held in custom properties because .item-photo bleeds to the card
       edge by cancelling this exact padding with negative margins; when
       the two were separate literals they silently drifted out of sync
       and left a hairline of card visible beside the photo. */
    --item-pad-y: 1.15rem;
    --item-pad-x: 1.25rem;
    padding: var(--item-pad-y) var(--item-pad-x);
    transition: transform .3s cubic-bezier(.2,.7,.3,1), box-shadow .3s, border-color .3s;
  }
  @media (min-width: 34rem) {
    .item { --item-pad-y: 1.5rem; --item-pad-x: 1.6rem; }
  }
  .item:hover {
    transform: translateY(-6px) scale(1.012); box-shadow: var(--sh-warm-3);
    border-color: oklch(from var(--ember) l c h / .5);
  }
  .item.feature {
    background: linear-gradient(168deg, var(--char), var(--char-2));
    border: 1px solid oklch(from var(--ember) l c h / .3); color: var(--bone);
    position: relative; overflow: hidden;
  }
  .item.feature::before {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(circle at 88% 8%, oklch(from var(--ember) l c h / .3), transparent 58%);
  }
  @media (min-width: 42rem) { .item.feature { grid-column: span 2; } }
  @container (min-width: 420px) {
    .item.feature .feat-in {
      display: grid; grid-template-columns: 1fr auto; gap: 1.4rem; align-items: end;
    }
  }
  /* Derived from the card's own padding, never hardcoded — see .item.
     max-width:none is REQUIRED, not defensive: the base layer sets
     `img { max-width: 100% }` globally, which silently clamped this
     back to the content box and defeated the bleed entirely — the
     photo sat ~41px short of the card's right edge and had done since
     the rebuild. The width calc alone can't win that; it's a different
     property, so there's no specificity contest to lose, just a cap. */
  .item-photo {
    width: calc(100% + (var(--item-pad-x) * 2));
    max-width: none;
    margin: calc(var(--item-pad-y) * -1) calc(var(--item-pad-x) * -1) 1rem;
    aspect-ratio: 4/3; object-fit: cover;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
  }
  .feat-photo {
    width: 100%; aspect-ratio: 4/3; object-fit: cover;
    border-radius: var(--r); box-shadow: var(--sh-2); margin-top: 1rem;
  }
  @container (min-width: 420px) { .feat-photo { max-width: 220px; margin-top: 0; } }

  .tag {
    align-self: start; font-size: .65rem; font-weight: 700;
    letter-spacing: .1em; text-transform: uppercase;
    padding: .32em .8em; border-radius: var(--r-pill);
    margin-bottom: .7rem; width: max-content;
  }
  .tag-limited { background: var(--ember-fill); color: #fff; }
  .tag-value   { background: var(--brass); color: oklch(22% .05 82); }
  .tag-std     { background: var(--bone); color: var(--muted); }

  .item h3 { font-size: var(--step-1); margin-bottom: .3rem; }
  .item.feature h3 { color: #fff; font-size: var(--step-2); }
  .item p { font-size: var(--step--1); color: var(--muted); flex: 1; }
  .item.feature p { color: oklch(92% .018 78/.85); }
  .item-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; margin-top: auto; padding-top: .9rem;
    border-top: 1px solid var(--rule);
  }
  .item.feature .item-foot { border-color: oklch(92% .018 78/.2); }
  .price {
    font-family: var(--f-display); font-size: var(--step-1);
    letter-spacing: .03em; font-variant-numeric: tabular-nums;
  }
  .item.feature .price { color: var(--brass-lt); }
  .add {
    background: var(--ink); color: #fff; border: 0;
    border-radius: var(--r-pill); padding: .55em 1.2em;
    font-weight: 700; font-size: var(--step--1); min-height: 44px;
    transition: background .2s, transform .15s;
  }
  .add:hover { background: var(--ember-fill); transform: translateY(-1px); }
  .item.feature .add { background: var(--ember-fill); }
  .item.feature .add:hover { background: var(--ember-lt); color: var(--ink); }
  .add[disabled] { background: var(--muted); opacity: .55; cursor: not-allowed; transform: none; }

  .countdown {
    display: flex; align-items: baseline; gap: .5rem; margin-top: .9rem;
    font-size: var(--step--1); color: var(--brass-lt); font-weight: 600;
  }
  .countdown b {
    font-family: var(--f-display); font-size: 2.1rem;
    color: var(--ember-lt); line-height: 1; font-variant-numeric: tabular-nums;
  }
  .allergen { margin-top: 2rem; font-size: var(--step--1); color: var(--muted); }
  .on-dark .allergen { color: oklch(92% .018 78/.7); }

  .sizes {
    border: 0; margin: .9rem 0 0; display: flex; gap: .5rem; flex-wrap: wrap;
  }
  .sizes label { position: relative; display: inline-flex; }
  .sizes input { position: absolute; opacity: 0; width: 0; height: 0; }
  .sizes span {
    display: inline-flex; align-items: center; min-height: 44px;
    border: 1.5px solid var(--rule); border-radius: var(--r-pill);
    padding: .4em 1em; font-size: var(--step--1); font-weight: 600;
    cursor: pointer; transition: background .18s, border-color .18s, color .18s;
  }
  .sizes label:hover span { border-color: var(--ember); }
  .sizes input:checked + span { background: var(--ink); color: #fff; border-color: var(--ink); }
  .sizes input:focus-visible + span { outline: 3px solid var(--ember); outline-offset: 3px; }

  /* ── The Method ──────────────────────────────────────────────────── */
  /* No base fill (see .on-dark above) — the radial glow in ::before is
     what makes this section still read as distinct from its neighbours,
     not a solid colour boundary. */
  .method { color: var(--bone); position: relative; overflow: hidden; }
  .method::before {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: radial-gradient(ellipse 60% 50% at 85% 0%, oklch(from var(--brass) l c h / .22), transparent 65%);
  }
  .method-in { position: relative; z-index: 1; }
  .method .sec-h { color: #fff; }
  .method-lede {
    max-width: 60ch; color: oklch(92% .018 78/.86);
    font-size: var(--step-1); margin-bottom: .9em;
  }
  .method-credit {
    max-width: 62ch; color: oklch(92% .018 78/.7); font-size: var(--step--1);
    margin-bottom: 2.2em; padding-left: 1rem; border-left: 2px solid var(--brass);
  }
  .method-credit strong { color: var(--brass-lt); }
  .steps {
    display: grid; grid-template-columns: 1fr; gap: var(--sp-md); counter-reset: step;
  }
  @media (min-width: 34rem) {
    .steps { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
  }
  .step {
    position: relative; background: oklch(100% 0 0/.05);
    border: 1px solid oklch(92% .018 78/.16); border-radius: var(--r-lg);
    padding: 1.5rem 1.5rem 1.4rem; counter-increment: step;
  }
  .step::before {
    content: counter(step); position: absolute; top: -.35em; right: .6rem;
    font-family: var(--f-display); font-size: 4.2rem; line-height: 1;
    color: oklch(from var(--brass) l c h / .22);
  }
  .step h3 { font-size: var(--step-1); color: #fff; margin-bottom: .35rem; position: relative; }
  .step .temp {
    display: inline-block; font-size: .68rem; font-weight: 700;
    letter-spacing: .08em; text-transform: uppercase;
    color: var(--ember-lt); margin-bottom: .5rem; font-variant-numeric: tabular-nums;
  }
  .step p { font-size: var(--step--1); color: oklch(92% .018 78/.8); position: relative; }
  .method-foot {
    margin-top: 2rem; padding-top: 1.3rem;
    border-top: 1px solid oklch(92% .018 78/.16);
    font-size: var(--step--1); color: oklch(92% .018 78/.75); max-width: 64ch;
  }
  .method-foot strong { color: #fff; }

  /* ── Cards / campus / booking ────────────────────────────────────── */
  .two { display: grid; grid-template-columns: 1fr; gap: var(--sp-md); margin-top: var(--sp-lg); }
  @media (min-width: 49rem) { .two { grid-template-columns: 1fr 1fr; } }
  .bcard {
    background: oklch(100% 0 0/.055); border: 1px solid oklch(92% .018 78/.17);
    border-radius: var(--r-lg); padding: 1.5rem 1.6rem;
  }
  .bcard h3 { font-size: var(--step-1); color: #fff; margin-bottom: .4rem; }
  .bcard p, .bcard ul { font-size: var(--step--1); color: oklch(92% .018 78/.8); }
  .bcard ul { list-style: none; margin-top: .9rem; }
  .bcard li { padding-left: 1.35em; position: relative; margin: .32em 0; }
  .bcard li::before { content: "→"; position: absolute; left: 0; color: var(--ember); }

  /* No fill — floats on the dark canvas like everything else on the
     homepage now. */
  .camp-grid {
    display: grid; grid-template-columns: 1fr; gap: var(--sp-md); margin-top: var(--sp-lg);
  }
  @media (min-width: 34rem) {
    .camp-grid { grid-template-columns: repeat(auto-fit, minmax(228px, 1fr)); }
  }
  /* color:var(--ink) explicit — same reasoning as .item above: a light
     card whose section text-colour now varies needs its own. */
  .camp {
    background: var(--paper); color: var(--ink); border: 1px solid var(--rule);
    border-radius: var(--r-lg); padding: 1.5rem 1.6rem; box-shadow: var(--sh-warm-1);
    transition: transform .3s cubic-bezier(.2,.7,.3,1), box-shadow .3s;
    display: flex; flex-direction: column;
  }
  .camp:hover { transform: translateY(-6px) scale(1.012); box-shadow: var(--sh-warm-3); }
  .camp .when {
    display: inline-block; font-size: .68rem; font-weight: 700;
    letter-spacing: .09em; text-transform: uppercase;
    color: var(--ember-dk); margin-bottom: .5rem;
  }
  .camp h3 { font-size: var(--step-1); margin-bottom: .35rem; }
  .camp p { font-size: var(--step--1); color: var(--muted); flex: 1; }
  .camp.self { border-color: var(--ember); border-width: 2px; }
  .camp-link {
    display: inline-flex; align-items: center; gap: .35rem;
    margin-top: .9rem; font-size: var(--step--1); font-weight: 700;
    color: var(--ember-dk); text-decoration: none; min-height: 44px;
  }
  .camp-link:hover { text-decoration: underline; }

  /* ── Book page: the advantage cards, service styles, quote form ──── */
  .edge { background: var(--paper); border-block: 1px solid var(--rule); }
  .edge-grid { display: grid; grid-template-columns: 1fr; gap: var(--sp-md); margin-top: var(--sp-lg); }
  @media (min-width: 32rem) { .edge-grid { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); } }
  .edge-card {
    background: var(--cream); border: 1px solid var(--rule); border-left: 4px solid var(--brass);
    border-radius: var(--r); padding: 1.3rem 1.4rem;
  }
  .edge-card h3 { font-size: var(--step-1); margin-bottom: .4rem; }
  .edge-card p { font-size: var(--step--1); color: var(--muted); }

  .svc-grid { display: grid; grid-template-columns: 1fr; gap: var(--sp-md); margin-top: var(--sp-lg); }
  @media (min-width: 34rem) { .svc-grid { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); } }
  .svc {
    container-type: inline-size; background: var(--paper); border: 1px solid var(--rule);
    border-radius: var(--r-lg); padding: 1.5rem 1.6rem; box-shadow: var(--sh-1);
    transition: transform .22s, box-shadow .22s, border-color .22s;
  }
  .svc:hover { transform: translateY(-3px); box-shadow: var(--sh-2); border-color: oklch(from var(--ember) l c h / .45); }
  .svc .tag {
    display: inline-block; font-size: .65rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
    padding: .32em .8em; border-radius: var(--r-pill); background: var(--bone); color: var(--muted); margin-bottom: .7rem;
  }
  .svc h3 { font-size: var(--step-1); margin-bottom: .4rem; }
  .svc p { font-size: var(--step--1); color: var(--muted); }
  .svc ul { list-style: none; margin-top: .8rem; font-size: var(--step--1); color: var(--muted); }
  .svc li { padding-left: 1.3em; position: relative; margin: .3em 0; }
  .svc li::before { content: "→"; position: absolute; left: 0; color: var(--ember); }

  .quote-section { background: var(--char); color: var(--bone); position: relative; overflow: hidden; }
  .quote-section .sec-h { color: #fff; }
  .quote-section .sec-sub { color: oklch(92% .018 78/.78); }
  .form {
    position: relative; z-index: 1; background: oklch(100% 0 0/.05);
    border: 1px solid oklch(92% .018 78/.17); border-radius: var(--r-lg);
    padding: 1.5rem; margin-top: 1.6rem; max-width: 760px;
  }
  @media (min-width: 30rem) { .form { padding: 1.7rem; } }
  .row { display: grid; grid-template-columns: 1fr; gap: 1rem; margin-bottom: 1rem; }
  @media (min-width: 40rem) { .row { grid-template-columns: 1fr 1fr; } }
  .form .fld { display: flex; flex-direction: column; gap: .35rem; }
  .form .fld label { font-size: .7rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--brass-lt); }
  .form .fld input, .form .fld select, .form .fld textarea {
    border: 1.5px solid oklch(92% .018 78/.22); background: oklch(100% 0 0/.06); color: #fff;
    border-radius: 10px; padding: .8em .9em; font: inherit; font-size: var(--step--1); min-height: 44px;
  }
  .form .fld textarea { min-height: 90px; resize: vertical; }
  .form .fld input::placeholder, .form .fld textarea::placeholder { color: oklch(92% .018 78/.4); }
  .form .fld select option { background: var(--char); color: #fff; }
  .form .fld input:focus-visible, .form .fld select:focus-visible, .form .fld textarea:focus-visible {
    border-color: var(--ember); outline: none; box-shadow: 0 0 0 3px oklch(from var(--ember) l c h/.25);
  }
  .form .fld input:user-invalid, .form .fld select:user-invalid { border-color: var(--ember); }
  .form-note { font-size: .8rem; color: oklch(92% .018 78/.6); margin-top: .9rem; }
  .submit { width: 100%; justify-content: center; background: var(--ember-fill); color: #fff; margin-top: .5rem; }
  .submit:hover { background: var(--ember-fill-hover); }
  .sent {
    margin-top: 1rem; padding: 1rem; border-radius: var(--r);
    background: oklch(from var(--ok) l c h/.16); border: 1.5px solid var(--ok);
    text-align: center; font-size: var(--step--1);
  }
  .sent[hidden] { display: none; }

  /* ── Privacy page ────────────────────────────────────────────────── */
  .legal { max-width: var(--measure); }
  .legal h2 { font-size: var(--step-1); margin-top: 2rem; margin-bottom: .5rem; }
  .legal h2:first-child { margin-top: 0; }
  .legal p { margin-bottom: 1rem; }
  .legal .updated { color: var(--muted); font-size: var(--step--1); margin-bottom: 2rem; }
  .legal .card {
    background: var(--paper); border: 1px solid var(--rule); border-radius: var(--r);
    padding: 1.2rem 1.4rem; margin-bottom: 1rem;
  }
  .legal .card ul { padding-left: 1.2rem; }
  .legal .card li { margin: .4rem 0; font-size: var(--step--1); }

  /* ── Footer ──────────────────────────────────────────────────────── */
  .foot {
    background: var(--ink); color: oklch(92% .018 78/.76);
    font-size: var(--step--1);
    padding: var(--sp-fluid) 0 calc(2.2rem + env(safe-area-inset-bottom));
  }
  .foot-emblem { width: 96px; height: auto; margin: 0 auto 1.8rem; opacity: .92; }
  .foot-grid {
    display: grid; grid-template-columns: 1fr; gap: 1.7rem; margin-bottom: 1.9rem;
  }
  @media (min-width: 34rem) {
    .foot-grid { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
  }
  .foot h4 {
    font-family: var(--f-display); font-size: 1.02rem; color: #fff;
    margin-bottom: .55rem; letter-spacing: .05em;
  }
  .foot a { color: var(--brass-lt); text-decoration: none; }
  .foot a:hover { text-decoration: underline; }
  .foot-btm {
    border-top: 1px solid oklch(92% .018 78/.15); padding-top: 1.3rem;
    font-size: .79rem; color: oklch(92% .018 78/.55);
    display: flex; flex-wrap: wrap; gap: .8rem; justify-content: space-between;
  }

  /* ── Sticky order bar (phones only) ─────────────────────────────────
     The site's primary job is taking an order, but on a phone the only
     order controls were the nav button (which scrolls away) and the
     Order Ahead section — so through the entire menu, the Method and
     the campus sections there was no way to act without scrolling back
     up. This is a persistent, thumb-reachable one.

     Hidden by default and revealed by JS once the hero has scrolled
     out, so it never competes with the hero's own CTA. Suppressed
     above 56rem, where the nav's Order Ahead button is always visible
     anyway. */
  .order-bar {
    position: fixed; z-index: 70;
    left: 0; right: 0; bottom: 0;
    display: flex; align-items: center; gap: .9rem;
    padding: .8rem calc(var(--gutter) + env(safe-area-inset-right))
             calc(.8rem + env(safe-area-inset-bottom))
             calc(var(--gutter) + env(safe-area-inset-left));
    background: oklch(from var(--ink) l c h / .9);
    backdrop-filter: blur(16px) saturate(1.3);
    border-top: 1px solid oklch(from var(--ember) l c h / .35);
    /* Off-screen rather than display:none so it can transition, and
       inert so it's unreachable by keyboard/AT while hidden. */
    translate: 0 110%;
    visibility: hidden;
    transition: translate .32s cubic-bezier(.2,.7,.3,1), visibility .32s;
  }
  .order-bar.show { translate: 0 0; visibility: visible; }
  /* The bar is fixed over the page, so at the very bottom of the scroll
     it would sit on top of the footer's last line. Reserve its height
     (and scroll-padding, so anchor jumps don't land underneath it)
     only while it's actually out. */
  body:has(.order-bar.show) { padding-bottom: 78px; }
  body:has(.order-bar.show) { scroll-padding-bottom: 90px; }
  .order-bar-txt {
    flex: 1; min-width: 0; font-size: var(--step--1);
    color: oklch(92% .018 78/.85); line-height: 1.25;
  }
  .order-bar-txt b {
    display: block; color: #fff; font-weight: 700;
    font-size: var(--step-0);
  }
  .order-bar .btn { flex: none; padding: .8em 1.4em; }
  @media (min-width: 56rem) { .order-bar { display: none; } }

  /* ── Toast ───────────────────────────────────────────────────────── */
  /* Rides above the sticky bar when it's out, so an "added to order"
     toast is never hidden behind the thing that triggered it. */
  body:has(.order-bar.show) #toast { bottom: calc(94px + env(safe-area-inset-bottom)); }
  #toast {
    position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom));
    translate: -50% 0; z-index: 90;
    background: var(--ink); color: #fff;
    padding: .85em 1.4em; border-radius: var(--r-pill);
    box-shadow: var(--sh-3); font-size: var(--step--1); font-weight: 600;
    opacity: 0; pointer-events: none;
    transition: opacity .25s, translate .25s;
  }
  #toast.show { opacity: 1; translate: -50% -6px; }
}

/* ───────────────────────────────────────────────────────────── MOTION ─── */
@layer motion {
  /* Scroll-driven first, JS fallback second. Only opacity and transform —
     never height/width/top/left. Compositor-only keeps 60fps on the cheap
     Android phones a rural Delta audience actually carries. */
  /* This layer loads after components, so `.rv`'s `transition` property
     always wins over e.g. `.item:hover`'s own transition list for as
     long as `.rv` still matches — which is forever, since JS adds `.in`
     alongside `.rv` rather than removing it. Left with only opacity/
     transform in the list, box-shadow and border-color hover changes
     would SNAP instead of easing. Carrying every hover-animatable
     property here keeps them smooth regardless of which layer's
     `transition` value actually wins. */
  .rv {
    opacity: 0; transform: translateY(20px) scale(.98);
    transition: opacity .6s cubic-bezier(.2,.7,.3,1), transform .6s cubic-bezier(.2,.7,.3,1),
      box-shadow .3s, border-color .3s, background .3s;
  }
  .rv.in { opacity: 1; transform: none; }
  @supports (animation-timeline: view()) {
    /* NOT `transition: none` — that was silently killing every hover
       transition (transform, box-shadow, border-color) on every card
       that carries .rv, in every browser that supports scroll-driven
       animation, which by 2026 is most of them. The entrance here runs
       once via `animation` and finishes with `fill:both`; by the time
       anyone hovers, that animation has already stopped, so `transition`
       is free to govern transform/box-shadow/border-color again without
       fighting it. Keep this list identical to the fallback `.rv` above. */
    .rv {
      opacity: 1; transform: none;
      transition: opacity .6s cubic-bezier(.2,.7,.3,1), transform .6s cubic-bezier(.2,.7,.3,1),
        box-shadow .3s, border-color .3s, background .3s;
      animation: rise both linear;
      animation-timeline: view();
      animation-range: entry 5% entry 62%;
    }
    @keyframes rise {
      from { opacity: 0; transform: translateY(22px) scale(.98); }
      to   { opacity: 1; transform: none; }
    }
  }
  /* Cascading entrance for card grids — each grid's own children stagger
     against EACH OTHER (nth-child), not the rest of the page, so a row
     of cards settles in as one gesture rather than one long file. Scoped
     to :not(.in) so the delay only ever applies to the reveal itself —
     once settled, hover transitions stay instant-on, never queued
     behind a leftover reveal delay. */
  .menu-grid .item.rv:not(.in):nth-child(1), .camp-grid .camp.rv:not(.in):nth-child(1), .howto-steps li.rv:not(.in):nth-child(1), .steps .step.rv:not(.in):nth-child(1) { transition-delay: 0s; }
  .menu-grid .item.rv:not(.in):nth-child(2), .camp-grid .camp.rv:not(.in):nth-child(2), .howto-steps li.rv:not(.in):nth-child(2), .steps .step.rv:not(.in):nth-child(2) { transition-delay: .08s; }
  .menu-grid .item.rv:not(.in):nth-child(3), .camp-grid .camp.rv:not(.in):nth-child(3), .howto-steps li.rv:not(.in):nth-child(3), .steps .step.rv:not(.in):nth-child(3) { transition-delay: .16s; }
  .menu-grid .item.rv:not(.in):nth-child(4), .howto-steps li.rv:not(.in):nth-child(4) { transition-delay: .24s; }
  .menu-grid .item.rv:not(.in):nth-child(5) { transition-delay: .32s; }
  .menu-grid .item.rv:not(.in):nth-child(6) { transition-delay: .4s; }
  /* The animation-timeline:view() path above doesn't use .in at all —
     stagger there via animation-delay on the same selectors, harmlessly
     inert wherever the @supports block didn't match. */
  @supports (animation-timeline: view()) {
    .menu-grid .item:nth-child(2), .camp-grid .camp:nth-child(2), .howto-steps li:nth-child(2), .steps .step:nth-child(2) { animation-delay: .08s; }
    .menu-grid .item:nth-child(3), .camp-grid .camp:nth-child(3), .howto-steps li:nth-child(3), .steps .step:nth-child(3) { animation-delay: .16s; }
    .menu-grid .item:nth-child(4), .howto-steps li:nth-child(4) { animation-delay: .24s; }
    .menu-grid .item:nth-child(5) { animation-delay: .32s; }
    .menu-grid .item:nth-child(6) { animation-delay: .4s; }
  }

  .progress {
    position: fixed; top: 0; left: 0; height: 3px; width: 100%;
    transform-origin: 0 50%; transform: scaleX(0);
    background: linear-gradient(90deg, var(--ember), var(--brass));
    z-index: 80;
  }
  @supports (animation-timeline: scroll()) {
    .progress { animation: grow linear; animation-timeline: scroll(root); }
    @keyframes grow { to { transform: scaleX(1); } }
  }

  /* ── Hero entrance ──────────────────────────────────────────────────
     The hero copy previously had NO entrance at all — .rv was only ever
     applied to section headings and cards further down, so the single
     most important screen on the site just appeared, fully formed, with
     nothing to lead the eye. This is a one-shot staggered rise, pure CSS
     and running immediately on load (never waiting on JS, per the
     above-the-fold rule). Each element enters ~110ms after the one
     before it, so the eye is walked eyebrow -> headline -> lede ->
     buttons -> footnote in the order they should be read. */
  .hero-in > * {
    animation: hero-rise .85s cubic-bezier(.16, 1, .3, 1) both;
  }
  .hero-in > .eyebrow    { animation-delay: .05s; }
  .hero-in > h1          { animation-delay: .16s; }
  .hero-in > .lede       { animation-delay: .30s; }
  .hero-in > .btns       { animation-delay: .42s; }
  .hero-in > .hero-foot  { animation-delay: .54s; }
  @keyframes hero-rise {
    from { opacity: 0; transform: translateY(26px); filter: blur(6px); }
    to   { opacity: 1; transform: none; filter: blur(0); }
  }

  /* ── Scroll-linked hero parallax ────────────────────────────────────
     The hero content drifts up and fades slightly as it leaves, so the
     transition into the page below is a continuous move rather than a
     hard scroll-off. Scroll-driven, so it costs no scroll listener and
     no JS; entirely absent where animation-timeline isn't supported. */
  @supports (animation-timeline: view()) {
    .hero-in {
      animation: hero-part linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 90%;
    }
    @keyframes hero-part {
      from { opacity: 1; transform: translateY(0); }
      to   { opacity: .25; transform: translateY(-60px); }
    }
    /* The watermark moves further and slower than the copy — the depth
       cue that makes it read as sitting behind, not on, the text.
       BOTH animations are re-declared here, not just the new one: a bare
       `animation: hero-mark-part` would replace the shorthand set
       earlier and silently drop mark-breathe wherever scroll-driven
       animation is supported (i.e. almost everywhere). Two names, two
       timelines — `auto` keeps breathe on the document timeline while
       the parallax rides the scroll. */
    .hero-mark {
      animation: mark-breathe 26s ease-in-out infinite alternate,
                 hero-mark-part linear both;
      animation-timeline: auto, view();
      animation-range: normal, exit 0% exit 100%;
    }
    /* Both keyframes must carry the -50% — `translate` is the same
       property the base rule uses to vertically centre this element, so
       animating a bare offset here would drop the centring and snap the
       emblem down half its own height the moment the animation applies. */
    @keyframes hero-mark-part {
      from { translate: 0 -50%; }
      to   { translate: 0 calc(-50% - 110px); }
    }
  }

  /* Cross-document continuity. ~10 lines for the single biggest
     perceived-quality jump available on a multi-page static site. */
  @view-transition { navigation: auto; }
  .nav { view-transition-name: site-nav; }
  .foot { view-transition-name: site-foot; }

  /* Nothing loops except the status pulse. It means LIVE — anything else
     moving on a timer competes with the one thing that should read as
     alive. The ambient orbs are the one deliberate exception (see
     .ambient above): they're background atmosphere, not foreground UI,
     so they're addressed on their own terms below rather than folded
     into that rule. */
  @media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    .rv {
      opacity: 1 !important; transform: none !important;
      transition: none !important; animation: none !important;
    }
    .progress { display: none; }
    .led { animation: none; }
    /* Every moving background layer stops. The layers themselves stay —
       vignette, grain and the colour fields are static design, not
       motion. Only the drift is suppressed. */
    .ambient .orb, .ambient .embers { animation: none; }
    .ambient { animation: none; transform: none; filter: none; }
    /* The hero entrance, its scroll parallax and the emblem drift all
       resolve to their finished state immediately rather than being
       skipped — `both` fill would otherwise leave hero copy stuck at
       opacity:0 with animations disabled, i.e. an invisible hero. */
    .hero-in > *, .hero-in, .hero-mark {
      animation: none !important;
      opacity: 1 !important; transform: none !important; filter: none !important;
    }
    .hero-mark { translate: 0 -50% !important; }
    .item:hover, .camp:hover, .day:hover, .howto-steps li:hover,
    .btn:hover, .add:hover, .order-btn:hover { transform: none; }
    /* Appears/disappears instantly rather than sliding. It still needs
       to MOVE (it's translated off-screen when hidden) — so this kills
       the transition, not the positioning. */
    .order-bar { transition: none; }
    *, *::before, *::after { scroll-behavior: auto !important; }
  }
}

/* ────────────────────────────────────────────────────────── UTILITIES ─── */
@layer utilities {
  .center { text-align: center; }
  .stack-sm > * + * { margin-top: .5rem; }
  .mt-lg { margin-top: 1.7rem; }
  [hidden] { display: none !important; }
  /* Premium grain — one inline SVG, no asset request. */
  .grain::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    opacity: .05; mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)'/%3E%3C/svg%3E");
  }
}
