/* Parikh Labs — one stylesheet for every page. Design tokens first, then layout, then components.
   Light theme only. Colour pairs used for text meet WCAG AA (4.5:1); lime is only ever used as a fill or on dark. */

@font-face { font-family: 'Atkinson Hyperlegible Next'; font-style: normal; font-weight: 300 700; font-display: optional;
  src: url('/assets/fonts/atkinson-hyperlegible-next-latin.woff2?v=18b2a1a39a') 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: 'Atkinson Hyperlegible Next'; font-style: normal; font-weight: 300 700; font-display: optional;
  src: url('/assets/fonts/atkinson-hyperlegible-next-latin-ext.woff2?v=7dae0c6c66') 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: 'Familjen Grotesk'; font-style: normal; font-weight: 400 700; font-display: optional;
  src: url('/assets/fonts/familjen-grotesk-latin.woff2?v=414d5dfe5f') 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: 'Familjen Grotesk'; font-style: normal; font-weight: 400 700; font-display: optional;
  src: url('/assets/fonts/familjen-grotesk-latin-ext.woff2?v=c53f18ecf4') 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; }

/* ───────────── tokens ───────────── */
:root {
  --font: 'Atkinson Hyperlegible Next', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --display: 'Familjen Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, Consolas, monospace;
  --r-btn: 14px; --r-sm: 12px; --r-md: 18px; --r-lg: 24px; --r-xl: 32px; --r-panel: 48px;
  --gutter: clamp(16px, 5vw, 120px);
  --panel-inset: clamp(8px, 3.4vw, 48px);
  /* A phone on its side: the notch and rounded corners take the sides (viewport-fit=cover), so nothing sits closer to
     an edge than its safe area. */
  --safe-l: env(safe-area-inset-left, 0px); --safe-r: env(safe-area-inset-right, 0px);
  --max: 1200px;

  /* "only light": OS dark mode and Chrome's auto-dark never recolour the page. */
  color-scheme: only light;
  --bg: #F8F5EE; --ink: #0C0B08; --ink2: #5E5B52; --dot: rgba(12,11,8,.13); --line: rgba(12,11,8,.09); --hairline: rgba(12,11,8,.22);
  --well: rgba(12,11,8,.045); --well2: rgba(12,11,8,.08);
  --g-top: rgba(255,255,255,.94); --g-bot: rgba(255,255,255,.82); --g-edge: rgba(255,255,255,.95); --g-hi: #fff;
  --g-solid: #FDFCF8;
  /* Depth: lifted paper. Every raised surface is a fill, a light edge (a highlight on top, a faint bevel at the
     bottom) and three shadows: contact, ambient, key. Touch screens keep the fill and a one-line edge and swap
     the three shadows for two static ones (the block at the end of this file). */
  --edge: inset 0 1px 0 #fff, inset 0 -1px 0 rgba(12,11,8,.05);
  --g-shadow: 0 1px 2px rgba(12,11,8,.06), 0 8px 20px -8px rgba(12,11,8,.10), 0 32px 64px -32px rgba(12,11,8,.28);
  --g-shadow-hover: 0 1px 2px rgba(12,11,8,.05), 0 14px 28px -10px rgba(12,11,8,.14), 0 44px 80px -36px rgba(12,11,8,.34);
  --g-shadow-press: 0 1px 2px rgba(12,11,8,.08), 0 4px 10px -6px rgba(12,11,8,.10);
  --sheet-shadow: 0 2px 4px rgba(12,11,8,.06), 0 16px 32px -12px rgba(12,11,8,.14), 0 56px 100px -40px rgba(12,11,8,.36);
  --node-shadow: 0 1px 2px rgba(12,11,8,.06), 0 6px 14px -6px rgba(12,11,8,.12), 0 20px 40px -24px rgba(12,11,8,.22);
  --node-shadow-on: 0 1px 2px rgba(12,11,8,.06), 0 10px 20px -8px rgba(12,11,8,.14), 0 28px 56px -28px rgba(12,11,8,.28);
  --chip-shadow: 0 1px 2px rgba(12,11,8,.06), 0 6px 14px -6px rgba(12,11,8,.12);
  --btn-edge: inset 0 1px 0 rgba(255,255,255,.14);
  --btn-shadow: 0 1px 2px rgba(12,11,8,.25), 0 8px 16px -8px rgba(12,11,8,.50);
  --soft-edge: inset 0 1px 0 rgba(255,255,255,.6);
  --recess: inset 0 1px 2px rgba(12,11,8,.06);
  --ring: 0 0 0 2px var(--lime);
  --dark-fill: linear-gradient(180deg, #1E1D18, #0C0B08);
  --paper-fill: linear-gradient(180deg, var(--g-top), var(--g-bot));
  --press-dur: 120ms;
  --btn-bg: #0C0B08; --btn-ink: #F8F5EE; --btn2: rgba(12,11,8,.06); --btn2-hover: rgba(12,11,8,.10);
  --lime: #C4F033; --lime-ink: #4A5C0E; --lime-well: rgba(196,240,51,.28);
  --warn-ink: #7A4B00; --warn-well: rgba(230,160,40,.16);
  --danger-ink: #9B1C12; --danger-well: rgba(220,60,40,.12);
  --paper: #FFFFFF;
}
/* motion tokens: generated by site-src/build.mjs from assets/js/motion.js */
:root {
  --spring-snappy: linear(0, 0.0216, 0.0408, 0.0913, 0.1526, 0.2199, 0.2546, 0.3244, 0.393, 0.4586, 0.4901, 0.5498, 0.6048, 0.6304, 0.6782, 0.7211, 0.7594, 0.7769, 0.8088, 0.8368, 0.8613, 0.8723, 0.8921, 0.9092, 0.9239, 0.9304, 0.942, 0.9519, 0.9563, 0.9639, 0.9704, 0.9758, 0.9782, 0.9823, 0.9858, 0.9886, 0.9898, 0.9919, 0.9937, 0.9944, 0.9957, 0.9967, 0.9975, 0.9979, 0.9984, 0.9989, 0.9992, 0.9994, 1);
  --spring-snappy-dur: 346ms;
  --spring-standard: linear(0, 0.0098, 0.0305, 0.0768, 0.1152, 0.1576, 0.2028, 0.2496, 0.2972, 0.3683, 0.4147, 0.4599, 0.5034, 0.545, 0.5845, 0.6397, 0.6737, 0.7054, 0.7349, 0.7622, 0.7873, 0.8212, 0.8414, 0.8598, 0.8765, 0.8916, 0.9053, 0.9232, 0.9336, 0.9428, 0.9511, 0.9584, 0.9648, 0.9731, 0.9777, 0.9818, 0.9853, 0.9883, 0.9909, 0.9942, 0.9959, 0.9974, 0.9986, 0.9996, 1.0004, 1.0013, 1.0018, 1.0021, 1);
  --spring-standard-dur: 433ms;
  --spring-bouncy: linear(0, 0.0218, 0.0692, 0.111, 0.1841, 0.2647, 0.3483, 0.404, 0.4855, 0.5628, 0.6345, 0.6788, 0.7394, 0.7931, 0.8398, 0.8672, 0.903, 0.9328, 0.9573, 0.9709, 0.9877, 1.0006, 1.0103, 1.0151, 1.0205, 1.0238, 1.0255, 1.026, 1.0258, 1.0249, 1.0234, 1.0221, 1.0201, 1.018, 1.0158, 1.0144, 1.0123, 1.0104, 1.0086, 1.0075, 1.006, 1.0047, 1.0036, 1.0029, 1.0021, 1.0014, 1.0008, 1.0005, 1);
  --spring-bouncy-dur: 550ms;
  --fade: cubic-bezier(.2, 0, 0, 1);
  --fade-dur: 180ms;
  --native-scroll: linear(0, .004, .022, .063, .147, .303, .485, .615, .705, .77, .819, .86, .892, .917, .937, .955, .967, .98, .988, .994, .998, 1);
  --native-scroll-dur: 350ms;
}
@supports not (transition-timing-function: linear(0, 1)) {
  :root {
    --spring-snappy: cubic-bezier(.25, 1, .5, 1);
    --spring-standard: cubic-bezier(.2, .9, .25, 1);
    --spring-bouncy: cubic-bezier(.3, 1.3, .5, 1);
    --native-scroll: cubic-bezier(.2, .8, .2, 1);
  }
}
@media (prefers-reduced-motion: reduce) {
  :root { --spring-snappy-dur: 0ms; --spring-standard-dur: 0ms; --spring-bouncy-dur: 0ms; --fade-dur: 0ms; --native-scroll-dur: 0ms; }
}
/* end of motion tokens */

/* ───────────── base ───────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scroll-padding-top: 104px; -webkit-tap-highlight-color: transparent; }
body {
  margin: 0; min-height: 100vh; font-family: var(--font); font-size: 16px; line-height: 1.5; color: var(--ink);
  background-color: var(--bg);
  background-image: radial-gradient(circle at 1px 1px, var(--dot) 1.1px, transparent 1.5px);
  background-size: 24px 24px; background-position: 12px 12px;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
img, svg { display: block; max-width: 100%; }
[hidden] { display: none !important; }
a { color: inherit; }
button, input, textarea, select { font: inherit; color: inherit; }
h1, h2, h3, h4 { margin: 0; text-wrap: balance; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; border-radius: 6px; }
.visually-hidden { position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.skip { position: absolute; left: 12px; top: -80px; z-index: 50; padding: 10px 16px; border-radius: var(--r-btn); background: var(--btn-bg); color: var(--btn-ink); font-weight: 700; text-decoration: none; }
.skip:focus { top: 12px; }
.num { font-variant-numeric: tabular-nums lining-nums; }
.mono { font-family: var(--mono); font-size: .92em; }


/* ───────────── motion background: lime dots follow the pointer, tilt or scroll ───────────── */
.bg-glow {
  position: fixed; left: 0; top: 0; z-index: -1; width: 520px; height: 520px; pointer-events: none;
  background-image: radial-gradient(circle at 1px 1px, var(--lime) 1.6px, transparent 2.1px);
  background-size: 24px 24px;
  -webkit-mask-image: radial-gradient(circle at center, #000 0, rgba(0,0,0,.35) 36%, transparent 68%);
  mask-image: radial-gradient(circle at center, #000 0, rgba(0,0,0,.35) 36%, transparent 68%);
  opacity: .75; transform: translate3d(calc(70vw - 260px), 120px, 0); will-change: transform;
}
/* Touch screens: no glow at all (see the phone block at the end): a fixed, masked, always-composited layer
   under everything cost more scrolling smoothness than it gave. */

/* ───────────── typography ───────────── */
.display { font-family: var(--display); font-weight: 600; font-size: clamp(52px, 7.3vw, 104px); line-height: .96; letter-spacing: -0.04em; }
.h1-doc { font-family: var(--display); font-weight: 600; font-size: clamp(44px, 5.6vw, 80px); line-height: .98; letter-spacing: -0.035em; }
.h2 { font-family: var(--display); font-weight: 600; font-size: clamp(34px, 3.9vw, 56px); line-height: 1.04; letter-spacing: -0.03em; }
.h3 { font-family: var(--display); font-weight: 600; font-size: clamp(22px, 2vw, 28px); line-height: 1.1; letter-spacing: -0.015em; }
.lead { font-size: clamp(17px, 1.5vw, 21px); line-height: 1.45; color: var(--ink2); max-width: 36em; }
.sub { font-size: clamp(16px, 1.3vw, 18px); line-height: 1.5; color: var(--ink2); }
.eyebrow { font-size: 14px; font-weight: 700; letter-spacing: .01em; color: var(--ink2); }
.muted { color: var(--ink2); }
.small { font-size: 14px; }
.figure { font-family: var(--display); font-weight: 600; letter-spacing: -0.02em; font-variant-numeric: tabular-nums lining-nums; }

/* ───────────── layout ───────────── */
.wrap { width: 100%; max-width: calc(var(--max) + 2 * var(--gutter)); margin-inline: auto; padding-inline: max(var(--gutter), var(--safe-l)) max(var(--gutter), var(--safe-r)); }
main { display: flex; flex-direction: column; gap: clamp(88px, 11vw, 160px); padding-bottom: clamp(88px, 11vw, 160px); }
.section-head { display: flex; flex-direction: column; gap: 12px; max-width: 760px; }
.section-head .h2 { margin-top: 4px; }
/* A display title's descenders hang below its tight line box: 12px more keeps them clear of the lead (About, Contact). */
.section-head > .display + .lead { margin-top: 12px; }
.split-head { display: flex; justify-content: space-between; align-items: flex-end; gap: 32px; flex-wrap: wrap; }

/* ───────────── glass ───────────── */
.glass { background: var(--paper-fill); border: 1px solid var(--g-edge); box-shadow: var(--edge), var(--g-shadow); }
/* A sheet: the larger surfaces (panels, the POS demo, the result card, the phone's panel). Not interactive. */
.surface-sheet { box-shadow: var(--edge), var(--sheet-shadow); }
.panel {
  margin-inline: max(var(--panel-inset), var(--safe-l)) max(var(--panel-inset), var(--safe-r)); border-radius: clamp(36px, 4vw, 48px);
  padding: clamp(28px, 5vw, 72px) max(20px, calc(var(--gutter) - var(--panel-inset)));
  position: relative;
}
.panel > .panel-in { max-width: var(--max); margin-inline: auto; display: flex; flex-direction: column; gap: clamp(28px, 3.4vw, 48px); }
@media (prefers-reduced-transparency: reduce) {
  .glass, .site-header::before, .menu-sheet { background: var(--g-solid) !important; -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }
}

/* ───────────── header ─────────────
   A floating glass bar: 64px, condensing to 56px with stronger glass once the page scrolls past 24px. */
.site-header {
  position: fixed; top: 20px; left: max(var(--panel-inset), var(--safe-l)); right: max(var(--panel-inset), var(--safe-r)); z-index: 30; height: 64px; border-radius: 20px; isolation: isolate;
  display: flex; align-items: center; gap: 20px;
  /* Like the panels and the footer: the glass reaches the panels' edges, its contents sit on the page column.
     The column's edge is max(gutter, (viewport − max) / 2), and 100% here is the viewport (a fixed box's
     containing block). The 6px on the right is the Contact link's own padding, so its text ends on the edge. */
  padding: 0 max(12px, calc(max(var(--gutter), (100% - var(--max)) / 2) - var(--panel-inset) - 6px)) 0 max(16px, calc(max(var(--gutter), (100% - var(--max)) / 2) - var(--panel-inset)));
}
.site-header::before, .site-header::after { content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit; pointer-events: none;
  transition: transform var(--spring-snappy-dur) var(--spring-snappy), opacity var(--fade-dur) var(--fade); }
.site-header::before {
  background: linear-gradient(180deg, rgba(255,255,255,.80), rgba(255,255,255,.64)); border: 1px solid var(--g-edge);
  box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgba(12,11,8,.06), 0 8px 20px -8px rgba(12,11,8,.10), 0 28px 56px -28px rgba(12,11,8,.28);
  -webkit-backdrop-filter: blur(16px) saturate(1.4); backdrop-filter: blur(16px) saturate(1.4);
}
.site-header::after { background: rgba(253,252,248,.9); box-shadow: 0 2px 8px rgba(12,11,8,.06), 0 20px 40px -24px rgba(12,11,8,.35); opacity: 0; }
.site-header[data-scrolled="true"]::before, .site-header[data-scrolled="true"]::after { transform: scaleY(.875); }
.site-header[data-scrolled="true"]::after { opacity: 1; }
/* A page that opens already scrolled (a #link, a reload, Back) shows the condensed bar at once (site.js). */
.site-header[data-instant]::before, .site-header[data-instant]::after { transition-duration: 0s; }
.brand { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; color: var(--ink); min-height: 44px; flex-shrink: 0; }
.brand svg { width: 12px; height: 22px; flex-shrink: 0; }
.brand-word { font-family: Helvetica, Arial, sans-serif; font-weight: 700; font-size: 15px; letter-spacing: -0.01em; white-space: nowrap; }
.brand-word .dot { color: var(--lime); }
.nav { margin-left: auto; }
.nav > ul { display: flex; align-items: center; gap: 2px; list-style: none; }
.nav-link {
  display: inline-flex; align-items: center; gap: 4px; min-height: 44px; padding: 0 12px; border: 0; background: none; cursor: pointer;
  font-size: 15px; font-weight: 500; color: var(--ink); text-decoration: none; border-radius: 10px;
}
.nav-link svg { width: 16px; height: 16px; transition: transform var(--spring-snappy-dur) var(--spring-snappy); }
.nav-link[aria-expanded="true"] svg { transform: rotate(180deg); }
/* Current page: a 2px lime underline under the link text. */
.nav-link[aria-current="page"], .nav-link[data-current="true"], .text-link[aria-current="page"], .menu-sheet a[aria-current="page"] {
  font-weight: 700; text-decoration-line: underline; text-decoration-color: var(--lime); text-decoration-thickness: 2px; text-underline-offset: 8px;
}
.nav-menu { position: relative; }
.dropdown {
  position: absolute; top: calc(100% + 10px); left: -8px; z-index: 31; width: 288px; padding: 8px; border-radius: 18px; list-style: none;
  background: rgba(253,252,248,.98); -webkit-backdrop-filter: none; backdrop-filter: none;
}
.dropdown a { display: flex; flex-direction: column; justify-content: center; min-height: 56px; padding: 8px 12px; border-radius: 12px; text-decoration: none; }
.dropdown a span { font-weight: 600; }
.dropdown a small { font-size: 13px; color: var(--ink2); }
.dropdown a[aria-current="page"] span { text-decoration: underline 2px var(--lime); text-underline-offset: 5px; }
.bar-actions { display: flex; align-items: center; gap: 18px; flex-shrink: 0; }
.text-link { display: inline-flex; align-items: center; min-height: 44px; padding: 0 6px; font-size: 15px; font-weight: 600; text-decoration: none; }
.menu-btn { display: none; margin-left: auto; width: 44px; height: 44px; border-radius: 12px; border: 0; background: var(--btn2); box-shadow: var(--soft-edge); cursor: pointer; align-items: center; justify-content: center; }
.menu-btn svg { width: 22px; height: 22px; }
.menu-btn .i-close, .menu-btn[aria-expanded="true"] .i-open { display: none; }
.menu-btn[aria-expanded="true"] .i-close { display: block; }
/* Mobile menu: a full-height sheet under the bar, 56px rows. */
.menu-sheet {
  position: fixed; inset: 0; z-index: 29; overflow-y: auto; overscroll-behavior: contain; background-color: var(--bg);
  padding: 88px max(16px, env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  display: flex; flex-direction: column; gap: 24px; transition: opacity var(--fade-dur) var(--fade), transform var(--spring-snappy-dur) var(--spring-snappy);
}
@starting-style { .menu-sheet { opacity: 0; transform: translateY(-8px); } }
.menu-sheet ul { list-style: none; }
.menu-sheet li a { display: flex; align-items: center; min-height: 56px; padding: 0 4px; border-bottom: 1px solid var(--line); text-decoration: none; font-size: 18px; font-weight: 600; }
.menu-sheet .label { margin: 0 4px 4px; }
.menu-sheet .sheet-more { margin-top: 20px; }
.menu-sheet .btn { flex-shrink: 0; margin-top: 24px; } /* (inside the menu's nav, so inside its landmark) */
html.menu-open, html.menu-open body { overflow: hidden; }
/* Locking the page removes a classic scrollbar; keeping its gutter stops the bar and the close button jumping sideways. */
html.menu-open { scrollbar-gutter: stable; }

/* ───────────── buttons & chips ───────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 52px; padding: 0 24px; border-radius: var(--r-btn);
  font-size: 16px; font-weight: 600; border: 0; cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: transform var(--spring-snappy-dur) var(--spring-snappy);
}
.btn:active { transform: scale(.97); transition-duration: var(--press-dur); }
.btn svg { width: 20px; height: 20px; flex-shrink: 0; }
.btn-primary { background: var(--dark-fill); color: var(--btn-ink); box-shadow: var(--btn-edge), var(--btn-shadow); }
.btn-secondary { background: var(--btn2); color: var(--ink); box-shadow: var(--soft-edge); }
.btn-ghost { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
.btn-lime { background: var(--lime); color: #0C0B08; box-shadow: inset 0 1px 0 rgba(255,255,255,.5), 0 1px 2px rgba(12,11,8,.12), 0 8px 16px -8px rgba(74,92,14,.35); }
.btn-sm { height: 44px; padding: 0 20px; font-size: 15px; }
.btn-xs { height: 44px; padding: 0 14px; font-size: 14px; }
.btn-block { width: 100%; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.row-btns { display: flex; gap: 12px; flex-wrap: wrap; }
/* Labels and meta lines replace decorative pills. Pills stay only for real toggles and the demo label. */
.label { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink2); line-height: 1.3; }
.label-slash svg { width: 8px; height: 14px; }
/* Each item draws a hairline on its left; the line is shifted and clipped by that much, so a wrapped line
   never starts with a separator. The " · " text between items is for screen readers and copying. */
.meta-line { display: flex; flex-wrap: wrap; align-items: center; row-gap: 2px; margin-left: -21px; clip-path: inset(0 0 0 21px); font-size: 15px; color: var(--ink2); line-height: 1.4; }
.meta-line > span:not(.visually-hidden) { margin-left: 10px; padding-left: 10px; border-left: 1px solid var(--hairline); line-height: 1.1; min-width: 0; }
.tag-lime { display: inline-flex; align-items: center; height: 24px; padding: 0 10px; border-radius: 999px; background: var(--lime); color: #0C0B08; font-size: 12px; font-weight: 700; white-space: nowrap; }
.ticks { list-style: none; display: flex; flex-direction: column; gap: 12px; }
.ticks li { display: flex; align-items: flex-start; gap: 12px; font-size: 16px; line-height: 1.4; }
.ticks li::before {
  content: ''; flex-shrink: 0; width: 22px; height: 22px; border-radius: 999px; margin-top: -1px;
  background: var(--lime) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230C0B08' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 12.5 4 4L18 8'/%3E%3C/svg%3E") center / 14px no-repeat;
}
.icon-well { width: 40px; height: 40px; border-radius: 12px; background: var(--well); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--ink); }
.icon-well svg { width: 20px; height: 20px; }
.link-arrow { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; text-decoration: none; min-height: 44px; }
.link-arrow svg { width: 18px; height: 18px; transition: transform var(--spring-snappy-dur) var(--spring-snappy); }

/* ───────────── hero ───────────── */
.hero { padding-top: clamp(120px, 12vw, 176px); display: grid; grid-template-columns: minmax(0, 1fr) 560px; gap: 40px; align-items: center; min-height: min(100vh, 900px); }
.hero-copy { display: flex; flex-direction: column; gap: 28px; }
.hero-copy .lead { max-width: 540px; }
.hero-copy .tm { max-width: 540px; font-size: 13px; }
.hero-art { position: relative; width: 560px; height: 640px; perspective: 1600px; }
.hero-art .tilt { position: absolute; transform-style: preserve-3d; will-change: transform; }
.hero-art .card-dash { left: 0; top: 120px; transform: rotateY(14deg) rotateX(6deg) rotateZ(-2deg); transform-origin: left center; }
.hero-art .card-phone { left: 230px; top: 10px; transform: rotateY(-10deg) rotateX(4deg); }

/* Mock dashboard (sample data) */
.mini-dash { width: 360px; border-radius: 24px; padding: 18px; display: flex; flex-direction: column; gap: 12px; }
.mini-dash .top { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; font-size: 13px; font-weight: 700; }
.mini-dash .big { font-size: 34px; line-height: 1.05; }
.stat-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.stat { padding: 8px 10px; border-radius: 12px; background: var(--well); box-shadow: inset 0 1px 2px rgba(12,11,8,.05); display: flex; flex-direction: column; }
.stat b { font-family: var(--display); font-weight: 600; font-size: 20px; line-height: 1.1; font-variant-numeric: tabular-nums lining-nums; }
.stat span { font-size: 12px; color: var(--ink2); }
.bars { display: flex; align-items: flex-end; gap: 6px; height: 64px; }
.bars span { flex: 1; border-radius: 4px; background: var(--well2); }
.bars span.on { background: var(--lime); }

/* Result card (scanner's three answers). Used by the demo and the live scanner. */
.rcard { border-radius: 22px; padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.rcard-type { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink2); }
.rcard-type .icon-well { width: 28px; height: 28px; border-radius: 9px; }
.rcard-type .icon-well svg { width: 15px; height: 15px; }
.rcard-title { font-family: var(--display); font-weight: 600; font-size: 28px; line-height: 1.05; letter-spacing: -0.015em; overflow-wrap: anywhere; }
.safe { display: flex; gap: 10px; align-items: flex-start; padding: 12px; border-radius: 14px; background: var(--well); font-size: 14px; line-height: 1.4; }
.safe svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--lime-ink); margin-top: 1px; }
.safe strong { display: block; font-size: 15px; }
.safe.warn { background: var(--warn-well); } .safe.warn svg { color: var(--warn-ink); }
.safe.danger { background: var(--danger-well); } .safe.danger svg { color: var(--danger-ink); }

/* ───────────── product cards ───────────── */
.products { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
.pcard { border-radius: 36px; padding: 32px; display: flex; flex-direction: column; gap: 24px; }
.pcard-art { height: 300px; border-radius: 28px; background: var(--well); box-shadow: var(--recess); display: flex; align-items: center; justify-content: center; gap: 20px; overflow: hidden; padding: 20px; }
.pcard-head { display: flex; gap: 14px; align-items: flex-start; }
.pcard-head .icon-well { width: 44px; height: 44px; }
.pcard-head p { color: var(--ink2); font-size: 15px; margin-top: 2px; }
.pcard .ticks { gap: 10px; }
.code-tile { background: #fff; border-radius: 20px; padding: 18px; box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgba(12,11,8,.08), 0 16px 32px -16px rgba(12,11,8,.35); flex-shrink: 0; }
.code-tile svg { width: 124px; height: 124px; }

/* ───────────── scanner demo (sample codes → result) ───────────── */
.scan-demo { display: flex; flex-direction: column; gap: 24px; }
.samples { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
.sample {
  position: relative; border: 0; cursor: pointer; border-radius: 24px; padding: 16px 16px 12px; display: flex; flex-direction: column; align-items: center; gap: 12px;
  font-weight: 700; font-size: 15px; min-width: 0; color: var(--ink);
}
/* Selected: a lime ring, with no transition. */
.sample[aria-expanded="true"] { box-shadow: var(--ring), var(--edge), var(--g-shadow); transition: none; }
.sample .code-face { width: 100%; aspect-ratio: 1.2 / 1; background: #fff; border-radius: 16px; display: flex; align-items: center; justify-content: center; overflow: hidden; position: relative; box-shadow: inset 0 0 0 1px rgba(12,11,8,.05); }
.sample .code-face svg { width: 62%; height: auto; max-height: 82%; }
.sample .lbl { display: inline-flex; align-items: center; gap: 8px; }
.sample .lbl svg { width: 16px; height: 16px; }
/* The result opens over the codes, in the space the grid already takes up: nothing below moves (CLS 0). */
.samples-stage { position: relative; }
/* Covered by an open card: the codes fade out, then are hidden. Hidden, not just transparent: when an opacity fade
   ends, the browser paints the codes again (their QR images included: 19 ms a frame on a low-end Android);
   hidden content isn't painted at all. Coming back, visibility returns at once and the fade runs. */
.samples { transition: opacity var(--fade-dur) var(--fade), visibility 0s linear 0s; }
.samples[data-covered="true"] { opacity: 0; visibility: hidden; pointer-events: none; transition: opacity var(--fade-dur) var(--fade), visibility 0s linear var(--fade-dur); }
/* Reduced motion stretches every transition to .01ms (the rule under "scroll reveal"), visibility's too: the codes
   would still be hidden in the instant a card closes, too hidden to take focus back. */
@media (prefers-reduced-motion: reduce) { .samples { transition: none !important; } }
/* No scroll box: the card's shadow is never clipped and the card never scrolls inside itself (scan-demo.js grows
   the stage when the card is taller than the codes). */
.result-slot { position: absolute; inset: 0; z-index: 2; }
/* Fast open (scan-demo.js): every card is built ahead, laid out and hidden, its layout and paint contained so
   revealing one touches nothing else. Placed with `translate` (no layout). Its shadow moves and fades with it
   (only opacity and transform animate). A card leaving (data-leaving) is closed but still drawn while it fades,
   and click-through: the codes, or the next card, under it answer the pointer at once. The open card is always
   the one on top ("Try another code": the new card fades in over the old one, which fades out under it). */
.result-slot[data-mode="prepared"] { pointer-events: none; }
.result-slot[data-mode="prepared"] > .result-card { position: absolute; top: 0; left: 0; right: 0; visibility: hidden; contain: layout paint; }
.result-slot[data-mode="prepared"] > .result-card[data-open="true"] { visibility: visible; pointer-events: auto; z-index: 1; }
.result-slot[data-mode="prepared"] > .result-card[data-leaving="true"] { visibility: visible; pointer-events: none; }
/* ...except its Close: a double click or tap on Close lands there and does nothing, instead of opening the code
   under it (scan-demo.js keeps the focus on the code). */
.result-slot[data-mode="prepared"] > .result-card[data-leaving="true"] .rc-close { pointer-events: auto; }
/* Reduced motion turns every change into a .01ms transition (the rule under "scroll reveal"), visibility's too: the
   card and its title would still count as hidden in the frame the title is focused, and the focus would be lost. */
@media (prefers-reduced-motion: reduce) {
  .result-slot[data-mode="prepared"] > .result-card, .result-slot[data-mode="prepared"] > .result-card * { transition: none !important; }
}
/* The hint on the home page: one pulse on the first code (a ring, transform and opacity only), then gone after
   the first tap. The ring is on z-index 1, painted after everything else: below it, each code after it and the
   rest of the page would need a layer of its own above the moving ring while it runs. */
.tap-hint { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 15px; font-weight: 600; color: var(--ink2); transition: opacity var(--fade-dur) var(--fade); }
.tap-hint svg { width: 18px; height: 18px; color: var(--lime-ink); flex-shrink: 0; }
.tap-hint[data-done="true"] { opacity: 0; }
.sample[data-hint="true"]::after { content: ''; position: absolute; inset: 0; z-index: 1; border-radius: inherit; border: 2px solid var(--lime); pointer-events: none; opacity: 0;
  animation: tap-pulse 1.4s cubic-bezier(.2, 0, 0, 1) 1 backwards; } /* the ring closes onto the tile's edge: never outside it */
@keyframes tap-pulse { 0% { opacity: 0; transform: scale(.92); } 30% { opacity: 1; } 100% { opacity: 0; transform: scale(1); } }
.result-slot .result-card { background: var(--g-solid); }
/* The demo's card groups its buttons into rows (scan-demo.js): the main button with the other actions, and
   the details button with "Try another code". On a phone the main button keeps its own full-width row. */
.result-slot .rc-acts, .result-slot .rc-foot { display: flex; flex-wrap: wrap; gap: 8px 12px; }
.result-slot .rc-acts { align-items: flex-end; }
.result-slot .rc-acts > .main-act { display: contents; } /* its note gets a row of its own; the button sits beside the other actions */
.result-slot .rc-acts > .main-act > .act-line { flex-basis: 100%; }
@media (min-width: 641px) { .result-slot .rc-acts > .rc-secondary { margin-bottom: 6px; } } /* centred on the 56px main button beside it */
.result-slot .rc-foot { align-items: center; }
.result-slot .rc-foot .rc-details-btn, .result-slot .rc-foot .rc-another { align-self: auto; }
@media (max-width: 640px) { .result-slot .rc-acts > .main-act > * { flex-basis: 100%; } }
.phone-link { grid-column: 1 / -1; border-radius: 28px; padding: 24px 28px; display: flex; gap: 24px; align-items: center; }
.phone-link svg.qr { width: 104px; height: 104px; flex-shrink: 0; background: #fff; border-radius: 12px; padding: 6px; box-shadow: var(--recess); }
.phone-link .url { font-size: 14px; color: var(--ink2); margin-top: 8px; }
/* Phones and tablets are already where the scanner runs: the same width as the scanner page's desktop-only QR. */
@media (max-width: 960px) { .phone-link { display: none; } }
.details { border-top: 1px solid var(--line); }
.details dl { margin: 0; display: flex; flex-direction: column; }
.details .kv { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 12px 0; border-bottom: 1px solid var(--line); font-size: 15px; }
.details .kv dt { flex-shrink: 0; }
.details .kv dd { min-width: 0; display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 8px; }
.details dt { color: var(--ink2); }
.details dd { margin: 0; text-align: right; font-weight: 600; overflow-wrap: anywhere; }

/* ───────────── result card: one layout for every device ─────────────
   type → title → safety line → one main button → Copy / Share → details. Spacing from 4/8/12/16/24 only. */
.result-card { border-radius: 32px; padding: 24px; display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.rc-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; }
.result-card .rcard-type { font-size: 14px; gap: 8px; }
.rc-close { width: 44px; height: 44px; flex-shrink: 0; border: 0; border-radius: 12px; background: var(--btn2); box-shadow: var(--soft-edge); color: var(--ink); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.rc-close svg { width: 20px; height: 20px; }
/* The demo's "Try another code": the next sample's result, in place (assets/js/scan-demo.js). */
.result-card .rc-another { align-self: flex-start; }
.rc-what { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.result-card .rcard-title { font-size: clamp(28px, 3vw, 36px); outline: none; }
.result-card .main-action { align-self: flex-start; max-width: 100%; }
.main-act { display: flex; flex-direction: column; gap: 8px; }
.main-act .main-action { align-self: flex-start; }
.demo-note:empty { display: none; }
.rc-secondary { display: flex; flex-wrap: wrap; gap: 8px; }
.rc-secondary:empty { display: none; }
.rc-details-btn { align-self: flex-start; display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 16px; border: 0; border-radius: var(--r-btn); background: var(--btn2); box-shadow: var(--soft-edge); font-size: 15px; font-weight: 600; cursor: pointer; }
.rc-details-btn svg { width: 16px; height: 16px; transition: transform var(--spring-snappy-dur) var(--spring-snappy); }
.rc-details-btn[aria-expanded="true"] svg { transform: rotate(180deg); }
/* Details: the button toggles `hidden`; the contents ease in (card.js). */
.rc-details-in { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
/* The result card's contents (tools/qr-scanner/lib/card.js): the scanner, the demo and the phone preview share them. */
.results-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.results-head h2 { font-family: var(--display); font-weight: 600; font-size: 24px; letter-spacing: -0.015em; }
.num-badge { width: 32px; height: 32px; border-radius: 999px; background: var(--lime); color: #0C0B08; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
.result-sub { color: var(--ink2); font-size: 15px; line-height: 1.45; }
.alerts { display: flex; flex-direction: column; gap: 8px; }
.alert strong { display: block; margin-bottom: 2px; }
.main-act:empty { display: none; }
.main-act .btn { height: 56px; font-size: 17px; }
/* A label can be long ("Open in OpenStreetMap"), or as long as what was scanned ("Email asha@example.com"): it wraps inside its
   button rather than running past the card. One line keeps the usual height (44px, the main button 56px);
   the text starts beside its icon. The smaller buttons have the details button's 16px sides, so a phone
   number ("Call +1 202-555-0143") stays on one line on a 320px phone instead of breaking at a hyphen. */
.result-card .btn { max-width: 100%; height: auto; min-height: 44px; padding-block: 10px; white-space: normal; overflow-wrap: anywhere; text-align: left; }
.result-card .btn-sm { padding-inline: 16px; }
.result-card .main-act .btn { min-height: 56px; }
.details-sec { border-top: 1px solid var(--line); padding-top: 12px; }
.details-h { font-size: 14px; font-weight: 700; color: var(--ink2); margin-bottom: 4px; }
.fields { margin: 0; display: flex; flex-direction: column; }
.fields .row { display: grid; grid-template-columns: minmax(96px, 36%) minmax(0, 1fr); gap: 4px 16px; padding: 10px 0; border-bottom: 1px solid var(--line); align-items: start; }
.fields .row:last-child { border-bottom: 0; }
.fields dt { color: var(--ink2); font-size: 15px; }
.fields dd { margin: 0; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: 16px; overflow-wrap: anywhere; min-width: 0; }
.fields .val.em { font-weight: 700; }
.fields .val.mono { font-family: var(--mono); font-size: 15px; }
.fields .val.block { white-space: pre-wrap; display: block; width: 100%; }
.basis { display: inline-flex; align-items: center; gap: 8px; align-self: flex-start; padding: 8px 0 0; border-top: 1px solid var(--line); font-size: 14px; color: var(--ink2); flex-wrap: wrap; min-width: 0; }
.basis svg { width: 16px; height: 16px; color: var(--lime-ink); flex-shrink: 0; }
.basis strong { color: var(--ink); }
.basis-tag { display: inline-block; margin-left: 8px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink2); }
.fields .field-note { grid-column: 1 / -1; display: block; font-size: 14px; color: var(--ink2); }
.mini { position: relative; min-height: 36px; min-width: 44px; padding: 0 12px; border-radius: 10px; border: 0; background: var(--btn2); box-shadow: var(--soft-edge); font-size: 14px; font-weight: 600; cursor: pointer; }
/* A 44px-tall hit area around the 36px button, so the field rows keep their height. */
.mini::after { content: ''; position: absolute; inset: -4px 0; }
.actions { display: flex; flex-wrap: wrap; gap: 8px; }
.raw summary { cursor: pointer; font-size: 14px; color: var(--ink2); min-height: 44px; display: flex; align-items: center; }
.raw pre { white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--mono); font-size: 13px; background: var(--well); padding: 12px; border-radius: 12px; margin: 0 0 8px; max-height: 260px; overflow: auto; }
.raw dl { margin: 0; display: grid; gap: 4px; font-size: 14px; }
.raw dl div { display: flex; gap: 8px; } .raw dt { color: var(--ink2); } .raw dd { margin: 0; }
.lookup { display: flex; flex-direction: column; gap: 12px; }
.lookup-found:empty { display: none; }
.lookup-hint, .lookup-note { font-size: 14px; color: var(--ink2); display: flex; gap: 8px; align-items: flex-start; }
.lookup-note svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--lime-ink); margin-top: 2px; }
.ai-label { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 700; }
.ai-label svg { width: 16px; height: 16px; }
.lookup-error { font-size: 15px; color: var(--danger-ink); }
.lookup-empty { font-size: 15px; color: var(--ink2); display: flex; flex-direction: column; gap: 8px; }
.lookup-empty .lookup-says { color: var(--ink); }
.lookup-name { font-family: var(--display); font-size: 22px; }
.source { border-radius: 18px; background: var(--well); box-shadow: inset 0 1px 2px rgba(12,11,8,.05); padding: 14px 16px; }
.source h4 { font-size: 14px; font-weight: 700; color: var(--ink2); }
.attribution { font-size: 13px; color: var(--ink2); margin-top: 6px; }
@media (max-width: 640px) {
  .result-card { padding: 20px; border-radius: 24px; }
  .result-card .main-action, .main-act .main-action { align-self: stretch; width: 100%; padding-inline: 8px; } /* full width, its label centred: a long one uses the sides before it wraps */
  .fields .row { grid-template-columns: minmax(0, 1fr); }
}

/* Layer 1: up to three facts, then the one button with its line (a warning only when a trigger fired). */
/* Each fact is as wide as its value and shares a line only when that fits, so an ID or a network name is never
   broken mid-word by a column too narrow for it; a value wider than the card still wraps, as a last resort. */
dl.facts { margin: 0; display: flex; flex-wrap: wrap; gap: 10px 16px; }
dl.facts:empty { display: none; }
dl.facts .fact { flex: 1 1 auto; min-width: min(128px, 100%); display: flex; flex-direction: column; gap: 2px; }
.facts dt { font-size: 13px; color: var(--ink2); }
.facts dd { margin: 0; font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }
.facts dd.mono { font-family: var(--mono); font-weight: 500; font-size: 15px; }
.act-line { display: flex; gap: 8px; align-items: flex-start; margin: 0; font-size: 15px; line-height: 1.4; }
.act-line svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }
.act-warn { padding: 12px; border-radius: 12px; background: var(--warn-well); }
.act-warn svg { color: var(--warn-ink); }
.act-warn[data-level="danger"] { background: var(--danger-well); }
.act-warn[data-level="danger"] svg { color: var(--danger-ink); }
.act-note { color: var(--ink2); }
.act-note svg { color: var(--lime-ink); }
.lookup-main:empty { display: none; }

/* Payments: a specific app one tap further (Android), the three steps to pay from a saved image (iPhone),
   and one hint line. */
.apps-more summary { cursor: pointer; font-size: 15px; font-weight: 600; min-height: 44px; display: flex; align-items: center; }
.apps-more[open] summary { margin-bottom: 8px; }
.pay-apps { list-style: none; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 0; padding: 0; }
.pay-apps .pay-app { width: 100%; }
.pay-steps { margin: 0; padding-left: 1.4em; display: flex; flex-direction: column; gap: 4px; font-size: 15px; }
.pay-hint { margin: 0; }

/* Layer 2: sections with short labels, score chips, nutrient levels, and the database source. */
.details-sec h4 { font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink2); margin-bottom: 4px; }
.lookup-panel-sections:empty, .lookup-sources:empty { display: none; }
.lookup-panel-sections, .lookup-sources { display: flex; flex-direction: column; gap: 16px; }
.fields .val.muted { color: var(--ink2); font-weight: 400; }
.scores { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.score { border-radius: 14px; padding: 10px 12px; background: var(--well); box-shadow: inset 0 1px 2px rgba(12,11,8,.05); display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.score-name { font-size: 13px; color: var(--ink2); }
.score-grade { font-family: var(--display); font-size: 26px; font-weight: 700; line-height: 1.1; }
.score[data-grade="none"] .score-grade { font-family: inherit; font-size: 15px; font-weight: 600; color: var(--ink2); }
.score-meaning { font-size: 13px; color: var(--ink2); }
.levels .row[data-level="high"] .level { color: var(--danger-ink); font-weight: 700; }
.levels .row[data-level="medium"] .level { color: var(--warn-ink); font-weight: 700; }
.levels .row[data-level="low"] .level { color: var(--lime-ink); font-weight: 700; }
.additives { font-size: 15px; overflow-wrap: anywhere; }
.ingredients summary { cursor: pointer; font-size: 15px; font-weight: 600; min-height: 44px; display: flex; align-items: center; }
.ingredients p { font-size: 15px; line-height: 1.5; overflow-wrap: anywhere; }
.source-links { margin-top: 6px; }
.source-links a { font-weight: 600; }
.ai-foot { font-size: 13px; color: var(--ink2); }

/* ───────────── the scanner's surface ─────────────
   The live scanner (tools/qr-scanner/) and the phone preview share these rules, so the preview is drawn by
   the scanner's own CSS. Sizes follow the width of .scan-ui itself (a container), not the page: the preview
   is a phone wherever it sits. */
.privacy-line { display: flex; align-items: center; gap: 8px; color: var(--ink2); font-size: 15px; }
.privacy-line svg { width: 18px; height: 18px; flex-shrink: 0; }
.scan-ui { container: scan-ui / inline-size; }
.scan-ui .scanner { display: flex; flex-direction: column; gap: 14px; outline: none; }
.scan-ui .viewport {
  position: relative; aspect-ratio: 4 / 3; background: #0C0B08; border-radius: 28px; overflow: hidden;
  display: flex; align-items: center; justify-content: center; box-shadow: inset 0 0 0 1px rgba(248,245,238,.08);
}
.scan-ui .idle { position: relative; text-align: center; color: #F8F5EE; padding: 24px; max-width: 340px; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.scan-ui .idle-icon { width: 52px; height: 52px; color: var(--lime); }
.scan-ui .idle p { font-size: 16px; line-height: 1.45; color: rgba(248,245,238,.86); }
/* The viewfinder: lime corners and a scanline sweeping its height (84cqh: the frame is a size container). */
.scan-ui .frame { position: absolute; width: 62%; aspect-ratio: 1; max-height: 78%; pointer-events: none; container-type: size; }
.scan-ui .frame i { position: absolute; width: 32px; height: 32px; border: 0 solid var(--lime); }
.scan-ui .frame i:nth-child(1) { top: 0; left: 0; border-top-width: 3px; border-left-width: 3px; border-top-left-radius: 14px; }
.scan-ui .frame i:nth-child(2) { top: 0; right: 0; border-top-width: 3px; border-right-width: 3px; border-top-right-radius: 14px; }
.scan-ui .frame i:nth-child(3) { bottom: 0; left: 0; border-bottom-width: 3px; border-left-width: 3px; border-bottom-left-radius: 14px; }
.scan-ui .frame i:nth-child(4) { bottom: 0; right: 0; border-bottom-width: 3px; border-right-width: 3px; border-bottom-right-radius: 14px; }
.scan-ui .frame::after { content: ''; position: absolute; left: 6%; right: 6%; height: 2px; border-radius: 2px; background: var(--lime); box-shadow: 0 0 12px var(--lime); top: 8%; animation: scanline 2.2s cubic-bezier(.45, 0, .2, 1) infinite alternate; }
@keyframes scanline { to { transform: translateY(84cqh); } }
@media (prefers-reduced-motion: reduce) { .scan-ui .frame::after { animation: none; top: 50%; } }
.scan-ui .status { position: absolute; left: 12px; right: 12px; bottom: 12px; margin: 0 auto; width: max-content; max-width: calc(100% - 24px); text-align: center;
  font-size: 14px; font-weight: 600; padding: 8px 14px; border-radius: 12px; background: rgba(248,245,238,.94); color: #0C0B08; }
.scan-ui .status:empty { display: none; }
.scan-ui .status.err { background: #FFE5E0; color: #7A1A10; }
.scan-ui .controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.scan-ui .results-col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.scan-ui .empty-result { border-radius: 28px; padding: 24px; display: flex; gap: 14px; align-items: flex-start; color: var(--ink2); font-size: 16px; line-height: 1.45; }
.scan-ui .empty-result strong { color: var(--ink); display: block; }
.scan-ui .results-col:has(.results:not(:empty)) .empty-result { display: none; }
/* Scrolled to just under the header: html's scroll-padding-top leaves the header's room, this adds a little air. */
.scan-ui .results { display: flex; flex-direction: column; gap: 16px; scroll-margin-top: 16px; }
.scan-ui .results:empty { display: none; }
.scan-ui .result { animation: rise var(--spring-standard-dur) var(--spring-standard) both; }
@keyframes rise { from { opacity: 0; transform: translateY(16px); } }
.scan-ui .result .rcard-title { font-size: clamp(26px, 3vw, 34px); }
@container scan-ui (max-width: 600px) {
  .scan-ui .viewport { aspect-ratio: 3 / 4; border-radius: 24px; }
  .scan-ui .controls .btn { flex: 1 1 auto; }
  .scan-ui .result { padding: 20px; border-radius: 24px; }
  .scan-ui .result .rcard-title { font-size: 26px; }
  .scan-ui .result .main-action, .scan-ui .main-act .main-action { align-self: stretch; width: 100%; padding-inline: 8px; }
  .scan-ui .fields .row { grid-template-columns: minmax(0, 1fr); }
}

/* ───────────── phone preview (the hero phone; the Scanner demo, further down) ─────────────
   An unbranded phone with the proportions of a current 6.3-inch flagship, in points: a 402 × 874 screen with
   62 pt corners inside a 16 pt frame (434 × 906, 78 pt corners), a 126 × 37 pill cutout 11 pt from the top and
   a status bar beside it. Under the status bar is the browser's viewport: the scanner page at the scroll
   position the real page reaches (the hero's still frame sits at --pv-result-y, where the result card is in
   view), the site's header over it and the browser's address bar at the bottom. Drawn at 1 pt = 1 px and
   zoomed to fit where it's used. */
.pv { position: relative; width: 434px; height: 906px; flex-shrink: 0; border-radius: 78px; text-align: left; }
.pv-device { position: absolute; inset: 0; border-radius: 78px; padding: 16px; background: #0C0B08;
  box-shadow: inset 0 0 0 1.5px #4A4740, inset 0 0 0 4px #1C1B18, 0 30px 60px -20px rgba(12,11,8,.45); }
.pv-screen { position: relative; width: 402px; height: 874px; border-radius: 62px; overflow: hidden; background: var(--bg); isolation: isolate; }
.pv-bar { position: absolute; z-index: 3; left: 0; right: 0; top: 0; height: 54px; display: grid; grid-template-columns: 1fr 126px 1fr; align-items: center; background: var(--bg); color: #0C0B08; }
.pv-time { justify-self: center; padding-top: 4px; font: 600 17px/1 -apple-system, system-ui, var(--font); letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.pv-island { width: 126px; height: 37px; border-radius: 19px; background: #000; align-self: start; margin-top: 11px; }
.pv-sys { justify-self: center; display: inline-flex; align-items: center; gap: 6px; padding-top: 4px; }
.pv-sys svg { height: 12px; width: auto; fill: currentColor; }
.pv-viewport { position: absolute; left: 0; right: 0; top: 54px; bottom: 0; overflow: hidden; }
/* The page: the scanner panel as the site lays it out at 402 px (13.5 px inset, 40 × 20 padding). */
.pv-page { position: absolute; left: 0; right: 0; top: 0; padding: 0 0 240px; --pv-start-y: -98px; --pv-result-y: -676px; transform: translateY(var(--pv-start-y)); }
.pv[data-state="result"] .pv-page { transform: translateY(var(--pv-result-y)); }
.pv-panel { margin: 0 13.5px; padding: 40px 20px; border-radius: 36px; display: flex; flex-direction: column; gap: 28px; }
.pv-h2 { font-family: var(--display); font-weight: 600; font-size: 34px; line-height: 1.04; letter-spacing: -0.03em; }
.pv-app { display: flex; flex-direction: column; gap: 24px; position: relative; min-height: 1180px; }
/* The results column sits under the scanner as in the real page (24 px gap), placed with a fixed transform at
   its place under the idle scanner (444 + 14 + two 52 px button rows and their 10 px gap). */
.pv-app .results-col { position: absolute; left: 0; right: 0; top: 0; transform: translateY(596px); }
.pv-cam { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: radial-gradient(circle at 50% 45%, #3B3934, #151412 72%); }
.pv-code { background: #fff; border-radius: 14px; padding: 14px; width: 190px; display: flex; flex-direction: column; align-items: center; gap: 6px; transform: rotate(-3deg); box-shadow: 0 18px 40px -12px rgba(0,0,0,.7); }
/* The demo's camera box is always 3:4 portrait, so the viewfinder's height cap never applies there; and under
   the phone's zoom WebKit takes the cap's % of the zoomed height, which shrank the viewfinder to a fraction. */
.pv .scan-ui .frame { max-height: none; }
.pv-code svg { width: 100%; height: auto; }
.pv-code.is-bar svg { height: 96px; }
.pv-code .ean-digits { font-size: 13px; }
.pv-rh { font-family: var(--display); font-weight: 600; font-size: 24px; letter-spacing: -0.015em; }
/* The site's header as a phone shows it over a scrolled page. */
.pv-header { position: absolute; z-index: 2; top: 12px; left: 12px; right: 12px; height: 56px; padding: 0 6px 0 16px; border-radius: 16px; display: flex; align-items: center; justify-content: space-between;
  background: var(--g-solid); /* opaque: nothing on the page shows through the phone's header */ border: 1px solid var(--g-edge); box-shadow: 0 2px 8px rgba(12,11,8,.06), 0 20px 40px -24px rgba(12,11,8,.35); }
.pv-header .brand-word { font-size: 13px; }
.pv-header .brand svg { width: 10px; height: 18px; }
.pv-menu { width: 44px; height: 44px; border-radius: 12px; background: var(--btn2); display: inline-flex; align-items: center; justify-content: center; }
.pv-menu svg { width: 20px; height: 20px; }
/* The browser's address bar, floating at the bottom, and the home indicator. */
.pv-url { position: absolute; z-index: 2; left: 0; right: 0; bottom: 0; padding: 32px 16px 30px; background: linear-gradient(rgba(248,245,238,0), rgba(248,245,238,.94) 45%); }
.pv-url-pill { height: 48px; border-radius: 24px; display: flex; align-items: center; justify-content: center; gap: 6px; background: rgba(255,255,255,.9); box-shadow: 0 0 0 .5px rgba(12,11,8,.12), 0 8px 24px -8px rgba(12,11,8,.25);
  font: 500 16px/1 -apple-system, system-ui, var(--font); color: #0C0B08; }
.pv-url-pill svg { width: 13px; height: 13px; }
/* The simulated finger: an iPhone-style touch circle where the demo taps, fading out (the Scanner demo below).
   Placed with `translate`, animated with transform and opacity only. */
.pv-touch { position: absolute; z-index: 4; left: 0; top: 0; width: 46px; height: 46px; margin: -23px 0 0 -23px; border-radius: 50%;
  background: rgba(12,11,8,.16); border: 1.5px solid rgba(255,255,255,.75); opacity: 0; pointer-events: none; }
.pv-home { position: absolute; z-index: 3; left: 50%; bottom: 8px; width: 140px; height: 5px; margin-left: -70px; border-radius: 3px; background: #0C0B08; }
/* The phone's box (.pv-fit): each placement gives it a size, and assets/js/site.js sets --pv-zoom so the whole
   phone fits inside it with its own proportions, like an image with object-fit: contain. The values here are
   the same fit worked out for each placement's usual size, so the first frame (and no-JS) is right too. */
.pv-fit { --pv-zoom: .3; display: flex; align-items: center; justify-content: center; min-width: 0; min-height: 0; }
.pv-fit > .pv { zoom: var(--pv-zoom); }
.hero-art .card-phone .pv-fit { width: 300px; aspect-ratio: 434 / 906; --pv-zoom: .691; }
@media (max-width: 640px) { .hero-art .card-phone .pv-fit { width: min(300px, 100%); } }

/* ───────────── what it reads: a mind map (assets/js/reads-map.js) ─────────────
   The nested list reads as an indented tree with rails; from 1024px the script lays it out radially. */
.reads-map { position: relative; }
.rm-lines { display: none; }
.rm-root, .rm-groups, .rm-leaves { list-style: none; margin: 0; padding: 0; }
.rm-center { display: inline-flex; align-items: center; min-height: 48px; padding: 0 20px; border-radius: 16px; background: var(--dark-fill); box-shadow: var(--btn-edge), 0 1px 2px rgba(12,11,8,.25), 0 12px 24px -12px rgba(12,11,8,.5); color: var(--btn-ink); font-family: var(--display); font-weight: 600; font-size: 20px; white-space: nowrap; }
.rm-groups { display: flex; flex-direction: column; gap: 16px; margin-left: 24px; padding: 16px 0 0 24px; border-left: 1px solid var(--hairline); }
.rm-branch { position: relative; }
.rm-branch::before { content: ''; position: absolute; left: -24px; top: 22px; width: 16px; height: 1px; background: var(--hairline); }
.rm-group { min-height: 44px; padding: 0 16px; border: 1px solid var(--g-edge); border-radius: 12px; background: var(--paper-fill); box-shadow: inset 0 1px 0 #fff, var(--chip-shadow); font-weight: 700; font-size: 15px; cursor: pointer; white-space: nowrap; color: var(--ink); transition: transform var(--press-dur) ease-out; }
.rm-group[aria-pressed="true"] { background: var(--dark-fill); color: var(--btn-ink); border-color: transparent; }
.rm-group:active { transform: scale(.96); }
.rm-leaves { display: flex; flex-direction: column; gap: 8px; margin-left: 24px; padding: 8px 0 0 24px; border-left: 1px solid var(--hairline); }
.rm-leaf { position: relative; display: flex; align-items: center; gap: 12px; max-width: 360px; min-width: 0; padding: 8px 12px; border-radius: 16px; border: 1px solid var(--g-edge); background: var(--paper-fill); box-shadow: inset 0 1px 0 #fff, var(--chip-shadow); }
.rm-leaf::before { content: ''; position: absolute; left: -24px; top: 50%; width: 16px; height: 1px; background: var(--hairline); }
.rm-leaf .icon-well { width: 36px; height: 36px; border-radius: 10px; }
.rm-leaf .icon-well svg { width: 18px; height: 18px; }
.rm-text { display: flex; flex-direction: column; min-width: 0; }
.rm-name { font-weight: 700; font-size: 15px; }
.rm-act { font-size: 14px; color: var(--ink2); overflow-wrap: anywhere; }
.rm-branch[data-on="true"] .rm-leaf, .rm-branch[data-on="true"] .rm-group { box-shadow: var(--ring), inset 0 1px 0 #fff, 0 1px 2px rgba(12,11,8,.06), 0 10px 20px -8px rgba(12,11,8,.14); }
.reads-map[data-layout="radial"] .rm-lines { display: block; position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.reads-map[data-layout="radial"] .rm-node { position: absolute; margin: 0; }
.reads-map[data-layout="radial"] .rm-groups, .reads-map[data-layout="radial"] .rm-leaves { border: 0; margin: 0; padding: 0; }
/* Nodes are positioned against the map itself, so nothing in between may be a containing block. */
.reads-map[data-layout="radial"] .rm-branch, .reads-map[data-layout="radial"] .rm-root > li { position: static; }
.reads-map[data-layout="radial"] .rm-branch::before, .reads-map[data-layout="radial"] .rm-leaf::before { display: none; }
.reads-map[data-layout="radial"] .rm-leaf { width: 216px; }
.rm-lines path { fill: none; stroke: var(--hairline); stroke-width: 1.5; transition: opacity var(--spring-standard-dur) var(--spring-standard); }
.rm-lines path:not([data-drawn]) { opacity: 0; }
.rm-lines path[data-on="true"] { stroke: var(--lime-ink); stroke-width: 2.5; }

/* ───────────── three answers / type grid ───────────── */
.answers { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; list-style: none; counter-reset: a; }
.answer { border-radius: 28px; padding: 28px; display: flex; flex-direction: column; gap: 10px; }
.answer .n { width: 40px; height: 40px; border-radius: 999px; background: var(--lime); color: #0C0B08; font-family: var(--display); font-weight: 700; font-size: 18px; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 8px; box-shadow: inset 0 1px 0 rgba(255,255,255,.5), 0 2px 6px -2px rgba(74,92,14,.4); }

/* ───────────── diagrams ───────────── */
.chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.chips .lbl { font-size: 14px; color: var(--ink2); margin-right: 8px; }
.chip-btn { height: 44px; min-width: 44px; padding: 0 16px; border-radius: 999px; border: 0; cursor: pointer; background: var(--btn2); box-shadow: var(--soft-edge); font-size: 15px; font-weight: 500; transition: transform var(--press-dur) ease-out; }
/* The pressed state swaps with no transition. */
.chip-btn[aria-pressed="true"] { background: var(--btn-bg); color: var(--btn-ink); font-weight: 700; box-shadow: var(--btn-edge), 0 4px 10px -6px rgba(12,11,8,.5); }
.chip-btn:active { transform: scale(.96); }
.flow { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 40px; position: relative; }
.zone { position: relative; border-radius: 32px; padding: 24px; min-height: 460px; }
.zone-phone { background: var(--well); border: 1px solid var(--line); box-shadow: inset 0 1px 3px rgba(12,11,8,.06); }
.zone-out { border: 1px dashed var(--hairline); }
.zone-title { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700; }
.zone-title svg { width: 18px; height: 18px; }
.steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px; margin-top: 150px; position: relative; list-style: none; }
/* Diagram node: the same fill and light edge as a card, smaller shadows. Active: a lime ring (and, with a
   mouse, a 2px lift). Dimmed: faded, text still AA. A pickable node is a real <button> with aria-pressed. */
.node {
  position: relative; border-radius: 18px; padding: 16px; display: flex; flex-direction: column; gap: 6px; min-height: 120px; border: 0; color: var(--ink);
  background: var(--paper-fill); box-shadow: var(--edge), var(--node-shadow);
  transition: transform var(--spring-snappy-dur) var(--spring-snappy), opacity var(--fade-dur) var(--fade);
}
button.node { font: inherit; text-align: left; cursor: pointer; }
button.node:active, .sale-step:active { transform: translateY(1px) scale(.985); transition-duration: var(--press-dur); }
.node .icon-well { width: 36px; height: 36px; border-radius: 10px; margin-bottom: 6px; box-shadow: inset 0 1px 2px rgba(12,11,8,.05); }
.node .icon-well svg { width: 18px; height: 18px; }
.node strong { font-size: 17px; }
.node span { font-size: 14px; color: var(--ink2); line-height: 1.35; }
.node[data-on="true"], .node[aria-pressed="true"] { box-shadow: var(--ring), var(--edge), var(--node-shadow-on); }
/* Dimmed text turns full ink, so at 65% it still reads at AA (about 6.4:1); an opened step is never dimmed. */
.node[data-on="false"]:not(:has([aria-expanded="true"])) { opacity: .65; }
.node[data-on="false"]:not(:has([aria-expanded="true"])) :is(strong, span) { color: var(--ink); }
/* Numbered steps (phones, and the loop on About). */
.step-n { width: 32px; height: 32px; border-radius: 999px; background: var(--lime); color: #0C0B08; font-family: var(--display); font-weight: 700; font-size: 15px; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; box-shadow: inset 0 1px 0 rgba(255,255,255,.5), 0 2px 6px -2px rgba(74,92,14,.4); }
/* Connectors between cards that sit in a row (and, on phones, in a column). */
.arrow-h { display: flex; align-items: center; justify-content: center; color: var(--lime-ink); flex-shrink: 0; }
.arrow-h svg { width: 24px; height: 24px; }
.stays { position: absolute; left: 24px; bottom: 24px; display: flex; align-items: center; gap: 12px; font-weight: 700; font-size: 15px; }
.outs { display: flex; flex-direction: column; gap: 110px; margin-top: 24px; }
.flow-say { display: flex; gap: 10px; align-items: flex-start; font-size: 17px; }
.flow-say svg { width: 20px; height: 20px; flex-shrink: 0; margin-top: 2px; color: var(--lime-ink); }
.edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.edges path { fill: none; stroke: var(--line); stroke-width: 1.5; stroke-dasharray: 5 6; }
.edges path[data-on="true"] { stroke: var(--lime-ink); stroke-width: 2.5; stroke-dasharray: none; }
.edges marker path { fill: var(--lime-ink); stroke: none; }
.sysmap .edges path[data-on="true"] { marker-end: url(#sys-arrow); }
.flow .edges path[data-on="true"] { marker-end: url(#flow-arrow); }
/* The dash that travels along an active line (assets/js/diagram.js flowAlong). */
.edges path.edge-flow { stroke: var(--lime); stroke-width: 5; stroke-linecap: round; marker-end: none; }
.edge-label { position: absolute; font-size: 12px; font-weight: 700; color: var(--ink2); background: var(--g-solid); padding: 2px 8px; border-radius: 999px; white-space: nowrap; }
.list-alt summary { cursor: pointer; font-weight: 600; font-size: 15px; min-height: 44px; display: inline-flex; align-items: center; gap: 10px; padding: 0 18px; border-radius: var(--r-btn); background: var(--btn2); box-shadow: var(--soft-edge); list-style: none; width: fit-content; }
.list-alt summary::-webkit-details-marker { display: none; }
.list-alt summary::after { content: ""; width: 7px; height: 7px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: translateY(-2px) rotate(45deg); }
.list-alt[open] summary::after { transform: translateY(2px) rotate(-135deg); }
.list-alt summary:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.list-alt ol, .list-alt ul { padding: 12px 0 0 1.2em; display: flex; flex-direction: column; gap: 6px; color: var(--ink2); }

/* System map (Motel POS) */
.sys-legend { max-width: 60ch; }
.sysmap { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px 64px; position: relative; }
.sys-col { display: flex; flex-direction: column; gap: 12px; position: relative; z-index: 1; }
.sys-col h3 { font-size: 13px; font-weight: 700; color: var(--ink2); margin-bottom: 8px; }
.sys-node {
  position: relative; text-align: left; border: 1px solid var(--g-edge); border-radius: 16px; padding: 12px 14px; min-height: 48px; cursor: pointer; font-weight: 600; font-size: 15px; color: var(--ink);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--paper-fill); box-shadow: inset 0 1px 0 #fff, var(--chip-shadow); transition: transform var(--press-dur) ease-out, opacity var(--fade-dur) var(--fade);
}
.sys-node[aria-pressed="true"] { background: var(--dark-fill); color: var(--btn-ink); border-color: transparent; box-shadow: var(--btn-edge), 0 1px 2px rgba(12,11,8,.25), 0 12px 24px -12px rgba(12,11,8,.5); }
.sys-node[data-near="false"] { opacity: .65; }
.sys-node:active { transform: scale(.98); }
/* The lime ring a linked part pulses on a phone (assets/js/connect.js): only its opacity animates. A spread
   shadow, unlike a border or an outset box, adds no scrollable overflow. */
.sys-node::before { content: ''; position: absolute; inset: 0; border-radius: 15px; box-shadow: 0 0 0 4px var(--lime); opacity: 0; pointer-events: none; }
.sys-detail { border-radius: 24px; padding: 20px 24px; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; background: var(--g-solid); }
.sys-dirs { display: flex; flex-direction: column; gap: 6px; }
.sys-detail .lnks { font-size: 15px; }
.sys-detail .lnks .label { font-size: 12px; }
/* One column, no lines: the linked parts say which way the data goes. The tag is decoration (empty alt text,
   so screen readers keep the button's own name); the detail box says the same in words. */
@media (max-width: 960px) {
  .sys-node[data-rel="in"]::after, .sys-node[data-rel="out"]::after { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--lime-ink); flex-shrink: 0; }
  .sys-node[data-rel="in"]::after { content: 'from'; content: 'from' / ''; }
  .sys-node[data-rel="out"]::after { content: 'to'; content: 'to' / ''; }
  .sys-node[aria-pressed="true"]::after { color: var(--lime); }
}

/* ───────────── POS demo ───────────── */
.pos { border-radius: 36px; overflow: hidden; }
.pos-top { display: flex; align-items: center; gap: 16px; padding: 18px 24px; border-bottom: 1px solid var(--line); flex-wrap: wrap; }
.pos-top .title { font-family: var(--display); font-weight: 600; font-size: 20px; }
.pos-top .right { margin-left: auto; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.seg { display: inline-flex; padding: 4px; gap: 2px; border-radius: 999px; background: var(--well); box-shadow: var(--recess); }
.seg button { height: 44px; min-width: 44px; padding: 0 16px; border: 0; border-radius: 999px; background: transparent; color: var(--ink2); font-size: 15px; font-weight: 500; cursor: pointer; transition: transform var(--press-dur) ease-out; }
.seg button[aria-pressed="true"], .seg button[aria-checked="true"] { background: var(--paper); color: #0C0B08; font-weight: 700; box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgba(12,11,8,.12), 0 6px 16px -6px rgba(12,11,8,.25); }
.seg button:active { transform: scale(.96); }
.pos-body { display: grid; grid-template-columns: 184px minmax(0, 1fr) 272px; min-height: 560px; }
.pos-body.no-rail { grid-template-columns: 184px minmax(0, 1fr); }
/* Room for the longest label, bold "Weekly report", on one line in the 172px column; if a label ever wraps,
   the tab grows rather than pressing the text against its edges. */
.pos-tabs { padding: 16px 12px; border-right: 1px solid var(--line); display: flex; flex-direction: column; gap: 4px; }
.pos-tabs button { display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 6px 10px; line-height: 1.25; border: 0; border-radius: 14px; background: transparent; cursor: pointer; font-size: 15px; text-align: left; }
.pos-tabs button svg { width: 18px; height: 18px; }
.pos-tabs button[aria-selected="true"] { background: var(--dark-fill); color: var(--btn-ink); font-weight: 700; box-shadow: var(--btn-edge), 0 6px 14px -8px rgba(12,11,8,.5); }
/* The screen's panel is a size container: its width depends on the tab column and the rail as much as on the
   screen, so the KPI row and the week chart fit the panel, not the viewport. */
.pos-main { padding: 20px 22px; display: flex; flex-direction: column; gap: 16px; min-width: 0; container: pos-main / inline-size; }
.pos-main h3 { font-family: var(--display); font-weight: 600; font-size: 24px; }
.pos-rail { border-left: 1px solid var(--line); padding: 20px 18px; display: flex; flex-direction: column; gap: 12px; }
.pos-items { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.pos-item { text-align: left; border: 1px solid var(--g-edge); background: var(--paper-fill); box-shadow: inset 0 1px 0 #fff, var(--chip-shadow); color: var(--ink); border-radius: 16px; padding: 12px; cursor: pointer; display: flex; flex-direction: column; gap: 6px; min-height: 76px; position: relative; transition: transform var(--press-dur) ease-out; }
.pos-item:active { transform: scale(.98); }
.pos-item .nm { font-weight: 700; font-size: 15px; }
.pos-item .meta { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0 8px; font-size: 14px; color: var(--ink2); font-variant-numeric: tabular-nums; }
.pos-item::after, .pos-row::after { content: ''; position: absolute; inset: 0; border-radius: inherit; background: var(--lime-well); opacity: 0; pointer-events: none; transition: opacity 400ms var(--fade); }
.pos-item[data-flash="true"]::after, .pos-row[data-flash="true"]::after { opacity: 1; transition-duration: 0ms; }
.pos-item[aria-disabled="true"] { opacity: .45; }
/* The cart is one grid and every line borrows its columns (subgrid), so the − 1 + steppers and the totals line
   up whatever the price. A browser without subgrid keeps each line's own columns. Lines sit 12px apart, so the
   steppers' 44px hit areas don't overlap. */
.cart { border-radius: 18px; background: var(--well); box-shadow: inset 0 1px 3px rgba(12,11,8,.06); padding: 14px; display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 12px 10px; }
.cart > * { grid-column: 1 / -1; }
.cart-head { display: flex; justify-content: space-between; font-size: 14px; color: var(--ink2); }
.cart-line { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; grid-template-columns: subgrid; align-items: center; gap: 10px; font-size: 15px; }
.cart-line > .num:last-child { text-align: right; }
.qty { display: inline-flex; align-items: center; gap: 4px; }
.qty > .num { flex: 1 0 auto; text-align: center; }
.qty button { position: relative; width: 32px; height: 32px; border-radius: 10px; border: 0; background: var(--btn2); box-shadow: var(--soft-edge); cursor: pointer; font-size: 16px; }
.qty button::after { content: ''; position: absolute; inset: -6px; } /* the 32px look, a 44px hit area */
.cart-foot { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; border-top: 1px solid var(--line); padding-top: 12px; gap: 12px; }
.cart-foot .figure { font-size: 24px; }
.pos-rows { display: flex; flex-direction: column; }
.pos-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 12px; align-items: center; padding: 12px 10px; border-bottom: 1px solid var(--line); border-radius: 10px; position: relative; }
.pos-row .l { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pos-row .l small { color: var(--ink2); font-size: 14px; }
.meter { height: 6px; border-radius: 6px; background: var(--well2); overflow: hidden; margin-top: 6px; }
.meter i { display: block; height: 100%; border-radius: 6px; background: var(--ink2); transform-origin: 0 50%; transition: transform var(--spring-standard-dur) var(--spring-standard); }
.meter i.low { background: var(--lime); }
.low-tag { font-size: 12px; font-weight: 700; color: var(--lime-ink); }
.kpi { border-radius: 16px; background: var(--well); box-shadow: inset 0 1px 2px rgba(12,11,8,.05); padding: 12px 14px; display: flex; flex-direction: column; }
.kpi span { font-size: 13px; color: var(--ink2); }
.kpi b { font-family: var(--display); font-weight: 600; font-size: 30px; line-height: 1.1; font-variant-numeric: tabular-nums lining-nums; }
.kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
/* Three 30px money figures don't fit a narrow panel: the first takes its own row, as the rail's Sales today
   does. A lone tile (On shift) keeps its half. */
@container pos-main (max-width: 600px) {
  .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpis > .kpi:first-child:not(:only-child) { grid-column: 1 / -1; }
}
.live { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 700; }
.live::before { content: ''; width: 8px; height: 8px; border-radius: 99px; background: var(--lime); box-shadow: 0 0 0 3px var(--lime-well); }
.log { list-style: none; display: flex; flex-direction: column; }
.log li { display: grid; grid-template-columns: 64px minmax(0, 1fr); gap: 10px; padding: 10px 0; border-top: 1px solid var(--line); font-size: 14px; }
.log time { color: var(--ink2); font-variant-numeric: tabular-nums; }
/* Each bar's size is --h, 8 to 108 (pos-core.js, set by html.js): a height here, a length on its side. */
.week { display: flex; align-items: flex-end; gap: 10px; height: 150px; list-style: none; }
.week li { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 6px; font-size: 12px; color: var(--ink2); }
.week i { width: 100%; height: calc(var(--h, 8) * 1px); border-radius: 8px; background: var(--well2); }
.week i.on { background: var(--lime); }
/* A panel too narrow for seven labelled columns lays the week on its side: day, bar, amount on each row, so
   every day and its full amount stay readable. */
@container pos-main (max-width: 460px) {
  .week { flex-direction: column; align-items: stretch; height: auto; gap: 8px; }
  .week li { display: grid; grid-template-columns: 32px minmax(0, 1fr) 72px; align-items: center; gap: 10px; }
  .week li > span:last-child { order: -1; }
  .week .num { order: 1; text-align: right; }
  .week i { height: 12px; width: calc(var(--h, 8) / 108 * 100%); }
}
/* The toast sits in the demo's bottom edge on a wide screen, unless that edge is below the fold (pos-demo.js sets
   data-float). Narrower, the demo stacks and its bottom edge can be far below the button that was tapped, so
   the toast always keeps to the screen's edge: the bottom, or the top when the control used is down there
   (pos-demo.js). It stays under the header and the menu. */
.pos-toast { position: absolute; left: 50%; bottom: 20px; transform: translate(-50%, 16px); opacity: 0; width: max-content; max-width: calc(100% - 32px); padding: 12px 18px; border-radius: var(--r-btn); background: var(--btn-bg); color: var(--btn-ink); font-size: 14px; font-weight: 600; text-align: center; transition: opacity var(--fade-dur) var(--fade), transform var(--spring-snappy-dur) var(--spring-snappy); pointer-events: none; }
.pos-toast[data-show="true"] { opacity: 1; transform: translate(-50%, 0); }
.pos-toast[data-float="true"] { position: fixed; bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 28; }
.pos-wrap { position: relative; }

/* ───────────── "How it works": a short intro that links to each answer on the page ───────────── */
.how-nav { display: flex; flex-wrap: wrap; gap: 8px; }
.how-nav a { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 16px; border-radius: 999px; background: var(--btn2); box-shadow: var(--soft-edge); text-decoration: none; font-size: 15px; font-weight: 600; transition: transform var(--press-dur) ease-out; }
.how-nav a:active { transform: scale(.96); }
.how-nav a svg { width: 16px; height: 16px; }

/* ───────────── where product info comes from (assets/js/lookup-chain.js) ─────────────
   A row of stages on a wide screen; a column with arrows pointing down below 1180px. */
.src-wrap { display: flex; flex-direction: column; gap: 24px; }
.src-chain { display: grid; grid-template-columns: 200px 24px minmax(0, 1fr) 24px minmax(0, 1fr) 24px minmax(0, 1fr) 24px minmax(0, 1fr) 24px 160px; align-items: stretch; }
.src-books { display: grid; grid-template-columns: 200px 24px minmax(0, 1fr) 24px minmax(0, 1fr); align-items: stretch; }
.src-node { min-height: 132px; }
.src-node small { font-size: 13px; color: var(--ink2); }
.src-node.src-end { cursor: default; }
.src-pair { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.src-pair .node { min-height: 0; flex: 1; }
.src-pair-lbl { font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink2); }
/* The book chain's label: a full-width row above it, so it never reads as a step after "Not found". */
.src-books > .src-pair-lbl { grid-column: 1 / -1; margin-bottom: 10px; }
.src-out { border-radius: 24px; padding: 24px; display: flex; flex-direction: column; gap: 12px; background: var(--g-solid); }
.src-out .h3 { font-size: 24px; }
.src-fields { display: flex; flex-wrap: wrap; gap: 6px; list-style: none; }
.src-fields li { display: inline-flex; align-items: center; height: 28px; padding: 0 10px; border-radius: 999px; background: var(--well); box-shadow: inset 0 1px 2px rgba(12,11,8,.04); font-size: 13px; font-weight: 600; }
.src-notes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.src-note { display: flex; gap: 8px; align-items: flex-start; font-size: 14px; color: var(--ink2); }
.src-note svg { width: 16px; height: 16px; flex-shrink: 0; margin-top: 2px; color: var(--lime-ink); }

/* ───────────── one sale, start to finish (Motel POS, assets/js/sale-flow.js) ───────────── */
.sale-wrap { display: flex; flex-direction: column; gap: 24px; }
.sale-top { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; }
.sale-top p { max-width: 60ch; }
.sale-flow { display: grid; grid-template-columns: minmax(0, 1fr) 32px minmax(0, 1fr) 32px minmax(0, 1fr) 32px minmax(0, 1fr); align-items: stretch; }
.sale-step { min-height: 150px; gap: 8px; }
.sale-step .who { display: inline-flex; align-items: center; gap: 6px; margin-top: auto; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--lime-ink); }
.sale-detail { margin-top: 6px; padding-top: 10px; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 4px; }
.sale-detail[hidden] { display: none; }
.sale-step .sale-detail span { color: var(--ink); }
/* The step's toggle: its ::after covers the whole card, so the card is the target and shows the focus ring. */
.sale-toggle { font: inherit; color: inherit; text-align: left; background: none; border: 0; padding: 0; margin: 0; cursor: pointer; display: flex; flex-direction: column; gap: 8px; }
.sale-toggle::after { content: ""; position: absolute; inset: 0; border-radius: 18px; }
.sale-toggle:focus-visible { outline: none; }
.sale-toggle:focus-visible::after { outline: 2px solid var(--ink); outline-offset: 3px; }
.sale-say { display: flex; gap: 10px; align-items: flex-start; font-size: 17px; }
.sale-say svg { width: 20px; height: 20px; flex-shrink: 0; margin-top: 2px; color: var(--lime-ink); }

/* ───────────── how we work (About, assets/js/work-loop.js) ─────────────
   A list with arrows pointing down; from 1024px the script sets it out as a loop, with lines between steps. */
.work { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 40px; align-items: center; }
.work-ring { position: relative; display: flex; flex-direction: column; }
.work-ring .node { min-height: 0; padding: 14px 16px; flex-direction: row; align-items: flex-start; gap: 12px; }
.work-ring .wtext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.work-ring .wtext strong { font-size: 17px; }
.arrow-v { display: flex; justify-content: center; align-items: center; height: 28px; color: var(--lime-ink); }
.arrow-v svg { width: 24px; height: 24px; transform: rotate(90deg); }
.work-back { text-align: center; font-size: 14px; font-weight: 600; color: var(--ink2); margin-top: 4px; }
.work-center, .work-lines { display: none; }
.work-ring[data-layout="ring"] { display: block; }
.work-ring[data-layout="ring"] .node { position: absolute; width: 220px; }
.work-ring[data-layout="ring"] .arrow-v, .work-ring[data-layout="ring"] .work-back { display: none; }
.work-ring[data-layout="ring"] .work-center { display: inline-flex; position: absolute; }
.work-ring[data-layout="ring"] .work-lines { display: block; }
.work-lines path[data-on="true"] { marker-end: url(#work-arrow); }
.work-detail { border-radius: 28px; padding: 28px; display: flex; flex-direction: column; gap: 10px; background: var(--g-solid); }
.work-detail .h3 { font-size: 26px; }
.principles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; list-style: none; }
.principle { border-radius: 24px; padding: 20px; display: flex; flex-direction: column; gap: 6px; }
.principle h3 { font-size: 17px; font-weight: 700; }
.principle p { font-size: 15px; color: var(--ink2); max-width: 60ch; }
@media (max-width: 1180px) {
  .src-chain, .src-books { display: flex; flex-direction: column; }
  .src-books { margin-top: 16px; }
  .src-chain .arrow-h, .src-books .arrow-h { height: 28px; }
  .src-chain .arrow-h svg, .src-books .arrow-h svg { transform: rotate(90deg); }
  .src-node { min-height: 0; }
  .work { grid-template-columns: minmax(0, 1fr); gap: 24px; }
}
@media (max-width: 960px) {
  .sale-flow { display: flex; flex-direction: column; }
  .sale-flow .arrow-h { height: 28px; }
  .sale-flow .arrow-h svg { transform: rotate(90deg); }
  .sale-step { min-height: 0; }
  .principles, .src-notes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Two across: an odd last card takes the whole row instead of standing alone in half of it. */
  .principle:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@media (max-width: 640px) {
  .principles, .src-notes { grid-template-columns: minmax(0, 1fr); }
}

/* ───────────── principles / features ───────────── */
.cards4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px; list-style: none; }
.card { border-radius: 28px; padding: 24px; display: flex; flex-direction: column; gap: 10px; }
.card .icon-well { margin-bottom: 16px; }
.card h3 { font-size: 18px; font-weight: 700; }
.card p { color: var(--ink2); font-size: 15px; }
.meta-line.about-facts { font-size: 16px; font-weight: 600; color: var(--ink); row-gap: 6px; }

/* ───────────── how this site works: five cards, three across on desktop ───────────── */
.trust { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; list-style: none; }
.trust-card p { max-width: 44ch; }
/* Each card's links are a row of 44px targets; the " · " between them is only for the plain text. */
.trust-links { margin-top: auto; display: flex; flex-wrap: wrap; align-items: center; column-gap: 16px; color: var(--ink2); }
.trust-links a { display: inline-flex; align-items: center; min-height: 44px; font-weight: 600; color: var(--ink); }
.trust-links .sep { display: none; }

/* ───────────── contact form ───────────── */
.contact { display: grid; grid-template-columns: minmax(0, 1fr) 560px; gap: 64px; }
.form { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.field { display: flex; flex-direction: column; gap: 8px; }
.field.full { grid-column: 1 / -1; }
/* html's scroll-padding leaves room for the header; a field brought into view (contact.js) gets a little air. */
.form .field { scroll-margin-top: 16px; }
.field label, .field legend { font-size: 14px; font-weight: 700; }
fieldset.field { border: 0; padding: 0; margin: 0; min-width: 0; }
fieldset.field legend { padding: 0; margin-bottom: 8px; }
.field input, .field textarea, .field select {
  width: 100%; min-height: 52px; padding: 14px 16px; border-radius: 16px; border: 1px solid var(--line); background: var(--well);
  box-shadow: inset 0 1px 3px rgba(12,11,8,.06); font-size: 16px;
}
.field textarea { min-height: 140px; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: var(--ink2); opacity: 1; }
.field input:focus, .field textarea:focus { outline: none; border-color: var(--ink); background: var(--g-top); }
.form .hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-status { grid-column: 1 / -1; font-size: 15px; min-height: 22px; }
/* Without JavaScript the form can't send (form-action is 'none'), so it says where to write instead. */
.form noscript { grid-column: 1 / -1; font-size: 15px; }
.form-status[data-kind="error"] { color: var(--danger-ink); }
.form-status[data-kind="ok"] { color: var(--ink); font-weight: 600; }
.form .ts-widget:empty { display: none; }
.radio-row { display: flex; flex-wrap: wrap; gap: 8px; border: 0; padding: 0; margin: 0; }
.radio-row label { position: relative; display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px; border-radius: 999px; background: var(--btn2); box-shadow: var(--soft-edge); cursor: pointer; font-size: 15px; }
/* The real radio, unseen, covers its whole pill: a tap anywhere on the pill lands on the input itself. */
.radio-row input { position: absolute; inset: 0; width: 100%; height: 100%; min-height: 0; margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer; }
.radio-row input:checked + span { font-weight: 700; }
.radio-row label:has(input:checked) { background: var(--btn-bg); color: var(--btn-ink); }
.radio-row label:has(input:focus-visible) { outline: 2px solid var(--ink); outline-offset: 2px; }
.contact-facts { display: flex; flex-direction: column; gap: 20px; margin-top: 12px; }
.contact-facts dt { font-size: 14px; font-weight: 700; color: var(--ink2); }
.contact-facts dd { margin: 4px 0 0; font-size: 18px; }
/* The address is a 44px target; the negative margin keeps the list's spacing as it was. */
.contact-facts dd a { display: inline-flex; align-items: center; min-height: 44px; margin-block: -10px; }
/* The contact card on About: what to write to, and one button to the form. */
.contact-card { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 24px; align-items: center; }
@media (max-width: 640px) { .contact-card { grid-template-columns: minmax(0, 1fr); } .contact-card .btn { width: 100%; } }

/* ───────────── footer ───────────── */
.site-footer { margin: 0 max(var(--panel-inset), var(--safe-r)) 16px max(var(--panel-inset), var(--safe-l)); border-radius: 40px; padding: 48px calc(var(--gutter) - var(--panel-inset)) 28px; }
.foot-in { max-width: calc(var(--max) + 2 * 48px); margin-inline: auto; display: flex; flex-direction: column; gap: 32px; }
.foot-top { display: grid; grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr)); gap: 32px; }
.foot-top p { color: var(--ink2); font-size: 15px; margin-top: 10px; }
.foot-col h2 { font-size: 14px; font-weight: 700; color: var(--ink2); margin-bottom: 10px; font-family: var(--font); }
.foot-col ul { list-style: none; display: flex; flex-direction: column; }
.foot-col a { display: inline-flex; align-items: center; min-height: 44px; text-decoration: none; font-size: 15px; }
.foot-bot { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding-top: 24px; border-top: 1px solid var(--line); flex-wrap: wrap; font-size: 14px; color: var(--ink2); }
.foot-bot .tm { font-size: 13px; max-width: 72ch; }
/* Side by side, the wordmark (centred in its 44px link) lines up with the columns' headings (a 21px line). */
@media (min-width: 961px) { .foot-top > :first-child { margin-top: calc((21px - 44px) / 2); } }

/* ───────────── documents (privacy, terms) ───────────── */
.doc { display: grid; grid-template-columns: 260px minmax(0, 720px); gap: 64px; padding-top: clamp(120px, 12vw, 176px); }
.doc-head { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 16px; max-width: 880px; }
.doc-head .updated { color: var(--ink2); font-size: 15px; }
.toc { position: sticky; top: 112px; align-self: start; border-radius: 24px; padding: 20px; }
.toc h2 { font-size: 14px; font-weight: 700; color: var(--ink2); margin-bottom: 8px; font-family: var(--font); padding-inline: 8px; }
.toc ol { list-style: none; display: flex; flex-direction: column; }
.toc a { display: flex; align-items: center; min-height: 36px; text-decoration: none; font-size: 15px; line-height: 1.35; padding: 4px 8px; border-radius: 10px; }
/* 44px rows on touch screens and wherever the list sits above the text; beside it (sticky), a short screen
   scrolls the list inside itself rather than cutting off its last entries. */
@media (pointer: coarse), (max-width: 1180px) { .toc a { min-height: 44px; } }
@media (min-width: 1181px) { .toc { max-height: calc(100svh - 128px); overflow-y: auto; overscroll-behavior: contain; } }
.prose { font-size: 17px; line-height: 1.7; max-width: 68ch; display: flex; flex-direction: column; gap: 16px; }
/* html's scroll-padding already clears the header; 8px more lands a heading level with the sticky list (top: 112px). */
.prose h2 { font-family: var(--display); font-weight: 600; font-size: 28px; letter-spacing: -0.015em; line-height: 1.15; margin-top: 32px; scroll-margin-top: 8px; }
.prose h2:first-child { margin-top: 0; }
.prose h3 { font-size: 18px; font-weight: 700; margin-top: 8px; }
.prose ul, .prose ol { padding-left: 1.3em; display: flex; flex-direction: column; gap: 8px; }
.prose a { text-underline-offset: 3px; }
.prose table { width: 100%; border-collapse: collapse; font-size: 15px; line-height: 1.5; }
.prose th, .prose td { text-align: left; vertical-align: top; padding: 10px 12px 10px 0; border-bottom: 1px solid var(--line); }
.prose th { font-weight: 700; color: var(--ink2); font-size: 14px; }
.table-scroll { overflow-x: auto; }
.callout { border-radius: 20px; padding: 18px 20px; background: var(--well); font-size: 16px; }

/* ───────────── 404 ───────────── */
.nf { min-height: 80vh; display: flex; align-items: center; justify-content: center; text-align: center; padding-top: 120px; }
.nf-in { display: flex; flex-direction: column; align-items: center; gap: 20px; }
.nf .code-num { font-family: var(--display); font-weight: 600; font-size: 20px; color: var(--ink2); }
.nf .row-btns { justify-content: center; }

/* ───────────── scroll reveal (motion only when allowed) ───────────── */
@media (prefers-reduced-motion: no-preference) {
  .reveal { opacity: 0; transform: translateY(24px); transition: opacity var(--spring-standard-dur) var(--spring-standard), transform var(--spring-standard-dur) var(--spring-standard); }
  .reveal[data-in="true"] { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
  .bg-glow { display: none; }
}

/* ───────────── depth: lift and press ─────────────
   Interactive raised surfaces (.lift) sit down on press everywhere, with a transform only. With a mouse they also
   lift on hover and their shadows grow; on touch screens no shadow ever changes. */
.lift { transition: transform var(--press-dur) ease-out, opacity var(--spring-standard-dur) var(--spring-standard); }
.lift:active { transform: scale(.985); }
/* The browser's tap highlight is off (the html rule under "base": Android's is a square blue box drawn over
   everything, even the card a tap opens), so every control shows a press of its own; these had only the
   highlight. Buttons sit down, links and text rows dim. Transform and opacity only, with no transition. */
.rc-close:active, .menu-btn:active, .mini:active, .rc-details-btn:active, .pos-tabs button:active, .qty button:active,
.radio-row label:active, .list-alt summary:active { transform: scale(.96); }
.nav-link:active, .text-link:active, .foot-col a:active, .menu-sheet li a:active, .dropdown a:active,
.raw summary:active, .apps-more summary:active, .ingredients summary:active { opacity: .7; }
@media (hover: hover) and (pointer: fine) {
  .lift { transition: transform var(--spring-snappy-dur) var(--spring-snappy), box-shadow 240ms ease, opacity var(--spring-standard-dur) var(--spring-standard); }
  .lift:hover { transform: translateY(-4px); box-shadow: var(--edge), var(--g-shadow-hover); }
  .lift:active { transform: translateY(1px) scale(.985); transition-duration: var(--press-dur); box-shadow: var(--edge), var(--g-shadow-press); }
  .sample[aria-expanded="true"]:hover { box-shadow: var(--ring), var(--edge), var(--g-shadow-hover); }
  /* A sample's code face recesses while it is pressed. */
  .sample .code-face { transition: box-shadow var(--press-dur) ease; }
  .sample:active .code-face { box-shadow: inset 0 2px 6px rgba(12,11,8,.12), inset 0 0 0 1px rgba(12,11,8,.06); }
  .btn-primary { transition: transform var(--spring-snappy-dur) var(--spring-snappy), box-shadow 240ms ease; }
  .btn-primary:hover { transform: translateY(-1px); box-shadow: var(--btn-edge), 0 1px 2px rgba(12,11,8,.25), 0 12px 24px -10px rgba(12,11,8,.60); }
  .btn-primary:active { transform: scale(.97); transition-duration: var(--press-dur); box-shadow: inset 0 1px 0 rgba(255,255,255,.08), 0 1px 2px rgba(12,11,8,.3); }
  .node { transition: transform var(--spring-snappy-dur) var(--spring-snappy), opacity var(--fade-dur) var(--fade), box-shadow 200ms ease; }
  /* Only parts you can pick lift; the flow's cards have lines drawn to them and stay put. */
  button.node[aria-pressed="true"], button.node:hover, .sale-step:hover { transform: translateY(-2px); }
  button.node:active, .sale-step:active { transform: translateY(1px) scale(.985); transition-duration: var(--press-dur); }
  /* Plain hover tints and underlines: with a mouse only, so a tap never leaves a hover state stuck on. */
  .nav-link:hover { color: var(--ink2); }
  .dropdown a:hover { background: var(--btn2); }
  .text-link:hover { text-decoration: underline; text-underline-offset: 6px; }
  .btn-secondary:hover { background: var(--btn2-hover); }
  .btn-ghost:hover { background: var(--btn2); }
  .link-arrow:hover svg { transform: translateX(3px); }
  .rc-close:hover { background: var(--btn2-hover); }
  .rc-details-btn:hover { background: var(--btn2-hover); }
  .mini:hover { background: var(--btn2-hover); }
  .how-nav a:hover { background: var(--btn2-hover); }
  .foot-col a:hover { text-decoration: underline; text-underline-offset: 3px; }
  .toc a:hover { background: var(--btn2); }
}

/* ───────────── responsive ───────────── */
@media (max-width: 1180px) {
  .hero { grid-template-columns: minmax(0, 1fr); }
  .hero-art { justify-self: center; }
  .flow { grid-template-columns: minmax(0, 1fr); }
  /* The zones stack, and the lines only fit zones side by side: the steps are numbered instead. */
  .flow .steps { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; margin-top: 16px; counter-reset: step; }
  .flow .steps .node { padding-left: 60px; }
  .flow .steps .node::before { counter-increment: step; content: counter(step); position: absolute; left: 16px; top: 16px; width: 32px; height: 32px; border-radius: 999px; background: var(--lime); color: #0C0B08; font-family: var(--display); font-weight: 700; font-size: 15px; display: inline-flex; align-items: center; justify-content: center; }
  .flow .zone { min-height: 0; }
  .flow .stays { position: static; margin-top: 16px; flex-wrap: wrap; }
  .flow .outs { gap: 16px; }
  .flow .edges { display: none; }
  .pos-body, .pos-body.no-rail { grid-template-columns: 172px minmax(0, 1fr); }
  .pos-rail { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--line); display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  /* Label across the top, Sales today beside the three tiles, the activity list across the bottom: the tiles
     never stretch to the list's height. */
  .pos-rail > .live, .pos-rail > div:last-child { grid-column: 1 / -1; }
  .pos-toast { position: fixed; bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 28; }
  .contact { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .doc { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .toc { position: static; }
}
@media (max-width: 960px) {
  .site-header { top: 12px; left: max(12px, var(--safe-l)); right: max(12px, var(--safe-r)); height: 56px; padding: 0 6px 0 16px; border-radius: 16px; }
  .site-header[data-scrolled="true"]::before, .site-header[data-scrolled="true"]::after { transform: none; }
  .brand-word { font-size: 13px; } .brand svg { width: 10px; height: 18px; }
  .nav, .bar-actions { display: none; }
  .menu-btn { display: inline-flex; }
  html { scroll-padding-top: 84px; }
  .products { grid-template-columns: minmax(0, 1fr); }
  .answers { grid-template-columns: minmax(0, 1fr); }
  .cards4, .trust { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .trust > li:last-child:nth-child(odd) { grid-column: 1 / -1; }
  .foot-top { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .foot-top > :first-child { grid-column: 1 / -1; }
  .sysmap { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .flow .steps { grid-template-columns: minmax(0, 1fr); }
  .edges { display: none; }
}
@media (max-width: 640px) {
  .hero { padding-top: 112px; gap: 32px; min-height: 0; }
  .hero .row-btns .btn { width: 100%; }
  .nf .row-btns, .nf .row-btns .btn { width: 100%; }
  .hero-art { width: 100%; height: auto; perspective: none; }
  .hero-art .card-dash { display: none; }
  .hero-art .card-phone { position: static; transform: none; }
  .pcard { padding: 20px; border-radius: 28px; }
  .pcard-art { height: 260px; padding: 12px; }
  .pcard-art .code-tile { display: none; }
  .samples { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .hero-art .pv { margin-inline: auto; }
  .cards4, .trust { grid-template-columns: minmax(0, 1fr); }
  .form { grid-template-columns: minmax(0, 1fr); }
  .foot-top { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .site-footer { border-radius: 28px; padding: 24px; margin: 0 max(12px, var(--safe-r)) 16px max(12px, var(--safe-l)); }
  .pos-top { padding: 14px 16px; }
  .pos-body { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .pos-body.no-rail { grid-template-columns: minmax(0, 1fr); }
  .pos-tabs { flex-direction: row; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--line); padding: 10px; }
  .pos-tabs button { flex-shrink: 0; }
  .pos-items { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .pos-rail { grid-template-columns: minmax(0, 1fr); }
  .pos-main { padding: 16px; }
  .panel { border-radius: 36px; }
}

/* ───────────── page pieces ───────────── */
main:focus { outline: none; }
main > section.wrap:not(.hero):not(.nf):not(.contact):not(.cta-row) { display: flex; flex-direction: column; gap: clamp(24px, 3vw, 40px); }
.page-top { padding-top: clamp(120px, 12vw, 176px); }
.center-note { text-align: center; }
.prod-rows { display: flex; flex-direction: column; gap: 24px; }
.prow { border-radius: 40px; padding: clamp(24px, 4vw, 48px); display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 40px; align-items: center; }
.prow-copy { display: flex; flex-direction: column; gap: 18px; align-items: flex-start; }
.prow-art { min-height: 320px; border-radius: 32px; background: var(--well); box-shadow: var(--recess); display: flex; align-items: center; justify-content: center; gap: 24px; padding: 24px; }

.mini-dash .md-sales { display: flex; flex-direction: column; }
.big-dash { width: min(420px, 100%); }
/* 15 characters (13 digits, 2 spaces) in 90% of the code face: the tracking gives way before the size does, so a
   narrow face (a phone's two-across grid, the first four-across width) keeps readable digits. */
.ean-digits { display: block; font-family: var(--mono); font-size: min(11px, 10cqi); letter-spacing: min(.1em, max(0px, (90cqi - 9em) / 15)); color: #0C0B08; text-align: center; white-space: nowrap; }
.sample .code-face { flex-direction: column; gap: 6px; container-type: inline-size; }
.sample .code-face svg.bar { width: 72%; height: 64px; max-height: none; }
.bars span:nth-child(1) { height: 44px; } .bars span:nth-child(2) { height: 38px; } .bars span:nth-child(3) { height: 48px; }
.bars span:nth-child(4) { height: 42px; } .bars span:nth-child(5) { height: 54px; } .bars span:nth-child(6) { height: 64px; } .bars span:nth-child(7) { height: 34px; }
.cta-row { display: flex; justify-content: space-between; align-items: flex-end; gap: 32px; flex-wrap: wrap; }
.form-card { border-radius: 40px; padding: clamp(24px, 4vw, 48px); }
.hero-pos { min-height: 0; }
/* A definite width: centred in the grid, the item would shrink to its content and the card's 100% with it. */
.hero-dash { perspective: 1600px; justify-self: center; width: min(420px, 100%); }
.hero-dash .tilt { transform: rotateY(-12deg) rotateX(6deg); will-change: transform; }
.hero-art .card-dash { transform: rotateY(14deg) rotateX(6deg) rotateZ(-2deg); }
.hero-art .card-phone { transform: rotateY(-10deg) rotateX(4deg); }
.sys-wrap, .flow-wrap { display: flex; flex-direction: column; gap: 24px; }
.phone-link h3 { font-size: 22px; }
.foot-in { max-width: var(--max); }
.pos-rail .kpis .kpi b { font-size: 22px; }
.pos-rail .kpis .kpi span { font-size: 12px; white-space: nowrap; }
.pos-rail .kpis .kpi { padding: 10px; }
@media (max-width: 960px) {
  .prow { grid-template-columns: minmax(0, 1fr); gap: 24px; border-radius: 32px; }
  .prow-art { min-height: 260px; }
}
@media (max-width: 640px) {
  .prow-art { flex-direction: column; padding: 16px; min-height: 0; }
  .hero-dash { width: 100%; }
  .hero-dash .tilt { transform: none; }
  .cta-row .btn { width: 100%; }
}

/* ───────────── the animated demos (assets/js/story.js; Scanner: scan-story.js, Motel POS: pos-story.js) ─────────────
   A stage, the captions, a row of progress dots + "Sample data" + one button, and the static strip. Laid out
   by the demo's own width (a container), so it fits a product card, a product row or a hero the same way.
   Predicted cost of what moves, per frame while it moves:
     transform, opacity (every change below) ... composite only: no style recalc beyond the element, no layout,
                                                 no paint once the layer exists
     the data-* attribute set by a cue ......... one style recalc of the demo, about 0.1–0.3 ms, once per cue
     zoom (the fit) ............................ layout of the demo, only when its box changes size, never
                                                 while it plays
   Nothing changes size, colour, blur or shadow while it plays: colour changes (the lock outline, the lime
   flashes, the lime connector, Sunday's bar, the dots) are a second layer fading in. */
.story { container: story / inline-size; width: 100%; display: flex; flex-direction: column; align-items: center; gap: 16px; }
.story-stage { width: 100%; display: flex; justify-content: center; align-items: center; }
/* Captions: one grid cell, so the tallest sets the height and a swap moves nothing. A swap fades through: the
   old caption leaves quickly and the new one waits for it, so two are never on top of each other. */
.story-caps { list-style: none; margin: 0; padding: 0; width: 100%; display: grid; font-family: var(--display); font-weight: 600; font-size: 20px; line-height: 1.3; letter-spacing: -0.01em; }
.story-cap { grid-area: 1 / 1; align-self: start; text-align: center; text-wrap: balance; opacity: 0; transform: translateY(6px);
  transition: opacity 100ms linear, transform var(--spring-standard-dur) var(--spring-standard); }
.story[data-scene="1"] .story-cap[data-cap="1"], .story[data-scene="2"] .story-cap[data-cap="2"],
.story[data-scene="3"] .story-cap[data-cap="3"], .story[data-scene="4"] .story-cap[data-cap="4"] { opacity: 1; transform: none;
  transition: opacity var(--fade-dur) var(--fade) 100ms, transform var(--spring-standard-dur) var(--spring-standard) 100ms; }
/* The row: the dots, the "Sample data" label and the button, in three columns, so the label sits under the
   captions' centre however wide the two sides are. */
.story-row { width: 100%; max-width: 420px; min-height: 44px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px; }
.story-dots { display: flex; align-items: center; gap: 8px; justify-self: start; }
.story-dots i { width: 9px; height: 9px; border-radius: 50%; background: var(--ink); opacity: .18; transition: opacity var(--fade-dur) var(--fade); }
.story[data-scene="1"] .story-dots i:nth-child(-n+1), .story[data-scene="2"] .story-dots i:nth-child(-n+2),
.story[data-scene="3"] .story-dots i:nth-child(-n+3), .story[data-scene="4"] .story-dots i:nth-child(-n+4),
.story[data-done="true"] .story-dots i { opacity: 1; }
/* Play → Pause → Resume, a ghost button; Replay at the end, dark. At least 44 × 44 px and as wide as its widest
   label; each state has its icon and the label starts at the same place, so a new label never moves anything. */
.story-ctl { justify-self: end; display: inline-flex; align-items: center; justify-content: flex-start; gap: 8px; min-width: 120px; min-height: 44px; padding: 0 18px; border: 0; border-radius: var(--r-btn);
  font: 600 15px var(--font); color: var(--ink); background: transparent; box-shadow: inset 0 0 0 1px var(--line); cursor: pointer; transition: transform var(--press-dur) ease-out; }
.story-ctl:active { transform: scale(.97); }
.story-ctl:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.story-ic { display: none; }
.story-ic svg { width: 18px; height: 18px; }
.story[data-state="done"] .story-ctl { background: var(--dark-fill); color: var(--btn-ink); box-shadow: var(--btn-edge), var(--btn-shadow); }
.story[data-state="ready"] .story-ic[data-ic="play"], .story[data-state="paused"] .story-ic[data-ic="play"], .story[data-state="playing"] .story-ic[data-ic="pause"],
.story[data-state="done"] .story-ic[data-ic="replay"] { display: inline-flex; }
.story:not([data-ready]) .story-ctl { visibility: hidden; }
/* Narrow: the button is its icon (its name stays for screen readers). */
@container story (max-width: 319px) { .story-row { gap: 8px; } }
@container story (max-width: 359px) {
  .story-ctl { width: 44px; min-width: 44px; padding: 0; justify-content: center; }
  .story-lbl { position: absolute; clip-path: inset(50%); white-space: nowrap; pointer-events: none; }
}
/* The strip: reduced motion (no script needed) and the lite rules (data-mode="strip"). Its figures are sample
   data too, so the row stays for its label, centred under the strip; the dots and the button go. */
.story-strip { display: none; list-style: none; margin: 0; padding: 0; width: 100%; }
.story[data-mode="strip"] :is(.story-stage, .story-caps, .story-dots, .story-ctl) { display: none; }
.story[data-mode="strip"] .story-strip { display: grid; }
.story[data-mode="strip"] .story-row { order: 1; min-height: 0; display: flex; justify-content: center; }
/* (the first dot comes on at once while the demo mounts, before it has a mode: a fade started there kept
   running in WebKit under the strip's hidden dots) */
.story:not([data-mode]) .story-dots i { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .story :is(.story-stage, .story-caps, .story-dots, .story-ctl) { display: none; }
  .story .story-strip { display: grid; }
  .story .story-row { order: 1; min-height: 0; display: flex; justify-content: center; }
}
.story-n { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 24px; height: 24px; border-radius: 999px; background: var(--lime); color: #0C0B08; font: 700 12px var(--font); }

/* ── the Scanner demo ──
   The phone at 434 × 906 pt, zoomed to fit its box (site.js). The box: the placement's phone height, never
   wider than the demo, and short enough that the phone, captions and controls fit one screen below the fixed
   header (landscape). --head-clear: the header's bottom edge and 12 px of air; 146 px: the captions (at 22 px)
   and the row, 12 px of air below. */
.story-scan { --phone-h: 598px; --head-clear: 96px; }
@media (max-width: 960px) { .story-scan { --head-clear: 80px; } }
.story-scan .story-stage > .pv-fit { height: min(var(--phone-h), 100cqi * 906 / 434, 100svh - var(--head-clear) - 146px); aspect-ratio: 434 / 906; max-width: 100%; --pv-zoom: .66; }
.story-scan .story-strip { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.story-scan .story-strip li { display: flex; flex-direction: column; align-items: center; gap: 8px; min-width: 0; }
.story-strip-art { width: 100%; display: flex; justify-content: center; }
.story-strip-art .pv-fit { width: min(100%, 183px); aspect-ratio: 434 / 906; --pv-zoom: .26; }
/* The first frame (before site.js, or without it): the same fit site.js makes, worked out from the same sizes as
   the boxes, where CSS can divide a length by a length. Elsewhere, the usual size of each placement. */
.products .story-scan .story-stage > .pv-fit { --pv-zoom: .574; }
@media (max-width: 640px) { .story-scan .story-stage > .pv-fit, .products .story-scan .story-stage > .pv-fit { --pv-zoom: .6004; } }
@supports (zoom: calc(1px / 1px)) {
  .story-scan .story-stage > .pv-fit, .products .story-scan .story-stage > .pv-fit { --pv-zoom: calc(min(var(--phone-h), 100cqi * 906 / 434, 100svh - var(--head-clear) - 146px) / 906px); }
  .story-strip-art .pv-fit { --pv-zoom: calc(min((100cqi - 16px) / 3, 183px) / 434px); }
  @container story (min-width: 560px) { .story-strip-art .pv-fit { --pv-zoom: calc(min((100cqi - 32px) / 3, 183px) / 434px); } }
  @container story (max-width: 239px) { .story-strip-art .pv-fit { --pv-zoom: calc(56px / 434px); } }
  @media (orientation: landscape) and (max-height: 500px) {
    .story-scan .story-stage > .pv-fit, .products .story-scan .story-stage > .pv-fit { --pv-zoom: calc(min(var(--phone-h), 100svh - var(--head-clear) - 12px) / 906px); }
  }
}
.story-strip-cap { display: flex; flex-direction: column; align-items: center; gap: 4px; max-width: 190px; font-size: 13px; line-height: 1.35; text-align: center; }
@container story (min-width: 560px) {
  .story-caps { font-size: 22px; }
  .story-scan .story-strip { gap: 16px; }
  .story-scan .story-strip li { gap: 10px; }
  .story-strip-cap { font-size: 15px; }
}
/* The page inside the phone, laid out as the real scanner page at 402 pt: each part at the place the page puts
   it, so the demo only moves and fades them. */
.ss-page { position: absolute; left: 0; right: 0; top: 0; height: 1500px; transform: translateY(-42px); transition: transform var(--native-scroll-dur) var(--native-scroll); }
.ss-pv[data-found="true"] .ss-page { transform: translateY(-727px); }
.ss-panel { position: absolute; top: 16px; left: 13.5px; right: 13.5px; height: 1400px; margin: 0; padding: 0; display: block; }
.ss-h2 { position: absolute; top: 40px; left: 20px; }
.ss-privacy { position: absolute; top: 88px; left: 20px; width: 335px; align-items: flex-start; line-height: 1.4; }
.ss-privacy svg { margin-top: 2px; }
.ss-ui { position: absolute; top: 152px; left: 20px; width: 335px; }
.ss-h2, .ss-privacy, .ss-ctls, .ss-rh { transition: opacity 300ms var(--fade); }
/* The camera: the view and viewfinder fade in over the idle text; the code slides in; the outline lights. */
.ss-cam, .ss-vf { opacity: 0; transition: opacity var(--fade-dur) var(--fade); }
.ss-idle { transition: opacity var(--fade-dur) var(--fade); }
.ss-pv[data-cam="true"] :is(.ss-cam, .ss-vf) { opacity: 1; }
.ss-pv[data-cam="true"] .ss-idle { opacity: 0; }
.ss-code { position: relative; opacity: 0; transform: translateX(170px) rotate(-3deg);
  transition: transform var(--spring-standard-dur) var(--spring-standard), opacity var(--fade-dur) var(--fade); }
.ss-pv[data-code="true"] .ss-code { opacity: 1; transform: rotate(-3deg); }
.ss-code::after { content: ''; position: absolute; inset: -9px; border: 3px solid var(--lime); border-radius: 23px; opacity: 0; transition: opacity var(--fade-dur) var(--fade); }
.ss-pv[data-lock="true"] .ss-code::after { opacity: 1; }
/* The ring takes over from the viewfinder's corners: the two are the same size, 3° apart, and would cross at
   every corner. The scanline stays (paused). */
.ss-vf i { transition: opacity var(--fade-dur) var(--fade); }
.ss-pv[data-lock="true"] .ss-vf i { opacity: 0; }
/* The scanline sweeps only while the camera looks, pauses where it is on lock-on, and sits at 50% in stills. */
/* (top stays at 8%, with reduced motion too; the still position is a transform, so starting and stopping it never
   lays anything out. In px, not cqh: in the phone's own pixels the viewfinder is always 208 px, and under the
   phone's zoom WebKit takes cqh from the zoomed size, which cut the sweep to two thirds and the still to 35%.) */
.ss-pv .ss-vf::after { animation: none; top: 8%; transform: translateY(87px); }
.story-stage .ss-pv[data-cam="true"] .ss-vf::after { transform: none; animation: ss-scanline 1.1s cubic-bezier(.45, 0, .2, 1) infinite alternate; }
@keyframes ss-scanline { to { transform: translateY(175px); } }
.story-stage .ss-pv[data-lock="true"] .ss-vf::after { animation-play-state: paused; }
.ss-st { opacity: 0; transition: opacity var(--fade-dur) var(--fade); }
.ss-pv[data-status="start"] .ss-st[data-st="start"], .ss-pv[data-status="look"] .ss-st[data-st="look"] { opacity: 1; }
/* Start camera ⇄ Stop camera: both rows in one place, crossfading. */
.ss-ctls { position: absolute; top: 461px; left: 0; width: 335px; display: grid; }
.ss-ctl { grid-area: 1 / 1; flex-direction: column; align-items: stretch; transition: opacity var(--fade-dur) var(--fade); }
.ss-ctl[data-ctl="cam"], .ss-pv[data-cam="true"] .ss-ctl[data-ctl="idle"] { opacity: 0; }
.ss-pv[data-cam="true"] .ss-ctl[data-ctl="cam"] { opacity: 1; }
.ss-ctl .btn { width: 100%; }
/* Once the page has scrolled to the result, the buttons are above it, half under the status bar: they fade out. */
.ss-pv[data-found="true"] .ss-ctls { opacity: 0; }
.ss-ctl .btn-primary { transition: transform var(--press-dur) ease-out; }
/* (pressed only between the tap and the camera opening: in scene 3 the camera closes but nothing presses it) */
.ss-pv[data-tap="true"]:not([data-cam="true"]):not([data-found="true"]) .ss-ctl[data-ctl="idle"] .btn-primary { transform: scale(.97); }
/* The result: "Scan result" and the scanner's own card rise in as the page scrolls to them. */
.ss-results { position: absolute; top: 599px; left: 0; width: 335px; display: flex; flex-direction: column; gap: 16px; opacity: 0; transform: translateY(16px);
  transition: opacity var(--fade-dur) var(--fade), transform var(--spring-standard-dur) var(--spring-standard); }
.ss-pv[data-found="true"] .ss-results { opacity: 1; transform: none; }
.pv .result { animation: none; } /* every preview phone: the card is there from the first frame (the demo's rises with .ss-results) */
/* The live phone's fading parts are their own layers from the start (measured on the low-end profile: painted
   at mount, not in the frame the camera opens). Small: each is inside the zoomed phone. */
.story-stage :is(.ss-cam, .ss-vf, .ss-idle, .ss-st, .ss-ctl, .ss-code, .ss-results) { will-change: opacity, transform; }
/* The finger: an iPhone-style touch circle on Start camera. */
.ss-pv .pv-touch { translate: 201px 667px; }
.ss-pv[data-tap="true"] .pv-touch { animation: story-touch 600ms ease-out backwards; }
@keyframes story-touch { 0% { opacity: 0; transform: scale(.85); } 20% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(1.15); } }
.story-strip .pv-touch { display: none; }
/* Spotlight: the scene's subject stays; the rest of the screen drops to 45%. Lifted at the end. In scene 1 the
   controls dim only once the camera is open, so the finger never taps a button that looks disabled. */
.ss-pv:not([data-done="true"])[data-scene="1"] :is(.ss-h2, .ss-privacy),
.ss-pv:not([data-done="true"])[data-scene="1"][data-cam="true"] .ss-ctls,
.ss-pv:not([data-done="true"])[data-scene="2"] :is(.ss-h2, .ss-ctls),
.ss-pv:not([data-done="true"])[data-scene="3"] .ss-rh { opacity: .45; }
/* Replay: everything snaps back with no transition for one frame. */
.story [data-instant="true"], .story [data-instant="true"] *, .story [data-instant="true"] *::after { transition: none !important; animation: none !important; }
/* Nothing in a demo transitions before it starts, while its box is being fitted (a new zoom changes every
   transform's pixels), in the strip, or with reduced motion: only the timeline moves it. */
.story:not([data-ready]) *, .story [data-fitting] *, .story[data-mode="strip"] *, .story:not([data-ready]) *::after, .story [data-fitting] *::after, .story[data-mode="strip"] *::after { transition: none !important; }
.story[data-mode="strip"] *, .story[data-mode="strip"] *::after { animation: none !important; }
/* Paused (the button, off screen, a hidden tab): CSS animations hold where they are. story.js pauses only the
   transitions: a CSS animation it paused and played would no longer follow CSS (the scanline's stop on lock-on).
   Named, not every element: a pause then restyles these few, not the whole demo (the scanline, the touch circles,
   the flashes; a new animation in a demo goes here too, and the Pause tests check that nothing runs). */
.story[data-state="paused"] :is(.pv-touch, .ps-touch), .story[data-state="paused"] :is(.ss-vf, .ps-tile, .ps-srow)::after { animation-play-state: paused !important; }
@media (prefers-reduced-motion: reduce) { .story *, .story *::after { transition: none !important; animation: none !important; } }

/* ── the Motel POS demo ──
   One sale through four cards: Sell → Stock → Live dashboard → Weekly report, a lime dot carrying it along.
   Three layouts, one type scale (12 px and up, never under 10.5 px on screen):
     phones and narrow cards (under 520 px), and short landscape screens: the card the scene is about, at full
       size, above a four-step track the dot travels along (the other cards wait underneath, faded out);
     520 px and up: the design's 2 × 2 snake (across, down, back across), drawn at 540 × 504 and zoomed to fit;
     1000 px and up: four in a row, drawn at 1120 × 232 and zoomed to fit.
   Text that the drawings keep on one line is set not to wrap; the phone layout lets everything wrap. */
.ps-grid { position: relative; --travel: 500ms; }
.ps-card { border-radius: 24px; padding: 16px; display: flex; flex-direction: column; gap: 8px; min-width: 0;
  transition: opacity 300ms var(--fade), transform var(--spring-standard-dur) var(--spring-standard); }
.ps-head { display: flex; flex-wrap: wrap; align-items: center; column-gap: 8px; row-gap: 2px; min-height: 20px; }
.ps-head strong { font-family: var(--display); font-weight: 600; font-size: 16px; line-height: 1.25; letter-spacing: -0.01em; }
.ps-head small { margin-left: auto; font-size: 12px; color: var(--ink2); white-space: nowrap; }
.ps-head svg { width: 18px; height: 18px; flex-shrink: 0; }
.ps-head.ps-live::before { content: ''; width: 8px; height: 8px; border-radius: 99px; background: var(--lime); box-shadow: 0 0 0 3px var(--lime-well); flex-shrink: 0; }
.ps-swap { display: inline-grid; font-variant-numeric: tabular-nums; }
.ps-swap > span { grid-area: 1 / 1; transition: opacity var(--fade-dur) var(--fade); }
.ps-swap > .b { opacity: 0; }
.ps-grid[data-stock="true"] .st-n > .a, .ps-grid[data-dash="true"] .d-n > .a { opacity: 0; }
.ps-grid[data-stock="true"] .st-n > .b, .ps-grid[data-dash="true"] .d-n > .b { opacity: 1; }
/* A lime flash: on at once, fading over 400 ms (the demo's own data-flash). */
.ps-tile::after, .ps-srow::after { content: ''; position: absolute; inset: 0; border-radius: inherit; background: var(--lime-well); opacity: 0; pointer-events: none; }
.ps-grid[data-add="true"] .ps-tile::after, .ps-grid[data-stock="true"] .ps-srow::after { animation: story-flash 400ms var(--fade) backwards; } /* (ends where it rests: nothing left filling) */
@keyframes story-flash { from { opacity: 1; } to { opacity: 0; } }
/* The finger: a touch circle in the middle of what it taps. */
.ps-touch { position: absolute; z-index: 4; left: 50%; top: 50%; width: 38px; height: 38px; margin: -19px 0 0 -19px; border-radius: 50%; background: rgba(12,11,8,.16); border: 1.5px solid rgba(255,255,255,.75); opacity: 0; pointer-events: none; }
.ps-grid[data-tap1="true"] .ps-tile .ps-touch, .ps-grid[data-tap2="true"] .ps-bt .ps-touch { animation: story-touch 600ms ease-out backwards; }
/* 1 · Sell */
.ps-tile { position: relative; border-radius: 14px; padding: 10px 12px; background: var(--paper-fill); border: 1px solid var(--g-edge); box-shadow: inset 0 1px 0 #fff, var(--chip-shadow); display: flex; flex-direction: column; gap: 2px; transition: transform var(--press-dur) ease-out; }
.ps-tile .nm { font-weight: 700; font-size: 14px; }
.ps-tile .meta { font-size: 12px; color: var(--ink2); font-variant-numeric: tabular-nums; }
.ps-grid[data-tap1="true"]:not([data-add="true"]) .ps-tile { transform: scale(.98); }
.ps-cartline { display: flex; justify-content: space-between; gap: 8px; font-size: 13px; padding: 0 2px; opacity: 0; transform: translateY(6px); transition: opacity var(--fade-dur) var(--fade), transform var(--spring-standard-dur) var(--spring-standard); }
.ps-cartline span:first-child { color: var(--ink2); }
.ps-grid[data-add="true"] .ps-cartline { opacity: 1; transform: none; }
/* The button and the toast share one place: when the sale is recorded the button fades out and the toast takes
   its place (so they never overlap, however the toast wraps); afterwards the cart is empty and the button is
   disabled, as in the demo. Each swap fades through: the one arriving waits one fade for the one leaving, so
   their texts are never on top of each other. */
.ps-act { display: grid; align-items: start; }
.ps-act > * { grid-area: 1 / 1; }
.ps-bt { position: relative; justify-self: start; display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 36px; padding: 0 16px; border-radius: 12px; background: var(--dark-fill); color: var(--btn-ink); font-size: 13px; font-weight: 600; white-space: nowrap; box-shadow: var(--btn-edge), var(--btn-shadow); transition: transform var(--press-dur) ease-out, opacity var(--fade-dur) var(--fade); }
.ps-bt svg { width: 16px; height: 16px; }
.ps-grid[data-sale="true"][data-scene="1"] .ps-bt { opacity: 0; }
.ps-grid[data-sale="true"]:not([data-scene="1"]) .ps-bt { opacity: .4; transition-delay: 0s, var(--fade-dur); } /* (transform, opacity) */
.ps-grid[data-sale="true"]:not([data-scene="1"]) .ps-cartline { opacity: 0; }
.ps-grid[data-tap2="true"]:not([data-sale="true"]) .ps-bt { transform: scale(.97); }
.ps-toast { margin: 0; padding: 7px 10px; border-radius: 12px; background: #0C0B08; color: var(--btn-ink); font-size: 12px; line-height: 1.3; font-weight: 600; text-align: center;
  opacity: 0; transform: translateY(8px); transition: opacity var(--fade-dur) var(--fade), transform var(--spring-standard-dur) var(--spring-standard); }
.ps-grid[data-sale="true"][data-scene="1"] .ps-toast { opacity: 1; transform: none; transition-delay: var(--fade-dur); }
/* 2 · Stock */
.ps-srow { position: relative; border-radius: 12px; padding: 8px 8px 10px; display: flex; flex-direction: column; gap: 6px; }
.ps-srow .l { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; column-gap: 8px; font-size: 14px; }
.ps-meter { height: 6px; border-radius: 6px; background: var(--well2); overflow: hidden; }
.ps-meter i { display: block; height: 100%; width: 40%; border-radius: 6px; background: var(--ink2); } /* 24 of the demo's 60 scale; it does not move */
.ps-note { margin: 0; font-size: 12px; line-height: 1.4; color: var(--ink2); }
/* 3 · Live dashboard */
.ps-kpi { display: flex; flex-direction: column; line-height: 1.1; }
.ps-kpi > span { font-size: 12px; color: var(--ink2); }
.ps-kpi b { font-family: var(--display); font-weight: 600; font-size: 30px; letter-spacing: -0.02em; }
.ps-kp2 { display: flex; flex-wrap: wrap; column-gap: 12px; font-size: 12px; color: var(--ink2); }
.ps-kp2 > span { white-space: nowrap; }
.ps-kp2 b { color: var(--ink); font-weight: 700; }
.ps-log { position: relative; margin-top: 4px; border-top: 1px solid var(--line); display: flex; flex-direction: column; }
.ps-log p { margin: 0; display: grid; grid-template-columns: 54px minmax(0, 1fr); gap: 6px; padding-top: 6px; font-size: 12px; line-height: 16px; }
.ps-log time { color: var(--ink2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ps-log .old { transition: transform var(--spring-standard-dur) var(--spring-standard); }
.ps-log .new { opacity: 0; transform: translateY(-8px); transition: opacity var(--fade-dur) var(--fade), transform var(--spring-standard-dur) var(--spring-standard); }
.ps-grid[data-dash="true"] .ps-log .new { opacity: 1; transform: none; }
/* 4 · Weekly report */
.ps-mail { border-radius: 14px; padding: 10px 12px; background: var(--well); box-shadow: var(--recess); display: flex; flex-direction: column; gap: 6px;
  opacity: 0; transform: translateY(10px); transition: opacity var(--fade-dur) var(--fade), transform var(--spring-standard-dur) var(--spring-standard); }
.ps-grid[data-report="true"] .ps-mail { opacity: 1; transform: none; }
.ps-mail .from { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink2); }
.ps-mail .from svg { width: 15px; height: 15px; color: var(--ink); flex-shrink: 0; }
.ps-mail .subj { font-size: 13px; font-weight: 700; }
.ps-week { height: 44px; }
.ps-bars { display: block; width: 100%; height: 100%; }
.ps-bars .bar { fill: var(--well2); }
.ps-bars .bar-on { fill: var(--lime); opacity: 0; transition: opacity var(--fade-dur) var(--fade); }
.ps-grid[data-report="true"] .ps-bars .bar-on { opacity: 1; }
.ps-days { display: flex; gap: 5px; font-size: 12px; color: var(--ink2); }
.ps-days > span { flex: 1; min-width: 0; text-align: center; }
.ps-days .d1 { display: none; }
/* Narrow cards: the days' initials (Mon…Sun do not fit seven across). */
@container story (max-width: 379px) { .ps-days .d3 { display: none; } .ps-days .d1 { display: inline; } }
.ps-tot { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; color: var(--ink2); }
.ps-tot b { color: var(--ink); font-variant-numeric: tabular-nums; }
/* Connectors (the drawings): a hairline, then lime with the dot travelling along it. */
.ps-con { display: none; position: absolute; opacity: .55; transition: opacity 300ms var(--fade); }
.ps-con .ln, .ps-con .lm { position: absolute; left: 0; right: 0; top: 8px; height: 2px; border-radius: 2px; }
.ps-con .ln { background: var(--hairline); }
.ps-con .lm { background: var(--lime); opacity: 0; transition: opacity var(--travel) var(--fade); }
.ps-con .dt { position: absolute; left: -3px; top: 4px; width: 10px; height: 10px; border-radius: 50%; background: var(--lime-ink); box-shadow: 0 0 0 3px var(--lime-well); opacity: 0;
  transition: opacity var(--fade-dur) var(--fade), transform var(--travel) var(--spring-standard); }
.ps-grid[data-tok1="true"] .c12 .dt, .ps-grid[data-stock="true"] .c23 .dt, .ps-grid[data-dash="true"] .c34 .dt { opacity: 1; }
.ps-grid[data-go1="true"] .c12, .ps-grid[data-go2="true"] .c23, .ps-grid[data-go3="true"] .c34,
.ps-grid[data-go1="true"] .c12 .lm, .ps-grid[data-go2="true"] .c23 .lm, .ps-grid[data-go3="true"] .c34 .lm { opacity: 1; }
.ps-grid[data-go1="true"] .c12 .dt, .ps-grid[data-go2="true"] .c23 .dt, .ps-grid[data-go3="true"] .c34 .dt { transform: var(--go); }

/* The phone layout (the default): one card at a time under the four-step track. The cards share one surface
   that stays put (the grid's ::before, in their cell); a scene change fades only their contents, through: the
   old card leaves quickly and the new one waits for it, so two are never on top of each other. */
.story-pos .ps-fit { width: min(100%, 380px); }
.ps-grid { display: grid; gap: 14px; }
.ps-grid::before { content: ''; grid-area: 2 / 1; border-radius: 24px; background: var(--paper-fill); border: 1px solid var(--g-edge); box-shadow: var(--edge), var(--g-shadow); }
@media (prefers-reduced-transparency: reduce) { .ps-grid::before { background: var(--g-solid); } } /* solid, as .glass is */
html.perf-no-depth .ps-grid::before { background: var(--g-solid) !important; }
.ps-card { grid-area: 2 / 1; opacity: 0; transform: translateY(8px); }
.ps-grid .ps-card { background: none; border-color: transparent; box-shadow: none; transition: opacity 120ms linear, transform var(--spring-standard-dur) var(--spring-standard); }
.ps-grid[data-scene="1"] .k1, .ps-grid[data-scene="2"] .k2, .ps-grid[data-scene="3"] .k3, .ps-grid[data-scene="4"] .k4 { opacity: 1; transform: none;
  transition: opacity 200ms var(--fade) 120ms, transform var(--spring-standard-dur) var(--spring-standard) 120ms; }
.ps-track { position: relative; height: 32px; margin-inline: 20px; /* half a step circle and its ring */ container-type: inline-size; } /* the dot moves by thirds of its width (cqi) */
.ps-track .tl, .ps-track .tf { position: absolute; left: 0; right: 0; top: 15px; height: 2px; border-radius: 2px; }
.ps-track .tl { background: var(--hairline); }
.ps-track .tf { background: var(--lime); transform-origin: left; transform: scaleX(0); transition: transform var(--travel) var(--spring-standard); }
.ps-grid[data-go1="true"] .ps-track .tf { transform: scaleX(.3333); }
.ps-grid[data-go2="true"] .ps-track .tf { transform: scaleX(.6667); }
.ps-grid[data-go3="true"] .ps-track .tf { transform: none; }
.ps-track .tn { position: absolute; z-index: 1; top: 0; width: 32px; height: 32px; margin-left: -16px; border-radius: 50%; background: var(--paper-fill); border: 1px solid var(--g-edge); box-shadow: inset 0 1px 0 #fff, var(--chip-shadow); display: inline-flex; align-items: center; justify-content: center; }
.ps-track .tn svg { width: 16px; height: 16px; }
.ps-track .tn[data-n="1"] { left: 0; } .ps-track .tn[data-n="2"] { left: 33.333%; } .ps-track .tn[data-n="3"] { left: 66.667%; } .ps-track .tn[data-n="4"] { left: 100%; }
.ps-track .tn::after { content: ''; position: absolute; inset: -4px; border-radius: 50%; box-shadow: 0 0 0 2px var(--lime); opacity: 0; transition: opacity var(--fade-dur) var(--fade); }
.ps-grid[data-scene="1"] .tn[data-n="1"]::after, .ps-grid[data-scene="2"] .tn[data-n="2"]::after, .ps-grid[data-scene="3"] .tn[data-n="3"]::after, .ps-grid[data-scene="4"] .tn[data-n="4"]::after { opacity: 1; }
.ps-track .tr { position: absolute; z-index: 0; /* under the step circles: it slides in beneath the one it reaches */ left: 0; top: 0; width: 0; height: 100%; transition: transform var(--travel) var(--spring-standard); }
.ps-track .td { position: absolute; left: -5px; top: 11px; width: 10px; height: 10px; border-radius: 50%; background: var(--lime-ink); box-shadow: 0 0 0 3px var(--lime-well); opacity: 0; transition: opacity var(--fade-dur) var(--fade); }
.ps-grid[data-tok1="true"] .ps-track .td { opacity: 1; }
.ps-grid[data-go1="true"] .ps-track .tr { transform: translateX(33.333cqi); }
.ps-grid[data-go2="true"] .ps-track .tr { transform: translateX(66.667cqi); }
.ps-grid[data-go3="true"] .ps-track .tr { transform: translateX(100cqi); }

/* The drawings, from 520 px (not on a short landscape screen, where the phone layout fits better). */
@media not ((orientation: landscape) and (max-height: 500px)) {
  @container story (min-width: 520px) {
    /* The ratio holds even before the cards are zoomed. Short enough to fit one screen, but never zoomed under
       .88 (12 px text at 10.5 px or more on screen, with room for the box's whole-pixel rounding): on a short
       window the demo is taller than the screen rather than unreadable. */
    .story-pos .ps-fit { --fit-w: 540; --fit-h: 504; min-height: 0;
      width: min(calc(var(--fit-w) * 1px), 100%, max(calc(var(--fit-w) * .88px), (100svh - 176px) * var(--fit-w) / var(--fit-h))); aspect-ratio: var(--fit-w) / var(--fit-h); }
    /* (--zoom from site.js; before it, the same fit worked out here where CSS can divide lengths, or 1) */
    .ps-grid { display: block; width: calc(var(--fit-w) * 1px); height: calc(var(--fit-h) * 1px);
      zoom: var(--zoom, min(1, 100cqi / (var(--fit-w) * 1px), max(.88, (100svh - 176px) / (var(--fit-h) * 1px)))); }
    .ps-track { display: none; }
    .ps-card { position: absolute; width: 250px; height: 232px; opacity: 1; transform: none; }
    .ps-card.k1 { left: 0; top: 0; } .ps-card.k2 { left: 290px; top: 0; } .ps-card.k3 { left: 290px; top: 272px; } .ps-card.k4 { left: 0; top: 272px; }
    /* each card its own glass again, and the spotlight's plain 300 ms fade */
    .ps-grid::before { display: none; }
    .ps-grid .ps-card { background: var(--paper-fill); border-color: var(--g-edge); box-shadow: var(--edge), var(--g-shadow); }
    .ps-grid[data-scene] .ps-card { transition: opacity 300ms var(--fade), transform var(--spring-standard-dur) var(--spring-standard); }
    .ps-grid:not([data-done="true"])[data-scene="1"] .ps-card:not(.k1), .ps-grid:not([data-done="true"])[data-scene="2"] .ps-card:not(.k2),
    .ps-grid:not([data-done="true"])[data-scene="3"] .ps-card:not(.k3), .ps-grid:not([data-done="true"])[data-scene="4"] .ps-card:not(.k4) { opacity: .5; }
    /* one line each, as drawn */
    .ps-head { flex-wrap: nowrap; }
    .ps-head strong, .ps-srow .l, .ps-kp2, .ps-mail .subj, .ps-log p, .ps-tot, .ps-tile .meta, .ps-cartline { white-space: nowrap; }
    .ps-srow .l, .ps-kp2 { flex-wrap: nowrap; }
    .ps-log { display: block; height: 45px; } /* 1 px border + two 22 px lines */
    .ps-log p { position: absolute; left: 0; right: 0; top: 0; }
    .ps-grid[data-dash="true"] .ps-log .old { transform: translateY(22px); }
    .ps-con { display: block; }
    .ps-con.c12 { left: 250px; top: 107px; width: 40px; height: 18px; --go: translateX(36px); }
    .ps-con.c23 { left: 406px; top: 232px; width: 18px; height: 40px; --go: translateY(36px); }
    .ps-con.c23 :is(.ln, .lm) { top: 0; bottom: 0; left: 8px; right: auto; width: 2px; height: auto; }
    .ps-con.c23 .dt { top: -3px; left: 4px; }
    .ps-con.c34 { left: 250px; top: 379px; width: 40px; height: 18px; --go: translateX(-36px); }
    .ps-con.c34 .dt { left: 33px; }
  }
  @container story (min-width: 1000px) {
    .story-pos .ps-fit { --fit-w: 1120; --fit-h: 232; }
    .ps-card.k3 { left: 580px; top: 0; } .ps-card.k4 { left: 870px; top: 0; }
    .ps-con.c23 { left: 540px; top: 107px; width: 40px; height: 18px; --go: translateX(36px); }
    .ps-con.c23 :is(.ln, .lm) { left: 0; right: 0; top: 8px; bottom: auto; width: auto; height: 2px; }
    .ps-con.c23 .dt { left: -3px; top: 4px; }
    .ps-con.c34 { left: 830px; top: 107px; --go: translateX(36px); }
    .ps-con.c34 .dt { left: -3px; }
    .story-pos .story-caps { font-size: 24px; }
  }
}
/* The strip: a list on phones, four in a row when there is room. */
.ps-strip { gap: 10px; }
.ps-step { display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: 10px; align-items: center; padding: 10px 12px; border-radius: 14px; font-size: 14px; line-height: 1.35; }
.ps-step small { display: block; color: var(--ink2); font-size: 12px; }
@container story (min-width: 720px) {
  .ps-strip { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px; }
  .ps-step { display: flex; flex-direction: column; align-items: flex-start; padding: 16px; border-radius: 20px; font-family: var(--display); font-weight: 600; font-size: 16px; }
  .ps-step small { font-family: var(--font); font-weight: 400; font-size: 13px; }
  .ps-step .story-n { width: 26px; height: 26px; font-size: 13px; }
}

/* Where the demos sit: the art of a product card or product row, and the scanner's hero. */
.pcard-art.story-art, .prow-art.story-art { height: auto; min-height: 0; display: block; padding: 24px 20px; }
.hero-loop { justify-self: center; width: min(100%, 420px); }
.hero-loop .story-scan { --phone-h: 598px; }
.products .story { --phone-h: 520px; }
/* Two product cards side by side (Home, About): both art boxes take the row's height, so the headings, ticks and
   buttons line up; each demo is centred in its box, and its stage takes the extra height, so the captions and
   buttons line up too. (One column: nothing to share.) */
.products .pcard-art.story-art { flex: 1 0 auto; display: flex; flex-direction: column; justify-content: center; }
.products .story-art > .story { flex: 1 0 auto; justify-content: center; }
.products .story-stage { flex: 1 0 auto; }
/* ...and the two cards share their rows (art, heading, ticks, button), so a heading that wraps in one card (the
   POS line between 961 and 999 px) doesn't push that card's demo out of line with the other's. */
@media (min-width: 961px) {
  @supports (grid-template-rows: subgrid) {
    .products { grid-template-rows: repeat(4, auto); row-gap: 0; }
    .products > .pcard { display: grid; grid-row: span 4; grid-template-rows: subgrid; row-gap: 24px; align-content: start; }
  }
}
@media (max-width: 640px) {
  .story-scan, .hero-loop .story-scan, .products .story { --phone-h: 544px; }
  .pcard-art.story-art, .prow-art.story-art { padding: 20px 12px; }
}
/* Very narrow (a phone at 200% zoom): the row wraps, centred; the strips become lists. */
.story-strip-cap, .ps-step { overflow-wrap: anywhere; }
@container story (max-width: 239px) {
  .story-row { display: flex; flex-wrap: wrap; justify-content: center; row-gap: 8px; }
  .story-scan .story-strip { grid-template-columns: minmax(0, 1fr); }
  .story-scan .story-strip li { flex-direction: row; }
  .story-strip-art { width: 56px; flex-shrink: 0; }
  .story-strip-cap { align-items: flex-start; text-align: left; }
  .ps-step { grid-template-columns: minmax(0, 1fr); }
}
/* A phone on its side: the stage beside its captions and controls, sized to the screen's height, so all of it
   is in view at once. */
@media (orientation: landscape) and (max-height: 500px) {
  .story { display: grid; grid-template-columns: auto minmax(0, 1fr); grid-template-rows: 1fr auto auto 1fr; column-gap: 24px; row-gap: 16px; align-items: center; }
  .story-stage { grid-column: 1; grid-row: 1 / -1; width: auto; }
  .story-caps { grid-column: 2; grid-row: 2; }
  .story-row { grid-column: 2; grid-row: 3; justify-self: center; display: flex; flex-wrap: wrap; justify-content: center; row-gap: 8px; }
  .story-strip { grid-column: 1 / -1; grid-row: 1 / -1; }
  .story[data-mode="strip"] .story-row { grid-column: 1 / -1; grid-row: auto; } /* the strip's label: under it, not over it */
  .story-scan .story-stage > .pv-fit { height: min(var(--phone-h), 100svh - var(--head-clear) - 12px); }
  .story-pos .ps-fit { width: min(380px, 55cqi); }
  /* Under 350 px (a small phone on its side): the card and its track at .88 (270 px tall at full size), so they
     fit under the header with air to spare; 12 px text stays at 10.5 px or more on screen. */
  @media (max-height: 349px) { .story-pos .ps-grid { zoom: .88; } }
}
@media (orientation: landscape) and (max-height: 500px) and (prefers-reduced-motion: reduce) { .story .story-row { grid-column: 1 / -1; grid-row: auto; } }
/* The Motel POS row on the Products page: text above, the demo across the whole row (four cards in a row). */
.prow.prow-stack { grid-template-columns: minmax(0, 1fr); }

/* ───────────── phones and tablets: cheaper to paint and composite (measured with tests/qr-scanner/scripts/frames.mjs) ─────────────
   The phone column of the depth spec: touch screens keep each surface's fill and light edge, swap its shadows for
   two static ones, and get no glow layer, no backdrop blur, no perspective or 3D tilt, and opacity-only reveals.
   Only transform and opacity animate. Nothing here changes what a page says. */
@media (pointer: coarse) {
  /* Two static shadows per surface (blur ≤ 24px on cards, ≤ 40px on sheets) and a one-line light edge. */
  :root {
    --edge: inset 0 1px 0 #fff;
    --g-shadow: 0 1px 2px rgba(12,11,8,.06), 0 12px 24px -16px rgba(12,11,8,.22);
    --sheet-shadow: 0 2px 8px rgba(12,11,8,.06), 0 20px 40px -24px rgba(12,11,8,.30);
    --node-shadow: 0 1px 2px rgba(12,11,8,.06), 0 8px 18px -12px rgba(12,11,8,.22);
    --node-shadow-on: var(--node-shadow);
    --btn-shadow: 0 1px 2px rgba(12,11,8,.25), 0 6px 12px -8px rgba(12,11,8,.40);
  }
  .bg-glow { display: none; }
  .pv-device { box-shadow: inset 0 0 0 1.5px #4A4740, inset 0 0 0 4px #1C1B18, 0 16px 32px -16px rgba(12,11,8,.35); }
  /* The hero shows the phone alone, flat and centred: no perspective, no tilt, no 3D layers. */
  .hero-art, .hero-dash { perspective: none; }
  .hero-art { width: 100%; height: auto; display: flex; justify-content: center; }
  .hero-art .card-dash { display: none; }
  .hero-art .tilt, .hero-dash .tilt { position: static; transform: none; transform-style: flat; will-change: auto; }
  /* Reveals are opacity only; presses are transform only; nodes never lift. */
  .reveal { transform: none; transition: opacity var(--spring-standard-dur) var(--spring-standard); }
  .reveal.lift { transition: transform var(--press-dur) ease-out, opacity var(--spring-standard-dur) var(--spring-standard); }
  .node { transition: transform var(--press-dur) ease-out, opacity var(--fade-dur) var(--fade); }
  button.node:active, .sale-step:active { transform: scale(.985); }
}
/* No backdrop blur on phones and tablets: the header is near-solid instead of blurring what scrolls under it. */
@media (pointer: coarse), (max-width: 960px) {
  .site-header::before { -webkit-backdrop-filter: none; backdrop-filter: none; background: rgba(253,252,248,.94);
    box-shadow: inset 0 1px 0 #fff, 0 1px 2px rgba(12,11,8,.06), 0 12px 24px -16px rgba(12,11,8,.22); }
  /* The scrolled layer only fades its fill in: its own shadows would stack on those two. */
  .site-header::after { box-shadow: none; }
}
/* ───────────── performance mode (?perf=1, assets/js/perf.js) ─────────────
   A frame meter and switches that each turn one effect off, for testing on a real phone. None of these
   classes is ever set for a normal visitor. */
.perf-panel { position: fixed; z-index: 60; left: 8px; bottom: calc(8px + env(safe-area-inset-bottom)); width: min(300px, calc(100vw - 16px)); max-height: 45vh; overflow: auto;
  padding: 8px 10px; border-radius: 14px; background: #0C0B08; color: #F8F5EE; font-size: 12px; line-height: 1.3; }
.perf-meter { display: block; width: 100%; min-height: 32px; padding: 0; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; font-variant-numeric: tabular-nums; }
.perf-fps { font-size: 18px; }
.perf-hint { margin: 4px 0 0; color: rgba(248,245,238,.7); }
.perf-panel[data-state="good"] .perf-fps { color: var(--lime); }
.perf-panel[data-state="poor"] .perf-fps { color: #FF8A7A; }
.perf-switches { list-style: none; margin: 4px 0 8px; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0 6px; }
.perf-switches label { display: flex; align-items: center; gap: 6px; min-height: 32px; cursor: pointer; }
.perf-switches input { width: 18px; height: 18px; margin: 0; flex-shrink: 0; accent-color: var(--lime); }
.perf-btn { min-height: 32px; padding: 0 12px; border: 0; border-radius: 10px; background: rgba(248,245,238,.14); color: inherit; font: inherit; font-weight: 700; cursor: pointer; }
.perf-btn[disabled] { opacity: .5; }
.perf-row { display: flex; gap: 8px; flex-wrap: wrap; }
.perf-result { margin: 8px 0 0; color: rgba(248,245,238,.8); }
html.perf-no-dots body { background-image: none; }
html.perf-no-dots .bg-glow { display: none; }
html.perf-no-shadows *, html.perf-no-shadows *::before, html.perf-no-shadows *::after { box-shadow: none !important; }
html.perf-no-depth .glass, html.perf-no-depth .node, html.perf-no-depth .sys-node, html.perf-no-depth .rm-group, html.perf-no-depth .rm-leaf, html.perf-no-depth .pos-item { background: var(--g-solid) !important; }
html.perf-no-depth .btn-primary, html.perf-no-depth .rm-center, html.perf-no-depth .pos-tabs button[aria-selected="true"] { background: var(--btn-bg) !important; }
html.perf-no-depth *, html.perf-no-depth *::before, html.perf-no-depth *::after { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; perspective: none !important; transform-style: flat !important; }
html.perf-no-depth .tilt { transform: none !important; }
html.perf-no-reveal .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
html.perf-no-tiles .sample { background: var(--g-solid) !important; border-color: transparent !important; box-shadow: none !important; transition: none !important; }
html.perf-no-tiles .sample .code-face { box-shadow: none !important; transition: none !important; }
html.perf-no-codes .sample .code-face svg, html.perf-no-codes .sample .ean-digits { visibility: hidden; }
html.perf-no-header .site-header::before, html.perf-no-header .site-header::after { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; background: var(--g-solid) !important; box-shadow: none !important; transition: none !important; }

/* The last sections of a page (the closing row, the credits, the footer): the browser skips their layout and
   paint until they near the screen. Only at the end, because until a section renders it takes this guessed
   height, and when it renders everything after it moves: a demo, or a link's target, below a skipped section
   would jump (Safari has no scroll anchoring to hide it). Never around a demo. */
.cv { content-visibility: auto; contain-intrinsic-size: auto 600px; }
