/* GENERATED — packages/design-tokens/src/{tokens.css,type.css,components.css,status.css,alias-landing.css} */

/* ==== tokens.css ==== */
/* ShopSmart Design Tokens v1.1 - 2026-07-29
 * ============================================================================
 * CANONICAL SOURCE OF TRUTH for every ShopSmart surface.
 *
 * Lines 1-151 of this file are ShopSmart_Brand_Book_Complete_Package_v1.1/
 * tokens/shopsmart-tokens.css, VERBATIM. Do not edit them. If the brand book
 * revises, re-copy that file and re-apply the extension block at the bottom.
 *
 * Consumed by:
 *   apps/portals  -> src/styles.css               (+ alias layer)
 *   apps/pos      -> src/styles.css               (+ alias layer)
 *   apps/kiosk    -> src/styles.css               (+ alias layer)
 *   landing/      -> landing/shopsmart-tokens.css (generated copy, see
 *                    packages/design-tokens/scripts/sync-landing.mjs)
 *
 * Surfaces must NOT redefine these names. They map their own legacy token
 * names onto these via an alias layer, so existing var(--legacy) call sites
 * keep working while resolving to brand-approved values.
 * ============================================================================
 */
:root {
  --blue-950: #061226;
  --blue-900: #0B1F3F;
  --blue-800: #102B5A;
  --blue-700: #1A3B7A;
  --blue-600: #2457C5;
  --blue-500: #3B78FF;
  --blue-400: #6A9BFF;
  --blue-300: #9BBAFF;
  --blue-200: #CFDCFF;
  --blue-100: #E3ECFF;
  --blue-50: #EEF4FF;
  --blue-25: #F6F9FF;
  --logo-navy: #0F3156;
  --logo-cyan: #298ED0;

  --surface-page: #F6F9FF;
  --surface-card: #FFFFFF;
  --surface-sunken: #EEF4FF;
  --surface-hover: #E3ECFF;
  --surface-active: #CFDCFF;
  --surface-raised: #FFFFFF;
  --text-1: #061226;
  --text-2: #102B5A;
  --text-3: #344969;
  --text-4: #55657C;
  --ink-strong: #061226;
  --on-accent: #061226;
  --line-1: #CFDCFF;
  --line-2: #9BBAFF;
  --line-strong: #6A9BFF;
  --accent: #3B78FF;
  --accent-dark: #2457C5;
  --accent-soft: #EEF4FF;
  --accent-ink: #2457C5;
  --accent-line: #9BBAFF;
  --focus-ring: #3B78FF;
  --ok: #107A4D;
  --ok-soft: #E9F8F0;
  --ok-line: #72C99D;
  --warn: #9A5700;
  --warn-soft: #FFF4E5;
  --warn-line: #F1B65B;
  --bad: #B42318;
  --bad-soft: #FFF0EF;
  --bad-line: #F29B95;
  --green: #0E7A47;
  --green-soft: #E9F8F0;
  --success: #107A4D;
  --success-soft: #E9F8F0;
  --success-line: #72C99D;
  --mall: #E8334A;
  --mall-soft: #FFF0F2;

  --text-xxs: 11px;
  --text-xs: 12px;
  --text-sm: 13px;
  --text-base: 14px;
  --text-md: 15px;
  --text-lg: 16px;
  --text-xl: 18px;
  --text-2xl: 20px;
  --text-3xl: 24px;
  --space-0: 0;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;
  --space-20: 80px;
  --radius-sm: 10px;
  --radius: 16px;
  --radius-lg: 22px;
  --radius-pill: 999px;
  --touch-sm: 32px;
  --touch-md: 44px;
  --touch-lg: 64px;
  --motion-flash: 200ms;
  --motion-toast: 200ms;
  --motion-modal: 250ms;
  --motion-drawer: 350ms;
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-enter: cubic-bezier(0, 0, 0, 1);
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);
  --shadow-sm: 0 2px 8px rgba(6,18,38,.08);
  --shadow: 0 10px 28px rgba(6,18,38,.12);
  --shadow-lg: 0 24px 64px rgba(6,18,38,.20);
  --font-display: "Bricolage Grotesque", "Arial Black", Arial, sans-serif;
  --font-body: "Plus Jakarta Sans", Arial, Helvetica, sans-serif;
}

html[data-theme="dark"] {
  --surface-page: #061226;
  --surface-card: #0B1F3F;
  --surface-sunken: #040B17;
  --surface-hover: #102B5A;
  --surface-active: #1A3B7A;
  --surface-raised: #102B5A;
  --text-1: #FFFFFF;
  --text-2: #E3ECFF;
  --text-3: #CFDCFF;
  --text-4: #9BBAFF;
  --ink-strong: #FFFFFF;
  --on-accent: #061226;
  --line-1: #1A3B7A;
  --line-2: #2457C5;
  --line-strong: #6A9BFF;
  --accent: #6A9BFF;
  --accent-dark: #3B78FF;
  --accent-soft: #102B5A;
  --accent-ink: #E3ECFF;
  --accent-line: #3B78FF;
  --focus-ring: #9BBAFF;
  --ok: #58D89B;
  --ok-soft: #0B3325;
  --ok-line: #2F8D65;
  --warn: #FFC56A;
  --warn-soft: #3A2809;
  --warn-line: #9A6B22;
  --bad: #FF8C86;
  --bad-soft: #3B1719;
  --bad-line: #A94A49;
  --green: #58D89B;
  --green-soft: #0B3325;
  --success: #6DE0A8;
  --success-soft: #0D3929;
  --success-line: #2F8D65;
  --mall: #FF7182;
  --mall-soft: #3D1720;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-flash: 1ms;
    --motion-toast: 1ms;
    --motion-modal: 1ms;
    --motion-drawer: 1ms;
  }
  *, *::before, *::after {
    scroll-behavior: auto !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}

/* ============================================================================
 * EXTENSION BLOCK — everything above this line is brand book verbatim.
 *
 * The brand book defines 96 tokens. The four product surfaces reference roles
 * it does not name: tooltips, backdrops, skeleton loaders, role accents,
 * font weights, control heights. Rather than let those keep resolving to
 * ad-hoc hexes, they are defined here and DERIVED FROM THE BRAND RAMP —
 * every colour below is either a brand-book value or a documented tint of one.
 * ============================================================================
 */
:root {
  /* --- Role accents -------------------------------------------------------
   * Secondary signal only, ~5% of surface (per portal spec). These are the
   * one sanctioned departure from blue-only: they disambiguate WHICH portal
   * you are in. Kept at AA on their soft backgrounds.
   */
  --role-admin: #4A3FC0;
  --role-admin-dark: #3A2F9E;
  --role-admin-soft: #EFEDFD;
  --role-merchant: #0A6E6E;
  --role-merchant-dark: #085757;
  --role-merchant-soft: #E6F7F7;
  --indigo: var(--role-admin);

  /* --- Informational (4th status family, alongside ok/warn/bad) ----------- */
  --info: var(--accent-dark);
  --info-soft: var(--blue-50);
  --info-line: var(--blue-300);

  /* --- Overlay chrome ----------------------------------------------------- */
  --tooltip-bg: var(--blue-950);
  --tooltip-text: var(--blue-50);
  --backdrop: rgba(6, 18, 38, 0.55);
  --glass: rgba(255, 255, 255, 0.82);
  --glass-line: rgba(255, 255, 255, 0.90);

  /* --- Skeleton loaders --------------------------------------------------- */
  --skeleton-from: var(--surface-sunken);
  --skeleton-to: var(--surface-hover);

  /* --- Extra elevation tiers referenced by portals ------------------------ */
  --shadow-xs: 0 1px 2px rgba(6, 18, 38, .06);
  --shadow-modal: 0 16px 48px rgba(6, 18, 38, .18);
  --shadow-save-bar: 0 -4px 24px rgba(6, 18, 38, .12);

  /* --- Type weights ------------------------------------------------------- */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --weight-display: 800;

  /* --- Line heights, paired to type-scale.csv ----------------------------- */
  --leading-xxs: 16px;
  --leading-xs: 18px;
  --leading-sm: 20px;
  --leading-base: 22px;
  --leading-md: 24px;
  --leading-lg: 24px;
  --leading-xl: 26px;
  --leading-2xl: 28px;
  --leading-3xl: 32px;

  /* --- Monospace (data/IDs/codes; not a brand display face) --------------- */
  --font-mono: ui-monospace, "JetBrains Mono", "DM Mono", SFMono-Regular, Menlo, monospace;

  /* --- Control geometry, driven by the 32/44/64 touch ramps --------------- */
  --control-h-sm: var(--touch-sm);
  --control-h: var(--touch-md);
  --control-h-lg: var(--touch-lg);
  --cta-height-desk: var(--touch-md);
  --cta-height-mob: var(--touch-md);
  --cta-height-kiosk: var(--touch-lg);
  --tile-h-desk: 32px;
  --tile-h-mob: 36px;
  --tile-h-kiosk: 56px;
  --drawer-w-desk: 340px;
  --modal-w-kiosk: 380px;

  /* --- Focus ring geometry (colour comes from --focus-ring) --------------- */
  --focus-ring-width: 2px;
  --focus-ring-offset: 2px;
  --focus-ring-soft: rgba(59, 120, 255, 0.35);

  /* --- Sale / Mall ribbon — DELIBERATE DEVIATION from the brand book ------
   * The brand book fixes --mall at #E8334A but its contrast-matrix.csv does
   * not cover the mall role, so the omission went unchecked. The colour ships
   * as `background: var(--mall); color: #fff` (group-order.css,
   * gift-request.css, checkout-social.css) — white on #E8334A is 4.20:1,
   * which fails AA for badge text.
   *
   * --mall below is that same colour scaled uniformly to 95% RGB: hue and
   * saturation are mathematically unchanged, it just clears 4.5:1.
   *   #E8334A -> white 4.20 FAIL      #DC3046 -> white 4.61 PASS
   *
   * --mall-ink is a separate step for the OTHER usage — the colour as text on
   * --mall-soft — which no single value can serve alongside a badge fill.
   *   --mall  on --mall-soft = 3.94 FAIL
   *   --mall-ink on --mall-soft = 4.73 PASS
   *
   * FLAG FOR THE BRAND DESIGNER: ratify or supply an accessible sale red.
   */
  --mall: #DC3046;
  --mall-ink: #CC2D41;
  --on-mall: #FFFFFF;
}

html[data-theme="dark"] {
  --role-admin: #A99FFF;
  --role-admin-dark: #8B7EF5;
  --role-admin-soft: #211C4A;
  --role-merchant: #5FD5D5;
  --role-merchant-dark: #3FBABA;
  --role-merchant-soft: #0B3333;

  --info: var(--accent);
  --info-soft: #102B5A;
  --info-line: #2457C5;

  --tooltip-bg: #1A3B7A;
  --tooltip-text: #E3ECFF;
  --backdrop: rgba(4, 11, 23, 0.70);
  --glass: rgba(11, 31, 63, 0.82);
  --glass-line: rgba(26, 59, 122, 0.90);

  /* The brand book leaves --shadow-* at their light values in dark mode.
   * Ink-on-ink shadows are invisible there, so the dark tier is re-based on
   * true black at higher opacity. Same geometry, so elevation stays identical. */
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, .35);
  --shadow: 0 10px 28px rgba(0, 0, 0, .45);
  --shadow-lg: 0 24px 64px rgba(0, 0, 0, .60);
  --shadow-xs: 0 1px 2px rgba(0, 0, 0, .30);
  --shadow-modal: 0 16px 48px rgba(0, 0, 0, .55);
  --shadow-save-bar: 0 -4px 24px rgba(0, 0, 0, .45);

  --focus-ring-soft: rgba(155, 186, 255, 0.45);

  /* Dark: the ribbon inverts — light-red fill takes dark ink. */
  --mall-ink: #FF7182;
  --on-mall: #061226;
}


/* ==== type.css ==== */
/* ShopSmart typography layer — Brand Book v1.1
 * ============================================================================
 * Two families, no exceptions:
 *   --font-display  Bricolage Grotesque  — page/section titles, prices, numerals
 *   --font-body     Plus Jakarta Sans    — everything else
 *
 * Replaces the six families measured across the repo (Manrope, DM Sans,
 * Figtree, Sora, Inter, Clash Display). --font-mono is a data face for IDs,
 * SKUs and codes; it is not a brand display face.
 *
 * Size/leading/weight pairs are implementation/type-scale.csv verbatim.
 * ============================================================================
 */

/* Self-hostable via the surface's own @font-face or a Google Fonts <link>.
 * Surfaces must request ONLY these two families with these weights:
 *   Bricolage Grotesque : 500;600;700;800
 *   Plus Jakarta Sans   : 400;500;600;700;800
 */

.ss-type-display { font-family: var(--font-display); }
.ss-type-body    { font-family: var(--font-body); }
.ss-type-mono    { font-family: var(--font-mono); font-variant-ligatures: none; }

/* --- The nine steps, per type-scale.csv ---------------------------------- */
.ss-t-xxs  { font: var(--weight-semibold) var(--text-xxs)/var(--leading-xxs)   var(--font-body); }
.ss-t-xs   { font: var(--weight-medium)   var(--text-xs)/var(--leading-xs)     var(--font-body); }
.ss-t-sm   { font: var(--weight-medium)   var(--text-sm)/var(--leading-sm)     var(--font-body); }
.ss-t-base { font: var(--weight-regular)  var(--text-base)/var(--leading-base) var(--font-body); }
.ss-t-md   { font: var(--weight-medium)   var(--text-md)/var(--leading-md)     var(--font-body); }
.ss-t-lg   { font: var(--weight-semibold) var(--text-lg)/var(--leading-lg)     var(--font-body); }
.ss-t-xl   { font: var(--weight-semibold) var(--text-xl)/var(--leading-xl)     var(--font-body); }
.ss-t-2xl  { font: var(--weight-bold)     var(--text-2xl)/var(--leading-2xl)   var(--font-display); }
.ss-t-3xl  { font: var(--weight-bold)     var(--text-3xl)/var(--leading-3xl)   var(--font-display); }

/* --- Role shorthands ------------------------------------------------------ */
.ss-page-title    { font: var(--weight-bold) var(--text-3xl)/var(--leading-3xl) var(--font-display); color: var(--text-1); letter-spacing: -0.02em; }
.ss-page-subhead  { font: var(--weight-bold) var(--text-2xl)/var(--leading-2xl) var(--font-display); color: var(--text-1); letter-spacing: -0.01em; }
.ss-section-title { font: var(--weight-semibold) var(--text-xl)/var(--leading-xl) var(--font-body); color: var(--text-1); }
.ss-card-title    { font: var(--weight-semibold) var(--text-lg)/var(--leading-lg) var(--font-body); color: var(--text-1); }
.ss-body          { font: var(--weight-regular) var(--text-base)/var(--leading-base) var(--font-body); color: var(--text-2); }
.ss-help          { font: var(--weight-medium) var(--text-xs)/var(--leading-xs) var(--font-body); color: var(--text-3); }
.ss-meta          { font: var(--weight-semibold) var(--text-xxs)/var(--leading-xxs) var(--font-body); color: var(--text-3); letter-spacing: .04em; text-transform: uppercase; }

/* Price uses the display face and tabular figures so columns align. */
.ss-price {
  font: var(--weight-bold) var(--text-xl)/var(--leading-xl) var(--font-display);
  color: var(--text-1);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.ss-numeric { font-variant-numeric: tabular-nums; }

/* --- Muted text floor -----------------------------------------------------
 * --text-4 is the most muted role that still clears AA on card (5.93:1).
 * There is deliberately no step below it. Anything dimmer fails.
 */
.ss-muted { color: var(--text-4); }


/* ==== components.css ==== */
/* ShopSmart component layer — Brand Book v1.1
 * ============================================================================
 * Implements implementation/component-inventory.csv in full:
 *
 *   Button      primary|secondary|ghost|destructive|icon-only
 *   Input       text|search|password|phone|money
 *   Selection   checkbox|radio|switch|segmented|chip
 *   Card        product|order|merchant|stat|KPI
 *   Data table  dense|comfortable|sticky|bulk-selection
 *   Overlay     modal|drawer|bottom-sheet|popover|tooltip
 *   Feedback    toast|inline-alert|banner|empty|skeleton|error-page
 *   Status      status-pill|timeline|stepper   (see status.css)
 *
 * Every component carries its full required state set and the 32/44/64 touch
 * ramps. Colour comes exclusively from tokens.css, so light/dark is automatic.
 *
 * Sizing convention, uniform across every component:
 *   .ss-sm -> 32px   (desktop secondary)
 *   default -> 44px  (desktop primary, all mobile — WCAG target floor)
 *   .ss-lg -> 64px   (kiosk)
 * ============================================================================
 */

/* --- Shared focus treatment ----------------------------------------------
 * focus-visible only, so pointer users never see a ring but keyboard users
 * always do. Offset keeps it clear of the control's own border.
 */
.ss-btn:focus-visible,
.ss-input:focus-visible,
.ss-check:focus-visible + .ss-check-box,
.ss-radio:focus-visible + .ss-radio-box,
.ss-switch:focus-visible + .ss-switch-track,
.ss-seg-item:focus-visible,
.ss-chip:focus-visible,
.ss-card[tabindex]:focus-visible,
.ss-table tbody tr[tabindex]:focus-visible,
.ss-toast-close:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
}

/* =========================================================================
   BUTTON
   variants: primary | secondary | ghost | destructive | icon-only
   states:   default hover focus-visible active disabled loading error
   ========================================================================= */
.ss-btn {
  --ss-ctl-h: var(--touch-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--ss-ctl-h);
  padding: 0 var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font: var(--weight-semibold) var(--text-base)/1 var(--font-body);
  cursor: pointer;
  position: relative;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--motion-flash) var(--ease-standard),
              border-color var(--motion-flash) var(--ease-standard),
              color var(--motion-flash) var(--ease-standard),
              box-shadow var(--motion-flash) var(--ease-standard);
}
.ss-btn.ss-sm { --ss-ctl-h: var(--touch-sm); padding: 0 var(--space-3); font-size: var(--text-sm); }
.ss-btn.ss-lg { --ss-ctl-h: var(--touch-lg); padding: 0 var(--space-6); font-size: var(--text-lg); border-radius: var(--radius); }
.ss-btn.ss-block { display: flex; width: 100%; }

/* primary — on-accent is #061226, NOT white: white on --accent is 3.95:1 and
 * fails AA for normal text. This is the contrast-matrix exception. */
.ss-btn-primary { background: var(--accent); color: var(--on-accent); }
.ss-btn-primary:hover:not(:disabled):not([aria-busy="true"]) { background: var(--accent-dark); color: #FFFFFF; }
.ss-btn-primary:active:not(:disabled) { background: var(--accent-dark); color: #FFFFFF; transform: translateY(1px); }

.ss-btn-secondary { background: var(--surface-card); color: var(--accent-ink); border-color: var(--accent-line); }
.ss-btn-secondary:hover:not(:disabled):not([aria-busy="true"]) { background: var(--accent-soft); border-color: var(--line-strong); }
.ss-btn-secondary:active:not(:disabled) { background: var(--surface-active); transform: translateY(1px); }

.ss-btn-ghost { background: transparent; color: var(--accent-ink); }
.ss-btn-ghost:hover:not(:disabled):not([aria-busy="true"]) { background: var(--surface-hover); }
.ss-btn-ghost:active:not(:disabled) { background: var(--surface-active); transform: translateY(1px); }

.ss-btn-destructive { background: var(--bad); color: #FFFFFF; }
.ss-btn-destructive:hover:not(:disabled):not([aria-busy="true"]) { filter: brightness(0.90); }
.ss-btn-destructive:active:not(:disabled) { filter: brightness(0.82); transform: translateY(1px); }

.ss-btn-icon {
  padding: 0;
  width: var(--ss-ctl-h);
  min-width: var(--ss-ctl-h);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-2);
}
.ss-btn-icon:hover:not(:disabled) { background: var(--surface-hover); color: var(--text-1); }
.ss-btn-icon.ss-lg { border-radius: var(--radius); }

/* disabled — never conveyed by colour alone; cursor + aria-disabled carry it */
.ss-btn:disabled,
.ss-btn[aria-disabled="true"] {
  background: var(--surface-active);
  color: var(--text-4);
  border-color: transparent;
  cursor: not-allowed;
  transform: none;
  filter: none;
}

/* loading — aria-busy is the source of truth so SRs announce it too */
.ss-btn[aria-busy="true"] { color: transparent; pointer-events: none; }
.ss-btn[aria-busy="true"]::after {
  content: "";
  position: absolute;
  width: 1em; height: 1em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: var(--radius-pill);
  animation: ss-spin 640ms linear infinite;
  color: var(--on-accent);
}
.ss-btn-secondary[aria-busy="true"]::after,
.ss-btn-ghost[aria-busy="true"]::after { color: var(--accent-ink); }
.ss-btn-destructive[aria-busy="true"]::after { color: #FFFFFF; }
@keyframes ss-spin { to { transform: rotate(360deg); } }

/* error — a button that just failed its action */
.ss-btn[data-state="error"] {
  background: var(--bad-soft);
  color: var(--bad);
  border-color: var(--bad-line);
}

/* =========================================================================
   INPUT
   variants: text | search | password | phone | money
   states:   default hover focus-visible disabled filled error loading
   ========================================================================= */
.ss-field { display: flex; flex-direction: column; gap: var(--space-1); }
.ss-label {
  font: var(--weight-medium) var(--text-sm)/var(--leading-sm) var(--font-body);
  color: var(--text-2);
}
.ss-label[data-required]::after { content: " *"; color: var(--bad); }

.ss-input-wrap { position: relative; display: flex; align-items: center; }

.ss-input {
  --ss-ctl-h: var(--touch-md);
  width: 100%;
  min-height: var(--ss-ctl-h);
  padding: 0 var(--space-3);
  background: var(--surface-card);
  color: var(--text-1);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-sm);
  font: var(--weight-regular) var(--text-base)/1.4 var(--font-body);
  transition: border-color var(--motion-flash) var(--ease-standard),
              background-color var(--motion-flash) var(--ease-standard);
}
.ss-input.ss-sm { --ss-ctl-h: var(--touch-sm); font-size: var(--text-sm); }
.ss-input.ss-lg { --ss-ctl-h: var(--touch-lg); font-size: var(--text-lg); padding: 0 var(--space-4); border-radius: var(--radius); }
.ss-input::placeholder { color: var(--text-4); }
.ss-input:hover:not(:disabled) { border-color: var(--line-strong); }
.ss-input:focus-visible { border-color: var(--accent); }
.ss-input:disabled { background: var(--surface-sunken); color: var(--text-4); cursor: not-allowed; }
.ss-input:not(:placeholder-shown) { background: var(--surface-card); }

/* error — border + tinted background + the message below; three signals, so
 * this never depends on hue alone */
.ss-input[aria-invalid="true"] { border-color: var(--bad); background: var(--bad-soft); }
.ss-error-text {
  display: flex; align-items: center; gap: var(--space-1);
  font: var(--weight-medium) var(--text-xs)/var(--leading-xs) var(--font-body);
  color: var(--bad);
}
.ss-error-text::before { content: "⚠"; font-size: 1.1em; line-height: 1; }
.ss-help-text {
  font: var(--weight-medium) var(--text-xs)/var(--leading-xs) var(--font-body);
  color: var(--text-3);
}

/* loading (async validation / lookup) */
.ss-input-wrap[aria-busy="true"]::after {
  content: "";
  position: absolute; right: var(--space-3);
  width: 14px; height: 14px;
  border: 2px solid var(--line-strong);
  border-top-color: var(--accent);
  border-radius: var(--radius-pill);
  animation: ss-spin 640ms linear infinite;
}

/* affordances — leading/trailing slots */
.ss-input-wrap > .ss-input-lead,
.ss-input-wrap > .ss-input-trail {
  position: absolute;
  display: flex; align-items: center;
  color: var(--text-3);
  pointer-events: none;
}
.ss-input-lead  { left: var(--space-3); }
.ss-input-trail { right: var(--space-3); }

/* Prefix width varies by content ("₱" vs "+63"), and CSS cannot measure it, so
 * it is a variable with a sane default rather than one number that is wrong
 * half the time. 40px fits a single glyph; override for longer prefixes. */
.ss-input-wrap { --ss-lead-w: var(--space-10); }
.ss-input-wrap:has(> .ss-input-phone) { --ss-lead-w: 56px; }   /* "+63" + gap */
.ss-input-wrap:has(> .ss-input-lead)  .ss-input { padding-left: var(--ss-lead-w); }
.ss-input-wrap:has(> .ss-input-trail) .ss-input { padding-right: var(--space-8); }

/* variant: search */
.ss-input-search { border-radius: var(--radius-pill); }
/* variant: password — trailing reveal toggle is interactive */
.ss-input-wrap > .ss-input-trail.ss-interactive { pointer-events: auto; cursor: pointer; }
/* variant: phone + money — tabular figures so digits don't jitter while typing */
.ss-input-phone,
.ss-input-money { font-variant-numeric: tabular-nums; }
/* money — currency sits in the leading slot, value right-aligns */
.ss-input-money { text-align: right; }

/* =========================================================================
   SELECTION
   variants: checkbox | radio | switch | segmented | chip
   states:   default hover focus-visible selected disabled error
   ========================================================================= */
.ss-choice {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--touch-md);
  cursor: pointer;
  font: var(--weight-regular) var(--text-base)/1.4 var(--font-body);
  color: var(--text-1);
}
.ss-choice.ss-sm { min-height: var(--touch-sm); font-size: var(--text-sm); }
.ss-choice.ss-lg { min-height: var(--touch-lg); font-size: var(--text-lg); }

.ss-check, .ss-radio, .ss-switch {
  position: absolute; opacity: 0; width: 0; height: 0;
}
.ss-check-box, .ss-radio-box {
  display: grid; place-items: center;
  width: 20px; height: 20px;
  flex: none;
  background: var(--surface-card);
  border: 1.5px solid var(--line-strong);
  transition: background-color var(--motion-flash) var(--ease-standard),
              border-color var(--motion-flash) var(--ease-standard);
}
.ss-check-box { border-radius: 6px; }
.ss-radio-box { border-radius: var(--radius-pill); }
.ss-lg .ss-check-box, .ss-lg .ss-radio-box { width: 28px; height: 28px; }

.ss-choice:hover .ss-check-box,
.ss-choice:hover .ss-radio-box { border-color: var(--accent); }

/* selected — glyph, not just fill, so the state survives greyscale */
.ss-check:checked + .ss-check-box { background: var(--accent); border-color: var(--accent); }
.ss-check:checked + .ss-check-box::after {
  content: ""; width: 5px; height: 9px;
  border: solid var(--on-accent); border-width: 0 2px 2px 0;
  transform: rotate(45deg) translate(-1px, -1px);
}
.ss-check:indeterminate + .ss-check-box { background: var(--accent); border-color: var(--accent); }
.ss-check:indeterminate + .ss-check-box::after {
  content: ""; width: 9px; height: 2px; background: var(--on-accent); border-radius: 1px;
}
.ss-radio:checked + .ss-radio-box { border-color: var(--accent); border-width: 2px; }
.ss-radio:checked + .ss-radio-box::after {
  content: ""; width: 10px; height: 10px; border-radius: var(--radius-pill); background: var(--accent);
}

.ss-check:disabled + .ss-check-box,
.ss-radio:disabled + .ss-radio-box { background: var(--surface-active); border-color: var(--line-1); cursor: not-allowed; }
.ss-check[aria-invalid="true"] + .ss-check-box,
.ss-radio[aria-invalid="true"] + .ss-radio-box { border-color: var(--bad); }

/* switch */
.ss-switch-track {
  position: relative;
  width: 44px; height: 26px;
  flex: none;
  background: var(--surface-active);
  border-radius: var(--radius-pill);
  transition: background-color var(--motion-flash) var(--ease-standard);
}
.ss-switch-track::after {
  content: "";
  position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px;
  background: var(--surface-card);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-sm);
  transition: transform var(--motion-flash) var(--ease-standard);
}
.ss-switch:checked + .ss-switch-track { background: var(--accent); }
.ss-switch:checked + .ss-switch-track::after { transform: translateX(18px); }
.ss-switch:disabled + .ss-switch-track { opacity: .5; cursor: not-allowed; }
.ss-lg .ss-switch-track { width: 60px; height: 34px; }
.ss-lg .ss-switch-track::after { width: 28px; height: 28px; }
.ss-lg .ss-switch:checked + .ss-switch-track::after { transform: translateX(26px); }

/* segmented */
.ss-seg {
  display: inline-flex;
  padding: 3px;
  background: var(--surface-sunken);
  border: 1px solid var(--line-1);
  border-radius: var(--radius-sm);
  gap: 2px;
}
.ss-seg-item {
  --ss-ctl-h: var(--touch-sm);
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--ss-ctl-h);
  padding: 0 var(--space-3);
  border: 0; background: transparent;
  border-radius: 7px;
  font: var(--weight-medium) var(--text-sm)/1 var(--font-body);
  color: var(--text-3);
  cursor: pointer;
  transition: background-color var(--motion-flash) var(--ease-standard),
              color var(--motion-flash) var(--ease-standard);
}
.ss-seg-item:hover:not([disabled]) { color: var(--text-1); }
.ss-seg-item[aria-selected="true"],
.ss-seg-item[aria-pressed="true"] {
  background: var(--surface-card);
  color: var(--accent-ink);
  font-weight: var(--weight-semibold);
  box-shadow: var(--shadow-xs);
}
.ss-seg-item[disabled] { color: var(--text-4); cursor: not-allowed; }
.ss-seg.ss-lg .ss-seg-item { --ss-ctl-h: var(--touch-lg); font-size: var(--text-lg); padding: 0 var(--space-5); }

/* chip */
.ss-chip {
  --ss-ctl-h: var(--touch-sm);
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--ss-ctl-h);
  padding: 0 var(--space-3);
  background: var(--surface-card);
  color: var(--text-2);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-pill);
  font: var(--weight-medium) var(--text-sm)/1 var(--font-body);
  cursor: pointer;
}
.ss-chip:hover:not([disabled]) { background: var(--surface-hover); border-color: var(--line-strong); }
.ss-chip[aria-selected="true"] {
  background: var(--accent-soft);
  color: var(--accent-ink);
  border-color: var(--accent);
  font-weight: var(--weight-semibold);
}
/* selected chips also get a check glyph — colour is never the only signal */
.ss-chip[aria-selected="true"]::before { content: "✓"; font-size: .9em; }
.ss-chip[disabled] { color: var(--text-4); cursor: not-allowed; background: var(--surface-sunken); }
.ss-chip[aria-invalid="true"] { border-color: var(--bad); color: var(--bad); }
.ss-chip.ss-md { --ss-ctl-h: var(--touch-md); }
.ss-chip.ss-lg { --ss-ctl-h: var(--touch-lg); font-size: var(--text-lg); padding: 0 var(--space-5); }

/* =========================================================================
   CARD
   variants: product | order | merchant | stat | KPI
   states:   default hover focus-visible selected loading error
   ========================================================================= */
.ss-card {
  display: flex;
  flex-direction: column;
  background: var(--surface-card);
  border: 1px solid var(--line-1);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color var(--motion-flash) var(--ease-standard),
              box-shadow var(--motion-flash) var(--ease-standard),
              transform var(--motion-flash) var(--ease-standard);
}
.ss-card-interactive { cursor: pointer; }
.ss-card-interactive:hover { border-color: var(--line-strong); box-shadow: var(--shadow); transform: translateY(-2px); }
.ss-card[aria-selected="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.ss-card[data-state="error"] { border-color: var(--bad-line); background: var(--bad-soft); }
.ss-card-body { padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }
.ss-card-pad-lg .ss-card-body { padding: var(--space-6); }

/* product */
.ss-card-product .ss-card-media { aspect-ratio: 1; background: var(--surface-sunken); overflow: hidden; }
.ss-card-product .ss-card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ss-card-product .ss-card-title {
  font: var(--weight-medium) var(--text-base)/var(--leading-base) var(--font-body);
  color: var(--text-1);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  line-clamp: 2; overflow: hidden;
}

/* order / merchant — row-shaped cards */
.ss-card-order .ss-card-body,
.ss-card-merchant .ss-card-body { flex-direction: row; align-items: center; gap: var(--space-4); }

/* stat / KPI — the number is the hero, so it takes the display face */
.ss-card-stat .ss-card-body,
.ss-card-kpi .ss-card-body { gap: var(--space-1); }
.ss-stat-label {
  font: var(--weight-semibold) var(--text-xxs)/var(--leading-xxs) var(--font-body);
  color: var(--text-3);
  letter-spacing: .04em;
  text-transform: uppercase;
}
.ss-stat-value {
  font: var(--weight-bold) var(--text-3xl)/var(--leading-3xl) var(--font-display);
  color: var(--text-1);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
/* deltas pair an arrow with the colour, so direction survives greyscale */
.ss-stat-delta { display: inline-flex; align-items: center; gap: var(--space-1); font: var(--weight-semibold) var(--text-xs)/1 var(--font-body); }
.ss-stat-delta[data-dir="up"]   { color: var(--ok); }
.ss-stat-delta[data-dir="down"] { color: var(--bad); }
.ss-stat-delta[data-dir="flat"] { color: var(--text-3); }
.ss-stat-delta[data-dir="up"]::before   { content: "▲"; font-size: .85em; }
.ss-stat-delta[data-dir="down"]::before { content: "▼"; font-size: .85em; }
.ss-stat-delta[data-dir="flat"]::before { content: "—"; }

/* loading */
.ss-card[aria-busy="true"] .ss-card-body > * { visibility: hidden; }
.ss-card[aria-busy="true"] .ss-card-body { position: relative; min-height: 96px; }
.ss-card[aria-busy="true"] .ss-card-body::after {
  content: ""; position: absolute; inset: var(--space-4);
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--skeleton-from) 25%, var(--skeleton-to) 37%, var(--skeleton-from) 63%);
  background-size: 400% 100%;
  animation: ss-shimmer 1400ms ease-in-out infinite;
}

/* =========================================================================
   DATA TABLE
   variants: dense | comfortable | sticky | bulk-selection
   states:   default hover focus-visible selected loading empty error
   ========================================================================= */
.ss-table-wrap { width: 100%; overflow-x: auto; border: 1px solid var(--line-1); border-radius: var(--radius); background: var(--surface-card); }
.ss-table { width: 100%; border-collapse: separate; border-spacing: 0; font-family: var(--font-body); }
.ss-table th {
  text-align: left;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-sunken);
  color: var(--text-3);
  font: var(--weight-semibold) var(--text-xxs)/var(--leading-xxs) var(--font-body);
  letter-spacing: .04em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--line-1);
  white-space: nowrap;
}
.ss-table td {
  padding: var(--space-3) var(--space-4);
  color: var(--text-2);
  font: var(--weight-regular) var(--text-sm)/var(--leading-sm) var(--font-body);
  border-bottom: 1px solid var(--line-2);
}
.ss-table tbody tr:last-child td { border-bottom: 0; }
.ss-table tbody tr:hover td { background: var(--surface-hover); }
.ss-table tbody tr[aria-selected="true"] td { background: var(--accent-soft); }
.ss-table .ss-num { text-align: right; font-variant-numeric: tabular-nums; }

/* dense (32px rows) vs comfortable (44px rows) */
.ss-table-dense td, .ss-table-dense th { padding: var(--space-2) var(--space-3); height: var(--touch-sm); }
.ss-table-comfortable td, .ss-table-comfortable th { padding: var(--space-3) var(--space-4); height: var(--touch-md); }

/* sticky header */
.ss-table-sticky thead th { position: sticky; top: 0; z-index: 2; }

/* bulk-selection */
.ss-table-bulk td:first-child, .ss-table-bulk th:first-child { width: 44px; padding-right: 0; }
.ss-bulk-bar {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--accent-soft);
  border-bottom: 1px solid var(--accent-line);
  color: var(--accent-ink);
  font: var(--weight-semibold) var(--text-sm)/1 var(--font-body);
}

/* loading / empty / error */
.ss-table-state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-2);
  padding: var(--space-12) var(--space-4);
  text-align: center;
}
/* grid-safe: an empty state dropped into a grid parent must span all columns */
.ss-table-state { grid-column: 1 / -1; }
.ss-table-state-title { font: var(--weight-semibold) var(--text-lg)/var(--leading-lg) var(--font-body); color: var(--text-2); }
.ss-table-state-body { font: var(--weight-regular) var(--text-base)/var(--leading-base) var(--font-body); color: var(--text-3); max-width: 42ch; }

/* =========================================================================
   OVERLAY
   variants: modal | drawer | bottom-sheet | popover | tooltip
   states:   enter | open | focus-trap | exit | loading | error
   ========================================================================= */
.ss-backdrop {
  position: fixed; inset: 0; z-index: 1000;
  background: var(--backdrop);
  animation: ss-fade-in var(--motion-modal) var(--ease-enter);
}
.ss-backdrop[data-state="exit"] { animation: ss-fade-out var(--motion-modal) var(--ease-exit) forwards; }

.ss-modal {
  position: fixed; z-index: 1001;
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(560px, calc(100vw - var(--space-8)));
  max-height: calc(100vh - var(--space-16));
  display: flex; flex-direction: column;
  background: var(--surface-raised);
  border: 1px solid var(--line-1);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
  animation: ss-modal-in var(--motion-modal) var(--ease-enter);
}
.ss-drawer {
  position: fixed; z-index: 1001;
  top: 0; right: 0; bottom: 0;
  width: min(var(--drawer-w-desk), 100vw);
  display: flex; flex-direction: column;
  background: var(--surface-raised);
  border-left: 1px solid var(--line-1);
  box-shadow: var(--shadow-lg);
  animation: ss-drawer-in var(--motion-drawer) var(--ease-enter);
}
.ss-sheet {
  position: fixed; z-index: 1001;
  left: 0; right: 0; bottom: 0;
  max-height: 90vh;
  display: flex; flex-direction: column;
  background: var(--surface-raised);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-lg);
  padding-bottom: env(safe-area-inset-bottom);
  animation: ss-sheet-in var(--motion-drawer) var(--ease-enter);
}
.ss-sheet::before {
  content: ""; width: 36px; height: 4px; flex: none;
  margin: var(--space-3) auto var(--space-1);
  background: var(--line-strong); border-radius: var(--radius-pill);
}
.ss-popover {
  position: absolute; z-index: 1002;
  min-width: 180px;
  background: var(--surface-raised);
  border: 1px solid var(--line-1);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  padding: var(--space-2);
  animation: ss-fade-in var(--motion-flash) var(--ease-enter);
}
.ss-tooltip {
  position: absolute; z-index: 1003;
  max-width: 28ch;
  padding: var(--space-2) var(--space-3);
  background: var(--tooltip-bg);
  color: var(--tooltip-text);
  border-radius: var(--radius-sm);
  font: var(--weight-medium) var(--text-xs)/var(--leading-xs) var(--font-body);
  box-shadow: var(--shadow-sm);
  pointer-events: none;
  animation: ss-fade-in var(--motion-flash) var(--ease-enter);
}

.ss-overlay-head {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-5) var(--space-6);
  border-bottom: 1px solid var(--line-1);
  flex: none;
}
.ss-overlay-title { font: var(--weight-bold) var(--text-2xl)/var(--leading-2xl) var(--font-display); color: var(--text-1); margin: 0; }
.ss-overlay-body { padding: var(--space-6); overflow-y: auto; flex: 1; color: var(--text-2); }
.ss-overlay-foot {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--line-1);
  flex: none;
}
/* kiosk ramp — overlays get the 64px controls */
.ss-overlay-lg .ss-btn { --ss-ctl-h: var(--touch-lg); font-size: var(--text-lg); }
.ss-modal.ss-overlay-lg { width: min(var(--modal-w-kiosk), calc(100vw - var(--space-8))); }

/* exit states */
.ss-modal[data-state="exit"]  { animation: ss-modal-out var(--motion-modal) var(--ease-exit) forwards; }
.ss-drawer[data-state="exit"] { animation: ss-drawer-out var(--motion-drawer) var(--ease-exit) forwards; }
.ss-sheet[data-state="exit"]  { animation: ss-sheet-out var(--motion-drawer) var(--ease-exit) forwards; }

/* loading / error inside an overlay */
.ss-overlay-body[aria-busy="true"] { min-height: 140px; position: relative; }
.ss-overlay-body[aria-busy="true"]::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 28px; height: 28px; margin: -14px 0 0 -14px;
  border: 3px solid var(--line-1); border-top-color: var(--accent);
  border-radius: var(--radius-pill);
  animation: ss-spin 640ms linear infinite;
}

/* focus-trap sentinels — zero-size, focusable, invisible */
.ss-focus-sentinel { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* body scroll lock companion. overscroll-behavior belongs on <html>, not
 * <body> — on body it silently does nothing in iOS Safari. */
html.ss-scroll-locked { overflow: hidden; overscroll-behavior: none; }

/* =========================================================================
   FEEDBACK
   variants: toast | inline-alert | banner | empty | skeleton | error-page
   tones:    info | success | warning | danger | loading
   ========================================================================= */
.ss-alert {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid;
  border-radius: var(--radius-sm);
  font: var(--weight-medium) var(--text-base)/var(--leading-base) var(--font-body);
}
/* Each tone carries a glyph as well as a hue — required non-colour signal. */
.ss-alert::before { font-size: 1.05em; line-height: 1.35; flex: none; }
.ss-alert-info    { background: var(--info-soft); border-color: var(--info-line); color: var(--info); }
.ss-alert-info::before { content: "ℹ"; }
.ss-alert-success { background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok); }
.ss-alert-success::before { content: "✓"; }
.ss-alert-warning { background: var(--warn-soft); border-color: var(--warn-line); color: var(--warn); }
.ss-alert-warning::before { content: "⚠"; }
.ss-alert-danger  { background: var(--bad-soft); border-color: var(--bad-line); color: var(--bad); }
.ss-alert-danger::before { content: "✕"; }
.ss-alert-loading { background: var(--surface-sunken); border-color: var(--line-1); color: var(--text-2); }
.ss-alert-loading::before {
  content: ""; width: 14px; height: 14px; margin-top: 3px;
  border: 2px solid var(--line-strong); border-top-color: var(--accent);
  border-radius: var(--radius-pill);
  animation: ss-spin 640ms linear infinite;
}
.ss-alert-title { font-weight: var(--weight-semibold); }
.ss-alert-body  { color: inherit; opacity: .92; font-weight: var(--weight-regular); }

/* banner — full-bleed, squared, page-level */
.ss-banner { border-radius: 0; border-left: 0; border-right: 0; padding: var(--space-3) var(--space-6); }

/* toast */
.ss-toast-region {
  position: fixed; z-index: 1100;
  right: var(--space-4); bottom: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-2);
  max-width: min(420px, calc(100vw - var(--space-8)));
  pointer-events: none;
}
.ss-toast {
  pointer-events: auto;
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-raised);
  border: 1px solid var(--line-1);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  color: var(--text-1);
  font: var(--weight-medium) var(--text-base)/var(--leading-base) var(--font-body);
  animation: ss-toast-in var(--motion-toast) var(--ease-enter);
}
.ss-toast[data-state="exit"] { animation: ss-toast-out var(--motion-toast) var(--ease-exit) forwards; }
.ss-toast-info    { border-left-color: var(--info); }
.ss-toast-success { border-left-color: var(--ok); }
.ss-toast-warning { border-left-color: var(--warn); }
.ss-toast-danger  { border-left-color: var(--bad); }
.ss-toast::before { font-size: 1.05em; line-height: 1.35; flex: none; }
.ss-toast-info::before    { content: "ℹ"; color: var(--info); }
.ss-toast-success::before { content: "✓"; color: var(--ok); }
.ss-toast-warning::before { content: "⚠"; color: var(--warn); }
.ss-toast-danger::before  { content: "✕"; color: var(--bad); }
.ss-toast-close {
  margin-left: auto; flex: none;
  width: var(--touch-sm); height: var(--touch-sm);
  display: grid; place-items: center;
  border: 0; background: transparent; color: var(--text-3);
  border-radius: var(--radius-sm); cursor: pointer;
}
.ss-toast-close:hover { background: var(--surface-hover); color: var(--text-1); }

/* empty + error-page */
.ss-empty, .ss-error-page {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-3);
  padding: var(--space-12) var(--space-6);
  text-align: center;
  grid-column: 1 / -1;   /* survives being dropped into a CSS grid */
}
.ss-empty-icon, .ss-error-page-icon {
  display: grid; place-items: center;
  width: 56px; height: 56px;
  border-radius: var(--radius-pill);
  background: var(--surface-sunken);
  color: var(--text-3);
  font-size: 24px;
}
.ss-error-page-icon { background: var(--bad-soft); color: var(--bad); }
.ss-empty-title, .ss-error-page-title {
  font: var(--weight-bold) var(--text-2xl)/var(--leading-2xl) var(--font-display);
  color: var(--text-1); margin: 0;
}
.ss-empty-body, .ss-error-page-body {
  font: var(--weight-regular) var(--text-base)/var(--leading-base) var(--font-body);
  color: var(--text-3); max-width: 46ch; margin: 0;
}

/* skeleton */
.ss-skeleton {
  background: linear-gradient(90deg, var(--skeleton-from) 25%, var(--skeleton-to) 37%, var(--skeleton-from) 63%);
  background-size: 400% 100%;
  animation: ss-shimmer 1400ms ease-in-out infinite;
  border-radius: var(--radius-sm);
}
.ss-skeleton-text { height: 12px; margin: 6px 0; }
.ss-skeleton-title { height: 20px; width: 45%; margin: 8px 0; }
.ss-skeleton-circle { border-radius: var(--radius-pill); }

/* =========================================================================
   MOTION
   Durations/easings come from tokens; the reduced-motion block in tokens.css
   collapses them to 1ms, which neutralises every animation defined here.
   ========================================================================= */
@keyframes ss-shimmer   { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@keyframes ss-fade-in   { from { opacity: 0; } to { opacity: 1; } }
@keyframes ss-fade-out  { from { opacity: 1; } to { opacity: 0; } }
@keyframes ss-modal-in  { from { opacity: 0; transform: translate(-50%, -46%) scale(.97); } to { opacity: 1; transform: translate(-50%, -50%) scale(1); } }
@keyframes ss-modal-out { from { opacity: 1; transform: translate(-50%, -50%) scale(1); } to { opacity: 0; transform: translate(-50%, -48%) scale(.98); } }
@keyframes ss-drawer-in  { from { transform: translateX(100%); } to { transform: translateX(0); } }
@keyframes ss-drawer-out { from { transform: translateX(0); } to { transform: translateX(100%); } }
@keyframes ss-sheet-in   { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes ss-sheet-out  { from { transform: translateY(0); } to { transform: translateY(100%); } }
@keyframes ss-toast-in   { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes ss-toast-out  { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(8px); } }

/* Screen-reader-only utility, used to voice state that is shown graphically. */
.ss-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}


/* ==== status.css ==== */
/* ShopSmart status layer — Brand Book v1.1
 * ============================================================================
 * Implements implementation/order-status-colours.csv in full.
 *
 * The controlling rule from that file: every status family carries a
 * REQUIRED NON-COLOUR SIGNAL — an icon AND a text label. Colour is never the
 * only carrier of meaning, so the system stays readable under greyscale
 * printing, colour-vision deficiency, and low-quality kiosk panels.
 *
 * Icons are monochrome SVG masks painted with `currentColor`, so each icon
 * automatically inherits its family's semantic colour in both themes. No
 * icon font, no sprite sheet, no network request.
 *
 * Usage:
 *   <span class="ss-status ss-status-pending">Awaiting payment</span>
 *   <span class="ss-status ss-status-out-for-delivery">Out for delivery</span>
 *
 * The visible text IS the label — do not ship an icon-only status.
 * ============================================================================
 */

/* --- Icon masks ---------------------------------------------------------- */
.ss-status::before,
.ss-timeline-item::before,
.ss-step::before {
  content: "";
  flex: none;
  width: 1em;
  height: 1em;
  background-color: currentColor;
  -webkit-mask: var(--ss-icon) center / contain no-repeat;
  mask: var(--ss-icon) center / contain no-repeat;
}

/* clock */
.ss-status-pending {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l3 2'/%3E%3C/svg%3E");
}
.ss-status.ss-status-pending {
  background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent-line);
}
/* check-circle */
.ss-status-confirmed {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpolyline points='8 12.5 11 15.5 16 9'/%3E%3C/svg%3E");
}
.ss-status.ss-status-confirmed {
  background: var(--ok-soft); color: var(--ok); border-color: var(--ok-line);
}
/* parcel */
.ss-status-processing {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 8l-9-5-9 5 9 5 9-5z'/%3E%3Cpath d='M3 8v8l9 5 9-5V8'/%3E%3Cpath d='M12 13v8'/%3E%3C/svg%3E");
}
.ss-status.ss-status-processing {
  background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent-line);
}
/* truck */
.ss-status-booked {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 6h11v10H3z'/%3E%3Cpath d='M14 10h4l3 3v3h-7z'/%3E%3Ccircle cx='7' cy='18' r='2'/%3E%3Ccircle cx='17.5' cy='18' r='2'/%3E%3C/svg%3E");
}
.ss-status.ss-status-booked {
  background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent-line);
}
/* rider */
.ss-status-out-for-delivery {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='5' cy='17' r='3'/%3E%3Ccircle cx='19' cy='17' r='3'/%3E%3Cpath d='M8 17h5l3-6h3'/%3E%3Cpath d='M13 11L11 7H8'/%3E%3Cpath d='M16 7h3'/%3E%3C/svg%3E");
}
.ss-status.ss-status-out-for-delivery {
  background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line);
}
/* check-badge (double check — distinct from 'confirmed' at a glance) */
.ss-status-complete {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='1.5 12.5 5.5 16.5 12 8'/%3E%3Cpolyline points='9.5 14.5 12.5 17.5 21 7.5'/%3E%3C/svg%3E");
}
.ss-status.ss-status-complete {
  background: var(--ok-soft); color: var(--ok); border-color: var(--ok-line);
}
/* x-circle */
.ss-status-failed {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M9 9l6 6M15 9l-6 6'/%3E%3C/svg%3E");
}
.ss-status.ss-status-failed {
  background: var(--bad-soft); color: var(--bad); border-color: var(--bad-line);
}
/* return-arrow */
.ss-status-reverse {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 14 4 9.5 9 5'/%3E%3Cpath d='M4 9.5h10a5.5 5.5 0 0 1 0 11h-3'/%3E%3C/svg%3E");
}
.ss-status.ss-status-reverse {
  background: var(--surface-sunken); color: var(--text-3); border-color: var(--line-1);
}
/* clock-off */
.ss-status-expired {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7v5l2.5 1.5'/%3E%3Cpath d='M4.5 4.5l15 15'/%3E%3C/svg%3E");
}
.ss-status.ss-status-expired {
  background: var(--surface-sunken); color: var(--text-3); border-color: var(--line-1);
}
/* alert-circle */
.ss-status-cancellation-request {
  --ss-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 7.5v5'/%3E%3Cpath d='M12 16.5h.01'/%3E%3C/svg%3E");
}
.ss-status.ss-status-cancellation-request {
  background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line);
}

/* =========================================================================
   STATUS PILL
   ========================================================================= */
.ss-status {
  --ss-ctl-h: var(--touch-sm);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--ss-ctl-h);
  padding: 0 var(--space-3);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  font: var(--weight-semibold) var(--text-xs)/1 var(--font-body);
  white-space: nowrap;
}
.ss-status.ss-md { --ss-ctl-h: var(--touch-md); font-size: var(--text-sm); padding: 0 var(--space-4); }
.ss-status.ss-lg { --ss-ctl-h: var(--touch-lg); font-size: var(--text-lg); padding: 0 var(--space-5); gap: var(--space-3); }

/* =========================================================================
   TIMELINE
   states: pending | current | complete | exception
   ========================================================================= */
.ss-timeline { display: flex; flex-direction: column; margin: 0; padding: 0; list-style: none; }
.ss-timeline-item {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: 0 0 var(--space-6) var(--space-8);
  color: var(--text-3);
  font: var(--weight-medium) var(--text-base)/var(--leading-base) var(--font-body);
}
/* the rail */
.ss-timeline-item::after {
  content: "";
  position: absolute;
  left: 11px; top: 22px; bottom: 0;
  width: 2px;
  background: var(--line-1);
}
.ss-timeline-item:last-child { padding-bottom: 0; }
.ss-timeline-item:last-child::after { display: none; }
/* the node — ::before is the icon mask, positioned onto the rail */
.ss-timeline-item::before {
  position: absolute;
  left: 0; top: 2px;
  width: 24px; height: 24px;
  padding: 0;
}

.ss-timeline-item[data-state="pending"] { color: var(--text-4); }
.ss-timeline-item[data-state="current"] { color: var(--accent-ink); font-weight: var(--weight-semibold); }
.ss-timeline-item[data-state="current"]::after { background: var(--line-1); }
.ss-timeline-item[data-state="complete"] { color: var(--text-2); }
.ss-timeline-item[data-state="complete"]::after { background: var(--ok-line); }
.ss-timeline-item[data-state="exception"] { color: var(--bad); font-weight: var(--weight-semibold); }
.ss-timeline-item[data-state="exception"]::after { background: var(--bad-line); }

.ss-timeline-time {
  display: block;
  font: var(--weight-medium) var(--text-xs)/var(--leading-xs) var(--font-body);
  color: var(--text-4);
  font-variant-numeric: tabular-nums;
}

/* =========================================================================
   STEPPER
   states: pending | current | complete | exception
   ========================================================================= */
.ss-stepper { display: flex; align-items: flex-start; gap: 0; margin: 0; padding: 0; list-style: none; }
.ss-step {
  position: relative;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-2);
  text-align: center;
  color: var(--text-3);
  font: var(--weight-medium) var(--text-xs)/var(--leading-xs) var(--font-body);
}
/* connector */
.ss-step::after {
  content: "";
  position: absolute;
  top: 15px;
  left: calc(50% + 20px);
  right: calc(-50% + 20px);
  height: 2px;
  background: var(--line-1);
}
.ss-step:last-child::after { display: none; }
/* node */
.ss-step::before {
  width: 32px; height: 32px;
  padding: 5px;
  border-radius: var(--radius-pill);
  /* mask + background-color already set by the family rule; the ring is drawn
     by the wrapper below so the icon stays a clean mask */
}
.ss-step-node {
  display: grid; place-items: center;
  width: 32px; height: 32px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--line-strong);
  background: var(--surface-card);
  font: var(--weight-bold) var(--text-sm)/1 var(--font-body);
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
.ss-step[data-state="pending"]  .ss-step-node { border-color: var(--line-1); color: var(--text-4); }
.ss-step[data-state="current"]  .ss-step-node { border-color: var(--accent); background: var(--accent); color: var(--on-accent); }
.ss-step[data-state="complete"] .ss-step-node { border-color: var(--ok); background: var(--ok); color: #FFFFFF; }
.ss-step[data-state="complete"] .ss-step-node::after { content: "✓"; }
.ss-step[data-state="complete"] .ss-step-label { color: var(--text-2); }
.ss-step[data-state="complete"]::after { background: var(--ok); }
.ss-step[data-state="exception"] .ss-step-node { border-color: var(--bad); background: var(--bad); color: #FFFFFF; }
.ss-step[data-state="exception"] .ss-step-node::after { content: "!"; }
.ss-step[data-state="exception"] .ss-step-label { color: var(--bad); font-weight: var(--weight-semibold); }
.ss-step[data-state="current"]  .ss-step-label { color: var(--accent-ink); font-weight: var(--weight-semibold); }

/* When a stepper uses numbered nodes the icon mask is suppressed. */
.ss-stepper .ss-step::before { display: none; }

/* kiosk ramp */
.ss-stepper.ss-lg .ss-step-node { width: 48px; height: 48px; font-size: var(--text-lg); }
.ss-stepper.ss-lg .ss-step { font-size: var(--text-base); }
.ss-stepper.ss-lg .ss-step::after { top: 23px; left: calc(50% + 28px); right: calc(-50% + 28px); }

/* Stack the stepper on narrow screens so labels never truncate. */
@media (max-width: 640px) {
  .ss-stepper { flex-direction: column; align-items: stretch; gap: var(--space-3); }
  .ss-step { flex-direction: row; align-items: center; text-align: left; gap: var(--space-3); }
  .ss-step::after { display: none; }
}


/* ==== alias-landing.css ==== */
/* Landing / storefront alias layer — Brand Book v1.1 adoption
 * ============================================================================
 * Maps the storefront's legacy token names onto brand tokens. Every existing
 * `var(--legacy)` call site across the 40 landing pages keeps working, but now
 * resolves to a brand-approved, contrast-verified value.
 *
 * Call-site counts at time of writing (nothing below is dead weight):
 *   --accent 381 · --ink 255 · --ink-soft 217 · --line 210 · --muted 153
 *
 * ACCESSIBILITY FIX carried by this layer:
 *   --muted was #8890a6 = 3.19:1 on white — FAILS WCAG AA for normal text,
 *   across 153 call sites. It now resolves to --text-4 (#55657C = 5.93:1).
 *
 * DARK MODE:
 *   Not handled here. The storefront uses the same pre-paint inline script as
 *   the portals (landing/assets/theme-init.js) to stamp data-theme on <html>,
 *   which flips the single dark block in tokens.css. Mirroring those values
 *   into a @media (prefers-color-scheme) block would re-create exactly the
 *   duplication this package exists to remove.
 * ============================================================================
 */
:root {
  /* --- Neutrals ---------------------------------------------------------- */
  --ink:       var(--text-1);
  --ink-soft:  var(--text-3);
  --muted:     var(--text-4);   /* was #8890a6 — 3.19:1 FAIL -> 5.93:1 PASS */
  --line:      var(--line-1);
  --line-soft: var(--line-2);
  --bg:        var(--surface-page);
  --white:     var(--surface-card);

  /* --- Typography aliases (legacy names for the two brand faces) --------- */
  --display: var(--font-display);
  --body:    var(--font-body);

  /* --- Danger family -> brand --bad --------------------------------------
   * The storefront carried nine distinct reds; they collapse to one role.
   */
  --danger:      var(--bad);
  --danger-mid:  var(--bad);
  --danger-soft: var(--bad-soft);
  --danger-line: var(--bad-line);

  /* --- Promo/warning amber -> brand --warn (was eleven distinct ambers) --- */
  --warn-mid:  var(--warn);

  /* --- Success ------------------------------------------------------------
   * The storefront distinguished --green (free-shipping label + seller-online
   * dot) from --success (applied/qualified/saved). Both now resolve to the
   * single brand success role; the semantic distinction lives in class names,
   * not in near-identical hexes.
   */
  --success-mid:    var(--success);
  --success-bright: var(--success);

  /* --- Previously-undefined tokens ---------------------------------------
   * These six were referenced by landing CSS but never defined anywhere, so
   * the properties using them silently fell back to inherit/initial. They
   * pre-date this work; defining them on-brand fixes the rendering as a side
   * effect of the migration.
   *   --brand              checkout.css, product.html
   *   --cloud              checkout-social.css
   *   --color-text-*       pc-card-extras.css
   *   --shadow-md          gift-finder.html, search.html
   */
  --brand: var(--accent);
  --cloud: var(--surface-sunken);
  --color-text-primary:   var(--text-1);
  --color-text-secondary: var(--text-3);
  --color-text-tertiary:  var(--text-4);
  --shadow-md: var(--shadow);
}

/* Product photography must never be dimmed or filtered by the theme — buyers
 * judge goods by these images. Only chrome adapts. Opt a decorative image into
 * theme adaptation with data-theme-adapt. */
html[data-theme="dark"] img:not([data-theme-adapt]) {
  filter: none;
}

/* ==========================================================================
   Brand logo — carried over from the hand-written storefront tokens file.
   23 landing pages reference these classes, so they must survive the move to
   a generated file. The canonical mark is the horizontal cart + wordmark.

   Usage:
     <img src="/assets/shopsmart-logo-horizontal.jpg" alt="ShopSmart" class="ss-logo-img">

   .ss-logo / .ss-logo-dot are legacy classes kept for third-party embeds that
   may still reference them; new pages should use .ss-logo-img.
   ========================================================================== */
.ss-logo-img {
  display: block;
  height: 28px;
  width: auto;
  border-radius: 4px;
}
.ss-logo {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  letter-spacing: -0.02em;
}
.ss-logo-dot { color: var(--accent); }
