/* ==========================================================================
   WOK GUYS
   Three colours only: #000000, #E51727, #FFFFFF.
   Every neutral on this page is an alpha of one of those three.
   ========================================================================== */

/* The three faces are served from this folder rather than from Google, so a
   first visit spends no DNS or TLS on a third party before it can draw text.
   Regenerate with: node tools/fetch-fonts.js */
/* === FONT-FACE:START — generated by tools/fetch-fonts.js === */
/* 14 subsets, 417KB on disk. A Latin-only
   page fetches roughly a third of that: unicode-range does the filtering. */
@font-face{font-family:"Archivo";font-style:normal;font-weight:400;font-display:swap;src:url(fonts/archivo-400-ext.woff2) format("woff2");unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:"Archivo";font-style:normal;font-weight:400;font-display:swap;src:url(fonts/archivo-400.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:"Archivo";font-style:normal;font-weight:500;font-display:swap;src:url(fonts/archivo-500-ext.woff2) format("woff2");unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:"Archivo";font-style:normal;font-weight:500;font-display:swap;src:url(fonts/archivo-500.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:"Archivo";font-style:normal;font-weight:600;font-display:swap;src:url(fonts/archivo-600-ext.woff2) format("woff2");unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:"Archivo";font-style:normal;font-weight:600;font-display:swap;src:url(fonts/archivo-600.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:"Montserrat";font-style:normal;font-weight:700;font-display:swap;src:url(fonts/montserrat-700-ext.woff2) format("woff2");unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:"Montserrat";font-style:normal;font-weight:700;font-display:swap;src:url(fonts/montserrat-700.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:"Spline Sans Mono";font-style:normal;font-weight:400;font-display:swap;src:url(fonts/spline-sans-mono-400-ext.woff2) format("woff2");unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:"Spline Sans Mono";font-style:normal;font-weight:400;font-display:swap;src:url(fonts/spline-sans-mono-400.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:"Spline Sans Mono";font-style:normal;font-weight:500;font-display:swap;src:url(fonts/spline-sans-mono-500-ext.woff2) format("woff2");unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:"Spline Sans Mono";font-style:normal;font-weight:500;font-display:swap;src:url(fonts/spline-sans-mono-500.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
@font-face{font-family:"Spline Sans Mono";font-style:normal;font-weight:700;font-display:swap;src:url(fonts/spline-sans-mono-700-ext.woff2) format("woff2");unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF}
@font-face{font-family:"Spline Sans Mono";font-style:normal;font-weight:700;font-display:swap;src:url(fonts/spline-sans-mono-700.woff2) format("woff2");unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
/* === FONT-FACE:END === */

:root{
  /* the locked brand triad */
  --ink:#000000;
  --fire:#E51727;
  --paper:#FFFFFF;

  /* derived on paper */
  --panel:rgba(0,0,0,.035);
  --panel-2:rgba(0,0,0,.06);
  --hair:rgba(0,0,0,.12);
  --edge:rgba(0,0,0,.28);
  --dim:rgba(0,0,0,.55);
  --strong:rgba(0,0,0,.78);

  /* The resting border of a form field. --hair sits at 1.32:1 against paper,
     which is fine for a decorative rule and far too faint for the boundary of
     something you have to find and type into. WCAG asks 3:1 for that; this is
     3.3:1. Fields are the only place on the site that needs the heavier line,
     so it gets its own token rather than pushing --hair up everywhere. */
  --field-edge:rgba(0,0,0,.42);

  /* derived on ink */
  --panel-d:rgba(255,255,255,.06);
  --panel-d2:rgba(255,255,255,.1);
  --hair-d:rgba(255,255,255,.14);
  --edge-d:rgba(255,255,255,.34);
  --dim-d:rgba(255,255,255,.62);

  /* the chopstick angle, taken from the logo */
  --chop:28deg;

  /* The second tone. White with the brand red at about 6 percent and a
     whisper of black, so the palette gains no new hue: it is still only the
     three locked colours, one of them just used lightly. Reads as warm stone
     against pure white rather than as pink. */
  --bone:#F8F1F1;

  /* type */
  --display:'Montserrat',system-ui,sans-serif;
  --body:'Archivo','Helvetica Neue',Arial,sans-serif;
  --mono:'Spline Sans Mono',ui-monospace,monospace;

  --fs-hero:clamp(2.6rem,9vw,8rem);
  --fs-h2:clamp(1.75rem,3.9vw,3.15rem);
  --fs-h3:clamp(1.25rem,2.2vw,1.7rem);
  --fs-body:clamp(1rem,1.05vw,1.0625rem);
  --fs-label:.6875rem;

  /* Tracking. Montserrat's caps need POSITIVE letter-spacing: the family is
     drawn for tight lowercase, so uppercase set at its default sidebearings
     collides, and the previous build's -.03em made that worse. The amount
     scales inversely with size, the way optical sizing would do it by hand:
     big display type needs almost none, small caps need a lot. */
  --track-display:.005em;
  --track-h2:.018em;
  --track-h3:.035em;
  --track-caps:.06em;

  /* space */
  --gut:clamp(1.25rem,5vw,5rem);
  --sec:clamp(4.5rem,10vw,9rem);
  --wrap:1320px;

  /* How far an anchor has to stop short of the top so the fixed nav does not
     sit on the thing you just jumped to. The menu's own anchors need more,
     because the sticky tools bar stacks under the nav there -- see .cat. */
  --nav-h:4.25rem;
  --anchor-offset:calc(var(--nav-h) + 1rem);

  /* Breakpoints, written down so the next person has a ladder to follow rather
     than picking a new number. These are documentation: CSS cannot interpolate
     a custom property into a media query, so the literals below still have to
     match by hand.
        --bp-xs  420px   phone, tools bar wraps
        --bp-sm  520px   phone, hero mark goes full width
        --bp-md  760px   single column
        --bp-lg  900px   nav collapses to the panel (fires at 860)
        --bp-xl  1080px  nav phone number loses its label            */

  /* motion */
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --tshadow:0 1px 2px rgba(0,0,0,.95),0 3px 14px rgba(0,0,0,.8),0 12px 48px rgba(0,0,0,.72);
}

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

html,body{
  overflow-x:hidden;
  overflow-x:clip;
  margin:0;
  padding:0;
}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;scroll-padding-top:var(--anchor-offset)}

body{
  background:var(--paper);
  color:var(--ink);
  font-family:var(--body);
  font-size:var(--fs-body);
  line-height:1.6;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
}
/* The page-load fade only exists when JavaScript is running. Starting at
   opacity 0 unconditionally means a blocked script or one JS error leaves a
   blank white page, so the fade is gated on the html.js flag instead. */
html.js body{opacity:0;transition:opacity .7s var(--ease)}
html.js body.lit{opacity:1}

noscript .ns{
  display:block;margin:2rem 0;padding:1.5rem;
  border:2px solid var(--ink);border-radius:12px;max-width:44rem;
}
noscript .ns strong{display:block;font-family:var(--display);font-weight:700;text-transform:uppercase;font-size:1.25rem;margin-bottom:.5rem}
noscript .ns p{color:var(--dim);font-size:.9375rem}

img,svg{max-width:100%;display:block}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
input,select,textarea{font:inherit;color:inherit}
a{color:inherit;text-decoration:none}
ul,ol{margin:0;padding:0;list-style:none}
h1,h2,h3,h4{margin:0;font-family:var(--display);font-weight:700;text-transform:uppercase}
h1{letter-spacing:var(--track-display);line-height:1.02}
h2{letter-spacing:var(--track-h2);line-height:1.08}
h3,h4{letter-spacing:var(--track-h3);line-height:1.24}
p{margin:0}

::selection{background:var(--fire);color:var(--paper)}

:focus-visible{outline:2.5px solid var(--fire);outline-offset:3px;border-radius:2px}
.on-ink :focus-visible{outline-color:var(--fire)}

.skip{
  position:absolute;left:-9999px;top:0;z-index:200;
  background:var(--ink);color:var(--paper);padding:.9rem 1.25rem;
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.14em;text-transform:uppercase;
}
.skip:focus{left:.5rem;top:.5rem}

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

.wrap{max-width:var(--wrap);margin:0 auto;padding:0 var(--gut)}

/* --------------------------------------------------------------------------
   shared type atoms
   -------------------------------------------------------------------------- */
.kicker{
  font-family:var(--mono);font-size:var(--fs-label);font-weight:500;
  letter-spacing:.2em;text-transform:uppercase;color:var(--fire);
  display:flex;align-items:center;gap:.6rem;margin-bottom:1.5rem;
}
.kicker::before{
  content:"";width:1.75rem;height:2px;background:var(--fire);flex:0 0 auto;
}
/* Measured: #E51727 on #000 is 4.48:1, which fails the 4.5 floor for small
   text. So on black the label goes white and the red stays in the rule,
   which is a graphic and only needs 3:1. Red text lives on white only. */
.on-ink .kicker{color:var(--paper)}
.lede{font-size:clamp(1.0625rem,1.5vw,1.25rem);line-height:1.55;color:var(--dim);max-width:52ch}
.on-ink .lede{color:var(--dim-d)}
h2+.lede,h2+p.lede{margin-top:1.5rem}

.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}
.tiny{font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.16em;text-transform:uppercase}

/* --------------------------------------------------------------------------
   the environment layer (one place, behind everything)
   -------------------------------------------------------------------------- */
/* The diagonal field. Fixed behind everything and pointer-events:none, so it
   can never intercept a tap meant for a menu row. Two things move on it: a
   set of hairlines ruled at the logo's own 28 degree chopstick angle, which
   slide slowly as the page scrolls, and two soft red beams that drift across
   at the same angle. Both are measured in single-digit alpha, because this
   is supposed to give the white sections some weather, not decorate them. */
.env{
  position:fixed;inset:0;z-index:0;pointer-events:none;
  overflow:hidden;
  background-image:repeating-linear-gradient(118deg,
    rgba(0,0,0,.032) 0 1px,
    rgba(0,0,0,0) 1px 132px);
  background-position:0 calc(var(--sy,0px) * -.055);
}
.env .beam{
  position:absolute;left:-40%;right:-40%;height:46vh;
  top:calc(6% + var(--sy,0px) * .012);
  background:linear-gradient(118deg,
    rgba(229,23,39,0) 0%,rgba(229,23,39,.038) 46%,rgba(229,23,39,0) 88%);
  transform:rotate(-28deg);
  filter:blur(46px);
  animation:beamDrift 26s ease-in-out infinite;
}
.env .beam.b2{
  top:calc(62% - var(--sy,0px) * .020);
  background:linear-gradient(118deg,
    rgba(229,23,39,0) 0%,rgba(229,23,39,.026) 52%,rgba(229,23,39,0) 92%);
  animation-duration:34s;animation-delay:-11s;
}
@keyframes beamDrift{
  0%,100%{transform:rotate(-28deg) translateX(-6%)}
  50%{transform:rotate(-28deg) translateX(6%)}
}
/* On the black sections the hairlines would be invisible and the beams would
   fight the photography, so the whole field simply stops there. */
.on-ink,.hero,.foot{position:relative;z-index:1}
.env::before{
  content:"";position:absolute;inset:-20%;
  background:radial-gradient(ellipse 55% 45% at 50% 42%,rgba(229,23,39,.16),rgba(229,23,39,0) 62%);
  animation:breathe 74s ease-in-out -18s infinite;
  opacity:0;
  transition:opacity 1.2s var(--ease);
}
.env::after{
  content:"";position:absolute;inset:0;opacity:.03;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}
@keyframes breathe{
  0%,100%{transform:scale(1) translateY(0)}
  50%{transform:scale(1.14) translateY(-3%)}
}

/* --------------------------------------------------------------------------
   nav
   -------------------------------------------------------------------------- */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:60;
  display:flex;align-items:center;gap:1.5rem;
  padding:.85rem var(--gut);
  transform:translateY(-102%);
  transition:transform .5s var(--ease-out),background-color .4s var(--ease),border-color .4s var(--ease);
  border-bottom:1px solid transparent;
}
.nav.show{transform:translateY(0)}
.nav.solid{background:rgba(255,255,255,.94);backdrop-filter:blur(14px);border-bottom-color:var(--hair)}
/* The official mark, not a typeset stand-in. The tagline is cropped off for
   this size because it is illegible under about 40px tall. */
.nav-mark{display:flex;align-items:center;flex:0 0 auto}
.nav-mark img{height:1.75rem;width:auto;display:block}
.nav-links{display:flex;gap:1.75rem;margin-left:auto}
.nav-links a{
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.14em;text-transform:uppercase;
  padding:.4rem 0;position:relative;color:var(--strong);transition:color .3s var(--ease);
}
.nav-links a::after{
  content:"";position:absolute;left:0;right:100%;bottom:0;height:1.5px;background:var(--fire);
  transition:right .4s var(--ease-out);
}
.nav-links a:hover{color:var(--ink)}
.nav-links a:hover::after{right:0}
.nav-cta{
  flex:0 0 auto;background:var(--fire);color:var(--paper);
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.14em;text-transform:uppercase;
  padding:.7rem 1.15rem;border-radius:999px;
  transition:transform .35s var(--ease-out),box-shadow .35s var(--ease);
}
.nav-cta:hover{transform:translateY(-2px);box-shadow:0 8px 22px rgba(229,23,39,.35)}
@media (max-width:860px){
  .nav-links{display:none}
  .nav-cta{margin-left:auto}
}

/* --------------------------------------------------------------------------
   HERO
   One screen, no scroll-scrubbing. The whole entrance plays on load in about
   1.5s and then holds. Nothing here reacts to scroll, so the hero can never
   be the reason the page feels slow to get past. Identical on every size:
   the only thing that changes is the type scale.
   -------------------------------------------------------------------------- */
.hero{
  position:relative;min-height:100svh;
  display:grid;place-items:center;
  background:var(--ink);color:var(--paper);
  padding:calc(5.5rem + env(safe-area-inset-top)) var(--gut) calc(3.5rem + env(safe-area-inset-bottom));
  overflow:hidden;isolation:isolate;text-align:center;
}
.hero-bg{position:absolute;inset:0;z-index:0;pointer-events:none}
.hero-glow{
  position:absolute;left:50%;top:46%;width:min(115vh,140vw);aspect-ratio:1;
  transform:translate(-50%,-50%);
  background:radial-gradient(circle,rgba(229,23,39,.34),rgba(229,23,39,.10) 42%,rgba(229,23,39,0) 66%);
  animation:heroBreathe 13s ease-in-out infinite;
}
@keyframes heroBreathe{
  0%,100%{transform:translate(-50%,-50%) scale(1);opacity:.85}
  50%{transform:translate(-50%,-50%) scale(1.1);opacity:1}
}
.hero-in{position:relative;z-index:2;width:100%;max-width:62rem}

/* ---- the mark and the ignition ----------------------------------------
   The wordmark is the designer's artwork, placed as an image and never
   redrawn. The only addition is .port: a circle whose left/top/width/height
   are the MEASURED position of the red disc inside that artwork, taken by
   scanning the file's pixels (build-notes/logo-build.js), not eyeballed.

   The film lives in that port. For the ignition it scales up and squares off
   to fill the screen, then collapses back down into the O and stays there,
   burning, for as long as anyone is looking. Because it starts and ends in
   the port, the landing is exact on every screen size, whatever the footage
   happened to be framed like.                                            */
/* Registering the custom property is what lets the mask's soft edge animate
   rather than jump. Browsers without @property simply snap, which is a
   half-second detail nobody will catch. */
@property --edge{syntax:'<percentage>';inherits:false;initial-value:76%}

.lockup{
  position:relative;display:block;margin:0 auto;
  width:min(80vw,58rem);
  font-size:0;                       /* kill the inline-image baseline gap */
}
.logo-img{
  width:100%;height:auto;display:block;
  opacity:0;transform:scale(1.03);
  filter:drop-shadow(0 6px 40px rgba(0,0,0,.55));
}

/* Measured geometry of the red disc within the wordmark artwork.
   Disc is a true 220x220 circle at (753.5, 510.5) in a 2400x950 export;
   the ink box is 1905x561 at (248,185). Percentages below are derived from
   exactly those numbers, so the port and the O cannot drift apart. */
.port{
  position:absolute;
  left:20.7874%;  top:38.5027%;
  width:11.5486%; height:39.2157%;
  border-radius:50%;
  overflow:hidden;
  transform-origin:50% 50%;
  /* A hard circular clip shows its own edge wherever the footage is lighter
     than the page behind it, which is exactly what the steel hob does at the
     sides. Feathering the last few percent hides the boundary at full size,
     and at rest it just means the fire glows inside the disc instead of
     stopping dead against its rim. */
  --edge:76%;
  -webkit-mask-image:radial-gradient(circle at 50% 50%,#000 var(--edge),rgba(0,0,0,0) 99%);
  mask-image:radial-gradient(circle at 50% 50%,#000 var(--edge),rgba(0,0,0,0) 99%);
  transition:--edge .5s var(--ease);
  /* screen keeps the black of the footage transparent, so the red disc and
     the chopsticks crossing it still read underneath the fire */
  mix-blend-mode:screen;
  background:#140406 url("img/ignite-poster.jpg") center/cover no-repeat;
  z-index:2;
  /* defaults so the resting state is correct before JS measures, and if JS
     never runs at all */
  --fs:9; --fx:0px; --fy:0px;
}
.port video{
  width:100%;height:100%;object-fit:cover;display:block;
  /* the footage is graded warm already; this pushes it the last step toward
     the brand red so the fire and the disc are the same red */
  filter:saturate(1.15) hue-rotate(-6deg) contrast(1.06);
  transition:filter .6s var(--ease);
}
/* Once the disc has landed in the O it is only about 107px across, so the
   feathering that hid its edge at full size now eats the fire ring itself.
   Settled, the edge goes crisp against the disc rim and the grade lifts, so
   the flame actually reads at that size. */
.hero.settled .port{--edge:94%}
.hero.settled .port video{filter:saturate(1.3) hue-rotate(-6deg) contrast(1.14) brightness(1.22)}

/* ---- the words under the mark ---- */
.hero-line{
  font-size:clamp(1rem,1.55vw,1.1875rem);line-height:1.55;
  color:var(--paper);max-width:38ch;margin:2rem auto 0;opacity:0;
}
/* The restaurant's tagline, set as the first line of the hero copy. */
.hero-tag{
  display:block;margin-bottom:.6rem;
  font-family:var(--display);font-weight:700;font-size:1.3em;
  letter-spacing:var(--track-h3);text-transform:uppercase;line-height:1.2;
}
.hero-cta{
  margin-top:2.25rem;display:flex;gap:.75rem;justify-content:center;flex-wrap:wrap;
  opacity:0;
}

/* ---- the ignition, on a fixed clock ----------------------------------
   0.00 - 1.35s   the film fills the screen and burns
   1.35 - 2.60s   it collapses into the O and rounds off
   1.90 - 2.60s   the wordmark resolves around it
   2.60 - 3.25s   the line and the buttons arrive
   Nothing is scroll-linked, so scrolling away at any point costs nothing. */
.hero.in .port{animation:ignite 2.6s cubic-bezier(.65,0,.18,1) both}
.hero.in .logo-img{animation:logoIn .72s var(--ease-out) 1.9s both}
.hero.in .hero-line{animation:upIn .62s var(--ease-out) 2.62s both}
.hero.in .hero-cta{animation:upIn .62s var(--ease-out) 2.76s both}

/* The port stays a circle the whole way through. The footage is black at its
   edges and the hero is black behind it, so at full size the circle has no
   visible boundary and simply reads as the shot. Which means the ignition is
   one unbroken move: a red hot disc the size of the screen, shrinking until
   it is the O in the wordmark. The disc is the brand's own motif, so the
   entrance is the mark arriving rather than a video playing. */
@keyframes ignite{
  0%   {transform:translate(var(--fx),var(--fy)) scale(var(--fs));opacity:0}
  7%   {opacity:1}
  48%  {transform:translate(var(--fx),var(--fy)) scale(var(--fs));opacity:1}
  100% {transform:translate(0,0) scale(1);opacity:1}
}
@keyframes logoIn{
  from{opacity:0;transform:scale(1.03)}
  to  {opacity:1;transform:scale(1)}
}
@keyframes upIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}

/* While the film is still full-bleed the glow behind it would just wash it
   out, so it holds back and comes up as the film shrinks away. */
.hero.in .hero-glow{animation:glowIn 1.4s var(--ease) 1.5s both}
@keyframes glowIn{from{opacity:0}to{opacity:1}}
/* embers: the one thing that never stops. They drift for as long as the hero
   is on screen and are the reason the page still looks alive mid-scroll. */
.embers{position:absolute;inset:0;pointer-events:none}
.ember{
  position:absolute;bottom:-4%;width:3px;height:3px;border-radius:50%;
  background:var(--fire);
  animation:rise linear infinite;
}
@keyframes rise{
  0%{transform:translateY(0) translateX(0);opacity:0}
  12%{opacity:.9}
  70%{opacity:.55}
  100%{transform:translateY(-104vh) translateX(var(--dx,14px));opacity:0}
}
body.paused .ember,body.paused .hero-glow{animation-play-state:paused}

/* Without JavaScript nothing gets the .in class, so the ignition never runs
   and the film is never given its source. What is left is the finished
   lockup with the poster frame burning inside the O, which is the state the
   ignition was travelling to anyway. A JS-off visitor gets the hero, not a
   black screen. */
html:not(.js) .logo-img,
html:not(.js) .hero-line,html:not(.js) .hero-cta{
  opacity:1;transform:none;
}
/* no ignition ever runs, so the port is already at its settled edge */
html:not(.js) .port{--edge:94%}

/* --------------------------------------------------------------------------
   sections
   -------------------------------------------------------------------------- */
.sec{position:relative;z-index:1;padding:var(--sec) 0}
.on-ink{background:var(--ink);color:var(--paper)}
.on-ink h1,.on-ink h2,.on-ink h3{color:var(--paper)}

/* the chopstick diagonal as a section edge */
.slice-top{clip-path:polygon(0 3.2vw,100% 0,100% 100%,0 100%)}
.slice-bot{clip-path:polygon(0 0,100% 0,100% calc(100% - 3.2vw),0 100%)}

/* S1 : wok hei.
   Black section carrying a photograph that was shot on black, so the image
   has no visible edge: it dissolves into the section instead of sitting in a
   box on it. The mask does the dissolving on all four sides. */
.s1{overflow:hidden}
.s1-grid{display:grid;grid-template-columns:1fr 1.05fr;gap:clamp(2rem,6vw,5.5rem);align-items:center}
.s1 h2{font-size:var(--fs-h2);max-width:15ch}
.s1 .lede{margin-top:1.6rem}
.s1-photo{margin:0;position:relative}
.s1-photo img,.s1-film{
  display:block;width:100%;height:auto;border-radius:2px;
  /* The shot was lit on black, so feathering the edges dissolves it into the
     section instead of ending it on a rectangle. Applies to the poster and
     the film alike, so the swap from one to the other is invisible. */
  -webkit-mask-image:radial-gradient(ellipse 78% 76% at 50% 50%,#000 42%,transparent 100%);
  mask-image:radial-gradient(ellipse 78% 76% at 50% 50%,#000 42%,transparent 100%);
  transform:scale(1.06);opacity:0;
  transition:transform 1.6s var(--ease-out),opacity 1.1s var(--ease);
}
.s1-photo.in img,.s1-photo.in .s1-film{transform:scale(1);opacity:1}

/* two facts, not a slogan triplet */
.specs{margin:2.5rem 0 0;display:flex;flex-wrap:wrap;gap:1.75rem 3rem}
.specs div{display:flex;flex-direction:column;gap:.4rem}
.specs dt{
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.16em;
  text-transform:uppercase;color:var(--dim-d);
}
.specs dd{
  margin:0;font-family:var(--display);font-weight:700;text-transform:uppercase;
  font-size:clamp(1.0625rem,1.7vw,1.35rem);letter-spacing:var(--track-h3);
}
@media (max-width:900px){
  .s1-grid{grid-template-columns:1fr}
  .s1-photo{order:-1;max-width:34rem;margin:0 auto}
}

/* --------------------------------------------------------------------------
   S2 : THE MENU
   -------------------------------------------------------------------------- */
/* The menu no longer sits on flat white. A bone panel cuts across it on a
   diagonal with a red hairline along the edge, so the section reads as two
   tones meeting rather than one empty field. It drifts: the angle breathes
   on a 54 second cycle and the panel slides against the scroll, so the split
   is never in quite the same place twice.

   clip-path rather than overflow:hidden. overflow:hidden would turn this
   section into a scroll container and kill the sticky tools bar inside it;
   clip-path crops the rotated corners without touching sticky. */
@property --tilt{syntax:'<angle>';inherits:false;initial-value:-3.5deg}

/* An explicit background, not transparency. clip-path already makes this
   section a stacking context, so the lanterns multiplying inside it need a
   real surface here to blend against; against transparency they box out. */
.s2{padding-bottom:calc(var(--sec) * .7);clip-path:inset(0);background:var(--paper)}
/* The edge is deliberately high in the section and shallow. Placed further
   down it cut straight through the dish rows and read as a strikethrough on
   the descriptions, which is the one thing a background must never do. Up
   here it crosses nothing but the header's empty right side, and the menu
   itself then sits wholly on the bone tone. */
.s2::before{
  content:"";position:absolute;
  left:-25%;right:-25%;top:clamp(3.5rem,7vw,7rem);bottom:-45%;
  background:var(--bone);
  border-top:1.5px solid rgba(229,23,39,.28);
  transform:rotate(var(--tilt)) translateY(calc(var(--sy,0px) * -.012));
  transform-origin:50% 0;
  z-index:0;pointer-events:none;
  animation:tiltDrift 54s ease-in-out infinite;
}
@keyframes tiltDrift{
  0%,100%{--tilt:-4.4deg}
  50%    {--tilt:-2.5deg}
}
/* Everything real in the section sits above the panel. .menu-tools is
   deliberately NOT listed: it is position:sticky with its own z-index, and
   restating position:relative here would silently un-stick it. */
.s2>.wrap{position:relative;z-index:1}

/* ---- the drifting lanterns --------------------------------------------
   Barely there on purpose. Turn --lantern-opacity up to about .35 if you
   need to actually look at them, then put it back.

   multiply is what removes their backgrounds. The source images are shot on
   pure white, and multiplying white against the page leaves the page
   untouched, so the lanterns arrive already cut out with no alpha channel
   and no knockout step. It also means they pick up the bone panel's tone as
   they drift across it instead of sitting on top of it as flat stickers. */
/* Deliberately NO z-index here, and no contain, isolation or filter either.
   Any one of them opens a stacking context, and mix-blend-mode only blends
   against the backdrop inside its own stacking context: isolate this layer
   and the lanterns have nothing to multiply against, so their white JPEG
   background renders as an opaque box. z-index:auto still paints them above
   the bone panel and below .wrap purely by tree order, which is all the
   ordering this needs. */
.lanterns{
  position:absolute;inset:0;
  pointer-events:none;overflow:hidden;
}

/* Two elements per lantern, three clocks between them.

   The outer span owns the anchor, the size, the fade and the blend, and
   drifts horizontally. The inner <i> owns the picture and drifts vertically
   while swaying. Every one of those three animations is a plain transform,
   so all of it is handed to the compositor: the menu accordion can open and
   close, reflowing thousands of pixels of dish rows, without any of it
   touching the lanterns' animation frames.

   The blend and the opacity live on the OUTER element and must stay there.
   mix-blend-mode blends against the backdrop of its nearest ancestor
   stacking context, and the outer span's own transform makes it one for
   everything inside it. Move the blend down onto the <i> and it would be
   blending against nothing but its transparent parent, which paints the
   source JPEG's white background as an opaque white box. On the outer span
   the whole lantern blends as a group against the section behind it, which
   is what cuts it out of its white background for free. */
.lantern{
  --lantern-opacity:.08;
  /* A multiplier rather than an override. The per-lantern travel arrives as
     an inline --ax/--ay from app.js, and an inline custom property beats any
     stylesheet rule, media query included -- so the phone breakpoint cannot
     simply restate --ax. It scales this instead, which the keyframes fold in.
     Custom properties inherit, so setting it here covers the inner <i> too. */
  --amp:1;
  --w:calc(clamp(8rem,15vw,15rem) * var(--s,1));
  /* How far this one's centre must stay clear of each edge: its own half
     width, plus the whole of its travel in that axis. The half width is
     taken as .62 rather than .5 because the sway tilts the square, and a
     square turned by t is wider than its own side by (cos t + sin t) -- at
     the steepest tilt any lantern reaches, about 13 degrees, that is a fifth
     again. Guard on .5 and the corner still catches the edge. */
  --gx:calc(var(--w) * .62 + var(--ax,5vw) * var(--amp));
  --gy:calc(var(--w) * .62 + var(--ay,4rem) * var(--amp));
  position:absolute;
  width:var(--w);aspect-ratio:1;
  /* The anchor from app.js, pulled back inside the section if it sits too
     near an edge for this lantern's size and travel. clamp() takes the
     percentage and the length side by side and resolves both against the
     section, so this re-solves itself on every resize with no measuring and
     no breakpoint. Without it the fixed 8rem size floor drags the left-hand
     lanterns off the edge on a narrow phone, where they would be sliced in
     half against the clip rather than drifting inside it. */
  left:clamp(var(--gx), var(--x,50%), calc(100% - var(--gx)));
  top: clamp(var(--gy), var(--y,50%), calc(100% - var(--gy)));
  /* Centred on the anchor with margins rather than a translate, so the
     transform is left entirely free for the drift. */
  margin-left:calc(var(--w) / -2);
  margin-top:calc(var(--w) / -2);
  opacity:var(--lantern-opacity);
  mix-blend-mode:multiply;
  animation:lanternX var(--tx,37s) ease-in-out var(--dx,0s) infinite alternate;
  will-change:transform;
}
.lantern>i{
  display:block;position:absolute;inset:0;
  background-size:contain;background-repeat:no-repeat;background-position:center;
  /* translate and rotate are separate animatable properties, so these two
     animations drive the same element without fighting over one value.

     The vertical drift uses the translate property rather than transform,
     and the difference is not cosmetic. The engine composes an element as
     translate, then rotate, then scale, then transform -- rightmost applied
     first. Put the drift in transform and the rotation is applied to it
     afterwards, which swings the lantern's vertical travel round into a
     horizontal one: at 11 degrees a 58px rise leaks 11px sideways, which is
     enough to push it past the edge guard the anchor was clamped against.
     In translate it sits on the far side of the rotation and stays purely
     vertical, so the guard's arithmetic holds. */
  animation:
    lanternY var(--ty,29s) ease-in-out var(--dy,0s) infinite alternate,
    lanternR var(--tr,19s) ease-in-out var(--dr,0s) infinite alternate;
  will-change:translate,rotate;
}
/* Each leg is a simple there-and-back on `alternate`. The wander comes from
   the three periods being mutually prime rather than from any one path. */
@keyframes lanternX{
  from{transform:translateX(calc(var(--ax,5vw) * var(--amp,1) * -1))}
  to  {transform:translateX(calc(var(--ax,5vw) * var(--amp,1)))}
}
@keyframes lanternY{
  from{translate:0 calc(var(--ay,4rem) * var(--amp,1) * -1)}
  to  {translate:0 calc(var(--ay,4rem) * var(--amp,1))}
}
@keyframes lanternR{
  from{rotate:calc(var(--r,0deg) - 4deg)}
  to  {rotate:calc(var(--r,0deg) + 4deg)}
}
@media (max-width:760px){
  /* Less room and more scrolling on a phone, so the two that would crowd the
     dish rows step out rather than being shrunk into smudges. */
  .lantern:nth-child(3),.lantern:nth-child(5){display:none}
  .lantern{--lantern-opacity:.055}
  /* Travel is pulled in, but only a little. The horizontal swing is in vw
     and so already shrinks with the screen; cutting it much further left the
     phone version barely moving at all. The edge clamp above absorbs the
     difference by seating the anchors further in, so this can stay generous
     without any of them reaching a margin. */
  .lantern{--amp:.8}
}
@media (prefers-reduced-motion:reduce){
  /* Still placed and still tilted, just not moving. */
  .lantern,.lantern>i{animation:none}
  .lantern>i{translate:0;rotate:var(--r,0deg)}
}
.menu-head{display:grid;grid-template-columns:1fr auto;gap:2rem;align-items:end;margin-bottom:2.5rem}
.menu-head h2{font-size:var(--fs-h2);max-width:20ch}
.menu-head .note{
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.12em;text-transform:uppercase;
  color:var(--strong);white-space:nowrap;
  display:inline-flex;align-items:center;gap:.55rem;
  padding:.6rem 1rem;border:1px solid var(--edge);border-radius:999px;
}
.menu-head .note::before{content:"";width:.45rem;height:.45rem;border-radius:50%;background:var(--fire);flex:0 0 auto}
@media (max-width:760px){.menu-head{grid-template-columns:1fr}.menu-head .note{white-space:normal}}

.menu-tools{
  position:sticky;top:3.55rem;z-index:40;
  background:rgba(255,255,255,.95);backdrop-filter:blur(14px);
  border-top:1px solid var(--hair);border-bottom:1px solid var(--hair);
  padding:.8rem 0;margin-bottom:2.5rem;
}
.tools-in{display:flex;gap:.75rem;align-items:center;flex-wrap:wrap}
.search{position:relative;flex:1 1 15rem;min-width:12rem}
.search input{
  width:100%;padding:.7rem .9rem .7rem 2.35rem;
  background:var(--panel);border:1px solid var(--hair);border-radius:999px;
  font-size:.9375rem;transition:border-color .3s var(--ease),background-color .3s var(--ease);
}
.search input:hover{border-color:var(--edge)}
.search input::placeholder{color:var(--dim)}
.search svg{position:absolute;left:.85rem;top:50%;transform:translateY(-50%);width:15px;height:15px;stroke:var(--dim);fill:none;stroke-width:2}
.chips{display:flex;gap:.4rem;flex-wrap:wrap}
.chip{
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.12em;text-transform:uppercase;
  padding:.55rem .85rem;border:1px solid var(--edge);border-radius:999px;
  transition:background-color .3s var(--ease),color .3s var(--ease),border-color .3s var(--ease);
  white-space:nowrap;
}
.chip:hover{border-color:var(--ink)}
.chip[aria-pressed="true"]{background:var(--ink);color:var(--paper);border-color:var(--ink)}

.menu-body{display:grid;grid-template-columns:11.5rem 1fr;gap:clamp(1.5rem,4vw,4rem);align-items:start}
.rail{position:sticky;top:9rem;display:grid;gap:.1rem}
.rail a{
  display:flex;align-items:center;gap:.6rem;padding:.5rem 0;
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.12em;text-transform:uppercase;
  color:var(--dim);transition:color .3s var(--ease);
}
.rail a .dot{width:7px;height:7px;border-radius:50%;border:1.5px solid var(--edge);flex:0 0 auto;transition:background-color .3s var(--ease),border-color .3s var(--ease),transform .3s var(--ease-out)}
.rail a:hover{color:var(--ink)}
.rail a[aria-current="true"]{color:var(--ink)}
.rail a[aria-current="true"] .dot{background:var(--fire);border-color:var(--fire);transform:scale(1.35)}
@media (max-width:900px){
  .menu-body{grid-template-columns:1fr}
  .rail{
    position:sticky;top:6.6rem;z-index:38;
    display:flex;gap:.3rem;overflow-x:auto;overflow-y:hidden;
    background:rgba(255,255,255,.95);backdrop-filter:blur(14px);
    padding:.6rem 0;margin:0 calc(var(--gut) * -1);padding-left:var(--gut);padding-right:var(--gut);
    border-bottom:1px solid var(--hair);
    scrollbar-width:none;
  }
  .rail::-webkit-scrollbar{display:none}
  .rail a{white-space:nowrap;padding:.45rem .75rem;border:1px solid var(--hair);border-radius:999px}
  .rail a[aria-current="true"]{background:var(--ink);color:var(--paper);border-color:var(--ink)}
  .rail a[aria-current="true"] .dot{background:var(--paper);border-color:var(--paper)}
}

/* Collapsed by default, so the whole menu is nine rows you can take in at a
   glance instead of a scroll that never ends. The rail still jumps straight
   to a section, and jumping to one opens it. */
.cat{margin-bottom:.5rem;scroll-margin-top:11rem}
.cat-head{
  width:100%;text-align:left;
  display:flex;align-items:center;gap:.9rem;
  padding:1.15rem .25rem;border-bottom:2px solid var(--ink);
  transition:padding-left .35s var(--ease-out);
}
.cat-head:hover{padding-left:.75rem}
.cat-head .disc{
  width:12px;height:12px;border-radius:50%;background:var(--fire);flex:0 0 auto;
  transition:transform .45s var(--ease-out);
}
.cat[data-open="true"] .cat-head .disc{transform:scale(1.5)}
.cat-head h3{font-size:var(--fs-h3)}
.cat-head .cnote{
  margin-left:auto;font-family:var(--mono);font-size:var(--fs-label);
  letter-spacing:.1em;text-transform:uppercase;color:var(--dim);text-align:right;
}
/* the open/close indicator: a plus that rolls into a minus */
.cat-head .ind{
  position:relative;width:1.6rem;height:1.6rem;flex:0 0 auto;margin-left:.25rem;
  border:1.5px solid var(--edge);border-radius:50%;
  transition:background-color .35s var(--ease),border-color .35s var(--ease),transform .45s var(--ease-out);
}
.cat-head .ind::before,.cat-head .ind::after{
  content:"";position:absolute;left:50%;top:50%;width:.6rem;height:1.5px;
  background:var(--ink);transform:translate(-50%,-50%);
  transition:transform .45s var(--ease-out),background-color .35s var(--ease);
}
.cat-head .ind::after{transform:translate(-50%,-50%) rotate(90deg)}
.cat-head:hover .ind{border-color:var(--ink)}
.cat[data-open="true"] .cat-head .ind{background:var(--ink);border-color:var(--ink);transform:rotate(180deg)}
.cat[data-open="true"] .cat-head .ind::before{background:var(--paper)}
.cat[data-open="true"] .cat-head .ind::after{transform:translate(-50%,-50%) rotate(0deg);background:var(--paper)}

/* 0fr to 1fr is the one grid trick that animates a variable height cleanly
   without measuring anything in JavaScript. */
.cat-body{display:grid;grid-template-rows:0fr;transition:grid-template-rows .48s var(--ease-out)}
.cat[data-open="true"] .cat-body{grid-template-rows:1fr}
.cat-body>.cat-rows{overflow:hidden;min-height:0}
.cat[data-open="true"] .cat-body>.cat-rows{padding-bottom:1.5rem}

.expand-all{
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.12em;text-transform:uppercase;
  padding:.55rem .85rem;border:1px solid var(--edge);border-radius:999px;white-space:nowrap;
  transition:background-color .3s var(--ease),color .3s var(--ease),border-color .3s var(--ease);
}
.expand-all:hover{border-color:var(--ink)}
.expand-all[aria-pressed="true"]{background:var(--ink);color:var(--paper);border-color:var(--ink)}

@media (max-width:620px){.cat-head .cnote{display:none}}
@media (prefers-reduced-motion:reduce){
  .cat-body{transition:none}
}

.dish{
  position:relative;width:100%;text-align:left;
  display:grid;grid-template-columns:3.25rem 1fr auto;gap:1rem;align-items:center;
  padding:1.05rem .75rem;border-bottom:1px solid var(--hair);
  overflow:hidden;transition:padding-left .4s var(--ease-out);
}
/* The skew shears the top corner back INTO the row, so the resting position
   has to clear the row by more than the shear, not just by its own width. */
.dish::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:140%;
  background:var(--fire);transform:translateX(-118%) skewX(calc(var(--chop) * -1));transform-origin:left;
  transition:transform .5s var(--ease-out);z-index:0;
}
.dish:hover::before,.dish:has(:focus-visible)::before{transform:translateX(-88%) skewX(calc(var(--chop) * -1))}
.dish:hover{padding-left:1.35rem}
.dish>*{position:relative;z-index:1}
.dish-badge{
  position:relative;
  width:3.25rem;height:3.25rem;border-radius:50%;
  background:var(--panel);display:grid;place-items:center;overflow:hidden;flex:0 0 auto;
  transition:background-color .35s var(--ease);
}
.dish-badge svg{width:56%;height:56%;stroke:var(--ink);fill:none;stroke-width:1.7;opacity:.7}
.dish-badge img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.dish:hover .dish-badge{background:var(--panel-2)}
.dish-main{min-width:0}
.dish-name{font-weight:600;font-size:1.0625rem;letter-spacing:-.005em;display:flex;align-items:baseline;gap:.5rem;flex-wrap:wrap}
.dish-desc{color:var(--dim);font-size:.9375rem;line-height:1.45;margin-top:.15rem}
.dish-meta{display:flex;align-items:center;gap:.9rem;flex:0 0 auto}
.dish-price{font-family:var(--mono);font-size:.9375rem;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap}
.dish-add{
  width:2.35rem;height:2.35rem;border-radius:50%;border:1.5px solid var(--edge);
  display:grid;place-items:center;flex:0 0 auto;
  transition:background-color .3s var(--ease),border-color .3s var(--ease),transform .3s var(--ease-out);
}
.dish-add svg{width:13px;height:13px;stroke:var(--ink);stroke-width:2.4;fill:none}
.dish:hover .dish-add{background:var(--ink);border-color:var(--ink);transform:rotate(90deg)}
.dish:hover .dish-add svg{stroke:var(--paper)}

.tag{
  font-family:var(--mono);font-size:.5625rem;letter-spacing:.14em;text-transform:uppercase;
  /* Declared, not inherited. These pills sit inside .dish-name, which is
     weight 600, so they were asking for Spline Sans Mono 600 -- a weight the
     font request never included, leaving the browser to fake it. 500 is the
     weight the rest of the label system uses. */
  font-weight:500;
  padding:.18rem .42rem;border-radius:3px;white-space:nowrap;
}
.tag.pop{background:var(--fire);color:var(--paper)}
.tag.veg{border:1px solid var(--edge);color:var(--dim)}

.heat{display:inline-flex;gap:2px;align-items:center;flex:0 0 auto}
.heat svg{width:11px;height:13px}
.heat svg path{fill:rgba(0,0,0,.2)}
.heat svg.on path{fill:var(--fire)}

@media (max-width:560px){
  .dish{grid-template-columns:2.75rem 1fr;gap:.85rem;padding:.95rem .5rem;row-gap:.6rem}
  .dish-badge{width:2.75rem;height:2.75rem}
  .dish-meta{grid-column:2;justify-content:flex-start}
  .dish:hover{padding-left:.5rem}
}

.no-results{padding:3rem 0;text-align:center;color:var(--dim)}
.no-results strong{display:block;font-family:var(--display);font-weight:700;text-transform:uppercase;font-size:1.25rem;color:var(--ink);margin-bottom:.4rem}

/* --------------------------------------------------------------------------
   buttons
   -------------------------------------------------------------------------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:.55rem;
  font-family:var(--mono);font-size:.75rem;font-weight:500;letter-spacing:.16em;text-transform:uppercase;
  padding:1rem 1.65rem;border-radius:999px;border:1.5px solid transparent;
  transition:transform .35s var(--ease-out),background-color .3s var(--ease),color .3s var(--ease),border-color .3s var(--ease),box-shadow .35s var(--ease);
  text-align:center;
}
.btn-fire{background:var(--fire);color:var(--paper)}
.btn-fire:hover{transform:translateY(-2px);box-shadow:0 10px 28px rgba(229,23,39,.4)}
.btn-fire:disabled{opacity:.4;cursor:not-allowed;transform:none;box-shadow:none}
.btn-ghost{border-color:var(--edge-d);color:var(--paper)}
.btn-ghost:hover{border-color:var(--paper);transform:translateY(-2px)}
.btn-line{border-color:var(--edge);color:var(--ink)}
.btn-line:hover{border-color:var(--ink);transform:translateY(-2px)}
.btn-full{width:100%}
@media (pointer:coarse){
  .btn,.chip,.dish-add,.rail a{min-height:44px;display:inline-flex;align-items:center;justify-content:center}
  .rail a{justify-content:flex-start}
  .dish-add{width:44px;height:44px}
  /* room under the last dish so the floating wok button never sits on it */
  .s2{padding-bottom:8rem}
  .wok-btn{width:4.75rem;height:4.75rem}
}

/* --------------------------------------------------------------------------
   S4 : how it works
   -------------------------------------------------------------------------- */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(1.5rem,4vw,3.5rem);margin-top:3.5rem}
.step{position:relative;padding-top:3rem;border-top:1px solid var(--hair-d)}
.step .n{
  position:absolute;top:1.1rem;left:0;
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.2em;color:var(--dim-d);
}
.step h3{font-size:clamp(1.25rem,2vw,1.6rem);margin-bottom:.75rem}
.step p{color:var(--dim-d);font-size:.9375rem;line-height:1.55}
.step .bar{position:absolute;top:-1px;left:0;height:2px;background:var(--fire);width:calc(var(--g,0)*100%);}
@media (max-width:800px){.steps{grid-template-columns:1fr;gap:2rem}}

/* --------------------------------------------------------------------------
   S5 : FAQ
   -------------------------------------------------------------------------- */
.faq{max-width:44rem;margin:3rem auto 0}
.qa{border-bottom:1px solid var(--hair)}
.qa summary{
  list-style:none;cursor:pointer;padding:1.35rem .25rem;
  display:flex;align-items:flex-start;gap:1rem;
  font-family:var(--display);font-weight:700;text-transform:uppercase;
  font-size:clamp(.9375rem,1.5vw,1.125rem);letter-spacing:var(--track-h3);line-height:1.3;
  transition:color .3s var(--ease);
}
.qa summary::-webkit-details-marker{display:none}
.qa summary:hover{color:var(--fire)}
.qa summary .ind{
  margin-left:auto;flex:0 0 auto;width:1.35rem;height:1.35rem;position:relative;
  transition:transform .4s var(--ease-out);
}
.qa summary .ind::before,.qa summary .ind::after{
  content:"";position:absolute;background:var(--fire);
  left:50%;top:50%;transform:translate(-50%,-50%);
}
.qa summary .ind::before{width:100%;height:2px}
.qa summary .ind::after{width:2px;height:100%;transition:transform .4s var(--ease-out)}
.qa[open] summary .ind::after{transform:translate(-50%,-50%) scaleY(0)}
.qa .ans{padding:0 3rem 1.5rem .25rem;color:var(--dim);font-size:.9375rem;line-height:1.6;max-width:56ch}
.qa[open] .ans{animation:reveal .45s var(--ease-out) both}
@keyframes reveal{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}

/* --------------------------------------------------------------------------
   S6 : pre-launch
   -------------------------------------------------------------------------- */
.s6-grid{display:grid;grid-template-columns:1.1fr .9fr;gap:clamp(2rem,6vw,6rem);align-items:center}
.s6 h2{font-size:var(--fs-h2);max-width:15ch}
.s6 .plaque{
  border:1px solid var(--hair-d);border-radius:14px;padding:clamp(1.5rem,3vw,2.25rem);
  display:grid;gap:1.1rem;
}
.plaque-row{display:grid;grid-template-columns:auto 1fr;gap:1rem;align-items:baseline;padding-bottom:1.1rem;border-bottom:1px solid var(--hair-d)}
.plaque-row:last-child{border-bottom:0;padding-bottom:0}
.plaque-row .k{font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.16em;text-transform:uppercase;color:var(--dim-d);white-space:nowrap;display:flex;align-items:baseline;gap:.5rem}
.plaque-row .k::before{content:"";width:.4rem;height:.4rem;border-radius:50%;background:var(--fire);flex:0 0 auto;transform:translateY(-.15em)}
.plaque-row .v{font-size:.9375rem;color:var(--dim-d)}
@media (max-width:860px){.s6-grid{grid-template-columns:1fr}}

/* --------------------------------------------------------------------------
   footer
   -------------------------------------------------------------------------- */
.foot{background:var(--ink);color:var(--paper);padding:var(--sec) 0 2rem}
.foot-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:clamp(2rem,5vw,4rem);padding-bottom:3rem;border-bottom:1px solid var(--hair-d)}
.foot-mark{width:min(100%,17rem);height:auto;display:block}
.foot h3{font-family:var(--mono);font-size:var(--fs-label);font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--paper);margin-bottom:1rem;padding-bottom:.6rem;border-bottom:2px solid var(--fire);display:inline-block}
.foot li{margin-bottom:.6rem;font-size:.9375rem;color:var(--dim-d)}
.foot li a{transition:color .3s var(--ease)}
.foot li a:hover{color:var(--paper)}
.foot-base{display:flex;gap:1.5rem;flex-wrap:wrap;justify-content:space-between;padding-top:1.75rem;font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.1em;text-transform:uppercase;color:var(--dim-d)}
/* flagged as unconfirmed. White text so it passes contrast on black, with a
   red underline doing the flagging instead of red type. */
.tbc{color:var(--paper);font-weight:500;border-bottom:1.5px solid var(--fire);padding-bottom:1px}
.foot .tbc{color:var(--dim-d)}

/* Placeholder social row under the footer mark. Same circular-icon language
   the rest of the page never actually uses elsewhere, kept deliberately
   plain -- muted stroke, no fill, no hover lift -- so the row reads as
   'coming soon' rather than as three live buttons that happen to do nothing.
   The moment a link goes live (aria-disabled removed in the markup) the
   :not([aria-disabled]) rule below takes over and it behaves like every
   other link on the page. */
.foot-social-label{
  font-family:var(--mono);font-size:.6875rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--dim-d);margin:1.75rem 0 .85rem;
}
.foot-social{display:flex;gap:.65rem}
.soc-btn{
  width:2.5rem;height:2.5rem;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  border:1.5px solid var(--hair-d);color:var(--dim-d);
  transition:color .3s var(--ease),border-color .3s var(--ease);
}
.soc-btn svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linejoin:round;stroke-linecap:round}
.soc-btn svg.filled{fill:currentColor;stroke:none}
.soc-btn[aria-disabled="true"]{cursor:default}
.soc-btn:not([aria-disabled="true"]):hover{color:var(--paper);border-color:var(--fire);cursor:pointer}
.soc-btn:not([aria-disabled="true"]):focus-visible{outline:2.5px solid var(--fire);outline-offset:2px}

@media (max-width:800px){.foot-grid{grid-template-columns:1fr;gap:2.5rem}}

/* --------------------------------------------------------------------------
   THE WOK CART BUTTON : the signature, second life
   -------------------------------------------------------------------------- */
.wok-btn{
  position:fixed;z-index:70;
  right:clamp(1rem,3vw,2rem);bottom:calc(clamp(1rem,3vw,2rem) + env(safe-area-inset-bottom));
  width:4.5rem;height:4.5rem;border-radius:50%;
  background:var(--ink);border:2px solid var(--fire);
  display:grid;place-items:center;overflow:hidden;
  transform:translateY(150%) scale(.85);opacity:0;
  transition:transform .55s var(--ease-out),opacity .4s var(--ease),box-shadow .4s var(--ease);
  box-shadow:0 10px 34px rgba(0,0,0,.4);
}
.wok-btn.show{transform:translateY(0) scale(1);opacity:1}
.wok-btn:hover{box-shadow:0 14px 40px rgba(229,23,39,.45)}
.wok-btn .fill{
  position:absolute;left:0;right:0;bottom:0;height:100%;background:var(--fire);
  transform:translateY(calc(100% - var(--fill,0) * 100%));
  transition:transform .6s var(--ease-out);
}
.wok-btn .count{
  position:relative;z-index:2;font-family:var(--display);font-weight:700;font-size:1.15rem;
  color:var(--paper);line-height:1;
}
.wok-btn .lbl{
  position:relative;z-index:2;font-family:var(--mono);font-size:.4375rem;letter-spacing:.14em;
  text-transform:uppercase;color:var(--paper);opacity:.85;margin-top:.1rem;white-space:nowrap;
}
.wok-btn .inner{position:relative;z-index:2;display:grid;place-items:center}
.wok-btn.bump{animation:bump .5s var(--ease-out)}
@keyframes bump{0%{transform:scale(1)}38%{transform:scale(1.16)}100%{transform:scale(1)}}

/* the flying dot: an item landing in the wok */
.fly{
  position:fixed;z-index:69;width:12px;height:12px;border-radius:50%;background:var(--fire);
  pointer-events:none;will-change:transform,opacity;
}

/* --------------------------------------------------------------------------
   OVERLAYS : dish sheet and the order drawer
   -------------------------------------------------------------------------- */
.veil{
  position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.62);
  backdrop-filter:blur(6px);opacity:0;visibility:hidden;
  transition:opacity .45s var(--ease),visibility .45s;
}
.veil.open{opacity:1;visibility:visible}

/* dish sheet */
.sheet{
  position:fixed;z-index:90;left:50%;top:50%;
  width:min(30rem,calc(100vw - 2rem));max-height:min(86vh,44rem);
  transform:translate(-50%,-46%) scale(.96);opacity:0;visibility:hidden;
  background:var(--paper);border-radius:16px;overflow:hidden auto;
  transition:transform .5s var(--ease-out),opacity .35s var(--ease),visibility .5s;
  box-shadow:0 30px 90px rgba(0,0,0,.45);
}
.sheet.open{transform:translate(-50%,-50%) scale(1);opacity:1;visibility:visible}
.sheet-top{padding:1.5rem 1.5rem 1.25rem;border-bottom:1px solid var(--hair);position:relative}
.sheet-top h3{font-size:1.375rem;padding-right:2.5rem;line-height:1.1}
.sheet-top .d{color:var(--dim);font-size:.9375rem;margin-top:.5rem}
.sheet-top .row{display:flex;align-items:center;gap:.75rem;margin-top:.85rem}
.sheet-body{padding:1.25rem 1.5rem}
.sheet-foot{
  position:sticky;bottom:0;background:var(--paper);
  padding:1.1rem 1.5rem calc(1.1rem + env(safe-area-inset-bottom));
  border-top:1px solid var(--hair);display:flex;gap:.85rem;align-items:center;
}
.x{
  position:absolute;top:1.1rem;right:1.1rem;width:2.25rem;height:2.25rem;border-radius:50%;
  display:grid;place-items:center;border:1px solid var(--hair);
  transition:background-color .3s var(--ease),border-color .3s var(--ease),transform .3s var(--ease-out);
}
.x svg{width:12px;height:12px;stroke:var(--ink);stroke-width:2.2;fill:none}
.x:hover{background:var(--ink);border-color:var(--ink);transform:rotate(90deg)}
.x:hover svg{stroke:var(--paper)}

.optgroup{margin-bottom:1.35rem}
.optgroup>.gl{font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.16em;text-transform:uppercase;color:var(--dim);margin-bottom:.6rem;display:block}
.segs{display:flex;gap:.4rem;flex-wrap:wrap}
.seg{
  padding:.6rem 1rem;border:1.5px solid var(--hair);border-radius:999px;
  font-size:.875rem;font-weight:500;
  transition:border-color .3s var(--ease),background-color .3s var(--ease),color .3s var(--ease);
}
.seg:hover{border-color:var(--edge)}
.seg[aria-pressed="true"]{background:var(--ink);color:var(--paper);border-color:var(--ink)}
.seg .plus{font-family:var(--mono);font-size:.6875rem;opacity:.65;margin-left:.35rem}
.checks{display:grid;gap:.3rem}
.check{
  display:flex;align-items:center;gap:.75rem;padding:.65rem .25rem;
  border-bottom:1px solid var(--hair);font-size:.9375rem;cursor:pointer;
}
.check:last-child{border-bottom:0}
.check .box{
  width:1.15rem;height:1.15rem;border:1.5px solid var(--edge);border-radius:4px;flex:0 0 auto;
  display:grid;place-items:center;transition:background-color .25s var(--ease),border-color .25s var(--ease);
}
.check .box svg{width:11px;height:11px;stroke:var(--paper);stroke-width:2.8;fill:none;opacity:0;transition:opacity .2s var(--ease)}
.check input{position:absolute;opacity:0;width:0;height:0}
.check input:checked+.box{background:var(--fire);border-color:var(--fire)}
.check input:checked+.box svg{opacity:1}
.check input:focus-visible+.box{outline:2.5px solid var(--fire);outline-offset:3px}
.check .p{margin-left:auto;font-family:var(--mono);font-size:.8125rem;color:var(--dim)}

.qty{display:flex;align-items:center;gap:.15rem;border:1.5px solid var(--hair);border-radius:999px;padding:.2rem;flex:0 0 auto}
.qty button{width:2.1rem;height:2.1rem;border-radius:50%;display:grid;place-items:center;transition:background-color .25s var(--ease)}
.qty button:hover{background:var(--panel-2)}
.qty button:disabled{opacity:.3;cursor:not-allowed}
.qty svg{width:11px;height:11px;stroke:var(--ink);stroke-width:2.4;fill:none}
.qty .v{min-width:1.6rem;text-align:center;font-family:var(--mono);font-weight:500;font-size:.9375rem}

/* --------------------------------------------------------------------------
   THE ORDER DRAWER : a kitchen docket
   -------------------------------------------------------------------------- */
.drawer{
  position:fixed;z-index:90;top:0;right:0;bottom:0;
  width:min(28.5rem,100vw);background:var(--paper);
  transform:translateX(102%);visibility:hidden;
  transition:transform .58s var(--ease-out),visibility .58s;
  display:flex;flex-direction:column;
  box-shadow:-20px 0 70px rgba(0,0,0,.35);
}
.drawer.open{transform:none;visibility:visible}
.drawer-top{
  flex:0 0 auto;padding:1.35rem 1.35rem 1.1rem;border-bottom:2px solid var(--ink);
  padding-top:calc(1.35rem + env(safe-area-inset-top));position:relative;
}
.drawer-top .t{font-family:var(--display);font-weight:700;text-transform:uppercase;font-size:1.25rem;letter-spacing:var(--track-h3)}
.drawer-top .s{font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.16em;text-transform:uppercase;color:var(--dim);margin-top:.3rem}
.drawer-scroll{flex:1 1 auto;overflow-y:auto;overscroll-behavior:contain;padding:1.35rem}
.drawer-foot{
  flex:0 0 auto;padding:1.1rem 1.35rem calc(1.1rem + env(safe-area-inset-bottom));
  border-top:1px solid var(--hair);background:var(--paper);
}

/* the docket */
.docket{border-top:1px dashed var(--edge);margin-bottom:1.25rem}
.line{display:grid;grid-template-columns:1fr auto;gap:.65rem 1rem;padding:.9rem 0;border-bottom:1px dashed var(--hair)}
.line .n{font-weight:600;font-size:.9375rem;line-height:1.3}
.line .o{font-family:var(--mono);font-size:.6875rem;letter-spacing:.06em;color:var(--dim);margin-top:.25rem;text-transform:uppercase}
.line .amt{font-family:var(--mono);font-size:.9375rem;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right}
.line .ctl{grid-column:1/-1;display:flex;align-items:center;gap:.75rem}
.line .rm{font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.12em;text-transform:uppercase;color:var(--dim);transition:color .3s var(--ease)}
.line .rm:hover{color:var(--fire)}
.line.going{animation:collapse .38s var(--ease) forwards}
@keyframes collapse{to{opacity:0;transform:translateX(24px)}}

.totals{display:grid;gap:.5rem;font-size:.9375rem;margin-bottom:.35rem}
.totals .r{display:flex;justify-content:space-between;gap:1rem;color:var(--dim)}
.totals .r span:last-child{font-family:var(--mono);font-variant-numeric:tabular-nums}
.totals .r.big{
  color:var(--ink);font-family:var(--display);font-weight:700;text-transform:uppercase;
  font-size:1.125rem;letter-spacing:var(--track-h3);padding-top:.75rem;margin-top:.35rem;border-top:2px solid var(--ink);
}
.totals .r.big span:last-child{font-family:var(--mono);font-weight:700;font-size:1.0625rem}
.totals .r.free span:last-child{color:var(--fire)}

.empty{padding:4rem 1rem;text-align:center}
.empty .disc{width:3.5rem;height:3.5rem;border-radius:50%;border:2px dashed var(--edge);margin:0 auto 1.25rem}
.empty strong{display:block;font-family:var(--display);font-weight:700;text-transform:uppercase;font-size:1.25rem;margin-bottom:.4rem}
.empty p{color:var(--dim);font-size:.9375rem}

/* steps */
.stepbar{display:flex;gap:.3rem;margin-top:.9rem}
.stepbar i{flex:1;height:3px;background:var(--hair);border-radius:2px;overflow:hidden}
.stepbar i::after{content:"";display:block;height:100%;width:0;background:var(--fire);transition:width .5s var(--ease-out)}
.stepbar i.done::after,.stepbar i.now::after{width:100%}

.pane{display:none}
.pane.on{display:block;animation:paneIn .45s var(--ease-out) both}
@keyframes paneIn{from{opacity:0;transform:translateX(14px)}to{opacity:1;transform:none}}

.field{margin-bottom:1rem}
.field label{display:block;font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.16em;text-transform:uppercase;color:var(--dim);margin-bottom:.45rem}
.field input,.field select,.field textarea{
  width:100%;padding:.8rem .9rem;background:var(--panel);
  border:1.5px solid var(--field-edge);border-radius:8px;font-size:.9375rem;
  transition:border-color .3s var(--ease),background-color .3s var(--ease);
}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--edge)}
.field textarea{resize:vertical;min-height:4.5rem}
.field .help{font-size:.8125rem;color:var(--dim);margin-top:.4rem}
.field.bad input,.field.bad select{border-color:var(--fire);background:rgba(229,23,39,.05)}
.field .err{display:none;font-family:var(--mono);font-size:.6875rem;letter-spacing:.1em;text-transform:uppercase;color:var(--fire);margin-top:.45rem}
.field.bad .err{display:block}
.two{display:grid;grid-template-columns:1fr 1fr;gap:.75rem}
@media (max-width:400px){.two{grid-template-columns:1fr}}

.pay{display:grid;gap:.5rem}
.payopt{
  display:grid;grid-template-columns:auto 1fr;gap:.85rem;align-items:start;
  padding:1rem;border:1.5px solid var(--hair);border-radius:10px;cursor:pointer;
  transition:border-color .3s var(--ease),background-color .3s var(--ease);
}
.payopt:hover{border-color:var(--edge)}
.payopt .dot{width:1.15rem;height:1.15rem;border-radius:50%;border:1.5px solid var(--edge);flex:0 0 auto;display:grid;place-items:center;margin-top:.1rem;transition:border-color .25s var(--ease)}
.payopt .dot::after{content:"";width:.55rem;height:.55rem;border-radius:50%;background:var(--fire);transform:scale(0);transition:transform .25s var(--ease-out)}
.payopt input{position:absolute;opacity:0;width:0;height:0}
.payopt.sel .dot{border-color:var(--fire)}
.payopt.sel{border-color:var(--fire);background:rgba(229,23,39,.04)}
.payopt.sel .dot::after{transform:scale(1)}
.payopt .n{font-weight:600;font-size:.9375rem;display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
.payopt .d{color:var(--dim);font-size:.8125rem;margin-top:.25rem;line-height:1.45}
.payopt:focus-within{outline:2.5px solid var(--fire);outline-offset:3px}
.payopt .wrapx{min-width:0}

.cardbox{margin-top:.85rem;padding:1rem;border:1px dashed var(--edge);border-radius:10px}
.demo-note{
  display:flex;gap:.65rem;align-items:flex-start;margin-top:1rem;padding:.85rem 1rem;
  border-left:3px solid var(--fire);background:var(--panel);border-radius:0 8px 8px 0;
}
.demo-note p{font-size:.8125rem;color:var(--strong);line-height:1.5}
.demo-note svg{width:15px;height:15px;stroke:var(--fire);stroke-width:2;fill:none;flex:0 0 auto;margin-top:.15rem}

.review-row{display:flex;justify-content:space-between;gap:1rem;padding:.75rem 0;border-bottom:1px solid var(--hair);font-size:.9375rem}
.review-row .k{font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.14em;text-transform:uppercase;color:var(--dim);flex:0 0 auto}
.review-row .v{text-align:right;font-weight:500}

/* confirmation */
.done-pane{text-align:center;padding:1rem 0 0}
.ring{width:9rem;height:9rem;margin:0 auto 1.75rem;position:relative}
.ring svg{width:100%;height:100%;transform:rotate(-90deg)}
.ring circle{fill:none;stroke-width:5;stroke-linecap:round}
.ring .bg{stroke:var(--hair)}
.ring .fg{stroke:var(--fire);stroke-dasharray:396;stroke-dashoffset:calc(396 - 396 * var(--pr,0));transition:stroke-dashoffset 1.1s var(--ease-out)}
.ring .mid{position:absolute;inset:0;display:grid;place-items:center;text-align:center}
.ring .mid .num{font-family:var(--mono);font-size:.6875rem;letter-spacing:.14em;text-transform:uppercase;color:var(--dim)}
.ring .mid .st{font-family:var(--display);font-weight:700;text-transform:uppercase;font-size:.9375rem;line-height:1.1;margin-top:.25rem;max-width:7.5rem;letter-spacing:var(--track-h3)}
.done-pane h3{font-size:1.75rem;margin-bottom:.6rem}
.done-pane .msg{color:var(--dim);font-size:.9375rem;max-width:30ch;margin:0 auto}
.track{display:grid;gap:.15rem;margin:2rem 0 1.5rem;text-align:left}
.tstep{display:grid;grid-template-columns:auto 1fr auto;gap:.85rem;align-items:center;padding:.8rem 0;border-bottom:1px solid var(--hair)}
.tstep .b{width:.85rem;height:.85rem;border-radius:50%;border:1.5px solid var(--edge);transition:background-color .4s var(--ease),border-color .4s var(--ease)}
.tstep .l{font-family:var(--mono);font-size:.75rem;letter-spacing:.12em;text-transform:uppercase;color:var(--dim);transition:color .4s var(--ease)}
.tstep .t{font-family:var(--mono);font-size:.6875rem;color:var(--dim);opacity:0;transition:opacity .4s var(--ease)}
.tstep.hit .b{background:var(--fire);border-color:var(--fire)}
.tstep.hit .l{color:var(--ink)}
.tstep.hit .t{opacity:1}

/* --------------------------------------------------------------------------
   entrances
   -------------------------------------------------------------------------- */
.rise{opacity:0;transform:translateY(26px);transition:opacity .8s var(--ease-out),transform .8s var(--ease-out)}
.in .rise,.rise.in{opacity:1;transform:none}
.stag>*{opacity:0;transform:translateY(22px);transition:opacity .75s var(--ease-out),transform .75s var(--ease-out)}
.stag.in>*{opacity:1;transform:none}
.stag.in>*:nth-child(1){transition-delay:0s}
.stag.in>*:nth-child(2){transition-delay:.09s}
.stag.in>*:nth-child(3){transition-delay:.18s}
.stag.in>*:nth-child(4){transition-delay:.27s}
.stag.in>*:nth-child(5){transition-delay:.36s}
.stag.done>*:nth-child(1),.stag.done>*:nth-child(2),.stag.done>*:nth-child(3),
.stag.done>*:nth-child(4),.stag.done>*:nth-child(5){transition-delay:0s}

/* one living element per section, whisper level, paused when off-screen */
.live-dot{width:8px;height:8px;border-radius:50%;background:var(--fire)}
.vis .live-dot{animation:pulse 5.2s ease-in-out -1.7s infinite}
@keyframes pulse{0%,100%{opacity:.45;transform:scale(.85)}50%{opacity:1;transform:scale(1.15)}}

body.paused *,body.paused *::before,body.paused *::after{animation-play-state:paused !important}

/* --------------------------------------------------------------------------
   reduced motion, honoured completely
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation:none !important;
    transition-duration:.01ms !important;
    transition-delay:0s !important;
  }
  body{opacity:1}
  .rise,.stag>*{opacity:1 !important;transform:none !important}
  .wok-btn.show{transform:translateY(0) scale(1)}
  .drawer.open{transform:none}
  .sheet.open{transform:translate(-50%,-50%) scale(1)}
  .env::before{opacity:0 !important}
  /* The hero entrance is animation-driven, so killing animations above would
     leave every piece of it at its opacity:0 start state. Each one is pinned
     to its finished position here instead: same hero, it just arrives whole
     rather than assembling. The fire stops moving and reads as a still. */
  /* No ignition. The lockup is simply already assembled, with the poster
     frame sitting still inside the O and the film never asked to play. */
  .logo-img,.hero-line,.hero-cta,.s1-photo img,.s1-film{
    opacity:1 !important;transform:none !important;
  }
  .port{
    transform:none !important;border-radius:50% !important;opacity:1 !important;
    --edge:94%;
  }
  .embers{display:none}
}
body.rm .rise,body.rm .stag>*{opacity:1 !important;transform:none !important}
body.rm .step .bar{width:100% !important}

/* --------------------------------------------------------------------------
   late additions the markup needs
   -------------------------------------------------------------------------- */
[hidden]{display:none !important}

/* the whole dish row is one hit area, with the add button sitting above it */
.dish-hit{
  font:inherit;font-weight:600;text-align:left;padding:0;
  letter-spacing:-.005em;transition:color .3s var(--ease);
}
.dish-hit::after{content:"";position:absolute;inset:0;z-index:2}
.dish-add{position:relative;z-index:3}
.dish:hover .dish-hit{color:var(--ink)}

.payopt .wrapx{min-width:0;display:block}
.payopt{position:relative}

/* keep the sticky menu tools clear of the sticky nav */
.nav.show~main .menu-tools{top:3.55rem}

/* --------------------------------------------------------------------------
   revision pass: new components
   -------------------------------------------------------------------------- */

/* the phone number in the nav */
.nav-tel{
  display:inline-flex;align-items:center;gap:.5rem;flex:0 0 auto;
  font-family:var(--mono);font-size:var(--fs-label);letter-spacing:.1em;
  color:var(--strong);white-space:nowrap;
  transition:color .3s var(--ease);
}
.nav-tel svg{width:14px;height:14px;fill:none;stroke:var(--fire);stroke-width:1.8;stroke-linejoin:round}
.nav-tel:hover{color:var(--ink)}
@media (max-width:1080px){.nav-tel span{display:none}}
@media (max-width:520px){.nav-tel{display:none}}

/* the chopstick run behind the ordering steps */
/* The chopsticks. On their own clock, not the scroll's: they pinch closed and
   open again on a 4 second loop the way a hand actually holds them, pivoting
   about the grip end so the tips are what move. An ember sits between the
   tips and brightens as they close on it. */
.how-head{
  display:grid;grid-template-columns:1fr auto;gap:2rem;align-items:center;
  margin-bottom:3.25rem;
}
.chopsticks{
  width:clamp(120px,13vw,190px);height:auto;flex:0 0 auto;overflow:visible;
}
.cs-stick{
  fill:none;stroke:var(--paper);stroke-width:7;stroke-linecap:round;
}
.cs-grip{stroke-width:11}
.cs-move{
  transform-origin:212px 46px;   /* the grip end, where a hand would hold it */
  animation:pinch 4s cubic-bezier(.45,0,.35,1) infinite;
}
.cs-ember{
  fill:var(--fire);
  transform-origin:26px 189px;
  animation:emberHold 4s cubic-bezier(.45,0,.35,1) infinite;
}
@keyframes pinch{
  0%,100%{transform:rotate(0deg)}
  38%    {transform:rotate(-3.1deg)}
  62%    {transform:rotate(-3.1deg)}
}
@keyframes emberHold{
  0%,100%{transform:scale(1);opacity:.72}
  38%    {transform:scale(1.18);opacity:1}
  62%    {transform:scale(1.18);opacity:1}
}
@media (max-width:760px){
  .how-head{grid-template-columns:1fr;gap:1.25rem}
  .chopsticks{width:110px;justify-self:start}
}
@media (prefers-reduced-motion:reduce){
  .cs-move,.cs-ember{animation:none}
}

.how-cta,.find-cta{margin-top:3rem;display:flex;gap:.75rem;flex-wrap:wrap;align-items:center}
.find-cta{margin-top:2.25rem}

/* the photo behind the dish sheet header */
/* The photo covers the whole header rather than a fixed strip at the top. A
   fixed strip meant the description landed on the seam and went white on
   white; covering the header means every line of header type has dark behind
   it, and the fade to paper happens exactly at the body boundary. */
.sheet-photo{
  position:absolute;inset:0;z-index:0;overflow:hidden;
  background:var(--ink);
}
.sheet-photo img{
  width:100%;height:100%;object-fit:cover;
  opacity:0;transform:scale(1.08);
  transition:opacity .7s var(--ease),transform 1.4s var(--ease-out);
}
.sheet-photo.ready img{opacity:.55;transform:scale(1)}
.sheet-photo::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(rgba(0,0,0,.28) 0%,rgba(0,0,0,.42) 42%,rgba(0,0,0,.72) 86%,var(--paper) 100%);
}
.sheet-top{position:relative;isolation:isolate}
/* :not(.sheet-photo) matters. Without it this rule wins over the photo's own
   position:absolute, the photo drops into the flow, and the header opens with
   a white band above a boxed-in image instead of a full-bleed one. */
.sheet-top>*:not(.sheet-photo){position:relative;z-index:1}
/* With a photo behind it the header type has to hold its own against a
   mid-tone image, so it goes white and gets a shadow for the top band only. */
.sheet-top.has-photo{padding-top:8.5rem}
.sheet-top.has-photo #sheetName{color:var(--paper);text-shadow:0 2px 12px rgba(0,0,0,.85)}
.sheet-top.has-photo .d{color:rgba(255,255,255,.9);text-shadow:0 1px 8px rgba(0,0,0,.85)}
/* the close button and the heat dial both sit on the photo too */
.sheet-top.has-photo .x{color:var(--paper);background:rgba(0,0,0,.42);border-color:rgba(255,255,255,.35)}
.sheet-top.has-photo .x svg{stroke:var(--paper)}
.sheet-top.has-photo .heat svg{fill:rgba(255,255,255,.32)}
.sheet-top.has-photo .heat svg.on{fill:var(--fire)}

/* --------------------------------------------------------------------------
   responsive corrections found in the phone pass
   -------------------------------------------------------------------------- */
.tools-row2{display:flex;gap:.4rem;align-items:center;flex-wrap:wrap}

/* <address> is the right element for the shop's details, and it carries a UA
   default of font-style:italic that has nothing to do with this design. */
.plaque{font-style:normal}

@media (max-width:900px){
  /* The tools bar and the section rail are both sticky and stack against each
     other. Its height changes with the viewport and with how the chips wrap,
     so the rail's offset is measured at runtime rather than guessed: guessing
     it is what put the rail underneath the tools bar and hid it completely. */
  .rail{top:calc(3.55rem + var(--tools-h,6rem))}

  /* one scrollable row instead of two wrapped ones, so the sticky bar stays
     short enough to leave the rail somewhere to sit */
  .tools-row2{
    flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;
    scrollbar-width:none;
    margin:0 calc(var(--gut) * -1);padding:.15rem var(--gut);
  }
  .tools-row2::-webkit-scrollbar{display:none}
  .chips{flex-wrap:nowrap}
  .chip,.expand-all{flex:0 0 auto}

  /* The price and the add button dropped onto their own line and sat hard
     left, reading as though they belonged to nothing. Pushed back to the
     right edge they line up down the column again. */
  .dish{grid-template-columns:3.25rem 1fr}
  .dish-meta{grid-column:2;justify-content:flex-end;margin-top:.35rem}
}

@media (max-width:520px){
  /* the mark takes almost the full width on a phone */
  .lockup{width:min(92vw,30rem)}
  .hero-cta .btn{width:100%;justify-content:center}
}

/* ==========================================================================
   REVISION PASS 2
   Utility classes lifted out of inline style attributes, the mobile nav, and
   the responsive fixes. Nothing above this line changed shape.
   ========================================================================== */

/* --- utilities that used to be style="" in the markup -------------------- */
.center{text-align:center}
.h2-sm{font-size:var(--fs-h2)}
.h2-tight{max-width:16ch}
.kicker-center{justify-content:center}
.btn-grow{flex:1}
.field-spaced{margin-top:.75rem}
.live-dot{margin-right:.2rem}

/* --- anchors clear the sticky chrome -------------------------------------
   html carries scroll-padding-top for the fixed nav. Inside the menu the
   tools bar stacks underneath it, so those anchors need the nav plus the
   measured height of the bar, which app.js writes to --tools-h. */
.cat{scroll-margin-top:calc(var(--nav-h) + var(--tools-h, 4.5rem) + .75rem)}

/* --- the mobile nav panel -------------------------------------------------
   Below 860px the four nav links have no room and used to be set to
   display:none with nothing in their place, which left a phone with a logo,
   an Order button, and no way to reach any section of the page. They live
   here instead. The panel is a sibling of .nav rather than a child so it can
   sit under the bar without inheriting its blur or its transform. */
.nav-burger{
  display:none;
  flex:0 0 auto;width:2.75rem;height:2.75rem;margin-left:-.35rem;
  align-items:center;justify-content:center;
  background:none;border:0;cursor:pointer;color:var(--ink);
}
.nav-burger .bars{display:block;width:1.25rem;height:.75rem;position:relative}
.nav-burger .bars i{
  position:absolute;left:0;right:0;height:2px;background:currentColor;border-radius:2px;
  transition:transform .35s var(--ease-out),opacity .2s var(--ease);
}
.nav-burger .bars i:nth-child(1){top:0}
.nav-burger .bars i:nth-child(2){top:5px}
.nav-burger .bars i:nth-child(3){top:10px}
.nav-burger[aria-expanded="true"] .bars i:nth-child(1){transform:translateY(5px) rotate(45deg)}
.nav-burger[aria-expanded="true"] .bars i:nth-child(2){opacity:0}
.nav-burger[aria-expanded="true"] .bars i:nth-child(3){transform:translateY(-5px) rotate(-45deg)}

.nav-panel{
  position:fixed;top:0;left:0;right:0;z-index:59;
  background:rgba(255,255,255,.98);
  border-bottom:1px solid var(--hair);
  transform:translateY(-100%);
  transition:transform .42s var(--ease-out);
  padding-top:var(--nav-h);
}
@supports (backdrop-filter:blur(14px)){
  .nav-panel{background:rgba(255,255,255,.92);backdrop-filter:blur(14px)}
}
.nav-panel.open{transform:translateY(0)}
.nav-panel-in{
  display:flex;flex-direction:column;
  padding:.5rem var(--gut) 1.1rem;
}
.nav-panel-in a{
  font-family:var(--mono);font-size:.8125rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--strong);
  padding:.95rem .25rem;                   /* 44px+ rows without a fixed height */
  border-bottom:1px solid var(--hair);
  transition:color .25s var(--ease);
}
.nav-panel-in a:last-child{border-bottom:0}
.nav-panel-in a:hover{color:var(--ink)}
.nav-panel-in .np-tel{
  display:flex;align-items:center;gap:.6rem;
  color:var(--fire);margin-top:.35rem;
}
.nav-panel-in .np-tel svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linejoin:round}

@media (max-width:860px){
  .nav-burger{display:flex;order:-1}
  /* the mark stops being the first thing in the bar, so it centres instead */
  .nav-mark{margin-right:auto}
}
@media (min-width:861px){
  /* the panel can never be reachable on a desktop, even mid-resize */
  .nav-panel{display:none}
}

/* --- the tools bar row on a phone -----------------------------------------
   Popular / Vegetarian / Spicy / Open all cannot share one line below about
   440px no matter how it wraps -- the four labels alone run past 340px before
   any gaps or padding. Wrapping Open all onto its own line, pushed hard right
   by margin-left:auto, fixed the old clipping but reads as a stray button
   floating alone under the chips. The 900px rule above already made this row
   scroll horizontally rather than wrap; the fix here is to trust that same
   pattern all the way down instead of overriding it at the narrowest widths,
   and add the one thing it was missing -- a visible reason to swipe, so the
   Open all button reads as one swipe away rather than cut off. */
/* min-width:0 opts this out of the flex default (min-width:auto), which
   sizes a flex item to its content's full width regardless of the row it is
   in. Without it, this wrapper grew to the row's natural ~400px content
   width instead of shrinking to the space actually available, and the
   overflow was silently clipped by html,body's own overflow-x suppression --
   invisibly, since .tools-row2's overflow-x:auto never got a chance to
   engage. That was the same clipping bug back under a different mechanism. */
.tools-row2-wrap{position:relative;min-width:0}
@media (max-width:900px){
  .tools-row2-wrap::after{
    content:'';position:absolute;top:0;right:0;bottom:0;width:2.5rem;
    background:linear-gradient(to right,rgba(255,255,255,0),rgba(255,255,255,.98));
    pointer-events:none;
  }
}

/* --- short and landscape viewports ---------------------------------------
   The hero is one screen tall by design, but its internal spacing was fixed,
   so on a landscape phone (about 375px tall) the mark alone filled the
   viewport and both CTAs sat below the fold with no nav yet visible. Under
   560px of height the spacing compresses instead of overflowing. */
@media (max-height:560px) and (min-width:521px){
  .hero{min-height:auto;padding-top:4.5rem;padding-bottom:2.5rem}
  .lockup{width:min(56vw,34rem)}
  .hero-line{margin-top:1rem;font-size:.9375rem}
  .hero-cta{margin-top:1.1rem}
  .hero-cta .btn{padding:.7rem 1.2rem}
}
@media (max-height:460px){
  .hero{padding-top:3.75rem;padding-bottom:1.75rem}
  .hero-line{display:none}          /* the CTAs matter more than the sentence */
}

/* --- touch targets on the remaining text links ---------------------------
   The coarse-pointer block above lifts buttons, chips and rail links to 44px
   but never reached the footer or the FAQ answers, where the phone number is
   an 18px-tall line of text. */
@media (pointer:coarse){
  .foot li a,.ans a,.plaque-row a{display:inline-block;padding:.35rem 0;min-height:1.5rem}
  .foot li{margin-bottom:.35rem}
  .dish-hit{padding:.35rem 0}
  .soc-btn{width:2.75rem;height:2.75rem}
}


/* ======================================================================
   WHAT THE KITCHEN SAYS

   Three states this page could not previously be in, because nothing it
   showed depended on anything outside the file: a dish sold out tonight,
   an order that failed to send, and a shop that is shut.
   ====================================================================== */

/* A dish that has run out. Greyed rather than removed, because a regular
   looking for their usual should find out it is off, not think the menu
   changed. The name stays readable and openable; only the add button dies. */
.dish.soldout .dish-badge,
.dish.soldout .dish-desc,
.dish.soldout .dish-price{opacity:.4}
.dish.soldout .dish-hit{text-decoration:line-through;text-decoration-thickness:1px;
  text-underline-offset:2px;opacity:.55}
.dish-soldout{
  font-family:var(--mono);font-size:.625rem;font-weight:500;
  text-transform:uppercase;letter-spacing:.08em;color:var(--fire);
  border:1px solid currentColor;padding:.1rem .3rem;margin-right:.5rem;white-space:nowrap
}
.dish-add:disabled{opacity:.25;cursor:not-allowed}
.dish-add:disabled:hover{background:none;transform:none}

/* The checkout could not fail before, so it had nowhere to say that it had.
   Sits directly under the order it refers to rather than at the top of a
   scrolled panel where it would be off screen at the moment it appears. */
.order-error{
  margin:1rem 0 0;padding:.75rem .9rem;
  border-left:3px solid var(--fire);
  background:rgba(229,23,39,.06);
  font-size:.9375rem;line-height:1.45
}

/* Closed. A strip above everything, not a modal: the menu is still worth
   reading at 3am and an order can still be built for the morning. */
.shop-state{
  margin:0;padding:.6rem 1rem;
  background:var(--fire);color:#fff;
  font-size:.8125rem;line-height:1.4;text-align:center
}

@media (prefers-reduced-motion:reduce){
  .dish-add:disabled{transition:none}
}

/* --------------------------------------------------------------------------
   CALL-ONLY BUILD
   <html data-ordering="off"> while the shop takes orders by phone and
   WhatsApp only. The cart, the + buttons, the drawer and the sheet's options
   stay in the markup and in app.js, and these rules keep them out of sight.
   Delete the attribute and they all come back; the WhatsApp corner button
   and the sheet's Call/WhatsApp foot step aside on their own.
   -------------------------------------------------------------------------- */
html[data-ordering="off"] .wok-btn,
html[data-ordering="off"] .dish-add,
html[data-ordering="off"] .drawer,
html[data-ordering="off"] #sheetQty,
html[data-ordering="off"] #sheetAdd,
html[data-ordering="off"] #sheetBody{display:none!important}
.sheet-call{display:none}
html[data-ordering="off"] .sheet-call{display:inline-flex}
html:not([data-ordering="off"]) .wa-fab{display:none}

/* the WhatsApp glyph wherever it sits next to words */
.wa-ico{width:16px;height:16px;flex:0 0 auto;fill:currentColor;stroke:none;display:block}

/* the strip across the very top. Sits above the hero, scrolls away with it */
.announce{
  position:relative;z-index:2;margin:0;padding:.65rem var(--gut);
  background:var(--fire);color:var(--paper);
  font-family:var(--mono);font-size:var(--fs-label);font-weight:500;
  letter-spacing:var(--track-caps);text-transform:uppercase;text-align:center;line-height:1.5;
}
.announce strong{font-family:var(--display);font-weight:700;letter-spacing:var(--track-caps)}

/* WhatsApp, in the corner the cart button uses when ordering is on.
   Black disc, the standard WhatsApp glyph in white (Simple Icons, CC0),
   drawn at an exact size on an exact grid centre so it cannot sit off true.
   A hairline ring keeps it visible on the black sections. */
.wa-fab{
  position:fixed;z-index:70;
  right:clamp(1rem,3vw,2rem);bottom:calc(clamp(1rem,3vw,2rem) + env(safe-area-inset-bottom));
  width:3.75rem;height:3.75rem;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  background:var(--ink);color:var(--paper);
  box-shadow:0 0 0 1px rgba(255,255,255,.22),0 10px 30px rgba(0,0,0,.35);
  transition:transform .35s var(--ease-out),box-shadow .35s var(--ease);
}
.wa-fab .wa-ico{width:28px;height:28px}
.wa-fab:hover{transform:translateY(-3px);box-shadow:0 0 0 1px rgba(255,255,255,.4),0 14px 36px rgba(0,0,0,.45)}

/* "Delivery coming soon", under the three steps */
.soon{margin:2.75rem 0 0;display:flex;flex-wrap:wrap;align-items:center;gap:.75rem;color:var(--dim-d);font-size:.9375rem}
.soon-tag{
  font-family:var(--mono);font-size:var(--fs-label);font-weight:500;letter-spacing:.16em;text-transform:uppercase;
  color:var(--paper);background:var(--fire);padding:.35rem .7rem;border-radius:999px;
}
.how-cta{margin-top:2rem}

/* The QR. Desktop only: on a phone you cannot scan your own screen, and the
   WhatsApp button beside it already opens the chat. */
.qr-card{display:none}
@media (hover:hover) and (pointer:fine) and (min-width:860px){
  .qr-card{
    display:flex;align-items:center;gap:1rem;margin:0 0 0 1rem;
    padding:.75rem 1.1rem .75rem .75rem;border-radius:14px;
    background:var(--paper);color:var(--ink);
  }
}
.qr-card img{width:112px;height:112px;display:block}
.qr-card figcaption{display:flex;flex-direction:column;gap:.35rem}
.qr-k{font-family:var(--mono);font-size:var(--fs-label);font-weight:500;letter-spacing:.16em;text-transform:uppercase;color:var(--dim)}
.qr-v{font-family:var(--display);font-weight:700;font-size:1.25rem;letter-spacing:var(--track-h3)}
