/* =============================================================================
   BIZI DESIGN SYSTEM
   -----------------------------------------------------------------------------
   Mobile-first, no build step, no framework. One stylesheet, cached forever.

   The aesthetic target is a social app, not business software: large media,
   confident type, one obvious action per screen, controls under the thumb.
   Every rule here is written for a 390px screen first and widened afterwards.

   Colour is split in two on purpose:
     --brand / --accent   the MERCHANT's colours, injected per page
     --bizi-*            the PLATFORM's colours, never shown on a public page
   A customer should see the gym, not us.
   ========================================================================== */

/* ---------------------------------------------------------------- tokens -- */
/*
   BIZI is midnight navy, warm white, pale cool blue and one electric cobalt.
   Not a rainbow: every product used to arrive with its own pastel, and
   twenty-five pastels on one screen is a catalogue, not a brand. Products keep
   an accent — it is how a merchant recognises theirs in a list — but it is a
   hairline, a small icon tile, never a whole card.

   The merchant's own colour still owns their customer-facing pages: those are
   the merchant's shopfront, not ours. `themeVars` overrides --brand there and
   only there, so the same stylesheet renders BIZI in cobalt and Somchai's
   booking page in Somchai's red.
*/
:root {
  /* Platform identity — the LOCKED final palette. Deep navy is the ink and
     the dark surface; cobalt is the one electric action colour; coral is the
     warm note, now in the red family rather than orange. These exact values
     are the approved visual guide; do not drift them per screen. */
  --bizi-midnight: #071A33;
  --bizi-navy: #0E2A55;
  --bizi-navy-2: #16407E;
  --bizi-blue: #2867F0;
  --bizi-blue-700: #1E52CC;
  --bizi-sky: #EEF4FF;
  --bizi-coral: #F04F66;

  /* Merchant defaults, overridden inline on customer pages only. */
  --brand: #2867F0;
  --brand-700: #1E52CC;
  --accent: #F04F66;
  --brand-ink: #FFFFFF;
  --accent-ink: #FFFFFF;
  /* A wash of whatever --brand currently is. color-mix resolves at use time,
     so these follow a merchant override without themeVars computing them. */
  --brand-soft: color-mix(in srgb, var(--brand) 12%, var(--bg));
  --brand-tint: color-mix(in srgb, var(--brand) 6%, var(--bg));
  --brand-line: color-mix(in srgb, var(--brand) 28%, var(--line));
  /* BIZI's OWN buttons are rounded rectangles, not pills.
   *
   * The guide's component sheet draws them that way and puts pills in a
   * separate category, used for chips — which is why `.chip` keeps its own
   * hard-coded 999px below. A full pill on a 56px full-width button is the
   * default every framework ships, and next to the approved screens it read
   * as generic.
   *
   * This is only the PLATFORM default. A merchant's page always receives an
   * explicit value from `themeVars()` in engines/profile.ts, so a merchant
   * who chose pill still gets a pill and nobody's published page moves. */
  --radius-btn: 16px;

  /* Ink. */
  --ink: #071A33;
  --ink-2: #42506C;
  --ink-3: #75819A;
  --ink-on-dark: #FFFFFF;
  --ink-on-dark-2: rgba(255,255,255,.76);
  --ink-on-dark-3: rgba(255,255,255,.55);

  /* Surfaces, lightest to deepest. This is the hierarchy every screen uses:
     PAGE → SURFACE → CARD → CONTROL → PRIMARY ACTION.
     The page is WARM white — paper, not software grey — so a pure-white card
     lifts off it with nothing but a hairline. Cool blue is reserved for
     tinted accents, never the whole page. */
  --bg: #FFFFFF;
  --bg-2: #F8F7F3;
  --bg-3: #EFEDE6;
  --bg-sky: #EEF4FF;
  --line: rgba(7,26,51,.10);
  --line-2: rgba(7,26,51,.06);

  /* Semantic. Each family keeps TWO strengths: the text-safe dark value
     (used for words, meets 4.5:1 on white) and the vivid guide value (used
     for badges, meters and graphics where the guide's exact hue matters). */
  --ok: #12805C;        --ok-bg: #E3F5EC;    --ok-vivid: #20A57A;
  --warn: #99610A;      --warn-bg: #FBF2DF;  --warn-vivid: #E8A329;
  --err: #C92B44;       --err-bg: #FDECEF;   --err-vivid: #F04F66;
  --info: #1E52CC;      --info-bg: #EEF4FF;
  --violet: #6D3BE4;    --violet-bg: #F2ECFE; --violet-vivid: #8B5CF6;

  /* Radius. Large surfaces 20–24, cards 16–18, inputs 14–16, pills 999. */
  --r-xs: 8px;
  --r-sm: 12px;
  --r-input: 14px;
  --r: 16px;
  --r-lg: 20px;
  --r-xl: 24px;
  --r-2xl: 28px;

  /* Elevation. Borders carry the hierarchy; shadows are for the few things
     that genuinely float — sticky bars, sheets, the primary action. */
  --e-1: 0 1px 2px rgba(7,26,51,.04);
  --e-2: 0 4px 14px rgba(7,26,51,.07), 0 1px 3px rgba(7,26,51,.05);
  --e-3: 0 12px 32px rgba(7,26,51,.12), 0 4px 8px rgba(7,26,51,.06);
  --e-4: 0 24px 56px rgba(7,26,51,.18);
  --e-cta: 0 -10px 30px rgba(7,26,51,.10);
  /* A primary action sits above the card it is on, in its own colour. */
  --e-action: 0 6px 18px color-mix(in srgb, var(--brand) 30%, transparent);

  /* Kept as aliases so nothing downstream had to be rewritten at once. */
  --shadow-1: var(--e-1);
  --shadow-2: var(--e-2);
  --shadow-3: var(--e-3);
  --shadow-cta: var(--e-cta);

  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-7: 32px; --sp-8: 44px; --sp-9: 64px;

  --font: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Inter,
          "Helvetica Neue", "Noto Sans Thai", "IBM Plex Sans Thai", Arial, sans-serif;
  --font-display: var(--font);
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace;

  --tap: 48px;                 /* primary controls */
  --tap-min: 44px;             /* the floor for anything tappable, incl. chips */
  --page: 560px;               /* content column; phones are the design target */
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-t: env(safe-area-inset-top, 0px);
}

/*
   Dark mode is not this sprint's job, but the tokens are arranged so it can be
   introduced by overriding this block alone — no component reaches past a
   variable for a colour.
*/
[data-theme="dark"] {
  --ink: #ECF1F8;
  --ink-2: #A8B4C8;
  --ink-3: #7D8AA0;
  --line: #22304A;
  --line-2: #182338;
  --bg: #0C1526;
  --bg-2: #070E1B;
  --bg-3: #16223A;
  --bg-sky: #142440;
  --ok-bg: #0B2B1F;
  --warn-bg: #2C1F09;
  --err-bg: #2F1414;
  --info-bg: #142440;
  --violet-bg: #1D1436;
  --e-1: 0 1px 2px rgba(0,0,0,.45);
  --e-2: 0 4px 14px rgba(0,0,0,.5);
  --e-3: 0 12px 32px rgba(0,0,0,.58);
  --e-4: 0 24px 56px rgba(0,0,0,.65);
}

/* ---------------------------------------------------------------- reset --- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* Anchor jumps and focus scrolls land clear of the fixed bottom CTA bar. */
html { scroll-padding-bottom: 110px; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--bg-2);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
}
img, svg, video { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4, p, figure { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
::selection { background: color-mix(in srgb, var(--accent) 30%, transparent); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* The hidden attribute means hidden. Without this a display rule anywhere
   below silently outranks it, and an element JavaScript has just hidden stays
   on screen. */
[hidden] { display: none !important; }

/* Screen-reader-only, used for form labels that are visually implied. */
.sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
      clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

.skip {
  position: absolute; left: -9999px; top: 8px; z-index: 100;
  background: var(--ink); color: #fff; padding: 12px 18px; border-radius: var(--r);
}
.skip:focus { left: 8px; }

/* ------------------------------------------------------------ typography -- */
/*
   Seven levels, and the gaps between them are deliberately large. The old
   scale ran 22 / 18 / 16 / 16 — four steps inside six points, which is why
   every page read as one long undifferentiated column. A phone has one
   screenful to establish what matters; the display size has to earn it.

     .d1   display    the one thing this screen is about
     .d2   page title
     .h1   section    a band of content
     .h2   subsection
     .h3   card title
     .body / .small / .tiny   content, metadata, caption
*/
.d1 {
  font-family: var(--font-display);
  font-size: clamp(32px, 9vw, 44px); line-height: 1.02;
  letter-spacing: -.038em; font-weight: 800;
}
.d2 {
  font-size: clamp(26px, 7vw, 34px); line-height: 1.1;
  letter-spacing: -.03em; font-weight: 780;
}
.h1 { font-size: 22px; line-height: 1.18; letter-spacing: -.026em; font-weight: 760; }
.h2 { font-size: 18px; line-height: 1.26; letter-spacing: -.018em; font-weight: 720; }
.h3 { font-size: 15.5px; line-height: 1.32; letter-spacing: -.012em; font-weight: 680; }
.body { font-size: 15.5px; line-height: 1.55; }
.small { font-size: 13.5px; line-height: 1.5; }
.tiny { font-size: 12px; line-height: 1.45; }
.muted { color: var(--ink-3); }
.dim { color: var(--ink-2); }
.eyebrow {
  font-size: 11.5px; font-weight: 750; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3);
}
.num { font-variant-numeric: tabular-nums; }
.mono { font-family: var(--font-mono); letter-spacing: .02em; }
.center { text-align: center; }
.balance { text-wrap: balance; }
/* A number that is the point of its card. */
.figure {
  font-size: 30px; font-weight: 800; letter-spacing: -.035em;
  line-height: 1.05; font-variant-numeric: tabular-nums;
}
.lede { font-size: 16.5px; line-height: 1.5; color: var(--ink-2); }

/* On a dark surface the whole scale shifts one step lighter. */
.on-dark { color: var(--ink-on-dark); }
.on-dark .muted, .on-dark .tiny.muted { color: var(--ink-on-dark-3); }
.on-dark .dim, .on-dark .lede { color: var(--ink-on-dark-2); }
/* A dark surface is a dark surface however it was reached; the type does not
   have to be told twice. */
.surface-dark .lede, .surface-dark .dim { color: var(--ink-on-dark-2); }
.surface-dark .muted, .surface-dark .eyebrow { color: var(--ink-on-dark-3); }
.surface-dark .h1, .surface-dark .h2, .surface-dark .h3,
.surface-dark .d1, .surface-dark .d2 { color: var(--ink-on-dark); }
.on-dark .eyebrow { color: var(--ink-on-dark-3); }

/* ---------------------------------------------------------------- layout -- */
.wrap { width: 100%; max-width: var(--page); margin-inline: auto; padding-inline: var(--sp-5); }
.wrap-wide { max-width: 1080px; }
.stack > * + * { margin-top: var(--sp-4); }
.stack-sm > * + * { margin-top: var(--sp-2); }
.stack-lg > * + * { margin-top: var(--sp-5); }
.stack-xs > * + * { margin-top: var(--sp-1); }
.row { display: flex; align-items: center; gap: var(--sp-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.row-wrap { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.grow { flex: 1 1 auto; min-width: 0; }
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
.grid-3 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
@media (min-width: 720px) { .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.section { padding-block: var(--sp-5); }
.section-tight { padding-block: var(--sp-4); }
.pad-bottom-cta { padding-bottom: calc(96px + var(--safe-b)); }
.hide { display: none !important; }
@media (min-width: 720px) { .only-mobile { display: none !important; } }
@media (max-width: 719px) { .only-desktop { display: none !important; } }

/* --------------------------------------------------------------- surfaces - */
/*
   PAGE → SURFACE → CARD → CONTROL → PRIMARY ACTION.

   Five levels, and a screen should never need more than three of them at once.
   The old design put a white card on a white page with a hairline border, which
   is not a hierarchy — it is two surfaces pretending to be different. The page
   is now pale cool blue so a white card genuinely lifts off it.
*/
body { background: var(--bg-2); }

.card {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--e-1);
  overflow: hidden;
}
.card-pad { padding: var(--sp-5); }
.card-flat { box-shadow: none; }
.card-tap { transition: transform .14s ease, box-shadow .14s ease; }
.card-tap:hover { box-shadow: var(--e-2); }
.card-tap:active { transform: scale(.985); box-shadow: var(--e-1); }
/* One card per screen may matter more than the others. */
.card-raised { box-shadow: var(--e-2); }
.card-sky { background: var(--bg-sky); border-color: transparent; box-shadow: none; }
/* The brand-tinted card: a recommendation, a live state, something chosen. */
.card-brand {
  background: var(--brand-tint);
  border-color: var(--brand-line);
}

/*
   The dark surface. BIZI's one dramatic gesture, used at the top of a screen
   that is selling something: the store, a product, a live confirmation.
   Never behind body copy — navy under a paragraph is a landing page from 2014.
*/
.surface-dark {
  position: relative;
  background:
    radial-gradient(120% 90% at 12% 0%, rgba(40,103,240,.34), transparent 58%),
    radial-gradient(90% 80% at 96% 12%, rgba(139,92,246,.22), transparent 62%),
    linear-gradient(168deg, var(--bizi-navy) 0%, var(--bizi-midnight) 72%);
  color: var(--ink-on-dark);
}
.surface-dark::after {
  /* A whisper of grain so the gradient does not read as a flat CSS fill. */
  content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='.055'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
.surface-dark > * { position: relative; z-index: 1; }

.panel { background: var(--bg); border-radius: var(--r-lg); padding: var(--sp-5); border: 1px solid var(--line); }
.divider { height: 1px; background: var(--line); border: 0; margin-block: var(--sp-4); }

/* An icon in its own tinted tile. The one place a product's accent is allowed
   to be a colour rather than a hairline. */
.itile {
  width: 44px; height: 44px; border-radius: var(--r-sm);
  display: grid; place-items: center; flex: 0 0 auto;
  background: color-mix(in srgb, var(--tone, var(--brand)) 12%, var(--bg));
  color: var(--tone, var(--brand));
}
.itile-sm { width: 34px; height: 34px; border-radius: 10px; }
.itile-lg { width: 56px; height: 56px; border-radius: var(--r); }
.on-dark .itile, .surface-dark .itile {
  background: rgba(255,255,255,.14); color: #fff;
}

/* --------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--tap); padding: 0 var(--sp-6);
  border-radius: var(--radius-btn); border: 1px solid transparent;
  font-size: 16px; font-weight: 660; letter-spacing: -.01em;
  cursor: pointer; text-align: center; user-select: none;
  transition: transform .12s ease, filter .12s ease, background .12s ease;
}
.btn:active { transform: scale(.975); }
/* A button waiting for the customer to choose a time is not a failed button.
   Rendered at 390px, a 50%-opacity coral CTA read as an error state. It now
   reads as inert-but-fine: neutral surface, real contrast, legible label. */
.btn[disabled], .btn[aria-disabled="true"] { pointer-events: none; }
/* Inert, not broken — and legible while it waits. --ink-3 on --bg-3 measured
   around 3:1, which is a label you squint at. */
.btn-primary[disabled], .btn-primary[aria-disabled="true"],
.btn-accent[disabled], .btn-accent[aria-disabled="true"] {
  background: var(--bg-3); color: var(--ink-2); box-shadow: none;
}
/* The primary action carries its own shadow in its own colour, so it reads as
   the thing that is above everything else on the screen. */
.btn-primary { background: var(--brand); color: var(--brand-ink); box-shadow: var(--e-action); }
.btn-primary:hover { background: var(--brand-700); }
.btn-accent { background: var(--accent); color: var(--accent-ink); box-shadow: var(--e-2); }
.btn-dark { background: var(--bizi-midnight); color: #fff; box-shadow: var(--e-2); }
.btn-ghost { background: var(--bg); color: var(--ink); border-color: var(--line); box-shadow: var(--e-1); }
.btn-ghost:hover { background: var(--bg-2); }
.btn-quiet { background: transparent; color: var(--ink-2); }
.btn-quiet:hover { background: var(--bg-3); }
/* On a dark surface a ghost button has to be built from light, not from ink. */
.surface-dark .btn-ghost, .on-dark .btn-ghost {
  background: rgba(255,255,255,.12); color: #fff; border-color: rgba(255,255,255,.22); box-shadow: none;
}
.surface-dark .btn-quiet, .on-dark .btn-quiet { color: rgba(255,255,255,.86); }
.btn-block { display: flex; width: 100%; }
.btn-sm { min-height: var(--tap-min); padding: 0 var(--sp-4); font-size: 14px; font-weight: 620; }
.btn-lg { min-height: 56px; font-size: 17px; font-weight: 720; }
.btn-danger { background: var(--err-bg); color: var(--err); box-shadow: none; }

/* Sticky action bar. The primary action on a public page is always here, always
   reachable with a thumb, and never scrolled past. */
.cta-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  padding: var(--sp-3) var(--sp-4) calc(var(--sp-3) + var(--safe-b));
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  border-top: 1px solid var(--line);
  box-shadow: var(--shadow-cta);
}
.cta-bar .wrap { padding-inline: 0; display: flex; align-items: center; gap: var(--sp-3); }

/* ---------------------------------------------------------------- chips --- */
/* Non-interactive chips stay compact; anything tappable is raised to --tap-min.
   Measured in a browser at 390px: contact chips were 40px and "Open in Maps"
   34px, both under the threshold the design system claims to hold. */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 34px; padding: 0 var(--sp-3);
  border-radius: 999px; background: var(--bg-3); color: var(--ink-2);
  font-size: 13.5px; font-weight: 600; white-space: nowrap;
}
.chip-brand { background: color-mix(in srgb, var(--brand) 12%, transparent); color: var(--brand); }
/* A chip holding a URL is as wide as the URL, which on a 360px phone is wider
   than the phone. It keeps its shape and loses the middle instead. */
.chip-ellipsis { max-width: 100%; }
.chip-ellipsis > span, .chip-ellipsis { overflow: hidden; text-overflow: ellipsis; }
.chip-ok { background: var(--ok-bg); color: var(--ok); }
.chip-warn { background: var(--warn-bg); color: var(--warn); }
.chip-err { background: var(--err-bg); color: var(--err); }
.chip-live::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--ok); }
.chip-row { display: flex; gap: var(--sp-2); overflow-x: auto; scrollbar-width: none; padding-block: 2px; }
.chip-row::-webkit-scrollbar { display: none; }
a.chip, button.chip, label.chip { min-height: var(--tap-min); padding-inline: var(--sp-4); }
.chip-tab { min-height: var(--tap-min); padding: 0 var(--sp-4); background: var(--bg); border: 1px solid var(--line); }
.chip-tab[aria-selected="true"] { background: var(--ink); color: var(--bg); border-color: var(--ink); }

.badge {
  display: inline-block; padding: 3px 9px; border-radius: 7px;
  font-size: 11px; font-weight: 750; letter-spacing: .05em; text-transform: uppercase;
  background: var(--accent); color: var(--accent-ink);
}

/* ---------------------------------------------------------------- forms --- */
.field { display: block; }
.field + .field { margin-top: var(--sp-5); }
.field-label { display: block; font-size: 14.5px; font-weight: 640; margin-bottom: 7px; }
.field-help { display: block; font-size: 13px; color: var(--ink-3); margin-top: 6px; line-height: 1.45; }
.field-err { display: block; font-size: 13px; color: var(--err); margin-top: 6px; font-weight: 600; }
.input, .select, .textarea {
  width: 100%; min-height: var(--tap); padding: 12px var(--sp-4);
  background: var(--bg); color: var(--ink);
  border: 1.5px solid var(--line); border-radius: var(--r-input);
  font-size: 16px; /* never below 16px: iOS zooms the page otherwise */
  transition: border-color .12s ease, box-shadow .12s ease;
}
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 16%, transparent);
}
.input[aria-invalid="true"] { border-color: var(--err); }
.textarea { min-height: 108px; resize: vertical; line-height: 1.5; }
.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none' stroke='%236B7684' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M5 8l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; padding-right: 44px;
}
.input-prefix { position: relative; }
.input-prefix .input { padding-left: 38px; }
.input-prefix span {
  position: absolute; left: var(--sp-4); top: 50%; transform: translateY(-50%);
  color: var(--ink-3); font-weight: 600; pointer-events: none;
}

.switch { display: flex; align-items: center; gap: var(--sp-3); min-height: var(--tap); cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
  flex: 0 0 auto; width: 52px; height: 31px; border-radius: 999px;
  background: var(--bg-3); border: 1px solid var(--line); position: relative;
  transition: background .16s ease, border-color .16s ease;
}
.switch-track::after {
  content: ''; position: absolute; inset: 2px auto 2px 2px; width: 25px;
  border-radius: 50%; background: #fff; box-shadow: var(--shadow-1);
  transition: transform .16s cubic-bezier(.3,.9,.4,1);
}
.switch input:checked + .switch-track { background: var(--brand); border-color: var(--brand); }
.switch input:checked + .switch-track::after { transform: translateX(21px); }
.switch input:focus-visible + .switch-track { outline: 3px solid var(--accent); outline-offset: 2px; }

.check { display: flex; gap: var(--sp-3); align-items: flex-start; cursor: pointer; padding: var(--sp-2) 0; }
.check input { width: 24px; height: 24px; margin: 1px 0 0; accent-color: var(--brand); flex: 0 0 auto; }

/* Segmented control — used for deposit type, fulfilment, amount mode. */
.seg { display: flex; background: var(--bg-3); border-radius: 999px; padding: 4px; gap: 4px; }
.seg label { flex: 1; }
.seg input { position: absolute; opacity: 0; }
.seg span {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap-min); border-radius: 999px; font-size: 14px; font-weight: 640;
  color: var(--ink-2); cursor: pointer; text-align: center; padding-inline: 8px;
}
.seg input:checked + span { background: var(--bg); color: var(--ink); box-shadow: var(--shadow-1); }
.seg input:focus-visible + span { outline: 3px solid var(--accent); }

/* --------------------------------------------------------------- pickers -- */
.day-strip { display: flex; gap: var(--sp-2); overflow-x: auto; scrollbar-width: none; padding: 2px 0 var(--sp-1); }
.day-strip::-webkit-scrollbar { display: none; }
.day {
  flex: 0 0 auto; width: 64px; padding: var(--sp-3) 0; border-radius: var(--r);
  background: var(--bg); border: 1.5px solid var(--line); text-align: center; cursor: pointer;
}
.day-name { font-size: 11.5px; font-weight: 700; color: var(--ink-3); text-transform: uppercase; letter-spacing: .05em; }
.day-num { font-size: 19px; font-weight: 750; letter-spacing: -.02em; margin-top: 2px; }
.day-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ok); margin: 5px auto 0; }
.day[aria-selected="true"] { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.day[aria-selected="true"] .day-name { color: color-mix(in srgb, var(--brand-ink) 80%, transparent); }
.day[aria-selected="true"] .day-dot { background: var(--brand-ink); }
.day[data-empty="1"] { opacity: .38; }

.slots { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2); }
@media (min-width: 480px) { .slots { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.slot {
  min-height: var(--tap); border-radius: var(--r);
  background: var(--bg); border: 1.5px solid var(--line);
  font-size: 15.5px; font-weight: 660; font-variant-numeric: tabular-nums;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  cursor: pointer; transition: transform .1s ease;
}
.slot:active { transform: scale(.96); }
.slot small { font-size: 10.5px; font-weight: 600; color: var(--ink-3); letter-spacing: .02em; }
.slot[aria-selected="true"] { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.slot[aria-selected="true"] small { color: color-mix(in srgb, var(--brand-ink) 78%, transparent); }
.slot[disabled] { opacity: .35; cursor: not-allowed; text-decoration: line-through; }

/* The rest of a long day, one honest control away. Six slots answer "can I
   go?"; a twenty-one-button wall answers nothing. */
.slots-more { margin-top: var(--sp-3); }
.slots-more summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  min-height: var(--tap-min); border-radius: var(--r-input);
  border: 1.5px dashed var(--line); color: var(--ink-2);
  font-size: 14px; font-weight: 640;
}
.slots-more summary::-webkit-details-marker { display: none; }
.slots-more summary::after { content: '▾'; font-size: 12px; color: var(--ink-3); }
.slots-more[open] summary::after { content: '▴'; }
.slots-more summary:hover { background: var(--bg-2); }

/* Setup's compact preview: closed by default, the full page one tap away. */
.pv summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--tap-min); border-radius: var(--r-input);
  border: 1.5px dashed var(--line); color: var(--ink-2);
  font-size: 14px; font-weight: 640;
}
.pv summary::-webkit-details-marker { display: none; }
.pv summary svg { color: var(--brand); }
.pv[open] summary { display: none; }
.pv-frame {
  border-radius: var(--r); overflow: hidden;
  border: 1px solid var(--line); background: var(--bg-2);
}

/* The time the customer already holds. Not struck through — it is not gone,
   it is theirs — but clearly not something to pick again. */
.slot-current {
  cursor: default; border-style: dashed; border-color: var(--brand);
  color: var(--brand); background: var(--brand-bg, transparent);
}

.stepper { display: inline-flex; align-items: center; gap: var(--sp-2); }
.stepper button {
  width: 44px; height: 44px; border-radius: 50%; border: 1.5px solid var(--line);
  background: var(--bg); font-size: 20px; font-weight: 600; cursor: pointer;
  display: grid; place-items: center;
}
.stepper output { min-width: 40px; text-align: center; font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ----------------------------------------------------------------- media -- */
.hero { position: relative; aspect-ratio: 4 / 3; background: var(--bg-3); overflow: hidden; }
.hero > img, .hero > svg { width: 100%; height: 100%; object-fit: cover; }
.hero-16 { aspect-ratio: 16 / 9; }
.hero-1 { aspect-ratio: 1 / 1; }
.hero-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(4,10,18,.05) 0%, rgba(4,10,18,.10) 45%, rgba(4,10,18,.78) 100%);
}
.hero-body { position: absolute; left: 0; right: 0; bottom: 0; padding: var(--sp-5); color: #fff; }
.hero-body .d1, .hero-body .d2 { color: #fff; text-shadow: 0 1px 24px rgba(0,0,0,.35); }

/* ------------------------------------------------------------------ front page
 * The platform's own opening screen, per the approved visual guide: one
 * photograph, the promise set in the display face, and the four things a
 * business owner actually wants, before any chrome.
 *
 * A taller frame than a catalogue hero (4/5 on a phone rather than 4/3): the
 * guide's welcome screen is a full-bleed photo, and a 4/3 crop of a wide
 * landscape leaves the headline sitting on the horizon.
 */
/* The guide's welcome screen is ONE photograph filling the phone, carrying the
 * brand, the promise, the four lines AND both buttons. The first attempt made
 * it a photo BAND with the buttons on white underneath — a website with a
 * header image, not the approved screen. Hence a full viewport.
 *
 * `svh`, not `vh`: a mobile browser's collapsing toolbar makes `100vh` taller
 * than what is actually on screen, which would push the buttons under the fold
 * on first paint — on the one screen whose whole job is to be tapped.
 *
 * `aspect-ratio` is cleared deliberately. Combined with a max-height the
 * browser shrinks the WIDTH to keep the ratio, which left the hero ending
 * 178px short of the viewport with a band of page showing beside it. */
.hero-front {
  aspect-ratio: auto;
  height: 100svh; min-height: 600px; max-height: 940px;
  display: flex; flex-direction: column; justify-content: flex-end;
}
/* Art direction, not decoration. `object-fit: cover` on a phone-shaped frame
 * shows a landscape photograph's FULL height and crops the sides, so the
 * welcome screen opened on 40% bare sky with the coastline squeezed into a
 * strip. Scaling up moves the visible window down onto the water, the karst
 * and the bow. The origin sits below centre so the growth is taken off the
 * top, where there is nothing, rather than off the foreground. */
.hero-front > img {
  position: absolute; inset: 0;
  transform: scale(1.38); transform-origin: 50% 72%;
}

/* The app bar rides ON the photograph. The guide's welcome screen has no
 * opaque chrome above the image at all. */
.appbar.appbar-over {
  position: absolute; top: 0; left: 0; right: 0; z-index: 3;
  background: none; backdrop-filter: none; border-bottom: 0; color: #fff;
}
.appbar.appbar-over .h2, .appbar.appbar-over a { color: #fff; }
.appbar.appbar-over .btn-ghost {
  background: rgba(255,255,255,.14); color: #fff;
  border-color: rgba(255,255,255,.34); box-shadow: none;
  backdrop-filter: saturate(140%) blur(10px);
}

/* Secondary action ON a photograph. `.btn-ghost` is a solid white pill, which
 * over a bright image reads as the primary and outshouts the cobalt one. */
.btn-on-photo {
  background: rgba(255,255,255,.16); color: #fff;
  border-color: rgba(255,255,255,.42); box-shadow: none;
  backdrop-filter: saturate(140%) blur(10px);
}
.btn-on-photo:hover { background: rgba(255,255,255,.26); }

.hero-cta { margin-top: var(--sp-6); display: grid; gap: var(--sp-3); }

/* Before/after on the front page. NOT `.grid-2`, which is two columns at every
 * width: five sentences a side in 175px on a phone is unreadable. One column
 * until there is room, and the order then reads left-to-right as it should. */
.compare-grid { display: grid; gap: var(--sp-3); }
@media (min-width: 640px) { .compare-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.compare { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.compare li {
  display: flex; gap: var(--sp-2); align-items: flex-start;
  font-size: 14.5px; line-height: 1.45; color: var(--ink-2);
}
/* A dot, not a cross. The left column is how things are today, not a list of
 * the reader's failures — a business owner reading five red crosses against
 * their own working day is being told off, which is a poor way to sell. */
.compare:not(.compare-yes) li::before {
  content: ""; flex: 0 0 auto; width: 6px; height: 6px; margin-top: 8px;
  border-radius: 50%; background: var(--ink-3); opacity: .55;
}
.compare-yes li { color: var(--ink); }
.compare-yes svg { flex: 0 0 auto; margin-top: 2px; color: var(--brand); }
.card-accent { border-color: var(--brand-line); background: var(--brand-tint); }

/* No coloured glow on the photograph. `--e-action` throws a cobalt halo that
 * reads as depth on a white page and as a neon rim over a dark image. The
 * photo already separates the button from its background. */
.hero-cta .btn-primary { box-shadow: 0 2px 14px rgba(4,10,18,.34); }

@media (min-width: 700px) {
  .hero-front .hero-body { padding: var(--sp-8) var(--sp-7); }
  .hero-front .d1 { font-size: clamp(40px, 4.4vw, 56px); }
  .hero-front .hero-lede, .hero-front .proof, .hero-cta { max-width: 430px; }
}

/* Deeper than .hero-scrim. That one is tuned for a card title over a dark-ish
 * product photo; this carries a headline, a paragraph AND four lines over
 * bright turquoise water, which the lighter gradient could not hold. */
.hero-scrim-front {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(4,10,18,.34) 0%, rgba(4,10,18,.04) 26%,
                    rgba(4,10,18,.46) 48%, rgba(4,10,18,.86) 74%,
                    rgba(4,10,18,.96) 100%);
}

/* Relative, not absolutely pinned to the bottom: the hero is a flex column
 * ending in this block, so it grows with the copy instead of overflowing the
 * frame when the four lines and two buttons are added to it. */
.hero-front .hero-body { position: relative; padding: var(--sp-6) var(--sp-5) var(--sp-7); }
.hero-front .eyebrow { color: rgba(255,255,255,.72); }
.hero-lede {
  font-size: clamp(15px, 4.1vw, 17px); line-height: 1.45;
  color: rgba(255,255,255,.9); margin-top: var(--sp-3); max-width: 34ch;
}

/* The guide's four value lines. A list, because that is what it is. */
.proof { list-style: none; margin: var(--sp-5) 0 0; padding: 0; display: grid; gap: var(--sp-2); }
.proof li { display: flex; align-items: center; gap: var(--sp-2); font-size: 15px; font-weight: 620; letter-spacing: -.01em; }
/* White, as the guide draws them. Green read as a status tick — "done" —
 * rather than as the list of things the product does for you. */
.proof svg { flex: 0 0 auto; color: #fff; opacity: .92; }

.avatar { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; background: var(--bg-3); flex: 0 0 auto; }
.avatar-lg { width: 68px; height: 68px; }
.avatar-ring { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--brand); }

.thumb { width: 84px; height: 84px; border-radius: var(--r); object-fit: cover; background: var(--bg-3); flex: 0 0 auto; }

/* ------------------------------------------------------------ list rows --- */
.rows { border-radius: var(--r-lg); overflow: hidden; border: 1px solid var(--line); background: var(--bg); }
.rowitem {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-4); min-height: 68px; width: 100%; text-align: left;
  background: var(--bg); border: 0; border-bottom: 1px solid var(--line-2); cursor: pointer;
}
.rowitem:last-child { border-bottom: 0; }
.rowitem:active { background: var(--bg-2); }
.rowitem-static { cursor: default; flex-wrap: wrap; }
/* Long status values (readiness/health boards) wrap under their label instead
   of crushing it to one character per line. */
.rowitem-static .chip { white-space: normal; height: auto; min-height: 34px; padding-block: 6px; border-radius: 18px; text-align: left; }

/* ---------------------------------------------------------------- stats --- */
.stat { padding: var(--sp-4); border-radius: var(--r); background: var(--bg); border: 1px solid var(--line); }
.stat-value { font-size: 27px; font-weight: 780; letter-spacing: -.028em; font-variant-numeric: tabular-nums; line-height: 1.1; }
.stat-label { font-size: 12.5px; font-weight: 600; color: var(--ink-3); margin-top: 3px; }
.stat-hero { background: linear-gradient(150deg, var(--bizi-deep), var(--bizi-deep-2)); color: #fff; border: 0; }
.stat-hero .stat-label { color: rgba(255,255,255,.72); }

.bar { height: 8px; border-radius: 999px; background: var(--bg-3); overflow: hidden; }
.bar > i { display: block; height: 100%; border-radius: 999px; background: var(--brand); }

.spark { display: flex; align-items: flex-end; gap: 3px; height: 52px; }
.spark i { flex: 1; background: color-mix(in srgb, var(--brand) 28%, transparent); border-radius: 3px 3px 0 0; min-height: 3px; }
.spark i.on { background: var(--brand); }

/* ------------------------------------------------------------ navigation -- */
.appbar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: 56px; padding: var(--sp-2) var(--sp-4);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(160%) blur(14px);
  border-bottom: 1px solid var(--line);
}
.appbar-title { font-size: 17px; font-weight: 700; letter-spacing: -.015em; }
.appbar > a.row { min-height: var(--tap-min); }
.iconbtn {
  width: var(--tap); height: var(--tap); border-radius: 50%; border: 0;
  background: transparent; display: grid; place-items: center; cursor: pointer; color: var(--ink);
  flex: 0 0 auto;
}
.iconbtn:hover { background: var(--bg-3); }

.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
  display: grid; grid-template-columns: repeat(5, 1fr);
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  backdrop-filter: saturate(160%) blur(16px);
  border-top: 1px solid var(--line);
  padding-bottom: var(--safe-b);
}
.tab {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 58px; font-size: 11px; font-weight: 640; color: var(--ink-3); letter-spacing: .005em;
}
.tab svg { width: 23px; height: 23px; }
.tab[aria-current="page"] { color: var(--bizi-deep); }
[data-theme="dark"] .tab[aria-current="page"] { color: var(--bizi-coral); }
.has-tabbar { padding-bottom: calc(64px + var(--safe-b)); }

/* --------------------------------------------------------------- sheets --- */
.sheet-backdrop {
  position: fixed; inset: 0; z-index: 60; background: rgba(8, 14, 24, .5);
  display: flex; align-items: flex-end; justify-content: center;
  animation: fade .16s ease;
}
.sheet {
  width: 100%; max-width: var(--page);
  background: var(--bg); border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: var(--sp-3) var(--sp-4) calc(var(--sp-6) + var(--safe-b));
  max-height: 88vh; overflow-y: auto;
  animation: rise .22s cubic-bezier(.2,.9,.3,1);
}
.sheet-grab { width: 40px; height: 4px; border-radius: 999px; background: var(--line); margin: 0 auto var(--sp-4); }
@keyframes rise { from { transform: translateY(18px); opacity: .6; } to { transform: none; opacity: 1; } }
@keyframes fade { from { opacity: 0 } to { opacity: 1 } }
@media (min-width: 720px) {
  .sheet-backdrop { align-items: center; }
  .sheet { border-radius: var(--r-xl); max-width: 480px; }
}

/* -------------------------------------------------------------- feedback -- */
.note { padding: var(--sp-4); border-radius: var(--r); font-size: 14.5px; line-height: 1.5; }
.note-ok { background: var(--ok-bg); color: var(--ok); }
.note-warn { background: var(--warn-bg); color: var(--warn); }
.note-err { background: var(--err-bg); color: var(--err); }
.note-info { background: var(--bg-3); color: var(--ink-2); }

.flash {
  position: fixed; left: 50%; transform: translateX(-50%);
  top: calc(12px + env(safe-area-inset-top, 0px)); z-index: 80;
  max-width: calc(100% - 32px); padding: 12px var(--sp-5); border-radius: 999px;
  box-shadow: var(--shadow-3); font-size: 14.5px; font-weight: 600;
  animation: drop .24s cubic-bezier(.2,.9,.3,1);
}
@keyframes drop { from { transform: translate(-50%, -14px); opacity: 0 } to { transform: translate(-50%, 0); opacity: 1 } }

/* Empty states carry their own weight — an empty screen is where merchants quit. */
.empty { text-align: center; padding: var(--sp-8) var(--sp-5); }
.empty-art { width: 92px; height: 92px; margin: 0 auto var(--sp-5); opacity: .9; }
.empty h3 { font-size: 18px; font-weight: 700; letter-spacing: -.015em; }
.empty p { color: var(--ink-3); margin-top: 6px; font-size: 15px; }
.empty .btn { margin-top: var(--sp-5); }

.skeleton {
  background: linear-gradient(90deg, var(--bg-3) 25%, var(--bg-2) 50%, var(--bg-3) 75%);
  background-size: 200% 100%; animation: shimmer 1.3s infinite; border-radius: var(--r-sm);
}
@keyframes shimmer { from { background-position: 200% 0 } to { background-position: -200% 0 } }

.spinner {
  width: 20px; height: 20px; border-radius: 50%;
  border: 2.5px solid color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: currentColor; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg) } }
.is-busy { pointer-events: none; opacity: .72; }

/* ---------------------------------------------------------------- price --- */
.price { font-size: 26px; font-weight: 780; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.price-sm { font-size: 17px; font-weight: 700; letter-spacing: -.02em; }
.price-was { font-size: 15px; color: var(--ink-3); text-decoration: line-through; font-weight: 500; }
.price-from { font-size: 12.5px; color: var(--ink-3); font-weight: 600; }

.stars { display: inline-flex; gap: 1px; color: #F5A524; }
.stars svg { width: 15px; height: 15px; }

/* -------------------------------------------------- product store cards --- */
.tile {
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-4); border-radius: var(--r-lg);
  background: var(--bg); border: 1px solid var(--line); box-shadow: var(--shadow-1);
  height: 100%; text-align: left; cursor: pointer;
  transition: transform .12s ease, box-shadow .12s ease;
}
.tile:active { transform: scale(.985); }
.tile:hover { box-shadow: var(--shadow-2); }
.tile-icon {
  width: 46px; height: 46px; border-radius: 13px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--tile-accent, var(--bizi-deep)) 13%, transparent);
  color: var(--tile-accent, var(--bizi-deep));
}
.tile-icon svg { width: 24px; height: 24px; }
.tile-name { font-size: 15.5px; font-weight: 700; letter-spacing: -.014em; line-height: 1.25; }
.tile-outcome { font-size: 13.5px; color: var(--ink-3); line-height: 1.4; }
.tile-foot {
  margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
}
.tile-cta { font-size: 12px; font-weight: 750; letter-spacing: .06em; text-transform: uppercase; color: var(--bizi-deep); }
.tile-cta.installed { color: var(--ok); }
[data-theme="dark"] .tile-cta { color: var(--bizi-coral); }
/* The objection every merchant has, answered before they tap. */
.tile-mins {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 11.5px; font-weight: 600; color: var(--ink-3); white-space: nowrap;
}
.tile-mins svg { width: 13px; height: 13px; }

/* Merchant-facing hero band, platform colours. */
.band {
  background: linear-gradient(155deg, var(--bizi-deep) 0%, var(--bizi-deep-2) 62%, #052E2B 100%);
  color: #fff; padding: var(--sp-6) 0 var(--sp-7);
}
.band .muted, .band .dim { color: rgba(255,255,255,.70); }
.band-coral { background: linear-gradient(150deg, #FF8A5C, var(--bizi-coral) 60%, #E04D2E); }
/* The store's own band: a promise, not a chapter. */
.band-tight { padding: var(--sp-6) 0; }

/* -------------------------------------------------------------- search -----
   One field, full width, at thumb height. The store's first job is to let a
   merchant who knows what they want stop browsing. */
.searchbar {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 0 var(--sp-4); min-height: var(--tap);
  /* A field, so it takes the field radius. The guide draws the store search
   * as a rounded rectangle like every other input; a 999px pill made it read
   * as a chip sitting above the chip row rather than as something to type in. */
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-input);
  box-shadow: var(--shadow-1);
}
.searchbar-icon { color: var(--ink-3); display: flex; flex: 0 0 auto; }
.searchbar-input {
  flex: 1 1 auto; min-width: 0; border: 0; background: transparent;
  font: inherit; font-size: 15px; color: var(--ink); min-height: var(--tap);
}
.searchbar-input:focus { outline: none; }
.searchbar:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.searchbar-input::-webkit-search-cancel-button { -webkit-appearance: none; }

/* ---------------------------------------------------------------- misc ---- */
.copyfield {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-2) var(--sp-2) var(--sp-4);
  background: var(--bg-3); border-radius: var(--r); min-height: var(--tap);
}
.copyfield code { flex: 1; min-width: 0; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.qr { width: 100%; max-width: 240px; margin-inline: auto; background: #fff; padding: var(--sp-4); border-radius: var(--r); }
.qr svg { width: 100%; height: auto; }

.progress-dots { display: flex; gap: 6px; }
.progress-dots i { width: 26px; height: 4px; border-radius: 999px; background: var(--line); }
.progress-dots i.on { background: var(--brand); }

.toolbar-scroll { display: flex; gap: var(--sp-2); overflow-x: auto; scrollbar-width: none; }
.toolbar-scroll::-webkit-scrollbar { display: none; }

/* Secondary links are still tapped by a thumb. Measured at 360-1440: section
   "see all" links were 21px and the demo hand-off link 14px. */
.link-underline { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1.5px; }
a.link-underline, a.small { display: inline-flex; align-items: center; min-height: var(--tap-min); }
.footer-note a.link-underline { min-height: var(--tap-min); }
.footer-note { padding: var(--sp-6) var(--sp-4) var(--sp-8); text-align: center; color: var(--ink-3); font-size: 12.5px; }
.footer-note a { display: inline-flex; align-items: center; min-height: var(--tap-min); padding-inline: var(--sp-3); }

/* Desktop is a courtesy, not the target. The phone layout is centred and the
   extra width is used for breathing room, never for a second column of controls. */
@media (min-width: 900px) {
  .app-shell { display: grid; grid-template-columns: 240px minmax(0, 1fr); min-height: 100vh; }
  .app-shell .tabbar { display: none; }
  .app-shell .has-tabbar { padding-bottom: var(--sp-7); }
  .sidenav {
    position: sticky; top: 0; height: 100vh; padding: var(--sp-6) var(--sp-4);
    border-right: 1px solid var(--line); background: var(--bg);
  }
  .sidenav > a.row { min-height: var(--tap-min); }
  .sidenav .tab {
    flex-direction: row; justify-content: flex-start; gap: var(--sp-3);
    padding: 0 var(--sp-4); border-radius: var(--r); font-size: 14.5px; min-height: 46px;
  }
  .sidenav .tab[aria-current="page"] { background: color-mix(in srgb, var(--bizi-deep) 10%, transparent); }
}
@media (max-width: 899px) { .sidenav { display: none; } }

/* =========================================================== specialist ====
   Blocks that make a yacht not look like a coach appointment. Shared markup,
   chosen per product by its manifest, filled from data the merchant already
   gave us. Mobile-first: everything here is a horizontal scroller or a stack.
   ========================================================================= */

/* The fact strip: the four numbers that decide a purchase, under the price. */
.facts {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(88px, 1fr);
  gap: var(--sp-2); overflow-x: auto; scrollbar-width: none;
  padding: var(--sp-1) 0;
}
.facts::-webkit-scrollbar { display: none; }
.fact {
  display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  padding: var(--sp-3); border-radius: var(--r);
  background: var(--bg); border: 1px solid var(--line); min-width: 0;
}
.fact-icon { color: var(--brand); display: block; }
.fact-value { font-size: 15px; font-weight: 700; letter-spacing: -.01em; line-height: 1.2; }
.fact-label { font-size: 11.5px; color: var(--ink-3); font-weight: 600; }

/* A gallery that scrolls with a thumb and snaps. */
.gallery {
  display: grid; grid-auto-flow: column; grid-auto-columns: 78%;
  gap: var(--sp-2); overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: none; padding-bottom: 2px;
}
.gallery::-webkit-scrollbar { display: none; }
.gallery-item {
  scroll-snap-align: center; border-radius: var(--r-lg); overflow: hidden;
  background: var(--bg-3); aspect-ratio: 4 / 3;
}
.gallery-item img, .gallery-item svg { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (min-width: 720px) { .gallery { grid-auto-columns: 46%; } }

/* Who you are buying: the coach, the therapist. */
.people { display: grid; gap: var(--sp-2); }
.person {
  display: flex; gap: var(--sp-4); align-items: flex-start;
  padding: var(--sp-4); border-radius: var(--r-lg);
  background: var(--bg); border: 1px solid var(--line);
}
.person-photo { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; overflow: hidden; display: block; }
.person-photo svg { border-radius: 50%; }

/* The thing itself: a boat, a scooter, a room. */
.fleet {
  display: grid; grid-auto-flow: column; grid-auto-columns: 82%;
  gap: var(--sp-3); overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none;
}
.fleet::-webkit-scrollbar { display: none; }
.fleet-item {
  scroll-snap-align: center; border-radius: var(--r-lg); overflow: hidden;
  background: var(--bg); border: 1px solid var(--line);
}
.fleet-media { aspect-ratio: 3 / 2; background: var(--bg-3); overflow: hidden; }
.fleet-media img, .fleet-media svg { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (min-width: 720px) { .fleet { grid-auto-columns: 44%; } }

/* Order is the information, so the itinerary is numbered, not ticked. */
.itinerary { display: grid; gap: 0; counter-reset: step; }
.itinerary-step {
  display: grid; grid-template-columns: 76px 1fr; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); border-left: 2px solid var(--line); margin-left: var(--sp-2);
  position: relative;
}
.itinerary-step::before {
  content: ''; position: absolute; left: -6px; top: 20px;
  width: 10px; height: 10px; border-radius: 50%; background: var(--brand);
}
.itinerary-when { font-size: 13px; font-weight: 700; color: var(--brand); font-variant-numeric: tabular-nums; }
.itinerary-what { font-size: 14.5px; line-height: 1.45; }

/* -------------------------------------------------------------- photos --- */

/* The merchant's own photo grid. Square tiles because a merchant judging
   "is this the right main photo" wants them comparable, not artistic. */
.media-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: var(--sp-2);
}
.media-tile {
  position: relative; margin: 0; border-radius: var(--r); overflow: hidden;
  background: var(--bg-3); aspect-ratio: 1 / 1;
}
.media-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.media-badge {
  position: absolute; top: 4px; left: 4px; padding: 2px 6px; border-radius: 999px;
  background: rgba(0,0,0,.62); color: #fff; font-size: 11px; font-weight: 700;
  letter-spacing: .03em; text-transform: uppercase;
}
.media-actions {
  position: absolute; inset-inline: 0; bottom: 0; display: flex; justify-content: space-between;
  background: linear-gradient(to top, rgba(0,0,0,.55), transparent);
}
.media-actions .btn { color: #fff; min-height: 44px; min-width: 44px; }
.media-chip { border-radius: var(--r-sm, 8px); object-fit: cover; flex: none; }

/* The upload target. A whole tappable row, because a 44px button is the
   difference between a merchant adding photos and giving up. */
.upload-drop {
  display: flex; align-items: center; gap: var(--sp-3); min-height: 64px;
  padding: var(--sp-3); border: 1.5px dashed var(--line); border-radius: var(--r-lg);
  background: var(--bg); cursor: pointer; color: var(--ink);
}
.upload-drop:focus-within { border-color: var(--brand); }

/* ------------------------------------------------ what a customer holds --- */

/* Package progress. A bar, not a donut: "how many left" is one number. */
.meter { height: 8px; border-radius: 999px; background: var(--bg-3); overflow: hidden; width: 100%; }
.meter span { display: block; height: 100%; background: var(--brand); border-radius: 999px; }

/* Loyalty stamps. Real dots, because a progress bar does not feel like a card
   somebody carries in their wallet. */
.stamps { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--sp-2); justify-items: center; }
.stamp {
  width: 44px; height: 44px; border-radius: 50%; border: 1.5px dashed var(--line);
  display: grid; place-items: center; color: var(--brand-ink);
}
.stamp-on { background: var(--brand); border-style: solid; border-color: var(--brand); color: var(--brand-ink); }

/* A ticket. The notch is what makes it read as a ticket rather than a card. */
.ticket { position: relative; overflow: hidden; }
.ticket::before, .ticket::after {
  content: ''; position: absolute; width: 22px; height: 22px; border-radius: 50%;
  background: var(--bg-2); top: 50%; transform: translateY(-50%);
}
.ticket::before { left: -11px; }
.ticket::after { right: -11px; }
.ticket-used { opacity: .6; }
.ticket-qr { background: #fff; padding: var(--sp-3); border-radius: var(--r); display: inline-block; }
.ticket-qr svg { display: block; width: 100%; height: auto; max-width: 200px; }

/* Programme modules. */
.module { border: 1.5px solid var(--line); border-radius: var(--r); padding: var(--sp-3); background: var(--bg); }
.module + .module { margin-top: var(--sp-2); }
.module summary { cursor: pointer; min-height: var(--tap-min); display: flex; align-items: center; }

/* A ticked benefits list, for membership perks and what-is-included. */
.ticks { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--sp-2); }
.ticks li { display: flex; gap: var(--sp-2); align-items: flex-start; font-size: 15px; }
.ticks li svg { flex: none; margin-top: 2px; color: var(--ok); }

/* ---------------------------------------------------------- table codes --- */

.table-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: var(--sp-3); }
.table-card {
  background: var(--bg); border-radius: var(--r-lg); padding: var(--sp-3);
  display: grid; gap: var(--sp-2); justify-items: center; text-align: center;
}
.table-card .qr { background: #fff; padding: var(--sp-2); border-radius: var(--r); }
.table-card .qr svg { display: block; width: 100%; height: auto; max-width: 150px; }

/* The print sheet. Its own page, because a merchant with a phone and a print
   shop needs a page that prints, not a PDF dependency. */
.print-sheet { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--sp-4); }
.print-card {
  border: 1.5px dashed var(--line); border-radius: var(--r-lg); padding: var(--sp-4);
  display: grid; gap: var(--sp-2); justify-items: center; text-align: center; background: #fff;
  break-inside: avoid;
}
.print-card .qr svg { display: block; width: 100%; height: auto; max-width: 220px; }

@media print {
  .no-print, .appbar, .tabbar, .cta-bar { display: none !important; }
  body { background: #fff; }
  .print-sheet { grid-template-columns: repeat(2, 1fr); gap: 12mm; }
  .print-card { border-color: #bbb; page-break-inside: avoid; }
}

/* ---------------------------------------------------------------- summary --
   The figures directly above a pay button. Rows are quiet, the total is not:
   it is the number the customer is agreeing to, and it should be the number
   their eye lands on. */
.summary .total-row {
  margin-top: var(--sp-2); padding-top: var(--sp-2);
  border-top: 1px solid var(--line);
  font-weight: 700; font-size: 16px;
}
.summary .total-row .num { font-size: 18px; letter-spacing: -.01em; }

/* --------------------------------------------------------------- calendar --
   The availability calendar for stays and rentals.

   A month you can look at, on a phone, with the taken nights visibly taken.
   Cells are 40px minimum because a thumb is about 44 and a mis-tapped date on
   a villa booking is an expensive mistake to discover at checkout. */
.cal { display: flex; flex-direction: column; gap: var(--sp-4); }
.cal-month { min-width: 0; }
.cal-title {
  font-size: 14px; font-weight: 700; margin: 0 0 var(--sp-2);
  color: var(--ink-2); letter-spacing: -.01em;
}
.cal-grid {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 2px; align-items: center;
}
.cal-dow {
  font-size: 11px; font-weight: 700; color: var(--ink-3);
  text-align: center; padding-bottom: 4px;
}
.cal-pad { min-height: 40px; }
.cal-day {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap-min); padding: 0; border: 0; border-radius: var(--r);
  background: transparent; color: var(--ink); font: inherit;
  font-size: 14px; font-weight: 600; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.cal-day:hover:not(.is-off) { background: var(--bg-3); }
.cal-day:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

/* Taken is struck through, not merely greyed: colour alone does not survive a
   bright Phuket afternoon or a customer who cannot distinguish it. */
.cal-day.is-off {
  color: var(--ink-3); cursor: default; opacity: .45;
  text-decoration: line-through; text-decoration-thickness: 1px;
}
.cal-day.is-between { background: var(--brand-soft); border-radius: 0; }
.cal-day.is-start, .cal-day.is-end {
  background: var(--brand); color: #fff; font-weight: 700;
}
.cal-day.is-start { border-radius: var(--r) 0 0 var(--r); }
.cal-day.is-end { border-radius: 0 var(--r) var(--r) 0; }
.cal-day.is-start.is-end { border-radius: var(--r); }

.range-picked {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3); border: 1px solid var(--line);
  border-radius: var(--r-lg); background: var(--bg);
}
.range-picked > div { min-width: 0; flex: 1; }
.range-picked strong { display: block; font-size: 15px; letter-spacing: -.01em; }
.range-arrow { color: var(--ink-3); flex: 0 0 auto; display: block; }

/* Side by side only where a month still has room to be tappable. Seven
   columns need about 260px before a day cell drops under the 32px floor, and
   three months crammed into a 560px column is 26px a cell. */
@media (min-width: 620px) {
  .cal {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--sp-5);
  }
}


/* =============================================================================
   THE MOBILE SYSTEM
   -----------------------------------------------------------------------------
   The components a merchant screen is actually made of. Everything below is
   shared: a status is written one way in Today, in Sales, in Messages and on
   the customer's own link, because three vocabularies for "they have paid" is
   three chances to say it differently.
   ========================================================================== */

/* ------------------------------------------------------------ status pill --
   Status is never colour alone. Every pill carries its own word, and the
   dot is a second, non-colour cue for the same fact — a merchant reading a
   phone in daylight on a Chalong forecourt gets no gradients. */
.status {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 700; letter-spacing: -.005em; white-space: nowrap;
  background: var(--bg-3); color: var(--ink-2);
}
.status::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: 0 0 auto;
}
.status-paid      { background: var(--ok-bg);   color: var(--ok); }
.status-part      { background: var(--ok-bg);   color: var(--ok); }
.status-due       { background: var(--warn-bg); color: var(--warn); }
.status-off       { background: var(--err-bg);  color: var(--err); }
.status-new       { background: color-mix(in srgb, var(--brand) 12%, var(--bg)); color: var(--brand); }
.status-done      { background: var(--bg-3);    color: var(--ink-2); }

/* --------------------------------------------------------------- tiles -----
   The four numbers a merchant opens the app to see. Compact and in one row:
   a KPI that needs a scroll is a KPI nobody reads. Below 340px they scroll
   horizontally rather than wrap into a ragged second line. */
.tiles {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--sp-2);
}
.tile {
  padding: var(--sp-3) var(--sp-2); border-radius: var(--r);
  background: var(--bg); border: 1px solid var(--line);
  text-align: left; min-width: 0;
}
.tile-value {
  font-size: 17px; font-weight: 750; letter-spacing: -.028em; line-height: 1.15;
  font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tile-label {
  font-size: 11px; font-weight: 600; color: var(--ink-3); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tile-due .tile-value { color: var(--warn); }
.tile-paid .tile-value { color: var(--ok); }
@media (max-width: 339px) {
  .tiles { display: flex; overflow-x: auto; scrollbar-width: none; }
  .tiles::-webkit-scrollbar { display: none; }
  .tile { flex: 0 0 40%; }
}

/* --------------------------------------------------------- section header --
   A heading and, where there is somewhere to go, the way there. The link is
   a real tap target rather than 14px of underlined text. */
.sec-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3); margin-bottom: var(--sp-3);
}
.sec-head h2, .sec-head .h2 { margin: 0; }
.sec-more {
  font-size: 14px; font-weight: 650; color: var(--brand);
  display: inline-flex; align-items: center; gap: 2px;
  min-height: var(--tap-min); padding-inline: var(--sp-2); margin-right: calc(var(--sp-2) * -1);
}

/* -------------------------------------------------------------- agenda -----
   Today, as a diary. The time is a fixed gutter so the eye runs down one
   column of times rather than hunting for them inside each card. */
.agenda { display: flex; flex-direction: column; gap: var(--sp-2); }
.agenda-item {
  display: flex; gap: var(--sp-3); align-items: stretch;
  padding: var(--sp-3) var(--sp-4); width: 100%; text-align: left;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1); cursor: pointer; min-height: 64px;
}
.agenda-item:active { background: var(--bg-2); }
.agenda-time {
  flex: 0 0 52px; font-size: 14px; font-weight: 750; color: var(--ink);
  font-variant-numeric: tabular-nums; padding-top: 1px; line-height: 1.3;
}
.agenda-time .tiny { font-weight: 600; }
.agenda-body { flex: 1 1 auto; min-width: 0; display: block; color: inherit; }
a.agenda-body { min-height: 44px; display: flex; flex-direction: column; justify-content: center; }
/* display:block, because these are spans inside a flex item and an inline span
   does not start a new line. The name and the product ran together on one row
   as "Marek Kowalski Private Muay Thai". */
.agenda-who {
  display: block;
  font-size: 15px; font-weight: 680; letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.agenda-what {
  display: block;
  font-size: 13px; color: var(--ink-3); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.agenda-side { flex: 0 0 auto; display: flex; align-items: center; }
/* A hairline of the product's own colour, so a spa hour and a boat hour are
   distinguishable at a glance without turning the list into a paint chart. */
.agenda-item { border-left: 3px solid var(--tone, var(--line)); }

/* ------------------------------------------------------------- messages ----
   One row per customer: who, the last thing that passed between you, when. */
.msg-row {
  display: flex; gap: var(--sp-3); align-items: center;
  padding: var(--sp-3) var(--sp-4); width: 100%; text-align: left;
  background: var(--bg); border: 0; border-bottom: 1px solid var(--line-2);
  cursor: pointer; min-height: 72px;
}
.msg-row:last-child { border-bottom: 0; }
.msg-row:active { background: var(--bg-2); }
.msg-body { flex: 1 1 auto; min-width: 0; }
.msg-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2); }
.msg-who { font-size: 15px; font-weight: 680; letter-spacing: -.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-when { font-size: 12px; color: var(--ink-3); flex: 0 0 auto; }
.msg-last {
  font-size: 13.5px; color: var(--ink-3); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msg-thread { display: flex; flex-direction: column; gap: var(--sp-3); }
.msg-bubble {
  max-width: 88%; padding: var(--sp-3) var(--sp-4); border-radius: var(--r-lg);
  background: var(--bg-3); font-size: 14.5px; line-height: 1.5; white-space: pre-wrap;
}
.msg-out { align-self: flex-end; background: color-mix(in srgb, var(--brand) 10%, var(--bg)); }
.msg-in { align-self: flex-start; }
.msg-meta { font-size: 11.5px; color: var(--ink-3); margin-top: 4px; }

/* --------------------------------------------------------- opportunities ---
   BIZI's most distinctive pattern: a problem the merchant's OWN numbers
   found, and one action. Cool-blue surface so it reads as a different kind
   of object from every white card around it — the machine noticed something. */
.opp {
  padding: var(--sp-4); border-radius: var(--r-lg);
  background: var(--bg-sky);
  border: 1px solid color-mix(in srgb, var(--bizi-blue) 22%, transparent);
}
.opp + .opp { margin-top: var(--sp-2); }
.opp-icon {
  width: 38px; height: 38px; border-radius: 12px; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--bizi-blue); color: #fff;
}
.opp-evidence { display: block; font-size: 15.5px; font-weight: 720; letter-spacing: -.014em; line-height: 1.3; }
.opp-outcome { display: block; font-size: 13px; color: var(--ink-2); line-height: 1.4; margin-top: 2px; }
.opp .btn { margin-top: var(--sp-3); width: 100%; }

/* ---------------------------------------------------- your products row ----
   The merchant's own products as small visual cards, one thumb row. */
.hprod-row {
  display: flex; gap: var(--sp-3); overflow-x: auto; scrollbar-width: none;
  margin-inline: calc(var(--sp-5) * -1); padding: 2px var(--sp-5);
}
.hprod-row::-webkit-scrollbar { display: none; }
.hprod { flex: 0 0 auto; width: 132px; color: var(--ink); }
.hprod-media {
  display: block; width: 132px; height: 88px; border-radius: var(--r);
  overflow: hidden; border: 1px solid var(--line); background: var(--bg-3);
}
.hprod-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hprod-tile {
  width: 100%; height: 100%; display: grid; place-items: center;
  background: color-mix(in srgb, var(--tone, var(--brand)) 12%, var(--bg));
  color: var(--tone, var(--brand));
}
.hprod-name {
  display: block; margin-top: 6px; font-size: 12.5px; font-weight: 650; line-height: 1.25;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* ------------------------------------------------------------ quick acts ---
   Four things a merchant does often, at thumb size, without a card each the
   size of a postcard. */
.qa-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-2); }
.qa {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  min-height: 76px; padding: var(--sp-2); border-radius: var(--r);
  background: var(--bg); border: 1px solid var(--line); text-align: center;
  font-size: 11.5px; font-weight: 650; color: var(--ink-2); line-height: 1.2;
}
.qa:active { background: var(--bg-2); }
.qa-icon {
  width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center;
  background: color-mix(in srgb, var(--brand) 10%, var(--bg)); color: var(--brand);
}

/* ------------------------------------------------------------- empty -------
   An empty screen should say what will fill it, not apologise. */
.empty { text-align: center; padding: var(--sp-7) var(--sp-4); }
.empty-icon {
  width: 52px; height: 52px; border-radius: 16px; margin: 0 auto var(--sp-4);
  display: grid; place-items: center; background: var(--bg-3); color: var(--ink-3);
}
.empty h3 { font-size: 16px; font-weight: 700; margin-bottom: 4px; }
.empty p { font-size: 14px; color: var(--ink-3); max-width: 34ch; margin-inline: auto; }

/* ------------------------------------------------------------ identity -----
   Who you are, at the top of your own business. */
.biz-head { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-4) 0 var(--sp-2); }
.biz-name { font-size: 19px; font-weight: 760; letter-spacing: -.02em; line-height: 1.2; }
.biz-sub { font-size: 13px; color: var(--ink-3); display: inline-flex; align-items: center; gap: 4px; }

/* A bare checkbox or radio that is NOT visually replaced still has to be big
   enough to hit. Measured at 13x13 on the consent rows. */
input[type="checkbox"]:not(.seg input):not(.itemcard input),
input[type="radio"]:not(.seg input):not(.itemcard input) {
  width: 22px; height: 22px; flex: 0 0 auto;
}

/* The tab bar's current item gets weight and a mark, not only a colour. */
.tab[aria-current="page"] { font-weight: 750; }
.tab[aria-current="page"] svg { transform: translateY(-1px); }

/* --------------------------------------------------------- promo assets ----
   A Story is authored at 1080×1920 because that is what Instagram wants, and
   the SVG carries those numbers. Dropped into a 260px preview with the size
   attributes intact it renders at full size and gets clipped to its top-left
   corner, which on a dark-topped design looks like an empty box. The frame
   sets the size; the artwork fills it. */
.asset-frame { overflow: hidden; }
.asset-frame > svg {
  display: block; width: 100%; height: 100%;
}


/* =============================================================================
   THE DESIGN SYSTEM PAGE, AND THE PRIMITIVES IT DOCUMENTS
   ========================================================================== */

.ds-block { padding-bottom: var(--sp-6); border-bottom: 1px solid var(--line); }
.ds-block:last-of-type { border-bottom: 0; }
.ds-swatch { display: flex; align-items: center; gap: var(--sp-3); }
.ds-chipcolor {
  width: 48px; height: 34px; border-radius: var(--r-xs); flex: 0 0 auto;
  border: 1px solid rgba(7,26,51,.10); box-shadow: var(--e-1);
}

/* ------------------------------------------------------------ success ------
   The moment a merchant's product goes live, or a customer's booking lands.
   It is worth a real gesture: this is the screen they screenshot. */
.success-mark {
  width: 76px; height: 76px; border-radius: 50%; margin: 0 auto;
  display: grid; place-items: center;
  background: var(--ok-bg); color: var(--ok);
  box-shadow: 0 0 0 10px color-mix(in srgb, var(--ok) 8%, transparent);
}

/* ------------------------------------------------------- product card -----
   The store's unit. Image first, then category, name, one outcome sentence,
   and the two facts that decide it: what it costs and how long setup takes.
   A twenty-five-card grid of these is a marketplace; a grid of text is a
   spreadsheet. */
.pcard {
  display: flex; gap: var(--sp-4); align-items: stretch;
  padding: var(--sp-3); border-radius: var(--r-lg);
  background: var(--bg); border: 1px solid var(--line); box-shadow: var(--e-1);
  transition: transform .14s ease, box-shadow .14s ease;
  color: var(--ink);
}
.pcard:hover { box-shadow: var(--e-2); }
.pcard:active { transform: scale(.99); }
.pcard-art {
  flex: 0 0 96px; width: 96px; border-radius: var(--r); overflow: hidden;
  background: var(--bg-3); position: relative; align-self: stretch; min-height: 96px;
}
.pcard-art svg, .pcard-art img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block;
}
/* Generated artwork carries its authored size, which is right for a download
   and wrong for a box. Anywhere it is used as a fill, the box decides. */
/* display:block matters: these are spans, and an inline element ignores
   aspect-ratio and gives an absolutely-positioned child nothing to sit in.
   The store's hero card rendered as a blank white box for exactly that. */
.art-fill { display: block; position: relative; overflow: hidden; background: var(--bg-3); }
.art-fill > svg, .art-fill > img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block;
}
.pcard-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; padding: var(--sp-2) var(--sp-2) var(--sp-2) 0; }
.pcard-cat {
  font-size: 11px; font-weight: 750; letter-spacing: .08em; text-transform: uppercase;
  color: var(--tone, var(--brand));
}
.pcard-name { font-size: 15.5px; font-weight: 720; letter-spacing: -.016em; line-height: 1.22; }
.pcard-outcome {
  font-size: 13.5px; color: var(--ink-2); line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.pcard-foot {
  margin-top: auto; padding-top: var(--sp-2);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
}
.pcard-price { font-size: 13.5px; font-weight: 720; letter-spacing: -.01em; }
.pcard-added { display: inline-flex; align-items: center; gap: 4px; color: var(--ok); }
.pcard-added svg { width: 14px; height: 14px; }
.pcard-mins {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 11.5px; font-weight: 600; color: var(--ink-3); white-space: nowrap;
}
.pcard-mins svg { width: 13px; height: 13px; }
.pcard.is-installed { border-color: color-mix(in srgb, var(--ok) 40%, var(--line)); }


/* =============================================================================
   THE STORE
   ========================================================================== */

/* The promise, on BIZI's one dark surface, and then straight into the work.
   The old hero was a coloured band with a heading in it; this one is the first
   thing that makes the store feel like a place rather than a list. */
.store-hero { padding: var(--sp-7) 0 var(--sp-8); }
.store-hero .d1 { max-width: 13ch; }
/* The shop itself opens light: warm paper, one dark question, the search.
   (The dark band above still opens the narrative pages — /fix, /for.) */
.store-top { padding-top: var(--sp-6); }
.store-title { max-width: 14ch; }
@media (min-width: 720px) { .store-title { max-width: 22ch; } }
.store-body { padding-block: var(--sp-6) var(--sp-9); }
.store-body > * + * { margin-top: var(--sp-7); }

/* Problem chips: five doors, one thumb row. White pills so they read as
   controls against the warm page, not as more cards. */
.probchips { margin-inline: calc(var(--sp-5) * -1); padding-inline: var(--sp-5); }
.probchip { background: var(--bg); border: 1px solid var(--line); color: var(--ink); font-weight: 640; }
.probchip svg { color: var(--brand); }
.store-checkup {
  display: inline-flex; align-items: center; gap: 6px; min-height: var(--tap-min);
  margin-top: var(--sp-2); font-size: 13.5px; font-weight: 620; color: var(--ink-2);
}
.store-checkup svg:first-child { color: var(--brand); }

/* Popular in Phuket: the trades as photographs. */
.trow {
  display: flex; gap: var(--sp-3); overflow-x: auto; scrollbar-width: none;
  margin-inline: calc(var(--sp-5) * -1); padding: 2px var(--sp-5);
}
.trow::-webkit-scrollbar { display: none; }
.tcard { flex: 0 0 auto; width: 104px; color: var(--ink); }
.tcard-media {
  display: block; width: 104px; height: 104px; border-radius: var(--r-lg);
  overflow: hidden; border: 1px solid var(--line); background: var(--bg-3);
}
.tcard-media img, .tcard-media svg { width: 100%; height: 100%; object-fit: cover; display: block; }
.tcard:active .tcard-media { transform: scale(.97); }
.tcard-label {
  display: block; margin-top: 7px; font-size: 12.5px; font-weight: 650;
  line-height: 1.25; text-align: center;
}

/* Browse by goal: the collections as doors, not shelves. */
.goal-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.goalrow {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--tap); padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r); background: var(--bg); border: 1px solid var(--line);
  color: var(--ink);
}
.goalrow:active { transform: scale(.99); }
.goalrow > svg { color: var(--ink-3); flex: 0 0 auto; }
.goal-title { display: block; font-size: 15px; font-weight: 700; letter-spacing: -.012em; }
.goal-sub {
  display: block; font-size: 12.5px; color: var(--ink-3); line-height: 1.35; margin-top: 1px;
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden;
}
.goal-n { font-size: 12.5px; font-weight: 700; color: var(--ink-3); }

/* ------------------------------------------------------------ categories ---
   A trade as a thing, not as a filter label. Icon over word, thumb-sized,
   scrolling horizontally so six trades do not become two ragged rows. */
.catrow {
  display: flex; gap: var(--sp-2); overflow-x: auto; scrollbar-width: none;
  padding: 2px 0; margin-inline: calc(var(--sp-4) * -1); padding-inline: var(--sp-4);
}
.catrow::-webkit-scrollbar { display: none; }
.cat {
  flex: 0 0 auto; width: 76px; min-height: 76px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  border-radius: var(--r); background: var(--bg); border: 1px solid var(--line);
  color: var(--ink-2); text-align: center; box-shadow: var(--e-1);
  transition: transform .12s ease;
}
.cat:active { transform: scale(.97); }
.cat-icon {
  width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center;
  background: var(--bg-sky); color: var(--brand);
}
.cat-label { font-size: 11.5px; font-weight: 650; line-height: 1.15; padding-inline: 4px; }
.cat.is-on {
  background: var(--bizi-midnight); border-color: var(--bizi-midnight); color: #fff;
  box-shadow: var(--e-2);
}
.cat.is-on .cat-icon { background: rgba(255,255,255,.16); color: #fff; }

/* --------------------------------------------------------------- hero card --
   One per screen. A page of hero cards is a page of no heroes. */
.hcard {
  display: block; border-radius: var(--r-xl); overflow: hidden;
  background: var(--bg); border: 1px solid var(--line); box-shadow: var(--e-2);
  color: var(--ink); transition: transform .14s ease, box-shadow .14s ease;
}
.hcard:hover { box-shadow: var(--e-3); }
.hcard:active { transform: scale(.99); }
.hcard-art { aspect-ratio: 16 / 10; }
.hcard-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(6,14,28,.86) 0%, rgba(6,14,28,.30) 46%, transparent 72%);
}
.hcard-over {
  position: absolute; left: 0; right: 0; bottom: 0; padding: var(--sp-5);
  display: flex; flex-direction: column; gap: 4px;
}
.hcard-cat {
  font-size: 11px; font-weight: 750; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255,255,255,.78);
}
.hcard-name {
  font-size: 24px; font-weight: 780; letter-spacing: -.028em; line-height: 1.1; color: #fff;
  text-wrap: balance;
}
.hcard-body { padding: var(--sp-4) var(--sp-5) var(--sp-5); display: block; }
.hcard-outcome { font-size: 15px; line-height: 1.45; color: var(--ink-2); display: block; }
.hcard-foot {
  margin-top: var(--sp-4); display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}

/* A product card in a scrolling row rather than a stack. */
.pcard-row {
  display: flex; gap: var(--sp-3); overflow-x: auto; scrollbar-width: none;
  margin-inline: calc(var(--sp-4) * -1); padding: 2px var(--sp-4);
}
.pcard-row::-webkit-scrollbar { display: none; }
.pcard-row > .pcard { flex: 0 0 84%; }

/* ------------------------------------------------------------- problems ----
   "What do you want to fix?" — the first thing the store asks. Six pains in
   the owner's own words, each one a door. Two columns on a phone so all six
   fit one screenful; three from tablet up. */
.probgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
@media (min-width: 720px) { .probgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.prob {
  display: flex; flex-direction: column; gap: 6px; padding: var(--sp-3);
  border-radius: var(--r-lg); background: var(--bg); border: 1px solid var(--line);
  box-shadow: var(--e-1); color: var(--ink);
  transition: transform .12s ease, box-shadow .12s ease;
}
.prob:hover { box-shadow: var(--e-2); }
.prob:active { transform: scale(.98); }
.prob-icon {
  width: 34px; height: 34px; border-radius: 11px; display: grid; place-items: center;
  background: var(--bg-sky); color: var(--brand);
}
.prob-title { font-size: 13.5px; font-weight: 720; letter-spacing: -.01em; line-height: 1.2; }
.prob-pain {
  font-size: 12px; color: var(--ink-3); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* -------------------------------------------------------- featured cards ---
   A collection as a scrolling row of upright cards: art on top, outcome
   under. Sized so about two and a half show on a phone — enough to say "this
   scrolls" while every card stays readable. */
.fcard-row {
  display: flex; gap: var(--sp-3); overflow-x: auto; scrollbar-width: none;
  margin-inline: calc(var(--sp-4) * -1); padding: 2px var(--sp-4) 6px;
  scroll-snap-type: x proximity;
}
.fcard-row::-webkit-scrollbar { display: none; }
.fcard {
  flex: 0 0 232px; scroll-snap-align: start;
  display: flex; flex-direction: column; border-radius: var(--r-lg); overflow: hidden;
  background: var(--bg); border: 1px solid var(--line); box-shadow: var(--e-1);
  color: var(--ink); transition: transform .14s ease, box-shadow .14s ease;
}
@media (min-width: 900px) { .fcard { flex-basis: 256px; } }
.fcard:hover { box-shadow: var(--e-2); }
.fcard:active { transform: scale(.99); }
.fcard-art { aspect-ratio: 16 / 10; }
.fcard-body { display: flex; flex-direction: column; gap: 3px; padding: var(--sp-3); flex: 1; }
.fcard-name { font-size: 14.5px; font-weight: 720; letter-spacing: -.014em; line-height: 1.2; }
.fcard-outcome {
  font-size: 12.5px; color: var(--ink-2); line-height: 1.38;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.fcard-foot {
  margin-top: auto; padding-top: var(--sp-2);
  display: flex; align-items: center; justify-content: space-between; color: var(--ink-3);
}
.fcard.is-installed { border-color: color-mix(in srgb, var(--ok) 40%, var(--line)); }

/* =============================================================================
   CREATIVE STUDIO
   ========================================================================== */

/* Pick YOUR photo. A radio dressed as the photograph itself. */
.photopick-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2); }
.photopick {
  position: relative; display: block; aspect-ratio: 1; border-radius: var(--r);
  overflow: hidden; background: var(--bg-3); cursor: pointer;
}
.photopick input { position: absolute; opacity: 0; inset: 0; }
.photopick img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.photopick-check {
  position: absolute; top: 6px; right: 6px; width: 24px; height: 24px; border-radius: 999px;
  display: grid; place-items: center; background: rgba(255,255,255,.85); color: transparent;
  transition: background .12s ease, color .12s ease;
}
.photopick:has(input:checked) { box-shadow: 0 0 0 3px var(--brand); }
.photopick:has(input:checked) .photopick-check { background: var(--brand); color: #fff; }
.photopick:has(input:focus-visible) { box-shadow: 0 0 0 3px var(--brand-soft), 0 0 0 5px var(--brand); }

/* A style, by name. A radio dressed as a chip. */
.stylechip { position: relative; display: inline-flex; }
.stylechip input { position: absolute; opacity: 0; inset: 0; }
.stylechip .chip { cursor: pointer; user-select: none; }
.stylechip:has(input:checked) .chip {
  background: var(--bizi-midnight); border-color: var(--bizi-midnight); color: #fff;
}
.stylechip:has(input:focus-visible) .chip { box-shadow: 0 0 0 3px var(--brand-soft); }

/* ------------------------------------------------------------- compare -----
   Side by side from tablet width; on a phone a two-way toggle over one frame.
   Radios drive it, so the keyboard flips it too. Without :has() support the
   original stays visible — degraded, never dishonest. */
.compare-input {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.compare { position: relative; }
.compare-pair { display: grid; gap: var(--sp-3); }
.compare-fig { margin: 0; display: flex; flex-direction: column; gap: 4px; }
.compare-fig img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
  border-radius: var(--r-sm); background: var(--bg-3);
}
.compare-toggle {
  display: flex; gap: 4px; background: var(--bg-3); border-radius: 999px; padding: 3px;
}
.compare-toggle label {
  flex: 1; text-align: center; padding: 11px 0; border-radius: 999px;
  font-size: 13px; font-weight: 650; color: var(--ink-2); cursor: pointer;
}
@media (max-width: 719.9px) {
  .compare-fig[data-side="after"] { display: none; }
  .compare:has(input[value="after"]:checked) .compare-fig[data-side="before"] { display: none; }
  .compare:has(input[value="after"]:checked) .compare-fig[data-side="after"] { display: flex; }
  .compare:has(input[value="before"]:checked) .compare-toggle label[data-side="before"],
  .compare:has(input[value="after"]:checked) .compare-toggle label[data-side="after"] {
    background: var(--bg); color: var(--ink); box-shadow: var(--e-1);
  }
}
@media (min-width: 720px) {
  .compare-pair { grid-template-columns: 1fr 1fr; }
  .compare-toggle { display: none; }
}
.compare:has(input:focus-visible) .compare-toggle { box-shadow: 0 0 0 3px var(--brand-soft); }

/* --------------------------------------------------- context previews ------
   The enhanced photo inside the real templates, at postcard size, scrolling
   sideways. Fixed height, aspect ratio per template. */
.ctxrow {
  display: flex; gap: var(--sp-3); overflow-x: auto; scrollbar-width: none;
  margin-inline: calc(var(--sp-4) * -1); padding: 2px var(--sp-4) 6px; align-items: flex-start;
}
.ctxrow::-webkit-scrollbar { display: none; }
.ctx {
  margin: 0; flex: 0 0 auto; height: 300px; position: relative;
  border-radius: var(--r); overflow: hidden; background: var(--bg-3);
  border: 1px solid var(--line); box-shadow: var(--e-1);
}
.ctx > svg { width: 100%; height: 100%; display: block; }
.ctx img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ctx figcaption {
  position: absolute; left: 8px; bottom: 6px; z-index: 2;
  background: rgba(255,255,255,.88); border-radius: 6px; padding: 2px 7px;
}
/* When the template itself writes at the bottom, the caption moves up. */
.ctx .ctx-cap-top { bottom: auto; top: 6px; }

/* ------------------------------------------------------------- the funnel --
   Attention → repeat as a numbered rail. Educational, not navigational —
   the chips inside are the links. */
.funnel { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.funnel-step {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3); border-radius: var(--r-lg);
  background: var(--bg); border: 1px solid var(--line); box-shadow: var(--e-1);
}
.funnel-n {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 999px;
  display: grid; place-items: center; font-size: 12.5px; font-weight: 750;
  background: var(--bg-sky); color: var(--brand);
}

/* The dev creative lab's contact sheet. */
.lab-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-3); }
@media (min-width: 720px) { .lab-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.lab-cell { margin: 0; display: flex; flex-direction: column; gap: 4px; }
.lab-cell .art-fill { border-radius: var(--r-sm); }


/* =============================================================================
   PRODUCT DETAIL
   ========================================================================== */

/* The artwork is the header, the name sits in it, and the back button floats
   over the top. A coloured band with a heading in it is a landing page; this
   is a product. */
.pdp-hero { min-height: 340px; display: flex; flex-direction: column; }
.pdp-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(6,14,28,.92) 4%, rgba(6,14,28,.34) 52%, rgba(6,14,28,.34) 100%);
}
.pdp-top {
  position: relative; z-index: 2;
  padding: calc(var(--sp-3) + var(--safe-t)) var(--sp-3) 0;
}
.pdp-back { background: rgba(255,255,255,.16); color: #fff; backdrop-filter: blur(8px); }
.pdp-back:hover { background: rgba(255,255,255,.26); }
.pdp-head { position: relative; z-index: 2; margin-top: auto; padding-bottom: var(--sp-6); }
.pdp-name {
  font-size: clamp(28px, 8vw, 38px); font-weight: 800; letter-spacing: -.034em;
  line-height: 1.04; color: #fff; margin-top: 4px; text-wrap: balance;
}
.pdp-outcome { font-size: 16px; line-height: 1.45; color: rgba(255,255,255,.84); margin-top: var(--sp-3); max-width: 30ch; }
.chip-glass { background: rgba(255,255,255,.16); color: #fff; backdrop-filter: blur(8px); }

/* ------------------------------------------------------ what they can do ---
   Three things the CUSTOMER can do, because that is what the merchant is
   actually buying. */
.capgrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-2); }
.cap {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: var(--sp-2);
  padding: var(--sp-4) var(--sp-2); border-radius: var(--r);
  background: var(--bg); border: 1px solid var(--line); box-shadow: var(--e-1); text-align: center;
}
.cap-label { font-size: 12.5px; font-weight: 620; line-height: 1.25; color: var(--ink-2); }

/* --------------------------------------------------------------- preview ---
   A real page, at phone width, inside a phone. The one thing a description
   cannot do, and the question every merchant asks before paying. */
.preview { display: flex; justify-content: center; padding: var(--sp-2) 0 var(--sp-4); }
/* Two live phones side by side: scrolls on a phone, centres on a desk. */
.preview-row {
  display: flex; gap: var(--sp-4); overflow-x: auto; scrollbar-width: none;
  padding: var(--sp-2) var(--sp-4) var(--sp-4);
  margin-inline: calc(var(--sp-4) * -1);
}
.preview-row::-webkit-scrollbar { display: none; }
@media (min-width: 720px) { .preview-row { justify-content: center; overflow: visible; } }
.preview-phone {
  position: relative; width: 236px; height: 420px;
  border-radius: 30px; padding: 8px;
  background: linear-gradient(160deg, #2A3550, var(--bizi-midnight));
  box-shadow: var(--e-3);
  flex: 0 0 auto;
}
/* No notch. It read as a device, and it sat on top of the first twenty pixels
   of the page it was there to show — decoration bought with content. */
.preview-frame {
  width: 390px; height: 694px; border: 0; display: block;
  transform: scale(.5641); transform-origin: 0 0;
  border-radius: 22px; background: var(--bg);
  pointer-events: none;
}
.preview-phone > .preview-frame { position: absolute; top: 8px; left: 8px; }
.preview-phone { overflow: hidden; }

/* ----------------------------------------------------------------- steps ---
   Numbered, with a rule running between them so four steps read as a sequence
   rather than as four unrelated rows. */
.steps { list-style: none; margin: 0; padding: 0; }
.step { display: flex; gap: var(--sp-4); align-items: flex-start; position: relative; padding-bottom: var(--sp-5); }
.step:last-child { padding-bottom: 0; }
.step::before {
  content: ''; position: absolute; left: 17px; top: 36px; bottom: 6px; width: 2px;
  background: var(--line); border-radius: 2px;
}
.step:last-child::before { display: none; }
.step-n {
  flex: 0 0 auto; width: 36px; height: 36px; border-radius: 50%;
  display: grid; place-items: center; position: relative; z-index: 1;
  background: var(--bizi-midnight); color: #fff;
  font-size: 14px; font-weight: 750; font-variant-numeric: tabular-nums;
}
.step-body { display: flex; flex-direction: column; gap: 2px; padding-top: 7px; min-width: 0; }
/* The chevron belongs beside the title, not on a line of its own. */
.step-body > a.h3 { display: inline-flex; align-items: center; gap: 4px; color: var(--ink); }
.step-body > a.h3 svg { flex: 0 0 auto; color: var(--brand); }


/* ------------------------------------------------------------ date strip ---
   A week you can see. Two chevrons make a merchant tap forward four times and
   count to answer "is Saturday busy". */
.datestrip {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px;
}
.dchip {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  min-height: 56px; border-radius: var(--r-sm);
  background: var(--bg); border: 1px solid var(--line); color: var(--ink-2);
  transition: transform .12s ease;
}
.dchip:active { transform: scale(.96); }
.dchip-dow { font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-3); }
.dchip-day { font-size: 16px; font-weight: 720; font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
/* Today is marked even when you are looking at another day, so you always know
   where you are relative to now. */
.dchip.is-now { border-color: var(--brand-line); }
.dchip.is-now .dchip-dow { color: var(--brand); }
.dchip.is-on {
  background: var(--bizi-midnight); border-color: var(--bizi-midnight); color: #fff;
  box-shadow: var(--e-2);
}
.dchip.is-on .dchip-dow { color: rgba(255,255,255,.7); }


/* ---------------------------------------------------------------- menu -----
   A menu is read standing up, one-handed, on a phone the customer is also
   using to talk to the table. Every row gets a visual slot: a column of text
   with prices on the right is a price list, and a menu is supposed to make
   somebody hungry. */
.dish {
  display: flex; gap: var(--sp-4); align-items: flex-start;
  padding: var(--sp-3); border-radius: var(--r-lg);
  background: var(--bg); border: 1px solid var(--line); box-shadow: var(--e-1);
}
.dish-shot {
  flex: 0 0 auto; width: 84px; height: 84px; border-radius: var(--r);
  object-fit: cover; background: var(--bg-3); display: block;
}
.dish-body { flex: 1 1 auto; min-width: 0; padding-top: 2px; }
.dish-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); }
.dish-name { font-size: 15.5px; font-weight: 680; letter-spacing: -.014em; line-height: 1.28; }
.dish-price { font-size: 15.5px; font-weight: 750; letter-spacing: -.02em; flex: 0 0 auto; }
.dish-note { font-size: 13.5px; line-height: 1.45; color: var(--ink-3); margin-top: 4px; }


/* ----------------------------------------------------------------- live ----
   The activation moment. A merchant has just spent four minutes typing prices
   into a phone; what they get back should not be a green toast above a form. */
.live {
  padding: var(--sp-8) 0 calc(var(--sp-9) + var(--safe-b));
  background:
    radial-gradient(90% 60% at 50% 0%, color-mix(in srgb, var(--ok) 12%, transparent), transparent 70%),
    var(--bg-2);
  min-height: 100%;
}
.live .d2, .live .lede { text-wrap: balance; }
.live .lede { max-width: 30ch; margin-inline: auto; }


/* ----------------------------------------------------------------- pass ----
   What a customer opens at the door, one-handed. Built as a pass rather than
   as a record: dark, self-contained, the reference readable across a counter,
   and everything that matters visible without scrolling. */
.pass {
  border-radius: var(--r-xl); padding: var(--sp-5);
  box-shadow: var(--e-3); overflow: hidden;
}
.pass-top { display: flex; align-items: flex-start; gap: var(--sp-3); }
.pass-title {
  font-size: 24px; font-weight: 780; letter-spacing: -.028em; line-height: 1.12;
  color: #fff; margin-top: 4px; text-wrap: balance;
}
.pass-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4); margin-top: var(--sp-5);
}
.pass-cell { min-width: 0; }
.pass-label {
  display: block; font-size: 11px; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: rgba(255,255,255,.55);
}
.pass-value {
  display: block; margin-top: 3px; font-size: 15px; font-weight: 650;
  color: #fff; line-height: 1.3;
}
/* The tear-off: a dashed rule, because that is what a ticket looks like and
   the reference number is the part somebody reads out loud. */
.pass-stub {
  margin-top: var(--sp-5); padding-top: var(--sp-4);
  border-top: 1px dashed rgba(255,255,255,.26);
  display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-3);
}
.pass-code {
  display: block; margin-top: 3px;
  font-size: 21px; font-weight: 700; letter-spacing: .06em; color: #fff;
}
.pass-status .status { background: rgba(255,255,255,.16); color: #fff; }
.pass-status .status-paid, .pass-status .status-done { background: rgba(255,255,255,.16); color: #9BE8C4; }
.pass-status .status-off { background: rgba(255,255,255,.16); color: #FFB3AC; }
.pass-status .status-due, .pass-status .status-part { background: rgba(255,255,255,.16); color: #FFD79B; }
.pass-note { margin-top: var(--sp-3); font-size: 12px; color: rgba(255,255,255,.55); }
/* Facts the reservation carries — a flight number, a drop-off — read as pass
   rows too rather than as body text on a dark card. */
.pass .factline { color: rgba(255,255,255,.86); }


/* ------------------------------------------------------------ gift card ----
   Sold as a list of radio buttons a gift card is a top-up form. Shown as a
   card with a ribbon it is a present, which is what somebody came to buy. */
.giftwrap {
  padding: var(--sp-6) 0 var(--sp-6);
  background: linear-gradient(180deg, var(--bg-sky), var(--bg-2));
}
.giftcard {
  position: relative; width: min(300px, 82%); margin-inline: auto;
  aspect-ratio: 1.586;                      /* a real card */
  border-radius: var(--r-lg); padding: var(--sp-5);
  display: flex; flex-direction: column;
  background:
    radial-gradient(120% 100% at 8% 0%, rgba(40,103,240,.5), transparent 60%),
    linear-gradient(150deg, var(--bizi-navy), var(--bizi-midnight));
  color: #fff; box-shadow: var(--e-3); overflow: hidden;
}
/* The ribbon: two bands crossing the card, at low contrast so the amount wins. */
.giftcard::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(115deg, transparent 46%, rgba(255,255,255,.10) 46%, rgba(255,255,255,.10) 54%, transparent 54%),
    linear-gradient(25deg, transparent 62%, rgba(255,107,74,.22) 62%, rgba(255,107,74,.22) 70%, transparent 70%);
}
.giftcard-sheen {
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,.14) 46%, transparent 58%);
}
.giftcard-top {
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}
.giftcard-brand {
  font-size: 12px; font-weight: 750; letter-spacing: .1em; text-transform: uppercase;
  color: rgba(255,255,255,.82);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.giftcard-value {
  position: relative; z-index: 1; margin-top: auto;
  font-size: 34px; font-weight: 800; letter-spacing: -.035em; line-height: 1;
}
.giftcard-foot {
  position: relative; z-index: 1; margin-top: 6px;
  font-size: 11.5px; font-weight: 600; color: rgba(255,255,255,.6);
}

/* Amounts as chips. Four taps side by side, not four full-width rows. */
.amountgrid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-2); }
.amount { position: relative; display: block; }
.amount input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.amount > span {
  display: flex; align-items: center; justify-content: center;
  min-height: var(--tap); padding: 0 var(--sp-2); border-radius: var(--r);
  background: var(--bg); border: 1.5px solid var(--line); box-shadow: var(--e-1);
  font-size: 14.5px; font-weight: 700; letter-spacing: -.02em; color: var(--ink);
  cursor: pointer; white-space: nowrap;
}
.amount input:checked + span {
  background: var(--bizi-midnight); border-color: var(--bizi-midnight); color: #fff;
  box-shadow: var(--e-2);
}
.amount input:focus-visible + span { outline: 2px solid var(--brand); outline-offset: 2px; }
@media (max-width: 360px) { .amountgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }


/* ---------------------------------------------------------------- pilot ----
   The control room. Every line is a question with a real answer, so the marks
   have to be readable at a glance and distinguishable without colour. */
.verdict {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-5); border-radius: var(--r-xl);
  background: var(--bg); border: 1px solid var(--line); box-shadow: var(--e-2);
}
.verdict-mark {
  width: 56px; height: 56px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center;
  background: var(--warn-bg); color: var(--warn);
}
.verdict.is-ready { border-color: color-mix(in srgb, var(--ok) 40%, var(--line)); }
.verdict.is-ready .verdict-mark { background: var(--ok-bg); color: var(--ok); }
.verdict .small { color: var(--ink-2); }

.pilot-mark {
  width: 26px; height: 26px; border-radius: 50%; flex: 0 0 auto;
  display: grid; place-items: center;
}
/* Four states, and each readable without colour: the glyph and the word both
   say which one it is. */
.pilot-mark.done { background: var(--ok-bg); color: var(--ok); }
.pilot-mark.wait { background: var(--warn-bg); color: var(--warn); }
.pilot-mark.off  { background: var(--bg-3); color: var(--ink-3); }
.pilot-mark.bad  { background: var(--err-bg); color: var(--err); }

.pilot-state {
  flex: 0 0 auto; font-size: 10px; font-weight: 750; letter-spacing: .06em;
  text-transform: uppercase; text-align: right; max-width: 88px; line-height: 1.2;
}
.pilot-state.done { color: var(--ok); }
.pilot-state.wait { color: var(--warn); }
.pilot-state.off  { color: var(--ink-3); }
.pilot-state.bad  { color: var(--err); }

/* A step that is behind you, and the one you are on. */
.step-n.is-done { background: var(--ok); color: #fff; }
.step-n.is-next { background: var(--brand); color: var(--brand-ink); box-shadow: var(--e-action); }


/* ------------------------------------------------- sticky bars, stacked ----
   A merchant browsing the store is inside the app shell, so the page has BOTH
   a bottom tab bar and a sticky call to action. They were both fixed to the
   bottom of the viewport, and the tab bar's higher z-index put it directly on
   top of the button: tapping "Get this product" hit Messages.

   They stack now. The action sits on the tab bar, and the page reserves room
   for both so nothing is trapped under either. */
.app-shell .cta-bar {
  bottom: calc(58px + var(--safe-b));
  padding-bottom: var(--sp-3);
  border-radius: 0;
}
.app-shell .pad-bottom-cta { padding-bottom: calc(96px + 58px + var(--safe-b)); }
.app-shell .has-tabbar:has(.cta-bar) { padding-bottom: calc(96px + 58px + var(--safe-b)); }

@media (min-width: 720px) {
  /* The sidebar replaces the tab bar on wide screens, so the action goes back
     to the foot of the window. */
  .app-shell .cta-bar { bottom: 0; padding-bottom: calc(var(--sp-3) + var(--safe-b)); }
  .app-shell .pad-bottom-cta { padding-bottom: calc(96px + var(--safe-b)); }
}

/* ==========================================================================
   PUBLIC MARKETING SHELL
   ==========================================================================

   WHY THIS LAYER EXISTS.

   `--page: 560px` is commented "phones are the design target", and that is
   correct for the merchant app and for a customer completing a booking on a
   phone. It was ALSO governing the marketing site. Measured on the deployed
   host on 9 Sep 2026: at 1440 the landing and the catalogue both rendered in a
   560px column with 440px of empty page each side; at 1920, 680px each side.
   Nothing was broken — it was a phone laid out in the middle of a desktop.

   So this is a THIRD shell, not a change to the first two. `.wrap` keeps its
   560px and every merchant and customer screen is untouched. Marketing pages
   use `.mk` instead. The two must never be nested: competing width constraints
   are how a container ends up 520px wide inside a 1200px one.

   The container is the release requirement: min(1200px, 100% - 48px), centred.
   At 1440 that is 120px of margin each side, matched left and right, and the
   header, hero, grids and footer all inherit it so they visibly share an edge.
   ========================================================================== */

.mk {
  width: min(1200px, calc(100% - 48px));
  margin-inline: auto;
}
/* Phones get a tighter gutter — 48px of a 390px screen is an eighth of the
   readable width. */
@media (max-width: 560px) { .mk { width: calc(100% - 32px); } }

/* Sections carry the vertical rhythm so a page is composed by ORDER, not by
   each block inventing its own padding. */
.mk-section { padding-block: clamp(44px, 5.5vw, 80px); }
.mk-section-tight { padding-block: clamp(32px, 4vw, 56px); }
.mk-navy { background: var(--ink); color: var(--ink-on-dark); }
.mk-navy .mk-eyebrow { color: rgba(255,255,255,.62); }
.mk-navy .mk-lede { color: var(--ink-on-dark-2); }
.mk-paper { background: var(--bg-2); }
.mk-sky { background: var(--bg-sky); }

.mk-eyebrow {
  font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-3); margin: 0 0 var(--sp-3);
}
/* Public sales type is deliberately larger and more expressive than the
   merchant working screens, which stay at their existing sizes. */
.mk-h1 {
  font-size: clamp(32px, 4.4vw, 56px); line-height: 1.06;
  letter-spacing: -0.022em; font-weight: 800; margin: 0;
}
.mk-h2 {
  font-size: clamp(24px, 2.6vw, 34px); line-height: 1.16;
  letter-spacing: -0.018em; font-weight: 800; margin: 0;
}
.mk-h3 { font-size: 18px; line-height: 1.3; font-weight: 700; margin: 0; }
/* Comfortable measure. Centre the CONTAINER, not the paragraph. */
.mk-lede { font-size: clamp(16px, 1.25vw, 19px); line-height: 1.55; color: var(--ink-2); max-width: 56ch; margin: var(--sp-4) 0 0; }
.mk-note { font-size: 14px; color: var(--ink-3); margin: var(--sp-3) 0 0; }

/* ---- header ---- */
.mk-head {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--bg-2) 88%, transparent);
  backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--line-2);
}
.mk-head-in { display: flex; align-items: center; gap: var(--sp-5); min-height: 68px; }
.mk-brand { display: flex; align-items: center; gap: var(--sp-3); text-decoration: none; color: var(--ink); }
.mk-brand span { font-size: 19px; font-weight: 800; letter-spacing: -0.01em; }
.mk-nav { display: none; gap: var(--sp-5); margin-inline-start: var(--sp-6); }
.mk-nav a { font-size: 15px; color: var(--ink-2); text-decoration: none; }
.mk-nav a:hover { color: var(--ink); }
.mk-head-cta { margin-inline-start: auto; display: flex; align-items: center; gap: var(--sp-3); }
@media (min-width: 860px) { .mk-nav { display: flex; } }

/* ---- split hero ---- */
.mk-hero { display: grid; gap: clamp(28px, 4vw, 56px); align-items: start; }
/* Two columns on desktop; on a phone the preview goes BELOW the explanation
   and the action, so the offer is read before the picture. */
@media (min-width: 900px) { .mk-hero { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); } }
.mk-hero-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-6); }
.mk-hero-actions .btn { width: auto; }
@media (max-width: 559px) { .mk-hero-actions { display: grid; } .mk-hero-actions .btn { width: 100%; } }

/* ---- the product preview frame ----
   ONE frame for every product preview in the catalogue and on the sales
   pages. The owner's complaint that photography, artwork and placeholders
   look inconsistent is answered here rather than by regenerating assets: a
   photograph and an interface capture look like one system when the frame,
   proportion, radius and label around them are identical. */
.mk-frame {
  position: relative; border-radius: var(--r-lg); overflow: hidden;
  background: var(--bg); border: 1px solid var(--line);
  box-shadow: var(--e-3);
}
.mk-frame > img, .mk-frame > svg { display: block; width: 100%; height: 100%; object-fit: cover; }
.mk-frame-4x3 { aspect-ratio: 4 / 3; }
.mk-frame-16x10 { aspect-ratio: 16 / 10; }
.mk-frame-phone { aspect-ratio: 9 / 16; max-width: 300px; }
.mk-navy .mk-frame { border-color: rgba(255,255,255,.14); }
/* Says whose screen this is. A visitor must never mistake the customer's view
   for the merchant's, and a demonstration must never read as a real business. */
.mk-frame-tag {
  position: absolute; inset-inline-start: 10px; inset-block-start: 10px;
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  padding: 5px 9px; border-radius: 999px;
  background: rgba(7,26,51,.72); color: #fff; backdrop-filter: blur(6px);
  /* Above the preview. The iframe is positioned, so without this the label
     that says whose screen this is gets painted over by the screen itself. */
  z-index: 2;
}

/* ---- grids ---- */
.mk-grid { display: grid; gap: var(--sp-5); grid-template-columns: 1fr; }
@media (min-width: 640px) { .mk-grid-2, .mk-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .mk-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ---- product card ---- */
.mk-card {
  display: flex; flex-direction: column; text-decoration: none; color: inherit;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
  overflow: hidden; transition: border-color .16s ease, transform .16s ease, box-shadow .16s ease;
}
.mk-card:hover { border-color: color-mix(in srgb, var(--brand) 45%, var(--line)); transform: translateY(-2px); box-shadow: var(--e-3); }
.mk-card .mk-frame { border: 0; border-radius: 0; box-shadow: none; border-bottom: 1px solid var(--line-2); }
.mk-card-body { padding: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-2); flex: 1; }
.mk-card-outcome { font-size: 15px; font-weight: 700; line-height: 1.3; }
.mk-card-name { font-size: 13px; color: var(--ink-3); font-weight: 600; }
.mk-card-note { font-size: 14px; color: var(--ink-2); margin: 0; }
/* BIZI'S price, never the merchant's. The two are kept in separate rows with
   separate labels precisely because a charter fee beside an ADD button is what
   made the catalogue read as a travel agency. */
.mk-card-foot {
  margin-top: auto; padding-top: var(--sp-4); border-top: 1px solid var(--line-2);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}
.mk-price { font-size: 13px; color: var(--ink-2); }
.mk-price b { color: var(--ink); font-weight: 700; }

/* ---- three steps ---- */
.mk-step { display: flex; gap: var(--sp-4); align-items: flex-start; }
.mk-step-n {
  flex: none; width: 34px; height: 34px; border-radius: 999px;
  display: grid; place-items: center; font-weight: 800; font-size: 14px;
  background: var(--brand); color: var(--brand-ink);
}
.mk-navy .mk-step-n { background: rgba(255,255,255,.14); color: #fff; }

/* ---- footer ---- */
.mk-foot { background: var(--ink); color: var(--ink-on-dark-2); padding-block: clamp(40px, 5vw, 64px); }
.mk-foot a { color: var(--ink-on-dark-2); text-decoration: none; }
.mk-foot a:hover { color: #fff; }
.mk-foot-grid { display: grid; gap: var(--sp-6); grid-template-columns: 1fr; }
@media (min-width: 720px) { .mk-foot-grid { grid-template-columns: 2fr 1fr 1fr; } }
.mk-foot-col h4 { font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: rgba(255,255,255,.5); margin: 0 0 var(--sp-3); }
.mk-foot-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); font-size: 14px; }
.mk-foot-bar { margin-top: var(--sp-7); padding-top: var(--sp-5); border-top: 1px solid rgba(255,255,255,.12); font-size: 13px; color: rgba(255,255,255,.5); }

/* A live BIZI page inside a preview frame. The iframe is inert: it is a
   picture of the product that happens to be rendered by the real server, so it
   must not take focus or scroll under the visitor's finger. */
.mk-hero-frame {
  width: 100%; height: 100%; border: 0; display: block;
  pointer-events: none; background: var(--bg);
}

/* ---- THE TWO SIDES BAND ----------------------------------------------
   The single hardest thing to understand about BIZI is that there are TWO
   businesses and TWO payments in the story, and prose kept failing to carry
   it. This band draws it: your customer's screen on the left, your screen on
   the right, the booking travelling one way and the money travelling the
   other, each arrow labelled with who pays whom. */
.mk-sides { display: grid; gap: var(--sp-5); align-items: center; grid-template-columns: 1fr; }
@media (min-width: 900px) { .mk-sides { grid-template-columns: 1fr auto 1fr; } }
.mk-side {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: var(--sp-5); display: grid; gap: var(--sp-3);
}
.mk-side-who {
  font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
  color: var(--brand); display: flex; align-items: center; gap: 8px;
}
.mk-side-who::before { content: ""; width: 8px; height: 8px; border-radius: 999px; background: var(--brand); }
.mk-side-alt .mk-side-who { color: var(--ok); }
.mk-side-alt .mk-side-who::before { background: var(--ok); }
.mk-side h3 { font-size: 17px; font-weight: 700; margin: 0; line-height: 1.3; }
.mk-side p { font-size: 14px; color: var(--ink-2); margin: 0; }
.mk-side ul { margin: 0; padding-left: 1.05em; font-size: 14px; color: var(--ink-2); display: grid; gap: 4px; }

/* The connector. Vertical on a phone, horizontal on a desktop, and it always
   carries WORDS — an unlabelled arrow between two boxes explains nothing. */
.mk-flow { display: grid; gap: var(--sp-3); justify-items: center; padding-block: var(--sp-2); }
.mk-flow-arrow {
  display: flex; align-items: center; gap: 8px;
  font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap;
}
.mk-flow-arrow i { display: block; width: 26px; height: 1px; background: var(--line); position: relative; }
.mk-flow-arrow i::after {
  content: ""; position: absolute; inset-inline-end: -1px; top: -3px;
  border: 3.5px solid transparent; border-inline-start-color: var(--ink-3);
}
.mk-flow-arrow.back i::after { inset-inline-end: auto; inset-inline-start: -1px; border-inline-start-color: transparent; border-inline-end-color: var(--ok); }
.mk-flow-arrow.back { color: var(--ok); }
.mk-flow-arrow.back i { background: color-mix(in srgb, var(--ok) 35%, var(--line)); }

/* The BIZI fee, stated apart from the customer money so the two can never be
   read as the same transaction. */
.mk-fee {
  margin-top: var(--sp-6); border: 1px dashed var(--line-strong, rgba(7,26,51,.2));
  border-radius: var(--r); padding: var(--sp-4) var(--sp-5);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
  background: var(--bg-sky);
}
.mk-fee b { font-weight: 700; }

/* ---- step cards ---- */
.mk-stepcard {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: var(--sp-6); display: grid; gap: var(--sp-3); align-content: start;
}
.mk-navy .mk-stepcard { background: rgba(255,255,255,.05); border-color: rgba(255,255,255,.12); }
.mk-navy .mk-stepcard p { color: var(--ink-on-dark-2); }
.mk-stepcard p { margin: 0; font-size: 14.5px; color: var(--ink-2); line-height: 1.5; }

/* A live page used as a picture. Scaled down so a phone-width page reads as a
   cover rather than a cropped corner, and inert so it cannot be scrolled,
   focused or clicked — the card's own link is the control. */
.mk-frame-live { background: var(--bg-3); }
.mk-shot {
  position: absolute; inset: 0; border: 0; display: block;
  width: 420px; height: 700px;
  transform: scale(.62); transform-origin: top left;
  pointer-events: none; background: var(--bg);
}

/* The catalogue, in the public shell, is a STORE and lays out as one. Scoped
   to `.mk.store-body` so the merchant-side catalogue — which keeps `.wrap` and
   its single column — is not touched by this. */
.mk.store-body .stack-sm { display: grid; gap: var(--sp-4); grid-template-columns: 1fr; }
.mk.store-body .stack-sm > * + * { margin-top: 0; }
@media (min-width: 640px) { .mk.store-body .stack-sm { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1040px) { .mk.store-body .stack-sm { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* A card in a grid must fill its cell, or a short one leaves a ragged row. */
.mk.store-body .stack-sm > * { height: 100%; }

/* The catalogue card, rebuilt around the product. The outcome leads, the
   product's own name sits under it, and BIZI's commercial line and the preview
   action share the foot — where the merchant's own service price can never
   reach them. */
.pcard-art { position: relative; overflow: hidden; background: var(--bg-3); }
.pcard-outcome { font-weight: 700; font-size: 15px; line-height: 1.3; color: var(--ink); }
.pcard-name { font-size: 13px; color: var(--ink-3); font-weight: 600; }
.pcard-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.pcard-price { font-size: 13px; color: var(--ink-2); }
.pcard-go { font-size: 13px; font-weight: 700; color: var(--brand); white-space: nowrap; }

/* `.probchips` bleeds -20px to sit flush with `.wrap`'s 20px padding. `.mk`
   has no padding — it sets its width by calc — so inside the public shell that
   bleed pushed the row 20px past the container on both sides and gave the
   catalogue 4px of real horizontal page scroll at 390px. Measured, then fixed
   here rather than by widening the container. */
.mk .probchips { margin-inline: 0; padding-inline: 0; }

/* Same bleed problem as `.probchips`: `.trow` uses -20px margins to sit flush
   with `.wrap`'s padding, and `.mk` has none. This was the actual 4px of
   horizontal scroll on the catalogue at 390px — found by walking every element
   whose right edge passed the viewport and whose ancestors were not scrollers. */
.mk .trow { margin-inline: 0; padding-inline: 0; }
