:root {
  --paper: #f7f6ec;
  --ink: #111111;
  --lime: #c6ff00;
  --lime-soft: #dcff70;
  --amber: #ffa94d;
  --green: #16c172;
  --red: #ff5252;
  --border: 3px solid var(--ink);
  --shadow: 6px 6px 0 var(--ink);
  --shadow-sm: 4px 4px 0 var(--ink);
  --mono: "Space Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --sans: "Archivo", -apple-system, "Helvetica Neue", Arial, sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.45;
  /* The footer sits at the BOTTOM OF THE SCREEN on a short page, not
     bunched up under the widget. One rule on body covers every screen,
     including the pages that carry their own <body> rather than extending
     base — walk-in, team, holding, the edit form.

     svh, not vh: on iOS vh is the tall-viewport height, so 100vh puts the
     footer just below the fold and the page scrolls for no reason. */
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}
/* takes the slack, so the footer is pushed down rather than floated.
   width: 100% is NOT decoration: main has margin: 0 auto, and auto margins
   on a flex item cancel the stretch, so main was sizing itself to its
   content — a seven-chip day strip made it 560px on a 375px phone and the
   whole booking page was cut off down the right (Thom, 30 Sep: "hideous
   on my phone"). Full width, then max-width caps it as before. */
body > main { flex: 1 0 auto; width: 100%; }
body > footer { margin-top: auto; }

/* ------------------------------------------------ header */
header {
  /* sticky, not relative — it still anchors the floating menu, and the way
     back in (Log in, the menu on admin) is now reachable from anywhere on
     the page instead of only from the top of it. Below every overlay:
     .card-back is 50, .confirm-over 60, .modal 70, the billing lock 80. */
  position: sticky; top: 0; z-index: 45;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 28px;
  border-bottom: var(--border);
  background: var(--paper);
}
/* so an anchor jump does not land the target underneath the header */
html { scroll-padding-top: 92px; }
.logo { display: flex; align-items: center; gap: 12px; text-decoration: none; color: var(--ink); }
.logo-mark {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  background: var(--ink); color: var(--lime);
  font-family: var(--mono); font-weight: 700; font-size: 22px;
  border: var(--border); box-shadow: var(--shadow-sm);
}
.logo-name { font-weight: 900; font-size: 24px; letter-spacing: -0.5px; }
nav { display: flex; gap: 10px; align-items: center; }
nav a {
  font-family: var(--mono); font-size: 13px; text-decoration: none;
  color: var(--ink); padding: 8px 12px;
}
nav a.active, nav a:hover { background: var(--ink); color: var(--paper); }
/* the logged-out header. Log in is the boxed one because it is the link a
   venue already on Turn comes back for; signing up has the whole page. */
.pubnav { gap: 4px; }
.pubnav .hdr-login { border: 2px solid var(--ink); box-shadow: 3px 3px 0 var(--ink); }
.pubnav .hdr-login:hover { background: var(--lime); color: var(--ink); }
@media (max-width: 440px) { .pubnav a { padding: 8px 9px; font-size: 12px; } }

/* burger + drop-down menu — same block as the logo mark: black square, lime marks */
.burger {
  width: 44px; height: 44px; padding: 0; cursor: pointer;
  background: var(--ink); border: var(--border); box-shadow: var(--shadow-sm);
  display: grid; place-content: center; gap: 5px;
}
.burger span { display: block; width: 20px; height: 3px; background: var(--lime); }
.burger.open { background: var(--lime); }
.burger.open span { background: var(--ink); }
.burger:active { transform: translate(3px, 3px); box-shadow: none; }
/* floats over the page under the burger — never pushes content down */
/* a card hanging off the burger, inset from the edge with its full
   border and the house shadow — flush-right and borderless read as
   clipped (Thom, 1 Oct) */
.menu {
  position: absolute; top: calc(100% + 8px); right: 20px; z-index: 40;
  width: min(240px, 78vw);
  display: flex; flex-direction: column; align-items: stretch; gap: 0;
  background: #fff; border: var(--border);
  box-shadow: var(--shadow);
}
.menu[hidden] { display: none; }
.menu a {
  font-family: var(--mono); font-size: 14px; letter-spacing: 1px;
  text-decoration: none; color: var(--ink); text-align: left;
  padding: 13px 20px; border-bottom: 2px solid var(--ink);
}
.menu a:last-child { border-bottom: none; }
.menu a:hover { background: var(--lime-soft); }
.menu a.active { background: var(--ink); color: var(--lime); }

main { max-width: 1100px; margin: 0 auto; padding: 40px 28px 80px; }
main.narrow { max-width: 560px; }
/* the venue's own desk — wider than a guest page, it has tables in it */
main.desk { max-width: 820px; }
/* the booking page is built to fit one screen, so it doesn't get the site's
   generous top and bottom padding — every pixel here is widget */
main.booking { padding-top: 18px; padding-bottom: 12px; }
main.booking h1 { margin-bottom: 0; }
/* details sit higher in the widget so the address underneath has air */
main.booking .scroll-area { padding-top: 16px; }

/* ------------------------------------------------ type */
.kicker {
  display: inline-block;
  font-family: var(--mono); font-size: 13px; letter-spacing: 2px;
  background: var(--ink); color: var(--paper);
  padding: 8px 14px; margin-bottom: 18px; text-transform: uppercase;
}
h1 { font-weight: 900; font-size: clamp(34px, 6vw, 56px); letter-spacing: -1.5px; line-height: 1.05; }
h1 .hl { background: var(--lime); padding: 0 10px; box-decoration-break: clone; }
.sub .hl { background: var(--lime); padding: 1px 6px; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
/* h2 is the mono section label; this is the other kind — a real headline
   inside a panel. */
.big-head {
  font-family: inherit; font-weight: 900; text-transform: none;
  letter-spacing: -1px; font-size: clamp(26px, 4vw, 40px);
  line-height: 1.08; margin: 0;
}
.big-head .hl { background: var(--lime); padding: 0 8px; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
.sec-pane { margin-top: 18px; }
.sec-pane[hidden] { display: none; }
.sub { margin-top: 14px; font-size: 18px; max-width: 46ch; }
/* Hero claim list — mono lines with a lime mark. */
.hero-points {
  list-style: none; margin: 22px 0 0; padding: 0; max-width: 46ch;
  display: grid; gap: 9px;
}
.hero-points li {
  font-family: var(--mono); font-size: 14px; line-height: 1.45;
  display: flex; gap: 10px; align-items: baseline;
}
/* A lime block inside an ink border — the same shape as .hl and the
   buttons. Lime on its own is not legible on paper. */
.hero-points .pt-mark {
  width: 9px; height: 9px; flex: 0 0 auto;
  align-self: flex-start; margin-top: 5px;
  background: var(--lime); border: 2px solid var(--ink);
}
h2 {
  font-family: var(--mono); font-size: 14px; letter-spacing: 2px;
  text-transform: uppercase; margin: 26px 0 12px;
}
.sec-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
}
.sec-head h2 { margin-bottom: 12px; }

/* ------------------------------------------------ cards / panels */
.panel { background: #fff; border: var(--border); box-shadow: var(--shadow); }
.panel-head {
  display: flex; align-items: center; justify-content: space-between;
  background: var(--ink); color: var(--paper);
  font-family: var(--mono); font-size: 13px; letter-spacing: 2px;
  padding: 12px 16px; text-transform: uppercase;
}
.panel-head .dot { color: var(--lime); margin-right: 8px; }
.panel-head .head-link {
  background: none; border: 0; padding: 0; color: var(--lime); cursor: pointer;
  font-family: var(--mono); font-size: 11px; letter-spacing: 1px;
  text-transform: uppercase; text-decoration: underline;
  white-space: nowrap; flex: 0 0 auto; margin-left: 12px;
}
/* on a phone the step title gives way, the link never breaks */
.panel-head > span:first-child { min-width: 0; }
@media (max-width: 420px) { .panel-head { letter-spacing: 1px; font-size: 12px; } }
.panel-body { padding: 22px; }

/* ------------------------------------------------ forms */
label { font-family: var(--mono); font-size: 12px; letter-spacing: 1.5px; text-transform: uppercase; display: block; margin: 16px 0 6px; }
input[type=text], input[type=tel], input[type=time], input[type=number], select, textarea {
  width: 100%; padding: 12px 14px;
  font-family: var(--mono); font-size: 15px;
  border: var(--border); background: #fff; color: var(--ink);
}
input:focus, select:focus, textarea:focus { outline: 3px solid var(--lime); outline-offset: -1px; }
/* the OS default is a blue tick, which is the one off-brand colour on the page */
input[type=checkbox], input[type=radio] { accent-color: var(--lime); }

.btn {
  display: inline-block; cursor: pointer;
  font-family: var(--mono); font-weight: 700; font-size: 15px;
  letter-spacing: 1px; text-transform: uppercase; text-decoration: none; text-align: center;
  color: var(--ink); background: var(--lime);
  border: var(--border); box-shadow: var(--shadow-sm);
  padding: 14px 22px;
}
.btn:active { transform: translate(4px, 4px); box-shadow: none; }
.btn.ghost { background: #fff; }
.btn.dark { background: var(--ink); color: var(--lime); }
.btn.danger { background: var(--red); color: #fff; }
.btn.sm { padding: 8px 12px; font-size: 12px; box-shadow: 3px 3px 0 var(--ink); }
.btn.block { display: block; width: 100%; }

/* ------------------------------------------------ chips (dates / times / party) */
.chip-row { display: flex; flex-wrap: wrap; gap: 10px; }
/* When a venue's own mark carries the page, what the page is FOR drops to
   a line under it rather than competing with it (Thom, 2 Oct). */
.brandmark-sub {
  font-family: var(--mono); font-size: 13px; opacity: .75;
  margin: 8px 0 0; letter-spacing: .2px;
}
/* The "nothing to pick yet" line inside a row of chips. It is a sentence,
   not a chip, so it takes the whole row — and the row is laid out two
   different ways depending on which picker it is. Covers and times are
   GRIDS (repeat(8) and repeat(3)), everything else is flex, so the
   placeholder landed in a single 27px column and read one word per line
   (Thom, 2 Oct). Both properties, because a span cannot know which parent
   it is in: grid-column is ignored under flex and flex under grid. */
.chip-row > .note { flex: 1 0 100%; grid-column: 1 / -1; margin: 0; }
/* The pickers are <button> so a keyboard, a screen reader and voice control
   can all use them — a <div> can only be clicked. These three lines keep a
   button looking exactly like the div it replaced, so nothing moves. */
/* NOT `font: inherit` — button.chip outranks .chip on specificity, so that
   shorthand was quietly overriding .chip's mono with the body sans on every
   chip that had been converted from a div to a button. */
button.chip {
  color: inherit; text-align: inherit; line-height: inherit;
  -webkit-appearance: none; appearance: none;
}
/* A chip that is a LINK — the feedback ranges, the directory, the floor
   rooms — was coming out as a blue underlined browser link in the system
   face, because .chip never reset an anchor the way it resets a button
   (Thom, 1 Oct). Four of them across the app, all wrong the same way. */
a.chip {
  color: inherit; text-decoration: none; display: inline-block;
}
a.chip:hover { background: var(--lime-soft); }
a.chip.on:hover { background: var(--lime); }
.chip:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }
/* the week as one sideways swipe. Scrolls inside itself — the page never
   scrolls horizontally — and the chips keep their width instead of squashing */
.day-strip {
  flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
  padding-bottom: 8px; margin-bottom: -4px;
}
.day-strip .chip { flex: 0 0 auto; scroll-snap-align: start; text-align: center; line-height: 1.25; }
/* One right edge for Day, People and Time (Thom, 30 Sep). The strip already
   clips at the widget's inner edge; the fade is the tease that there's more,
   and it lives INSIDE that edge. People and Time fill the same width as
   grids instead of wrapping ragged and stopping short of it. */
.day-strip { -webkit-mask-image: linear-gradient(to right, #000 82%, transparent 100%);
             mask-image: linear-gradient(to right, #000 82%, transparent 100%); }
.day-strip.at-end { -webkit-mask-image: none; mask-image: none; }
#party.chip-row { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 8px; }
#party .chip { padding-left: 0; padding-right: 0; text-align: center; }
#times.chip-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
#times .chip { text-align: center; padding-left: 8px; padding-right: 8px; }
#times .note, #times .wait-offer { grid-column: 1 / -1; }
/* the staff time picker: same chips as the widget, full ones greyed but
   still pickable (Thom, 1 Oct) */
#timepick .times { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin-bottom: 10px; }
#timepick .times .chip { text-align: center; padding-left: 4px; padding-right: 4px; }
#timepick .chip.full { opacity: .4; }
#timepick .chip.full.on { opacity: 1; }
#timepick .chip.off { background: var(--paper); }
#timepick .svc { display: block; font-family: var(--mono); font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; margin: 8px 0 6px; }
#timepick .fold { margin-top: 4px; }
/* staff people chips: nine across, the ninth is 9+ */
#bform #party.chip-row { grid-template-columns: repeat(9, minmax(0, 1fr)); }
/* Covers on the PREPAID widget. A set menu starts at its own minimum, so
   the numbers are few and each one matters — they get real squares you can
   hit with a thumb and scroll along, the same way as the day above them,
   rather than eight grid columns squeezed to fit (Thom, 2 Oct). Scoped to
   #bookform so the booking and waitlist widgets keep their own grids.
   Two ids beat the one-id grid rule above. */
/* The menu picker reads as two lines: what it is, then what it costs. */
.pickbtn .pb-name { display: block; line-height: 1.25; }
.pickbtn .pb-price { display: block; font-size: 13px; opacity: .75; margin-top: 3px; }
#bookform #party.chip-row {
  display: flex; gap: 10px;
}
#bookform #party .chip {
  flex: 0 0 auto; scroll-snap-align: start;
  width: 58px; height: 58px; padding: 0;
  display: grid; place-items: center;
  font-size: 18px; line-height: 1; text-align: center;
}
@media (max-width: 420px) { #timepick .times { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* two lines, one face: "Wed" over "30th Sep" (Thom, 30 Sep) */
.day-strip .d-day, .day-strip .d-date { display: block; font-family: var(--mono); letter-spacing: 0; }
.day-strip .d-day { font-size: 12px; opacity: .7; margin-bottom: 3px; }
.day-strip .d-date { font-size: 14px; }
.day-strip .chip.on .d-day { opacity: .85; }

/* The booking widget scrolls inside itself instead of dragging the whole page.
   svh, not vh: on a phone vh is the tall-viewport height, so a vh-sized box
   hides its own bottom behind the browser chrome. overscroll-behavior stops
   the page scrolling on behind it once you hit the end of the box. */
.scroller { position: relative; }
.scroll-area {
  max-height: min(66svh, 660px);
  overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
/* the cue: same lime square as the fold markers, floating over the bottom edge */
.scroll-cue {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
  width: 38px; height: 38px; padding: 0; cursor: pointer;
  display: grid; place-items: center; color: var(--ink);
  background: var(--lime); border: 3px solid var(--ink);
  box-shadow: 4px 4px 0 var(--paper);
  transition: opacity .18s ease, transform .18s ease;
}
.scroll-cue:hover { transform: translateX(-50%) translateY(2px); }
/* .at-end is set the moment there's nothing left below — and when the widget
   never needed to scroll at all, so it can't sit there pointing at nothing */
.scroller.at-end .scroll-cue {
  opacity: 0; pointer-events: none; transform: translateX(-50%) translateY(6px);
}
@media (prefers-reduced-motion: reduce) {
  .scroll-cue, .scroll-cue:hover { transition: none; transform: translateX(-50%); }
}

/* The booking, in one line instead of four stacked label-and-value blocks —
   date, time, size, and that's the lot. The number isn't here: they confirmed
   on that handset a second ago, so printing it back is a row of dead digits. */
.bk-line {
  font-family: var(--mono); font-size: 17px; font-weight: 700;
  margin: 0 0 4px; line-height: 1.5;
}
/* what they told us — shown so they can see it landed */
.bk-extra { font-family: var(--mono); font-size: 14px; opacity: .6; margin: 0; }

/* details on the left, check-in code on the right, centred against the text.
   No frame and no caption — the code reads as a code, and a box round it
   only made it take more room. Deliberately small: a phone at a door, not a
   poster. */
.bk-top { display: flex; align-items: center; gap: 16px; }
.bk-meta { flex: 1; min-width: 0; }
.bk-qr { flex: 0 0 auto; }
.bk-qr svg { display: block; width: 80px; height: 80px; }
/* the address, quieter than the booking itself, with a copy button beside it */
.bk-addr {
  display: flex; align-items: center; gap: 10px; margin-top: 8px;
  font-family: var(--mono); font-weight: 700; font-size: 13px;
  line-height: 1.5;
}
/* A real button, not a hint. It was a 15px glyph at 80% opacity, which is
   invisible to anyone who isn't looking for it — same lime square as the fold
   markers and the scroll cue, and big enough to actually hit with a thumb. */
.addr-copy {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 32px; height: 32px; padding: 0; cursor: pointer;
  background: var(--lime); border: 3px solid var(--ink);
  box-shadow: 3px 3px 0 var(--ink);
  font-family: var(--mono); font-size: 16px; line-height: 1; color: var(--ink);
}
.addr-copy:active { transform: translate(3px, 3px); box-shadow: none; }
/* dim the words, never the button — opacity on the paragraph was draining
   the lime out of it and putting us back where we started */
#addrText { flex: 1; min-width: 0; opacity: .7; }
.bk-pair { display: flex; gap: 10px; margin-top: 18px; }
/* the pair stretch to the taller one, so a single-line label has to centre
   itself in that height — left at the top it reads as the lighter button
   even though the border and shadow are identical */
.bk-pair .btn {
  flex: 1; padding-left: 8px; padding-right: 8px;
  display: flex; align-items: center; justify-content: center; text-align: center;
}

/* change is a change, not a mistake — amber. Cancelling is the red one. */
.btn.warn, .btn.warn:hover { background: var(--amber); color: var(--ink); }
.btn.danger, .btn.danger:hover { background: var(--red); color: #fff; }

/* Book it, before it's bookable. Red and honest rather than greyed out and
   dead — you can still press it, and pressing it takes you to what's missing. */
.btn.missing, .btn.missing:hover { background: var(--red); color: #fff; }
/* and the thing it sent you to, marked for a moment when you land on it */
.needs { outline: 3px solid var(--amber); outline-offset: 5px; }

/* Optional fields, folded. A hard-edged row you tap, not a browser triangle. */
.fold { border: var(--border); background: #fff; margin-top: 12px; }
.fold + .fold { margin-top: 10px; }
.fold > summary {
  font-family: var(--mono); font-size: 13px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  padding: 13px 14px; cursor: pointer; user-select: none;
  display: flex; align-items: flex-start; gap: 10px; list-style: none;
}
/* heading on top, what's picked underneath — inline, it ran off the edge */
.fold-label { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
/* small enough to stay on the heading's line instead of wrapping under it */
.fold-label i {
  font-style: normal; font-weight: 400; opacity: .45;
  font-size: 11px; letter-spacing: .04em;
}
.fold > summary::-webkit-details-marker { display: none; }  /* Safari's triangle */
/* the marker is the brand square: + to open, − to close */
.fold > summary::before {
  content: "+"; font-size: 15px; line-height: 1;
  width: 20px; height: 20px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--lime); border: 2px solid var(--ink);
}
.fold[open] > summary::before { content: "–"; }
.fold[open] > summary { border-bottom: var(--border); }
.fold > summary:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }
/* what's inside, while it's shut — nothing a guest chose can hide in here.
   Wraps onto as many lines as it needs: truncating it was how a third
   allergen quietly disappeared off the right-hand edge. */
.fold-picked {
  font-weight: 400; text-transform: none; letter-spacing: 0;
  color: var(--ink); opacity: .65; white-space: normal;
  overflow-wrap: anywhere; line-height: 1.45;
}
.fold[open] .fold-picked { display: none; }
/* padding on the body, never margins on the children: the inputs in here are
   width:100%, so a margin pushes them straight out through the card edge */
.fold-body { padding: 14px; }
.fold-body > * { margin: 0; }
.fold-body > * + * { margin-top: 10px; }
.chip {
  font-family: var(--mono); font-size: 14px; cursor: pointer;
  background: #fff; border: var(--border);
  padding: 10px 14px; user-select: none;
}
.chip small { display: block; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; }
/* the turn's end time under a slot — makes it read as a window, not just an arrival */
.chip .slot-end { margin-top: 2px; color: #8a8a8a; letter-spacing: .5px; }
.chip.on .slot-end { color: #9a9a9a; }
.chip.on { background: var(--ink); color: var(--lime); box-shadow: var(--shadow-sm); }
@media (hover: hover) {
  .chip:hover .slot-end { color: #6a6a6a; }
  .chip:hover { background: var(--lime-soft); }
  .chip.on:hover { background: var(--ink); }
}

/* ------------------------------------------------ save this link */
/* FINAL STEP — the wallet ask. Given its own block on ink so it reads as a
   step rather than another button in the stack, because this is the one we
   actually want taken. Skipping stays available and stays small. */
.finalstep-over .confirm-inner { max-width: 420px; width: 100%; gap: 0; }
/* .confirm-over sets display:grid, which beats [hidden]'s display:none —
   without this, skipping the step wouldn't actually remove it */
.confirm-over[hidden] { display: none; }
.finalstep-over .confirm-where { margin: 14px 0 26px; }
.fs-kicker {
  display: inline-block; background: var(--lime); color: var(--ink);
  font-family: var(--mono); font-weight: 700; font-size: 11px;
  letter-spacing: .14em; text-transform: uppercase; padding: 4px 9px;
}
.finalstep-over .wallet-btn { display: block; width: 100%; }
.finalstep-over .wallet-btn + .wallet-btn { margin-top: 12px; }
/* on ink, an ink border and an ink shadow are invisible — same cream shadow
   the cancel button already uses on the dark overlays */
.finalstep-over .btn { box-shadow: 6px 6px 0 var(--paper); }
.finalstep-over .btn:active { box-shadow: none; }
/* what the button can't do yet, said plainly rather than pretended */
.fs-soon {
  font-family: var(--mono); font-size: 13px; line-height: 1.5;
  color: var(--lime); text-align: center; margin-top: 14px;
}
.fs-soon[hidden] { display: none; }
.fs-fail { color: var(--amber); }
/* the operator line is a statement of fact, not a nav item */
.foot-legal { opacity: .6; }
.fs-linky {
  background: none; border: none; padding: 0; cursor: pointer;
  color: inherit; font: inherit; text-decoration: underline;
}

/* small on purpose — it's the way out, not the way on */
.fs-skip {
  display: block; width: 100%; margin-top: 22px; padding: 8px;
  background: none; border: none; cursor: pointer;
  color: var(--paper); opacity: .5; text-decoration: underline;
  font-family: var(--mono); font-size: 13px;
}
.fs-skip:hover { opacity: .85; }

/* The savebox used to be its own panel, which supplied the frame — it lives
   inside the booking widget now, so the button wears the normal border and
   shadow like every other button beside it. */
.savebox { position: relative; }
.savebox > .btn { display: block; width: 100%; }
/* Not in a wallet yet. Square, not a round dot — everything else here is. */
.save-dot {
  position: absolute; top: -8px; right: -8px; z-index: 2;
  width: 18px; height: 18px;
  background: var(--red); border: 3px solid var(--ink);
}
.savebox.saved .save-dot { display: none; }
.saveopts { border: var(--border); border-top: none; background: #fff; }
.saveopt {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left;
  font-family: var(--mono); font-size: 14px; cursor: pointer; color: var(--ink);
  background: #fff; border: none; border-bottom: 2px solid var(--ink);
  padding: 14px 18px; text-decoration: none;
}
.saveopt.end { border-bottom: none; }
.saveopt:hover { background: var(--lime-soft); }
.saveopt .mark {
  flex: none; width: 28px; height: 28px; display: grid; place-items: center;
  background: var(--ink); color: var(--lime); font-size: 15px;
}
.saveopt small {
  display: block; margin-top: 2px; font-family: var(--sans); font-size: 12px;
  letter-spacing: 0; text-transform: none; color: #666;
}
.savehow { margin: 0; padding: 12px 18px; border-top: 2px solid var(--ink); background: var(--lime-soft); }

/* ------------------------------------------------ status + score */
.tag {
  display: inline-block; font-family: var(--mono); font-size: 11px;
  letter-spacing: 1px; text-transform: uppercase;
  border: 2px solid var(--ink); padding: 3px 8px; background: #fff;
  color: var(--ink);   /* never inherit — these sit on dark panels too */
}
.tag.pending   { background: var(--amber); color: var(--ink); }
.tag.confirmed { background: var(--lime); color: var(--ink); }
.tag.seated    { background: var(--green); color: #fff; }
.tag.finished  { background: var(--ink); color: var(--paper); }
.tag.no_show   { background: var(--red); color: #fff; }
.tag.cancelled { background: #ddd; color: var(--ink); }
/* Paid for up front. On the day view it tells the host there is nothing
   to take at the end; on the guest's own page it is why the money has
   already gone (Thom, 2 Oct). */
.tag.prepaid { background: var(--ink); color: var(--lime); }
.brow-tag.prepaid { background: var(--ink); color: var(--lime); border-color: var(--ink); }

.score {
  display: inline-grid; place-items: center;
  min-width: 44px; height: 30px; padding: 0 6px;
  font-family: var(--mono); font-weight: 700; font-size: 14px;
  border: 2px solid var(--ink); background: var(--lime);
}
.score.mid { background: var(--amber); }
.score.low { background: var(--red); color: #fff; }
.score.new { background: #fff; }
.fav-star { color: var(--ink); background: var(--lime); border: 2px solid var(--ink); padding: 0 5px; font-size: 13px; }

/* ------------------------------------------------ admin timeline */
.timeline { overflow-x: auto; }
 .tl-grid { min-width: 760px; width: calc(100% * var(--tlzoom, 1)); }
.tl-zoom { display: flex; align-items: center; gap: 10px; justify-content: flex-end; margin: 10px 0 0; }
.tl-zoom .btn { padding: 6px 12px; font-size: 15px; line-height: 1; }
.tl-row { display: grid; grid-template-columns: 90px 1fr; border-bottom: 2px solid var(--ink); }
.tl-row:last-child { border-bottom: none; }
.tl-label {
  font-family: var(--mono); font-size: 13px; font-weight: 700;
  padding: 10px 12px; border-right: var(--border);
  /* stays put while the zoomed grid scrolls under it */
  position: sticky; left: 0; background: #fff; z-index: 3;
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
}
.tl-label small { font-weight: 400; font-size: 10px; letter-spacing: 1px; }
.tl-track { position: relative; min-height: 58px; background:
  repeating-linear-gradient(90deg, transparent 0, transparent calc(100%/var(--tlhours, 13) - 1px), #11111122 calc(100%/var(--tlhours, 13) - 1px), #11111122 calc(100%/var(--tlhours, 13))); }
.tl-block {
  position: absolute; top: 7px; bottom: 7px;
  border: 2px solid var(--ink); box-shadow: 3px 3px 0 var(--ink);
  font-family: var(--mono); font-size: 11px; line-height: 1.25;
  padding: 5px 8px; overflow: hidden; white-space: nowrap;
  text-decoration: none; color: var(--ink); background: var(--amber);
}
.tl-block.confirmed { background: var(--lime); }
.tl-block.seated { background: var(--green); color: #fff; }
.tl-block.finished { background: #fff; opacity: .55; }
/* Filtered OUT on the timeline, not removed from it. Punching holes in a
   Gantt chart reads as free tables, which is the one thing a host must
   never be told wrongly — so it fades and stays exactly where it is. */
.tl-block.dimmed { opacity: .18; }
.tl-block[data-id] { cursor: pointer; touch-action: none; user-select: none; }
.tl-block.dragging { opacity: .9; box-shadow: 6px 6px 0 var(--ink); z-index: 5; }
.drag-time {
  position: fixed; z-index: 60; pointer-events: none; transform: translateX(-50%);
  font-family: var(--mono); font-weight: 700; font-size: 13px;
  background: var(--lime); border: var(--border); padding: 6px 10px;
}

/* floating flash messages */
#toasts {
  position: fixed; top: 16px; left: 50%; transform: translateX(-50%);
  z-index: 80; display: flex; flex-direction: column; gap: 10px;
  align-items: center; pointer-events: none; width: max-content; max-width: 92vw;
}
.toast {
  background: var(--red); color: #fff; border: var(--border);
  box-shadow: var(--shadow); padding: 13px 20px; text-align: center;
  font-family: var(--mono); font-weight: 700; font-size: 14px;
  animation: toast-in .16s ease-out;
}
.toast.ok { background: var(--lime); color: var(--ink); }
.toast.out { opacity: 0; transform: translateY(-10px); transition: all .3s ease-in; }
@keyframes toast-in { from { opacity: 0; transform: translateY(-12px); } }

/* booking card (tap a block on the plan) */
.card-back {
  position: fixed; inset: 0; z-index: 50; background: rgba(17, 17, 17, .55);
  display: grid; place-items: center; padding: 20px;
}
.card {
  background: #fff; border: var(--border); box-shadow: var(--shadow);
  width: min(440px, 100%); max-height: 86vh; overflow: auto;
}
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  background: var(--ink); color: var(--paper); padding: 12px 16px;
  font-family: var(--mono); font-size: 13px; letter-spacing: 2px;
}
.card-x {
  background: none; border: none; color: var(--lime);
  font-size: 18px; cursor: pointer; padding: 0 2px;
}
.card-body { padding: 20px; }
/* the list row, lifted into the card from a timeline tap */
.card-row { width: min(560px, 100%); }
.card-row .brow { grid-template-columns: minmax(0, 1fr) auto; row-gap: 8px; }
.card-row .brow-when { grid-column: 1; grid-row: 1; }
.card-row .brow-who { grid-column: 1; grid-row: 2; }
.card-row .brow-status { grid-column: 2; grid-row: 1 / span 2; align-items: flex-end; }
.card-row .brow-status .tag { min-width: 0; }
.card-row .brow-actions { grid-column: 1 / -1; grid-row: 3; justify-content: space-between; }
.card-row .brow-more { grid-column: 1 / -1; }
.tl-block.no_show { background: var(--red); color: #fff; }
.tl-hours { display: grid; grid-template-columns: 90px 1fr; font-family: var(--mono); font-size: 10px; }
.tl-hours > div:first-child { position: sticky; left: 0; background: #fff; z-index: 3; }
.tl-hours .marks { display: flex; justify-content: space-between; padding: 6px 0; }

/* ------------------------------------------------ booking list rows */
/* ------------------------------------------------ booking rows (aligned grid) */
.brow {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr) 124px auto;
  align-items: center;
  gap: 18px;
  padding: 18px;
  border-bottom: 3px solid var(--ink);
  cursor: pointer;
}
.brow:last-child { border-bottom: none; }
/* finished, no-show, cancelled: still there, out of the way */
.brow.done { opacity: .5; }
.brow[hidden] { display: none; }
.brow.done:hover, .brow.open { opacity: 1; }
/* the rest of what you can do to a booking, one tap under the row */
.brow-more {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end;
  padding-top: 6px; border-top: 2px dashed #ddd; margin-top: 2px;
}
.brow-more[hidden] { display: none; }
.brow-more[data-drawer="contact"] { flex-wrap: nowrap; justify-content: space-between; align-items: center; gap: 8px; }
.brow-more[data-drawer="contact"] .btn { white-space: nowrap; flex: 0 0 auto; }
.brow-more .brow-num { font-size: 12px; letter-spacing: 0; flex: 1 1 auto; min-width: 0; white-space: nowrap; }
@media (max-width: 420px) {
  .brow-more[data-drawer="contact"] .btn.sm { padding: 8px 7px; font-size: 10px; letter-spacing: .3px; }
  .brow-more .brow-num { font-size: 11px; }
}
.brow-brief { justify-content: space-between; }
.brow-brief .brief { font-family: var(--mono); font-size: 12px; line-height: 1.5; flex: 1 1 100%; }
.brow-brief .gnotes { display: flex; gap: 8px; align-items: flex-end; flex: 1 1 100%; }
.brow-brief .gnotes textarea { flex: 1 1 auto; margin: 0; font-size: 13px; }
.brow-more form { display: inline; }
/* the small squares: one line icon each, the size the ⋯ was */
.btn.ico { padding: 8px 9px; line-height: 0; position: relative; }
.btn.ico svg { width: 18px; height: 18px; display: block; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linejoin: miter; stroke-linecap: square; }
.btn.ico .ico-badge { position: absolute; top: -9px; right: -9px; min-width: 20px; height: 20px; font-size: 11px; border: 2px solid var(--paper); }
.daystrip .hot b { background: var(--red); color: #fff; padding: 0 6px; }
/* the waitlist is orange, messages are red (Thom, 1 Oct) */
.daystrip .hot-q b { background: var(--amber); color: var(--ink); padding: 0 6px; }
.unread.qbadge { background: var(--amber); color: var(--ink); }
/* time, table, covers: the three things a host reads first (Thom, 1 Oct) */
.brow-when { font-family: var(--mono); font-size: 15px; font-weight: 700; line-height: 1.3; white-space: nowrap; }
.brow-when b { display: block; font-family: var(--sans); font-size: 24px; font-weight: 900; line-height: 1.1; margin-bottom: 3px; }
.brow-when .cov { background: var(--lime); padding: 0 5px; }
.brow-who { min-width: 0; }
.brow-headline { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.brow-name { font-weight: 700; }
/* the star is about the GUEST, so it sits with their name rather than in
   a row of things you do to a booking */
.favtoggle {
  background: none; border: 0; padding: 0 2px; cursor: pointer;
  font-size: 17px; line-height: 1; color: #b9b9b9;
}
.favtoggle.on { color: var(--ink); }
.favtoggle:hover { color: var(--ink); }
.brow-phone { font-family: var(--mono); font-size: 11px; color: #555; margin-top: 4px; }
.brow-phone a { color: var(--ink); text-decoration: none; border-bottom: 1px solid #bbb; }
.brow-phone a:hover { background: var(--lime-soft); }
.brow-contact { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.brow-num { font-family: var(--mono); font-weight: 700; font-size: 16px; letter-spacing: .5px; }
.btn.xs {
  padding: 4px 9px; font-size: 11px; letter-spacing: .5px;
  box-shadow: 2px 2px 0 var(--ink);
}
.brow-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.brow-tag {
  font-family: var(--mono); font-size: 11px; line-height: 1.5;
  border: 2px solid var(--ink); padding: 1px 7px; background: #fff;
}
.brow-tag.allergen { background: var(--amber); font-weight: 700; }
.brow-tag.occasion { background: var(--lime-soft); }
.timer {
  display: block; margin-top: 6px;
  font-family: var(--mono); text-align: center;
  border: 2px solid var(--ink); padding: 4px 6px 3px; background: #fff;
}
.timer-num { display: block; font-weight: 700; font-size: 17px; line-height: 1.1; }
.timer-label {
  display: block; font-style: normal; font-size: 9px;
  letter-spacing: 1.5px; text-transform: uppercase; opacity: .75;
}
.timer.warn { background: var(--amber); }
.timer.over { background: var(--red); color: #fff; }
/* late, not yet seated: pastel red, so it reads as "chase this" rather
   than the hard red of a table over its time */
.timer.late { background: #ffd6d1; }
.tl-block .timer {
  display: inline; margin: 0 0 0 4px; border: none; padding: 0;
  background: none; font-size: 11px;
}
.tl-block .timer-num { display: inline; font-size: 11px; }
.tl-block .timer-label { display: none; }

.brow-status { display: flex; flex-direction: column; justify-content: flex-start; }
.brow-status .tag { min-width: 100%; text-align: center; }
.brow-actions {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end; align-items: center;
}
.brow-tools, .brow-do { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.brow-tools:empty { display: none; }
.brow-actions form { display: inline; }

/* narrow: time | status on top, guest under, actions below */
@media (max-width: 900px) {
  .brow { grid-template-columns: minmax(0, 1fr) auto; align-items: start; row-gap: 8px; }
  .brow-when   { grid-column: 1; grid-row: 1; }
  .brow-who    { grid-column: 1; grid-row: 2; }
  /* status + timer sit to the right of both, so they don't stretch row 1
     and leave a gap between the time and the guest name */
  .brow-status { grid-column: 2; grid-row: 1 / span 2; align-items: flex-end; }
  .brow-status .tag { min-width: 0; }
  .brow-status .timer { min-width: 116px; }
  .brow-actions{ grid-column: 1 / -1; grid-row: 3; justify-content: space-between; margin-top: 4px; gap: 6px; }
  .brow-tools, .brow-do { gap: 6px; }
  .brow-do { justify-content: flex-end; margin-left: auto; }
  /* so squares and Edit · Cancel · Seat share one line on a phone */
  .brow-do .btn.sm { padding: 8px 8px; font-size: 11px; letter-spacing: .5px; }
  .brow-tools .btn.ico { padding: 7px 8px; }
  .brow-tools .btn.ico svg { width: 16px; height: 16px; }
  .brow-more   { grid-column: 1 / -1; grid-row: 4; }
}

.rows { display: flex; flex-direction: column; }
.row {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 18px; border-bottom: 3px solid var(--ink);
}
.row:last-child { border-bottom: none; }
.row .who { font-weight: 700; min-width: 140px; }
.row .who small { display: block; font-family: var(--mono); font-weight: 400; font-size: 11px; }
.row .meta { font-family: var(--mono); font-size: 13px; }
.row .grow { flex: 1; }
.row form { display: inline; }

.dayhead {
  display: flex; align-items: flex-end; justify-content: space-between;
  flex-wrap: wrap; gap: 14px;
}
/* tonight's numbers as one line. They were six tiles; a host wants the
   figures and then the list. */
.daystrip {
  display: flex; flex-wrap: wrap; gap: 6px 22px; margin: 22px 0 0;
  font-family: var(--mono); font-size: 12px; letter-spacing: 1px;
  text-transform: uppercase;
}
.daystrip b { font-family: var(--sans); font-size: 20px; font-weight: 900; letter-spacing: 0; margin-right: 4px; }
@media (max-width: 700px) {
  .daystrip { gap: 4px 14px; font-size: 11px; }
  .daystrip b { font-size: 17px; }
}
/* the rare actions, behind one word */
.more { position: relative; }
.more > summary { list-style: none; display: inline-block; }
.more > summary::-webkit-details-marker { display: none; }
.more-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20;
  min-width: 170px; background: #fff; border: var(--border); box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column;
}
.more-menu > * {
  display: block; width: 100%; text-align: left; background: none; border: 0;
  border-bottom: 2px solid var(--ink); padding: 11px 14px; cursor: pointer;
  font-family: var(--mono); font-weight: 700; font-size: 12px; letter-spacing: 1px;
  text-transform: uppercase; color: var(--ink); text-decoration: none;
}
.more-menu > *:last-child { border-bottom: 0; }
@media (hover: hover) { .more-menu > *:hover { background: var(--lime-soft); } }

.stat-row { display: flex; gap: 16px; flex-wrap: wrap; margin: 26px 0 0; }
.stat { background: #fff; border: var(--border); box-shadow: var(--shadow-sm); padding: 14px 16px; min-width: 108px; flex: 1 1 auto; }
.stat b { display: block; font-size: 26px; font-weight: 900; }
.stat span { font-family: var(--mono); font-size: 11px; letter-spacing: 1px; text-transform: uppercase; white-space: nowrap; }
/* One tile that cycles every party size, rather than one tile each: five
   of them was the same fact five times and pushed the counts off screen.
   The slides stack, only one is lit, and the tile keeps the height of the
   tallest so nothing jumps as it turns over. */
/* flex-grow 0: .stat stretches to fill its row, and when this one wrapped
   onto a line by itself it became a full-width banner. */
.stat-cycle { display: grid; flex: 0 1 auto; min-width: 190px; }
.stat-cycle .wslide {
  grid-area: 1 / 1; transition: opacity .35s ease;
}
.stat-cycle .wslide.off { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .stat-cycle .wslide { transition: none; }
}
/* a party this room cannot seat at all tonight — not a wait, a no */
.stat-no b { color: var(--red); }
.stat-no span { opacity: .55; }

.note { font-family: var(--mono); font-size: 12px; margin-top: 14px; }
.err { background: var(--red); color: #fff; font-family: var(--mono); padding: 10px 14px; border: var(--border); margin-bottom: 16px; }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 640px) { .grid2 { grid-template-columns: 1fr; } }

.copylink {
  display: flex; gap: 8px; align-items: center; margin-top: 8px;
  font-family: var(--mono); font-size: 12px;
}
.copylink input { flex: 1; padding: 8px 10px; font-size: 12px; border-width: 2px; }

/* ------------------------------------------------ landing: pillars + comparison */
.pillar-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; }
.pillar {
  background: #fff; border: var(--border); box-shadow: var(--shadow-sm);
  padding: 16px 18px; font-size: 15px; line-height: 1.5;
}
.pillar b {
  display: block; font-family: var(--mono); font-size: 12px;
  letter-spacing: 2px; margin-bottom: 8px;
  background: var(--lime); border: 2px solid var(--ink);
  padding: 3px 8px; width: fit-content;
}
table.cmp { width: 100%; border-collapse: collapse; font-size: 15px; }
table.cmp th {
  background: var(--ink); color: var(--paper);
  font-family: var(--mono); font-size: 12px; letter-spacing: 2px;
  text-transform: uppercase; text-align: left; padding: 12px 16px;
}
table.cmp th:nth-child(2) { color: var(--lime); }
table.cmp td { padding: 12px 16px; border-bottom: 2px solid var(--ink); }
table.cmp tr:last-child td { border-bottom: none; }
table.cmp td:first-child { font-family: var(--mono); font-size: 13px; }
table.cmp td:nth-child(2) { font-weight: 700; background: var(--lime-soft); }
/* Three columns in 375px turned "A year of it, before a single cover" into
   five stacked words against a five-line answer. Each row becomes its own
   block instead, label on top, and the column headings are carried down
   onto the cells as data-l (set in script, from the table's own thead —
   these tables do not share headings). */
@media (max-width: 620px) {
  table.cmp, table.cmp tbody, table.cmp tr, table.cmp td { display: block; width: 100%; }
  table.cmp thead { display: none; }
  table.cmp tr { border-bottom: 2px solid var(--ink); padding: 12px 0; }
  table.cmp tr:last-child { border-bottom: none; }
  table.cmp td { border-bottom: none; padding: 3px 16px; }
  table.cmp td:first-child {
    font-size: 12px; letter-spacing: 1px; text-transform: uppercase;
    opacity: .6; padding-bottom: 6px;
  }
  table.cmp td:nth-child(2) { background: none; }
  table.cmp td[data-l]::before {
    content: attr(data-l) " — "; font-family: var(--mono); font-size: 12px;
    font-weight: 400; opacity: .6;
  }
  table.cmp td[data-l]:nth-child(2) { color: var(--ink); }
  table.cmp td[data-l]:nth-child(2)::before { color: inherit; }
}

footer {
  border-top: var(--border); padding: 18px 28px;
  font-family: var(--mono); font-size: 12px; letter-spacing: 1px;
  /* flex-wrap: gap was not a value — it was being dropped, so a narrow
     footer overflowed instead of wrapping */
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px;
}
/* the policy link is a link, not a label — plain type, underlined, no
   monospace tracking making it look like part of the furniture */
footer a {
  font-family: var(--sans); font-size: 14px; letter-spacing: 0;
  text-decoration: underline; color: var(--ink);
}

/* sample message copy on the messaging policy page */
.mono-sample {
  font-family: var(--mono); font-size: 14px; line-height: 1.5;
  background: var(--paper); border-left: 6px solid var(--lime);
  padding: 12px 14px; margin-top: 4px; word-break: break-word;
}

/* country code beside the number — the guest says which country, we never guess */
.phone-row { display: flex; gap: 10px; }
.phone-row select {
  width: auto; flex: none; min-width: 124px;
  font-family: var(--mono); font-size: 15px;
  padding: 12px 10px; border: var(--border); background: #fff;
}
.phone-row input { flex: 1; min-width: 0; }

/* waiting for the tap that confirms — the mark turns, because that's the name */
.waiting-head {
  font-family: var(--sans); font-size: clamp(19px, 5vw, 23px);
  /* a highlight through the words, like the lime in the headlines —
     not another box on a screen that already has two */
  background: var(--amber); color: var(--ink);
  padding: 2px 12px; box-decoration-break: clone; margin-top: 4px;
}
.spin-t {
  width: 104px; height: 104px; display: grid; place-items: center;
  background: var(--lime); color: var(--ink);
  font-family: var(--mono); font-weight: 700; font-size: 56px;
  border: var(--border); box-shadow: 8px 8px 0 var(--paper);
  animation: turnspin 2.6s cubic-bezier(.6, 0, .4, 1) infinite;
}
@keyframes turnspin { to { transform: rotateY(360deg); } }
@media (prefers-reduced-motion: reduce) { .spin-t { animation-duration: 8s; } }

/* the tap that confirms — everything else can wait behind it */
.confirm-over {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center; padding: 24px;
  /* SOLID, not 93% — at any transparency the cream text behind still reads
     clean through it, and Thom's rule is that everything else disappears
     while this is up */
  background: var(--ink);
  animation: confirmin .25s ease-out;
}
@keyframes confirmin { from { opacity: 0; } }
.confirm-inner { display: flex; flex-direction: column; align-items: center; gap: 14px; text-align: center; }
/* an instruction, not a label — it tells them what to do next */
.confirm-do {
  font-family: var(--mono); font-size: 15px; line-height: 1.4;
  color: var(--paper); margin: 2px 0 4px; max-width: 32ch;
}
.confirm-where { font-family: var(--sans); font-weight: 900; font-size: clamp(28px, 7vw, 40px);
  letter-spacing: -1px; text-transform: none; color: var(--paper); margin: 0; }
.confirm-when { font-family: var(--mono); font-size: 14px; color: var(--lime); margin: 0; }
.confirm-dial {
  width: min(66vw, 240px); aspect-ratio: 1; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: var(--lime); color: var(--ink);
  /* heavier than the page's 3px — this is the only thing to do on screen.
     The shadow is paper because an ink shadow vanishes on the dark overlay */
  border: 8px solid var(--ink); box-shadow: 10px 10px 0 var(--paper);
  font-family: var(--mono); font-weight: 700; font-size: clamp(18px, 5vw, 23px);
  line-height: 1.25; letter-spacing: 1px; text-transform: uppercase;
}
.confirm-dial:active { transform: translate(6px, 6px); box-shadow: none; }
/* the other half of the decision — square against the circle, and red
   because it's the one that gives the table away */
.confirm-cancel {
  margin-top: 20px; cursor: pointer;
  padding: 13px 34px;
  background: var(--red); color: #fff;
  border: var(--border); box-shadow: 9px 9px 0 var(--paper);
  font-family: var(--mono); font-weight: 700; font-size: 16px;
  letter-spacing: 2px; text-transform: uppercase;
}
.confirm-cancel:active { transform: translate(6px, 6px); box-shadow: none; }

/* the wait fills the screen: booking at the top, the turning mark in the
   middle where the eye lands, and cancel out of the way at the bottom */
.waiting-over { place-items: stretch; }
.waiting-over .confirm-inner { position: relative; height: 100%; justify-content: space-between; gap: 0; }
.co-top, .co-mid { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.co-top { gap: 6px; padding-top: 34px; }
/* dead centre of the screen, not centre of whatever space is left over
   once the text below has taken its share */
.co-mid { position: absolute; top: 50%; left: 0; right: 0; transform: translateY(-50%); }
.co-bottom { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.waiting-over .confirm-cancel { margin-top: 4px; margin-bottom: 6px; }
/* the hold, ticking down — same amber as the state it belongs to */
.hold-left { color: var(--amber); font-weight: 700; white-space: nowrap; }

/* how many replies nobody has looked at yet */
.unread {
  display: inline-block; min-width: 20px; padding: 0 5px;
  background: var(--red); color: #fff; border: 2px solid var(--ink);
  font-size: 11px; line-height: 16px; text-align: center;
}
/* ---------------------------------------- the day's view switch
   ONE control, not three buttons in a row. .chip is built for the booking
   page's slot picker — 10px/14px padding, its own border and shadow — so
   three of them beside Block / Walk-in / Add booking read as six competing
   controls on one line. Segmented: one outline, one shadow, dividers
   between, and the same mono-uppercase-tracked type as .btn so it belongs
   to the same family instead of visiting from another page. */
/* a number is a code and a number — one control, not two fields that
   look unrelated to each other */
.dialrow { display: flex; gap: 8px; }

/* From / To / Show on one line, wrapping to two on a phone rather than
   squeezing two date inputs to the width of a thumbnail */
.daterange { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.daterange > div { flex: 1 1 140px; min-width: 0; }
.daterange label { margin-top: 0; }
.daterange input { width: 100%; }
.daterange .btn { flex: 0 0 auto; }

/* Settings, in groups. Wraps rather than scrolls — six labels squeezed onto
   one line is six labels nobody can read. */
/* clear of the h1 above it — they were touching */
.setpane > h2:first-child { margin-top: 0; }
/* Save follows you down a long tab instead of living at the bottom of it.
   The pane carries matching bottom padding — a sticky bar with nothing
   under it sits ON the last field, which on this page was a map pin you
   could no longer read. */
.setsave {
  position: sticky; bottom: 0; z-index: 3;
  /* --bg does not exist in this palette; it is --paper. The sticky bar
     was transparent and the page scrolled visibly through it. */
  background: var(--paper); padding: 16px 0 12px;
  border-top: var(--border); margin-top: 26px;
}
.setpane { padding-bottom: 96px; }
/* One rhythm down the whole pane, whatever got included into it. The table
   plan came in carrying its own top margins and sat at a different pitch
   from everything above it. */
.setpane > h2 { margin: 30px 0 12px; }
.setpane > h2:first-child { margin-top: 0; }
.setpane > .panel { margin-bottom: 18px; }
/* Money going back out of the door, said before they press it. */
.blk-refund { color: #b3261e; font-weight: 700; margin: 6px 0 0; }
/* The console's world. Thom's colours (1 Oct): green on and well, amber
   on with something off, red closed to new venues, blue down for
   maintenance, grey we do not operate there. */
.worldmap { width: 100%; height: auto; display: block; border: var(--border); background: #fff; }
.wm-sea { fill: #f2f1e7; }
.wm-grid { stroke: #ddd; stroke-width: 1; }
.wm-equator { stroke: #ccc; stroke-width: 2; stroke-dasharray: 6 6; }
.wm-box { fill: none; stroke: var(--ink); stroke-width: 2; stroke-dasharray: 5 4; }
.wm-dot circle { stroke: var(--ink); stroke-width: 3; }
.wm-dot text { font-family: var(--mono); font-size: 19px; fill: var(--ink); }
.wm-green circle  { fill: var(--green); }
.wm-amber circle  { fill: var(--amber); }
.wm-red circle    { fill: var(--red); }
.wm-blue circle   { fill: #4da3ff; }
.wm-grey circle   { fill: #fff; stroke: #bbb; }
.wm-grey text     { fill: #999; }
.wm-key { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px;
          font-family: var(--mono); font-size: 12px; }
.wm-k::before { content: ""; display: inline-block; width: 12px; height: 12px;
                border: 2px solid var(--ink); margin-right: 6px;
                vertical-align: -2px; }
.wm-k.wm-green::before { background: var(--green); }
.wm-k.wm-amber::before { background: var(--amber); }
.wm-k.wm-red::before   { background: var(--red); }
.wm-k.wm-blue::before  { background: #4da3ff; }
.wm-k.wm-grey::before  { background: #fff; border-color: #bbb; }
/* the same colours on the market chips, so the map and the switches read
   as one thing */
.wm-chip.wm-green { background: var(--green); }
.wm-chip.wm-amber { background: var(--amber); }
.wm-chip.wm-red   { background: var(--red); color: #fff; }
.wm-chip.wm-blue  { background: #4da3ff; }

/* ---- the monitoring board -------------------------------------------
   One line per subsystem, worst first, with the colour carried on a left
   bar so a red line is findable without reading any of them. The console
   already had a yes/no list; the thing it could not do was raise its
   hand (Thom, 1 Oct: "status all in ops powerful monitoring board"). */
.board .ln {
  display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap;
  padding: 10px 0 10px 12px; border-bottom: 2px solid #eee;
  border-left: 9px solid var(--green);
}
.board .ln:last-child { border-bottom: none; }
.board .ln b { font-size: 14px; min-width: 14ch; }
.board .ln span { font-family: var(--mono); font-size: 12px; opacity: .7; }
.board .ln.warn { border-left-color: var(--amber); }
.board .ln.bad  { border-left-color: var(--red); background: #fff3f3; }
.board .ln.bad span { opacity: 1; color: var(--red); }

/* How a venue is doing, as a pill. Same five-state vocabulary as the
   world map so the two boards do not have to be learned separately. */
.st { font-family: var(--mono); font-size: 10.5px; letter-spacing: .5px;
      text-transform: uppercase; padding: 3px 7px; border: 2px solid var(--ink);
      white-space: nowrap; display: inline-block; }
.st-live    { background: var(--lime); }
.st-quiet   { background: var(--amber); }
.st-cold    { background: var(--red); color: #fff; }
.st-setting { background: #fff; }
.st-new     { background: #eee; }
/* A venue nobody has opened in a fortnight is the row to act on, so the
   whole line leans on it rather than just the pill. */
table.ops tr.row-cold td { background: #fff3f3; }
/* Something is held back today. Amber, because it is not an error — the
   venue did it on purpose — but it is the answer to "why is nothing coming
   in" and it has to be impossible to miss (Thom, 1 Oct). */
.blockbar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 12px;
  background: var(--amber); border: var(--border); box-shadow: var(--shadow-sm);
  padding: 12px 14px; margin: 0 0 18px;
  font-family: var(--mono); font-size: 13px;
}
.blockbar span { display: block; }
.blockbar-why { opacity: .8; margin-top: 2px; }
.blockbar-do { display: flex; gap: 8px; align-items: center; }
/* Picking a plan on the way in: the whole row is the target, not a 12px
   radio somebody has to aim at on a phone. */
.planpick {
  display: flex; gap: 12px; align-items: flex-start; cursor: pointer;
  border: var(--border); padding: 14px; margin-top: 10px; background: #fff;
}
.planpick:has(input:checked) { outline: 3px solid var(--lime); }
.planpick small { display: block; font-family: var(--mono); font-size: 12px; opacity: .75; margin-top: 4px; }
/* The checklist itself. A done step keeps its words — a venue should be
   able to read back what it decided, not just see a tick. */
/* A GRID, not a wrapping flex row. As a flex row the three parts wrapped
   wherever the words happened to run out: a short label left the Done tag
   on the right, a longer one dropped it to the left underneath, and the
   longest pushed the LABEL itself below the tick — so no two rows in the
   checklist lined up (Thom, 2 Oct). Three columns cannot do that, however
   long the words get. */
.setup-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px 16px;
}
.setup-row .grow { display: none; }     /* the flex spacer has no job here */
.setup-row .setup-tick {
  width: 26px; height: 26px; display: grid; place-items: center;
  border: var(--border); background: #fff; font-weight: 700;
  align-self: start;
}
/* Narrow: the tick and the words keep their line and the state drops
   underneath, lined up with the words rather than the tick. */
@media (max-width: 560px) {
  .setup-row {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "tick who" "tick act";
  }
  .setup-row .setup-tick { grid-area: tick; }
  .setup-row .who { grid-area: who; }
  .setup-row > .tag, .setup-row > .btn { grid-area: act; justify-self: start; }
}
/* A switch that is dead because of WHERE a venue is, not because of a
   decision they made. Greyed and unclickable, with the reason where On/Off
   would be (Thom, 2 Oct: "on the switcher it says not available in your
   region yet"). */
.bigswitch.is-blocked { opacity: .5; }
.bigswitch.is-blocked .switch,
.bigswitch.is-blocked .switch input { cursor: not-allowed; }
.bigswitch.is-blocked .switch-label small { font-style: italic; }
/* The same for a settings tab nobody in this country can use. Still
   clickable — the pane is where the reason is written. */
.settabs .chip.is-off, .substrip .chip.is-off { opacity: .45; }
.setup-row.is-done .setup-tick { background: var(--lime); }
.setup-row.is-done .who { opacity: .7; }
/* Running on a laptop, not on the live site. Loud on purpose: a local
   page saying "card payments are not switched on yet" is correct and
   reads exactly like a production fault (Thom, 2 Oct). */
.localbar {
  background: #111; color: #ff5a1f; border-bottom: var(--border);
  padding: 7px 14px; font-family: var(--mono); font-size: 12px;
}
/* How far through setup they are. A strip, not a modal: it has to be
   glanceable on every admin page without ever being in the way. */
.setupbar { border-bottom: var(--border); background: var(--lime); }
/* ONE line. It was three on a phone: a media query put "Next:" on its own
   row, and wrapping then pushed "Finish setup" onto a third, so a strip
   meant to be glanceable took up more height than the page heading (Thom,
   2 Oct: "condense this"). Nothing wraps now — the progress, the count and
   what is next sit tight together on the left, the way out is on the
   right, and it is the NEXT STEP's name that gives way on a narrow screen
   because it is the only part that can be read from the page it links to. */
.setupbar a {
  display: flex; align-items: center; gap: 9px; flex-wrap: nowrap;
  padding: 9px 14px; color: var(--ink); text-decoration: none;
  font-family: var(--mono); font-size: 13px;
}
.setupbar-bar {
  flex: 0 0 58px; height: 9px; background: #fff; border: 2px solid var(--ink);
}
.setupbar-bar span { display: block; height: 100%; background: var(--ink); }
.setupbar a > b { flex: 0 0 auto; white-space: nowrap; }
.setupbar-next {
  opacity: .75; min-width: 0; flex: 0 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.setupbar-go {
  margin-left: auto; flex: 0 0 auto; white-space: nowrap;
  font-weight: 700; text-decoration: underline;
}
/* The Invoices heading with its bulk download beside it, and the two
   pickers under it (Thom, 1 Oct). */
.invhead { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 26px 0 12px; }
.invpick { display: flex; gap: 10px; }
.invpick select { flex: 1; min-width: 0; }
/* The guest book's cuts: a search and a button on one line, the rest
   stacked under it. And a guest's counts on their own line, so the score
   can stay beside the name instead of wrapping away (Thom, 1 Oct). */
.gfilter-top { display: flex; gap: 10px; align-items: stretch; }
.gfilter-top input { flex: 1; min-width: 0; }
.gfilter-top .btn { flex: 0 0 auto; }
.gfilter label { margin-top: 14px; }
/* A guest's row is a grid, not the shared wrapping flex: the star, the
   name and the score on one line, the counts underneath. As a flex row the
   score kept wrapping under the star and read as missing (Thom, 1 Oct). */
.gbook .row { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px 12px; align-items: center; }
.gbook .row .gmeta { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 10px; margin: 0; }
.gbook .row .who { min-width: 0; }
.setpane > .panel + .panel { margin-top: 0; }
/* ...except the combinations. Those are separate things a venue made, not
   sections of one long form, so they stand apart instead of reading as one
   block with the new one welded to the last (Thom, 1 Oct). */
.subpane[data-sub="combined"] .panel + .panel,
.subpane[data-sub="combined"] #combonew { margin-top: 16px; }
.setpane .panel[style*="margin-top:28px"] { margin-top: 0 !important; }
.setpane .panel-body > label:first-child { margin-top: 0; }
.setpane .note { margin-top: 6px; }

/* Confirm-your-email. A strip across the top of the admin, not a lock over
   it — their booking page is live and taking tables either way. */
/* Was a one-line strip. A venue read straight past it — the thing it is
   asking for is the only reason a password reset can ever find them, so it
   is a block now, with room to say why. */
.verifyblock {
  background: var(--amber); border-bottom: var(--border); padding: 18px 22px;
}
.verifyblock-in {
  display: flex; gap: 18px; align-items: center; flex-wrap: wrap;
  max-width: 1100px; margin: 0 auto;
}
.verifyblock-in > div { flex: 1 1 320px; min-width: 0; }
.verifyblock b {
  font-family: var(--mono); font-size: 13px; letter-spacing: 1.5px;
  text-transform: uppercase; display: block; margin-bottom: 6px;
}
.verifyblock p { font-size: 15px; line-height: 1.45; margin-top: 4px; }
.verifyblock p b {
  display: inline; text-transform: none; letter-spacing: 0;
  font-size: inherit; font-family: inherit; font-weight: 700;
}
.verifyblock .vb-note { font-family: var(--mono); font-size: 12.5px; opacity: .75; }
.verifyblock form { flex: 0 0 auto; }

/* A party size and its wait in one target, big enough for a thumb on a
   busy pass. The number leads; the wait is the second line. */
.partychip {
  min-width: 54px; text-align: center; padding: 10px 8px; line-height: 1.1;
}
.partychip b { display: block; font-size: 19px; }
.partychip small {
  display: block; font-family: var(--mono); font-size: 10px;
  letter-spacing: .5px; opacity: .6; margin-top: 3px;
}
.partychip.free { background: var(--lime); }
/* the picked size was lime on lime — invisible — when it was also free (1 Oct) */
.partychip.free.on, .partychip.free.on:hover { background: var(--ink); color: var(--lime); }
/* ten sizes: ten across on a laptop, two rows of five on a phone */
#wform #party.chip-row { grid-template-columns: repeat(9, minmax(0, 1fr)); }
@media (max-width: 600px) { #wform #party.chip-row { grid-template-columns: repeat(5, minmax(0, 1fr)); } .partychip { min-width: 0; } }
.partychip.on small, .partychip.free.on small { opacity: .85; }

.dialrow select { flex: 0 0 auto; width: auto; }
.dialrow input { flex: 1 1 auto; min-width: 0; }

.viewswitch {
  display: inline-flex; gap: 0; flex-wrap: nowrap;
  border: var(--border); box-shadow: var(--shadow-sm); background: #fff;
}
.viewswitch .chip {
  border: 0; border-right: var(--border); box-shadow: none;
  background: transparent; cursor: pointer;
  font-family: var(--mono); font-weight: 700; font-size: 12px;
  letter-spacing: 1px; text-transform: uppercase;
  padding: 9px 16px; color: var(--ink); white-space: nowrap;
}
.viewswitch .chip:last-child { border-right: 0; }
@media (hover: hover) { .viewswitch .chip:hover { background: var(--lime-soft); } }
.viewswitch .chip.on,
.viewswitch .chip.on:hover { background: var(--ink); color: var(--lime); }
.viewswitch .chip.soon,
.viewswitch .chip.soon:hover {
  background: transparent; color: #9a9a9a; cursor: not-allowed;
}

/* the switch is a mode, the buttons beside it are actions — so they get a
   line of their own rather than wrapping into each other on a laptop */
.day-controls {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap; margin: 22px 0 18px;
}
@media (max-width: 700px) {
  .day-controls { gap: 12px; }
  /* wraps on a phone: List · Timeline · Waitlist · Table plan ran off the
     right edge with the last chip cut in half (1 Oct) */
  .viewswitch { width: 100%; flex-wrap: wrap; }
  .viewswitch .chip { flex: 1 1 auto; text-align: center; }
  .viewswitch .chip { flex: 1; padding: 9px 8px; }
  /* Add booking · Walk-in · More on one line, not three */
  .day-controls .chip-row { width: 100%; display: flex; flex-wrap: nowrap; gap: 8px; }
  .day-controls .chip-row > * { flex: 1 1 auto; min-width: 0; }
  .day-controls .chip-row .btn,
  .day-controls .more > summary {
    width: 100%; padding: 9px 4px; text-align: center; box-sizing: border-box;
    white-space: nowrap; font-size: 11px; letter-spacing: .5px;
  }
  .day-controls .chip-row > .more { flex: 0 0 auto; }
  .day-controls .chip-row .more > .btn { width: auto; padding: 9px 10px; }
  /* the details sits at the right edge here too, but a menu wider than
     the word runs off the left of a phone if it hangs from the right */
  .more-menu { left: auto; right: 0; }
}

/* a message waiting, on the one control that is always on screen */
.burger { position: relative; }
.burger.has-unread::after {
  content: ""; position: absolute; top: -5px; right: -5px;
  width: 12px; height: 12px; background: var(--red);
  border: 2px solid var(--ink); border-radius: 50%;
}
/* and on the booking itself, so it says WHICH table rather than just
   that something is waiting somewhere */
.tl-unread { color: var(--red); margin-right: 4px; font-size: 13px; }

.reply-row { display: flex; flex-direction: column; gap: 6px; }
.reply-body { font-family: var(--mono); font-size: 15px; }
.reply-row.unhandled { background: var(--lime-soft); }

/* the conversation on a booking — theirs on the left, ours on the right */
/* Bounded and scrolling, so the reply box stays on screen instead of being
   pushed off the bottom by a long conversation. svh, not vh: on iOS the
   address bar makes vh taller than what you can actually see. */
.thread {
  display: flex; flex-direction: column; gap: 12px;
  max-height: 52svh; overflow-y: auto; padding-right: 4px;
}

/* The inbox: one row per person. Name, what was said last, when. The whole
   row is the target — a thumb on a busy pass is not aiming at a link. */
.thread-row {
  display: grid; grid-template-columns: 46px minmax(0, 1fr) auto; gap: 12px; align-items: center;
  padding: 12px 14px; border-bottom: 2px solid var(--ink); color: var(--ink); text-decoration: none;
}
.thread-row:last-child { border-bottom: 0; }
a.thread-row:hover { background: var(--lime-soft); }
.thread-row.unseen { background: #fff; }
.thread-row[hidden] { display: none; }
/* initials in the mark's own square: ink, lime letters */
.thread-ini {
  width: 46px; height: 46px; display: grid; place-items: center;
  background: var(--ink); color: var(--lime); font-family: var(--mono); font-weight: 700; font-size: 15px;
}
.thread-mid { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.thread-mid b { font-size: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.thread-bk { font-family: var(--mono); font-size: 12px; color: #555; }
.thread-row.unseen .thread-bk { color: var(--ink); }
.thread-side { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.thread-when { font-family: var(--mono); font-size: 11px; color: #555; }
.thread-row.unseen .thread-when { color: var(--red); font-weight: 700; }
.thread-badge {
  min-width: 22px; height: 22px; padding: 0 6px; display: grid; place-items: center;
  background: var(--red); color: #fff; font-family: var(--mono); font-weight: 700; font-size: 12px; border-radius: 11px;
}
.msg { max-width: 86%; border: var(--border); padding: 10px 13px; }
.msg p { font-family: var(--mono); font-size: 14px; line-height: 1.4; margin: 0; }
.msg-when {
  display: block; margin-top: 6px;
  font-family: var(--mono); font-size: 11px; letter-spacing: 1px;
  text-transform: uppercase; color: #777;
}
.msg.in  { align-self: flex-start; background: #fff; box-shadow: var(--shadow-sm); }
.msg.out { align-self: flex-end; background: var(--lime-soft); box-shadow: var(--shadow-sm); }

/* Turn's own console — dense on purpose, it's not a customer screen */
table.ops { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 13px; }
table.ops th {
  text-align: left; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase;
  border-bottom: var(--border); padding: 8px 10px 8px 0;
}
table.ops td { padding: 9px 10px 9px 0; border-bottom: 2px solid #eee; }
table.ops tr:last-child td { border-bottom: none; }
table.ops a { color: var(--ink); }
.ops-err { color: var(--red); max-width: 34ch; }
/* a verification code is worth reading at a glance */
.ops-msg { font-size: 14px; max-width: 46ch; }

/* "didn't get the text?" — quiet until it's needed, then the way out */
.confirm-nudge {
  background: none; border: none; cursor: pointer; padding: 6px;
  font-family: var(--mono); font-size: 14px; letter-spacing: 1px;
  color: var(--paper); text-decoration: underline; margin-top: 4px;
}
/* while the resend form is open, nothing else is on screen */
.waiting-over.resending .co-top,
.waiting-over.resending .co-mid,
.waiting-over.resending .waiting-head,
.waiting-over.resending .confirm-do,
.waiting-over.resending .confirm-nudge,
.waiting-over.resending .confirm-cancel { display: none; }
.waiting-over.resending .confirm-inner { justify-content: center; }
.resend-head { display: block; font-family: var(--sans); font-size: 20px; margin-bottom: 10px; }
.resend-back {
  display: block; width: 100%; margin-top: 10px; padding: 8px; cursor: pointer;
  background: none; border: none; text-decoration: underline;
  font-family: var(--mono); font-size: 13px; letter-spacing: 1px; color: #666;
}
.resend {
  width: min(92vw, 380px); margin-top: 6px;
  background: #fff; border: var(--border); box-shadow: 8px 8px 0 var(--paper);
  padding: 16px; text-align: left;
}
.resend-why { font-family: var(--sans); font-size: 14px; line-height: 1.45;
  color: var(--ink); margin-bottom: 12px; }
.resend select, .resend input { font-size: 15px; }


/* ---- settings: services, turn rules, closed dates ---- */
/* One row per sitting. A day with no rows is a day the venue is shut, which
   is why removing the last row has to feel deliberate rather than broken. */
.svc-day { border-bottom: var(--border); padding: 12px 14px; }
.svc-day:last-child { border-bottom: none; }
.svc-dayname {
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  letter-spacing: 1.5px; text-transform: uppercase; margin-bottom: 8px;
}
/* A grid, not a wrapping flex row: name / from / – / to / every / remove.
   Flex wrapped these onto three ragged lines the moment the panel narrowed. */
.svc-row {
  display: grid; align-items: center; gap: 8px; padding: 8px 0;
  grid-template-columns: minmax(90px, 1fr) auto 10px auto 30px;
}
.svc-head { padding: 0 14px 4px; border-bottom: 2px solid var(--ink); }
.svc-head .meta { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }
.turn-row { display: flex; align-items: center; gap: 8px; padding: 8px 14px; }
.svc-row input, .svc-row select { min-width: 0; width: 100%; }
.svc-row input[type=time] { width: auto; }
.turn-row select { width: auto; flex: 0 0 auto; }
/* on a phone it becomes two tidy lines instead of three ragged ones */
@media (max-width: 620px) {
  .svc-row {
    grid-template-columns: 1fr auto 10px auto 30px;
  }
  .svc-row select { grid-column: 1 / -1; }
}
.turn-row { padding: 8px 14px; }
.turn-rows .turn-row { border-bottom: 2px solid var(--ink); }
.turn-rows .turn-row:last-child { border-bottom: none; }
#turnadd, .svc-add { margin: 4px 14px 10px; }
/* the remove button: small, red, unmistakably destructive */
/* What a sitting is for, under its times. Greyed when the feature is off —
   it cannot be picked, and the line under the day says where to switch it
   on (Thom, 1 Oct). */
.svc-for { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.svc-for .tickbox { font-size: 12px; margin: 0; }
.svc-for .tickbox.off { opacity: .4; cursor: not-allowed; }
/* In a form (rather than squeezed under a service row) the three sit one
   per line, so they do not wrap two-and-one and read as ragged. */
.comboform .svc-for, #combonew .svc-for { display: block; }
.comboform .svc-for .tickbox, #combonew .svc-for .tickbox {
  display: flex; font-size: 14px; margin-top: 10px;
}
/* A day with no sittings says so, rather than leaving a gap that reads
   like something failed to load (Thom, 1 Oct). */
.svc-none {
  font-family: var(--mono); font-size: 13px; text-transform: uppercase;
  letter-spacing: .06em; opacity: .55; padding: 6px 0;
}
.svc-del {
  flex: 0 0 auto; width: 30px; height: 30px; padding: 0; cursor: pointer;
  display: grid; place-items: center;
  background: var(--red); color: #fff; border: 2px solid var(--ink);
  font-family: var(--mono); font-size: 13px; line-height: 1;
}
.svc-del:active { transform: translate(2px, 2px); }
.closed-add {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 12px 14px; border-top: var(--border);
}
.closed-add input[type=date] { width: auto; flex: 0 0 auto; }
.closed-add input[type=text] { flex: 1 1 140px; min-width: 0; }
.svc-empty {
  font-family: var(--mono); font-size: 13px; opacity: .55; padding: 6px 0;
}


/* ---- the block popup ---- */
/* Floats over the day rather than navigating away from it: blocking a table
   is something you do while looking at the service you are blocking. */
.modal {
  position: fixed; inset: 0; z-index: 70;
  display: grid; place-items: center; padding: 20px;
  background: rgba(17, 17, 17, .72);
}
.modal[hidden] { display: none; }
.modal-card {
  width: min(520px, 100%); max-height: 88svh; overflow-y: auto;
  background: #fff; border: var(--border); box-shadow: var(--shadow);
}
.modal-card .panel-head { position: sticky; top: 0; z-index: 1; }
.modal-x {
  background: none; border: none; cursor: pointer;
  color: var(--paper); font-family: var(--mono); font-size: 15px; padding: 0 2px;
}
/* The picker and View menu on one line: the picker takes what is left and
   wraps to two, the button is a square beside it (Thom, 1 Oct). */
.pick-row { display: flex; gap: 10px; align-items: stretch; }
.pick-row .pickbtn { flex: 1; min-width: 0; }
.viewmenu {
  flex: 0 0 auto; width: 72px; padding: 0;
  font-size: 11px; line-height: 1.3; letter-spacing: .5px;
}
/* A bigger party than the chips offer: a box, not the whole width. */
.partymore { width: 110px; }
/* The time row keeps its height while it reloads, so changing the day or
   the covers does not collapse it and jump the whole panel (Thom, 1 Oct).
   Fading it says "this is being asked again" without blanking it. */
#times { min-height: 52px; }
#times.busy { opacity: .45; }
/* One line of a split: who, and how many covers they are carrying. */
/* One person per block: their name across the top, then their number and
   what they are covering underneath. It was three fields on one line; a
   number is a fourth and will not fit beside them on a phone (Thom,
   2 Oct: "need their phone number"). The rule above each block is what
   separates one person from the next. */
.splitrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 84px;
  /* The NUMBER gets the full line and the name shares with the covers.
     A name is read at a glance and a long one can be trimmed; a mobile
     number is checked digit by digit, and it was running out of its box
     (Thom, 2 Oct: "name should be the shorter - Number should be visable
     so it can be checked easier"). */
  grid-template-areas: "name covers" "phone phone";
  gap: 8px; align-items: center;
  margin-top: 12px; padding-top: 12px; border-top: 2px solid #eee;
}
/* The row with a ✕ on it keeps the same right edge as the one without,
   and as the Pay button above them both. The name used to span only the
   first two of three columns, so every field stopped short of the edge
   and the whole block read as narrower than the button (Thom, 2 Oct:
   "bring these out in line with the pay button"). The ✕ now takes its
   width out of the covers line, not out of the row. */
.splitrow:has(.svc-del) {
  grid-template-columns: minmax(0, 1fr) 84px 34px;
  grid-template-areas: "name covers del" "phone phone phone";
}
.splitrow:first-child { border-top: none; padding-top: 0; }
.splitrow > [name="share_name"] { grid-area: name; }
.splitrow > [name="share_phone"] { grid-area: phone; }
.splitrow > select { grid-area: covers; }
.splitrow > .svc-del { grid-area: del; }
.splitrow input, .splitrow select { width: 100%; min-width: 0; }
.splitrow input[readonly] { opacity: .8; }
.splitrow select { padding: 12px 30px 12px 10px; background-position: right 9px center; }
/* Covers fill the row rather than bunching to the left (Thom, 1 Oct). */
#party { display: flex; gap: 8px; }
#party .chip { flex: 1 1 0; min-width: 0; padding-left: 0; padding-right: 0; text-align: center; }
/* One menu in the picker: its name, and what it costs a head. */
.exrow {
  display: flex; align-items: center; width: 100%; cursor: pointer;
  text-align: left; font: inherit; color: var(--ink);
  background: #fff; border: var(--border); padding: 12px 14px;
}
.exrow + .exrow { margin-top: 8px; }
.exrow.on { background: var(--lime); }
.exrow .who { font-weight: 700; }
.exrow .who small {
  display: block; font-family: var(--mono); font-weight: 400;
  font-size: 12px; opacity: .75; margin-top: 2px;
}
.brand-dark .exrow { background: #262626; color: var(--ink); }
.brand-dark .exrow.on { background: var(--lime); color: #111; }
/* The menu itself, in the pop-up rather than a tab the guest has to find
   their way back from. */
.menu-body { height: min(70svh, 620px); background: #fff; }
.menu-body iframe { display: block; width: 100%; height: 100%; border: 0; }
.blk-dates { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.blk-dates input[type=date] { width: auto; flex: 1 1 140px; min-width: 0; }
.blk-dates input:disabled { opacity: .45; }
/* a tickbox that reads as a sentence, not a form field */
.blk-tick {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  font-family: var(--mono); font-size: 14px; text-transform: none;
  letter-spacing: 0; margin: 0;
}

/* A booking a host put where Turn said no. Hatched, not red: it isn't an
   error, it's a decision — but it has to stay visible for whoever takes
   over the pass later and didn't make it. */
.tl-block.forced {
  background-image: repeating-linear-gradient(45deg,
    rgba(17, 17, 17, .22) 0 4px, transparent 4px 9px);
  outline: 2px solid var(--amber);
  outline-offset: -2px;
}
.tl-forced {
  display: inline-block;
  background: var(--amber);
  color: var(--ink);
  font-weight: 800;
  width: 14px;
  text-align: center;
  margin-right: 4px;
}

/* The queue offer, where the times would have been. Deliberately not styled
   as an error: nothing is wrong, there is just a different thing on offer. */
.wait-offer { border: var(--border); padding: 14px 16px; background: var(--paper); }
.wait-offer b { display: block; }
.wait-offer p { margin: 8px 0 14px; font-size: 14px; opacity: .8; }

.wait-near { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.wait-near span { font-size: 13px; opacity: .85; }

/* ------------------------------------------------ the floor plan
   A room drawn as rectangles on a fixed grid. Everything is a percentage of
   the grid, never a pixel, so one arrangement is right on the office laptop
   and on the phone behind the bar — the cells resize, the layout doesn't. */
.room-tabs { margin: 0 0 18px; }
.room-tabs .chip { text-decoration: none; }
.floor-panel { padding-bottom: 4px; }
.floor-bar {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 14px 18px; border-bottom: var(--border);
}
.floor-when { margin: 0; }
.floor-when input { margin: 0; padding: 8px 10px; border-width: 2px; }
.floor-scrub { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 220px; }
.floor-scrub input[type=range] { flex: 1; accent-color: var(--ink); min-width: 120px; }
.floor-scrub b { font-family: var(--mono); font-size: 18px; }

/* The room pans, it never shrinks. A phone is 375px, and 375 across 24 cells
   is a 15px cell — a two-top would be 31px square, which is under the size a
   thumb can hit and under the size a name can be read at. So the floor keeps
   a floor's worth of width and the narrow screen scrolls to it. */
.floor-wrap { padding: 18px; overflow-x: auto; }
.floor {
  position: relative;
  width: 100%;
  min-width: 720px;            /* 30px cells: a two-top is 60px square */
  border: var(--border);
  background-color: #fffdf2;
  /* the grid itself: faint, and exactly the cells tables snap to */
  background-image:
    linear-gradient(to right, rgba(17, 17, 17, .07) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(17, 17, 17, .07) 1px, transparent 1px);
  background-size: calc(100% / var(--cols, 24)) calc(100% / var(--rows, 16));
}
.floor-empty {
  position: absolute; inset: 0; display: grid; place-content: center;
  text-align: center; font-family: var(--mono); font-size: 13px;
}

.ft {
  position: absolute;
  display: grid;
  place-content: center;
  text-align: center;
  padding: 2px;
  overflow: hidden;
  border: var(--border);
  background: var(--paper);
  font-family: var(--mono);
  line-height: 1.1;
  transition: background .12s, color .12s;
}
.ft-name { font-weight: 700; font-size: 13px; }
.ft-seats { font-size: 10px; opacity: .6; }
.ft-who {
  font-size: 10px; margin-top: 2px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ft-who:empty { display: none; }

.ft[data-state=booked] { background: var(--lime); }
.ft[data-state=seated] { background: var(--ink); color: var(--paper); }
.ft[data-state=seated] .ft-seats { opacity: .75; }
.ft[data-state=blocked] { background: var(--red); }
.ft[data-state=held] {
  background:
    repeating-linear-gradient(45deg, transparent 0 6px,
                              rgba(17, 17, 17, .13) 6px 12px) var(--paper);
}
/* it moved on screen and the save never landed — say so rather than lie */
.ft-unsaved { outline: 3px dashed var(--red); outline-offset: 3px; }

.floor-key {
  display: flex; flex-wrap: wrap; gap: 6px 18px;
  padding: 0 18px 16px; font-family: var(--mono); font-size: 11px;
}
.floor-key span { display: flex; align-items: center; gap: 7px; }
.floor-key i { width: 14px; height: 14px; border: 2px solid var(--ink); }
.floor-key .k-free { background: var(--paper); }
.floor-key .k-booked { background: var(--lime); }
.floor-key .k-seated { background: var(--ink); }
.floor-key .k-blocked { background: var(--red); }
.floor-key .k-held {
  background:
    repeating-linear-gradient(45deg, transparent 0 3px,
                              rgba(17, 17, 17, .35) 3px 6px) var(--paper);
}

/* Arranging. Off by default: during service the floor is for reading, and a
   table you can shove about by accident is worse than one you can't move. */
.arrange-only { display: none; }
body.arranging .arrange-only { display: block; }
body.arranging .floor { background-color: var(--paper); cursor: grab; }
body.arranging .ft {
  cursor: grab;
  touch-action: none;          /* the finger is dragging a table, not the page */
  user-select: none;
  box-shadow: var(--shadow-sm);
}
body.arranging .ft-rot { display: grid; }
body.arranging .ft-who { display: none; }
.ft.lifted {
  cursor: grabbing;
  z-index: 5;
  background: var(--lime-soft);
  color: var(--ink);
  box-shadow: var(--shadow);
}
.ft-rot {
  display: none;
  position: absolute; top: 0; right: 0;
  width: 20px; height: 20px; padding: 0;
  place-content: center;
  border: 0; border-left: 2px solid var(--ink); border-bottom: 2px solid var(--ink);
  background: var(--ink); color: var(--lime);
  font-size: 12px; line-height: 1; cursor: pointer;
}
.ft-rot:hover { background: var(--lime); color: var(--ink); }
.room-edit { display: flex; gap: 8px; align-items: center; margin: 0; }
.room-edit input { margin: 0; padding: 8px 10px; border-width: 2px; max-width: 200px; }

@media (max-width: 640px) {
  .floor-wrap { padding: 10px; }
  .floor-bar { padding: 12px; gap: 10px; }
  /* the plan is the page on a phone — give it the height back */
  .floor-panel .panel-body, .floor-key { padding-left: 12px; padding-right: 12px; }
}

/* ------------------------------------------------ legal pages
   Long-form prose, not a form. Wider measure than .narrow gives, and the
   labels inside panels act as sub-headings the way they do in /help. */
.legal .panel { margin-bottom: 18px; }
.legal .panel-body > p { margin-bottom: 12px; }
.legal .panel-body > p:last-child { margin-bottom: 0; }
.legal-list { margin: 0 0 12px 20px; }
.legal-list li { margin-bottom: 7px; }
.legal-list li:last-child { margin-bottom: 0; }
.legal h2 { scroll-margin-top: 20px; }
.legal [id] { scroll-margin-top: 20px; }

/* Shown until a real legal entity is named. Deliberately loud: a notice
   with nobody's name on it must not read as finished. */
.legal-unset {
  border: var(--border);
  background: var(--amber);
  padding: 14px 16px;
  margin: 20px 0 8px;
  font-size: 14px;
  line-height: 1.5;
}

/* The line by the phone field, and by the venue signup button. Small, but
   it is the only notice given at the moment data is actually handed over. */
.consent { opacity: .8; line-height: 1.5; }
.consent a { color: var(--ink); }

/* the feature list on the landing page — dense on purpose: it is read by
   people scanning, and by crawlers reading every line of it */
.feature-list { display: grid; gap: 14px; }
.feat { display: grid; gap: 2px; }
.feat b { font-family: var(--mono); font-size: 13px; letter-spacing: .3px; }
.feat span { font-size: 14px; opacity: .85; }
/* One badge for "not shipped yet", wherever it appears. Must sit AFTER
   `.feat span` and carry the element in the selector, or that rule's 14px
   wins on specificity and the badge silently renders bold and oversized —
   which is what it had been doing in the feature list. */
span.soon-tag {
  display: inline-block; font-size: 10px; font-weight: 400;
  letter-spacing: .5px; text-transform: uppercase; white-space: nowrap;
  background: var(--amber); color: var(--ink);
  padding: 1px 6px; border: 2px solid var(--ink);
  vertical-align: 2px; margin-left: 4px;
}
@media (min-width: 720px) { .feature-list { grid-template-columns: 1fr 1fr; gap: 18px 26px; } }

/* Launch badge — the signup panel before the doors open. Loud, because a
   visitor needs to know in one glance that they cannot sign up yet and
   that this is a date rather than a fault. */
.launch-badge {
  border: var(--border);
  background: var(--lime);
  box-shadow: var(--shadow-sm);
  padding: 14px 16px;
  font-family: var(--mono);
  font-weight: 700;
  font-size: clamp(13px, 2.4vw, 16px);
  letter-spacing: .5px;
  text-align: center;
}

/* ------------------------------------------------ the business dashboard
   Owner-only. Dense and unglamorous on purpose: this is a tool for reading
   numbers quickly, not a page to show anyone. */
.kpi-grid { display: grid; gap: 14px; margin: 22px 0 8px; }
@media (min-width: 720px) { .kpi-grid { grid-template-columns: repeat(4, 1fr); } }
.kpi {
  border: var(--border); box-shadow: var(--shadow-sm);
  background: var(--paper); padding: 16px;
  display: grid; gap: 4px; align-content: start;
}
.kpi span { font-family: var(--mono); font-size: 11px; letter-spacing: .6px;
            text-transform: uppercase; opacity: .7; }
.kpi b { font-size: clamp(22px, 3.4vw, 30px); line-height: 1.05; letter-spacing: -.5px; }
.kpi small { font-family: var(--mono); font-size: 10.5px; opacity: .65; line-height: 1.45; }
.kpi-good { background: var(--lime); }
.kpi-bad { background: var(--red); }
.kpi-bad span, .kpi-bad small { opacity: .85; }

.row-now td { background: var(--lime-soft); }
.bad { color: #b30000; }
table.ops code { font-size: 12px; }
.bar-cell { width: 45%; }
.bar { display: block; height: 10px; background: var(--ink); min-width: 2px; }

/* visits a day — bars, because a line chart needs a library and this does not */
.spark { display: flex; align-items: flex-end; gap: 2px; height: 90px;
         border-bottom: var(--border); }
.spark-bar { flex: 1; background: var(--ink); min-height: 2px; }
.spark-bar:hover { background: var(--lime); }

/* The billing lock. Greys the admin out and puts one action in front of
   them. Never shown on a guest-facing page: a restaurant's customers are
   not the ones who should feel a declined card. */
.billing-lock {
  position: fixed; inset: 0; z-index: 80;
  background: rgba(17, 17, 17, .72);
  backdrop-filter: blur(3px);
  display: grid; place-items: center; padding: 22px;
}
.billing-lock-card {
  background: var(--paper); border: var(--border); box-shadow: var(--shadow);
  padding: 26px 24px; max-width: 460px; width: 100%;
}
.billing-lock-card h2 { font-size: clamp(22px, 5vw, 30px); margin: 10px 0 12px; }
.billing-lock-card p { font-size: 15px; margin-bottom: 12px; }
.billing-lock-card .btn { margin-top: 8px; }
.billing-lock-out {
  display: block; text-align: center; margin-top: 12px;
  font-family: var(--mono); font-size: 12px; color: #666;
}

/* [hidden] has to WIN here. `.finalstep-over .wallet-btn` is two classes
   and beats a bare [hidden], so hiding the wrong platform's button did
   nothing and both showed. Same trap as .confirm-over above, and as the
   spinner: anything that sets `display` outranks the attribute. */
.finalstep-over .wallet-btn[hidden],
.saveopt[hidden] { display: none !important; }

/* Mark-read for a thread with no booking to open. Sits outside the row
   because that row is an <a> on the other branch, and a form inside a
   link is not legal HTML. */
.thread-done { padding: 0 16px 14px; border-bottom: var(--border); }
.thread-done:last-child { border-bottom: 0; }


/* Which table, and for how long. A host at a door is choosing between real
   options, so show them as targets with the number on each rather than as a
   dropdown that says nothing. */
.tblpick { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; }
.tblpick .tbl {
  flex: 0 0 auto; display: block; cursor: pointer; margin: 0;
  padding: 9px 13px; min-width: 96px; text-align: left;
  background: #fff; border: var(--border); box-shadow: var(--shadow-sm);
  text-transform: none; letter-spacing: 0;
}
.tblpick .tbl input { position: absolute; opacity: 0; pointer-events: none; }
.tblpick .tbl b { display: block; font-size: 15px; }
.tblpick .tbl span {
  display: block; font-family: var(--mono); font-size: 11px;
  letter-spacing: .5px; opacity: .65; margin-top: 2px;
}
.tblpick .tbl:has(input:checked) { background: var(--lime); }
.tblpick .tbl.tight { opacity: .62; }
.tblpick .tbl.tight span { color: #a05a00; opacity: 1; }

/* The restaurant's own note on their booking page. Reads as them talking,
   not as one of our labels — so it sits in the venue's amber, not the
   system lime. */
/* the restaurant's own words, plain — it's their widget (Thom, 30 Sep) */
.venue-note {
  margin: 0 0 16px; padding: 0;
  font-family: var(--mono); font-size: 13px; line-height: 1.5;
  white-space: pre-line;
}

/* A table someone is sitting at, or one that is booked. Shown, because a
   host can see the room better than we can — but never as an option. */
.tblpick .tbl.taken { opacity: .45; }

/* A chip that is a radio. The input itself is hidden — the lit chip is the
   answer — so it stays keyboard-reachable rather than being swapped for a
   div that only a mouse can use. */
.chip input[type=radio] { position: absolute; opacity: 0; width: 0; height: 0; }
.chip small {
  display: inline-block; margin-left: 6px; font-size: 10px;
  letter-spacing: .5px; opacity: .6;
}
.chip.on small { opacity: .8; }
/* keyboard focus only. :focus-within lit up on every tap and stayed until the
   next one (Thom, 30 Sep). For chips wrapping a radio/checkbox the input
   holds focus, so ask whether THAT focus is the visible kind. */
.chip:has(> input:focus-visible) { outline: 3px solid var(--ink); outline-offset: 2px; }

/* the menu upload inside the announcement window */
.menurow { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.menurow input[type=file] { flex: 1 1 180px; min-width: 0; font-family: var(--mono); font-size: 12px; }
.menurow .btn { flex: 0 0 auto; }
.modal-rule { border: none; border-top: 2px solid var(--ink); opacity: .25; margin: 20px 0; }
.venue-menu { margin-top: 12px; }
/* Latest menu: a button in Turn's face, not a link in the system blue */
.menu-btn {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  letter-spacing: 1px; text-transform: uppercase; text-decoration: none;
  color: var(--ink); background: #fff; border: 2px solid var(--ink);
  padding: 7px 12px 6px; box-shadow: 3px 3px 0 var(--ink);
}
.menu-btn:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--ink); }
.menu-btn svg { flex: 0 0 auto; }
/* the Time box before its times arrive: the shape of them, never words */
.chip.skel {
  min-width: 72px; height: 46px; pointer-events: none;
  background: rgba(128, 128, 128, .18); border-color: rgba(128, 128, 128, .28);
  box-shadow: none; animation: skel 1.1s ease-in-out infinite alternate;
}
@keyframes skel { from { opacity: .45; } to { opacity: 1; } }

/* a setting almost nobody needs, folded away rather than deleted */
.adv { margin-top: 18px; border-top: 2px solid var(--ink); padding-top: 12px; }
.adv summary {
  cursor: pointer; font-family: var(--mono); font-size: 12px;
  letter-spacing: 1px; text-transform: uppercase;
}


/* footer on guest pages: the mark, with "Powered by" over it */
.powered { display: inline-flex; flex-direction: column; gap: 4px; text-decoration: none; color: var(--ink); }
.powered small { font-family: var(--mono); font-size: 10px; letter-spacing: 1px; text-transform: uppercase; opacity: .6; }
.powered .logo { gap: 8px; }
.powered .logo-mark { width: 30px; height: 30px; font-size: 18px; line-height: 30px; box-shadow: 3px 3px 0 var(--ink); }
.powered .logo-name { font-size: 18px; }

/* the one-line refusal inside the widget, above the redrawn times */
.refused { margin: 0 0 10px; padding: 10px 12px; border: var(--border); border-left-width: 8px;
  border-color: var(--red); font-family: var(--mono); font-size: 13px; }

.dir-opts { margin-top: 8px; }

/* the waitlist page */
.h1-sub { display: block; font-family: var(--mono); font-weight: 400; font-size: 14px; letter-spacing: 1px; text-transform: uppercase; margin-top: 8px; }
.q-wait { margin: 10px 0 14px; font-family: var(--mono); font-size: 14px; font-weight: 700; }

.q-eta { font-family: var(--sans); font-weight: 900; font-size: 44px; letter-spacing: -1px; line-height: 1.05; margin: 4px 0 10px; }
.q-text { font-family: var(--mono); font-size: 13px; letter-spacing: 2px; text-transform: uppercase; margin: 0 0 6px; }

/* feedback: six rows of five stars, a live average */
.rate-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 0; border-bottom: 2px solid var(--ink); }
.rate-row:last-of-type { border-bottom: 0; }
.rate-label { font-family: var(--mono); font-size: 13px; letter-spacing: 1px; text-transform: uppercase; }
.stars { display: flex; gap: 2px; }
.star { background: none; border: 0; padding: 2px; font-size: 26px; line-height: 1; color: #d6d6cf; cursor: pointer; }
.star.on { color: var(--ink); }
.star:focus-visible { outline: 3px solid var(--ink); outline-offset: 1px; }
.fb-avg { margin: 14px 0 0; text-align: center; font-family: var(--sans); font-weight: 900; font-size: 30px; letter-spacing: -0.5px; }

/* link-in-bio */
.bio { display: grid; gap: 14px; margin-top: 28px; }
.bio .btn { padding: 18px 20px; font-size: 16px; }

/* the hidden attribute always wins, even over .btn.block (1 Oct) */
[hidden] { display: none !important; }

/* social centre tabs */
.stab[hidden] { display: none; }
.freedpick.on { background: var(--lime-soft) !important; }
#socialtabs .chip { flex: 1; text-align: center; }
.freedday { font-family: var(--mono); font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; padding: 10px 18px 6px; background: var(--paper); border-bottom: 2px solid var(--ink); }

/* the brief sheet */
.brief-sheet { margin: 0; font-family: var(--mono); font-size: 13px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }

/* ---- Settings, sectioned (1 Oct) ---- */
.set-title { margin: 6px 0 0; }
.setpane > .panel.setform { margin-bottom: 18px; }
.saverow { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-top: 24px; }
.saverow .save { min-width: 150px; }
.saverow .save:disabled { background: #fff; color: #888; cursor: default; box-shadow: none; transform: none; }
.saverow .cancel { flex: 0 0 auto; }
.label-row { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.label-link { background: none; border: 0; cursor: pointer; font: inherit;
  letter-spacing: inherit; text-transform: none; text-decoration: underline;
  color: var(--ink);
  /* A thumb needs about 44px. The text is 17px tall, so the rest is
     padding, pulled back out with a negative margin so nothing on the
     page moves — the tap area grows, the layout does not (1 Oct). */
  padding: 13px 10px; margin: -13px -10px; }
.lookup { position: relative; }
.lookup-list { position: absolute; left: 0; right: 0; top: 100%; z-index: 20; background: #fff; border: var(--border); box-shadow: var(--shadow-sm); max-height: 260px; overflow-y: auto; }
.lookup-hit { display: block; width: 100%; text-align: left; background: #fff; border: 0; border-bottom: 1px solid #ddd; padding: 11px 12px; font-family: var(--mono); font-size: 13px; cursor: pointer; }
.lookup-hit:last-child { border-bottom: 0; }
.lookup-hit:hover, .lookup-hit:focus { background: var(--lime); outline: 0; }
.steps { margin: 0; padding-left: 20px; font-family: var(--mono); font-size: 14px; line-height: 1.6; }
.steps li + li { margin-top: 8px; }

/* selects look like ours, not the OS's */
select {
  -webkit-appearance: none; appearance: none; padding-right: 44px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%23111' stroke-width='2.4' stroke-linecap='square'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
}
select:disabled { color: #999; }
/* settings tabs: the day strip's shape, one row that scrolls */
.settabs { margin: 18px 0 22px; }
.settabs .chip { font-family: var(--mono); font-weight: 700; font-size: 12px; letter-spacing: 1px; text-transform: uppercase; padding: 11px 16px; }
.big-head.sec { font-size: clamp(24px, 4vw, 32px); margin: 26px 0 12px; }
.setpane > .big-head.sec:first-child { margin-top: 0; }
.pickbtn {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 12px 44px 12px 14px; font-family: var(--mono); font-size: 15px;
  border: var(--border); background: #fff; color: var(--ink);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%23111' stroke-width='2.4' stroke-linecap='square'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
}
.chip-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.chip-grid .chip { flex: 0 0 auto; }
#map { height: min(60svh, 460px); border-top: var(--border); border-bottom: var(--border); }
.leaflet-container { font-family: var(--mono); }
.pin-mark { width: 22px; height: 22px; background: var(--lime); border: 3px solid var(--ink); box-shadow: 3px 3px 0 var(--ink); cursor: grab; }
.leaflet-bar { border: var(--border) !important; border-radius: 0 !important; box-shadow: var(--shadow-sm) !important; }
.leaflet-bar a { border-radius: 0 !important; border-bottom: 2px solid var(--ink) !important; color: var(--ink) !important; font-family: var(--mono); font-weight: 700; background: #fff !important; }
.leaflet-bar a:last-child { border-bottom: 0 !important; }
.leaflet-container .leaflet-control-attribution { font-family: var(--mono); font-size: 10px; }
/* the room as one list: tap a row to edit it in place */
.trow { display: flex; align-items: center; gap: 12px; padding: 0 18px 0 0; border-bottom: 2px solid var(--ink); }
.trow:last-child { border-bottom: 0; }
.trow-main { flex: 1; display: flex; flex-direction: column; align-items: flex-start; gap: 3px; text-align: left; background: none; border: 0; padding: 16px 18px; cursor: pointer; color: var(--ink); font: inherit; }
.trow-name { font-weight: 700; font-size: 17px; }
.trow-sub { font-family: var(--mono); font-size: 12px; opacity: .75; }
.trow-edit { font-family: var(--mono); font-size: 11px; letter-spacing: 1px; text-transform: uppercase; text-decoration: underline; }
.trow.open { background: var(--lime-soft); }
.trow.open .trow-edit { text-decoration: none; opacity: .5; }
.tdrawer { padding: 4px 18px 20px; border-bottom: 2px solid var(--ink); background: var(--paper); }
.tdrawer > label:first-of-type { margin-top: 10px; }
.tremove { padding: 0 18px 18px; background: var(--paper); border-bottom: 2px solid var(--ink); }
.tdrawer + .tremove { margin-top: -2px; }
.tfoot { border-top: 2px solid var(--ink); }
.tnew .tdrawer { padding-left: 0; padding-right: 0; border-bottom: 0; background: transparent; }
.chip.tick { cursor: pointer; }
.chip.tick input[type=checkbox] { position: absolute; opacity: 0; width: 0; height: 0; }
.chip.tick:has(> input:checked) { background: var(--ink); color: var(--lime); box-shadow: var(--shadow-sm); }
.chip.walk { margin-top: 16px; }
.combo-ends { margin-top: 12px; border: var(--border); background: var(--amber); padding: 10px 12px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; font-family: var(--mono); font-size: 13px; }
.combo-ends[hidden] { display: none; }
.chip.walk { display: inline-flex; align-items: center; gap: 8px; }
.tblpick .tbl.small b::after { content: " ·"; opacity: .4; }
.tickbox { display: flex; align-items: center; gap: 10px; margin-top: 16px; font-family: var(--mono); font-size: 14px; text-transform: none; letter-spacing: 0; cursor: pointer; }
.tickbox input[type=checkbox] { width: 20px; height: 20px; margin: 0; accent-color: var(--lime); }
.tickgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 14px; }
.tickgrid .tickbox { margin-top: 8px; font-size: 13px; }
@media (max-width: 420px) { .tickgrid { grid-template-columns: 1fr; } }
.lede { font-family: var(--mono); font-size: 13px; line-height: 1.5; margin: -4px 0 14px; max-width: 46ch; }
.tag.plain { background: #fff; color: var(--ink); }
.trow-name .tag { margin-left: 8px; vertical-align: 2px; }
.tickbox input[type=radio] { width: 20px; height: 20px; margin: 0; accent-color: var(--lime); }
.setform .svc-day { padding: 12px 0; }
.setform #svcbox { border: 0; }
@media (max-width: 520px) {
  .svc-row { grid-template-columns: auto 10px auto 30px; }
  .svc-row input[type=text] { grid-column: 1 / -1; }
  .svc-head { display: none; }
}
@media (max-width: 520px) {
  .svc-row input[type=time] { width: 118px; padding-left: 10px; padding-right: 8px; }
  .svc-row { grid-template-columns: 118px 10px 118px 30px; }
}
@media (max-width: 520px) {
  .svc-row { grid-template-columns: minmax(0, 1fr) 10px minmax(0, 1fr) 30px; }
  .svc-row input[type=time] { width: 100%; min-width: 0; }
}
/* the big switch at the top of a tab: on, and the tab's settings open */
.bigswitch { display: flex; align-items: center; gap: 14px; margin: 0 0 22px; }
.switch { position: relative; width: 66px; height: 38px; border: var(--border); border-radius: 999px; background: #fff; box-shadow: var(--shadow-sm); cursor: pointer; flex: 0 0 auto; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .knob { position: absolute; top: 4px; left: 4px; width: 26px; height: 26px; border-radius: 50%; background: #bbb; transition: left .15s ease; }
.switch:has(input:checked) { background: var(--ink); }
.switch:has(input:checked) .knob { left: 32px; background: var(--lime); }
.switch:has(input:focus-visible) { outline: 3px solid var(--ink); outline-offset: 3px; }
.switch-label { font-weight: 900; font-size: 20px; letter-spacing: -.3px; line-height: 1.1; }
.switch-label small { display: block; font-family: var(--mono); font-size: 11px; letter-spacing: 1px; text-transform: uppercase; font-weight: 400; opacity: .7; margin-top: 3px; }
.when-on[hidden] { display: none; }
.capgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
@media (max-width: 420px) { .capgrid { grid-template-columns: 1fr; } }
.bignum { font-weight: 900; font-size: 40px; letter-spacing: -1px; line-height: 1; margin-top: 4px; }
.captable { width: 100%; border-collapse: collapse; margin-top: 6px; font-size: 14px; }
.captable td { padding: 10px 0; border-bottom: 1px solid #ddd; vertical-align: top; }
.captable tr:last-child td { border-bottom: 0; }
.captable td small { display: block; font-family: var(--mono); font-size: 11px; opacity: .7; margin-top: 2px; }
.captable .num { text-align: right; font-family: var(--mono); white-space: nowrap; padding-left: 10px; }
.captable .total td { border-top: 2px solid var(--ink); border-bottom: 0; }
input[type=date] { width: 100%; padding: 12px 14px; font-family: var(--mono); font-size: 15px; border: var(--border); background: #fff; color: var(--ink); -webkit-appearance: none; appearance: none; min-height: 50px; }
.row button.cl-edit { background: none; border: 0; padding: 0; text-align: left; cursor: pointer; color: var(--ink); font: inherit; font-weight: 700; min-width: 140px; }
.row button.cl-edit small { display: block; font-family: var(--mono); font-weight: 400; font-size: 11px; }
/* three now: party size · max · minimum for dynamic (Algorithm 1, 12 Oct) */
.turn-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) 30px; gap: 8px; padding: 6px 0; align-items: center; }
@media (max-width: 620px) {
  /* a phone: size on its own line, then max and minimum side by side */
  .turn-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 30px; }
  .turn-row select[name=turn_from] { grid-column: 1 / 3; grid-row: 1; }
  .turn-row .svc-del { grid-column: 3; grid-row: 1; }
  .turn-row select[name=turn_mins] { grid-column: 1; grid-row: 2; }
  .turn-row select[name=turn_squeeze] { grid-column: 2 / 4; grid-row: 2; }
}
.turn-row select { width: 100%; min-width: 0; flex: none; }
.turn-row select { padding: 12px 30px 12px 10px; background-position: right 9px center; font-size: 14px; }
/* The shorter-duration select is a third control on a row Thom already
   called gross once when it overflowed, so it drops to its own full-width
   line underneath rather than squeezing the other two (1 Oct). */
.turn-row .squeezecol { grid-column: 1 / -1; }
.modal { box-sizing: border-box; }
.modal-card { box-sizing: border-box; max-width: 100%; }
.brow-wait .brow-when { white-space: normal; overflow-wrap: anywhere; }
/* brand kit + the links/widgets tab */
.brandmark { max-height: 72px; max-width: 220px; display: block; }
.brandrow { display: flex; align-items: center; gap: 14px; }
.brandrow input[type=color] { width: 64px; height: 44px; padding: 2px; border: var(--border); background: #fff; cursor: pointer; }
.sub-head { font-family: var(--mono); font-size: 12px; letter-spacing: 2px; text-transform: uppercase; margin: 26px 0 10px; padding-top: 16px; border-top: 2px solid var(--ink); }
.setpane details.adv { margin-top: 10px; }
.setpane details.adv summary { font-family: var(--mono); font-size: 12px; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; }
.setpane .linkqr { margin-top: 10px; max-width: 180px; }
.setpane .linkqr svg { width: 100%; height: auto; display: block; }
input[type=file] { width: 100%; font-family: var(--mono); font-size: 13px; padding: 10px 0; }
/* Turn's own mark keeps Turn's colour, whatever the venue painted the page */
.logo-mark { color: #c6ff00; }
/* a section strip inside a tab — the tab strip's shape, one size down */
.substrip { margin: 0 0 20px; }
.substrip .chip { font-family: var(--mono); font-weight: 700; font-size: 11px; letter-spacing: 1px; text-transform: uppercase; padding: 9px 13px; }
.subpane[hidden] { display: none !important; }
.subpane > .big-head.sec { margin-top: 0; }
/* A venue that chose a dark ground. The WIDGET only, not the page around
   it (Thom, 1 Oct) — so the venue's own site keeps its own background and
   only the thing we draw changes. Tokens on the card; everything inside
   inherits them. */
.brand-dark {
  --paper: #121212; --ink: #f4f2e8;
  background: #1d1d1d; color: var(--ink);
}
.brand-dark .panel,
.brand-dark input[type=text], .brand-dark input[type=tel],
.brand-dark input[type=time], .brand-dark input[type=number],
.brand-dark input[type=date], .brand-dark select, .brand-dark textarea,
.brand-dark .chip, .brand-dark .btn.ghost, .brand-dark .tbl { background: #262626; color: var(--ink); }
/* text sitting ON the accent stays dark, whatever the ground is */
.brand-dark .btn, .brand-dark .tag.confirmed, .brand-dark .tag.pending,
.brand-dark .chip.on, .brand-dark .partychip.on, .brand-dark .hl,
.brand-dark .tblpick .tbl:has(input:checked) { color: #111; }
.brand-dark .btn.ghost { color: var(--ink); }
.brand-dark .chip.on { background: var(--lime); }
.brand-dark .kicker { color: #111; background: var(--lime); }
.brand-dark .chip .slot-end { color: #9a9a9a; }
/* The folds. Without these the body stayed white on a dark widget and the
   summary's label was grey on white — unreadable (Thom, 1 Oct). */
.brand-dark .fold { background: #262626; border-color: var(--ink); }
.brand-dark .fold > summary { color: var(--ink); }
.brand-dark .fold-picked { color: var(--ink); }
.brand-dark .fold > summary::before { border-color: var(--ink); }
.brand-dark .fold[open] > summary { border-bottom-color: var(--ink); }
.brand-dark .pickbtn {
  background-color: #262626; color: var(--ink);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%23f4f2e8' stroke-width='2.4' stroke-linecap='square'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
}
.brand-dark select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' viewBox='0 0 14 9'%3E%3Cpath d='M1 1l6 6 6-6' fill='none' stroke='%23f4f2e8' stroke-width='2.4' stroke-linecap='square'/%3E%3C/svg%3E");
  /* The `background: #262626` shorthand above resets repeat and position,
     so the chevron tiled right across the control and read as diagonal
     hatching — on every select on every dark widget (1 Oct). */
  background-color: #262626;
  background-repeat: no-repeat;
  background-position: right 16px center;
}
input[type=password] { width: 100%; padding: 12px 14px; font-family: var(--mono); font-size: 15px; border: var(--border); background: #fff; color: var(--ink); }
/* the experience date calendar */
.cal { margin-top: 8px; border: var(--border); background: #fff; padding: 10px; }
.cal-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-family: var(--mono); font-size: 13px; margin-bottom: 8px; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.cal-dow { font-family: var(--mono); font-size: 10px; text-align: center; opacity: .6; }
.cal-day { font-family: var(--mono); font-size: 13px; padding: 8px 0; background: #fff; border: 2px solid var(--ink); cursor: pointer; color: var(--ink); }
.cal-day.on { background: var(--lime); font-weight: 700; }
.timerow { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.timerow input[type=time] { width: auto; flex: 1 1 auto; }
.expform .modebox[hidden] { display: none !important; }
/* the country picker: 64 of them, so it searches */
.cp-search { width: 100%; margin-bottom: 12px; }
.cp-list { max-height: min(52svh, 420px); overflow-y: auto; border: var(--border); }
.cp-row { display: block; width: 100%; text-align: left; background: #fff; border: 0;
  border-bottom: 1px solid #ddd; padding: 12px 14px; font-family: var(--mono);
  font-size: 14px; cursor: pointer; color: var(--ink); }
.cp-row:last-child { border-bottom: 0; }
.cp-row:hover, .cp-row:focus { background: var(--lime-soft); outline: 0; }
.cp-row.on { background: var(--lime); font-weight: 700; }
.cp-row[hidden] { display: none !important; }

/* ---- the messenger on the booking page (8 Oct) ---------------------------
   Replies happen here, keyed to the booking by the token in the URL, instead
   of by text. Venue on the left, the guest on the right, the way every phone
   draws a conversation — no new vocabulary for something people already
   know how to read. */
.msg-list { max-height: 46vh; overflow-y: auto; display: flex;
  flex-direction: column; gap: 8px; margin-bottom: 10px; }
.msg { max-width: 86%; padding: 8px 11px; border: 2px solid var(--ink);
  line-height: 1.4; word-wrap: break-word; }
.msg b { display: block; font-size: 11px; letter-spacing: .4px;
  text-transform: uppercase; margin-bottom: 2px; opacity: .7; }
.msg span { white-space: pre-wrap; }
.msg-venue { align-self: flex-start; background: var(--paper, #fff); }
.msg-you { align-self: flex-end; background: var(--lime);
  /* lime is a light colour in BOTH palettes, so the text on it is fixed
     dark — under the dark palette --ink turns light, which would put light
     text on lime and make the guest's own words unreadable */
  color: #111111; border-color: #111111; }
.msg-form { display: grid; grid-template-columns: 1fr auto; gap: 8px;
  align-items: end; }
.msg-form textarea { width: 100%; box-sizing: border-box; resize: vertical;
  min-height: 44px; }

/* ---- the quick switcher (Cmd/Ctrl+K) ---- */
.jumper { align-items: start; padding-top: 12vh; z-index: 200; }
.jump-card { width: min(560px, calc(100vw - 32px)); padding: 0; }
.jump-card input[type=search] { width: 100%; box-sizing: border-box; border: 0;
  border-bottom: 2px solid #111111; font-size: 16px; padding: 16px 18px; margin: 0; }
.jump-list { list-style: none; margin: 0; padding: 6px 0; max-height: 50vh; overflow-y: auto; }
.jump-item { padding: 10px 18px; cursor: pointer; display: flex; flex-direction: column; gap: 2px; }
.jump-item b { font-weight: 600; }
.jump-item span { font-size: 12px; opacity: .65; }
.jump-item.on { background: #111111; color: #f4f2e8; }
.jump-none { padding: 14px 18px; opacity: .6; }

/* walk-in "time given": a time the chosen table cannot give is greyed out */
#given .chip.off { opacity: .35; cursor: not-allowed; }

/* Algorithm 6: the waiting list's planned tables, and the invite prompt */
.tl-block.ghost { background: transparent; border: 2px dashed var(--ink); color: var(--ink); opacity: .75; }
.brow-wait.due { box-shadow: inset 4px 0 0 var(--lime); }
.btn.invite { background: var(--lime); color: #111111; }

/* the prepaid widget's View menu, gone when the menu has nothing to show */
.viewmenu[hidden] { display: none; }
/* the pay screen's bill in the widget's own face: the line on the left
   was the system sans while the sums beside it were mono */
main.booking .captable td { font-family: var(--mono); }
/* the combination widget's tabs: Book · Waitlist · Prepaid, equal widths,
   the one you are on lit like a picked chip */
.combobar { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: 8px; padding: 18px 22px 0; }   /* inside the panel, under its head */
.combobar[hidden] { display: none; }
.combobar .chip { text-align: center; text-decoration: none; min-width: 0;
  overflow-wrap: break-word;
  font-family: var(--mono); font-size: 12px; font-weight: 700;
  letter-spacing: 1px; text-transform: uppercase; padding: 11px 6px; }
/* more than Book · Waitlist · Prepaid — another system's tabs added (19 Oct):
   two to a row, so a name like "Private dining" fits whole on a phone */
.combobar.many { grid-auto-flow: row; grid-template-columns: 1fr 1fr; }
/* the lit tab reads like a picked chip on either ground */
.brand-dark .combobar .chip.on { color: #111; }
/* a tab opening with the guest's number already known: keep Step 1 out of
   sight for the moment it takes to say who they are */
.resuming #step1 { visibility: hidden; }

/* System integrations (19 Oct): another booking system's own page inside a
   combined widget's tab. Tall enough to book in without a second scrollbar
   on a phone; the panel's border is the frame's border. */
.intframe { display: block; width: 100%; height: min(78svh, 760px); border: 0;
  border-top: 3px solid var(--ink); background: #fff; }
.intlink a { color: inherit; }

/* The bot trap (app.py, bot_fields): a field a person never sees or tabs
   to, offscreen rather than display:none so a bot filling every box fills
   this one too. */
.bot-trap { position: absolute; left: -10000px; top: auto; width: 1px;
  height: 1px; overflow: hidden; }
