/* Guest surface — Organic layout.
 *
 * The markup is the Guest Role Workspace exactly as `public/guest.js` binds it;
 * this file is the only thing that changed in the restyle. Every selector here
 * therefore names a class the engine or `guest.html` already emitted —
 * nothing was renamed to suit the visual system, because a renamed hook is a
 * broken contract rather than a style choice.
 *
 * Loads after `organic-system.css`, which carries the tokens and the
 * component classes (`.btn`, `.tag`, `.card`, `.input`). The legacy control
 * classes (`.primary`, `.secondary`, `.text-button`) are mapped onto that
 * system near the bottom rather than restyled per panel.
 *
 * Sizing floors from the handoff are deliberate, not decoration: 64px menu
 * rows, 52px steppers, 60px Place Order, and nothing interactive under 44px.
 * The device is held one-handed at a counter.
 *
 * `--space-5` and `--space-7` do not exist. An undefined custom property
 * computes to the initial value, so `padding: var(--space-5)` silently
 * collapses to 0.
 */

/* ------------------------------------------------------------------ *
 * Ground
 * ------------------------------------------------------------------ */

/* Load-bearing, and first. Every layout rule below sets an explicit `display`,
 * and an author `display` beats the UA stylesheet's `[hidden] { display: none }`
 * — so without this the hidden sign-in panel, the hidden shell and the zeroed
 * tab badges all render at once. `guest.js` hides by attribute throughout, so
 * this is what makes every one of those states real.
 */
[hidden] {
  display: none !important;
}

/*
 * An app shell, not a document. The page itself never scrolls: the header and
 * the pinned region are fixed in place and `.guest-tab-content` is the only
 * scroller, so the tab bar stays put and a Guest never has to scroll the whole
 * surface to bring a notice back into view.
 *
 * This has to be a definite height rather than `min-height`. Under
 * `min-height` the shell is sized by its content, so the tab content grew past
 * the viewport instead of scrolling inside it, the document became taller than
 * the screen, and a restored scroll position after reload could leave the
 * session-expired alert above the fold.
 */
body.guest-page {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  overflow: hidden;
}

.guest-blob {
  position: fixed;
  z-index: 0;
  pointer-events: none;
  filter: blur(14px);
}

.guest-blob-top {
  top: -90px;
  right: -120px;
  width: 340px;
  height: 340px;
  border-radius: 48% 52% 58% 42%;
  background: radial-gradient(
    circle at 35% 35%,
    var(--color-accent-300),
    var(--color-accent-200) 55%,
    transparent 75%
  );
  opacity: 0.75;
}

.guest-blob-bottom {
  bottom: 120px;
  left: -110px;
  width: 280px;
  height: 280px;
  border-radius: 52% 48% 42% 58%;
  filter: blur(16px);
  background: radial-gradient(
    circle at 60% 40%,
    var(--color-accent-2-300),
    transparent 70%
  );
  opacity: 0.7;
}

/* Every real region sits above the ornament.
 *
 * The column is capped and centred rather than filling the viewport: this
 * surface is a phone held one-handed, and a Guest opening it on a tablet or a
 * desktop browser should get the same reading order and the same 64px rows, not
 * a stretched version of them. */
body.guest-page > header,
body.guest-page > section,
body.guest-page > main {
  position: relative;
  z-index: 1;
  box-sizing: border-box;
  width: 100%;
  max-width: 430px;
  margin-inline: auto;
}

body.guest-page > header {
  flex: none;
}

/* The route state and the sign-in panel stand in for the whole workspace, so
   they take the space the shell would have and scroll within it. */
body.guest-page > section {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
}

/* ------------------------------------------------------------------ *
 * Header
 * ------------------------------------------------------------------ */

body.guest-page > header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 12px;
  /* The top inset is the notch where there is one, and the ordinary step where
     there is not — a Guest holding a phone must not have the brand under a
     camera housing. */
  padding: max(var(--space-4), env(safe-area-inset-top, 0px)) var(--space-4)
    var(--space-3);
}

.guest-page .brand {
  display: inline-flex;
  align-items: center;
  min-width: 0;
  min-height: 44px;
  overflow: hidden;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 20px;
  line-height: 1.1;
  color: var(--color-text);
  text-decoration: none;
}

.guest-page .guest-session-actions {
  flex: 0 1 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  min-width: 0;
  max-width: 100%;
  gap: var(--space-1);
}

/* Tinted, not raised. The chip is a readout rather than a control, so it takes
   the accent fill the prototype gives it instead of a card's elevation. */
.guest-credit-chip {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 7px;
  max-width: 100%;
  padding: 7px 14px;
  border-radius: 999px;
  background: var(--color-accent-200);
  color: var(--color-accent-800);
}

.guest-credit-chip > span {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.guest-credit-chip output {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 16px;
  font-variant-numeric: tabular-nums;
}

/* This is explicitly an estimate. It stays under the authoritative balance in
 * the header, outside the tab scroller, so it remains in view while a Guest
 * browses a long menu. */
.guest-page .guest-basket-credit {
  display: block;
  flex: 0 1 100%;
  min-width: 0;
  max-width: 100%;
  overflow-wrap: anywhere;
  color: var(--color-accent-700);
  font-size: 11px;
  line-height: 1.25;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.guest-page .guest-basket-credit.short {
  color: var(--color-accent-800);
}

.guest-pass-link,
.guest-page .text-button {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 15px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--color-accent-700);
  font: inherit;
  font-size: 13.5px;
  text-decoration: none;
  /* Two words, and it wrapped to two lines at 375px, dragging the header to
     twice its height. Nothing in this row is allowed to wrap; the brand gives
     up its width first. */
  white-space: nowrap;
  cursor: pointer;
}

.guest-pass-link:hover,
.guest-page .text-button:hover {
  background: var(--color-accent-100);
}

/* ------------------------------------------------------------------ *
 * Route state and sign-in — the whole page before a Guest Session exists
 * ------------------------------------------------------------------ */

.guest-route-state,
.role-sign-in {
  margin: var(--space-4);
  padding: var(--space-6);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-md);
}

.guest-route-state h1,
.role-sign-in h1 {
  margin: var(--space-2) 0 0;
  font-size: 30px;
  line-height: 1.12;
}

.role-sign-in h1 em {
  font-style: normal;
  color: var(--color-accent-700);
}

/* ------------------------------------------------------------------ *
 * Shared small type
 * ------------------------------------------------------------------ */

.guest-page .eyebrow {
  margin: 0;
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-accent-700);
}

.guest-page .hint,
.guest-page .help {
  margin: var(--space-2) 0 0;
  font-size: 13px;
  line-height: 1.55;
  color: color-mix(in srgb, var(--color-text) 65%, transparent);
}

/* ------------------------------------------------------------------ *
 * Shell
 * ------------------------------------------------------------------ */

/* Takes the height the header leaves and passes it down: `min-height: 0` is
   what lets the tab content actually shrink and scroll rather than pushing the
   shell past the bottom of the screen. */
.guest-shell {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Position and padding only — background, radius and color are the shared
   Shell States band's (organic-system.css) to set, not this surface's to
   override. */
.connectivity-status {
  margin: 0 var(--space-4) var(--space-3);
  padding: var(--space-3) var(--space-4);
}

/* ------------------------------------------------------------------ *
 * Pinned — Ready for Pickup and the Undo Window stay above the tabs
 * ------------------------------------------------------------------ */

.guest-pinned {
  display: grid;
  gap: var(--space-2);
  padding: 0 var(--space-4);
}

.ready-panel,
.undo-panel {
  padding: var(--space-4);
  border-radius: 32px;
  box-shadow: var(--shadow-md);
}

/*
 * The panel a Guest reads walking to a counter, and the one that carries the
 * dominant pair on this surface. Both figures are the same size here: the
 * Stall Order and the Pickup Code are said together at the stall, and sizing
 * one above the other implies an order of importance that does not exist.
 */
.guest-page .ready-panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "head toggle"
    "summary toggle"
    "detail detail";
  align-items: center;
  background: var(--color-accent-100);
}

/*
 * The eyebrow carries the serving-tray mark the design puts in front of
 * `COLLECT NOW` — Lucide "hand-platter" at the system's 2.75 stroke, drawn as
 * a mask so it takes the eyebrow's own colour and needs no separate asset.
 * Decorative: "Collect now" already says it.
 */
.ready-panel .eyebrow {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--color-accent-700);
}

.ready-panel .eyebrow::before {
  content: "";
  flex: none;
  width: 15px;
  height: 15px;
  background: currentColor;
  mask: var(--collect-mark) center / contain no-repeat;
  -webkit-mask: var(--collect-mark) center / contain no-repeat;
}

.guest-page {
  --collect-mark: url("/icons/hand-platter.svg");
}

.ready-panel h2 {
  margin: var(--space-1) 0 0;
  /* 25px, as the design sets it. */
  font-size: 25px;
  line-height: 1.15;
  color: var(--color-text);
}

/* The Undo Window is the one pinned panel that is not read at a counter, so it
   takes the dark treatment and stays visually distinct from readiness. */
.undo-panel {
  background: var(--color-accent-2-800);
  color: var(--color-neutral-100);
}

.undo-panel .eyebrow {
  color: var(--color-accent-2-300);
}

.undo-panel h2 {
  margin: var(--space-1) 0 0;
  font-size: 24px;
  line-height: 1.15;
  color: var(--color-neutral-100);
}

/* The count is a tag, not a suffix on the heading. */
.ready-total {
  display: inline-flex;
  align-items: center;
  margin-left: var(--space-2);
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--color-accent-2-200);
  color: var(--color-accent-2-800);
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 12.5px;
}

.ready-instruction {
  margin: var(--space-2) 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: color-mix(in srgb, var(--color-text) 68%, transparent);
}

.undo-panel .hint {
  margin: var(--space-2) 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: color-mix(in srgb, var(--color-neutral-100) 78%, transparent);
}

.guest-page .undo-panel #undo-order {
  min-height: 44px;
  margin-top: var(--space-3);
}

.ready-list {
  display: grid;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}

.ready-empty {
  font-size: 13.5px;
  color: color-mix(in srgb, var(--color-text) 62%, transparent);
}

.ready-row {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: 26px;
  background: var(--color-bg);
}

.ready-row:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.ready-stall {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 17px;
}

/* Two equal columns, so the pair reads as a pair. */
.ready-figures {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px var(--space-4);
  margin-top: var(--space-2);
}

.ready-figure {
  display: grid;
  gap: 2px;
}

.ready-figure-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-accent-700);
}

/*
 * The dominant pair on this surface, at 46px.
 *
 * There is still exactly one dominant instance of the Pickup Code per viewport
 * — it lives here rather than in the Orders card, because this is the panel a
 * Guest is looking at while walking to a stall. The Orders card states the same
 * numbers quietly at 22px.
 */
.ready-figure-value {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 46px;
  line-height: 1.02;
  font-variant-numeric: tabular-nums;
}

/*
 * Both figures step down together while the Undo Window is open. The pinned
 * region holds two cards then, and 46px twice would push the Undo countdown —
 * the thing that is actually time-critical — off the screen.
 */
.guest-pinned:has(#undo-panel:not([hidden])) .ready-figure-value {
  font-size: 30px;
}

/* `.ready-code` wraps the Pickup Code's label and value; it carries no size of
   its own, because the pair is deliberately one size. */

/* ------------------------------------------------------------------ *
 * Undo Window
 * ------------------------------------------------------------------ */

.undo-progress {
  height: 8px;
  margin: var(--space-3) 0 0;
  border-radius: 999px;
  background: color-mix(in srgb, var(--color-neutral-100) 18%, transparent);
  overflow: hidden;
}

/* `startCountdown` sets `--elapsed` on this node as a 0–1 share, filled rather
   than remaining: the bar grows toward the Order being sent. */
.undo-progress::after {
  content: "";
  display: block;
  height: 100%;
  width: calc(var(--elapsed, 0) * 100%);
  border-radius: 999px;
  background: var(--color-accent-2-300);
  transition: width 240ms linear;
}

.undo-countdown {
  margin: var(--space-2) 0 0;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 19px;
  font-variant-numeric: tabular-nums;
  color: var(--color-neutral-100);
}

/* ------------------------------------------------------------------ *
 * Pickup notifications
 * ------------------------------------------------------------------ */

/*
 * The second dark panel, as the prototype draws it. Being told when food is
 * ready is the one thing on the Account tab that changes what a Guest has to
 * do next, so it takes the same weight as the balance rather than sitting as
 * another sand card among several.
 */
.pickup-notifications {
  padding: var(--space-4);
  border-radius: 32px;
  background: var(--color-accent-2-800);
  color: var(--color-neutral-100);
  box-shadow: var(--shadow-sm);
}

.pickup-notifications .eyebrow {
  color: var(--color-accent-2-300);
}

.pickup-notifications h3 {
  margin: var(--space-1) 0 0;
  font-size: 19px;
  line-height: 1.2;
}

.pickup-notifications-detail {
  margin: var(--space-2) 0 var(--space-3);
  font-size: 13px;
  line-height: 1.55;
  color: color-mix(in srgb, var(--color-neutral-100) 78%, transparent);
}

.install-steps {
  margin: var(--space-3) 0 0;
  padding-left: var(--space-4);
  font-size: 13px;
  line-height: 1.7;
  color: color-mix(in srgb, var(--color-neutral-100) 78%, transparent);
}

/*
 * The dark panel's action is the accent-filled one, as `Guest.dc.html` draws
 * it: `--color-accent` ground, `--color-bg` text, 44px.
 *
 * This rule used to set only a light border and text, and it lost. Further
 * down this file `.guest-page button.secondary` gives every secondary control
 * `--color-accent-800` for the cream ground — the same specificity (0,2,1) and
 * later in the file, so it won here too and painted near-black-brown text on
 * dark green. The control was legible only as a shape. Scoped one level deeper
 * so the panel's own treatment wins wherever the generic rule sits.
 */
.guest-page .pickup-notifications button.secondary {
  min-height: 44px;
  border-color: transparent;
  background: var(--color-accent);
  color: var(--color-bg);
}

.guest-page .pickup-notifications button.secondary:hover {
  background: var(--color-accent-600);
}

/* ------------------------------------------------------------------ *
 * Tab content
 * ------------------------------------------------------------------ */

.guest-tab-content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-3) var(--space-4) var(--space-4);
  scrollbar-width: none;
}

.guest-tab-content::-webkit-scrollbar {
  width: 0;
}

.guest-tab-content > section > .eyebrow,
.guest-account-panel > .eyebrow {
  margin-bottom: var(--space-1);
}

.guest-tab-content h1,
.guest-tab-content h2 {
  margin: var(--space-2) 0 var(--space-4);
  font-size: 34px;
  line-height: 1.12;
}

/*
 * The Guest's own name is not an accent.
 *
 * `Guest.dc.html` draws this heading as one ink-coloured line — 34px Caprasimo,
 * `--color-text`, no emphasis span. Tinting the name terracotta made the
 * greeting read as two things, and on a real name rather than the design's
 * "Priya" it also wrapped, pushing the balance below the fold. The `<em>`
 * stays: it is what `guest.js` writes the name into, and emphasising the
 * subject of the sentence is honest markup even when it is not painted.
 */
.guest-greeting em {
  font-style: normal;
  color: inherit;
}

/* ------------------------------------------------------------------ *
 * Account — balance and recent activity
 * ------------------------------------------------------------------ */

.balance-panel {
  display: grid;
  gap: var(--space-3);
}

.balance-summary {
  padding: var(--space-6) var(--space-4);
  border-radius: 32px;
  background: var(--color-accent-2-800);
  color: var(--color-neutral-100);
  box-shadow: var(--shadow-md);
}

.balance-summary .eyebrow {
  color: var(--color-accent-2-300);
}

.balance-summary output {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 52px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

#event-disclosure {
  margin: var(--space-3) 0 0;
  font-size: 12.5px;
  color: color-mix(in srgb, var(--color-neutral-100) 72%, transparent);
}

.credit-preview,
.activity-panel,
.receive-panel,
.transfer-panel {
  padding: var(--space-4);
  border-radius: 32px;
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

#credit-summary,
#credit-activity {
  display: grid;
  gap: var(--space-1);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  font-size: 13.5px;
}

/* The type and the amount are separate spans with nothing between them, so
   without this they render as "Credit load20.00 Credit". The amount is pushed
   to the right rather than merely spaced, so a column of them aligns. */
#credit-summary li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

#credit-summary li strong {
  font-variant-numeric: tabular-nums;
}

.activity-entry {
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-divider);
}

.activity-entry:last-child {
  border-bottom: 0;
}

.activity-entry-summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: baseline;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ *
 * Menu — 64px rows, the stepper in the row
 * ------------------------------------------------------------------ */

.ordering-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-1);
}

.stall-list {
  display: grid;
  gap: var(--space-3);
}

/*
 * A grid declared without `grid-template-columns` gets ONE implicit column
 * sized `auto`, and an `auto` track's minimum is its item's automatic minimum:
 * the widest child's min-content. So `.stall-row` was not overflowing this
 * list the way a block box does - it was SIZING the track, holding itself at
 * 200 CSS px inside a list that fit comfortably. At 200% zoom that pushed the
 * stall heading and its "Orders Ahead" line past `body.guest-page`'s
 * `overflow-x: hidden`, which cut them off with no scrollbar to reach them by.
 *
 * The constraint is on the row AS AN ITEM of this grid, one level up from
 * where it looks like it lives - which is why guarding the row's own children
 * does nothing.
 */
.stall-list > * {
  min-width: 0;
}

.stall-row {
  padding: var(--space-4);
  border-radius: 32px;
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

.stall-row h3 {
  margin: 0;
  font-size: 21px;
  line-height: 1.15;
}

.capacity {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: var(--space-1);
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--color-accent-2-200);
  color: var(--color-accent-2-800);
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 12.5px;
}

.guest-menu {
  display: grid;
  gap: 2px;
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
}

/* A pill, not a card row. 64px is a floor rather than a height: the control
   inside is 52px and the row pads 6px around it, so the row is exactly as tall
   as a thumb needs and no taller. */
.guest-menu li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: 64px;
  padding: 6px 6px 6px var(--space-4);
  border-radius: 999px;
  background: var(--color-bg);
}

/* The paused / nothing-available line is a sentence, not a row. */
.guest-menu li:not(:has(.menu-item-label)) {
  justify-content: flex-start;
  padding: var(--space-2) var(--space-4);
  background: transparent;
  font-size: 13px;
  color: color-mix(in srgb, var(--color-text) 65%, transparent);
}

.menu-item-label {
  flex: 1;
  min-width: 0;
  display: grid;
  gap: 1px;
}

.menu-item-name {
  font-size: 15.5px;
  line-height: 1.25;
}

.menu-item-price {
  font-size: 12.5px;
  color: color-mix(in srgb, var(--color-text) 60%, transparent);
  font-variant-numeric: tabular-nums;
}

/* The stepper is itself a pill, tinted so the row reads as "in the Basket" at a
   glance without a separate badge saying so. It occupies the same slot Add did,
   so the `+` lands under the thumb that just tapped Add. */
.quantity-control {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 3px;
  border-radius: 999px;
  background: var(--color-accent-100);
}

.menu-item-quantity {
  min-width: 24px;
  text-align: center;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 18px;
  font-variant-numeric: tabular-nums;
}

/* Add and the stepper share one slot, so Add carries the stepper's height.
   `button.primary` below sets `width: 100%` for the full-width controls on this
   surface; this must outrank it on specificity or Add stretches across the row
   and collides with the item name. */
.guest-page button.primary.menu-add {
  flex: none;
  width: auto;
  min-height: 52px;
  padding-inline: 24px;
  font-size: 15px;
}

.quantity-control button,
.basket-quantity button {
  width: 52px;
  height: 52px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-accent-200);
  border-radius: 999px;
  background: var(--color-bg);
  color: var(--color-text);
  font: inherit;
  font-size: 21px;
  line-height: 1;
  cursor: pointer;
}

.quantity-control button:hover,
.basket-quantity button:hover {
  background: var(--color-accent-100);
}

.quantity-control button:active,
.basket-quantity button:active {
  background: var(--color-accent-200);
}

.quantity-control button:disabled,
.basket-quantity button:disabled {
  opacity: 0.45;
  cursor: default;
}

.basket-quantity-value,
.quantity-control output {
  min-width: 34px;
  text-align: center;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 19px;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ *
 * Basket
 * ------------------------------------------------------------------ */

.basket-panel {
  display: block;
}

#basket-problem-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--color-accent-700);
  font-size: 13.5px;
}

/* `minmax(0, 1fr)`, not `1fr`. A grid item's automatic minimum size is its
   content, so at 200% zoom the two 52px steppers and their labels held the row
   wider than the column and pushed the whole surface into horizontal scroll. */
#basket-lines {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.basket-line {
  display: grid;
  min-width: 0;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: 26px;
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

.basket-line-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  min-width: 0;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 17px;
}

/* The name reads on the left and shrinks; only *it* breaks. `overflow-wrap`
 * used to sit on the whole heading row, which meant it applied to the Remove
 * button too and broke the word "Remove" across two lines. */
.basket-description {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 15px;
  color: var(--color-text);
}

/* The line total, hard right, in the display face — the figure a Guest
 * compares down the column. */
.basket-line-total {
  flex: none;
  font-variant-numeric: tabular-nums;
}

.basket-unit-price {
  font-size: 12.5px;
  color: color-mix(in srgb, var(--color-text) 60%, transparent);
  font-variant-numeric: tabular-nums;
}

.basket-line-edit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-width: 0;
}

.basket-edit-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-accent-700);
}

.basket-quantity {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.remove-item {
  /* Never squeezed: it is a word, and a wrapped one reads as a rendering
     fault rather than as a control. */
  flex: none;
  white-space: nowrap;
  min-height: 44px;
  padding: 0 15px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--color-accent-700);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}

.remove-item:hover {
  background: var(--color-accent-100);
}

/* A paused stall warns rather than silently dropping the line. */
.basket-warning {
  padding: var(--space-2) var(--space-3);
  border-radius: 18px;
  background: var(--color-accent-100);
  color: var(--color-accent-800);
  font-size: 12.5px;
  line-height: 1.5;
}

.basket-checkout-dock {
  position: sticky;
  bottom: 0;
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding: var(--space-4) 0 0;
  background: linear-gradient(
    to bottom,
    transparent,
    var(--color-bg) var(--space-3)
  );
}

#basket-total {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 24px;
  font-variant-numeric: tabular-nums;
}

#basket-status {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--color-accent-700);
}

/* 60px, because this is the control the whole surface exists to reach. */
.guest-page .place-order {
  min-height: 60px;
  font-size: 17px;
}

/* The rule 33 block, named immediately above the controls it governs. It is an
   explanation, so it stays fully legible — the disabled controls below carry
   the dimming. */
.ordering-blocked {
  margin: 0 0 var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: 26px;
  background: var(--color-accent-100);
  color: var(--color-accent-800);
  font-size: 13px;
  line-height: 1.55;
}

/* ------------------------------------------------------------------ *
 * Order review — the explicit second confirmation
 * ------------------------------------------------------------------ */

.basket-review,
.transfer-review {
  margin-top: var(--space-3);
  padding: var(--space-4);
  border-radius: 32px;
  background: var(--color-accent-100);
  box-shadow: var(--shadow-sm);
}

.basket-review h3,
.transfer-review h3 {
  margin: var(--space-1) 0 0;
  font-size: 21px;
  line-height: 1.15;
}

.review-lines {
  display: grid;
  gap: var(--space-1);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}

.review-total {
  margin: var(--space-3) 0 0;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 21px;
  font-variant-numeric: tabular-nums;
}

.review-actions,
.form-actions {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

/* ------------------------------------------------------------------ *
 * Orders
 * ------------------------------------------------------------------ */

#order-list {
  display: grid;
  gap: var(--space-3);
}

/* One card per Stall Order, flat in the list — there is no Order card above it
   to nest inside, so this is the card the artboard draws: surface ground, 32px,
   one soft shadow. */
.order-card {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: 32px;
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

/*
 * A pill at the right of the heading row, as the design draws it.
 *
 * Readiness is carried by this tag, not by a coloured card. The artboard draws
 * every Stall Order the same way and lets the tag and the pinned Collect Now
 * banner say which one is ready — a second colour channel on the card competes
 * with the banner for the same glance.
 */
.order-progress-status {
  margin-left: auto;
  flex: none;
  padding: 4px 11px;
  border-radius: 999px;
  background: var(--color-accent-100);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  color: var(--color-accent-700);
  white-space: nowrap;
}

/* Ready is the sage voice, as it is on every other surface's status. */
.order-progress-status.is-ready {
  background: var(--color-accent-2-200);
  color: var(--color-accent-2-800);
}

.order-progress-status.is-complete {
  background: var(--color-neutral-200);
  color: var(--color-neutral-800);
}

.order-progress-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 19px;
}

/*
 * The stall name yields before the pill does. A long name wraps within its own
 * block; the state never breaks, because a fragment of "Ready for Pickup" is
 * worse than no pill at all.
 */
.order-progress-heading > .order-progress-stall {
  flex: 1 1 auto;
  min-width: 0;
}

.order-progress-figures {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.order-progress-figure {
  display: grid;
  gap: 2px;
}

.order-progress-label {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--color-neutral-600);
}

.order-progress-value {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 22px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/*
 * Quiet, and the same size ready or not. The dominant instance of the Pickup
 * Code lives in the pinned Collect Now banner, which is what a Guest reads
 * walking to a stall; this card states the same numbers for reference while
 * they wait. The accent marks the code out here, not the size.
 */
.pickup-code-value,
.pickup-code-figure .order-progress-label {
  color: var(--color-accent-700);
}

.order-progress-cue {
  margin: var(--space-2) 0 0;
  font-size: 12.5px;
  color: var(--color-accent-700);
}

/* A retained record must never borrow the live accent — a cached number that
   reads as a current one is the exact harm this state exists to avoid. */
.retained-order {
  background: transparent;
  border: 1px dashed var(--color-neutral-300);
  border-left-color: var(--color-neutral-300);
  box-shadow: none;
}

.retained-identity {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 18px;
  font-variant-numeric: tabular-nums;
}

.retained-note {
  margin: 0 0 var(--space-3);
}

/* ------------------------------------------------------------------ *
 * Credit tools
 * ------------------------------------------------------------------ */

.credit-tools {
  display: grid;
  gap: var(--space-3);
}

.guest-page .credit-tools > *,
.guest-page .recipient-code {
  min-width: 0;
}

.guest-page #receive-credits-link {
  min-height: 44px;
  margin-top: var(--space-2);
}

.access-qr {
  display: grid;
  place-items: center;
  min-height: 180px;
  margin: var(--space-3) 0;
  padding: var(--space-3);
  border-radius: 28px;
  background: var(--color-neutral-100);
}

.access-qr svg,
.access-qr img,
.access-qr canvas {
  max-width: 100%;
  height: auto;
}

.guest-page .recipient-code {
  display: block;
  overflow-wrap: anywhere;
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 25px;
  letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums;
}

.guest-page .account-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.guest-page .account-actions .guest-pass-link,
.guest-page .account-actions .text-button {
  justify-content: center;
}

/*
 * `minmax(0, 1fr)` rather than the implicit `1fr`.
 *
 * A grid item's default `min-width: auto` refuses to shrink below its own
 * content, so this form measured 387px inside a 320px panel and pushed the
 * Review transfer button off the right edge of a 390px screen. The track has
 * to be allowed to be narrower than what it holds.
 */
#transfer-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-3);
}

#transfer-form > *,
#transfer-form label {
  min-width: 0;
}

#transfer-form label {
  display: grid;
  gap: var(--space-1);
  font-size: 13px;
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
}

#transfer-form input {
  min-height: 52px;
  padding: 0 var(--space-4);
  border: 1px solid var(--color-accent-200);
  border-radius: 999px;
  background: var(--color-bg);
  color: var(--color-text);
  font: inherit;
  font-size: 16px;
}

#transfer-form input:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/*
 * Frozen, not merely disabled. `setControlsDisabled` stamps
 * `data-online-disabled` on every control it locks while a result is
 * unresolved or the device is offline, and the dashed edge says the field is
 * being held rather than that it was never available. Selected through the id
 * because the rule above is id-scoped and would otherwise win.
 */
#transfer-form input[data-online-disabled] {
  border-style: dashed;
}

/* Rule 35 — the refusal is written at the field and left there. */
.transfer-error {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-radius: 18px;
  background: var(--color-accent-200);
  color: var(--color-accent-900);
  font-size: 12.5px;
  line-height: 1.5;
}

.transfer-review strong {
  color: var(--color-accent-800);
}

/* ------------------------------------------------------------------ *
 * Live region
 * ------------------------------------------------------------------ */

.guest-page .status {
  margin: var(--space-3) 0 0;
  font-size: 13px;
  color: var(--color-accent-700);
}

/*
 * An empty live region occupies no space and cannot be hit-tested. It stays in
 * the DOM — moving it would break the announcement contract — but a silent
 * region that still covers the control beneath it is a control a Guest cannot
 * tap.
 */
.guest-page .status:empty {
  margin: 0;
  visibility: hidden;
}

/* ------------------------------------------------------------------ *
 * Tab bar
 * ------------------------------------------------------------------ */

/*
 * In the flow at the foot of the column, not floating over it. The shell
 * already gives the scroller its own height, so the dock does not need to be
 * fixed — and fixing it meant reserving its height in the scroller separately,
 * which is one more thing to get wrong.
 */
.guest-tab-bar {
  flex: none;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 4px;
  margin: 0 var(--space-4) max(var(--space-2), env(safe-area-inset-bottom, 0px));
  padding: 5px;
  border-radius: 999px;
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}

.guest-tab-bar button {
  position: relative;
  min-width: 0;
  min-height: 62px;
  padding: 0 4px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--color-accent-800);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}

.guest-tab-bar button svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.guest-tab-bar button[aria-current="page"] {
  background: var(--color-accent-600);
  color: var(--color-bg);
}

.guest-tab-badge {
  position: absolute;
  top: 7px;
  right: calc(50% - 22px);
  min-width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--color-accent);
  color: var(--color-neutral-100);
  font-size: 10px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ *
 * Legacy control classes mapped onto the Organic system
 *
 * The engine emits `primary` / `secondary` / `text-button`; the system file
 * styles `.btn`. Mapping here keeps both contracts intact without a rename.
 * ------------------------------------------------------------------ */

.guest-page button.primary,
.guest-page button.secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 52px;
  padding: 0 var(--space-6);
  border: 1px solid transparent;
  border-radius: 999px;
  font: inherit;
  font-size: 15.5px;
  cursor: pointer;
}

/* The base accent, not a ramp step: #180 fixed the commit control as the filled
   terracotta and `guest-order.spec.js` asserts that exact value. Hover and
   pressed walk up the ramp from here. */
.guest-page button.primary {
  background: var(--color-accent);
  color: var(--color-neutral-100);
  box-shadow: var(--shadow-sm);
}

.guest-page button.primary:hover {
  background: var(--color-accent-600);
}

.guest-page button.primary:active {
  background: var(--color-accent-700);
}

.guest-page button.secondary {
  background: transparent;
  border-color: var(--color-accent-300);
  color: var(--color-accent-800);
}

.guest-page button.secondary:hover {
  background: var(--color-accent-100);
}

.guest-page button:disabled {
  opacity: 0.45;
  cursor: default;
}

/* `:focus` as well as `:focus-visible`. A control focused programmatically —
   which is how this surface hands focus across the Add/stepper swap and after a
   refusal — does not reliably match `:focus-visible`, and a Guest moved to a
   control they cannot see is worse than a ring shown after a mouse click. */
.guest-page :focus,
.guest-page :focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* ------------------------------------------------------------------ *
 * Motion
 * ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .undo-progress::after {
    transition: none;
  }
}

/* ------------------------------------------------------------------ *
 * What was ordered
 * ------------------------------------------------------------------ */

/*
 * One pill per line, on the ground colour so they read against the card's
 * surface. The quantity leads because that is what a Guest checks — whether
 * they asked for one or two — and the price sits right-aligned and tabular so
 * a column of them lines up.
 */
.order-items {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.order-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: 999px;
  background: var(--color-bg);
  font-size: 14px;
}

.order-item-label {
  min-width: 0;
}

.order-item-price {
  flex: none;
  font-variant-numeric: tabular-nums;
  color: color-mix(in srgb, var(--color-text) 60%, transparent);
}

/* ------------------------------------------------------------------ *
 * Collect now: the fold
 * ------------------------------------------------------------------ */

.guest-page .ready-head {
  grid-area: head;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.guest-page .ready-detail {
  grid-area: detail;
}

.ready-head .eyebrow {
  margin: 0;
  flex: 1;
  min-width: 0;
}

/*
 * The chevron. Icon-only, so it carries a real label rather than an aria-label
 * on an empty button — the label text is clipped, not absent, and it changes
 * with the state so a screen reader hears what pressing it will do.
 */
.guest-page .ready-toggle {
  grid-area: toggle;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Square at the touch floor: an icon-only control has no text to widen it. */
  width: 44px;
  height: 44px;
  margin: -10px -10px -10px 0;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--color-accent-700);
  cursor: pointer;
}

.ready-toggle:hover {
  background: color-mix(in srgb, var(--color-accent-700) 10%, transparent);
}

.ready-toggle-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.ready-toggle::after {
  content: "";
  width: 17px;
  height: 17px;
  background: currentColor;
  mask: var(--chevron-mark) center / contain no-repeat;
  -webkit-mask: var(--chevron-mark) center / contain no-repeat;
}

/* Points up while open — pressing it puts the card away — and down while
   folded, where pressing it brings the card back. */
.ready-panel[data-ready="full"] .ready-toggle::after {
  transform: rotate(180deg);
}

.guest-page {
  --chevron-mark: url("/icons/chevron-down.svg");
}

/*
 * Folded: the whole panel becomes one pill, 39px against the card's ~280px.
 * The eyebrow's serving-tray mark stays as the thing that identifies it at a
 * glance; its words go, because the count and the stall say more in the space.
 */
.guest-page .ready-panel[data-ready="compact"] {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    "head summary toggle"
    "detail detail detail";
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: 999px;
}

.ready-panel[data-ready="compact"] .ready-head {
  flex: none;
  gap: var(--space-2);
}

.ready-panel[data-ready="compact"] .ready-head .eyebrow {
  flex: none;
  font-size: 0;
  letter-spacing: 0;
}

.ready-panel[data-ready="compact"] .ready-head .eyebrow::before {
  margin: 0;
}

.ready-panel[data-ready="compact"] .ready-total {
  margin-left: 0;
}

.guest-page .ready-summary {
  grid-area: summary;
  flex: 1;
  min-width: 0;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  color: color-mix(in srgb, var(--color-text) 62%, transparent);
}

/* The chevron keeps the right edge in both the folded and full states. */
.guest-page .ready-panel[data-ready="compact"] .ready-toggle {
  margin: -6px -6px -6px 0;
}

/* The label is kept for the accessible description of the stepper beside it,
 * but the design prints no such label — the two circular controls and the
 * number between them say what they do. */
.basket-edit-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The stepper leads, then the unit price, then Remove at the right edge. */
.basket-line-edit .basket-unit-price {
  margin-right: auto;
}

/* An empty warning is not a warning. It carried padding and a tinted ground
 * regardless of content, so every healthy Basket line had a blank bar under
 * its name that read as a broken element. */
.basket-warning:empty {
  display: none;
}

/* The total: a quiet label and a figure, as the design sets it. The two spans
 * concatenate to the same sentence the accessible name and the specs use. */
#basket-total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.basket-total-label {
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 60%, transparent);
}

.basket-total-value {
  font-family: var(--font-heading);
  font-weight: var(--font-heading-weight);
  font-size: 26px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/*
 * Review transfer sizes to its label, as the design draws it (149x44). Place
 * Order stays full width because it is the surface's one commit; making every
 * primary control full-bleed said they were all that important.
 */
#transfer-form button[type="submit"] {
  justify-self: start;
  width: auto;
  min-height: 44px;
}

/* 20px, as the design sets it. It had been taking the tab content's h2 scale
 * and reading louder than the balance above it. */
#activity-title {
  margin: 0 0 var(--space-3);
  font-size: 20px;
}
