/* The app's stylesheet. Screen and print share one layout, deliberately.
   The design language is the reader's (reader/reader.css, from
   spike/format-study.html); the menu and recipe additions are the prototype's
   (proto/app.css). Styles for later stages are added with those stages. */

:root {
  --paper: #FCFBF8;
  --ink: #1B1B1F;
  --ink-2: #4A4A52;
  --rule: #C8C5BC;
  --chip: #ECEAE2;
  --accent: #0E5A4A;
  --accent-ink: #FFFFFF;
  --warn-bg: #FFF3E3;
  --warn-edge: #B4581B;
  --ok-bg: #E4F1EC;
  --bar: #F1EFE8;
  --heart: #A3283C;
  --fs: 19px;
  --fs-h1: 2.6rem;
  --fs-step: 1.6rem;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #151518; --ink: #F0EEE8; --ink-2: #B9B7B0; --rule: #3A3A40;
    --chip: #26262C; --accent: #5FC4A9; --accent-ink: #0F1A17;
    --warn-bg: #2B2118; --warn-edge: #E08A4A; --ok-bg: #16271F; --bar: #1E1E22; --heart: #E8798E;
  }
}
:root[data-theme="dark"] {
  --paper: #151518; --ink: #F0EEE8; --ink-2: #B9B7B0; --rule: #3A3A40;
  --chip: #26262C; --accent: #5FC4A9; --accent-ink: #0F1A17;
  --warn-bg: #2B2118; --warn-edge: #E08A4A; --ok-bg: #16271F; --bar: #1E1E22; --heart: #E8798E;
}
:root[data-size="s"]  { --fs: 16px; --fs-h1: 2.2rem; --fs-step: 1.4rem; --ctl: 16px; }
:root[data-size="m"]  { --fs: 19px; --fs-h1: 2.6rem; --fs-step: 1.6rem; --ctl: 17px; }
:root[data-size="l"]  { --fs: 23px; --fs-h1: 2.8rem; --fs-step: 1.7rem; --ctl: 20px; }
:root[data-size="xl"] { --fs: 28px; --fs-h1: 3rem;   --fs-step: 1.8rem; --ctl: 24px; }

* { box-sizing: border-box; }
html, body { margin: 0; }
body {
  background: var(--paper); color: var(--ink);
  font-family: "Atkinson Hyperlegible", "Helvetica Neue", Arial, sans-serif;
  font-size: var(--fs); line-height: 1.45;
  -webkit-text-size-adjust: 100%;
  padding-top: env(safe-area-inset-top, 0px);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
a { color: var(--accent); }
img { max-width: 100%; }
[hidden] { display: none !important; }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---------- top bar ---------- */
.bar {
  position: sticky; top: env(safe-area-inset-top, 0px); z-index: 5;
  background: var(--bar); border-bottom: 1px solid var(--rule);
  padding: 10px 16px; display: flex; flex-wrap: wrap; gap: 10px 22px; align-items: center;
  font-size: 15px;
}
.bar .home {
  font-family: "Fraunces", Georgia, serif; font-weight: 700; font-size: 20px;
  color: var(--ink); text-decoration: none; margin-right: auto;
}
.bar .home:hover { color: var(--accent); }
.grp { display: flex; align-items: center; gap: 6px; }
.lbl { color: var(--ink-2); text-transform: uppercase; letter-spacing: .06em; font-size: 12px; }
button {
  font: inherit; font-size: var(--ctl, 15px); font-weight: 700; padding: 6px 12px; border-radius: 6px;
  border: 1.5px solid var(--rule); background: var(--paper); color: var(--ink); cursor: pointer;
  min-height: 40px; min-width: 40px;
}
button[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
button:disabled { opacity: .45; cursor: not-allowed; }
/* Buttons and text boxes follow the text size too, a little smaller than the text, as --ctl
   (features/reading_settings.feature); the top bar keeps one size at every text size, so it never
   wraps. */
.bar button { font-size: 15px; }
/* :where() gives this no weight, so any box styled elsewhere (the editor's, the list's) keeps its
   own size; an old browser without :where() skips it and keeps today's boxes. */
:where(input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), textarea) {
  font: inherit; font-size: max(16px, var(--ctl, 1em));
}
.bar input { font-size: 15px; }
/* Every choice box follows the text size, never below 16px, which an iPhone would zoom in on
   (features/reading_settings.feature). Without this a select keeps the browser's own small text. */
select {
  font: inherit; font-size: max(16px, 1em); max-width: 100%; min-height: 44px;
  padding: 6px 10px; border-radius: 8px; border: 1.5px solid var(--rule); background: var(--paper); color: var(--ink);
}
button:focus-visible, input:focus-visible, select:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.favbtn[aria-pressed="true"] { background: var(--heart); border-color: var(--heart); color: #fff; }

.status { padding: 24px 16px; color: var(--ink-2); max-width: 65ch; }
.status.error { color: var(--warn-edge); font-weight: 700; }
/* js/too-old.js: said instead of "Loading…" on a browser too old for the app. */
.status.tooold { color: var(--ink); background: var(--warn-bg); border-left: 5px solid var(--warn-edge); margin: 16px; }

.page { max-width: 820px; margin: 0 auto; padding: 28px 16px 64px; }

/* ---------- list ---------- */
.finder { display: grid; gap: 14px; margin-bottom: 20px; }
/* The search box stays in view under the top bar as the list scrolls (Jeff, 2026-10-22). */
.list .finder { position: sticky; top: calc(env(safe-area-inset-top, 0px) + var(--bar-h, 0px)); z-index: 4;
  background: var(--paper); padding-top: 10px; padding-bottom: 10px; margin-top: -10px; margin-bottom: 10px; }
.search-btn { display: inline-flex; align-items: center; justify-content: center; padding: 6px 10px; }
.recipes .why { flex-basis: 100%; color: var(--ink-2); font-size: .85em; }
.srch input {
  font: inherit; width: 100%; padding: 12px 14px; border-radius: 8px;
  border: 1.5px solid var(--rule); background: var(--paper); color: var(--ink);
}
.count { color: var(--ink-2); font-size: .85em; margin: 0 0 10px; }

.recipes { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.recipes li { border-bottom: 1px solid var(--rule); }
.recipes a {
  display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline;
  padding: 12px 4px; text-decoration: none; color: var(--ink);
}
.recipes a:hover, .recipes a:focus-visible { background: var(--chip); }
.recipes .nm { font-weight: 700; }
.recipes .cat { color: var(--ink-2); font-size: .8em; text-transform: uppercase; letter-spacing: .06em; }
.recipes .fav { color: var(--heart); margin-left: auto; }
/* Added and not yet saved (features/adding_a_recipe.feature): seen only by whoever added it. */
.recipes .unfinished { color: var(--ink-2); font-size: .8em; border: 1px solid currentColor; border-radius: 4px; padding: 0 .35em; }

/* ---------- recipe header ---------- */
header.recipe { border-bottom: 3px solid var(--ink); padding-bottom: 14px; margin-bottom: 22px; }
.crumb { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; font-size: .85em; }
.crumb .cat { text-transform: uppercase; letter-spacing: .08em; font-weight: 700; color: var(--accent); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip { background: var(--chip); border-radius: 999px; padding: 2px 11px; font-size: .8em; color: var(--ink-2); }
h1 {
  font-family: "Fraunces", Georgia, "Times New Roman", serif; font-weight: 700;
  font-size: var(--fs-h1); line-height: 1.05; margin: 12px 0 10px; text-wrap: balance; letter-spacing: -.01em;
}
.metarow { display: flex; flex-wrap: wrap; gap: 12px 28px; align-items: center; }
.meta { display: flex; flex-wrap: wrap; gap: 6px 28px; font-size: .95em; }
.meta span { color: var(--ink-2); }
.meta b { font-variant-numeric: tabular-nums; }
.scaler { display: flex; align-items: center; gap: 8px; }
.scaler output { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 2.2ch; text-align: center; }
.scaler .reset { font-weight: 400; font-size: var(--ctl); padding: 4px 10px; min-height: 34px; }
/* The whole photo, full width, at the 3:2 shape the editor frames it to (Jeff,
   2026-10-18). A photo of another shape, added before framing existed, is
   cropped to 3:2 about its center until someone frames it. */
.photo { display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: cover; border-radius: 6px; margin: 4px 0 14px; }
.photo[hidden] { display: none; }
.head { font-size: 1.02em; margin: 0 0 4px; max-width: 65ch; white-space: pre-line; }
.src { color: var(--ink-2); font-size: .85em; margin: 0 0 4px; }

/* ---------- body ---------- */
h2 {
  font-family: "Fraunces", Georgia, serif; font-weight: 700; font-size: 1.5em; margin: 0 0 12px;
  padding-bottom: 6px; border-bottom: 1.5px solid var(--rule); break-after: avoid;
}
h3 { font-size: 1.05em; text-transform: uppercase; letter-spacing: .06em; color: var(--accent); margin: 22px 0 8px; break-after: avoid; }
h3:first-child { margin-top: 0; }

.scalenote { font-size: .85em; color: var(--warn-edge); margin: 0 0 10px; font-weight: 700; }

.ing ul { list-style: none; margin: 0; padding: 0; }
.ing li {
  display: grid; grid-template-columns: 1.15em 1fr; gap: 10px; align-items: start;
  padding: 5px 0; break-inside: avoid;
}
.ing li.grp-head { display: block; padding: 16px 0 4px; }
.ing li.grp-head h3 { margin: 0; }
/* A checkbox doesn't inherit the text size, so it stayed small and high at the
   larger sizes (Jeff, 2026-10-19). Inherit it, then center the box on the first line. */
.ing input { font: inherit; width: 1.1em; height: 1.1em; margin: .18em 0 0; margin: calc((1lh - 1.1em) / 2) 0 0; accent-color: var(--accent); }
.ing li.done .txt { text-decoration: line-through; color: var(--ink-2); }
.ing .n { color: var(--ink-2); font-size: .9em; display: block; line-height: 1.3; }
.ing .q { font-weight: 700; font-variant-numeric: tabular-nums; }

/* Numeral is positioned, not a grid cell, so inline children stay in the text flow.
   A grid here wrapped <i> and <b> into the numeral column, one word per line. */
.method ol { list-style: none; margin: 0; padding: 0; counter-reset: step; }
.method ol li {
  counter-increment: step; position: relative; padding-left: 2.6em;
  margin: 0 0 12px; break-inside: avoid; max-width: 68ch;
}
.method ol li::before {
  content: counter(step); position: absolute; left: 0; top: .05em;
  font-family: "Fraunces", Georgia, serif; font-weight: 700;
  font-size: var(--fs-step); line-height: 1; color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.method p { max-width: 68ch; margin: 0 0 12px; white-space: pre-line; }
.callout {
  background: var(--warn-bg); border-left: 5px solid var(--warn-edge); padding: 10px 14px; margin: 0 0 14px;
  max-width: 68ch; break-inside: avoid; white-space: pre-line;
}
.nutr { border: 1.5px dashed var(--rule); padding: 12px 14px; color: var(--ink-2); font-size: .9em; max-width: 68ch; }
.nutr table { border-collapse: collapse; width: 100%; margin-top: 8px; }
.nutr td { padding: 3px 0; border-bottom: 1px solid var(--rule); }
.nutr td:last-child { text-align: right; font-variant-numeric: tabular-nums; }
.prov { margin-top: 32px; padding-top: 10px; border-top: 1px solid var(--rule); color: var(--ink-2); font-size: .8em; display: flex; flex-wrap: wrap; gap: 6px 24px; }

/* ---------- layout variants ---------- */
.body { display: grid; gap: 28px; }
[data-layout="stacked"] .body { grid-template-columns: 1fr; }
[data-layout="stacked"] .ing ul { column-count: 2; column-gap: 32px; }
[data-layout="stacked"] .ing li { break-inside: avoid; }
[data-layout="sidebar"] .body { grid-template-columns: minmax(230px, 1fr) 2fr; gap: 36px; align-items: start; }
[data-layout="sidebar"] .ing { position: sticky; top: calc(env(safe-area-inset-top, 0px) + 70px); }

@media (max-width: 640px) {
  .body, [data-layout="sidebar"] .body { grid-template-columns: 1fr; }
  [data-layout="stacked"] .ing ul { column-count: 1; }
  [data-layout="sidebar"] .ing { position: static; }
}

/* ---------- print: US Letter, always light, same layout as the screen ---------- */
@page { size: letter; margin: 0.6in 0.65in; }
@media print {
  :root { --paper: #FFFFFF; --ink: #000000; --ink-2: #333333; --rule: #999999; --chip: #EEEEEE;
          --accent: #0E5A4A; --warn-bg: #F4F4F4; --warn-edge: #000000; --heart: #000000; }
  html, body { background: #FFFFFF; padding: 0; }
  /* Sizes on a Mac. An iPad or iPhone prints the same page about 20% larger (Jeff's prints,
     2026-10-28), so 11.5pt here is about 14pt from an iPad. */
  body { font-size: 11.5pt; line-height: 1.38; }
  :root[data-size="l"] body { font-size: 13pt; }
  :root[data-size="xl"] body { font-size: 14.5pt; }
  :root[data-size="s"], :root[data-size="m"] { --fs-step: 17.5pt; }
  :root[data-size="l"], :root[data-size="xl"] { --fs-h1: 24pt; --fs-step: 14pt; }
  .bar, .drawer, .status, .scaler button, .favbtn, .more, .shopbtn, .shoppanel, .finder, .count, .photo, .notice { display: none !important; }
  .page { max-width: none; padding: 0; }
  h1 { font-size: 24pt; }
  .scaler { gap: 4px; }
  .ing input { -webkit-appearance: none; appearance: none; border: 1.5px solid #000; border-radius: 2px; width: .95em; height: .95em; margin-top: calc((1lh - .95em) / 2); }
  [data-layout="sidebar"] .ing { position: static; }
  [data-layout="sidebar"] .body { grid-template-columns: 2.1in 1fr; gap: .3in; }
  /* Two columns on paper, down then across, as on screen. A grid, because an iPad prints
     columns (column-count) as one (Jeff's test print, 2026-10-28) but prints a grid. --rows,
     half the lines rounded up, is set where the list is made. */
  [data-layout="stacked"] .ing ul { column-count: auto; display: grid; grid-auto-flow: column;
    grid-template-rows: repeat(var(--rows, 1), auto); grid-auto-columns: 1fr; column-gap: 32px; }
  a { color: #000; text-decoration: none; }
  .prov, .nutr { break-inside: avoid; }
  /* The step number is taller than one printed line and sits outside the text's flow, so a
     one-line step at the foot of a page sent its number to the next page (iPad, 2026-10-28).
     Tall enough for the number keeps the two together. */
  .method ol li { min-height: calc(var(--fs-step) * 1.1); }
  /* An iPad ignores break-after: avoid, and left "Stove prep is first" alone at the foot of a
     page (2026-10-28). An invisible block below a heading reserves room for the first lines
     after it; where they won't fit, the heading, which is never split, moves to the next page. */
  .method h3 { break-inside: avoid; }
  .method h3::after { content: ""; display: block; height: 3.5em; margin-bottom: -3.5em; }
}
@media (prefers-reduced-motion: no-preference) { button { transition: background .15s, border-color .15s; } }

/* ---------- shell: the menu (☰) ---------- */
.menu-btn { font-size: 20px; padding: 4px 10px; }
.bar .home { margin-right: auto; }

/* ---------- shell: hideable left menu ---------- */
.shell { display: flex; align-items: flex-start; }
.main { flex: 1; min-width: 0; }
.drawer {
  position: sticky; top: 64px; align-self: flex-start;
  width: 270px; flex: none; max-height: calc(100vh - 64px); overflow-y: auto;
  padding: 16px; border-right: 1px solid var(--rule); background: var(--bar);
  display: flex; flex-direction: column; gap: 2px; font-size: 16px;
}
.drawer > a {
  display: block; padding: 9px 10px; border-radius: 6px; color: var(--ink); text-decoration: none;
}
.drawer > a:hover { background: var(--chip); }
.drawer > a[aria-current="page"] { background: var(--accent); color: var(--accent-ink); font-weight: 700; }
.drawer .dh {
  font-family: inherit; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-2); border: 0; margin: 16px 0 4px; padding: 0 10px;
}
.filters { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding-bottom: 6px; }
/* Big enough for a finger, and it follows the text size (Jeff, 2026-10-19). */
.filters select {
  font: inherit; font-size: max(16px, var(--fs)); width: 100%; max-width: 100%; min-height: 44px;
  padding: 6px 10px; border-radius: 8px; border: 1.5px solid var(--rule); background: var(--paper); color: var(--ink);
}
.filters details { padding: 0 10px; }
.filters summary { cursor: pointer; font-weight: 700; padding: 4px 0 8px; }
.drawer { overflow-x: hidden; }
.filters .sel { padding: 0 10px; flex-wrap: wrap; }
.filters .favbtn { justify-self: start; margin-left: 10px; }
.filters label.favonly { align-self: flex-start; justify-self: start; margin-left: 10px; }
.filters label.favonly:has(input:checked) { background: var(--heart); border-color: var(--heart); color: #fff; }
.filters .chips, .tagset .chips { display: flex; flex-wrap: wrap; gap: 6px; }
.filters button.tag { font-weight: 400; font-size: 14px; padding: 4px 11px; border-radius: 999px; min-height: 34px; }
/* Each tag is a box to tick, drawn as a chip: the box itself is hidden but still the control.
   The same in the menu and in the editor's Tags part. */
.filters label.tag, .tagset label.tag {
  position: relative; display: inline-flex; align-items: center; font-size: 14px; padding: 4px 11px;
  border-radius: 999px; min-height: 34px; border: 1.5px solid var(--rule); background: var(--paper); cursor: pointer;
}
.filters label.tag input, .tagset label.tag input { position: absolute; opacity: 0; top: 0; right: 0; bottom: 0; left: 0; margin: 0; cursor: pointer; }
.filters label.tag:has(input:checked), .tagset label.tag:has(input:checked) { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.filters label.tag:has(input:focus-visible), .tagset label.tag:has(input:focus-visible) { outline: 3px solid var(--accent); outline-offset: 2px; }
.filter-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 0 10px; }

/* On a phone the menu covers the page instead of sitting beside it. */
@media (max-width: 900px) {
  /* align-self: stretch, or newer browsers apply the wide screen's flex-start to
     the fixed menu too and it stops where its contents end. */
  .drawer { position: fixed; top: 0; left: 0; bottom: 0; z-index: 20; max-height: none; width: min(300px, 85vw); align-self: stretch;
            box-shadow: 0 0 0 100vmax rgba(0,0,0,.35); padding-top: calc(16px + env(safe-area-inset-top, 0px)); }
}

/* ---------- recipe additions ---------- */
.made { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; margin-top: 10px; color: var(--ink-2); font-size: .9em; }
.cooks, .authors { margin-top: 32px; max-width: 68ch; }
.hint { color: var(--ink-2); font-size: .85em; margin: -4px 0 12px; }
.comment { border-left: 4px solid var(--accent); padding: 6px 12px; margin: 0 0 12px; }
.comment .who { color: var(--ink-2); font-size: .8em; }
.form input, .form select, .form textarea {
  font: inherit; width: 100%; padding: 10px 12px; border-radius: 8px;
  border: 1.5px solid var(--rule); background: var(--paper); color: var(--ink);
}
.notice { background: var(--warn-bg); border-left: 5px solid var(--warn-edge); padding: 10px 14px; margin: 12px 0; max-width: 68ch; }
/* A confirmation ("Saved.", "pack was merged into package.") is green, apart from a warning (Jeff, 2026-11-08). */
.notice.ok { background: var(--ok-bg); border-left-color: var(--accent); }
/* Undo beside "Saved." (features/undoing_a_save.feature). */
.notice form.undo { display: inline-block; margin-left: 12px; vertical-align: middle; }
/* An unfinished recipe's note, with "Don't keep this recipe" under it (features/adding_a_recipe.feature). */
[data-part="unfinished-box"] p { margin: 0 0 10px; }
[data-part="unfinished-box"] form { margin: 0; }

/* ---------- forms (sign-in) ---------- */
.form { display: grid; gap: 18px; max-width: 760px; }
.form.narrow { max-width: 420px; }
/* The eye inside the end of a password box (features/showing_the_password.feature). */
.with-eye { position: relative; display: block; }
.form .with-eye input { padding-right: 52px; }
.with-eye .eye { position: absolute; top: 0; right: 0; bottom: 0; width: 48px; display: flex; align-items: center;
  justify-content: center; background: none; border: 0; padding: 0; color: var(--ink-2); cursor: pointer; }
.form label > span:first-child { display: block; font-weight: 700; margin-bottom: 4px; }
.form .primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); justify-self: start; }

/* ---------- the admins' list of lines with no food ---------- */
.nofood { padding-left: 0; list-style: none; display: grid; gap: 14px; }
.nofood > li > a { font-weight: 700; }
.nofood ul { margin: 4px 0 0; padding-left: 1.2em; }

/* ---------- the editor ---------- */
/* Which recipe is being edited, under the heading. */
.editing { font-family: Fraunces, serif; font-size: 1.3em; font-weight: 700; margin: -6px 0 18px; }
.form textarea { width: 100%; }
.form .req { color: var(--warn-edge); font-weight: 400; font-size: .85em; }
.form .row { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
.form fieldset { border: 1.5px solid var(--rule); border-radius: 8px; padding: 12px 14px; display: grid; gap: 10px; min-width: 0; }
.form legend { font-weight: 700; padding: 0 6px; }
.form .hint { margin: 0; }
.form .rows { display: grid; gap: 10px; }
.form .err { color: var(--warn-edge); font-weight: 700; font-size: .9em; margin: 4px 0 0; }
.form .actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.form .del { min-width: 44px; min-height: 44px; padding: 0; font-weight: 400; }
.form .add { justify-self: start; font-size: var(--ctl); min-height: 44px; }
.form .cancel { align-self: center; }
/* An ingredient row: the line, then Parts, + and ✕; the four boxes and a note under it span
   the row. A heading has no Parts, so its box takes that column too. */
.form .ingrow { display: grid; grid-template-columns: minmax(0, 1fr) auto 44px 44px; gap: 6px 8px; align-items: start; }
.form .ingrow > input:not([type="hidden"]), .form .ingrow > .fixed { grid-column: 1; grid-row: 1; min-width: 0; width: 100%; box-sizing: border-box; }
.form .ingrow.heading > input:not([type="hidden"]) { grid-column: 1 / 3; }
/* Another recipe: Amount, Unit and Recipe in one row where there is room. */
.form .ingrow > .linkrow { grid-column: 1 / 3; grid-row: 1; display: grid; grid-template-columns: 5em 8em minmax(0, 1fr); gap: 6px; }
.form .linkrow input { width: 100%; box-sizing: border-box; min-width: 0; }
.form .linkrow label > span:first-child, .form .linkrow .linked > span { display: block; font-size: .85em; margin-bottom: 2px; }
.form .linkrow .linked a { display: block; padding: 10px 0; overflow-wrap: anywhere; }
.form .linkrow > .sug { grid-column: 1 / -1; }
.form .ingrow > .parts { grid-column: 2; grid-row: 1; }
.form .ingrow > .add-here { grid-column: 3; grid-row: 1; width: 44px; }
.form .ingrow > .del { grid-column: 4; grid-row: 1; }
/* Moving lines (features/moving_lines.feature): the grip sits in room left at the row's start,
   outside its grid, and shows only with a script. On a touch screen a finger on it drags the
   row rather than scrolling the page. */
.form .ingrow > .grip { display: none; }
.form.can-move .ingrow { position: relative; padding-left: 48px; }
.form.can-move .ingrow > .grip {
  display: block; position: absolute; left: 0; top: 0; width: 44px; height: 44px; padding: 0;
  font-size: 20px; font-weight: 400; cursor: grab; background: none; border: 0; color: var(--ink-2);
  touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.form.can-move .ingrow > .grip:active { cursor: grabbing; }
/* The grip with the keyboard: its arrow keys move the row, so show which. */
.form.can-move .ingrow > .grip:focus { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 8px; color: var(--accent); }
.form .ingrow.dragging { background: var(--chip); box-shadow: 0 2px 10px rgba(0, 0, 0, .2); border-radius: 8px; }
/* A step (features/editing_the_steps.feature): an ingredient row's layout, its box a textarea
   taking Parts' column too, and its number in room left at the start, after the grip's. */
.form .ingrow.step { position: relative; padding-left: 2em; }
.form.can-move .ingrow.step { padding-left: calc(48px + 2em); }
.form .ingrow.step > .num { position: absolute; left: 0; top: 10px; width: 1.6em; text-align: right;
  font-family: "Fraunces", Georgia, serif; font-weight: 700; color: var(--accent); }
.form.can-move .ingrow.step > .num { left: 48px; }
.form .ingrow.step > textarea { grid-column: 1 / 3; grid-row: 1; min-width: 0; box-sizing: border-box; resize: vertical; overflow: hidden; }
.form .ingrow > .fixrow { grid-column: 1 / -1; border-left: 4px solid var(--accent); padding: 4px 0 4px 10px; }
.form .ingrow > input[readonly] { background: var(--chip); }
.form .ingrow.heading > input { font-weight: 700; }
.form .ingrow > .fixed { align-self: center; padding: 8px 0; }
.form .ingrow > .err, .form .ingrow > [data-part="preview"] { grid-column: 1 / -1; }
/* Suggestions as you type (suggest.js): the app's own list, under the box, since iPad and iPhone
   show a <datalist> only as three guesses above the keyboard. It scrolls inside itself. */
.form .ingrow > .sug, .form .fixrow > .sug { grid-column: 1 / -1; }
.form .sug { list-style: none; margin: 4px 0 0; padding: 4px 0; max-height: 15em; overflow-y: auto;
  border: 1.5px solid var(--rule); border-radius: 8px; background: var(--paper); font-weight: 400;
  -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.form .sug li { padding: 10px 12px; min-height: 44px; box-sizing: border-box; cursor: pointer; border-bottom: 1px solid var(--chip); }
.form .sug li:last-child { border-bottom: 0; }
.form .sug li .kind { color: var(--ink-2); font-size: .9em; }
.form .ingrow > [data-part="not-kept"] { grid-column: 1 / -1; }
.form .sug li[aria-selected="true"], .form .sug li:hover { background: var(--chip); }
.form [data-part="preview"] > p { margin: 4px 0 0; }
.form [data-part="preview"] > .hint { color: var(--ink-2); font-size: .9em; }
.form .ingrow > .parts[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
/* Reached from the link at Save: room above for the box the note is about, and the bar. */
.form [id^="problem-"] { scroll-margin-top: 140px; }
.form .add-here > summary { list-style: none; min-width: 44px; min-height: 44px; display: grid; place-items: center;
  border: 1.5px solid var(--rule); border-radius: 8px; cursor: pointer; font-size: 20px; }
.form .add-here > summary::-webkit-details-marker { display: none; }
.form .ingrow > .add-here[open] { grid-column: 1 / -1; grid-row: 2; width: auto; display: flex; gap: 8px; flex-wrap: wrap; }
.form .add-here[open] > summary { display: none; }
/* Return in a box saves: the form's first button, out of sight. */
.form .enter-saves { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form .chips { display: flex; flex-wrap: wrap; gap: 6px; }
.form button.tag { font-weight: 400; font-size: var(--ctl); padding: 4px 12px; border-radius: 999px; min-height: 40px; }
.form .tagset summary { cursor: pointer; font-weight: 700; padding: 4px 0 10px; }

/* Parts and the split under a line, ported from today's app for slice 3b; the row itself is above. */
.form .ingrow .parts { min-height: 44px; padding: 0 10px; font-size: var(--ctl); font-weight: 400; }
.form .fixrow input { width: 100%; box-sizing: border-box; min-width: 0; }
.form .ingrow .split { grid-column: 2 / -1; font-size: .9em; }
.form .ingrow .split:empty { display: none; }
.form .split.open { border-left: 4px solid var(--accent); padding: 6px 0 6px 10px; }
.form .split.open.doubtful { border-left-color: var(--warn-edge); }
.form .split .doubt { margin: 0 0 8px; font-weight: 700; color: var(--warn-edge); }
.form .split .newword { margin: 4px 0 0; color: var(--ink-2); font-style: italic; }
.form .fixrow { display: grid; grid-template-columns: 6em 8em 1fr 1fr; gap: 6px; }
.form .fixrow label > span:first-child { font-size: .85em; margin-bottom: 2px; }
.form button.link {
  border: 0; background: none; padding: 0 4px; min-height: 0; min-width: 0; font-weight: 400; font-size: inherit;
  color: var(--accent); text-decoration: underline; text-underline-offset: 2px;
}
.form .headrow, .form .refrow { display: grid; grid-template-columns: auto 1fr auto auto; gap: 6px; align-items: center; }
.form .headrow input { font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--accent); }
.form .refrow { grid-template-columns: auto 4.5em 9em 1fr auto auto; }
.form .refrow > span { padding: 10px 12px; border: 1.5px dashed var(--rule); border-radius: 8px; }
.form .refpick { display: grid; grid-template-columns: auto 1fr auto; gap: 6px; align-items: start; }
.form .refpick > span { padding-top: 10px; }
.form .steprow, .form .noterow { display: grid; grid-template-columns: auto 2em 1fr auto auto; gap: 6px; align-items: start; }
.form .noterow { grid-template-columns: 1fr auto; }
/* Moving lines: the grip to drag one, and + for a line just below it. */
.form .grip {
  min-width: 32px; min-height: 44px; padding: 0; border: 0; background: none; color: var(--ink-2);
  font-size: 20px; font-weight: 400; cursor: grab; touch-action: none;
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.form .grip:active { cursor: grabbing; }
.form .plus { min-width: 44px; min-height: 44px; padding: 0; font-weight: 400; font-size: 20px; }
.form .dragging { opacity: .75; outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 8px; background: var(--paper); }
.form .steprow .n { font-family: "Fraunces", Georgia, serif; font-weight: 700; color: var(--accent); padding-top: 10px; }
/* Suggestions as you type (js/suggest.js): the app's own list, since iPad and iPhone
 * show a <datalist> only as three guesses above the keyboard. */
.sugwrap { position: relative; min-width: 0; }
.sug {
  list-style: none; margin: 4px 0 0; padding: 4px 0; max-height: 15em; overflow-y: auto;
  border: 1.5px solid var(--rule); border-radius: 8px; background: var(--paper);
  font-weight: 400; -webkit-overflow-scrolling: touch;
}
.sug[hidden] { display: none; }
.sug li { padding: 10px 12px; min-height: 44px; cursor: pointer; border-bottom: 1px solid var(--chip); }
.sug li:last-child { border-bottom: 0; }
.sug li[aria-selected="true"], .sug li:hover { background: var(--chip); }
@media (max-width: 640px) {
  .form .fixrow { grid-template-columns: 1fr 1fr; }
  /* On a phone the line takes the width, as in today's app, and its + and ✕ go under it. */
  .form .ingrow > input:not([type="hidden"]), .form .ingrow > .fixed { grid-column: 1 / -1; }
  .form .ingrow.heading > input:not([type="hidden"]) { grid-column: 1 / -1; }
  .form .ingrow.step > textarea { grid-column: 1 / -1; }
  /* On a phone the recipe goes under its amount and unit. */
  .form .ingrow > .linkrow { grid-column: 1 / -1; grid-template-columns: 5em minmax(0, 1fr); }
  .form .linkrow > .which, .form .linkrow > .linked { grid-column: 1 / -1; }
  .form .ingrow > .parts, .form .ingrow > .add-here, .form .ingrow > .del { grid-row: 2; }
  .form .ingrow > .add-here[open] { grid-row: 3; }
  .form .refpick { grid-template-columns: 1fr auto; }
  .form .refpick > span { grid-column: 1 / -1; }
  /* On a phone the recipe's name goes under its amount and unit. */
  .form .refrow { grid-template-columns: auto 4.5em 1fr auto auto; }
  .form .refrow > span { grid-column: 2 / -1; grid-row: 2; }
  .form .refrow > .grip { grid-row: span 2; align-self: start; }
}

/* ---------- photo ---------- */
.photobox .photo { max-height: none; aspect-ratio: 3 / 2; margin: 0; }
.photobox .photo[hidden] { display: none; }
.photobox .empty { margin: 0; }
/* Add a photo, or Replace photo: with no script, its words over the browser's own file box;
   once photo.js runs, a button like the others, the file box out of sight but still the one
   a tap opens. */
.form .choose { display: flex; flex-direction: column; gap: 4px; font-weight: 700; }
.form.photo-script .choose { position: relative; display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--ctl); padding: 6px 12px; border-radius: 6px; border: 1.5px solid var(--rule); background: var(--paper);
  min-height: 40px; cursor: pointer; }
.form.photo-script .choose input { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.form.photo-script .choose:focus-within { outline: 3px solid var(--accent); outline-offset: 2px; }
.form .photoset [hidden] { display: none; }
/* The framing screen covers everything, and the page behind it must not scroll or zoom. */
body.framing { overflow: hidden; }
.framer {
  position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 50; background: #111; color: #eee;
  display: grid; grid-template-rows: auto 1fr auto auto; gap: 10px;
  padding: calc(12px + env(safe-area-inset-top, 0px)) 16px calc(12px + env(safe-area-inset-bottom, 0px));
}
.framehelp, .frameinfo { margin: 0; font-size: 15px; text-align: center; }
.frameinfo { font-variant-numeric: tabular-nums; color: #bbb; }
.frameinfo.soft { color: #ffcf6b; }
.framer .stage {
  position: relative; overflow: hidden; display: grid; place-items: center; min-height: 0;
  touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
}
.framer .stage img { display: block; max-width: none; }
.framer .frame {
  position: absolute; outline: 2px solid #fff; cursor: move;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .6);
}
/* Big touch targets around small visible corners. */
.framer .corner { position: absolute; width: 44px; height: 44px; }
.framer .corner::after { content: ""; position: absolute; width: 16px; height: 16px; border: 4px solid #fff; }
.framer .corner.nw { left: -22px; top: -22px; cursor: nwse-resize; }
.framer .corner.ne { right: -22px; top: -22px; cursor: nesw-resize; }
.framer .corner.sw { left: -22px; bottom: -22px; cursor: nesw-resize; }
.framer .corner.se { right: -22px; bottom: -22px; cursor: nwse-resize; }
.framer .corner.nw::after { left: 18px; top: 18px; border-right: 0; border-bottom: 0; }
.framer .corner.ne::after { right: 18px; top: 18px; border-left: 0; border-bottom: 0; }
.framer .corner.sw::after { left: 18px; bottom: 18px; border-right: 0; border-top: 0; }
.framer .corner.se::after { right: 18px; bottom: 18px; border-left: 0; border-top: 0; }
.framebar { display: flex; justify-content: center; gap: 12px; }
.framebar button { min-width: 9em; }
.framer .primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* ---------- simple pages ---------- */
.plain h1 { margin-top: 0; }
.empty { color: var(--ink-2); }
.list .empty { padding: 12px 4px; }

/* Signed out: no menu to open. */
body.signedout .menu-btn, body.signedout .search-btn, body.signedout .drawer { display: none !important; }

@media print {
  .hint { display: none !important; }
}

/* Leaving with unsaved changes: the app's own question, at the top of the screen. */
.leave {
  position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 60; background: rgba(0, 0, 0, .45);
  display: flex; justify-content: center; align-items: flex-start;
  padding: calc(16px + env(safe-area-inset-top, 0px)) 16px 16px;
}
.leavebox {
  background: var(--paper); color: var(--ink); border-radius: 12px; padding: 18px 20px;
  max-width: 460px; width: 100%; box-shadow: 0 8px 30px rgba(0, 0, 0, .3);
}
.leavebox p { margin: 0 0 14px; font-weight: 700; }
.leavebox .actions { display: flex; gap: 10px; flex-wrap: wrap; }
.leavebox .primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* ---------- shopping list (stage 3) ---------- */
.shopbtn {
  font-size: var(--ctl); font-weight: 700; padding: 8px 14px; border-radius: 6px; min-height: 40px;
  border: 1.5px solid var(--accent); color: var(--accent); background: var(--paper);
}
a.shopbtn { display: inline-flex; align-items: center; text-decoration: none; box-sizing: border-box; }
/* The panel sits under Ingredients, so it undoes the ingredient rows' grid, columns and
   crossing-out, which are for ticking off while cooking. */
.ing .shoppanel ul { list-style: none; padding: 0; margin: 0 0 12px; column-count: 1; display: block; }
.ing .shoppanel li { display: block; padding: 0; border: 0; }
.ing .shoppanel label { display: flex; gap: 10px; align-items: center; padding: 6px 0; min-height: 40px; }
.ing .shoppanel input[type="checkbox"] { width: 22px; height: 22px; margin: 0; flex: none; accent-color: var(--accent); }
.shoppanel .btn { display: inline-flex; align-items: center; min-height: 40px; padding: 6px 12px; color: var(--ink); }
.added { border-left: 4px solid var(--accent); padding: 8px 12px; background: var(--chip); }
/* The recipe page's "More" menu. It opens leftwards from the button's right edge,
   so on a phone it stays on the screen; recipe.js turns it rightwards when the
   button starts a line. */
.more { position: relative; }
.morepanel { position: absolute; top: calc(100% + 6px); right: 0; z-index: 5; min-width: 200px;
  display: grid; background: var(--paper); border: 1.5px solid var(--rule); border-radius: 8px;
  box-shadow: 0 6px 20px rgb(0 0 0 / .15); padding: 4px; }
.morepanel.fromleft { right: auto; left: 0; }
.morepanel[hidden] { display: none; }
.morepanel > a, .morepanel > button, .morepanel [data-for] > a, .morepanel [data-for] > form > button { display: flex; align-items: center; min-height: 44px; padding: 0 12px;
  border: 0; border-radius: 6px; background: none; color: var(--ink); font-size: var(--ctl); font-weight: 700;
  text-decoration: none; text-align: left; }
.morepanel > a:hover, .morepanel > button:hover, .morepanel [data-for] > a:hover,
.morepanel [data-for] > form > button:hover { background: var(--chip); }
/* Duplicate is a form, so it works with no script (features/duplicate.feature). */
.morepanel [data-for] > form { display: grid; margin: 0; }
.morepanel hr { border: 0; border-top: 1px solid var(--rule); margin: 4px 8px; }
/* The recipe page's "Add to shopping list" panel, under the Ingredients heading. */
.shoppanel { border: 1.5px solid var(--accent); border-radius: 8px; padding: 12px 14px; margin: 0 0 16px; }
.shoppanel[hidden] { display: none; }
.shoppanel h3 { margin: 0 0 6px; }
.shoppanel .hint { margin: 0 0 8px; }
[data-layout] .shoppanel ul { column-count: 1; margin-bottom: 12px; }
.shoppanel .actions, .shop .actions { display: flex; gap: 10px; flex-wrap: wrap; }
.shoppanel .primary, .shop .primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.shoppanel .err { color: var(--warn-edge); font-weight: 700; }
.shoppanel .notice { margin: 0; }

/* The list itself: big rows to tap in a shop, grouped by aisle. */
.shop .additem { display: grid; grid-template-columns: 1fr auto; gap: 8px; margin: 0 0 20px; }
.shop .additem input {
  font: inherit; width: 100%; padding: 10px 12px; border-radius: 8px; min-height: 44px;
  border: 1.5px solid var(--rule); background: var(--paper); color: var(--ink);
}
.shop .aisle { margin: 0 0 20px; }
.shop .aisle h2 {
  font-size: .8em; text-transform: uppercase; letter-spacing: .06em; color: var(--accent);
  margin: 0 0 4px; padding-bottom: 4px; border-bottom: 1.5px solid var(--rule);
}
.shop ul { list-style: none; margin: 0; padding: 0; }
.shop .item { display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: start; border-bottom: 1px solid var(--chip); }
.shop .item input {
  width: 1.3em; height: 1.3em; margin: calc((1lh - 1.3em) / 2 + 12px) 0 0; accent-color: var(--accent);
}
.shop .item .txt { padding: 12px 0; cursor: pointer; min-height: 44px; }
.shop .item .main { display: block; }
.shop .item .n, .shop .item .from { display: block; color: var(--ink-2); font-size: .85em; line-height: 1.3; }
.shop .item .from { font-style: italic; }
.shop .item.done .main { text-decoration: line-through; color: var(--ink-2); }
.shop .item .del { min-width: 44px; min-height: 44px; padding: 0; font-weight: 400; margin-top: 4px; }
.shop .cart h2 { color: var(--ink-2); }
.shop .cart .actions { margin-top: 12px; }
.shop .onlist li { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 6px 0; }
.shop .onlist .times { color: var(--ink-2); }
.shop .onlist button { font-size: var(--ctl); font-weight: 400; min-height: 40px; }
.shop .empty { color: var(--ink-2); }
/* The webapp's list: each item is a button in a form, so a tap ticks it with no script too. */
.shop .item { grid-template-columns: 1fr auto; gap: 8px; align-items: center; }
.shop .item form { margin: 0; }
.shop .itembtn {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; background: none; border: 0;
  border-radius: 0; padding: 12px 0; min-height: 44px; font-weight: 400; font-size: inherit; color: inherit;
}
.shop .itembtn[aria-pressed="true"] { background: none; color: inherit; }
.shop .itembtn .box {
  flex: none; width: 24px; height: 24px; border: 2px solid var(--rule); border-radius: 6px; background: var(--paper);
}
.shop .item.done .box { background: var(--accent); border-color: var(--accent); }
.shop .item.done .box::after { content: "✓"; display: block; color: var(--accent-ink); text-align: center; line-height: 20px; font-weight: 700; }
.shop .itembtn .words { display: block; }
.shop .item.done .txt { text-decoration: line-through; color: var(--ink-2); }
.shop .onlist form { display: inline; margin: 0; }
.shop .actions .btn { display: inline-flex; align-items: center; min-height: 40px; padding: 6px 12px; border: 1.5px solid var(--rule); border-radius: 6px; color: var(--ink); text-decoration: none; font-weight: 700; font-size: var(--ctl); }
.shop .status:empty { display: none; }
.shop .status { color: var(--warn-edge); font-weight: 700; }

/* ---- The web app (webapp/), on top of today's styles above ----
 * A ticked ingredient is struck through by the stylesheet alone, no script. */
.ing li:has(input:checked) .txt { text-decoration: line-through; color: var(--ink-2); }
/* The More menu is a <details>, so it opens without a script. */
.more summary { list-style: none; cursor: pointer; }
.more summary::-webkit-details-marker { display: none; }
.morepanel [data-for] { display: grid; }
/* The stepper and the sign-out are forms; they sit in line like the buttons they were. */
.scaler, .bar form { margin: 0; }

.bar .search-btn { color: var(--ink); text-decoration: none; }

/* The menu (☰), drawn by the server: beside the page on a wide screen, open or closed as the
   device last left it (data-menu); over the page on a phone, opened by a box its label ticks. */
.menu-toggle { position: absolute; opacity: 0; pointer-events: none; }
.menu-btn { display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  min-height: 40px; min-width: 40px; border: 1.5px solid var(--rule); border-radius: 6px; background: var(--paper); }
.drawer > button { display: block; padding: 9px 10px; border: 0; background: none; text-align: left;
  font-size: inherit; font-weight: 400; min-height: 0; color: var(--ink-2); cursor: default; }
.scrim { display: none; }
@media (min-width: 901px) {
  .menu-phone { display: none !important; }
  :root[data-menu="closed"] .drawer { display: none; }
}
@media (max-width: 900px) {
  .menu-wide { display: none !important; }
  .drawer { display: none; }
  .menu-toggle:checked ~ .shell .drawer { display: flex; }
  .menu-toggle:checked ~ .shell .scrim { display: block; position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 19; }
}

/* Keep screen on, where the browser can't: grayed out like a disabled button, and a tap says why. */
.wake summary.cant {
  display: inline-flex; align-items: center; min-height: 40px; padding: 6px 12px; border-radius: 6px;
  border: 1.5px solid var(--rule); font-size: var(--ctl); font-weight: 700; opacity: .45; cursor: help;
}
.wake .morepanel { min-width: 260px; max-width: calc(100vw - 32px); }
/* With no script to turn it (menus.js), it opens rightwards on a phone, where it starts a line. */
@media (max-width: 640px) { .wake .morepanel { right: auto; left: 0; } }
.wake .morepanel p { margin: 8px 10px; font-size: 15px; }

/* The editor's Tags part (features/editing_the_tags.feature). */
.tagset { margin: 0 0 16px; }
.tagset .chips { margin: 8px 0; }
.tagset label.tag { font-weight: 400; }
.tagset label.tag input { width: auto; padding: 0; border: 0; }

/* The editor's servings and times (features/editing_servings_and_times.feature): two to a line
   on a phone (Jeff, 2026-11-01), three on a wider screen, in the recipe page's order. */
.timeset { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; }
@media (min-width: 640px) { .timeset { grid-template-columns: repeat(3, 1fr); } }
.timeset label { min-width: 0; }
.timeset .hint { display: block; margin-top: 4px; }
/* The note for neither a Serves nor a Yield with a measure, under both boxes (Jeff, 2026-11-01). */
.timeset .err.across { grid-column: 1 / -1; margin-top: 0; }
/* A box with a problem is outlined, so the eye finds it, not only the note under it. */
.form input[aria-invalid="true"] { border-color: var(--warn-edge); box-shadow: 0 0 0 1px var(--warn-edge); }

/* The editor's nutrition (features/editing_the_nutrition.feature): two to a line on a phone,
   three on a wider screen, as the servings and times. */
.nutritionset { margin: 0 0 16px; }
.form .nutritionset summary { cursor: pointer; font-weight: 700; padding: 4px 0 10px; }
.nutritionset .figures { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; margin-top: 10px; }
@media (min-width: 640px) { .nutritionset .figures { grid-template-columns: repeat(3, 1fr); } }
.nutritionset label { min-width: 0; }
.nutritionset .hint:empty { display: none; }

/* The editor's author's notes (features/editing_notes_and_source.feature): a heading and its
   words, side by side with ✕, the words' box growing with what they hold. */
.noterow { display: grid; grid-template-columns: 1fr auto; gap: 6px 8px; align-items: start; margin-bottom: 12px; }
.noterow .notebox { display: grid; gap: 6px; min-width: 0; }
.noterow .err { grid-column: 1 / -1; }
[data-part="source-box"] .hint:empty { display: none; }

/* "Leave without saving your changes?" (features/leaving_with_unsaved_changes.feature), today's
   app's box at the top of the screen. Top, right, bottom and left, not inset, for Safari 12. */
.leave { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 60; background: rgba(0, 0, 0, .45);
  display: flex; justify-content: center; align-items: flex-start;
  padding: calc(16px + env(safe-area-inset-top, 0px)) 16px 16px; }
.leave[hidden] { display: none; }
.leavebox { background: var(--paper); color: var(--ink); border-radius: 12px; padding: 18px 20px;
  max-width: 460px; width: 100%; box-shadow: 0 8px 30px rgba(0, 0, 0, .3); }
.leavebox p { margin: 0 0 14px; font-weight: 700; }
.leavebox .actions { display: flex; gap: 10px; flex-wrap: wrap; }
.leavebox .primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* Categories and tags (features/managing_categories_and_tags.feature). Margins, not flex gap,
   which the old iPads' Safari doesn't know. */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.organizers section { margin: 0 0 28px; }
/* Where the page opens after a change: a little above the row, so the top bar doesn't cover it,
   even on a phone, where the bar is two rows tall.
   A placeholder, not scroll-margin, which the old iPads' Safari doesn't know. */
.organizers .at { display: block; position: relative; top: -140px; height: 0; }
.organizers form.add { margin: 8px 0 14px; }
.organizers form.add input { width: auto; max-width: 16em; margin: 0 8px 0 6px; }
.organizer-list { list-style: none; padding: 0; margin: 0; max-width: 68ch; }
.organizer-list > li { padding: 10px 0; border-top: 1px solid var(--rule); }
.organizer-list .oname { display: block; margin: 0 0 6px; font-weight: 600; }
.organizer-list form { display: inline-block; vertical-align: middle; margin: 0 10px 6px 0; }
.organizer-list form.rename input { width: 12em; max-width: 60vw; margin: 0 6px 0 0; }
.organizer-list .notice form, .organizer-list .notice a { display: inline-block; margin: 6px 12px 0 0; }
.organizer-list .notice p { margin: 0; }

/* Our stores and a store's page (features/basic_grocery_store_layout.feature): the buttons for
   what can be done to a store sit beside its name; each opens a window over the page
   (_store_window.html), part of the page, never the browser's own box. Margins, not flex gap,
   for the old iPads. */
.store-list > li > a[data-part="name"] { display: inline-block; margin: 0 14px 6px 0; font-weight: 600; }
.store-actions { display: inline-block; }
.store-actions form { display: inline-block; margin: 0 10px 6px 0; }
/* On a store's own page, set apart from Save and Cancel, which keep what is typed above them. */
.our-store > .store-actions { display: block; margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--rule); }
.modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 50; background: rgba(0, 0, 0, .45);
  display: flex; align-items: center; justify-content: center; padding: 16px; }
.modal-box { background: var(--paper); color: var(--ink); border: 1.5px solid var(--rule); border-radius: 10px;
  padding: 18px 20px; width: 100%; max-width: 32em; max-height: 100%; overflow: auto;
  box-shadow: 0 8px 30px rgba(0, 0, 0, .35); }
.modal-box h2 { margin: 0 0 12px; }
.modal-box p { margin: 0 0 12px; }
.modal-box label { display: block; margin: 0 0 12px; font-weight: 600; }
.modal-box label input { display: block; width: 100%; margin: 6px 0 0; box-sizing: border-box; }
.modal-actions button, .modal-actions .btn { margin: 0 10px 0 0; }
.modal-box .btn { display: inline-flex; align-items: center; min-height: 40px; padding: 6px 12px;
  border: 1.5px solid var(--rule); border-radius: 6px; color: var(--ink); text-decoration: none; font-weight: 700;
  font-size: var(--ctl, 15px); }

/* ---------- the old iPads: spacing without flex gap ----------
   Safari before 14.1 (iOS 12.5.8 on Janette's iPad) ignores "gap" in a flex row, so the words
   in it touch: under a recipe's name, "Added by …" at its foot, and elsewhere (Jeff, 2026-11-08).
   base.html marks the page no-flexgap in such a browser, and these rules space each row's
   parts with margins instead, the same sizes as its gap. Every newer browser uses the gap and
   never reads them. Made from each flex rule with a gap above, and a test
   (tests/presentation/test_old_safari.py) fails if one is added without its line here. */
.no-flexgap .bar > * { margin-right: 22px; margin-bottom: 10px; }
.no-flexgap .grp > * { margin-right: 6px; }
.no-flexgap .recipes a > * { margin-right: 12px; margin-bottom: 4px; }
.no-flexgap .crumb > * { margin-right: 14px; margin-bottom: 8px; }
.no-flexgap .chips > * { margin-right: 6px; margin-bottom: 6px; }
.no-flexgap .metarow > * { margin-right: 28px; margin-bottom: 12px; }
.no-flexgap .meta > * { margin-right: 28px; margin-bottom: 6px; }
.no-flexgap .scaler > * { margin-right: 8px; }
.no-flexgap .prov > * { margin-right: 24px; margin-bottom: 6px; }
.no-flexgap .drawer > * { margin-bottom: 2px; }
.no-flexgap .filters .chips > *, .no-flexgap .tagset .chips > * { margin-right: 6px; margin-bottom: 6px; }
.no-flexgap .filter-actions > * { margin-right: 14px; margin-bottom: 8px; }
.no-flexgap .made > * { margin-right: 16px; margin-bottom: 8px; }
.no-flexgap .form .actions > * { margin-right: 10px; margin-bottom: 10px; }
.no-flexgap .form .ingrow > .add-here[open] > * { margin-right: 8px; margin-bottom: 8px; }
.no-flexgap .form .chips > * { margin-right: 6px; margin-bottom: 6px; }
.no-flexgap .form .choose > * { margin-bottom: 4px; }
.no-flexgap .framebar > * { margin-right: 12px; }
.no-flexgap .leavebox .actions > * { margin-right: 10px; margin-bottom: 10px; }
.no-flexgap .ing .shoppanel label > * { margin-right: 10px; }
.no-flexgap .shoppanel .actions > *, .no-flexgap .shop .actions > * { margin-right: 10px; margin-bottom: 10px; }
.no-flexgap .shop .onlist li > * { margin-right: 8px; margin-bottom: 8px; }
.no-flexgap .shop .itembtn > * { margin-right: 12px; }
/* The last part of a row needs no space after it. Not a ticked box and its words, where the
   box is the last tag but the words come after it. */
.no-flexgap .bar > *:last-child,
.no-flexgap .grp > *:last-child,
.no-flexgap .recipes a > *:last-child,
.no-flexgap .crumb > *:last-child,
.no-flexgap .chips > *:last-child,
.no-flexgap .metarow > *:last-child,
.no-flexgap .meta > *:last-child,
.no-flexgap .scaler > *:last-child,
.no-flexgap .prov > *:last-child,
.no-flexgap .filters .chips > *:last-child,
.no-flexgap .tagset .chips > *:last-child,
.no-flexgap .filter-actions > *:last-child,
.no-flexgap .made > *:last-child,
.no-flexgap .form .actions > *:last-child,
.no-flexgap .form .ingrow > .add-here[open] > *:last-child,
.no-flexgap .form .chips > *:last-child,
.no-flexgap .framebar > *:last-child,
.no-flexgap .leavebox .actions > *:last-child,
.no-flexgap .shoppanel .actions > *:last-child,
.no-flexgap .shop .actions > *:last-child,
.no-flexgap .shop .onlist li > *:last-child,
.no-flexgap .shop .itembtn > *:last-child { margin-right: 0; }
.no-flexgap .bar .home { margin-right: auto; }

/* Our store (features/arranging_our_store.feature): the sections in walking order, numbered. */
.store-sections > li .num { color: var(--ink-2); font-variant-numeric: tabular-nums; }
.store-sections > li .oname { display: inline; margin-right: 10px; }
.store-sections .moves { display: inline-block; white-space: nowrap; }
/* One form saved once (Jeff-Hayes/recipes#342): the grip with a script, ↑ and ↓ without. */
.form.arranging { gap: 14px; }
.basic-layout form[data-part="undo"] { margin: 0 0 16px; }
.form.arranging .store-sections > li { display: flex; flex-wrap: wrap; align-items: center; }
.form.arranging .store-sections > li > * { margin-right: 8px; }
.form.arranging .moves button { min-width: 44px; min-height: 40px; margin-right: 6px; }
.form.arranging .can-move .moves, .form.arranging.can-move .moves { display: none; }
.form.arranging .aisle-box { font-size: .9em; color: var(--ink-2); flex-basis: 100%; }
.form.arranging .aisle-box input { width: 12em; max-width: 60vw; margin-left: 6px; }
.form.arranging .store-name-box input { max-width: 24em; }
.form.arranging .actions { display: flex; align-items: center; flex-wrap: wrap; }
.form.arranging .actions > * { margin-right: 16px; }
.form.arranging .store-sections > li.dragging { background: var(--paper); }

/* The shared foods (features/keeping_the_foods.feature): each food found opens to its boxes, one
   to a row, their words in a column on a wide screen and above the box on a phone. */
.food-list summary { cursor: pointer; }
.food-list summary .oname { display: inline; margin: 0 0 0 4px; }
.food-list summary .facts { display: block; color: var(--ink-2); margin: 4px 0 0; }
.food-list details[open] summary { margin-bottom: 8px; }
.food-list details form { display: block; margin: 0 0 10px; }
.food-list details form .lt { display: inline-block; width: 8.5em; }
.food-list details form.rename input, .food-list details select { width: 14em; max-width: 60vw; margin: 0 6px 0 0; }
.food-list .also { list-style: none; padding: 0; margin: 0 0 10px 8.5em; }
.food-list .also li { margin: 0 0 6px; }
.food-list .also li span { margin-right: 10px; }
.food-list .also form { display: inline-block; margin: 0; }
/* A unit's plural and short forms are saved together, each box on its own row. */
.units form.forms label { display: block; margin: 0 0 6px; }
@media (max-width: 600px) {
  .food-list details form .lt { display: block; width: auto; margin: 0 0 4px; }
  .food-list .also { margin-left: 0; }
}

/* Our household (features/our_household.feature): the new invitation's link, wide enough to read
   and select, and each open invitation with its Cancel beside it. */
.household input.link { display: block; width: 100%; box-sizing: border-box; font: inherit; padding: 8px 10px; margin: 8px 0; }
.household .invitations li form { display: inline-block; margin-left: 12px; }

/* Equipment (features/equipment_and_supplies.feature). On the recipe page, under the
   ingredients, each with its note; no tick box, since nothing is used up. */
.ing .equip { margin-top: 16px; }
.ing .equip li { display: block; padding: 5px 0; }
/* In the editor: the name and its note side by side with ✕; what is said, and equipment like
   it, under them. Safari 12 has grid but not gap on flex, so grid. */
.equiprow { display: grid; grid-template-columns: 1fr 1fr auto; gap: 6px 8px; align-items: start; margin-bottom: 12px; }
.equiprow label { min-width: 0; }
.equiprow input { width: 100%; box-sizing: border-box; }
.equiprow .err, .equiprow .hint, .equiprow .like { grid-column: 1 / -1; margin: 0; }
.equiprow .like button { margin: 4px 6px 0 0; }
@media (max-width: 480px) {
  .equiprow { grid-template-columns: 1fr auto; }
  .equiprow label:nth-of-type(2) { grid-column: 1; }
}
/* Our equipment: each piece's details, and, for a manager, what typed words it is like. */
.organizer-list .details { display: block; margin: -2px 0 6px; color: var(--ink-2); }
.organizer-list .like { display: block; margin: 0 0 6px; padding: 2px 8px; border-left: 4px solid var(--accent);
  background: var(--ok-bg); font-size: .9em; }
/* What cooks typed, waiting for a manager, shaded so it reads as something to do; and Our
   equipment in the menu while anything waits (Jeff, 2026-11-09). */
.waiting { background: var(--warn-bg); border-left: 4px solid var(--warn-edge); padding: 4px 12px 8px; margin: 0 0 20px; }
.waiting .organizer-list > li:first-child { border-top: 0; }
.drawer a.attention { color: var(--warn-edge); font-weight: 700; }
.drawer a.attention::after { content: " ●"; }
