/* The chair page. Held one-handed, on a phone, by somebody whose other hand is wet.
 *
 * NO LITERAL COLOUR IN THIS FILE. Every colour is a var() the active theme fills in, apart from
 * `transparent` and `currentColor`, which name no colour of their own. A theme that omits a
 * token then fails visibly rather than half-applying.
 *
 * No icons: meaning is carried in type, colour, rule weight and space. Nothing tappable is
 * under 44px.
 */

*, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body { margin: 0; padding: 0; }

/* The one webfont, for the house display face. Self-hosted, latin only, 17KB, and `swap` so the
   fallback is readable from the first frame. Themes do not get to add another. */
@font-face {
  font-family: "DM Serif Display";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/app/q/fonts/dm-serif-display-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

html {
  background: var(--bg);

  /* One sheet of metal for the whole page. Both ends are the same stop because the sheet tiles,
     and a gradient whose first and last colours differ seams at every repeat. */
  --foil-img: linear-gradient(100deg,
    var(--foil-1) 0%, var(--foil-2) 13%, var(--foil-3) 30%,
    var(--foil-4) 44%, var(--foil-3) 56%, var(--foil-2) 74%,
    var(--foil-1) 88%, var(--foil-1) 100%);
  --foil: 0px;
}

/* Set anything in metal. DISPLAY TYPE ONLY: the foil stops are held to 3:1, the floor for large
   text, not the 4.5:1 ordinary copy owes. Labels, drink names and sentences stay in ink. */
.foil {
  background-image: var(--foil-img);
  background-size: 190% 100%;
  background-position: 22% 0;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

/* The ground, as one fixed sheet. Four layers, top to bottom: the theme's veil, its backdrop
   photograph, a pool of light at the top, and the bg-to-bg2 gradient. A theme with no photograph
   sets the first two to `none` and the last two are the whole background -- no second code path.

   The pool sits UNDER the backdrop; over it, it would hide the picture exactly where the wordmark
   and crest are. A fixed pseudo-element rather than `background-attachment: fixed`, which iOS
   Safari ignores on a scrolling page. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    var(--veil),
    var(--backdrop) center center / cover no-repeat,
    radial-gradient(120% 80% at 50% 0%, var(--panel) 0%, rgba(0, 0, 0, 0) 58%),
    linear-gradient(180deg, var(--bg) 0%, var(--bg2) 100%);
}

body {
  color: var(--ink);
  font: 16px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  min-height: 100svh;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  /* The help bar is fixed; this keeps the last drink reachable above it. */
  padding: env(safe-area-inset-top) env(safe-area-inset-right)
           calc(84px + env(safe-area-inset-bottom)) env(safe-area-inset-left);
}

/* --- what is behind everything -------------------------------------------------------------
   Two engines, both fixed behind the page and both deaf to touch: `field` is the weather and
   `paper` the cut scenery a theme may hang behind it. Structure only -- every colour, shape and
   shadow comes from the theme's own spec. A theme that asks for neither draws nothing.

   `overflow:hidden` on .paper is load-bearing: the near sheets slide a long way sideways with
   the parallax, and without a clip they would grow the document rather than leave past the edge
   of it. */
.field { position: fixed; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 0; display: block; }
.paper { position: fixed; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
/* No `will-change`: each sheet is viewport-sized, so the hint would hold seven full-screen
   textures for the whole visit. A running transform is promoted anyway. */
.pl { position: absolute; inset: 0; }
.pl svg { position: absolute; top: 0; left: 0; display: block; }

/* Everything a guest reads or taps sits above both. */
#landing, .step, .helpbar { position: relative; z-index: 1; }

/* --- steps ---------------------------------------------------------------------------------
   One section is on at a time. `hidden` is not used here: an author display rule would beat the
   UA stylesheet's [hidden]{display:none} and the attribute would silently do nothing. */
.step { display: none; }
.step.on { display: block; animation: rise .32s ease both; }
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .step.on { animation: none; }
  #landing .lnd > * { animation: none !important; opacity: 1 !important; }
  * { transition-duration: 0s !important; }
}

/* --- the engraved rule -----------------------------------------------------------------------
   A centred label with a tapered hairline either side and a lozenge where line meets words.
   Drawn entirely in CSS; the lozenge is a rotated square, not a glyph. */
.orn { display: flex; align-items: center; justify-content: center; gap: 11px; width: 100%; }
.orn-t {
  flex: 0 0 auto; font-size: 10px; font-weight: 800; text-transform: uppercase;
  letter-spacing: .24em; color: var(--ink3); white-space: nowrap; padding-left: .24em;
}
.orn-x {
  position: relative; flex: 1 1 0; max-width: 78px; height: 1px;
  background: linear-gradient(90deg, transparent, var(--metal-3));
}
.orn-x:last-child { background: linear-gradient(270deg, transparent, var(--metal-3)); }
/* A section's own limit, under its heading. A condition on the list below, not a heading. */
.orn-note {
  margin: -4px 0 10px; text-align: center; font-family: var(--serif); font-style: italic;
  font-size: 12.5px; line-height: 1.4; color: var(--ink3);
}
.orn-x::after {
  content: ""; position: absolute; top: -2px; width: 5px; height: 5px;
  background: var(--metal-gold); transform: rotate(45deg);
}
.orn-x:first-child::after { right: -2px; }
.orn-x:last-child::after { left: -2px; }

/* --- the crest -------------------------------------------------------------------------------
   The logo and name. Not a header bar: it scrolls with the page. */
.crest { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 22px 16px 4px; }
/* Bounded both ways, because the picture is somebody else's file: a tall logo must not push the
   first drink off the screen, and object-fit keeps a wide one from being squashed. */
.crest-mark { max-width: 168px; max-height: 46px; object-fit: contain; display: block; }
.crest-mark[hidden] { display: none; }
/* Bigger on the hub, which has nothing to scroll. On the menu every pixel here is a drink
   pushed off the first screen. */
[data-step="hub"] .crest-mark { max-width: 240px; max-height: 118px; }
.crest-name {
  font-family: var(--display); font-size: 21px; font-weight: var(--display-weight); letter-spacing: .03em;
  text-align: center; line-height: 1.15;
}

/* --- 1. the landing --------------------------------------------------------------------------
   Full screen, nothing pressable. A touch anywhere leaves at once and steps.js leaves after six
   seconds regardless.

   No plate of its own: the ground behind it is the same fixed `body::before` as everywhere else,
   so the theme's weather stays visible through it. */
#landing {
  position: fixed; inset: 0; z-index: 40;
  display: flex; align-items: center; justify-content: center; text-align: center;
  padding: 24px 22px;
  transition: opacity .5s ease;
}
#landing.gone { opacity: 0; pointer-events: none; }
/* The measure is set by the name, which is the longest line here. */
.lnd { position: relative; z-index: 1; width: 100%; max-width: 26ch; }

/* Height drives it: a portrait mark grows to the cap, a wide one is held by the column's width,
   so neither shape ends up a stripe. */
.lnd-mark {
  display: block; max-width: 100%; max-height: 180px; object-fit: contain; margin: 0 auto 24px;
}
.lnd-mark[hidden] { display: none; }
.lnd-lead {
  margin: 0 0 14px; font-size: 11px; font-weight: 800; letter-spacing: .26em;
  text-transform: uppercase; color: var(--ink3); padding-left: .26em;
}
/* Painted with the theme's foil ramp through background-clip, not a flat fill. `-webkit-` is
   still required on Safari. Wraps rather than shrinking; a long name may be two lines. */
.lnd-brand {
  margin: 0; font-family: var(--display); font-weight: var(--display-weight);
  font-size: clamp(26px, 8.4vw, 40px); line-height: 1.12; letter-spacing: .004em;
  text-wrap: balance;
}
/* Metal too, and thin. It draws itself across, once. */
.lnd-rule {
  height: 1.5px; width: min(210px, 64%); margin: 26px auto 0; transform: scaleX(0);
  background: linear-gradient(90deg, transparent, var(--metal-3) 18%, var(--metal-gold) 50%,
    var(--metal-3) 82%, transparent);
}
@keyframes draw { to { transform: scaleX(1); } }
/* The invitation, in the same italic serif as a drink's own line further down. */
.lnd-tag {
  margin: 18px auto 0; color: var(--ink2); font-family: var(--serif); font-style: italic;
  font-size: 16px; line-height: 1.55; max-width: 22ch; text-wrap: balance;
}
/* Everything arrives from above and settles; nothing rises and nothing overshoots. The rule is
   the exception and draws instead.

   THESE MUST COME AFTER `.lnd > *`, not before. Both selectors are one class deep, so source
   order is the whole tie-break -- and because the geometry rests at `scaleX(0)`, a rule that
   loses the tie is not merely un-animated, it is invisible. */
.lnd > * { animation: settle .85s cubic-bezier(.2, .7, .3, 1) both; }
.lnd-lead  { animation-delay: .15s; }
.lnd-brand { animation-delay: .3s; }
.lnd-rule  { animation: draw 1.1s .55s ease-in-out both; }
.lnd-tag   { animation-delay: .8s; }
/* Last. It is a `.lnd > *` like the rest, so without a delay of its own it would arrive at
   zero -- before the name. */
.lnd .stamp { animation-delay: 1.05s; }
@keyframes settle { from { opacity: 0; transform: translateY(-16px); } to { opacity: 1; transform: none; } }

/* HELD UNTIL THERE IS SOMETHING TO ANIMATE. These keyframes start when the stylesheet applies,
   but the words they animate arrive a network round trip later -- so left running, the whole
   sequence plays out against empty elements and the text then appears with no arrival at all.
   Declared paused; steps.js starts them once the copy is in. `both` as the fill mode holds the
   `from` frame meanwhile, so nothing flashes its finished state while it waits.

   AFTER `.lnd-rule`: `animation` is a shorthand and resets `animation-play-state` to running, so
   a paused declaration written above it would be unset for the one element that draws. */
.lnd > * { animation-play-state: paused; }
/* `playing`, not `go`: `go` is already the Send button here. With no scoping and no bundler a
   class name is a global, and a short one is a collision waiting to happen. */
#landing.playing .lnd > * { animation-play-state: running; }

/* --- 2. the menu ----------------------------------------------------------------------------
   Bounded, though it is a phone page: the same page is previewed and opened on a desktop from
   the settings screens, and unbounded it draws a drink card a metre wide. */
main { padding: 0 14px; max-width: 560px; margin: 0 auto; }
.crest { max-width: 560px; margin: 0 auto; }

/* The seat, above the menu. Display type, so it is set in metal. */
.seat {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  width: 100%; min-height: 56px; padding: 6px 8px 4px; margin: 0 0 4px;
  border: 0; background: none; font: inherit; cursor: pointer;
}
.seat-name { font-family: var(--display); font-size: 28px; font-weight: var(--display-weight); line-height: 1.08; }
.seat-alt { font-size: 11px; color: var(--ink3); letter-spacing: .06em; }
.seat:active .seat-alt { color: var(--accent-dk); }

.sec-orn { margin: 34px 0 15px; }

/* --- the word that says a drink is a button --------------------------------------------------
   A word, not a glyph: no icons on this page, and a chevron is exactly the case that rule names.

   Never tappable in its own right -- a span inside the button that already is the target, so the
   whole card takes the press and a screen reader hears one control. */
.tap {
  display: inline-block; margin-top: 11px; padding: 6px 15px;
  border-radius: var(--chip-radius); border: 1px solid var(--accent-line);
  background: var(--accent-bg); color: var(--accent-dk);
  font-size: 10px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; line-height: 1;
}
/* The same chip carrying a reason instead of an invitation. Dashed and unfilled: a house rule
   that has been kept, not an error. */
.tap-out {
  background: none; border-style: dashed; border-color: var(--rule2);
  color: var(--ink3); letter-spacing: .12em;
}

/* --- photographed drinks: soft-edged tiles ---------------------------------------------------
   Only drinks that have a photograph. `.carte` below draws the rest. */
.tiles { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px 13px; }
@media (min-width: 520px) { .tiles { grid-template-columns: repeat(3, 1fr); } }
.tile {
  display: flex; flex-direction: column; align-items: center; min-width: 0; width: 100%;
  padding: 0 0 13px; cursor: pointer; font: inherit; color: inherit; text-align: center;
  /* Shape, surface and cut edge belong to the theme, not to this file. */
  border-radius: var(--tile-radius);
  background: var(--card);
  border: 1px solid var(--card-edge);
  box-shadow: 0 1px 0 rgba(var(--sheen), .8) inset,
              0 14px 26px -20px rgba(var(--shade), .6);
  overflow: hidden;
}
/* `background-color`, not `background`: the shorthand would take the theme's surface off with it
   and the tile would go flat for as long as a finger was on it. */
.tile:active { border-color: var(--accent); background-color: var(--accent-bg); }
/* An uploaded photograph is in nobody's palette, so it is lit to sit in one: saturation off, a
   wash of the house ink over the top, a soft vignette inside the crown of the frame. */
.tile-pic { position: relative; display: block; width: 100%; aspect-ratio: 1;
  background: var(--panel2); overflow: hidden; }
.tile-pic img { width: 100%; height: 100%; object-fit: cover; display: block;
  filter: saturate(.88) contrast(1.02) brightness(1.02); }
.tile-pic::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(178deg, rgba(var(--wash), .20) 0%, rgba(var(--wash), 0) 40%),
    radial-gradient(120% 90% at 50% 4%, rgba(var(--vignette), .13) 0%, rgba(var(--vignette), 0) 70%);
}
.tile-nm { display: block; padding: 12px 11px 0; font-size: 12.5px; font-weight: 700;
  line-height: 1.3; letter-spacing: .08em; text-transform: uppercase; color: var(--accent-dk); }
.tile-bl { padding: 4px 11px 0; font-family: var(--serif); font-size: 12px; line-height: 1.35;
  color: var(--ink2); font-style: italic;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }

/* --- written drinks: a printed card ----------------------------------------------------------
   Most menus have no photograph on most rows, and the reason the old grid felt empty is that a
   drink whose entire content is its words was being drawn as a picture frame with no picture in
   it -- with the drink's first letter sitting in the hole where the picture should be. This is
   what a printed card actually does: the name, centred, and what is in it underneath.

   WHETHER THIS IS A COLUMN OR A STACK OF CARDS IS THE THEME'S CALL, and it is one number. With
   `carte-rule` set and `row-gap` at zero the rows touch under one hairline and read as a printed
   list; with a gap they come apart into separate pieces of paper, at which point the list's rules
   would be lines floating in the space between cards and `carte-rule` turns them off. */
.carte { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column;
  gap: var(--row-gap);
  border-top: 1px solid var(--carte-rule); border-bottom: 1px solid var(--carte-rule); }
.carte-l {
  display: flex; flex-direction: column; align-items: center; gap: 3px; width: 100%;
  min-height: 60px; padding: 15px 12px 14px; cursor: pointer; font: inherit; color: inherit;
  text-align: center; background-color: transparent; background-image: none;
  background: var(--card);
  border: 1px solid var(--card-edge); border-radius: var(--card-radius);
  box-shadow: 0 1px 0 rgba(var(--sheen), .7) inset,
              0 10px 20px -18px rgba(var(--shade), .55);
}
/* The divider is a hairline of its own, not the row's top border, so a theme can set
   `card-edge` and `carte-rule` independently. Second row onward, so there is never a trailing
   one. */
.carte-l + .carte-l { position: relative; }
.carte-l + .carte-l::before { content: ""; position: absolute; left: 0; right: 0; top: -1px;
  border-top: 1px solid var(--carte-rule); pointer-events: none; }
.carte-l:active { background-color: var(--accent-bg); border-color: var(--accent); }
.carte-nm { font-family: var(--display); font-size: 17px; font-weight: var(--display-weight); line-height: 1.2;
  color: var(--ink); }
/* The line under a drink's name. Set in the BODY face, not the display one: a display cut at
   13px is doing a job it was not drawn for, and one with no italic gets a faked slope. */
.carte-bl { font-family: var(--serif); font-size: 13px; line-height: 1.35; color: var(--ink2);
  font-style: italic; max-width: 30ch; }

/* A drink this guest has had their allowance of. Dimmed but still readable; a menu that hides
   what you cannot have reads as a menu that changed. The dim is on the drink and not the whole
   control, because the chip underneath carries the reason. */
.tile[disabled], .carte-l[disabled] { cursor: default; }
.tile[disabled] .tile-nm, .tile[disabled] .tile-bl,
.carte-l[disabled] .carte-nm, .carte-l[disabled] .carte-bl { opacity: .42; }
.tile[disabled] .tile-pic { opacity: .42; }
.tile[disabled] .tile-pic img { filter: saturate(.35) brightness(1.04); }

/* --- the stamp: that the drinks are free ------------------------------------------------------
   Struck in a double rule, in the same metal as the wordmark: one hairline box reads as a form
   field, two as a certificate. The inner rule is inset and held at .45, because a second line of
   equal weight reads as a mistake.

   Drawn on all three screens somebody can start on, so this is never something you had to arrive
   by the right route to be told. */
.stamp {
  display: inline-flex; flex-direction: column; align-items: center; gap: 5px;
  position: relative; margin: 18px auto 2px; padding: 11px 17px 10px; max-width: 100%;
  border: 1px solid var(--metal-3); border-radius: var(--radius);
}
.stamp[hidden] { display: none; }
.stamp::before {
  content: ""; position: absolute; inset: 3px; pointer-events: none;
  border: 1px solid var(--metal-4); border-radius: calc(var(--radius) - 3px); opacity: .45;
}
/* Display type, so it is set in the display face and held to the foil's 3:1 rather than 4.5:1. */
.stamp-w {
  font-family: var(--display); font-weight: var(--display-weight); line-height: 1;
  font-size: clamp(18px, 5vw, 21px); letter-spacing: .14em; padding-left: .14em;
  text-transform: uppercase;
}
/* Ordinary copy in ordinary case, under the announcement above it.

   `pre-line`, because these are the location's own words shown as typed: a line break in the
   settings box is a line break here. Runs of spaces still collapse -- a double space is a typo,
   a newline is a decision. */
.stamp-s {
  font-family: var(--serif); font-size: 12.5px; line-height: 1.45; color: var(--ink3);
  text-align: center; max-width: 30ch; white-space: pre-line;
}
.stamp-s em { font-style: normal; font-weight: 600; color: var(--ink2); }
/* Centred by its container, not itself: inline-flex shrinks the box to the words, which is what
   makes it a stamp rather than a banner.

   `:not([hidden])` is load-bearing. These selectors are one class deeper than `.stamp[hidden]`
   above, so without it they win and the box shows through its own hidden attribute. */
main > .stamp:not([hidden]), .lnd > .stamp:not([hidden]) {
  display: flex; width: fit-content;
}
/* Inside the hub's column the grid decides the width, so it stretches with the rest. */
.hub-acts > .stamp:not([hidden]) { display: flex; width: auto; }

/* --- the returning guest's column -------------------------------------------------------------
   One column as wide as its widest child, which is the stamp. `max-content` measures every item
   and takes the largest; `minmax(0, …)` stops that running past the edge of a narrow phone. The
   buttons stretch to it, so all three line up with no width written down anywhere. */
.hub-acts {
  display: grid; grid-template-columns: minmax(0, max-content); justify-content: center;
  gap: 11px; margin: 20px 0 0;
}
.hub-acts .go, .hub-acts .help {
  width: 100%; min-height: 58px; padding: 0 26px; border-radius: var(--chip-radius);
}

/* --- asking for a person ---------------------------------------------------------------------
   Set as a piece of house metal rather than a form button: bevelled rim, lit surface, words
   tracked out. It reads as a bell-pull. */
.helpbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 6;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: linear-gradient(to top, var(--bg2) 62%, transparent);
}
.help {
  width: 100%; min-height: 54px; border-radius: var(--chip-radius); cursor: pointer;
  font-weight: 700; font-size: 13px; line-height: 1; font-family: inherit;
  letter-spacing: .22em; text-transform: uppercase; color: var(--accent-dk);
  border: 1px solid transparent;
  background-image:
    linear-gradient(168deg, var(--panel) 0%, var(--accent-bg) 100%),
    linear-gradient(150deg, var(--metal-5) 0%, var(--metal-3) 30%, var(--metal-2) 55%,
                    var(--metal-3) 78%, var(--metal-5) 100%);
  background-origin: padding-box, border-box;
  background-clip: padding-box, border-box;
  box-shadow: 0 1px 0 rgba(var(--sheen), .85) inset, 0 12px 22px -18px rgba(var(--shade), .9);
}
.help:active {
  color: var(--panel);
  background-image: linear-gradient(168deg, var(--accent), var(--foil-2)),
    linear-gradient(150deg, var(--foil-2), var(--foil-1));
}

/* --- sheets ---------------------------------------------------------------------------------- */
dialog {
  border: 0; padding: 0; background: transparent; color: var(--ink);
  width: 100%; max-width: 520px; margin: auto auto 0;
}
dialog::backdrop { background: rgba(var(--scrim), .45); }
.sheet {
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 18px 18px 0 0;
  padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
  box-shadow: 0 -8px 40px rgba(var(--shade), .18);
}
@media (min-width: 560px) {
  dialog { margin: auto; }
  .sheet { border-radius: 18px; }
}
.sheet h2 { font-family: var(--display); font-weight: var(--display-weight); font-size: 22px; margin: 0 0 16px; line-height: 1.2;
  text-align: center; }
/* The drink's own line, said in full here. Sits tight under the name so it reads as part of it
   rather than as the first of the questions below. */
.cbl { margin: -12px 0 14px; font-family: var(--serif); font-size: 14.5px; line-height: 1.45;
  color: var(--ink2); text-align: center; font-style: italic; }
.cbl[hidden] { display: none; }

/* WHERE IT IS GOING, at the moment that matters. A guest who moved chairs finds out they need to
   say so when they order, not while they are reading the menu. */
.dest {
  display: flex; align-items: baseline; justify-content: center; gap: 9px; flex-wrap: wrap;
  width: 100%; min-height: 48px; margin: 0 0 14px; padding: 10px 12px; cursor: pointer;
  font: inherit; text-align: center;
  background: var(--panel2); border: 1px dashed var(--accent-line); border-radius: 12px;
}
.dest-l { font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink3); }
.dest-n { font-family: var(--display); font-size: 18px; font-weight: var(--display-weight); color: var(--ink); }
.dest-c { font-size: 11px; letter-spacing: .06em; color: var(--accent-dk);
  text-decoration: underline; text-underline-offset: 3px; }

.fld { display: block; margin-bottom: 13px; }
/* Hidden has to win, and it does not on its own: the line above sets `display` from an author
   stylesheet, and the UA's [hidden]{display:none} loses to any author rule. */
.fld[hidden] { display: none; }
.fld > span { display: block; font-size: 13px; font-weight: 600; color: var(--ink2);
  margin-bottom: 6px; text-align: center; }
.fld em { font-style: normal; font-weight: 400; color: var(--ink3); }
/* 16px is what stops iOS Safari zooming the page on focus and never zooming back out. Set the
   parts, never the `font` shorthand: `font:16px inherit` is invalid and silently dropped. */
.fld input, .fld textarea {
  width: 100%; min-height: 48px; padding: 10px 13px; text-align: center;
  font-size: 16px; font-family: inherit; line-height: 1.4;
  border: 1.5px solid var(--rule2); border-radius: 11px;
  background: var(--panel2); color: var(--ink); resize: none;
}
.fld input::placeholder, .fld textarea::placeholder { color: var(--ink3); font-style: italic; }
.fld input:focus, .fld textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px;
  background: var(--panel); }

/* A required choice. Not a select: on a phone that is a modal inside a modal, and these lists
   are never longer than six. Radios rather than buttons, for the reason given in the sheet's
   markup: nothing here can be released once it is set.

   PRESSED AND CHOSEN ARE TOLD APART BY DEPTH, NOT BY A MARK. The old build had two states -- pale,
   and filled-with-the-accent for chosen -- and filled-with-the-accent is also exactly what a chip
   held down under a thumb looks like, so with a finger over it the two were one picture. The
   answer is not to draw something extra on the chip. It is that a chosen chip sits PROUD, lifted
   on a shadow, and a chip under the finger sits IN:

     · not chosen -- pale, flat on the page
     · under the finger -- pushed 1px in with the light off it, over whichever resting state it is
       sitting on, and it springs back when the finger lifts. This is the state that was missing:
       `-webkit-tap-highlight-color` is off page-wide, so a press on the chip already chosen used
       to be entirely silent, and silence after a tap reads as "it came off"
     · chosen -- filled and raised, and it stays

   Nothing else is drawn. The fill is a LIGHTNESS jump and not a hue one -- near-black ink against
   a pale panel -- so it already survives a colour-blind guest and a phone held in the sun, and a
   tick or a ring on top of it would be saying the same thing twice. */
.picks { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.pick {
  font: inherit; font-size: 16px; min-height: 44px; padding: 10px 16px; cursor: pointer;
  border-radius: var(--chip-radius);
  border: 1px solid var(--accent-line); background: var(--panel2); color: var(--ink);
  transition: background-color .16s ease, border-color .16s ease, color .16s ease,
              box-shadow .16s ease, transform .09s ease;
}
.pick[aria-checked="true"] {
  background: var(--accent); border-color: var(--accent); color: var(--panel);
  box-shadow: 0 3px 9px -4px rgba(var(--shade), .95);
}
/* The press, and ONLY the press: in, never off. It cannot change which one is chosen -- a second
   press on the chosen one is a no-op in the handler -- so nothing here may look like a release. */
.pick:active { transform: translateY(1px); }
.pick:not([aria-checked="true"]):active { background: var(--accent-bg); border-color: var(--accent);
  box-shadow: 0 1px 2px rgba(var(--scrim), .16) inset; }
.pick[aria-checked="true"]:active { background: var(--accent-dk); border-color: var(--accent-dk);
  box-shadow: 0 1px 3px rgba(var(--scrim), .5) inset; }
.pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .pick { transition: none; }
  .pick:active { transform: none; }
}

/* The age acknowledgement, as its own control rather than words on the Send button: folded in,
   one tap would mean both consent and submit. Split, it has to be answered before Send comes
   alive. The heading asks and the button answers. */
.ackbtn {
  display: flex; align-items: center; justify-content: center; gap: 11px;
  width: 100%; min-height: 50px; padding: 10px 14px; cursor: pointer;
  font: inherit; font-size: 16px; color: var(--ink);
  background: var(--panel2); border: 1.5px solid var(--rule2); border-radius: 11px;
}
.ackbox { width: 22px; height: 22px; flex: none; border-radius: 6px;
  border: 1.5px solid var(--rule2); background: var(--panel); }
.ackbtn[aria-checked="true"] { border-color: var(--accent); background: var(--accent-bg); }
.ackbtn[aria-checked="true"] .ackbox { background: var(--accent); border-color: var(--accent); }
.ackid { margin: 8px 0 0; font-size: 12px; text-align: center; color: var(--ink3);
  font-family: var(--serif); font-style: italic; }

.acts { display: flex; gap: 10px; margin-top: 4px; }
.acts button { flex: 1; min-height: 52px; border-radius: 12px; font-weight: 700; font-size: 15px;
  font-family: inherit; cursor: pointer; }
.ghost { border: 1.5px solid var(--rule2); background: var(--panel); color: var(--ink2);
  flex: 0 0 34%; }
/* The only filled control in a sheet, so it carries the material outright: struck metal, lit
   along the top, words tracked out. */
.go {
  border: 1px solid transparent; color: var(--panel);
  font-weight: 700; font-size: 13.5px; line-height: 1; font-family: inherit;
  letter-spacing: .2em; text-transform: uppercase;
  background-image:
    linear-gradient(168deg, var(--foil-3) 0%, var(--accent) 44%, var(--foil-2) 100%),
    linear-gradient(150deg, var(--metal-4) 0%, var(--foil-2) 40%, var(--foil-1) 100%);
  background-origin: padding-box, border-box;
  background-clip: padding-box, border-box;
  box-shadow: 0 1px 0 rgba(var(--sheen), .35) inset, 0 10px 18px -14px rgba(var(--shade), .9);
}
.go[disabled] { opacity: .45; }
.go:active { background-image: linear-gradient(168deg, var(--foil-2), var(--foil-1)),
  linear-gradient(150deg, var(--foil-1), var(--foil-1)); }

/* --- the receipt / the refusal ---------------------------------------------------------------- */
.outcome { padding: 60px 18px 40px; display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: 14px; }
.out-mark { font-size: 10px; font-weight: 800; letter-spacing: .24em; text-transform: uppercase;
  padding-left: .24em; }
.out-mark.good { color: var(--ok); }
.out-mark.stop { color: var(--stale); }

/* THE SHEET SAYING WHY IT DID NOT SEND. Inside the sheet, above the buttons, because the thing it
   is about is the button underneath it and the answer to it is to press that button again in a
   moment. Bordered rather than filled: a solid warning band over a half-filled form reads as the
   form having been rejected, and nothing here has been -- every answer the guest gave is still
   on screen and still good. */
.csay {
  margin: 14px 0 0; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--stale); background: var(--panel2);
  display: flex; flex-direction: column; gap: 10px;
}
/* Hidden has to win, for the same reason .fld and .cbl above say so: the rule over this one
   sets `display` from an author stylesheet, and the UA's [hidden]{display:none} loses to any
   author rule. Without this line the box was NEVER hidden -- every confirm sheet carried an
   empty bordered panel with an unexplained "Ask someone here" button under the note field, on
   every drink, for every guest, and hushSheet() had no effect at all. */
.csay[hidden] { display: none; }
.csay p { margin: 0; font-size: 14px; line-height: 1.45; color: var(--ink); }
.csay .ghost { align-self: flex-start; }
.out-title { font-family: var(--display); font-weight: var(--display-weight); font-size: 27px; margin: 0; line-height: 1.2; }
.out-note { font-size: 16px; color: var(--ink2); margin: 0; max-width: 32ch; line-height: 1.5; }
.out-card {
  width: 100%; max-width: 34ch; padding: 16px; border-radius: var(--radius);
  font-family: var(--serif); font-size: 15px; color: var(--ink2);
  background: var(--ok-bg); border: 1px solid var(--rule);
}
.out-card.stop { background: var(--panel2); }
.outcome .ghost { min-height: 52px; border-radius: 12px; font-weight: 700; font-size: 15px;
  font-family: inherit; cursor: pointer; padding: 0 26px; flex: none; }

/* --- the station picker ------------------------------------------------------------------------
   A guest who moved chairs is still holding the code they scanned, and the drink has to arrive
   where they are now. */
.roster { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center;
  max-height: 46vh; overflow-y: auto; }
.roster-group {
  width: 100%; font-size: 10px; font-weight: 800; letter-spacing: .24em; text-transform: uppercase;
  color: var(--ink3); margin-top: 8px; text-align: center; padding-left: .24em;
}

.closed-note { padding: 70px 22px; text-align: center; }
.closed-note .out-title { margin-bottom: 12px; }
/* .out-note is capped at 32ch, and `text-align: center` centres the WORDS INSIDE that box, not the
   box. In a plain block the box stays flush left, so on anything wider than the line the note sat
   visibly left of the heading above it. .outcome never showed this because it is a flex column
   with align-items: center doing the same job; this pane is not one, so the margin does it. */
.closed-note .out-note { margin-left: auto; margin-right: auto; }
