/*
 * The ingredient row's zone geometry — rail / body / amount — as ONE copy,
 * shared by every surface that renders an ingredient line (#4292).
 *
 * Role: owns the grid contract only (tracks, column assignments, row pinning,
 * gap, the amount's quiet treatment). It is deliberately NOT a general recipe
 * stylesheet: everything else about a recipe document stays in base.css.
 *
 * Collaborators:
 *   - layouts/application.html.erb — links this immediately after base.css, so
 *     the relative order of components/*, navshell, mirabel and print is
 *     unchanged and page sheets loaded via content_for(:head) still override.
 *   - layouts/print.html.erb — links this between fonts.css and
 *     print_document.css, so print_document's own overrides still win.
 *   - print_document.css / document_editor.css / print.css — the three page
 *     sheets that build on this contract rather than restating it.
 *   - test/integration_browser/ingredient_grid_geometry.spec.mjs — the gate.
 *     Measures bounding rects across both ingredient_display orders on all
 *     four surfaces; it is the only tier that can see any of this.
 *
 * Non-obvious constraints:
 *   - TWO ROOT FONT SIZES. base.css sets `html { font-size: 18px }` and
 *     print_document.css sets `html { font-size: 12pt }` (16px), so every rem
 *     value here resolves differently on the two surfaces — the `column-gap:
 *     0.5rem` below is 9px on screen and 8px on paper. That divergence is
 *     intended: this sheet shares the TOKEN, never the pixel.
 *   - NO CUSTOM PROPERTIES REACH THE PRINT DOCUMENT. base.css owns every
 *     `:root` block, and the print layout loads only fonts.css, this sheet and
 *     print_document.css — none of which declares one. So every `var()` in
 *     this file carries a LITERAL FALLBACK, and that fallback is what the
 *     print document actually gets. A bare `var(--token)` here would be
 *     invalid-at-computed-value-time on paper and silently fall back to the
 *     inherited value — a value nobody chose.
 *   - `:where()` IS LOAD-BEARING. It keeps these selectors at specificity 0 in
 *     the wrapper so page sheets can override without an arms race, and its
 *     `.recipe` arm is what lets this file reach the print document
 *     (`<article class="recipe print-document">`) and the editor
 *     (`<article class="recipe document-editor">`) with zero selector edits.
 *     Do not "simplify" it to a bare descendant selector.
 */

/* Kept bare (not .ing-row-scoped) so it still matches every ingredient <li> —
   the grid rule below deliberately omits font-size, and this is where it
   lives. 1rem/18px clears the iOS Safari zoom-on-focus floor (16px) and
   matches the editor's own text controls (document_editor.css), so read and
   edit now agree on type size; the type-size cue's replacement is the
   editor row's rail marker (.ing-rail__marker, document_editor.css) — the only
   at-rest editor-only signal on EVERY row, since Phase 3 (#4175) replaced the
   per-row tool cluster with one pinned per-step cluster that is itself hidden
   until a row is engaged (2026-08-11 Phase 1b). Optional rows carry a second
   difference (bare .ing-opt-mark there vs the optional-pill component here),
   but only those. */
:where(.recipe, .embedded-recipe) .ingredients li {
  font-size: 1rem;
}

/* Ingredient row: a three-zone grid — rail / body / amount — shared with the
   editor's .doc-ingredients rows so read and edit render one geometry
   (2026-08-11 spec). Zones can't collide, so a long name never wraps into the
   amount and the amount never strands on its own line. The two
   ingredient_display orders differ ONLY in which column the amount occupies;
   the markup is identical, which is what stops them drifting.
   `line-height`'s fallback is 1.5, NOT --leading-body's 1.4: 1.5 is what
   print_document.css's own `body` rule already gives these rows, so the
   fallback preserves paper's line-height rather than quietly retuning it. */
:where(.recipe, .embedded-recipe) .ingredients li.ing-row {
  display: grid;
  align-items: baseline;
  column-gap: 0.5rem;
  break-inside: avoid;
  padding: 0.3rem 0;
  line-height: var(--leading-body, 1.5);
}

/* Sparse grid auto-placement bites here: DOM order is always rail, [amount],
   body, but name-first assigns amount to column 3 — placing it advances the
   auto-placement cursor PAST column 2, so body (column 2, placed after in the
   DOM) is behind the cursor and wraps onto an implicit second row instead of
   sharing row 1. Pinning every zone to row 1 explicitly removes auto-
   placement from the picture entirely, for both orders. */
:where(.recipe, .embedded-recipe) .ing-row > * {
  grid-row: 1;
}

/* grid-template-columns lives on the two ORDER MODIFIERS, never on the
   `.ingredients li.ing-row` rule above — and that placement is a bug receipt,
   not a style preference (#4291 fix round 2, then #4292). A template declared
   on `.ingredients li.ing-row` (0,2,1 counting the `li`) outranks
   `.ing-row--quantity-first` (0,1,0) on SPECIFICITY, so quantity_first's
   override parses, lints, and never applies — every row renders as if
   name_first, with the amount alone in the 1fr track and the name flung to
   the margin. That shipped once, on the standalone print page, because the
   port folded the template onto the base rule. Keeping the template only on
   the two modifiers holds them at EQUAL specificity, so whichever class the
   row actually carries wins. Equal-specificity ties are invisible to
   stylelint and to review-by-reading — only the browser gate sees this. */
:where(.recipe, .embedded-recipe) .ing-row--name-first {
  grid-template-columns: 20px 1fr auto;
}

:where(.recipe, .embedded-recipe) .ing-row--name-first .ing-body { grid-column: 2; }
:where(.recipe, .embedded-recipe) .ing-row--name-first .ing-amount { grid-column: 3; }

:where(.recipe, .embedded-recipe) .ing-row--quantity-first {
  grid-template-columns: 20px auto 1fr;
}

:where(.recipe, .embedded-recipe) .ing-row--quantity-first .ing-amount { grid-column: 2; }
:where(.recipe, .embedded-recipe) .ing-row--quantity-first .ing-body { grid-column: 3; }

:where(.recipe, .embedded-recipe) .ing-rail { grid-column: 1; }

/* A legacy (alternatives-bearing) row has no .ing-amount sibling at all — the
   markup order is rail/[amount]/body, so .ing-body is the DOM's last child in
   BOTH shapes and can't be targeted by :last-child; :has() is what actually
   discriminates "no amount zone" from "amount zone present". Let body span
   the vacated amount column too, rather than leaving a dead gap. This beats
   the order-scoped .ing-body rules above on SPECIFICITY, not source order —
   :where() zeroes the wrapper, but :not(:has(.ing-amount)) still counts as a
   class-level selector, giving this rule three (.ing-row, :not(:has(...)),
   .ing-body) against their two — so it wins regardless of where in the file
   it's declared. */
:where(.recipe, .embedded-recipe) .ing-row:not(:has(.ing-amount)) .ing-body {
  grid-column: 2 / -1;
}

/* The amount reads as the row's quiet companion. .quantity's own rule (base.css)
   supplies the same colour/weight/nowrap for the inner span on screen; this
   positions and quiets the zone itself, which is the only one of the two the
   print document sees.
   `color`'s fallback is `inherit`, not a literal grey: on paper the amount
   should stay the document's ink (print_document.css's `body { color: black }`),
   which is exactly what it inherited before this sheet existed. The fallback
   makes that outcome chosen rather than accidental. */
:where(.recipe, .embedded-recipe) .ing-amount {
  color: var(--text-light, inherit);
  font-weight: 300;
  white-space: nowrap;
}

/* Prep note is the body's continuation line, not a third zone. Load-bearing on
   the print document specifically: without it the <small> _ingredient_line
   places after the name falls back to inline and renders on the same line
   (verified live — computed `display` read "inline" on that page when only
   base.css carried this rule). */
:where(.recipe, .embedded-recipe) .ing-body small {
  display: block;
}
