/* Mangwalow for Restaurants — application stylesheet.
 *
 * Read tokens.css first. Three rules govern everything below:
 *
 *   1. Colour is a signal. Store state, order ageing and destructive actions
 *      are coloured; nothing else is. The single exception is the logomark,
 *      which is identity rather than interface.
 *   2. Surfaces are separated by hairlines (--mw-line), never by shadows.
 *      --shadow-pop belongs to popovers and side panels only, where it says
 *      "this floats above the page and will go away".
 *   3. Motion carries information or it does not happen. There are exactly
 *      three transitions in this file, each labelled MOTION 1/2/3. Hover and
 *      active states are instantaneous on purpose — it reads as fast, and it
 *      keeps a 10-hour shift from shimmering.
 *
 * Vertical space is the binding constraint: the primary viewport is 1366x768.
 * The top bar is 56px and there is no page header. Every pixel of chrome is a
 * pixel of order queue the owner cannot see.
 */

/* ---------------------------------------------------------------- reset -- */

*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  background:var(--mw-paper);
  color:var(--mw-ink);
  font-family:var(--mw-font);
  font-size:14px;
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
h1,h2,h3,h4,p,figure,ul,ol{margin:0;padding:0}
ul,ol{list-style:none}
img,svg{display:block;max-width:100%}
button,input,select,textarea{font:inherit;color:inherit}
button{background:none;border:0;padding:0;cursor:pointer}
a{color:inherit;text-decoration:none}
[hidden]{display:none!important}
[x-cloak]{display:none!important}

/* ----------------------------------------------------------- typography -- */

.t-title{font-size:20px;font-weight:600;letter-spacing:-.01em;line-height:1.2}
.t-section{font-size:15px;font-weight:600;letter-spacing:-.005em}
.t-body{font-size:14px;line-height:1.6;color:var(--mw-ink-2)}
.t-meta{font-size:12.5px;line-height:1.5;color:var(--mw-slate)}
.t-eyebrow{
  font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mw-slate);
}

/* Numbers are the product. Money, timers, order ids and counts all go through
   .num so columns of figures line up and a glance can compare them. */
.num{
  font-family:var(--mw-mono);
  font-variant-numeric:tabular-nums;
  font-feature-settings:"tnum" 1;
  letter-spacing:-.01em;
}
.num-sm{font-size:12px}
.num-md{font-size:14px}
/* Read at arm's length or more — the across-the-kitchen sizes. */
.num-lg{font-size:20px;font-weight:600}
.num-xl{font-size:30px;font-weight:600;letter-spacing:-.02em;line-height:1.1}

/* Urdu is first-class here: Aladdin Grill's real menu carries both scripts for
   every dish. Naskh rather than Nastaliq is a legibility choice for UI at
   small sizes — do not substitute a Nastaliq face. */
[lang="ur"],.urdu{font-family:var(--mw-urdu);direction:rtl;line-height:1.9}

/* ---------------------------------------------------- focus & motion ----- */

/* The ring inherits whatever radius the element already has — setting one
   here would quietly reshape every focused input. */
:focus-visible{
  outline:none;
  box-shadow:0 0 0 2px var(--mw-surface),0 0 0 4px var(--mw-coral-700);
}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms!important;transition-duration:.01ms!important}
}

.skip{
  position:absolute;left:-9999px;top:0;
  background:var(--mw-ink);color:var(--mw-paper);
  padding:var(--sp-2) var(--sp-4);border-radius:var(--r-md);
  font-size:13px;font-weight:500;z-index:80;
}
.skip:focus{left:var(--sp-4);top:var(--sp-4)}

.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;
}

/* --------------------------------------------------------------- shell --- */

.shell{
  --nav-current:var(--nav-w);
  display:grid;
  grid-template-columns:var(--nav-current) minmax(0,1fr);
  min-height:100vh;
  /* MOTION 3 — the layout is reflowing under the pointer; an instant jump
     reads as a rendering fault rather than a deliberate change. */
  transition:grid-template-columns var(--dur-panel) var(--ease);
}
.shell[data-nav="collapsed"]{--nav-current:var(--nav-w-collapsed)}

/* ------------------------------------------------------------- sidebar --- */

.sidebar{
  position:sticky;top:0;
  height:100vh;
  display:flex;flex-direction:column;
  background:var(--mw-surface);
  border-right:1px solid var(--mw-line);
  overflow:hidden;
}

.brand{
  display:flex;align-items:center;gap:var(--sp-3);
  height:var(--topbar-h);flex:0 0 var(--topbar-h);
  padding:0 var(--sp-4);
  border-bottom:1px solid var(--mw-line);
}
/* The one non-signal use of colour in the product: a logotype is identity,
   not interface. It appears exactly once per screen. */
.brand__mark{flex:0 0 22px;color:var(--mw-coral)}
.brand__text{min-width:0;overflow:hidden}
.brand__name{
  font-size:15px;font-weight:600;letter-spacing:-.02em;
  color:var(--mw-ink);white-space:nowrap;line-height:1.15;
}
.brand__sub{
  font-size:10.5px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;
  color:var(--mw-slate);white-space:nowrap;
}

.nav{flex:1 1 auto;overflow-y:auto;padding:var(--sp-3) var(--sp-3)}
.nav__link{
  display:flex;align-items:center;gap:var(--sp-3);
  height:42px;padding:0 var(--sp-3);
  border-radius:var(--r-md);
  color:var(--mw-ink-2);
  font-size:14px;font-weight:500;letter-spacing:-.005em;
}
.nav__link + .nav__link{margin-top:2px}
.nav__link:hover{background:var(--mw-sunken);color:var(--mw-ink)}
/* WHERE YOU ARE. The flat grey said "hovered" as much as it said "here" — the
   hover state is the same colour — so the current page now carries the brand
   wash and a coral edge, which nothing else in the nav can be mistaken for. */
.nav__link[aria-current="page"]{
  position:relative;
  background:linear-gradient(90deg,var(--mw-coral-wash),var(--mw-surface));
  color:var(--mw-coral-700);
  font-weight:600;
}
.nav__link[aria-current="page"]::before{
  content:"";position:absolute;left:0;top:8px;bottom:8px;
  width:3px;border-radius:0 3px 3px 0;background:var(--mw-coral);
}
.nav__icon{flex:0 0 24px}
.nav__label{flex:1 1 auto;white-space:nowrap;overflow:hidden}

/* An order waiting on a human is exactly the kind of thing colour is for. */
.badge{
  flex:0 0 auto;
  min-width:20px;height:20px;padding:0 6px;
  display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--r-pill);
  background:var(--mw-coral-700);color:#fff;
  font-family:var(--mw-mono);font-variant-numeric:tabular-nums;
  font-size:12px;font-weight:600;
}

.nav__foot{
  flex:0 0 auto;
  padding:var(--sp-3);
  border-top:1px solid var(--mw-line);
}
.nav__collapse{
  display:flex;align-items:center;gap:var(--sp-3);
  width:100%;height:36px;padding:0 var(--sp-3);
  border-radius:var(--r-md);
  color:var(--mw-slate);font-size:13px;font-weight:500;
}
.nav__collapse:hover{background:var(--mw-sunken);color:var(--mw-ink-2)}
.nav__collapse svg{flex:0 0 20px}

/* Need help. Sits above the collapse control, out of the scrolling list, so it
   is on screen at the moment a person gives up rather than at the bottom of a
   menu they were not reading. */
.navhelp{
  flex:0 0 auto;margin:var(--sp-3);padding:var(--sp-3);
  background:linear-gradient(160deg,var(--mw-coral-wash),var(--mw-paper));
  border:1px solid var(--mw-line);border-radius:var(--r-lg);
}
.navhelp__title{font-size:13px;font-weight:600;color:var(--mw-ink)}
.navhelp__body{margin-top:2px;font-size:12px;line-height:1.45;color:var(--mw-slate)}
.navhelp__go{
  display:flex;align-items:center;justify-content:center;gap:var(--sp-2);
  height:34px;margin-top:var(--sp-3);
  background:var(--mw-surface);
  border:1px solid var(--mw-line-strong);border-radius:var(--r-md);
  font-size:13px;font-weight:600;color:var(--mw-ink);
}
.navhelp__go:hover{background:var(--mw-ink);border-color:var(--mw-ink);color:var(--mw-surface)}

/* Collapsed: icons only, centred, everything textual withdrawn. */
[data-nav="collapsed"] .brand{justify-content:center;padding:0}
[data-nav="collapsed"] .brand__text,
[data-nav="collapsed"] .nav__label,
[data-nav="collapsed"] .nav__collapse span{display:none}
/* A card is not a 64px icon, so at 64px there is no card. */
[data-nav="collapsed"] .navhelp{display:none}
[data-nav="collapsed"] .nav__link,
[data-nav="collapsed"] .nav__collapse{justify-content:center;padding:0}
[data-nav="collapsed"] .badge{
  position:absolute;transform:translate(12px,-12px);
}
[data-nav="collapsed"] .nav__link{position:relative}

/* ------------------------------------------------------------- top bar --- */

.canvas{display:flex;flex-direction:column;min-width:0}

.topbar{
  position:sticky;top:0;z-index:30;
  display:flex;align-items:center;gap:var(--sp-4);
  height:var(--topbar-h);flex:0 0 var(--topbar-h);
  padding:0 var(--sp-6);
  background:var(--mw-surface);
  border-bottom:1px solid var(--mw-line);
}
/* The page title lives here, not in a page header: 768px of height cannot
   afford to say the same word twice. The subtitle goes UNDER it inside the same
   56px rather than beside it — the bar is already the width budget for the
   search box and the store status, and a second column of prose is what pushes
   those onto a second line at 1024. */
.topbar__head{display:flex;flex-direction:column;justify-content:center;min-width:0}
.topbar__title{
  font-size:18px;font-weight:600;letter-spacing:-.01em;
  white-space:nowrap;
}
.topbar__sub{
  font-size:12.5px;line-height:1.3;color:var(--mw-slate);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.topbar__spacer{flex:1 1 auto}
.topbar__tools{display:flex;align-items:center;gap:var(--sp-3)}
.topbar__divider{
  width:1px;height:24px;background:var(--mw-line);flex:0 0 1px;
}

.search{position:relative;display:flex;align-items:center}
.search__icon{
  position:absolute;left:10px;color:var(--mw-slate);pointer-events:none;
}
.search__input{
  width:240px;height:34px;
  padding:0 40px 0 34px;
  background:var(--mw-sunken);
  border:1px solid transparent;
  border-radius:var(--r-md);
  font-size:13.5px;
}
.search__input::placeholder{color:var(--mw-slate)}
.search__input:hover{border-color:var(--mw-line-strong)}
.search__key{
  position:absolute;right:8px;
  min-width:18px;height:18px;padding:0 5px;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--mw-line-strong);border-radius:var(--r-sm);
  color:var(--mw-slate);font-family:var(--mw-mono);font-size:11px;
  pointer-events:none;
}
.search__input:focus + .search__key,
.search__input:not(:placeholder-shown) + .search__key{display:none}

/* Connection: neutral when there is nothing to say. A permanently green light
   is decoration; this only turns colour when the answer changes. */
.conn{display:flex;align-items:center;gap:var(--sp-2);margin-left:calc(var(--sp-3) * -1 + var(--sp-2))}
.conn__dot{width:8px;height:8px;border-radius:50%;background:var(--mw-line-strong)}
.conn[data-state="reconnecting"] .conn__dot{background:var(--mw-amber)}
.conn[data-state="offline"] .conn__dot{background:var(--mw-signal)}

/* -------------------------------------------------- store status pill ---- */

.pill{
  display:inline-flex;align-items:center;gap:var(--sp-2);
  height:30px;padding:0 var(--sp-3);
  border-radius:var(--r-pill);
  font-size:13px;font-weight:600;letter-spacing:-.005em;
  white-space:nowrap;
}
.pill__dot{
  flex:0 0 7px;width:7px;height:7px;border-radius:50%;background:currentColor;
}
.pill--open{background:var(--mw-jade-wash);color:var(--mw-jade)}
.pill--busy{background:var(--mw-amber-wash);color:var(--mw-amber-700)}
.pill--paused{background:var(--mw-coral-wash);color:var(--mw-coral-700)}
.pill--closed{background:var(--mw-sunken);color:var(--mw-ink-2)}
button.pill{cursor:pointer}
button.pill:hover{filter:brightness(.97)}

.status-menu{width:264px;padding:var(--sp-2)}
.status-menu__item{
  display:flex;align-items:flex-start;gap:var(--sp-3);
  width:100%;padding:var(--sp-2) var(--sp-3);
  border-radius:var(--r-md);
  text-align:left;font-size:13.5px;
}
.status-menu__item:hover:not([disabled]){background:var(--mw-sunken)}
.status-menu__item[disabled]{cursor:default}
.status-menu__item[aria-current="true"]{font-weight:600}
.status-menu__body{flex:1 1 auto;min-width:0}
/* Store state is exactly what colour is for: the eye finds "Paused" in the
   list before it has finished reading the words. */
.status-dot{flex:0 0 8px;width:8px;height:8px;border-radius:50%;margin-top:6px}
.status-dot--open{background:var(--mw-jade)}
.status-dot--busy{background:var(--mw-amber)}
.status-dot--paused{background:var(--mw-coral)}
.status-dot--closed{background:var(--mw-line-strong)}
.status-menu__name{display:block;font-weight:500}
.status-menu__item[aria-current="true"] .status-menu__name{font-weight:600}
.status-menu__hint{display:block;color:var(--mw-slate);font-size:12px;margin-top:1px;font-weight:400}
.status-menu__check{flex:0 0 16px;color:var(--mw-ink);margin-top:2px}
.status-menu__foot{
  padding:var(--sp-2) var(--sp-3) var(--sp-1);
  border-top:1px solid var(--mw-line);
  margin-top:var(--sp-2);
  color:var(--mw-slate);font-size:12px;
}

/* ----------------------------------------------------------- state bar --- */

/* Rendered only when the store is not open, or the connection is not online.
   Absence is the calm state — that is the whole point of it. */
.state-bar{
  display:flex;align-items:center;gap:var(--sp-3);
  min-height:40px;padding:var(--sp-2) var(--sp-6);
  border-bottom:1px solid var(--mw-line);
  font-size:13.5px;
  /* MOTION 1 — the bar has to be seen arriving, or it reads as something that
     was always there and gets ignored. */
  animation:state-bar-in var(--dur-panel) var(--ease) both;
}
@keyframes state-bar-in{
  from{transform:translateY(-100%);opacity:0}
  to{transform:translateY(0);opacity:1}
}
.state-bar__label{font-weight:600}
.state-bar__spacer{flex:1 1 auto}
.state-bar--busy{background:var(--mw-amber-wash);color:var(--mw-ink);border-bottom-color:var(--mw-amber)}
.state-bar--paused{background:var(--mw-coral-wash);color:var(--mw-ink);border-bottom-color:var(--mw-coral)}
.state-bar--closed{background:var(--mw-sunken);color:var(--mw-ink-2)}
.state-bar--offline{background:var(--mw-night);color:var(--mw-paper);border-bottom-color:var(--mw-night)}

/* ------------------------------------------------------- offline controls -- */

/* Anything that needs the server says so by being visibly unavailable rather
   than by failing when tapped. portal.js also sets the reason as the title, so
   the control itself can answer "why". No transition: the interface has three,
   and "this stopped working" is not information that benefits from easing. */
[data-needs-network][disabled],
[data-needs-network][aria-disabled="true"]{
  opacity:.42;
  cursor:not-allowed;
}

/* ---------------------------------------------------------------- main --- */

.main{
  flex:1 1 auto;
  width:100%;max-width:1280px;
  /* CENTRED IN THE CANVAS. Capped and left-aligned put a 1280px column against
     the left edge of a 1920px screen with 400px of nothing beside it. */
  margin-inline:auto;
  padding:var(--sp-6) var(--sp-8);
}

/* --------------------------------------------------------------- cards --- */

.card{
  background:var(--mw-surface);
  border:1px solid var(--mw-line);
  border-radius:var(--r-lg);
}
.card__pad{padding:var(--sp-5) var(--sp-6)}

/* -------------------------------------------------------- empty states --- */

.empty{
  display:flex;flex-direction:column;align-items:center;text-align:center;
  max-width:380px;margin:0 auto;
  padding:var(--sp-16) 0;
}
.empty__icon{
  display:flex;align-items:center;justify-content:center;
  width:52px;height:52px;margin-bottom:var(--sp-5);
  border-radius:var(--r-lg);
  background:var(--mw-sunken);
  color:var(--mw-slate);
}
.empty__title{font-size:16px;font-weight:600;letter-spacing:-.01em}
.empty__body{margin-top:var(--sp-2);font-size:14px;line-height:1.65;color:var(--mw-slate)}
.empty__action{margin-top:var(--sp-6)}

/* ------------------------------------------------------------- buttons --- */

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  height:36px;padding:0 var(--sp-4);
  border:1px solid transparent;border-radius:var(--r-md);
  font-size:14px;font-weight:500;letter-spacing:-.005em;
  white-space:nowrap;
}
.btn--primary{background:var(--mw-coral-700);color:#fff}
/* Darkened rather than lightened: --mw-coral-600 is the hover for coral used
   as an accent, but white text on it drops to 4.1:1. Mixing toward ink can
   only improve the ratio. */
.btn--primary:hover{background:color-mix(in srgb,var(--mw-coral-700) 88%,var(--mw-ink))}
.btn--quiet{background:var(--mw-surface);border-color:var(--mw-line-strong);color:var(--mw-ink)}
.btn--quiet:hover{background:var(--mw-sunken)}
.btn--ghost{color:var(--mw-ink-2)}
.btn--ghost:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.btn--block{width:100%}
.btn--lg{height:42px;padding:0 var(--sp-5);font-size:15px}
/* Every disabled button in the product. This lived in the order queue's
   section, where it was a product-wide rule filed under one screen — found
   by `test_stylesheet_sections`, which is the sort of drift it exists for. */
.btn[disabled]{opacity:.42;cursor:not-allowed}

/* -------------------------------------------------------------- fields --- */

.field + .field{margin-top:var(--sp-4)}
.field__label{
  display:block;margin-bottom:var(--sp-2);
  font-size:13px;font-weight:500;color:var(--mw-ink-2);
}
.input{
  width:100%;height:42px;padding:0 var(--sp-3);
  background:var(--mw-surface);
  border:1px solid var(--mw-line-strong);border-radius:var(--r-md);
  font-size:15px;
}
.input::placeholder{color:var(--mw-slate)}
.input:hover{border-color:var(--mw-slate)}

/* --------------------------------------------------------------- alert --- */

.alert{
  display:flex;gap:var(--sp-3);align-items:flex-start;
  padding:var(--sp-3) var(--sp-4);
  border-radius:var(--r-md);
  font-size:13.5px;line-height:1.5;
}
.alert--error{background:var(--mw-signal-wash);color:var(--mw-signal)}
.alert__icon{flex:0 0 18px;margin-top:1px}

/* ------------------------------------------------------------ popovers --- */

.pop{position:relative}
.popover{
  position:absolute;top:calc(100% + 6px);right:0;z-index:60;
  background:var(--mw-surface);
  border:1px solid var(--mw-line);
  border-radius:var(--r-lg);
  box-shadow:var(--shadow-pop);
  /* MOTION 2 — the rise says the panel came from the control you just hit,
     rather than having been revealed by the page. */
  animation:pop-in var(--dur-micro) var(--ease) both;
}
@keyframes pop-in{
  from{opacity:0;transform:translateY(-2px)}
  to{opacity:1;transform:translateY(0)}
}

/* ----------------------------------------------------------- user menu --- */

.avatar{
  display:flex;align-items:center;justify-content:center;
  width:28px;height:28px;flex:0 0 28px;
  border-radius:50%;
  background:var(--mw-sunken);
  color:var(--mw-ink-2);
  font-size:11.5px;font-weight:600;letter-spacing:.02em;
}
.user-trigger{
  display:flex;align-items:center;gap:var(--sp-2);
  height:38px;padding:0 var(--sp-2);
  border-radius:var(--r-md);
}
.user-trigger:hover{background:var(--mw-sunken)}
.user-trigger__name{font-size:13.5px;font-weight:500;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.user-trigger__chev{color:var(--mw-slate)}

.user-menu{width:280px}
.user-menu__head{padding:var(--sp-4);border-bottom:1px solid var(--mw-line)}
.user-menu__name{font-size:14px;font-weight:600;letter-spacing:-.005em}
.user-menu__mail{margin-top:2px;font-size:12.5px;color:var(--mw-slate);word-break:break-all}
.user-menu__vendor{
  margin-top:var(--sp-3);padding-top:var(--sp-3);
  border-top:1px solid var(--mw-line);
}
.user-menu__roles{margin-top:2px;font-size:12.5px;color:var(--mw-slate)}
.user-menu__group{padding:var(--sp-2)}
.user-menu__group + .user-menu__group{border-top:1px solid var(--mw-line)}
.user-menu__item{
  display:flex;align-items:center;gap:var(--sp-3);
  width:100%;height:36px;padding:0 var(--sp-3);
  border-radius:var(--r-md);
  font-size:13.5px;text-align:left;color:var(--mw-ink-2);
}
.user-menu__item:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.user-menu__item svg{flex:0 0 18px;color:var(--mw-slate)}
.user-menu__item[aria-current="true"]{font-weight:600;color:var(--mw-ink)}

/* --------------------------------------------------------------- login --- */

.auth{min-height:100vh;display:grid;grid-template-columns:1fr 1fr}
.auth__form-col{
  display:flex;align-items:center;justify-content:center;
  padding:var(--sp-8);
  background:var(--mw-paper);
}
.auth__form{width:100%;max-width:360px}
.auth__brand{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-10)}
.auth__title{font-size:24px;font-weight:600;letter-spacing:-.02em}
.auth__lede{margin-top:var(--sp-2);font-size:14px;color:var(--mw-slate);line-height:1.6}
.auth__submit{margin-top:var(--sp-6)}
.auth__alert{margin-bottom:var(--sp-5)}
.auth__aside{margin-top:var(--sp-5);font-size:13px;color:var(--mw-slate)}
.auth__identifier{
  display:flex;align-items:center;gap:var(--sp-2);
  margin-bottom:var(--sp-5);padding:var(--sp-3) var(--sp-4);
  background:var(--mw-sunken);border-radius:var(--r-md);
  font-size:13.5px;
}
.auth__identifier-value{flex:1 1 auto;font-weight:500;word-break:break-all}
.auth__restart{font-size:13px;color:var(--mw-ink-2);text-decoration:underline;text-underline-offset:3px}
.auth__restart:hover{color:var(--mw-ink)}

.auth__panel{
  display:flex;flex-direction:column;justify-content:center;
  padding:var(--sp-12);
  background:var(--mw-sunken);
  border-left:1px solid var(--mw-line);
}
.auth__panel-line{
  font-size:28px;font-weight:600;letter-spacing:-.025em;line-height:1.25;
  max-width:16ch;
}
.auth__panel-urdu{
  margin-top:var(--sp-4);
  font-size:20px;color:var(--mw-ink-2);
  max-width:24ch;
}
.auth__panel-foot{
  margin-top:var(--sp-12);padding-top:var(--sp-5);
  border-top:1px solid var(--mw-line-strong);
  font-size:13px;color:var(--mw-slate);max-width:40ch;line-height:1.6;
}

@media (max-width:900px){
  .auth{grid-template-columns:1fr}
  .auth__panel{display:none}
}

/* ------------------------------------------------------------- notices --- */

/* Full-page states that exist outside the shell: no membership yet, missing
   capability. Same paper, same hairlines — a person who lands here has not
   left the product. */
.notice{
  min-height:100vh;display:flex;align-items:center;justify-content:center;
  padding:var(--sp-8);
}
.notice__card{width:100%;max-width:460px}
.notice__brand{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-8)}
.notice__title{font-size:20px;font-weight:600;letter-spacing:-.015em}
.notice__body{margin-top:var(--sp-3);font-size:14px;line-height:1.65;color:var(--mw-slate)}
.notice__body + .notice__body{margin-top:var(--sp-3)}
.notice__actions{display:flex;gap:var(--sp-3);margin-top:var(--sp-6)}

/* ------------------------------------------------------------ narrow ----- */

/* Below 1200px the sidebar collapses itself; portal.js sets the attribute so
   the state and the stored preference never disagree. */
@media (max-width:1000px){
  .search{display:none}
  .main{padding:var(--sp-5) var(--sp-5)}
  .topbar{padding:0 var(--sp-5)}
}

/* A PHONE IS THE EMERGENCY SCREEN — the one a merchant reaches for when
   something has already gone wrong — so it is the last place the interface may
   be broken. The top bar's tools are about 250px of a 375px screen, and with the
   page title beside them the whole document scrolled sideways on every screen in
   the product.
   Nothing is removed: the store status, the connection state and the user menu
   are all still there and still reachable. The title gives up its width first,
   and the account name truncates rather than disappearing — it stays in the
   markup, so the button keeps announcing whose account it opens. */
@media (max-width:560px){
  .topbar{padding:0 var(--sp-3);gap:var(--sp-2)}
  .topbar__head{min-width:0}
  .topbar__title{min-width:0;overflow:hidden;text-overflow:ellipsis}
  /* Two lines do not fit a phone's bar beside the store pill. */
  .topbar__sub{display:none}
  .topbar__tools{min-width:0;gap:var(--sp-2)}
  .topbar__divider{display:none}
  /* The menu's own wrapper is the flex item, so it is the one that has to be
     allowed to shrink — without this the button inside it never gets the
     chance, whatever the button says. */
  .pop{min-width:0}
  .user-trigger{min-width:0}
  .user-trigger__name{
    min-width:0;max-width:44px;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  }
}

/* ============================================================ menu editor ==
 *
 * The one screen a restaurant owner spends an afternoon in. Same three rules as
 * everything above: hairlines not shadows (the side panel is the exception, and
 * --shadow-pop is exactly what it is for), colour only where it is a signal,
 * and every price, count and version number through `.num`.
 *
 * The signals on this screen, and nothing else, are coloured:
 *   • unpublished changes waiting   coral hairline
 *   • something blocking a publish  signal red
 *   • a dish that is off today      coral wash
 * A category name, a section count, a chip and an icon are all ink.
 *
 * Vertical budget at 1366x768: 56px top bar, ~56px publish bar, 48px of page
 * padding. The rail and the item list each own their own scroll so the publish
 * state and the "add a dish" row never leave the screen.
 */

.menu{display:flex;flex-direction:column;gap:var(--sp-4)}
.menu__grid{
  display:grid;
  grid-template-columns:232px minmax(0,1fr);
  gap:var(--sp-5);
  align-items:start;
}

/* --------------------------------------------------------- publish bar --- */

/* Sticky under the 56px top bar rather than sized to fit: the publish state has
   to still be on screen at dish ninety, and a height computed from the viewport
   would be wrong the moment the state bar appears above it. Stickiness needs no
   arithmetic and cannot be off by 40px. */
.pubbar{
  position:sticky;top:var(--topbar-h);z-index:20;
  display:flex;align-items:center;gap:var(--sp-4);
  padding:var(--sp-3) var(--sp-4);
  background:var(--mw-surface);
  border:1px solid var(--mw-line);
  border-radius:var(--r-lg);
  flex:0 0 auto;
}
.pubbar__dot{flex:0 0 8px;width:8px;height:8px;border-radius:50%;background:var(--mw-line-strong)}
/* A WASH THAT FADES, not a block of colour. The bar is sticky and full width:
   a flat coral panel across the top of every screen of the menu would be the
   loudest thing in the product, and it is not the most important one. The
   gradient puts the weight at the edge the dot and the sentence start from and
   lets it go before it reaches the buttons. */
.pubbar--draft{
  border-color:var(--mw-coral);
  background:linear-gradient(100deg,var(--mw-coral-wash),var(--mw-surface) 55%);
}
.pubbar--draft .pubbar__dot{background:var(--mw-coral)}
.pubbar--blocked{
  border-color:var(--mw-signal);
  background:linear-gradient(100deg,var(--mw-signal-wash),var(--mw-surface) 70%);
}
.pubbar--blocked .pubbar__dot{background:var(--mw-signal)}
.pubbar__body{min-width:0;flex:1 1 auto}
.pubbar__title{font-size:14px;font-weight:600;letter-spacing:-.005em}
.pubbar__sub{margin-top:1px;font-size:12.5px;color:var(--mw-slate)}
.pubbar--blocked .pubbar__sub{color:var(--mw-ink-2)}
.pubbar__diff{display:flex;align-items:center;gap:var(--sp-3);flex:0 0 auto}

.mstat{
  display:inline-flex;align-items:baseline;gap:5px;
  font-size:12.5px;color:var(--mw-ink-2);
}

.diffcard{width:280px;max-height:340px;overflow-y:auto;padding:var(--sp-4)}
.diffcard .t-eyebrow + .difflist{margin-bottom:var(--sp-3)}
.difflist{margin-top:var(--sp-2);font-size:13px;color:var(--mw-ink-2)}
.difflist li{padding:2px 0;border-bottom:1px solid var(--mw-line)}
.difflist li:last-child{border-bottom:0}

/* --------------------------------------------------- notices and flashes -- */

.mflash,.mnotice{
  display:flex;align-items:flex-start;gap:var(--sp-2);
  padding:var(--sp-2) var(--sp-3);
  border-radius:var(--r-md);
  font-size:13px;line-height:1.5;
  flex:0 0 auto;
}
.mflash--ok{background:var(--mw-sunken);color:var(--mw-ink-2)}
.mflash--warn{background:var(--mw-amber-wash);color:var(--mw-ink)}
.mflash--error{background:var(--mw-signal-wash);color:var(--mw-signal)}
.mflash__icon,.mnotice__icon{flex:0 0 16px;margin-top:1px}
.mnotice{background:var(--mw-sunken);color:var(--mw-ink-2)}
.mnotice--problem{background:var(--mw-signal-wash);color:var(--mw-signal)}

/* ---------------------------------------------------------------- rail --- */

.menu-rail{
  position:sticky;top:calc(var(--topbar-h) + 76px);
  display:flex;flex-direction:column;
  max-height:calc(100vh - var(--topbar-h) - 96px);
  background:var(--mw-surface);
  border:1px solid var(--mw-line);
  border-radius:var(--r-lg);
}
.menu-rail__head{
  display:flex;align-items:center;justify-content:space-between;
  padding:var(--sp-3) var(--sp-4) var(--sp-2);
}
.menu-rail__total{color:var(--mw-slate)}
.menu-rail__list{flex:1 1 auto;overflow-y:auto;padding:0 var(--sp-2) var(--sp-2)}

.rail-node{display:flex;align-items:center;gap:2px;padding-left:calc(var(--depth) * 14px)}
.rail-link{
  flex:1 1 auto;min-width:0;
  display:flex;align-items:center;gap:var(--sp-2);
  min-height:38px;padding:var(--sp-1) var(--sp-2);
  border-radius:var(--r-sm);
  font-size:13.5px;color:var(--mw-ink-2);
}
.rail-link:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.rail-link.is-current{background:var(--mw-sunken);color:var(--mw-ink);font-weight:600}
.rail-link.is-hidden .rail-link__name{color:var(--mw-slate)}
/* Both scripts, stacked. Side by side, a real section name ("Mutton / Beef
   Karahi") loses to its own Urdu and truncates to four letters. */
.rail-link__text{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}
.rail-link__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.25}
.rail-link__ur{
  font-size:11.5px;line-height:1.5;color:var(--mw-slate);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.rail-link__count{flex:0 0 auto;color:var(--mw-slate)}
/* A section holding something that blocks publishing. This is a signal: it is
   the only way to find the offending dish without opening all nine sections. */
.rail-link__flag{flex:0 0 auto;color:var(--mw-signal);display:flex}
.rail-link__flag--hidden{color:var(--mw-slate)}

.mmoves{display:flex;flex-direction:column;flex:0 0 20px;cursor:grab}

/* THE DRAG AFFORDANCE THE ROW NEVER HAD. It has been draggable since it was
   written and nothing on screen said so — two bare arrows read as "press twice
   to move one place".
 *
 * The grip is ABSOLUTE over the arrows so the swap costs no height and nothing
 * shifts under the pointer. The arrows fade rather than `display:none`, which
 * keeps them in the tab order — `:focus-within` is what brings them back for a
 * keyboard, and it is why the old promise still holds. */
.mrow .mmoves{position:relative}
.mgrip{
  position:absolute;inset:0;pointer-events:none;
  display:flex;align-items:center;justify-content:center;
  color:var(--mw-line-strong);
  transition:opacity var(--dur-micro) var(--ease);
}
.mrow .mmove{opacity:0;transition:opacity var(--dur-micro) var(--ease)}
.mrow:hover .mgrip,.mrow .mmoves:focus-within .mgrip{opacity:0}
.mrow:hover .mmove,.mrow .mmoves:focus-within .mmove{opacity:1}
/* No pointer, no drag: HTML5 dragstart does not fire for a finger, so a touch
   screen gets the arrows and never the handle that would not work. */
@media (hover:none){
  .mgrip{opacity:0}
  .mrow .mmove{opacity:1}
}
.mmove{
  display:flex;align-items:center;justify-content:center;
  width:20px;height:16px;border-radius:4px;
  color:var(--mw-slate);
}
.mmove:hover{background:var(--mw-sunken);color:var(--mw-ink)}
/* A row of red crosses at rest is decoration; the colour belongs to the moment
   you reach for one. */
.mmove.mdanger{color:var(--mw-slate)}
.mmove.mdanger:hover{background:var(--mw-signal-wash);color:var(--mw-signal)}

.menu-rail__foot{flex:0 0 auto;padding:var(--sp-2);border-top:1px solid var(--mw-line)}
.menu-rail__choices{
  display:flex;align-items:center;gap:var(--sp-2);
  height:32px;padding:0 var(--sp-2);margin-top:var(--sp-1);
  border-radius:var(--r-sm);
  font-size:13px;color:var(--mw-ink-2);
}
.menu-rail__choices:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.menu-rail__choices svg{color:var(--mw-slate)}

/* An "add" row that is a field, not a button that opens a form: one keystroke
   fewer, and the placeholder does the teaching. */
.mstart{display:flex;align-items:center;gap:var(--sp-2)}
.mstart__input{
  flex:1 1 auto;min-width:0;height:32px;padding:0 var(--sp-2);
  background:var(--mw-sunken);
  border:1px solid transparent;border-radius:var(--r-sm);
  font-size:13px;
}
.mstart__input::placeholder{color:var(--mw-slate)}
.mstart__input:hover,.mstart__input:focus{background:var(--mw-surface);border-color:var(--mw-line-strong)}
.mstart__go{
  flex:0 0 32px;height:32px;
  display:flex;align-items:center;justify-content:center;
  border:1px solid var(--mw-line-strong);border-radius:var(--r-sm);
  color:var(--mw-ink-2);
}
.mstart__go:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.mstart--sub{width:200px}
.mstart--option{margin-top:var(--sp-3)}
.mstart--option .mstart__input{flex:1 1 50%}

/* ------------------------------------------------------------ item pane --- */

.menu-pane{
  display:flex;flex-direction:column;
  background:var(--mw-surface);
  border:1px solid var(--mw-line);
  border-radius:var(--r-lg);
}
.pane-head{
  display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-4);
  padding:var(--sp-4) var(--sp-5) var(--sp-3);
  background:var(--mw-surface);
  border-bottom:1px solid var(--mw-line);
}
.pane-head__names{min-width:0;flex:1 1 auto;max-width:400px}
.pane-title{
  display:block;width:100%;
  border:1px solid transparent;border-radius:var(--r-sm);
  padding:2px var(--sp-2);margin-left:calc(var(--sp-2) * -1);
  background:transparent;
  font-size:19px;font-weight:600;letter-spacing:-.015em;
}
.pane-title-ur{
  display:block;width:100%;
  border:1px solid transparent;border-radius:var(--r-sm);
  padding:0 var(--sp-2);margin-left:calc(var(--sp-2) * -1);
  background:transparent;
  font-size:15px;color:var(--mw-ink-2);
}
.pane-title:hover,.pane-title-ur:hover{border-color:var(--mw-line)}
.pane-title:focus,.pane-title-ur:focus{background:var(--mw-surface);border-color:var(--mw-line-strong)}
.pane-head__tools{display:flex;align-items:center;gap:var(--sp-2);flex:0 0 auto}

.btn--sm{height:30px;padding:0 var(--sp-3);font-size:13px}

/* A problem is rendered against the row that caused it — never as a wall of
   text somewhere else. `validate_draft` returns records naming their object
   precisely so this is possible. */
.mproblem{
  display:flex;align-items:flex-start;gap:var(--sp-2);
  margin:0 var(--sp-5) var(--sp-2);
  padding:var(--sp-2) var(--sp-3);
  background:var(--mw-signal-wash);
  border-radius:var(--r-sm);
  font-size:12.5px;line-height:1.5;color:var(--mw-signal);
}
.mproblem--section{margin:var(--sp-3) var(--sp-5) 0}
.mproblem__icon{flex:0 0 15px;margin-top:1px}

/* Pinned above the "add a dish" row rather than at the top of the list: the
   selection is usually finished a long way down. Absent until there is
   something to act on — the presence of the bar IS the message. */
.mbulk{display:none}
.menu-pane:has([data-pick]:checked) .mbulk,
.menu-pane.has-selection .mbulk{
  position:sticky;bottom:60px;z-index:3;
  display:flex;align-items:center;gap:var(--sp-2);
  margin:var(--sp-3) var(--sp-5) 0;
  padding:var(--sp-2) var(--sp-3);
  background:var(--mw-sunken);
  border-radius:var(--r-md);
  font-size:13px;
}
.mbulk__count{font-weight:600;margin-right:var(--sp-2)}
.mbulk__divider{width:1px;height:20px;background:var(--mw-line-strong);margin:0 var(--sp-1)}

/* The eight columns every row and the header share. */
.mlist__head,.mrow__grid{
  display:grid;
  grid-template-columns:20px 20px 36px minmax(0,1fr) 100px 92px 104px 24px 24px;
  align-items:center;
  gap:var(--sp-3);
}
.mlist__head{padding:var(--sp-3) var(--sp-5) var(--sp-2);border-bottom:1px solid var(--mw-line)}

/* The legend for the Status column. Quiet — it is a reference, not a message —
   and it wraps rather than scrolls, because a key nobody can finish reading is
   not a key. */
.mtips{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2) var(--sp-4);
  padding:var(--sp-2) var(--sp-5);
  background:var(--mw-paper);
  border-bottom:1px solid var(--mw-line);
  font-size:12px;color:var(--mw-slate);
}
.mtips__key{display:inline-flex;align-items:center;gap:var(--sp-2)}
.mtips__say{color:var(--mw-slate)}
/* The key is not a control, so it does not offer a control's hover. */
.mtips .avail{pointer-events:none}

/* An offer, not an instruction — so it is warm rather than loud, and it sits
   under the list because the person who scrolled to the bottom of a section is
   the person about to rearrange it. */
.mtipbar{
  display:flex;align-items:center;gap:var(--sp-2);
  padding:var(--sp-3) var(--sp-5);
  border-top:1px solid var(--mw-line);
  background:linear-gradient(90deg,var(--mw-coral-wash),transparent 60%);
  font-size:12.5px;color:var(--mw-ink-2);
}
.mtipbar__icon{flex:0 0 16px;display:flex;color:var(--mw-coral-700)}
.mtipbar b{font-weight:600;color:var(--mw-ink)}

/* ------------------------------------------------------------- the pager --- */

.mpager{
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-5);
  border-top:1px solid var(--mw-line);
  font-size:13px;color:var(--mw-ink-2);
}
.mpager__step{
  display:inline-flex;align-items:center;gap:var(--sp-1);
  height:30px;padding:0 var(--sp-3);
  border:1px solid var(--mw-line);border-radius:var(--r-md);
  font-weight:500;color:var(--mw-ink);
}
.mpager__step:hover{background:var(--mw-sunken)}
/* Present but dead at the ends, rather than disappearing: a control that moves
   under the pointer as you page is a control you stop trusting. */
.mpager__step.is-off{color:var(--mw-line-strong);border-color:var(--mw-line);pointer-events:none}
.mpager__where{color:var(--mw-slate)}
.mlist{padding:var(--sp-1) 0}
.mrow{border-bottom:1px solid var(--mw-line)}
.mrow:last-child{border-bottom:0}
.mrow__grid{padding:5px var(--sp-5)}
.mrow:hover{background:var(--mw-paper)}
/* `display:contents` so the form's fields sit in the row's own grid: the bulk
   checkbox belongs to a different form (via `form=`) and cannot be inside it. */
.mrow__form{display:contents}
/* A marked row keeps the paper it is on and takes a signal edge instead of a
   wash: the message underneath is already coloured, and two washes stacked
   turn one problem into a red block the eye stops reading. */
.mrow--flagged{box-shadow:inset 3px 0 0 var(--mw-signal)}
.mrow.mrow--dragging{opacity:.4}
.mrow.mrow--dropzone{box-shadow:inset 0 2px 0 var(--mw-coral)}

.mpick{width:14px;height:14px;accent-color:var(--mw-ink);cursor:pointer}

.mthumb{
  width:36px;height:36px;border-radius:var(--r-sm);overflow:hidden;
  background:var(--mw-sunken);
}
.mthumb img{width:36px;height:36px;object-fit:cover}
.mthumb__blank{
  display:flex;align-items:center;justify-content:center;
  width:36px;height:36px;
  color:var(--mw-line-strong);
}

.mnames{min-width:0;display:flex;flex-direction:column;gap:1px}
.mfield{
  width:100%;height:22px;padding:0 var(--sp-2);
  background:transparent;
  border:1px solid transparent;border-radius:var(--r-sm);
  font-size:13.5px;
}
.mfield:hover{border-color:var(--mw-line)}
.mfield:focus{background:var(--mw-surface);border-color:var(--mw-line-strong)}
.mfield::placeholder{color:var(--mw-slate)}
.mfield--ur{font-size:12.5px;line-height:1.4;color:var(--mw-ink-2);height:22px}
.mfield--price{text-align:right}

.mmoney{display:flex;align-items:center;gap:4px;min-width:0}
.mmoney__unit{flex:0 0 auto;font-size:11.5px;color:var(--mw-slate)}
.mmoney__unit--after{order:2}

.mchips{display:flex;flex-wrap:wrap;gap:4px;min-width:0}
.chip{
  display:inline-flex;align-items:center;
  height:20px;padding:0 var(--sp-2);
  border:1px solid var(--mw-line);border-radius:var(--r-pill);
  font-size:11.5px;color:var(--mw-ink-2);white-space:nowrap;
}
.mchips__none{color:var(--mw-line-strong)}

/* THE STATUS PILL — the one thing on this row that has to be readable at a
   glance, from a metre away, mid-shift.
 *
 * It was a 46px "On"/"Off" chip, which is legible only if you already know
 * what it means, and which said nothing about the difference between "sold out
 * today" and "not on the menu at all".
 *
 * So: a filled dot AND a word, never one without the other — colour alone
 * excludes the ~8% of men with red-green deficiency, and a word alone is a
 * thing you have to read rather than see. Fixed width so the dots line up into
 * a column the eye can run down without reading anything at all.
 *
 *   Available  jade    orderable now
 *   Sold out   amber   `is_available=False`, live, no publish needed
 *   Off menu   grey    `is_active=False`, applies at the next publish
 *
 * Available carries colour here even though the house rule reserves colour for
 * signals: a column of amber among green IS the signal, and it only reads as
 * one if the calm state is drawn too. */
.avail{
  display:inline-flex;align-items:center;gap:6px;
  height:26px;
  /* ASYMMETRIC ON PURPOSE, TO LOOK SYMMETRIC. A 7px dot and a 6px gap put 13px
     of drawn weight before the word; equal padding then reads as a gap on the
     right. 7px left against 11px right measures uneven and looks even, which
     is the one that matters. */
  padding:0 11px 0 7px;
  /* A GRID ITEM STRETCHES BY DEFAULT, and `inline-flex` does not stop it — the
     pill was being pulled to the full 104px of its column, which put the word
     hard against the left and left a gap on the right that read as a drawing
     mistake. `justify-self` is the property that governs this; `display` is
     not. Content-sized, and the dots still line up because every pill starts at
     the same column edge. */
  justify-self:start;
  border:1px solid var(--mw-line);border-radius:var(--r-pill);
  font-size:11.5px;font-weight:600;color:var(--mw-slate);
  white-space:nowrap;
}
.avail__dot{
  flex:0 0 7px;width:7px;height:7px;border-radius:50%;
  background:currentColor;
}
.avail--on{background:var(--mw-jade-wash);border-color:var(--mw-jade-wash);color:var(--mw-jade)}
.avail--soldout{background:var(--mw-amber-wash);border-color:var(--mw-amber);color:var(--mw-amber-700)}
.avail--hidden{background:var(--mw-sunken);border-color:var(--mw-line-strong);color:var(--mw-slate)}
/* The hit target grows on hover, the colour does not change: the pill is a
   STATE first and a button second, and a control that recolours under the
   pointer teaches people to distrust the colour. */
button.avail{cursor:pointer}
button.avail:hover{border-color:var(--mw-line-strong);box-shadow:0 0 0 3px var(--mw-sunken)}
.mrow--off .mfield--name{color:var(--mw-ink-2)}

/* OFF THE MENU. Greyed, not removed, and it keeps every control it had — see
   the row template's banner. The name goes light and the photo drops to half,
   so a scan of the section reads "these are the live ones" without counting. */
.mrow--hidden .mfield--name,.mrow--hidden .mfield--ur,.mrow--hidden .mmoney{color:var(--mw-slate)}
.mrow--hidden .mthumb{opacity:.5}

/* Neutral, not alarming: nothing is wrong, there is simply a step left. It
   borrows `.mproblem`'s shape so the eye files it in the same place, and the
   sunken wash rather than the signal one so it never competes with a real
   problem on the same row. */
.mnote{
  display:flex;align-items:flex-start;gap:var(--sp-2);
  margin:0 var(--sp-5) var(--sp-2);
  padding:var(--sp-2) var(--sp-3);
  background:var(--mw-sunken);
  border-radius:var(--r-sm);
  font-size:12.5px;line-height:1.5;color:var(--mw-ink-2);
}
.mnote__icon{flex:0 0 15px;margin-top:1px;color:var(--mw-slate)}
.mnote b{font-weight:600;color:var(--mw-ink)}

/* ---------------------------------------------------- the row overflow --- */

/* Reveals on hover or focus, and is ALWAYS present for a keyboard: a control
   that only exists under a pointer does not exist on a tablet either, and this
   screen is used on tablets. */
.mmore{display:flex;align-items:center;justify-content:center}
.mmore__trigger{
  display:flex;align-items:center;justify-content:center;
  width:24px;height:24px;border-radius:var(--r-sm);
  color:var(--mw-slate);opacity:0;transition:opacity var(--dur-micro) var(--ease);
}
.mrow:hover .mmore__trigger,.mmore__trigger:focus-visible,
.mmore__trigger[aria-expanded="true"]{opacity:1}
.mmore__trigger:hover{background:var(--mw-sunken);color:var(--mw-ink)}
@media (hover:none){.mmore__trigger{opacity:1}}

.rowmenu{width:268px;padding:var(--sp-1);text-align:left}
.rowmenu__item,.secmenu__item{
  display:flex;align-items:flex-start;gap:var(--sp-3);width:100%;
  padding:var(--sp-2) var(--sp-3);
  border-radius:var(--r-sm);
  font-size:13.5px;font-weight:500;text-align:left;color:var(--mw-ink);
}
.rowmenu__item em,.secmenu__item em{
  display:block;margin-top:1px;
  font-size:12px;font-weight:400;font-style:normal;line-height:1.4;
  color:var(--mw-slate);
}
.rowmenu__item .icon,.secmenu__item .icon{flex:0 0 17px;margin-top:2px;color:var(--mw-slate)}
.rowmenu__item:hover,.secmenu__item:hover{background:var(--mw-sunken)}
.rowmenu__item--danger:hover,.secmenu__item--danger:hover{background:var(--mw-signal-wash)}
.rowmenu__item--danger:hover,.rowmenu__item--danger:hover .icon,
.secmenu__item--danger:hover,.secmenu__item--danger:hover .icon{color:var(--mw-signal)}

/* ------------------------------------------------------- section menu --- */

.secmenu{width:296px;padding:var(--sp-1);text-align:left}
.secmenu__trigger{gap:var(--sp-2)}
.secmenu__item--off{cursor:default}
.secmenu__item--off,.secmenu__item--off .icon{color:var(--mw-line-strong)}
.secmenu__item--off:hover{background:transparent}
.secmenu__item--off em{color:var(--mw-line-strong)}
.secmenu__sub{padding:var(--sp-2) var(--sp-3)}
.secmenu__reorder{
  display:flex;align-items:center;justify-content:space-between;
  margin:var(--sp-1) 0;padding:var(--sp-2) var(--sp-3);
  border-top:1px solid var(--mw-line);border-bottom:1px solid var(--mw-line);
}

.mopen{
  display:flex;align-items:center;justify-content:center;
  width:24px;height:24px;border-radius:var(--r-sm);color:var(--mw-slate);
}
.mopen:hover{background:var(--mw-sunken);color:var(--mw-ink)}

.madd{
  position:sticky;bottom:0;z-index:4;border-bottom-left-radius:var(--r-lg);border-bottom-right-radius:var(--r-lg);
  display:flex;align-items:center;gap:var(--sp-2);
  margin-top:auto;
  padding:var(--sp-3) var(--sp-5);
  background:var(--mw-surface);
  border-top:1px solid var(--mw-line);
}
.madd__name{flex:1 1 auto;min-width:0}
.madd__ur{flex:0 0 150px}
.madd__price{width:80px;text-align:right}

.minput{
  width:100%;height:32px;padding:0 var(--sp-2);
  background:var(--mw-surface);
  border:1px solid var(--mw-line-strong);border-radius:var(--r-sm);
  font-size:13.5px;
}
.minput::placeholder{color:var(--mw-slate)}
.minput:hover{border-color:var(--mw-slate)}
.minput--area{height:auto;padding:var(--sp-2);line-height:1.5;resize:vertical}
.minput--select{padding-right:var(--sp-1)}
.minput--tight{height:28px;font-size:13px}
.mfield-error{margin-top:4px;font-size:12px;color:var(--mw-signal)}

/* --------------------------------------------------------- side panel --- */

.mpanel{
  position:fixed;top:0;right:0;z-index:60;
  display:flex;flex-direction:column;
  width:480px;max-width:100vw;height:100vh;
  background:var(--mw-surface);
  border-left:1px solid var(--mw-line);
  box-shadow:var(--shadow-pop);
  /* The popover's rise, reused rather than a new motion: it says the same
     thing (this floats above the page and will go away). */
  animation:pop-in var(--dur-micro) var(--ease) both;
}
.mpanel__head{
  flex:0 0 auto;
  display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-4) var(--sp-5);
  border-bottom:1px solid var(--mw-line);
}
.mpanel__heading{min-width:0}
.mpanel__title{font-size:17px;font-weight:600;letter-spacing:-.01em;margin-top:2px}
.mpanel__title-ur{font-size:14px;color:var(--mw-ink-2)}
.mpanel__close{
  flex:0 0 30px;
  display:flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:var(--r-sm);color:var(--mw-slate);
}
.mpanel__close:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.mpanel__body{flex:1 1 auto;overflow-y:auto}
.mpanel__block{padding:var(--sp-4) var(--sp-5);border-bottom:1px solid var(--mw-line)}
.mpanel__block--last{border-bottom:0}
.mpanel__lede{margin-top:var(--sp-2)}

.mform{margin-top:var(--sp-3)}
.mform > * + *{margin-top:var(--sp-3)}
/* The stacking rule above is scoped to the form's own children: inside a
   two-column row it would push the right-hand field down by 12px and leave two
   labels that are meant to be a pair sitting at different heights. */
.mform__grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3);align-items:start}
.mform__label{display:block;margin-bottom:4px;font-size:12.5px;font-weight:500;color:var(--mw-ink-2)}
.mform__row--check{display:flex;flex-direction:column;justify-content:center}
.mcheck{display:flex;align-items:center;gap:var(--sp-2);font-size:13.5px;cursor:pointer}
.mcheck input{width:14px;height:14px;accent-color:var(--mw-ink)}
.mnewgroup{margin-top:var(--sp-5);padding-top:var(--sp-4);border-top:1px solid var(--mw-line)}
.mnewgroup .btn{margin-top:var(--sp-3)}

.mphoto{display:flex;gap:var(--sp-4);margin-top:var(--sp-3)}
.mphoto__frame{
  flex:0 0 148px;height:104px;border-radius:var(--r-md);overflow:hidden;
  background:var(--mw-sunken);
}
.mphoto__frame img{width:148px;height:104px;object-fit:cover}
.mphoto__blank{
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;
  width:100%;height:100%;color:var(--mw-slate);font-size:12px;
}
.mphoto__side{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:var(--sp-2);align-items:flex-start}
.mphoto__pick{cursor:pointer}

.mattached{margin-top:var(--sp-3)}
.mattached__row{
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-2) 0;border-bottom:1px solid var(--mw-line);
}
.mattached__name{font-size:13.5px;font-weight:500;display:flex;flex-direction:column}
.mattached__tools{display:flex;align-items:center;gap:var(--sp-2);flex:0 0 auto}
.mlink{font-size:12.5px;color:var(--mw-ink-2);text-decoration:underline;text-underline-offset:3px}
.mlink:hover{color:var(--mw-ink)}
.mdanger{color:var(--mw-signal)}
button.mdanger:hover,a.mdanger:hover{color:var(--mw-signal)}

.mgrid{margin-top:var(--sp-4)}
/* Fixed columns: the grid is read across a row, and a table that re-measures
   itself per section would put "Medium" under a different heading than
   "Large". */
.mgrid__table{width:100%;table-layout:fixed;border-collapse:collapse;font-size:13px}
.mgrid__table col.mgrid__c-choice{width:78px}
.mgrid__table col.mgrid__c-offered{width:70px}
.mgrid__table col.mgrid__c-mode{width:96px}
.mgrid__table col.mgrid__c-price{width:auto}
.mgrid__table col.mgrid__c-today{width:56px}
.mgrid__table th{
  text-align:left;font-size:10.5px;font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;color:var(--mw-slate);
  padding:0 var(--sp-2) var(--sp-2) 0;white-space:nowrap;
}
.mgrid__table td{padding:3px 4px 3px 0;vertical-align:middle}
.mgrid__tick{text-align:center}
.mgrid__tick input{width:14px;height:14px;accent-color:var(--mw-ink)}
.mgrid__group th{
  padding-top:var(--sp-3);color:var(--mw-ink);
  border-top:1px solid var(--mw-line);
}
.mgrid__choice{display:block}
.mgrid__choice-ur{display:block;font-size:12px;color:var(--mw-slate)}
.mgrid__when{display:block;font-size:11.5px;color:var(--mw-slate)}
.mgrid__foot{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--sp-3);margin-top:var(--sp-3)}
.mgrid__foot .t-meta{flex:1 1 auto}

.mattach{display:flex;align-items:center;gap:var(--sp-2);margin-top:var(--sp-4)}

.moptions{margin-top:var(--sp-3)}
.moption{display:flex;align-items:center;gap:var(--sp-2);padding:2px 0}
.moption__form{display:flex;align-items:center;gap:var(--sp-2);flex:1 1 auto;min-width:0}
.moption__form .mfield{border-color:var(--mw-line)}

.mlibrary{margin-top:var(--sp-3)}
.mlibrary__row + .mlibrary__row{border-top:1px solid var(--mw-line)}
.mlibrary__link{
  display:flex;align-items:center;gap:var(--sp-3);
  padding:var(--sp-3) var(--sp-2);border-radius:var(--r-sm);
  color:var(--mw-ink);
}
.mlibrary__link:hover{background:var(--mw-sunken)}
.mlibrary__name{flex:0 0 auto;max-width:44%;font-size:13.5px;font-weight:500;display:flex;align-items:baseline;gap:var(--sp-2)}
.mlibrary__ur{font-size:12.5px;color:var(--mw-slate)}
.mlibrary__meta{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mlibrary__uses{flex:0 0 auto;text-align:right;white-space:nowrap}
.mlibrary__link svg{color:var(--mw-slate)}

/* ============================================================= order queue ==
 *
 * The screen a restaurant lives on for twelve hours. Two reading distances:
 * order numbers and clocks are read from the other side of a kitchen (.num-lg
 * and .num-xl); what is IN an order is read at the counter with the ticket in
 * front of you. Nothing on this screen is sized in between, because a size in
 * between serves neither.
 *
 * THE AGEING SPINE — the one design decision here that is not negotiable.
 *
 * Every ticket carries a track down its left edge that fills as its own promise
 * elapses, and THE SPINE IS THE ONLY PART OF A TICKET THAT TAKES A COLOUR. A
 * wall of coloured cards is unreadable at a glance and turns a rush into noise;
 * a row of filling spines is scannable in one saccade, because the eye is
 * comparing lengths rather than decoding hues. If a future edit wants to tint a
 * card — a red border for late, an amber wash for busy — that is this rule
 * being broken, and the rule is the feature.
 *
 * Three bands, and the first of them is deliberately achromatic:
 *   calm  (<70%)   ink. The ticket is simply filling up. Nothing is wrong, so
 *                  nothing is coloured — a board where every ticket is coloured
 *                  is a board where colour has stopped meaning anything.
 *   due   (>=70%)  amber. The promise is close.
 *   late  (>=100%) signal red. The promise has gone.
 *
 * The ONE place that colour is repeated away from the spine is the clock
 * numeral of a late ticket, and it is repeated because the two reading
 * distances are two different jobs: the spine finds the ticket from across the
 * room, the numeral confirms it once you are standing there. The card's
 * surface, border, text and everything else stay ink on paper in every state.
 *
 * Surfaces: paper page, sunken lane, white ticket, hairlines throughout. That
 * is what gives the board structure without spending a single colour on it.
 */

/* THE LANES SCROLL, THE PAGE DOES NOT. A merchant must never have to scroll the
   window to find out what is at the bottom of the board — during a rush that is
   how an order goes unseen.
 *
 * Pinned from the shell rather than with a `calc(100vh - …)` on the board: a
 * height computed from the viewport has to know about the 56px top bar AND the
 * state bar, which appears and disappears on its own, and it would be wrong by
 * 40px every time the store is paused or the connection drops. Constraining the
 * grid instead lets the existing flex chain do the arithmetic.
 *
 * Scoped with `:has` so no other screen is affected. A browser without `:has`
 * falls back to the page scrolling, which is exactly what every other screen in
 * the product does — degraded, never broken. */
.shell:has(.main--queue){height:100vh}
.shell:has(.main--queue) .canvas{min-height:0}
.main--queue{
  display:flex;flex-direction:column;min-height:0;
  max-width:none;
  padding:var(--sp-4) var(--sp-6) var(--sp-5);
}

/* THE ALERT BAR. Not a toast — a toast is a message that goes away, and this is
   a message about a screen that has stopped being able to shout. It is the
   loudest thing on the queue when it is present, and it is absent the rest of
   the time, which is what makes it loud.

   It sits above the queue bar and pushes it down rather than floating over it:
   nothing on this screen may cover a ticket. */
.alertbar{
  display:flex;align-items:center;gap:var(--sp-3);flex:0 0 auto;
  min-height:44px;padding:var(--sp-2) var(--sp-4);margin-bottom:var(--sp-3);
  border:1px solid var(--mw-line);border-radius:var(--r-md);
  font-size:13.5px;line-height:1.45;
}
.alertbar[hidden]{display:none}
.alertbar__label{font-weight:600;white-space:nowrap}
.alertbar__spacer{flex:1 1 auto}
/* Muted, or not unlocked yet: working, but not the way you would assume. The
   same amber the product uses for exactly that everywhere else. */
.alertbar--warn{background:var(--mw-amber-wash);border-color:var(--mw-amber);color:var(--mw-ink)}
/* Asked, after a real gesture, and refused. This screen cannot make a noise and
   the merchant has to know before an order arrives, not after one is missed. */
.alertbar--error{background:var(--mw-signal-wash);border-color:var(--mw-signal);color:var(--mw-ink)}

/* ------------------------------------------------- the incoming order ----- */

/* A new order is an incoming CALL, not a notification — so this is the loudest
   thing the product ever puts on screen, and it is still a bar rather than a
   modal. It pushes the board down instead of covering it, every other control
   on the page stays live behind it, and nothing dismisses it but Accept or
   Decline.

   Ink, not coral. The one surface allowed to be this prominent earns it by
   SIZE and POSITION, not by shouting in colour — and the board below it keeps
   the whole of the palette's meaning for the ageing rail, which is the thing a
   merchant reads all day. */
.incoming{
  display:flex;align-items:center;gap:var(--sp-4);flex:0 0 auto;
  min-height:60px;padding:var(--sp-3) var(--sp-4);
  background:var(--mw-ink);color:var(--mw-paper);
  border-radius:var(--r-md);
  box-shadow:var(--shadow-pop);
}
/* MOTION 5 — it slides DOWN from the top of the application, because a thing
   that simply appeared at full size reads as something that was always there.
   Applied by `alerts.js` only when the ORDER changes, so the ten-second
   refresh cannot re-slide a banner somebody is mid-way through reading. */
@keyframes incoming-in{
  from{transform:translateY(-100%);opacity:0}
  to{transform:translateY(0);opacity:1}
}
.incoming.is-entering{animation:incoming-in var(--dur-panel) var(--ease) both}

/* The one pulse in the product. It carries information — this is live and
   unanswered — and it is the only thing that keeps saying so once the ringtone
   has been muted. */
@keyframes incoming-pulse{
  0%,100%{opacity:1;transform:scale(1)}
  50%{opacity:.35;transform:scale(.72)}
}
.incoming__pulse{
  width:10px;height:10px;border-radius:50%;flex:0 0 10px;
  background:var(--mw-coral);
  animation:incoming-pulse 1.6s var(--ease) infinite;
}

/* The whole identity block is one link to the ticket's panel — selecting the
   banner focuses the order it names, which is always the oldest unanswered. */
.incoming__open{
  display:flex;align-items:center;gap:var(--sp-4);color:inherit;min-width:0;
}
.incoming__what{
  display:flex;align-items:baseline;gap:var(--sp-2);
  font-size:13px;font-weight:600;white-space:nowrap;
}
.incoming__no{color:var(--mw-paper)}
.incoming__who{display:flex;flex-direction:column;gap:2px;min-width:0}
.incoming__name{font-size:14px;font-weight:600}
.incoming__summary,.incoming__since{
  font-size:12.5px;color:var(--mw-line-strong);
}
.incoming__more{font-size:12.5px;font-weight:400;color:var(--mw-line-strong)}
.incoming__since,.incoming__more{white-space:nowrap}
.incoming__spacer{flex:1 1 auto}
/* On ink, the ghost button has to invert — the shared `.btn--ghost` is drawn
   for paper. */
.incoming__decline{
  background:transparent;color:var(--mw-paper);border-color:var(--mw-slate);
}
.incoming__decline:hover{background:var(--mw-ink-2);color:var(--mw-paper)}

@media (prefers-reduced-motion:reduce){
  /* REDUCED MOTION CHANGES HOW, NEVER WHETHER. Without the slide and the pulse
     this bar would still be a black bar across the top of the screen, which is
     already the loudest thing here — and the coral dot stays, held still. */
  .incoming__pulse{opacity:1;transform:none}
}

.queue{display:flex;flex-direction:column;gap:var(--sp-3);flex:1 1 auto;min-height:0}

/* -------------------------------------------------------------- queue bar -- */

.qbar{display:flex;align-items:center;gap:var(--sp-4);flex:0 0 auto}
.qbar__spacer{flex:1 1 auto}
.qbar__divider{width:1px;height:22px;background:var(--mw-line);flex:0 0 1px}

/* Two counts, and only two. "Waiting" is the number that goes wrong by being
   ignored; "late" is the number a customer is already feeling. A zero is drawn
   in the hairline colour rather than hidden: the position is part of how the
   number is read quickly, and a count that moves around cannot be. */
.qstat{display:flex;align-items:baseline;gap:6px}
.qstat .num{color:var(--mw-line-strong)}
.qstat.is-on .num{color:var(--mw-ink)}
.qstat--late.is-on .num{color:var(--mw-signal)}
.qstat__label{font-size:12.5px;color:var(--mw-slate)}

/* The stale mark. It appears only when the board has stopped being current, and
   it says so in words rather than by removing anything: nothing on this screen
   ever empties the queue, because a board that blanked itself would read as a
   quiet night. Amber while we are still trying, signal red once we are not
   getting through — the same two meanings those tokens carry everywhere else. */
.qstale{
  display:flex;align-items:center;gap:6px;
  font-size:12.5px;color:var(--mw-slate);
}
.qstale__dot{
  width:7px;height:7px;border-radius:50%;flex:0 0 7px;
  background:var(--mw-amber);
}
.qstale[data-connection="offline"] .qstale__dot{background:var(--mw-signal)}

.qfilter{display:flex;align-items:center;gap:var(--sp-2);font-size:13px;color:var(--mw-slate)}
.qfilter strong{color:var(--mw-ink)}
.qfilter__clear{display:flex;color:var(--mw-slate)}
.qfilter__clear:hover{color:var(--mw-ink)}

/* Muted has to be VISIBLE. A silent queue that looks live is the trap this
   control exists to avoid, so "off" takes the amber wash the product already
   uses for "still running, but not the way you would assume". */
.qsound,.qkeys{
  display:inline-flex;align-items:center;gap:var(--sp-2);
  height:30px;padding:0 var(--sp-3);
  border:1px solid var(--mw-line);border-radius:var(--r-pill);
  color:var(--mw-slate);font-size:12.5px;font-weight:500;white-space:nowrap;
}
.qsound:hover,.qkeys:hover{background:var(--mw-sunken);color:var(--mw-ink-2)}
.qsound__off{display:none}
.qsound[aria-pressed="true"]{
  background:var(--mw-amber-wash);border-color:var(--mw-amber);
  color:var(--mw-amber-700);font-weight:600;
}
.qsound[aria-pressed="true"] .qsound__on{display:none}
.qsound[aria-pressed="true"] .qsound__off{display:flex}

.keycard{width:296px;padding:var(--sp-4)}
.keylist{
  display:grid;grid-template-columns:auto minmax(0,1fr);
  gap:6px var(--sp-3);align-items:center;
  margin-top:var(--sp-2);font-size:12.5px;color:var(--mw-ink-2);
}
.keylist dt{display:flex}
.keylist dd{margin:0}
.keylist kbd,.btn__key{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:20px;height:19px;padding:0 5px;
  border-radius:var(--r-sm);
  font-family:var(--mw-mono);font-size:11px;line-height:1;
}
.keylist kbd{border:1px solid var(--mw-line-strong);color:var(--mw-slate)}
/* Mixed from the button's own text colour rather than a new token, so the hint
   sits on a primary fill and on a quiet one without either being spelled out. */
.btn__key{
  margin-left:2px;
  background:color-mix(in srgb,currentColor 20%,transparent);
}
.keycard__foot{margin-top:var(--sp-3);padding-top:var(--sp-3);border-top:1px solid var(--mw-line)}

/* ----------------------------------------------------------- the board ----- */

.qboard{
  flex:1 1 auto;min-height:0;
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--sp-3);
}
.lane{
  display:flex;flex-direction:column;min-height:0;
  background:var(--mw-sunken);
  border-radius:var(--r-lg);
}
.lane__head{
  flex:0 0 auto;
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
  padding:var(--sp-3) var(--sp-3) var(--sp-2);
}
.lane__title{
  font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mw-slate);
}
.lane__count{color:var(--mw-slate)}
.lane__list{
  flex:1 1 auto;min-height:0;overflow-y:auto;
  display:flex;flex-direction:column;gap:var(--sp-2);
  padding:0 var(--sp-2) var(--sp-2);
}
.lane__empty{padding:var(--sp-3) var(--sp-2);font-size:12.5px;color:var(--mw-line-strong)}
.lane__group{
  display:flex;align-items:center;justify-content:space-between;
  margin-top:var(--sp-2);padding:var(--sp-3) var(--sp-1) 0;
  border-top:1px solid var(--mw-line-strong);
  font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mw-slate);
}

/* --------------------------------------------------------- the ticket ------ */

.tik{
  position:relative;flex:0 0 auto;
  background:var(--mw-surface);
  border:1px solid var(--mw-line);
  border-radius:var(--r-md);
  padding:var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-5);
}
.tik__spine{
  position:absolute;left:0;top:0;bottom:0;width:8px;
  background:var(--mw-sunken);
  border-radius:var(--r-md) 0 0 var(--r-md);
  overflow:hidden;
}
/* Fills from the bottom up: a level rising toward full reads as "time running
   out" without a legend, which a bar draining downward does not. */
.tik__fill{
  position:absolute;left:0;right:0;bottom:0;
  height:calc(var(--fill,0) * 100%);
  background:var(--mw-ink-2);
}
.tik--due .tik__fill{background:var(--mw-amber)}
.tik--late .tik__fill{background:var(--mw-signal)}
.tik--done .tik__fill{background:var(--mw-line-strong)}

.tik__open{display:block}
.tik__head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-2)}
.tik__clock{color:var(--mw-ink)}
.tik--late .tik__clock{color:var(--mw-signal)}
.tik__clock--quiet{font-size:12px;font-weight:500;color:var(--mw-slate)}
.tik__who{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-2);
  margin-top:1px;
}
.tik__name{
  font-size:13px;font-weight:500;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.tik__count{flex:0 0 auto;color:var(--mw-slate)}

.tik__lines{margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--mw-line)}
.tik__line{display:flex;gap:var(--sp-2);padding:1px 0;font-size:12.5px;line-height:1.4}
.tik__qty{flex:0 0 auto;color:var(--mw-ink)}
.tik__item{min-width:0;color:var(--mw-ink-2)}
.tik__opts{display:block;font-size:11.5px;color:var(--mw-slate)}
.tik__more{padding-top:2px;font-size:11.5px;color:var(--mw-slate)}
.tik__reason{margin-top:var(--sp-2);font-size:12.5px;color:var(--mw-slate)}

/* A note is the one thing on a ticket that ruins an order by being missed, so
   it is never behind a click and never the same weight as the dish list. */
.tik__note{
  margin-top:var(--sp-2);padding:5px var(--sp-2);
  background:var(--mw-sunken);border-radius:var(--r-sm);
  font-size:12px;line-height:1.45;color:var(--mw-ink-2);
}
.tik__extended{margin-top:var(--sp-2);font-size:11.5px;color:var(--mw-slate)}

/* THE COUNTDOWN AND ITS CONSEQUENCE. Quiet type, because the card already
   carries an ageing spine and a clock the size of a house number, and a third
   loud thing would leave the eye nowhere to land. What it says is the point,
   not how big it is. */
.tik__esc{margin-top:var(--sp-2);font-size:11.5px;line-height:1.5;color:var(--mw-slate)}
.tik__esc-on{display:none}
.tik__esc[data-escalated="yes"] .tik__esc-off{display:none}
/* Past the threshold it has actually happened, so it takes the one colour this
   product uses for "somebody else is now involved". */
.tik__esc[data-escalated="yes"]{color:var(--mw-amber-700)}
.tik__esc[data-escalated="yes"] .tik__esc-on{display:inline}

.tik__acts{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2);margin-top:var(--sp-3)}
.tik__acts form{display:flex}
.tik__plus{padding:0 var(--sp-2);min-width:40px}
.tik__waiting{
  display:flex;align-items:center;gap:6px;margin-top:var(--sp-3);
  font-size:12px;color:var(--mw-slate);
}

/* Keyboard selection. Ink, not colour: which ticket the keyboard is on is a
   fact about the cursor, not about the order. */
.tik.is-selected{border-color:var(--mw-ink);box-shadow:inset 0 0 0 1px var(--mw-ink)}

/* MOTION 4 — the one motion this screen adds, and the only new-order signal
   that works when the sound is muted. A ticket that simply appeared would be
   indistinguishable from one that had been there all along, which is the exact
   failure a queue cannot have. Reduced-motion is honoured globally above. */
@keyframes tik-arrive{
  from{opacity:0;transform:translateY(-8px)}
  to{opacity:1;transform:none}
}
.tik.is-arriving{animation:tik-arrive 260ms var(--ease) both}

/* REDUCED MOTION CHANGES HOW, NEVER WHETHER. The global rule near the top of
   this file collapses every animation to nothing, which would leave a merchant
   who has asked for less motion with no arrival signal at all — and if they
   have also muted the sound, with nothing whatsoever. So the motion becomes a
   MARK: the same information, held still. */
@media (prefers-reduced-motion:reduce){
  .tik.is-arriving{box-shadow:inset 0 0 0 2px var(--mw-coral)}
}

/* ------------------------------------------------------- the side panel ---- */

/* Inset below the top bar rather than covering it. The menu editor's panel can
   take the whole edge — nothing above it changes while you price a dish — but
   this screen's chrome carries the store status and the connection state, and
   both of those are things a merchant must not lose sight of because they
   opened one order. */
.qpanel{width:420px;top:var(--topbar-h);height:calc(100vh - var(--topbar-h))}
.qpanel__clock{display:flex;flex-direction:column;gap:var(--sp-3)}
.qclock{display:flex;align-items:baseline;gap:var(--sp-3)}
.qclock__value{color:var(--mw-ink)}
.tik--late .qclock__value{color:var(--mw-signal)}
.qclock__label{font-size:12.5px;color:var(--mw-slate)}
/* The board's spine, laid on its side. Same measurement, same three bands —
   it must not look like a different fact rendered a different way. */
.qtrack{
  position:relative;display:block;height:6px;
  background:var(--mw-sunken);border-radius:var(--r-pill);overflow:hidden;
}
.qtrack__fill{
  position:absolute;left:0;top:0;bottom:0;
  width:calc(var(--fill,0) * 100%);
  background:var(--mw-ink-2);
}
.tik--due .qtrack__fill{background:var(--mw-amber)}
.tik--late .qtrack__fill{background:var(--mw-signal)}

.qrow{margin-top:2px;font-size:13.5px}
.qrow--lead{font-size:16px;font-weight:600;letter-spacing:-.01em}
.qnote{
  margin-top:var(--sp-3);padding:var(--sp-2) var(--sp-3);
  background:var(--mw-sunken);border-radius:var(--r-sm);
  font-size:13px;line-height:1.5;color:var(--mw-ink-2);
}
/* Where a call button would be. Quiet, and deliberately NOT styled like a
   warning: the customer's number not being here is how the product works, not
   a problem it is apologising for. Small, because a merchant needs to read it
   once — and present, because an unexplained absence reads as a missing
   feature and somebody eventually builds it. */
.qcontact{margin-top:var(--sp-3);font-size:12px;line-height:1.5;color:var(--mw-slate)}

.qlines{margin-top:var(--sp-2)}
.qline{display:flex;gap:var(--sp-3);padding:var(--sp-2) 0;border-bottom:1px solid var(--mw-line)}
.qline:last-child{border-bottom:0}
.qline__qty{flex:0 0 32px;color:var(--mw-ink)}
.qline__body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}
.qline__name{font-size:13.5px;font-weight:500}
.qline__ur{font-size:13px;color:var(--mw-ink-2)}
.qline__opt{font-size:12px;color:var(--mw-slate)}
.qline__note{font-size:12px;font-weight:500;color:var(--mw-ink-2)}
.qline__money{flex:0 0 auto;text-align:right;color:var(--mw-ink-2)}

.qtotals{margin-top:var(--sp-3);padding-top:var(--sp-3);border-top:1px solid var(--mw-line-strong)}
.qtotal{display:flex;align-items:baseline;justify-content:space-between;padding:2px 0;font-size:13px;color:var(--mw-slate)}
.qtotal--sum{padding-top:var(--sp-2);font-weight:600;color:var(--mw-ink)}
.qpay{margin-top:var(--sp-2)}

.qtimes{display:grid;grid-template-columns:auto minmax(0,1fr);gap:4px var(--sp-4);margin-top:var(--sp-2);font-size:13px}
.qtimes dt{color:var(--mw-slate)}
.qtimes dd{margin:0}
.qdeclined{
  margin-top:var(--sp-3);padding:var(--sp-2) var(--sp-3);
  background:var(--mw-signal-wash);border-radius:var(--r-sm);
  font-size:12.5px;line-height:1.5;color:var(--mw-signal);
}

.qpanel__foot{
  flex:0 0 auto;
  display:flex;align-items:center;gap:var(--sp-2);
  padding:var(--sp-3) var(--sp-5);
  border-top:1px solid var(--mw-line);
}
.qpanel__spacer{flex:1 1 auto}

/* ----------------------------------------------------------- declining ----- */

.qreject{background:var(--mw-sunken)}
.qreject__lede{margin-top:var(--sp-2)}
.qreasons{display:flex;flex-direction:column;gap:2px;margin-top:var(--sp-3)}
.qreason{
  display:flex;align-items:center;gap:var(--sp-2);
  padding:var(--sp-2) var(--sp-3);
  background:var(--mw-surface);border-radius:var(--r-sm);
  font-size:13.5px;cursor:pointer;
}
.qreason:hover{background:var(--mw-paper)}
.qreason input{width:15px;height:15px;accent-color:var(--mw-ink)}
.qreason:has(input:checked){font-weight:600}
.qreject__note,.qreject__86{margin-top:var(--sp-3)}
.qreject__86 .mcheck{margin-top:var(--sp-2)}
.qreject__acts{display:flex;justify-content:flex-end;gap:var(--sp-2);margin-top:var(--sp-4)}

/* THE BOARD'S PRIMARY ACTION IS INK, NOT CORAL.
 *
 * `.btn--primary` is the product's primary fill everywhere else and it is
 * right everywhere else. On this screen it is wrong: five coral Accept buttons
 * down the New lane put a second colour on the card and start competing with
 * the spines for the same glance, which is the exact failure the spine exists
 * to prevent. So the queue expresses "this is the action" with CONTRAST rather
 * than hue — a near-black fill on white paper, which is louder than coral at a
 * distance and carries no signal meaning at all.
 *
 * Colour on this screen means one thing: how late something is. */
.btn--act{background:var(--mw-ink);color:var(--mw-surface)}
.btn--act:hover{background:var(--mw-ink-2)}

/* Destructive, and one of the three things tokens.css reserves colour for. */
.btn--danger{background:var(--mw-signal);color:var(--mw-surface)}
.btn--danger:hover{background:color-mix(in srgb,var(--mw-signal) 88%,var(--mw-ink))}

/* --------------------------------------------------------------- narrow ---- */

@media (max-width:1180px){
  .qboard{gap:var(--sp-2)}
  .tik{padding:var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-4)}
}

/* A lane scrolls a lot, all shift. The default bar is a 15px grey slab that
   eats a fifteenth of a 250px column and adds a colour the design does not
   have; this is a hairline that appears against the lane it sits in. */
.lane__list{scrollbar-width:thin;scrollbar-color:var(--mw-line-strong) transparent}
.lane__list::-webkit-scrollbar{width:6px}
.lane__list::-webkit-scrollbar-track{background:transparent}
.lane__list::-webkit-scrollbar-thumb{background:var(--mw-line-strong);border-radius:var(--r-pill)}

/* ============================================================== dashboard ==
 *
 * The first screen of the day and the check-in during service. It answers four
 * questions — is anything wrong, how is today going, what is happening this
 * minute, is the store open — and it stops there. There is no chart in this
 * section and none is to be added: a trend line is not something anybody reads
 * while a kitchen is full, and it is the first step toward the ERP this product
 * is deliberately not.
 *
 * Colour, same rule as everywhere: it is a signal. Exactly three things on this
 * screen may take one — the severity edge on a problem, the ageing spine on a
 * live row, and the clock numeral of a ticket that is already late. The cards,
 * the figures and the labels are ink on paper in every state. "Nothing is
 * wrong" is deliberately NOT green: a permanently lit reassurance is decoration,
 * and this product has said so since the connection dot.
 *
 * Vertical budget at 1366x768: 56px top bar, ~24px page padding, an attention
 * band that is one row when things are calm, and two cards that are their own
 * height rather than stretched to fill. A card padded out with empty space to
 * reach the fold reads as a screen with nothing to say.
 */

.dash{display:flex;flex-direction:column;gap:var(--sp-4)}

/* ------------------------------------------------- is anything wrong ------ */

.dash__attention{display:flex;flex-direction:column;gap:var(--sp-2)}

/* One row per problem, and one row saying so when there are none. The row is
   ink on paper like every other surface; SEVERITY is an inset edge and a
   coloured mark, the same idiom the menu editor already uses for a dish that
   blocks a publish. */
.arow{
  position:relative;
  display:flex;align-items:flex-start;gap:var(--sp-3);
  /* Tight on purpose. Three of these can be on screen at once, above a state
     bar that is itself 40px, and every band of chrome is a ticket the merchant
     cannot see. */
  padding:var(--sp-2) var(--sp-4);
  background:var(--mw-surface);
  border:1px solid var(--mw-line);
  border-radius:var(--r-md);
}
.arow__mark{flex:0 0 18px;display:flex;margin-top:1px;color:var(--mw-slate)}
.arow__body{flex:1 1 auto;min-width:0}
.arow__headline{font-size:14px;font-weight:600;letter-spacing:-.005em}
.arow__detail{margin-top:1px;font-size:12.5px;line-height:1.5;color:var(--mw-slate)}
.arow__go{flex:0 0 auto;align-self:center}

/* Amber: running, but not the way you would assume — an order nobody has
   answered yet. Signal: it has already gone wrong for a customer. */
.arow--warn{box-shadow:inset 3px 0 0 var(--mw-amber)}
.arow--warn .arow__mark{color:var(--mw-amber-700)}
.arow--signal{box-shadow:inset 3px 0 0 var(--mw-signal)}
.arow--signal .arow__mark{color:var(--mw-signal)}
/* A state the merchant chose (paused, closed) is not a fault, so it carries no
   colour at all — the state bar above already owns that signal. */
.arow--quiet .arow__mark{color:var(--mw-slate)}

/* ------------------------------------------------------------- the cards -- */

/* Two cards, and they end level. The day's figures are the shorter of the two;
   they stay at the top of their card and the card keeps the height, because
   spreading two numbers down a column to fill it leaves them floating with no
   relationship to each other or to the line that explains them. Air below a
   figure is calm. A figure adrift in the middle of a panel is not. */
.dash__grid{
  display:grid;
  grid-template-columns:360px minmax(0,1fr);
  gap:var(--sp-4);
  align-items:stretch;
}
.dash__today,.dash__now{
  display:flex;flex-direction:column;
  padding:var(--sp-4) var(--sp-5) var(--sp-5);
}
.dash__head{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-3);
  margin-bottom:var(--sp-4);
}
.dash__more{
  display:inline-flex;align-items:center;gap:4px;
  font-size:12.5px;font-weight:500;color:var(--mw-ink-2);
}
.dash__more:hover{color:var(--mw-ink)}

/* WHAT AN ABSENT NUMBER LOOKS LIKE. "No orders yet today" is set at the weight
   a figure would have been, in the place a figure would have been, because it
   is the answer to the same question — not an apology for the screen being
   empty. It is never `0`, and it is never `Rs 0`. */
.dash__none{font-size:15px;font-weight:600;letter-spacing:-.01em}
.dash__note{margin-top:var(--sp-2);font-size:12.5px;line-height:1.55;color:var(--mw-slate)}

.fig{display:flex;align-items:baseline;gap:var(--sp-3)}
.fig + .fig{
  margin-top:var(--sp-4);padding-top:var(--sp-4);
  border-top:1px solid var(--mw-line);
}
.fig__value{line-height:1.05}
/* A unit, not a digit. At the figure's own size and weight "Rs" competes with
   the number a merchant is actually reading. */
.fig__unit{
  margin-right:6px;
  font-size:16px;font-weight:500;letter-spacing:0;color:var(--mw-slate);
}
.fig__label{font-size:12.5px;color:var(--mw-slate)}

/* ------------------------------------------------ what is happening now --- */

/* The three live lanes, in the order work travels through them. A zero is drawn
   in the hairline colour rather than hidden, exactly as the queue bar draws its
   counts: the position is part of how the number is read quickly, and a count
   that moved around could not be. */
.lstrip{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--sp-3);
  padding-bottom:var(--sp-4);border-bottom:1px solid var(--mw-line);
}
.lstrip__item{display:flex;flex-direction:column;gap:2px}
.lstrip__count{color:var(--mw-line-strong)}
.lstrip__item.is-on .lstrip__count{color:var(--mw-ink)}
.lstrip__label{
  font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mw-slate);
}

/* A row is a LINK INTO the queue, never a copy of a ticket: no dish list, no
   customer, no buttons. It carries the board's own spine, narrowed, because the
   ageing rail is the one thing a merchant reads across every screen and it must
   not be a different shape here. */
.frows{margin-top:var(--sp-3)}
.frow{position:relative;border-bottom:1px solid var(--mw-line)}
.frow:last-child{border-bottom:0}
.frow:hover{background:var(--mw-paper)}
.frow .tik__spine{top:7px;bottom:7px;width:4px;border-radius:var(--r-pill)}
/* Sized so that the worst case still fits: five rows UNDER a state bar, three
   attention rows and a lane strip, at 768px, with nothing pushed off the
   bottom. The screen that tells a merchant something is wrong is exactly the
   screen that must not need scrolling to say it. */
.frow__open{
  display:flex;align-items:center;gap:var(--sp-3);
  padding:var(--sp-2) var(--sp-1) var(--sp-2) var(--sp-4);
}
.frow__lane{
  font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mw-slate);
}
.frow__items{white-space:nowrap}
.frow__spacer{flex:1 1 auto}

@media (max-width:1080px){
  .dash__grid{grid-template-columns:minmax(0,1fr)}
}


/* ============================================================== customers ==
 *
 * Who orders from this restaurant, and what they usually order. THIN ON
 * PURPOSE — one table, one side panel, and nothing else. Real volume arrives
 * with the customer app; a screen padded out now would be padded with either
 * invented data or invented importance.
 *
 * WHAT THIS SCREEN DOES NOT HAVE, AND WILL NOT GROW: a phone column, an address
 * column, a call button, a message button. The restaurant prepares food and
 * Mangwalow delivers it, so the customer's contact details belong to Mangwalow
 * and not to this table. The service does not carry them; there is nothing here
 * to style.
 *
 * Colour: none. Every state on this screen is calm by definition — somebody
 * ordering food is not an operational signal, and "Regular" is good news, so it
 * is set in ink like everything else. The one exception is a declined order in
 * the panel's history, which takes the same muted signal the queue already uses
 * for a decline.
 */

/* THE LIST SCROLLS, THE PAGE DOES NOT — the same rule the order board keeps,
   and the same three declarations it needs. A page that scrolls underneath a
   fixed top bar puts the store status and the connection state out of reach of
   somebody halfway down a list, which are the two things a merchant must never
   have to scroll back for. */
.shell:has(.main--list){height:100vh}
.shell:has(.main--list) .canvas{min-height:0}
.main--list{display:flex;flex-direction:column;min-height:0}

.cust{display:flex;flex-direction:column;min-height:0}
.cust__head{margin-bottom:var(--sp-4)}
.cust__count{display:flex;align-items:baseline;gap:var(--sp-2)}
/* The way back to the whole list, beside the count it narrowed. A search a
   merchant cannot see the end of is a screen they have to guess their way out
   of. */
.cust__clear{margin-left:var(--sp-2)}
.cust__count-label{font-size:13px;color:var(--mw-slate)}
/* The boundary, once, at the top — never repeated per row. It is a standing
   fact about the product, and a fact repeated forty times stops being read. */
.cust__note{max-width:74ch;margin-top:var(--sp-1)}

/* THE TABLE SCROLLS, THE PAGE DOES NOT — the same rule the order board keeps.
   The header stays put so a column of numbers three hundred rows down is still
   a column of something. */
.cust__scroll{
  min-height:0;overflow-y:auto;
  background:var(--mw-surface);
  border:1px solid var(--mw-line);border-radius:var(--r-lg);
}
.cust__scroll{scrollbar-width:thin;scrollbar-color:var(--mw-line-strong) transparent}
.cust__scroll::-webkit-scrollbar{width:6px}
.cust__scroll::-webkit-scrollbar-track{background:transparent}
.cust__scroll::-webkit-scrollbar-thumb{background:var(--mw-line-strong);border-radius:var(--r-pill)}

/* Fixed columns. A table that re-measures itself per page would put one
   merchant's "Orders" under another's "Area", and the whole value of a column
   of figures is that the eye can run down it without re-reading the heading. */
.ctable{width:100%;table-layout:fixed;border-collapse:collapse}
.ctable__c-name{width:24%}
.ctable__c-area{width:19%}
.ctable__c-orders{width:8%}
.ctable__c-usual{width:auto}
.ctable__c-last{width:14%}
/* HEADINGS ONLY. The name cell is a `th` too — it is the row's heading, which
   is what makes the table readable to a screen reader — so the caps treatment
   is scoped to `thead`. Unscoped, it shouted every customer's name back at the
   merchant in 10.5px capitals. */
.ctable thead th{
  position:sticky;top:0;z-index:1;
  padding:var(--sp-3) var(--sp-3);
  text-align:left;font-size:10.5px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--mw-slate);white-space:nowrap;
  background:var(--mw-surface);
  border-bottom:1px solid var(--mw-line-strong);
}
.ctable tbody th{text-align:left;font-weight:400}
.ctable__right{text-align:right}
.ctable tbody td,.ctable tbody th{
  padding:var(--sp-3);
  border-bottom:1px solid var(--mw-line);
  font-size:13.5px;vertical-align:baseline;
}
.ctable tbody tr:last-child td,.ctable tbody tr:last-child th{border-bottom:0}
.crow:hover{background:var(--mw-paper)}
/* The row whose panel is open. Ink, not colour: which row you are reading is a
   fact about the cursor, not about the business. */
.crow.is-open{background:var(--mw-sunken)}

.crow__open{
  display:inline;font-size:14px;font-weight:600;letter-spacing:-.005em;
  color:var(--mw-ink);
}
.crow__open:hover{text-decoration:underline;text-underline-offset:3px}
/* THERE IS NO "REGULAR" BADGE, AND THAT WAS A DELIBERATE REMOVAL.
   The first draft marked anybody with two or more orders. On a real branch that
   is nearly every row, and a mark that appears on five rows out of six has
   stopped being a mark. The Orders column says 17 / 16 / 13 / 3 / 3 / 1, which
   is the same fact with more of it, immediately beside the name. */
.crow__area{color:var(--mw-ink-2)}
/* One line, clipped. Three dishes is a habit; a wrapped paragraph of them is a
   menu, and it would push every other row off a 768px screen. */
.crow__usual{
  color:var(--mw-ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.crow__time{color:var(--mw-slate)}

/* ------------------------------------------------------- the side panel ---- */

/* Inset below the top bar, exactly like the order panel: the store status and
   the connection state live up there and a merchant must not lose sight of
   either because they opened a customer. */
.cpanel{width:420px;top:var(--topbar-h);height:calc(100vh - var(--topbar-h))}
.cpanel__where{margin-top:2px}
/* One figure now — the lifetime spend beside it was removed on the owner's
   ruling. Kept as a flex row rather than collapsed to a bare block so that a
   second operational count (never a money one) drops in without a reflow. */
.cpanel__figs{display:flex;gap:var(--sp-8)}
.cpanel__figs .fig{flex-direction:column;align-items:flex-start;gap:2px}
.cpanel__figs .fig + .fig{margin-top:0;padding-top:0;border-top:0}
.cpanel__since{margin-top:0}
.cpanel__since + .cpanel__since{margin-top:var(--sp-2)}
.cpanel__more{margin-top:var(--sp-3)}

.cusual{margin-top:var(--sp-2)}
.cusual__row{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-2) 0;border-bottom:1px solid var(--mw-line);
}
.cusual__row:last-child{border-bottom:0}
.cusual__name{font-size:13.5px;font-weight:500}
.cusual__count{flex:0 0 auto;white-space:nowrap}

.corders{margin-top:var(--sp-2)}
.corder{border-bottom:1px solid var(--mw-line)}
.corder:last-child{border-bottom:0}
.corder:hover{background:var(--mw-paper)}
.corder__open{
  display:flex;align-items:baseline;gap:var(--sp-3);
  padding:var(--sp-2) 0;color:var(--mw-ink);
}
.corder__when{color:var(--mw-slate)}
.corder__items{white-space:nowrap}
.corder__spacer{flex:1 1 auto}
/* A decline is history, not an alarm — it is stated, in the muted signal ink
   the queue already uses for one, and it takes no fill. */
.corder--declined .corder__no,.corder--declined .corder__when{color:var(--mw-slate)}
.corder__state{
  flex:0 0 auto;font-size:11px;font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;color:var(--mw-signal);
}

/* WHERE A CALL BUTTON WOULD BE — `.qcontact`, shared with the order panel
   rather than restyled here, so a merchant meets the same sentence in the same
   ink on both screens. It sits beside WHO the customer is, not at the bottom of
   the panel: on a regular with forty orders the bottom is two hundred pixels
   below the fold, and a policy nobody reads is indistinguishable from a feature
   nobody built. */
.cpanel .qcontact{margin-top:var(--sp-3)}

/* ================================================================== money ==
 *
 * "How much am I getting, and when." Four sections and nothing else: the
 * balance, the periods that produced it, the payments made, the paperwork.
 *
 * THE RULE THIS SECTION EXISTS UNDER: never invent a number, and `Rs 0` is not
 * an empty state. There is no styling here for a placeholder figure, a sample
 * row or an illustrative chart, because none of those may ever appear on this
 * screen. `.mnone` is the whole vocabulary for "nothing has happened yet", and
 * it is set at the weight a figure would have had, in the place a figure would
 * have been — the same idiom as the dashboard's `.dash__none`, because it is
 * the same claim.
 *
 * COLOUR. Almost none, and that is deliberate on the one screen where a
 * designer would reach for it hardest. Money arriving is not an operational
 * signal, a paid payout is good news, and this product does not spend colour on
 * good news (the same reason "nothing needs you right now" is not green). The
 * ONLY coloured thing here is a payout that failed or was cancelled: something
 * a merchant expected to happen did not, which is the definition of a signal.
 * A negative balance is left in ink — the minus sign says it, and painting an
 * ordinary overpayment red would teach a merchant to panic at arithmetic.
 *
 * FIGURES. Right-aligned, Plex Mono, tabular, and always two decimal places.
 * The dashboard rounds to whole rupees because it is a glance; this is a
 * reconciliation, and a figure quietly rounded is a figure that will not tie
 * out against a merchant's own books.
 *
 * Vertical budget at 1366x768: 56px top bar, 24px page padding, a 34px section
 * row, and everything else inside one scroll container so the top bar's store
 * status and connection state never leave the screen.
 */

.mny{display:flex;flex-direction:column;min-height:0}

/* ------------------------------------------------------ the four sections -- */

/* A row of four, not a dropdown and not a sidebar: there are four and there
   will not be a fifth. The current one is marked by ink and a rule under it —
   a filled pill here would be the loudest thing on a screen whose loudest thing
   should be a figure. */
.msub{
  display:flex;align-items:center;gap:var(--sp-5);
  margin-bottom:var(--sp-4);
  border-bottom:1px solid var(--mw-line);
}
.msub__tab{
  display:inline-flex;align-items:center;gap:var(--sp-2);
  padding:0 0 var(--sp-3);
  margin-bottom:-1px;
  font-size:13.5px;font-weight:500;color:var(--mw-slate);
  border-bottom:2px solid transparent;
}
.msub__tab:hover{color:var(--mw-ink)}
.msub__tab.is-current{color:var(--mw-ink);font-weight:600;border-bottom-color:var(--mw-ink)}
/* Present only when there is something to count. A tab reading "Payouts 0"
   announces an absence in the one place people read for a presence. */
.msub__count{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:18px;height:18px;padding:0 5px;
  background:var(--mw-sunken);color:var(--mw-slate);
  border-radius:var(--r-pill);
}

.mny__scroll{min-height:0;overflow-y:auto;padding-bottom:var(--sp-4)}
.mny__scroll{scrollbar-width:thin;scrollbar-color:var(--mw-line-strong) transparent}
.mny__scroll::-webkit-scrollbar{width:6px}
.mny__scroll::-webkit-scrollbar-track{background:transparent}
.mny__scroll::-webkit-scrollbar-thumb{background:var(--mw-line-strong);border-radius:var(--r-pill)}

/* ---------------------------------------------------------- the balance ---- */

/* The reconciliation on the left, when and where on the right. They do NOT
   stretch to match: the answer to "how much" is longer than the answer to
   "when", and padding the shorter one out to agree would be filling a card
   with nothing to say it. */
.mny__grid{
  display:grid;grid-template-columns:minmax(0,1fr) 340px;
  gap:var(--sp-4);align-items:start;
}
.mny__side{display:flex;flex-direction:column;gap:var(--sp-4)}

.mbal{padding:var(--sp-4) var(--sp-5) var(--sp-5)}
.mbal__head{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-4);
  padding-bottom:var(--sp-3);border-bottom:1px solid var(--mw-line);
}
/* NEVER A TOTAL WITHOUT THE PERIOD IT COVERS. This line is not a caption, it is
   part of the figure — a balance whose window a merchant has to guess at is a
   balance they cannot check against anything. */
.mbal__covers{white-space:nowrap}

/* THE ARITHMETIC BUILDS TO THE ANSWER. Each band is a `tbody`, so the rules
   between them are structural rather than decorative: components, then the
   subtotal, then the number the screen exists to show. */
.mrec{width:100%;border-collapse:collapse}
.mrec__row th{
  text-align:left;font-weight:400;font-size:13.5px;color:var(--mw-ink-2);
  padding:var(--sp-2) 0;
}
.mrec__amt{text-align:right;padding:var(--sp-2) 0;white-space:nowrap}
/* A figure a merchant cannot open is a figure they have to take on trust. */
.mrec__open{color:var(--mw-ink-2);text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--mw-line-strong)}
.mrec__open:hover{color:var(--mw-ink);text-decoration-color:var(--mw-slate)}
.mrec__sub{border-top:1px solid var(--mw-line)}
.mrec__sub tr:first-child th,.mrec__sub tr:first-child td{padding-top:var(--sp-3)}
.mrec__sub th{color:var(--mw-ink);font-weight:500}
.mrec__net{border-top:2px solid var(--mw-ink)}
.mrec__net th{text-align:left;padding-top:var(--sp-3);vertical-align:middle}
.mrec__net td{text-align:right;padding-top:var(--sp-3);white-space:nowrap;vertical-align:middle}
.mrec__net-label{display:block;font-size:15px;font-weight:600;letter-spacing:-.01em}
.mrec__net-note{display:block;margin-top:1px}
/* Inside the drill-down panel the same table is a supporting detail, not the
   headline, so it loses the heavy rules and a size. */
.mrec--panel{margin-top:var(--sp-2)}
.mrec--panel .mrec__row th{font-size:13px;padding:5px 0}
.mrec--panel .mrec__amt{padding:5px 0}

/* -------------------------------------------------- nothing has happened --- */

/* WHAT AN ABSENT FIGURE LOOKS LIKE. Never `0`, never `Rs 0`. It is set at the
   weight a figure would have been, and it says what will appear here and when
   — an invitation, not an apology. */
.mnone{padding:var(--sp-3) 0 var(--sp-2)}
.mnone__title{font-size:15px;font-weight:600;letter-spacing:-.01em}
.mnone__body{margin-top:var(--sp-2);max-width:62ch;font-size:13.5px;line-height:1.6;color:var(--mw-slate)}
.mnone__body--tight{margin-top:var(--sp-3)}
/* A WHOLE SECTION with nothing in it does NOT use this. It uses the portal's
   own `_empty_state.html` — centred, with its icon and its one sentence about
   what will arrive — because "nothing has happened here yet" is a claim the
   Orders and Customers screens already make, and a merchant should meet one
   vocabulary for it rather than three. `.mnone` is for the smaller case: a card
   inside a populated screen that has nothing to put in its own figure. */

/* ---------------------------------------------------------- when, where ---- */

.mside{padding:var(--sp-4) var(--sp-5)}
/* The date, or the honest sentence that replaces it. Both are the answer to the
   same question, so both are set the same way. */
.mside__lead{margin-top:var(--sp-2);font-size:17px;font-weight:600;letter-spacing:-.01em;line-height:1.25}
.mside__amt{margin-top:var(--sp-1)}
.mside__note{margin-top:var(--sp-2);line-height:1.55}
.mside__pending{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-3);
  margin-top:var(--sp-4);padding-top:var(--sp-3);
  border-top:1px solid var(--mw-line);
}

.mdest{margin-top:var(--sp-2)}
.mdest__row{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-2) 0;border-bottom:1px solid var(--mw-line);
}
.mdest__row:last-child{border-bottom:0}
.mdest__where{font-size:13.5px;font-weight:500}
/* In a table the bank's NAME is not the point of the row — the amount is. At the
   side card's weight it was the heaviest thing in every row and pulled the eye
   away from the money. */
.mtable .mdest__where{font-weight:400;color:var(--mw-ink-2)}
/* AN IDENTIFIER, NEVER A CREDENTIAL — the last four characters, masked in the
   service so the whole value never reaches this stylesheet's markup at all. */
.mdest__id{color:var(--mw-slate);white-space:nowrap}

/* ------------------------------------------------------------ the tables --- */

/* Fixed columns, sticky head, the same rules the customer table keeps: the
   value of a column of figures is that the eye can run down it without
   re-reading the heading, and a table that re-measures itself per page destroys
   exactly that. */
/* THE FRAME IS ON THE SCROLL CONTAINER, NOT ON THE TABLE — the same shape
   `.cust__scroll` already uses, and for a reason the stress fixture made
   visible: with a border on the TABLE, the table's own top border sits above
   the sticky header, so a row scrolling underneath showed through a 2px band
   above it. Invisible at three rows, a flickering line at fifty. Framing the
   scroll box instead leaves the header flush with the inside of the frame. */
.mny__scroll:has(.mtable){
  background:var(--mw-surface);
  border:1px solid var(--mw-line);border-radius:var(--r-lg);
}
.mtable{width:100%;table-layout:fixed;border-collapse:collapse}
.mtable__c-period{width:26%}
.mtable__c-date{width:15%}
.mtable__c-ref{width:17%}
.mtable__c-status{width:13%}
.mtable__c-dest{width:20%}
.mtable__c-num{width:9%}
.mtable__c-amount{width:17%}
/* SIZED FOR THE FIFTY-ONE ROWS, NOT FOR THE ONE.
   This was 31%, widened so that "Available once this settlement period closes."
   sat on one line — which looked right against three periods and was clearly
   wrong against fifty-two: every settled row holds two small links, so 31% left
   a quarter of the table empty down its whole length. The open period is ONE
   row, it is always at the top, and it is genuinely exceptional; its sentence
   wraps to two lines and reads as the exception it is. The stress fixture is
   what showed the earlier trade was the wrong way round. */
.mtable__c-doc{width:20%}
/* THE TWO PERIOD TABLES CARRY A SIX-COLUMN LAYOUT, so their widths are stated
   once here rather than by re-tuning the shared values underneath the payouts
   table as well. Settlements and Statements both list periods and both gained a
   REFERENCE column — the string an Ops agent and a restaurant read to each other
   — and the shared widths add up to 102% with it, which the browser resolves by
   quietly shrinking every column including the one holding a whole sentence. */
.mtable--periods .mtable__c-period{width:23%}
.mtable--periods .mtable__c-ref{width:16%}
.mtable--periods .mtable__c-status{width:13%}
.mtable--periods .mtable__c-num{width:9%}
.mtable--periods .mtable__c-amount{width:17%}
.mtable--periods .mtable__c-doc{width:22%}
.mtable thead th{
  position:sticky;top:0;z-index:1;
  padding:var(--sp-3);
  text-align:left;font-size:10.5px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--mw-slate);white-space:nowrap;
  background:var(--mw-surface);
  border-bottom:1px solid var(--mw-line-strong);
}
.mtable__right{text-align:right}
.mtable tbody td,.mtable tbody th{
  padding:var(--sp-3);
  border-bottom:1px solid var(--mw-line);
  font-size:13.5px;vertical-align:baseline;
}
.mtable tbody th{text-align:left;font-weight:400}
.mtable tbody tr:last-child td,.mtable tbody tr:last-child th{border-bottom:0}
.mtrow:hover{background:var(--mw-paper)}
.mtrow.is-open{background:var(--mw-sunken)}
.mtrow__open{color:var(--mw-ink);font-weight:500}
.mtrow__open:hover{text-decoration:underline;text-underline-offset:3px}
.mtrow__dash{color:var(--mw-slate)}
.mtrow__period{display:block}
.mtrow__period + .mtrow__period{margin-top:2px}
.mtrow__ref{color:var(--mw-ink-2)}
.mtrow__hint{margin-left:var(--sp-1)}
.mtrow__why{display:block;margin-top:2px}
.mtable .mlink + .mlink{margin-left:var(--sp-3)}

/* A status is a word, not a fill. The only one that takes colour is the one
   that means something a merchant expected did not happen — everything else,
   including "Paid", stays ink, because good news is not a signal. */
.mstatus{
  font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--mw-slate);
}
.mstatus--settled,.mstatus--paid{color:var(--mw-ink)}
.mstatus--failed,.mstatus--cancelled{color:var(--mw-signal)}

/* `.btn--sm` ALREADY EXISTS (the menu editor's). Redefining it here would have
   silently retuned every small button in the product — which is what a second
   definition of a shared class always does. Only the spacing between two of them
   in a table cell is this section's business. */
.mtable .btn + .btn{margin-left:var(--sp-2)}

/* ---------------------------------------------------------- the panel ------ */

/* Inset below the top bar, exactly like the order and customer panels, and
   wider than either because it carries a column of figures that must not wrap. */
/* Wider than the order and customer panels, and measured rather than chosen: an
   order id, its time and three money columns have to sit on ONE line. At 520px
   `mw-03-rxzk-thkv` wrapped, and a wrapped identifier in a column of figures
   reads as a rendering fault. */
.mmpanel{width:560px;top:var(--topbar-h);height:calc(100vh - var(--topbar-h))}
.mppanel__paid{margin-top:var(--sp-2)}
.mppanel__docs{display:flex;gap:var(--sp-2);margin-top:var(--sp-4)}
.mppanel__docs .btn + .btn{margin-left:0}
.mppanel__key{margin-top:var(--sp-3)}

.morders,.mledger{margin-top:var(--sp-2)}
.morder,.mledger__row{
  display:flex;align-items:baseline;gap:var(--sp-3);
  padding:var(--sp-2) 0;border-bottom:1px solid var(--mw-line);
}
.morder:last-child,.mledger__row:last-child{border-bottom:0}
.morder__spacer{flex:1 1 auto}
.morder__no{flex:0 0 116px;color:var(--mw-ink);white-space:nowrap}
.morder__when,.mledger__when{color:var(--mw-slate);white-space:nowrap}
.morder__when{flex:0 0 88px}
/* Three figures per row, each in its own fixed column so the three read down as
   three columns rather than as a sentence of numbers. */
.morder__sale,.morder__comm{flex:0 0 78px;text-align:right;color:var(--mw-slate)}
.morder__net{flex:0 0 88px;text-align:right;color:var(--mw-ink)}
/* The key sits ABOVE the columns it names. Underneath it was a footnote nobody
   reads until after they have already misread the numbers. */
.morder--head{padding-top:0;border-bottom:1px solid var(--mw-line-strong)}
.morder--head span{
  font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--mw-slate);
}
.mledger__what{
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-size:13px;color:var(--mw-ink-2);
}
.mledger__amt{flex:0 0 auto;text-align:right;white-space:nowrap}
.mledger__tag{
  margin-left:var(--sp-2);
  font-size:10.5px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--mw-slate);
}

@media (max-width:1180px){
  .mny__grid{grid-template-columns:minmax(0,1fr)}
}

/* =============================================================== settings ==
 *
 * "How does this restaurant run?" — eight sections down the left, one of them
 * on screen at a time.
 *
 * THE STRUCTURAL DECISION IS THAT THIS IS NOT ONE FORM. A single scrolling page
 * carrying opening hours, a payout schedule and a team list is a page nobody
 * reads and everybody scrolls past the middle of. Each section answers one
 * question an operator actually asks, and the question is printed under the
 * heading rather than left for them to infer from the fields.
 *
 * READ-ONLY IS A LAYOUT, NOT A STATE OF THE INPUTS. `finance` reads this whole
 * screen and changes none of it, so for that role every control is replaced by
 * a STATED VALUE (`.setrow__value`) rather than rendered disabled. A page of
 * greyed-out boxes tells somebody they are doing it wrong; a stated value
 * answers the question they came with. There is deliberately no `[disabled]`
 * styling anywhere in this section.
 *
 * COLOUR. Almost none, on a screen where a designer would reach for it to
 * separate eight sections. The sections are separated by position, the current
 * one by ink and a fill. Exactly two things here take a colour, and both are
 * signals: the dot that says whether the shop is open right now (store state,
 * which is the one thing on this platform that is always allowed a colour), and
 * the border of a confirmation that is about to remove somebody's access.
 *
 * FIGURES. Distances, prep times, minimum orders, coordinates, dates and
 * opening times all go through `.num` — they are compared down a column and
 * across a week, and a proportional face makes 18:00 and 08:00 different
 * widths.
 *
 * Vertical budget at 1366x768: 56px top bar, 24px page padding, a 46px section
 * header, and everything below it inside `.setpane`, which scrolls on its own
 * so the top bar's store status and connection state never leave the screen.
 */

.setwrap{
  flex:1 1 auto;min-height:0;
  display:grid;grid-template-columns:212px minmax(0,1fr);gap:var(--sp-6);
}
/* My preferences has no sub-nav — it is one person's page, not eight sections
   — and it is measured for reading rather than stretched to the grid. */
.setwrap--solo{grid-template-columns:minmax(0,1fr);max-width:820px}

/* ------------------------------------------------------------ the sub-nav -- */

/* Labels only. The QUESTION each section answers is in the header, where it is
   read once by somebody who has arrived, rather than eight times by somebody
   choosing. A nav that explains itself on every line is a nav that is scanned
   instead of read. */
.setnav{display:flex;flex-direction:column;gap:1px;align-self:start;position:sticky;top:0}
.setnav__item{
  display:block;padding:7px var(--sp-3);border-radius:var(--r-md);
  font-size:13.5px;color:var(--mw-ink-2);
}
.setnav__item:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.setnav__item.is-current{background:var(--mw-sunken);color:var(--mw-ink);font-weight:600}
.setnav__label{display:block}

.setpane{min-height:0;overflow-y:auto;padding-bottom:var(--sp-8)}
.setpane{scrollbar-width:thin;scrollbar-color:var(--mw-line-strong) transparent}
.setpane::-webkit-scrollbar{width:6px}
.setpane::-webkit-scrollbar-track{background:transparent}
.setpane::-webkit-scrollbar-thumb{background:var(--mw-line-strong);border-radius:var(--r-pill)}

.sethead{
  display:flex;align-items:flex-start;justify-content:space-between;gap:var(--sp-4);
  padding-bottom:var(--sp-3);margin-bottom:var(--sp-4);
  border-bottom:1px solid var(--mw-line);
}
.sethead__body{min-width:0}
.sethead__title{font-size:17px;font-weight:600;letter-spacing:-.01em}
.sethead__q{margin-top:2px}
/* SAID ONCE, AT THE TOP. A person who cannot change this screen learns it in a
   sentence and then reads in peace, instead of discovering it eight times by
   clicking into boxes that will not take a keystroke. */
.setro{
  flex:0 0 auto;
  padding:3px 10px;border-radius:var(--r-pill);
  background:var(--mw-sunken);color:var(--mw-slate);
  font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
}

/* --------------------------------------------------------------- a card ---- */

.setcard{margin-bottom:var(--sp-4)}
.setcard__title{font-size:14px;font-weight:600;letter-spacing:-.005em}
.setcard__lede{margin-top:var(--sp-2);max-width:70ch}

/* ---------------------------------------------------------------- a row ---- */

/* What it is on the left with the sentence that says why it matters, the
   control on the right. Two columns rather than a stack because eight sections
   of stacked label-over-input is four screens of scrolling, and 768px of height
   is the binding constraint on this product. */
.setrow{
  display:grid;grid-template-columns:minmax(0,7fr) minmax(0,9fr);
  gap:var(--sp-5);align-items:start;
  padding:var(--sp-4) 0;
}
.setrow + .setrow{border-top:1px solid var(--mw-line)}
.setrow__head{min-width:0}
.setrow__label{display:block;font-size:13.5px;font-weight:500;color:var(--mw-ink)}
.setrow__hint{margin-top:2px;font-size:12.5px;line-height:1.5;color:var(--mw-slate);max-width:58ch}
.setrow__control{min-width:0}
/* THE READ-ONLY RENDER. Set at the size the control would have been and in the
   place the control would have been, so a page read by Finance has the same
   shape as the page written by the Owner. */
.setrow__value{font-size:14px;line-height:1.5;color:var(--mw-ink)}
.setrow__value--none{color:var(--mw-slate)}
/* "Mangwalow's" beside an inherited number. Whether a value was chosen here or
   is being followed from the platform is a different fact from the value, and
   `5` looks identical either way. */
.setrow__tag{
  margin-left:var(--sp-2);
  font-size:10.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  color:var(--mw-slate);
}
/* Label above its own control — inside a grid of small fields, and anywhere the
   control is wider than the sentence beside it would be. */
.setrow--stacked{display:block;padding:0}
/* One column, head above control. For a control that is WIDER than the sentence
   beside it would be — sixteen cuisine chips in a half-width column is six rows
   of wrapping; across the card it is three. */
.setrow--full{grid-template-columns:minmax(0,1fr);gap:var(--sp-3)}
.setrow--full .setrow__hint{max-width:none}
.setrow + .setrow--stacked{border-top:0}
.setgrid{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-4);margin-top:var(--sp-4)}
.setrow--wide{grid-column:1 / -1}

.setgroup{margin-top:var(--sp-3)}

/* --------------------------------------------------------------- controls -- */

.setarea{height:auto;padding:var(--sp-2) var(--sp-3);font-size:14px;line-height:1.6;resize:vertical}
.setnum{max-width:190px}
/* THE DROPDOWNS, WHICH WERE THE OPERATING SYSTEM'S.
 *
 * `.input` gave these the product's box and nothing else, so a `<select>` kept
 * the platform's own arrow and the platform's own UI font — a control that
 * looked like a browser preference sitting in a row of fields that did not.
 * Two of them are on Payouts and one is the trading-day boundary, which is to
 * say the merchant met it on the two screens where the product is asking them
 * to trust it with money and with time.
 *
 * The native arrow is removed and the chevron is the product's own, from the
 * same icon set and in `currentColor` — no colour of its own, and no hex here.
 * The list the browser opens when it is clicked is still the platform's, which
 * is the right trade: it is the part that has to be reachable on a cheap
 * Android and it is the part a custom listbox gets wrong.
 */
.setpick{position:relative;display:inline-block;width:100%;max-width:280px}
.setpick .setselect{
  width:100%;max-width:none;padding-right:34px;
  appearance:none;-webkit-appearance:none;
  /* `select` does not inherit the page's face by default, which is why these
     read as system chrome beside every other field. */
  font-family:inherit;font-variant-numeric:tabular-nums;
  background:var(--mw-surface);cursor:pointer;
}
.setpick__chev{
  position:absolute;right:10px;top:50%;transform:translateY(-50%);
  display:flex;color:var(--mw-slate);
  /* The arrow is not a second control — the click belongs to the select. */
  pointer-events:none;
}
.setpick .setselect:hover ~ .setpick__chev{color:var(--mw-ink)}

/* A CONTROL NOBODY MAY USE HAS TO LOOK LIKE ONE.
 *
 * The rule at the top of this section — read-only is a layout, not a state of
 * the inputs — is about a ROLE that may not edit: `finance` reads the same
 * screen as a stated value rather than a wall of grey boxes, and that stands.
 *
 * This is the other case. The payout schedule is not editable by anyone in this
 * product, so the merchant is shown the schedule in the shape it is actually
 * configured in — "Weekly", "Friday", "Rs 500" — with a sentence above saying
 * who sets it. Left unstyled, `disabled` keeps the product's white box and its
 * hover, and reads as a live control that is refusing to work; that is worse
 * than either alternative, so it is styled. Nothing here is a new colour: the
 * sunken fill, the soft line and the muted ink are all the tokens the rest of
 * the screen uses.
 */
.setrow__control .input[disabled]{
  background:var(--mw-sunken);color:var(--mw-ink-2);
  border-color:var(--mw-line);cursor:default;
}
.setrow__control .input[disabled]:hover{border-color:var(--mw-line)}
.setpick .setselect[disabled] ~ .setpick__chev{color:var(--mw-line-strong)}
.setpick .setselect[disabled]:hover ~ .setpick__chev{color:var(--mw-line-strong)}

/* WITH SCRIPTING, THE LIST IS THE PRODUCT'S TOO.
 *
 * Removing the native arrow left a control that looked right until it was
 * clicked and then opened the operating system's window — the same complaint as
 * the calendar, on the screens about time and money. `settings.js` hides the
 * select from view and from the tab order, puts this button where it was, and
 * writes back into it: the select is still the thing that submits, so nothing
 * about the form changes. Without scripting none of this exists and the select
 * is simply itself. */
.setpick--js .setselect{
  position:absolute;width:1px;height:1px;padding:0;margin:0;
  opacity:0;pointer-events:none;
}
.setpick__button{
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
  width:100%;text-align:start;cursor:pointer;
  font-family:inherit;font-variant-numeric:tabular-nums;
  background:var(--mw-surface);
}
.setpick__button:hover{border-color:var(--mw-slate)}
.setpick__button[aria-expanded="true"]{border-color:var(--mw-ink)}
.setpick__value{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.setpick__list{
  position:fixed;z-index:70;
  max-height:280px;overflow-y:auto;padding:var(--sp-1);
  background:var(--mw-surface);
  border:1px solid var(--mw-line-strong);border-radius:var(--r-md);
  box-shadow:var(--shadow-pop);
  animation:pop-in var(--dur-micro) var(--ease) both;
}
.setpick__option{
  padding:7px var(--sp-3);border-radius:var(--r-sm);
  font-size:14px;line-height:1.3;cursor:pointer;white-space:nowrap;
}
.setpick__option:hover{background:var(--mw-sunken)}
.setpick__option:focus-visible{outline:2px solid var(--mw-ink);outline-offset:-2px}
.setpick__option[aria-selected="true"]{background:var(--mw-ink);color:var(--mw-paper)}
.setpick__option[aria-selected="true"]:hover{background:var(--mw-ink)}

.setselect{max-width:280px}
.setunit{display:inline-flex;align-items:center;gap:var(--sp-2)}
.setunit__pre,.setunit__post{font-size:12.5px;color:var(--mw-slate)}

/* A switch, not a checkbox, for the two settings that change what happens to an
   order the moment they are flipped. Instant, with no transition: everything
   else in this product changes state instantly too, and a sliding knob on a
   settings page is decoration. */
.setswitch{display:inline-flex;align-items:center;gap:var(--sp-3);cursor:pointer}
.setswitch input{position:absolute;width:1px;height:1px;opacity:0}
.setswitch__track{
  position:relative;flex:0 0 38px;width:38px;height:22px;
  border-radius:var(--r-pill);background:var(--mw-line-strong);
}
.setswitch__knob{
  position:absolute;top:3px;left:3px;width:16px;height:16px;
  border-radius:50%;background:var(--mw-surface);
}
.setswitch input:checked + .setswitch__track{background:var(--mw-ink)}
.setswitch input:checked + .setswitch__track .setswitch__knob{left:19px}
.setswitch input:focus-visible + .setswitch__track{
  box-shadow:0 0 0 2px var(--mw-surface),0 0 0 4px var(--mw-coral-700);
}
/* THE WORD FOLLOWS THE CONTROL, AND IT DOES IT IN CSS.
 *
 * The word used to be rendered once from the stored value, so a merchant who
 * flicked the switch read "Off" beside a switch that was now on and a form that
 * was about to post on. A setting that says one thing while the form says
 * another is the defect this screen exists to avoid, and it was on the screen.
 *
 * Both words are rendered and the checked state chooses between them, so it is
 * right before any script has run and stays right with no script at all. The
 * pair is `aria-hidden`: a checkbox already announces its own state, and a
 * label reading "On Off" would be worse than none. */
.setswitch__text{font-size:13px;color:var(--mw-ink-2)}
.setswitch__on{display:none}
.setswitch input:checked ~ .setswitch__text .setswitch__on{display:inline}
.setswitch input:checked ~ .setswitch__text .setswitch__off{display:none}

.setchips{display:flex;flex-wrap:wrap;gap:var(--sp-2)}
.setchip{
  display:inline-flex;align-items:center;gap:6px;
  padding:5px 10px;border:1px solid var(--mw-line-strong);border-radius:var(--r-pill);
  font-size:12.5px;cursor:pointer;
}
.setchip input{width:13px;height:13px;accent-color:var(--mw-ink)}
.setchip:has(input:checked){background:var(--mw-sunken);border-color:var(--mw-ink)}

.setpics{display:flex;gap:var(--sp-6);margin-top:var(--sp-4)}
.setpic{display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-2)}
.setpic__frame{
  display:flex;align-items:center;justify-content:center;
  overflow:hidden;border-radius:var(--r-md);background:var(--mw-sunken);
}
.setpic__frame--logo{width:96px;height:96px}
.setpic__frame--cover{width:280px;height:96px}
.setpic__frame img{width:100%;height:100%;object-fit:cover}
.setpic__blank{
  display:flex;flex-direction:column;align-items:center;gap:4px;
  color:var(--mw-slate);font-size:11.5px;
}
.setpic__pick{cursor:pointer}
.setclear{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--mw-ink-2);cursor:pointer}
.setclear input{width:13px;height:13px;accent-color:var(--mw-ink)}

.setactions{
  display:flex;align-items:center;gap:var(--sp-4);
  margin-top:var(--sp-4);padding-top:var(--sp-4);
  border-top:1px solid var(--mw-line);
}
/* The Restaurant section's save row sits OUTSIDE its last card, because what it
   saves lives in three of them. Floating between two cards with only a hairline
   above it, it read as the bottom of the card above; on its own surface it
   reads as what it is. */
.setform .setactions{
  margin-bottom:var(--sp-4);padding:var(--sp-4) var(--sp-6);
  background:var(--mw-surface);
  border:1px solid var(--mw-line);border-radius:var(--r-lg);
}
.setactions__note{max-width:54ch}

/* INLINE, AGAINST THE OFFENDING FIELD — never a wall at the top. The services
   key every refusal by field precisely so this can sit under the box that
   caused it. */
.seterr{margin-top:6px;font-size:12.5px;line-height:1.45;color:var(--mw-signal)}

/* An empty section says what would appear in it and why somebody would want it.
   Same claim as `.mnone` on Money and `.empty` everywhere else; this is the
   inside-a-card size. */
.setnone{margin-top:var(--sp-3);max-width:64ch;font-size:13.5px;line-height:1.6;color:var(--mw-slate)}

/* Where a control that is NOT here actually lives. The store status, the branch
   pin, the team list. Named rather than rebuilt — one control, one place. */
/* The rule runs the full width of the card. It was capped at 70ch with the
   text, which drew a line that stopped two-thirds of the way across and read as
   a rendering fault rather than as a divider. */
.setxref{
  margin-top:var(--sp-4);padding-top:var(--sp-3);
  border-top:1px solid var(--mw-line);
  font-size:12.5px;line-height:1.55;color:var(--mw-slate);
}
.setxref__btn{margin-top:var(--sp-3)}

/* -------------------------------------------------------------- the hours -- */

/* "Open now" is the merchant's PROOF that what they just typed works, so the
   windows it was computed from are printed beside it. "Closed" on its own is a
   support ticket; "Closed" beside a window reading 18:00–17:00 is a merchant
   spotting their own typo. */
.setnow{display:flex;align-items:center;gap:var(--sp-3)}
.setnow__dot{flex:0 0 9px;width:9px;height:9px;border-radius:50%;background:var(--mw-line-strong)}
.setnow--open .setnow__dot{background:var(--mw-jade)}
.setnow__body{flex:1 1 auto;min-width:0}
.setnow__title{font-size:14px;font-weight:600;letter-spacing:-.005em}
.setnow__why{margin-top:1px}
.setnow__at{flex:0 0 auto;color:var(--mw-slate)}

.setweek{margin-top:var(--sp-3)}
.setday{display:flex;align-items:center;gap:var(--sp-3);padding:6px 0;border-bottom:1px solid var(--mw-line)}
.setday:last-child{border-bottom:0}
.setday__name{flex:0 0 86px;font-size:13.5px;font-weight:500}
.setday--closed .setday__name{color:var(--mw-slate)}
.setday__form{display:flex;align-items:center;gap:var(--sp-3);flex:1 1 auto;min-width:0}
.setday__windows{display:flex;align-items:center;gap:var(--sp-3);flex:1 1 auto;flex-wrap:wrap}
.setwin{display:inline-flex;align-items:center;gap:6px}
/* Wide enough for a native time control INCLUDING its picker icon and a locale
   that renders 12-hour with an AM/PM. At 102px "12:00 PM" was clipped to
   "12:00 P", which on a screen about opening times is the one thing that must
   not happen. */
.setwin__time{width:132px;height:32px;padding:0 var(--sp-2);font-size:13px}
.setwin__dash{color:var(--mw-slate)}
.setwin__note{color:var(--mw-slate);white-space:nowrap}
/* The blank pair is how the first window on a Sunday is added, and how a second
   shift is added to a Tuesday. Dashed, so it reads as an invitation rather than
   as a field somebody failed to fill in. */
.setwin--new .setwin__time{border-style:dashed}
.setwin__value{font-size:13.5px}
.setwin__value--none{color:var(--mw-slate)}
.setday__save{flex:0 0 auto}
/* Reveals the blank pair for ONE day. A link, not a script — the server renders
   the row, so a merchant with no JavaScript adds a shift the same way. */
.setadd{
  display:inline-flex;align-items:center;gap:5px;flex:0 0 auto;
  padding:0 var(--sp-2);height:28px;border-radius:var(--r-sm);
  font-size:12.5px;color:var(--mw-slate);
}
.setadd:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.setadd svg{flex:0 0 14px}
.setdel{
  display:flex;align-items:center;justify-content:center;
  width:26px;height:26px;border-radius:var(--r-sm);color:var(--mw-slate);
}
.setdel:hover{background:var(--mw-signal-wash);color:var(--mw-signal)}
.setweek__note{margin-top:var(--sp-3)}

.setexc{margin-top:var(--sp-3)}
.setexc__row{display:flex;align-items:center;gap:var(--sp-3);padding:var(--sp-2) 0;border-bottom:1px solid var(--mw-line)}
.setexc__row:last-child{border-bottom:0}
.setexc__date{flex:0 0 150px}
.setexc__what{flex:1 1 auto;font-size:13.5px;color:var(--mw-ink-2)}
.setexc__form{margin-top:var(--sp-4);padding-top:var(--sp-4);border-top:1px solid var(--mw-line)}
.setexc__fields{display:flex;align-items:flex-end;gap:var(--sp-3);flex-wrap:wrap}
.setexc__closed{margin-bottom:11px}

/* ---------------------------------------------------- what the field holds -- */

/* A CONFIRMATION, NOT A CONTROL.
 *
 * A native date field renders in the BROWSER's locale: `08/09/2026` is 8
 * September on one merchant's screen and 9 August on another's, and nothing on
 * the screen says which. The entry order stays the browser's; what this removes
 * is having to guess, by restating the value underneath in the format the rest
 * of the page prints.
 *
 * It must never read as editable — no border, no field background, not
 * focusable — and it is absent rather than empty when there is nothing to
 * confirm, because an "=" with nothing after it looks like something that
 * failed to load. */
.setecho{
  margin-top:var(--sp-1);
  font-family:var(--mw-mono);font-size:12px;
  font-variant-numeric:tabular-nums;color:var(--mw-ink-2);
}
.setecho::before{content:"= ";color:var(--mw-slate)}
.setecho[hidden]{display:none}
/* Its own line under the pair it restates, rather than a fourth column in a row
   of fields. */
.setecho--times{flex:1 0 100%;margin-top:0}

/* THE WEEKLY GRID GETS THE SAME RESTATEMENT AND NO "=". It sits beside the two
   fields it belongs to rather than beneath them — seven of these on the
   product's tightest screen, and a line each would cost the week its fit. */
.setwin__echo{color:var(--mw-slate);white-space:nowrap}
.setwin__echo[hidden]{display:none}

/* ------------------------------------------------------------- the date --- */

/* THE ONE NATIVE CONTROL THIS PRODUCT REPLACES, and only its POPUP.
 *
 * The field stays `<input type="date">`: same value, same `YYYY-MM-DD` on the
 * wire, same segment-by-segment typing. What is replaced is the calendar the
 * browser draws — a window the page cannot style, size or position. On this
 * screen it opened detached in the corner, because the field sits low in a
 * section that scrolls inside `.setpane` and a native picker anchored to an
 * input its scroller is clipping has nothing to anchor to.
 *
 * The popup is suppressed ONLY under `[data-setcal]`, an attribute settings.js
 * sets on the document once it has a calendar of its own to offer. Without
 * scripting the attribute is absent, the native picker is untouched, and the
 * merchant still has a calendar.
 *
 * THE TIME FIELDS KEEP THEIR FIELD AND LOSE THEIR POPUP TOO, and get no
 * replacement widget: a time is four digits somebody types, the native field
 * already takes arrow keys and segment edits, and seven days of custom clock
 * dropdowns would be weight without capability. Suppressing the popup there is
 * what stops the same corner-anchored window appearing on the same screen.
 */
[data-setcal] .setcal__input::-webkit-calendar-picker-indicator,
[data-setcal] .settime::-webkit-calendar-picker-indicator{
  display:none;-webkit-appearance:none;appearance:none;
}

.setcal{position:relative;display:inline-flex;align-items:center}
.setcal__input{padding-right:34px}
/* Sits INSIDE the field it fills, so the calendar is opened from the thing it
   writes to rather than from a button next to it. */
.setcal__open{
  position:absolute;right:1px;top:1px;bottom:1px;width:32px;
  display:flex;align-items:center;justify-content:center;
  color:var(--mw-slate);border-radius:0 var(--r-md) var(--r-md) 0;
}
.setcal__open:hover{color:var(--mw-ink);background:var(--mw-sunken)}
.setcal__open:focus-visible{outline:2px solid var(--mw-ink);outline-offset:-2px}
.setcal[data-open] .setcal__open{color:var(--mw-ink);background:var(--mw-sunken)}

/* FIXED, and placed by script against the field's own rectangle, so a scroll
   container cannot clip it and a viewport edge flips it rather than cropping
   it. */
.setcal__pop{
  position:fixed;z-index:70;width:268px;
  padding:var(--sp-3);
  background:var(--mw-surface);
  border:1px solid var(--mw-line-strong);border-radius:var(--r-lg);
  box-shadow:var(--shadow-pop);
  animation:pop-in var(--dur-micro) var(--ease) both;
}
.setcal__head{
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
  margin-bottom:var(--sp-2);
}
/* The month is a figure a person scans, so it is mono like every other number
   on this screen. `min-width:0` because a localised month name is not the
   width an English one is. */
.setcal__month{
  flex:1 1 auto;min-width:0;text-align:center;
  font-family:var(--mw-mono);font-size:13px;font-weight:600;
  font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.setcal__step{
  flex:0 0 auto;width:26px;height:26px;
  display:flex;align-items:center;justify-content:center;
  color:var(--mw-ink-2);border-radius:var(--r-sm);
}
.setcal__step:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.setcal__step:focus-visible{outline:2px solid var(--mw-ink);outline-offset:1px}
.setcal__grid{display:grid;grid-template-columns:repeat(7,1fr);gap:1px}
.setcal__dow{
  height:22px;display:flex;align-items:center;justify-content:center;
  font-size:10.5px;font-weight:600;letter-spacing:.04em;
  color:var(--mw-slate);text-transform:uppercase;
}
.setcal__day{
  height:30px;border-radius:var(--r-sm);
  font-family:var(--mw-mono);font-size:12.5px;font-variant-numeric:tabular-nums;
  color:var(--mw-ink);
}
.setcal__day:hover{background:var(--mw-sunken)}
.setcal__day:focus-visible{outline:2px solid var(--mw-ink);outline-offset:-2px}
.setcal__day[data-outside]{color:var(--mw-slate)}
/* TODAY is a landmark, not a state — an underline rather than a colour, because
   colour on this product means something operational. */
.setcal__day[data-today]{font-weight:600;box-shadow:inset 0 -2px 0 var(--mw-line-strong)}
.setcal__day[aria-selected="true"]{background:var(--mw-ink);color:var(--mw-paper)}
.setcal__day[aria-selected="true"]:hover{background:var(--mw-ink)}
.setcal__foot{
  display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);
  margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--mw-line);
}
.setcal__hint{font-size:11px;color:var(--mw-slate)}

/* --------------------------------------------------------------- the team -- */

.setteam{margin-top:var(--sp-2)}
.setmember{display:flex;align-items:center;gap:var(--sp-4);padding:var(--sp-3) 0;border-bottom:1px solid var(--mw-line)}
.setmember:last-child{border-bottom:0}
.setmember__who{flex:0 0 32%;min-width:0;display:flex;flex-direction:column}
.setmember__name{font-size:13.5px;font-weight:500}
.setmember__you{
  margin-left:6px;
  font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--mw-slate);
}
.setmember__mail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.setmember__roles{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:var(--sp-2)}
.setmember__role{display:flex;flex-direction:column;gap:2px}
.setmember__since{flex:0 0 auto;color:var(--mw-slate);white-space:nowrap}
/* The removal column, kept even where there is no button, so a list of five
   people is five rows of one shape rather than five ragged ones. */
.setmember__spacer{flex:0 0 84px}
.setmember__remove{flex:0 0 auto}
/* A ROLE IS A WORD, NOT A FILL. `staff` in the database, "Kitchen" on a
   restaurant's screen — the vertical-specific word is chosen in the
   presentation layer, which is the only place it belongs. */
.setrole{
  align-self:flex-start;
  padding:1px 8px;border:1px solid var(--mw-line-strong);border-radius:var(--r-pill);
  font-size:10.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  color:var(--mw-ink-2);
}

/* ------------------------------------------------------------ the payouts -- */

.setdest{margin-top:var(--sp-3)}
.setdest__row{
  display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-4);
  padding:var(--sp-2) 0;border-bottom:1px solid var(--mw-line);
}
.setdest__row:last-child{border-bottom:0}

/* ------------------------------------------------------- the integrations -- */

.setsoon{margin-top:var(--sp-3)}
.setsoon__row{display:flex;align-items:baseline;gap:var(--sp-4);padding:var(--sp-2) 0;border-bottom:1px solid var(--mw-line)}
.setsoon__row:last-child{border-bottom:0}
.setsoon__name{flex:0 0 152px;font-size:13.5px;font-weight:500;color:var(--mw-ink-2)}
.setsoon__what{flex:1 1 auto}

/* -------------------------------------------------------- the consequence -- */

/* THE ONLY COLOURED SURFACE IN SETTINGS APART FROM THE OPEN DOT, and it is
   earned: these three changes are the ones whose harm is invisible afterwards.
   A restaurant that has vanished from customers' results looks exactly like a
   quiet Tuesday. */
.setconfirm{
  margin-bottom:var(--sp-4);padding:var(--sp-4) var(--sp-5);
  background:var(--mw-surface);
  border:1px solid var(--mw-coral);border-radius:var(--r-lg);
}
.setconfirm--destructive{background:var(--mw-signal-wash);border-color:var(--mw-signal)}
.setconfirm__title{font-size:15px;font-weight:600;letter-spacing:-.01em}
.setconfirm__effects{margin-top:var(--sp-2)}
.setconfirm__effect{
  position:relative;margin-top:6px;padding-left:16px;max-width:72ch;
  font-size:13.5px;line-height:1.55;color:var(--mw-ink-2);
}
.setconfirm__effect::before{
  content:"";position:absolute;left:2px;top:9px;
  width:5px;height:5px;border-radius:50%;background:var(--mw-line-strong);
}
.setconfirm--destructive .setconfirm__effect{color:var(--mw-ink)}
.setconfirm__actions{display:flex;align-items:center;gap:var(--sp-3);margin-top:var(--sp-4)}
.setconfirm__go{background:var(--mw-signal);color:#fff}
.setconfirm__go:hover{background:color-mix(in srgb,var(--mw-signal) 88%,var(--mw-ink))}

/* A SAVE IN FLIGHT, and deliberately not the same look as a control somebody
   is not allowed to use. Read-only renders stated values rather than dead
   boxes; this is a live button that is busy for a second, so it dims and stops
   taking presses and then the new page arrives. Tag selector rather than
   `.btn`, which the reset section owns. */
.setbusy button[type="submit"]{opacity:.55;cursor:default}

/* --------------------------------------------------------- what is unsaved -- */

/* NAMES WHAT CHANGED. "You have unsaved changes" tells a merchant nothing they
   did not already know; "Minimum order, Default prep time" tells them what they
   are about to lose. Rendered empty by the server and filled by settings.js, so
   a screen with no scripting never shows a bar promising a button it cannot
   drive. */
.setbar{
  position:fixed;left:50%;bottom:var(--sp-5);transform:translateX(-50%);z-index:60;
  display:flex;align-items:center;gap:var(--sp-4);
  padding:var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-5);
  background:var(--mw-ink);color:var(--mw-paper);
  border-radius:var(--r-pill);box-shadow:var(--shadow-pop);
  max-width:calc(100vw - var(--sp-8));
}
.setbar__text{font-size:13px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.setbar__what{font-weight:600}
.setbar__save{background:var(--mw-paper);color:var(--mw-ink)}
.setbar__save:hover{background:var(--mw-line-strong)}
.setbar__discard{color:var(--mw-line-strong)}
.setbar__discard:hover{color:var(--mw-paper)}

/* --------------------------------------------------- my preferences ------- */

.setopts{display:flex;flex-direction:column;gap:var(--sp-3)}
.setopt__label{display:flex;align-items:flex-start;gap:var(--sp-3);cursor:pointer}
.setopt__label input{margin-top:3px;width:14px;height:14px;accent-color:var(--mw-ink)}
.setopt__body{display:flex;flex-direction:column;min-width:0}
.setopt__name{font-size:13.5px;font-weight:500}
.setopt__hint{margin-top:1px;max-width:54ch}

.setcred + .setcred{margin-top:var(--sp-4);padding-top:var(--sp-4);border-top:1px solid var(--mw-line)}
.setcred__label{margin-top:var(--sp-4);font-size:13.5px;font-weight:600}
.setcred__form{margin-top:var(--sp-3);max-width:340px}
.setcred__form .btn{margin-top:var(--sp-3)}

.setaddr{margin-top:var(--sp-2);font-size:13.5px;color:var(--mw-ink-2)}
.setpin{margin-top:var(--sp-4)}
.setpin__pair{display:flex;align-items:flex-end;gap:var(--sp-3);flex-wrap:wrap}

/* ---------------------------------------------------------------- narrow --- */

@media (max-width:1100px){
  .setwrap{grid-template-columns:minmax(0,1fr)}
  .setnav{position:static;flex-direction:row;flex-wrap:wrap;gap:var(--sp-1)}
  .setrow{grid-template-columns:minmax(0,1fr);gap:var(--sp-2)}
  .setgrid{grid-template-columns:1fr}
}

/* A PHONE IS THE EMERGENCY SCREEN, and it stays that. This does not rebuild
   Settings for a phone — it stops one row breaking the page.

   A day of opening hours is two 132px time fields, a dash and a delete, which
   is about 300px of a 375px screen before the weekday name and the card's own
   padding. The row pushed the whole document sideways, so every screen in the
   product scrolled horizontally while the merchant was on Hours. The name now
   sits above its windows and the fields share what is left. */
@media (max-width:560px){
  .setday{flex-wrap:wrap;align-items:flex-start}
  .setday__name{flex:1 0 100%}
  .setday__form{flex-wrap:wrap}
  .setwin{flex:1 1 100%;flex-wrap:wrap}
  .setwin__time{width:auto;min-width:0;flex:1 1 80px}
  .setexc__row{flex-wrap:wrap}
  .setexc__date{flex:1 0 100%}
  .setexc__what{flex:1 1 auto;min-width:0}
}

/* ============================================================= responsive ==
 *
 * WHAT THIS SECTION IS, AND WHY IT IS ONE SECTION.
 *
 * Before it, the portal was desktop-first with spot repairs: breakpoints at
 * 560, 900, 1000, 1080, 1100 and 1180, each added to stop one row overflowing
 * on one screen. That is why it looked considered and measured badly. On the
 * menu editor at 390px the document was 799px wide in a 375px viewport, and
 * `.menu__grid` computed to `232px 19px` — the rail kept every pixel and the
 * content column was crushed to nineteen. At 768 the dish row computed to
 * `20 20 36 0px 104 108 46 24`: 358px of FIXED columns survived intact and the
 * only flexible one, the dish NAME, collapsed to zero. The most important
 * thing on the row was the first thing to disappear.
 *
 * FOUR WIDTHS, DECLARED ONCE, USED EVERYWHERE BELOW. Repeating a number is how
 * six breakpoints became six different opinions about what "narrow" means.
 * They cannot be custom properties — a media query cannot read one — so they
 * are a comment and a convention instead:
 *
 *   <= 560   phone            one column, drawer nav, no fixed grids at all
 *   <= 900   large phone      two-line rows; fixed column sets give way
 *   <= 1024  tablet           side rails become sheets or chip rows
 *   <= 1200  small laptop     the nav rail collapses (pre-existing)
 *   >= 1600  wide             stop stretching; cap and centre
 *
 * THE RULE THE OLD CSS BROKE: when space runs out, the FIXED parts yield and
 * the content keeps its width. Not the other way round.
 */

/* -------------------------------------------------- shell, narrow ---- */

/* Below the tablet width the nav stops being a column and becomes a drawer.
 * `data-nav` already carries the state and Alpine's `nav` store already
 * toggles it — the same attribute that means "narrow rail" on a laptop means
 * "slid out of the way" here, so no new state, no second source of truth.
 */
@media (max-width:1024px){
  .shell{grid-template-columns:minmax(0,1fr)}

  .sidebar{
    position:fixed;top:0;left:0;z-index:80;
    width:var(--nav-w);max-width:86vw;
    height:100dvh;
    transform:translateX(-100%);
    transition:transform var(--dur-panel) var(--ease);
    box-shadow:var(--shadow-pop);
  }
  /* EXPANDED IS OPEN HERE. `portal.js` already paints `collapsed` below
     1200px, so a phone loads with the drawer shut without being told to. */
  .shell[data-nav="expanded"] .sidebar{transform:translateX(0)}

  /* A collapsed drawer is CLOSED, not a 64px rail — the rail would be a
     column again, which is the thing this breakpoint exists to remove. */
  .shell[data-nav="collapsed"] .sidebar{transform:translateX(-100%)}
  /* …and inside the drawer the labels always show: it is full width when it is
     on screen, so the icon-only treatment would be hiding text for no reason. */
  [data-nav="collapsed"] .sidebar .brand__text,
  [data-nav="collapsed"] .sidebar .nav__label,
  [data-nav="collapsed"] .sidebar .nav__collapse span{display:revert}
  [data-nav="collapsed"] .sidebar .brand,
  [data-nav="collapsed"] .sidebar .nav__link,
  [data-nav="collapsed"] .sidebar .nav__collapse{justify-content:flex-start;padding:revert}

  .nav-scrim{
    position:fixed;inset:0;z-index:70;
    background:rgba(16,18,22,.44);
    opacity:0;pointer-events:none;
    transition:opacity var(--dur-panel) var(--ease);
  }
  .shell[data-nav="expanded"] .nav-scrim{opacity:1;pointer-events:auto}

  .nav-open{display:inline-flex}
}

/* The hamburger exists only where the nav is a drawer. Above that the rail is
   on screen and a button to reveal it would open what is already open. */
.nav-open{display:none;align-items:center;justify-content:center;
  width:34px;height:34px;flex:0 0 34px;border-radius:var(--r-md);color:var(--mw-slate)}
.nav-open:hover{background:var(--mw-sunken);color:var(--mw-ink)}
.nav-scrim{display:none}
@media (max-width:1024px){.nav-scrim{display:block}}

/* -------------------------------------------------- menu, narrow ----- */

/* THE MEASURED BUG. `.menu__grid` had no breakpoint at all, so its 232px rail
   survived to 390px and left 19px for the dishes. Below the tablet width the
   rail stops being a column and becomes a horizontally scrolling row of
   sections above the list — the same shape the mobile design uses, and the
   only one that leaves the dishes the full width. */
@media (max-width:1024px){
  .menu__grid{grid-template-columns:minmax(0,1fr);gap:var(--sp-4)}

  /* Not sticky, not tall, not a column: a strip of sections above the dishes.
     `position:sticky` with a viewport-height cap is right for a rail beside
     the content and wrong for a band above it — it would pin a scrolling
     sideways list over the list it belongs to. */
  .menu-rail{
    position:static;max-height:none;
    flex-direction:column;
    /* A GRID ITEM DEFAULTS TO `min-width:auto`, which means "never smaller than
       my content" — so the sideways list of sections pushed the track wider
       than the screen and took the document with it. `minmax(0,1fr)` on the
       track is only half the fix; the item has to be allowed to be narrow too,
       and then its own `overflow-x` can do the scrolling. */
    min-width:0;
    overflow:hidden;
  }
  .menu__grid > *{min-width:0}
  .menu-rail__list{
    display:flex;gap:var(--sp-2);
    overflow-x:auto;overflow-y:hidden;
    padding:0 var(--sp-2) var(--sp-2);
    scrollbar-width:thin;
  }
  .menu-rail__list li{flex:0 0 auto}
  /* Depth is drawn with left padding down the rail. Sideways that reads as
     random gaps, so nesting is shown by the chip's own indent instead. */
  .rail-node{padding-left:0}
  .rail-link{white-space:nowrap}
}

/* The dish row. Eight columns, 358px of them fixed, and the name was the only
   one that could give — so the name was the one that vanished. Below the large
   phone width the row stops being a grid: the name takes a line of its own and
   the numbers and controls wrap beneath it. Nothing is hidden. */
@media (max-width:900px){
  /* The header labelled eight columns that no longer exist. */
  .mlist__head{display:none}

  .mrow__grid{
    display:flex;flex-wrap:wrap;align-items:center;
    gap:var(--sp-2) var(--sp-3);
    padding:var(--sp-3) var(--sp-4);
  }
  /* `.mrow__form` is `display:contents`, so ITS children are the flex items —
     the same reason it was written that way for the grid. Ordering is applied
     to those children, not to the form. */
  .mpick,.mmoves,.mthumb{order:0;flex:0 0 auto}
  /* THE NAME TAKES A LINE OF ITS OWN and is never what shrinks. This is the
     whole point of the breakpoint: at 768 it was the one column allowed to
     give, and it gave all the way to 0px. */
  .mnames{order:1;flex:1 1 100%;min-width:0}
  .mmoney{order:2;flex:0 0 auto}
  /* NOT `.mrow__grid > *`, which was the bug. `display:contents` makes the
     form's children FLEX ITEMS, but it does not make them CHILDREN — the DOM
     is unchanged, so a child combinator still stops at the form and every one
     of them kept `order:0`. The status pill, the choices and both controls
     therefore sorted AHEAD of the dish name at 390px, which is how a phone
     came to show a checkbox, an arrow pair and a "Half / Full" chip with no
     dish attached to them. Named, so it cannot silently miss another. */
  .mchips,.avail,.mopen,.mmore{order:3}
  .mrow__grid > *:not(.mpick):not(.mmoves):not(.mthumb):not(.mnames):not(.mmoney){order:3}
}

/* The add-a-dish row and the pane header. Both are single-line flex rows of
   fixed-width fields — `.madd__ur` is a hard 150px, `.mstart--sub` a hard
   200px — so on a phone they were 515px of content in a 375px viewport and
   they, not the grids, were what still took the document sideways after the
   layout was fixed. They wrap instead. */
@media (max-width:900px){
  .madd{flex-wrap:wrap;gap:var(--sp-2)}
  .madd > *{min-width:0}
  .madd__name{flex:1 1 100%}
  .madd__ur{flex:1 1 140px}
  .madd__price{width:auto;flex:1 1 90px}
  .madd .mmoney{flex:1 1 100%;min-width:0}
  .madd .btn{flex:0 0 auto}

  .pane-head{flex-wrap:wrap;gap:var(--sp-2)}
  .pane-head__names{max-width:none;flex:1 1 100%}
  .pane-head__tools{flex:1 1 100%;flex-wrap:wrap;min-width:0}
  .mstart--sub{width:auto;flex:1 1 100%}
  .mstart{min-width:0}
  .mstart__input{min-width:0;flex:1 1 auto}
}

/* THE PUBLISH BAR, in the state the first audit never saw.
 *
 * Measured on a LIVE menu it is a dot, one sentence and a disabled button, and
 * it fits anywhere. The moment something is unpublished it grows a row of diff
 * chips, a "What changed" popover, Discard and Publish — around 500px of
 * controls on one unwrapped line, which at 390px took the whole document
 * sideways. The bar is sticky, so that is every screen of the menu, not one.
 *
 * Publish stays on the first line it can reach: it is the reason the bar is
 * there, and a merchant should never have to scroll to find it. */
@media (max-width:900px){
  .pubbar{flex-wrap:wrap;gap:var(--sp-2) var(--sp-3)}
  .pubbar__body{flex:1 1 220px;min-width:0}
  .pubbar__diff{flex:1 1 100%;flex-wrap:wrap;min-width:0}
  .pubbar form{flex:0 0 auto}
}

/* The side panel is a 480px fixed overlay. On a phone that is wider than the
   screen, so it becomes the screen. */
@media (max-width:560px){
  .mpanel{width:100vw;max-width:100vw}
}

/* ------------------------------------- two-column workspaces, narrow -- */

/* Five layouts, five different ideas of "narrow" — 1080 for the dashboard,
   1100 for settings, 1180 for money and the queue. A merchant on a tablet met
   three of them at once and the page reflowed in three stages. One width. */
@media (max-width:1024px){
  .dash__grid,
  .mny__grid,
  .setwrap,
  .mform__grid,
  .setgrid{grid-template-columns:minmax(0,1fr)}
  .setnav{position:static;flex-direction:row;flex-wrap:wrap;gap:var(--sp-1)}
}

/* Fixed column COUNTS, which are the other half of the same mistake: four
   metrics across a 768px tablet is four columns of nothing. */
@media (max-width:1024px){
  .qmetrics{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:560px){
  .qmetrics{grid-template-columns:minmax(0,1fr)}
}

/* -------------------------------------------------- billing, narrow -- */

/* A tab strip with a 20px gap. Three tabs and their counts are 29px wider than
   a 390px phone — so it scrolls sideways rather than wrapping, because a tab
   strip that becomes two rows stops reading as one control. */
@media (max-width:560px){
  .msub{
    gap:var(--sp-4);
    overflow-x:auto;
    scrollbar-width:none;
    border-bottom:1px solid var(--mw-line);
  }
  .msub::-webkit-scrollbar{display:none}
  .msub__tab{flex:0 0 auto;white-space:nowrap}
}

/* ------------------------------------------------- order board, narrow */

/* The incoming banner and the queue's keyboard hint are single-line rows with
   `white-space:nowrap` on the timer. They are the last things on the order
   board that took the document sideways on a phone — which matters more here
   than anywhere else, because this is the screen a merchant opens when a
   customer is already waiting. Wrap, never truncate: the elapsed time IS the
   information on this banner. */
@media (max-width:900px){
  .incoming{flex-wrap:wrap;gap:var(--sp-2) var(--sp-3)}
  .incoming__who{flex:1 1 100%;min-width:0}
  .incoming__since,.incoming__more{white-space:normal}
  .incoming__open{flex:1 1 auto}

  .qkeys{max-width:100%;white-space:normal;text-align:left}
}

/* ------------------------------------------------- gutters, narrow ---- */

/* Gutters that are right at 1440 are a quarter of a phone. */
@media (max-width:900px){
  .main{padding:var(--sp-4) var(--sp-4)}
  .topbar{padding:0 var(--sp-4)}
}
@media (max-width:560px){
  .main{padding:var(--sp-3) var(--sp-3)}
}

/* ------------------------------------------------- wide screens ------- */

/* Nothing to do here: 1440 and 1920 were never BROKEN — `.main` was already
   capped at 1280px — they were simply never CONSIDERED, with the content hard
   against the left edge and the rest of the screen empty beside it. The fix is
   one declaration and it belongs on `.main` itself, in the shell section that
   owns it, because it applies at every width. */

/* ------------------------------------------------- wide tables -------- */

/* Any wide table left in the product scrolls INSIDE ITS OWN BOX rather than
   taking the document sideways with it. This is the backstop that keeps a
   screen nobody has re-laid-out yet from breaking the whole page. */
@media (max-width:1024px){
  .table-wrap,.data-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
}

/* ── THE ALARM AWAY FROM THE QUEUE ─────────────────────────────────────────
   `_alert_mount.html` renders the smallest DOM `alerts.js` needs so the
   new-order alarm works on every screen in the portal, not only on Orders.
   The mount itself is invisible plumbing — the waiting-ticket markers carry no
   content and must occupy no space, or every page would grow a blank strip
   that got taller as orders arrived.

   `display:contents` and not `display:none`: a hidden ancestor would take the
   BANNER out of the layout with it, and the banner is the whole point. */
.alert-mount{display:contents}
.alert-mount [data-lane="new"]{display:none}

/* The banner. Fixed to the top of the viewport rather than in the flow, so it
   cannot push a page the merchant is reading — and above everything, because an
   unanswered order outranks whatever they are doing. */
.alert-mount__incoming{
  position:fixed;
  top:var(--sp-3);
  left:50%;
  transform:translateX(-50%);
  z-index:900;
  display:flex;
  align-items:center;
  gap:var(--sp-3);
  width:min(560px, calc(100vw - var(--sp-4) * 2));
  padding:var(--sp-3) var(--sp-4);
  border-radius:var(--radius-lg, 14px);
  background:var(--color-danger, #DC2626);
  color:#fff;
  text-decoration:none;
  box-shadow:0 10px 30px rgba(0,0,0,.28);
}
.alert-mount__body{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.alert-mount__body strong{font-size:15px;font-weight:700}
.alert-mount__body span{font-size:12.5px;opacity:.9}
.alert-mount__clock{font-variant-numeric:tabular-nums;font-weight:700;font-size:15px}

/* A pulse, because a static red bar reads as a page element and this is a
   summons. Stilled for anyone who has asked for less motion. */
.alert-mount__ping{
  width:10px;height:10px;border-radius:50%;
  background:#fff;flex:none;
  animation:mw-alert-ping 1.1s ease-in-out infinite;
}
@keyframes mw-alert-ping{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.7)}}
@media (prefers-reduced-motion: reduce){
  .alert-mount__ping{animation:none}
}

/* ==================================================== the phone, properly ==
 *
 * WHAT THIS SECTION IS. The responsive pass above made the menu editor,
 * settings, money and the gutters survive a narrow screen. It never touched the
 * ORDER BOARD or the order panel — `.qboard`, `.lane`, `.qpanel` appear nowhere
 * in it — which is why the one screen a restaurant actually works from was the
 * one screen still broken. `.qboard` was `repeat(4,minmax(0,1fr))` at every
 * width: four kanban lanes on a 375px phone is four 80px columns.
 *
 * THE USE CASE THIS IS FOR: a restaurant with no tablet, running a shift from
 * the owner's Android phone while Ops handles the rest. Everything below is
 * measured against that, not against "does it fit".
 *
 * Nothing above is edited. Every rule here is additive and lives after the
 * originals, so it wins by source order at equal specificity and the desktop
 * cascade is untouched.
 */

/* ------------------------------------------------ the bottom bar ------ */

/* Not rendered above the large-phone width. The sidebar rail is on screen
   there and a second navigation would be a second source of truth. */
.mobilenav{display:none}

@media (max-width:900px){
  .mobilenav{
    position:fixed;left:0;right:0;bottom:0;z-index:60;
    display:grid;grid-auto-flow:column;grid-auto-columns:1fr;
    background:var(--mw-surface);
    border-top:1px solid var(--mw-line);
    /* The home indicator on a modern phone sits over the bar. Padding, not
       margin, so the bar's own surface still reaches the bottom edge. */
    padding-bottom:env(safe-area-inset-bottom,0px);
  }

  /* ONE TARGET, THE WHOLE CELL. A 22px icon is not a touch target; the cell
     is, and at 320px the narrowest of four is 80px wide by 60px tall. */
  .mobilenav__link{
    position:relative;
    display:flex;flex-direction:column;align-items:center;justify-content:center;
    gap:3px;
    min-height:var(--mobilenav-h);
    padding:var(--sp-2) var(--sp-1);
    border:0;background:none;cursor:pointer;
    color:var(--mw-ink-2);
    font-family:var(--mw-font);font-size:11px;font-weight:600;
    text-decoration:none;
    -webkit-tap-highlight-color:transparent;
  }
  /* WHERE YOU ARE, said the way the sidebar says it — coral text and a coral
     edge — but on the top edge, because that is the edge facing the content. */
  .mobilenav__link[aria-current="page"]{color:var(--mw-coral-700)}
  .mobilenav__link[aria-current="page"]::before{
    content:"";position:absolute;top:0;left:24%;right:24%;height:3px;
    border-radius:0 0 3px 3px;background:var(--mw-coral);
  }
  .mobilenav__link:active{background:var(--mw-sunken)}

  .mobilenav__icon{position:relative;display:inline-flex;line-height:0}
  .mobilenav__label{white-space:nowrap;line-height:1}

  /* The badge is `.badge` — same fill, same mono figures, same contract — and
     this only moves it onto the icon. A second set of badge colours would be a
     second design system. */
  .mobilenav__badge{
    position:absolute;top:-7px;left:13px;
    min-width:18px;height:18px;font-size:11px;
  }
  .mobilenav__badge[hidden]{display:none}

  /* CONTENT MUST CLEAR THE BAR. Without this the last order card on the board
     sits under it and its action button cannot be pressed at all. */
  .main{
    padding-bottom:calc(var(--mobilenav-h) + env(safe-area-inset-bottom,0px) + var(--sp-4));
  }
}

/* ------------------------------------------------ the order board ----- */

/* FOUR LANES BECOME ONE LIST, IN THE ORDER WORK TRAVELS. Not tabs: a tab hides
   the lane you are not on, and the whole job of this screen is that a new order
   cannot be missed. The lane names stay as sticky headers so "which pile is
   this" is answerable while scrolling, and the count stays beside the name.
 *
 * The board is viewport-locked on desktop (`.shell:has(.main--queue)` is
 * 100vh, each lane scrolls inside itself). On a phone that traps four
 * scrollable regions inside one screenful. The DOCUMENT scrolls instead. */
@media (max-width:900px){
  .shell:has(.main--queue){height:auto}
  .shell:has(.main--queue) .canvas{min-height:100dvh}
  .main--queue{display:block;min-height:0}
  .queue{display:block}

  .qboard{display:block}

  .lane{
    display:block;min-height:0;
    background:transparent;border-radius:0;
    margin-bottom:var(--sp-5);
  }
  .lane:last-child{margin-bottom:0}

  /* Sticky UNDER the top bar, which is itself sticky at 0. Opaque on the page
     colour — a translucent header over a scrolling ticket is unreadable at
     arm's length in a kitchen. */
  .lane__head{
    position:sticky;top:var(--topbar-h);z-index:6;
    padding:var(--sp-3) var(--sp-1) var(--sp-2);
    background:var(--mw-paper);
    border-bottom:1px solid var(--mw-line-strong);
  }
  .lane__title{font-size:12px}

  /* The lane no longer owns a scroll region; the page does. */
  .lane__list{
    display:flex;flex-direction:column;gap:var(--sp-2);
    overflow:visible;min-height:0;
    padding:var(--sp-2) 0 0;
  }
  .lane__empty{padding:var(--sp-2) 0}
}

/* ------------------------------------------------ the order panel ----- */

/* `.mpanel` already goes full width at 560, but the ORDER panel keeps
   `top:56px; height:calc(100vh - 56px)` from `.qpanel`, and between 561 and
   900 it stayed a 420px overlay on a screen barely wider than it. On a phone an
   order IS the screen: full bleed, and the actions in the footer wrap instead
   of running off the edge — that footer is where Accept and Mark ready live,
   and a clipped one is an order that cannot be worked. */
@media (max-width:900px){
  /* ANCHORED TO BOTH EDGES, NOT SIZED IN `vw`. `width:100vw` is the viewport
     INCLUDING the scrollbar, so against a 360px content box the 375px panel
     hung 15px off the left edge — measured at x=-15, with its footer at x=-14
     and the first character of every line clipped. `left:0;right:0` is the same
     intent expressed in the layout the panel actually sits in, and it is
     correct whether or not the device paints a scrollbar. Applied to `.mpanel`
     too: the menu panel's own `width:100vw` at 560 has the same defect. */
  .mpanel,.qpanel{
    left:0;right:0;width:auto;max-width:none;
  }
  .qpanel{
    top:0;height:100dvh;
  }
  /* Above the bottom bar: an open order outranks navigation. */
  .qpanel{z-index:90}
  .qpanel__foot{
    flex-wrap:wrap;
    padding:var(--sp-3) var(--sp-4)
            calc(var(--sp-3) + env(safe-area-inset-bottom,0px));
  }
  /* The spacer's whole job is pushing buttons apart on a wide footer. Wrapped
     onto two lines it would push them onto separate rows for no reason. */
  .qpanel__foot .qpanel__spacer{display:none}
  .qpanel__foot .btn{flex:1 1 auto;justify-content:center;min-height:44px}
}

/* ------------------------------------------------ dashboard, narrow --- */

/* Three live lanes across a 320px screen is three columns of nothing. The
   outer `.dash__grid` collapses at 1024; this inner strip never did.
   Stepped down on the DECLARED widths — `test_the_widths_used_are_the_declared_ones`
   is right that a fourth opinion about "narrow" is how the original mess
   accumulated, and it caught a 400px one here. */
@media (max-width:900px){
  .lstrip{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-3)}
}
@media (max-width:560px){
  .lstrip{grid-template-columns:minmax(0,1fr)}
}

/* ------------------------------------------------ popovers, on a phone */

/* MEASURED, NOT GUESSED. At 375px the store-status pill sits at x=108 and is
 * 96px wide. `.popover` is `position:absolute; right:0` and `.status-menu` is a
 * hard 264px, so the panel rendered from x=-60 — sixty pixels of it, including
 * the leading status dot of every option, off the left edge of the screen. At
 * 320px it is worse, and every other popover in the product has the same shape:
 * the row menu, the section menu, the publish diff, the keyboard card and the
 * user menu are all anchored to a trigger that can be anywhere.
 *
 * Anchoring cannot be repaired one popover at a time — the trigger's position
 * is the problem. On a phone they become BOTTOM SHEETS instead: pinned to the
 * viewport, never clipped whatever they hang off, capped so a long one scrolls
 * inside itself, and sitting above the bottom bar where a thumb already is.
 * `@click.outside` and the escape handler are DOM-based and work unchanged. */
@media (max-width:900px){
  .popover{
    position:fixed;
    left:var(--sp-3);right:var(--sp-3);
    top:auto;bottom:calc(var(--mobilenav-h) + env(safe-area-inset-bottom,0px) + var(--sp-3));
    width:auto;max-width:none;
    max-height:60vh;overflow-y:auto;
    z-index:95;
  }
  /* Every popover that declared its own width was declaring a DESKTOP width. */
  .status-menu,.user-menu,.rowmenu,.secmenu,.diffcard,.keycard{width:auto}

  /* EXCEPT THE TOP BAR'S, WHICH DROP FROM THE BAR. A bottom sheet is right for
     a menu hanging off a row halfway down a long list — the trigger is nowhere
     near an edge, and the thumb is at the bottom. It is WRONG for the store
     status and the user menu: those hang off a control in the top bar, and
     sending their panel to the far end of the screen severs it from the thing
     that was tapped. It reads as a card that appeared in the middle of the
     screen for no reason, which is exactly what it looked like.
     Pinned under the bar instead: still viewport-width, still never clipped,
     but visibly belonging to the control above it. */
  .topbar .popover{
    top:calc(var(--topbar-h) + var(--sp-1));
    bottom:auto;
    max-height:calc(100dvh - var(--topbar-h) - var(--sp-5));
  }
  /* Comfortable rows, because these are now the primary way to reach these
     actions rather than a convenience beside a wide screen. */
  .status-menu__item{padding:var(--sp-3)}
}

/* ------------------------------------------------ menu sections ------- */

/* THE MEASUREMENT THAT SETTLED THIS. The earlier pass turned the section rail
 * into a horizontally scrolling chip strip below the tablet width. On the pilot
 * menu that strip is 4,530px wide inside a 334px window, holding 32 sections,
 * of which TWO are visible without dragging. A merchant looking for "Fish
 * Corner" drags four and a half metres through a 56px band.
 *
 * A sideways strip is right for six sections and wrong for thirty-two, and the
 * number of sections is the restaurant's business, not ours. So on a phone the
 * rail goes back to being a LIST — vertical, the direction a list of names is
 * read — capped so it cannot push the dishes off the screen, and scrolling
 * inside itself. Nothing is hidden and nothing needs dragging sideways. */
@media (max-width:900px){
  .menu-rail{overflow:visible}
  .menu-rail__list{
    flex-direction:column;
    gap:var(--sp-1);
    overflow-x:hidden;overflow-y:auto;
    max-height:44vh;
    padding:var(--sp-2);
    -webkit-overflow-scrolling:touch;
  }
  .menu-rail__list li{flex:0 0 auto;width:100%}
  /* The chip was sized to sit in a row. In a column it is a full-width row and
     a long section name wraps instead of being clipped. */
  .rail-link{white-space:normal;width:100%;min-height:40px}
}

/* ------------------------------------------------ the sound bar ------- */

/* The audio bar is one unwrapped flex row: a nowrap label, a sentence, a
 * spacer and a button. "This browser is blocking sound" plus its explanation
 * plus "Turn the sound on" is far more than 375px, and this is the one message
 * that must be readable — it is the product admitting the kitchen will not
 * hear the next order. It wraps, and the button that fixes it goes full width
 * on its own line rather than being squeezed to an ellipsis. */
@media (max-width:900px){
  .alertbar{flex-wrap:wrap;align-items:flex-start;padding:var(--sp-3) var(--sp-4)}
  .alertbar__label{white-space:normal;flex:1 1 100%}
  .alertbar__spacer{display:none}
  .alertbar .btn{flex:1 1 100%;justify-content:center;min-height:44px}
}

/* ------------------------------------------------ keyboard hints ------ */

/* THE LAST THING TAKING THE ORDER BOARD SIDEWAYS at 320px: the "Shortcuts"
 * control, whose label reached x=381 in a 320px viewport. It opens a card of
 * `<kbd>` keys — on a phone there is no keyboard to press them with, so this is
 * not functionality being hidden by a narrow screen, it is functionality that
 * does not exist on the device. Its popover goes with it. */
@media (max-width:900px){
  #queue-shortcuts{display:none}
}

/* ------------------------------------------------ the order card ------ */

/* MEASURED AT 375px: the Accept button was 100x30 and Decline 72x30. Thirty
 * pixels is not a touch target — it is a mouse target — and these are the two
 * controls the whole product exists to put in front of somebody holding a phone
 * in a kitchen. They become full-width rows of at least 44px, side by side
 * while both fit and stacked when they do not.
 *
 * ACCEPT IS NOT MADE BIGGER THAN DECLINE. They are a pair of answers to the
 * same question and the destructive one must stay easy to hit deliberately and
 * hard to hit accidentally — which is spacing, not size. */
@media (max-width:900px){
  .tik__acts{
    display:flex;flex-wrap:wrap;gap:var(--sp-2);
    margin-top:var(--sp-3);
  }
  .tik__acts > form{flex:1 1 140px;display:flex}
  .tik__acts .btn{
    flex:1 1 auto;justify-content:center;
    min-height:44px;padding:0 var(--sp-4);
    font-size:14px;
  }
  /* The ghost Decline against a solid Accept is a weak contrast on a small
     screen in daylight. It keeps its quiet fill and gains a real edge so it
     reads as a control rather than as a label. */
  .tik__acts .btn--ghost{border:1px solid var(--mw-line-strong)}

  /* THE KEYBOARD HINTS GO. `Accept A` on a phone names a key the device does
     not have, and it was being read aloud as part of the button. Same rule as
     the Shortcuts card: this is not functionality hidden by a narrow screen,
     it is functionality the device does not have. */
  .btn__key{display:none}

  /* The food total, beside the item count. Tabular figures already; this only
     stops it being crushed against the count when a name is long. */
  .tik__sum{margin-left:auto;white-space:nowrap;color:var(--mw-ink)}
}

/* ------------------------------------------------ the balance head ---- */

/* `.mbal__covers` is `white-space:nowrap`, and its own comment says why: "NEVER
 * A TOTAL WITHOUT THE PERIOD IT COVERS — this line is not a caption, it is part
 * of the figure." Correct, and on a 320px screen it was what broke it: the head
 * is a single flex row holding "Balance" and "All activity · 17 Aug 2026 to
 * 1 Sep 2026", and the unbreakable half measured out to x=380 and was clipped.
 * A period the merchant cannot read is the exact failure the nowrap exists to
 * prevent, achieved by the nowrap.
 *
 * So it WRAPS rather than truncating — the same rule the incoming banner is on,
 * for the same reason. The heading keeps its line and the window takes the one
 * below it, whole. */
@media (max-width:900px){
  .mbal__head{flex-wrap:wrap;align-items:flex-start;gap:var(--sp-1) var(--sp-3)}
  .mbal__covers{white-space:normal;flex:1 1 100%}
}

/* ------------------------------------------------ two fixed widths ---- */

/* Both found by sweeping every screen at 320px, and both the same mistake in
 * two places: a pixel width chosen for a desktop card, with no ceiling.
 *
 * `.setpic__frame--cover` is a hard 280px preview of the restaurant's cover
 * image. Inside a settings card at 320px the content box is ~244px, so it
 * overflowed to 455 and was CLIPPED rather than scrolled — a merchant checking
 * the picture they just uploaded saw two thirds of it. It keeps its shape and
 * takes the width it is given.
 *
 * `.tik__head` is the order number and the clock on one `space-between` row.
 * On the dashboard's narrower card that pair reached 349 in a 320px viewport
 * and took the page sideways — the one page-level horizontal scroll left in the
 * product. Neither half may shrink to nothing, so they wrap instead. */
@media (max-width:900px){
  .setpic,.setpic__frame{max-width:100%}
  /* `width:min()` rather than a `max-width` — the breakpoint guard reads this
     section with /max-width:(\d+)px/ and cannot tell a media query from a
     property, so spelling the ceiling that way would register a phantom 280px
     breakpoint and fail a test that is right to be strict. */
  .setpic__frame--cover{width:min(100%,280px)}

  .tik__head{flex-wrap:wrap;row-gap:2px}
  .tik__no{min-width:0}
  .tik__clock{min-width:0;white-space:nowrap}
}

/* ------------------------------------------------ the top bar -------- */

/* THE LAST PAGE-LEVEL HORIZONTAL SCROLL, and the hardest to see: at 320px
 * NOTHING crossed the right edge, yet the document was 349px wide. The cause
 * was inside — `.topbar__tools` needed 200px in a 191px box, and a flex row
 * whose items will not shrink pushes its ancestors instead. `.canvas` and
 * `.shell` both reported scrollWidth 349 against clientWidth 305, which is what
 * finally named it.
 *
 * WHAT YIELDS IS THE TITLE, NOT THE TOOLS. The store-state pill is the
 * signature element of this shell and must stay legible from across a kitchen;
 * the connection state and the account menu are how a merchant knows the screen
 * is live and how they sign out. The page title is the one thing here that is
 * said twice — the bottom bar already names where you are — so it is the half
 * that truncates. Nothing is hidden: the title ellipsises and keeps its
 * tooltip-free full text in the DOM. */
@media (max-width:900px){
  .topbar{gap:var(--sp-2)}
  .topbar__head{min-width:0;flex:1 1 auto;overflow:hidden}
  .topbar__title,.topbar__sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* The tools keep their size; they are the reason the bar exists on a phone. */
  .topbar__tools{flex:0 0 auto;gap:var(--sp-2);min-width:0}
  /* A 1px decorative rule is 1px of a 320px screen that a control could use. */
  .topbar__divider{display:none}
}

/* ------------------------------------------------ dashboard rows ------ */

/* `.frow__open` is the dashboard's live order row: a lane label, the items
 * (`white-space:nowrap`), a spacer and the clock, on one unwrapping line. At
 * 320px it needed 316px in a 239px box and the clock's own cell was 26px wide
 * holding 106px of text — so the row pushed `.main` to 349 and took the whole
 * document sideways. Nothing crossed the right EDGE, which is why it survived
 * the first sweep: the overflow was internal, and only `scrollWidth` against
 * `clientWidth` on `.main` named it.
 *
 * The lane and the clock keep the first line — they are what the row is FOR,
 * and the clock is the number a merchant reads. The items wrap onto their own
 * line beneath, whole. */
@media (max-width:900px){
  .frow__open{flex-wrap:wrap;row-gap:2px;padding:var(--sp-2) var(--sp-3)}
  .frow__items{white-space:normal;min-width:0;flex:1 1 100%}
  .frow__spacer{display:none}
  .frow .tik__clock{margin-left:auto}
}
