/* ==========================================================================
   Kick'n Hibachi — site styles
   Responsive, 320px to 1920px. The 1200px container is the ceiling, not the
   floor: past it the container holds and the margins grow.
   Breakpoints are listed in the RESPONSIVE LAYER section near the bottom.
   ========================================================================== */

:root{
  /* flames */
  --flame-yellow:#FFA51A;
  --flame-yellow-dim:#D98A12;
  --flame-orange:#F2701B;
  --flame-orange-lift:#FF8A3D;
  --flame-red:#D8341F;

  /* smoke */
  --smoke-900:#1A1B1D;
  --smoke-800:#26282B;
  --smoke-700:#303337;
  --smoke-650:#3A3E43;
  --smoke-600:#454A4F;

  /* Boundary of an interactive control — form fields, secondary buttons.
     WCAG 1.4.11 wants 3:1 against adjacent colour for anything that IDENTIFIES
     a component, and --smoke-600 measured 1.65:1 against the page and 1.42:1
     against a field fill: the field was very nearly not visible as a field.
     Decorative dividers keep --smoke-600, which 1.4.11 does not cover.
     Measured: 3.60:1 on --smoke-800, 3.09:1 on --smoke-700. */
  --edge:#787E85;

  /* Placeholder text. Was #8A9096 = 3.94:1 on --smoke-700, under the 4.5:1
     body-text floor, while carrying real hints ("City, or the venue name").
     Measured: 4.64:1. */
  --placeholder:#979DA4;

  /* Height of the fixed mobile quick-action bar, reserved by the footer as
     its own bottom padding so the near-black runs under the bar rather than
     leaving a lighter band of body colour above it. Zero above 860px, where
     the bar does not exist.

     10px pad + button + 10px pad + 1px border, plus the home-indicator inset.
     The button is 50px normally but 44px under @media (hover:none), so the
     bar is 71px with a mouse and 65px on touch — the height varies by INPUT
     TYPE, not by width, and no media query can resolve that here. Reserving
     the larger of the two means content is never covered on either; the 6px
     of slack on touch is footer padding and is invisible. */
  --cta-bar-h:0px;

  /* contrast pair */
  --off-white:#F4F2ED;
  --near-black:#131314;
  --muted:#A9AFB5;
  --muted-on-raised:#B4BAC0;

  /* type */
  --display:"Archivo Black","Archivo",Arial Black,sans-serif;
  --body:"Archivo","Helvetica Neue",Arial,sans-serif;
  --mono:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;

  /* space */
  --container:1200px;
  --gutter:24px;
  --band:72px;

  /* motion */
  --ease:cubic-bezier(.22,1,.36,1);
  --fast:180ms;

  /* layers */
  --z-header:100;
  --z-skip:200;
}

*{box-sizing:border-box}
/* The `hidden` attribute is only a UA-level display:none, so ANY author rule
   that sets display beats it. .empty-state{display:grid} was doing exactly
   that, leaving the empty-state panel on screen underneath a list that had
   items in it. Make hidden actually mean hidden. */
[hidden]{display:none !important}


html{
  scroll-behavior:smooth;scroll-padding-top:96px;
  /* The sticky header changes height, which is a layout change above the
     viewport. Scroll anchoring would "helpfully" shift scrollY to compensate,
     which is what made the header flicker. Every image here has width/height
     set, so nothing else depends on anchoring. */
  overflow-anchor:none
}

body{
  margin:0;
  background:var(--smoke-800);
  color:var(--off-white);
  font-family:var(--body);
  font-size:16px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
}

img{max-width:100%;display:block}

.container{width:100%;max-width:var(--container);margin:0 auto;padding-inline:clamp(18px, 4vw, 24px)}

/* ---------- skip link ---------- */
.skip{
  position:absolute;left:-9999px;top:0;z-index:var(--z-skip);
  background:var(--flame-yellow);color:var(--near-black);
  padding:12px 20px;font-weight:700;text-decoration:none
}
.skip:focus{left:12px;top:12px}

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

/* ---------- type ---------- */
h1,h2,h3{margin:0;font-family:var(--display);font-weight:400;letter-spacing:-.01em;text-wrap:balance}
h1{font-size:clamp(34px, 6.2vw, 56px);line-height:1.05}
h2{font-size:clamp(25px, 3.4vw, 34px);line-height:1.12}
h3{font-size:19px;line-height:1.25}
p{margin:0;text-wrap:pretty}
.prose{max-width:66ch;color:var(--muted)}
.prose-tight{max-width:60ch}
.eyebrow{
  font-family:var(--mono);font-size:12px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--flame-orange-lift);margin:0 0 14px
}
.lead{font-size:18px;color:var(--off-white);max-width:60ch}
.muted{color:var(--muted)}
.num{font-variant-numeric:tabular-nums}
.mono{font-family:var(--mono)}

a{color:var(--flame-orange-lift)}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,
textarea:focus-visible,[tabindex]:focus-visible{
  outline:2px solid var(--flame-yellow);
  outline-offset:2px
}

/* ---------- header ----------
   Two tiers: a slim utility strip for the details a caterer gets asked for,
   then the main bar. The mark is a portrait crop of the illustration with
   its lettering removed — the artwork carries the name in it, so pairing the
   full logo with a type wordmark printed the name twice. Mark plus type is
   one lockup; the name appears once. The badge no longer overhangs, so the
   header stops distorting the top of every page. */
.site-header{
  position:sticky;top:0;z-index:var(--z-header);
  background:var(--smoke-900);
  border-bottom:2px solid transparent;
  border-image:linear-gradient(90deg,var(--flame-yellow),var(--flame-orange) 45%,var(--flame-red)) 1;
}
.site-header .bar{
  display:flex;align-items:center;gap:36px;
  min-height:82px;padding-block:12px;
  transition:min-height var(--fast) var(--ease), padding-block var(--fast) var(--ease)
}

.brand{display:inline-flex;align-items:center;gap:14px;text-decoration:none;color:inherit}
.brand-mark{
  width:64px;height:64px;flex:0 0 auto;display:block;
  /* Circular badge. The artwork covers ~85% of the inscribed circle, so it
     needs a disc behind it — without one the gaps read as holes punched in
     the ring. object-fit:cover so the portrait fills the coin edge to edge. */
  border-radius:50%;
  background:var(--near-black);
  object-fit:cover;
  box-shadow:
    0 0 0 2px var(--flame-orange),
    0 0 20px -5px rgba(216,52,31,.6);
  transition:width var(--fast) var(--ease), height var(--fast) var(--ease),
             box-shadow var(--fast) var(--ease), transform var(--fast) var(--ease)
}
.brand:hover .brand-mark{
  box-shadow:
    0 0 0 2px var(--flame-yellow),
    0 0 26px -3px rgba(255,165,26,.75);
  transform:scale(1.03)
}
.brand:focus-visible .brand-mark{
  box-shadow:
    0 0 0 2px var(--flame-yellow),
    0 0 26px -3px rgba(255,165,26,.75)
}
.brand-text{display:grid;gap:2px;line-height:1}
.brand .wordmark{
  font-family:var(--display);font-size:23px;letter-spacing:-.01em;
  color:var(--off-white);white-space:nowrap;
  transition:font-size var(--fast) var(--ease)
}
.wordmark .spark{color:var(--flame-orange)}
.brand-tag{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.13em;text-transform:uppercase;
  color:var(--muted);white-space:nowrap;
  transition:opacity var(--fast) var(--ease)
}

.site-nav{display:flex;gap:26px;margin-left:auto}
/* Drawer-only. On desktop the same action already sits in .header-actions,
   and a second yellow fill in the nav row would break the one-primary-action
   rule the palette is built on. Shown again inside the 860 block. */
.site-nav .nav-cta{display:none}
.site-nav a{
  color:var(--muted);text-decoration:none;font-size:15px;font-weight:500;
  white-space:nowrap;   /* "Find Us" was breaking across two lines near 900px */
  padding-block:6px;border-bottom:2px solid transparent;
  transition:color var(--fast) var(--ease), border-color var(--fast) var(--ease)
}
.site-nav a:hover{color:var(--off-white)}
.site-nav a[aria-current="page"]{color:var(--off-white);border-bottom-color:var(--flame-orange)}

.header-actions{display:flex;align-items:center;gap:16px}

/* Compact: the utility strip rolls up and the lockup tightens. */
.site-header.is-scrolled .bar{min-height:62px;padding-block:8px}
.site-header.is-scrolled .brand-mark{width:46px;height:46px}
.site-header.is-scrolled .brand .wordmark{font-size:19px}
.site-header.is-scrolled .brand-tag{opacity:0}

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;white-space:nowrap;
  font-family:var(--body);font-size:14px;font-weight:700;letter-spacing:.04em;
  text-transform:uppercase;text-decoration:none;cursor:pointer;
  padding:14px 26px;border:2px solid transparent;border-radius:2px;
  transition:background-color var(--fast) var(--ease), border-color var(--fast) var(--ease), color var(--fast) var(--ease)
}
.btn-primary{background:var(--flame-yellow);color:var(--near-black);border-color:var(--flame-yellow)}
.btn-primary:hover{background:var(--flame-orange);border-color:var(--flame-orange)}
.btn-secondary{background:transparent;color:var(--off-white);border-color:var(--edge)}
.btn-secondary:hover{border-color:var(--flame-orange);color:var(--flame-orange-lift)}
.btn-sm{padding:10px 18px;font-size:13px}
.btn[disabled],.btn[aria-disabled="true"]{opacity:.5;cursor:not-allowed}
.btn[disabled]:hover{background:var(--flame-yellow);border-color:var(--flame-yellow)}

.spinner{
  width:13px;height:13px;border:2px solid currentColor;border-right-color:transparent;
  border-radius:50%;animation:spin .7s linear infinite
}
@keyframes spin{to{transform:rotate(360deg)}}

/* ---------- bands ---------- */
.band{padding-block:var(--band)}
.band-tight{padding-block:46px}
main > .band:first-child:not(.hero){padding-top:64px}
.band-raised{background:var(--smoke-700)}
.band + .band{border-top:1px solid var(--smoke-600)}

.band-head{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;margin-bottom:34px}
.band-head .link{
  font-family:var(--mono);font-size:13px;color:var(--flame-orange-lift);
  text-decoration:none;white-space:nowrap;border-bottom:1px solid transparent;
  transition:border-color var(--fast) var(--ease)
}
.band-head .link:hover{border-bottom-color:var(--flame-orange-lift)}

/* ---------- grid ---------- */
.grid{display:grid;gap:var(--gutter)}
.g-2{grid-template-columns:repeat(2,1fr)}
.g-3{grid-template-columns:repeat(3,1fr)}
.g-4{grid-template-columns:repeat(4,1fr)}
.g-5{grid-template-columns:repeat(5,1fr)}
.g-6{grid-template-columns:repeat(6,1fr)}
.split-hero{display:grid;grid-template-columns:1fr 1fr;gap:56px;align-items:center}
.split-7-5{display:grid;grid-template-columns:7fr 5fr;gap:48px;align-items:start}
.split-5-7{display:grid;grid-template-columns:5fr 7fr;gap:34px;align-items:center}
.split-3-9{display:grid;grid-template-columns:3fr 9fr;gap:40px;align-items:start}

/* Footer lockup: same mark, sized for a footer rather than a nav bar. */
.footer-mark .brand{margin-bottom:4px}
.footer-mark .brand-mark{width:84px;height:84px}
.footer-mark .brand .wordmark{font-size:22px}
.footer-mark .brand-tag{display:none}
.brand-mark.is-missing{
  filter:none;
  background:
    linear-gradient(to top right,transparent calc(50% - .5px),var(--smoke-600) 50%,transparent calc(50% + .5px)),
    linear-gradient(to bottom right,transparent calc(50% - .5px),var(--smoke-600) 50%,transparent calc(50% + .5px)),
    var(--smoke-700);
  border:1px solid var(--smoke-600)
}

/* ---------- skeleton loaders ----------
   Any <img> inside a .media-frame gets one for free: the frame already
   reserves the exact aspect box, so the skeleton fills space that was always
   going to be that size and nothing shifts when the picture arrives (CLS 0).

   The skeleton sits BEHIND the image rather than replacing it, so a partially
   decoded image paints straight over it. It is removed by JS on load, and a
   CSS expiry fades it out after 12s regardless — a skeleton that outlives a
   failed script would otherwise sit on the page forever. */
.media-frame::before{
  content:"";
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    linear-gradient(90deg,
      transparent 0%,
      rgba(255,255,255,.05) 40%,
      rgba(255,255,255,.09) 50%,
      rgba(255,255,255,.05) 60%,
      transparent 100%),
    var(--smoke-700);
  background-size:220% 100%, 100% 100%;
  background-repeat:no-repeat;
  opacity:1;
  transition:opacity .35s var(--ease);
  animation:
    skeleton-sweep 1.5s linear infinite,
    skeleton-expire .4s linear 12s forwards;
}
.media-frame.is-loaded::before{opacity:0;animation-play-state:paused}
/* the broken-image placeholder draws its own state, so stand down */
.media-frame.is-missing::before{display:none}
.media-frame img{position:relative;z-index:1}

@keyframes skeleton-sweep{
  from{background-position:-110% 0, 0 0}
  to{background-position:210% 0, 0 0}
}
@keyframes skeleton-expire{
  to{opacity:0;visibility:hidden}
}

/* A sweeping highlight is decorative motion; the reveal on load is not.
   Reduced motion keeps a flat tone and still clears it when the image lands. */
@media (prefers-reduced-motion:reduce){
  .media-frame::before{
    background:var(--smoke-700);
    animation:skeleton-expire .01ms linear 12s forwards !important
  }
}

/* ---------- framed media ----------
   owners.png carries its own alpha (45% of it is transparent), so the artwork
   sits straight on the smoke ground — no card, no panel. The frame is now
   just a sized box that holds the aspect ratio. */
.media-frame{
  background:transparent;border:0;
  display:grid;place-items:center;padding:0;overflow:hidden;position:relative
}
.media-frame img{width:100%;height:100%;object-fit:contain;display:block}
.media-frame.r54{aspect-ratio:5/4}
.media-frame.r169{aspect-ratio:16/9}
.media-frame.r11{aspect-ratio:1/1}
.media-frame.r32{aspect-ratio:3/2}
.media-frame.is-missing{
  border:1px solid var(--smoke-600);
  background:
    linear-gradient(to top right,transparent calc(50% - .5px),var(--smoke-600) 50%,transparent calc(50% + .5px)),
    linear-gradient(to bottom right,transparent calc(50% - .5px),var(--smoke-600) 50%,transparent calc(50% + .5px)),
    var(--smoke-700)
}
.media-frame.is-missing img{display:none}
.media-frame .missing-label{
  font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);background:var(--smoke-700);padding:5px 10px;text-align:center
}

/* ---------- image placeholders (blank by design) ---------- */
.ph{
  position:relative;
  background:
    linear-gradient(to top right,transparent calc(50% - .5px),var(--smoke-600) 50%,transparent calc(50% + .5px)),
    linear-gradient(to bottom right,transparent calc(50% - .5px),var(--smoke-600) 50%,transparent calc(50% + .5px)),
    var(--smoke-700);
  border:1px solid var(--smoke-600);
  display:grid;place-items:center
}
.ph span{
  font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);background:var(--smoke-700);padding:4px 9px
}
.ph-43{aspect-ratio:4/3}
.ph-169{aspect-ratio:16/9}
.ph-11{aspect-ratio:1/1}
/* A slot holding its real photo. The cross-hatched .ph background is left in
   place on purpose: it shows while the image decodes and stays as the
   fallback if the file is ever missing. overflow keeps object-fit:cover
   from spilling past the border radius. */
.ph.photo{overflow:hidden}
.ph.photo > img{display:block;width:100%;height:100%;object-fit:cover}
.ph-54{aspect-ratio:5/4}

/* ---------- hero ----------
   Sized around the four-word slogan: the headline breaks to two lines, and
   the facts strip gives the copy column enough height to sit level with the
   4:3 image beside it. */
/* The two pads are tokens because the hero picture cancels them with a
   matching negative margin to reach the section's full height. Hard-coding
   the same numbers twice would let them drift apart silently. */
.hero{
  --hero-pad-top:92px;--hero-pad-bottom:80px;
  padding-block:var(--hero-pad-top) var(--hero-pad-bottom);
  position:relative;overflow:visible
}
.hero .split-hero{gap:34px}
.hero-copy{display:grid;gap:28px;justify-items:start}
.hero h1{
  font-size:clamp(38px, 7vw, 64px);line-height:1.02;max-width:11ch;
  /* Flame gradient, matching the yellow-into-red lettering in the logo.
     Hottest at the top, cooling to ember at the baseline. Solid colour is
     declared first so the heading is never invisible if background-clip
     is unsupported — transparent text with no clip support is a blank hero. */
  color:var(--flame-yellow);
  /* background-clip:text paints only inside the padding box. With a tight
     line-height the glyph ink overruns it, so any descender would render
     transparent. Pad past the ink and pull the box back optically. */
  padding:.10em 0 .16em;
  margin:-.10em 0 -.16em
}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .hero h1{
    background-image:linear-gradient(
      180deg,
      #FFE27A 0%,
      #FFD34A 14%,
      #FFA51A 38%,
      #F2701B 68%,
      #E8452C 100%
    );
    /* Taller than the text, so there is ramp to drift through.
       transform is no use here: it would move the glyphs, not the fill.
       background-position is a paint-only property, so nothing relayouts. */
    background-size:100% 150%;
    background-position:0 0%;
    -webkit-background-clip:text;
    background-clip:text;
    color:transparent;
    -webkit-text-fill-color:transparent;
    /* Haze first, then the glow — so the glow is not itself displaced. */
    filter:url(#heat-haze) drop-shadow(0 0 26px rgba(216,52,31,.3));
    animation:flame-drift 3.6s ease-in-out infinite alternate
  }
  /* Alternate rather than a loop: the ramp breathes up and down instead of
     wrapping, so the heading never flips to red-on-top mid-cycle and the
     seam never shows. */
  @keyframes flame-drift{
    from{background-position:0 0%}
    to{background-position:0 100%}
  }
  /* Transparent fill makes native selection unreadable, so paint it back. */
  .hero h1::selection{-webkit-text-fill-color:var(--near-black);color:var(--near-black);background:var(--flame-yellow)}
  .hero h1 ::selection{-webkit-text-fill-color:var(--near-black);color:var(--near-black);background:var(--flame-yellow)}
}
/* Readers who ask for more contrast get one solid, high-contrast colour. */
@media (prefers-contrast:more){
  .hero h1{
    background-image:none;color:var(--off-white);
    -webkit-text-fill-color:var(--off-white);filter:none
  }
}
.hero .lead{max-width:44ch}
.hero .flame-rule{
  position:relative;
  width:148px;height:7px;
  background:linear-gradient(90deg,var(--flame-yellow),var(--flame-orange) 52%,var(--flame-red))
}
/* The air directly above the rule carries the heat, so the shimmer lives
   there rather than being painted over the whole hero. */
.hero .flame-rule::after{
  content:"";position:absolute;left:-4px;right:-4px;bottom:100%;height:22px;
  background:linear-gradient(180deg,transparent,rgba(255,165,26,.13));
  /* blur only: url(#heat-haze) on a near-transparent strip renders its
     filter region as a visible grey block. The haze stays on the
     headline, where the source is opaque glyphs. */
  filter:blur(2px);
  pointer-events:none;
  animation:heat-rise 14s ease-in-out infinite alternate
}
@keyframes heat-rise{
  from{transform:translateY(1px) scaleY(.96);opacity:.34}
  to{transform:translateY(-2px) scaleY(1.04);opacity:.52}
}
.hero-actions{display:flex;gap:14px;flex-wrap:wrap}
.hero-facts{
  display:flex;gap:30px;align-items:baseline;width:100%;
  margin:6px 0 0;padding-top:24px;border-top:1px solid var(--smoke-600)
}
.hero-facts div{display:grid;gap:3px}
.hero-facts dt{
  font-family:var(--mono);font-size:11px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--muted)
}
.hero-facts dd{margin:0;font-size:15px;font-weight:600}

/* Left edge dissolves into the page so the picture reads as part of the
   surface rather than a pasted rectangle. The mask covers the first 25% of
   the width — transparent at the very left, fully opaque from the 25% mark
   on — leaving three quarters of the artwork untouched. It sits on the frame
   rather than the <img> so anything behind the image fades with it. */
.media-fade-left{
  -webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 25%);
  mask-image:linear-gradient(90deg,transparent 0%,#000 25%)
}
.media-fade-left img{
  /* contain, not cover: cover cropped the wordmark in the artwork down to
     "ickn ibachi". Nothing is cut now — the mask alone does the blending,
     and the transparent letterboxing above and below is invisible. */
  width:100%;height:100%;object-fit:contain;object-position:center
}

/* Hero picture fills its column, wide screens only.
   At a fixed 5:4 the picture came out 447px tall against 489px of copy, and
   that 42px shortfall read as the image floating high in the row. Let the
   frame take the row's full height instead and crop the photograph to suit —
   contain would letterbox it, which is the same imbalance in a new place.
   This is a photograph, so cropping 8% off it costs nothing; the previous
   artwork had a wordmark in it, which is why the global rule says contain. */
@media (min-width:941px){
  .hero .split-hero{align-items:stretch}
  /* stretch already sizes the item to the row; the negative margins then
     cancel the section's own padding so the picture reaches the top and
     bottom edges of the hero band rather than stopping level with the copy. */
  /* The top sits level with the copy rather than running up under the header
     — flush against the header read as the picture colliding with it, not as
     a deliberate edge. The bottom is the one that overshoots: it drops past
     the row by --hero-img-foot and dissolves there, so the picture is taller
     than the copy column without becoming the tallest thing on the page. */
  .hero .media-frame{
    --hero-img-foot:32px;
    aspect-ratio:auto;
    margin-top:0;
    margin-bottom:calc(-1 * var(--hero-img-foot))
  }
  /* .media-frame is a grid with place-items:center. In that grid the image's
     height:100% had no definite row to resolve against, so it fell back to
     auto and the intrinsic 5:4 took over — which is why the picture kept
     coming up short of the frame however tall the frame itself grew, and why
     align-self:stretch could not fix it either (stretch is ignored once a
     height is declared). Making the hero frame a plain block gives the image
     a definite height to measure against. */
  .hero .media-frame{display:block}
  .hero .media-fade-left img{
    width:100%;height:100%;
    object-fit:cover;object-position:50% 50%
  }
  /* Two masks intersected: the existing left-edge fade, plus a bottom-up one
     on the same principle — a short, fast ramp rather than a long wash, so
     the photograph keeps its weight and only the last sliver dissolves into
     the band below. Only the bottom is faded; the top edge butts against the
     header, where a clean line is what you want. */
  .hero .media-fade-left{
    -webkit-mask-image:
      linear-gradient(90deg,transparent 0%,#000 25%),
      linear-gradient(0deg,transparent 0%,#000 14%);
    -webkit-mask-composite:source-in;
    mask-image:
      linear-gradient(90deg,transparent 0%,#000 25%),
      linear-gradient(0deg,transparent 0%,#000 14%);
    mask-composite:intersect
  }
}


/* ---------- item cards ---------- */
.item-card{display:grid;gap:14px;align-content:start}
.item-card .ph{transition:border-color var(--fast) var(--ease)}
.item-card h3{font-family:var(--body);font-weight:700;font-size:17px}
.item-card p{color:var(--muted);font-size:15px}
a.item-card{text-decoration:none;color:inherit}
a.item-card:hover .ph{border-color:var(--flame-orange)}

/* ---------- services ---------- */
.service{position:relative;display:grid;gap:8px;align-content:start;padding-top:18px}
.service::before{
  content:"";position:absolute;top:0;left:0;right:0;height:2px;
  background:var(--flame-orange);transform-origin:left center
}
.service h3{font-family:var(--body);font-weight:700;font-size:16px}
.service p{color:var(--muted-on-raised);font-size:14px}

/* ---------- map embed ----------
   The supplied iframe is a fixed 600x450; here it fills whatever slot it is
   given so the two pages can use different shapes. Sized by aspect-ratio so
   the space is reserved before the frame loads — same no-shift rule as the
   images. The frame is third-party content: it is lazy, referrer-limited,
   and titled so screen readers announce it as something other than "frame". */
.map-embed{
  position:relative;width:100%;overflow:hidden;
  border:1px solid var(--smoke-600);
  background:var(--smoke-700)
}
.map-embed.r169{aspect-ratio:16/9}
.map-embed.r54{aspect-ratio:5/4}
.map-embed iframe{
  position:absolute;inset:0;
  width:100%;height:100%;border:0;display:block;
  transition:opacity .25s var(--ease)
}
/* Switching pitch reloads the frame, which blanks it. Dim and shimmer until
   the new map reports back, so the swap does not read as a broken embed. */
.map-embed.is-switching iframe{opacity:.25}
.map-embed.is-switching::after{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    linear-gradient(90deg,transparent,rgba(255,255,255,.07) 50%,transparent),
    var(--smoke-700);
  background-size:220% 100%,100% 100%;
  animation:skeleton-sweep 1.2s linear infinite
}
.map-caption{
  margin-top:10px;font-family:var(--mono);font-size:12px;color:var(--muted)
}
.map-caption strong{color:var(--off-white);font-weight:600}

/* ---------- schedule ---------- */
.schedule{margin:0;padding:0;list-style:none}
.schedule li{
  display:flex;align-items:baseline;justify-content:space-between;gap:20px;
  border-bottom:1px solid var(--smoke-600)
}
.schedule li.static,.schedule li.is-closed{padding-block:15px}

/* Days with a known pitch select the map. Real buttons, so they are
   keyboard reachable and announce their pressed state. */
.day-btn{
  flex:1;display:flex;align-items:baseline;justify-content:space-between;gap:20px;
  width:100%;padding:15px 12px 15px 14px;margin-inline:-14px -12px;
  background:transparent;border:0;cursor:pointer;text-align:left;
  color:inherit;font:inherit;position:relative;
  transition:background-color var(--fast) var(--ease), color var(--fast) var(--ease)
}
.day-btn::before{
  content:"";position:absolute;left:0;top:8px;bottom:8px;width:3px;
  background:var(--flame-orange);transform:scaleY(0);transform-origin:center;
  transition:transform var(--fast) var(--ease)
}
.day-btn:hover{background:var(--smoke-700)}
.day-btn:hover::before{transform:scaleY(.6)}
.day-btn[aria-pressed="true"]{background:var(--smoke-700)}
.day-btn[aria-pressed="true"]::before{transform:scaleY(1)}
.day-btn[aria-pressed="true"] .day{color:var(--flame-yellow)}
.day-btn:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:-2px}
.schedule .day{font-weight:700}
.schedule .spot{color:var(--muted);font-size:14px;display:block;font-weight:400}
.schedule .hours{font-family:var(--mono);font-size:13px;color:var(--muted);white-space:nowrap}
.schedule li.is-closed{opacity:.55}

/* ---------- menu page ---------- */
.filter-rail{
  border:1px solid var(--smoke-600);background:var(--smoke-700);
  padding:18px;position:sticky;top:96px
}
.filter-rail h2{font-family:var(--body);font-weight:700;font-size:13px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin-bottom:14px}
.filter-list{margin:0;padding:0;list-style:none;display:grid;gap:2px}
.filter-btn{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:12px;
  background:transparent;border:0;border-left:0;cursor:pointer;
  color:var(--off-white);font-family:var(--body);font-size:15px;text-align:left;
  padding:9px 10px;
  transition:background-color var(--fast) var(--ease), color var(--fast) var(--ease)
}
.filter-btn .count{font-family:var(--mono);font-size:12px;color:var(--muted)}
.filter-btn:hover{background:var(--smoke-650)}
.filter-btn[aria-pressed="true"]{background:var(--flame-yellow);color:var(--near-black);font-weight:700}
.filter-btn[aria-pressed="true"] .count{color:var(--near-black)}
.rail-note{
  margin-top:16px;padding-top:14px;border-top:1px solid var(--smoke-600);
  font-family:var(--mono);font-size:11px;line-height:1.5;color:var(--muted)
}

.menu-group{margin-bottom:44px}
.menu-group:last-child{margin-bottom:0}
.menu-group > h2{
  font-family:var(--body);font-size:13px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--flame-orange-lift);margin-bottom:16px;
  padding-bottom:8px;border-bottom:1px solid var(--smoke-600)
}
.menu-items{display:grid;grid-template-columns:1fr 1fr;gap:10px 36px}
.menu-item{display:flex;gap:16px;align-items:flex-start;padding-block:14px}
.menu-item .ph{flex:0 0 88px;width:88px}
.menu-item .body{flex:1;min-width:0;display:grid;gap:5px}
.menu-item h3{font-family:var(--body);font-weight:700;font-size:16px}
.menu-item p{color:var(--muted);font-size:14px}
.menu-item[hidden]{display:none}

.empty-state{
  border:1px dashed var(--smoke-600);padding:52px 24px;text-align:center;
  display:grid;gap:14px;justify-items:center
}
.empty-state h2{font-family:var(--body);font-size:18px;font-weight:700}
.empty-state p{color:var(--muted);max-width:44ch}

/* ---------- gallery carousel ----------
   Native scroll-snap does the actual scrolling, so with no JS this is still a
   working horizontal gallery: it scrolls by wheel, trackpad, touch and — since
   the track is focusable — by keyboard. The arrows and dots are progressive
   enhancement and stay hidden until the script enables them, rather than
   sitting there dead.

   No autoplay. A carousel that moves on its own steals focus from whatever
   someone is reading, and every accessible implementation then has to bolt a
   pause button back on. If the client wants motion, a pause control comes
   with it. */
.carousel{position:relative}

.carousel-track{
  display:flex;gap:var(--gutter);
  margin:0;padding:0 0 4px;list-style:none;
  overflow-x:auto;overflow-y:hidden;
  scroll-snap-type:x mandatory;
  /* No scroll-behavior here on purpose. The script passes `behavior` on each
     scrollTo, and having CSS animate it as well makes the two fight — the
     track stalls a few pixels in, then jumps to the end. */
  scrollbar-width:none;
  overscroll-behavior-x:contain   /* do not chain to the page or trigger back-swipe */
}
.carousel-track::-webkit-scrollbar{display:none}
.carousel-track:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:4px}

.carousel-slide{
  flex:0 0 calc((100% - 2 * var(--gutter)) / 3);
  scroll-snap-align:start
}

.carousel-btn{
  position:absolute;top:calc(50% - 26px);z-index:2;
  width:52px;height:52px;border-radius:50%;
  background:var(--smoke-900);border:2px solid var(--flame-orange);
  cursor:pointer;display:grid;place-items:center;
  transition:background-color var(--fast) var(--ease), border-color var(--fast) var(--ease),
             opacity var(--fast) var(--ease), transform var(--fast) var(--ease)
}
.carousel-btn.prev{left:-26px}
.carousel-btn.next{right:-26px}
.carousel-btn::before{
  content:"";width:12px;height:12px;
  border-top:2px solid var(--off-white);border-right:2px solid var(--off-white);
  transition:border-color var(--fast) var(--ease)
}
.carousel-btn.prev::before{transform:translateX(3px) rotate(-135deg)}
.carousel-btn.next::before{transform:translateX(-3px) rotate(45deg)}
.carousel-btn:hover{background:var(--flame-orange);transform:scale(1.06)}
.carousel-btn:hover::before{border-color:var(--near-black)}
.carousel-btn:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:3px}
.carousel-btn[disabled]{opacity:.3;cursor:default;transform:none}
.carousel-btn[disabled]:hover{background:var(--smoke-900);transform:none}
.carousel-btn[disabled]:hover::before{border-color:var(--off-white)}

.carousel-dots{display:flex;justify-content:center;margin-top:22px}
/* The button is always the width of the ACTIVE pill; the dot is drawn by
   ::before and clip-path reveals the pill. Animating width moved every
   sibling on every slide change (layout on each frame); clip-path changes
   paint only. The fixed box also makes each dot a 26x24 target instead of a
   9px one, which clears WCAG 2.5.8. --dot and --pill are the only knobs. */
.carousel-dot{
  --dot:9px;--pill:26px;
  position:relative;width:var(--pill);height:24px;padding:0;border:0;
  background:none;cursor:pointer
}
.carousel-dot::before{
  content:"";position:absolute;left:0;right:0;top:50%;
  height:var(--dot);margin-top:calc(var(--dot) / -2);
  border-radius:calc(var(--dot) / 2);background:var(--smoke-600);
  clip-path:inset(0 calc((var(--pill) - var(--dot)) / 2) round calc(var(--dot) / 2));
  transition:background-color var(--fast) var(--ease), clip-path var(--fast) var(--ease)
}
.carousel-dot[aria-current="true"]::before{
  background:var(--flame-orange);clip-path:inset(0 0 round calc(var(--dot) / 2))
}
.carousel-dot:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:3px}

/* ---------- gallery zoom ----------
   Each gallery picture is wrapped in a button by the script, so the whole
   image is one large target and it enlarges on tap or Enter. Built as
   progressive enhancement: with no JS (or no <dialog> support) the markup
   stays a plain picture rather than a control that does nothing. */
.zoom-btn{
  position:relative;display:block;width:100%;height:100%;
  padding:0;border:0;background:none;cursor:zoom-in
}
.zoom-btn::after{
  /* Standing badge rather than a hover reveal — a touch screen has no hover,
     and this is the only cue that the picture does anything. */
  content:"+";position:absolute;right:10px;bottom:10px;
  width:36px;height:36px;border-radius:50%;
  display:grid;place-items:center;
  font-family:var(--body);font-size:23px;font-weight:700;line-height:1;
  color:var(--near-black);background:var(--flame-yellow);
  box-shadow:0 2px 10px rgba(0,0,0,.5);
  transition:transform var(--fast) var(--ease), background-color var(--fast) var(--ease)
}
.zoom-btn:hover::after{transform:scale(1.09);background:var(--flame-orange-lift)}
.zoom-btn:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:3px}

.lightbox{
  inset:0;margin:0;border:0;padding:0;
  width:100%;max-width:100%;height:100%;max-height:100%;
  background:transparent;overflow:hidden
}
.lightbox::backdrop{background:rgba(10,10,11,.93)}
.lightbox-fig{
  margin:0;height:100%;
  display:grid;grid-template-rows:1fr auto;gap:14px;
  padding:clamp(12px,3vw,34px);place-items:center
}
.lightbox-img{
  max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain
}
.lightbox-cap{
  margin:0;font-size:15px;line-height:1.45;color:var(--off-white);
  text-align:center;max-width:70ch
}
.lightbox-close{
  position:absolute;top:14px;right:14px;z-index:2;
  width:48px;height:48px;border-radius:50%;cursor:pointer;
  background:var(--smoke-900);border:2px solid var(--flame-orange);
  display:grid;place-items:center
}
.lightbox-close::before,.lightbox-close::after{
  content:"";position:absolute;width:20px;height:2px;background:var(--off-white);
  transition:background-color var(--fast) var(--ease)
}
.lightbox-close::before{transform:rotate(45deg)}
.lightbox-close::after{transform:rotate(-45deg)}
.lightbox-close:hover{background:var(--flame-orange)}
.lightbox-close:hover::before,.lightbox-close:hover::after{background:var(--near-black)}
.lightbox-close:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:3px}

/* ---------- dish detail ----------
   A menu row opens a card carrying the photo at a useful size and the copy
   that actually sells the dish. The list stays scannable: one line each,
   which is the whole reason a second surface is needed.

   Progressive enhancement. The long copy ships in the HTML and is hidden by
   this stylesheet, with a <noscript> override on the page that reveals it
   again — so with JavaScript off the descriptions are simply inline and
   nothing is lost. The script adds the button; it is never in the markup,
   because a control that cannot do anything is worse than no control. */
.item-more{display:none}

.menu-item{position:relative}
.item-open{
  font:inherit;color:inherit;background:none;border:0;padding:0;margin:0;
  text-align:left;cursor:pointer
}
/* The accessible name is the dish name alone, but the hit area is the whole
   row — the picture is most of what people aim at. */
.item-open::after{content:"";position:absolute;inset:0}
.menu-item:hover h3,.menu-item:focus-within h3{color:var(--flame-orange-lift)}
.menu-item:hover .ph,.menu-item:focus-within .ph{border-color:var(--flame-orange)}

/* The affordance is a disclosure chevron at the end of the row, not a
   "+ View dish" caption. The caption said the same four words twelve times
   down the page, competing with the dish names for attention; a chevron is
   the standing convention for "this row opens something" and costs one
   glyph. It is decorative — the button already announces what it does. */
.menu-item::after{
  content:"";flex:0 0 auto;align-self:center;
  width:8px;height:8px;margin-left:6px;
  border-top:2px solid var(--smoke-600);
  border-right:2px solid var(--smoke-600);
  transform:rotate(45deg);
  transition:border-color var(--fast) var(--ease), transform var(--fast) var(--ease)
}
.menu-item:hover::after,.menu-item:focus-within::after{
  border-color:var(--flame-orange);transform:rotate(45deg) translate(2px,-2px)
}

/* Keyboard only. :focus-within fires on a mouse click too, so tapping a dish
   and closing the card left a yellow box drawn around the row — which read as
   a selection highlight rather than a focus ring. Outlining the button's own
   full-row overlay gives a row-sized ring at the site's standard 2px offset,
   and only when the browser says focus should be visible. */
.item-open:focus-visible{outline:none}
.item-open:focus-visible::after{outline:2px solid var(--flame-yellow);outline-offset:2px}

.dish{
  inset:0;margin:auto;border:0;padding:0;
  width:min(880px,calc(100vw - 32px));max-width:calc(100vw - 32px);
  /* fit-content for the same reason as the phone rule: centred with inset:0
     and margin:auto, a height:auto dialog resolves to fill, which left the
     card taller than its content and put slack inside the copy column. */
  height:fit-content;max-height:calc(100vh - 32px);
  background:var(--smoke-800);color:var(--off-white);
  border:1px solid var(--smoke-600);overflow:hidden
}
.dish::backdrop{background:rgba(10,10,11,.88)}

/* 60 / 40, picture left. The photograph is the reason the card exists, so it
   takes the larger share; the right column is a short read, not an essay. */
.dish-inner{display:grid;grid-template-columns:60% 40%;max-height:calc(100vh - 32px)}
.dish-media{
  position:relative;background:var(--smoke-700);
  /* No aspect-ratio in the two-column layout. A fixed ratio fixes the height,
     so when the copy column ran taller the picture stopped short and left a
     band of empty card under it. Stretching to the row and setting a floor
     lets the copy decide the height and the photograph fill it. */
  align-self:stretch;min-height:clamp(300px,30vw,430px)
}
.dish-media > *{
  /* Absolute, so the picture cannot dictate the card height. The clone keeps
     whatever aspect-ratio the menu row used (.ph-11 is square), and in flow
     that ratio was setting the media height and therefore the whole card —
     880px wide meant a 527px tall card with 77px of dead space in the copy.
     Out of flow, the copy column decides the height and the picture fills it. */
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block
}
.dish-body{
  /* Generous and evenly spaced: this column is a short read, and cramming it
     against the fold was making a four-line list feel like a form. */
  padding:32px 34px 34px;gap:18px;
  /* flex, not grid: margin-top:auto on the action can only absorb free space
     in a flex column. In a grid with align-content:start the leftover sits
     after the tracks, out of an item's reach, which is why the button floated
     with 90px of nothing under it. */
  display:flex;flex-direction:column;
  overflow-y:auto   /* the copy scrolls, the picture does not */
}
.dish-body .eyebrow{margin:0}
.dish-body h2{font-size:25px;line-height:1.15}
.dish-lede{font-size:16px;line-height:1.5;color:var(--off-white)}

/* The wrapper the detail block is cloned into. It needs its own rhythm: it is
   one grid item of .dish-body, so the body's gap spaces it from the heading
   above and the button below, and nothing inside it. */
.dish-copy{display:grid;gap:14px}

/* The description is a contents list, not a pitch: what is actually on the
   plate, in the order it would be read out. */
.incl-label{
  font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--muted);margin:0
}
.item-includes{
  /* Two across wherever the column can hold it. A contents list is a set of
     short nouns, so a single column of four wasted the width and, on a phone,
     pushed the CTA off the bottom of the card. auto-fit rather than a fixed
     2, so a long ingredient name drops to one per row instead of truncating. */
  margin:4px 0 0;padding:0;list-style:none;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));
  gap:10px 14px;
  font-size:15px;line-height:1.4;color:var(--off-white)
}
.item-includes li{display:flex;gap:9px;align-items:baseline}
.item-includes li::before{
  content:"";flex:0 0 auto;width:5px;height:5px;border-radius:50%;
  background:var(--flame-orange);transform:translateY(-2px)
}
.dish-facts{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:18px;
  margin:8px 0 0
}
.dish-facts div{display:grid;gap:5px}
.dish-facts dt{
  font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--muted)
}
.dish-facts dd{margin:0;font-size:15px;font-weight:600}
.dish-actions{
  display:flex;
  border-top:1px solid var(--smoke-600);padding-top:22px;
  /* auto, not a fixed margin: the column is as tall as the picture beside it,
     so when the copy is short there is slack. Let the action absorb it and sit
     at the foot of the column — the rule then reads as a footer divider rather
     than as a line with a button floating under it. On a phone the card is
     sized to its content, so there is no slack and this does nothing. */
  margin-top:auto
}
.dish-actions .btn{flex:1}
.dish-close{
  position:absolute;top:12px;right:12px;z-index:2;
  width:44px;height:44px;border-radius:50%;cursor:pointer;
  background:var(--smoke-900);border:2px solid var(--flame-orange);
  display:grid;place-items:center
}
.dish-close::before,.dish-close::after{
  content:"";position:absolute;width:18px;height:2px;background:var(--off-white);
  transition:background-color var(--fast) var(--ease)
}
.dish-close::before{transform:rotate(45deg)}
.dish-close::after{transform:rotate(-45deg)}
.dish-close:hover{background:var(--flame-orange)}
.dish-close:hover::before,.dish-close:hover::after{background:var(--near-black)}
.dish-close:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:3px}

@media (max-width:760px){
  /* Below this the 40% column is too narrow for a contents list, so the card
     stacks and the picture goes back on top. */
  .dish-inner{grid-template-columns:1fr}
  /* Stacked again, so the ratio comes back — there is no second column left
     for it to match. */
  /* An explicit height, not aspect-ratio alone. Everything inside is
     absolutely positioned, so the box has no content height of its own,
     and older WebKit can drop aspect-ratio on a stretched grid item —
     the box collapses to 0px and the photo vanishes. The height below is
     the same 16:10 of the card width (.dish is 100vw - 32px here), capped
     as before; aspect-ratio stays as the fallback. */
  .dish-media{aspect-ratio:16/10;min-height:0;max-height:38vh;align-self:start;
    height:min(calc((100vw - 32px) * .625),38vh)}
}

@media (max-width:560px){
  /* A centred card at 85% of the width rather than a full screen: the margin
     and the dimmed page behind it are what say "this is a layer you can
     dismiss" — a full-bleed panel reads as a new page. */
  .dish{
    width:85vw;max-width:85vw;
    /* fit-content, not auto. The dialog is centred with inset:0 + margin:auto,
       and an absolutely positioned box with both edges pinned and height:auto
       resolves to FILL that space — which left 200px of empty card under the
       CTA. fit-content makes it shrink to the content and stay centred. */
    height:fit-content;max-height:calc(100vh - 48px)
  }
  .dish-inner{max-height:calc(100vh - 48px)}
  /* Same fix at phone width, where the card is 85vw. */
  .dish-media{aspect-ratio:16/10;min-height:0;max-height:30vh;align-self:start;
    height:min(calc(85vw * .625),30vh)}
  .dish-body{padding:20px 20px 22px;gap:14px}
  .dish-body h2{font-size:22px}
  /* Exactly two across on a phone: the column is narrow and predictable, so
     auto-fit has nothing to decide. */
  .item-includes{grid-template-columns:1fr 1fr}
  .dish-copy{gap:12px}
  .dish-facts{gap:14px}
  .dish-actions{padding-top:20px}
  .dish-close{top:10px;right:10px;width:40px;height:40px}
}



/* ---------- map block ---------- */
.map-block{display:grid;gap:18px}
.map-actions{display:flex;gap:14px;flex-wrap:wrap}
.address-card{
  border:1px solid var(--smoke-600);background:var(--smoke-700);padding:20px;
  display:grid;gap:10px
}
.address-card dt{
  font-family:var(--mono);font-size:11px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--muted);margin-bottom:2px
}
.address-card dd{margin:0;font-size:15px}
.address-card a{text-decoration:none}
.address-card a:hover{text-decoration:underline}

/* ---------- forms ---------- */
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px 24px}
.field{display:grid;gap:7px}
.field-wide{grid-column:1 / -1}
.field label{font-size:14px;font-weight:600}
.field .req{color:var(--flame-orange-lift)}
.field .hint{font-size:13px;color:var(--muted)}
.field input,.field select,.field textarea{
  font-family:var(--body);font-size:15px;color:var(--off-white);
  background:var(--smoke-700);border:1px solid var(--edge);border-radius:2px;
  padding:0 13px;height:46px;width:100%;
  transition:border-color var(--fast) var(--ease)
}
.field textarea{height:132px;padding-block:12px;resize:vertical;line-height:1.5}
.field input::placeholder,.field textarea::placeholder{color:var(--placeholder)}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--smoke-600)}
.field input[aria-invalid="true"],.field select[aria-invalid="true"],.field textarea[aria-invalid="true"]{
  border-color:var(--flame-yellow);border-width:2px
}
/* Matches errors inside a .field AND the consent error, which sits directly
   in .form-grid. Scoping to .field alone left that one unstyled, unbadged,
   and occupying a grid cell even when empty. */
.field .error,.form-grid > .error{
  font-size:13px;color:var(--flame-yellow);display:flex;gap:8px;align-items:flex-start;min-height:0
}
.field .error:empty,.form-grid > .error:empty{display:none}
.field .error::before,.form-grid > .error::before{content:"!";
  font-weight:700;background:var(--flame-yellow);color:var(--near-black);
  width:16px;height:16px;display:grid;place-items:center;flex:0 0 auto;border-radius:50%;font-size:11px;margin-top:2px
}
.consent{display:flex;gap:11px;align-items:flex-start;grid-column:1 / -1}
.consent input{width:24px;height:24px;flex:0 0 auto;margin-top:1px;accent-color:var(--flame-orange)}
.consent label{font-size:14px;color:var(--muted);font-weight:400}
.form-actions{grid-column:1 / -1;display:flex;align-items:center;gap:20px;flex-wrap:wrap}
.form-actions .note{font-family:var(--mono);font-size:12px;color:var(--muted)}

/* honeypot: off-screen, never displayed to real users */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

.form-status{margin-top:22px}
.form-status:empty{display:none}
.status-box{border:1px solid var(--flame-orange);background:rgba(242,112,27,.12);padding:18px 20px;display:grid;gap:6px}
.status-box strong{font-size:16px}
.status-box span{color:var(--off-white);font-size:14px}
.status-box.is-error{border-color:var(--flame-yellow);background:rgba(255,197,49,.12)}

.info-rail{border:1px solid var(--smoke-600);background:var(--smoke-700);padding:24px;display:grid;gap:16px;align-content:start}
.info-rail h2{font-family:var(--display);font-size:20px}
.info-rail dl{margin:0;display:grid;gap:14px}
.info-rail dt{font-family:var(--mono);font-size:11px;letter-spacing:.11em;text-transform:uppercase;color:var(--muted)}
.info-rail dd{margin:0;font-size:15px}
.info-rail a{text-decoration:none}
.info-rail a:hover{text-decoration:underline}

/* ---------- cta band ---------- */
.cta-band{display:flex;align-items:center;justify-content:space-between;gap:32px}
.cta-band .copy{display:grid;gap:8px}

/* ---------- footer ----------
   One wide identity column carries the wordmark, the line, and the social
   marks; three content columns of similar length sit beside it. An even
   4-up left the social column nearly empty next to three text columns. */
.site-footer{background:var(--smoke-900);border-top:1px solid var(--smoke-600);padding-block:64px 28px}
.footer-grid{display:grid;grid-template-columns:1.7fr 1fr 1fr 1.15fr;gap:40px}
.site-footer h2{
  font-family:var(--body);font-size:12px;font-weight:700;letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted);margin:0 0 16px;
  padding-top:5px /* pulls the small caps onto the wordmark's cap height */
}
.site-footer ul{margin:0;padding:0;list-style:none;display:grid;gap:11px}
.site-footer li{font-size:15px;line-height:1.45}
.site-footer a{color:var(--off-white);text-decoration:none;font-size:15px}
.site-footer a:hover{color:var(--flame-orange-lift)}
.site-footer .muted{font-size:15px}

.footer-mark{display:grid;gap:18px;align-content:start;max-width:34ch}
.footer-mark p{color:var(--muted);font-size:15px;line-height:1.5}
.footer-mark .wordmark{font-size:20px}

.social{display:flex;gap:10px}
/* 38px square: clears the 24px WCAG 2.5.8 minimum with room to spare on a
   thumb. --edge keeps the outline at 3:1 against the footer (1.4.11). */
.social a{
  display:grid;place-items:center;width:38px;height:38px;
  border:1px solid var(--edge);color:var(--muted);
  transition:color var(--fast) var(--ease),border-color var(--fast) var(--ease)
}
.social a:hover{color:var(--flame-orange);border-color:var(--flame-orange)}
.social-icon{width:18px;height:18px;display:block}

.footer-base{
  margin-top:52px;padding-top:22px;border-top:1px solid var(--smoke-600);
  display:flex;align-items:center;justify-content:space-between;gap:16px 28px;flex-wrap:wrap;
  font-family:var(--mono);font-size:12px;color:var(--muted)
}
.footer-legal{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.footer-legal .sep{color:var(--smoke-600)}
.footer-legal a{
  font-family:var(--mono);font-size:12px;color:var(--muted);text-decoration:none;
  border-bottom:1px solid transparent;transition:color var(--fast) var(--ease), border-color var(--fast) var(--ease)
}
.footer-legal a:hover{color:var(--off-white);border-bottom-color:var(--flame-orange)}

/* ---------- legal / long-form page ---------- */
.legal{max-width:68ch}
.legal h2{font-size:24px;margin:44px 0 14px}
.legal h2:first-of-type{margin-top:0}
.legal p,.legal li{color:var(--muted);font-size:16px;line-height:1.65}
.legal p + p{margin-top:14px}
.legal ul{margin:14px 0 0;padding-left:22px;display:grid;gap:9px}
.legal strong{color:var(--off-white);font-weight:600}
.legal a{text-decoration:underline}
.legal dl{margin:16px 0 0;display:grid;gap:14px}
.legal dt{font-weight:700;color:var(--off-white);font-size:15px}
.legal dd{margin:4px 0 0;color:var(--muted)}
.legal-notice{
  border:1px solid var(--flame-orange);background:rgba(242,112,27,.12);
  padding:18px 20px;margin-bottom:40px;max-width:68ch;display:grid;gap:6px
}
.legal-notice strong{font-size:15px}
.legal-notice p{color:var(--off-white);font-size:14px;line-height:1.55}
.legal-updated{font-family:var(--mono);font-size:12px;color:var(--muted);margin-top:40px;padding-top:18px;border-top:1px solid var(--smoke-600);max-width:68ch}

/* ---------- prototype banner ---------- */
.proto-note{
  background:var(--flame-yellow);color:var(--near-black);
  font-family:var(--mono);font-size:12px;text-align:center;padding:7px 16px;font-weight:600
}

/* ==========================================================================
   Scroll reveals

   Built on CSS scroll-driven animations rather than an IntersectionObserver
   so there is no failure mode where content stays hidden: every element is
   fully visible by default, and a browser without view() support simply
   renders the page static. Nothing is gated behind a class that might never
   arrive.

   Each treatment is matched to what it reveals rather than one entrance
   stamped on every section — that uniformity is the tell.
   ========================================================================== */
@media (prefers-reduced-motion:no-preference){
  @supports (animation-timeline:view()){

    /* Lists rise into place, staggered by shifting each item's range rather
       than by delay — with a view timeline, range IS the timing. */
    .item-card,
    .carousel{
      animation:rise-in linear both;
      animation-timeline:view();
      animation-range:entry 6% cover 26%
    }
    .item-card:nth-child(2){animation-range:entry 6% cover 32%}
    .item-card:nth-child(3){animation-range:entry 6% cover 38%}
            
    /* Services: the flame rule draws across, because that rule is the
       element's own device. The text underneath only fades. */
    .service::before{
      animation:draw-rule linear both;
      animation-timeline:view();
      animation-range:entry 10% cover 30%
    }
    .service:nth-child(2)::before{animation-range:entry 10% cover 34%}
    .service:nth-child(3)::before{animation-range:entry 10% cover 38%}
    .service:nth-child(4)::before{animation-range:entry 10% cover 42%}
    .service:nth-child(5)::before{animation-range:entry 10% cover 46%}
    .service:nth-child(6)::before{animation-range:entry 10% cover 50%}
    .service{
      animation:fade-in linear both;
      animation-timeline:view();
      animation-range:entry 10% cover 34%
    }

    /* Schedule rows settle in from the left, the way a list is read down. */
    .schedule li{
      animation:slide-in linear both;
      animation-timeline:view();
      animation-range:entry 8% cover 26%
    }
    .schedule li:nth-child(2){animation-range:entry 8% cover 30%}
    .schedule li:nth-child(3){animation-range:entry 8% cover 34%}
    .schedule li:nth-child(4){animation-range:entry 8% cover 38%}
    .schedule li:nth-child(5){animation-range:entry 8% cover 42%}
    .schedule li:nth-child(6){animation-range:entry 8% cover 46%}

    /* Menu rows: quieter still, since there are twelve of them. */
    .menu-item{
      animation:fade-in linear both;
      animation-timeline:view();
      animation-range:entry 4% cover 22%
    }

    /* Single blocks: one soft lift, no stagger to make. */
    .cta-band,
    .band .map-block,
    .address-card,
    .legal-notice{
      animation:rise-in linear both;
      animation-timeline:view();
      animation-range:entry 8% cover 30%
    }

    /* Section headings settle a touch ahead of their content. */
    .band-head{
      animation:rise-in linear both;
      animation-timeline:view();
      animation-range:entry 4% cover 22%
    }
  }
}

@keyframes rise-in{
  from{opacity:0;transform:translateY(14px)}
  to{opacity:1;transform:none}
}
@keyframes fade-in{
  from{opacity:0}
  to{opacity:1}
}
@keyframes slide-in{
  from{opacity:0;transform:translateX(-10px)}
  to{opacity:1;transform:none}
}
@keyframes draw-rule{
  from{transform:scaleX(0)}
  to{transform:scaleX(1)}
}

/* A print or full-page capture never scrolls, so a view-timeline animation
   would freeze that content at its start frame — blank. Pin the end state. */
@media print{
  .item-card,.gallery > *,.service,.service::before,.schedule li,.menu-item,
  .cta-band,.map-block,.address-card,.legal-notice,.band-head{
    animation:none !important;opacity:1 !important;transform:none !important
  }
}

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

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}
  /* Hold the flame at its brightest resting frame rather than a random one,
     and drop the displacement entirely — distorted glyphs are exactly what
     this preference is asking us not to do. */
  .hero h1{
    animation:none !important;background-position:0 35% !important;
    filter:drop-shadow(0 0 26px rgba(216,52,31,.3)) !important
  }
  .hero .flame-rule::after{animation:none !important;opacity:.34}
}


/* Grid and flex children default to min-width:auto, so one wide descendant
   (the horizontally scrolling chip rail, a long word, a wide table) forces the
   whole track wider than the viewport and quietly clips everything beside it.
   min-width:0 lets the track shrink and hands the overflow to the element that
   actually scrolls. */
.grid > *,
.split-hero > *,
.split-7-5 > *,
.split-5-7 > *,
.split-3-9 > *,
.footer-grid > *,
.form-grid > *,
.carousel-slide,
.menu-item .body,
.day-btn{min-width:0}

/* ==========================================================================
   RESPONSIVE LAYER
   The build began desktop-first on a fixed 1200px canvas, so this reflows it
   rather than pretending it was mobile-first. Breakpoints sit where the
   content actually breaks, not at device names:

     1180  three-across rows run out of room
      940  side-by-side splits stop working
      860  the horizontal nav no longer fits -> drawer + sticky call bar
      560  dish dialog goes to a centred card
      460  small phones: tighten spacing, never shrink tap targets

   Plus two device-class rules that are not width breakpoints:
     (hover:none)                      44px minimum tap targets, any width
     landscape phone, max-height 520   unstick the header and the CTA bar
   ========================================================================== */

/* ---------- nav toggle + drawer chrome (inert until 860) ---------- */
.nav-toggle{
  display:none;
  width:46px;height:46px;flex:0 0 auto;
  background:transparent;border:1px solid var(--smoke-600);border-radius:4px;
  cursor:pointer;place-items:center;
  transition:border-color var(--fast) var(--ease)
}
.nav-toggle:hover{border-color:var(--flame-orange)}
.nav-toggle:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:2px}
.nav-bars,.nav-bars::before,.nav-bars::after{
  content:"";display:block;width:20px;height:2px;background:var(--off-white);
  transition:transform var(--fast) var(--ease), background-color var(--fast) var(--ease)
}
.nav-bars{position:relative}
.nav-bars::before{position:absolute;top:-6px}
.nav-bars::after{position:absolute;top:6px}
.nav-toggle[aria-expanded="true"] .nav-bars{background:transparent}
.nav-toggle[aria-expanded="true"] .nav-bars::before{transform:translateY(6px) rotate(45deg)}
.nav-toggle[aria-expanded="true"] .nav-bars::after{transform:translateY(-6px) rotate(-45deg)}

.nav-scrim{
  position:fixed;inset:0;z-index:90;
  background:rgba(10,10,11,.62);
  opacity:0;transition:opacity var(--fast) var(--ease)
}
.nav-scrim.is-open{opacity:1}

/* ---------- sticky phone action bar (shown at 760) ---------- */
.mobile-cta{
  display:none;
  position:fixed;left:0;right:0;bottom:0;z-index:95;
  gap:10px;padding:10px 14px;
  padding-bottom:calc(10px + env(safe-area-inset-bottom));
  background:var(--smoke-900);
  border-top:1px solid var(--smoke-600)
}
.mobile-cta-btn{
  flex:1;display:flex;align-items:center;justify-content:center;gap:8px;
  min-height:50px;padding:0 16px;border-radius:4px;
  font-size:15px;font-weight:700;letter-spacing:.03em;text-decoration:none;
  color:var(--off-white);background:var(--smoke-700);border:1px solid var(--edge)
}
.mobile-cta-btn.primary{
  background:var(--flame-yellow);color:var(--near-black);border-color:var(--flame-yellow)
}
/* Inline SVG, not a glyph or emoji. ☎ (U+260E) rendered as a full-colour
   emoji on most phones — wrong weight, wrong colour, and it ignored
   currentColor. fill:currentColor keeps the icon locked to the label. */
.mobile-cta-btn .cta-icon{
  width:18px;height:18px;flex:0 0 auto;fill:currentColor;display:block
}
.mobile-cta-btn:focus-visible{outline:2px solid var(--flame-yellow);outline-offset:2px}

/* ==========================================================================
   1180 — three-across rows run out of room
   ========================================================================== */
@media (max-width:1180px){
  .g-6,.g-5{grid-template-columns:repeat(3,1fr)}
  .g-4{grid-template-columns:repeat(2,1fr)}
  .gallery{grid-template-columns:repeat(3,1fr)}
  .carousel-slide{flex:0 0 calc((100% - var(--gutter)) / 2)}
  .split-3-9{grid-template-columns:4fr 8fr;gap:28px}
  .footer-grid{grid-template-columns:1.6fr 1fr 1fr;gap:32px}
  .footer-grid > div:last-child{grid-column:2 / -1}
}

/* ==========================================================================
   940 — side-by-side splits stop working
   ========================================================================== */
@media (max-width:940px){
  :root{--band:56px}

  .split-hero,.split-7-5,.split-5-7,.split-3-9{grid-template-columns:1fr;gap:34px}

  .split-hero{gap:28px}
  .hero{padding-block:52px 56px}
  /* Cap the art so the headline and the buttons are reachable without a
     scroll on a standard phone screen. */
  /* Full-bleed width. Stacked above the copy, a picture inset by the
     container gutter reads as a floating card rather than the top of the
     page, so cancel the gutter with a matching negative margin — the exact
     same clamp() the container uses, so the two always agree. */
  /* The 38vh cap belongs on the FRAME, not the image. On the image it was a
     constraint violation on a replaced element, so the browser preserved the
     aspect ratio by shrinking the WIDTH to match the clamped height — which
     quietly undid the full-bleed on any viewport tall enough to trigger it
     (fine at 375x812, letterboxed to ~408px on a 668-wide screen). Capping
     the frame lets the box keep its full width and the image cover it. */
  .hero .media-frame{
    /* display:block for the same reason as the desktop rule — inside the
       centring grid the image has no definite height to measure against.
       The width is stated rather than left auto because auto width plus
       aspect-ratio plus the max-height clamp makes the box derive its WIDTH
       from the clamped height and shrink with it; it is written as the
       content width plus both gutters so the negative margin still lands the
       edges on the viewport. */
    display:block;
    aspect-ratio:3/2;max-height:38vh;
    width:calc(100% + 2 * clamp(18px, 4vw, 24px));
    margin-inline:calc(-1 * clamp(18px, 4vw, 24px))
  }
  .hero .media-frame img{max-height:none;width:100%;height:100%;object-fit:cover}
  .hero-copy{justify-items:stretch}
  .hero .media-frame{order:-1}
  .hero .lead{max-width:none}

  /* Once the hero stacks, the picture sits ABOVE the copy rather than beside
     it, so a left-edge fade no longer points anywhere — the edge it was
     blending into is gone. Rotate the mask to run bottom-up instead: solid to
     75%, dissolving into the page over the last quarter, so the artwork melts
     into the headline underneath the way a full-bleed hero does. Same 75/25
     split as the horizontal version, just turned 90 degrees. */
  .media-fade-left{
    -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 75%,transparent 100%);
    mask-image:linear-gradient(180deg,#000 0%,#000 75%,transparent 100%)
  }

  .g-5,.g-6{grid-template-columns:repeat(2,1fr)}
  .menu-items{grid-template-columns:1fr;gap:0}
  .filter-rail{position:static}
  .band-head{flex-direction:column;align-items:flex-start;gap:10px}
  .cta-band{flex-direction:column;align-items:flex-start;gap:18px}
  .cta-band .btn{width:100%;justify-content:center}
  .info-rail{margin-top:8px}
}

/* ==========================================================================
   860 — the horizontal nav no longer fits.
   Measured, not guessed: at 768 the brand, five links and the CTA together
   overflow the bar, wrapping "Find Us" onto two lines and the button onto
   three. The drawer takes over before that happens.
   ========================================================================== */
@media (max-width:860px){
  :root{--band:44px;--gutter:16px}

  /* ---- header: mark + hamburger ---- */
  .site-header .bar{min-height:64px;padding-block:10px;gap:12px}
  .site-header.is-scrolled .bar{min-height:56px}
  .brand-mark{width:44px;height:44px}
  .site-header.is-scrolled .brand-mark{width:40px;height:40px}
  .brand .wordmark{font-size:17px}
  .brand-tag{display:none}
  .header-actions .btn{display:none}
  /* .site-nav carries margin-left:auto on desktop, which is what pushes the
     right-hand group over. On mobile the nav is position:fixed and out of
     flow, so that auto margin stops doing anything and the toggle collapses
     next to the wordmark. The actions group has to claim the space itself. */
  .header-actions{margin-left:auto}
  .nav-toggle{display:grid}

  /* ---- nav as an off-canvas drawer ---- */
  .site-nav{
    position:fixed;top:0;right:0;bottom:0;z-index:100;
    width:min(78vw, 340px);
    flex-direction:column;align-items:stretch;gap:0;
    margin:0;padding:84px 20px calc(28px + env(safe-area-inset-bottom));
    background:var(--smoke-900);
    border-left:1px solid var(--smoke-600);
    box-shadow:-24px 0 60px -30px #000;
    transform:translateX(100%);
    transition:transform .26s var(--ease);
    overflow-y:auto;overscroll-behavior:contain;
    pointer-events:none
  }
  .site-nav.is-open{transform:translateX(0);pointer-events:auto}
  /* Keyboard reachability is handled by the `inert` attribute in site.js, not
     by visibility. Transitioning visibility either blocks focus for the whole
     animation (on open) or leaves the closed panel tabbable off-screen (on
     close) — both were observed. inert removes it from the tab order and the
     accessibility tree outright, with no timing to get wrong. */
  .site-nav[inert]{transform:translateX(100%)}
  .site-nav a{white-space:nowrap}
  .site-nav:focus{outline:none}
  .site-nav a{
    font-size:19px;font-weight:600;color:var(--off-white);
    padding:16px 4px 16px 14px;
    border-bottom:1px solid var(--smoke-700);
    border-left:3px solid transparent
  }
  .site-nav a[aria-current="page"]{
    color:var(--flame-yellow);
    border-left-color:var(--flame-orange)
  }

  /* The drawer is a 340px panel at z-index 100 and the sticky bar sits at 95,
     so while the drawer is open the bar's "Book the grill" is covered. Without
     this, opening the menu takes the primary action off the screen. Pinned to
     the bottom of the panel with margin-top:auto, as drawn. */
  .site-nav .nav-cta{
    margin-top:auto;
    display:flex;align-items:center;justify-content:center;
    min-height:50px;padding:0 16px;
    background:var(--flame-yellow);color:var(--near-black);
    border:0;border-radius:4px;
    font-weight:700;letter-spacing:.03em;white-space:nowrap
  }
  /* .site-nav a:hover sets --off-white, which on a yellow fill is 1.8:1.
     The CTA keeps near-black in every state and darkens the fill instead. */
  .site-nav .nav-cta[aria-current="page"],
  .site-nav .nav-cta:hover,
  .site-nav .nav-cta:focus-visible{color:var(--near-black)}
  .site-nav .nav-cta:hover{background:var(--flame-orange)}

  /* ---- sticky quick actions ---- */
  :root{--cta-bar-h:calc(71px + env(safe-area-inset-bottom, 0px))}
  .mobile-cta{display:flex}

  /* The space behind the bar is reserved by the FOOTER, not by body padding.
     body is --smoke-800 and the footer is --smoke-900, so padding the body
     left a lighter band between the end of the footer and the bar.

     It is also derived rather than hardcoded. The bar is 10 + 50 + 10 plus
     the home-indicator inset; the old body{padding-bottom:78px} was a fixed
     number that could not track that. With inset 0 it over-reserved by 13px
     (the visible band); on an iPhone with a 34px inset the bar grows to 104px
     against a 78px reserve and covers the last 26px of the footer. */
  .site-footer{padding-bottom:calc(28px + var(--cta-bar-h))}

  /* ---- content ---- */
  .lead{font-size:17px}
  .band-head{margin-bottom:24px}

  .g-2,.g-3,.g-4,.g-5,.g-6{grid-template-columns:1fr}
  .grid{gap:22px}
  .gallery{grid-template-columns:repeat(2,1fr)}

  /* Exactly one card at a time. The peeking sliver of the next slide was
     meant to advertise the swipe, but it read as a misaligned layout; the
     dots carry that job instead. */
  .carousel-slide{flex:0 0 100%}
  .carousel-btn{display:none}
  .carousel-dots{margin-top:16px}

  .form-grid{grid-template-columns:1fr;gap:18px}
  .field input,.field select{height:50px}
  .field textarea{height:150px}
  .form-actions{flex-direction:column;align-items:stretch;gap:12px}
  .form-actions .btn{width:100%;justify-content:center}
  .form-actions .note{text-align:center}

  .hero-facts{flex-direction:column;gap:14px;padding-top:20px}
  .hero-actions{width:100%;flex-direction:column}
  .hero-actions .btn{width:100%;justify-content:center}

  .schedule li{flex-wrap:wrap}
  .day-btn{flex-wrap:wrap;gap:6px 20px;padding-block:16px}
  .day-btn .hours{width:100%}
  .map-actions{flex-direction:column}
  .map-actions .btn{width:100%;justify-content:center}

  .menu-item .ph{flex:0 0 72px;width:72px}

  /* Categories: WRAP, do not scroll sideways.
     Measured at 375px, the horizontal rail showed 2 of 5 categories and hid
     the rest behind a scroll with no visible affordance — "Meal prep" and
     "Drinks & sweets" simply did not exist as far as the visitor knew.
     Wrapping to two rows costs ~40px and shows every option at once. */
  .filter-list{
    display:flex;flex-wrap:wrap;gap:8px;
    overflow:visible;padding:0;margin:0;list-style:none
  }
  .filter-list li{flex:0 0 auto}
  .filter-btn{
    white-space:nowrap;border:1px solid var(--smoke-600);border-radius:999px;
    padding:11px 15px;gap:8px;width:auto;background:var(--smoke-700)
  }
  .filter-btn .count{font-size:11px;opacity:.8}
  .filter-btn[aria-pressed="true"]{
    border-color:var(--flame-yellow);background:var(--flame-yellow);color:var(--near-black)
  }
  /* Kept, not hidden. This is the "menu changes with the season, call to
     confirm" line — the one standing caveat on the page, and the device most
     people read it on is the one it used to be display:none for. Demoted in
     weight rather than removed. */
  .rail-note{
    display:block;margin-top:14px;padding-top:12px;
    border-top:1px solid var(--smoke-600);
    font-size:12.5px;line-height:1.5;color:var(--muted)
  }

  /* Deliberately NOT sticky. Wrapped chips need three rows at 375px, and
     pinning ~150px of filter under the header would hold nearly a fifth of
     the screen hostage for the whole scroll. The sticky CATEGORY HEADING
     below does the same job for a fraction of the height: you always know
     which section you are in, and the headings are short hops apart. */
  .filter-rail{
    position:static;
    border:0;border-bottom:1px solid var(--smoke-600);
    background:transparent;
    padding:0 0 16px;margin-bottom:4px
  }
  /* The label is kept for screen readers (the list is aria-labelledby it)
     but costs a row of screen on a phone, and chips are self-evident. */
  .filter-rail h2{
    position:absolute;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0
  }

  /* Photography is what sells a menu item, so give it more room. */
  .menu-item{padding-block:16px;gap:14px;align-items:center}
  .menu-item .ph{flex:0 0 88px;width:88px}
  .menu-item h3{font-size:17px}
  .menu-item p{font-size:14px;line-height:1.45}
  .menu-group > h2{
    /* 68px = compressed bar (56) + the flame rule, measured at 375px.
       At 56 the heading slid under the header and lost its top edge. */
    position:sticky;top:68px;z-index:10;
    background:var(--smoke-800);margin:0 0 4px;padding:10px 0;
    border-bottom:1px solid var(--smoke-700)
  }
  [data-menu-status]{
    font-family:var(--mono);font-size:12px;letter-spacing:.06em;
    text-transform:uppercase;color:var(--muted);margin:14px 0 6px
  }

  .footer-grid{grid-template-columns:1fr 1fr;gap:28px}
  .footer-mark{grid-column:1 / -1;max-width:none}
  .footer-grid > div:last-child{grid-column:auto}
  .footer-base{flex-direction:column;align-items:flex-start;gap:10px}

  .legal h2{font-size:21px;margin-top:34px}
  .ruleset li{grid-template-columns:1fr;gap:4px}

  .proto-note{font-size:11px;padding:8px 14px;text-align:left}
}

/* ==========================================================================
   460 — small phones
   ========================================================================== */
@media (max-width:460px){
  :root{--band:36px}
  .brand .wordmark{font-size:15px}
  .brand-mark{width:40px;height:40px}
  .gallery{grid-template-columns:1fr}
  .carousel-slide{flex:0 0 100%}
  .btn{padding:13px 18px;font-size:13px}
  .hero-facts dd{font-size:14px}
  .mobile-cta{padding-inline:10px;gap:8px}
  .mobile-cta-btn{font-size:14px;padding:0 10px}
}

/* Landscape phones are short: a tall sticky header eats the whole screen. */
@media (max-width:900px) and (orientation:landscape) and (max-height:520px){
  .site-header{position:static}
  .mobile-cta{position:static;padding-bottom:10px}
  body{padding-bottom:0}
}

/* Coarse pointers cannot hover, and fingers are not cursors: keep every
   target at 44px regardless of how narrow the viewport gets. */
@media (hover:none){
  .btn,.mobile-cta-btn,.nav-toggle,.filter-btn,.day-btn{min-height:44px}
  .carousel-dot{--dot:12px;--pill:30px}
}
