/* The app's one stylesheet: the tokens, the three shells (Operator, staff,
   storefront), and every component more than one screen uses.

   Row 7.7 (card #53): a component lives HERE, once. A screen's own stylesheet
   holds only what is unique to that screen, and never redefines a component
   this file owns — tests/unit/checks/css.php fails when one does, and when a
   font-size, spacing or radius is a literal rather than a token.
   docs/OPERATOR-LOOK.md is the spec. */

:root {
  /* Colour. Row 7.10 (Cameron, 2026-10-19: "a modern professional look"): a cooler
     neutral page, white cards that sit on it with a soft shadow, and one blue. */
  --bg: #f4f5f7; --surface: #ffffff; --ink: #111827; --muted: #5b6472; --line: #e3e6eb;
  --line-strong: #8a93a2;  /* a control's edge: 3.1:1 on white, the floor for a border */
  --brand: #2b59c3; --brand-ink: #ffffff; --accent: #eef3ff; --field: #ffffff;
  --chart: #2b59c3;  /* row 19.10: a chart's columns; the dataviz validator's lightness band and 3:1 on --surface, both modes */
  --ok-bg: #e7f5ec; --ok-ink: #1c5e33; --error-bg: #fcebea; --error-ink: #8a1f17;
  --info-bg: #eef3ff; --info-ink: #1f3f8a; --viewing-bg: #f39c12; --viewing-ink: #1d1300;
  --shadow: 0 1px 2px rgb(16 24 40 / .06), 0 1px 3px rgb(16 24 40 / .08);
  --shadow-raised: 0 8px 24px rgb(16 24 40 / .14);

  /* Type. Five sizes, and no others: every font-size in public/assets/*.css is one of these. */
  --text-xs: .8125rem;   /* 13px: table meta, status pills, counts */
  --text-s: .875rem;     /* 14px: hints, sub-menu items, table cells */
  --text-m: 1rem;        /* body, fields, buttons */
  --text-l: 1.25rem;     /* card and section headings (h2) */
  --text-xl: 1.625rem;   /* screen title (h1), tile counts */
  --weight-strong: 600;  /* h1, h2, current menu item, primary button */
  --weight-label: 500;   /* labels, table headings, top menu items */

  /* Spacing. Every margin, gap and padding is one of these. */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px; --space-6: 32px;

  /* Shape and control height. */
  --radius: 12px;           /* cards */
  --radius-control: 8px;    /* inputs, buttons, tabs, menu items */
  --radius-pill: 999px;     /* status pills and count badges */
  --control-h: 44px;        /* buttons, inputs, selects: the tap minimum */
  --control-h-s: 36px;      /* button.small, table row actions (desktop only) */

  /* The Operator's frame. Row 7.10 keeps row 7.6's shape — a dark sidebar under a
     dark bar, the current group open — and modernises it: wider, rounded items,
     a soft highlight instead of wp-admin's blue block and arrow.
     dark-ok: the frame is the SAME in light and dark; the screens inside it keep
     the tokens above and follow the device. */
  --bar-h: 52px;
  --nav-w: 240px;
  --nav-bg: #111827;
  --nav-ink: #e5e7eb;
  --nav-icon: #9ca3af;
  --nav-hover-ink: #ffffff;
  --nav-hover-bg: rgb(255 255 255 / .07);
  --nav-open-bg: #1f2937;               /* the account panel and the folded flyouts */
  --nav-sub-ink: #c3c8d0;
  --nav-heading-ink: #9ca3af;
  --nav-current-bg: rgb(255 255 255 / .12);
  --nav-current-ink: #ffffff;
  --nav-mark: #7aa7ff;                  /* the bar beside the current item */
  --nav-divider: #1f2937;               /* row 7.15: the line between the store's groups and the Kitt's */
  --nav-arrow: var(--bg);

  color-scheme: light dark;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0d1117; --surface: #161b22; --ink: #e6edf3; --muted: #9aa5b4; --line: #2a313c; --line-strong: #5c6a80;
    --brand: #7aa7ff; --brand-ink: #0b1220; --accent: #1b2538; --field: #0d1117; --chart: #5b8def;
    --ok-bg: #173323; --ok-ink: #a6e0bb; --error-bg: #3a1714; --error-ink: #f3b1aa;
    --info-bg: #182638; --info-ink: #b5d0f0;
    --shadow: 0 0 0 1px rgb(255 255 255 / .02); --shadow-raised: 0 8px 24px rgb(0 0 0 / .5);
  }
}
/* Row 7.16 (card #259), the Look switch: Dark chosen in the account menu is these same dark tokens whatever the
   device says. Light needs nothing more, as the block above leaves data-theme="light" out. The browser's own
   controls (scroll bars, date pickers) follow the choice too. Not on paper: a printed order sheet stays black on
   white (orders.css). tests/unit/checks/look.php holds the two dark blocks to the same tokens, here and in every
   other stylesheet with a dark block. */
@media not print {
  :root[data-theme="dark"] {
    --bg: #0d1117; --surface: #161b22; --ink: #e6edf3; --muted: #9aa5b4; --line: #2a313c; --line-strong: #5c6a80;
    --brand: #7aa7ff; --brand-ink: #0b1220; --accent: #1b2538; --field: #0d1117; --chart: #5b8def;
    --ok-bg: #173323; --ok-ink: #a6e0bb; --error-bg: #3a1714; --error-ink: #f3b1aa;
    --info-bg: #182638; --info-ink: #b5d0f0;
    --shadow: 0 0 0 1px rgb(255 255 255 / .02); --shadow-raised: 0 8px 24px rgb(0 0 0 / .5);
    color-scheme: dark;
  }
}
:root[data-theme="light"] { color-scheme: light; }

/* Row 18.7 (card #302): a message's picture with a dark copy shows the copy her Look calls for, as the tokens
   above do: the first copy unless she chose Dark, or Match this device on a dark device. The other is
   display: none, and loading="lazy" keeps the browser from fetching it. */
.message-picture .message-picture-dark { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .message-picture.has-dark .message-picture-light { display: none; }
  :root:not([data-theme="light"]) .message-picture.has-dark .message-picture-dark { display: block; }
}
@media not print {
  :root[data-theme="dark"] .message-picture.has-dark .message-picture-light { display: none; }
  :root[data-theme="dark"] .message-picture.has-dark .message-picture-dark { display: block; }
}

/* ---- base ---------------------------------------------------------------- */

* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--ink); font: var(--text-m)/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased; }
a { color: var(--brand); text-underline-offset: 2px; }
h1 { font-size: var(--text-xl); font-weight: var(--weight-strong); letter-spacing: -.015em; line-height: 1.2; margin: 0 0 var(--space-3); }
h2 { font-size: var(--text-l); font-weight: var(--weight-strong); letter-spacing: -.01em; line-height: 1.3; margin: 0 0 var(--space-3); }
h3 { font-size: var(--text-m); font-weight: var(--weight-strong); margin: 0 0 var(--space-2); }
p { margin: 0 0 var(--space-3); }
.skip { position: absolute; left: -9999px; }
.skip:focus { left: var(--space-2); top: var(--space-2); background: var(--surface); padding: var(--space-2); z-index: 10; }
.muted, .hint { color: var(--muted); }
.hint { margin: var(--space-1) 0 0; font-size: var(--text-s); }
.small { font-size: var(--text-xs); }
.crumbs { margin: 0 0 var(--space-1); font-size: var(--text-s); color: var(--muted); }
.crumbs a { color: var(--muted); }
.lead { max-width: 760px; color: var(--muted); }
.pre, .prewrap { white-space: pre-wrap; overflow-wrap: anywhere; }
/* A placeholder or shortcode is one long word: it breaks rather than run off a phone at a larger text size (card #167). */
code { overflow-wrap: anywhere; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---- shells ------------------------------------------------------------- */

/* The staff shell's bar and the Operator's are the frame's (below). */
.topbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); padding: var(--space-3) var(--space-4); background: var(--brand); color: var(--brand-ink); }
.topbar a, .topbar .button-link { color: var(--brand-ink); }
.topbar .store-name { font-weight: var(--weight-strong); text-decoration: none; margin-right: auto; }
.topbar .who { font-size: var(--text-s); }
.topbar .button-quiet { border: 1px solid currentColor; background: transparent; color: var(--brand-ink); }
.site-header { padding: var(--space-4); background: var(--surface); border-bottom: 1px solid var(--line); }
.site-header .store-name { font-weight: var(--weight-strong); font-size: var(--text-l); text-decoration: none; color: var(--ink); }

/* minmax(0, 1fr), not 1fr: a 1fr track will not shrink below its content, which is what
   made the whole page scroll sideways at 835 px (docs/OPERATOR-LOOK.md, Overview 2). */
.frame { display: grid; grid-template-columns: var(--nav-w) minmax(0, 1fr); min-height: calc(100dvh - var(--bar-seen, var(--bar-h))); }
/* Centred in the space beside the menu (Cameron, 2026-10-31: "We want the site to look good on wide screens"): past
   1440px the screen sits in the middle with even space either side, not against the menu with the rest left empty. */
main { padding: var(--space-5) var(--space-6); max-width: 1440px; width: 100%; min-width: 0; margin-inline: auto; }
.shell-storefront main, main.narrow { margin: 0 auto; max-width: 720px; }

/* ---- the Operator's frame: a dark bar and sidebar, in both modes -------- */

/* One row at the standard text size, at every width. When her words are larger than the bar has room for (the
   app's sheet sets a minimum text size from the phone's, docs/APP-API.md "The sheet"; a computer's text size does
   the same), the bar wraps onto a second row rather than cut off its right end, where the account menu and Sign
   out are (card #167). Every control keeps its own width (flex: none); the store's name and the search box start
   from their smallest (flex-basis 0 and 9rem), because a flex row breaks a line by each item's starting width
   before anything shrinks, and then grow into what the row leaves. A row that wrapped keeps to the right, so the
   account menu's panel opens inside the screen. The sidebar sticks under the bar's measured height, --bar-seen
   (app-menu.js), so a bar of two rows never covers it. */
.shell-operator .topbar, .shell-staff .topbar {
  position: sticky; top: 0; z-index: 40;
  flex-wrap: wrap; justify-content: flex-end; gap: var(--space-1) var(--space-2); padding: 0 var(--space-4) 0 var(--space-3); min-height: var(--bar-h);
  background: var(--nav-bg); color: var(--nav-ink); font-size: var(--text-s);
  border-bottom: 1px solid rgb(255 255 255 / .08);
}
/* :where(), so the name's and the search box's own flex below win over it. */
:where(.shell-operator .topbar, .shell-staff .topbar) > * { flex: none; }
.shell-operator .topbar a, .shell-staff .topbar a,
.shell-operator .topbar .button-link, .shell-staff .topbar .button-link { color: var(--nav-ink); }
.shell-operator .topbar a:hover, .shell-staff .topbar a:hover,
.shell-operator .topbar a:focus-visible, .shell-staff .topbar a:focus-visible,
.shell-operator .topbar .button-link:hover, .shell-staff .topbar .button-link:hover { color: var(--nav-hover-ink); }
/* The bar is dark in BOTH modes, so a control on it takes the bar's own ink, never --brand-ink,
   which is near-black in dark mode and would leave the Test Mode pill unreadable. */
.shell-operator .topbar .button-quiet, .shell-staff .topbar .button-quiet { border-color: rgb(255 255 255 / .28); color: var(--nav-ink);
  min-height: var(--control-h-s); font-size: var(--text-xs); border-radius: var(--radius-pill); padding: 0 var(--space-3); }
.shell-operator .topbar .button-quiet:hover, .shell-staff .topbar .button-quiet:hover { border-color: var(--nav-hover-ink); color: var(--nav-hover-ink); background: var(--nav-hover-bg); }
.shell-operator .topbar :focus-visible, .shell-staff .topbar :focus-visible { outline-color: var(--nav-mark); }

/* The store's name, with its first letter as a badge, as a modern admin's workspace switcher. */
.shell-operator .topbar .store-name, .shell-staff .topbar .store-name { display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: var(--control-h); font-size: var(--text-m); font-weight: var(--weight-strong); }
.shell-operator .topbar .store-name::before { content: attr(data-initial); display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 30px; height: 30px; border-radius: var(--radius-control); background: var(--nav-mark); color: var(--nav-bg);
  font-size: var(--text-s); font-weight: 700; }
.shell-operator .topbar .store-name[data-initial=""]::before { display: none; }

/* Visit site and Report a problem: quiet links with an icon, the words hidden below 480px. */
.topbar .visit-site, .topbar .report-problem { display: inline-flex; align-items: center; gap: var(--space-2); text-decoration: none;
  min-height: var(--control-h-s); padding-inline: var(--space-3); border-radius: var(--radius-pill); }
.shell-operator .topbar .visit-site, .shell-staff .topbar .visit-site,
.shell-operator .topbar .report-problem { color: var(--nav-sub-ink); font-size: var(--text-xs); }
.shell-operator .topbar .visit-site:hover, .shell-staff .topbar .visit-site:hover,
.shell-operator .topbar .report-problem:hover { color: var(--nav-hover-ink); background: var(--nav-hover-bg); }
@media (max-width: 480px) {
  .topbar .visit-site-words { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
}
/* A 360 px phone keeps the bar on one row at the standard text size: Menu, the store's badge, Visit site, the bell,
   Test Mode and the account menu, 4 px apart. Row 18.6 (card #299): to make room for the bell, the Operator bar's Menu
   shows its ☰ alone here, its word kept for a screen reader and a voice command, as Visit site's is below 480 px. */
@media (max-width: 400px) {
  .shell-operator .topbar, .shell-staff .topbar { column-gap: var(--space-1); padding-inline: var(--space-2); }
  .shell-operator .topbar .menu-button-words { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
}
.account-panel .report-problem-menu { display: none; }
/* Row 15.1 (card #172): Help, just before Report a problem and the same quiet link, in the same window. As with Report a
   problem, the account menu's copy shows only below 768 px, where the bar's is hidden, so exactly one is on screen. */
.topbar .help-link { display: inline-flex; align-items: center; gap: var(--space-2); text-decoration: none;
  min-height: var(--control-h-s); padding-inline: var(--space-3); border-radius: var(--radius-pill); }
.shell-operator .topbar .help-link { color: var(--nav-sub-ink); font-size: var(--text-xs); }
.shell-operator .topbar .help-link:hover, .shell-operator .topbar .help-link[aria-current="page"] { color: var(--nav-hover-ink); background: var(--nav-hover-bg); }
.account-panel .help-link-menu { display: none; }
/* Row 15.2 (card #173): Help for this page, a panel over the screen (public/assets/js/help.js). A side sheet from
   768 px, the whole screen on a phone; the article inside brings help.css with it. */
.help-panel { margin: 0 0 0 auto; padding: 0; width: min(36rem, 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh;
  border: 0; border-left: 1px solid var(--line); background: var(--bg); color: var(--ink); box-shadow: var(--shadow-raised); }
.help-panel[open] { display: flex; flex-direction: column; }
.help-panel::backdrop { background: rgb(0 0 0 / .45); }
/* Rows 17.1 and 17.2 (cards #179, #180): a question asked before a text or an email goes, in a dialog its script draws. */
.ask-dialog { width: min(32rem, calc(100vw - 2 * var(--space-4))); border: 0; border-radius: var(--radius); padding: var(--space-5);
  background: var(--surface); color: var(--ink); box-shadow: var(--shadow-raised); }
.ask-dialog::backdrop { background: rgb(0 0 0 / .45); }
.ask-dialog h2 { margin-top: 0; }
/* Card #178 (OPERATOR-LOOK.md, "Settings form, unsaved change"): a Save row marked data-save-bar rides along the bottom
   of the window once its form has changed (site-pages.js), so Save is in reach on a long screen like Header. Below the
   sidebar and its flyouts (30), the phone menu (35) and the top bar (40), so no menu item is ever under it. */
.save-bar.is-floating { position: sticky; bottom: 0; z-index: 20; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  margin: 0; padding: var(--space-3) var(--space-4); background: var(--surface); border-top: 1px solid var(--line); box-shadow: var(--shadow-raised); }
@media (prefers-reduced-motion: no-preference) { .save-bar.is-floating { animation: fmb-save-bar 200ms ease-out; } }
/* Card #178: what Save and visit site says beside its buttons, once it has posted the form into a new tab. */
.save-status { color: var(--muted); }
.save-status:empty { display: none; }
@keyframes fmb-save-bar { from { transform: translateY(100%); } to { transform: translateY(0); } }
.help-panel-bar { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-2) var(--space-1) var(--space-4);
  background: var(--nav-bg); color: var(--nav-ink); }
.help-panel-name { margin: 0 auto 0 0; font-weight: var(--weight-strong); }
.help-panel-bar .help-panel-all { display: inline-flex; align-items: center; min-height: 48px; padding: 0 var(--space-3); color: var(--nav-ink);
  border-radius: var(--radius-control); }
.help-panel-close { min-width: 48px; min-height: 48px; padding: 0 var(--space-3); border: 1px solid rgb(255 255 255 / .25); border-radius: var(--radius-control);
  background: transparent; color: var(--nav-ink); font-size: var(--text-m); } /* dark-ok: the panel's bar is the top bar's colour */
.help-panel-bar :focus-visible { outline-color: var(--nav-mark); }
.help-panel-body { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: var(--space-4) var(--space-5) var(--space-6); }
.help-panel-body .help-article h1 { font-size: var(--text-xl); }
.help-panel-wait { color: var(--muted); }
body.help-open { overflow: hidden; }

/* The name gives way first, never below 3em (its badge and a letter), so the bar wraps only when even that leaves
   no room. From nothing (flex-basis 0) it grows into its row before the search box does (its grow of 1000 to the
   box's 1), up to its own full width (max-content) and no wider, so a long name is cut short with an ellipsis
   rather than push the account menu onto a second row: on a 1024 px computer, "Nourish Together Freezer Meals"
   with "Stephanie Rodriguez-Hernandez" in the account menu had sent her name to a row of its own (card #164's
   review). */
.shell-operator .topbar .store-name, .shell-staff .topbar .store-name { flex: 1000 1 0; min-width: 3em; margin-right: auto; max-width: max-content;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.menu-button { display: none; align-items: center; gap: var(--space-2); background: transparent; color: var(--nav-ink);
  border: 0; min-height: var(--control-h); min-width: var(--control-h); padding: 0 var(--space-2); font-size: var(--text-s); }
.menu-button:hover, .menu-button:focus-visible { color: var(--nav-hover-ink); }

/* Row 18.6 (card #299): the bell, the Messages of the Day she has not acknowledged, its count a pill on its corner.
   Its panel opens under it as the account menu's does, on the bar's dark colours in both modes. */
.bell { position: relative; }
.bell > summary { position: relative; display: flex; align-items: center; justify-content: center; cursor: pointer; list-style: none;
  min-height: var(--control-h); min-width: var(--control-h); padding: 0 var(--space-1); border-radius: var(--radius-pill); color: var(--nav-sub-ink); }
.bell > summary::-webkit-details-marker { display: none; }
.bell > summary:hover, .bell > summary:focus-visible, .bell[open] > summary { color: var(--nav-hover-ink); background: var(--nav-hover-bg); }
.bell.has-count > summary { color: var(--nav-ink); }
.bell-count { position: absolute; top: var(--space-1); right: 0; min-width: 1.4em; padding: 0 var(--space-1); border-radius: var(--radius-pill);
  background: var(--nav-mark); color: var(--nav-bg); font-size: var(--text-xs); font-weight: var(--weight-strong); line-height: 1.4; text-align: center; }
.bell-panel { position: absolute; right: 0; top: calc(100% + var(--space-1)); z-index: 50; display: flex; flex-direction: column; gap: var(--space-2);
  width: min(22rem, calc(100vw - 2 * var(--space-3))); max-height: calc(100dvh - var(--bar-h) - var(--space-4)); overflow-y: auto; padding: var(--space-3);
  background: var(--nav-open-bg); color: var(--nav-ink); border: 1px solid rgb(255 255 255 / .08); border-radius: var(--radius); box-shadow: var(--shadow-raised); }
.bell-head { margin: 0; font-weight: var(--weight-strong); }
.bell-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.bell-list li { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); padding: var(--space-2) 0; border-top: 1px solid rgb(255 255 255 / .08); }
.bell-title { font-weight: var(--weight-label); overflow-wrap: anywhere; }
.bell-from, .bell-note { color: var(--nav-sub-ink); font-size: var(--text-xs); margin: 0; }
.shell-operator .topbar .bell-list .button-quiet { min-height: var(--control-h); }
.bell-all { display: flex; align-items: center; min-height: var(--control-h); padding: 0 var(--space-2); border-radius: var(--radius-control); }
.bell-all:hover { background: var(--nav-hover-bg); }
/* On a phone the bell sits mid-bar, so its panel spans the window under the bar instead of hanging off its left edge. */
@media (max-width: 767px) {
  .bell-panel { position: fixed; left: var(--space-3); right: var(--space-3); top: calc(var(--bar-seen, var(--bar-h)) + var(--space-1)); width: auto; }
}
@media print { .bell { display: none; } }
.account-menu { position: relative; }
.account-menu > summary { display: flex; align-items: center; gap: var(--space-2); cursor: pointer; list-style: none;
  min-height: var(--control-h); padding: 0 var(--space-3); white-space: nowrap; border-radius: var(--radius-pill); }
.account-menu > summary::-webkit-details-marker { display: none; }
.account-menu > summary:hover, .account-menu > summary:focus-visible { color: var(--nav-hover-ink); background: var(--nav-hover-bg); }
.account-panel { position: absolute; right: 0; top: calc(100% + var(--space-1)); z-index: 50; display: flex; flex-direction: column; align-items: stretch;
  gap: 0; min-width: 13rem; padding: var(--space-2); background: var(--nav-open-bg); color: var(--nav-ink);
  border: 1px solid rgb(255 255 255 / .08); border-radius: var(--radius); box-shadow: var(--shadow-raised); }
.account-panel a, .account-panel .button-link { color: var(--nav-ink); text-decoration: none; min-height: var(--control-h); display: flex; align-items: center;
  justify-content: flex-start; padding: 0 var(--space-3); border-radius: var(--radius-control); width: 100%; }
.account-panel form { display: flex; }
.account-panel a:hover, .account-panel .button-link:hover { color: var(--nav-hover-ink); background: var(--nav-hover-bg); }
/* Row 7.16 (card #259): the Look switch, three rows of the account menu under a small heading; the one in use is
   marked as the menu marks the screen she is on. */
.account-panel .look-form { display: block; margin: var(--space-1) 0; padding: var(--space-1) 0;
  border-top: 1px solid rgb(255 255 255 / .08); border-bottom: 1px solid rgb(255 255 255 / .08); }
.look-label { margin: var(--space-1) var(--space-3); font-size: var(--text-xs); font-weight: var(--weight-strong); letter-spacing: .06em;
  text-transform: uppercase; color: var(--nav-heading-ink); }
.look-choices { display: flex; flex-direction: column; }
.look-choices button { display: flex; align-items: center; justify-content: flex-start; width: 100%; min-height: var(--control-h); padding: 0 var(--space-3); border: 0;
  border-radius: var(--radius-control); background: transparent; color: var(--nav-ink); font: inherit; text-align: left; cursor: pointer; box-shadow: none; }
.look-choices button:hover { color: var(--nav-hover-ink); background: var(--nav-hover-bg); }
.look-choices button[aria-pressed="true"] { color: var(--nav-current-ink); background: var(--nav-current-bg); box-shadow: inset 3px 0 0 var(--nav-mark);
  font-weight: var(--weight-strong); }

/* The sidebar. */
.app-menu { background: var(--nav-bg); color: var(--nav-ink); padding: var(--space-3) var(--space-2) var(--space-6);
  position: sticky; top: var(--bar-seen, var(--bar-h)); align-self: start; height: calc(100dvh - var(--bar-seen, var(--bar-h))); overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: rgb(255 255 255 / .2) transparent; }
.app-menu ul { list-style: none; margin: 0; padding: 0; }
.app-menu > .menu-top > li + li { margin-top: var(--space-1); }
.app-menu .menu-row { display: flex; align-items: stretch; border-radius: var(--radius-control); }
.app-menu a { display: flex; align-items: center; gap: var(--space-3); text-decoration: none; color: var(--nav-ink); border-radius: var(--radius-control); }
.app-menu a[data-level="top"], .app-menu a[data-level="admin"], .app-menu a[data-level="kitt"] { flex: 1; min-width: 0; min-height: 38px; padding: 0 var(--space-2) 0 var(--space-3); font-size: var(--text-s); font-weight: var(--weight-label); }
.app-menu .menu-icon { flex: none; color: var(--nav-icon); }
.app-menu .menu-words { min-width: 0; overflow-wrap: normal; word-break: keep-all; }
/* Row 7.15 (card #258), as the PurrTeach design draws the menu: the › of a group that opens, inside its link and shown
   only from 1024 px (below), and a line between the store's own groups and the Business Kitt's, Course and Admin. */
.app-menu .menu-chevron { display: none; flex: none; margin-left: auto; color: var(--nav-icon); }
.app-menu > .menu-top > li.menu-divider { height: 1px; margin: var(--space-3) var(--space-1); background: var(--nav-divider); }
.app-menu > .menu-top > li.menu-divider + li { margin-top: 0; }
/* Row 7.18 (card #298): Help, the sidebar's last item for every Operator, set a little apart from the groups above it. */
.app-menu > .menu-top > li.menu-help { margin-top: var(--space-4); }
.app-menu a[data-level="help"] { flex: 1; min-width: 0; min-height: 38px; padding: 0 var(--space-2) 0 var(--space-3); font-size: var(--text-s); font-weight: var(--weight-label); }
/* Course › Questions waiting's count (row 7.18), a small pill after its words; none waiting reads quieter. */
.app-menu .menu-count { margin-left: auto; min-width: 1.5em; padding: 0 var(--space-1); border-radius: var(--radius-pill); background: var(--nav-mark);
  color: var(--nav-bg); font-size: var(--text-xs); font-weight: var(--weight-strong); line-height: 1.5; text-align: center; }
.app-menu .menu-count.is-none { background: var(--nav-hover-bg); color: var(--nav-sub-ink); }
.app-menu .menu-row:hover, .app-menu a[data-level="sub"]:hover, .app-menu a[data-level="beyond"]:hover { background: var(--nav-hover-bg); }
.app-menu a:hover, .app-menu a:focus-visible, .app-menu .menu-toggle:hover, .app-menu .menu-toggle:focus-visible { color: var(--nav-hover-ink); }
.app-menu a:hover .menu-icon, .app-menu a:focus-visible .menu-icon { color: var(--nav-hover-ink); }
.app-menu :focus-visible { outline-color: var(--nav-mark); outline-offset: -2px; }

/* The chevron is its own button, so Enter on the label navigates and Enter here opens the group. */
.app-menu .menu-toggle { flex: none; width: 36px; min-height: 38px; padding: 0; background: transparent; border: 0;
  color: var(--nav-icon); border-radius: var(--radius-control); }
.app-menu .menu-toggle svg { transition: transform 150ms ease-out; }
@media (prefers-reduced-motion: reduce) { .app-menu .menu-toggle svg { transition: none; } }
.app-menu li.is-open > .menu-row > .menu-toggle svg { transform: rotate(90deg); }

/* Only the open groups' items show. Every <a> stays in the HTML: tests/parity/lib/menu.py reads it.
   The sub-list hangs from a thin guide line under its group's icon. */
.app-menu .menu-sub { display: none; margin: var(--space-1) 0 var(--space-2) var(--space-5); padding-left: var(--space-2);
  border-left: 1px solid rgb(255 255 255 / .12); }
.app-menu li.is-open > .menu-sub { display: block; }
.app-menu a[data-level="sub"], .app-menu a[data-level="beyond"] { min-height: 32px; padding: 0 var(--space-3); font-size: var(--text-s); color: var(--nav-sub-ink); }
/* My Site Settings' eighteen items under four small headings (row 7.10). Not links: the menu's contract is its <a>s. */
.app-menu .menu-heading { margin: var(--space-3) 0 var(--space-1); padding: 0 var(--space-3); font-size: var(--text-xs); font-weight: var(--weight-strong);
  letter-spacing: .06em; text-transform: uppercase; color: var(--nav-heading-ink); }
.app-menu .menu-heading:first-child { margin-top: var(--space-1); }

/* The group a screen is in: its label bright; the item itself: a soft highlight and a mark. */
.app-menu li.is-current > .menu-row, .app-menu a[data-level="top"][aria-current="page"], .app-menu a[data-level="admin"][aria-current="page"], .app-menu a[data-level="kitt"][aria-current="page"] { background: var(--nav-current-bg); }
.app-menu li.is-here > .menu-row > a[data-level="top"], .app-menu li.is-here > .menu-row > a[data-level="admin"], .app-menu li.is-here > .menu-row > a[data-level="kitt"], .app-menu li.is-current > .menu-row > a[data-level="top"], .app-menu li.is-current > .menu-row > a[data-level="admin"], .app-menu li.is-current > .menu-row > a[data-level="kitt"] { color: var(--nav-current-ink); font-weight: var(--weight-strong); }
.app-menu li.is-here > .menu-row > a[data-level="top"] .menu-icon, .app-menu li.is-here > .menu-row > a[data-level="admin"] .menu-icon, .app-menu li.is-here > .menu-row > a[data-level="kitt"] .menu-icon, .app-menu li.is-current > .menu-row > a[data-level="top"] .menu-icon, .app-menu li.is-current > .menu-row > a[data-level="admin"] .menu-icon , .app-menu li.is-current > .menu-row > a[data-level="kitt"] .menu-icon { color: var(--nav-mark); }
.app-menu a[aria-current="page"] { color: var(--nav-current-ink); font-weight: var(--weight-strong); }
.app-menu a[aria-current="page"] .menu-icon { color: var(--nav-mark); }
.app-menu .menu-sub a[aria-current="page"] { background: var(--nav-current-bg); box-shadow: inset 3px 0 0 var(--nav-mark); }

/* From 1024 px the sidebar shows the five groups alone (row 7.10): the current group's screens are the
   tabs over the screen (.screen-tabs), and each group's list opens as a flyout on hover or focus, so
   every screen stays one press from anywhere and no visible tab shares a name with a visible link. */
@media (min-width: 1024px) {
  /* Above the screen, so a flyout opens over the page's cards, never under them. */
  .shell-operator .app-menu { overflow: visible; z-index: 30; }
  .shell-operator .app-menu > .menu-top > li { position: relative; }
  .shell-operator .app-menu .menu-toggle { display: none; }
  .shell-operator .app-menu .menu-chevron { display: block; }
  .shell-operator .app-menu li.is-here > .menu-row .menu-chevron, .shell-operator .app-menu a:hover .menu-chevron { color: var(--nav-current-ink); }
  .shell-operator .app-menu a[data-level="top"], .shell-operator .app-menu a[data-level="admin"], .shell-operator .app-menu a[data-level="kitt"] { padding-right: var(--space-3); }
  .shell-operator .app-menu li.is-here > .menu-row { background: var(--nav-current-bg); box-shadow: inset 3px 0 0 var(--nav-mark); }
  /* The flyout fits the window as the rail's does (card #214; Admin ran past the bottom, Cameron, 2026-11-01):
     app-menu.js's --flyout-lift raises one taller than the room below its name, never over the top bar, and
     --flyout-room caps what is left, which scrolls. */
  .shell-operator .app-menu .menu-sub { display: block; position: absolute; left: calc(100% + var(--space-2)); top: calc(-1 * var(--space-2) - var(--flyout-lift, 0px)); z-index: 30;
    width: 240px; margin: 0; padding: var(--space-2); border: 1px solid rgb(255 255 255 / .08); border-radius: var(--radius);
    max-height: var(--flyout-room, none); overflow-y: auto; overscroll-behavior: contain;
    background: var(--nav-open-bg); box-shadow: var(--shadow-raised); visibility: hidden; opacity: 0; transition: opacity 100ms linear, visibility 0s 100ms; }
  /* The pointer crosses the gap to the flyout without leaving the group. The bridge hangs off the group's row, since the
     flyout's scrolling would clip it, spans the flyout's height (--flyout-span) and is there only while the group is
     open, as it was when it hung off the hidden flyout. */
  .shell-operator .app-menu > .menu-top > li:not(.is-here):hover::after,
  .shell-operator .app-menu > .menu-top > li:not(.is-here):focus-within::after { content: ""; position: absolute; left: 100%;
    top: calc(-1 * var(--space-2) - var(--flyout-lift, 0px)); height: var(--flyout-span, calc(100% + var(--space-4))); width: var(--space-2); }
  @media (prefers-reduced-motion: reduce) { .shell-operator .app-menu .menu-sub { transition: none; } }
  .shell-operator .app-menu > .menu-top > li:hover > .menu-sub,
  .shell-operator .app-menu > .menu-top > li:focus-within > .menu-sub { visibility: visible; opacity: 1; transition: opacity 100ms linear; }
  /* My Site Settings' flyout is long: two columns of sets. */
  .shell-operator .app-menu #menu-group-my-site-settings { width: 460px; columns: 2; column-gap: var(--space-2); }
  .shell-operator .app-menu #menu-group-my-site-settings > li { break-inside: avoid; }
  .shell-operator .app-menu #menu-group-my-site-settings > .menu-heading { break-before: auto; }
  .shell-operator .app-menu a[data-level="help"] { padding-right: var(--space-3); }
  /* Row 7.18 (card #298, Cameron, 2026-10-30): the group she is in opens in place under its name and stays open on every
     screen of it (li.is-here, shop_menu_group_state()), as the sub-list does below 1024 px; every other group still opens
     as a flyout. Its › turns down, as an open group's chevron does. My Site Settings, open in place, is one column. */
  .shell-operator .app-menu > .menu-top > li.is-here > .menu-sub,
  .shell-operator .app-menu > .menu-top > li.is-here > #menu-group-my-site-settings { position: static; width: auto; columns: auto; visibility: visible; opacity: 1;
    transition: none; margin: var(--space-1) 0 var(--space-2) var(--space-5); padding: 0 0 0 var(--space-2); border: 0;
    border-left: 1px solid rgb(255 255 255 / .12); background: transparent; box-shadow: none; max-height: none; overflow: visible; }
  .shell-operator .app-menu li.is-here > .menu-row .menu-chevron { transform: rotate(90deg); }
}

/* The current group's screens, as tabs over the screen (row 7.10). */
.screen-tabs { margin: 0 0 var(--space-5); }
.screen-tabs ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; }
.screen-tabs a { display: inline-flex; align-items: center; text-decoration: none; white-space: nowrap; font-weight: var(--weight-label); font-size: var(--text-s); }
.screen-tabs-group { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-2); color: var(--muted);
  font-size: var(--text-xs); font-weight: var(--weight-strong); letter-spacing: .06em; text-transform: uppercase; }
.screen-tabs-group .menu-icon { width: 16px; height: 16px; color: var(--brand); }
.screen-sets { gap: 2px; padding: 3px; margin: 0 0 var(--space-3); width: fit-content; max-width: 100%; /* token-exempt: a segmented control's hairline inset */
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-control); box-shadow: var(--shadow); }
.screen-sets a { min-height: 34px; padding: 0 var(--space-3); border-radius: var(--radius-control); color: var(--muted); }
.screen-sets a:hover { color: var(--ink); background: var(--bg); }
.screen-sets a[aria-current="page"] { background: var(--brand); color: var(--brand-ink); }
.screen-tab-list { gap: 0 var(--space-1); border-bottom: 1px solid var(--line); }
.screen-tab-list a { min-height: 44px; padding: 0 var(--space-3); margin-bottom: -1px; /* token-exempt: the tab's underline sits on the row's rule */
  color: var(--muted); border-bottom: 2px solid transparent; }
.screen-tab-list a:hover { color: var(--ink); border-bottom-color: var(--line-strong); }
.screen-tab-list a[aria-current="page"] { color: var(--brand); border-bottom-color: var(--brand); font-weight: var(--weight-strong); }
/* On a phone and a tablet the rows scroll sideways rather than wrap. */
@media (max-width: 1023px) {
  .screen-tabs ul { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
  .screen-tabs ul::-webkit-scrollbar { display: none; }
  .screen-tabs li { flex: none; }
}
@media print { .screen-tabs { display: none; } }

/* At a larger text size (the app's sheet) Switch back goes onto a second row before the words shrink below 8em. */
.viewing-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); min-height: var(--bar-h);
  padding: 0 var(--space-3); background: var(--viewing-bg); color: var(--viewing-ink); font-size: var(--text-s); }
.viewing-dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
.viewing-words { flex: 1 1 0; min-width: min(8em, 60vw); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viewing-bar form { margin-left: auto; }
.viewing-bar button { background: var(--viewing-ink); color: #fff; } /* dark-ok: the viewing bar is one colour in both modes */

/* Card #230: a migration's preview says so on every page, in one quiet line under the viewing bar (lib/migration.php).
   The notice colours, so it reads in light and dark; in the shop's shell they are pinned light, as every token there is. */
.migration-bar { padding: var(--space-2) var(--space-3); background: var(--info-bg); color: var(--info-ink); border-bottom: 1px solid var(--line);
  font-size: var(--text-s); line-height: 1.4; overflow-wrap: anywhere; }
.migration-bar a { color: inherit; font-weight: var(--weight-strong); text-decoration: underline; }
@media print { .migration-bar { display: none; } }

/* 768–1023 px: a rail of icons, each group a flyout. */
@media (min-width: 768px) and (max-width: 1023px) {
  .frame { grid-template-columns: 60px minmax(0, 1fr); }
  main { padding: var(--space-5) var(--space-4); }
  .app-menu { overflow: visible; padding: var(--space-3) var(--space-2); z-index: 30; }
  .app-menu > .menu-top > li { position: relative; }
  .app-menu .menu-words { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .app-menu a[data-level="top"], .app-menu a[data-level="admin"], .app-menu a[data-level="kitt"] { justify-content: center; padding: 0; min-height: 44px; }
  .app-menu a[data-level="help"] { justify-content: center; padding: 0; min-height: 44px; }
  .app-menu .menu-toggle { display: none; }
  /* Card #214: the flyout fits the window. app-menu.js measures the room as it opens: --flyout-lift raises a flyout
     taller than the room below its icon, never above the top bar, and --flyout-room caps what is left, which scrolls. */
  .app-menu .menu-sub { display: block; position: absolute; left: calc(100% + var(--space-2)); top: calc(-1 * var(--flyout-lift, 0px)); z-index: 30; width: 220px; margin: 0;
    max-height: var(--flyout-room, none); overflow-y: auto; overscroll-behavior: contain;
    padding: var(--space-2); border: 1px solid rgb(255 255 255 / .08); border-radius: var(--radius); background: var(--nav-open-bg);
    box-shadow: var(--shadow-raised); visibility: hidden; opacity: 0; transition: opacity 100ms linear, visibility 0s 100ms; }
  /* Card #214: the pointer crosses the gap between an icon and its flyout without leaving the group. The bridge hangs
     off the icon's row, not the flyout, whose scrolling would clip it; the hide waits 100 ms, which is not motion. */
  .app-menu > .menu-top > li::after { content: ""; position: absolute; left: 100%; top: 0; bottom: 0; width: var(--space-2); }
  @media (prefers-reduced-motion: reduce) { .app-menu .menu-sub { transition: visibility 0s 100ms; } }
  .app-menu > .menu-top > li:hover > .menu-sub,
  .app-menu > .menu-top > li:focus-within > .menu-sub { visibility: visible; opacity: 1; transition: opacity 100ms linear; }
  @media (prefers-reduced-motion: reduce) {
    .app-menu > .menu-top > li:hover > .menu-sub,
    .app-menu > .menu-top > li:focus-within > .menu-sub { transition: none; }
  }
  /* Card #214's review: heading on a slant for an item below or above the icon crosses other icons. While the pointer
     keeps heading for the open flyout (app-menu.js), that one stays and the others wait. */
  .app-menu.is-aiming > .menu-top > li.is-held > .menu-sub { visibility: visible; opacity: 1; transition: none; }
  .app-menu.is-aiming > .menu-top > li:not(.is-held) > .menu-sub { visibility: hidden; opacity: 0; transition: none; }
}

/* Below 768 px: no sidebar. The Menu button opens it as a sheet, and the screen starts under the bar. */
@media (max-width: 767px) {
  :root { --bar-h: 52px; }
  .frame { grid-template-columns: minmax(0, 1fr); }
  .menu-button { display: inline-flex; }
  main { padding: var(--space-4); }
  /* From nothing (flex-basis 0) the name grows into all its row leaves, so a long name never pushes Test Mode or
     the account menu onto a second row at the standard size: only words too large for one row do. */
  .shell-operator .topbar .store-name, .shell-staff .topbar .store-name { flex: 1 1 0; max-width: none; }
  .account-menu .account-name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .topbar .report-problem { display: none; }
  .account-panel .report-problem-menu { display: inline-flex; }
  .topbar .help-link { display: none; }
  .account-panel .help-link-menu { display: inline-flex; }
  .help-panel { width: 100vw; border-left: 0; }
  .help-panel-body { padding: var(--space-4) var(--space-4) var(--space-6); }
  /* --menu-top is the bar's bottom edge, set by app-menu.js as the sheet opens: lower while a viewing bar is above it. */
  .app-menu { display: none; position: fixed; left: 0; right: 0; top: var(--menu-top, var(--bar-h)); z-index: 35; height: auto;
    max-height: calc(100dvh - var(--menu-top, var(--bar-h))); overflow-y: auto; box-shadow: var(--shadow-raised); }
  .app-menu.is-shown { display: block; animation: fmb-menu-in 180ms ease-out; }
  @media (prefers-reduced-motion: reduce) { .app-menu.is-shown { animation: none; } }
  .app-menu a[data-level="top"], .app-menu a[data-level="admin"], .app-menu a[data-level="kitt"], .app-menu .menu-toggle { min-height: var(--control-h); }
  .app-menu a[data-level="sub"], .app-menu a[data-level="beyond"] { min-height: var(--control-h); }
  .app-menu a[data-level="help"] { min-height: var(--control-h); }
}
@keyframes fmb-menu-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }

/* ---- Search settings (row 7.11, card #160) ------------------------------
   One box: in the bar from 768 px, at the top of the menu sheet below it (while
   the sheet is open), and in <main> on /admin/search-settings. Its list is a
   card on the screen's own surface, so it follows light and dark; the box in
   the bar is on the bar, which is dark in both modes. Every target is 48 px. */
.settings-search { position: relative; }
.settings-search input[type=search] { min-height: 48px; padding-left: var(--space-6); }
.settings-search-icon { position: absolute; left: var(--space-3); top: 15px; pointer-events: none; color: var(--muted); } /* token-exempt: centres the 18 px icon in the 48 px box */
.settings-search-page { max-width: 36rem; margin-bottom: var(--space-4); }
.shell-operator .topbar:has(> .settings-search) .store-name { margin-right: 0; }
/* From its smallest (9rem), so it never sends the bar onto a second row that shrinking would have spared. */
.settings-search-bar { flex: 1 1 9rem; max-width: 26rem; min-width: 9rem; margin-right: auto; }
.settings-search-bar input[type=search] { background: rgb(255 255 255 / .08); border-color: rgb(255 255 255 / .2); color: var(--nav-ink); box-shadow: none; } /* dark-ok: the bar is dark in both modes */
.settings-search-bar input[type=search]:hover { border-color: rgb(255 255 255 / .4); } /* dark-ok: the bar is dark in both modes */
.settings-search-bar input[type=search]::placeholder { color: var(--nav-sub-ink); opacity: 1; }
.settings-search-bar input[type=search]:focus-visible { outline-color: var(--nav-mark); }
.settings-search-bar .settings-search-icon { color: var(--nav-icon); }
.settings-search-results { position: absolute; top: calc(100% + var(--space-1)); left: 0; z-index: 60; width: min(30rem, calc(100vw - var(--space-6)));
  max-height: min(70vh, 34rem); overflow-y: auto; overscroll-behavior: contain; padding: var(--space-2); font-size: var(--text-m);
  background: var(--surface); color: var(--ink); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-raised); }
.settings-search-results ul, .settings-search-list { list-style: none; margin: 0; padding: 0; }
.settings-search-results ul a, .settings-search-list a { display: flex; flex-direction: column; justify-content: center; min-height: 48px;
  padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); color: var(--ink); text-decoration: none; }
.settings-search-results ul a:hover, .settings-search-results ul a.is-active, .settings-search-list a:hover { background: var(--accent); }
.settings-search-name { font-weight: var(--weight-label); }
.settings-search-line { font-size: var(--text-xs); color: var(--muted); }
.settings-search-count { margin: 0; padding: var(--space-1) var(--space-3) var(--space-2); font-size: var(--text-xs); color: var(--muted); }
.settings-search-none { margin: 0; padding: var(--space-2) var(--space-3); }
.settings-search-all, .settings-search-menu { display: flex; align-items: center; min-height: 48px; padding: 0 var(--space-3); font-weight: var(--weight-label); }
/* The list hangs from the bar but sits on the screen's surface, so the bar's own link colours and focus ring stay on the bar. */
.shell-operator .topbar .settings-search-results ul a, .shell-operator .topbar .settings-search-results ul a:hover,
.shell-operator .topbar .settings-search-results ul a:focus-visible { color: var(--ink); }
.shell-operator .topbar .settings-search-results .settings-search-all, .shell-operator .topbar .settings-search-results .settings-search-menu,
.shell-operator .topbar .settings-search-results .settings-search-all:hover, .shell-operator .topbar .settings-search-results .settings-search-menu:hover { color: var(--brand); }
.shell-operator .topbar .settings-search-results :focus-visible { outline-color: var(--brand); outline-offset: -3px; }
.settings-search-list { max-width: 40rem; }
.settings-search-list li + li { border-top: 1px solid var(--line); }
.settings-search-note { margin: var(--space-2) 0; }
/* The setting a result opened: an outline and a wash that fades, for a moment (settings-search.js takes it off). */
:target, #preview, .settings-search-found { scroll-margin-top: calc(var(--bar-seen, var(--bar-h)) + var(--space-5)); }
.settings-search-found { outline: 3px solid var(--brand); outline-offset: 4px; border-radius: var(--radius-control); }
@media (prefers-reduced-motion: no-preference) { .settings-search-found { animation: fmb-found 2.4s ease-out; } }
@keyframes fmb-found { from, 35% { background-color: var(--accent); } to { background-color: transparent; } }
/* 768–1023 px: Visit site, Help, Report a problem and the account menu keep their icons and their names (as on a phone), and give
   the box their words' room; the store's name keeps its own, up to 30% of the width, rather than be squeezed under the box. */
@media (min-width: 768px) and (max-width: 1023px) {
  .shell-operator .topbar:has(> .settings-search) .visit-site-words,
  .shell-operator .topbar:has(> .settings-search) .report-problem-words,
  .shell-operator .topbar:has(> .settings-search) .help-link-words,
  .shell-operator .topbar:has(> .settings-search) .account-name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .shell-operator .topbar:has(> .settings-search) .store-name { flex: 0 0 auto; max-width: 30vw; }
}
/* Below 768 px the box is a strip that hangs from the bar's bottom edge while the sheet is open: placed in the bar
   (which is sticky), never over it, so the bar's Menu button, store name and account menu stay seen and tappable
   wherever the bar is, a viewing bar above it or not (row 7.11 review). The sheet starts under the strip. */
@media (max-width: 767px) {
  .settings-search-bar { display: none; }
  body.menu-open .settings-search-bar { display: block; position: absolute; top: calc(100% + 1px); left: 0; right: 0; z-index: 36; max-width: none; margin: 0;
    padding: var(--space-3) var(--space-4); background: var(--nav-bg); border-bottom: 1px solid rgb(255 255 255 / .08); } /* dark-ok: the sheet is the bar's colour */
  /* --menu-strip is the strip's own height, measured by app-menu.js as the sheet opens: 72 px at the standard text size, more at a larger one. */
  body.menu-open:has(.settings-search-bar) .app-menu { top: calc(var(--menu-top, var(--bar-h)) + var(--menu-strip, 72px));
    max-height: calc(100dvh - var(--menu-top, var(--bar-h)) - var(--menu-strip, 72px)); }
  .settings-search-bar .settings-search-icon { left: var(--space-5); top: 27px; } /* token-exempt: the box sits 12 px down the sheet's strip */
  .settings-search-bar .settings-search-results { left: var(--space-4); right: var(--space-4); width: auto; max-height: calc(100dvh - var(--menu-top, var(--bar-h)) - 96px); }
}
@media print { .settings-search { display: none; } }

/* ---- title row --------------------------------------------------------- */

/* h1 and the screen's one primary action on a line, as WordPress's "Products [Add new product]". */
.page-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-4); margin-bottom: var(--space-4); }
.page-head > :last-child:not(h1):not(:first-child) { margin-left: auto; }
.page-head h1 { margin: 0; }
.section-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2); }

/* ---- cards and settings rows ------------------------------------------- */

.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--space-4) var(--space-5); margin-bottom: var(--space-4); min-width: 0; box-shadow: var(--shadow); }
/* A card's own heading is a label for the card, not a second page title. */
.card > h2, .card > header > h2, .card > .section-head > h2, .card > legend h2 { font-size: var(--text-m); margin-bottom: var(--space-3); }
.card > :last-child { margin-bottom: 0; }
.card.narrow { max-width: 440px; margin: var(--space-5) auto; }
.card-inset { border: 1px solid var(--line); border-radius: var(--radius-control); padding: var(--space-3) var(--space-4); background: var(--bg); }
/* An informational card: Shop Organization's note, and a screen not built yet. */
.notice-card { background: var(--info-bg); color: var(--info-ink); border-color: var(--info-ink); }

/* A settings field: label beside the field from 900px, as WordPress's form-table.
   The hint goes UNDER the field, never on a negative margin. */
/* minmax(0, 1fr): a rich editor's toolbar or a long shortcode never makes the row wider than a phone (card #167). */
.settings-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-1); margin: 0 0 var(--space-3); }
.settings-row > label, .settings-row > .settings-label { margin: 0; font-weight: var(--weight-label); font-size: var(--text-s); }
.settings-row .hint { margin-top: var(--space-1); }
@media (min-width: 900px) {
  .settings-row { grid-template-columns: 200px minmax(0, 640px); gap: var(--space-1) var(--space-5); align-items: start; padding: var(--space-3) 0; margin: 0; border-top: 1px solid var(--line); }
  .settings-row:first-of-type { border-top: 0; }
  .settings-row > label, .settings-row > .settings-label { grid-column: 1; padding-top: var(--space-3); color: var(--ink); }
  .settings-row > :not(label):not(.settings-label) { grid-column: 2; }
}

/* ---- forms ------------------------------------------------------------- */

label { font-weight: var(--weight-label); margin-top: var(--space-2); font-size: var(--text-s); }
input[type=text], input[type=email], input[type=password], input[type=search], input[type=tel], input[type=url], input[type=date],
input[type=time], input[type=datetime-local], input[type=number], select, textarea {
  font: inherit; font-size: var(--text-m); padding: var(--space-2) var(--space-3); border: 1px solid var(--line-strong);
  border-radius: var(--radius-control); background: var(--field); color: var(--ink); width: 100%; max-width: 100%; min-height: var(--control-h);
  box-shadow: inset 0 1px 1px rgb(16 24 40 / .04); transition: border-color 120ms, box-shadow 120ms;
}
input[type=text]:hover, input[type=email]:hover, input[type=password]:hover, input[type=search]:hover, input[type=tel]:hover, input[type=url]:hover,
input[type=number]:hover, input[type=date]:hover, input[type=time]:hover, input[type=datetime-local]:hover, select:hover, textarea:hover { border-color: var(--muted); }
input[type=checkbox], input[type=radio] { accent-color: var(--brand); }
textarea { min-height: calc(var(--control-h) * 2); }
/* Card #192: Safari draws today's date and the time, in grey, inside an empty date or time box. empty-dates.js marks such a
   box (data-empty, Apple's engine only) until she types in it; its stand-in is hidden meanwhile, so an empty box looks empty.
   On the box, not its parts: WebKit greys an empty part from the box's own colour after the stylesheet, so a part's colour
   hides only a part that holds a value (the review of #192, in WebKit). */
input[data-empty]:not(:focus) { color: transparent; }
input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible, a:focus-visible, summary:focus-visible {
  outline: 3px solid var(--brand); outline-offset: 2px;
}
.stack { display: flex; flex-direction: column; gap: var(--space-2); max-width: 440px; }
.stack-wide { display: flex; flex-direction: column; gap: var(--space-2); }
.stack button { align-self: flex-start; margin-top: var(--space-2); }
.two { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.two > div { display: flex; flex-direction: column; gap: var(--space-2); }
.field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--space-2) var(--space-3); align-items: end; }
.field-row > div { display: flex; flex-direction: column; gap: var(--space-1); }
fieldset { border: 1px solid var(--line); border-radius: var(--radius); margin: 0 0 var(--space-3); padding: var(--space-2) var(--space-4) var(--space-3); min-width: 0; }
legend { font-weight: var(--weight-label); padding: 0 var(--space-1); }
.choices { border: 0; padding: 0; margin: 0 0 var(--space-2); display: flex; flex-direction: column; gap: var(--space-1); }
.choices legend { font-weight: var(--weight-label); margin-bottom: var(--space-1); }

/* One tick and its words: a real 44px target, because it is tapped at a pickup. */
label.check { font-weight: 400; display: flex; align-items: center; gap: var(--space-2); margin: 0; min-height: var(--control-h); }
label.check input { width: 20px; height: 20px; flex: none; }
/* Its words wrap, an email address anywhere, rather than run off a phone at a larger text size (card #167). */
label.check { overflow-wrap: anywhere; }
label.check > :not(input) { min-width: 0; }
/* In a table a tick's words keep whole, so the table sizes its columns as before (a wide table scrolls in its own box). */
td label.check { overflow-wrap: normal; }

/* The browser's file control, hidden, with its own label as a quiet button. */
.file-input { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.file-input input[type=file] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
input[type=file] { max-width: 100%; }
.file-input > label { display: inline-flex; align-items: center; justify-content: center; margin: 0; cursor: pointer;
  min-height: var(--control-h); padding: 0 var(--space-4); border: 1px solid var(--line-strong); border-radius: var(--radius-control);
  background: var(--surface); color: var(--ink); font-weight: var(--weight-label); box-shadow: var(--shadow); }
.file-input > label:hover { background: var(--bg); }
.file-input input[type=file]:focus-visible + label { outline: 3px solid var(--brand); outline-offset: 2px; }
.file-input .file-name { color: var(--muted); font-size: var(--text-s); }

/* ---- buttons ----------------------------------------------------------- */

/* Primary: one per form or title row. Secondary: an outline. Tertiary: underlined text. */
button, .button { font: inherit; font-size: var(--text-m); font-weight: var(--weight-strong); padding: 0 var(--space-4);
  min-height: var(--control-h); border-radius: var(--radius-control); border: 0; background: var(--brand); color: var(--brand-ink);
  cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  box-shadow: var(--shadow); transition: filter 120ms, background-color 120ms, border-color 120ms; white-space: nowrap; }
/* On an Operator or staff screen, which the app's sheet shows at her larger text size (card #167), a button is never
   wider than its row, and its words wrap whenever one line is wider than the button's room, rather than run off a
   phone's edge. A table's row of buttons keeps one line above 640 px. Only there: shop.css styles the shop too, and
   at a 360 px phone's standard size every meal card's "Add to cart" broke onto two lines in its 130 px column, which
   its one line had fit (card #164's review), so a public page's button keeps its words on one line, as before.
   :where(), so each keeps the specificity of the rule above. */
:where(.shell-operator, .shell-staff) button, :where(.shell-operator, .shell-staff) .button { white-space: normal; text-align: center; max-width: 100%; }
@media (min-width: 641px) { table button, table .button { white-space: nowrap; } }
button:hover, .button:hover { filter: brightness(1.08); }
/* Secondary: white with a grey edge, as every modern admin's; the brand colour is kept for the one primary. */
.button-quiet { border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); font-weight: var(--weight-label); }
.button-quiet:hover { filter: none; background: var(--bg); border-color: var(--muted); }
.button-secondary { background: var(--accent); color: var(--brand); border: 1px solid var(--line); font-weight: var(--weight-label); box-shadow: none; }
.button-link { background: none; border: 0; padding: 0; min-height: 0; font-weight: 400; text-decoration: underline; color: var(--brand); box-shadow: none; }
.button-link:hover { filter: none; text-decoration-thickness: 2px; }
.button-danger { background: var(--error-ink); color: var(--surface); }
.button-small, button.button-small, .button-quiet.button-small { box-shadow: none; }
.button-small, button.button-small { min-height: var(--control-h); padding: 0 var(--space-3); font-size: var(--text-s); }
button[aria-busy="true"] { opacity: .7; }
/* A press that started something slow (public/assets/js/working.js, card #158): a 16 px spinner before the words. */
button[aria-busy="true"]::before { content: ""; width: 16px; height: 16px; flex: none; border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%; animation: fmb-spin .8s linear infinite; }
@keyframes fmb-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { button[aria-busy="true"]::before { animation: none; border-right-color: currentColor; } }
button:disabled, .button[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }

/* A row's Call, Email and Text (card #103, shop_contact_buttons_html()): three square icon buttons in one
   row, as the original site's Orders and Customers lists draw them. Each is a 44px target around a 36px
   face, so the faces sit 8px apart and three fit in 132px; a paused one is a dashed, muted square with
   the word "paused", and may take the row to a second line. */
.contact-buttons { display: inline-flex; flex-wrap: wrap; align-items: center; vertical-align: middle; min-width: calc(var(--control-h) * 3); }
.contact-button { display: inline-flex; align-items: center; justify-content: center; min-width: var(--control-h); min-height: var(--control-h);
  padding: var(--space-1); color: var(--brand); text-decoration: none; }
.contact-face { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-1); min-width: var(--control-h-s);
  min-height: var(--control-h-s); padding: 0 var(--space-2); border: 1px solid var(--brand); border-radius: var(--radius-control);
  background: var(--field); font-size: var(--text-xs); white-space: nowrap; }
.contact-icon { flex: none; }
a.contact-button:hover .contact-face { background: var(--accent); border-color: var(--brand); }
a.contact-button:focus-visible { outline: none; }
a.contact-button:focus-visible .contact-face { outline: 3px solid var(--brand); outline-offset: 2px; }
.contact-button.is-off { color: var(--muted); cursor: default; }
.contact-button.is-off .contact-face { border-style: dashed; border-color: var(--muted); background: transparent; }
form.inline, .inline-form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: 0; min-width: 0; max-width: 100%; }
form.inline { display: inline-flex; }
.inline-form label { margin: 0; }
.inline-form select, .inline-form input { width: auto; max-width: 100%; }
.actions { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; margin: var(--space-2) 0 0; }
.row-actions { white-space: nowrap; }
.row-actions > * { margin-right: var(--space-3); }
.row-actions .button-link { min-height: var(--control-h-s); }

/* ---- status tabs, filters, bulk bar ------------------------------------ */

/* Status tabs (row 7.10): a row of tabs on a rule, the current one underlined in the brand colour.
   A view switch (Orders' All orders / Process / By Location) is a segmented control.
   A sideways scroller of pills on a phone, never four wrapped lines. */
.status-tabs, .coupons-views { display: flex; flex-wrap: wrap; align-items: stretch; gap: 0 var(--space-1); margin: 0 0 var(--space-4);
  font-size: var(--text-s); border-bottom: 1px solid var(--line); }
.status-tabs a, .coupons-views a { display: inline-flex; align-items: center; min-height: 40px; padding: 0 var(--space-3);
  color: var(--muted); text-decoration: none; border-bottom: 2px solid transparent; font-weight: var(--weight-label); white-space: nowrap; }
.status-tabs a, .coupons-views a { margin-bottom: -1px; } /* token-exempt: the tab's underline sits on the row's rule */
.status-tabs a:hover, .coupons-views a:hover { color: var(--ink); border-bottom-color: var(--line-strong); }
.order-views { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 2px; padding: 3px; margin: 0; /* token-exempt: a segmented control's hairline inset */
  font-size: var(--text-s); background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-control); }
.order-views a { display: inline-flex; align-items: center; min-height: 32px; padding: 0 var(--space-3); border-radius: var(--radius-control);
  color: var(--muted); text-decoration: none; font-weight: var(--weight-label); }
.order-views a:hover { color: var(--ink); }
.order-views a[aria-current="page"] { background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }
.status-tabs a[aria-current="page"], .coupons-views a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--brand); font-weight: var(--weight-strong); }
.pages a[aria-current="page"] { font-weight: var(--weight-strong); color: var(--ink); text-decoration: none; }
.status-tabs .count, .order-views .count { color: var(--muted); }

/* A filter bar is always open on a desktop: its summary only appears on a phone,
   where the bar folds away so the table is the first thing on the screen. The
   markup carries `open`, because CSS cannot open a closed <details>. */
.filters-box { border: 0; padding: 0; margin: 0 0 var(--space-4); }
.filters-box > summary { display: none; }
@media (max-width: 767px) {
  .filters-box > summary { display: flex; align-items: center; gap: var(--space-2); cursor: pointer;
    min-height: var(--control-h); padding: 0 var(--space-3); border: 1px solid var(--line);
    border-radius: var(--radius-control); background: var(--surface); font-weight: var(--weight-label); }
  .filters-box[open] > summary { margin-bottom: var(--space-2); }
  .filters-box > summary .count { color: var(--muted); font-weight: 400; }

  /* Ten status links wrapped to four lines is what this replaces: one row that scrolls sideways. */
  .status-tabs, .order-views, .coupons-views { flex-wrap: nowrap; overflow-x: auto; scroll-snap-type: x proximity;
    gap: var(--space-2); padding-bottom: var(--space-1); -webkit-overflow-scrolling: touch; }
  .status-tabs > *, .order-views > *, .coupons-views > * { flex: none; scroll-snap-align: start; }
  .order-views { display: flex; width: 100%; background: transparent; border: 0; padding: 0; margin-bottom: var(--space-2); }
  .order-views a[aria-current="page"] { box-shadow: none; }
  .status-tabs, .coupons-views { border-bottom: 0; }
  .status-tabs a, .order-views a, .coupons-views a { display: inline-flex; align-items: center; min-height: var(--control-h-s); margin: 0;
    padding: 0 var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-pill); background: var(--surface); text-decoration: none; }
  .status-tabs a[aria-current="page"], .order-views a[aria-current="page"], .coupons-views a[aria-current="page"] { background: var(--accent); border-color: var(--brand); }
}

/* Every field fills its cell: no fixed widths, so nothing pushes the page sideways. */
.filters, .order-filters, .coupons-filters, .report-filter, .tc-filter-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--space-3);
  margin: 0 0 var(--space-4); padding: var(--space-3) var(--space-4); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); align-items: end;
}
.filters > div, .order-filters > div, .coupons-filters > div, .report-filter > p, .tc-filter-grid > div,
.filter-field, .export-field { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; margin: 0; }
.filters label, .order-filters label, .coupons-filters label, .report-filter label, .tc-filter-grid label,
.filter-field label, .export-field label { margin: 0; font-size: var(--text-s); }
.filters input, .filters select, .order-filters input, .order-filters select, .coupons-filters input, .coupons-filters select,
.report-filter select, .tc-filter-grid input, .tc-filter-grid select { width: 100%; min-width: 0; }
/* The Filter button takes the next cell, not a row of its own: a whole row for one button was most of the filter bar. */
.filter-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: end; justify-content: flex-start; }

/* Bulk actions on the left, the count and pager on the right, wrapping as one unit. */
.bulk, .bulk-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: 0 0 var(--space-3); }
.bulk label, .bulk-bar label { margin: 0; }
.bulk select, .bulk-bar select { width: auto; }
.bulk-end { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.pages, .cust-pages { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); align-items: center; }
.pages span[aria-current], .cust-pages span[aria-current] { font-weight: var(--weight-strong); }
.filtered-total { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-2) var(--space-3); margin: var(--space-3) 0; }
.filtered-total strong { font-size: var(--text-l); }

/* ---- a finger's minimum on a phone (row 3.4) ---------------------------------------------------------------------
   docs/THEMES.md: a button is at least 44 px tall. Desktop keeps its compact 36 px row actions, tabs and bar pills; below 640 px (the
   Operator app's sheet is an iPhone, 390 px) each one a finger presses takes --control-h. Only the height changes: every control keeps
   its words and its accessible name. dev/phone-walk-check.mjs measures every screen. Rules that carry the same specificity as the
   ones they raise sit here, after them; the area stylesheets raise their own (templates.css, site-pages.css). */
@media (max-width: 640px) {
  button.button-link, .row-actions .button-link, .filter-actions .button-link { min-height: var(--control-h); }
  .button-small, button.button-small, .button-quiet.small, button.small { min-height: var(--control-h); }
  .shell-operator .topbar .button-quiet, .shell-staff .topbar .button-quiet,
  .topbar .visit-site, .topbar .report-problem, .topbar .help-link { min-height: var(--control-h); }
  .screen-sets a, .status-tabs a, .order-views a, .coupons-views a { min-height: var(--control-h); }
  /* A word with no space in it (an email address, a file path, a link) is wider than a phone's line at her larger text size
     (Chrome's minimum font size 28 and 34, as the sheet sets it) and widened the whole page. It breaks inside its own line instead. */
  main { overflow-wrap: break-word; }
}

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

/* position: relative, so a box hidden for screen readers in a cell (a row's tick's label) scrolls with the table
   inside it rather than escape it and make the whole page scroll sideways (card #167). */
.table-wrap, .coupons-table, .cust-table-wrap, .table-scroll {
  position: relative; overflow-x: auto; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); margin: 0 0 var(--space-5);
  box-shadow: var(--shadow);
}
table.list, .coupons-table table, table.cust-list { border-collapse: collapse; width: 100%; font-size: var(--text-s); }
table.list th, table.list td, .coupons-table th, .coupons-table td, table.cust-list th, table.cust-list td {
  text-align: left; padding: var(--space-2) var(--space-3); border-bottom: 1px solid var(--line); vertical-align: middle;
}
/* Row 7.10: a quiet heading row, small capitals on the page colour, as a modern admin's table. */
table.list thead th, .coupons-table thead th, table.cust-list thead th { background: var(--bg); color: var(--muted); font-weight: var(--weight-strong);
  font-size: var(--text-xs); letter-spacing: .03em; text-transform: uppercase; white-space: nowrap; padding-block: var(--space-3); }
table.list thead th a, .coupons-table thead th a, table.cust-list thead th a { color: var(--muted); text-decoration: none; }
table.list thead th a:hover { color: var(--ink); text-decoration: underline; }
table.list thead th[aria-sort="ascending"] a::after { content: " ↑"; }
table.list thead th[aria-sort="descending"] a::after { content: " ↓"; }
/* A date, a sum or a status reads on one line; on a desktop the table scrolls sideways before it wraps them. */
@media (min-width: 641px) {
  table.list td[data-label="Total"], table.list td[data-label="Status"], table.list td[data-label="Items"],
  table.list td[data-label="Price"], table.list td[data-label="Order #"], table.list td.num { white-space: nowrap; }
}
table.list tr:last-child td, .coupons-table tr:last-child td, table.cust-list tr:last-child td, table.cust-list tr:last-child th { border-bottom: 0; }
table.list tbody tr, .coupons-table tbody tr { transition: background-color 80ms; }
table.list tbody tr:hover, .coupons-table tbody tr:hover { background: var(--accent); }
table.list .row-title, .coupons-table .row-title, .cust-name { font-weight: var(--weight-label); }
table.list .row-title a, .coupons-table .row-title a, a.row-title { text-decoration: none; font-weight: var(--weight-strong); }
table.list .row-title a:hover, .coupons-table .row-title a:hover { text-decoration: underline; }
table.list td.name, table.list .row-title { min-width: 12rem; }
table.list input[type=checkbox], .coupons-table input[type=checkbox] { width: 20px; height: 20px; }
table.list td.thumb { width: 56px; }
table.list td.thumb img { object-fit: cover; border-radius: var(--radius-control); background: var(--accent); }
table.list td.num, table.list th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.list select { width: auto; max-width: 16rem; }
tr.retired td, tr.is-paused td { color: var(--muted); }
.empty, .coupons-empty { color: var(--muted); padding: var(--space-3) 0; }

/* Below 640px a row is a card, and each value carries its own heading. */
@media (max-width: 640px) {
  table.list thead { position: absolute; left: -9999px; }
  /* Two values a line, each with its heading above it, so a row is half as tall as a list of lines. */
  table.list tr { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--line); padding: var(--space-3); }
  table.list tbody tr:hover { background: transparent; }
  table.list td { display: block; border: 0; padding: 0; min-width: 0; overflow-wrap: anywhere; }
  /* A first cell with no heading is the card's first full-width line: its words (a name, a link, a button, a phone number)
     are real content and must not be covered. Only a tick box (td.pick) is pinned to the card's top-right corner, where
     it takes no room, so the fix is one rule for every list rather than one per screen (row 3.4). */
  table.list tr { position: relative; }
  table.list td:not([data-label]):first-child { grid-column: 1 / -1; }
  table.list td.pick:first-child { position: absolute; right: var(--space-3); top: var(--space-3); grid-column: auto; }
  table.list td[data-label="Customer"], table.list td[data-label="Name"], table.list td[data-label="Contact"],
  table.list td[data-label="Actions"], table.list td.name, table.list td.contact { grid-column: 1 / -1; }
  table.list td[data-label]::before { content: attr(data-label); display: block; color: var(--muted); font-size: var(--text-xs);
    font-weight: var(--weight-label); text-transform: uppercase; letter-spacing: .03em; }
  table.list td[data-label="Actions"]::before, table.list td[data-label="Contact"]::before { content: none; }
}

/* ---- status pill ------------------------------------------------------- */

.status, .pill, .cust-tag, .coupon-draft, .tag, .log-redirected {
  display: inline-block; padding: 2px var(--space-2); /* token-exempt: 2px 8px is the pill's own, from docs/OPERATOR-LOOK.md's Components table */
  border-radius: var(--radius-pill); font-size: var(--text-xs); line-height: 1.5;
  font-weight: var(--weight-label); background: var(--accent); color: var(--ink); white-space: nowrap;
}
/* A dot before an order's status, in the pill's own colour, so a column of them scans by shape too. */
.status[class*="order-status-"]::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; margin-right: 6px; vertical-align: middle; /* token-exempt: the dot's own gap */ }
.status-published, .status-went, .pill-paid, .order-status-completed { background: var(--ok-bg); color: var(--ok-ink); }
.status-offline, .status-held, .pill-unpaid, .order-status-ready, .coupon-draft, .log-redirected { background: var(--info-bg); color: var(--info-ink); }
.status-stopped, .status-paused, .status-trash, .cust-tag,
.order-status-refunded, .order-status-cancelled, .order-status-failed, .order-status-trash { background: var(--error-bg); color: var(--error-ink); }
.tag { color: var(--muted); margin-left: var(--space-2); }

/* ---- tiles ------------------------------------------------------------- */

.tiles, .cust-tiles { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: var(--space-3); }
.tile, .cust-tile { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-4); border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--surface); color: var(--ink); text-decoration: none; position: relative; box-shadow: var(--shadow);
  transition: border-color 120ms, transform 120ms, box-shadow 120ms; }
.tile:hover, .tile:focus-visible, .cust-tile:hover, .cust-tile:focus-visible { border-color: var(--brand); box-shadow: var(--shadow-raised); }
@media (prefers-reduced-motion: no-preference) { .tile:hover { transform: translateY(-1px); } }
.tile-count, .cust-tile-value { font-size: var(--text-xl); font-weight: 700; line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }
.tile-count.is-long { font-size: var(--text-l); }
.tile-label, .cust-tile-label { font-weight: var(--weight-label); color: var(--muted); font-size: var(--text-s); }
.cust-tile-label, .cust-tile-meta { color: var(--muted); font-size: var(--text-s); }
/* The whole tile is the link, so it needs no separate "View": an arrow says so. */
.tile::after { content: "→"; position: absolute; top: var(--space-3); right: var(--space-4); color: var(--muted); }
.tile:hover::after { color: var(--brand); }

/* ---- notices and flashes ---------------------------------------------- */

.flash, .notice, .banner { padding: var(--space-3) var(--space-4); border-radius: var(--radius-control); margin: 0 0 var(--space-4); overflow-wrap: anywhere;
  border: 1px solid color-mix(in srgb, currentColor 25%, transparent); border-left: 4px solid currentColor; }
.flash { display: flex; align-items: flex-start; gap: var(--space-3); }
/* A flash that is one paragraph reads as one: "Warning:" and its sentence on the same lines, never two columns. */
p.flash { display: block; }
.flash-words { flex: 1; min-width: 0; }
.flash-close { background: none; border: 0; color: inherit; min-height: var(--control-h); min-width: var(--control-h); padding: 0; font-size: var(--text-l); }
.flash-ok, .notice-ok, .store-open.is-open { background: var(--ok-bg); color: var(--ok-ink); }
.flash-error, .notice-warning, .store-open.is-closed, .banner { background: var(--error-bg); color: var(--error-ink); }
.flash-info, .notice-info, .banner.info, .cust-notice { background: var(--info-bg); color: var(--info-ink); }
.notice p, .notice ul, .banner p { margin: var(--space-1) 0; }
.banner p:last-child { margin: 0; }
.banner a { color: inherit; font-weight: var(--weight-strong); }
.field-error { color: var(--error-ink); margin: 0; font-size: var(--text-s); }

/* ---- facts list -------------------------------------------------------- */

/* minmax(0, 1fr), not 1fr: a 1fr track will not shrink below its longest word, and a customer's email address is
   one, which ran one order's cards off a phone's right edge (card #166). The address breaks anywhere instead. A
   label takes the room it needs up to 55% of the list, so at a larger text size it wraps rather than push its
   answer off the screen (card #167). */
.facts { display: grid; grid-template-columns: fit-content(55%) minmax(0, 1fr); gap: var(--space-2) var(--space-4); }
.facts dt { font-weight: var(--weight-label); color: var(--muted); overflow-wrap: break-word; }
.facts dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }

@media (max-width: 760px) {
  .two, .field-row { grid-template-columns: 1fr; }
}

/* ---- Admin's dashboard (row 19.10, card #297) -------------------------- */

.purrk-ranges { margin-bottom: var(--space-3); }
.purrk-dates { margin-bottom: var(--space-4); }
.tile-change { color: var(--muted); font-size: var(--text-xs); }
.tile.is-current { border-color: var(--brand); box-shadow: inset 0 0 0 1px var(--brand); }
/* A tile that opens nothing (Kitts sold, What you earned) has no arrow and does not lift. */
div.tile:hover { border-color: var(--line); box-shadow: none; transform: none; }
div.tile::after { content: none; }
.purrk-sources { margin: 0 0 var(--space-4); }
.purrk-sources summary { cursor: pointer; color: var(--muted); }
.purrk-sources .facts { margin-top: var(--space-3); }
.purrk-chart-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.purrk-groups { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); }
.purrk-groups a { display: inline-flex; align-items: center; min-height: var(--control-h); color: var(--muted); }
.purrk-groups a[aria-current="page"] { color: var(--ink); font-weight: var(--weight-strong); }
/* One series, so no legend: the heading names it. Drawn in HTML (lib/purrk_dashboard.php) so its words stay at the
   type scale on a phone. Hairline grid on clean ticks, the baseline a step stronger, ticks in muted ink; columns in
   --chart, at most 24 px thick with 4 px round tops and square at the baseline, a 2 px gap between neighbours. A
   column's whole slot answers the pointer, not only its bar. */
.purrk-chart-plot { position: relative; margin: var(--space-3) 0; }
.purrk-chart-plot:focus-visible { outline: 2px solid var(--brand); outline-offset: 4px; border-radius: var(--radius-control); }
.purrk-bars { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr); row-gap: var(--space-1); }
.purrk-bars-plot { position: relative; grid-column: 2; height: 14rem; }
.purrk-bars-plot .grid { position: absolute; left: 0; right: 0; bottom: var(--at); border-top: 1px solid var(--line); }
.purrk-bars-plot .grid:first-child { border-top-color: var(--line-strong); }
.purrk-bars-plot .tick { position: absolute; right: calc(100% + var(--space-2)); top: -.65em; color: var(--muted); font-size: var(--text-xs);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.purrk-bars-plot .cols { position: absolute; inset: 0; display: flex; align-items: flex-end; }
.purrk-bars-plot .col { position: relative; flex: 1 1 0; min-width: 0; height: 100%; display: flex; align-items: flex-end; justify-content: center;
  padding: 0 1px; } /* token-exempt: half the 2px gap between columns */
.purrk-bars-plot .col.is-on { background: var(--accent); }
.purrk-bars-plot .bar { display: block; width: 100%; max-width: 24px; height: var(--h); background: var(--chart); border-radius: 4px 4px 0 0; } /* token-exempt: the chart's 4px round top */
.purrk-bars-x { grid-column: 2; display: flex; color: var(--muted); font-size: var(--text-xs); }
.purrk-bars-x span { flex: 1 1 0; min-width: 0; overflow: visible; white-space: nowrap; }
.purrk-tip { position: absolute; top: 0; display: flex; flex-direction: column; gap: 2px; padding: var(--space-2) var(--space-3); /* token-exempt: 2px between the tip's two lines */
  background: var(--surface); color: var(--muted); border: 1px solid var(--line); border-radius: var(--radius-control); box-shadow: var(--shadow-raised);
  font-size: var(--text-s); pointer-events: none; white-space: nowrap; }
.purrk-tip strong { color: var(--ink); }
.purrk-tip[hidden] { display: none; }
/* On a phone every other period is named under the chart, so the names never run into each other. */
@media (max-width: 480px) { .purrk-bars-x span:nth-child(even) { visibility: hidden; } }

/* ---- Admin › Operator Stores List (row 19.9, card #296) ----------------- */

.purrk-stores-search { margin-bottom: var(--space-4); }
.purrk-stores-search input[type=search] { min-width: min(100%, 18rem); }
.purrk-stores-results[aria-busy="true"] { opacity: .6; }
/* Rows 19.12 and 19.13 (cards #310 and #311): Admin › Sales Page and Admin › Affiliates. Lists, not tables, so a
   phone at the largest text reads each line whole. */
.kitt-choices li { margin-bottom: var(--space-2); }
.sp-rules summary { cursor: pointer; }
/* Sales Page laid out as the sales page (Cameron, 2026-10-31: "make it feel like the actual sales page outline"): a
   band for each part of the page, tinted where the page's is, each box as large as its words show there, photos beside
   or under them, cards in a grid. Every grid column is minmax(0, 1fr), so a 390 px phone at the largest text stacks
   them and scrolls nothing sideways. */
.sp-outline { display: flex; flex-direction: column; gap: 0; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow); margin-bottom: var(--space-4); }
.sp-band { padding: var(--space-5) var(--space-3); display: flex; flex-direction: column; gap: var(--space-4); min-width: 0;
  border-top: 1px solid var(--line); }
.sp-band:first-of-type { border-top: 0; }
.sp-band-wash { background: var(--accent); }
.sp-band-prose > * { width: 100%; max-width: 46rem; }
.sp-band-closing { align-items: center; text-align: center; }
.sp-band-closing .sp-field { text-align: left; }
.sp-band-name { margin: 0; font-size: var(--text-xs); font-weight: var(--weight-label); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.sp-o-h2, .sp-band h3.sp-o-h2 { margin: 0; font-size: var(--text-xl); line-height: 1.25; }
.sp-label { margin: 0; font-size: var(--text-s); font-weight: var(--weight-label); color: var(--muted); }
.sp-field, .sp-photo { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.sp-field input[type=text], .sp-field input[type=url], .sp-field textarea { width: 100%; max-width: 100%; }
.sp-look-h1 input { font-size: var(--text-xl); font-weight: var(--weight-strong); line-height: 1.2; min-height: 3.25rem; }
.sp-look-h2 input { font-size: var(--text-l); font-weight: var(--weight-strong); line-height: 1.25; min-height: 2.75rem; }
.sp-look-lede textarea, .sp-look-lede .ql-editor { font-size: var(--text-l); line-height: 1.5; }
.sp-look-prose textarea, .sp-look-prose .ql-editor { line-height: 1.6; }
/* A formatted paragraph's editor starts about as tall as its words, as a box does, and grows with them. */
.sp-field .rich-editor-words .ql-editor { min-height: 6em; }
.sp-look-lede .rich-editor-words .ql-editor { min-height: 5em; }
.sp-look-price input { font-size: var(--text-xl); font-weight: var(--weight-strong); }
.sp-note { font-style: italic; }
.sp-split { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4) var(--space-5); align-items: start; }
.sp-split-words { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
@media (min-width: 48rem) { .sp-band, .sp-save { padding-left: var(--space-6); padding-right: var(--space-6); } }
@media (min-width: 64rem) {
  .sp-split { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
  .sp-split-photo-first { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
}
.sp-photo img { display: block; width: 100%; max-width: 100%; height: auto; aspect-ratio: 4 / 3; max-height: 20rem; object-fit: cover; border-radius: var(--radius); background: var(--surface); }
.sp-look-wide img { max-height: 16rem; }
.sp-look-strip img { aspect-ratio: 1; max-height: none; }
.sp-no-photo { margin: 0; padding: var(--space-5) var(--space-3); border: 2px dashed var(--line-strong); border-radius: var(--radius); text-align: center; color: var(--muted); }
.sp-photo-change summary { cursor: pointer; display: inline-flex; align-items: center; min-height: 44px; font-weight: var(--weight-label); color: var(--brand); }
.sp-photo-change[open] { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--surface); }
.sp-photo-change button { align-self: flex-start; }
.sp-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 10rem), 1fr)); gap: var(--space-3); }
.sp-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3); }
.sp-look-grid .sp-cards { grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); }
.sp-look-quotes .sp-cards { grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr)); }
.sp-card { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; padding: var(--space-3); background: var(--surface);
  border: 1px solid var(--line); border-top: 3px solid var(--brand); border-radius: var(--radius-control); }
.sp-band-wash .sp-card { background: var(--surface); }
.sp-card-label { font-size: var(--text-xs); font-weight: var(--weight-label); color: var(--muted); }
.sp-card-title { font-weight: var(--weight-strong); }
.sp-look-steps .sp-cards { counter-reset: sp-step; }
.sp-look-steps .sp-card { counter-increment: sp-step; }
.sp-look-steps .sp-card::before { content: counter(sp-step); display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem;
  border-radius: 50%; background: var(--brand); color: var(--brand-ink); font-weight: var(--weight-strong); }
.sp-look-quotes .sp-card textarea, .sp-look-quotes .sp-card-title { font-style: italic; }
.sp-look-faq .sp-card { border-top-width: 1px; border-left: 3px solid var(--brand); }
.sp-card-add { margin: 0; }
.sp-mock-bar { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--surface); font-size: var(--text-s); }
.sp-mock-name { font-weight: var(--weight-strong); margin-right: auto; }
.sp-mock-buttons { margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); }
.sp-mock-btn { display: inline-flex; align-items: center; padding: var(--space-1) var(--space-4); border-radius: var(--radius-pill);
  background: var(--brand); color: var(--brand-ink); font-weight: var(--weight-label); font-size: var(--text-s); }
.sp-mock-quiet { background: transparent; color: var(--brand); border: 1px solid var(--brand); }
.sp-o-price { margin: 0; font-size: var(--text-l); }
.sp-o-price a { font-size: var(--text-s); margin-left: var(--space-2); }
.sp-price-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: var(--space-4); }
.sp-price-card { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; margin: 0; padding: var(--space-4);
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.sp-price-card h3, .sp-price-card legend { margin: 0; font-size: var(--text-l); font-weight: var(--weight-strong); padding: 0; }
.sp-price-card legend { float: left; width: 100%; }
.sp-price-card legend + * { clear: both; }
.sp-price-save { margin: 0; }
.sp-guarantee { padding: var(--space-3) var(--space-4); border: 1px dashed var(--line-strong); border-radius: var(--radius); }
.sp-sides { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
/* Two by two, as the sales page pairs them: who it is for beside who it is not, Catnip beside Litter Box. */
@media (min-width: 40rem) { .sp-sides { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.sp-side { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; padding: var(--space-4); border-radius: var(--radius); background: var(--surface);
  border: 1px solid var(--line); border-top: 4px solid var(--ok-ink); }
.sp-side-hard { border-top-color: var(--error-ink); }
.sp-side h3 { margin: 0; font-size: var(--text-l); }
.sp-save { margin: 0; padding: var(--space-3); border-top: 1px solid var(--line); display: flex; flex-wrap: wrap;
  align-items: center; gap: var(--space-2) var(--space-3); }
/* Row 19.15 (card #317): Admin › Sales Page › Layout, one card per block, its style choices wrapping to one a line on a phone. */
.sp-layout-link { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.sl-ask { margin: 0 0 var(--space-3); padding: var(--space-3) var(--space-4); border-left: 4px solid var(--brand); }
.sl-said:empty, .sl-form > button[hidden] { display: none; }
.sl-said { margin: 0 0 var(--space-3); padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); background: var(--info-bg); color: var(--info-ink); }
.sl-look { display: flex; flex-wrap: wrap; gap: var(--space-3) var(--space-6); }
.sl-look > legend { font-weight: var(--weight-strong); }
.sl-look-choice { min-width: 0; }
.sl-radio, .sl-check { display: inline-flex; align-items: center; gap: var(--space-2); min-height: 44px; }
.sl-blocks { list-style: none; margin: 0 0 var(--space-4); padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.sl-block { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.sl-hidden { border-style: dashed; background: transparent; }
.sl-hidden .sl-name { color: var(--muted); }
.sl-wrong { border-color: var(--error-ink); border-width: 2px; }
.sl-name { margin: 0; font-size: var(--text-l); overflow-wrap: anywhere; }
.sl-name:focus { outline: 3px solid var(--brand); outline-offset: 4px; }
.sl-n { color: var(--muted); font-weight: var(--weight-label); }
.sl-tag { display: inline-block; margin-left: var(--space-1); padding: 0 var(--space-2); border-radius: var(--radius-pill); border: 1px solid var(--line-strong);
  font-size: var(--text-xs); font-weight: var(--weight-label); color: var(--muted); vertical-align: middle; }
.sl-style { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr)); gap: var(--space-2) var(--space-4); align-items: end; }
.sl-field { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.sl-field select { width: 100%; max-width: 100%; min-height: 44px; }
.sl-field .hint { margin: 0; }
.sl-moves { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.sl-moves button { min-height: 44px; }
.sl-add { display: flex; flex-direction: column; gap: var(--space-1); max-width: 30rem; margin-bottom: var(--space-4); }
.sl-add select { min-height: 44px; max-width: 100%; }
.sl-add p { margin: 0; }
.sl-save { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
/* Row 19.16: her own HTML, the whole page's box and a block's, and what each says. */
.sl-own-page { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; margin: var(--space-4) 0 0; }
.sl-own-page > legend { font-weight: var(--weight-strong); }
.sl-own-page .hint, .sl-html-field .hint { margin: 0; }
.sl-code { width: 100%; max-width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: var(--text-s); }
.sl-html-state, .sl-html-reason { margin: 0; overflow-wrap: anywhere; }
.sl-html-waiting strong, .sl-html-sent_back strong { color: var(--info-ink); }
.sl-html-live strong { color: var(--ok-ink); }
.sl-html-removed { margin: 0; overflow-wrap: anywhere; }
.sl-html-removed p, .sl-html-removed ul { margin: 0; }
.sl-photo-urls { margin: var(--space-3) 0; overflow-wrap: anywhere; }
.sl-photo-urls code, .sl-own-page code, .sl-html-field code { overflow-wrap: anywhere; word-break: break-word; }
/* Row 19.17 (card #323): each HTML box as a code editor (sales-html-editor.js over the vendored CodeMirror, whose own
   look is set from these tokens by EditorView.theme) with its toolbar, a live preview beside it from 1024 px and under
   it below, and a live region. Every track that holds words is minmax(0, 1fr), so a phone at the largest text size
   never scrolls sideways; the editor scrolls inside itself. */
.sl-editor { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.sl-editor-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; }
.sl-editor-bar button { min-height: 44px; }
/* A button set hidden by the script is gone: button's own display would otherwise beat the browser's [hidden] rule. */
.sl-editor-bar button[hidden], .sl-inplace button[hidden] { display: none; }
.sl-editor-bar button[aria-pressed="true"] { background: var(--brand); color: var(--brand-ink); border-color: var(--brand); }
.sl-editor-group { display: inline-flex; flex-wrap: wrap; gap: var(--space-1); }
.sl-editor-panes { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3); align-items: start; }
@media (min-width: 64rem) { .sl-editor-panes { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.sl-editor-code { min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.sl-editor-code .cm-editor { max-width: 100%; }
.sl-editor-said { margin: 0; }
.sl-preview { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; padding: var(--space-3); border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--surface); }
.sl-preview-head { margin: 0; }
.sl-preview .hint { margin: 0; }
.sl-preview-frame { display: block; width: 100%; max-width: 100%; height: 60vh; min-height: 420px; border: 1px solid var(--line);
  border-radius: var(--radius-control); background: #fff; } /* dark-ok: the sales page is her own paper, not the app */
.sl-preview-phone { background: var(--bg); }
.sl-preview-phone .sl-preview-frame { box-sizing: content-box; width: min(390px, calc(100% - 12px)); margin: 0 auto; border: 6px solid var(--line-strong); }
.sl-preview-out { margin: 0; overflow-wrap: anywhere; }
.sl-preview-out p, .sl-preview-out ul { margin: 0; }
.sl-preview-out ul { padding-left: var(--space-5); }
/* Row 19.18 (card #324): the bar for the element open in the preview frame, in the page above the frame so it wears
   these tokens and speak.js reaches it; sticky under the top bar as card #178's Save bar is along the bottom. Its
   words wrap anywhere (a heading's first forty characters), every button is 44 px, and the photo buttons stack in one
   minmax(0, 1fr) track. The frame's own outlines are literal colours in sales-html-editor.js: the frame is her page. */
.sl-inplace { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); position: sticky; top: calc(var(--bar-seen, var(--bar-h)) + var(--space-2)); z-index: 2;
  min-width: 0; margin: 0; padding: var(--space-2) var(--space-3); background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-control);
  /* Stuck under the top bar, it never grows past the window: at the sheet's largest text, or with nine photos to
     choose from, it scrolls inside itself (as the sidebar does), so Keep it and Edit code are always in reach. */
  max-height: calc(100vh - var(--bar-seen, var(--bar-h)) - 2 * var(--space-2)); max-height: calc(100dvh - var(--bar-seen, var(--bar-h)) - 2 * var(--space-2)); overflow-y: auto; overscroll-behavior: contain; }
.sl-inplace[hidden] { display: none; }
.sl-inplace button { min-height: 44px; }
.sl-inplace button[aria-pressed="true"] { background: var(--brand); color: var(--brand-ink); border-color: var(--brand); }
.sl-inplace-what { margin: 0; flex: 1 1 100%; overflow-wrap: anywhere; }
.sl-inplace .speak { flex: 1 1 100%; margin: 0; }
.sl-inplace-photos { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-1); width: 100%; }
.sl-inplace-photos button { overflow-wrap: anywhere; text-align: left; }
.sl-inplace-photos[hidden] { display: none; }
.sp-findings { margin: var(--space-1) 0 0; padding-left: var(--space-5); }
.sp-instead { display: block; }
.sp-plan { min-width: 0; }
.sp-ask-lines li { margin-bottom: var(--space-2); }
.aff-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--space-2) var(--space-4); }
.aff-head h2 { margin: 0; }
.aff-add-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr)); gap: var(--space-3) var(--space-4); }
.aff-add-fields > div { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.aff-facts, .aff-links, .aff-months { list-style: none; margin: 0; padding: 0; }
.aff-facts li { margin-bottom: var(--space-1); }
.aff-links li, .aff-months li { padding: var(--space-3) 0; border-top: 1px solid var(--line); }
.aff-months p { margin: 0 0 var(--space-2); }
.aff-link, .aff-email, .aff-coupons li { overflow-wrap: anywhere; }
.aff-link { display: block; }
/* Row 19.14 (card #313): a sign-up's Approve and Decline side by side, wrapping on a phone; a long "how they will share it"
   on lines of its own; Change one's name or email behind a summary as tall as a button; each Kitt's plan. */
.aff-decide { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-2) var(--space-4); }
.aff-decide form p { margin: 0; }
.aff-share { display: block; }
/* A list item, not flex, so it keeps its disclosure triangle; its padding makes it as tall as a button. */
.aff-edit > summary { min-height: var(--control-h); padding: var(--space-2) 0; color: var(--brand); font-weight: var(--weight-label); cursor: pointer; overflow-wrap: anywhere; }
.aff-edit[open] > summary { margin-bottom: var(--space-2); }
/* Row 19.23 (card #336): Admin › Pricing, every price line, locked or hers, and the worked example under the price box. */
.pr-intro { max-width: 60rem; }
.pr-offer h2 .pr-kind { font-size: var(--text-s); font-weight: normal; }
.pr-offer h3 { margin: var(--space-4) 0 var(--space-2); font-size: var(--text-m); }
.pr-lines { list-style: none; margin: 0 0 var(--space-3); padding: 0; }
.pr-line { padding: var(--space-2) 0; border-top: 1px solid var(--line); min-width: 0; }
.pr-line .hint { margin-top: var(--space-1); }
.pr-line-what { display: flex; align-items: flex-start; gap: var(--space-2); margin: 0; min-width: 0; overflow-wrap: anywhere; }
.pr-line-words { flex: 1 1 0; min-width: 0; }
.pr-line-name { font-weight: var(--weight-label); }
.pr-lock { display: inline-flex; flex: none; padding-top: var(--space-1); color: var(--muted); }
.pr-actions [hidden] { display: none; }
.pr-tag { font-size: var(--text-xs); padding: 0 var(--space-2); border-radius: var(--radius-pill); background: var(--ok-bg); color: var(--ok-ink); }
.pr-purrk-price { font-size: var(--text-l); }
.pr-field { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; max-width: 28rem; margin-bottom: var(--space-3); }
.pr-field .hint { margin: 0; }
.pr-plan { border: 1px solid var(--line); border-radius: var(--radius-control); padding: var(--space-3); margin: 0 0 var(--space-3); min-width: 0; }
.pr-plan legend { padding: 0 var(--space-1); font-weight: var(--weight-label); overflow-wrap: anywhere; }
.pr-plan > .hint { margin: 0 0 var(--space-2); }
.pr-plan-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); gap: 0 var(--space-4); }
.pr-plan-fields .pr-field { margin-bottom: 0; }
.pr-example { background: var(--accent); color: var(--ink); border-radius: var(--radius-control); padding: var(--space-3); margin: 0 0 var(--space-3); min-width: 0; overflow-wrap: anywhere; }
.pr-example-head { margin: 0 0 var(--space-1); font-weight: var(--weight-label); }
.pr-example .hint { margin-top: var(--space-2); }
.pr-scenarios { margin: 0; padding-left: var(--space-4); }
.pr-scenarios li { margin-bottom: var(--space-1); }
.pr-when { font-weight: var(--weight-label); }
.pr-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; }
.pr-actions button { min-width: 0; max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
.pr-ask-lines, .pr-ask-split { margin: 0; padding-left: var(--space-4); }
.pr-ask-lines li, .pr-ask-split li { margin-bottom: var(--space-2); overflow-wrap: anywhere; }
/* Row 19.24 (card #337): the months free with the Kitt, under its Purrk, below who pays during them. */
.pr-months { border-top: 1px solid var(--line); margin-top: var(--space-4); min-width: 0; }
.pr-free-note { margin: 0 0 var(--space-3); }
/* One store: how to reach her beside her store, one above the other when there is no room for both. */
.purrk-store-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr)); gap: var(--space-4); }
.purrk-store-grid > * { min-width: 0; }
/* Her contact card as a QR code: big enough for a phone's camera across a desk, never wider than the card. */
.purrk-qr { margin: var(--space-3) 0 0; }
.purrk-qr svg { display: block; width: 12rem; max-width: 100%; height: auto; }

/* Row 16.2 (card #305): a pickup place's picture, wherever the place is shown
   (lib/pickup_picture.php, shop_pickup_picture_html()): never wider than its box,
   its own shape kept so a map's cross roads are never cropped, a tap opening it larger. */
.pickup-picture { margin: var(--space-2) 0 0; max-width: 20rem; }
.pickup-picture a { display: block; }
.pickup-picture img { display: block; width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--radius-control); }
.pickup-picture-thumb { max-width: 8rem; }
.next-pickup-picture { max-width: 14rem; }
/* Order Pickup: the map's box and My own picture's boxes each show only while their choice is chosen;
   without :has() all of them show and work. */
.pickup-picture-field { border: 1px solid var(--line); border-radius: var(--radius-control); padding: var(--space-3) var(--space-4); margin: 0; }
.pickup-picture-field legend { padding: 0 var(--space-1); font-weight: var(--weight-label); }
.picture-choice { display: flex; align-items: center; gap: var(--space-2); min-height: var(--control-h); }
.pickup-picture-own, .pickup-picture-map { display: grid; gap: var(--space-1); margin-top: var(--space-2); }
.pickup-picture-map p { margin: 0; }
.pickup-picture-field:has(#location-picture-own:not(:checked)) .pickup-picture-own { display: none; }
.pickup-picture-field:has(#location-picture-map:not(:checked)) .pickup-picture-map { display: none; }
/* Card #315: while Zoom in, Zoom out or Show the map draws in place (pickup-map.js), the map shown stays, dimmed. */
.pickup-picture-map.is-drawing figure img { opacity: .45; transition: opacity .15s; }
.pickup-picture-map.is-drawing { cursor: progress; }
.pickup-map-status { margin: 0; }
.pickup-map-status:empty, .pickup-picture-field:has(#location-picture-map:not(:checked)) .pickup-map-status { display: none; }

/* Row 19.19 (card #326): the sales page editor's own shell (layout/sales.php, operator/kitt_sales_edit.php), like
   Course mode's: a blue bar in place of the Operator menu. Card #330 lays it out as a page builder: the bar carries the
   page's state as two short chips; on the left two tabs, Sections (sales-blocks.js) and Price and photos; on the right
   her page, large, as a canvas, with the section toolbar over the section chosen. Tokens everywhere but the bar: its
   blue is written out, as Course mode's is, so it is the same in every store and both modes and changing the sales
   page is never mistaken for the shop. --bar-h is the bar's height here, so the in-place bar (.sl-inplace) sticks
   under it as it does under the Operator top bar. */
body.shell-sales { margin: 0; --bar-h: 56px; --bar-seen: 56px; background: var(--bg); }
.se-report { margin: 0; display: flex; justify-content: flex-end; padding: 0 var(--space-4); background: var(--bg); }
.se-report a { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--control-h); padding: 0 var(--space-2);
  border-radius: var(--radius-control); color: var(--muted); font-size: var(--text-s); text-decoration: none; }
.se-report a:hover { background: var(--surface); color: var(--ink); text-decoration: underline; }
.se { min-height: 100vh; display: flex; flex-direction: column; }
.se-bar { position: sticky; top: 0; z-index: 20; min-height: 56px; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3);
  padding: var(--space-2) var(--space-4); background: #2b59c3; color: #fff; } /* dark-ok: the editor's bar, the same blue as Course mode's */
.se-bar a { color: #fff; } /* dark-ok */
/* On a phone the bar wraps to several rows, a third of the screen: it scrolls away with the page there. */
@media (max-width: 47.99rem) { .se-bar { position: static; } body.shell-sales { --bar-seen: 0px; } }
/* Every control wraps its words: at the sheet's largest text a 360 px phone has no room for "Send to Copycat Go" on one
   line, and a button that will not wrap (the base rule's nowrap) is what scrolls a page sideways. */
.se-bar > *, .se-panel > * { min-width: 0; max-width: 100%; }
.se button, .se .button, .se-back, .se-see { white-space: normal; max-width: 100%; overflow-wrap: anywhere; text-align: center; }
.se-back { display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--space-3); border: 2px solid #fff; border-radius: var(--radius-control);
  text-decoration: none; font-weight: var(--weight-strong); } /* dark-ok */
.se-back:hover { background: rgb(255 255 255 / .12); } /* dark-ok */
.se-title { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; flex: 1 1 14rem; line-height: 1.25; }
.se-title strong { font-size: var(--text-m); }
.se-sub { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); font-size: var(--text-s); color: #dbe5ff; overflow-wrap: anywhere; } /* dark-ok */
.se-chip { display: inline-flex; align-items: center; gap: var(--space-2); padding: 0 var(--space-2); border-radius: var(--radius-pill); line-height: 1.7;
  background: rgb(255 255 255 / .16); color: #fff; font-weight: var(--weight-label); } /* dark-ok */
.se-chip.sl-html-state::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: #cbd5e1; } /* dark-ok */
.se-chip.sl-html-live::before { background: #4ade80; } /* dark-ok */
.se-chip.sl-html-waiting::before { background: #fbbf24; } /* dark-ok */
.se-chip.sl-html-sent_back::before { background: #fca5a5; } /* dark-ok */
.se-chip-draft { background: #fff3c4; color: #5a4300; } /* dark-ok: 8.9:1 */
.se-chip-draft[hidden] { display: none; }
.se-status { margin: 0; flex: 0 1 12rem; min-width: 0; font-size: var(--text-s); color: #dbe5ff; overflow-wrap: anywhere; } /* dark-ok */
.se-see { display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--space-2); text-underline-offset: 3px; }
.se-save, .se-preview { min-height: 44px; padding: 0 var(--space-3); border: 2px solid #fff; background: transparent; color: #fff; } /* dark-ok */
.se-save[hidden] { display: none; }
.se-save:hover, .se-preview:hover { background: rgb(255 255 255 / .12); color: #fff; } /* dark-ok */
.se-send { min-height: 44px; padding: 0 var(--space-4); border: 0; border-radius: var(--radius-control); background: #fff; color: #1f3f8a; font-weight: var(--weight-strong); } /* dark-ok */
.se-send:hover { background: #e8eefc; color: #1f3f8a; } /* dark-ok */
.se .flash { margin: 0 0 var(--space-3); }
.se > .flash { margin: var(--space-3) var(--space-4) 0; }
.se-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-3); padding: var(--space-3); flex: 1; align-items: start; }
@media (min-width: 64rem) {
  .se-body { grid-template-columns: minmax(0, 19rem) minmax(0, 1fr); gap: var(--space-4); padding: var(--space-3) var(--space-4); }
  .se-side { position: sticky; top: calc(var(--bar-seen) + var(--space-3)); max-height: calc(100vh - var(--bar-seen) - var(--space-5)); overflow-y: auto; overscroll-behavior: contain; }
}
.se-main { min-width: 0; }
.se-main .sl-said:empty { display: none; }
/* The box's label names the editor for every control (Record, the previews); on screen the canvas says what it is. */
.se-main .sl-html-field > label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.se-notice { margin: 0 0 var(--space-3); }
.se-removed { padding: 0 var(--space-3); border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--surface); }
.se-removed summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; font-weight: var(--weight-label); }
.se-removed ul { margin: 0 0 var(--space-2); }

/* The left column: two tabs (shown by sales-draft.js; with no script both panels show, one under the other). */
.se-side { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.se-tabs { display: flex; gap: var(--space-1); padding: var(--space-1); border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--surface); }
.se-tabs[hidden] { display: none; }
.se-tab { flex: 1 1 0; min-height: 44px; padding: 0 var(--space-2); border: 0; border-radius: calc(var(--radius-control) - 2px); background: transparent; color: var(--muted); font-weight: var(--weight-label); }
.se-tab:hover { color: var(--ink); background: var(--bg); }
.se-tab[aria-pressed="true"] { background: var(--brand); color: var(--brand-ink); }
.se-tabpanel { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.se-tabpanel[hidden] { display: none; }
.se-tabpanel-h { margin: 0; font-size: var(--text-m); }
.se-tabs:not([hidden]) ~ .se-tabpanel .se-tabpanel-h { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.se-panel { min-width: 0; padding: var(--space-3); border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.se-panel h2 { margin: 0 0 var(--space-2); font-size: var(--text-m); }
.se-panel .hint { margin: 0 0 var(--space-2); }
.se-panel .sl-ask { margin: 0 0 var(--space-2); }
.se-panel button, .se-panel summary { min-height: 44px; }
.se-panel summary { display: flex; align-items: center; cursor: pointer; }
.se-panel .flash { margin: 0 0 var(--space-2); }
.se-field { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; margin: 0 0 var(--space-2); }
.se-field input { width: 100%; max-width: 100%; min-height: 44px; }
.se-field .hint, .se-field .field-error { margin: 0; }
.se-plan { min-width: 0; margin: 0 0 var(--space-2); }
.se-panel-act { margin: 0; }
/* Row 19.27 (card #342): her Q&A webinar and email list pages, a fieldset each, a box's words wrapping on a phone. */
.se-field textarea { width: 100%; max-width: 100%; min-width: 0; }
.se-pages-set { margin: 0 0 var(--space-3); padding: var(--space-2) var(--space-2) var(--space-1); }
.se-pages-set legend { overflow-wrap: anywhere; }
.se-see-page { margin: 0 0 var(--space-2); }
.se-see-page a { display: inline-flex; align-items: center; min-height: 44px; overflow-wrap: anywhere; }
.se-photos { list-style: none; margin: 0; padding: 0; }
.se-photo { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-1); padding: var(--space-2) 0; border-top: 1px solid var(--line); min-width: 0; max-width: 100%; }
.se-photo-label { font-weight: var(--weight-strong); }
.se-photo img { display: block; max-width: 100%; width: auto; height: auto; max-height: 5rem; border-radius: var(--radius-control); }
.se-photo-url { display: block; max-width: 100%; font-size: var(--text-xs); color: var(--muted); overflow-wrap: anywhere; word-break: break-all; }
.se-copied:empty { display: none; }
.se-copied { font-size: var(--text-s); color: var(--muted); }
.se-photo-change { width: 100%; min-width: 0; }
.se-photo-change input[type="file"] { max-width: 100%; }
.se-photo-change label { display: block; margin: var(--space-1) 0; }

/* The canvas: the editor's bar as one row of tools (HTML code, Undo, Redo on the left; the preview's widths on the
   right), and her page filling the rest of the window. Find and replace and Plain box show only with the code. */
.shell-sales .sl-editor-bar { gap: var(--space-2); }
.shell-sales .sl-editor-bar button { flex: none; display: inline-flex; align-items: center; gap: var(--space-2); padding: 0 var(--space-3); white-space: nowrap; overflow-wrap: normal; }
.shell-sales .sl-editor-bar svg { flex: none; width: 18px; height: 18px; }
.shell-sales .sl-editor-bar button[hidden] { display: none; }
.shell-sales .sl-editor-group:not(.sb-switch) { margin-left: auto; }
.shell-sales .sl-editor-group { gap: 0; }
.shell-sales .sl-editor-group > button { border-radius: 0; }
.shell-sales .sl-editor-group > button:first-child { border-radius: var(--radius-control) 0 0 var(--radius-control); }
.shell-sales .sl-editor-group > button:last-child { border-radius: 0 var(--radius-control) var(--radius-control) 0; }
.shell-sales .sl-editor-group > button:only-child { border-radius: var(--radius-control); }
.shell-sales .sl-editor-panes.sb-visual { grid-template-columns: minmax(0, 1fr); }
.shell-sales .sl-preview { position: relative; padding: 0; border: 0; background: transparent; }
.shell-sales .sl-preview > .sl-preview-head, .shell-sales .sl-preview > .hint { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.shell-sales .sl-preview-frame { height: max(420px, calc(100vh - var(--bar-seen) - 13rem)); min-height: 420px; border: 1px solid var(--line-strong); border-radius: var(--radius-control);
  background: #fff; box-shadow: var(--shadow-raised); } /* dark-ok: her page's own paper */
.shell-sales .sl-editor-code .cm-editor { max-height: max(420px, calc(100vh - var(--bar-seen) - 13rem)); }
.sl-editor-code[hidden] { display: none; }
/* The tablet's width for the preview (row 19.20), between a computer's and a phone's. */
.sl-preview-tablet { background: var(--bg); }
.sl-preview-tablet .sl-preview-frame { box-sizing: content-box; width: min(768px, calc(100% - 12px)); margin: 0 auto; border: 6px solid var(--line-strong); }

/* Row 19.20 (card #329), card #330: the sections (sales-blocks.js). In the left column, the list of her page's bands,
   top to bottom: a row is the band's number and name, a press chooses it; a hidden one says Hidden with Show. Over the
   canvas, the toolbar for the section chosen (Move up, Move down, Hide, Duplicate, Delete, Edit HTML, Add a section
   below), placed over the section's top edge from 48rem and above the page under it. Add a section and one section's
   HTML open as dialogs. What a press did is said in a note along the window's bottom. Tokens only. */
.sb { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.sb-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); margin: 0; }
.sb-count { color: var(--muted); font-size: var(--text-s); }
.sb-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
@media (max-width: 63.99rem) { .sb-list { max-height: 40vh; overflow-y: auto; overscroll-behavior: contain; } }
.sb-band { display: flex; align-items: center; gap: var(--space-1); min-width: 0; }
.se .sb-pick { flex: 1 1 auto; min-width: 0; min-height: 44px; display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) var(--space-2);
  border: 1px solid transparent; border-radius: var(--radius-control); background: transparent; color: var(--ink); text-align: left; font-weight: 400; }
.sb-pick:hover { background: var(--surface); border-color: var(--line); }
.sb-band.sb-on .sb-pick { background: var(--accent); border-color: var(--brand); }
.sb-n { flex: none; min-width: 1.9em; padding: 0 var(--space-1); border-radius: var(--radius-control); border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  font-size: var(--text-s); text-align: center; line-height: 1.7; }
.sb-band.sb-on .sb-n { border-color: var(--brand); color: var(--brand); }
.sb-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-hidden .sb-name { color: var(--muted); font-style: italic; }
.sb-tag { flex: none; padding: 0 var(--space-2); border-radius: var(--radius-pill); border: 1px solid var(--line-strong); color: var(--muted); font-size: var(--text-s); }
.sb-show { flex: none; min-height: 44px; padding: 0 var(--space-2); font-size: var(--text-s); }
.sb-none { padding: var(--space-3); color: var(--muted); }
.se .sb-add-btn { min-height: 44px; width: 100%; border: 1px dashed var(--line-strong); border-radius: var(--radius-control); background: transparent; color: var(--brand); font-weight: var(--weight-label); }
.sb-add-btn:hover { background: var(--surface); border-style: solid; }
.se .sb-add-btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); }
.sb-add-btn svg { flex: none; width: 18px; height: 18px; }
.sb-tip { margin: 0; color: var(--muted); font-size: var(--text-s); }
.sb-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); padding: var(--space-1); min-width: 0; max-width: 100%;
  border: 1px solid var(--line-strong); border-radius: var(--radius-control); background: var(--surface); color: var(--ink); box-shadow: var(--shadow-raised); }
.sb-tools[hidden] { display: none; }
/* Card #329 (Cameron, 2026-11-01: "after I click into one box, i can not get into the other"): from 48rem the bar of
   the words or picture open on the page is docked under the canvas, its controls in one row, and the canvas is made
   that much shorter (sales-blocks.js, placeEdit()): the page in it never moves when words are tapped and the bar never
   covers the words she taps next. Floating it over the canvas, under the words, covered exactly those. The section's
   toolbar is put away while it is open, one bar at a time. */
.shell-sales .sl-preview > .sl-preview-frame { order: 1; }
.shell-sales .sl-preview > .sl-preview-frame ~ * { order: 3; }
@media (min-width: 48rem) {
  .se .sl-inplace.sb-dock { order: 2; position: static; max-height: 45vh; overflow-y: auto; }
  .se .sl-inplace.sb-dock .speak { flex: 0 1 auto; }
}
.se .sl-preview:has(.sl-inplace:not([hidden])) .sb-tools { display: none; }
.sb-loose { padding: var(--space-2) var(--space-3); border: 1px solid var(--error-ink); border-radius: var(--radius-control); background: var(--surface); }
.sb-loose[hidden] { display: none; }
.sb-loose-words { margin: 0 0 var(--space-2); overflow-wrap: anywhere; }
/* Row 19.21 (card #330): copycatgo.com's page moved after the draft started; its choices in a row that wraps. */
.se-moved { display: block; }
/* Row 19.22 (card #331): Sales page and Terms, side by side in the editor's bar; the one open is marked. */
.se-pieces { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.se-pieces a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--space-3); border: 2px solid #fff; border-radius: var(--radius-control);
  text-decoration: none; font-weight: var(--weight-label); } /* dark-ok: on the editor's blue bar */
.se-pieces a:hover { background: rgb(255 255 255 / .12); } /* dark-ok */
.se-bar .se-pieces a[aria-current="page"] { background: #fff; color: #2b59c3; font-weight: var(--weight-strong); } /* dark-ok: white on the blue bar, the bar's own blue on it */
.se-moved p { margin: 0 0 var(--space-2); overflow-wrap: anywhere; }
.se-moved-acts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0; }
.se-moved-acts form { margin: 0; }
.se-moved-acts .button, .se-moved-acts button { min-height: 44px; }
@media (min-width: 48rem) { .sb-tools { position: absolute; z-index: 5; right: var(--space-3); max-width: calc(100% - 2 * var(--space-3)); } }
/* The section's name is the toolbar's group name and the outline on the page; on a phone, where the toolbar is not on
   the section, it is shown too. */
.sb-tools-name { flex: 1 1 100%; min-width: 0; margin: 0 var(--space-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--text-s); font-weight: var(--weight-strong); }
@media (min-width: 48rem) { .sb-tools-name { position: absolute; width: 1px; height: 1px; margin: 0; overflow: hidden; clip: rect(0 0 0 0); } }
.se .sb-tools button { min-height: 44px; display: inline-flex; align-items: center; gap: var(--space-2); padding: 0 var(--space-2); border: 0; border-radius: calc(var(--radius-control) - 2px);
  background: transparent; color: var(--ink); font-size: var(--text-s); box-shadow: none; white-space: nowrap; overflow-wrap: normal; }
.sb-tools button:hover:not(:disabled) { background: var(--accent); }
.sb-tools button:disabled { opacity: .4; cursor: default; }
.se .sb-tools .sb-danger { color: var(--error-ink); }
.sb-tools svg, .sb-kind svg { flex: none; width: 18px; height: 18px; }
.shell-sales .sb-said { position: fixed; left: 50%; bottom: var(--space-5); z-index: 30; transform: translateX(-50%); width: max-content; max-width: min(36rem, calc(100% - 2 * var(--space-3)));
  margin: 0; padding: var(--space-2) var(--space-3); border-radius: var(--radius-control); background: var(--ink); color: var(--surface); box-shadow: var(--shadow-raised);
  font-size: var(--text-s); opacity: 0; pointer-events: none; transition: opacity .2s; }
.shell-sales .sb-said.sb-said-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .shell-sales .sb-said { transition: none; } }
.sb-dialog { width: min(40rem, calc(100vw - 2 * var(--space-3))); max-height: calc(100dvh - 2 * var(--space-4)); padding: var(--space-4); border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--surface); color: var(--ink); box-shadow: var(--shadow-raised); }
.sb-dialog::backdrop { background: rgb(16 24 40 / .45); }
.sb-dialog h2 { margin: 0 0 var(--space-1); font-size: var(--text-l); overflow-wrap: anywhere; }
.sb-dialog p { margin: 0; }
.sb-kinds { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr)); gap: var(--space-2); margin: var(--space-3) 0; }
.se .sb-kind { min-height: 88px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-1); padding: var(--space-2);
  border: 1px solid var(--line); border-radius: var(--radius-control); background: var(--bg); color: var(--ink); font-weight: var(--weight-label); }
.sb-kind:hover, .sb-kind:focus-visible { border-color: var(--brand); background: var(--accent); }
.sb-kind svg { width: 44px; height: 30px; color: var(--muted); }
.sb-dialog-acts { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); margin: 0; }
.sb-dialog-acts button { min-height: 44px; }
.sb-code-dialog { width: min(64rem, calc(100vw - 2 * var(--space-3))); }
.sb-code { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.sb-code-box { width: 100%; max-width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: var(--text-s); }
.sb-code-editor .cm-editor { max-width: 100%; max-height: 60vh; }
.sb-switch button[aria-pressed="true"] { background: var(--brand); color: var(--brand-ink); border-color: var(--brand); }

/* Row 19.25 (card #338): Admin › Pricing › Coupons, one card per code, its words and facts wrapping on a phone;
   a Purrk's "How much off" box hidden while Months free is chosen (without :has() it shows, and is not sent). */
.pr-coupons { margin: 0 0 var(--space-3); overflow-wrap: anywhere; }
.cp-back { margin: 0 0 var(--space-3); }
.cp-intro { max-width: 60rem; }
.cp-one h2, .cp-one-page h2 { overflow-wrap: anywhere; }
.cp-state { font-size: var(--text-s); font-weight: normal; }
.cp-one.retired .cp-words { color: var(--muted); }
.cp-words { margin: 0; font-weight: var(--weight-label); overflow-wrap: anywhere; }
.cp-facts { margin: 0; padding-left: var(--space-4); }
.cp-facts li { margin-bottom: var(--space-1); overflow-wrap: anywhere; }
.cp-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.cp-actions > *, .cp-actions form.inline button { margin: 0; }
.cp-actions .button, .cp-actions button, .cp-ask button, .cp-end button { min-width: 0; max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
.cp-kinds legend { overflow-wrap: anywhere; }
.cp-form input[type="date"], .cp-change-form input[type="date"], .cp-form select, #cp-change-form select, #cp-change-form input[type="date"] { max-width: 100%; }
.cp-form:has(#cp-kind-free_months:checked) .cp-off-field { display: none; }
