/* Static pages — the FAQ, the recipe index and the recipe pages
   (DEVELOPMENT §2.0). Loaded AFTER app.css, which supplies the palette, the
   reset and the body type; this file adds only what a document needs and the
   app does not.

   ⚠ There are no colour literals here, and there must not be. The palette lives
   between the COLOUR-TOOL EXPORT markers in app.css and is regenerated wholesale
   by app/260714_vaffel_colors.html; a hex code written here would keep its old
   value the next time the owner exports a palette, and the page would quietly
   stop matching the app.

   ⚠ Everything a page might want to say with a `style` attribute has to be a
   class here instead. The site CSP (`default-src 'self'`) blocks style
   attributes as well as inline <style> blocks — including ones that only set a
   custom property — and it does so silently: the declaration is simply dropped.

   The type is the APP's — app.css's system sans, at the app's sizes (owner,
   2026-09-01). The mockup proposed a serif reading face for the prose; it was
   built, looked at, and declined: these pages and the app are one product, and a
   different face is the fastest way to make a reader wonder whether they have
   left the site. Headings are larger here than in the app because a document has
   a title where a control surface has a column, not because they are a different
   family. */

/* ── Document frame ────────────────────────────────────────────────────────── */

.site-page {
  --page-measure: 68ch;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.site-main {
  flex: 1;
  width: 100%;
  max-width: 62rem;
  margin: 0 auto;
  padding: var(--sp-8) var(--sp-5);
}

/* The skip link is the first focusable thing in the document and invisible
   until it is focused — a keyboard user should not have to tab through the nav
   on every page to reach the recipe. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: var(--sp-2) var(--sp-4);
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-mid);
  border-radius: 0 0 var(--radius) 0;
  /* Above the sticky header (z-index 10), or the one control that exists to
     bypass the nav would appear behind it. */
  z-index: 20;
}

.skip-link:focus {
  left: 0;
}

/* ── Header ────────────────────────────────────────────────────────────────── */

/* Persistent on scroll (owner, 2026-09-03). A recipe page is long — prose,
   photographs, a table and three scales — and the way back to the generator was
   at the top of it. Sticky rather than fixed: fixed would need a matching top
   padding on `.site-main` to stop the first heading disappearing under it, and
   that padding is exactly the kind of number that stops matching the header the
   first time the brand changes size.

   `--surface` (opaque, from the app's palette) is load-bearing here: content
   scrolls UNDER this row, and a translucent background would show it through. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

/* ── The brand: the app's own, at the app's own metrics ──────────────────────
   Heart, wordmark and tagline, exactly as `App.svelte`'s header sets them — a
   46 px mark, a 26 px title and an 11.6 px tagline, sized so the two lines of
   type span the heart's drawn height. The numbers are copied from the app's
   header because they are METRICS of one drawing, not tokens; the artwork, the
   kerning and the words themselves all come from the app's modules
   (`vite/siteChrome.ts`). Do not resize one of the three without re-solving the
   other two — App.svelte's h1 comment describes how. */
.brand {
  display: inline-flex;
  /* flex-start, not center: the wordmark is positioned against the heart's
     drawn edges by the metrics below, which centring would fight. */
  align-items: flex-start;
  gap: 0;
  color: var(--title);
  text-decoration: none;
}

.brand .brand-mark {
  flex: none;
}

.brand-text {
  display: inline-flex;
  flex-direction: column;
  line-height: 1;
  /* Offsets the mark's stroke padding, so this block's top sits on the heart's
     drawn top rather than on its box. */
  margin-top: 1.21px;
}

/* Saira Stencil One ships exactly ONE weight (usWeightClass 400). Any heavier
   value is the browser faking a bold, which fattens the strokes into the
   stencil's gaps and destroys the face — so 400, and never a bold here. The
   face itself is declared in app.css, which every page links before this file.

   Tracking and pair kerning are per character, in the `.wm-avogadro .kN` rules
   generated onto the end of this stylesheet from the app's own kerning table. */
.brand-title {
  display: inline-block;
  margin-top: -0.096em;
  font-family: 'Saira Stencil One', system-ui, sans-serif;
  font-size: 26px;
  font-weight: 400;
  color: var(--title);
  letter-spacing: 0;
  white-space: nowrap;
}

.brand-subtitle {
  font-size: 11.6px;
  font-weight: 500;
  color: var(--text-secondary);
  letter-spacing: 0.01em;
  margin-top: 0.306em;
}

.site-nav {
  display: flex;
  gap: var(--sp-4);
  margin-right: auto;
  font-size: 0.9rem;
}

.nav-link {
  color: var(--text-secondary);
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
}

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

.nav-link.current {
  color: var(--accent-dark);
  font-weight: 700;
  border-bottom-color: var(--accent);
}

.lang-switch {
  display: flex;
  gap: var(--sp-1);
}

/* The language pair mirrors the app's picker: the current one is the filled
   selected state, the other an outlined unselected one. */
.lang-btn {
  padding: 0.15rem 0.45rem;
  border: 1px solid var(--btn-border);
  border-radius: var(--radius-sm);
  background: var(--btn-bg);
  color: var(--btn-text);
  font-size: 0.72rem;
  font-weight: 600;
  text-decoration: none;
}

.lang-btn.current {
  background: var(--btn-selected-bg);
  border-color: var(--btn-selected-bg);
  color: var(--on-accent);
}

/* ── Prose ─────────────────────────────────────────────────────────────────── */

/* A page whose content is text wraps itself in `.prose`. The main container
   stays wide because the index and the recipe pages need the room; a text page
   inside it would otherwise sit against the left edge of a 1200 px window with
   half the screen empty beside it, which reads as a layout that broke rather
   than one that chose a measure. */
.prose {
  max-width: 48rem;
  margin: 0 auto;
}

.site-main h1 {
  font-size: 1.9rem;
  line-height: 1.2;
  color: var(--title);
  margin-bottom: var(--sp-4);
}

.site-main h2 {
  font-size: 1.3rem;
  color: var(--title);
  margin-top: var(--sp-8);
}

.site-main h3 {
  font-size: 1.05rem;
  margin-top: var(--sp-6);
}

.site-main p,
.site-main li {
  line-height: 1.6;
  max-width: var(--page-measure);
}

.site-main a {
  color: var(--accent-dark);
}

.page-lede {
  font-size: 1.1rem;
  color: var(--text-secondary);
}

/* ── Footer ────────────────────────────────────────────────────────────────── */

.site-footer {
  border-top: 1px solid var(--border);
  padding: var(--sp-5);
  color: var(--text-muted);
  font-size: 0.85rem;
}

/* One line under one rule (owner, 2026-09-03). `flex-wrap` rather than a hard
   single line: on a 360 px phone the three cells do not fit, and a footer that
   overflows its own page is worse than one that takes two lines. */
.footer-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  margin: 0;
  max-width: none;
}

/* The separators are punctuation BETWEEN the cells, not content of any of them,
   so they are drawn rather than written into the markup — which also keeps them
   out of the accessible name of the links they sit beside. */
.footer-line a::before {
  content: '|';
  margin: 0 0.7em;
  color: var(--border-mid);
}

.site-footer a {
  color: var(--text-secondary);
}

/* ── Small screens ─────────────────────────────────────────────────────────── */

@media (max-width: 34rem) {
  .site-header {
    flex-wrap: wrap;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-4);
  }

  /* A sticky row costs viewport, and on a phone the tagline is the cell that
     buys the least: the wordmark beside the heart already says whose site this
     is. The two lines become one, which is ~14 px of recipe back. */
  .brand-subtitle {
    display: none;
  }

  .site-nav {
    order: 3;
    width: 100%;
    margin-right: 0;
    gap: var(--sp-5);
  }

  .lang-switch {
    margin-left: auto;
  }

  .site-main {
    padding: var(--sp-5) var(--sp-4);
  }

  .site-main h1 {
    font-size: 1.6rem;
  }
}

/* ── FAQ ───────────────────────────────────────────────────────────────────────
   Thirty-three questions in one column is a page you scroll rather than read,
   so each answer is a `<details>` and the sections have a jump list above them
   (owner, 2026-09-03). Nothing is hidden from a crawler by it — the answers are
   in the document either way; see `vite/faqPage.ts`. No JavaScript is involved:
   `<details>` is the browser's own disclosure widget. */

.faq-jump {
  margin: var(--sp-8) 0;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

/* Scoped through `.faq-jump` for specificity: `.site-main h2` is a class-plus-
   type selector and would otherwise keep its own `margin-top: var(--sp-8)`,
   opening a band of empty space inside the box. */
.faq-jump .jump-title {
  margin: 0 0 var(--sp-2);
  font-size: var(--heading-size);
  font-weight: var(--heading-weight);
  text-transform: var(--heading-transform);
  letter-spacing: var(--heading-tracking);
  color: var(--accent);
}

.faq-jump ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
}

.faq-jump li {
  max-width: none;
}

/* The header is sticky, so an in-page jump would otherwise land its target
   underneath it. 5rem clears the 46px mark plus the row's padding. */
.faq-section {
  scroll-margin-top: 5rem;
}

.faq-item {
  border-bottom: 1px solid var(--border);
}

/* The default triangle is replaced by a marker of our own so it can be sized
   and coloured with the type. `list-style: none` is what removes it in Chromium
   and Firefox; `::-webkit-details-marker` is what removes it in Safari. */
.faq-item summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-3) 0;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary::before {
  content: '+';
  flex: none;
  width: 1ch;
  color: var(--accent);
  font-weight: 700;
}

.faq-item[open] summary::before {
  content: '\2212'; /* MINUS SIGN — a hyphen next to a '+' reads as a dash. */
}

.faq-item summary:hover h3 {
  color: var(--accent-dark);
}

.faq-item summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The question is the control AND the outline entry, so the heading lives
   inside the summary; it must not reintroduce the block margins `.site-main h3`
   gives a standalone heading. */
.faq-item summary h3 {
  margin: 0;
  font-size: 1rem;
}

.faq-answer {
  padding: 0 0 var(--sp-4) calc(1ch + var(--sp-3));
}

.faq-answer > :first-child {
  margin-top: 0;
}

/* ── Recipe index ──────────────────────────────────────────────────────────── */

.recipe-grid {
  list-style: none;
  margin: var(--sp-8) 0 0;
  padding: 0;
  display: grid;
  gap: var(--sp-5);
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}

.card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: var(--sp-5);
}

/* The photograph sits ABOVE the card's padding, edge to edge — a card is a
   picture with a title under it, and an inset image reads as an illustration
   beside the text instead. Fixed 4:3 with `object-fit: cover`, so a portrait
   and a landscape photograph make the same shape and the grid's rows line up. */
.card-photo {
  display: block;
  margin: calc(var(--sp-5) * -1) calc(var(--sp-5) * -1) var(--sp-4);
  border-radius: var(--radius) var(--radius) 0 0;
  overflow: hidden;
}

.card-photo img {
  display: block;
  width: 100%;
  /* `height: auto` is load-bearing, not boilerplate: the img carries intrinsic
     `width`/`height` attributes (they are what stops the page jumping), and
     those map to the height PROPERTY. With both dimensions specified,
     `aspect-ratio` is ignored — the card rendered at the photograph's full
     900 px instead of the 4:3 crop. */
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.card h2 {
  margin: 0 0 var(--sp-2);
  font-size: 1.1rem;
}

.card h2 a {
  text-decoration: none;
}

.card h2 a:hover {
  text-decoration: underline;
}

.card-summary {
  margin: 0 0 var(--sp-3);
  font-size: 0.92rem;
  color: var(--text-secondary);
}

.card-meta {
  margin: 0;
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* ── Recipe page ───────────────────────────────────────────────────────────── */

.crumb {
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-bottom: var(--sp-3);
}

.crumb a {
  color: var(--text-secondary);
}

.crumb span::before {
  content: ' / ';
}

.cta {
  margin: var(--sp-6) 0;
}

.cta a {
  display: inline-block;
  padding: var(--sp-3) var(--sp-5);
  border-radius: var(--radius);
  background: var(--btn-selected-bg);
  color: var(--on-accent);
  font-weight: 600;
  text-decoration: none;
}

.cta a span {
  display: block;
  font-weight: 400;
  font-size: 0.85rem;
  opacity: 0.9;
}

/* ── The share row (owner, 2026-09-03) ────────────────────────────────────────
   The app's five SHARE targets, on the page they share. These rules MIRROR
   `ShareLink.svelte`'s scoped ones rather than importing them — Svelte compiles
   a component's styles under a per-component hash, so there is nothing here to
   reuse; what IS shared is the artwork (the same `ui/icons/*.svg` files, inlined
   by `vite/shareRow.ts`) and the URL builders. Keep the two in step by eye: the
   treatment is "no button chrome, the glyph's colour is the whole affordance".

   `.share-row-page` sits in the top-right corner of a document at the app's own
   glyph size; `.share-row-inline` sits above the ingredient table at the PRINTER
   glyph's size, because there it is a utility beside another utility rather than
   the page's own header. */

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

/* No breadcrumb to sit opposite (the index), so the row simply goes right. */
.page-top-bare {
  justify-content: flex-end;
}

.share-row {
  display: flex;
  align-items: center;
  flex: none;
}

/* SCOPED THROUGH `.share-row`, and that is not decoration (owner, 2026-09-05).
   Four of the seven targets are anchors, and `.site-main a { color:
   var(--accent-dark) }` is a class-plus-type selector that out-specifies a bare
   `.share-icon-btn` — so e-mail, X, Bluesky and Facebook came out a shade darker
   than the link, share-sheet and QR glyphs beside them, which is exactly the
   "different colours" the owner saw. In the app they are all one green because
   Svelte's scoped rules never meet a document-wide anchor rule. Two classes
   settles it for every state; do not unscope any of these back to one class. */
.share-row .share-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  transition: color 0.12s;
  flex: none;
  /* A `<summary>` is a list item by default, which would draw a marker beside
     the QR glyph in every browser. */
  list-style: none;
}

.share-row .share-icon-btn::-webkit-details-marker {
  display: none;
}

.share-row .share-icon-btn:hover {
  color: var(--accent-dark);
}

.share-row .share-icon-btn:active,
.share-row .share-icon-btn.is-active {
  color: var(--accent-deep);
}

.share-row .share-icon-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.icon-glyph {
  display: block;
}

/* The page row: the app's own metrics — a 1.925rem glyph in a 2.35 × 2.8rem
   box, which clears the WCAG 2.5.8 target size with room to spare. */
.share-row-page .share-icon-btn {
  width: 2.35rem;
  height: 2.8rem;
}

.share-row-page .icon-glyph {
  width: 1.925rem;
  height: 1.925rem;
}

/* The inline row: the printer glyph's metrics (`PrintButton.svelte`), so the six
   controls above the table read as one row of equals. */
.share-row-inline .share-icon-btn {
  width: 1.6rem;
  height: 1.6rem;
}

.share-row-inline .icon-glyph {
  width: 1.15rem;
  height: 1.15rem;
}

/* Share icons then the printer glyph, right-aligned above the table (owner:
   "to the left of the printer icon").

   NO negative bottom margin, unlike the app's `.print-action`: there the glyph
   overlaps a title row whose right-hand cells are empty on screen, and here the
   same row carries the two visible percentage labels — the overlap put the
   printer straight through "Baker's %". A row of its own, and since 2026-09-05
   no longer tight against the table: at --sp-1 the glyphs read as part of the
   table's header rather than as the utilities they are (owner). */
.recipe-tools {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-2);
  max-width: 34rem;
  padding-right: var(--table-cell-pad-h);
  margin: var(--sp-8) 0 var(--sp-4);
}

/* `PrintButton.svelte`'s own wrapper carries that negative margin and its own
   right inset, both tuned for standing alone above the app's table. Inside this
   row they would pull the table up under the icons and double the inset. Three
   classes out-specifies the component's scoped `.print-action.svelte-hash`
   (two), which is what lets a page stylesheet reach it at all. */
.recipe-tools .live-print .print-action {
  margin-bottom: 0;
  padding-right: 0;
}

/* The QR symbol is generated into the page, so the disclosure needs no script.
   Absolutely positioned so opening it does not push the page around — it is a
   thing you hold up to somebody, not a section of the document. */
.share-qr {
  position: relative;
  display: inline-flex;
}

.share-qr-card {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 5;
  padding: var(--sp-3);
  border: 1px solid var(--border-mid);
  border-radius: var(--radius);
  background: var(--bg);
  /* app.css uses the same neutral shadow on its own floating boxes; a shadow is
     depth, not a palette colour, which is why this is not a token. */
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}

/* `--text`, not the brand accent: a QR symbol is read by a camera, and the
   contrast between the modules and their ground is the only thing that matters
   — the same reason the printed sheet's symbol is forced dark rather than
   green. `--text` on `--bg` is the page's highest-contrast pair, and it keeps
   the no-colour-literals rule at the top of this file. */
.qr-symbol {
  display: block;
  width: 11rem;
  height: 11rem;
  color: var(--text);
}

/* ── The amounts ───────────────────────────────────────────────────────────── */

.recipe-table {
  margin: 0 0 var(--sp-8);
  max-width: 34rem;
}

.rt {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

/* The header row the app prints and expert mode shows: "Ingredients" spanning
   the three amount columns, the two percentage labels beside it, and a 2px rule
   closing the row (owner, 2026-09-03). `border-collapse` fuses the cells'
   identical bottom borders into one continuous line across the table. */
.rt .rt-head th {
  border-bottom: 2px solid var(--border-mid);
  padding: 0 0.6rem var(--sp-2);
  text-align: left;
  font-weight: 600;
  color: var(--title);
}

.rt .rt-head th.num {
  text-align: right;
}

/* The two percentage labels are the app's `print.column.*` strings, and they are
   the first thing to go when the table runs out of room — which is why the app's
   own quarter-width column hides them on screen. The COLUMNS stay: the numbers
   are still worth reading, and the sr-only header row still names them. */
@media (max-width: 34rem) {
  .rt .pct-head {
    font-size: 0;
  }
}

.rt td {
  padding: 0.3rem 0.6rem;
}

/* Whole groups shaded, exactly as the app's own table stripes them (owner,
   2026-09-02): the bands are DRY / WET / ADDITIONS / FAT, the four groups a cook
   works in, and shading them is what separates the categories at a glance. The
   alternation is over the bands PRESENT (`shadedBands`), so a batter with no
   grated produce still reads shaded / unshaded rather than leaving a gap. */
.rt tbody tr.banded td {
  background: var(--surface);
}

.rt .num {
  text-align: right;
  white-space: nowrap;
}

/* The secondary amount column: the gram figure an egg count or a spoon measure
   works out to, in smaller type beside the unit a cook counts in — the app's own
   `.col-secondary`. Left-padded to nothing so the pair reads as one amount
   rather than as two columns. */
.rt .aside {
  color: var(--text-muted);
  font-size: 0.85em;
  padding-left: 0.25rem;
  text-align: left;
}

.rt tfoot td {
  border-top: 2px solid var(--border-mid);
  border-bottom: 0;
  font-weight: 600;
  padding-top: var(--sp-3);
}

.rt tfoot .yield {
  text-align: right;
  font-weight: 400;
  font-style: italic;
  color: var(--text-muted);
}

/* ── The three scales (§2.0, S2) ───────────────────────────────────────────── */

.scales {
  margin-top: var(--sp-8);
}

.scale {
  margin: var(--sp-6) 0;
  max-width: 30rem;
}

.scale-label {
  margin: 0 0 var(--sp-2);
  font-size: 0.95rem;
}

/* Texture and bakeability are ORDINAL: five discrete steps, never a continuous
   track. A gradient with a dot at 76% would assert an interval scale the data
   does not have (docs/bake_report_design.md). */
.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--sp-1);
}

/* `.site-main li` carries the prose measure too — same cascade as `.scale-ends`
   above. Harmless today (each grid track is narrower than 68ch anyway), scoped
   so the escape hatch actually works if the layout ever widens. */
.steps .step {
  text-align: center;
  font-size: 0.72rem;
  color: var(--text-muted);
  max-width: none;
}

.step .dot {
  display: block;
  height: 6px;
  margin-bottom: var(--sp-2);
  border-radius: var(--radius-pill);
  background: var(--wall-track);
}

.step.on {
  color: var(--text);
  font-weight: 600;
}

.step.on .dot {
  background: var(--accent);
}

/* Sweetness is a genuinely continuous computed fraction, so it keeps a track —
   and that visual difference between the three scales is information, not
   inconsistency. */
.scale .track {
  display: block;
  width: 100%;
  height: 10px;
}

.track-bar {
  fill: var(--wall-track);
}

.track-mark {
  fill: var(--accent);
}

/* SCOPED THROUGH `.scale` for the same reason the share row is (owner,
   2026-09-05): this is a `<p>`, and `.site-main p { max-width:
   var(--page-measure) }` out-specifies a bare class, so the `max-width: none`
   below was being ignored and the row was capped at 68ch — measured in THIS
   element's 0.72rem type, which is ~24.5rem against the scale's own 30rem. That
   is why "very sweet" / "veldig søt" sat at ~85% of the track instead of at its
   right end: `space-between` was doing its job across a box four fifths the
   width of the axis above it. The ends must span the TRACK, so they must ignore
   the prose measure. */
.scale .scale-ends {
  display: flex;
  justify-content: space-between;
  margin: var(--sp-1) 0 0;
  font-size: 0.72rem;
  color: var(--text-muted);
  max-width: none;
}

.scale-note {
  margin: var(--sp-2) 0 0;
  font-size: 0.78rem;
  color: var(--text-muted);
}

@media (max-width: 34rem) {
  .steps {
    grid-template-columns: 1fr;
    gap: 0;
  }

  /* Five labels across a 390 px screen is ~70 px a column, which wraps every
     one of them. Stacked, each step reads as a row with its dot beside it.
     Scoped through `.steps` to match the base rule's specificity — otherwise the
     `text-align: center` up there wins here regardless of source order. */
  .steps .step {
    display: grid;
    grid-template-columns: 1.5rem 1fr;
    align-items: center;
    gap: var(--sp-2);
    text-align: left;
    padding: 0.15rem 0;
  }

  .steps .step .dot {
    margin-bottom: 0;
  }
}

/* ── The waffle heart (owner, 2026-09-02, layout 2026-09-03) ──────────────────
   The app's own layout, down to the caption: the mark on the left, and beside it
   the italic "The waffle heart says" line directly above the verdict it
   introduces — `WaffleIndicator.svelte`'s `.matrix-says` over `.matrix-verdict`.
   The explanation follows underneath. The app puts that explanation in a
   popover, which a static page has no way to open, so it is simply shown, in the
   app's own words (`matrix.explain.body`, verbatim). */
/* Air on BOTH sides of the mark (owner, 2026-09-05): the heart and the sentence
   it introduces are the one thing on the page that is not prose, and at --sp-4
   below it the explanation ran straight into the verdict. --sp-8 is the token
   ceiling, so the extra above it is a multiple of the same step rather than a
   new literal. */
.quality-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin: calc(var(--sp-8) * 1.5) 0 var(--sp-8);
}

.verdict-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  min-width: 0;
}

/* Italic, and NOT the app's all-caps section treatment: it is a sentence with
   its own punctuation, not a label — the reasoning `.matrix-says` carries. */
.quality .says {
  margin: 0 0 0.15rem;
  font-style: italic;
  font-weight: 600;
  color: var(--accent);
}

.quality-row .heart {
  flex: 0 0 auto;
}

/* The lit / unlit encoding is the app's: a filled cell against an empty one,
   which is also what survives a greyscale printer. */
.heart .lit {
  fill: var(--accent);
}

.heart .unlit {
  fill: none;
}

.heart .rules {
  stroke: var(--border-mid);
}

.verdict {
  margin: 0;
  font-size: 1.02rem;
  color: var(--title);
}

/* The explanation is prose, and its lead-in is a run-in rather than a heading
   (owner, 2026-09-03): same size as the sentence it introduces, bold, on the
   same line — a subtitle on a line of its own read as a new section when it is
   really the first three words of one paragraph. */
.explain {
  font-size: 0.88rem;
  color: var(--text-secondary);
}

.explain strong {
  color: var(--title);
}

/* ── Photographs ─────────────────────────────────────────────────────────────
   Placed by the owner's file, so they can land anywhere in the flow; the figure
   owns its own vertical rhythm rather than depending on what precedes it. */
/* 44rem is not a free number: `figureHtml` tells the browser `sizes="… 44rem"`,
   so the srcset ladder is chosen against it. Change one and change the other. */
.photo {
  margin: var(--sp-8) 0;
  max-width: 44rem;
}

.photo img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 4px;
}

.photo figcaption {
  margin-top: var(--sp-2);
  font-size: 0.82rem;
  font-style: italic;
  color: var(--text-muted);
}

/* ── The generated method, labelled as generated ─────────────────────────────
   The prefix is what lets the owner's own instructions follow in the prose
   without a reader wondering which of the two is the recipe. */
.auto-instruction b {
  color: var(--title);
}

/* ── The bake report ─────────────────────────────────────────────────────────
   On every recipe page, baked or not: the page is read by people who will bake
   it themselves, and their report is the point. */
.feedback-link {
  margin: var(--sp-6) 0;
  font-size: 0.9rem;
}


/* ── The app's own machinery on a recipe page (§2.0, owner 2026-09-02) ────────
   Two mount points and a print sheet, all empty until `recipe-main.ts` fills
   them. The emitted markup carries `hidden` on the two controls, so nothing
   here has to hide them; what these rules do is place them once they exist. */
.live-print {
  max-width: 34rem;
}

/* The stepper used to be pulled UP into the table's own bottom margin, which
   left ~0.5rem between the last amount and the egg control — close enough that
   the slider read as another row of the table (owner, 2026-09-05). Zero here
   lets `.recipe-table`'s --sp-8 stand as the gap it already is. */
.live-batch {
  max-width: 34rem;
  margin: 0 0 var(--sp-8);
}

/* The printed sheet is the APP's MAKE column, mounted whole. Hidden on screen
   here and revealed by the `@media print` block in app.css — which is the same
   rule that reveals it in the app, and the reason a recipe page and the app
   print the same sheet for the same batter rather than two versions of it. */
.col-make {
  display: none;
}

@media print {
  /* The sheet gets the whole page. app.css's print block already says
     `main { max-width: none }`, but `.site-main`'s own measure is a class rule
     and out-specifies it — which is why the recipe page's sheet came out a
     hand's width narrower than the app's for the same batter until this was
     added. `.site-page`'s min-height would likewise leave the flex column
     fighting the sheet's own page-height floor. */
  .site-main {
    max-width: none !important;
    padding: 0 !important;
  }

  .site-page {
    min-height: 0 !important;
    display: block !important;
  }

  /* The page's prose measure stops at the sheet's edge. `.site-main p` is a
     class-plus-type selector and out-specifies the component's own class, so
     the printed blurb was being held to 68ch — a hand's width narrower than the
     app prints the identical block. Nothing inside the sheet is page prose. */
  .col-make p,
  .col-make li {
    max-width: none !important;
  }

  /* …and everything the reader was looking at goes, for the same reason the app
     hides its picker and sliders: the sheet is the recipe, not the page about
     it. Listed rather than done with a `:not()` on `<main>`'s children, because
     the header and the footer are outside `<main>` and the sheet is inside it. */
  .site-header,
  .site-footer,
  .skip-link,
  .crumb,
  .page-top,
  .recipe-tools,
  .site-main > h1,
  .site-main > p,
  .site-main > ul,
  .site-main > figure,
  .site-main > [data-block],
  .live-print,
  .live-batch {
    display: none !important;
  }
}

/* GENERATED by vite/siteChrome.ts:brandKerningCss from the app's own brand
   modules (wordmarkKerning.ts, waffleLogoAnimation.ts). Do not edit — edit
   those, which the app renders from too. */
.brand .brand-mark { margin-right: 15.04px; }
.wm-avogadro .k0 { letter-spacing: -0.038846em; }
.wm-avogadro .k1 { letter-spacing: -0.018462em; }
.wm-avogadro .k2 { letter-spacing: -0.018462em; }
.wm-avogadro .k3 { letter-spacing: -0.018462em; }
.wm-avogadro .k4 { letter-spacing: -0.024615em; }
.wm-avogadro .k5 { letter-spacing: -0.021923em; }
.wm-avogadro .k6 { letter-spacing: -0.014231em; }
.wm-avogadro .k7 { letter-spacing: -0.005769em; }
