@charset "UTF-8";
/* app.scss
   Outline: single entry point; partial order matters — tokens first,
     print last so its overrides win.
   Purpose: `npm run css` → sass src/styles/app.scss public/css/app.css.
     The compiled file is a committed build artifact — never hand-edit
     it; rerun the script after any change here. */
/* _tokens.scss
   Outline: every design token as a CSS custom property — light palette on
     :root, dark palette as an override on [data-theme="dark"]. Type, spacing,
     radii, shadows, focus, touch-target, layout widths.
   Purpose: single source of truth; no raw values anywhere else.
   Why custom properties: dark mode is a property swap, not a 2nd stylesheet.
     When the manual toggle ships, mirror the dark block inside
     `@media (prefers-color-scheme: dark) { :root:not([data-theme=light]) }`
     (an SCSS mixin keeps the two in sync). */
:root {
  /* type ------------------------------------------------------------ */
  --font-ui: "Instrument Sans", system-ui, -apple-system, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace;
  --text-xs: 0.75rem; /* 12 — captions, th labels. Floor; never smaller */
  --text-sm: 0.84375rem; /* 13.5 — secondary UI, numerics */
  --text-base: 0.9375rem; /* 15 — body */
  --text-md: 1.0625rem; /* 17 — section headings */
  --text-lg: 1.375rem; /* 22 — sub-page titles, big numerals */
  --text-xl: 1.75rem; /* 28 — page h1 */
  --weight-normal: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --leading-tight: 1.25;
  --leading-base: 1.5;
  /* spacing (4px base) ---------------------------------------------- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-7: 2rem;
  --space-8: 2.5rem;
  --space-9: 3.5rem;
  /* radii ------------------------------------------------------------ */
  --radius-sm: 6px; /* inputs, cell-edit */
  --radius-md: 9px; /* buttons */
  --radius-lg: 12px; /* cards, dialogs */
  --radius-full: 999px; /* badges */
  /* layout ----------------------------------------------------------- */
  --content-narrow: 45rem;
  --content-wide: 60rem;
  --touch-target: 44px; /* min hit size on kitchen surfaces */
  /* light palette ------------------------------------------------------
     Butter & Ink: near-black warm ink on paper white; butter accent.
     accent       = fills (primary buttons, focus) — pair with on-accent ink
     accent-strong= text-safe accent (links, ghost buttons) — AA on surface
     accent-soft  = washes (active nav, hovers, highlights) */
  --color-bg: #fbfaf7;
  --color-surface: #ffffff;
  --color-surface-sunken: #f6f3ec; /* add-form strips, tfoot, page wells */
  --color-text: #17140f;
  --color-text-muted: #6e675c;
  --color-border: #e8e4db;
  --color-border-strong: #d3cdc0;
  --color-accent: #f0b429;
  --color-accent-hover: #e2a61c;
  --color-accent-strong: #7d5b04;
  --color-accent-soft: #faf0d0;
  --color-on-accent: #241a04;
  --color-success: #2e7d43;
  --color-success-soft: #e3f1e6;
  --color-warn: #a06c0a;
  --color-warn-soft: #f6ecd2;
  --color-danger: #b3362b;
  --color-danger-hover: #9c2c22;
  --color-danger-soft: #f9e7e4;
  --color-on-danger: #ffffff;
  /* effects ------------------------------------------------------------ */
  --shadow-1: 0 1px 2px rgba(23, 20, 15, 0.05);
  --shadow-2: 0 1px 2px rgba(23, 20, 15, 0.05), 0 4px 16px rgba(23, 20, 15, 0.06);
  --focus-ring: 2px solid var(--color-accent-strong);
  --focus-ring-offset: 2px;
}

/* dark palette — warm dark (hint of brown in the blacks), per direction.
   accent-strong flips to a LIGHT butter so links/ghosts stay readable.
   One mixin, two emission sites (U11): the explicit [data-theme=dark]
   attribute (manual toggle) and the OS preference when the user hasn't
   chosen — :root:not([data-theme=light]) inside the media query, so an
   explicit light choice always beats a dark OS. */
[data-theme=dark] {
  --color-bg: #1a1712;
  --color-surface: #241f18;
  --color-surface-sunken: #1f1b14;
  --color-text: #f1ede4;
  --color-text-muted: #a79e8e;
  --color-border: #363026;
  --color-border-strong: #4b4334;
  --color-accent: #f3c14b;
  --color-accent-hover: #eeb838;
  --color-accent-strong: #f7d077;
  --color-accent-soft: #3b321a;
  --color-on-accent: #241a04;
  --color-success: #6cc183;
  --color-success-soft: #23362a;
  --color-warn: #d9a83e;
  --color-warn-soft: #3a2f18;
  --color-danger: #e2766b;
  --color-danger-hover: #e98c82;
  --color-danger-soft: #422522;
  --color-on-danger: #2b0f0b;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-2: 0 1px 2px rgba(0, 0, 0, 0.3), 0 6px 20px rgba(0, 0, 0, 0.25);
  --focus-ring: 2px solid var(--color-accent-strong);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme=light]) {
    --color-bg: #1a1712;
    --color-surface: #241f18;
    --color-surface-sunken: #1f1b14;
    --color-text: #f1ede4;
    --color-text-muted: #a79e8e;
    --color-border: #363026;
    --color-border-strong: #4b4334;
    --color-accent: #f3c14b;
    --color-accent-hover: #eeb838;
    --color-accent-strong: #f7d077;
    --color-accent-soft: #3b321a;
    --color-on-accent: #241a04;
    --color-success: #6cc183;
    --color-success-soft: #23362a;
    --color-warn: #d9a83e;
    --color-warn-soft: #3a2f18;
    --color-danger: #e2766b;
    --color-danger-hover: #e98c82;
    --color-danger-soft: #422522;
    --color-on-danger: #2b0f0b;
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-2: 0 1px 2px rgba(0, 0, 0, 0.3), 0 6px 20px rgba(0, 0, 0, 0.25);
    --focus-ring: 2px solid var(--color-accent-strong);
  }
}
/* _base.scss
   Outline: minimal reset, element defaults, typography, focus ring,
     visually-hidden utility, .no-print marker class.
   Purpose: everything an unclassed element gets for free. */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--text-base);
  line-height: var(--leading-base);
  color: var(--color-text);
  background: var(--color-bg);
}

h1,
h2,
h3 {
  margin: 0;
  line-height: var(--leading-tight);
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 {
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
}

h2 {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
}

h3 {
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
}

p {
  margin: 0;
  text-wrap: pretty;
}

a {
  color: var(--color-accent-strong);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* one focus treatment everywhere — never remove, only restyle */
:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-ring-offset);
  border-radius: var(--radius-sm);
}

/* numerals: mono everywhere a number column appears */
.num {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}

.muted {
  color: var(--color-text-muted);
}

/* a11y — load-bearing, do not restyle away */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* _nav.scss
   Outline: app shell top nav — wordmark, links w/ active state, identity +
     logout, theme toggle slot. Wraps to two rows under 760px with a
     horizontally scrollable link row (no JS).
   Purpose: replaces today's plain-links nav; brand presence via wordmark. */
.app-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-6);
  padding: 0 var(--space-6);
  min-height: 56px;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}

.app-nav__wordmark {
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  letter-spacing: -0.02em;
  color: var(--color-text);
  text-decoration: none;
  white-space: nowrap;
}

.app-nav__wordmark:hover {
  text-decoration: none;
}

/* the butter dot — entire brand mark; cheap, scales, works in print-none */
.app-nav__wordmark::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--color-accent);
  margin-left: 3px;
}

.app-nav__links {
  display: flex;
  gap: var(--space-1);
  flex: 1;
  overflow-x: auto;
  scrollbar-width: none;
}

.app-nav__links::-webkit-scrollbar {
  display: none;
}

.app-nav__link {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text-muted);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  white-space: nowrap;
}

.app-nav__link:hover {
  text-decoration: none;
  color: var(--color-text);
  background: var(--color-surface-sunken);
}

.app-nav__link.is-active {
  color: var(--color-text);
  font-weight: var(--weight-semibold);
  background: var(--color-accent-soft);
}

.app-nav__user {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.app-nav__company {
  opacity: 0.75;
}

/* User menu — the display-name dropdown (markup: partials/nav.hbs, behaviour:
   public/js/user-menu.js). The wrapper anchors the absolutely-positioned
   panel. */
.user-menu {
  position: relative;
}

.user-menu__trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font: inherit;
  color: var(--color-text);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-2);
  cursor: pointer;
  white-space: nowrap;
}

.user-menu__trigger:hover,
.user-menu__trigger[aria-expanded=true] {
  background: var(--color-surface-sunken);
  border-color: var(--color-border);
}

.user-menu__caret {
  font-size: 0.7em;
  color: var(--color-text-muted);
  transition: transform 0.12s ease;
}

.user-menu__trigger[aria-expanded=true] .user-menu__caret {
  transform: rotate(180deg);
}

.user-menu__panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: 20;
  min-width: 12rem;
  /* `relative`-equivalent positioning context for the floating close button;
     `absolute` already establishes one, so its X anchors to this panel. */
  padding: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}

/* The panel toggles via the `hidden` attribute (set by user-menu.js). Author
   `display: flex` above outranks the UA stylesheet's `[hidden]{display:none}`,
   so we must re-assert it here — otherwise the panel is permanently visible.
   This selector is more specific than `.user-menu__panel`, so hidden wins. */
.user-menu__panel[hidden] {
  display: none;
}

/* Menu rows live in their own flow container so the floating close button
   (taken out of flow below) can't claim a row or disturb their spacing.
   The top padding reserves a clear corner band for the X. */
.user-menu__items {
  display: flex;
  flex-direction: column;
}

/* Close (X) floats in the panel's top-right corner, out of normal flow. */
.user-menu__close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  font-size: var(--text-md);
  line-height: 1;
  color: var(--color-text-muted);
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  cursor: pointer;
}

.user-menu__close:hover {
  background: var(--color-surface-sunken);
  color: var(--color-text);
}

.user-menu__form {
  margin: 0;
}

.user-menu__item {
  display: block;
  width: 100%;
  text-align: left;
  box-sizing: border-box;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text);
  text-decoration: none;
  font: inherit;
  white-space: nowrap;
}

.user-menu__item:hover {
  background: var(--color-surface-sunken);
}

/* The logout item is a submit button; strip the native chrome so it reads as
   a menu row identical to the links beside it. */
.user-menu__item--button {
  background: transparent;
  border: 0;
  cursor: pointer;
}

/* small screens: wordmark+user on row 1, links scroll on row 2 */
@media (max-width: 760px) {
  .app-nav {
    padding: var(--space-2) var(--space-4);
  }
  .app-nav__links {
    order: 3;
    flex-basis: 100%;
    margin: 0 calc(-1 * var(--space-4));
    padding: 0 var(--space-4) var(--space-2);
  }
  .app-nav__user {
    margin-left: auto;
  }
  .app-nav__company {
    display: none;
  } /* identity shrinks to name */
}
/* _buttons.scss
   Outline: .button + variants (primary / secondary / ghost / danger),
     sizes (sm / kitchen-tall), icon-only buttons, block. Applies identically
     to <button>, <a class="button">, and <button> inside POST <form>s.
   Purpose: one button system; kills today's ad-hoc sizing + inline-styled
     POST forms (give the form .inline-form instead of style attrs). */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  line-height: 1;
  padding: 0 var(--space-4);
  height: 36px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: none;
  color: var(--color-text);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}

.button:hover {
  text-decoration: none;
}

.button--primary {
  background: var(--color-accent);
  color: var(--color-on-accent);
}

.button--primary:hover {
  background: var(--color-accent-hover);
}

.button--secondary {
  background: var(--color-surface);
  border-color: var(--color-border-strong);
}

.button--secondary:hover {
  background: var(--color-surface-sunken);
}

.button--ghost {
  color: var(--color-accent-strong);
}

.button--ghost:hover {
  background: var(--color-accent-soft);
}

/* destructive: soft by default; solid reserved for dialog confirm */
.button--danger {
  background: var(--color-danger-soft);
  color: var(--color-danger);
}

.button--danger:hover {
  background: var(--color-danger);
  color: var(--color-on-danger);
}

.button--danger-solid {
  background: var(--color-danger);
  color: var(--color-on-danger);
}

.button--danger-solid:hover {
  background: var(--color-danger-hover);
}

.button[disabled],
.button.is-disabled {
  opacity: 0.45;
  cursor: default;
  pointer-events: none;
}

/* sizes */
.button--sm {
  height: 30px;
  padding: 0 var(--space-3);
  font-size: var(--text-xs);
}

.button--block {
  width: 100%;
}

/* kitchen height — production run / calculator primary actions */
.button--tall {
  height: var(--touch-target);
  font-size: var(--text-base);
}

/* icon-only (↑ ↓ ×) — always with aria-label; ghost until hovered */
.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.icon-button:hover {
  color: var(--color-text);
  background: var(--color-surface-sunken);
}

.icon-button--danger:hover {
  color: var(--color-danger);
  background: var(--color-danger-soft);
}

/* POST-action forms sit inline next to links/buttons without style attrs */
.inline-form {
  display: inline-flex;
}

/* button clusters (page actions, card header controls) */
.button-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

@media (pointer: coarse) {
  .icon-button {
    width: 36px;
    height: 36px;
  }
}
/* _google-button.scss
   Outline: a CSS replica of Google's RENDERED "Sign in with Google" button
     (pill shape, outline/light theme, large size, 4-colour G logo on the
     left) for the login page.
   Purpose: people recognize Google's own button, so we match its look — but
     it stays a plain <a> into our existing authorization-code flow
     (GET /auth/google). We deliberately do NOT load Google's GIS client
     library (that would swap us onto Google's ID-token credential flow);
     we only borrow the appearance.

   Design notes (a deliberate exception to the Butter & Ink tokens, UI.MD):
     - Google's brand spec: white fill, 1px #747775 border, fully-rounded
       pill, 40px tall, #1f1f1f label at 14px/500 in Google's own
       'Roboto', arial, sans-serif fallback chain (we don't bundle Roboto).
     - The button sizes to its CONTENT and is centred in the card (the .p
       wrapper centres it) — exactly how Google renders it. We do NOT stretch
       it full-width; stretching is what made an earlier attempt look broken. */
/* Centring wrapper: the button is inline-level, so text-align centres it. */
.google-signin {
  text-align: center;
}

.gsi-material-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 40px;
  padding: 0 16px 0 12px; /* a touch more breathing room after the label */
  box-sizing: border-box;
  background-color: #fff;
  border: 1px solid #747775;
  border-radius: 20px; /* == half the height -> full pill */
  color: #1f1f1f;
  font-family: "Roboto", arial, sans-serif;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.25px;
  line-height: 1;
  text-decoration: none; /* it's a link, not a button element */
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.218s, border-color 0.218s, box-shadow 0.218s;
}

/* The 4-colour G logo, sized to Google's 20px icon box. */
.gsi-material-button-icon {
  display: inline-flex;
  height: 20px;
  width: 20px;
}

.gsi-material-button-icon svg {
  display: block;
  height: 20px;
  width: 20px;
}

/* Google's subtle hover/active feedback: a faint grey wash + lift. */
.gsi-material-button:hover {
  background-color: #f7f8f8;
  box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);
}

.gsi-material-button:active {
  background-color: #f1f3f4;
}

.gsi-material-button:focus-visible {
  outline: 2px solid #4285f4;
  outline-offset: 2px;
}

/* _badges.scss
   Outline: .badge + tone modifiers. Used for run status, roles, multi /
     bake-together markers, read-only flag.
   Purpose: status is data from the presenter (label) + one class. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.02em;
  line-height: 1;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  background: var(--color-surface-sunken);
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* Planned, roles, informational highlights */
.badge--accent {
  background: var(--color-accent-soft);
  color: var(--color-accent-strong);
}

/* Completed */
.badge--success {
  background: var(--color-success-soft);
  color: var(--color-success);
}

/* Read only, caution states */
.badge--warn {
  background: var(--color-warn-soft);
  color: var(--color-warn);
}

.badge--danger {
  background: var(--color-danger-soft);
  color: var(--color-danger);
}

/* _tables.scss
   Outline: .table — the dominant surface. Numeric columns (.num), sortable
     headers (aria-sort driven), row hover, empty / no-matches rows, tfoot
     summary rows, kitchen density via .page--kitchen or coarse pointers.
   Purpose: comfortable desktop density, finger-sized kitchen rows. */
.table-wrap {
  overflow-x: auto; /* long ingredient tables survive narrow screens */
}

.table {
  width: 100%;
  border-collapse: collapse;
}

.table th {
  text-align: left;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  padding: var(--space-3) var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}

.table td {
  font-size: var(--text-base);
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

/* numeric columns right-align; .num sets mono (see _base) */
.table th.num,
.table td.num {
  text-align: right;
}

/* edge cells pick up the card's inner padding */
.table th:first-child,
.table td:first-child {
  padding-left: var(--space-5);
}

.table th:last-child,
.table td:last-child {
  padding-right: var(--space-5);
}

.table tbody tr:hover > td {
  background: color-mix(in oklab, var(--color-accent-soft) 35%, transparent);
}

/* summary rows (batch weight, totals) */
.table tfoot td {
  font-weight: var(--weight-semibold);
  background: var(--color-surface-sunken);
  border-bottom: none;
}

/* sortable headers — real <button> inside th, state via aria-sort on th */
th.sortable {
  padding: 0;
}

th.sortable .sort-button {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  width: 100%;
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: var(--space-3) var(--space-3) var(--space-2);
  cursor: pointer;
}

th.sortable .sort-button:hover {
  color: var(--color-text);
}

th.sortable .sort-button::after {
  content: "↕";
  opacity: 0.4;
  font-size: var(--text-xs);
}

th.sortable[aria-sort=ascending] .sort-button::after {
  content: "↑";
  opacity: 1;
}

th.sortable[aria-sort=descending] .sort-button::after {
  content: "↓";
  opacity: 1;
}

th.sortable[aria-sort=ascending] .sort-button,
th.sortable[aria-sort=descending] .sort-button {
  color: var(--color-accent-strong);
}

/* empty + no-matches rows */
.table .empty-row td {
  padding: var(--space-7) var(--space-5);
  text-align: center;
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

/* free-form notes column (ingredients / supplies lists): pre-wrap
   preserves user-entered newlines, max-width keeps long notes from
   blowing the table out, muted keeps primary columns dominant */
.table td.notes {
  white-space: pre-wrap;
  max-width: 20rem;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* row controls column (move/delete) */
.table .row-controls {
  white-space: nowrap;
  text-align: right;
  width: 1%;
}

/* kitchen density — production run, calculator, mix screens */
.page--kitchen .table td {
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
  font-size: var(--text-md);
}

@media (pointer: coarse) {
  .table td {
    padding-top: var(--space-3);
    padding-bottom: var(--space-3);
  }
}
/* _cell-edit.scss
   Outline: click-to-edit numeric cells. <button class="cell-edit"
     data-edit="..."> swaps to <input class="cell-edit-input"> on click
     (Enter/blur commits, Esc cancels) — mechanic unchanged, affordance new.
   States: idle (dashed butter underline), hover (butter wash), editing
     (input + ring), .cell-edit--static (non-editable: reference rows,
     read-only users — honest plain text, no fake affordance).
   Purpose: replaces the bare dotted underline with something that reads
     "tap me" at arm's length without shouting on a 30-row table. */
.cell-edit {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
  background: none;
  border: none;
  border-bottom: 1.5px dashed color-mix(in oklab, var(--color-accent) 55%, transparent);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  padding: 2px var(--space-1);
  cursor: pointer;
}

.cell-edit:hover {
  background: var(--color-accent-soft);
  border-bottom-color: var(--color-accent);
  border-bottom-style: solid;
}

.cell-edit-input {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  width: 5.5em;
  text-align: right;
  color: var(--color-text);
  background: var(--color-surface);
  border: 1.5px solid var(--color-accent);
  border-radius: var(--radius-sm);
  padding: 1px var(--space-1);
  outline: 2px solid color-mix(in oklab, var(--color-accent) 30%, transparent);
  outline-offset: 1px;
}

/* text variant (staff display names) — same affordance, but names are
   words, not numbers: UI face, left-aligned, room to breathe */
.cell-edit--text,
.cell-edit-input--text {
  font-family: var(--font-ui);
  font-size: var(--text-base);
  font-variant-numeric: normal;
  text-align: left;
}

.cell-edit-input--text {
  width: 14rem;
}

/* non-editable variant — same metrics, zero affordance */
.cell-edit--static {
  border-bottom-color: transparent;
  cursor: default;
}

.cell-edit--static:hover {
  background: none;
  border-bottom-color: transparent;
}

/* kitchen variant — bordered pill, full touch target */
.page--kitchen .cell-edit,
.cell-edit--kitchen {
  min-height: var(--touch-target);
  min-width: 56px;
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  border: 1.5px dashed color-mix(in oklab, var(--color-accent) 55%, transparent);
  border-radius: var(--radius-md);
  background: color-mix(in oklab, var(--color-accent-soft) 50%, transparent);
  padding: var(--space-1) var(--space-3);
}

.page--kitchen .cell-edit:hover,
.cell-edit--kitchen:hover {
  border-style: solid;
  background: var(--color-accent-soft);
}

.page--kitchen .cell-edit-input,
.cell-edit-input--kitchen {
  min-height: var(--touch-target);
  font-size: var(--text-md);
  width: 4.5em;
}

.page--kitchen .cell-edit--static,
.cell-edit--static.cell-edit--kitchen {
  border-color: transparent;
  background: none;
}

/* _forms.scss
   Outline: stacked labeled fields, hints, required marks, validation error
     list, large Markdown textarea, callouts (derived-pricing notice, cycle
     warning), inline builder add-forms, list-page search input.
   Purpose: forms read as one calm column; errors are one obvious block. */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-bottom: var(--space-5);
}

.field > label {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

.field .required {
  color: var(--color-danger);
  font-weight: var(--weight-bold);
}

.field .hint {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* Inline checkbox + label (e.g. the sign-up "I agree to the Terms" row).
   Overrides the stacked-column default so the box sits beside its text. */
.field--check > label {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-weight: var(--weight-normal);
}

.field--check input[type=checkbox] {
  flex: none;
}

.input,
.select,
.textarea {
  font-family: var(--font-ui);
  font-size: var(--text-base);
  color: var(--color-text);
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  min-height: 38px;
  width: 100%;
  max-width: 28rem;
}

.input::placeholder {
  color: var(--color-text-muted);
}

.input:focus-visible,
.select:focus-visible,
.textarea:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 0;
  border-color: var(--color-accent-strong);
}

.input--num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  max-width: 9rem;
}

/* Markdown preparation editor */
.textarea--markdown {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.6;
  min-height: 16rem;
  max-width: 100%;
  resize: vertical;
}

/* validation errors — one block above the form */
.form-errors {
  margin: 0 0 var(--space-5);
  padding: var(--space-3) var(--space-4);
  background: var(--color-danger-soft);
  border: 1px solid color-mix(in oklab, var(--color-danger) 30%, transparent);
  border-radius: var(--radius-md);
  color: var(--color-danger);
  font-size: var(--text-sm);
}

.form-errors ul {
  margin: 0;
  padding-left: var(--space-4);
}

/* callouts — derived pricing (info) and cycle warning (danger) */
.callout {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  background: var(--color-accent-soft);
  border: 1px solid color-mix(in oklab, var(--color-accent) 35%, transparent);
  margin-bottom: var(--space-5);
}

.callout strong {
  font-weight: var(--weight-semibold);
}

.callout--danger {
  background: var(--color-danger-soft);
  border-color: color-mix(in oklab, var(--color-danger) 30%, transparent);
  color: var(--color-danger);
}

/* success confirmation (e.g. admin "plan limits saved") */
.callout--success {
  background: var(--color-success-soft);
  border-color: color-mix(in oklab, var(--color-success) 30%, transparent);
  color: var(--color-success);
}

/* downgrade trim wizard: dense keep/drop checkbox list (tiered plans) */
.trim-list {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--space-1) var(--space-4);
}

.trim-list__item label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) 0;
  cursor: pointer;
}

/* inline add-forms under builder tables */
.builder-add-form {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  background: var(--color-surface-sunken);
  border-top: 1px solid var(--color-border);
}

.builder-add-form .input {
  min-width: 0;
}

.builder-add-form .input--num {
  flex: 0 0 6rem;
}

/* the add-section form is a card of its own (no table above it), so
   the strip's separating border would double the card edge */
.builder-add-form.builder-add-section {
  border-top: none;
}

@media (max-width: 560px) {
  .builder-add-form {
    flex-wrap: wrap;
  }
  .builder-add-form .typeahead {
    flex-basis: 100%;
  }
}
/* list-page search */
.list-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.list-toolbar .input {
  max-width: 20rem;
}

/* _typeahead.scss
   Outline: combobox-pattern picker — input + absolutely-positioned results
     list. ARIA wiring (role=combobox/listbox, aria-expanded) is in the JS;
     these styles key off those attributes where possible.
   Purpose: style only — keyboard behavior and data-typeahead hook stay. */
.typeahead {
  position: relative;
  flex: 1;
  max-width: 22rem;
}

.typeahead .input {
  width: 100%;
  max-width: none;
}

.typeahead__results {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 30;
  margin: 0;
  padding: var(--space-1);
  list-style: none;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-2);
  max-height: 16rem;
  overflow-y: auto;
}

.typeahead__option {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  cursor: pointer;
}

.typeahead__option .num {
  color: var(--color-text-muted);
}

/* keyboard highlight — aria-selected on the option */
.typeahead__option:hover,
.typeahead__option[aria-selected=true] {
  background: var(--color-accent-soft);
}

.typeahead__empty {
  padding: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  text-align: center;
}

@media (pointer: coarse) {
  .typeahead__option {
    padding: var(--space-3);
  }
}
/* _cards.scss
   Outline: .section-block (builder section card), .recipe-meta definition
     list, page header (h1 + back + actions), bottom page-actions row.
   Purpose: the builder becomes a stack of cards with real rhythm — the
     <hr>-as-spacer dies here. */
.section-block {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  overflow: hidden; /* table edges + sunken add-form clip to the radius */
}

/* card whose content sits directly inside (no __header/__body split, e.g.
   the trim wizard's keep/drop sections) — pad it to the same rhythm as a
   card body so the content clears the rounded edge */
.section-block--pad {
  padding: var(--space-5);
}

.section-block__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}

.section-block__title {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.section-block__count {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* padded free-form content inside a card (prose, empty-state lines) —
   tables and add-forms manage their own edge padding instead */
.section-block__body {
  padding: var(--space-5) var(--space-6);
  margin: 0;
}

/* meta block — 2-col+ dl under the page title */
.recipe-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, max-content));
  justify-content: start;
  gap: var(--space-3) var(--space-8);
  margin: 0;
  padding: var(--space-4) var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

.recipe-meta dt {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: 0 0 2px;
}

.recipe-meta dd {
  margin: 0;
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
}

/* page header */
.page-header {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

.page-header__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/* list-header action cluster: usage meter beside the "New" button
   (tiered plans Chunk 6) */
.header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.usage-meter {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.back-link {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  align-self: flex-start;
}

.back-link:hover {
  color: var(--color-text);
}

/* bottom action cluster on detail pages — destructive actions live here,
   visually separated, never beside the primary action */
.page-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}

.page-actions__spacer {
  flex: 1;
}

@media (max-width: 560px) {
  .page-actions {
    flex-direction: column;
    align-items: stretch;
  }
  .page-actions__spacer {
    display: none;
  }
  .page-actions .button {
    height: var(--touch-target);
  }
}
/* _feedback.scss
   Outline: toast (replaces window.alert for errors/saves), confirm + input
     dialogs on native <dialog> (replaces confirm/prompt), super-admin
     acting-as banner.
   Purpose: feedback stays vanilla-JS-friendly — a toast helper and
     dialog.showModal(), nothing more. */
/* toast — fixed bottom-center, safe on phones; role="status" in markup */
/* `display: flex` below would override the UA's [hidden] { display:
   none }, leaving an empty pill visible at rest — restate it stronger.
   (feedback.js toggles the hidden attribute, never inline styles.) */
.toast[hidden] {
  display: none;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: var(--space-6);
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: min(26rem, 100vw - 2rem);
  padding: var(--space-3) var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  font-size: var(--text-sm);
}

.toast::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-success);
}

.toast--error::before {
  background: var(--color-danger);
}

.toast--error {
  color: var(--color-danger);
}

/* dialogs — native <dialog>, small card */
.dialog {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-2);
  padding: var(--space-6);
  width: min(22rem, 100vw - 2rem);
}

.dialog::backdrop {
  background: rgba(23, 20, 15, 0.45);
}

.dialog h2 {
  margin-bottom: var(--space-2);
}

.dialog p {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-5);
}

.dialog .field {
  margin-bottom: var(--space-5);
}

.dialog__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
}

/* acting-as banner — pinned above nav, impossible to miss in both themes.
   Read-only acting: warn tones. Editing enabled: danger solid. */
.admin-banner {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-6);
  background: var(--color-warn-soft);
  border-bottom: 2px solid var(--color-warn);
  color: var(--color-warn);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

.admin-banner--editing {
  background: var(--color-danger);
  border-bottom-color: var(--color-danger-hover);
  color: var(--color-on-danger);
}

.admin-banner .button {
  height: 28px;
  font-size: var(--text-xs);
}

.admin-banner__spacer {
  flex: 1;
}

/* _prose.scss
   Outline: rendered Markdown (recipe preparation — the only raw-HTML
     interpolation, sanitized server-side).
   Purpose: readable instructions; never competes with the builder tables. */
.prose {
  font-size: var(--text-base);
  line-height: 1.65;
  max-width: 42rem;
}

.prose h1,
.prose h2,
.prose h3 {
  margin: 1.4em 0 0.5em;
}

.prose h1:first-child,
.prose h2:first-child,
.prose h3:first-child {
  margin-top: 0;
}

.prose h1 {
  font-size: var(--text-lg);
}

.prose h2 {
  font-size: var(--text-md);
}

.prose h3 {
  font-size: var(--text-base);
}

.prose p {
  margin: 0 0 0.9em;
}

.prose ul,
.prose ol {
  margin: 0 0 0.9em;
  padding-left: 1.4em;
}

.prose li {
  margin-bottom: 0.3em;
}

.prose code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  background: var(--color-surface-sunken);
  border-radius: var(--radius-sm);
  padding: 0.1em 0.35em;
}

.prose pre {
  background: var(--color-surface-sunken);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  overflow: auto;
  margin: 0 0 0.9em;
}

.prose table {
  border-collapse: collapse;
  margin: 0 0 0.9em;
}

.prose th,
.prose td {
  border: 1px solid var(--color-border);
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
}

.prose th {
  background: var(--color-surface-sunken);
  font-weight: var(--weight-semibold);
}

.prose strong {
  font-weight: var(--weight-semibold);
}

/* _pages.scss
   Outline: content column (45rem; 60rem on body.wide), vertical rhythm via
     .page > gap, the few true page-specific rules (calculator quote, oven
     plan, run lines on small screens, auth card).
   Purpose: layout rhythm replaces <hr> spacers. */
.page {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  max-width: var(--content-narrow);
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-9);
}

body.wide .page {
  max-width: var(--content-wide);
}

/* wrapper divs that must not break the .page column rhythm (builder
   roots carrying data-api-base etc.) — same flow + gap as .page */
.page-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/* group title between card stacks (Ingredients / Supplies / Preparation) */
.page-section-title {
  margin-top: var(--space-4);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
}

/* ---- home dashboard --------------------------------------------------- */
/* quick-create action cards: a responsive grid of full-card links. One
   --primary (New recipe) per the one-primary-per-view rule. */
.quick-actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-4);
}

.action-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  color: var(--color-text);
  text-decoration: none;
}

.action-card:hover {
  text-decoration: none;
  border-color: var(--color-border-strong);
  background: var(--color-surface-sunken);
}

.action-card__title {
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
}

/* arrow affordance — a glyph, not an icon asset (Rationale do-not list) */
.action-card__title::after {
  content: " →";
  color: var(--color-text-muted);
}

.action-card__desc {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* the one primary action: butter wash, keeps its tone on hover */
.action-card--primary {
  background: var(--color-accent-soft);
  border-color: color-mix(in oklab, var(--color-accent) 35%, transparent);
}

.action-card--primary:hover {
  background: var(--color-accent-soft);
  border-color: var(--color-accent);
}

.action-card--primary .action-card__title::after {
  color: var(--color-accent-strong);
}

/* at-a-glance stat cards: count + label, link into each section */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: var(--space-4);
}

.stat-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4) var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  color: var(--color-text);
  text-decoration: none;
}

.stat-card:hover {
  text-decoration: none;
  border-color: var(--color-border-strong);
  background: var(--color-surface-sunken);
}

.stat-card__count {
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  line-height: 1;
}

.stat-card__label {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.stat-card__sub {
  font-size: var(--text-xs);
  color: var(--color-accent-strong);
}

/* batch-weight strip under the last ingredient section */
.batch-line {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-5);
  background: var(--color-accent-soft);
  border: 1px solid color-mix(in oklab, var(--color-accent) 35%, transparent);
  border-radius: var(--radius-lg);
  font-size: var(--text-sm);
}

.batch-line strong {
  font-weight: var(--weight-semibold);
}

.batch-line__spacer {
  flex: 1;
}

/* ---- calculator ----------------------------------------------------- */
.quote {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}

/* Two columns: BATCH (the batch being made) | PER ITEM. The header and
   every line share the same 3-track grid (label, batch, item) so the two
   value columns line up down the card. */
.quote__head,
.quote__line {
  display: grid;
  grid-template-columns: 1fr minmax(4.5rem, max-content) minmax(4.5rem, max-content);
  gap: var(--space-4);
  align-items: baseline;
  padding: var(--space-2) var(--space-6);
}

.quote__head {
  padding-top: var(--space-4);
  padding-bottom: var(--space-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
}

.quote__head .num,
.quote__line .num {
  text-align: right;
}

.quote__line-label .muted {
  display: block;
  font-size: var(--text-xs);
  font-weight: var(--weight-normal);
}

/* component-cost rows (ingredients / labor / supplies / tax): quieter */
.quote__line--sub {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.quote__line--sub .num {
  font-size: var(--text-sm);
}

/* cost subtotal: divider + emphasis */
.quote__line--cost {
  font-weight: var(--weight-semibold);
  border-top: 1px solid var(--color-border);
  margin-top: var(--space-2);
  padding-top: var(--space-3);
}

/* price: the answer — accent band, both columns loud so the batch price
   reads next to the per-item price */
.quote__line--price {
  background: var(--color-accent-soft);
  border-top: 1px solid color-mix(in oklab, var(--color-accent) 35%, transparent);
  border-bottom: 1px solid color-mix(in oklab, var(--color-accent) 35%, transparent);
  font-weight: var(--weight-semibold);
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
}

.quote__line--price .num {
  font-size: var(--text-lg);
}

/* total with tax */
.quote__line--total {
  font-weight: var(--weight-semibold);
  padding-bottom: var(--space-3);
}

/* the calculator's detailed breakdown — emphasis rows keyed off the
   presenter's `emphasis` class (subtotal / price / total) */
.calc-table tr.subtotal td {
  font-weight: var(--weight-semibold);
  border-top: 1px solid var(--color-border-strong);
}

.calc-table tr.price td {
  font-weight: var(--weight-semibold);
  color: var(--color-accent-strong);
}

.calc-table tr.total td {
  font-weight: var(--weight-semibold);
  background: var(--color-surface-sunken);
}

/* ---- production run on phones: lines collapse to cards --------------- */
@media (max-width: 640px) {
  .run-lines thead {
    display: none;
  }
  .run-lines tr {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: var(--space-1) var(--space-4);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border);
  }
  .run-lines td {
    display: block;
    border: none;
    padding: 0;
  }
  .run-lines td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
  }
  .run-lines .row-controls {
    grid-column: 2;
    grid-row: 1;
  }
}
/* ---- printable sheets (mix lists / ingredient totals) ----------------- */
/* per-section label inside a sheet card — quieter than the card title */
.mix-sheet__section {
  padding: var(--space-4) var(--space-5) 0;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}

/* the homemade add-to-run mini-form inside the totals table */
.totals-table .inline-form {
  gap: var(--space-2);
  align-items: center;
}

.totals-table .input--num {
  max-width: 5.5rem;
}

/* ---- auth ------------------------------------------------------------ */
.auth-card {
  max-width: 22rem;
  margin: 10vh auto 0;
  padding: var(--space-7);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}

.auth-card .app-nav__wordmark {
  font-size: var(--text-lg);
  display: block;
  margin-bottom: var(--space-6);
}

/* the card manages its own rhythm — base zeroes element margins */
.auth-card h1 {
  font-size: var(--text-lg);
  margin-bottom: var(--space-5);
}

.auth-card p {
  margin: 0 0 var(--space-4);
}

.auth-card p:last-child {
  margin-bottom: 0;
}

/* _onboarding.scss
   Outline: the setup wizard's input adornment row ($ / % / "/ hour" beside
     the field) and the Back/Next action row.
   Purpose: keep the focused onboarding steps visually calm and aligned with
     the rest of the form system; only a couple of layout helpers are needed
     on top of the shared .input / .button styles. */
/* A field input flanked by small unit adornments ("$" before, "%" after). */
.onboard-input {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* The input inside the row shouldn't stretch full-width — these steps ask
   for one short value, so cap it and let the adornments sit close. */
.onboard-input .input {
  max-width: 14rem;
}

.onboard-input__addon {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
}

/* Back (ghost) on the left, the primary advance/finish button on the right. */
.onboard-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

/* _signup.scss
   Outline: the Google-only sign-up page (GET /register) — a centred heading,
     two plan cards (Free / Pro) side by side, each with its own
     "Sign up with Google" button, then the Terms line.
   Purpose: let a new owner pick a tier and sign up in one screen. Uses the
     design tokens; the Google button itself is styled in _google-button.scss. */
.signup {
  max-width: 720px;
  margin: 0 auto;
}

.signup__head {
  text-align: center;
  margin-bottom: var(--space-6);
}

.signup__plans {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
}

/* Stack the cards on narrow screens. */
@media (max-width: 560px) {
  .signup__plans {
    grid-template-columns: 1fr;
  }
}
.plan-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

/* The Free card is the gentler default; give Pro a touch more emphasis. */
.plan-card--free {
  border-style: dashed;
}

.plan-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

.plan-card__name {
  margin: 0;
  font-size: var(--text-lg);
}

.plan-card__price {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-text-muted);
}

.plan-card__blurb {
  margin: 0;
  color: var(--color-text);
}

.plan-card__caps {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* Push the sign-up button to the bottom so both cards line their buttons up
   even when the blurbs differ in length. */
.plan-card .gsi-material-button {
  margin-top: auto;
}

.signup__terms {
  text-align: center;
  margin-top: var(--space-6);
}

/* _help.scss
   Outline: the Help article footer pager (prev / next links).
   Purpose: the Help index reuses .quick-actions + .action-card and the
     article body reuses .prose, so the only Help-specific rule is the
     two-ended pager row at the foot of an article. */
.help-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}

/* When there's no previous article, an empty <span> holds the left slot so
   the "next" link still sits flush right. */
.help-pager > span:empty {
  flex: 1;
}

/* _splash.scss
   Outline: the public marketing splash at "/" (pages/splash.hbs). Its own top
     bar + hero + feature grid + steps + footer — independent of the app shell.
   Purpose: a clean, on-brand landing page for logged-out visitors. Reuses the
     design tokens + .button styles; everything else is splash-scoped. */
.splash {
  max-width: 72rem;
  margin: 0 auto;
  padding: 0 var(--space-5);
}

/* Top bar */
.splash-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) 0;
}

.splash-bar__brand {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  color: var(--color-text);
  text-decoration: none;
}

.splash-bar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* Hero */
.splash-hero {
  text-align: center;
  padding: clamp(var(--space-7), 8vw, 6rem) 0 var(--space-8);
  max-width: 48rem;
  margin: 0 auto;
}

.splash-hero__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--color-accent-strong, var(--color-text-muted));
  margin: 0 0 var(--space-3);
}

.splash-hero__title {
  font-size: clamp(2rem, 6vw, 3.25rem);
  line-height: 1.1;
  margin: 0 0 var(--space-4);
}

.splash-hero__lede {
  font-size: var(--text-md);
  line-height: 1.6;
  color: var(--color-text-muted);
  margin: 0 auto var(--space-6);
  max-width: 38rem;
}

.splash-hero__cta {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
  flex-wrap: wrap;
}

.splash-hero__note {
  margin: var(--space-4) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* Feature grid */
.splash-features {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--space-5);
  padding: var(--space-7) 0;
}

.splash-feature {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
  padding: var(--space-6);
}

.splash-feature__title {
  font-size: var(--text-md);
  margin: 0 0 var(--space-2);
}

.splash-feature__desc {
  margin: 0;
  color: var(--color-text-muted);
  line-height: 1.55;
}

/* How it works */
.splash-steps {
  text-align: center;
  padding: var(--space-7) 0 var(--space-8);
}

.splash-steps__title {
  font-size: var(--text-lg);
  margin: 0 0 var(--space-6);
}

.splash-steps__list {
  list-style: none;
  margin: 0 auto var(--space-6);
  padding: 0;
  max-width: 44rem;
  display: grid;
  gap: var(--space-4);
  text-align: left;
}

.splash-steps__list li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  font-size: var(--text-base);
}

.splash-steps__n {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--color-accent-soft);
  color: var(--color-text);
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
}

/* Footer */
.splash-footer {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-6) 0 var(--space-8);
  border-top: 1px solid var(--color-border);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}

.splash-footer__brand {
  font-weight: var(--weight-semibold);
  color: var(--color-text);
}

.splash-footer__muted {
  flex: 1;
}

/* _print.scss
   Outline: print sheets (mix lists, ingredient totals) — ink-cheap,
     black-on-white regardless of theme, page break per mix.
   Purpose: must not regress; paper stays plain, never themed. */
@media print {
  :root,
  [data-theme=dark] {
    /* force paper palette regardless of theme */
    --color-bg: #ffffff;
    --color-surface: #ffffff;
    --color-surface-sunken: #ffffff;
    --color-text: #000000;
    --color-text-muted: #444444;
    --color-border: #999999;
    --color-accent-soft: #ffffff;
  }
  body {
    background: #ffffff;
    color: #000000;
  }
  .app-nav,
  .admin-banner,
  .page-actions,
  .toast,
  .no-print {
    display: none !important;
  }
  .page {
    max-width: none;
    padding: 0;
  }
  .section-block,
  .recipe-meta,
  .quote {
    border: none;
    box-shadow: none;
    border-radius: 0;
  }
  .table th,
  .table td {
    border-bottom: 1px solid #999999;
    padding: 4pt 6pt;
  }
  /* on screen the edge cells absorb the card's inner padding; on
     paper there is no card, so flush the edges */
  .table th:first-child,
  .table td:first-child {
    padding-left: 0;
  }
  .table th:last-child,
  .table td:last-child {
    padding-right: 0;
  }
  .section-block__header,
  .section-block__body,
  .mix-sheet__section {
    padding-left: 0;
    padding-right: 0;
  }
  .table tbody tr:hover > td {
    background: none;
  }
  /* never split an ingredient row across two pages */
  .mix-table tr,
  .totals-table tr {
    break-inside: avoid;
  }
  /* one sheet per mix */
  .mix-sheet {
    break-after: page;
  }
  .mix-sheet:last-child {
    break-after: auto;
  }
  .mix-sheet h2 {
    font-size: 14pt;
    margin-bottom: 6pt;
  }
  /* click-to-edit prints as plain numbers */
  .cell-edit {
    border: none;
    background: none;
    padding: 0;
  }
}
