/*
 * Tonight — the ask, and the dinners it produces.
 *
 * Two views swap in place: the composer card, and the results. The Deck put one
 * dinner per viewport in a snap-scrolling container with a vertical action rail
 * and generative art behind display type; all of that is gone. One dinner wears
 * a card, the others are a short list under it, and the surface scrolls the way
 * every other surface here does.
 *
 * The hidden view keeps its element and takes `hidden` + `inert`, so focus and
 * the Escape handler have something stable to hold on to.
 */

/* ─── The column the surface lives in ─────────────────────────────────────── */

/*
 * One flex column for the large title and whichever view is up, so the composer
 * can claim the height the title does not.
 *
 * Before this, the composer card was a fixed 318px at every viewport: 368px of
 * empty field below it at 375x812 (45% of the screen), 488px at 430x932 (52%),
 * with both commitments stranded at y=270..372 — the top forty per cent of a
 * phone held one-handed, which is the one posture this screen was designed for.
 * `min-height: 100%` resolves against `.screen`, whose height is definite, so the
 * column is exactly the scrollport and nothing overflows on arrival.
 */
.tonight {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

/* ─── The composer ────────────────────────────────────────────────────────── */

/*
 * The card sits at the FOOT of the column, at its own height.
 *
 * Two compositions were built and measured before this one. Pinned to the top —
 * what shipped — left 368px of dead field below it at 375x812 and 488px at
 * 430x932, with both commitments at y=270..372, the top forty per cent of a phone
 * held in one hand. Stretched to fill, the dead space simply moved inside the
 * card: 660px of empty white, which reads as an unfinished page rather than as
 * room. A field with a card resting on the bottom of it is the third answer and
 * the right one: the grey above is the platform's own ground, the same ground
 * Kitchen and Week show, and every control lands where the thumb already is.
 *
 * It also makes the late layout shift structurally impossible. `/api/attention`
 * grows the free line after first paint; a bottom-anchored card grows UPWARD, so
 * the buttons do not move. They used to drop 50px onto the diner pills.
 */
.composer {
  display: flex;
  flex: 1;
  align-items: flex-end;
  min-height: 0;
}

/*
 * The bare-kitchen card is centred, not bottom-anchored.
 *
 * The ask sits at the bottom because that is where the thumb already is, and a
 * full ask card is tall enough for that to read as deliberate. The bare card is
 * three short lines and a button — bottom-anchored it left 640px of empty field
 * above it at 375x667, which on a brand-new household's first app-open reads as
 * a screen that failed to load rather than as one with nothing to say yet.
 */
.composer--bare { align-items: center; }
.composer__card {
  display: flex;
  flex: 1;
  flex-direction: column;
  max-height: 100%;
}

.composer__kicker {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--ink-2);
}

/*
 * The question, at the size a dish name gets: the question is the headline, not
 * a field beneath one. No new type step was invented for it — a second display
 * size half a step from the first would be a token earning its place by existing
 * rather than by being needed.
 *
 * `field-sizing: content` grows it as the sentence does, so a long ask never
 * scrolls inside a two-line box. Browsers without it keep `rows="2"`, which is
 * the old behaviour rather than a broken one.
 *
 * A floor and a ceiling, both of them fixes.
 *
 * The floor is the tap target, and it is bought with padding rather than with a
 * minimum height. Content-sized, this box measured 311x32.2 — twelve pixels under
 * the 44px minimum this product enforces in its own test suite, on the one
 * control the whole screen exists to receive. A `min-height` of two display lines
 * also clears 44 but leaves a visible band of nothing under a one-line question;
 * padding grows the target through the type instead of beside it.
 *
 * The ceiling is the card, which cannot exceed the column. A 480-character ask
 * grew this box to 676px and put "Find me this" at y=914 — off screen, with
 * nothing to scroll it back. Now the card stops at the column's height and the
 * ask scrolls inside itself, so the commitments stay where they were.
 *
 * The scrollbar is hidden rather than left to `auto`, which reserved a gutter and
 * painted a 1px rule beside the placeholder even with nothing to scroll
 * (scrollHeight 33 in a 32px box is enough for Chrome to draw one). Same
 * treatment `.screen` already gets.
 */
/*
 * The one cue that says "editable".
 *
 * Everything above is the argument for making the ask BE the heading, and it is
 * sound. But the affordance was spent entirely on typography: 28px, 700,
 * transparent, borderless, under a DINNER TONIGHT kicker, in exactly the
 * position a page title occupies. A first-time user reads "What do you feel
 * like?" as the screen's title, taps the one control that obviously does
 * something, and never discovers that the product's headline feature is typed.
 *
 * A rule under the text, not a box around it — a bordered field would return the
 * screen to a form and lose what makes it specific. `--sunk` at rest so it reads
 * as a writing line rather than as an underline on a heading, and the tint on
 * focus, which is the system's promise colour and was unspent on this control.
 */
.composer__ask {
  width: 100%;
  margin-top: var(--s2);
  field-sizing: content;
  padding: var(--s2) 0;
  border: 0;
  border-bottom: 2px solid var(--sunk);
  background: none;
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--text-display);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
  resize: none;
  overflow-y: auto;
  scrollbar-width: none;
}
.composer__ask::-webkit-scrollbar { display: none; }
/*
 * The primary input on the primary screen had no focus indicator at all.
 *
 * `.composer__ask:focus { outline: none }` is (0,2,0) and beat the system's
 * `:focus-visible` (0,1,0), and the field has no border and no fill to fall back
 * on — so the only cue a keyboard user got was the caret. Measured on a real Tab:
 * `outline-style: none` where every other stop on the screen showed a 2px tint
 * ring. This rule restores the system's own ring at the same specificity, and a
 * text field matches `:focus-visible` on pointer focus too, which is right: this
 * one looks like a heading until something says it is a field.
 */
.composer__ask:focus { border-bottom-color: var(--tint); }
.composer__ask:focus-visible {
  outline: 2px solid var(--tint);
  /* The system's own 2px and not a pixel more: the box already carries 8px of
     padding for the tap target, so the ring clears the glyphs without reaching
     up through the kicker above it. At 4px it struck the word DINNER. */
  outline-offset: 2px;
  border-radius: var(--radius-md);
}
/* The placeholder is not decoration here — it is the only thing on the screen
   saying what the box is for. `--ink-2` clears 4.5 on every surface; `--ink-3`
   bottoms out at 3.00 and is not for text at any size. Exact ratios live beside
   the tokens in app.css, and test/ui/contrast.test.js checks them. */
.composer__ask::placeholder { color: var(--ink-2); opacity: 1; }

/*
 * What the app already knows, and the one line on the arrival screen that proves
 * it knows anything.
 *
 * It sits directly above the diner pills, because half of it names them: "Cooking
 * for Noam and Tal" belongs beside the control that changes who that is, not
 * three hundred pixels above it under the question.
 */
.composer__free {
  margin-top: var(--s3);
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--ink-2);
  text-wrap: pretty;
}
/* What needs eating is the one thing on this line the app knows and the cook
   does not, so it is the one thing that carries a signal colour. Colour alone,
   not colour and weight: three expiring items set in bold amber read as an
   alarm, and this line is a courtesy. */
.composer__urgent { color: var(--warn); }

.composer__notice {
  margin-top: var(--s3);
  padding: var(--s2) var(--s3);
  border-radius: var(--radius-md);
  background: var(--gone-sunk);
  font-size: var(--text-sm);
  line-height: 1.4;
  color: var(--gone);
}

.diners--composer,
.diners--dish {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s4);
}
.composer__actions {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  margin-top: var(--s4);
}
.composer__primary { min-height: 50px; }

/*
 * A kitchen with nothing in it, said where the question goes.
 *
 * Same size and weight as `.composer__ask`, because it is doing that element's
 * job: the surface has one large thing on it either way. It replaces the ask
 * rather than sitting under it — a 28px "What do you feel like?" over a form
 * that cannot be submitted is a question the app has no way to answer.
 */
.composer__bare {
  margin-top: var(--s2);
  padding: var(--s2) 0;
  font-size: var(--text-display);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.15;
  text-wrap: balance;
}

/* Why the ask is not on offer, and what to do instead. Above the control now:
   the reason for an action does not belong underneath it. */
.composer__note {
  margin-bottom: var(--s5);
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* ─── The dinner ──────────────────────────────────────────────────────────── */

.dish__count {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--ink-2);
}
/* A dish built for a table that has since changed. `--warn` and words, never one
   without the other — the same rule the expiring rows follow, and the reason the
   line spells the mismatch out rather than tinting the old claim. */
.dish__count--stale { color: var(--warn); }

/*
 * The dish name. Large and heavy, but it wraps and is never clamped: it is the
 * answer to the question just asked, and truncating it would hide the difference
 * between two dinners whose first three words match.
 *
 * The Deck had to pick one of three type sizes per title, because at full
 * display scale a long name ran under the action rail. There is no rail and no
 * fixed-height viewport to overflow now, so the size is simply one size.
 */
.dish__name {
  margin-top: var(--s1);
  font-size: var(--text-display);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.1;
  text-wrap: balance;
  overflow-wrap: break-word;
}

.dish__meta {
  margin-top: var(--s2);
  font-size: var(--text-base);
  line-height: 1.4;
  color: var(--ink-2);
}
.dish__meta b { color: var(--ink); font-weight: 600; }

/*
 * "Uses up …" is a sentence, and `.tag` is not built for one.
 *
 * The primitive is `white-space: nowrap` on purpose: a tag is a compact capsule
 * and a two-word one should never break across lines. This one names every
 * ingredient the dish rescues, and six of them measured 388px inside a 358px
 * card — it ran straight out past the card edge and gave `.screen` a horizontal
 * scroll (scrollWidth 420 in a 390 viewport), so the whole surface slid sideways
 * under the thumb on a screen that has nothing to the right of it.
 *
 * It wraps here rather than the primitive relaxing everywhere: the capsule in a
 * `.row__meta` beside a quantity is still a label and still must not break.
 * `line-height` comes with it, because a capsule sized for one line closes its
 * two lines up against each other.
 */
.dish__uses { margin-top: var(--s3); }
.dish__uses .tag {
  display: inline-block;
  white-space: normal;
  line-height: 1.45;
}

.dish__acts {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  margin-top: var(--s4);
}
.dish__minor {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: var(--s2);
}
/* Save and Never are peers of each other and subordinate to the recipe: no
   fill, and the 44px floor kept because one of them is destructive. */
.dish__act {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: var(--tap);
  border-radius: var(--radius-md);
  background: var(--sunk);
  color: var(--tint);
  font-size: var(--text-sm);
  font-weight: 500;
}
.dish__act:hover { background: var(--pressed); }
.dish__act svg {
  width: 18px; height: 18px;
  fill: none; stroke: currentColor; stroke-width: 1.9;
  stroke-linecap: round; stroke-linejoin: round;
}
.dish__act[aria-pressed='true'] svg { fill: currentColor; }
/* The destructive one wears the alarm colour, which is what distinguishes it
   from Save at a glance. */
.dish__act[data-act='never'] { color: var(--gone); }

.dish__refine { margin-top: var(--s1); }

/* ─── The alternatives ────────────────────────────────────────────────────── */

/*
 * What the deck's flick became. A row per dinner still held, tapped to bring it
 * up to the card. Taller than a Kitchen row because there is no density floor
 * here and a dish name is longer than an ingredient name.
 */
.dish-alt {
  gap: var(--s3);
  padding-block: var(--s2);
}
.dish-alt .row__title { white-space: normal; }
.dish-alt .row__meta { margin-top: 2px; }
/* Same sentence as the card's, in a narrower column: "uses corn, tomatoes,
   spring onions, celery, bread and câpres" is wider than the row it sits in, and
   a nowrap flex item overflows its own `min-width: 0` parent rather than
   shrinking. See `.dish__uses .tag` above for why the primitive stays nowrap. */
.dish-alt .row__meta .chip { white-space: normal; line-height: 1.45; }
/* The mark points up because the row moves up. It is 13x8 where the disclosure
   chevron is 8x13, and it keeps `--ink-3` and `stroke` — a mark, not text, which
   is what lets the contrast test hold "--ink-3 is never a sentence" mechanically. */
.chev--up { width: 13px; height: 8px; }

/* ─── The trailing card ───────────────────────────────────────────────────── */

.dish--state { padding-block: var(--s2); }
.dish--state .empty { padding: var(--s6) var(--s4); }
.dish--state .empty__body { text-align: center; }

/*
 * The offer to build another: one line and a button, on one row.
 *
 * It was a full empty state and measured 299px — taller than the whole action
 * stack of the dinner above it, arguing with the answer inside the same scroll.
 * Its button also rendered at y=760 on a 375x812 screen, which is four pixels
 * behind the tab bar. At this height both problems are gone.
 */
.tail {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) var(--s2) var(--s2) var(--s3);
}
.tail__body { flex: 1; min-width: 0; }
.tail__title {
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: -0.02em;
}
.tail__note {
  margin-top: 1px;
  font-size: var(--text-sm);
  color: var(--ink-2);
}
.tail__act { flex-shrink: 0; padding-inline: var(--s3); }

/*
 * The wait, wearing the shape of the answer.
 *
 * The bars are the dish card's own parts at the sizes they will land at: two
 * lines of the 28px title, then the meta line. Nothing here is a spinner and
 * nothing is centred — a centred ring in a short card is a hole in the layout,
 * and the thing arriving is left-aligned text. `.skeleton` carries the shimmer
 * and, more to the point, the static `prefers-reduced-motion` fallback that the
 * ring this replaces did not have: under reduce the ring simply froze mid-turn
 * and sat there for thirty seconds looking broken.
 */
.building { padding: var(--s2) 0; }

.building__lines {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  margin-top: var(--s3);
}
/* 22px for a 28px line, the way a skeleton bar always sits inside the leading of
   the text it stands in for rather than filling it. */
.building__title { height: 22px; }
.building__title--short { width: 62%; }
.building__meta {
  height: 15px;
  width: 44%;
  margin-top: var(--s1);
}

.building__ask {
  margin-top: var(--s5);
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--ink-2);
  text-wrap: pretty;
}
/* The typed ask, quoted. `q` gets the reader's own locale quotes for free, which
   is the one piece of punctuation worth not hand-rolling. */
.building__ask q { color: var(--ink); }

.building__note {
  margin-top: var(--s1);
  font-size: var(--text-sm);
  line-height: 1.45;
  color: var(--ink-2);
  text-wrap: pretty;
}

/* The way out of the wait. Secondary and inline rather than block: it is an
   escape hatch on a card the household is hoping not to need, not an offer. */
.building__stop {
  margin-top: var(--s3);
  align-self: flex-start;
}

/* ─── The panels — recipe and saved library ───────────────────────────────── */

/*
 * These cover the screen rather than floating over it: a recipe you are cooking
 * from is the whole surface, and a half-visible list behind it is noise at the
 * exact moment attention matters most.
 */
.recipe-panel {
  position: absolute;
  inset: 0;
  z-index: var(--z-sheet);
  display: none;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: calc(env(safe-area-inset-top) + var(--s10)) 0
           calc(var(--s10) + env(safe-area-inset-bottom));
  background: var(--bg);
  /* `.screen` hides its scrollbar and this covers `.screen`, so a visible one
     here reads as a second window rather than as the same surface, deeper. */
  scrollbar-width: none;
}
.recipe-panel::-webkit-scrollbar { display: none; }
.recipe-panel[data-open] {
  display: block;
  animation: panel-up 320ms var(--ease);
}
@keyframes panel-up { from { translate: 0 24px; opacity: 0; } }

/*
 * The way out, and it stays on screen.
 *
 * `position: absolute` inside a panel that is itself the scroller meant the only
 * close control scrolled away with the recipe: measured at scrollTop 485 it sat
 * at y = -477, entirely gone, with the tab bar underneath `inert`. A person at
 * the hob on step five, one hand free, had no visible way back.
 *
 * `sticky` + `float` rather than `absolute`: the float reserves the corner so the
 * recipe title's line boxes shorten around it instead of running underneath, and
 * sticky pins it to the top of the scrollport from step one to the last.
 */
.recipe-panel__close {
  position: sticky;
  top: calc(env(safe-area-inset-top) + var(--s2));
  float: inline-end;
  margin-right: var(--s2);
  /* Local: above the panel's scrolling content, nothing else. */
  z-index: 1;
  border-radius: 50%;
  /*
   * `--sunk` at 72%, not `--sunk`.
   *
   * The plate was fully opaque, which made the `backdrop-filter` beneath it dead
   * code and turned the button into a hard occluder: measured a 20px overlap
   * that erased the word "Pantry" from the garlic row's location column — the
   * single most useful thing on that line at the fridge, and the thing this
   * panel exists to tell you. DESIGN.md specifies this component as translucent
   * on purpose: "It reads as nothing on the field and as a plate the moment
   * something is behind it, which is exactly when it is needed."
   *
   * Translucency makes occluded text legible. The padding on the ingredient rows
   * below makes it not occluded. Both, because the first is the documented
   * design and the second is what makes the location column reliable.
   */
  background: rgb(233 233 239 / 0.72);   /* --sunk at 72%, so the blur works */
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
