@layer components {
  /* Inline SVG icons (IconsHelper) — align with adjacent text and never shrink
     inside a flex button. Colour follows currentColor via stroke. */
  .icon {
    display: inline-block;
    vertical-align: -0.15em;
    flex-shrink: 0;
  }

  /* ── Standard surface: the glass card ─────────────────────────────── */
  .glass-card {
    position: relative;
    overflow: hidden;
    padding: 2rem 1.75rem;
    margin: 0.5rem;
    /* The rim stays clear glass — barely-there film, NO blur. Legibility comes
       from the frost field below, which is confined to the content zone. */
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius);
    box-shadow:
      0 8px 32px rgba(0, 0, 0, 1),
      inset 0 1px 0 rgba(255, 255, 255, 0.5),
      inset 0 -1px 0 rgba(255, 255, 255, 0.1),
      inset 0 0 0px 0px rgba(255, 255, 255, 0);
  }

  /* ── Frost field ──────────────────────────────────────────────────────
     A backdrop-blurred pane that backs the card's content for legibility,
     then feathers out over the padding gutter on every edge so it dissolves
     into the clear-glass rim — content reads frosted (like the nav) without
     ever resolving into a second card/panel. Sits behind the content
     (z-index:-1) so the text itself stays crisp. */
  .glass-card::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background: var(--glass-bg-strong);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    /* Feather ≈ the padding, so the frost is solid under the content and fades
       across the gutter to nothing at the rim. Left and right fade as before;
       top and bottom keep the same gutter but cut sharply instead of fading. */
    --frost-feather: 30px;
    -webkit-mask-image:
      linear-gradient(to bottom, transparent 0, transparent var(--frost-feather), #000 var(--frost-feather), #000 calc(100% - var(--frost-feather)), transparent calc(100% - var(--frost-feather))),
      linear-gradient(to right,  transparent 0, #000 var(--frost-feather), #000 calc(100% - var(--frost-feather)), transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
      linear-gradient(to bottom, transparent 0, transparent var(--frost-feather), #000 var(--frost-feather), #000 calc(100% - var(--frost-feather)), transparent calc(100% - var(--frost-feather))),
      linear-gradient(to right,  transparent 0, #000 var(--frost-feather), #000 calc(100% - var(--frost-feather)), transparent 100%);
    mask-composite: intersect;
    pointer-events: none;
  }

  /* Edge sheen — the original top + left highlights, folded into one overlay
     (two thin gradient layers) so ::before is free to carry the frost. Sits
     above the content on the 1px border edges only; transparent elsewhere. */
  .glass-card::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    background:
      linear-gradient(90deg, transparent, var(--glass-highlight), transparent) top / 100% 1px no-repeat,
      linear-gradient(180deg, var(--glass-highlight), transparent) left / 1px 100% no-repeat;
  }

  /* ── Button system ────────────────────────────────────────────────── */
  /* Class-based, plus Rails-generated submits, so the bare theme toggle
     (a plain <button>) is left untouched. The quick-tile button_to is exempt
     (:not(.quick-tile)) so it keeps the card look instead of the pill. */
  .btn,
  input[type="submit"],
  button[type="submit"]:not(.quick-tile) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.6rem 1.4rem;
    font: inherit;
    font-weight: 400;
    cursor: pointer;
    color: var(--text);
    /* Frost, no border, square corners. Tokenised so dark mode flips the white
       frost to a dark one (bright white panes wash out over the dark photo). */
    background: var(--btn-bg);
    border: none;
    border-radius: 0;
    backdrop-filter: var(--glass-filter-strong);
    -webkit-backdrop-filter: var(--glass-filter-strong);
    box-shadow: var(--glass-shadow);
    text-shadow: none;
    transition:
      transform 0.15s ease,
      background 0.2s ease,
      color 0.2s ease,
      box-shadow 0.2s ease;
  }

  .btn:hover,
  input[type="submit"]:hover,
  button[type="submit"]:not(.quick-tile):hover {
    transform: translateY(-1px);
  }

  .btn:active,
  input[type="submit"]:active,
  button[type="submit"]:not(.quick-tile):active {
    transform: translateY(0);
  }

  /* Disabled — dulled and inert, with the hover lift/tint neutralised so it
     never looks pressable. Used e.g. for Dispatch before a rider is picked. */
  .btn:disabled,
  input[type="submit"]:disabled,
  button[type="submit"]:not(.quick-tile):disabled,
  .btn:disabled:hover,
  input[type="submit"]:disabled:hover,
  button[type="submit"]:not(.quick-tile):disabled:hover {
    opacity: 0.45;
    cursor: not-allowed;
    transform: none;
    background: var(--glass-bg-strong);
    color: var(--accent-strong);
  }

  /* Primary — accent label on the frosted glass. Devise submits default to this. */
  .btn-primary,
  input[type="submit"],
  button[type="submit"]:not(.quick-tile) {
    color: var(--accent-strong);
  }

  .btn-primary:hover,
  input[type="submit"]:hover,
  button[type="submit"]:not(.quick-tile):hover {
    background: var(--accent);
    color: var(--accent-contrast);
  }

  /* Ghost / secondary — the plain glass look */
  .btn-glass {
    background: var(--glass-bg-strong);
    border-color: var(--glass-border);
    color: var(--text);
  }

  /* Destructive — a quiet warning tint that only fills in on hover, so a
     "Remove"/"Clear"/"Decline" reads as secondary to the primary action beside
     it. button_to renders a <button type="submit">, and that base rule carries
     :not(.quick-tile) (specificity 0,2,1) which would otherwise beat a plain
     .btn.btn-danger (0,2,0) and repaint it accent — so the submit form is
     spelled out here too, to out-specify it and keep the danger colour. */
  .btn.btn-danger,
  button[type="submit"].btn-danger:not(.quick-tile) { color: var(--danger); }
  .btn.btn-danger:hover,
  button[type="submit"].btn-danger:not(.quick-tile):hover {
    background: var(--danger);
    color: #fff;
  }

  /* Compact variant — for low-priority actions (e.g. per-line Remove). Spelled
     out for submit buttons for the same specificity reason as .btn-danger, or
     the base padding (0.6/1.4) would win and the button wouldn't shrink. */
  .btn.btn-sm,
  button[type="submit"].btn-sm:not(.quick-tile) {
    padding: 0.3rem 0.8rem;
    font-size: 0.85rem;
  }

  /* Touch screens: a 0.3rem-padded .btn-sm computes to ~29px tall — under the
     ~44px comfortable tap target, and the destructive-action convention
     (decline / reject / remove) rides on .btn-danger.btn-sm, exactly the
     buttons a mis-tap hurts most. Give them a 44px floor on coarse pointers
     only, so the compact look stays untouched on desktop (fine pointer). */
  @media (pointer: coarse) {
    .btn.btn-sm,
    button[type="submit"].btn-sm:not(.quick-tile) {
      min-height: 44px;
    }
    /* Keep the +/- stepper a square at the same floor. */
    .qty-stepper__btn.btn-sm {
      width: 2.75rem;
      height: 2.75rem;
    }
  }

  /* Prominent variant — for the primary call to action (e.g. Checkout). */
  .btn.btn-lg {
    padding: 0.8rem 2.4rem;
    font-size: 1.05rem;
  }

  /* ── Theme toggle: sits bare on the background ────────────────────── */
  /* Sized by height so the icon nearly fills the nav's vertical extent —
     the deliberate hero of the bar. Padding is trimmed so nothing holds it
     back; the button stretches to the nav row and centres the icon. */
  .theme-toggle {
    background: none;
    border: 0;
    cursor: pointer;
    padding: 0.1rem;
    align-self: stretch;
    display: inline-flex;
    align-items: center;
  }
  /* Language switch — a compact SW/EN chip beside the theme toggle. */
  .lang-toggle {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.5rem;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-decoration: none;
    color: inherit;
    border: 1px solid var(--glass-border);
    border-radius: 999px;
    opacity: 0.85;
  }
  .lang-toggle:hover,
  .lang-toggle:focus-visible {
    opacity: 1;
  }
  .theme-toggle img {
    height: 56px;
    width: auto;
    transition:
      opacity 0.2s ease,
      transform 0.2s ease;
  }
  .theme-toggle:hover img {
    transform: rotate(-12deg) scale(1.08);
  }

  /* ── Layout helpers ───────────────────────────────────────────────── */
  .mid-screen {
    min-height: 70vh;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  /* Sign-in pages in the demo stack the persona/sign-in tabs above the card,
     both at one width (a plain .mid-screen is a row, so they'd sit side by side). */
  .mid-screen--stack { flex-direction: column; }
  .mid-screen--stack > * { width: 100%; max-width: 420px; }

  /* Auth pages: the phone-login alternative and the secondary links (sign up /
     forgot password / confirmation) sit under the form. Space them out and set
     them off with a divider so they read as a list, not a cramped stack. */
  .auth-alt {
    margin-top: 1.25rem;
  }
  .auth-links {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--glass-border);
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
  }
  .auth-links p {
    margin: 0;
  }

  .field {
    margin: 1rem 0;
  }

  /* Put short fields side by side; wraps to stacked full-width on narrow screens */
  .field-row {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin: 1rem 0;
  }
  .field-row .field {
    /* grow to share the line, but wrap once a field can't keep ~12rem */
    flex: 1 1 12rem;
    margin: 0;
  }

  .actions {
    margin-top: 1.5rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
  }
  /* button_to wraps each button in its own <form>; let that form be a tidy
     flex item that hugs its button rather than a full-width block. */
  .actions form { margin: 0; }
  /* A destructive action (Decline/Reject/Cancel/Remove) is pushed to the far
     side, opposite its primary counterpart, so it's never flush against Accept
     and can't be tapped by mistake — whether it's a bare button or button_to.
     The cart footer (.cart-actions) has its own grid placement, so it's left
     out here. */
  .actions:not(.cart-actions) .btn-danger,
  .actions:not(.cart-actions) form:has(.btn-danger) { margin-left: auto; }

  /* Cart footer actions: Checkout sits dead-centre as the hero, while the
     destructive Clear cart is pushed to the far right (equal 1fr gutters keep
     Checkout centred regardless of the side content). */
  .cart-actions {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 0.75rem;
  }
  .cart-actions__checkout { grid-column: 2; }
  .cart-actions__clear {
    grid-column: 3;
    justify-self: end;
    margin: 0;
  }

  .muted { color: var(--text-muted); }

  /* ── Fulfillment picker (checkout) ────────────────────────────────────
     Delivery vs. self-pickup, surfaced as large square tab buttons with an
     icon and label. The radio input itself is visually hidden; the whole tile
     is the hit target and the checked state lights up the border. */
  .fulfillment-tabs {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 0.6rem;
    margin-top: 0.2rem;
  }
  .fulfillment-tab {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 1.1rem 0.8rem;
    text-align: center;
    cursor: pointer;
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    transition: border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
  }
  .fulfillment-tab:hover { border-color: var(--accent); }
  .fulfillment-tab__input {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
  .fulfillment-tab__icon svg {
    width: 2rem;
    height: 2rem;
    color: var(--text-muted);
    transition: color 0.2s ease;
  }
  .fulfillment-tab__label { font-weight: 500; }
  .fulfillment-tab:has(.fulfillment-tab__input:checked) {
    border-color: var(--accent);
    color: var(--accent-strong);
    box-shadow: 0 0 0 2px var(--accent-soft);
  }
  .fulfillment-tab:has(.fulfillment-tab__input:checked) .fulfillment-tab__icon svg {
    color: var(--accent-strong);
  }
  /* Keyboard focus lands on the hidden radio — ring the visible tile instead. */
  .fulfillment-tab:has(.fulfillment-tab__input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  @media (max-width: 560px) {
    .fulfillment-tabs { grid-template-columns: 1fr; }
  }

  /* ── Rider picker (seller dispatch overlay) ───────────────────────────
     Approved riders offered as tappable tabs (nearest first) rather than a
     dropdown. The radio is visually hidden; tapping a tab checks it and lights
     the border, and "Dispatch" sends whichever rider is checked. The list
     scrolls if a shop has many riders so it never overruns the overlay. */
  .rider-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.4rem;
    max-height: 40vh;
    overflow-y: auto;
  }
  .rider-tab {
    display: inline-flex;
    align-items: center;
    padding: 0.45rem 0.85rem;
    cursor: pointer;
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    transition: border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
  }
  .rider-tab:hover { border-color: var(--accent); }
  .rider-tab__input {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
  .rider-tab__name { font-weight: 500; }
  .rider-tab:has(.rider-tab__input:checked) {
    border-color: var(--accent);
    color: var(--accent-strong);
    box-shadow: 0 0 0 2px var(--accent-soft);
  }
  /* Keyboard focus lands on the hidden radio — ring the visible tile instead. */
  .rider-tab:has(.rider-tab__input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }

  /* ── Delivery-choice picker (checkout) ────────────────────────────────
     Three ways to cover paid-delivery shops, surfaced as tabs instead of a
     dropdown: horizontal on roomy screens, stacked vertical on narrow ones.
     An unavailable choice is dulled rather than hidden, and explains itself in
     a popover when tapped. */
  .delivery-choice .field-label {
    display: block;
    font-weight: 500;
    margin-bottom: 0.6rem;
  }

  .delivery-choice__tabs {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.85rem;
  }
  .delivery-choice__tab {
    flex: 1 1 0;
    padding: 0.6rem 0.9rem;
    font: inherit;
    color: var(--text);
    text-align: center;
    cursor: pointer;
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    transition: border-color 0.2s ease, color 0.2s ease, background 0.2s ease;
  }
  .delivery-choice__tab:hover { border-color: var(--accent); }
  .delivery-choice__tab.is-active {
    border-color: var(--accent);
    color: var(--accent-strong);
    font-weight: 600;
  }
  /* Inoperable: kept visible but visibly dulled. Still clickable so the popover
     can explain why — so no pointer-events:none here. */
  .delivery-choice__tab[data-operable="false"] {
    opacity: 0.45;
    cursor: not-allowed;
  }
  .delivery-choice__tab[data-operable="false"]:hover { border-color: var(--glass-border); }

  .delivery-choice__panel { margin-bottom: 0.25rem; }
  .delivery-choice__panel .muted { margin: 0 0 0.6rem; }

  .rider-option {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.55rem 0.7rem;
    margin-bottom: 0.4rem;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    background: var(--glass-bg);
    cursor: pointer;
  }
  .rider-option:has(input:checked) {
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-soft);
  }

  /* The combined-trip toggle only appears when the picked rider quotes one run
     around the shops cheaper. Flex display would beat the UA [hidden] rule, so
     keep [hidden] authoritative. */
  .combined-trip { border-style: dashed; }
  .combined-trip[hidden] { display: none; }

  .delivery-choice__popover {
    margin: 0.6rem 0 0;
    padding: 0.65rem 0.85rem;
    border-radius: var(--radius-sm);
    background: var(--glass-bg-strong);
    border: 1px solid var(--danger);
    color: var(--danger);
    box-shadow: var(--glass-shadow);
  }

  @media (max-width: 560px) {
    .delivery-choice__tabs { flex-direction: column; }
  }

  /* ── Flash messages ───────────────────────────────────────────────── */
  /* Floated over the page (fixed) so they never shove the content down. The
     stack lets pointer events through the gaps; only the messages catch clicks,
     so a flash you haven't dismissed doesn't block what's underneath it. */
  .flash-stack {
    position: fixed;
    top: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 150;
    width: min(720px, calc(100% - 2rem));
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    pointer-events: none;
  }
  .flash {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    padding: 0.8rem 1.1rem;
    border-radius: var(--radius-sm);
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    box-shadow: var(--glass-shadow);
    pointer-events: auto;
  }
  .flash__message { flex: 1; }
  .flash__close {
    flex-shrink: 0;
    padding: 0 0.2rem;
    background: none;
    border: none;
    color: inherit;
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.6;
    transition: opacity 0.2s ease;
  }
  .flash__close:hover { opacity: 1; }
  .flash--notice { border-color: var(--accent); color: var(--accent-strong); }
  .flash--alert  { border-color: rgba(200, 60, 60, 0.6); color: #c0392b; }

  /* ── App chrome (nav + main) ──────────────────────────────────────── */
  .app-nav {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.75rem 1.25rem;
    background: var(--nav-bg);
    border-bottom: 1px solid var(--glass-border);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    /* Kinetic nav: sticks to the top while the JS controller springs it up/down.
       touch-action:none lets us read vertical drags instead of scrolling away. */
    position: sticky;
    top: 0;
    z-index: 100;
    touch-action: none;
  }
  .app-nav__brand {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    color: var(--text);
    text-decoration: none;
    line-height: 1;
  }
  .app-nav__logo {
    height: 40px;
    width: auto;
    /* logo is transparent; let it sit cleanly over the glass nav */
    background: transparent;
  }
  .app-nav__brand-name {
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: 0.02em;
  }
  .app-nav__links {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-left: auto;
  }
  /* The collapsible group: inline on wide screens; the toggle is hidden there. */
  .app-nav__group {
    display: flex;
    align-items: center;
    gap: 1rem;
  }
  .app-nav__menu-toggle { display: none; }
  /* The cart shortcut keeps its button look at every width: it stands in for the
     "Get started" toggle (which only surfaces as the small-screen menu trigger)
     once the cart has items. */
  .app-nav__menu-toggle--cart:not([hidden]) { display: inline-flex; }
  /* Green sibling of the cart shortcut — a paid self-pickup order to collect.
     Same look as the cart, recoloured green so the two read as distinct. */
  .app-nav__menu-toggle--pickup:not([hidden]) { display: inline-flex; }
  .app-nav__menu-toggle--pickup {
    background: #1faa59;
    border-color: #1faa59;
    color: #fff;
  }
  /* Keep [hidden] authoritative over the display rules above so the add-to-cart
     controller can swap the cart shortcut and "Get started" toggle live, without
     a reload, at any screen width. */
  .app-nav__menu-toggle[hidden] { display: none; }
  /* Onboarding CTA is redundant beside Discover on wide screens; it returns in
     the small-screen dropdown below. */
  .app-nav__get-started { display: none; }

  /* Narrow screens: collapse the links into a "Get started" dropdown. */
  @media (max-width: 720px) {
    /* A 360px phone has no slack: the brand, menu toggle, bell, language and
       theme switches only fit with tighter spacing than the desktop bar's. */
    .app-nav { gap: 0.6rem; padding-inline: 0.9rem; }
    .app-nav__links { position: relative; }
    .app-nav__menu-toggle { display: inline-flex; }
    .app-nav__get-started { display: inline-flex; }
    .app-nav__group {
      /* Pinned to the right edge of the screen (not the nav), so it stays put
         while the kinetic bar springs around. */
      position: fixed;
      top: 0.75rem;
      right: 0.75rem;
      flex-direction: column;
      align-items: stretch;
      gap: 0.25rem;
      min-width: 13rem;
      padding: 0.6rem;
      /* Frosted panel — same blur as the nav, but a stronger fill so it reads
         as a solid frosted surface instead of near-transparent glass. */
      background: var(--menu-bg);
      border: 1px solid var(--glass-border);
      border-radius: var(--radius-sm);
      backdrop-filter: var(--glass-filter);
      -webkit-backdrop-filter: var(--glass-filter);
      box-shadow: var(--glass-shadow);
      /* hidden until opened; springs in from the right with a slide + fade */
      opacity: 0;
      transform: translateX(8px) scale(0.98);
      transform-origin: top right;
      pointer-events: none;
      transition: opacity 0.18s ease, transform 0.18s ease;
      z-index: 120;
    }
    .app-nav__links.is-open .app-nav__group {
      opacity: 1;
      transform: translateX(0) scale(1);
      pointer-events: auto;
    }
    .app-nav__group > * { width: 100%; }
    .app-nav__group a {
      padding: 0.55rem 0.65rem;
      border-radius: var(--radius-sm);
    }
  }
  /* The back-office nav lists up to ~18 areas: too many to sit inline at any
     width, so it keeps the small-screen dropdown everywhere (a tall list
     scrolls inside the panel). */
  /* The admin layout has no bottom bar, so its menu must survive the phone
     rule that hides .app-nav__links in favour of that bar. */
  .app-nav .app-nav__links--menu { display: flex; }
  .app-nav__links--menu .app-nav__menu-toggle { display: inline-flex; }
  .app-nav__links--menu { position: relative; }
  .app-nav__links--menu .app-nav__group {
    position: fixed;
    top: 0.75rem;
    right: 0.75rem;
    flex-direction: column;
    align-items: stretch;
    gap: 0.25rem;
    min-width: 13rem;
    max-height: calc(100vh - 1.5rem);
    overflow-y: auto;
    padding: 0.6rem;
    background: var(--menu-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    box-shadow: var(--glass-shadow);
    opacity: 0;
    transform: translateX(8px) scale(0.98);
    transform-origin: top right;
    pointer-events: none;
    transition: opacity 0.18s ease, transform 0.18s ease;
    z-index: 120;
  }
  .app-nav__links--menu.is-open .app-nav__group {
    opacity: 1;
    transform: translateX(0) scale(1);
    pointer-events: auto;
  }
  .app-nav__links--menu .app-nav__group > * { width: 100%; }
  .app-nav__links--menu .app-nav__group a {
    padding: 0.55rem 0.65rem;
    border-radius: var(--radius-sm);
  }
  .app-main {
    max-width: 1100px;
    margin: 0 auto;
    padding: 1.5rem 1.25rem;
  }

  /* ── Tables ───────────────────────────────────────────────────────── */
  /* Wrapper for any bare `.table`: on a narrow phone a multi-column table
     would otherwise crush its columns to slivers or overflow the glass-card
     with no way to reach the clipped edge. This lets the table keep its
     natural width and scroll sideways inside the card instead. Mirrors the
     .category-slider / .stat-spine scrollers (thin bar, momentum, a little
     bottom room for the scrollbar). Views that stack into cards on phones
     (.table--stack, .cart-table, .trips-table) don't overflow, so the
     wrapper simply never scrolls for them. */
  .table-scroll {
    overflow-x: auto;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 0.4rem;
  }
  .table {
    width: 100%;
    border-collapse: collapse;
  }
  .table th,
  .table td {
    text-align: left;
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid var(--glass-border);
  }
  .table th { font-weight: 400; color: var(--text-muted); }

  /* `.table--stack`: on a phone a multi-column table reads poorly even when it
     scrolls sideways (E1). This turns each row into a bordered glass card and
     each cell into a labelled line — the <td>'s data-label prefixes its value
     so it stays self-explanatory once the header row is hidden. Used on the
     buyer money screens (orders, purchases, deliveries, dashboard). The
     bespoke .cart-table (thumbnail grid) and .trips-table (accent "live"
     cards) keep their own treatments — this is the neutral default. */
  @media (max-width: 640px) {
    .table--stack thead { display: none; }
    .table--stack,
    .table--stack tbody,
    .table--stack tr,
    .table--stack td { display: block; }
    .table--stack tr {
      padding: 0.85rem 1rem;
      margin-bottom: 0.75rem;
      border: 1px solid var(--glass-border);
      border-radius: var(--radius-sm);
      background: var(--glass-bg);
    }
    .table--stack td {
      border: 0;
      padding: 0.2rem 0;
    }
    .table--stack td[data-label]::before {
      content: attr(data-label) ": ";
      color: var(--text-muted);
    }
    /* Unlabelled lead cell (a product name / order link) reads as the card's
       title, above its labelled detail lines. */
    .table--stack td.table--stack__title { font-weight: 600; padding-bottom: 0.35rem; }
    /* Trailing action cell (View / Pay): drop the big .actions top margin and
       give the button the full card width for an easy thumb tap. */
    .table--stack td.actions { margin-top: 0.4rem; padding-top: 0.5rem; }
    .table--stack td.actions .btn { width: 100%; }
  }

  /* ── Trips surface ────────────────────────────────────────────────────
     Active runs live in the table. The last cell carries the run buttons
     (picked up / delivered / failed) side by side, with the navigate /
     call-buyer links sitting below them so the rider works the cell top-down. */
  .trips-table__actions .actions {
    margin-top: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }
  .trips-table__actions .actions form { margin: 0; }
  .trips-table__actions .delivery-destination { margin-top: 0.5rem; }

  /* Finished trips settle below the table as read-only glass tablets — a
     light frosted surface (no full card chrome) carrying the receipt facts. */
  .trips-done-head { margin: 1.75rem 0 0.75rem; }
  .trip-tablets {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0.75rem;
  }
  .trip-tablet {
    padding: 1rem 1.1rem;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
  }
  .trip-tablet .card-head { margin-bottom: 0.5rem; }
  .trip-tablet h3 { margin: 0; font-size: 1.05rem; }
  .trip-tablet__meta {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 0.25rem 0.75rem;
    margin: 0;
  }
  .trip-tablet__meta > div { display: contents; }
  .trip-tablet__meta dt { color: var(--text-muted); }
  .trip-tablet__meta dd { margin: 0; }

  /* Phones: the actions cell can't share a row, so each active trip stacks
     into its own card — and since the table only holds *active* runs, the
     accent border (with a soft glow) signals "live" at a glance. The header
     row is hidden, so each cell carries its label inline. The past-trip
     tablets collapse to a single column. */
  @media (max-width: 640px) {
    .trips-table thead { display: none; }
    .trips-table,
    .trips-table tbody,
    .trips-table tr,
    .trips-table td { display: block; }
    .trips-table tr {
      padding: 1rem 1.1rem;
      margin-bottom: 0.85rem;
      border: 1px solid var(--accent);
      border-radius: var(--radius-sm);
      background: var(--accent-soft);
      box-shadow: 0 0 0 1px var(--accent-soft), 0 4px 18px rgba(0, 0, 0, 0.35);
    }
    .trips-table td {
      border: 0;
      padding: 0.2rem 0;
    }
    .trips-table td[data-label]::before {
      content: attr(data-label) ": ";
      color: var(--text-muted);
    }
    .trips-table__actions { padding-top: 0.6rem; }

    .trip-tablets { grid-template-columns: 1fr; }
  }

  /* Cart line thumbnail — small square between the item name and its price. */
  .cart-thumb {
    display: block;
    width: 56px;
    height: 56px;
    object-fit: cover;
    border-radius: var(--radius);
    border: 1px solid var(--glass-border);
  }
  .cart-thumb--empty {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    opacity: 0.6;
    background: var(--glass-bg);
  }

  /* Live quantity stepper — replaces the old "Update" form. The +/- buttons
     adjust the line on the fly (see cart_controller.js). */
  .qty-stepper {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
  }
  /* Square glass button (the .btn base), sized to a neat square with an
     oversized +/- glyph so the controls are easy to spot at a glance. */
  .qty-stepper__btn.btn-sm {
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1;
    /* The +/- glyphs take the link accent colour. */
    color: var(--accent);
    /* A subtle shade over the glass so each control reads as a button. */
    background:
      linear-gradient(rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08)),
      var(--glass-bg-strong);
  }
  .qty-stepper__btn:disabled {
    opacity: 0.5;
    cursor: progress;
  }
  .qty-stepper__value {
    min-width: 1.75rem;
    text-align: center;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
  }

  /* Phones: the 6-column cart row won't fit, so stack each line into a card —
     thumbnail on the left, name/price/qty/line/remove flowing down the right.
     Price and Line carry a data-label so the values stay self-explanatory once
     the header row is hidden. */
  @media (max-width: 560px) {
    .cart-table thead { display: none; }
    .cart-table tbody,
    .cart-table tr,
    .cart-table td { display: block; }
    .cart-table tr {
      display: grid;
      grid-template-columns: 56px 1fr;
      column-gap: 0.85rem;
      row-gap: 0.3rem;
      align-items: center;
      padding: 0.85rem 0.25rem;
      border-bottom: 1px solid var(--glass-border);
    }
    .cart-table td { border: 0; padding: 0; }
    /* Thumbnail pinned to the left column, spanning the stacked details. */
    .cart-table__media { grid-column: 1; grid-row: 1 / span 5; }
    .cart-table td:not(.cart-table__media) { grid-column: 2; }
    .cart-table__name { font-weight: 600; }
    .cart-table td[data-label]::before {
      content: attr(data-label) ": ";
      color: var(--text-muted);
    }
    .cart-table .qty-stepper { display: inline-flex; }
  }

  /* ── Misc ─────────────────────────────────────────────────────────── */
  .card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
  }

  .detail-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.4rem 1.25rem;
    margin: 1rem 0;
  }
  .detail-grid dt { color: var(--text-muted); }
  /* Long unbroken values (emails, references) wrap instead of pushing the
     card wider than a phone. A card head titled with one (an account's email)
     opts in with --long; ordinary titles keep whole words. */
  .detail-grid dd { min-width: 0; overflow-wrap: anywhere; }
  .card-head--long > h2 { min-width: 0; overflow-wrap: anywhere; }

  .shop-logo {
    border-radius: var(--radius-sm);
    margin: 0.75rem 0;
  }

  .badge {
    display: inline-block;
    padding: 0.15rem 0.6rem;
    border-radius: 0;
    font-size: 0.85rem;
    border: 1px solid var(--glass-border);
    background: var(--glass-bg-strong);
  }
  .badge--active    { border-color: var(--accent); color: var(--accent-strong); }
  .badge--pending   { border-color: rgba(200, 160, 40, 0.6); color: #b8860b; }
  .badge--suspended,
  .badge--rejected  { border-color: rgba(200, 60, 60, 0.6); color: #c0392b; }
  .badge--offline   { border-color: rgba(120, 120, 130, 0.6); color: #5b6168; }

  /* Glass cards used as page blocks stack with breathing room */
  .app-main > .glass-card { margin-bottom: 1.25rem; }

  /* ── Frosted text backing ─────────────────────────────────────────────
     For text that sits directly on the background with no card to back it
     (standalone page titles). Inside a .glass-card the frost field already
     backs the content, so headings/labels there are left clean — no pill.

     The backing is feathered at the line ends (mask) so it reads as a soft
     haze hugging the text rather than a hard pill. box-decoration-break keeps
     it continuous across wrapped lines. */
  .frost-text {
    display: inline;
    background: var(--glass-bg-strong);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    padding: 0.1em 0.5em;
    border-radius: 10px;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 0.7em, #000 calc(100% - 0.7em), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 0.7em, #000 calc(100% - 0.7em), transparent 100%);
  }

  /* ── Location picker ──────────────────────────────────────────────── */
  .cascade {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.4rem 0;
  }
  .cascade__step select { min-width: 12rem; }

  .location-picker__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 0.6rem 0;
  }

  .location-map {
    height: 320px;
    border-radius: var(--radius);
    border: 1px solid var(--glass-border);
    overflow: hidden;
    margin: 0.5rem 0;
  }

  /* ── Public shop page ─────────────────────────────────────────────── */
  /* Identity card and the metrics spine sit side by side on wide screens;
     the spine card wraps below on narrow ones. */
  .shop-public__layout {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-start;
  }
  .shop-public__layout > .shop-public { flex: 2 1 24rem; }
  .shop-public__layout > .shop-public__stats { flex: 1 1 16rem; }

  /* Logo beside the info block; the info wraps below the logo on narrow
     screens (flex-basis collapses). */
  .shop-public__header {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    align-items: flex-start;
  }
  .shop-public__logo {
    flex: 0 0 auto;
    width: 160px;
    height: 160px;
    object-fit: contain;
  }
  .shop-public__info { flex: 1 1 18rem; min-width: 0; }

  /* Tiny clickable locator map. */
  .shop-public__map-link {
    display: block;
    max-width: 22rem;
    margin-top: 0.75rem;
  }
  .shop-minimap {
    height: 140px;
    border-radius: var(--radius);
    border: 1px solid var(--glass-border);
    overflow: hidden;
    cursor: pointer;
  }
  .shop-public__map-note { font-size: 0.85rem; margin-top: 0.3rem; }

  /* Metrics spine: a multicolored line with sections branching off each node.
     Default (wide screens) runs vertically; the section grows out to the right.
     Each node sets its own --accent. */
  .stat-spine {
    list-style: none;
    margin: 0.75rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
  }
  .stat-spine__node {
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
    min-height: 3rem;
  }
  .stat-spine__rail {
    position: relative;
    flex: 0 0 14px;
    align-self: stretch;
    display: flex;
    justify-content: center;
  }
  /* The colored line segment — stacked nodes form one continuous, multicolored
     spine on wide screens. */
  .stat-spine__rail::before {
    content: "";
    width: 4px;
    background: var(--accent);
    border-radius: 2px;
  }
  .stat-spine__node:first-child .stat-spine__rail::before { margin-top: 0.45rem; }
  .stat-spine__node:last-child  .stat-spine__rail::before { margin-bottom: 0.45rem; }
  .stat-spine__dot {
    position: absolute;
    top: 0.4rem;
    left: 50%;
    transform: translateX(-50%);
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.08);
  }
  .stat-spine__body {
    display: flex;
    flex-direction: column;
    padding-bottom: 0.75rem;
  }
  .stat-spine__value { font-size: 1.5rem; font-weight: 600; line-height: 1.1; }
  .stat-spine__label { font-size: 0.85rem; color: var(--text-muted); }

  /* Narrow screens: the spine lies horizontally, broken into segments with
     vertical gaps between each section. */
  @media (max-width: 760px) {
    .stat-spine {
      flex-direction: row;
      gap: 0.5rem;
      margin-top: 1rem;
      overflow-x: auto;
      scroll-snap-type: x proximity;
      -webkit-overflow-scrolling: touch;
      padding-bottom: 0.4rem; /* room for the scrollbar */
    }
    /* Nodes keep a readable width and don't shrink — the row scrolls when the
       sections don't all fit. */
    .stat-spine__node {
      flex: 0 0 auto;
      min-width: 7rem;
      flex-direction: column;
      gap: 0.5rem;
      min-height: 0;
      scroll-snap-align: start;
    }
    .stat-spine__rail {
      flex: 0 0 12px;
      align-self: stretch;
      align-items: center;
    }
    .stat-spine__rail::before {
      width: 100%;
      height: 4px;
      margin: 0 !important;
    }
    .stat-spine__dot { top: 50%; left: 50%; transform: translate(-50%, -50%); }
    .stat-spine__body {
      align-items: center;
      text-align: center;
      padding: 0 0.25rem;
    }
    .stat-spine__value { font-size: 1.3rem; }
  }

  .latlng {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
  }
  .latlng span { display: flex; flex-direction: column; }

  /* ── Home hero ────────────────────────────────────────────────────── */
  .hero { max-width: 760px; margin: 2rem auto; }

  /* Activity globe (Phase 13) — admin BI only. */
  .globe { display: flex; flex-direction: column; gap: 0.5rem; position: relative; }
  .globe__controls { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
  .globe__canvas {
    width: 100%; min-height: 320px; height: min(60vw, 420px);
    border-radius: 16px; overflow: hidden;
  }
  /* Tap-to-load affordance for the gated (home / Save-Data) globe — centred over
     the empty canvas until the buyer opts into the heavy 3D module. */
  .globe__poster {
    position: absolute;
    inset: 0;
    width: fit-content;
    height: fit-content;
    margin: auto;
    padding: 0.7rem 1.4rem;
    font-weight: 600;
    color: inherit;
    background: var(--menu-bg, rgba(255, 255, 255, 0.06));
    border: 1px solid var(--glass-border);
    border-radius: 999px;
    cursor: pointer;
  }
  .globe__poster[hidden] { display: none; }

  /* Intro sits directly on the background — no glass surface.
     Title + lead stack on the left; the logo sits to the right, spanning
     both rows. */
  .hero__intro {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    column-gap: 1.5rem;
    padding: 0 0.5rem;
    margin-bottom: 1.25rem;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
  }
  .hero__title { grid-column: 1; font-size: clamp(1.8rem, 5vw, 2.8rem); margin: 0 0 0.75rem; }
  .hero__lead  {
    grid-column: 1;
    font-size: 1.1rem;
    max-width: 54ch;
    margin: 0;
    /* Thin + tall: lighter weight with airy line-height so it doesn't
       compete with the frosted heavy text on the glass below. */
    font-weight: 300;
    line-height: 1.85;
    letter-spacing: 0.02em;
  }
  .hero__logo {
    grid-column: 2;
    grid-row: 1 / span 2;
    height: clamp(96px, 16vw, 160px);
    width: auto;
    filter: drop-shadow(0 4px 16px rgba(0, 0, 0, 0.55));
  }

  /* Narrow screens: single column with the logo tucked below the title. */
  @media (max-width: 520px) {
    .hero__intro { grid-template-columns: 1fr; }
    .hero__title { grid-column: 1; grid-row: 1; }
    .hero__logo  { grid-column: 1; grid-row: 2; justify-self: start; margin: 0.5rem 0 1rem; }
    .hero__lead  { grid-column: 1; grid-row: 3; }
  }
  .hero__points {
    list-style: none;
    padding: 0;
    margin: 1.25rem 0;
    display: grid;
    gap: 0.9rem;
  }
  /* Key selling points — pronounced: larger, with a bold accent lead-in. */
  .hero__points li {
    padding-left: 1.6rem;
    position: relative;
    font-size: 1.15rem;
    line-height: 1.45;
  }
  .hero__points li strong {
    font-weight: 700;
    color: var(--accent-strong);
  }
  .hero__points li::before {
    content: "›";
    position: absolute;
    left: 0;
    top: -0.05em;
    font-size: 1.3em;
    font-weight: 700;
    color: var(--accent-strong);
  }
  .hero__actions { display: flex; gap: 0.75rem; flex-wrap: wrap; }

  /* ── Buyer dashboard: first-run empty state ───────────────────────── */
  .buyer-empty__subhead {
    margin: 1.75rem 0 0.75rem;
    font-size: 0.95rem;
    color: var(--text-muted);
  }
  .buyer-empty__points {
    list-style: none;
    padding: 0;
    margin: 1.75rem 0 0;
    display: grid;
    gap: 0.75rem;
  }
  .buyer-empty__points li { padding-left: 1.6rem; position: relative; line-height: 1.45; }
  .buyer-empty__points li strong { font-weight: 700; color: var(--accent-strong); }
  .buyer-empty__points li a { text-decoration: underline; text-decoration-thickness: 1px;
                              text-underline-offset: 3px; text-decoration-color: var(--accent-soft); }
  .buyer-empty__points li a:hover, .buyer-empty__points li a:focus-visible { text-decoration-color: var(--accent-strong); }
  .buyer-empty__points li::before {
    content: "›";
    position: absolute;
    left: 0;
    top: -0.05em;
    font-weight: 700;
    color: var(--accent-strong);
  }

  /* ── Signed-in quick-action board ─────────────────────────────────── */
  .quick { max-width: 900px; margin: 1.5rem auto; }
  .quick__title { font-size: clamp(1.6rem, 4vw, 2.2rem); margin: 0.5rem 0.5rem 0.25rem; }
  .quick__lead { color: var(--text-muted); margin: 0 0.5rem 1.5rem; }

  /* Tiles flex-wrap: each grows to fill the row and reflows to fewer columns as
     the screen narrows; gap (not per-tile margin) keeps the spacing even. */
  .quick__board {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 1rem;
  }
  /* button_to wraps its button in a form; collapse the form so the button is the
     flex item directly and sizes like the link tiles. */
  .quick__board form { display: contents; }

  .quick-tile {
    /* .glass-card supplies the frost, border, radius and shadow. */
    flex: 1 1 220px;
    max-width: 320px;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0;
    padding: 1.5rem 1.4rem;
    text-align: left;
    text-decoration: none;
    color: var(--text);
    font: inherit;
    cursor: pointer;
    transition: transform 0.15s ease, border-color 0.2s ease;
  }
  .quick-tile:hover { transform: translateY(-2px); border-color: var(--accent); }
  .quick-tile__icon { font-size: 2rem; line-height: 1; }
  .quick-tile__title { font-weight: 600; font-size: 1.05rem; }
  .quick-tile__desc { color: var(--text-muted); font-size: 0.9rem; }

  /* ── Demo: the till's "Go offline" panel (pos#toggleOffline) ─────── */
  .pos-demo { padding: 0.9rem 1rem; margin-bottom: 1rem; }
  .pos-demo__row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; }
  .pos-demo__toggle.is-offline { border-color: var(--danger); color: var(--danger); }
  .pos-demo__badge {
    padding: 0.35rem 0.7rem; border-radius: var(--radius-sm); font-weight: 600;
    background: var(--accent-soft); color: var(--accent-strong);
  }
  .pos-demo__explain { margin: 0.6rem 0 0; }
  .pos-demo__results { list-style: none; margin: 0.75rem 0 0.5rem; padding: 0; display: grid; gap: 0.35rem; }
  .pos-demo__results-head { font-weight: 600; }
  .pos-demo__result { padding-left: 0.75rem; border-left: 3px solid var(--success); }
  .pos-demo__result--duplicate { border-left-color: var(--accent); }
  .pos-demo__result--flagged, .pos-demo__result--error { border-left-color: var(--danger); }
  /* .btn's display would otherwise beat [hidden] on the resend button. */
  .pos-demo [hidden] { display: none !important; }

  /* ── My business: the owner's view (Shops::Overview) ─────────────── */
  .business { max-width: 640px; margin: 1rem auto; display: grid; gap: 1rem; }
  .business__head { margin: 0 0.25rem; }
  .business__card { padding: 1rem 1.1rem; }
  .business__label { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
  .business__big { margin: 0.1rem 0 0.2rem; font-size: clamp(1.9rem, 8vw, 2.6rem); font-weight: 700; }
  .business__title { display: flex; justify-content: space-between; align-items: baseline;
                     gap: 0.5rem; margin: 0 0 0.5rem; font-size: 1.15rem; }
  .business__count { font-size: 0.95rem; color: var(--text-muted); font-weight: 600; }
  .business__rows { list-style: none; margin: 0.5rem 0 0; padding: 0; }
  .business__rows li + li { border-top: 1px solid var(--glass-border); }
  .business__row, .business__week { display: flex; justify-content: space-between; align-items: center;
                    gap: 0.75rem; min-height: 44px; padding: 0.4rem 0; color: var(--text); text-decoration: none; }
  .business__week { flex-direction: column; align-items: stretch; gap: 0.2rem; }
  .business__row-main { display: flex; flex-direction: column; min-width: 0; }
  .business__row-main small { font-size: 0.8rem; }
  .business__alert, .business__down { color: var(--danger); }
  .business__up { color: var(--success, #2e8b57); }
  .business__bar { display: block; height: 6px; width: var(--w); min-width: 2px;
                   background: var(--accent); border-radius: 2px; }
  .business__bar--prior { background: var(--text-muted); opacity: 0.45; }

  /* ── Demo door (/start) and the persona bar that replaces Sign out ── */
  .auth-tabs {
    display: flex; gap: 0.25rem; padding: 0.25rem; margin: 0 auto 1rem;
    width: 100%; max-width: 420px; box-sizing: border-box; border-radius: var(--radius-sm);
    background: var(--glass-bg-strong);
  }
  .auth-tabs__tab {
    flex: 1; text-align: center; padding: 0.6rem 1rem; min-height: 44px;
    /* Nested inside the strip's 0.25rem padding, so a radius that much smaller
       keeps the corners concentric. */
    border-radius: calc(var(--radius-sm) - 0.25rem); color: var(--text-muted); text-decoration: none; font-weight: 600;
  }
  .auth-tabs__tab[aria-current="page"] { background: var(--accent); color: #fff; }
  .demo-start__current { border-color: var(--accent); }
  .demo-start__here { font-size: 0.8rem; font-weight: 600; color: var(--accent); }
  .demo-start__note { margin-top: 1.5rem; font-size: 0.9rem; }
  .demo-bar { display: inline-flex; gap: 0.5rem; align-items: center; white-space: nowrap; }
  .demo-bar__switch { font-weight: 600; text-decoration: underline; }

  /* Exit survey: a native popover over the /start page. */
  .demo-exit { margin-top: 1.5rem; }
  .exit-survey {
    width: min(92vw, 440px); max-height: 88vh; overflow-y: auto;
    margin: auto; padding: 1.25rem 1.4rem;
    color: var(--text); border-radius: var(--radius-sm);
    /* The dropdown menus' near-opaque surface, not the see-through glass: the
       persona tiles behind must not read through the questions. */
    background: var(--menu-bg);
    border: 1px solid var(--glass-border);
    box-shadow: var(--glass-shadow);
    backdrop-filter: blur(18px) saturate(1.4);
    -webkit-backdrop-filter: blur(18px) saturate(1.4);
  }
  .exit-survey::backdrop { background: rgba(0, 0, 0, 0.45); }
  .exit-survey__head { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
  .exit-survey__head h2 { margin: 0; }
  .exit-survey h3 { margin: 0.75rem 0 0.25rem; }
  /* "Just leave" (its own button_to form) stands apart from the step's actions. */
  .exit-survey > form:last-child { margin-top: 1rem; }
  .exit-survey__close {
    background: none; border: 0; font-size: 1.6rem; line-height: 1; color: var(--text-muted);
    cursor: pointer; min-width: 44px; min-height: 44px;
  }
  /* The step toggle sets [hidden]; state it outright so no display rule wins. */
  .exit-survey__step[hidden] { display: none; }
  .exit-survey__choice { display: flex; gap: 0.75rem; flex-wrap: wrap; }
  .exit-survey__option {
    display: flex; gap: 0.6rem; align-items: center; padding: 0.55rem 0; min-height: 44px;
  }
  .exit-survey__channels { display: grid; grid-template-columns: 1fr 1fr; column-gap: 0.75rem; }
  .exit-survey textarea, .exit-survey input[type="text"] { width: 100%; box-sizing: border-box; }
  /* Honeypot: off-screen rather than display:none, which some bots skip. */
  .exit-survey__trap { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }

  /* ── Onboarding flip card ─────────────────────────────────────────── */
  .onboarding {
    max-width: 640px;
    margin: 2rem auto;
  }
  .flip-card { perspective: 1600px; }
  .flip-card__inner {
    position: relative;
    transform-style: preserve-3d;
    transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  .flip-card.is-flipped .flip-card__inner { transform: rotateY(180deg); }

  /* Front sits in flow (defines height); back overlays, pre-rotated. */
  .flip-card__face {
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    margin: 0;
  }
  .flip-card__face--back {
    position: absolute;
    inset: 0;
    transform: rotateY(180deg);
  }
  /* "How do you want to use Dukafi?" — same frost token as the choices below:
     white in light mode, dark in dark mode. */
  .flip-card__face--front h2 {
    background: var(--btn-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    padding: 0.3rem 0.5rem;
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
  }

  .choice-grid {
    display: grid;
    gap: 0.75rem;
    margin-top: 1.25rem;
  }
  .choice {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    column-gap: 0.9rem;
    align-items: center;
    text-align: left;
    padding: 1rem 1.1rem;
    cursor: pointer;
    color: var(--text);
    /* Same frost token as the buttons: white in light mode, dark in dark mode. */
    background: var(--btn-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    box-shadow: var(--glass-shadow);
    transition: transform 0.15s ease, border-color 0.2s ease;
  }
  .choice:hover { transform: translateY(-1px); border-color: var(--accent); }
  .choice__icon { grid-row: 1 / span 2; font-size: 1.8rem; }
  /* Image icons: a fixed square keeps the three differently-shaped PNGs at an
     even visual weight; contain avoids cropping any of them. */
  .choice__icon img {
    display: block;
    width: 5rem;
    height: 5rem;
    object-fit: contain;
  }

  /* SVG icons (install / notifications) sit in the same square as the PNGs. */
  .choice__icon svg {
    display: block;
    width: 5rem;
    height: 5rem;
    padding: 1rem;
    color: var(--accent-strong);
  }
  /* The app-setup slot wraps its two cards without becoming a grid cell itself;
     .choice's display:grid would otherwise beat the [hidden] toggle. */
  .choice-grid__slot { display: contents; }
  .choice[hidden] { display: none; }

  .choice__title { font-weight: 600; }
  .choice__desc { color: var(--text-muted); font-size: 0.9rem; }

  .link-back {
    background: none;
    border: 0;
    padding: 0;
    margin-bottom: 0.5rem;
    color: var(--accent-strong);
    cursor: pointer;
    font: inherit;
  }

  /* Wider screens: spread the chooser into a 3-up row and grow the content
     to fill the space. The card keeps a min-height so the flipped form face
     (which overlays the now-shorter front) still has room to breathe. */
  @media (min-width: 768px) {
    .onboarding {
      max-width: 960px;
      margin-top: 3rem;
    }
    .flip-card__inner { min-height: 380px; }

    .flip-card__face--front h2 { font-size: 1.8rem; }

    .choice-grid {
      grid-template-columns: repeat(3, 1fr);
      gap: 1rem;
      margin-top: 1.75rem;
    }
    .choice {
      grid-template-columns: 1fr;
      grid-template-rows: auto auto auto;
      text-align: center;
      align-items: start;
      row-gap: 0.5rem;
      padding: 1.75rem 1.25rem;
    }
    .choice__icon { grid-row: auto; font-size: 2.75rem; }
    .choice__icon img,
    .choice__icon svg {
      width: 10rem;
      height: 10rem;
      margin-inline: auto;
    }
    .choice__icon svg { padding: 2.5rem; }

    .choice__title { font-size: 1.1rem; }

    /* Keep forms at a comfortable reading width within the wider card. */
    .flip-card__face--back .frame {
      max-width: 460px;
      margin-inline: auto;
    }
    /* The buyer frame's two choices sit side by side in that narrower frame. */
    .flip-card__face--back .choice-grid { grid-template-columns: repeat(2, 1fr); }
    .flip-card__face--back .choice { align-content: start; }
  }

  /* ── Shop grid (favorites — familiar spots to revisit) ────────────── */
  .shop-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 1rem;
    margin-top: 1rem;
  }
  /* A whole-card link: logo on top, name + distance below. */
  .shop-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    padding: 1rem;
    color: var(--text);
    text-align: center;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
  }
  .shop-card:hover { transform: translateY(-3px); }
  .shop-card__media {
    width: 72px;
    height: 72px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    overflow: hidden;
    background: var(--btn-bg);
  }
  .shop-card__media img { width: 100%; height: 100%; object-fit: cover; }
  .shop-card__noimg { font-size: 2rem; opacity: 0.6; }
  .shop-card__body {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
  }
  .shop-card__name { font-weight: 600; }
  .shop-card__distance { font-size: 0.85rem; }

  /* ── Product grid (discovery storefront) ──────────────────────────── */
  .product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 1rem;
    margin-top: 1rem;
  }
  /* Phones: three across so more products fit a screen. The cards get too
     narrow for price and basket side by side, so that row stacks. */
  @media (max-width: 560px) {
    .product-grid {
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 0.5rem;
    }
    /* Collapsed thumbnails only — the tapped-open overlay has room to spare. */
    .product-card:not(.is-expanded) .product-card__body { padding: 0.45rem 0.35rem 0.5rem; }
    .product-card:not(.is-expanded) .product-card__name,
    .product-card:not(.is-expanded) .product-card__price { font-size: 0.8rem; }
    .product-card__name { overflow-wrap: anywhere; }
    .product-card:not(.is-expanded) .product-card__buy {
      flex-direction: column;
      gap: 0.3rem;
    }
  }
  /* A clear-glass thumbnail. On hover (a quick peek) or click (.is-expanded —
     the gallery controller pins one open) the WHOLE card magnifies, revealing
     all its details at a readable size. Using transform means the magnified
     card floats over its neighbours instead of reflowing them. */
  .product-card {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: 0;
    margin: 0;
    color: var(--text);
    overflow: hidden;
    transform-origin: center;
    transition: transform 0.25s cubic-bezier(0.2, 0.7, 0.2, 1);
    /* Keep the glass rim highlights but drop .glass-card's heavy drop shadow —
       the grid reads cleaner without a dark halo around every thumbnail. */
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.5),
      inset 0 -1px 0 rgba(255, 255, 255, 0.1);
  }
  /* No frost field on product thumbnails — its hard-cut rectangle showed as a
     dark shade beside narrow images and behind the details. Clear glass only. */
  .product-card::before { content: none; }
  /* The magnified look: a pinned card (.is-expanded) on any device, plus a
     transient hover peek added only on pointer devices in the hover media query
     below — touch screens skip it so a tap pins directly (no sticky :hover
     leaving the wrong card magnified). */
  .product-card.is-expanded {
    transform: scale(1.5);
    /* Above the sticky nav and footer (z 100) so the magnified card — and its
       details panel — always floats clear of them, never tucked underneath. */
    z-index: 102;
    overflow: visible;          /* let the revealed part show below the card */
    /* Frosted once expanded so text reads clearly over the cards behind (clear
       glass stays on the collapsed thumbnail). The card's own transform traps a
       descendant's backdrop-filter, so the revealed section can't rely on blur —
       both use the opaque-fill frosted-panel token to stay uniform and legible. */
    background: var(--menu-bg);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    /* Drop the bottom edge so the revealed part continues the card seamlessly. */
    border-bottom: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  @media (hover: hover) and (pointer: fine) {
    /* Above the navbars (z 100), but below a pinned card (z 102) so it stays on
       top when a neighbour is hovered. */
    .product-card:hover {
      transform: scale(1.5);
      z-index: 101;
      overflow: visible;
      background: var(--menu-bg);
      backdrop-filter: var(--glass-filter);
      -webkit-backdrop-filter: var(--glass-filter);
      border-bottom: 0;
      border-bottom-left-radius: 0;
      border-bottom-right-radius: 0;
    }
    .product-card:hover .product-card__reveal {
      opacity: 1;
      pointer-events: auto;
    }
    /* The ×1.5 magnify would balloon this small button past a normal button's
       size; divide its text/padding by the scale so it stays proportional. */
    .product-card:hover .product-card__add {
      font-size: calc(0.85rem / 1.5);
      padding: calc(0.3rem / 1.5) calc(0.8rem / 1.5);
    }
    .product-card:hover .product-card__buy .btn.product-card__basket { font-size: calc(1rem / 1.5); }
  }

  /* Square media is the click target (pin/unpin the magnify); reset button
     chrome. It carries its own rounded top corners so they survive the card
     switching to overflow:visible when expanded (which stops clipping children). */
  .product-card__media {
    width: 100%;
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--glass-bg);
    border: 0;
    padding: 0;
    margin: 0;
    cursor: pointer;
    overflow: hidden;
    border-top-left-radius: var(--radius);
    border-top-right-radius: var(--radius);
  }
  .product-card__media img { width: 100%; height: 100%; object-fit: cover; }
  .product-card__noimg { font-size: 2.5rem; opacity: 0.6; }

  /* Corner flag marking an item that's already in the cart. Pinned to the
     thumbnail so picked items stay obvious without expanding the card. */
  .product-card.is-in-cart .product-card__media {
    box-shadow: inset 0 0 0 2px var(--accent-strong);
  }
  .product-card__incart {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
    padding: 0.1rem 0.4rem;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.4;
    color: #fff;
    background: var(--accent-strong);
    border-radius: 999px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  }
  .product-card__body {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.15rem;
    padding: 0.6rem 0.7rem 0.7rem;
    cursor: pointer;   /* part of the toggle target, like the thumbnail */
    flex: 1;           /* fill the card, so the buy row can sit on its floor */
  }
  .product-card__name { font-weight: 600; }
  .product-card__price { color: var(--accent-strong); }
  /* Price on the left, the basket-plus add button on the right — one line. */
  .product-card__buy {
    display: flex;
    width: 100%;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    /* Pinned to the card's bottom: names wrap to one, two or three lines, and
       every price and basket in a row still line up. */
    margin-top: auto;
    padding-top: 0.15rem;
  }
  .product-card__buy form { margin: 0; flex: none; }
  /* Round icon-only add button. Sized in em so the magnify rules below can
     shrink it back by one font-size change. Scoped under __buy so it outranks
     the generic button[type="submit"] padding/radius. */
  .product-card__buy .btn.product-card__basket {
    font-size: 1rem;
    width: 2.25em;
    height: 2.25em;
    padding: 0;
    justify-content: center;
    border-radius: 50%;
    color: var(--success);   /* green "add" cue, set apart from the blue price */
  }
  .product-card__basket .icon { width: 1.15em; height: 1.15em; }
  @media (pointer: coarse) {
    .product-card__buy .btn.product-card__basket { width: 2.75em; height: 2.75em; }  /* 44px tap target */
  }
  /* Quiet link to the product's own page, tucked under the price. */
  .product-card__details {
    font-size: 0.75rem;
    color: var(--text-muted);
    text-decoration: none;
  }
  .product-card__details:hover,
  .product-card__details:focus-visible {
    color: var(--accent);
    text-decoration: underline;
  }
  /* Shop on the left, distance on the right — one tidy line instead of two
     stacked rows, so the reveal reads wide (≈4:3) rather than tall. */
  .product-card__meta-row {
    display: flex;
    width: 100%;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
  }
  /* Accent-coloured so it reads as a link to the shop (the distance beside it
     stays muted). Underlines on hover/focus to confirm it's tappable. */
  .product-card__shop {
    color: var(--accent);
    text-decoration: none;
    min-width: 0;                /* allow the name to ellipsise, not push out */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .product-card__shop:hover,
  .product-card__shop:focus-visible { text-decoration: underline; }
  .product-card__shop,
  .product-card__distance { font-size: 0.85rem; }
  .product-card__distance { flex: none; }
  /* Compact, auto-width pill (btn-sm in the markup) — a low-key action that
     keeps the panel short rather than a full-width block. */
  .product-card__add { width: auto; margin-top: 0.2rem; }
  /* When a card is pinned open it magnifies ×1.5 (desktop only — the phone
     overlay isn't scaled), which oversizes this button. Counter the scale above
     the phone breakpoint so it matches normal button proportions. */
  @media (min-width: 561px) {
    .product-card.is-expanded .product-card__add {
      font-size: calc(0.85rem / 1.5);
      padding: calc(0.3rem / 1.5) calc(0.8rem / 1.5);
    }
    .product-card.is-expanded .product-card__buy .btn.product-card__basket { font-size: calc(1rem / 1.5); }
  }

  /* The rest of the info — hidden on the thumbnail, revealed when the card
     magnifies. Positioned out of flow so revealing it never reflows the grid.
     Matches the expanded card's navbar-style frosting (and drops its top edge)
     so it reads as the card continuing, not a separate drawer. */
  .product-card__reveal {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding: 0 0.7rem 0.7rem;
    /* The expanded card's transform traps this descendant's backdrop-filter, so
       the blur that frosts the card's top can't fire here — over the sharp photo
       the translucent --menu-bg alone reads as a lighter, washed-out shade. Stack
       it on an opaque --bg base so the bottom lands at the same solid shade as the
       frosted top, keeping the add-to-cart section legible. */
    background: linear-gradient(var(--menu-bg), var(--menu-bg)), var(--bg);
    border: 1px solid var(--glass-border);
    border-top: 0;
    border-radius: 0 0 var(--radius) var(--radius);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease;
  }
  .product-card.is-expanded .product-card__reveal {
    opacity: 1;
    pointer-events: auto;
  }

  /* Owner product grid: collapsed stock line + revealed meta and the inline
     stock-adjust control. */
  .product-card__stock { font-size: 0.85rem; }
  .product-card__meta { font-size: 0.85rem; }
  /* A draft/archived flag reusing the corner-badge slot (no cart here). */
  .product-card__flag { background: var(--accent-strong); }
  /* Number field with its native up/down steppers, Apply stacked right below. */
  .product-card__stock-form {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    width: 100%;
    margin: 0.2rem 0 0;
  }
  .product-card__stock-input {
    width: 100%;
    min-width: 0;
    padding: 0.45rem 0.5rem;
    color: var(--text);
    background: var(--btn-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    text-align: center;
  }

  /* Dimmed backdrop behind the phone-only centred overlay. Above the nav/footer
     (z 100) but below the floating card. The gallery controller toggles [hidden],
     which must beat this rule's display, so it's pinned explicitly below. */
  .product-card__backdrop {
    position: fixed;
    inset: 0;
    z-index: 190;
    background: rgba(0, 0, 0, 0.5);
  }
  .product-card__backdrop[hidden] { display: none; }

  /* Phones: there's no room to float a scaled card in the grid (it spills off the
     edges) and the grid can't be hovered. So a tapped card lifts out of the grid
     and floats as a centred glass overlay — the whole highlight always on screen,
     details flowing inline beneath the thumbnail — over the backdrop above. */
  @media (max-width: 560px) {
    .product-card.is-expanded {
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: min(340px, 88vw);
      max-height: 88vh;
      overflow-y: auto;
      z-index: 200;
      border: 1px solid var(--glass-border);
      border-radius: var(--radius);
      transition: none;   /* the pop-in animation owns the entrance */
      animation: product-pop 0.2s cubic-bezier(0.2, 0.7, 0.2, 1);
    }
    .product-card.is-expanded .product-card__media {
      border-radius: var(--radius) var(--radius) 0 0;
    }
    /* Reveal flows inline at the foot of the floating card (not a hanging panel). */
    .product-card.is-expanded .product-card__reveal {
      position: static;
      border: 0;
      border-radius: 0;
      padding-top: 0;
    }
  }
  @keyframes product-pop {
    from { transform: translate(-50%, -50%) scale(0.94); opacity: 0; }
    to   { transform: translate(-50%, -50%) scale(1); opacity: 1; }
  }

  /* ── Public product page ──────────────────────────────────────────── */
  /* Gallery beside the info card on wide screens; stacked on phones. */
  .product-page { margin-top: 1rem; }
  @media (min-width: 768px) {
    .product-page {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      gap: 1.5rem;
      align-items: start;
    }
  }

  .product-page__stage {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1 / 1;
    padding: 0;
    overflow: hidden;
  }
  .product-page__stage img { width: 100%; height: 100%; object-fit: cover; }
  .product-page__noimg { font-size: 4rem; opacity: 0.6; }

  .product-page__thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.75rem 0.5rem 0;
  }
  .product-page__thumb {
    width: 64px;
    height: 64px;
    padding: 0;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    cursor: pointer;
    background: var(--glass-bg);
  }
  .product-page__thumb img { width: 100%; height: 100%; object-fit: cover; }
  .product-page__thumb.is-active { border-color: var(--accent); }

  .product-page__name { margin: 0; }
  .product-page__price {
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--accent-strong);
    margin: 0.5rem 0;
  }
  .product-page__shop { color: var(--accent); text-decoration: none; }
  .product-page__shop:hover,
  .product-page__shop:focus-visible { text-decoration: underline; }
  .product-page__desc { margin: 0.75rem 0; }
  .product-page__actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 1rem;
  }
  .product-page__carted { color: var(--accent-strong); }

  /* ── POS: cashier product picker ──────────────────────────────────── */
  /* Catalogue beside a sticky ticket on desktop; stacked on phones. */
  .pos { margin-top: 1rem; }
  @media (min-width: 768px) {
    .pos {
      display: grid;
      grid-template-columns: 1fr 320px;
      gap: 1.5rem;
      align-items: start;
    }
  }

  .pos__search {
    width: 100%;
    padding: 0.6rem 0.9rem;
    margin-bottom: 1rem;
    color: var(--text);
    background: var(--btn-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
  }
  .pos__search::placeholder { color: var(--text-muted); }

  /* Small, image-led tiles so the eye grabs the picture first. */
  .pos__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 0.6rem;
  }
  .pos-tile {
    display: flex;
    flex-direction: column;
    padding: 0;
    overflow: hidden;
    text-align: center;
    color: var(--text);
    cursor: pointer;
    background: var(--btn-bg);
    border: none;
    border-radius: var(--radius-sm);
    box-shadow: var(--glass-shadow);
    transition: transform 0.12s ease, box-shadow 0.2s ease;
  }
  .pos-tile:hover { transform: translateY(-2px); }
  .pos-tile:active { transform: translateY(0); }
  .pos-tile.is-selected { outline: 2px solid var(--accent); outline-offset: -2px; }
  .pos-tile.is-out { opacity: 0.55; cursor: not-allowed; }

  .pos-tile__media {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--glass-bg);
  }
  .pos-tile__media img { width: 100%; height: 100%; object-fit: cover; }
  .pos-tile__noimg { font-size: 2rem; opacity: 0.6; }

  /* Corner badge: how many of this product are on the ticket. */
  .pos-tile__qty {
    position: absolute;
    top: 0.3rem;
    right: 0.3rem;
    min-width: 1.4rem;
    padding: 0.05rem 0.35rem;
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--accent-contrast);
    background: var(--accent);
    border-radius: 999px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  }
  .pos-tile__out {
    position: absolute;
    bottom: 0.3rem;
    left: 0.3rem;
    padding: 0.05rem 0.35rem;
    font-size: 0.7rem;
    font-weight: 700;
    color: #fff;
    background: var(--danger);
    border-radius: 999px;
  }
  .pos-tile__name {
    padding: 0.4rem 0.5rem 0;
    font-size: 0.85rem;
    line-height: 1.2;
    /* Two-line clamp keeps tiles even when names run long. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .pos-tile__price {
    padding: 0.15rem 0.5rem 0.5rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--accent-strong);
  }
  .pos__no-results { margin-top: 1rem; }

  /* Ticket — the running sale. */
  .pos-ticket { margin: 1rem 0 0; }
  @media (min-width: 768px) {
    .pos-ticket { position: sticky; top: 1rem; margin: 0; }
  }
  .pos-ticket__head { margin: 0 0 0.75rem; }
  .pos-ticket__lines { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
  .pos-ticket__line {
    display: grid;
    grid-template-columns: 1fr auto auto auto;
    align-items: center;
    gap: 0.5rem;
  }
  .pos-ticket__name { font-size: 0.9rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .pos-ticket__line-total { font-variant-numeric: tabular-nums; font-size: 0.9rem; }
  .pos-ticket__remove {
    border: 0;
    background: none;
    color: var(--danger);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 0.2rem;
  }
  /* Compact stepper inside a ticket line. */
  .pos-ticket .qty-stepper { gap: 0.3rem; }
  .pos-ticket .qty-stepper__btn {
    width: 1.8rem;
    height: 1.8rem;
    padding: 0;
    font-size: 1.2rem;
    font-weight: 700;
    line-height: 1;
    color: var(--accent);
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    cursor: pointer;
  }
  .pos-ticket .qty-stepper__btn:disabled { opacity: 0.4; cursor: not-allowed; }
  .pos-ticket__qty { min-width: 1.4rem; text-align: center; font-weight: 600; font-variant-numeric: tabular-nums; }

  .pos-ticket__total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin: 1rem 0;
    padding-top: 0.75rem;
    border-top: 1px solid var(--glass-border);
    font-size: 1.2rem;
    font-weight: 700;
  }
  .pos-ticket__submit { width: 100%; }

  /* Mobile sticky bar: keeps the running sale reachable where the side ticket
     is hidden. Hidden on desktop and until the first item is added. */
  .pos-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    background: var(--glass-bg-strong);
    border-top: 1px solid var(--glass-border);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
  }
  .pos-bar[hidden] { display: none; }
  .pos-bar__summary { font-size: 0.95rem; }
  .pos-bar .btn { white-space: nowrap; }
  @media (min-width: 768px) {
    .pos-bar { display: none; }
  }
  @media (max-width: 767px) {
    .pos-ticket { display: none; }
    .pos { padding-bottom: 4.5rem; } /* clear the fixed bar */
  }

  /* Review modal: the sale expanded to the middle of the screen to confirm,
     adjust, or go back before payment. */
  .pos-review {
    position: fixed;
    inset: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(0, 0, 0, 0.5);
  }
  .pos-review[hidden] { display: none; }
  .pos-review__panel {
    width: 100%;
    max-width: 420px;
    max-height: 80vh;
    margin: 0;
    overflow-y: auto;
  }
  .pos-review__head { margin: 0 0 0.75rem; }
  .pos-review__actions { display: flex; gap: 0.6rem; }
  .pos-review__actions .btn { flex: 1; }

  /* ── POS payment panel (record cash / STK / M-Pesa till) ─────────── */
  .pos-payment__methods {
    display: grid;
    gap: 1rem;
    margin-top: 0.5rem;
  }
  @media (min-width: 720px) {
    .pos-payment__methods { grid-template-columns: repeat(3, 1fr); align-items: start; }
  }
  .pos-payment__form { margin: 0; }
  .pos-payment__awaiting {
    display: flex;
    align-items: center;
    gap: 0.6rem;
  }
  .spinner {
    width: 1rem;
    height: 1rem;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    display: inline-block;
    animation: spin 0.7s linear infinite;
  }
  @keyframes spin { to { transform: rotate(360deg); } }

  /* ── Discover header actions (location + view toggle) ─────────────── */
  .discover-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
  }
  /* Forms wrapping a button shouldn't add their own box. */
  .discover-actions form { margin: 0; }

  /* ── Discover layout: filters + product results ───────────────────── */
  /* Mobile-first: filters stack on top of the grid. */
  .discover { margin-top: 1rem; }
  .discover__filters { margin: 0 0.25rem 1rem; }

  /* On wider screens the filters become a sticky sidebar to the left. */
  @media (min-width: 768px) {
    .discover {
      display: grid;
      grid-template-columns: 220px 1fr;
      gap: 1.5rem;
      align-items: start;
    }
    .discover__filters {
      position: sticky;
      top: 1rem;
      margin: 0;
    }
  }

  /* Search — a frosted field with an attached submit affordance. */
  .discover-search {
    display: flex;
    gap: 0.4rem;
    margin: 0 0 0.9rem;
  }
  .discover-search__input {
    flex: 1;
    min-width: 0;
    padding: 0.55rem 0.8rem;
    color: var(--text);
    background: var(--btn-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
  }
  .discover-search__input::placeholder { color: var(--text-muted); }
  .discover-search__btn { flex: 0 0 auto; }

  /* Category slider — a horizontal, swipeable rail on phones; wraps into the
     sidebar column on wider screens. Built only from categories that have
     products, so a chip never leads to an empty result. */
  .category-slider {
    display: flex;
    gap: 0.5rem;
    overflow-x: auto;
    /* Room top & bottom so the chips' frost shadow and hover lift aren't clipped
       by the scroll container. */
    padding: 0.3rem 0 0.5rem;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
  }
  @media (min-width: 768px) {
    .category-slider { flex-wrap: wrap; overflow-x: visible; padding: 0; }
  }
  /* Match the app's buttons: frosted, borderless, square corners. */
  .category-chip {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: 0.4rem 0.85rem;
    font-size: 0.9rem;
    color: var(--text);
    text-decoration: none;
    background: var(--btn-bg);
    border: none;
    border-radius: 0;
    backdrop-filter: var(--glass-filter-strong);
    -webkit-backdrop-filter: var(--glass-filter-strong);
    box-shadow: var(--glass-shadow);
    transition: transform 0.15s ease, background 0.2s ease, color 0.2s ease;
  }
  .category-chip:hover { transform: translateY(-1px); }
  .category-chip.is-active {
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: 600;
  }

  /* Shop catalog: a stretched glass card of image+name category tabs, then the
     product grid below. The tab strip reuses .category-slider (swipeable on
     phones, wraps on wide screens). */
  .shop-catalog { margin-top: 1rem; }
  .cat-tab {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    width: 5rem;
    padding: 0.55rem 0.4rem;
    text-decoration: none;
    color: var(--text);
    background: var(--btn-bg);
    box-shadow: var(--glass-shadow);
    backdrop-filter: var(--glass-filter-strong);
    -webkit-backdrop-filter: var(--glass-filter-strong);
    transition: transform 0.15s ease, background 0.2s ease, color 0.2s ease;
  }
  .cat-tab:hover { transform: translateY(-1px); }
  .cat-tab.is-active { background: var(--accent); color: var(--accent-contrast); font-weight: 600; }
  .cat-tab__thumb {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    background: rgba(255, 255, 255, 0.08);
  }
  .cat-tab__thumb img { width: 100%; height: 100%; object-fit: cover; }
  .cat-tab__name {
    width: 100%;
    font-size: 0.78rem;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Glass category sheet — a phone-only picker raised when a chip is tapped (the
     category-picker controller). A frosted panel slides up from the bottom over
     a dimmed backdrop; the full list is laid out as comfortable, tappable rows. */
  .category-sheet {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: flex-end;
    justify-content: center;
  }
  /* [hidden] must beat the flex display above, or the sheet never hides. */
  .category-sheet[hidden] { display: none; }
  .category-sheet__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
  }
  .category-sheet__panel {
    position: relative;
    width: 100%;
    max-height: 70vh;
    display: flex;
    flex-direction: column;
    padding: 0.75rem 1rem 1.25rem;
    background: var(--menu-bg);
    border: 1px solid var(--glass-border);
    border-bottom: 0;
    border-radius: var(--radius) var(--radius) 0 0;
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    box-shadow: var(--glass-shadow);
    animation: category-sheet-up 0.22s cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  @keyframes category-sheet-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  .category-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.75rem;
  }
  .category-sheet__title { font-weight: 600; }
  .category-sheet__close {
    padding: 0.25rem 0.5rem;
    font-size: 1.1rem;
    line-height: 1;
    color: var(--text);
    background: transparent;
    border: none;
    cursor: pointer;
  }
  /* The full list as a tidy grid of the same frosted chips used on desktop. */
  .category-sheet__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    gap: 0.5rem;
    overflow-y: auto;
  }
  /* Chips fill their cell here (rather than hug their text) and centre the label,
     wrapping a long name instead of overflowing the tile. */
  .category-sheet__grid .category-chip {
    text-align: center;
    white-space: normal;
  }

  /* Live-search results heading ("Products matching …"). */
  .discover__results-title {
    margin: 0 0.25rem 0.75rem;
    font-size: 1.15rem;
  }

  /* ── Pagination (themed) ──────────────────────────────────────────── */
  .pagination {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 0.4rem;
    margin: 1.5rem 0 0.5rem;
  }
  /* Match the app's buttons: frosted, borderless, square corners. */
  .pagination__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.2rem;
    padding: 0.4rem 0.6rem;
    color: var(--text);
    text-decoration: none;
    background: var(--btn-bg);
    border: none;
    border-radius: 0;
    backdrop-filter: var(--glass-filter-strong);
    -webkit-backdrop-filter: var(--glass-filter-strong);
    box-shadow: var(--glass-shadow);
    transition: transform 0.15s ease, background 0.2s ease, color 0.2s ease;
  }
  .pagination__link:hover { transform: translateY(-1px); }
  .pagination__link.is-current {
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: 600;
  }
  .pagination__link.is-disabled {
    opacity: 0.45;
    pointer-events: none;
  }
  .pagination__gap { padding: 0 0.2rem; color: var(--text-muted); }
  .pagination__summary { text-align: center; margin: 0.25rem 0 0; font-size: 0.85rem; }
  /* Hide the word labels on the prev/next controls when space is tight. */
  @media (max-width: 420px) {
    .pagination__nav-label { display: none; }
  }

  /* Kinetic footer: the nav's mirror image at the foot of the page. Same frosted
     glass, but stuck to the bottom edge while the kinetic-nav controller springs
     it down/up (edge:bottom). touch-action:none lets it read vertical drags. */
  .app-footer {
    position: sticky;
    bottom: 0;
    z-index: 100;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    margin-top: 1.5rem;
    padding: 0.5rem 1.25rem 0.6rem;
    background: var(--nav-bg);
    border-top: 1px solid var(--glass-border);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    touch-action: none;
    will-change: transform;
  }
  /* The paginator already sits in the footer's padding — drop its own spacing. */
  .app-footer .pagination { margin: 0; }
  .app-footer .pagination__summary { margin: 0; }

  /* ── Shop radar / star view (discovery) ───────────────────────────── */
  .radar-card { text-align: center; }
  .radar {
    position: relative;
    width: 100%;                 /* fill the whole card — no wasted side space */
    height: clamp(380px, 68vh, 760px);
    overflow: hidden;            /* clip panned/zoomed content to the card */
    border-radius: var(--radius-sm);
    touch-action: none;          /* let us read pinch/drag instead of scroll */
  }
  .radar__canvas { display: block; width: 100%; height: 100%; }
  .radar__svg {
    width: 100%;
    height: 100%;
    display: block;
    cursor: grab;
    touch-action: none;
  }
  .radar__svg:active { cursor: grabbing; }
  .radar__hint { margin-top: 0.5rem; font-size: 0.85rem; }

  /* "At this spot": shops a cluster can't spread (same exact point). */
  .radar__stack {
    position: absolute;
    left: 50%;
    bottom: 0.75rem;
    z-index: 2;
    width: min(92%, 340px);
    transform: translateX(-50%);
    padding: 0.75rem 0.9rem;
    text-align: left;
    color: var(--text);
    background: var(--menu-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--glass-shadow);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
  }
  .radar__stack[hidden] { display: none; }
  .radar__stack-head { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
  .radar__stack-close {
    min-width: 44px; min-height: 44px; margin: -0.5rem -0.5rem -0.5rem 0;
    font-size: 1.4rem; line-height: 1; color: var(--text-muted); background: none; border: 0; cursor: pointer;
  }
  .radar__stack-list { list-style: none; margin: 0.25rem 0 0; padding: 0; }
  .radar__stack-list li + li { border-top: 1px solid var(--glass-border); }
  .radar__stack-link {
    display: flex; justify-content: space-between; align-items: center; gap: 0.75rem;
    min-height: 44px; color: var(--text); text-decoration: none; font-weight: 600;
  }
  .radar__stack-km { color: var(--text-muted); font-weight: 400; white-space: nowrap; }

  /* Zoom controls overlaid on the radar */
  .radar__zoom {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
  }
  .radar__zoom-btn {
    width: 2rem;
    height: 2rem;
    padding: 0;
    font-size: 1.1rem;
    line-height: 1;
    border-radius: var(--radius-sm);
    background: var(--glass-bg-strong);
    border: 1px solid var(--glass-border);
    color: var(--text);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
  }
  .radar__zoom-btn:hover { background: var(--accent); color: var(--accent-contrast); }

  /* Cluster marker (collapsed overlapping shops) */
  .radar__cluster { cursor: zoom-in; }
  .radar__cluster-ring {
    fill: none;
    stroke: var(--accent);
    stroke-width: 2;
  }
  .radar__cluster:hover .radar__cluster-ring,
  .radar__cluster:focus .radar__cluster-ring { stroke-width: 3.5; }
  .radar__cluster-count {
    fill: var(--accent-strong);
    font-size: 18px;
    font-weight: 700;
  }

  /* On phones, drop the card's interior padding so the radar fills the card
     width — the exterior margin (.app-main padding) stays as-is. */
  @media (max-width: 560px) {
    .radar-card { padding: 0; }
    .radar { height: clamp(420px, 72vh, 640px); }
    .radar__hint { padding: 0.75rem; }
  }

  /* Range rings + their distance labels */
  .radar__ring {
    fill: none;
    stroke: var(--glass-border);
    stroke-width: 1;
  }
  .radar__ring-label {
    fill: var(--text-muted);
    font-size: 13px;
  }

  /* Spokes from the buyer out to each shop — the "star" */
  .radar__spoke {
    stroke: var(--accent);
    stroke-width: 1.5;
    opacity: 0.5;
  }

  /* Shop nodes */
  .radar__node { cursor: pointer; }
  .radar__node-bg { fill: var(--glass-bg-strong); }
  .radar__node-ring {
    fill: none;
    stroke: var(--glass-border);
    stroke-width: 1.5;
  }
  .radar__node-icon { font-size: 24px; }
  .radar__node:hover .radar__node-ring { stroke: var(--accent); stroke-width: 2.5; }
  .radar__node:hover .radar__spoke { opacity: 1; }
  .radar__name {
    fill: var(--text);
    font-size: 13px;
    font-weight: 600;
  }
  .radar__dist {
    fill: var(--accent-strong);
    font-size: 12px;
  }

  /* The buyer at the centre */
  .radar__you-bg {
    fill: var(--accent-soft);
    stroke: var(--accent);
    stroke-width: 1.5;
  }
  .radar__you { font-size: 22px; }
  .radar__you-label {
    fill: var(--text-muted);
    font-size: 12px;
  }

  /* ── Delivery lifecycle badges ─────────────────────────────────────── */
  .badge--assigned   { border-color: rgba(200, 160, 40, 0.6); color: #b8860b; }
  .badge--accepted,
  .badge--picked_up,
  .badge--dispatched { border-color: var(--accent); color: var(--accent-strong); }
  .badge--delivered,
  .badge--completed  { border-color: var(--accent); color: var(--accent-strong); }
  .badge--failed,
  .badge--canceled   { border-color: rgba(200, 60, 60, 0.6); color: #c0392b; }

  /* ── Account dropdown (merchant mode switch + sign out) ──────────────
     Reuses the nav-menu pattern: a frosted panel that springs in, matching
     .app-nav__group's surface and motion. */
  .account-menu { position: relative; }
  /* Plain clickable icon + small label, mirroring the brand (no button chrome). */
  .app-nav__account {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text);
    font: inherit;
    line-height: 1;
  }
  .app-nav__account-icon { height: 28px; width: auto; }
  .app-nav__account-mode {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    opacity: 0.8;
  }
  .account-menu__panel {
    display: flex;
    gap: 0.5rem;
    position: absolute;
    top: calc(100% + 0.4rem);
    right: 0;
    padding: 0.5rem;
    background: var(--menu-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    box-shadow: var(--glass-shadow);
    /* hidden until opened; springs in like the main menu */
    opacity: 0;
    transform: translateY(-6px) scale(0.98);
    transform-origin: top right;
    pointer-events: none;
    transition: opacity 0.18s ease, transform 0.18s ease;
    z-index: 130;
  }
  .account-menu.is-open .account-menu__panel {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
  }
  .account-menu__panel form { flex: 1; }
  .account-menu__panel .btn { width: 100%; white-space: nowrap; }

  /* ── Blocking action overlay (rider offers, merchant dispatch) ─────── */
  .action-overlay,
  .marketing-overlay {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
  }
  /* A flex `display` would otherwise beat the UA `[hidden]` rule, so a popup
     overlay (e.g. POS "Send receipt") stays visible until its trigger opens it. */
  .action-overlay[hidden],
  .marketing-overlay[hidden] {
    display: none;
  }
  .action-overlay__card {
    width: min(28rem, 100%);
    margin: 0;
    position: relative;
  }
  /* Dismiss affordance for non-blocking overlays (e.g. order confirmation). */
  .overlay-close {
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
    background: none;
    border: 0;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    color: var(--text-muted, #888);
  }
  .overlay-close:hover { color: var(--text, inherit); }

  /* ── Marketing panels: Share & Print ───────────────────────────────── */
  /* The launcher wraps a trigger button + its fixed overlay; the button should
     sit inline in the dashboard's .actions row. */
  .marketing-launcher { display: inline-flex; }

  /* These panels are two-column (controls + live preview), so they need more
     room than the default 28rem overlay card. */
  .action-overlay__card.share-panel,
  .action-overlay__card.print-panel {
    width: min(56rem, 100%);
    max-height: 88vh;
    overflow-y: auto;
    text-align: left;
  }

  .share-panel__body,
  .print-panel__body {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.25rem;
    margin-top: 0.5rem;
  }
  @media (max-width: 640px) {
    .share-panel__body,
    .print-panel__body { grid-template-columns: 1fr; }
  }

  .share-panel__controls,
  .print-panel__controls { display: flex; flex-direction: column; gap: 0.9rem; }

  .share-panel__scope,
  .print-panel__scope,
  .share-panel__templates,
  .print-panel__templates {
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.75rem;
  }
  .share-panel__scope legend,
  .print-panel__scope legend,
  .share-panel__templates legend,
  .print-panel__templates legend { font-size: 0.8rem; color: var(--text-muted); padding: 0 0.25rem; }
  .share-panel__scope label,
  .print-panel__scope label { display: block; padding: 0.15rem 0; }

  .print-panel__field { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.85rem; }
  .share-panel__row select,
  .print-panel__row select,
  .print-panel__field select {
    width: 100%;
    padding: 0.4rem 0.6rem;
    color: var(--text);
    background: var(--btn-bg);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
  }

  /* Scrollable product picker — flex display would beat UA [hidden], so guard. */
  .share-panel__products {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    max-height: 10rem;
    overflow-y: auto;
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    padding: 0.4rem 0.6rem;
  }
  .share-panel__products[hidden] { display: none; }
  .share-panel__product { display: flex; align-items: center; gap: 0.4rem; }

  /* Template chips: the radio drives an accent-dotted label. */
  .share-panel__templates,
  .print-panel__templates { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
  .template-swatch {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--glass-border);
    border-radius: 999px;
    cursor: pointer;
    font-size: 0.85rem;
  }
  .template-swatch input { position: absolute; opacity: 0; pointer-events: none; }
  .template-swatch::before {
    content: "";
    width: 0.9rem;
    height: 0.9rem;
    border-radius: 50%;
    background: var(--swatch, #888);
  }
  .template-swatch:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
  .template-swatch--bold    { --swatch: #ff5a1f; }
  .template-swatch--minimal { --swatch: #111827; }
  .template-swatch--playful { --swatch: #ff2e88; }
  .template-swatch--classic { --swatch: #c8a24a; }

  .share-panel__preview img {
    width: 100%;
    border-radius: var(--radius-sm);
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
  }
  .print-panel__preview iframe {
    width: 100%;
    height: min(60vh, 520px);
    border: 1px solid var(--glass-border);
    border-radius: var(--radius-sm);
    background: #fff;
  }

  .share-panel__actions,
  .print-panel__actions,
  .share-panel__networks {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.9rem;
  }
  .share-panel__networks .btn-sm { flex: 1 1 auto; text-align: center; }

  /* ── Earnings chart (server-rendered SVG) ──────────────────────────── */
  .earnings-chart {
    width: 100%;
    height: auto;
    display: block;
  }
  .earnings-chart__bar { fill: var(--accent); }
  .earnings-chart__bar:hover { fill: var(--accent-strong); }
  .earnings-chart__axis {
    stroke: var(--glass-border);
    stroke-width: 1;
  }

  /* ── Shop owner dashboard (live-tile board) ────────────────────────── */
  .shop-board {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    grid-auto-rows: minmax(190px, auto);
    grid-auto-flow: row dense;
    gap: 1rem;
    margin: 0.5rem;
  }
  /* Tiles are glass cards laid out as a column: head, body (grows), actions
     pinned to the bottom so the button rows line up across the board. */
  .shop-tile {
    margin: 0;
    display: flex;
    flex-direction: column;
  }
  .shop-tile--lead { grid-column: span 2; grid-row: span 2; }
  .shop-tile--wide { grid-column: span 2; }
  /* The owner's management hub — accented so it reads as the admin entry point. */
  .shop-tile--admin {
    border-color: color-mix(in srgb, var(--accent, #6ea8fe) 55%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent, #6ea8fe) 25%, transparent);
  }
  .shop-tile__role { margin: 0.25rem 0 0; }
  .capability-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.5rem 0 0; }

  /* POS connectivity / offline-queue indicator. */
  .pos-conn {
    display: inline-flex; align-items: center; gap: 0.35rem;
    padding: 0.25rem 0.6rem; border-radius: 999px; font-size: 0.85rem;
    background: color-mix(in srgb, var(--accent, #6ea8fe) 18%, transparent);
  }
  /* inline-flex beats the UA [hidden] rule; without this the pill never goes
     and reads "Syncing 2…" after the sales have synced. */
  .pos-conn[hidden] { display: none; }

  /* Barcode-scan confirmation toast — a brief pill after each scan. */
  .pos-scan-toast {
    position: fixed; left: 50%; bottom: 5.5rem; transform: translateX(-50%);
    z-index: 60; max-width: 90vw;
    padding: 0.5rem 1rem; border-radius: 999px; font-weight: 600;
    color: #fff; background: color-mix(in srgb, #1a7f37 88%, transparent);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
  }
  .pos-scan-toast.is-error {
    background: color-mix(in srgb, var(--danger, #c9372c) 90%, transparent);
  }

  /* Onboarding wizard step indicator. */
  .wizard-steps {
    display: flex; flex-wrap: wrap; gap: 0.5rem;
    list-style: none; margin: 0 0 1rem; padding: 0;
  }
  .wizard-steps__item { color: var(--muted, #8a8a8a); font-size: 0.9rem; }
  .wizard-steps__item.is-current { color: inherit; font-weight: 600; }
  .wizard-steps__num {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.4rem; height: 1.4rem; border-radius: 50%;
    background: color-mix(in srgb, currentColor 12%, transparent);
    font-size: 0.8rem;
  }

  .shop-tile__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
  }
  .shop-tile__head h3 { margin: 0; }
  .shop-tile__metric { font-size: 1.5rem; font-weight: 700; margin: 0 0 0.5rem; }
  .shop-tile__big { font-size: 3rem; font-weight: 700; line-height: 1; margin: 0.25rem 0 0; }

  .shop-tile__body { flex: 1; min-height: 0; }
  /* A graph beside its legend. */
  .shop-tile__body--split {
    display: flex;
    align-items: center;
    gap: 1rem;
  }
  .shop-tile .actions { margin-top: auto; padding-top: 0.75rem; }

  /* The buttons inside tiles stay compact so four still fit a tile width. */
  .shop-tile .actions .btn {
    padding: 0.35rem 0.7rem;
    font-size: 0.85rem;
  }

  /* On phones the board collapses to one column; spanning tiles go full width. */
  @media (max-width: 560px) {
    .shop-board { grid-template-columns: 1fr; }
    .shop-tile--lead { grid-column: auto; grid-row: auto; }
    .shop-tile--wide { grid-column: auto; }
  }

  /* ── Inline SVG charts (bar + donut), theme-aware ──────────────────── */
  .chart { display: block; width: 100%; height: auto; }
  .chart--bars { max-height: 150px; }
  .chart__bar { fill: var(--chart-1); transition: fill 0.15s ease; }
  .chart__bar:hover { fill: var(--accent-strong); }
  .chart__label { fill: var(--text-muted); font-size: 9px; }

  /* Keep the donut square and modest so it sits beside its legend. */
  .chart--donut { width: auto; height: 120px; flex: 0 0 auto; }
  .chart__track { stroke: var(--chart-track); }
  .chart__donut-value { fill: var(--text); font-size: 22px; font-weight: 700; }
  .chart__donut-label { fill: var(--text-muted); font-size: 9px; }

  .chart-legend {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.3rem;
    font-size: 0.85rem;
  }
  .chart-legend li { display: flex; align-items: center; gap: 0.45rem; }
  .chart-legend__dot {
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 2px;
    flex: 0 0 auto;
  }

  /* ── Line chart (activity busy-times) ──────────────────────────────── */
  .chart--line { max-height: 200px; }
  .chart__area { fill: var(--chart-1); opacity: 0.14; }
  .chart__line { stroke: var(--chart-1); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
  .chart__dot { fill: var(--chart-1); opacity: 0; transition: opacity 0.15s ease; }
  .chart__dot:hover { opacity: 1; }

  /* ── Activity log: tabs + terminal feed ────────────────────────────── */
  .activity__head {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom: 1rem;
  }
  .activity__head h1 { margin: 0 0 0.25rem; }
  .activity__tabs { display: flex; gap: 0.4rem; flex-wrap: wrap; }
  .activity__tabs .btn.is-active,
  .activity__graph-toggle .btn.is-active {
    border-color: var(--accent);
    color: var(--accent-strong);
  }

  .activity__graph-head {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
  }
  .activity__graph-head h2 { margin: 0; }
  .activity__graph-toggle { display: flex; gap: 0.4rem; }
  .activity__more { margin-top: 1rem; }

  .terminal__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
  }
  .terminal__filter {
    flex: 1;
    max-width: 16rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
    padding: 0.35rem 0.6rem;
    border-radius: 0.5rem;
    border: 1px solid var(--chart-track);
    background: rgba(0, 0, 0, 0.25);
    color: inherit;
  }
  .terminal__meta { font-size: 0.8rem; white-space: nowrap; }

  /* The feed itself: a dark, scrollable, monospace console. */
  .terminal {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.82rem;
    line-height: 1.55;
    max-height: 60vh;
    overflow-y: auto;
    padding: 0.75rem 0.9rem;
    border-radius: 0.6rem;
    background: #0b0f14;
    color: #cdd6e0;
    border: 1px solid rgba(255, 255, 255, 0.08);
    box-shadow: inset 0 0 32px rgba(0, 0, 0, 0.6);
  }
  .terminal__line {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    padding: 0.05rem 0;
    white-space: pre-wrap;
    word-break: break-word;
  }
  .terminal__time { color: #5c6b7a; }
  .terminal__glyph { color: #8a9bb0; width: 1ch; text-align: center; }
  .terminal__actor { color: #7fd1ff; }
  .terminal__source { color: #5c6b7a; }
  .terminal__reason { color: #8a9bb0; font-style: italic; }
  .terminal__subject { color: #e7eef6; }
  .terminal__empty { padding: 0.5rem 0; }

  /* Severity tints the message + glyph so the eye lands on what matters. */
  .terminal__line--success .terminal__glyph,
  .terminal__line--success .terminal__msg { color: #58c08c; }
  .terminal__line--warn .terminal__glyph,
  .terminal__line--warn .terminal__msg { color: #e2c25c; }
  .terminal__line--danger .terminal__glyph,
  .terminal__line--danger .terminal__msg { color: #ef8a82; }
  .terminal__line--info .terminal__msg { color: #cdd6e0; }

  /* ── Delivery location (checkout overlay) ─────────────────────────── */
  .delivery-location__modes {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.75rem 0;
  }
  .delivery-location__modes .btn.is-active {
    background: var(--accent, #7fd1ff);
    color: #06121f;
  }

  /* ── Rider stop destination (trips / delivery) ────────────────────── */
  .stop { margin-bottom: 1rem; }
  .delivery-destination { margin: 0.5rem 0 0; }
  .delivery-destination__call {
    margin: 0.4rem 0;
    font-weight: 600;
  }
  .delivery-destination__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.5rem;
  }

  /* ── Small screens (≤ 900px): floating top, five-slot bottom bar ───────
     Phones and small tablets. The top bar dissolves: the logo and theme icon
     float over the meadow with the page dial between them, and every other
     nav function lives in the bottom bar (shared/_bottom_bar). Wider screens
     keep the top bar and the footer pager; none of this renders for them. */
  .bottom-bar-host, .app-nav__dial { display: none; }

  @media (max-width: 900px) {
    :root { --bottom-bar-h: 60px; }

    .app-nav {
      position: fixed;
      inset: 0 0 auto 0;
      justify-content: space-between;
      gap: 0.5rem;
      padding: 0.5rem 0.75rem;
      background: none;
      border-bottom: 0;
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
      pointer-events: none;           /* the empty band lets taps through... */
    }
    .app-nav > * { pointer-events: auto; } /* ...its floating pieces don't */
    .app-nav__links, .app-nav .notif-nav, .app-nav .lang-toggle { display: none; }
    .app-nav__dial { display: flex; flex: 1; justify-content: center; min-width: 0; }
    .theme-toggle img { height: 46px; }
    .app-main {
      padding-top: 5rem;
      padding-bottom: calc(var(--bottom-bar-h) + 3rem + env(safe-area-inset-bottom));
    }

    /* The dial pages paged lists here; Discover's search, category rail and
       location buttons are on the bar (Search, Categories, Near). */
    .app-footer, .discover__filters .discover-search, .discover .category-slider, .discover-actions { display: none; }
    .discover { display: block; }
    /* The till's sale bar sits on top of the bottom bar, not under it. */
    .pos-bar { bottom: calc(var(--bottom-bar-h) + 1.75rem + env(safe-area-inset-bottom)); }

    /* ── The bar ── */
    .bottom-bar-host { display: block; }
    .bottom-bar {
      position: fixed;
      inset: auto 0 0 0;
      z-index: 110;
      display: grid;
      grid-template-columns: repeat(5, 1fr);
      height: var(--bottom-bar-h);
      padding: 0 0.25rem env(safe-area-inset-bottom);
      box-sizing: content-box;
      background: var(--menu-bg);
      border-top: 1px solid var(--glass-border);
      backdrop-filter: var(--glass-filter);
      -webkit-backdrop-filter: var(--glass-filter);
      box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.12);
      touch-action: none;             /* kinetic-nav reads vertical drags */
    }
    .bottom-bar__slot {
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 0.15rem;
      min-height: 44px;
      padding: 0;
      font: inherit;
      font-size: 0.7rem;
      color: var(--text-muted);
      text-decoration: none;
      background: none;
      border: 0;
      cursor: pointer;
      transition: transform 0.25s ease, color 0.15s ease;
    }
    .bottom-bar__slot .icon { width: 22px; height: 22px; }
    .bottom-bar__slot:hover, .bottom-bar__slot:focus-visible { color: var(--text); }

    /* The raised centre: larger, lifted clear of the bar, so it still shows
       when the bar is tucked halfway. Squared, like the app's other boxes. */
    .bottom-bar__center { position: relative; }
    .bottom-bar__main {
      position: absolute;
      left: 50%;
      bottom: calc(var(--bottom-bar-h) * 0.3);
      display: flex;
      align-items: center;
      justify-content: center;
      width: 62px;
      height: 62px;
      margin-left: -31px;
      padding: 0;
      color: var(--accent-contrast);
      text-decoration: none;
      background: var(--accent);
      border: 3px solid var(--menu-bg);
      border-radius: var(--radius);
      box-shadow: 0 8px 22px rgba(0, 0, 0, 0.28);
      cursor: pointer;
    }
    .bottom-bar__main--cart { background: var(--accent-strong); }
    /* The night theme's accent is a light blue: white on it washes out, so the
       raised button and the dial's current page take the night sky instead. */
    :root.dark .bottom-bar__main, :root.dark .page-dial__num.is-current { color: var(--bg); }
    .bottom-bar__main-count {
      position: absolute;
      top: -8px;
      right: -8px;
      min-width: 1.4rem;
      padding: 1px 5px;
      font-size: 0.75rem;
      font-weight: 700;
      line-height: 1.3;
      color: var(--accent-contrast);
      background: var(--danger);
      border-radius: var(--radius-sm);
    }

    /* Attention: something unread or ready to collect. A bell-and-count on the
       Menu slot, and while the bar is tucked the Menu rides a third of the
       tuck higher than its neighbours. Transform/opacity only. */
    .bottom-bar__badge {
      position: absolute;
      top: 2px;
      left: calc(50% + 4px);
      padding: 1px 5px;
      font-size: 0.65rem;
      font-weight: 700;
      white-space: nowrap;
      color: #fff;
      background: var(--danger);
      border-radius: var(--radius-sm);
    }
    .bottom-bar__menu.has-attention { color: var(--text); }
    .bottom-bar.is-tucked .bottom-bar__menu.has-attention {
      transform: translateY(calc(var(--bottom-bar-h) * var(--bar-tuck, 0.5) / -3));
    }
    @media (prefers-reduced-motion: no-preference) {
      .bottom-bar__menu.has-attention .bottom-bar__badge { animation: attention-nudge 2.4s ease-in-out infinite; }
    }

    /* ── Sheets (Near, Search, Menu) ── */
    .bottom-sheet {
      position: fixed;
      inset: 0;
      z-index: 130;
      display: flex;
      align-items: flex-end;
    }
    .bottom-sheet__backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.45); }
    .bottom-sheet__panel {
      position: relative;
      box-sizing: border-box;
      width: 100%;
      max-height: 80vh;
      overflow-y: auto;
      padding: 1rem 1rem calc(1rem + env(safe-area-inset-bottom));
      color: var(--text);
      background: var(--menu-bg);
      border-top: 1px solid var(--glass-border);
      border-radius: var(--radius-sm) var(--radius-sm) 0 0;
      box-shadow: var(--glass-shadow);
      backdrop-filter: blur(18px) saturate(1.4);
      -webkit-backdrop-filter: blur(18px) saturate(1.4);
    }
    @media (prefers-reduced-motion: no-preference) {
      .bottom-sheet__panel { animation: sheet-up 0.18s ease-out; }
    }
    .bottom-sheet__title { margin: 0 0 0.75rem; font-size: 1.1rem; }
    .bottom-sheet__actions { display: grid; gap: 0.6rem; }
    .bottom-sheet__actions .btn { width: 100%; justify-content: center; }
    .bottom-sheet__search { margin: 0; }
    .bottom-sheet__row {
      display: flex;
      align-items: center;
      gap: 0.75rem;
      box-sizing: border-box;
      width: 100%;
      min-height: 48px;
      padding: 0.25rem 0.5rem;
      font: inherit;
      text-align: left;
      color: var(--text);
      text-decoration: none;
      background: none;
      border: 0;
      border-radius: var(--radius-sm);
      cursor: pointer;
    }
    .bottom-sheet__count {
      margin-left: auto;
      padding: 1px 8px;
      font-size: 0.8rem;
      font-weight: 700;
      color: #fff;
      background: var(--danger);
      border-radius: var(--radius-sm);
    }
    .bottom-sheet__links {
      display: grid;
      gap: 0.25rem;
      margin: 0.5rem 0;
      padding-top: 0.5rem;
      border-top: 1px solid var(--glass-border);
    }
    .bottom-sheet__links > a:not(.btn) {
      display: flex;
      align-items: center;
      min-height: 48px;
      padding: 0 0.5rem;
      color: var(--text);
      text-decoration: none;
    }
    .bottom-sheet__links .btn, .bottom-sheet__links form { width: 100%; }
    .bottom-sheet__lang {
      padding: 0 0.4rem;
      font-size: 0.8rem;
      font-weight: 700;
      border: 1px solid var(--glass-border);
      border-radius: var(--radius-sm);
    }

    /* [hidden] must win over the display rules above (.btn/flex beat it). */
    .bottom-bar [hidden], .bottom-sheet[hidden], .bottom-sheet [hidden] { display: none !important; }

    /* ── Discover pages turned side to side (page_turner, tilt_pager) ── */
    .endless { overflow-x: clip; }
    /* A hold is a gesture here, not a request for the phone's image menu. */
    .endless .product-card { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
    @media (prefers-reduced-motion: no-preference) {
      .endless__page.is-leaving-left   { animation: page-slide-out-left 0.26s ease-in both; }
      .endless__page.is-leaving-right  { animation: page-slide-out-right 0.26s ease-in both; }
      .endless__page.is-entering-right { animation: page-slide-in-right 0.26s ease-out both; }
      .endless__page.is-entering-left  { animation: page-slide-in-left 0.26s ease-out both; }
      .endless__page.is-turning-out    { transform-origin: left center; animation: page-turn-out 0.42s ease-in both; }
      .endless__page.is-turning-in     { transform-origin: right center; animation: page-turn-in 0.42s ease-out both; }
    }
    /* Said once, when the pages go round the end. */
    .endless__note {
      position: fixed;
      left: 50%;
      top: 4.5rem;
      z-index: 115;
      transform: translateX(-50%);
      max-width: 86vw;
      margin: 0;
      padding: 0.55rem 0.9rem;
      font-weight: 600;
      text-align: center;
      text-wrap: balance;
      color: var(--text);
      background: var(--menu-bg);
      border: 1px solid var(--glass-border);
      border-radius: var(--radius-sm);
      box-shadow: var(--glass-shadow);
      pointer-events: none;
    }
    .endless__note[hidden] { display: none; }
    .endless__tilt-hint {
      position: fixed;
      left: 50%;
      bottom: calc(var(--bottom-bar-h) + 5.5rem + env(safe-area-inset-bottom));
      z-index: 115;
      transform: translateX(-50%);
      margin: 0;
      padding: 0.4rem 0.8rem;
      font-size: 0.85rem;
      font-weight: 600;
      white-space: nowrap;
      color: var(--text);
      background: var(--menu-bg);
      border: 1px solid var(--glass-border);
      border-radius: var(--radius-sm);
      box-shadow: var(--glass-shadow);
      pointer-events: none;
    }
    .endless__tilt-hint[hidden] { display: none; }

    /* ── Page dial: a mouse wheel on its side (page_dial_controller) ── */
    .page-dial {
      position: relative;
      width: min(52vw, 220px);
      height: 44px;
      touch-action: none;             /* horizontal drags spin the wheel */
      outline: none;
      border-radius: var(--radius-sm);
      background: linear-gradient(90deg, transparent, var(--menu-bg) 20%, var(--menu-bg) 80%, transparent);
    }
    .page-dial:focus-visible { box-shadow: 0 0 0 2px var(--accent); }
    .page-dial__wheel { position: absolute; inset: 0; overflow: hidden; }
    .page-dial__num, .page-dial__tick {
      position: absolute;
      top: 50%;
      left: 50%;
      will-change: transform, opacity;
    }
    .page-dial__num {
      width: 2.4rem;
      height: 2.4rem;
      margin: -1.2rem 0 0 -1.2rem;
      padding: 0;
      font: inherit;
      font-weight: 600;
      color: var(--text);
      background: none;
      border: 0;
      border-radius: var(--radius-sm);
      cursor: pointer;
    }
    .page-dial__num.is-current { color: var(--accent-contrast); background: var(--accent); }
    .page-dial__tick {
      width: 2px;
      height: 18px;
      margin: -9px 0 0 -1px;
      background: var(--text);
      border-radius: 1px;
    }
    /* Without JS: a plain row of page links. */
    .page-dial__fallback {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 0.25rem;
      height: 100%;
    }
    .page-dial__fallback .page-dial__num { position: static; margin: 0; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; }
    .page-dial [hidden] { display: none !important; }
  }

  @keyframes attention-nudge {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.15); }
  }
  @keyframes page-slide-out-left  { to { transform: translateX(-110%); opacity: 0; } }
  @keyframes page-slide-out-right { to { transform: translateX(110%); opacity: 0; } }
  @keyframes page-slide-in-right  { from { transform: translateX(110%); opacity: 0; } }
  @keyframes page-slide-in-left   { from { transform: translateX(-110%); opacity: 0; } }
  @keyframes page-turn-in {
    from { transform: perspective(1200px) rotateY(-95deg); opacity: 0; }
    60% { opacity: 1; }
    to { transform: perspective(1200px) rotateY(0); opacity: 1; }
  }
  @keyframes page-turn-out {
    to { transform: perspective(1200px) rotateY(95deg); opacity: 0; }
  }
  @keyframes sheet-up {
    from { transform: translateY(24px); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
  }

  /* Screen-reader-only text. */
  .visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }

  /* ── Buyer dashboard (/account): short sections, live orders first ── */
  .buyer-board {
    display: grid;
    gap: 1rem;
    max-width: 760px;
    margin-inline: auto;
  }
  .buyer-greeting { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.75rem; }
  .buyer-greeting__hello { margin: 0; font-size: clamp(1.5rem, 5vw, 2rem); overflow-wrap: anywhere; }
  .buyer-greeting__name summary { color: var(--accent); cursor: pointer; font-size: 0.9rem; }
  .buyer-greeting__form { display: flex; gap: 0.5rem; margin-top: 0.5rem; }
  .buyer-greeting__form input[type="text"] { flex: 1; min-width: 0; }

  .buyer-section { padding: 1rem; display: grid; gap: 0.75rem; }
  .buyer-section__head { display: flex; justify-content: space-between; align-items: baseline; gap: 0.5rem; }
  .buyer-section__title { margin: 0; font-size: 1.1rem; }

  /* A live order: id · shop · total, the steps, then its one action. */
  .live-order { display: grid; gap: 0.6rem; }
  .live-order + .live-order { border-top: 1px solid var(--glass-border); padding-top: 0.75rem; }
  .live-order__head { display: flex; flex-wrap: wrap; gap: 0.25rem 0.6rem; align-items: baseline; }
  .live-order__shop { flex: 1; min-width: 0; overflow-wrap: anywhere; }
  .live-order__total { font-weight: 600; }
  .live-order__off { margin: 0; color: var(--danger); }
  .live-order__action { display: flex; justify-content: flex-end; }

  .order-steps {
    --dot: 0.8rem;
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
  }
  .order-steps__step {
    position: relative;
    display: grid;
    justify-items: center;
    gap: 0.3rem;
    text-align: center;
    font-size: 0.78rem;
    color: var(--text-muted);
  }
  /* The line to the previous step, drawn under the dots. */
  .order-steps__step + .order-steps__step::before {
    content: "";
    position: absolute;
    top: calc(var(--dot) / 2 - 1px);
    right: 50%;
    width: 100%;
    height: 2px;
    background: var(--glass-border);
  }
  .order-steps__step.is-done::before,
  .order-steps__step.is-current::before { background: var(--success); }
  .order-steps__dot {
    position: relative;
    z-index: 1;
    width: var(--dot);
    height: var(--dot);
    border-radius: 50%;
    border: 2px solid var(--glass-border);
    background: var(--bg);
  }
  .order-steps__step.is-done { color: var(--text); }
  .order-steps__step.is-done .order-steps__dot { background: var(--success); border-color: var(--success); }
  .order-steps__step.is-current { color: var(--text); font-weight: 600; }
  .order-steps__step.is-current .order-steps__dot {
    border-color: var(--success);
    animation: step-pulse 1.8s ease-in-out infinite;
  }
  @keyframes step-pulse {
    50% { transform: scale(1.3); opacity: 0.7; }
  }
  @media (prefers-reduced-motion: reduce) {
    .order-steps__step.is-current .order-steps__dot { animation: none; }
  }

  /* Buy again: what and where on the left, the button on the right. */
  .buy-again { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
  .buy-again__row { display: flex; align-items: center; gap: 0.75rem; justify-content: space-between; }
  .buy-again__row + .buy-again__row { border-top: 1px solid var(--glass-border); padding-top: 0.6rem; }
  .buy-again__what { display: grid; min-width: 0; }
  .buy-again__items { color: var(--text-muted); font-size: 0.85rem; overflow-wrap: anywhere; }
  .buy-again__row form { flex: none; }

  /* This month vs the same days of last month. */
  .buyer-month__total { font-size: 1.4rem; font-weight: 600; }
  .buyer-month__vs { margin: -0.4rem 0 0; }
  .buyer-month__change.is-up { color: var(--danger); }
  .buyer-month__change.is-down { color: var(--success); }
  .month-bars { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }
  .month-bars__row {
    display: grid;
    grid-template-columns: minmax(5rem, 30%) 1fr auto;
    gap: 0.6rem;
    align-items: center;
    font-size: 0.9rem;
  }
  .month-bars__name { overflow-wrap: anywhere; }
  .month-bars__track { display: grid; gap: 3px; }
  .month-bars__bar {
    display: block;
    height: 0.5rem;
    width: var(--w);
    min-width: 2px;
    border-radius: 999px;
    background: var(--accent);
  }
  .month-bars__bar--last { background: var(--chart-track); }
  .month-bars__amount { text-align: right; white-space: nowrap; display: grid; }
  .month-bars__amount .muted { font-size: 0.75rem; }

  .buyer-section__more { justify-self: start; color: var(--accent); font-size: 0.9rem; }
  .buyer-links { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; justify-content: center; padding: 0.25rem 0 1rem; }
  .buyer-links a { color: var(--accent); }

  /* Star input: radios in reverse order so ~ can light every star below the
     one hovered or checked; visually left-to-right 1..5. */
  .rate-order { display: grid; gap: 0.75rem; }
  .star-input { border: 0; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0 0.75rem; }
  .star-input legend { float: left; padding: 0; white-space: nowrap; }
  .star-input__stars { display: inline-flex; flex-direction: row-reverse; margin-left: auto; }
  .star-input__stars input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
  }
  .star-input__stars label {
    font-size: 1.7rem;
    line-height: 1;
    padding: 0.2rem;
    min-width: 40px;
    min-height: 44px;
    display: grid;
    place-items: center;
    color: var(--glass-border);
    cursor: pointer;
  }
  .star-input__stars input:checked ~ label,
  .star-input__stars label:hover,
  .star-input__stars label:hover ~ label { color: var(--accent); }
  .star-input__stars input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
  .rate-order__comment { width: 100%; }

  @media (min-width: 901px) {
    .buyer-board { grid-template-columns: 1fr 1fr; max-width: 1040px; align-items: start; }
    .buyer-greeting, .buyer-links { grid-column: 1 / -1; }
  }

  /* ── Care "view as" banner (BO2) ──────────────────────────────────────
     Pinned above the bottom bar (phones) or the page foot (desktop) while a
     care agent views as a customer. Red-edged so it can't pass for the
     customer's own UI. */
  .impersonation-banner {
    position: fixed;
    inset-inline: 0.75rem;
    bottom: calc(var(--bottom-bar-h, 0px) + 1rem + env(safe-area-inset-bottom));
    width: fit-content;
    margin-inline: auto;
    z-index: 200;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.6rem 0.5rem 1rem;
    background: var(--menu-bg);
    border: 1px solid rgba(200, 60, 60, 0.7);
    border-radius: var(--radius-sm);
    backdrop-filter: var(--glass-filter);
    -webkit-backdrop-filter: var(--glass-filter);
    box-shadow: var(--glass-shadow);
    font-size: 0.9rem;
  }
  .impersonation-banner span { overflow-wrap: anywhere; }
  .impersonation-banner form { flex: none; }

  /* ── Back-office lists (BO2) ─────────────────────────────────────────
     Lookup hits and note/trail entries: plain stacked rows with a hairline
     between them — reads the same at 360px as on a desk. */
  .lookup-list, .note-list { list-style: none; margin: 0; padding: 0; }
  .lookup-list li, .note-list li {
    padding: 0.55rem 0;
    border-bottom: 1px solid var(--glass-border);
    overflow-wrap: anywhere;
  }
  .lookup-list li:last-child, .note-list li:last-child { border-bottom: 0; }
  .note-list p { margin: 0 0 0.2rem; }
}
