/* Tasteet — a cast-iron kitchen, not a hospital chart.
   Dark-only. Pico CSS 2 supplies the reset/form/typographic scale; this file
   retunes its custom properties to our palette and adds everything Pico
   doesn't have an opinion about (the cook thread, star control, ingredient
   rows, recipe grid). */

/* `:root[data-theme]` (base.html always sets one) outranks Pico's own
   `:root:not([data-theme=dark])` light palette, which would otherwise win
   over a bare `:root` in light mode. */
:root, :root[data-theme] {
  /* ---- palette ---- */
  --bg: #17130f;
  --surface: #211b16;
  --surface-2: #2c2419;
  --border: #3a3028;
  --text: #f0e9e0;
  --text-dim: #a89a8c;
  --text-faint: #9a8b7d;

  --turmeric: #d4a22a;
  --turmeric-dim: #8a6a1e;
  --herb: #7fa05a;
  --herb-dim: #566e3c;
  --beet: #dc6a82;
  --beet-dim: #7a3040;

  --radius: 10px;
  --radius-sm: 6px;
  --shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
  --recipe-gutter: 4.5rem;
  /* Actual rendered height of .site-header — the sticky cookbook hero pins
     itself right below this so the two don't overlap. Redefined under the
     mobile breakpoint, where the header wraps its nav onto a second row. */
  --header-h: 64px;
  /* Height of the phone-only bottom tab bar (0 where it isn't shown), so
     page padding, the FABs and the toast clear it. */
  --bottom-nav-h: 0px;

  /* ---- type ---- */
  --font-heading: "Fraunces", Georgia, "Times New Roman", serif;
  --font-body: "IBM Plex Sans", -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  color-scheme: dark;

  /* ---- Pico retheme ---- */
  --pico-font-family: var(--font-body);
  --pico-background-color: var(--bg);
  --pico-color: var(--text);
  --pico-h1-color: var(--text);
  --pico-h2-color: var(--text);
  --pico-h3-color: var(--text);
  --pico-h4-color: var(--text);
  --pico-h5-color: var(--text);
  --pico-h6-color: var(--text);
  --pico-muted-color: var(--text-dim);
  --pico-muted-border-color: var(--border);
  --pico-primary: var(--turmeric);
  --pico-primary-background: var(--turmeric);
  --pico-primary-hover: #e4b23a;
  --pico-primary-focus: rgba(212, 162, 42, 0.35);
  --pico-primary-underline: var(--turmeric);
  --pico-primary-hover-background: #e4b23a;
  --pico-primary-inverse: #1c1509;
  --pico-secondary: var(--text-dim);
  --pico-secondary-hover: var(--text);
  --pico-secondary-focus: rgba(168, 154, 140, 0.3);
  --pico-secondary-inverse: var(--bg);
  --pico-contrast: var(--text);
  --pico-contrast-hover: var(--turmeric);
  --pico-contrast-inverse: var(--bg);
  --pico-card-background-color: var(--surface);
  --pico-card-border-color: var(--border);
  --pico-card-box-shadow: var(--shadow);
  --pico-card-sectioning-background-color: var(--surface-2);
  --pico-border-color: var(--border);
  --pico-form-element-background-color: var(--surface-2);
  --pico-form-element-active-background-color: var(--surface-2);
  --pico-form-element-border-color: var(--border);
  --pico-form-element-color: var(--text);
  --pico-form-element-placeholder-color: var(--text-faint);
  --pico-form-element-focus-color: var(--turmeric);
  --pico-form-element-active-border-color: var(--turmeric);
  --pico-form-element-invalid-border-color: var(--beet);
  --pico-switch-background-color: var(--surface-2);
  --pico-switch-checked-background-color: var(--turmeric);
  --pico-table-border-color: var(--border);
  --pico-table-row-stripped-background-color: rgba(255, 255, 255, 0.02);
  --pico-code-background-color: var(--surface-2);
  --pico-code-color: var(--text-dim);
  --pico-blockquote-border-color: var(--border);
  --pico-mark-background-color: rgba(212, 162, 42, 0.35);
  --pico-mark-color: var(--text);
  --pico-del-color: var(--beet);
  --pico-ins-color: var(--herb);
  --pico-dropdown-background-color: var(--surface-2);
  --pico-dropdown-border-color: var(--border);

  /* Pin the base scale — Pico's default grows this up to 131.25% on wide
     viewports, inflating every rem-sized heading/button/form control. */
  --pico-font-size: 100%;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
}

body {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  font-variant-numeric: tabular-nums;
}

h1, h2, h3, h4, .card-title, .brand, .auth-title {
  font-family: var(--font-heading);
  font-variation-settings: "SOFT" 40, "WONK" 1;
  font-weight: 600;
  letter-spacing: -0.01em;
}

h1 { font-size: 1.65rem; }

a { color: var(--turmeric); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Chromium tints autofill-eligible fields (title, email, etc.) with its own
   blue/yellow background via an internal UA style that normal CSS can't
   override directly — this is the standard workaround, keeping it on our
   palette instead of a jarring light patch on a dark form. */
input:-webkit-autofill,
input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--text);
  -webkit-box-shadow: 0 0 0 1000px var(--surface-2) inset;
  transition: background-color 5000s ease-in-out 0s;
}

.text-dim { color: var(--text-dim); }
.text-faint { color: var(--text-faint); }
.text-sm { font-size: 0.85rem; }

/* ---- Header / nav ---- */
.site-header {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  min-height: var(--header-h);
  padding: 0.85rem 1.25rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
  flex-wrap: wrap;
}
.site-header .brand {
  color: var(--text);
  font-size: 1.15rem;
  text-decoration: none;
  white-space: nowrap;
}
.env-badge {
  font-family: var(--font-body);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-dim);
}
.env-badge--production { color: var(--herb); border-color: var(--herb-dim); }
.env-badge--development { color: var(--turmeric); border-color: var(--turmeric-dim); }

.site-header nav {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  align-items: center;
}
/* Content links (Cookbook/Recommendations/Feed/Favorites) grow to fill the
   header, pushing the utility group (Groups/Users), the inbox icon, and the
   account menu to the right edge. */
.site-nav-main { flex: 1; }
.site-header nav a.nav-tab {
  color: var(--text-dim);
  font-size: 0.92rem;
  padding: 0.3rem 0;
  border-bottom: 2px solid transparent;
}
.site-header nav a.nav-tab:hover { color: var(--text); text-decoration: none; }
.site-header nav a.nav-tab.active { color: var(--text); border-bottom-color: var(--turmeric); }

.nav-divider {
  width: 1px;
  align-self: stretch;
  background: var(--border);
  margin: 0.15rem 0.15rem 0.15rem 0;
}
.site-header nav a.nav-admin-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--text-dim);
  font-size: 0.88rem;
  padding: 0.3rem 0;
}
.site-header nav a.nav-admin-link:hover { color: var(--text); text-decoration: none; }
.nav-admin-link .icon-lock { flex-shrink: 0; }

.header-user-name { color: var(--text); white-space: nowrap; }

/* Inbox icon button (partials/inbox_badge.html) — sits between the utility
   nav group and the account menu. .count-badge is absolutely positioned and
   needs this as its positioned ancestor. */
.header-icon-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  color: var(--text-dim);
  border: 1px solid transparent;
}
.header-icon-btn:hover { color: var(--text); background: var(--surface-2); text-decoration: none; }
.header-icon-btn.active { color: var(--turmeric); border-color: var(--turmeric-dim); }
.header-icon-btn .count-badge { top: -2px; right: -2px; }

/* ---- Dropdown menu (details/summary disclosure — account menu, per-recipe
   "more actions" menu, …). base.html closes any open `details.dropdown-menu`
   on outside click / Escape since native <details> doesn't do either. ---- */
/* Pico gives bare <details> a 1rem bottom margin (details{margin-bottom:
   var(--pico-spacing)}) that plain <a> tags never get — reset it here so a
   dropdown-menu sitting next to link-styled buttons in a flex row centers on
   the same line as its siblings instead of sitting visibly higher. */
.dropdown-menu { position: relative; margin: 0; }
.dropdown-menu > summary { list-style: none; cursor: pointer; }
/* Two separate rules, not one comma-separated list: current Chromium/Firefox
   render <summary>'s native disclosure triangle through ::marker and no
   longer recognize ::-webkit-details-marker at all — an unrecognized
   selector in a plain comma list invalidates the whole rule, which silently
   dropped the ::marker half too and left the native chevron showing. */
.dropdown-menu > summary::-webkit-details-marker { display: none; }
.dropdown-menu > summary::marker { display: none; }
/* Pico also draws its own accordion-style chevron via `details summary::after`
   (a background-image icon, unrelated to the native marker above) on every
   <summary> — this was the actual stray chevron next to the kebab dots and
   account-menu avatar, not a rendering artifact. */
.dropdown-menu > summary::after { display: none; }
.dropdown-panel {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 30;
  min-width: 10rem;
  display: flex;
  flex-direction: column;
  background: var(--pico-dropdown-background-color);
  border: 1px solid var(--pico-dropdown-border-color);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.dropdown-panel form { margin: 0; }
.dropdown-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.55rem 0.8rem;
  font-size: 0.85rem;
  color: var(--text-dim);
  background: none;
  border: none;
  border-radius: 0;
  min-height: auto;
  cursor: pointer;
}
.dropdown-item:hover { color: var(--text); background: var(--surface-2); }
.dropdown-item-danger { color: var(--beet); }
.dropdown-item-danger:hover { color: var(--text); background: var(--beet-dim); }

/* ---- Account menu ---- */
.account-menu-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.2rem 0.3rem;
  margin: 0;
  font: inherit;
  font-size: 0.9rem;
  color: var(--text-dim);
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.account-menu-toggle:hover { color: var(--text); background: var(--surface-2); }
.account-menu[open] .account-menu-toggle { color: var(--text); background: var(--surface-2); }
.account-menu-toggle .icon-chevron { flex-shrink: 0; color: var(--text-faint); transition: transform 0.15s ease; }
.account-menu[open] .account-menu-toggle .icon-chevron { transform: rotate(180deg); }
.account-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  background: var(--turmeric-dim);
  color: var(--text);
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 0.85rem;
  flex-shrink: 0;
}
.account-menu-dropdown {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  z-index: 20;
  min-width: 12rem;
  max-width: calc(100vw - 1.5rem);
  display: flex;
  flex-direction: column;
  background: var(--pico-dropdown-background-color);
  border: 1px solid var(--pico-dropdown-border-color);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.account-menu-dropdown form { margin: 0; }
.account-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.65rem 0.85rem;
  font-size: 0.88rem;
  color: var(--text-dim);
  background: none;
  border: none;
  border-radius: 0;
  min-height: auto;
}
.account-menu-budget {
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--border);
  cursor: default;
}
.account-menu-logout { cursor: pointer; }
.account-menu-link { text-decoration: none; }
.account-menu-link:hover,
.account-menu-logout:hover { color: var(--text); background: var(--surface-2); }

.link-button {
  background: none;
  border: none;
  color: var(--text-dim);
  padding: 0;
  margin: 0;
  font: inherit;
  line-height: inherit;
  min-height: auto;
  cursor: pointer;
  text-decoration: underline;
}
.link-button:hover { color: var(--text); }

/* ---- Layout ---- */
.container {
  max-width: 980px;
  margin: 0 auto;
  padding: 1.5rem 1.1rem 4rem;
}
.container--auth {
  max-width: 420px;
  margin-top: 8vh;
}
.container--wide {
  max-width: 1400px;
}

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.page-header h1 { margin: 0; }

/* ---- Auth ---- */
.auth-wrap { display: flex; justify-content: center; }
.auth-card {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 2rem;
  box-shadow: var(--shadow);
}
.auth-title { margin: 0 0 0.25rem; font-size: 1.6rem; text-align: center; }
.auth-subtitle { text-align: center; color: var(--text-dim); margin-bottom: 1.5rem; }
/* "X invited you" on the sign-up form (routes/signup.py, ?invite=). */
.auth-invited { text-align: center; color: var(--text); margin: -1rem 0 1.25rem; }
.auth-footer { text-align: center; color: var(--text-faint); font-size: 0.85rem; margin-top: 1rem; margin-bottom: 0; }
/* Sign-up honeypot field (routes/signup.py): off-screen rather than
   display:none, which simple bots skip; aria-hidden + tabindex=-1 keep people
   and screen readers out of it. */
.auth-trap { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
.auth-footer a { color: var(--text); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }

/* ---- Fingerprint sign-in (passkeys/list.html, login.html, static/js/passkeys.js) ---- */
.passkey-or { text-align: center; color: var(--text-faint); font-size: 0.85rem; margin: 0.6rem 0 1rem; }
.passkey-or .btn-sm { margin-top: 0.5rem; width: auto; }
.passkey-intro { margin-top: 0; }
.passkey-enrol small { display: block; margin: -0.4rem 0 0.75rem; }
.passkey-list { list-style: none; margin: 0; padding: 0; }
.passkey-row {
  list-style: none; display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 0.75rem;
  padding: 0.75rem 0; border-top: 1px solid var(--border);
}
.passkey-row:first-child { border-top: 0; padding-top: 0; }
.passkey-rename { display: flex; gap: 0.5rem; align-items: center; flex: 1 1 16rem; margin: 0; min-width: 0; }
.passkey-rename input { margin: 0; min-width: 0; }
.passkey-rename button, .passkey-remove button { width: auto; margin: 0; white-space: nowrap; }
.passkey-row small { flex: 1 1 100%; order: 2; }
.passkey-remove { margin: 0; }

/* ---- Cards ---- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.25rem;
  margin-bottom: 1.25rem;
}
.card-title { margin: 0 0 0.75rem; font-size: 1.1rem; }
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
}
.card-link {
  display: block;
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.15s ease;
}
.card-link:hover { border-color: var(--turmeric-dim); text-decoration: none; }

.results-section-title { margin: 0 0 0.85rem; font-size: 1.2rem; }

/* /groups "Other groups" directory (partials/group_list.html). */
.group-directory-heading { margin-top: 2rem; }
.group-directory-card .card-title a { color: inherit; }
.group-directory-action { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin: 0.5rem 0 0; }
.group-directory-action .btn-sm { width: auto; margin: 0; }
#recipe-results hr { margin: 1.75rem 0; border-color: var(--border); }

/* ---- Empty state ---- */
.empty-state {
  color: var(--text-dim);
  padding: 2rem 1rem;
  text-align: center;
  border: 1px dashed var(--border);
  border-radius: var(--radius);
}

/* ---- Tables ---- */
.table-wrap { overflow-x: auto; }
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}
.data-table th {
  text-align: left;
  color: var(--text-dim);
  font-weight: 600;
  padding: 0.4rem 0.4rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.data-table td {
  padding: 0.35rem 0.4rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.data-table tr:last-child td { border-bottom: none; }

/* ---- Forms ---- */
.form-inline {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
}
.form-row {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.form-row label { margin: 0; font-size: 0.85rem; color: var(--text-dim); }
.form-row input, .form-row select { margin: 0; }
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.9rem;
  color: var(--text-dim);
}
/* Was `width: auto`, which beats Pico's own `[type=checkbox]{width:1.25em}`
   on specificity (a classed selector always outranks a bare attribute one)
   without supplying a replacement size — and appearance:none strips the
   checkbox of any intrinsic content box, so "auto" collapsed it to just its
   2px border on each side: a ~4px sliver, full height, no visible square.
   Pinned to an explicit fixed size instead (not a relative unit) so it can
   never again be quietly out-cascaded by a future rule, and flex-shrink: 0
   so a tight container (the Share popover's 16rem-wide dropdown-panel-form)
   can't compress it further than that.

   :not([role=switch]) matters: admin/users.html's Admin/Wines toggles are
   `input[type=checkbox][role=switch]` inside a .checkbox-row too, and this
   selector (class + element + attribute) narrowly out-specifies Pico's own
   `[type=checkbox][role=switch]{width:2.25em;...}` sizing rule — the old
   buggy `width:auto` version didn't carry enough specificity to win that
   particular fight, so it never touched switches; a naive fix here
   would have started squashing them to the same 18px square. */
.checkbox-row input[type="checkbox"]:not([role="switch"]) {
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
}

.inline-budget-form {
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
}

/* ---- Buttons ---- */
.btn-primary {
  background: var(--turmeric);
  color: #1c1509;
  border: none;
  border-radius: var(--radius-sm);
  padding: 0.55rem 1.1rem;
  font-weight: 600;
  cursor: pointer;
}
.btn-primary:hover { background: #e4b23a; }
/* margin-bottom:0 undoes a Pico quirk: its
   `[type=button],[type=reset],[type=submit] { margin-bottom: var(--pico-spacing) }`
   rule is an attribute selector with no tag qualifier, so it also matches a
   plain `<button type="button">`/`<button type="submit">` — not just the
   `<input type=button>` it was written for. Every .btn-sm button (all of
   them declare an explicit type) picked up a stray 16px bottom margin,
   which in a flex row like .photo-grid-actions inflated the row height and
   shifted the button up relative to a sibling with no such margin (e.g.
   the "Add photos" label), a visible misalignment with "Generate photo". */
.btn-sm {
  font-size: 0.82rem;
  padding: 0.25rem 0.65rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  margin-bottom: 0;
}
/* .btn-sm + .btn-primary (a compact primary button, e.g. a card's "Create
   recipe") needs .btn-sm's padding/font-size but .btn-primary's color —
   plain source order can't give each rule only part of a combined element,
   so this compound selector (higher specificity than either alone) pins
   just the 3 color properties regardless of which single-class rule is
   declared later. Without it the two rules tie on background/border/color
   and whichever lands last in the file wins all three, which previously
   left a `.btn-sm.btn-primary` button dark-on-dark and unreadable. */
.btn-sm.btn-primary { background: var(--turmeric); color: #1c1509; border: none; }
.btn-sm.btn-primary:hover { background: #e4b23a; }
.btn-ghost { background: transparent; }
.btn-ghost:hover { background: var(--surface-2); border-color: var(--text-faint); }
.btn-danger {
  background: transparent;
  border-color: var(--beet-dim);
  color: var(--beet);
}
.btn-danger:hover { background: var(--beet-dim); color: var(--text); }
.btn-danger:disabled { opacity: 0.35; cursor: not-allowed; }
/* De-emphasized, text-only — for a low-stakes negative action (Dismiss)
   that shouldn't visually compete with a primary/secondary pair next to
   it. No border, smaller than .btn-sm; declared after it so background/
   border: none actually win the cascade tie. */
.btn-text {
  background: transparent;
  border: none;
  color: var(--text-faint);
  font-size: 0.76rem;
  padding: 0.2rem 0.4rem;
  cursor: pointer;
}
.btn-text:hover { color: var(--text-dim); background: transparent; }
.dropdown-toggle { display: inline-flex; align-items: center; justify-content: center; padding-left: 0.5rem; padding-right: 0.5rem; }

/* ---- Icon buttons (admin row actions) ---- */
.actions-cell { display: flex; align-items: center; gap: 0.35rem; }
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  flex: none;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
}
.icon-btn:hover { background: var(--surface-2); border-color: var(--text-faint); color: var(--text); }
.icon-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.icon-btn-danger { border-color: var(--beet-dim); color: var(--beet); }
.icon-btn-danger:hover { background: var(--beet-dim); color: var(--text); }
.icon-btn .icon-link-done { display: none; }
.icon-btn--done { border-color: var(--herb-dim); color: var(--herb); }
.icon-btn--done .icon-link-default { display: none; }
.icon-btn--done .icon-link-done { display: inline; }

/* ---- Badges ---- */
.badge {
  display: inline-block;
  font-size: 0.75rem;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-dim);
}
.badge-turmeric { color: var(--turmeric); border-color: var(--turmeric-dim); }
.badge-herb { color: var(--herb); border-color: var(--herb-dim); }
.badge-beet { color: var(--beet); border-color: var(--beet-dim); }

/* ---- Approval lists: /admin/users "Waiting for approval" (partials/admin_pending.html)
   and a group's "Join requests" (partials/group_members.html) ---- */
.pending-card { border-left: 3px solid var(--turmeric); }
.pending-heading { margin: 1rem 0 0.4rem; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-dim); }
.pending-list { list-style: none; margin: 0; padding: 0; }
.pending-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1rem;
  padding: 0.65rem 0;
  border-top: 1px solid var(--border);
}
.pending-who { display: flex; flex-direction: column; min-width: 12rem; flex: 1 1 14rem; }
.pending-who .text-sm { overflow-wrap: anywhere; }
.pending-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; flex: 1 1 14rem; }
.pending-actions { display: flex; gap: 0.5rem; }
.pending-actions .btn-sm { width: auto; margin: 0; }

/* ---- Cookbook hero "more filters" flyout ---- */
.filter-toggle { position: relative; }
.count-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 999px;
  background: var(--turmeric);
  color: var(--bg);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}
.count-badge[hidden] { display: none; }
.filter-panel { padding: 0.6rem 0.8rem; gap: 0.6rem; min-width: 12rem; }
.filter-panel fieldset { border: none; padding: 0; margin: 0; }
.filter-panel legend {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  padding: 0 0 0.3rem;
}
.filter-panel fieldset + fieldset { margin-top: 0.5rem; }

/* ---- Feedback ---- */
.error-msg {
  background: rgba(180, 72, 94, 0.12);
  border: 1px solid var(--beet-dim);
  color: #e6a3b0;
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.85rem;
  font-size: 0.9rem;
  margin: 0 0 1rem;
}
.error-msg--sm { padding: 0.2rem 0.5rem; margin: 0.3rem 0 0; font-size: 0.78rem; }
.notice-msg {
  background: rgba(127, 160, 90, 0.12);
  border: 1px solid var(--herb-dim);
  color: #b7d19e;
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.85rem;
  font-size: 0.9rem;
  margin: 0 0 1rem;
}
.notice-msg--sm { padding: 0.2rem 0.5rem; margin: 0.3rem 0 0; font-size: 0.78rem; }
.warning-msg {
  background: rgba(212, 162, 42, 0.12);
  border: 1px solid var(--turmeric-dim);
  color: #e6c67a;
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.85rem;
  font-size: 0.9rem;
  margin: 0 0 1rem;
}
.warning-dismiss { margin-left: 0.5rem; vertical-align: baseline; }
.warning-msg--sm { padding: 0.2rem 0.5rem; margin: 0.3rem 0 0; font-size: 0.78rem; }

/* ---- Invite links ---- */
.invite-link-box {
  background: var(--surface-2);
  border: 1px solid var(--turmeric-dim);
  border-radius: var(--radius-sm);
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
}
.invite-link-box p { margin: 0 0 0.5rem; font-size: 0.85rem; color: var(--text-dim); }
.invite-link-box input {
  width: 100%;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.82rem;
}

/* ---- Collapsible ---- */
.collapsible summary {
  cursor: pointer;
  color: var(--text-dim);
  font-size: 0.9rem;
  margin-bottom: 0.5rem;
}
.collapsible[open] summary { color: var(--text); }

/* ---- Mobile ---- */
@media (max-width: 600px) {
  /* One header row on a phone: brand (+ env badge), inbox, account menu.
     The four main sections move to the fixed bottom tab bar (see "Bottom
     tab bar" at the end of this file) and Groups/Users into the account
     menu (.account-menu-mobile), instead of wrapping the header onto three
     rows that, with the sticky cookbook hero under it, took half the
     screen. */
  :root[data-theme] { --header-h: 57px; }
  .site-header { padding: 0.6rem 0.9rem; gap: 0.5rem; flex-wrap: nowrap; }
  .site-header nav.site-nav-main, .site-header nav.site-nav-utility { display: none; }
  .header-icon-btn { margin-left: auto; }
  .account-menu-toggle .header-user-name { display: none; }
  /* Pinned to the viewport below the header: a full-width dropdown fits a
     phone better than one hanging off the avatar's edge. */
  .account-menu-dropdown {
    position: fixed;
    top: calc(var(--header-h) + 0.4rem);
    right: 0.9rem;
    left: 0.9rem;
    max-width: none;
  }
  .container { padding: 1rem 0.85rem calc(5rem + var(--bottom-nav-h)); }
  .form-inline { flex-direction: column; align-items: stretch; }
  .card-grid { grid-template-columns: 1fr; }
  .page-header { gap: 0.6rem; }
  .recipe-title { font-size: 1.55rem; }
  .cookbook-hero-top { flex-direction: column; align-items: stretch; }

  /* ---- Cookbook browse FAB pair — "+ New recipe"/"+ New wine" (primary)
     and "Import with AI" (secondary) float bottom-right instead of sitting
     in the hero's top row, so they stay reachable without pushing the
     recipe grid further down the page. Sticky's containing-block rules
     don't reach a fixed descendant, so pinning to the viewport here doesn't
     fight .cookbook-hero's own position: sticky. ---- */
  .cookbook-hero-actions {
    position: fixed;
    right: 1rem;
    bottom: calc(1.1rem + var(--bottom-nav-h));
    flex-direction: column-reverse;
    align-items: center;
    gap: 0.6rem;
    z-index: 9;
  }
  .fab-label { display: none; }
  .fab-primary, .fab-secondary {
    width: 56px;
    height: 56px;
    padding: 0;
    border-radius: 50%;
    justify-content: center;
    box-shadow: var(--shadow);
  }
  .fab-secondary { width: 44px; height: 44px; background: var(--surface); }

  /* ---- Cookbook filter bar: search + filter icon on their own row now
     that tags (cookbook_hero.html) sit up in .cookbook-hero-top instead of
     a second, horizontally scrolling chip row here. ---- */
  .filter-row-search { display: flex; gap: 0.5rem; width: 100%; }
  .filter-row-search .filter-search { flex: 1 1 auto; }
}

/* Tap targets ≥44px on touch — the star control, ingredient rows, and photo
   actions are the ones actually used one-handed in a kitchen. Scoped off
   plain checkboxes/radios (admin's Admin/Wines toggles, group visibility
   picker): they aren't hidden like .star-rating's, so a blanket min-height
   stretched them into unrecognizable thin bars. */
button, .btn-primary, .btn-sm {
  min-height: 32px;
}
/* "Take photo" opens the device camera via capture= — meaningless on a
   desktop/mouse setup with no camera-first workflow, so it only shows on
   coarse-pointer (touch) devices, same signal as the rest of this block. */
.photo-upload-form--camera { display: none; }
@media (pointer: coarse) {
  .btn-primary, button[type="submit"] { min-height: 44px; }
  .star-rating label { font-size: 1.9rem; padding: 0.35rem 0.5rem; }
  .photo-thumb-actions { opacity: 1; } /* no hover on touch — always visible */
  .photo-upload-form--camera { display: inline; }
}

/* ---- Recipe forms ---- */
.recipe-form .form-row { margin-bottom: 1rem; }
.recipe-form textarea { font-family: var(--font-body); resize: vertical; }
.form-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.75rem;
}
.form-actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  margin-top: 1.5rem;
}
#recipe-save-actions[hidden] { display: none; }

/* One ingredient = one .ing-row container (qty/unit/name/note/remove). Named
   grid areas let the mobile layout reflow without touching DOM order, so tab
   order stays qty -> unit -> name -> note -> remove. */
.ing-row {
  display: grid;
  grid-template-columns: 64px 88px 1fr 1fr 44px;
  grid-template-areas: "qty unit name note remove";
  gap: 8px;
  align-items: center;
  margin-bottom: 0.4rem;
}
.ing-row input { margin: 0; }
.ing-qty { grid-area: qty; }
.ing-unit { grid-area: unit; }
.ing-name { grid-area: name; }
.ing-note { grid-area: note; }
.ing-remove {
  grid-area: remove;
  width: 44px;
  height: 44px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* The note is secondary metadata, not a fourth required field — demoted the
   same way whether empty or filled, and back to full strength while editing. */
.ing-row .ing-note {
  border-style: dashed;
  opacity: 0.75;
  font-size: 0.85rem;
}
.ing-row .ing-note:focus { opacity: 1; }

.visibility-control.visibility-inline { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
/* Pico's select:not([multiple],[size]) sets 0.75rem/1rem padding (built for a
   full-width form field) — same specificity as the element+class selector
   here, so being later in the cascade is what lets this shrink it down to
   the Save button's scale instead of dwarfing it. */
select.visibility-select {
  margin: 0;
  width: auto;
  max-width: 7.5rem;
  height: 32px;
  padding: 0.25rem 2.2rem 0.25rem 0.65rem;
  font-size: 0.82rem;
}
/* Pico's button[type=submit]{width:100%} ties on specificity with .btn-sm/
   .btn-primary alone (same problem solved for button.tag-chip above) — the
   #id selector is what actually wins here. Explicit height matches
   .visibility-select so the two sit flush on the same row. */
#visibility-control button[type="submit"] { width: auto; height: 32px; }
.group-checklist--inline { flex-basis: 100%; margin: 0.6rem 0 0; }
#group-checklist {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  padding: 0.5rem 0.75rem;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  margin-bottom: 0.6rem;
}
#group-checklist[hidden] { display: none; }

/* ---- Recipe visibility panel (detail page — a <details> now: the summary
   is the compact "visible to · <chip>" row that sits in
   .recipe-visible-rating-row, the segmented control + group chips only
   render once tapped open, replacing what used to be a select+Save. Still
   immediate per-click HTMX persistence, no Save button. ---- */
/* Pico's `details{margin-bottom:var(--pico-spacing)}` default (often 1rem+)
   otherwise leaks through as extra space below .recipe-visible-rating-row,
   since this <details> now sits inline in that flex row instead of on its
   own full-width line — that's what created the oversized gap before the
   description text. */
.visibility-panel { margin-bottom: 0; }
.visibility-panel > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  cursor: pointer;
  list-style: none;
}
.visibility-panel > summary::-webkit-details-marker { display: none; }
.visibility-panel > summary::marker { display: none; }
.visibility-panel > summary::after { display: none; }
.visibility-chip {
  display: inline-flex;
  align-items: center;
  max-width: 60vw;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 600;
}
.visibility-editor { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.6rem; }
.visibility-label { color: var(--text-dim); font-size: 0.8rem; white-space: nowrap; }
.visibility-segmented {
  display: inline-flex;
  gap: 2px;
  padding: 2px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
}
/* Pico's [type=button]{margin-bottom:var(--pico-spacing)} is an
   attribute-selector rule (specificity 0,3,0) — a bare .visibility-segment
   class (0,1,0) can't out-rank it, so these are scoped under the panel's id
   the same way #visibility-control did above for its Save button. */
#visibility-panel .visibility-segment {
  width: auto;
  min-height: auto;
  margin: 0;
  padding: 3px 10px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: var(--text-dim);
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.5;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
}
#visibility-panel .visibility-segment:hover { color: var(--text); }
#visibility-panel .visibility-segment.active { background: var(--turmeric); color: var(--bg); }
#visibility-panel .visibility-segment:active { transform: scale(0.95); }

/* Sits inline right after the segmented control (same flex line, wraps
   onto its own line only when space runs out) rather than as an indented
   sub-row beneath it. */
.visibility-chip-row { display: flex; align-items: center; flex-wrap: wrap; gap: 0.3rem; }
.visibility-chip-row[hidden] { display: none; }
#visibility-panel .group-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  width: auto;
  min-height: auto;
  margin: 0;
  padding: 2px 8px;
  border-radius: 999px;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.1s ease;
}
#visibility-panel .group-chip:hover { border-color: var(--turmeric-dim); color: var(--text); }
/* Shared groups get a soft translucent tint, not the solid/filled amber
   used for the active segment — chips are meant to read lighter than the
   segmented control they sit beneath. */
#visibility-panel .group-chip--on {
  background: rgba(212, 162, 42, 0.16);
  border-color: var(--turmeric-dim);
  color: var(--turmeric);
}
#visibility-panel .group-chip--on:hover { color: var(--turmeric); }
#visibility-panel .group-chip:active { transform: scale(0.95); }
/* Not-yet-shared groups read as an "add" affordance; shared ones stay solid. */
#visibility-panel .group-chip:not(.group-chip--on) { border-style: dashed; }

/* ---- Cookbook hero (recipes/wines browse) ---- */
/* Sticks to the viewport right below .site-header (--header-h keeps the two
   from overlapping) so search/filters stay reachable while the card grid
   scrolls underneath. */
.cookbook-hero {
  position: sticky;
  /* 1px less than --header-h, not equal to it: two independently-composited
     sticky elements landing on the exact same boundary leave a hairline
     seam in Chromium where scrolled content peeks through. The header (z-index
     10, opaque) simply paints over this 1px of overlap. */
  top: calc(var(--header-h) - 1px);
  z-index: 9;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  margin-bottom: 1.25rem;
}
.cookbook-hero-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  /* Positioning context for the open "Tags" panel below — anchoring there
     rather than to the small chip itself keeps the panel from having to
     track exactly where in this row the chip landed, and top: 100% then
     simply means "right below this whole row". */
  position: relative;
}
.cookbook-hero-actions {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.cookbook-hero-left {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  row-gap: 0.5rem;
  gap: 0.6rem;
}
.fab-primary, .fab-secondary { display: inline-flex; align-items: center; gap: 0.4rem; }
.type-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.type-toggle a {
  padding: 0.45rem 1.15rem;
  border-radius: 999px;
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1;
  color: var(--text-dim);
  text-decoration: none;
}
.type-toggle a:hover { color: var(--text); text-decoration: none; }
.type-toggle a.active { background: var(--text); color: var(--bg); }
/* A little tighter on a phone, where the Recipes/Wines toggle, shuffle, and
   Tags chip are sharing one narrow row — horizontal-only, so the 44px+ tap
   height (pointer: coarse, further down) is untouched. Placed after the
   rules above (equal specificity) so it actually wins under 600px. */
@media (max-width: 600px) {
  .cookbook-hero-left { gap: 0.4rem; }
  .cookbook-hero-left .tag-filter-toggle { padding: 2px 7px; font-size: 0.76rem; }
  .type-toggle a { padding-left: 0.85rem; padding-right: 0.85rem; }
}

/* ---- Browse filter bar ---- */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
  margin-bottom: 0;
}
.filter-bar select, .filter-bar input { margin: 0; width: auto; }
.filter-search { flex: 1 1 220px; min-width: 160px; }
/* .filter-row-search (search input + the "more filters" funnel) is a
   layout-only grouping inside .filter-bar — `display: contents` makes it
   invisible to layout above the mobile breakpoint, so every control stays
   a direct flex item of .filter-bar and the desktop filter bar is
   pixel-identical to before this grouping was added. The mobile-only rule
   that gives it real layout lives in the @media (max-width: 600px) block. */
.filter-row-search { display: contents; }

/* ---- "Tags" filter (cookbook_hero.html: sits in .cookbook-hero-top, next
   to the Recipes/Wines toggle — not in .filter-bar above; see
   tag_filter.html for why it can still act on that form from out there). */
.tag-filter-row { display: flex; align-items: center; gap: 0.4rem; }
.tag-filter-details summary {
  cursor: pointer;
  list-style: none;
}
.tag-filter-details summary::-webkit-details-marker { display: none; }
/* Pico styles every `details summary::after` as its own accordion chevron (a
   background image, floated, rotated -90deg while closed) — left alone, that
   drew a second arrow and turned this one on its side. */
.tag-filter-toggle::after {
  content: " \25be";
  background: none; float: none; transform: none;
  width: auto; height: auto; margin: 0;
}
.tag-filter-details[open] .tag-filter-toggle::after { content: " \25b4"; }
/* Anchored to .cookbook-hero-top (position: relative, above), not to this
   chip: left/right span that whole row so the panel can't run off either
   edge regardless of where in the row the chip itself ends up sitting. */
.tag-filter {
  position: absolute;
  top: calc(100% + 0.5rem);
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  max-height: 50vh;
  overflow-y: auto;
  padding: 0.75rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow);
}
.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.78rem;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-dim);
  border: 1px solid var(--border);
  width: auto;
  white-space: nowrap;
}
button.tag-chip {
  cursor: pointer;
  font-family: var(--font-body);
  min-height: auto;
  /* Pico's button[type=submit]{width:100%} rule has the same specificity
     (element+attribute) as a plain .tag-chip{width:auto} and loads first,
     so it wins on the tie — matching it with button.tag-chip (element+class)
     is what actually overrides it. */
  width: auto;
}
button.tag-chip:hover { border-color: var(--turmeric-dim); color: var(--text); }
.tag-chip-clear { color: var(--beet); border-color: var(--beet-dim); }
.tag-chip-active { background: var(--turmeric); color: var(--bg); border-color: var(--turmeric); }
.tag-chip-active:hover { color: var(--bg); }
.chip-row { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-bottom: 0.35rem; }

/* ---- Recipe grid & card ---- */
.recipe-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
.recipe-card {
  display: block;
  position: relative;
  color: var(--text);
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color 0.15s ease;
}
.recipe-card:hover { border-color: var(--turmeric-dim); text-decoration: none; }
.recipe-card-image {
  aspect-ratio: 4 / 3;
  position: relative;
  display: flex;
}
.recipe-card-image img { width: 100%; height: 100%; object-fit: cover; }
/* Wraps the fav star and/or creator-attribution chip in the card's
   top-right corner so the two never overlap when both apply to the same
   card (a favorited recipe someone else created) — a plain flex row
   anchored to the corner, width driven by whichever of the two is present.
   Positioned against .recipe-card itself (not .recipe-card-image) so it
   still lands sensibly on mobile's photo-less rows, where the image
   collapses to a 4px stripe — see the Mobile section. */
.recipe-card-topright {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  max-width: calc(100% - 1rem);
}
.recipe-card-fav {
  flex-shrink: 0;
  color: var(--turmeric);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
  font-size: 1.1rem;
}
/* Creator-attribution chip — shown only on cards not created by the viewer.
   Same dark semi-opaque pill treatment as .recipe-card-visibility so it
   reads as a sibling element, not a different UI language. */
.recipe-card-creator {
  display: flex;
  align-items: center;
  gap: 0.32rem;
  min-width: 0;
  padding: 2px 8px 2px 2px;
  border-radius: 999px;
  background: rgba(23, 19, 15, 0.72);
  border: 1px solid rgba(240, 233, 224, 0.18);
}
.recipe-card-creator-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.1rem;
  height: 1.1rem;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--turmeric-dim);
  color: var(--text);
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 0.6rem;
}
.recipe-card-creator-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.68rem;
  color: var(--text-dim);
}
/* Positioned against .recipe-card, same reasoning as .recipe-card-topright
   above. */
.recipe-card-visibility {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  max-width: calc(100% - 1rem);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.68rem;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(23, 19, 15, 0.72);
  border: 1px solid rgba(240, 233, 224, 0.18);
  color: var(--text-dim);
}
/* Mobile-only duplicate of .recipe-card-visibility's text, rendered inline
   in the meta row instead of overlaid on the image — see the Mobile
   section below for why the overlay itself is hidden there. Hidden on
   desktop by default since the corner overlay already covers it there. */
.recipe-card-visibility-inline { display: none; }
.recipe-card-body { padding: 0.85rem 0.9rem; }
.recipe-card-title {
  margin: 0 0 0.4rem;
  font-size: 1.02rem;
  line-height: 1.3;
}
.recipe-card-meta { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.4rem; flex-wrap: wrap; }
/* Single line, overflow collapsed behind a "+N" chip — data-tag-row markup
   driven by collapseTagRow/expandTagRow in base.html, so the cutoff adapts
   to the card's actual width and each tag's length rather than an arbitrary
   count. */
.recipe-card-tags { display: flex; flex-wrap: nowrap; align-items: center; gap: 0.3rem; overflow: hidden; min-width: 3rem; }
.recipe-card-tags [data-tag] { flex-shrink: 0; }

/* ---- Recommendation cards ----
   A recommendation is a dish idea, never a saved Recipe row, and has no
   photo source (not even AI-generated) — so unlike recipe/wine cards it
   doesn't fall back to a 4:3 gradient tile when there's no image, it never
   has one. It also holds real buttons (status flip, "Create recipe") that
   can't nest inside an anchor, so it's an <article>, not the <a>-wrapped
   .recipe-card the other browse grids use. Both differences are why it
   gets its own class namespace instead of reusing .recipe-card — reusing it
   would also pull in the mobile compact-row treatment built for a photo
   thumbnail, which doesn't fit a photo-less card with a stacked button
   group. The gradient survives as a thin identity bar (.recommendation-
   card-accent) instead of a tile, reusing the same cover-gradient-N classes
   recipe/wine cards fall back to. */
.recommendation-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.recommendation-card {
  display: flex;
  flex-direction: column;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  transition: border-color 0.15s ease;
}
.recommendation-card:hover { border-color: var(--turmeric-dim); }
.recommendation-card-accent { display: block; height: 4px; flex: 0 0 4px; }
/* .card-grid's items stretch to the tallest card in their row by default
   (CSS Grid's normal align-items behavior), so making .recommendation-
   card-body a column flexbox filling that stretched height, with
   .recommendation-card-actions pinned via margin-top:auto, is enough to
   equalize footers across a row — no JS, no fixed heights. */
.recommendation-card-body { display: flex; flex-direction: column; flex: 1; padding: 0.85rem 0.9rem; }
.recommendation-card-title {
  margin: 0 0 0.4rem;
  font-size: 1.02rem;
  line-height: 1.3;
}
.recommendation-card-meta { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.4rem; flex-wrap: wrap; }
.recommendation-card-title-link { color: inherit; text-decoration: none; }
.recommendation-card-title-link:hover .recommendation-card-title { text-decoration: underline; }
/* 3-line clamp so a long AI description doesn't drive card height — the
   full text is still on the recipe once "Create recipe" saves it. */
.recommendation-card-desc {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}
/* Action hierarchy: "Create recipe" (.btn-primary) is the one action that
   adds real value, so it's the only filled button; the status-flip button
   stays outlined (.btn-sm.btn-ghost, same as everywhere else); "Dismiss"
   is the lowest-stakes action here and shouldn't compete visually with
   either, hence .btn-text — border-none, smaller, muted. The filled and
   outlined buttons stack full-width and pin to the card's bottom via
   margin-top:auto. */
.recommendation-card-actions { display: flex; flex-direction: column; gap: 0.4rem; margin-top: auto; padding-top: 0.6rem; }
.recommendation-card-actions a, .recommendation-card-actions button { display: block; width: 100%; text-align: center; margin: 0; }
.recommendation-status-control { display: flex; flex-direction: column; gap: 0.4rem; }
/* On a card, Dismiss and Delete share one row under the status button
   instead of taking a full-width row each. */
.recommendation-card-actions .recommendation-status-control { flex-direction: row; flex-wrap: wrap; }
.recommendation-card-actions .recommendation-status-control .btn-ghost { flex: 1 1 100%; }
.recommendation-card-actions .recommendation-status-control .btn-text { flex: 1 1 0; width: auto; }

.cover-gradient-0 { background: linear-gradient(135deg, #3a2f1e, #6b4a24); }
.cover-gradient-1 { background: linear-gradient(135deg, #2e3a24, #4d6b34); }
.cover-gradient-2 { background: linear-gradient(135deg, #3a2430, #6b3448); }
.cover-gradient-3 { background: linear-gradient(135deg, #24333a, #34586b); }
.cover-gradient-4 { background: linear-gradient(135deg, #3a2e1e, #8a6a1e); }
.cover-gradient-5 { background: linear-gradient(135deg, #2a241e, #566e3c); }

/* ---- Compact recipe/wine browse rows — a 72px square thumbnail (or, for
   a recipe/wine with no photo, a 4px colour stripe instead of wasting a
   72px placeholder square) with title/meta/tags to its right, instead of
   the desktop's stacked 4:3 card, so several rows fit above the fold.
   Recommendation cards don't use .recipe-card at all (see the
   Recommendation cards section) so they never hit this block — they just
   get .card-grid's plain 1-column mobile stacking.

   A second, later @media (max-width: 600px) block rather than a subsection
   of the main Mobile block above — this needs to override .recipe-card's
   unconditional desktop rule (display: block) and .recipe-card-image's
   unconditional aspect-ratio: 4/3, both in the Recipe grid & card section
   above, both equal specificity to the selectors here, so source order is
   what decides and this has to come after that section. Same reasoning as
   the second @media (max-width: 600px) block further down for
   .print-columns. ---- */
@media (max-width: 600px) {
  .recipe-grid { display: flex; flex-direction: column; gap: 0.5rem; }
  .recipe-card { display: flex; align-items: center; gap: 0; }
  .recipe-card-image {
    flex: 0 0 72px;
    width: 72px;
    height: 72px;
    aspect-ratio: 1;
    border-radius: var(--radius-sm);
    margin: 0.55rem 0 0.55rem 0.55rem;
  }
  /* No photo: the 72px square shrinks to a full-height 4px stripe so the
     title gets the width back instead of spending it on an empty
     placeholder. align-self: stretch overrides .recipe-card's
     align-items: center so the stripe runs the row's full height;
     .recipe-card's own overflow: hidden + border-radius clip its outer
     corners to match the card. Compound selector, not just the single
     class: .recipe-card-image's own unconditional aspect-ratio: 4/3 rule
     (see Recipe grid & card) has equal specificity and sits later in the
     file, so a single-class .recipe-card-image-empty would lose that
     property to it — collapsing the stripe's height to a sliver instead of
     stretching it. */
  .recipe-card-image.recipe-card-image-empty {
    flex: 0 0 4px;
    width: 4px;
    height: auto;
    align-self: stretch;
    aspect-ratio: auto;
    border-radius: 0;
    margin: 0;
  }
  .recipe-card-body { flex: 1; min-width: 0; padding: 0.55rem 0.8rem; }
  .recipe-card-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-bottom: 0.25rem;
  }
  /* .recipe-card-topright now anchors to .recipe-card (see the Recipe grid
     & card section), which on a photo-less row would otherwise sit right
     over the ellipsised title — give the title breathing room whenever
     that corner chip is present. */
  .recipe-card:has(.recipe-card-topright) .recipe-card-title { padding-right: 3.2rem; }
  /* Corner overlays and the secondary health/kcal item don't fit a 72px
     thumbnail or a one-line meta row — see recipe_card.html/wine_card.html. */
  .recipe-card-visibility, .recipe-card-creator-name, .recipe-card-meta-extra { display: none; }
  .recipe-card-meta .badge { background: none; border-color: transparent; padding: 0; }
  /* ...but the visibility note itself still needs to surface somewhere on
     mobile (which groups can see this card) — .recipe-card-visibility-inline
     carries the same text into the meta row instead of the image overlay. */
  .recipe-card-visibility-inline {
    display: inline-block;
    font-size: 0.68rem;
    padding: 1px 7px;
    border-radius: 999px;
    background: rgba(240, 233, 224, 0.08);
    border: 1px solid var(--border);
    color: var(--text-dim);
    white-space: nowrap;
  }
}

/* ---- Recipe detail ---- */
/* Cover photo above the title; cropped to a band, the link opens it whole. */
.recipe-hero { display: block; margin: 0 0 0.9rem; border-radius: var(--radius, 12px); overflow: hidden; border: 1px solid var(--border); }
.recipe-hero img { display: block; width: 100%; height: clamp(200px, 34vw, 340px); object-fit: cover; }
@media print { .recipe-hero { display: none; } }
.recipe-title { margin: 0 0 0.4rem; font-size: 1.9rem; }
.recipe-meta { display: flex; gap: 0.8rem; align-items: center; flex-wrap: wrap; }
.recipe-actions { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
/* Pico also gives [type=button] a 1rem bottom margin the <a>-styled buttons
   here don't have — same centering problem as .dropdown-menu above. */
.recipe-actions > button { margin: 0; }
/* Keeps Print/Edit/the overflow toggle a consistent height as one aligned
   row of small buttons. */
.recipe-actions .btn-sm { height: 32px; }
.recipe-description { color: var(--text-dim); max-width: 68ch; margin: 0 0 0.6rem; }
.btn-favorite-on { color: var(--turmeric); border-color: var(--turmeric-dim); }

/* Top-of-page bar — back link + overflow (⋮) menu as two small icon
   buttons on their own row, replacing the old plain search box. No cover
   image; see partials/recipe_topbar.html. */
.detail-topbar { display: flex; align-items: center; justify-content: space-between; margin: 0 0 0.9rem; }
.topbar-icon-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  cursor: pointer;
}
.topbar-icon-btn:hover { border-color: var(--turmeric-dim); color: var(--text); text-decoration: none; }
/* Favorite toggle + ⋮ menu share the right edge of the bar. Pico gives
   [type=button] a bottom margin, which would push the toggle off the ⋮
   button's centre line. */
.topbar-actions { display: flex; align-items: center; gap: 0.5rem; }
.topbar-actions .topbar-icon-btn { margin: 0; padding: 0; }
.topbar-icon-btn--on { color: var(--turmeric); border-color: var(--turmeric-dim); background: rgba(212, 162, 42, 0.12); }

/* Stat chips (rating, cooked/to-try, time, servings, kcal, health): icon +
   value pills in one wrapping row, so a narrow screen breaks between chips
   rather than inside one. Tags don't sit in this row — they have their
   own line below (.recipe-tag-row). Bottom margin matches .recipe-title's. */
.recipe-stat-row { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem 0.5rem; margin: 0 0 0.6rem; }
/* #recipe-nutrition-stat and #rating-summary wrap their chips in a span (an
   id for the OOB swaps to hit) — a flex container of their own so the chips
   inside keep the row's gap. */
.recipe-stat-group, #rating-summary { display: inline-flex; align-items: center; gap: 0.4rem 0.5rem; flex-wrap: wrap; }
.recipe-stat-group[hidden], #rating-summary[hidden] { display: none; }
.recipe-stat { display: inline-flex; align-items: center; gap: 0.3rem; color: var(--text-dim); font-size: 0.85rem; white-space: nowrap; }
.recipe-stat svg { flex-shrink: 0; color: var(--text-faint); }
.recipe-stat--chip {
  padding: 0.2rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2);
  line-height: 1.3;
}
/* Same three health tints as .badge-*, restated because .recipe-stat's own
   colour comes later in the file than the .badge-* rules and would win. */
.recipe-stat--chip.badge-herb { color: var(--herb); border-color: var(--herb-dim); }
.recipe-stat--chip.badge-turmeric { color: var(--turmeric); border-color: var(--turmeric-dim); }
.recipe-stat--chip.badge-beet { color: var(--beet); border-color: var(--beet-dim); }
.recipe-stat--chip.badge-herb svg, .recipe-stat--chip.badge-turmeric svg, .recipe-stat--chip.badge-beet svg { color: currentColor; }
/* Wraps the kcal icon+text inside .recipe-stat so the optional
   data-tooltip="portion_description" (Pico-native) has a single inline-flex
   element to hang off, matching .recipe-stat's own alignment. */
.recipe-stat-kcal { display: inline-flex; align-items: center; gap: 0.3rem; }

/* Tags stay on one line; whatever doesn't fit is hidden (via [hidden], set
   by collapseTagRow() in base.html) behind a trailing "+N" chip that
   expands the row in place on click — see expandTagRow(). Same
   data-tag-row/data-tag/data-tag-more markup is shared with .recipe-card-tags
   (browse grid cards, see recipe_card.html/wine_card.html) — one JS
   implementation, two render sites. On the detail page the row sits on its
   own line under the stat chips, full width. */
.recipe-tag-row { display: flex; flex-wrap: nowrap; align-items: center; gap: 0.35rem; overflow: hidden; min-width: 4rem; margin: 0 0 0.6rem; }
.recipe-tag-row [data-tag] { flex-shrink: 0; }
[data-tag-row].recipe-tag-row--expanded { flex-wrap: wrap; overflow: visible; }
/* Outline-only + muted so the overflow count reads as "more exist" rather
   than as just another tag. */
.tag-chip-more {
  cursor: pointer;
  font-family: var(--font-body);
  color: var(--text-faint);
  background: transparent;
  border-color: var(--border);
  flex-shrink: 0;
}
.tag-chip-more:hover { color: var(--text); border-color: var(--turmeric-dim); }

.recipe-visible-rating-row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem 1.25rem; margin: 0 0 0.9rem; }
.recipe-your-rating { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; }

/* Row of equal-width icon-over-label action buttons (optimize, edit,
   nutrition, share) — replaces the old .recipe-actions row of small text
   buttons. Flexbox with flex:1 on each child, not a grid column-count, so it
   still divides evenly when the owner-only ones are hidden (non-owner: 2
   buttons). Every button gets the same min-height and a two-line label slot,
   so a long label ("Re-estimate nutrition") wraps inside the same box as a
   short one instead of making its own taller/wider. */
.icon-action-row { display: flex; gap: 0.5rem; margin: 0 0 1.5rem; }
.icon-action-btn {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  width: auto;
  min-width: 0;
  margin: 0;
  min-height: 4.6rem;
  padding: 0.45rem 0.4rem;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}
.icon-action-btn:hover { color: var(--text); border-color: var(--turmeric-dim); background: var(--surface-2); text-decoration: none; }
.icon-action-btn > span { text-wrap: balance; }

/* Some icon-action-row items ("Optimize with AI"'s details/summary popover,
   "Re-estimate nutrition"'s span wrapper carrying the htmx swap id) wrap
   their .icon-action-btn in another element rather than being one — the
   row's flex:1 rule only targets .icon-action-btn directly, so the wrapper
   needs its own to still claim an equal share of the row, and needs to be a
   flex container itself so the inner .icon-action-btn's own flex:1 fills it. */
.icon-action-row > .icon-action-wrap { display: flex; flex: 1 1 0; min-width: 0; position: relative; }
/* "Estimating…" is an htmx-indicator (opacity 0 until a request runs) but
   still takes layout space, which in this flex wrapper made the nutrition
   button narrower than its siblings. Out of flow, centred under the button. */
.icon-action-wrap > .htmx-indicator { position: absolute; top: 100%; left: 0; right: 0; margin-top: 0.25rem; text-align: center; white-space: nowrap; }
/* Recipe "Listen" button (partials/recipe_narration.html). While its htmx
   request runs, the label says which step ("Writing narration script…" /
   "Recording voice…"); when the audio plays, the speaker icon becomes pause.
   A stale narration (recipe edited since) gets a dot and a sub-label. */
.recipe-narration .icon-action-btn { position: relative; }
.recipe-narration .narration-busy,
.recipe-narration .icon-action-btn.htmx-request .narration-idle { display: none; }
.recipe-narration .icon-action-btn.htmx-request .narration-busy { display: inline; }
.recipe-narration .icon-action-btn[disabled] { cursor: progress; opacity: 1; }
.narration-sub { display: block; font-size: 0.66rem; font-weight: 500; color: var(--turmeric-dim); }
.recipe-narration .icon-action-btn.is-stale::after {
  content: "";
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--turmeric-dim);
}
.narration-icon-pause,
.recipe-narration .is-playing .narration-icon-play { display: none; }
.recipe-narration .is-playing .narration-icon-pause { display: inline; }
.recipe-narration .is-playing { color: var(--text); border-color: var(--turmeric-dim); }
/* Speed chip: sits in the Listen button's top-right corner as a sibling (a
   button can't nest in a button), so it doesn't take a slot in the row. */
.narration-speed {
  position: absolute;
  top: 0.25rem;
  right: 0.25rem;
  width: auto;
  min-height: 0;
  margin: 0;
  padding: 0.1rem 0.35rem;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  color: var(--text-dim);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
}
.narration-speed:hover { color: var(--text); border-color: var(--turmeric-dim); }
.dropdown-panel-form { min-width: 16rem; padding: 0.6rem; }
/* /settings voice picker: one row per OpenAI voice, name then description. */
.voice-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 0.25rem 1rem; }
.voice-list legend { grid-column: 1 / -1; margin-bottom: 0.5rem; }
.voice-option { display: flex; align-items: baseline; gap: 0.5rem; margin: 0; padding: 0.3rem 0; }
.voice-name { font-weight: 600; min-width: 4.5rem; }

.dropdown-cancel { display: none; }

/* ---- Share menu (recipe detail page's Share popover) ---- */
/* Full-bleed rows want their own horizontal padding so a hover background
   can reach the panel's edges — 0 side padding here instead of
   .dropdown-panel-form's uniform 0.6rem, kept only on the vertical axis. */
.share-menu-panel { min-width: 17.5rem; padding: 0.35rem 0; }
.share-primary-row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.6rem 0.85rem;
  min-height: 44px;
  color: var(--text);
}
.share-primary-row:hover, .share-primary-row:focus-visible { background: var(--surface); text-decoration: none; }
.share-primary-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--turmeric);
  color: #1c1509;
}
.share-primary-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.share-primary-title { font-size: 0.9rem; font-weight: 600; color: var(--text); }
.share-primary-sub { font-size: 0.76rem; color: var(--text-dim); }

/* /invite (account/invite.html): the link with its Copy button, then the
   same share rows as the recipe Share popover, on a card instead. */
.invite-link-row { display: flex; gap: 0.5rem; align-items: center; }
.invite-link-row input { margin-bottom: 0; flex: 1; min-width: 0; }
.invite-link-row .btn-sm { flex: none; width: auto; }
.invite-share { margin-top: 1rem; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }

.share-menu-divider { border-top: 1px solid var(--border); margin: 0.3rem 0; }
.share-menu-group-label {
  padding: 0.3rem 0.85rem 0.15rem;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  /* text-dim, not text-faint: this is a label, not decoration — see the
     AA-contrast note on .share-menu-notice below. */
  color: var(--text-dim);
}

/* Shared by the checkbox row, the native/WhatsApp/Email buttons, and (via
   .checkbox-row) reuses the app-wide checkbox fix above. */
.share-menu-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.85rem;
  min-height: 44px;
  width: 100%;
  text-align: left;
  font-size: 0.85rem;
  color: var(--text-dim);
  background: none;
  border: none;
  border-radius: 0;
  min-width: 0;
  cursor: pointer;
}
.share-menu-row:hover, .share-menu-row:focus-visible { background: var(--surface); color: var(--text); }
.share-menu-row svg { flex: none; color: var(--text-faint); }
.share-menu-row:hover svg, .share-menu-row:focus-visible svg { color: var(--text-dim); }

/* The checkbox row carries two lines of text (title + hint), so it's
   top-aligned to the title's line instead of .checkbox-row's usual
   vertical-center — centering against a two-line block would float the box
   between the lines instead of next to the title. */
.share-menu-checkbox { align-items: flex-start; height: auto; min-height: 44px; }
.share-menu-checkbox input[type="checkbox"] { margin-top: 3px; }
.share-menu-row-text { display: flex; flex-direction: column; gap: 1px; }
.share-menu-row-title { font-size: 0.85rem; color: var(--text); }
.share-menu-row-sub { font-size: 0.74rem; color: var(--text-dim); }

/* AA note: this used to be .text-faint (~3:1 against this panel's
   background — fails the 4.5:1 normal-text minimum). Every text node added
   or touched in this popover uses --text/--text-dim instead; --text-faint
   is kept only on the row icons above, which are decorative (non-text UI
   components need only ~3:1). */
.share-menu-notice {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.65rem 0.85rem 0.5rem;
  margin-top: 0.2rem;
  font-size: 0.76rem;
  color: var(--text-dim);
  border-top: 1px solid var(--border);
}
.share-menu-notice svg { flex: none; margin-top: 1px; color: var(--turmeric); }

/* "Optimize with AI"'s popover, at narrow widths: .dropdown-panel's base
   rule anchors it `position: absolute; right: 0` off the *trigger button's*
   edge, which is fine for the account/topbar menus (both already sit at the
   page's right edge) but not here — this trigger can be anywhere in
   .icon-action-row, so the panel grew leftward off-screen. Below 600px it
   drops out of that absolute overlay entirely and becomes a normal in-flow
   block instead, so it pushes the rest of the page down rather than
   floating over the Ingredients heading. It has to widen its own .details
   wrapper to 100% of the row first (flex-wrap so that doesn't disturb
   Edit/Favorite/Re-estimate, which simply wrap to the line below), since a
   width:100% panel is only as wide as *that* box, not the whole row. */
@media (max-width: 600px) {
  .icon-action-row { flex-wrap: wrap; row-gap: 0.5rem; }
  /* This element is both .dropdown-menu and .icon-action-wrap at once (see
     recipes/detail.html) — widen it to the full row and let its own two
     children (summary, panel) wrap onto separate lines within it. */
  .icon-action-row .dropdown-menu[open] { flex: 1 1 100%; flex-wrap: wrap; }
  .icon-action-row .dropdown-menu[open] > summary { flex: 0 0 auto; }
  .dropdown-panel-form {
    position: static;
    width: 100%;
    max-width: none;
    min-width: 0;
    margin-top: 0.5rem;
    box-shadow: none;
  }
  .dropdown-panel-form .input-group { flex-wrap: wrap; }
  .dropdown-panel-form .input-group input {
    flex: 1 1 100%;
    min-width: 0;
    font-size: 16px; /* ≥16px so iOS doesn't zoom the viewport on focus */
    min-height: 44px;
  }
  .dropdown-panel-form .input-group button {
    /* Pico's `button[type=submit] { width: 100% }` (pico.min.css) otherwise
       wins over flex-basis here and forces Go/Cancel onto their own lines
       instead of sharing one. */
    width: auto;
    flex: 1 1 auto;
    min-height: 44px;
  }
  .dropdown-cancel { display: inline-flex; }
}

.recipe-photos { margin-bottom: 1.5rem; }
.recipe-chef-tips { margin-bottom: 1.5rem; }
/* Tips are written as paragraphs separated by blank lines. */
.chef-tips-body { white-space: pre-line; }
/* Photos' and chef tips' section headers: heading + action button(s) hug
   each other on the left, rather than .page-header's default
   space-between spreading them to opposite edges. */
.photo-grid-header,
.chef-tips-header {
  justify-content: flex-start;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}
.photo-grid-header .card-title,
.chef-tips-header .card-title { margin-bottom: 0; }
.photo-grid-actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.photo-grid-empty { color: var(--text-dim); font-size: 0.85rem; margin: 0; }

/* Ingredients/steps' "Optimize with AI" trigger, on the recipe edit form —
   no heading to hug, just a button + spinner, so .page-header's
   space-between (or the hug-left rule above) wouldn't fit. */
.optimize-trigger { display: flex; align-items: center; gap: 0.6rem; margin: 0.75rem 0; }

.recipe-body {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) 2fr;
  gap: 2rem;
  margin: 0 0 2rem;
}
.ingredient-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ingredient-list li {
  /* Pico's `ul li { list-style: square }` targets the <li> directly, which
     beats list-style:none on the parent <ul> above — inheritance only wins
     when nothing more specific sets the property on the element itself. */
  list-style: none;
  padding: 0.4rem 0;
  font-size: 0.94rem;
}
.ing-amount { color: var(--turmeric); font-variant-numeric: tabular-nums; }

/* Numbered steps get real weight — the one place numbering is genuine
   sequence, since following them on a phone in a kitchen is the app's main
   physical use. */
.steps-list {
  list-style: none;
  counter-reset: step;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.steps-list li {
  counter-increment: step;
  display: flex;
  gap: 0.625rem;
  align-items: flex-start;
}
.steps-list li::before {
  content: counter(step);
  font-family: var(--font-heading);
  font-variation-settings: "SOFT" 40, "WONK" 1;
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--turmeric);
  line-height: 1.3;
}

/* ---- Cook thread — the one bold element: a continuous line down the page,
   each cook as a node, instead of a grid of identical cards. It's the app's
   whole point (a recipe is cooked many times, not once), so the structure
   says so. ---- */
.cook-thread {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding-left: 1.75rem;
}
.cook-thread::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 6px;
  bottom: 6px;
  width: 2px;
  background: var(--border);
}
.cook-entry { position: relative; display: flex; }
.cook-entry-node {
  position: absolute;
  left: -1.75rem;
  top: 0.6rem;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--turmeric);
  box-shadow: 0 0 0 3px var(--bg);
}
.cook-entry-body {
  flex: 1;
  min-width: 0;
  background: var(--surface-2);
  border-radius: var(--radius);
  padding: 0.7rem 1rem;
}
.cook-entry-header {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.3rem;
}
.cook-entry-date {
  font-family: var(--font-heading);
  font-variation-settings: "SOFT" 40, "WONK" 1;
  font-weight: 600;
}
.cook-entry-edit { margin-left: auto; }
.cook-entry-notes { margin: 0.3rem 0 0.75rem; white-space: pre-line; overflow-wrap: anywhere; }
.cook-entry-edit-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin: 0.3rem 0 0.75rem;
}
/* Pico defaults both `input:not([type=checkbox],[type=radio])` and
   `button[type=submit]` to width:100% — left alone, the button's
   flex-basis resolves from that 100% width and swallows the row, squeezing
   the input to nothing. Overriding width:auto on both lets flex sizing
   (not Pico's width rule) decide each one's size. */
.cook-entry-edit-form input[type="date"] {
  margin: 0;
  width: auto;
  flex: 0 0 auto;
}
.cook-entry-edit-form textarea {
  margin: 0;
  flex: 1 1 100%;
  min-width: 0;
}
.cook-entry-edit-form button {
  margin: 0;
  width: auto;
  flex: 0 0 auto;
  white-space: nowrap;
}

.cook-form { margin-bottom: 1.25rem; }
.cook-log-actions { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }

/* ---- Photo grid ---- */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
  gap: 0.5rem;
  margin-bottom: 0.6rem;
}
.photo-thumb { position: relative; border-radius: var(--radius-sm); overflow: hidden; aspect-ratio: 1; background: var(--surface-2); }
.photo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-thumb-cover { outline: 2px solid var(--turmeric); outline-offset: -2px; }
/* The photo popup (js/lightbox.js). Dark in both themes, like any photo viewer. */
a[data-lightbox] { cursor: zoom-in; }
html:has(dialog.lightbox[open]) { overflow: hidden; }
dialog.lightbox { background-color: rgba(0, 0, 0, 0.88); cursor: zoom-out; }
dialog.lightbox img { display: block; max-width: calc(100vw - 2rem); max-height: calc(100dvh - 2rem); border-radius: var(--radius-sm); }
dialog.lightbox .lightbox-close {
  position: fixed;
  top: calc(0.75rem + env(safe-area-inset-top));
  right: 0.75rem;
  width: 44px;
  height: 44px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 1.6rem;
  line-height: 1;
}
.photo-thumb-actions {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.2rem;
  padding: 0.25rem;
  background: rgba(0, 0, 0, 0.6);
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none; /* let clicks on the dark strip fall through to the full-size link behind it */
}
.photo-thumb-cost { margin-right: auto; font-size: 0.66rem; }
.photo-thumb:hover .photo-thumb-actions, .photo-thumb:focus-within .photo-thumb-actions { opacity: 1; }
.photo-thumb-actions .btn-sm { font-size: 0.66rem; padding: 2px 6px; pointer-events: auto; }
.photo-badge-ai {
  position: absolute;
  top: 0.3rem;
  left: 0.3rem;
  z-index: 1;
  background: rgba(0, 0, 0, 0.55);
  font-size: 0.65rem;
  padding: 0 6px;
}
/* Visually hidden but still focusable/clickable — same pattern as
   .star-rating input — rather than display:none, which would drop it from
   the tab order and break the label's native click-to-open association.
   Pico's input width/margin rules (0,1,1 — `:not()` takes its most
   specific argument) out-specify a lone class, so the selector below
   carries a class+element extra: without it the input stays width:100%,
   an invisible strip wide enough to sit under the neighbouring "Generate
   photo" button and answer hovers there with Chrome's own file-input
   tooltip ("No file chosen"). pointer-events:none keeps that tooltip and
   any stray click off the input for good — label activation is a
   synthetic click and keyboard focus is unaffected, so both still open
   the picker. */
.photo-upload-form label > .photo-upload-input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  pointer-events: none;
}
/* Pico styles every <label> display:block with a 0.375rem bottom margin,
   and a block label leaves .btn-sm's min-height slack below its text
   while a real <button> centres its own content — together that sat these
   picker labels a few px above the "Generate photo" button in
   .photo-grid-actions' align-items:center row. flex + centring + no
   margin makes the label's box identical to the button's. */
.photo-upload-form label {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
}

/* ---- Star rating (radio-based, no JS) ---- */
.star-rating { display: inline-flex; flex-direction: row-reverse; justify-content: flex-end; gap: 1px; border: 0; padding: 0; margin: 0; min-width: 0; }
.star-rating input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.star-rating label { cursor: pointer; font-size: 1.5rem; line-height: 1; color: var(--border); }
/* Pico gives every radio's label but the last a 1em right margin, which
   spread four of the five stars apart. */
.star-rating input[type="radio"] ~ label { margin: 0; }
.star-rating input:checked ~ label,
.star-rating label:hover,
.star-rating label:hover ~ label { color: var(--turmeric); }
.rating-form { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; margin-bottom: 0.4rem; }

@media (max-width: 700px) {
  .recipe-body { grid-template-columns: 1fr; gap: 1.5rem; }
}

@media (max-width: 640px) {
  .ing-row {
    grid-template-columns: 60px 76px 1fr 44px;
    grid-template-areas:
      "qty unit name remove"
      "note note note note";
    row-gap: 8px;
    padding: 12px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--surface);
    margin-bottom: 12px;
  }
}

/* ---- AI assist ---- */
.ai-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.ai-sub-form { margin-bottom: 0.4rem; }
.input-group { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.input-group input {
  flex: 1 1 100%;
  min-width: 160px;
  margin: 0;
  border-radius: var(--radius-sm);
}
.input-group button {
  white-space: nowrap;
  margin: 0;
}
.ai-panel { margin-top: 0.75rem; }
.ai-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.9rem; }
.ai-list > li { padding-bottom: 0.9rem; border-bottom: 1px solid var(--border); }
.ai-list > li:last-child { border-bottom: none; padding-bottom: 0; }
.ai-list p { margin: 0.25rem 0 0; }
.ai-sublist { margin: 0.35rem 0 0; padding-left: 1.1rem; }
.ai-panel-note { margin: 0 0 0.5rem; }
.ai-panel-meta { margin: 0.5rem 0 0; }
.import-form textarea { font-family: var(--font-body); }
.import-photo-row { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; }
/* "Take a photo" (recipes/import.html, static/js/import_images.js) opens the
   device camera via a hidden capture="environment" file input — useless on a
   desktop browser, which ignores `capture` and would just reopen an ordinary
   file dialog, redundant with "Choose files" right next to it. Hidden by
   default, shown only on a coarse/touch pointer — the same signal
   .chat-hint below and recipe_chat.js's COARSE use for "this is a phone". */
.import-camera-btn { display: none; }
@media (pointer: coarse) { .import-camera-btn { display: inline-block; } }
.import-image-list { list-style: none; margin: 0.25rem 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.import-image-list:empty { display: none; }
.import-image-list li {
  display: flex; align-items: center; gap: 0.5rem; max-width: 100%;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 0.25rem 0.4rem 0.25rem 0.25rem; font-size: 0.85rem;
}
.import-image-list img { width: 40px; height: 40px; object-fit: cover; border-radius: 4px; flex: none; }
.import-form kbd {
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim);
  border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.78rem;
}
.import-image-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 14rem; }
.import-image-list button { width: auto; margin: 0; flex: none; }

/* ---- Chat about this recipe (partials/recipe_chat.html, static/js/recipe_chat.js) ---- */
.recipe-chat { --rc-on-accent: #1c1509; }
.recipe-chat-private { margin: -0.4rem 0 0.75rem; }
.recipe-chat-intro { margin: 0 0 0.75rem; }
button.recipe-chat-del {
  order: -1; align-self: center; width: auto; margin: 0 0.3rem; padding: 0 0.35rem;
  background: none; border: 0; box-shadow: none; color: var(--pico-muted-color);
  font-size: 1.1rem; line-height: 1; cursor: pointer; opacity: 0.6;
}
button.recipe-chat-del:hover, button.recipe-chat-del:focus-visible { opacity: 1; }
button.recipe-chat-clear {
  display: block; margin: 0.6rem 0 0 auto; padding: 0.2rem 0;
  background: none; border: 0; box-shadow: none; width: auto;
  text-decoration: underline; cursor: pointer;
}
.recipe-chat-log {
  display: flex; flex-direction: column; gap: 0.6rem;
  max-height: min(60vh, 32rem); overflow-y: auto;
  margin-bottom: 0.75rem; padding-right: 0.25rem;
}
.recipe-chat-limit {
  margin: 0 0 0.6rem; padding: 0.4rem 0.7rem; border-radius: 8px;
  border: 1px solid var(--turmeric-dim); color: var(--text);
}
.recipe-chat-msg { display: flex; }
.recipe-chat-msg--user { justify-content: flex-end; }
.recipe-chat-bubble {
  max-width: min(85%, 42rem); padding: 0.55rem 0.85rem; border-radius: 18px;
  background: var(--surface-2); color: var(--text);
  line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere;
}
.recipe-chat-msg--assistant .recipe-chat-bubble { border-bottom-left-radius: 6px; }
.recipe-chat-msg--user .recipe-chat-bubble { background: var(--turmeric); color: var(--rc-on-accent); border-bottom-right-radius: 6px; }
.recipe-chat-msg--pending .recipe-chat-bubble { color: var(--text-dim); font-style: italic; }
.recipe-chat-msg--error .recipe-chat-bubble { color: var(--beet); }
/* The reply as it streams in — a preview only; the server's own rendering of the
   finished card replaces it (static/js/recipe_chat.js). */
.recipe-chat-msg--streaming .recipe-chat-bubble::after {
  content: ""; display: inline-block; width: 0.5em; height: 1em; margin-left: 2px;
  vertical-align: text-bottom; background: var(--text-dim); border-radius: 1px;
  animation: recipe-chat-caret 1s steps(2, start) infinite;
}
@keyframes recipe-chat-caret { to { visibility: hidden; } }
@media (prefers-reduced-motion: reduce) {
  .recipe-chat-msg--streaming .recipe-chat-bubble::after { animation: none; }
}
.recipe-chat-composer {
  display: flex; align-items: flex-end; gap: 0.5rem;
  padding: 0.35rem 0.35rem 0.35rem 0.85rem;
  border: 1px solid var(--border); border-radius: 22px; background: var(--surface-2);
}
.recipe-chat-composer:focus-within { border-color: var(--turmeric-dim); }
.recipe-chat-input.recipe-chat-input {  /* doubled class beats Pico's textarea rules */
  flex: 1; min-width: 0; margin: 0; padding: 0.4rem 0; border: 0; background: transparent;
  box-shadow: none; outline: 0; resize: none; max-height: 160px; line-height: 1.4;
}
.recipe-chat-input:focus { background: transparent; box-shadow: none; }
button.recipe-chat-send {
  flex: none; width: 2.2rem; height: 2.2rem; margin: 0; padding: 0; border: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--turmeric); color: var(--rc-on-accent); cursor: pointer;
}
button.recipe-chat-send:hover:not(:disabled) { background: #e4b23a; }
button.recipe-chat-send:disabled { opacity: 0.4; cursor: not-allowed; }
button.recipe-chat-attach {
  flex: none; width: 2.2rem; height: 2.2rem; margin: 0 0 0 -0.45rem; padding: 0; border: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; box-shadow: none; color: var(--text-dim); cursor: pointer;
}
button.recipe-chat-attach:hover { color: var(--text); background: none; }
/* Same signal as .import-camera-btn: desktop browsers ignore capture= and would
   only reopen the file dialog the other button already gives. */
button.recipe-chat-attach--camera { display: none; }
@media (pointer: coarse) { button.recipe-chat-attach--camera { display: inline-flex; } }
.recipe-chat-attached { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.5rem; }
.recipe-chat-attached[hidden] { display: none; }
.recipe-chat-attached img { width: 3rem; height: 3rem; object-fit: cover; border-radius: 8px; flex: none; }
.recipe-chat-attached-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-dim); }
.recipe-chat-attached .btn-sm { flex: none; width: auto; margin: 0; }
.recipe-chat-photo-note { margin: 0 0 0.5rem; }
.recipe-chat-photo-note[hidden] { display: none; }
.recipe-chat-photo { display: block; margin-bottom: 0.4rem; }
.recipe-chat-photo img, .recipe-chat-bubble > img { display: block; max-width: 100%; max-height: 14rem; border-radius: 12px; margin-bottom: 0.4rem; }
.recipe-chat-photo img { margin-bottom: 0; }
.recipe-chat-photo-sent { display: block; font-size: 0.85em; font-style: italic; margin-bottom: 0.25rem; }
.recipe-chat-starters { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.6rem; }
.recipe-chat-starters[hidden] { display: none; }
@media (max-width: 600px) {
  .recipe-chat-bubble { max-width: 92%; }
  .recipe-chat-input.recipe-chat-input { font-size: 16px; }  /* 16px so iOS doesn't zoom on focus */
}

/* ---- Feed ---- */
.feed-list { list-style: none; margin: 0; padding: 0; }
.feed-item {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.65rem 0;
  border-bottom: 1px solid var(--border);
}
.feed-item:last-child { border-bottom: none; }
.feed-item-body { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.feed-thumb { flex: none; }
.feed-thumb img { width: 48px; height: 48px; object-fit: cover; border-radius: 8px; display: block; }
.feed-note { margin: 0.2rem 0 0; }
.feed-time { flex: none; white-space: nowrap; }
.feed-more { display: flex; justify-content: center; padding: 0.75rem 0; border-bottom: none; }

/* ---- Chat (messages/*.html) — see the Chat section at the end of this file ---- */

/* Generic secondary line under a form control. --muted uses --text-dim
   rather than the app-wide .text-faint utility (~3:1 against surface — see
   the AA note on .share-menu-notice above): every new/touched line in this
   form needed to clear 4.5:1, and .text-faint alone couldn't. */
.compose-info-line { display: flex; align-items: flex-start; gap: 0.4rem; margin: 0.4rem 0 0; font-size: 0.82rem; color: var(--text-dim); }
.compose-info-line svg { flex: none; margin-top: 1px; color: var(--turmeric); }
.compose-info-line--muted { color: var(--text-dim); }

#recipient-access-note:empty { display: none; }

.compose-email-option { display: flex; flex-direction: column; gap: 0.3rem; }
.compose-email-note {
  margin: 0;
  /* Indented to align under the checkbox label's text, not the checkbox
     itself — width matches .checkbox-row's checkbox (1.125rem) + its gap
     (0.4rem). */
  margin-left: calc(1.125rem + 0.4rem);
  font-size: 0.8rem;
  color: var(--text-dim);
}

/* ---- Print — clean, no chrome, meant for a kitchen counter ---- */
html[data-print] { background: #fdfbf8; }
.print-body { background: #fdfbf8; color: #1c1712; font-family: var(--font-body); min-height: 100vh; }
html[data-print] { color-scheme: light; }
.print-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 1rem; margin-bottom: 1.25rem; }
.print-toolbar a { color: #6b4c00; }
.print-toolbar button {
  width: auto; margin: 0; padding: 0.45rem 1.1rem; border-radius: 6px;
  background: #1c1712; color: #fdfbf8; border: none; font-weight: 600; cursor: pointer;
}
@media print { .print-toolbar { display: none; } }
.print-container { max-width: 680px; margin: 0 auto; padding: 1.75rem 1.5rem 3rem; }
.print-title {
  font-family: var(--font-heading);
  font-variation-settings: "SOFT" 40, "WONK" 1;
  font-size: 2rem;
  margin: 0 0 0.4rem;
  color: #1c1712;
}
.print-description { color: #5a4f43; margin: 0 0 0.75rem; }
.print-meta { display: flex; gap: 1.25rem; color: #5a4f43; font-size: 0.9rem; margin-bottom: 1.5rem; }
.print-columns { display: grid; grid-template-columns: 1fr 1.6fr; gap: 2rem; }
.print-columns h2 { font-size: 1.05rem; margin: 0 0 0.6rem; }
.print-ingredients ul { margin: 0; padding-left: 1.1rem; }
.print-ingredients li { margin-bottom: 0.35rem; }
.print-steps ol { margin: 0; padding-left: 1.2rem; }
.print-steps li { margin-bottom: 0.6rem; }
@media print {
  .print-columns { grid-template-columns: 1fr 1.6fr; }
}
@media (max-width: 600px) {
  .print-columns { grid-template-columns: 1fr; gap: 1.25rem; }
}

/* Wines — reuses .recipe-card/.recipe-grid/.badge/.filter-bar/.star-rating/
   .empty-state/.photo-grid as-is (pure layout, not recipe-specific data);
   only the two inline status/rating controls need their own rule. */
.wine-status-control, .wine-rating-control { display: inline-flex; align-items: center; gap: 0.4rem; }
.wine-rating-control .rating-form { margin-bottom: 0; }
.wine-subtitle { margin: 0 0 0.6rem; }
.wine-detail-tags { margin-bottom: 1.25rem; }

.recipe-rating-control { display: inline-flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.recipe-rating-control .rating-form { margin-bottom: 0; }
.recipe-rating-control input[type="range"] { accent-color: var(--turmeric); width: 100px; margin: 0; }
.recipe-rating-control output { font-variant-numeric: tabular-nums; color: var(--turmeric); min-width: 2ch; }
.recipe-rating-control.is-unrated input[type="range"] { opacity: 0.55; }
.recipe-rating-control.is-unrated output { color: var(--text-dim); font-size: 0.85rem; }

/* ---- Chat (messages/*.html, static/js/chat.js) ----------------------------
   Three columns on desktop (>= 1024px): conversation list | thread | shared
   recipes. Below that a single column: /messages is the list screen, a
   conversation is the thread screen (list and side hidden, site header
   swapped for the thread's own back-button header). Colours map onto the
   app's own tokens; --chat-* are the few the palette had no name for. */
.container--chat {
  max-width: none;
  padding: 0;
  height: calc(100vh - var(--header-h));
  height: calc(100dvh - var(--header-h));
}
.chat {
  --chat-bubble-in: #30271b;
  --chat-tile: #3a2e1f;
  --chat-accent-soft: #f0c56a;
  --chat-on-accent: #1c1509;
  --chat-error: #e58a9c;
  display: flex;
  height: 100%;
  min-height: 0;
  color: var(--text);
}
.chat :focus-visible { outline: 2px solid var(--turmeric); outline-offset: 2px; }
.chat /* A group caption styled like Pico's <label>, for a row of several inputs
   that no single `for` can point at (ingredients, visibility). */
.field-label { display: block; margin-bottom: calc(var(--pico-spacing) * 0.375); padding: 0; font-weight: inherit; }
.form-fieldset { border: 0; padding: 0; margin-left: 0; margin-right: 0; min-width: 0; }
.tag-chip-more--static { cursor: default; }
a.tag-chip { text-decoration: none; }
a.tag-chip:hover { color: var(--text); border-color: var(--turmeric-dim); text-decoration: none; }

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

/* Avatars: initials on one of four stable tones (jinja filter avatar_tone) */
.avatar {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 44px; height: 44px; border-radius: 50%;
  font-weight: 600; font-size: 15px; line-height: 1;
}
.avatar--sm { width: 32px; height: 32px; font-size: 12px; }
.avatar--0 { background: #4a3a24; color: #f0c56a; }
.avatar--1 { background: #2f3a2a; color: #b9d8a6; }
.avatar--2 { background: #3a2a33; color: #e6b6cc; }
.avatar--3 { background: #263540; color: #a9cde6; }

/* Shared 44px icon-only button */
.chat .chat-icon-btn {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 44px; height: 44px; margin: 0; padding: 0;
  background: transparent; border: 0; border-radius: 12px;
  color: var(--text-dim); cursor: pointer; text-decoration: none;
}
.chat .chat-icon-btn:hover { background: var(--surface-2); color: var(--text); }
.chat .chat-icon-btn--accent { background: var(--chat-tile); color: var(--chat-accent-soft); }
.chat .chat-icon-btn--danger:hover { color: var(--chat-error); }

/* ---- left: conversation list ---- */
.chat-list {
  display: flex; flex-direction: column; flex: none; width: 320px; min-height: 0;
  background: var(--surface); border-right: 1px solid var(--border);
}
.chat-list-head { display: flex; flex-direction: column; gap: 16px; padding: 24px 20px 16px; }
.chat-list-title { display: flex; align-items: center; justify-content: space-between; }
.chat-list-title h2 {
  margin: 0; font-family: var(--font-heading); font-size: 26px; font-weight: 600; line-height: 1.2; color: var(--text);
}
.chat-search {
  display: flex; align-items: center; gap: 10px; height: 44px; margin: 0; padding: 0 14px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 12px; color: var(--text-dim);
}
.chat-search:focus-within { border-color: var(--turmeric-dim); }
.chat-search input[type="search"] {
  flex: 1; min-width: 0; height: 100%; margin: 0; padding: 0;
  background: transparent; border: 0; box-shadow: none; outline: 0;
  color: var(--text); font-size: 14px; font-family: inherit;
}
.chat-search input[type="search"]:focus { box-shadow: none; background: transparent; }
.chat-rows { list-style: none; margin: 0; padding: 0 10px 16px; overflow-y: auto; flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 4px; }
.chat-empty-list { padding: 8px 20px; color: var(--text-dim); font-size: 14px; }
.chat-row {
  display: flex; gap: 12px; min-height: 44px; padding: 12px; border-radius: 14px;
  color: var(--text); text-decoration: none;
}
.chat-row:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.chat-row--active, .chat-row--active:hover { background: var(--chat-bubble-in); }
.chat-row-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.chat-row-top, .chat-row-mid { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.chat-row-mid { align-items: center; }
.chat-row-name { font-weight: 600; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-row-time { flex: none; font-size: 12px; color: var(--text-dim); }
.chat-row-snippet { min-width: 0; font-size: 14px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-row--unread .chat-row-snippet { color: var(--text); }
.chat-row-recipe { font-size: 12px; color: var(--turmeric); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-badge {
  display: flex; align-items: center; justify-content: center; flex: none;
  min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px;
  background: var(--turmeric); color: var(--chat-on-accent); font-size: 12px; font-weight: 600;
}

/* ---- centre: thread ---- */
.chat-main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; background: var(--bg); }
.chat-head {
  display: flex; align-items: center; gap: 12px; flex: none; height: 72px; padding: 0 28px;
  border-bottom: 1px solid var(--border);
}
.chat .chat-back { display: none; }
.chat-head-text { flex: 1; min-width: 0; }
.chat-head-name { margin: 0; font-family: var(--font-body); font-size: 16px; font-weight: 600; line-height: 1.3; color: var(--text); }
.chat-head-sub { margin: 2px 0 0; font-size: 13px; color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-head-sub a { color: var(--turmeric); text-decoration: none; }
.chat-head-open {
  display: inline-flex; align-items: center; gap: 8px; flex: none; height: 44px; padding: 0 18px;
  border: 1px solid var(--turmeric-dim); border-radius: 12px;
  color: var(--chat-accent-soft); font-size: 14px; font-weight: 500; text-decoration: none;
}
.chat-head-open:hover { background: var(--surface-2); color: var(--chat-accent-soft); }
.chat-pinned { display: none; }

.chat-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 24px 28px; display: flex; flex-direction: column; }
/* margin-top:auto (not justify-content:flex-end, which clips the top of an
   overflowing flex column) pins a short thread to the bottom yet still scrolls. */
.chat-thread { margin-top: auto; display: flex; flex-direction: column; gap: 18px; }
.chat-pending { display: flex; flex-direction: column; gap: 18px; }
.chat-pending:not(:empty) { margin-top: 18px; }
.chat-empty {
  margin: auto; display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 24px;
  color: var(--text-dim); text-align: center;
}
.chat-empty p { margin: 0; }
.chat-main--empty { justify-content: center; }
.chat-cta {
  display: flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 20px;
  border-radius: 12px; background: var(--turmeric); color: var(--chat-on-accent);
  font-size: 14px; font-weight: 600; text-decoration: none;
}
.chat-cta:hover { background: #e4b23a; color: var(--chat-on-accent); }

.chat-day { display: flex; align-items: center; gap: 14px; color: var(--text-dim); font-size: 12px; font-weight: 500; }
.chat-day::before, .chat-day::after { content: ""; flex: 1; height: 1px; background: var(--border); }

.chat-group { display: flex; align-items: flex-end; gap: 10px; }
.chat-group--mine { justify-content: flex-end; }
.chat-group--theirs > .avatar { margin-bottom: 22px; }  /* sits beside the last bubble, above the timestamp line */
.chat-stack { display: flex; flex-direction: column; gap: 4px; min-width: 0; max-width: 480px; }
.chat-group--mine .chat-stack { align-items: flex-end; }
.chat-group--theirs .chat-stack { align-items: flex-start; }
.chat-bubble {
  padding: 10px 14px; border-radius: 18px; background: var(--chat-bubble-in); color: var(--text);
  font-size: 15px; line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere;
}
.chat-group--mine .chat-bubble { background: var(--turmeric); color: var(--chat-on-accent); }
.chat-group--mine .chat-bubble--last { border-bottom-right-radius: 6px; }
.chat-group--theirs .chat-bubble--last { border-bottom-left-radius: 6px; }
.chat-group--mine .chat-bubble--notice { background: var(--chat-bubble-in); color: var(--text-dim); }
.chat-meta { display: flex; align-items: center; gap: 6px; margin-top: 2px; font-size: 12px; color: var(--text-dim); }
.chat-tick--read { color: var(--turmeric); }
.chat-system { align-self: center; margin: 0; font-size: 12px; color: var(--text-dim); text-align: center; }
.chat-group--pending .chat-bubble { opacity: 0.7; }
.chat-group--failed .chat-bubble { opacity: 0.85; }
.chat button.chat-retry {
  width: auto; min-height: 44px; margin: -12px 0; padding: 0 4px; background: none; border: 0;
  color: var(--chat-error); font-size: 12px; font-family: inherit; text-decoration: underline; cursor: pointer;
}

/* Recipe share card (message_recipe_card.html) */
.chat-card { width: 380px; max-width: 100%; background: var(--surface); border: 1px solid var(--border); border-radius: 16px; overflow: hidden; }
.chat-card-photo {
  display: flex; align-items: center; justify-content: center; height: 96px;
  background: var(--chat-tile); color: var(--text-dim); overflow: hidden;
}
.chat-card-photo img { width: 100%; height: 100%; object-fit: cover; }
.chat-card-photo--tall { height: 120px; }
.chat-card-body { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px 16px; }
.chat-card-text { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.chat-card-label { font-size: 11px; font-weight: 600; letter-spacing: 0.08em; color: var(--turmeric); }
.chat-card-title { font-family: var(--font-heading); font-size: 19px; font-weight: 600; line-height: 1.2; color: var(--text); overflow-wrap: anywhere; }
.chat-card-title--lg { font-size: 20px; }
.chat-card-view {
  display: inline-flex; align-items: center; justify-content: center; flex: none; min-height: 44px; padding: 0 16px;
  border-radius: 12px; background: var(--chat-tile); color: var(--chat-accent-soft);
  font-size: 14px; font-weight: 500; text-decoration: none;
}
.chat-card-view:hover { background: var(--surface-2); color: var(--chat-accent-soft); }
.chat-thumb {
  display: flex; align-items: center; justify-content: center; flex: none; width: 40px; height: 40px;
  border-radius: 10px; background: var(--chat-tile); color: var(--text-dim); overflow: hidden;
}
.chat-thumb img { width: 100%; height: 100%; object-fit: cover; }
.chat-thumb--lg { width: 44px; height: 44px; }
.chat-thumb--xl { width: 52px; height: 52px; }

/* ---- composer ---- */
.chat-compose { flex: 1; min-height: 0; display: flex; flex-direction: column; margin: 0; }
.chat-compose-fields { gap: 12px; }
.chat-composer-wrap { flex: none; display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 8px 28px 20px; }
.chat-composer {
  display: flex; align-items: flex-end; gap: 8px; padding: 8px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 20px;
}
.chat-composer:focus-within { border-color: var(--turmeric-dim); }
.chat-plus { position: relative; flex: none; }
/* button.-qualified: Pico gives [type="submit"]/[type="button"] a 100% width
   that a bare class selector ties with and loses to on source order. */
.chat button.chat-plus-btn, .chat button.chat-send {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 44px; height: 44px; margin: 0; padding: 0; border: 0; border-radius: 14px; cursor: pointer;
}
.chat-plus-btn { background: var(--chat-tile); color: var(--chat-accent-soft); }
.chat-send { background: var(--turmeric); color: var(--chat-on-accent); }
.chat-send:hover:not(:disabled) { background: #e4b23a; }
.chat-send:disabled { opacity: 0.4; cursor: not-allowed; }
.chat-input {
  flex: 1; min-width: 0; height: 44px; max-height: 132px; box-sizing: border-box; margin: 0; padding: 11px 6px;
  resize: none; overflow-y: auto; background: transparent; border: 0; border-radius: 0; box-shadow: none; outline: 0;
  color: var(--text); font-family: inherit; font-size: 15px; line-height: 22px;
}
.chat-input:focus, .chat-input:active { background: transparent; border: 0; box-shadow: none; outline: 0; }
.chat-hint { margin: 0; padding-left: 8px; font-size: 12px; color: var(--text-dim); }

.chat-chip {
  display: flex; align-items: center; gap: 10px; align-self: flex-start; max-width: 100%;
  padding: 4px 4px 4px 8px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 14px;
}
.chat-chip-text { display: flex; flex-direction: column; min-width: 0; }
.chat-chip-title { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-chip-sub { font-size: 12px; color: var(--text-dim); }

.recipe-picker {
  position: absolute; left: 0; bottom: calc(100% + 12px); z-index: 5;
  width: min(340px, calc(100vw - 32px)); padding: 12px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px; box-shadow: var(--shadow);
}
.recipe-picker-note { margin: 0 0 8px; font-size: 12px; color: var(--text-dim); }
.recipe-picker-search { height: 44px; margin: 0 0 8px; font-size: 14px; }
.recipe-picker-results { max-height: 264px; overflow-y: auto; }
.recipe-picker-empty { margin: 0; padding: 8px 4px; font-size: 14px; color: var(--text-dim); }
.recipe-picker-list { list-style: none; margin: 0; padding: 0; }
.recipe-picker-list li, .chat-rows li { list-style: none; }  /* Pico styles bare `ul li` markers */
.recipe-picker-item {
  display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 2px 6px; border-radius: 10px;
  color: var(--text); text-decoration: none;
}
.recipe-picker-item:hover { background: var(--surface-2); color: var(--text); }
.recipe-picker-title { font-size: 14px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- right: shared in this chat ---- */
.chat-side {
  display: flex; flex-direction: column; gap: 20px; flex: none; width: 320px; min-height: 0; overflow-y: auto;
  padding: 24px 20px; background: var(--surface); border-left: 1px solid var(--border);
}
.chat-side-title { margin: 0; font-family: var(--font-body); font-size: 12px; font-weight: 600; letter-spacing: 0.08em; color: var(--text-dim); }
.chat-side-card { background: var(--surface); border: 1px solid var(--turmeric-dim); border-radius: 16px; overflow: hidden; }
.chat-side-card-body { display: flex; flex-direction: column; gap: 12px; padding: 14px 16px 16px; }
.chat-side-meta { margin-top: 4px; font-size: 13px; color: var(--text-dim); }
.chat-side-item { display: flex; align-items: center; gap: 12px; min-height: 44px; padding: 10px; border-radius: 14px; color: var(--text); text-decoration: none; }
.chat-side-item:hover { background: var(--surface-2); color: var(--text); }
.chat-side-item-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.chat-side-item-title { font-size: 15px; font-weight: 500; overflow-wrap: anywhere; }

/* ---- below 1024px: one column ---- */
@media (max-width: 1023px) {
  /* The thread screen brings its own header (back button, avatar, name), so
     the site's nav steps aside; the list screen keeps it. */
  body.chat-page--thread .site-header { display: none; }
  body.chat-page--thread .container--chat { height: 100vh; height: 100dvh; }
  .chat-side { display: none; }
  .chat--list .chat-main { display: none; }
  .chat--thread .chat-list { display: none; }
  .chat-list { width: 100%; border-right: 0; }
  .chat .chat-back { display: inline-flex; margin-right: -4px; }
  .chat-head { height: 64px; padding: 0 8px; background: var(--surface); }
  .chat-head .avatar { width: 40px; height: 40px; font-size: 14px; }
  .chat-head-open { display: none; }
  .chat-head-sub { display: none; }  /* the pinned bar below carries the recipe on mobile */
  .chat-pinned {
    display: flex; align-items: center; gap: 12px; flex: none; padding: 10px 16px;
    background: var(--surface); border-bottom: 1px solid var(--border);
  }
  .chat-pinned-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
  .chat-pinned-title { font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .chat-scroll { padding: 16px 16px 12px; }
  .chat-thread, .chat-pending { gap: 16px; }
  .chat-stack { max-width: 280px; }
  .chat-card { width: 280px; }
  .chat-card-photo { height: 72px; }
  .chat-card-body { flex-direction: column; align-items: stretch; gap: 10px; padding: 12px 14px 14px; }
  .chat-card-title { font-size: 18px; }
  .chat-composer-wrap { padding: 8px 12px 28px; border-top: 1px solid var(--border); background: var(--bg); }
  .chat-composer { border-radius: 22px; padding: 6px; }
  .chat-input { font-size: 16px; }  /* 16px so iOS doesn't zoom on focus */
  .chat-hint { display: none; }
  .chat-search input[type="search"] { font-size: 16px; }
  .recipe-picker-search { font-size: 16px; }
}
@media (pointer: coarse) { .chat-hint { display: none; } }

/* =====================================================================
   Audit 2026-09-27 — accessibility, touch, feedback, phone navigation,
   print, and the light theme. Kept together at the end so each rule wins
   the cascade over the older section it adjusts.
   ===================================================================== */

/* ---- Contrast (AA 4.5:1 on every surface) ----
   --text-faint and --beet (palette at the top) were raised from 3.0-3.7:1
   and 2.9-3.5:1; these two used colours that stayed below it. */
.narration-sub { color: var(--text-dim); }
.star-rating label { color: var(--text-faint); }

/* ---- Visible keyboard focus everywhere ----
   Pico's ring is turmeric at 35% alpha (~2:1). Hidden inputs that drive a
   visible label (star radios, photo file inputs) pass the ring to it. */
:focus-visible { outline: 2px solid var(--turmeric); outline-offset: 2px; }
.star-rating input:focus-visible + label,
.photo-upload-form input:focus-visible { outline: 2px solid var(--turmeric); outline-offset: 2px; }
label:has(> .photo-upload-input:focus-visible) { outline: 2px solid var(--turmeric); outline-offset: 2px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute;
  left: 0.75rem;
  top: -3rem;
  z-index: 100;
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius-sm);
  background: var(--turmeric);
  color: var(--pico-primary-inverse);
  font-weight: 600;
}
.skip-link:focus { top: 0.75rem; text-decoration: none; }

/* ---- Long words (URLs, compound ingredient names) never widen the page ---- */
.recipe-title, .recipe-description, .ingredient-list li, .steps-list li,
.recipe-card-title, .chef-tips-body, .cook-entry-notes { overflow-wrap: anywhere; }

/* ---- Toasts (base.html: htmx errors and HX-Trigger showToast) ---- */
.toast-region {
  position: fixed;
  left: 50%;
  bottom: calc(1rem + var(--bottom-nav-h) + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 60;
  width: max-content;
  max-width: calc(100vw - 2rem);
  pointer-events: none;
}
.toast {
  padding: 0.65rem 1rem;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  box-shadow: var(--shadow);
  font-size: 0.9rem;
  pointer-events: auto;
  animation: toast-in 0.18s ease-out;
}
.toast--error { border-color: var(--beet-dim); }
.toast--error::before { content: "⚠ "; color: var(--beet); }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ---- Bottom tab bar (phones) ---- */
.bottom-nav { display: none; }
.account-menu-mobile { display: none; }
@media (max-width: 600px) {
  :root[data-theme] { --bottom-nav-h: calc(58px + env(safe-area-inset-bottom)); }
  .bottom-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    height: var(--bottom-nav-h);
    padding-bottom: env(safe-area-inset-bottom);
    background: var(--surface);
    border-top: 1px solid var(--border);
  }
  .bottom-nav-tab {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 44px;
    color: var(--text-dim);
    font-size: 0.7rem;
    font-weight: 600;
  }
  .bottom-nav-tab:hover { text-decoration: none; color: var(--text); }
  .bottom-nav-tab.active { color: var(--turmeric); }
  .account-menu-mobile { display: block; }
  /* The chat thread screen is full-height with its own composer at the
     bottom; the list screen keeps the tab bar and shrinks to clear it. */
  body.chat-page--thread { --bottom-nav-h: 0px; }
  body.chat-page--thread .bottom-nav { display: none; }
  body.chat-page .container--chat {
    height: calc(100vh - var(--header-h) - var(--bottom-nav-h));
    height: calc(100dvh - var(--header-h) - var(--bottom-nav-h));
  }
  body.chat-page--thread .container--chat { height: 100vh; height: 100dvh; }
}

/* ---- Touch: 44px targets and no iOS focus zoom ----
   Visual size stays; the hit area grows (min-height/min-width or padding). */
@media (pointer: coarse) {
  .icon-btn, .topbar-icon-btn, .header-icon-btn { min-width: 44px; min-height: 44px; }
  .icon-btn { width: 44px; height: 44px; }
  .topbar-icon-btn, .header-icon-btn { width: 44px; height: 44px; }
  .tag-chip { min-height: 36px; padding: 4px 12px; font-size: 0.85rem; }
  .btn-sm { min-height: 40px; }
  .photo-thumb-actions .btn-sm { min-height: 36px; font-size: 0.78rem; padding: 4px 10px; }
  .narration-speed { min-height: 36px; min-width: 44px; }
  button.recipe-chat-send, button.recipe-chat-attach { min-width: 44px; min-height: 44px; }
  .visibility-segmented label, .visibility-segmented button, .group-chip { min-height: 40px; }
  .site-header nav a.nav-tab { padding: 0.6rem 0; }
  .ing-row .ing-note, select.visibility-select, .filter-bar input, .filter-bar select,
  .cook-entry-edit-form textarea, .cook-entry-edit-form input { font-size: 16px; }
  select.visibility-select { height: 40px; }
}

/* ---- Phone layout ---- */
@media (max-width: 600px) {
  /* The recipe action row is shorter, so the ingredients start higher up. */
  .icon-action-btn { min-height: 3.6rem; font-size: 0.68rem; padding: 0.35rem 0.25rem; }
  /* Not sticky on a phone: pinned under the header it kept search/filters
     on screen at the cost of a third of the viewport for the cards. */
  .cookbook-hero { position: static; }
  .form-grid-3 { grid-template-columns: 1fr 1fr; }
}

/* ---- Desktop: the browse pages use the width they have ---- */
@media (min-width: 1100px) {
  .container--browse { max-width: 1280px; }
}

/* ---- Reduced motion, everywhere ---- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- Printing the recipe page itself (Ctrl+P) — only the recipe, on
   white. /recipes/{id}/print stays the dedicated print view. ---- */
@media print {
  :root[data-theme] {
    --bg: #fff; --surface: #fff; --surface-2: #fff; --border: #ccc;
    --text: #111; --text-dim: #333; --text-faint: #555; --turmeric: #6b4c00;
    color-scheme: light;
  }
  .site-header, .bottom-nav, .skip-link, .toast-region, .detail-topbar, .icon-action-row,
  .recipe-visible-rating-row, #recipe-chat, .recipe-photos, .ai-assist-card, .cook-log-card,
  .cook-mode-bar, .cookbook-hero-actions { display: none !important; }
  .container { max-width: none; padding: 0; }
  .recipe-body { grid-template-columns: 1fr 1.6fr; }
  a { color: inherit; }
}

/* ---- Light theme ----
   Applied when the device prefers light (base.html sets data-theme on
   <html> before first paint). Same tokens, daylight values; the handful of
   hard-coded colours that assumed a dark ground are restated here. */
:root[data-theme="light"] {
  --bg: #fbf7f1;
  --surface: #ffffff;
  --surface-2: #f3ece2;
  --border: #ded3c4;
  --text: #2a2119;
  --text-dim: #5e5246;
  --text-faint: #6f6356;
  --turmeric: #8a6200;
  --turmeric-dim: #c9a24a;
  --herb: #4d6b2f;
  --herb-dim: #9fb887;
  --beet: #a8324a;
  --beet-dim: #d9a0ac;
  --shadow: 0 2px 12px rgba(60, 40, 20, 0.12);
  color-scheme: light;

  --pico-primary-hover: #7d5900;
  --pico-primary-hover-background: #7d5900;
  --pico-primary-focus: rgba(138, 98, 0, 0.3);
  --pico-primary-inverse: #ffffff;
  --pico-secondary-focus: rgba(94, 82, 70, 0.25);
  --pico-mark-background-color: rgba(138, 98, 0, 0.2);
  --pico-table-row-stripped-background-color: rgba(0, 0, 0, 0.02);
}
[data-theme="light"] .btn-primary,
[data-theme="light"] .btn-sm.btn-primary,
[data-theme="light"] .share-primary-icon { color: #fff; }
[data-theme="light"] .btn-primary:hover,
[data-theme="light"] .btn-sm.btn-primary:hover,
[data-theme="light"] button.recipe-chat-send:hover:not(:disabled),
[data-theme="light"] .chat-cta:hover { background: #7d5900; }
[data-theme="light"] .recipe-chat { --rc-on-accent: #fff; }
[data-theme="light"] .chat {
  --chat-bubble-in: #efe6da;
  --chat-tile: #f3ece2;
  --chat-accent-soft: #8a6200;
  --chat-on-accent: #fff;
  --chat-error: #a8324a;
}
[data-theme="light"] .error-msg { color: #8f2a3f; background: rgba(168, 50, 74, 0.08); }
[data-theme="light"] .notice-msg { color: #3f5a26; background: rgba(77, 107, 47, 0.08); }
[data-theme="light"] .warning-msg { color: #6e4e00; background: rgba(138, 98, 0, 0.08); }
[data-theme="light"] .recipe-card-visibility-inline { background: rgba(0, 0, 0, 0.04); }
[data-theme="light"] .account-avatar,
[data-theme="light"] .recipe-card-creator-avatar { color: #2a2119; }
[data-theme="light"] input:-webkit-autofill,
[data-theme="light"] input:-webkit-autofill:focus { -webkit-box-shadow: 0 0 0 1000px var(--surface-2) inset; }

/* ---- Kitchen tools (recipes/detail.html #cook-tools, static/js/cook_mode.js) ---- */
.cook-mode-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  margin: 0 0 1rem;
}
.cook-mode-bar .btn-sm { display: inline-flex; align-items: center; gap: 0.35rem; margin: 0; width: auto; }
.cook-mode-toggle[aria-pressed="true"] { color: var(--turmeric); border-color: var(--turmeric-dim); }
.servings-scaler { display: inline-flex; align-items: center; gap: 0.25rem; }
.servings-scaler .btn-sm { min-width: 2.2rem; justify-content: center; font-size: 1rem; line-height: 1; }
.servings-scaler output { min-width: 6.5ch; text-align: center; font-size: 0.88rem; color: var(--text-dim); }
.cook-mode-bar.is-scaled .servings-scaler output { color: var(--turmeric); font-weight: 600; }
.cook-mode-note { flex-basis: 100%; margin: 0; }

.ing-check { display: flex; align-items: flex-start; gap: 0.55rem; cursor: pointer; margin: 0; }
.ing-check input[type="checkbox"] { flex: none; margin: 0.2rem 0 0; width: 1.05rem; height: 1.05rem; }
.ingredient-list li.is-ticked > .ing-check > span { color: var(--text-faint); text-decoration: line-through; }

.step-timer {
  display: inline-flex;
  align-items: center;
  width: auto;
  min-height: 0;
  margin: 0 0.1rem;
  padding: 0 0.45rem;
  border: 1px solid var(--turmeric-dim);
  border-radius: 999px;
  background: rgba(212, 162, 42, 0.12);
  color: var(--turmeric);
  font: inherit;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
  cursor: pointer;
}
.step-timer.is-running { background: var(--turmeric); color: var(--pico-primary-inverse); }
.step-timer.is-done { border-color: var(--herb-dim); color: var(--herb); background: transparent; }

/* Cook mode: bigger text, tappable steps, the current one highlighted. */
body.cook-mode .ingredient-list li { font-size: 1.12rem; padding: 0.55rem 0; }
body.cook-mode .steps-list li {
  font-size: 1.15rem;
  line-height: 1.6;
  cursor: pointer;
  padding: 0.5rem 0.6rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
}
body.cook-mode .steps-list li.is-current { border-color: var(--turmeric-dim); background: rgba(212, 162, 42, 0.08); }
body.cook-mode .steps-list li.is-done { opacity: 0.5; }
@media (pointer: coarse) {
  .ing-check input[type="checkbox"] { width: 1.35rem; height: 1.35rem; }
  .ingredient-list li { padding: 0.55rem 0; }
  .step-timer { min-height: 32px; }
}
/* Pico styles every [role=group] as a full-width, flex:1 button group;
   the servings scaler is a compact inline control. */
.servings-scaler[role="group"] {
  display: inline-flex;
  width: auto;
  margin: 0;
  border-radius: 0;
  box-shadow: none;
}
.servings-scaler[role="group"] > * { flex: none; }
.servings-scaler[role="group"] > .btn-sm { border-radius: var(--radius-sm); }

/* An idle htmx indicator is only transparent (htmx's own CSS), so in this
   row of buttons it would still hold its width as a gap. */
.photo-grid-actions .htmx-indicator { display: none; }
.photo-grid-actions .htmx-indicator.htmx-request { display: inline; }
