@font-face {
  font-family: 'Lora';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/lora-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lora';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/lora-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Lora';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/lora-italic-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lora';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url(/fonts/lora-italic-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Work Sans';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/worksans-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Work Sans';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/worksans-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url(/fonts/jetbrainsmono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url(/fonts/jetbrainsmono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --s1: 2px;
  --s2: 4px;
  --s3: 6px;
  --s4: 8px;
  --s5: 10px;
  --s6: 12px;
  --s7: 14px;
  --s8: 16px;
  --s9: 20px;
  --s10: 24px;
  --s11: 32px;
  --s12: 40px;
  --s13: 48px;

  --t1: 9px;
  --t2: 10px;
  --t3: 11px;
  --t4: 12.5px;
  --t5: 13.5px;
  --t6: 15px;
  --t7: 17px;
  --t8: 20px;
  --t9: 24px;
  --t10: 30px;
  --t11: 40px;

  /* Small text is read in bulk and wants air; a heading is one line and a
     loose one reads as two. */
  --lh1: 1.4;
  --lh2: 1.4;
  --lh3: 1.45;
  --lh4: 1.5;
  --lh5: 1.55;
  --lh6: 1.6;
  --lh7: 1.5;
  --lh8: 1.35;
  --lh9: 1.25;
  --lh10: 1.2;
  --lh11: 1.1;

  --r1: 2px;
  --r2: 4px;
  --r3: 6px;
  --r4: 8px;
  --r5: 10px;
  --r6: 12px;
  --r7: 16px;
  --r-pill: 999px;
}

:root {
  color-scheme: light dark;

  --page:    oklch(0.95 0.008 85);
  --surface: oklch(0.99 0.004 85);
  --rail:    oklch(0.965 0.006 85);
  --mid:     oklch(0.975 0.005 85);
  --line:    oklch(0.91 0.006 85);
  --line2:   oklch(0.88 0.006 85);
  --hair:    oklch(0.925 0.006 85);
  --field:   oklch(0.975 0.005 85);
  --chip:    oklch(0.955 0.006 85);

  --ink:   oklch(0.22 0.01 85);
  --ink2:  oklch(0.3 0.01 85);
  --ink3:  oklch(0.45 0.01 85);
  --mute:  oklch(0.62 0.01 85);
  --faint: oklch(0.66 0.01 85);

  --accent:      oklch(0.52 0.14 265);
  --accent-ink:  oklch(0.99 0.004 265);
  --accent-soft: oklch(0.52 0.14 265 / 0.13);
  --accent-line: oklch(0.52 0.14 265 / 0.4);
  /* A filled accent surface, pressed: the hover under a primary button. Both
     prototypes already draw it, and a sheet that mixed its own would be a
     second accent free to drift from the first. */
  --accent-deep: oklch(0.42 0.14 265);
  /* And what may sit *on* an accent surface — the count inside a filled
     button. It is the accent's own ink at low alpha rather than white,
     because in the dark scheme that ink is nearly black. */
  --accent-wash: oklch(0.99 0.004 265 / 0.18);

  --solid:     oklch(0.22 0.01 85);
  --solid-ink: oklch(0.97 0.004 85);

  --star:  oklch(0.58 0.14 145);
  --alert: oklch(0.58 0.16 35);

  --warn-bg:   oklch(0.87 0.05 60);
  --warn-line: oklch(0.88 0.06 60);
  --warn-ink:  oklch(0.38 0.07 60);
  --warn-dot:  oklch(0.66 0.15 55);

  --danger:      oklch(0.55 0.16 25);
  --danger-line: oklch(0.9 0.05 25);
  --danger-soft: oklch(0.96 0.02 25);
  --star-soft:   oklch(0.96 0.03 145);
  --info:        oklch(0.5 0.13 285);
  --info-soft:   oklch(0.96 0.025 285);
  --ok-bg:       oklch(0.55 0.12 145 / 0.12);
  --ok-ink:      oklch(0.42 0.1 145);

  /* The two colours in here that are not the design's, and must not be. A QR
     is read by a camera, not by a person, and the standard it is read against
     assumes black modules on white paper — a near-white page and a near-black
     ink are close enough to look right and far enough to cost somebody a scan
     on a dim phone. They do not swap with the scheme, and nothing but a QR may
     use them. Here rather than in an application's own sheet because the mail
     app already prints one and the portal's payment card is about to
     (BILL-5). */
  --qr-paper: oklch(1 0 0);
  --qr-ink:   oklch(0 0 0);

  --radius: 10px;

  /* An avatar's hue comes from the address, so one person is one colour
     everywhere, for everyone. Only the lightness and chroma change with the
     scheme — the hue is the identity and must not move. */
  --av-bg: 0.87 0.05;
  --av-fg: 0.38 0.07;

  --serif: 'Lora', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, serif;
  --sans:  'Work Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --mono:  'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --shadow: 0 1px 2px oklch(0.2 0.01 85 / 0.06), 0 20px 50px oklch(0.2 0.01 85 / 0.09);
}

@media (prefers-color-scheme: dark) {
  :root {
    --page:    oklch(0.155 0.006 85);
    --surface: oklch(0.195 0.006 85);
    --rail:    oklch(0.175 0.006 85);
    --mid:     oklch(0.195 0.006 85);
    --line:    oklch(0.27 0.006 85);
    --line2:   oklch(0.31 0.006 85);
    --hair:    oklch(0.27 0.006 85);
    --field:   oklch(0.22 0.006 85);
    --chip:    oklch(0.23 0.006 85);

    --ink:   oklch(0.94 0.004 85);
    --ink2:  oklch(0.88 0.004 85);
    --ink3:  oklch(0.72 0.008 85);
    --mute:  oklch(0.58 0.008 85);
    --faint: oklch(0.55 0.008 85);

    --accent:      oklch(0.72 0.08 265);
    --accent-ink:  oklch(0.18 0.01 85);
    --accent-soft: oklch(0.72 0.08 265 / 0.26);
    --accent-line: oklch(0.72 0.08 265 / 0.45);
    /* Lighter rather than darker: on a dark ground the pressed state of a
       filled control is the one that comes forward. */
    --accent-deep: oklch(0.80 0.08 265);
    --accent-wash: oklch(0.18 0.01 85 / 0.20);

    --solid:     oklch(0.94 0.004 85);
    --solid-ink: oklch(0.2 0.01 85);

    --star:  oklch(0.76 0.13 145);
    --alert: oklch(0.62 0.16 35);

    --warn-bg:   oklch(0.3 0.05 55);
    --warn-line: oklch(0.42 0.08 55);
    --warn-ink:  oklch(0.88 0.06 55);
    --warn-dot:  oklch(0.72 0.15 55);

    --danger:      oklch(0.72 0.14 30);
    --danger-line: oklch(0.4 0.08 30);
    --danger-soft: oklch(0.28 0.05 25);
    --star-soft:   oklch(0.27 0.04 145);
    --info:        oklch(0.76 0.11 285);
    --info-soft:   oklch(0.28 0.05 285);
    --ok-bg:       oklch(0.7 0.12 145 / 0.16);
    --ok-ink:      oklch(0.8 0.1 145);

    --av-bg: 0.36 0.06;
    --av-fg: 0.85 0.06;

    --shadow: 0 1px 2px oklch(0.1 0.01 85 / 0.3), 0 20px 50px oklch(0.1 0.01 85 / 0.3);
  }
}

/* A phone is read at arm's length in daylight, where the desk's soft dark
   grays wash out: on a small screen the dark scheme trades warmth for
   contrast — the ground goes nearly black and the ink nearly white, and the
   lines step up so they still read against the deeper ground. */
@media (prefers-color-scheme: dark) and (max-width: 640px) {
  :root {
    --page:    oklch(0.1 0.004 85);
    --surface: oklch(0.125 0.005 85);
    --rail:    oklch(0.11 0.004 85);
    --mid:     oklch(0.135 0.005 85);
    --line:    oklch(0.3 0.006 85);
    --line2:   oklch(0.34 0.006 85);
    --hair:    oklch(0.28 0.006 85);
    --field:   oklch(0.16 0.005 85);
    --chip:    oklch(0.17 0.005 85);

    --ink:   oklch(0.985 0.002 85);
    --ink2:  oklch(0.94 0.003 85);
    --ink3:  oklch(0.82 0.006 85);
    --mute:  oklch(0.7 0.008 85);
    --faint: oklch(0.64 0.008 85);
  }
}

/* The storefront's stylesheet and the shared controls, served at /theme.css.

   The palette and the type it names are theme::tokens, which the console and
   the mail app wear too (UI-17). Nothing here defines a colour of its own: a
   rule that reached past a token would be the one surface that stops following
   the design. The section markers below are what make it navigable. */

/* ── reset + base ───────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--body-size, 16px);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }
h1, h2, h3, p { margin: 0; }
code, .mono-num, .code { font-family: var(--mono); }

/* ── layout ─────────────────────────────────────────────────────────────── */

.container { width: 100%; max-width: var(--container, 1360px); margin-inline: auto; padding-inline: 1.75rem; }
.container-narrow { width: 100%; max-width: 720px; margin-inline: auto; padding-inline: 1.75rem; }
.container-tight { width: 100%; max-width: 420px; margin-inline: auto; padding-inline: 1.75rem; }
/* A phone's sign-in is mostly card: the desktop's 4rem of vertical air and
   1.75rem gutters ate a third of an iPhone mini's screen (2026-09-01). */
@media (max-width: 480px) {
  .container-tight { padding-inline: 1rem; }
  .center-page, .center-page-start { padding-block: 1.25rem; }
  .container-tight .panel { padding: 1rem; }
}
.center-page { min-height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding-block: 3rem; }
.center-page-start { min-height: 100%; display: flex; flex-direction: column; justify-content: center; padding-block: 4rem; }

.header {
  position: sticky; top: 0; z-index: 10;
  background: color-mix(in oklab, var(--surface) var(--nav-alpha, 85%), transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
  max-width: var(--nav-max, none); margin-inline: auto;
}
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; padding-block: 1.25rem; }
.brand-group { display: flex; align-items: center; gap: .75rem; min-width: 0; }
.brand { font-weight: 600; letter-spacing: -0.01em; font-size: 1rem; }
/* The company's mark beside its name, pressed as one thing wherever comm.ee
   is written. */
.brand-home { display: inline-flex; align-items: center; gap: .45em; color: inherit; text-decoration: none; }
.brand-mark { display: inline-flex; width: 1.15em; height: 1.15em; }
.brand-mark svg { width: 100%; height: 100%; }
.brand-logo { display: block; height: 1.6rem; width: auto; max-width: 12rem; object-fit: contain; }
/* Only the owner sees it, so it is quiet until reached for. */
.console-link { --rb-size: 2rem; opacity: .55; transition: opacity .2s; }
.console-link:hover, .console-link:focus-visible { opacity: 1; }
.nav { display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap; }
.nav a { color: var(--ink3); font-size: 0.875rem; transition: color .15s; }
.nav a:hover { color: var(--ink); }
.nav-links, .nav-extras { display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap; }

/* Past a handful of pages the rest wait under "More" — a sheet, not a wall. */
.nav-more { position: relative; }
.nav-more summary {
  list-style: none; cursor: pointer; color: var(--ink3); font-size: 0.875rem;
  transition: color .15s; user-select: none;
}
.nav-more summary::-webkit-details-marker { display: none; }
.nav-more summary::after { content: " ▾"; font-size: 0.7em; }
.nav-more summary:hover, .nav-more[open] summary { color: var(--ink); }
.nav-more-sheet {
  position: absolute; left: 0; top: calc(100% + 0.75rem); z-index: 30;
  display: flex; flex-direction: column; gap: 0.15rem;
  min-width: 13rem; max-height: 60vh; overflow-y: auto;
  padding: 0.6rem; background: var(--surface);
  border: 1px solid var(--line); border-radius: 0.6rem;
  box-shadow: 0 12px 34px rgb(0 0 0 / 0.12);
}
.nav-more-sheet a { padding: 0.35rem 0.6rem; border-radius: 0.4rem; }
.nav-more-sheet a:hover { background: var(--field); }

/* ── the header's theme answers (THEME-9). The defaults wear no class, so an
   unthemed header is exactly the block above. ─────────────────────────────── */

.header.nav-static { position: static; }
.header.nav-noline { border-bottom-color: transparent; }
.header.navpad-snug .header-inner { padding-block: .55rem; }
.header.navpad-tall .header-inner { padding-block: 2rem; }

.header.navlinks-left .nav, .header.navlinks-center .nav { flex: 1; }
.header.navlinks-left .nav .nav-links { flex: 1; justify-content: flex-start; }
.header.navlinks-center .nav .nav-links { flex: 1; justify-content: center; }

.header.nav-beside .header-inner { justify-content: flex-start; gap: 2rem; }
.header.nav-beside .nav { flex: 1; }
.header.nav-beside .nav .nav-extras { margin-left: auto; }
.header.nav-centre .header-inner,
.header.nav-stack .header-inner { flex-direction: column; gap: .6rem; }
.header.nav-centre .header-inner { align-items: center; text-align: center; }
.header.nav-stack .header-inner { align-items: flex-start; }
.header.nav-centre .nav, .header.nav-stack .nav { width: 100%; }
.header.nav-centre .nav .nav-links { flex: 1; justify-content: center; }
.header.nav-stack .nav .nav-links { flex: 1; justify-content: flex-start; }
.header.nav-centre .nav .nav-extras, .header.nav-stack .nav .nav-extras { margin-left: auto; }

.header.navbg-ink { background: color-mix(in oklab, var(--solid) var(--nav-alpha, 85%), transparent); color: var(--solid-ink); }
.header.navbg-ink .nav a { color: color-mix(in oklab, var(--solid-ink) 72%, transparent); }
.header.navbg-ink .nav a:hover, .header.navbg-ink .nav a.active { color: var(--solid-ink); }
.header.navbg-accent { background: color-mix(in oklab, var(--accent) var(--nav-alpha, 85%), transparent); color: var(--accent-ink); }
.header.navbg-accent .nav a { color: color-mix(in oklab, var(--accent-ink) 75%, transparent); }
.header.navbg-accent .nav a:hover, .header.navbg-accent .nav a.active { color: var(--accent-ink); }
.header.navbg-clear { background: transparent; backdrop-filter: none; border-bottom-color: transparent; }

.footer { margin-top: 5rem; border-top: 1px solid var(--line); padding-block: 2rem; color: var(--mute); font-size: 0.75rem; max-width: var(--footer-max, none); margin-inline: auto; }

.sb-root { max-width: var(--sections-max, none); margin-inline: auto; }

.section { margin-top: calc(3.5rem * var(--space, 1)); }
.section-tight { margin-top: calc(2.5rem * var(--space, 1)); }

/* ── type ───────────────────────────────────────────────────────────────── */

/* An eyebrow is a label on a machine reading, so it is set in the mono face
   at the size the design gives it, not in small caps of the running text. */
.eyebrow { font-family: var(--mono); text-transform: uppercase; letter-spacing: .08em; font-size: .65rem; color: var(--mute); }
/* Headings are the serif, running text is the sans. The pairing is the whole
   look — a page set entirely in one of them reads as a form. */
.h1 { font-family: var(--serif); font-size: clamp(1.75rem, 3.5vw, var(--h1-max, 2.5rem)); font-weight: 500; letter-spacing: -0.02em; line-height: 1.15; }
.h2 { font-family: var(--serif); font-size: calc(var(--h1-max, 2.5rem) / 2); font-weight: 500; letter-spacing: -0.015em; }
.h3 { font-size: .95rem; font-weight: 600; letter-spacing: -0.01em; }
.lede { margin-top: .75rem; color: var(--ink3); max-width: 42rem; }
.muted { color: var(--ink3); }
.faint { color: var(--mute); font-size: .8rem; }
.tiny { font-size: .7rem; }
.mono-num { font-variant-numeric: tabular-nums; }
.link { color: var(--accent); text-decoration: underline; text-underline-offset: .2em; }
.link:hover { opacity: .8; }
.code { background: var(--field); border-radius: 5px; padding: .1rem .4rem; font-size: .85em; }

/* ── spacing helpers ────────────────────────────────────────────────────── */

.mt-1 { margin-top: .25rem; } .mt-2 { margin-top: .5rem; } .mt-3 { margin-top: .75rem; }
.mt-4 { margin-top: 1rem; } .mt-5 { margin-top: 1.25rem; } .mt-6 { margin-top: 1.5rem; }
.mt-8 { margin-top: 2rem; } .mt-10 { margin-top: 2.5rem; }
.mb-4 { margin-bottom: 1rem; } .mb-6 { margin-bottom: 1.5rem; }

/* ── grid / cards ───────────────────────────────────────────────────────── */

.grid-products { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 640px) { .grid-products { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .grid-products { grid-template-columns: repeat(3, 1fr); } }

.grid-2 { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 640px) { .grid-2 { grid-template-columns: repeat(2, 1fr); } }

.card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem;
  background: var(--surface);
  transition: border-color .15s;
}
.card:hover { border-color: var(--mute); }
.card-raised { background: var(--field); box-shadow: var(--shadow); }

.card-media { aspect-ratio: 4 / 3; width: 100%; border-radius: var(--radius-img, calc(var(--radius) - 3px)); background: var(--field); object-fit: cover; }
.card-media-empty { aspect-ratio: 4 / 3; width: 100%; border-radius: var(--radius-img, calc(var(--radius) - 3px)); background: var(--field); display: flex; align-items: center; justify-content: center; color: var(--mute); font-size: .75rem; }

.price { margin-top: .75rem; font-weight: 600; }

.empty { padding: 4rem 0; text-align: center; color: var(--mute); font-size: .875rem; }
.empty-box { border: 1px dashed var(--line); border-radius: var(--radius); padding: 3.5rem 1.5rem; text-align: center; }

.cards-stack > * + * { margin-top: var(--gap, 1.25rem); }

/* ── forms ──────────────────────────────────────────────────────────────── */

.panel { border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem; background: var(--field); box-shadow: var(--shadow); }
.form > * + * { margin-top: 1rem; }
.field { display: block; }
.label { display: block; font-size: .72rem; font-weight: 600; color: var(--ink3); margin-bottom: .375rem; }
.hint { font-size: .68rem; color: var(--mute); margin-top: .3rem; }

.input, .textarea, .select {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius-input, calc(var(--radius) - 3px));
  padding: .55rem .75rem;
  font-size: .875rem;
  background: var(--surface);
  color: var(--ink);
  outline: none;
  transition: border-color .15s;
}
.input:disabled { background: var(--chip); color: var(--mute); }
.input:focus, .textarea:focus, .select:focus { border-color: var(--ink); }
.textarea { resize: vertical; min-height: 5rem; }

/* ── buttons ────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  border-radius: var(--radius-btn, calc(var(--radius) - 3px));
  padding: .55rem .9rem;
  font-size: .8rem; font-weight: 500;
  cursor: pointer; border: 1px solid transparent;
  transition: opacity .15s, background-color .15s, border-color .15s;
  background: none;
}
.btn-primary { background: var(--btn-fill, var(--solid)); color: var(--btn-ink, var(--solid-ink)); border-color: var(--btn-line, transparent); }
.btn-primary:hover { opacity: .82; }
.btn-secondary { border-color: var(--line); color: var(--ink); }
.btn-secondary:hover { background: var(--field); }
.btn-danger { border-color: var(--line); color: var(--danger); }
.btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); }
.btn-block { width: 100%; }
.btn-sm { padding: .3rem .6rem; font-size: .72rem; }

/* ── badges / pills / alerts ────────────────────────────────────────────── */

.badge { display: inline-flex; align-items: center; border-radius: 999px; padding: .1rem .55rem; font-size: .65rem; font-weight: 600; letter-spacing: .02em; }
.badge-neutral { background: var(--chip); color: var(--ink3); }
.badge-warning { background: var(--warn-bg); color: var(--warn-ink); }
.badge-danger { background: var(--danger-soft); color: var(--danger); }
.badge-success { background: var(--star-soft); color: var(--star); }
.badge-info { background: var(--info-soft); color: var(--info); }
.badge-primary { background: var(--solid); color: var(--solid-ink); }

.pill { display: inline-flex; align-items: center; gap: .4rem; border: 1px solid var(--line); border-radius: 999px; padding: .3rem .7rem; font-size: .75rem; }

.alert { padding: .65rem .9rem; border-radius: calc(var(--radius) - 3px); font-size: .85rem; }
.alert-warning { background: var(--warn-bg); color: var(--warn-ink); }
.alert-danger { background: var(--danger-soft); color: var(--danger); }
/* UI-33: the one way on out of a refusal, in the banner that states it. */
.alert a { color: inherit; font-weight: 600; text-decoration: underline; text-underline-offset: .2em; }

/* UI-29: what the server refused, under the control it refused. The banner is
   left for what has no field. */
.field-error { font-size: .7rem; font-weight: 600; color: var(--danger); margin-top: .3rem; }
.is-invalid .input,
.is-invalid .textarea,
.is-invalid .select,
.is-invalid .sel { border-color: var(--danger); }

/* ── misc components ───────────────────────────────────────────────────── */
/* A separator between two ways of signing in. It only means anything with
   something on both sides, so it lives inside the block that holds them. */
.signin-or { margin: 1rem 0; color: var(--mute); }
.link-button { border: 0; background: none; padding: 0; font: inherit; color: var(--accent); cursor: pointer; text-decoration: underline; }

.row { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; font-size: .875rem; }
.grow { flex: 1; }

.stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: .75rem; }
@media (min-width: 640px) { .stats-grid { grid-template-columns: repeat(5, 1fr); } }
.stat { border: 1px solid var(--line); border-radius: var(--radius); padding: .875rem 1rem; }
.stat-value { font-size: 1.5rem; font-weight: 600; }
.stat-label { font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; color: var(--ink3); }

.hidden { display: none !important; }
.center { text-align: center; }
.flex { display: flex; }
.flex-wrap { flex-wrap: wrap; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: .5rem; }
.gap-3 { gap: .75rem; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

details > summary { cursor: pointer; list-style: none; }
details > summary::-webkit-details-marker { display: none; }

/* ── the staged picture strip ───────────────────────────────────────────── */
/*
   Shared: the product's pane form and the section's properties column both
   stage pictures the same way, and both write them only on Save (UI-20).
*/

.product-thumb { position: relative; width: 4.5rem; height: 4.5rem; }
.product-thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: 6px; background: var(--field); }
.product-thumb button {
  position: absolute; top: -.4rem; right: -.4rem;
  width: 1.35rem; height: 1.35rem; padding: 0; font-size: .7rem; line-height: 1;
  border-radius: 999px; background: var(--surface); border-color: var(--line);
}
.thumb-cover {
  position: absolute; left: 0; bottom: 0; right: 0;
  background: rgb(0 0 0 / 0.55); color: #fff; font-size: .55rem;
  text-align: center; padding: .1rem 0; border-radius: 0 0 6px 6px;
  text-transform: uppercase; letter-spacing: .06em;
}

/* The staged picture list, wherever it is staged (UI-20).
   Two class names on the first rule, not one: `.field > .label` floats every
   label inside its control's border, and it is declared later in this
   stylesheet, so a single-class override loses on order rather than on
   intent. */
.field.media-field { position: static; }
.field.media-field > .label { position: static; margin-bottom: .5rem; font-size: .72rem; }
.media-strip { display: flex; flex-wrap: wrap; gap: .75rem; margin-bottom: .75rem; }

/* ── storefront: lightbox extras (thumbs, zoom) ────────────────────────── */

.lightbox-thumbs { display: flex; gap: .6rem; padding-top: .75rem; flex-wrap: wrap; }
.lightbox-thumb {
  width: 4rem; height: 4rem; object-fit: cover; border-radius: 6px;
  opacity: .5; cursor: pointer; border: 2px solid transparent;
  transition: opacity .15s, border-color .15s;
}
.lightbox-thumb:hover { opacity: .85; }
.lightbox-thumb.active { opacity: 1; border-color: var(--ink); }

/* ── storefront: chat widget ────────────────────────────────────────────── */

.chat-widget-toggle {
  position: fixed; right: 1.5rem; bottom: 1.5rem; z-index: 90;
  width: 3.25rem; height: 3.25rem; border-radius: 999px; border: none;
  background: var(--solid); color: var(--solid-ink);
  font-size: 1.3rem; cursor: pointer; box-shadow: var(--shadow);
  display: flex; align-items: center; justify-content: center;
}
.chat-widget-dot { position: absolute; top: .1rem; right: .1rem; width: .6rem; height: .6rem; border-radius: 999px; background: var(--danger); }

.chat-widget-panel {
  position: fixed; right: 1.5rem; bottom: 5.25rem; z-index: 90;
  width: min(22rem, calc(100vw - 3rem)); max-height: min(30rem, calc(100vh - 8rem));
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); overflow: hidden;
}
.chat-widget-header { padding: .75rem 1rem; border-bottom: 1px solid var(--line); font-weight: 600; font-size: .85rem; display: flex; justify-content: space-between; align-items: center; }
.chat-widget-body { flex: 1; overflow-y: auto; padding: .75rem 1rem; display: flex; flex-direction: column; gap: .5rem; min-height: 8rem; }
.chat-widget-bubble { max-width: 80%; padding: .5rem .7rem; border-radius: var(--radius); font-size: .82rem; }
.chat-widget-bubble-me { align-self: flex-end; background: var(--solid); color: var(--solid-ink); }
.chat-widget-bubble-them { align-self: flex-start; background: var(--field); }
.chat-widget-footer { border-top: 1px solid var(--line); padding: .6rem; }
.chat-widget-footer form { display: flex; gap: .4rem; }

/* ── layout slots ───────────────────────────────────────────────────────── */

/* A slot holds sections, so it needs no styling of its own beyond saying
   where it ends and the page begins. The sections inside carry their own
   arrangement and placement, exactly as a page's do (LAYOUT-2). */
.layout-slot { display: flex; flex-direction: column; }
.layout-header { margin-bottom: 1.5rem; padding-bottom: 1rem; border-bottom: 1px solid var(--line); }
.layout-footer { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--line); }

/* LAYOUT-7: where the page goes, on a layout drawn on its own. It is an
   outline rather than a box with a fill, because it is the one thing on the
   page that is not there — a solid tile reads as content. */
.sb-placeholder {
  display: flex; align-items: center; justify-content: center; gap: .6rem;
  margin: 1.25rem 0; padding: 2.5rem 1rem;
  border: 2px dashed var(--line); border-radius: var(--radius);
  color: var(--mute); font-size: .85rem; letter-spacing: .01em;
}
.sb-placeholder-icon { width: 1.1rem; height: 1.1rem; }

/* Current page in the site nav. */
.nav a.active { color: var(--ink); font-weight: 600; }
.brand.active { text-decoration: none; }

/* ── gallery (masonry) ──────────────────────────────────────────────────── */

/* A grid rather than CSS columns.
   `columns` balances: with four products across three tracks it fills two and
   leaves the third empty, so a small shop rendered as a lopsided half-page.
   `auto-fill` uses every track there is room for, and `align-items: start`
   keeps each tile at its picture's own height rather than stretching it to
   the tallest in the row — which is the part of masonry worth having. */
.gallery {
  display: grid; gap: 2.5rem; align-items: start;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
/* A tile is an `<a>` to the product's own page (COMM-20) and behaves like one
   for anybody who asks the browser for a link — so it wears no link colour and
   no underline of its own. */
.gallery-item {
  display: block; width: 100%; position: relative;
  border: none; padding: 0; background: none; cursor: pointer;
  color: inherit; text-align: left; text-decoration: none;
}
/* A minimum height and a background so an image that has not loaded, or a
   product that has none, still occupies the space it will take — otherwise
   the masonry columns collapse and the page jumps as pictures arrive. */
.gallery-item img {
  width: 100%; height: auto; display: block; border-radius: var(--radius-img, 2px);
  min-height: 8rem; background: var(--field);
}
.gallery-empty {
  aspect-ratio: 4 / 5; border-radius: 2px; background: var(--field);
  display: flex; align-items: center; justify-content: center;
  color: var(--mute); font-size: .75rem;
}
.gallery-item .gallery-caption {
  display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
  margin-top: .6rem; font-size: .8rem; text-align: left;
}
.gallery-item .gallery-caption .name { font-weight: 600; min-width: 0; }
.gallery-item .gallery-caption .name p { display: inline; margin: 0; }
.gallery-item .gallery-caption .price { margin-top: 0; color: var(--ink3); font-weight: 400; white-space: nowrap; }

/* ── properties: the badge on a card, and the bar over a wall (COMM-22) ─── */

/* Over the picture, not under the name: what a card says about a product is
   read while the eye is still on the picture. */
.gallery-badges {
  position: absolute; top: .5rem; left: .5rem; z-index: 1;
  display: flex; flex-wrap: wrap; gap: .25rem; max-width: calc(100% - 1rem);
}
.badge-soft {
  padding: .1rem .4rem; border-radius: 999px;
  background: color-mix(in oklab, var(--surface) 88%, transparent);
  color: var(--ink2); font-size: .65rem; font-weight: 600; line-height: 1.5;
  backdrop-filter: blur(4px);
}

/* Every control is a link, so a narrowed shop is an address (COMM-22). */
.facets {
  display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem;
  margin-top: 1.25rem; padding-block: .75rem;
  border-block: 1px solid var(--line);
}
.facet { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }
.facet-name {
  font-family: var(--mono); font-size: .62rem; text-transform: uppercase;
  letter-spacing: .08em; color: var(--mute); margin-right: .15rem;
}
.facet-option {
  padding: .2rem .6rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--ink3);
  font-size: .78rem; transition: border-color .15s, color .15s, background .15s;
}
.facet-option:hover { border-color: var(--mute); color: var(--ink); }
.facet-option.is-on { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.facet-count { margin-left: auto; color: var(--mute); font-size: .75rem; }
.facet-clear { margin-left: .6rem; color: var(--accent); text-decoration: underline; text-underline-offset: .2em; }
.gallery-item:hover .gallery-caption .name { text-decoration: underline; text-underline-offset: .2em; }

/* ── a wall's other pages (COMM-21) ─────────────────────────────────────── */

.sb-pager { display: flex; align-items: baseline; gap: 1.25rem; margin-top: 2.5rem; }
.sb-pager-count { color: var(--mute); font-size: .8rem; }

/* ── a product's own page (COMM-20) ─────────────────────────────────────── */

.product { display: grid; gap: 2.5rem; padding-block: 3rem; }
@media (min-width: 900px) {
  .product { grid-template-columns: 1fr 1fr; gap: 3.5rem; align-items: start; }
}
.product-shot img {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius-img, 2px); background: var(--field);
}
.product-thumbs { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .75rem; }
.product-thumb {
  width: 4rem; padding: 0; background: none; cursor: pointer;
  border: 1px solid transparent; border-radius: 2px;
}
.product-thumb.active { border-color: var(--ink); }
.product-thumb img { width: 100%; height: auto; display: block; border-radius: 2px; }
.product-name p { display: inline; margin: 0; }
.product-price { font-size: 1.25rem; font-weight: 600; margin-top: 1rem; }
#product-cart-slot { margin-top: 1.5rem; }
.product-desc { margin-top: 1.5rem; color: var(--ink3); }
.product-desc p + p { margin-top: .75rem; }
.product-comments {
  max-width: 44rem; margin: 0 auto 4rem;
  padding-top: 2rem; border-top: 1px solid var(--line);
}

/* ── the blog (BLOG-2, BLOG-4) ──────────────────────────────────────────── */

.post { padding-block: 3rem; }
.post-cover { width: 100%; height: auto; display: block; border-radius: 2px; margin-bottom: 2rem; }
.post-date { margin-top: .5rem; }
.post-body { margin-top: 2rem; color: var(--ink3); }
.post-body p + p { margin-top: .75rem; }
.post-comments { padding-top: 2rem; border-top: 1px solid var(--line); margin-bottom: 4rem; }

.post-list { display: grid; gap: 2.5rem; }
@media (min-width: 700px) { .post-list { grid-template-columns: 1fr 1fr; } }
.post-card { display: grid; gap: .35rem; color: inherit; text-decoration: none; }
.post-card-cover {
  width: 100%; height: auto; display: block;
  border-radius: 2px; background: var(--field); margin-bottom: .5rem;
}
.post-card-title { font-size: 1.25rem; font-weight: 600; }
.post-card-excerpt { color: var(--ink3); }

/* ── lightbox ───────────────────────────────────────────────────────────── */

/*
   The picture first, edge to edge, and everything you decide with underneath
   it in one centred column.

   This replaced a two-column layout that put the picture in a rounded card
   beside a details panel. That is a good shape for a spec sheet and the wrong
   one for a thing that is looked at: the picture was the smaller half of the
   screen, boxed, on a page background. A band the full width of the window on
   black is the picture being the subject.

   The band is 70vh so that what is below it is *visible* — a hair of the name
   and price shows under the fold, which is what says "there is more here"
   without a scroll hint that has to be drawn.
*/
/* Specificity rather than source order, so the two z-indexes cannot be
   reordered into each other by an edit above. */
.overlay.lightbox { z-index: 100; }

.lightbox-stage {
  position: relative;
  display: flex; align-items: center;
  width: 100%; height: 70vh;
  background: #000;
  /* Horizontal, not hidden: a picture wider than the window at this height is
     scrolled to rather than cropped. Vertical stays hidden so the band cannot
     grow its own scrollbar inside the page's. */
  overflow-x: auto; overflow-y: hidden;
}
.lightbox-stage img#lightbox-image {
  height: 100%; width: auto; max-width: none; margin: auto;
  flex-shrink: 0; cursor: zoom-in;
}

/* Zoomed is the whole window — the page enters browser fullscreen at the same
   moment, so this is the height of the screen rather than of the viewport
   inside the browser's chrome. `dvh` because a phone's toolbar retracts. */
.lightbox.zoomed { overflow: hidden; }
.lightbox.zoomed .lightbox-stage { height: 100dvh; }
.lightbox.zoomed .lightbox-details,
.lightbox.zoomed .lightbox-thumbs { display: none; }
.lightbox.zoomed img#lightbox-image { cursor: zoom-out; }

.lightbox-close { position: fixed; top: 1.25rem; right: 1.25rem; z-index: 2; }
/* Faint until wanted: they sit over the picture, which is the thing being
   looked at, and a control at full strength over an image is a control in
   front of it. */
.lightbox-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 1;
  opacity: .3; transition: opacity .2s ease-in-out;
}
.lightbox-nav:hover, .lightbox-nav:focus-visible { opacity: 1; }
.lightbox-prev { left: .9rem; }
.lightbox-next { right: .9rem; }

.lightbox-thumbs { justify-content: center; padding-inline: 1.75rem; }

.lightbox-details {
  display: grid; gap: 1rem; justify-items: center; text-align: center;
  max-width: 44rem; margin-inline: auto;
  padding: 2rem 1.75rem 4rem;
}
.lightbox-details > * { max-width: 100%; }
/* A picture has no price and nothing to buy, and an empty grid item still
   holds its gap open — which read as a hole where the price should be. */
.lightbox-details > :empty { display: none; }
.lightbox-details .name { font-weight: 600; font-size: 1.35rem; letter-spacing: -0.01em; }
.lightbox-details .name p { margin: 0; }
.lightbox-details .price { font-size: 1.1rem; font-weight: 600; }
.lightbox-details .desc { color: var(--ink3); font-size: .9rem; }
.lightbox-details .desc p + p { margin-top: .75rem; }

/* ── comments ───────────────────────────────────────────────────────────── */
/*
   A thread reads as a conversation or it reads as a log. What made this a log
   was that every line looked the same: no faces, no thread rail, a date
   instead of "an hour ago", and a form of three bare boxes that belonged to
   some other application. The rules below are the difference.
*/

.lightbox-comments {
  margin-top: 2.5rem; padding-top: 2rem;
  border-top: 1px solid var(--line);
  /* The product view centres its column; a conversation is read from the
     left edge like any other prose. */
  text-align: left;
}
.comment-title {
  display: flex; align-items: center; gap: .5rem;
  font-size: .95rem; font-weight: 600;
}
.comment-title-icon { display: inline-flex; color: var(--mute); }
.comment-empty { margin-top: .75rem; font-size: .85rem; color: var(--mute); }

.comment { display: flex; gap: .75rem; padding-block: 1.1rem; }
.comment + .comment { border-top: 1px solid var(--line); }
.comment-main { flex: 1; min-width: 0; }

/* Nobody has a face — there are no accounts — so it is the initial, on a hue
   derived from the name. Two people down a thread are then two colours,
   which is what makes a thread skimmable at all. */
.comment-avatar {
  flex-shrink: 0; width: 2.25rem; height: 2.25rem; border-radius: 999px;
  display: grid; place-items: center;
  font-size: .85rem; font-weight: 600; line-height: 1;
  color: hsl(var(--avatar-hue, 0) 45% 30%);
  background: hsl(var(--avatar-hue, 0) 60% 90%);
}
/* The shop is not one of the visitors, so it is not one of the colours. */
.comment-avatar.is-shop {
  background: var(--ink); color: var(--surface);
}
@media (prefers-color-scheme: dark) {
  .comment-avatar { color: hsl(var(--avatar-hue, 0) 70% 85%); background: hsl(var(--avatar-hue, 0) 35% 25%); }
}

.comment-head { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.comment-author { font-weight: 600; font-size: .875rem; }
.comment-when { color: var(--mute); font-size: .72rem; }
.comment-badge {
  font-size: .62rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  padding: .1rem .4rem; border-radius: 999px;
  background: var(--ink); color: var(--surface);
}
.comment-body {
  margin-top: .3rem; font-size: .9rem; line-height: 1.55;
  color: var(--ink3); white-space: pre-wrap; overflow-wrap: anywhere;
}

/* One level of indentation, because there is only one level to show
   (CMNT-1). The rail is what says these answer the comment above them. */
.comment-thread {
  margin-top: .9rem; padding-left: 1rem;
  border-left: 2px solid var(--line);
}
.comment-thread .comment { padding-block: .8rem; }
.comment-thread .comment:first-child { padding-top: 0; }
.comment-thread .comment-avatar { width: 1.75rem; height: 1.75rem; font-size: .72rem; }

.comment-reply-toggle {
  margin-top: .5rem; padding: 0; border: none; background: none; cursor: pointer;
  color: var(--mute); font-size: .75rem; font-weight: 600;
}
.comment-reply-toggle:hover { color: var(--ink); text-decoration: underline; }

.comment-form { display: grid; gap: .75rem; margin-top: 1.25rem; }
.comment-thread .comment-form, .comment .comment-form { margin-top: .75rem; }
/* Name and email side by side: they are one line of who-you-are, and stacked
   they made a three-box tower taller than the comment being answered. */
.comment-form-row { display: grid; gap: .75rem; }
@media (min-width: 560px) {
  .comment-form-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.comment-form .btn { justify-self: start; }
.comment-error { color: var(--danger); font-size: .78rem; }

/* CMNT-11: what replaces the form when a comment is waiting. Not an error and
   not a success — a receipt, saying what has to happen next. */
.comment-pending {
  margin-top: 1.25rem; padding: 1rem 1.1rem;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--field); font-size: .85rem;
}
.comment-pending p { margin-top: .35rem; color: var(--ink3); line-height: 1.5; }

/* ── pictures (SECT-13) ─────────────────────────────────────────────────── */

/* A picture section is one photograph at the width it was given. It is a
   `.gallery` of one, so the tile, the hover and the lightbox are the ones
   every other picture on the site uses — but not its columns: `.gallery`
   fills a row with 15rem tracks, which turned one photograph into a thumbnail
   with the rest of the row empty beside it. */
.sb-picture { margin: 0; display: block; }
.sb-picture .gallery-item > img { width: 100%; height: auto; }
/* A picture tile has no price under it, so its caption is one line and only
   worth showing when the picture has a name of its own. */
.gallery-item.is-picture .gallery-caption { justify-content: flex-start; }
.gallery-item.is-picture .gallery-caption:empty { display: none; }

/* ── cart ───────────────────────────────────────────────────────────────── */

.cart-toggle {
  display: inline-flex; align-items: center; gap: .4rem;
  border: 1px solid var(--line); border-radius: 999px;
  padding: .4rem .8rem; font-size: .8rem; background: none; cursor: pointer; color: var(--ink);
}
.cart-toggle:hover { background: var(--field); }
.cart-count { font-variant-numeric: tabular-nums; font-weight: 600; }

/* UI-14: the surface a whole job sits on. The lightbox is one of these too —
   it adds a stage and a zoom, not a way of covering the page. */
.overlay {
  position: fixed; inset: 0; z-index: 110;
  background: var(--surface);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.overlay-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; gap: 1rem;
  padding: 1rem 1.5rem;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.overlay-body { padding: 1.5rem; }
/* One scrollbar, one wheel: the page behind stays where it was. */
body.overlay-open { overflow: hidden; }

/* UI-22: the question a destructive button asks. A card over a dimmed page
   rather than the whole surface — the page behind is the thing being decided
   about, and covering it would take the answer away with it. */
.overlay.ask {
  /* Over the drawer's 120: it is a question about whatever is on screen, and
     one of the things it is asked about is a drawer being closed. */
  z-index: 130;
  display: grid; place-items: center; padding: 1.5rem;
  background: color-mix(in oklab, var(--ink) 55%, transparent);
}
.overlay.ask > .overlay-body {
  width: min(28rem, 100%);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 12px 32px rgb(0 0 0 / .22);
}
.ask-says { margin-top: .5rem; font-size: .875rem; color: var(--ink3); }
.ask-acts { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1.5rem; }

/* COMM-17: the order, as two halves that fit on one screen — what is being
   bought, and where it is going. They stack on a phone, basket first, because
   that is the half you check before you start typing. */
.checkout {
  display: grid; gap: 2rem;
  max-width: 68rem; margin: 0 auto;
}
@media (min-width: 900px) {
  .checkout { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); align-items: start; }
}
.checkout-basket {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--field); padding: 1.25rem;
}
.checkout-line {
  display: flex; gap: .75rem; align-items: flex-start;
  padding-block: .9rem; border-bottom: 1px solid var(--line);
}
.checkout-line img, .checkout-line-blank {
  width: 3.5rem; height: 3.5rem; object-fit: cover; border-radius: 4px; flex-shrink: 0;
}
.checkout-line-blank { background: var(--line); }
.checkout-line .info { flex: 1; min-width: 0; font-size: .82rem; }
.checkout-line .info .name { font-weight: 600; }
.checkout-line .line-total { font-size: .82rem; font-weight: 600; white-space: nowrap; }
.checkout-total {
  display: flex; justify-content: space-between;
  margin-top: 1.25rem; padding-top: 1rem;
  border-top: 1px solid var(--line); font-weight: 600;
}

/* Two column-fuls of form: who to reach, and where to go. One question per
   column-heading, so neither is a wall of eight identical boxes. */
.checkout-columns { display: grid; gap: 1.5rem; }
@media (min-width: 640px) {
  .checkout-columns { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
}
.checkout-column { display: grid; gap: 1rem; align-content: start; }

/* The decision, in the corner a decision belongs in. It used to sit under the
   left-hand column, level with nothing and a screen away from the total on a
   short form. Fixed to the overlay rather than the page, because the overlay
   is what is being read. */
.checkout-actions {
  position: fixed; right: 1.5rem; bottom: 1.5rem; z-index: 2;
  display: flex; justify-content: flex-end;
}
.checkout-actions .btn { box-shadow: var(--shadow); }

/* CUST-2 */
.account-doors { display: grid; gap: 1.5rem; margin-top: calc(2rem * var(--space, 1)); }
@media (min-width: 640px) {
  .account-doors { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
}
.account-doors > section {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--field); box-shadow: var(--shadow);
  padding: 1.5rem;
}
.account-doors .eyebrow { display: block; margin-bottom: 1.25rem; }
.account-doors .form .btn { margin-top: 1.5rem; }
.h1 + .alert { margin-top: 1rem; }

.qty-stepper { display: inline-flex; align-items: center; gap: .6rem; }
.qty-stepper button {
  width: 1.75rem; height: 1.75rem; border-radius: 999px; border: 1px solid var(--line);
  background: none; color: var(--ink); cursor: pointer; font-size: .9rem; line-height: 1;
}
.qty-stepper button:hover { background: var(--field); }
.qty-stepper .n { min-width: 1.25rem; text-align: center; font-variant-numeric: tabular-nums; }

/* ── icons ──────────────────────────────────────────────────────────────── */
/*
   Font Awesome Free, vendored as SVG in projects/platform/crates/web/icons and embedded at
   compile time. No icon font, no CDN, no second request — and the glyphs take
   their colour from the text they sit in.
*/

.icon {
  width: 1em; height: 1em;
  display: inline-block; vertical-align: -0.125em;
  fill: currentColor; flex-shrink: 0;
}

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

/* ── rounded button ─────────────────────────────────────────────────────── */
/*
   A pill that is an icon at rest and widens to show its label on hover or
   keyboard focus. `max-width` rather than `display` so the transition has
   something to animate, and `is-closed` keeps the label for a screen reader
   while never showing it.
*/

/*
   At rest it is a *circle*, not an oval: `--rb-size` is both the height and
   the minimum width, and the horizontal padding is set so that a single 1em
   icon lands exactly on that width. Padding alone cannot do it — the old
   `.55rem .7rem` made the box 2.4rem wide and 2.1rem tall, so every icon
   button in the product was subtly egg-shaped.
*/
.rb {
  --rb-size: 2.5rem;
  /* Minus the 1px border on each side: `min-width` is a floor, so a content
     box even slightly wider than it wins, and forgetting the border left
     every one of these two pixels wider than it was tall. */
  --rb-pad: calc((var(--rb-size) - 1rem - 2px) / 2);

  display: inline-flex; align-items: center; justify-content: center; gap: 0;
  height: var(--rb-size); min-width: var(--rb-size);
  padding-inline: var(--rb-pad);
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink);
  cursor: pointer; font-size: .8rem; line-height: 1;
  text-decoration: none; white-space: nowrap;
  transition: border-color .2s, background .2s, color .2s, box-shadow .2s;
}
.rb:hover, .rb:focus-visible {
  border-color: var(--ink);
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.07);
}
.rb .rb-icon { font-size: 1rem; }

/* The label unfurls rather than appearing: `max-width` is what a transition
   can actually animate, and the margin keeps it off the icon as it opens. */
.rb .rb-label {
  max-width: 0; overflow: hidden; white-space: nowrap;
  transition: max-width .35s cubic-bezier(.4, 0, .2, 1),
              margin-left .35s cubic-bezier(.4, 0, .2, 1);
  margin-left: 0;
}
.rb:hover .rb-label,
.rb:focus-visible .rb-label,
.rb.is-open .rb-label { max-width: 14rem; margin-left: .5rem; }
.rb.is-closed .rb-label { max-width: 0; margin-left: 0; }

/* One that always shows its label is a pill, not a circle, so it gets the
   breathing room a circle does not need. */
.rb.is-open { padding-inline: calc(var(--rb-pad) + .15rem); }

.rb.is-primary {
  background: var(--ink); color: var(--surface); border-color: var(--ink);
}
.rb.is-primary:hover { box-shadow: 0 2px 12px rgb(0 0 0 / 0.16); }
.rb.is-danger { color: var(--danger); border-color: var(--danger); }
.rb.is-danger:hover { background: var(--danger-soft); border-color: var(--danger); }

/* One that cannot be pressed has to look it. A primary that is off keeps the
   ink of a live button otherwise, which is a button lying about itself — and
   the lie is loudest exactly where it matters, on a Save with nothing to save. */
.rb:disabled { opacity: .4; cursor: default; }
.rb:disabled:hover { border-color: var(--line); box-shadow: none; }
.rb.is-danger:disabled:hover { background: none; border-color: var(--danger); }

@media (prefers-reduced-motion: reduce) {
  .rb, .rb .rb-label { transition: none; }
}

.btn-icon { margin-right: .5rem; }

/* ── floating-label fields ──────────────────────────────────────────────── */
/*
   The label sits inside the control's border and the input carries the top
   padding to clear it, so a form is a column of equal boxes rather than one
   of label-plus-box pairs at varying heights.
*/

/*
   The default for every field, not an opt-in variant: hand-written forms
   already put the label first and the control second, which is the order this
   needs, so one rule keeps every form in the product looking the same instead
   of only the ones built through `ui::field`.
*/
.field { position: relative; }
.field > .label {
  position: absolute; top: .35rem; left: .6rem; z-index: 1;
  margin: 0; font-size: .62rem; pointer-events: none;
}
.field.has-icon > .label { left: 2.4rem; }
/* Measured from the top, not the bottom: a field with a hint is taller, and
   from the bottom the icon lands in the hint instead of in the input. */
.field .field-icon {
  position: absolute; left: .75rem; top: 1.7rem;
  color: var(--mute); pointer-events: none; line-height: 1;
}
.field .input,
.field .select { padding: 1.5rem .7rem .45rem; height: 3.4rem; }
.field .textarea { padding: 1.5rem .7rem .5rem; }
.field.has-icon .input,
.field.has-icon .select,
.field.has-icon .textarea { padding-left: 2.4rem; }

/* UI-37: a price is one box — the amount stretches, the currency sits at its
   right edge. The code has no floating label over it, so it centres. */
.field.money { display: flex; flex-wrap: wrap; gap: .5rem; }
.field.money .money-amount { flex: 1 1 8rem; }
.field.money .money-cur { flex: 0 0 auto; width: auto; min-width: 5.2rem; padding: 0 .7rem; }
.field.money .money-cur:disabled { color: var(--mute); }
.field.money > .hint { flex-basis: 100%; }

/* ── choice lists ───────────────────────────────────────────────────────── */
/*
   A `select` is a radio list that collapses to the chosen row, not a native
   dropdown: a dropdown cannot show a tree, cannot animate, and looks like a
   different control on every platform.

   The collapse is `:has()`, so it is correct before any script runs and stays
   correct if one never does. The script only toggles `data-open`.
*/

.choice-field { position: relative; }
.choice-field > .label {
  position: absolute; top: .35rem; left: .6rem; z-index: 1;
  font-size: .62rem; pointer-events: none;
}
.choice-field.has-icon > .label { left: 2.4rem; }

.sel {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
  padding: 1.6rem .55rem .45rem;
  display: grid; gap: 0;
  transition: border-color .15s;
}
.sel:hover, .sel:focus-within { border-color: var(--mute); }
.choice-field.has-icon .sel { padding-left: 2.4rem; }

.sel-multi { max-height: 17rem; overflow-y: auto; }

.sel-node {
  visibility: visible;
  transition: max-height .4s cubic-bezier(.4, 0, .2, 1), opacity .25s ease, visibility 0s;
}

/* Closed, with something chosen: every branch that does not contain the
   chosen row folds away. The ones that do — the row itself and its
   ancestors — stay, so the list still says where you are. */
.sel[data-open="false"]:has(input:checked) .sel-node:not(:has(input:checked)) {
  max-height: 0; opacity: 0; visibility: hidden;
  transition: max-height .4s cubic-bezier(.4, 0, .2, 1), opacity .2s ease,
              visibility 0s linear .4s;
}
/* `visibility: hidden` is what takes a folded row out of the tab order —
   `opacity: 0` alone leaves a control you can focus but cannot see. */

.sel-opt {
  display: flex; align-items: flex-start; gap: .55rem;
  padding: .3rem 0; cursor: pointer; font-size: .82rem; line-height: 1.45;
}
.sel-opt:hover .opt-mark { border-color: var(--ink3); }
.sel-opt input { position: absolute; opacity: 0; width: 0; height: 0; }

.opt-mark {
  width: 1.05rem; height: 1.05rem; margin-top: .12rem; flex-shrink: 0;
  border: 1px solid var(--mute); background: var(--surface);
  display: grid; place-items: center;
  transition: background .18s ease, border-color .18s ease;
}
.opt-dot { border-radius: 999px; }
.opt-box { border-radius: 5px; }

.opt-mark::after {
  content: ""; opacity: 0; transform: scale(.4);
  transition: opacity .18s ease, transform .18s cubic-bezier(.34, 1.56, .64, 1);
}
.opt-dot::after {
  width: .45rem; height: .45rem; border-radius: 999px; background: var(--surface);
}
.opt-box::after {
  width: .3rem; height: .58rem; margin-top: -.12rem;
  border: solid var(--surface); border-width: 0 2px 2px 0;
}

input:checked + .opt-mark { background: var(--ink); border-color: var(--ink); }
input:checked + .opt-mark::after { opacity: 1; transform: scale(1); }
input:checked + .opt-box::after { transform: rotate(45deg) scale(1); }
.opt-box::after { transform: rotate(45deg) scale(.4); }

input:focus-visible + .opt-mark {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

.sel-text { min-width: 0; }
.sel-value { opacity: .35; font-size: .7rem; margin-left: .4rem; }

/* Nesting, marked by the line it hangs from rather than by indentation alone. */
.sel-kids {
  margin-left: .5rem; padding-left: .75rem;
  border-left: 1px dotted var(--line);
}

@media (prefers-reduced-motion: reduce) {
  .sel-node, .opt-mark, .opt-mark::after { transition: none; }
}

/* UI-13's group: a box that says what it holds and holds it shut. The border
   is what makes a folded group read as a lid rather than as the end of the
   form — closed, the summary is the only thing on that line. */
.advanced {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--field); padding: .5rem .6rem;
}
.advanced > summary {
  display: flex; align-items: center; gap: .4rem;
  font-size: .72rem; font-weight: 600; color: var(--ink3);
}
.advanced > summary:hover { color: var(--ink); }
.advanced > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The arrow points along the fold: right when shut, down when open. */
.advanced-caret { width: .7em; height: .7em; transition: transform .3s cubic-bezier(.4, 0, .2, 1); }
.advanced[open] > summary .advanced-caret { transform: rotate(90deg); }
.advanced-body > * + * { margin-top: 1rem; }
.advanced-body { margin-top: .85rem; }

@media (prefers-reduced-motion: reduce) {
  .advanced-caret { transition: none; }
}

/* A checkbox reads left to right, so it opts out of the floating label. */
.field-check { position: static; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.field-check > .label { position: static; margin: 0; font-size: .72rem; }
.field-check > .hint { flex-basis: 100%; margin-top: 0; }

/* A decision, not a field (UI-36). The whole row is the hit area, the note is
   inside the control, and the box it sits in is what tells it apart from the
   prose around it. */
.sw {
  display: flex; align-items: flex-start; gap: .85rem; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: .7rem .8rem;
}
.sw + .sw { margin-top: .5rem; }
.sw:hover { border-color: var(--accent-line); }
.sw:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The paint follows the checkbox itself, so a click repaints with no script
   and no reload — `is-on` is only the server's word for how it rendered. */
.sw:has(.sw-in:checked) { border-color: var(--accent-line); background: var(--accent-soft); }
.sw.is-locked { cursor: default; opacity: .72; }
.sw.is-locked:hover { border-color: var(--line); }

/* The real input carries the state and the keyboard; the track is what is
   seen. Hidden with clip rather than display:none, which would take it out of
   the tab order along with the paint. */
.sw-in {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

.sw-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .2rem; }
.sw-label { font-size: .82rem; font-weight: 500; color: var(--ink); }
.sw-note { font-size: .72rem; line-height: 1.45; color: var(--ink3); }

.sw-track {
  flex: none; width: 34px; height: 20px; margin-top: .1rem; border-radius: 10px;
  background: var(--line); transition: background .18s ease;
  display: flex; align-items: center; padding: 2px;
}
.sw-knob {
  width: 16px; height: 16px; border-radius: 50%; background: var(--surface);
  /* A knob's own shadow, not the page's card shadow: the 50px layer of
     var(--shadow) paints a smudge under every toggle on a calm surface. */
  box-shadow: 0 1px 2px oklch(0.2 0.01 85 / 0.25);
  transition: transform .18s ease;
}
.sw:has(.sw-in:checked) .sw-track { background: var(--accent); }
.sw:has(.sw-in:checked) .sw-knob { transform: translateX(14px); }

/* One of a run, so a tick rather than a track: a switch per option would say
   each could be on at once, which is the thing a run rules out. */
.sw-mark {
  flex: none; width: 20px; height: 20px; margin-top: .1rem; border-radius: 50%;
  border: 1px solid var(--line); display: flex; align-items: center; justify-content: center;
  color: var(--accent-ink); background: var(--surface);
}
.sw-mark svg { width: .7em; height: .7em; display: none; }
.sw-pick:has(.sw-in:checked) .sw-mark { background: var(--accent); border-color: var(--accent); }
.sw-pick:has(.sw-in:checked) .sw-mark svg { display: block; }

@media (prefers-reduced-motion: reduce) {
  .sw-track, .sw-knob { transition: none; }
}

/* A colour answered by pointing at it (UI-15): a run of painted chips, round
   for an accent, square for a paper. The paint is the value. */
.swatches { display: flex; flex-wrap: wrap; gap: .5rem; }
.swatch {
  width: 1.9rem; height: 1.9rem; border-radius: 7px; cursor: pointer;
  background: var(--swatch); border: 2px solid transparent;
  outline: 1px solid var(--line); outline-offset: -1px;
}
.swatch-round { border-radius: 50%; }
.swatch:has(input:checked) { border-color: var(--accent); outline-color: var(--surface); }
.swatch:focus-within { border-color: var(--accent); }
.swatch > input {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* ── sections ───────────────────────────────────────────────────────────── */
/*
   Arrangements are a closed set in the schema, so this is the whole list —
   a section cannot ask for a class that is not written here.
*/

.sb-stack { display: grid; gap: 2rem; }
/* One rule for every grid. The column counts arrive as custom properties on
   the element (SECT-7), so "three across on a phone, six on a desktop" is two
   integers rather than a class per combination — there are thirty-two, and a
   stylesheet with thirty-two near-identical rules is one nobody keeps right.
   One breakpoint, because two numbers is what an author was asked for. */
.sb-grid {
  display: grid; gap: .5rem;
  grid-template-columns: repeat(var(--cols-m, 1), minmax(0, 1fr));
}
@media (min-width: 640px) {
  .sb-grid { gap: 1rem; grid-template-columns: repeat(var(--cols-d, 3), minmax(0, 1fr)); }
}
/* A grid item is `min-width: auto` by default, so it refuses to shrink below
   its content — which pushes the last column out past its own container's
   border the moment anything inside it is wider than its track. */
.sb-children > * { min-width: 0; }

/* A gallery sizes its own tracks; inside an arranged section the section's
   count wins, so this comes after `.gallery` rather than before it. Without
   that the two grids fight and the arrangement does nothing. */
.gallery.sb-stack { grid-template-columns: 1fr; }

/* A tile in a grid is a square crop, so a wall of them is a wall rather than a
   ragged edge — the picture decides what is interesting, the grid decides the
   shape. The caption sits under it and does not enter the square. */
.gallery.sb-grid .gallery-item > img,
.gallery.sb-grid .gallery-empty {
  aspect-ratio: 1; width: 100%; height: auto; object-fit: cover;
}
.sb-heading { margin-top: 1rem; }

/* SECT-7's control: a mode, and — only when it is `grid` — two counts. */
.arrangement-field { display: grid; gap: .75rem; }
.arrangement-cols { display: flex; gap: .75rem; }
.arrangement-cols > * { flex: 1; min-width: 0; }

/* SECT-12's: two axes side by side, because vertical and horizontal are one
   question asked twice and reading them on one line says so. */
.placement-pair { display: flex; gap: .75rem; }
.placement-pair > * { flex: 1; min-width: 0; }

.tab-icon { margin-right: .4rem; color: var(--mute); }
.active-tab .tab-icon { color: inherit; }

/* A pill in a dense list is a smaller circle, not a squashed one — only
   `--rb-size` changes, so the padding that keeps it round follows. */
.row .rb, .pill .rb, .card .rb, .row-actions .rb { --rb-size: 2rem; }
.pill .rb { --rb-size: 1.5rem; }
.pill .rb .rb-icon { font-size: .75rem; }

/* BUILD-8: a root section is as wide as the window and carries its own reading
   column. The page used to be one 720px column with everything inside it,
   which left no way to express a full-bleed band, a wide gallery, or a
   two-column container that actually reads as two columns. Nesting is
   untouched — a child is as wide as the parent arranging it. */
.page-main { width: 100%; }
/* The title and a page-level layout slot are full-bleed bands too, so they
   line up with the reading column the sections carry instead of sitting in a
   narrower one of their own — which read as a centred heading over
   left-aligned content. */
/* The same 8px a padded section carries, so the title lines up with the
   content under it rather than sitting in a gutter of its own. */
.page-band { padding-inline: 8px; }
.page-title { padding-block: 2.5rem 0.5rem; }

/* SECT-12: a section's content fills the section, and a section fills the
   cell it was given. A root section is therefore as wide as the window, and a
   container split two ways gives two halves that really are halves.
   
   The 72rem reading column that used to sit here made that untrue: two columns
   were two halves of 1152px floating in a wider page, and there was no way to
   ask for anything else. Width is now a consequence of the tree — how many
   columns the parent has — and the space around it is `padded`/`spaced`,
   which an author sets per section rather than inheriting from a rule. */
.sb-node { display: grid; grid-template-columns: minmax(0, 1fr); }
.sb-body { min-width: 0; }
.sb-padded > .sb-body { padding: 8px; }
.sb-spaced > .sb-body { margin: 8px; }
.nav .rb { --rb-size: 1.6rem; }

/* ── drawer ─────────────────────────────────────────────────────────────── */
/*
   Where a record is made, on every console screen that makes one. One panel
   per page, refilled from whichever row opened it —
   the same shape the cart already uses on the storefront, so "a form" and
   "a thing sliding in from the right" are the same idea everywhere.
*/

.drawer {
  position: fixed; inset: 0; z-index: 120;
  background: rgb(0 0 0 / 0.4);
  display: flex; justify-content: flex-end;
  animation: drawer-backdrop-in .18s ease;
}
.drawer-panel {
  width: min(34rem, 100%); height: 100%;
  display: flex; flex-direction: column;
  background: var(--surface); border-left: 1px solid var(--line);
  animation: drawer-in .22s cubic-bezier(.4, 0, .2, 1);
}

/* Arriving and leaving are the same movement, and a panel that fades in over
   a fifth of a second and then vanishes between two frames reads as a bug.
   `is-closing` is worn for exactly as long as the leaving animation, and the
   script hides the drawer when it comes off. */
@keyframes drawer-backdrop-in { from { opacity: 0; } }
@keyframes drawer-backdrop-out { to { opacity: 0; } }
@keyframes drawer-in { from { transform: translateX(1.5rem); opacity: 0; } }
@keyframes drawer-out { to { transform: translateX(1.5rem); opacity: 0; } }
.drawer.is-closing { animation: drawer-backdrop-out .18s ease forwards; pointer-events: none; }
.drawer.is-closing .drawer-panel { animation: drawer-out .18s cubic-bezier(.4, 0, .2, 1) forwards; }
@media (prefers-reduced-motion: reduce) {
  .drawer, .drawer-panel, .drawer.is-closing, .drawer.is-closing .drawer-panel { animation: none; }
}

.drawer-head {
  display: flex; align-items: center; gap: .5rem;
  padding: 1rem 1.25rem; border-bottom: 1px solid var(--line);
  background: var(--field);
}
.drawer-body { flex: 1; overflow-y: auto; padding: 1.25rem; }
.drawer-actions { display: flex; align-items: center; gap: .5rem; }
.drawer-status { font-size: .68rem; color: var(--mute); }
.drawer-status.is-saved { color: var(--star); }
/* Settings with an endpoint of their own, below the form rather than in it. */
.drawer-extra { border-top: 1px solid var(--line); padding: 1.25rem; background: var(--field); }

/* The panel is the page's only interactive surface while it is open, so the
   page behind it does not scroll under it. */
body.drawer-open { overflow: hidden; }

/* ── claiming an address ────────────────────────────────────────────────── */

.brand-tail { color: var(--accent); }

.claim-main { padding-block: clamp(1.5rem, 4vh, 2.75rem) 4rem; }
.claim-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2.25rem; align-items: start; }
@media (min-width: 62rem) {
  .claim-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: 2.75rem; }
  /* The preview follows the form down: what is being typed and what it makes
     have to be on screen at the same time or neither is answering the other. */
  .claim-see { position: sticky; top: 6rem; }
}
.claim-say { min-width: 0; }
.claim-see { min-width: 0; }
.claim-see-head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; margin-bottom: .5rem; }

/* The address the title is about to take, under the field that decides it. */
.claim-host { font-family: var(--mono); font-size: .8rem; color: var(--ink3); min-height: 1.4em; margin-top: -.4rem; }
.claim-host-taken { color: var(--danger); }

.claim-go { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin-top: 1.5rem; }

/* Two fields that are one answer — a first name and a last name sit on one
   line at any width that has room for two. */
.pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 0 .75rem; }

/* ── the segmented chips ────────────────────────────────────────────────── */

.seg { display: inline-flex; gap: 3px; padding: 3px; border-radius: 9px; background: var(--chip); }
.seg-item {
  height: 1.55rem; padding: 0 .75rem; border: 0; border-radius: 7px;
  background: transparent; color: var(--ink3); font-size: .72rem; cursor: pointer;
  transition: background .15s, color .15s;
}
.seg-item[aria-pressed="true"],
.segf input:checked + .seg-item { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px oklch(0.2 0.01 85 / 0.08); }

/* The same chips answering a form question: a label wearing the chip, and the
   radio inside it carrying the state and the keyboard. */
.segf { display: flex; }
.segf-item { flex: 1; display: flex; position: relative; cursor: pointer; }
.segf-item .seg-item { flex: 1; display: inline-flex; align-items: center; justify-content: center; }
.segf-item:has(.sw-in:focus-visible) .seg-item { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── the template picker ────────────────────────────────────────────────── */

.picker { display: grid; grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr)); gap: .6rem; }
.pick {
  display: flex; flex-direction: column; gap: .45rem; position: relative;
  padding: .55rem; border-radius: 12px; cursor: pointer;
  border: 1.5px solid var(--line); background: var(--surface);
  transition: border-color .15s, background .15s;
}
/* Out of sight, still in the tab order and still announced: `hidden` took the
   radio out of both, which left the grid unreachable by keyboard. */
.pick-radio {
  position: absolute; width: 1px; height: 1px; margin: -1px;
  padding: 0; border: 0; overflow: hidden; clip-path: inset(50%);
}
.pick:hover { border-color: var(--mute); }
.pick:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.pick:has(input:focus-visible) { outline: 2px solid var(--accent-line); outline-offset: 2px; }
.pick-name { font-size: .78rem; font-weight: 600; }

/* The card draws the page it makes: a bar, a hero, a row of cards. */
.pick-plan { display: flex; flex-direction: column; gap: 3px; padding: 5px; border-radius: 7px; background: var(--field); }
.pick-bar { display: flex; align-items: center; gap: 3px; }
.pick-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--ink3); flex: none; }
.pick-nav { height: 2px; flex: 1; border-radius: 1px; background: var(--line); }
.pick-hero { display: flex; flex-direction: column; align-items: center; gap: 3px; padding-block: 5px; }
.pick-line { width: 60%; height: 3px; border-radius: 2px; background: var(--ink3); }
.pick-cta { width: 22%; height: 4px; border-radius: 2px; background: var(--accent); }
.pick-row { display: flex; gap: 3px; }
.pick-card { flex: 1; height: 9px; border-radius: 2px; background: var(--line); }
.pick-meta { margin-top: .5rem; }

/* A prepared look paints its own plan, so the grid reads as different sites. */
.pick-plan.tinted { background: var(--pk-bg); }
.pick-plan.tinted .pick-dot { background: var(--pk-accent); }
.pick-plan.tinted .pick-nav { background: color-mix(in srgb, var(--pk-ink) 22%, var(--pk-bg)); }
.pick-plan.tinted .pick-line { background: color-mix(in srgb, var(--pk-ink) 85%, var(--pk-bg)); }
.pick-plan.tinted .pick-cta { background: var(--pk-accent); }
.pick-plan.tinted .pick-card { background: color-mix(in srgb, var(--pk-ink) 16%, var(--pk-bg)); }

/* ── the preview ────────────────────────────────────────────────────────── */

.prev { --prev-width: 100%; }
.prev[data-segmented="phone"] { --prev-width: 20rem; }
.prev-frame {
  width: var(--prev-width); margin-inline: auto;
  border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; background: var(--surface); box-shadow: var(--shadow);
  transition: width .2s;
}
.prev-chrome { display: flex; align-items: center; gap: .35rem; padding: .45rem .6rem; background: var(--field); border-bottom: 1px solid var(--line); }
.prev-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--line); flex: none; }
.prev-url { flex: 1; margin-left: .35rem; font-family: var(--mono); font-size: .62rem; color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prev-page { display: flex; flex-direction: column; min-height: 19rem; }
.prev-bar { display: flex; align-items: center; gap: .5rem; padding: .7rem .9rem; border-bottom: 1px solid var(--hair); }
.prev-mark {
  width: 1.35rem; height: 1.35rem; flex: none; border-radius: 5px;
  background: var(--solid); color: var(--solid-ink);
  display: flex; align-items: center; justify-content: center;
  font-size: .55rem; font-weight: 600; letter-spacing: .02em;
}
.prev-brand { font-family: var(--serif); font-size: .85rem; font-weight: 600; letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prev-body { flex: 1; display: flex; flex-direction: column; }
.prev-sec { padding: 1.25rem .9rem; text-align: center; }
.prev-sec + .prev-sec { border-top: 1px solid var(--hair); }
.prev-h { font-family: var(--serif); font-size: 1rem; font-weight: 500; letter-spacing: -0.01em; }
.prev-p { margin-top: .25rem; font-size: .72rem; color: var(--ink3); line-height: 1.5; }
.prev-foot { padding: .6rem .9rem; border-top: 1px solid var(--hair); background: var(--field); font-size: .62rem; color: var(--mute); text-align: center; }

/* The chosen look tints the page being previewed (REG-11): the tint rides on
   three custom properties the script can move when the pick changes. */
.prev-page.tinted { background: var(--pv-bg); color: var(--pv-ink); }
.prev-page.tinted .prev-bar { border-bottom-color: color-mix(in srgb, var(--pv-ink) 14%, transparent); }
.prev-page.tinted .prev-mark { background: var(--pv-accent); color: var(--pv-bg); }
.prev-page.tinted .prev-h { color: var(--pv-ink); }
.prev-page.tinted .prev-p { color: color-mix(in srgb, var(--pv-ink) 72%, var(--pv-bg)); }
.prev-page.tinted .prev-sec + .prev-sec { border-top-color: color-mix(in srgb, var(--pv-ink) 12%, transparent); }
.prev-page.tinted .prev-foot {
  background: color-mix(in srgb, var(--pv-ink) 6%, var(--pv-bg));
  color: color-mix(in srgb, var(--pv-ink) 62%, var(--pv-bg));
  border-top-color: color-mix(in srgb, var(--pv-ink) 12%, transparent);
}

/* An example picture, before it exists: a placeholder in the offer's own
   colours, one wide or a grid — the shape the real section takes. */
.prev-art {
  height: 5.2rem; border-radius: 6px;
  background: linear-gradient(
    135deg,
    color-mix(in srgb, var(--pv-accent, var(--accent)) 42%, var(--pv-bg, var(--surface))),
    color-mix(in srgb, var(--pv-ink, var(--ink)) 34%, var(--pv-bg, var(--surface)))
  );
}
.prev-sec > .prev-art, .prev-sec > .prev-grid { margin-bottom: .6rem; }
.prev-grid { display: grid; grid-template-columns: repeat(var(--n, 3), 1fr); gap: 4px; }
.prev-grid .prev-art { height: 2.6rem; }

/* ── a phone ────────────────────────────────────────────────────────────── */
/* Read at arm's length: everything set in rem grows together off the root,
   and the smallest print — ten-pixel capitals nobody could read — takes a
   step more on top. The owner's own --body-size keeps ruling the prose and
   gains the point everything else gained. Fields land at 1rem, which is the
   16px under which iOS zooms into every focus. */
@media (max-width: 640px) {
  html { font-size: 112.5%; }
  body { font-size: calc(var(--body-size, 16px) + 1px); }
  .input, .textarea, .select { font-size: 1rem; }
  .eyebrow { font-size: .72rem; }
  .tiny { font-size: .75rem; }
  .hint { font-size: .75rem; }
  .label { font-size: .78rem; }
  .field-error { font-size: .75rem; }
  .stat-label { font-size: .72rem; }
  .nav a, .nav-more summary { font-size: .95rem; }
  .claim-host { font-size: .9rem; }
}

/* The utility layer (UI-42). Served after every application's own sheet, so a
   class here wins the tie against a component class setting the same property.

   Every value is a step on `theme/scale.rs` or a token on `theme/tokens.rs`.
   Nothing in this file names a number or a colour of its own. */

/* ── layout ──────────────────────────────────────────────────────────────── */

.stack { display: flex; flex-direction: column; }
.row { display: flex; flex-direction: row; align-items: center; }
.grid { display: grid; }
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.wrap { flex-wrap: wrap; }
.nowrap { flex-wrap: nowrap; }

.start { align-items: flex-start; }
.center { align-items: center; }
.end { align-items: flex-end; }
.baseline { align-items: baseline; }
.stretch { align-items: stretch; }
.between { justify-content: space-between; }
.around { justify-content: space-around; }
.jstart { justify-content: flex-start; }
.jcenter { justify-content: center; }
.jend { justify-content: flex-end; }

.grow { flex: 1 1 auto; min-width: 0; }
.none { flex: none; }
.shrink { min-width: 0; }

.block { display: block; }
.inline { display: inline; }
.iblock { display: inline-block; }
.iflex { display: inline-flex; align-items: center; }
.relative { position: relative; }
.absolute { position: absolute; }
.sticky { position: sticky; }
.clip { overflow: hidden; }
.scroll-y { overflow-y: auto; }
.scroll-x { overflow-x: auto; }

/* ── space ───────────────────────────────────────────────────────────────── */

.gap-0 { gap: 0; }
.gap-1 { gap: var(--s1); }
.gap-2 { gap: var(--s2); }
.gap-3 { gap: var(--s3); }
.gap-4 { gap: var(--s4); }
.gap-5 { gap: var(--s5); }
.gap-6 { gap: var(--s6); }
.gap-7 { gap: var(--s7); }
.gap-8 { gap: var(--s8); }
.gap-9 { gap: var(--s9); }
.gap-10 { gap: var(--s10); }
.gap-11 { gap: var(--s11); }
.gap-12 { gap: var(--s12); }
.gap-13 { gap: var(--s13); }

.gx-0 { column-gap: 0; }
.gx-1 { column-gap: var(--s1); }
.gx-2 { column-gap: var(--s2); }
.gx-3 { column-gap: var(--s3); }
.gx-4 { column-gap: var(--s4); }
.gx-5 { column-gap: var(--s5); }
.gx-6 { column-gap: var(--s6); }
.gx-7 { column-gap: var(--s7); }
.gx-8 { column-gap: var(--s8); }
.gx-9 { column-gap: var(--s9); }
.gx-10 { column-gap: var(--s10); }
.gx-11 { column-gap: var(--s11); }
.gx-12 { column-gap: var(--s12); }
.gx-13 { column-gap: var(--s13); }

.gy-0 { row-gap: 0; }
.gy-1 { row-gap: var(--s1); }
.gy-2 { row-gap: var(--s2); }
.gy-3 { row-gap: var(--s3); }
.gy-4 { row-gap: var(--s4); }
.gy-5 { row-gap: var(--s5); }
.gy-6 { row-gap: var(--s6); }
.gy-7 { row-gap: var(--s7); }
.gy-8 { row-gap: var(--s8); }
.gy-9 { row-gap: var(--s9); }
.gy-10 { row-gap: var(--s10); }
.gy-11 { row-gap: var(--s11); }
.gy-12 { row-gap: var(--s12); }
.gy-13 { row-gap: var(--s13); }

.p-0 { padding: 0; }
.p-1 { padding: var(--s1); }
.p-2 { padding: var(--s2); }
.p-3 { padding: var(--s3); }
.p-4 { padding: var(--s4); }
.p-5 { padding: var(--s5); }
.p-6 { padding: var(--s6); }
.p-7 { padding: var(--s7); }
.p-8 { padding: var(--s8); }
.p-9 { padding: var(--s9); }
.p-10 { padding: var(--s10); }
.p-11 { padding: var(--s11); }
.p-12 { padding: var(--s12); }
.p-13 { padding: var(--s13); }

.px-0 { padding-inline: 0; }
.px-1 { padding-inline: var(--s1); }
.px-2 { padding-inline: var(--s2); }
.px-3 { padding-inline: var(--s3); }
.px-4 { padding-inline: var(--s4); }
.px-5 { padding-inline: var(--s5); }
.px-6 { padding-inline: var(--s6); }
.px-7 { padding-inline: var(--s7); }
.px-8 { padding-inline: var(--s8); }
.px-9 { padding-inline: var(--s9); }
.px-10 { padding-inline: var(--s10); }
.px-11 { padding-inline: var(--s11); }
.px-12 { padding-inline: var(--s12); }
.px-13 { padding-inline: var(--s13); }

.py-0 { padding-block: 0; }
.py-1 { padding-block: var(--s1); }
.py-2 { padding-block: var(--s2); }
.py-3 { padding-block: var(--s3); }
.py-4 { padding-block: var(--s4); }
.py-5 { padding-block: var(--s5); }
.py-6 { padding-block: var(--s6); }
.py-7 { padding-block: var(--s7); }
.py-8 { padding-block: var(--s8); }
.py-9 { padding-block: var(--s9); }
.py-10 { padding-block: var(--s10); }
.py-11 { padding-block: var(--s11); }
.py-12 { padding-block: var(--s12); }
.py-13 { padding-block: var(--s13); }

.pt-0 { padding-top: 0; }
.pt-1 { padding-top: var(--s1); }
.pt-2 { padding-top: var(--s2); }
.pt-3 { padding-top: var(--s3); }
.pt-4 { padding-top: var(--s4); }
.pt-5 { padding-top: var(--s5); }
.pt-6 { padding-top: var(--s6); }
.pt-7 { padding-top: var(--s7); }
.pt-8 { padding-top: var(--s8); }
.pt-9 { padding-top: var(--s9); }
.pt-10 { padding-top: var(--s10); }
.pt-11 { padding-top: var(--s11); }
.pt-12 { padding-top: var(--s12); }
.pt-13 { padding-top: var(--s13); }

.pb-0 { padding-bottom: 0; }
.pb-1 { padding-bottom: var(--s1); }
.pb-2 { padding-bottom: var(--s2); }
.pb-3 { padding-bottom: var(--s3); }
.pb-4 { padding-bottom: var(--s4); }
.pb-5 { padding-bottom: var(--s5); }
.pb-6 { padding-bottom: var(--s6); }
.pb-7 { padding-bottom: var(--s7); }
.pb-8 { padding-bottom: var(--s8); }
.pb-9 { padding-bottom: var(--s9); }
.pb-10 { padding-bottom: var(--s10); }
.pb-11 { padding-bottom: var(--s11); }
.pb-12 { padding-bottom: var(--s12); }
.pb-13 { padding-bottom: var(--s13); }

/* The only two margins there are. Spacing between siblings belongs to the
   container's gap: a margin that suited one parent overlapped the next, which
   is what this layer exists to stop. */
.m-0 { margin: 0; }
.mx-auto { margin-inline: auto; }

/* ── type ────────────────────────────────────────────────────────────────── */

.t-1 { font-size: var(--t1); line-height: var(--lh1); }
.t-2 { font-size: var(--t2); line-height: var(--lh2); }
.t-3 { font-size: var(--t3); line-height: var(--lh3); }
.t-4 { font-size: var(--t4); line-height: var(--lh4); }
.t-5 { font-size: var(--t5); line-height: var(--lh5); }
.t-6 { font-size: var(--t6); line-height: var(--lh6); }
.t-7 { font-size: var(--t7); line-height: var(--lh7); }
.t-8 { font-size: var(--t8); line-height: var(--lh8); }
.t-9 { font-size: var(--t9); line-height: var(--lh9); }
.t-10 { font-size: var(--t10); line-height: var(--lh10); }
.t-11 { font-size: var(--t11); line-height: var(--lh11); }

.serif { font-family: var(--serif); }
.sans { font-family: var(--sans); }
.mono { font-family: var(--mono); }

.fw-4 { font-weight: 400; }
.fw-5 { font-weight: 500; }
.fw-6 { font-weight: 600; }
.fw-7 { font-weight: 700; }

.upper { text-transform: uppercase; }
.track { letter-spacing: 0.08em; }
.tabular { font-variant-numeric: tabular-nums; }
.tleft { text-align: left; }
.tcenter { text-align: center; }
.tright { text-align: right; }
.pretty { text-wrap: pretty; }
.balance { text-wrap: balance; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── ink ─────────────────────────────────────────────────────────────────── */

.text-ink { color: var(--ink); }
.text-ink2 { color: var(--ink2); }
.text-ink3 { color: var(--ink3); }
.text-mute { color: var(--mute); }
.text-faint { color: var(--faint); }
.text-accent { color: var(--accent); }
.text-accent-ink { color: var(--accent-ink); }
.text-solid-ink { color: var(--solid-ink); }
.text-danger { color: var(--danger); }
.text-ok { color: var(--ok-ink); }
.text-warn { color: var(--warn-ink); }
.text-info { color: var(--info); }
.text-star { color: var(--star); }

/* ── ground ──────────────────────────────────────────────────────────────── */

.bg-none { background: none; }
.bg-page { background: var(--page); }
.bg-surface { background: var(--surface); }
.bg-rail { background: var(--rail); }
.bg-mid { background: var(--mid); }
.bg-chip { background: var(--chip); }
.bg-field { background: var(--field); }
.bg-accent { background: var(--accent); }
.bg-accent-soft { background: var(--accent-soft); }
.bg-solid { background: var(--solid); }
.bg-danger-soft { background: var(--danger-soft); }
.bg-ok { background: var(--ok-bg); }
.bg-warn { background: var(--warn-bg); }
.bg-info-soft { background: var(--info-soft); }
.bg-star-soft { background: var(--star-soft); }

/* ── edges ───────────────────────────────────────────────────────────────── */

.line { border: 1px solid var(--line); }
.line-t { border-top: 1px solid var(--line); }
.line-b { border-bottom: 1px solid var(--line); }
.line-l { border-left: 1px solid var(--line); }
.line-r { border-right: 1px solid var(--line); }
.line2 { border: 1px solid var(--line2); }
.hair { border: 1px solid var(--hair); }
.hair-t { border-top: 1px solid var(--hair); }
.hair-b { border-bottom: 1px solid var(--hair); }
.line-accent { border: 1px solid var(--accent-line); }
.line-danger { border: 1px solid var(--danger-line); }
.line-warn { border: 1px solid var(--warn-line); }
.line-none { border: 0; }

.r-0 { border-radius: 0; }
.r-1 { border-radius: var(--r1); }
.r-2 { border-radius: var(--r2); }
.r-3 { border-radius: var(--r3); }
.r-4 { border-radius: var(--r4); }
.r-5 { border-radius: var(--r5); }
.r-6 { border-radius: var(--r6); }
.r-7 { border-radius: var(--r7); }
.r-pill { border-radius: var(--r-pill); }

.shadow { box-shadow: var(--shadow); }
.shadow-none { box-shadow: none; }
