/* ═══════════════════════════════════════════════════════════════════════
   hey_lovey — application stylesheet
   Derived from design-tokens.css (locked 9 Aug 2026).

   Hand-written CSS rather than Tailwind, deliberately: the design uses
   semantic component classes (.btn, .card, .eyebrow) rather than utility
   soup, which sidesteps the "class must appear verbatim in a .rs file for
   the scanner to see it" trap entirely and removes a node build step from
   the deploy. If utility classes are ever wanted, tokens.css already has
   the Tailwind mapping.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  --stone-50:#FAF7F2; --stone-100:#F3EFE8; --stone-200:#E9E3D9;
  --stone-300:#DBD3C5; --stone-400:#C3B8A6; --stone-500:#A79C8B;
  --paper:var(--stone-50); --line:#E3DCD2; --line-2:#D3C9BB;
  /* Contrast against --paper (#FAF7F2), WCAG AA needs 4.5:1 for body copy.
     --ink-4 was #A79E94 — 2.47:1. It carries `.fnt`, which is nearly every
     piece of 12.5px helper text on the admin screens, so the smallest type
     had the worst contrast. --ink-3 was 4.54:1, passing by a hair; both now
     have real headroom. */
  --ink:#16130F;    /* 17.33:1 */
  --ink-2:#4A443D;  /*  8.99:1 */
  --ink-3:#655D56;  /*  6.04:1 */
  --ink-4:#786E63;  /*  4.67:1 */

  --gold-300:#E4D298; --gold-400:#D2B45A; --gold-500:#C6A542;
  --gold-550:#B59333; --gold-600:#9A7B27; --gold-700:#7C6220;
  --gold-wash:#F8F2DF;

  --sauce-400:#B3452F; --sauce-500:#9A2E1E; --sauce-600:#7F2617;
  --sauce-wash:#F8EAE6;
  --sage:#5F7355; --sage-wash:#EDF1EA;
  --brass:#9C7A43; --brass-wash:#F5EFE2;

  --sh-md:0 4px 6px -1px rgba(22,19,15,.09), 0 2px 4px -2px rgba(22,19,15,.07);
  --sh-lg:0 8px 12px -3px rgba(22,19,15,.11), 0 3px 6px -3px rgba(22,19,15,.08);
  --sh-md-gold:0 4px 6px -1px rgba(84,64,18,.22), 0 2px 4px -2px rgba(84,64,18,.16);
  --sh-lg-gold:0 8px 13px -3px rgba(84,64,18,.26), 0 3px 6px -3px rgba(84,64,18,.18);
  --hl:inset 0 1px 0 rgba(255,255,255,.34);
  --hl-soft:inset 0 1px 0 rgba(255,255,255,.72);

  --serif:'New York',ui-serif,Georgia,'Iowan Old Style','Palatino Linotype',Palatino,serif;
  --sans:-apple-system,BlinkMacSystemFont,'SF Pro Text',Inter,system-ui,sans-serif;
  --mono:ui-monospace,'SF Mono',SFMono-Regular,Menlo,Consolas,monospace;

  --r-md:6px; --h-btn:52px; --h-input:46px;
  --w-app:1080px; --w-read:660px;
  --z-header:40; --z-modal:70; --z-toast:80;
  --t-fast:.07s; --t-base:.18s;
}

*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0}
body{
  background:var(--paper);color:var(--ink);
  font:17px/1.65 var(--sans);
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  min-height:100vh;
}
/* paper grain — keeps flat bone from reading as flat digital grey */
body::before{
  content:'';position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.035;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
main{position:relative;z-index:2;max-width:var(--w-app);margin:0 auto;padding:56px 30px 140px}

/* ── type ─────────────────────────────────────────────────────────── */
h1.d{font:400 46px/1.06 var(--serif);letter-spacing:-.022em;margin:0 0 14px}
h2.d{font:400 30px/1.14 var(--serif);letter-spacing:-.018em;margin:0 0 12px}
h3.d{font:400 22px/1.22 var(--serif);letter-spacing:-.012em;margin:0 0 7px}
.lede{font:400 19px/1.6 var(--sans);color:var(--ink-2);margin:0 0 34px;max-width:var(--w-read)}
p{margin:0 0 16px}
.eyebrow{
  font:500 10.5px/1 var(--mono);letter-spacing:.17em;text-transform:uppercase;
  color:var(--ink-4);display:block;
}
/* An eyebrow labels whatever follows it, and at 10.5px with heavy tracking it
   sits optically tight against anything larger. 16px below, always.

   The elements it precedes set their own `margin-top:0` at class specificity
   (`h1.d`, `.lede`), so a low-specificity rule never reaches them. Naming the
   element here matches that, and being later in the file it wins.

   `:not(.mt)` is not decoration: any selector strong enough to beat `h1.d` is
   also strong enough to beat `.mt`, so without it this floor would pin every
   spacing utility to 16px. Excluding the class makes the rule stop matching
   wherever an explicit override is present. */
.eyebrow + h1:not(.mt),
.eyebrow + h2:not(.mt),
.eyebrow + h3:not(.mt),
.eyebrow + p:not(.mt),
.eyebrow + div:not(.mt),
.eyebrow + table:not(.mt),
.eyebrow + input:not(.mt),
.eyebrow + textarea:not(.mt),
.eyebrow + select:not(.mt){margin-top:16px}

.mono{font-family:var(--mono);font-variant-numeric:tabular-nums}
.sm{font-size:14.5px} .xs{font-size:12.5px}
.mut{color:var(--ink-3)} .fnt{color:var(--ink-4)}
.sauce{color:var(--sauce-500)} .sage{color:var(--sage)} .brass{color:var(--brass)}
.med{font-weight:500} .semi{font-weight:600}

/* ── header ───────────────────────────────────────────────────────── */
header.top{
  position:sticky;top:0;z-index:var(--z-header);
  background:rgba(250,247,242,.86);backdrop-filter:saturate(180%) blur(18px);
  border-bottom:1px solid var(--line);
}
header.top .in{max-width:var(--w-app);margin:0 auto;padding:15px 30px;display:flex;align-items:baseline;gap:22px}
.mark{font:400 21px/1 var(--serif);letter-spacing:-.015em;text-decoration:none;color:var(--ink)}
.mark i{font-style:italic;color:var(--sauce-500)}
.mark-lg{font:400 34px/1 var(--serif);letter-spacing:-.02em;margin-bottom:14px}
.mark-lg i{font-style:italic;color:var(--sauce-500)}
nav.n{margin-left:auto;display:flex;gap:26px}
nav.n a{
  font:500 11px/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-3);text-decoration:none;padding-bottom:3px;
  border-bottom:1px solid transparent;
}
nav.n a:hover{color:var(--ink)}
nav.n a.on{color:var(--ink);border-bottom-color:var(--gold-600)}

/* ── buttons ──────────────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  height:var(--h-btn);padding:0 28px;border-radius:var(--r-md);
  border:1px solid var(--gold-600);
  background:linear-gradient(180deg,var(--gold-400) 0%,var(--gold-500) 52%,var(--gold-550) 100%);
  color:var(--ink);cursor:pointer;
  font:600 15.5px/1 var(--sans);letter-spacing:.008em;
  box-shadow:var(--sh-md-gold), var(--hl);
  transition:filter var(--t-base) ease, box-shadow var(--t-base) ease, transform var(--t-fast) ease;
}
.btn:hover{filter:brightness(1.05);transform:translateY(-1px);box-shadow:var(--sh-lg-gold), inset 0 1px 0 rgba(255,255,255,.42)}
.btn:active{transform:translateY(1px);box-shadow:0 1px 2px rgba(84,64,18,.2), inset 0 1px 3px rgba(84,64,18,.18)}
.btn:disabled{
  background:var(--stone-200);border-color:var(--stone-300);color:var(--stone-500);
  box-shadow:none;filter:none;transform:none;cursor:not-allowed;
}
.btn.w{width:100%}
.btn.sm{height:42px;padding:0 18px;font-size:13.5px}
.btn.gh{
  background:linear-gradient(180deg,#FBF9F5 0%,var(--stone-100) 100%);
  border-color:var(--stone-300);color:var(--ink);font-weight:500;
  box-shadow:var(--sh-md), var(--hl-soft);
}
.btn.gh:hover{filter:none;background:linear-gradient(180deg,#FFF 0%,#F7F4EE 100%);border-color:var(--stone-400);box-shadow:var(--sh-lg), var(--hl-soft)}
.btn.gh:active{transform:translateY(1px);box-shadow:0 1px 2px rgba(22,19,15,.08), inset 0 1px 3px rgba(22,19,15,.09)}
.btn.dgr{color:var(--sauce-600)}
.btn.dgr:hover{border-color:var(--sauce-400)}
/* Chosen. **Must outrank `.btn.gh:hover`**, which is where this went wrong:
   `.btn.sel` and `.btn.gh` are both (0,2,0), so at rest the gold won on
   source order — but hover is (0,2,1) and beat it. The button you had just
   clicked sat under the cursor wearing the plain hover style, so the old
   highlight vanished and the new one never appeared until you moved the
   mouse away.

   `.btn.gh.sel` is (0,3,0) and settles it. The prototype had exactly this
   selector; it was lost when the rule was shortened. */
.btn.sel,
.btn.gh.sel{
  border-color:var(--gold-600);
  background:linear-gradient(180deg,#FCF8EC 0%,var(--gold-wash) 100%);
  box-shadow:var(--sh-md-gold), var(--hl-soft);
}
/* Hovering the chosen one should deepen it, not undo it. */
.btn.gh.sel:hover{
  border-color:var(--gold-600);
  background:linear-gradient(180deg,#FDFAF1 0%,#F5EDD4 100%);
  box-shadow:var(--sh-lg-gold), var(--hl-soft);
}

.lnk{
  font:500 11px/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--gold-700);text-decoration:none;cursor:pointer;
  border-bottom:1px solid var(--gold-wash);padding-bottom:2px;
  transition:border-color var(--t-base) ease;
}
.lnk:hover{border-bottom-color:var(--gold-600)}

/* ── forms ────────────────────────────────────────────────────────── */
.f{margin-bottom:30px}
.f label{display:block;margin-bottom:9px}
.in{
  display:block;width:100%;height:var(--h-input);
  border:0;border-bottom:1px solid var(--stone-300);
  background:transparent;padding:0 0 6px;
  /* 17px, not 15 — below 16px iOS zooms the viewport on focus */
  font:400 17px/1 var(--sans);color:var(--ink);
  transition:border-color var(--t-base) ease;
}
.in::placeholder{color:var(--ink-4)}
/* Numeric fields read as figures, not prose, and align with the mono columns
   they sit beside. Still 17px — below 16px iOS zooms the viewport on focus. */
.in.num{font-family:var(--mono);font-size:17px;letter-spacing:-0.01em}
/* In a table cell the input has to look like a value until you touch it,
   or every row reads as a form. */
.in.cap{
  width:4.5ch;height:auto;text-align:right;padding:2px 4px;
  border:1px solid transparent;border-radius:var(--r-md);
  font-size:15px;transition:border-color var(--t-base) ease,background var(--t-base) ease;
}
.in.cap:hover{border-color:var(--stone-300)}
.in.cap:focus{border-color:var(--gold-600);background:var(--paper)}
/* Zero means nobody can order it — worth seeing at a glance, not just in
   the notice above the table. */
.in.cap.warn{color:var(--brass);background:var(--brass-wash);border-color:var(--brass)}
.hide{display:none}
.in:focus{outline:none;border-bottom-color:var(--gold-600)}
textarea.in{height:auto;line-height:1.6;padding:6px 0;resize:vertical}
select.in{
  -webkit-appearance:none;appearance:none;cursor:pointer;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7'%3E%3Cpath d='M1 1l4.5 4.5L10 1' stroke='%2378706A' fill='none' stroke-width='1.3'/%3E%3C/svg%3E") right 2px center/11px no-repeat;
}

/* ── surfaces ─────────────────────────────────────────────────────── */
.card{background:#FDFCFA;border:1px solid var(--stone-200);border-radius:var(--r-md);padding:20px}
.aside{border-left:2px solid var(--gold-500);padding:2px 0 2px 18px;margin:0 0 30px;color:var(--ink-2)}
.aside b{color:var(--ink);font-weight:600}
.aside.err{border-left-color:var(--sauce-500);color:var(--sauce-600)}
.rule{height:1px;background:var(--line);border:0;margin:0}
.stack{display:flex;flex-direction:column;gap:12px}

/* ── status ───────────────────────────────────────────────────────── */
.st{
  font:500 10.5px/1 var(--mono);letter-spacing:.15em;text-transform:uppercase;
  display:inline-flex;align-items:center;gap:7px;
}
.st::before{content:'';width:5px;height:5px;border-radius:50%;background:currentColor}
.st.live{color:var(--sauce-500)} .st.good{color:var(--sage)}
.st.caut{color:var(--brass)} .st.off{color:var(--ink-4)}

/* ── toasts ───────────────────────────────────────────────────────── */
.toast-wrap{
  position:fixed;top:20px;left:0;right:0;z-index:var(--z-toast);
  padding:0 30px;pointer-events:none;
  display:flex;flex-direction:column;align-items:center;gap:8px;
}
.toast{
  pointer-events:auto;max-width:var(--w-read);width:100%;
  border-radius:var(--r-md);border:1px solid;padding:14px 18px;
  font-size:14.5px;font-weight:500;box-shadow:var(--sh-lg);
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  animation:toast-in .2s ease;
}
@keyframes toast-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.toast-ok{background:var(--sage-wash);border-color:var(--sage);color:#3D4C36}
.toast-err{background:var(--sauce-wash);border-color:var(--sauce-400);color:var(--sauce-600)}
.toast-retry{
  flex:0 0 auto;background:none;border:1px solid currentColor;color:inherit;
  border-radius:var(--r-md);padding:6px 12px;cursor:pointer;
  font:500 10.5px/1 var(--mono);letter-spacing:.13em;text-transform:uppercase;
}

/* ── auth shell ───────────────────────────────────────────────────── */
.auth-shell{max-width:400px;margin:0 auto;padding-top:60px}
.auth-mark{margin-bottom:52px}
.auth-foot{margin-top:38px;text-align:center}

/* ── menu ─────────────────────────────────────────────────────────── */
.dish{
  display:grid;grid-template-columns:280px 1fr auto;gap:36px;align-items:center;
  padding:44px 0;border-bottom:1px solid var(--line);
}
.dish:last-child{border-bottom:0}
.dish-body h3.d{margin:12px 0 9px}
.dish-meta{display:flex;gap:22px;align-items:baseline;margin-top:16px}
/* The selling copy. Held to a readable measure — a description running the
   full width of a 1080px card is not read, it is skimmed past. */
.dish-desc{max-width:52ch;margin:0}
.dish-meta .price{font-size:19px}
.plate{
  aspect-ratio:5/4;border-radius:var(--r-md);position:relative;overflow:hidden;
  box-shadow:inset 0 0 0 1px rgba(22,19,15,.05);
}
.plate::after{
  content:'';position:absolute;inset:0;
  background:radial-gradient(120% 90% at 30% 20%,rgba(255,255,255,.34),transparent 62%);
}
.p1{background:linear-gradient(158deg,#E9DDC9,#C7A579)}
.p2{background:linear-gradient(158deg,#F0DAC0,#C67E48)}
.p3{background:linear-gradient(158deg,#E4CEBE,#A76848)}
.p4{background:linear-gradient(158deg,#F3DDD3,#CE886C)}
.p5{background:linear-gradient(158deg,#EED6C3,#B3603B)}
.p6{background:linear-gradient(158deg,#E7E5CF,#98A16D)}

/* A real photograph in the plate slot. `object-fit:cover` keeps the 5:4 crop
   whatever the operator shot, so one tall phone photo can't break the grid. */
img.plate{
  display:block;width:100%;height:auto;object-fit:cover;background:var(--stone-100);
  /* A real hairline, the same one the recipe editor's preview frame uses, so
     the menu and the admin preview look like the same object.
     `.plate`'s inset shadow is dropped in favour of it: an inset shadow is
     drawn *over* the image and muddies the top edge of a dark photo, whereas
     a border sits outside the content box.
     `box-sizing:border-box` is global, so this costs no layout width. */
  box-shadow:none;
  border:1px solid var(--stone-300);
}
/* The sheen sells a painted gradient; over a photo it just looks like haze. */
img.plate::after{content:none}

/* ── recipe editor: the photo card ── */
/* The frame is the same 5:4 plate the menu uses, so what you see here is what
   a guest sees. `position:relative` + absolutely-filled children means the
   box keeps its shape whether it holds an image, the empty state, or nothing
   at all — previously the well collapsed while an image loaded, and the card
   jumped. */
.photo-well{
  position:relative;aspect-ratio:5/4;width:100%;
  border-radius:var(--r-md);overflow:hidden;
  border:1px solid var(--stone-300);background:var(--stone-100);
}
.photo-well > *{position:absolute;inset:0}
.photo-preview{
  display:block;width:100%;height:100%;
  /* cover, not contain: the menu crops to 5:4 too, so a portrait photo is
     previewed the way it will actually appear rather than letterboxed. */
  object-fit:cover;object-position:center;
}
.photo-empty{
  display:flex;align-items:center;justify-content:center;
  padding:18px;text-align:center;
}
/* `.hide` is display:none, which would fight the absolute fill above. */
.photo-well > .hide{display:none}

/* ── stepper ──────────────────────────────────────────────────────── */
.step{display:inline-flex;align-items:center;gap:2px}
.step button{
  width:38px;height:38px;border-radius:var(--r-md);border:1px solid var(--stone-300);
  background:linear-gradient(180deg,#FBF9F5 0%,var(--stone-100) 100%);
  color:var(--ink);font:400 17px/1 var(--sans);cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  box-shadow:var(--sh-md), var(--hl-soft);
  transition:border-color var(--t-base) ease, box-shadow var(--t-base) ease, transform var(--t-fast) ease;
}
.step button:hover{border-color:var(--stone-400);transform:translateY(-1px);box-shadow:var(--sh-lg), var(--hl-soft)}
.step button:active{transform:translateY(1px);box-shadow:0 1px 2px rgba(22,19,15,.08), inset 0 1px 3px rgba(22,19,15,.1)}
.step .v{min-width:34px;text-align:center;font:500 16px/1 var(--mono);font-variant-numeric:tabular-nums}
.step.off .v{color:var(--ink-4)}

/* ── sticky bars ──────────────────────────────────────────────────── */
.bar{
  position:fixed;bottom:0;left:0;right:0;z-index:var(--z-header);
  background:rgba(250,247,242,.94);backdrop-filter:saturate(180%) blur(18px);
  border-top:1px solid var(--line-2);
  padding:16px 30px calc(16px + env(safe-area-inset-bottom));
}
.bar .in2{max-width:var(--w-app);margin:0 auto;display:flex;align-items:center;gap:24px}
.bar-text{flex:1;display:flex;flex-direction:column;gap:4px}
.bar-text .big{font-size:19px}

/* ── receipt ──────────────────────────────────────────────────────── */
.rcp{width:100%;border-collapse:collapse;margin:0 0 34px}
.rcp td{padding:15px 0;vertical-align:baseline;border-bottom:1px solid var(--line)}
.rcp tr:last-child td{border-bottom:0}
.rcp td.r{text-align:right;font-family:var(--mono);font-variant-numeric:tabular-nums;white-space:nowrap}
.rcp .tot td{border-bottom:0;border-top:1px solid var(--ink);padding-top:20px}
.grand{font:400 23px/1 var(--serif);letter-spacing:-.015em}
.grand-n{font-size:23px;font-weight:600}
.totals{margin-top:34px}

/* ── cart ─────────────────────────────────────────────────────────── */
.date-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:16px 0 26px}
.date-opt{height:auto;padding:20px;flex-direction:column;gap:6px;align-items:flex-start}
.addr-row,.promo-row{padding:28px 0}
.addr{margin:11px 0 0;line-height:1.55}
.promo-in{display:flex;gap:16px;align-items:flex-end}
.promo-in .in{flex:1}
/* Braintree renders its own markup inside here with no outer padding of its
   own, so the card fields sat flush against the border. 16px all round. */
.dropin{
  margin-top:18px;min-height:132px;border:1px solid var(--stone-300);
  border-radius:var(--r-md);background:var(--stone-100);
  padding:16px;
}
/* The Drop-In sets its own background; letting ours show through the gap is
   what makes the padding read as deliberate rather than as a misalignment. */
.dropin .braintree-dropin{background:transparent}
.pay-note{margin-top:12px}

/* ── address autocomplete ─────────────────────────────────────────── */
.addr-suggs{
  margin-top:6px;border:1px solid var(--stone-300);border-radius:var(--r-md);
  background:var(--stone-50);overflow:hidden;
}
.addr-sugg{
  display:block;width:100%;text-align:left;cursor:pointer;
  padding:11px 14px;border:0;border-bottom:1px solid var(--line);
  background:transparent;color:var(--ink);
  /* 16px minimum, or iOS zooms the viewport when the field above is focused. */
  font:400 16px/1.35 var(--sans);
}
.addr-sugg:last-child{border-bottom:0}
.addr-sugg:hover,.addr-sugg:focus{background:var(--stone-100);outline:none}

/* Sits under a disabled button, so it must not look like part of it. */
.bar-missing{margin:8px 0 0;color:var(--sauce-500);text-align:right}

/* ── the bundle deal card ─────────────────────────────────────────── */
/* Two numbers side by side read as one sentence: "every [2] meals, take off
   [5] dollars". Stacked, they read as two unrelated settings. */
.bundle-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.bundle-grid .in.num{width:100%}

/* ── the board's state dropdown ────────────────────────────────────── */
/* Reads as a value until touched, like the menu's cap field — a table of
   fully-drawn form controls is exhausting to scan. */
.state-pick{
  width:auto;min-width:11ch;height:auto;padding:3px 22px 3px 8px;
  border:1px solid transparent;border-bottom-color:transparent;
  border-radius:var(--r-md);font-size:13.5px;
  background-position:right 6px center;
  transition:border-color var(--t-base) ease,background-color var(--t-base) ease;
}
.state-pick:hover{border-color:var(--stone-300);background-color:var(--stone-50)}
.state-pick:focus{border-color:var(--gold-600);background-color:var(--paper);outline:none}
.confirm{max-width:520px;margin:0 auto;padding-top:40px}

/* ── timeline ─────────────────────────────────────────────────────── */
.tl{list-style:none;margin:0 0 44px;padding:0 0 0 26px;position:relative}
.tl::before{content:'';position:absolute;left:4px;top:7px;bottom:9px;width:1px;background:var(--line-2)}
.tl li{position:relative;padding:0 0 26px}
.tl li:last-child{padding-bottom:0}
.tl li::before{
  content:'';position:absolute;left:-26px;top:5px;width:9px;height:9px;border-radius:50%;
  background:var(--paper);border:1px solid var(--line-2);
}
.tl li.done::before{background:var(--sage);border-color:var(--sage)}
.tl li.now::before{background:var(--sauce-500);border-color:var(--sauce-500);box-shadow:0 0 0 4px var(--sauce-wash)}
.tl li.now .med{color:var(--sauce-500)}

/* ── orders ───────────────────────────────────────────────────────── */
.order-live{padding:44px 0;border-bottom:1px solid var(--line)}
.order-head{align-items:flex-start;margin-bottom:32px}
.order-head h3.d{margin:12px 0 6px}
.order-grid{display:grid;grid-template-columns:1fr 1fr;gap:44px}
.olines{margin-top:14px;line-height:2}
.oline .mono{color:var(--ink-3);margin-right:8px}
.order-past{padding:34px 0;border-bottom:1px solid var(--line)}
.order-past:last-child{border-bottom:0}
.order-past h3.d.small{font-size:20px;margin:11px 0 5px}
.order-right{text-align:right}
.order-actions{display:flex;gap:16px;justify-content:flex-end;margin-top:12px}
.struck{color:var(--ink-4);text-decoration:line-through}
/* `h2.past-head`, not `.past-head`.
   The element is `<h2 class="d past-head">`, and `h2.d{margin:0 0 12px}` is
   (0,1,1) while a bare `.past-head` is only (0,1,0) — so the 44px written
   here never applied and "Before that" sat flush against whatever preceded
   it. Matching on the element lifts this to (0,1,1) and, being later in the
   file, it now wins. Third time this trap has bitten (`.btn.sel`, `.mt`):
   when overriding a rule that names its element, name the element too. */
h2.past-head{font-size:21px;margin:44px 0 0}
/* No margin of its own. Space below the rule comes from whatever follows —
   `h2.past-head`'s 44px (which collapses with nothing above it, so it is the
   whole gap) or `.order-live`'s 44px padding. Giving the rule its own bottom
   margin looked right in the empty case and stacked to 60px in the common
   one, because padding does not collapse. */
.rule.dk{background:var(--line-2)}

/* ── account ──────────────────────────────────────────────────────── */
.sect{margin-top:26px}
hr.sect{margin:44px 0}
.addr-grid{display:grid;grid-template-columns:2fr 1fr 1fr;gap:20px}
.toggle-row{padding:20px 0;border-bottom:1px solid var(--line);cursor:pointer;align-items:flex-start}
.toggle-row:last-child{border-bottom:0}
.toggle-row .med{display:block}
.toggle-row .xs{display:block;margin-top:3px}
.chk{width:22px;height:22px;accent-color:var(--gold-600);cursor:pointer;flex:0 0 auto}
.wash{background:var(--stone-100);border:1px solid var(--stone-200);border-radius:var(--r-md);padding:16px 20px}
.acct-foot{padding:8px 0 0}
.lnk.quiet{color:var(--ink-3);border-bottom-color:var(--line)}

/* ── admin ────────────────────────────────────────────────────────── */
header.top.admin{background:rgba(243,239,232,.9)}
.mark .chip{
  font:500 8.5px/1 var(--mono);letter-spacing:.16em;color:var(--ink-4);
  border:1px solid var(--stone-300);border-radius:4px;padding:3px 5px;
  margin-left:9px;vertical-align:middle;
}
.admin-head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;margin-bottom:28px}
.admin-head h1.d{font-size:34px;margin:12px 0 0}
.head-tools{display:flex;gap:10px;align-items:center}
/* Used in 12 places and never defined, so every one of them was a plain
   block: the label and its control stacked instead of sitting on one line.
   `.toggle-row` is the tell — it sets `align-items:flex-start`, which means
   nothing unless something else makes the row a flex container. */
.between{display:flex;align-items:center;justify-content:space-between;gap:18px}
/* The select and its Add button. Was undefined, so the two sat with only the
   newline between them for separation. */
.add-ing{display:flex;align-items:center;gap:12px}
/* "Nothing here yet" spanning a whole table. */
.empty-cell{padding:22px 12px;color:var(--ink-4);text-align:center}
.in.auto{width:auto;border:1px solid var(--stone-300);border-radius:var(--r-md);height:44px;padding:0 12px;background:#FDFCF9}
.lnk.back{display:inline-block;margin-bottom:16px}
.ord{font-size:26px}

.stats{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));
  border:1px solid var(--stone-200);border-radius:var(--r-md);
  overflow:hidden;background:#FDFCFA;margin-bottom:26px;
}
.stat{padding:17px 19px;border-right:1px solid var(--line)}
.stat:last-child{border-right:0}
.stat .n{font:500 25px/1 var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.02em;display:block;margin-bottom:7px}
.stat .k{font:500 9.5px/1 var(--mono);letter-spacing:.15em;text-transform:uppercase;color:var(--ink-4)}
.stat .s{font-size:11.5px;margin-top:6px;color:var(--ink-3)}
.meter{height:4px;background:var(--stone-200);border-radius:2px;overflow:hidden;margin-top:9px}
.meter span{display:block;height:100%;background:var(--gold-500)}

.card-h{padding:14px 20px;border-bottom:1px solid var(--line)}
.card-p{padding:20px}
.card.scroll{overflow-x:auto;margin-bottom:18px}
table{width:100%;border-collapse:collapse;font-size:13.5px}
th{
  text-align:left;padding:11px 12px;
  font:500 9.5px/1.4 var(--mono);letter-spacing:.14em;text-transform:uppercase;
  color:var(--ink-4);border-bottom:1px solid var(--line-2);white-space:nowrap;
}
th.r,td.r{text-align:right}
td{padding:12px;border-bottom:1px solid var(--line);vertical-align:middle}
tbody tr:last-child td{border-bottom:0}
tbody tr:hover{background:rgba(198,165,66,.045)}
tr.flag{background:var(--brass-wash)}
tr.dim{opacity:.55}
table.mt{margin-top:14px}

.split{display:grid;grid-template-columns:1fr 322px;gap:22px;align-items:start}
.split .card{margin-bottom:18px}
.kvs{margin-top:16px}
.kv{display:flex;align-items:baseline;justify-content:space-between;gap:14px;padding:7px 0}
.note{border-left:2px solid var(--gold-500);padding:2px 0 2px 16px;margin:0 0 20px;color:var(--ink-2);font-size:13.5px}
.note b{color:var(--ink)}
.caution{
  border-left:2px solid var(--brass);background:var(--brass-wash);
  border-radius:0 var(--r-md) var(--r-md) 0;padding:14px 18px;margin:0 0 20px;
  font-size:13.5px;color:var(--ink-2);
}
.caution b{color:var(--ink)}
/* ── vertical rhythm ──────────────────────────────────────────────────
   A floor, not a fixed rhythm: no two blocks of copy sit closer than 8px.

   Written with :where(), which contributes **zero specificity**, so every
   explicit utility still wins — .mt raises it to 14px, .nomb removes the
   trailing gap. Inside a card the paragraph's own bottom margin is dropped
   first, so spacing comes from one direction only and never doubles. */
:where(.card, .card-p, .f, .wash, .note, .caution, .photo-empty) p{margin-bottom:0}
:where(.card, .card-p, .f, .wash, .note, .caution, .photo-empty) > * + *,
/* `p` sets `margin:0 0 16px` at element specificity, which outranks a bare
   :where() selector — so the floor never reached the very elements it was
   written for. Naming the element here matches that specificity, and being
   later in the file it wins; `.mt` (a class) still outranks both. */
:where(.card, .card-p, .f, .wash, .note, .caution, .photo-empty) > * + p{margin-top:8px}

/* A status line with nothing to say should take no room, rather than holding
   open a gap where a message will sometimes appear. */
#recipe-photo-status:empty{display:none}

.mt{margin-top:14px}
h3.d.mt{margin-top:14px}

/* ── reviews on the recipe screen ─────────────────────────────────── */
/* Stars as text rather than glyphs in a font that may not have them.
   The dim half carries the unearned stars so the row is a fixed width and
   the column does not ragged-edge down the table. */
.stars{font-size:14px;letter-spacing:2px;color:var(--gold-550);white-space:nowrap}
.stars .off{color:var(--stone-400)}
/* A guest's own words. Not truncated — the whole point of opening this table
   is to read them — but held to a comfortable measure. */
.rev-comment{max-width:52ch;color:var(--ink-2)}
.rev-none{color:var(--ink-4);font-style:italic}

/* prep sheet — printed and taped to a fridge */
.prep{margin-top:20px;font-size:20px}
.prep td{padding:16px 12px;border-bottom:1px solid var(--line-2)}
.prep-n{width:70px;font-size:26px;font-weight:600;text-align:right}
.prep-name{font-family:var(--serif);font-size:22px}
.prep-box{width:44px}
.prep-box::after{content:'';display:block;width:26px;height:26px;border:2px solid var(--ink-3);border-radius:4px}
.print-only{display:none}
@media print{
  body::before,header.top,.no-print,.bar{display:none !important}
  .print-only{display:block}
  main{padding:0}
  .prep td{border-color:#999}
}

@media (max-width:1080px){.split{grid-template-columns:1fr}}
@media (max-width:820px){
  .admin-head{flex-direction:column;align-items:flex-start}
  .admin-head h1.d{font-size:27px}
  .dish{grid-template-columns:1fr;gap:20px}
  .date-grid{grid-template-columns:1fr}
  .order-grid{grid-template-columns:1fr;gap:28px}
  .addr-grid{grid-template-columns:1fr 1fr}
  .bar{padding:14px 22px calc(14px + env(safe-area-inset-bottom))}
  .grand,.grand-n{font-size:20px}
}

@media (max-width:820px){
  main{padding:38px 22px 120px}
  h1.d{font-size:35px}
  .lede{font-size:17px}
  header.top .in{padding:14px 22px}
  nav.n{gap:16px}
  .auth-shell{padding-top:32px}
}

@media (prefers-reduced-motion:reduce){
  *{transition:none !important;animation:none !important}
}
