@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;
  }

  /* 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) {
    .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);
    }
  }
  .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); }

  .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::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; }

  /* ── 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;
  }

  .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 {
      width: 10rem;
      height: 10rem;
      margin-inline: auto;
    }

    .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;
    }
  }

  /* ── 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;
  }
  /* 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),
                box-shadow 0.25s ease;
  }
  /* 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 */
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5);
    /* 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;
      box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5);
      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);
    }
  }

  /* 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__carted { font-size: 0.85rem; color: var(--accent-strong); }
  .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 */
  }
  .product-card__name { font-weight: 600; }
  .product-card__price { color: var(--accent-strong); }
  /* 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);
    }
  }

  /* 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; }

  /* 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);
  }

  /* 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;
  }
}
