/* Frontend Style Plan §4.2 — self-hosted type. Public Sans is body/UI text;
   Fraunces is headings and recipe titles only. Self-hosted as the Google
   Fonts variable .ttf (not .woff2 — this is a LAN app, not a public site
   under bandwidth pressure, so the larger uncompressed format is fine and
   skips a conversion step). font-display: swap means the system fallback
   stack renders immediately with no layout break while each file loads. */
@font-face {
    font-family: "Public Sans";
    src: url("../fonts/public-sans-variable.ttf") format("truetype-variations"),
         url("../fonts/public-sans-variable.ttf") format("truetype");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Fraunces";
    src: url("../fonts/fraunces-variable.ttf") format("truetype-variations"),
         url("../fonts/fraunces-variable.ttf") format("truetype");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
}

:root {
    --font-family: "Public Sans", "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
    --font-family-display: "Fraunces", Georgia, "Times New Roman", serif;
    color-scheme: light;
    --background: #fffaf5;
    --surface: #f7efe7;
    --surface-muted: #efe3d6;
    --surface-strong: #e4cfba;
    --input-surface: #fffdfb;
    --text: #202124;
    --muted: #5f5750;
    --accent: #7a4b2b;
    --button-text: #ffffff;
    --border: #d8ccc0;
    /* SS9: a control boundary needs 3:1 against its surface; --border is a
       decorative divider and sits well below that. */
    --border-strong: #8d7f70;
    --shadow: rgba(48, 31, 18, 0.08);
    /* Frontend Style Plan §9: paper is not a theme surface. These three stay
       fixed whatever theme is on screen, because the printed page and the
       on-screen paper preview are both ink on paper and neither follows dark
       mode. Tokens rather than raw hex so §9's "no raw values" holds here too,
       and so the preview and the print block cannot drift apart. */
    --c-print-paper: #ffffff;
    --c-print-ink: #000000;
    --c-print-line: #d0d0d0;

    --alert-error-surface: #fff0f0;
    --alert-error-border: #c94b4b;
    --alert-error-text: #7d1f1f;
    --alert-error-shadow: rgba(201, 75, 75, 0.2);

    /* Frontend Style Plan §4.1 — state colors, split from --accent so status
       stops borrowing the button color (Law: --accent means "click me",
       these mean "this is its state"). */
    --c-sage: #4A6150;
    --c-sage-surface: #E6ECE7;
    --c-sage-border: #B9C7BC;
    --c-amber: #8A5B12;
    --c-amber-surface: #F5EAD3;
    --c-amber-border: #DDBE84;
    --c-clay: #9B2C2C;
    --c-clay-surface: #FBE4E4;
    --c-clay-border: #E3A9A9;

    /* Frontend Style Plan §4.2 — type scale, named by role. */
    --t-display: clamp(1.75rem, 1.4rem + 1.6vw, 2.5rem);
    --t-title: clamp(1.35rem, 1.2rem + 0.7vw, 1.6rem);
    --t-section: 1.125rem;
    --t-body: 1rem;
    --t-small: 0.875rem;
    --t-eyebrow: 0.75rem;

    /* §4.3 — the one deliberate break in the scale: ingredients and
       instructions, read from three feet away with your hands in a bowl.
       Applies only to .recipe-instructions and the ingredients list. */
    --t-cook: 1.1875rem;

    /* Frontend Style Plan §4.4 — 4px-based space ramp. */
    --s-1: 0.25rem;
    --s-2: 0.5rem;
    --s-3: 0.75rem;
    --s-4: 1rem;
    --s-5: 1.5rem;
    --s-6: 2.5rem;

    /* Frontend Style Plan §4.5 — radius and elevation. */
    --r-sm: 8px;
    --r-md: 12px;
    --r-lg: 16px;
    --r-pill: 999px;
    --e-card: 0 10px 24px var(--shadow);
    --e-raised: 0 16px 32px var(--shadow);
}

[data-theme="dark"] {
    color-scheme: dark;
    --background: #0d1117;
    --surface: #161b22;
    --surface-muted: #1d2530;
    --surface-strong: #2a3444;
    --input-surface: #10161f;
    --text: #f3f4f6;
    --muted: #c2cad5;
    --accent: #e4b074;
    --button-text: #111827;
    --border: #2f3847;
    --border-strong: #79879b;
    --shadow: rgba(0, 0, 0, 0.45);
    --alert-error-surface: #341618;
    --alert-error-border: #f28b82;
    --alert-error-text: #ffd7d3;
    --alert-error-shadow: rgba(242, 139, 130, 0.2);

    --c-sage: #8FB09B;
    --c-sage-surface: #1E2A22;
    --c-sage-border: #3B5344;
    --c-amber: #E4B074;
    --c-amber-surface: #2E2418;
    --c-amber-border: #5C4526;
    --c-clay: #F28B82;
    --c-clay-surface: #341618;
    --c-clay-border: #6B3535;
}

/* Frontend Style Plan §4.1 — the plan's own palette, added as two further
   themes rather than replacing the two above. §2 says the existing warm
   palette "stays … tightened, not replaced", while §4.1 specifies concrete
   hexes that differ from it; offering both settles that contradiction
   without taking either look away.
   These blocks declare the §4.1 names as the source of truth and map the
   semantic names the components actually use onto them, so the plan's
   vocabulary exists in the codebase for the first time. */
[data-theme="paper"] {
    color-scheme: light;

    --c-paper: #FBF6EF;
    --c-surface: #F5EDE3;
    --c-surface-sunk: #EDE2D4;
    --c-line: #DCCBB6;
    --c-ink: #241C16;
    --c-ink-soft: #5E5148;
    --c-ember: #A6421F;
    --c-ember-hover: #8A3418;
    --c-on-ember: #FFF8F2;

    --background: var(--c-paper);
    --surface: var(--c-surface);
    --surface-muted: var(--c-surface-sunk);
    --surface-strong: var(--c-line);
    --input-surface: #FFFCF7;
    --text: var(--c-ink);
    --muted: var(--c-ink-soft);
    --accent: var(--c-ember);
    --button-text: var(--c-on-ember);
    --border: var(--c-line);
    --border-strong: #93816C;
    --shadow: rgba(58, 38, 24, 0.10);
    --alert-error-surface: #FBE4E4;
    --alert-error-border: #9B2C2C;
    --alert-error-text: #6E1F1F;
    --alert-error-shadow: rgba(155, 44, 44, 0.2);

    --c-sage: #3D5344;
    --c-sage-surface: #E6ECE7;
    --c-sage-border: #B9C7BC;
    --c-amber: #7A5010;
    --c-amber-surface: #F5EAD3;
    --c-amber-border: #DDBE84;
    --c-clay: #9B2C2C;
    --c-clay-surface: #FBE4E4;
    --c-clay-border: #E3A9A9;
}

/* A dark counterpart to the same palette. §4.1 specifies only the light
   ramp, so the ink and paper roles are inverted around the same warm hue
   and the ember accent is lifted until it clears 4.5:1 on the surface. */
[data-theme="paper-dark"] {
    color-scheme: dark;

    --c-paper: #171310;
    --c-surface: #211B16;
    --c-surface-sunk: #2C241D;
    --c-line: #43372C;
    --c-ink: #F6EFE6;
    --c-ink-soft: #C3B4A4;
    --c-ember: #E8845C;
    --c-ember-hover: #F09A75;
    --c-on-ember: #2A1109;

    --background: var(--c-paper);
    --surface: var(--c-surface);
    --surface-muted: var(--c-surface-sunk);
    --surface-strong: var(--c-line);
    --input-surface: #120E0B;
    --text: var(--c-ink);
    --muted: var(--c-ink-soft);
    --accent: var(--c-ember);
    --button-text: var(--c-on-ember);
    --border: var(--c-line);
    --border-strong: #8C7862;
    --alert-error-surface: #3A1A18;
    --alert-error-border: #F0938A;
    --alert-error-text: #FFDAD5;
    --alert-error-shadow: rgba(240, 147, 138, 0.2);

    --c-sage: #9CBBA6;
    --c-sage-surface: #1D2721;
    --c-sage-border: #3D5344;
    --c-amber: #E2B274;
    --c-amber-surface: #2E2418;
    --c-amber-border: #5C4526;
    --c-clay: #F0938A;
    --c-clay-surface: #3A1A18;
    --c-clay-border: #6B3535;
}

* {
    box-sizing: border-box;
}

html {
    min-height: 100%;
    overflow-y: scroll;
    scrollbar-gutter: stable;
}

body {
    margin: 0;
    min-height: 100%;
    font-family: var(--font-family);
    background: var(--background);
    color: var(--text);
}

a {
    color: var(--accent);
}

h1, h2, h3, h4 {
    font-family: var(--font-family-display);
    font-weight: 600;
}

/* §4.2 — numbers align down the column instead of drifting with proportional
   spacing: ingredient quantities, stats, durations, table counts. */
.quantity, .stat, .time, td.num {
    font-variant-numeric: tabular-nums;
}

.page {
    min-height: 100vh;
    /* Narrow screens spend their horizontal budget on the card interior,
       not on the gutter outside it — the page gutter stays tight so cards
       fill the width, and .card below supplies the real breathing room. */
    padding: var(--s-4) var(--s-2) var(--s-6);
}

.shell {
    max-width: 880px;
    margin: 0 auto;
}

.shell--print {
    max-width: 960px;
}

.site-header {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: space-between;
    gap: var(--s-4);
}

.site-header.card {
    gap: var(--s-3);
}

.site-header__identity {
    min-width: 0;
}

/* Mobile-first default: the toggle-driven mobile nav is what renders until
   the min-width: 60rem tier (near the end of this file) swaps it for the
   inline desktop nav. See Frontend Style Plan §4.6 / Law 8 migration notes.
   Compound selector: .site-nav--desktop also carries .nav-links (display:
   flex), a same-specificity rule that sits later in this file, so a bare
   single-class selector here would lose the cascade at every width. */
.site-nav.site-nav--desktop {
    display: none;
}

.site-nav--mobile {
    width: 100%;
    display: block;
    margin-left: 0;
}

.site-nav__toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    width: 100%;
    padding: 0.65rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface-muted);
    color: var(--text);
    font-weight: 600;
    list-style: none;
    cursor: pointer;
}

.site-nav__toggle::-webkit-details-marker {
    display: none;
}

.site-nav__toggle::after {
    content: "▾";
    font-size: var(--t-eyebrow);
    color: var(--accent);
}

.site-nav[open] > .site-nav__toggle::after {
    transform: rotate(180deg);
}

.site-nav--mobile > .site-nav__panel {
    display: flex;
}

.site-nav--mobile:not([open]) > .site-nav__panel {
    display: none;
}

.site-nav--mobile[open] > .site-nav__panel {
    display: grid;
}

.site-nav__panel.nav-links {
    align-items: stretch;
    gap: var(--s-2);
    margin-top: 0.75rem;
}

.site-nav__panel.nav-links > a,
.site-nav__panel .nav-menu summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 0.65rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface-muted);
    text-decoration: none;
}

.site-nav__panel .nav-menu__summary-copy {
    align-items: flex-start;
}

.site-nav__panel .nav-menu__panel {
    margin-top: 0.5rem;
}

.site-title {
    margin: 0.35rem 0 0;
    font-size: var(--t-title);
}

/* The masthead brand. Fixed-length, so it can never displace the nav the way
   the page title did — but Law 6's guards are applied anyway, because the app
   name is configurable. */
.site-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    min-width: 0;
    color: var(--text);
    text-decoration: none;
    font-family: var(--font-family-display);
    font-size: var(--t-title);
    font-weight: 600;
    line-height: 1.1;
}

.site-brand__mark {
    flex: 0 0 auto;
    font-size: 1.1em;
    line-height: 1;
}

.site-brand__name {
    min-width: 0;
    overflow-wrap: anywhere;
}

.site-brand:hover .site-brand__name {
    color: var(--accent);
}

/* Law 6 — the nav is a control group and must never be pushed or squeezed by
   whatever sits beside it. */
.site-header__identity {
    flex: 1 1 auto;
}

.site-nav--desktop {
    flex: 0 0 auto;
}

/* Navigation items read as navigation, not as body links: no underline, a
   clear resting state, and an unambiguous "you are here". */
.site-nav__link {
    padding: 0.45rem 0.7rem;
    border-radius: var(--r-md);
    color: var(--muted);
    text-decoration: none;
    font-weight: 600;
    white-space: nowrap;
}

.site-nav__link:hover {
    background: var(--surface-muted);
    color: var(--text);
}

.site-nav__link--current {
    background: var(--surface-muted);
    color: var(--accent);
    box-shadow: inset 0 -2px 0 var(--accent);
}

.nav-links {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    flex-wrap: wrap;
}

.nav-menu {
    position: relative;
    width: 100%;
}

.nav-menu summary {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    cursor: pointer;
    color: var(--accent);
    font-weight: 600;
    list-style: none;
}

.nav-menu summary::-webkit-details-marker {
    display: none;
}

.nav-menu summary::after {
    content: "▾";
    font-size: var(--t-eyebrow);
}

.nav-menu--account summary {
    padding: 0.45rem 0.8rem;
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    background: var(--surface-muted);
}

.nav-menu__summary-copy {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    line-height: 1.15;
}

.nav-menu__summary-title {
    color: var(--muted);
    font-size: var(--t-eyebrow);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.nav-menu__summary-subtitle {
    color: var(--text);
    font-size: var(--t-body);
    font-weight: 700;
}

.nav-menu[open] summary::after {
    transform: rotate(180deg);
}

.nav-menu__panel {
    position: absolute;
    top: calc(100% + 0.75rem);
    right: 0;
    z-index: 20;
    min-width: 13rem;
    display: grid;
    gap: var(--s-3);
    margin-top: 0;
}

.nav-menu__panel a {
    text-decoration: none;
}

.nav-menu__account-summary {
    display: grid;
    gap: var(--s-1);
    padding-bottom: 0.35rem;
    border-bottom: 1px solid var(--border);
}

.nav-menu__account-name {
    color: var(--text);
    font-size: var(--t-body);
}

.nav-menu__account-role {
    color: var(--muted);
    font-size: var(--t-small);
}

.nav-menu__panel .inline-form {
    display: flex;
}

.nav-menu__panel :where(.button) {
    width: 100%;
}

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    /* Was 0.35rem, which left content (notably full-width stacked buttons)
       visually touching the card border and made the card read as a hairline
       frame rather than a surface. */
    padding: var(--s-4);
    margin-top: 1.5rem;
    box-shadow: var(--e-card);
}

.card .card {
    margin-top: 1rem;
}

.card--dense {
    padding: 1.15rem 1.2rem;
}

.eyebrow {
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--muted);
    font-size: var(--t-eyebrow);
}

.actions {
    display: flex;
    gap: var(--s-3);
    flex-wrap: wrap;
    margin-top: 1rem;
}

/* Law 4: stacking is the default, not the opt-in. Every button (and every
   button inside an .inline-form) takes the full row width until the
   min-width: 40rem tier below restores a side-by-side row. */
.actions > :where(.button),
.actions > .inline-form > :where(.button) {
    flex: 1 1 100%;
}

.actions > .inline-form {
    display: flex;
}

.actions > .inline-form > :where(.button) {
    width: 100%;
}

.actions--end {
    justify-content: flex-end;
}

.actions--between {
    justify-content: space-between;
    align-items: flex-start;
}

.actions--header {
    margin-top: 0;
    align-items: center;
}

/* Frontend Style Plan §6 — buttons take the --r-md corner, not --r-pill.
   --r-pill stays reserved for what §4.5 actually assigns it to: chips,
   badges, avatars and progress tracks. */
.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    padding: 0.65rem 1rem;
    border-radius: var(--r-md);
    border: 1px solid var(--accent);
    background: var(--accent);
    color: var(--button-text);
    text-decoration: none;
    cursor: pointer;
    appearance: none;
    font: inherit;
    font-weight: 600;
    line-height: 1.2;
}

.button__icon {
    display: none;
    align-items: center;
    justify-content: center;
}

.button__icon svg {
    width: 1.1em;
    height: 1.1em;
    stroke: currentColor;
}

.button__text {
    display: inline;
}

.button-small {
    padding: 0.45rem 0.8rem;
    font-size: var(--t-body);
}

.button-secondary {
    background: transparent;
    color: var(--accent);
}

/* .button-ghost and .button-danger are defined with their §6 contract names
   in the component block below. */

/* Law 3 — the destructive action sits alone at the far left of the row, as
   far from the primary as the row allows. Placing it first in the markup
   gives the right result in both directions: far left when the row is
   horizontal, and top of the stack — hardest to reach — once it wraps. On
   the stacked tier each button is full width, so the auto margin is inert. */
/* Compound selector on purpose. The destructive control is usually wrapped in
   an .inline-form, and `.inline-form { margin: 0 }` is declared later in this
   file — at equal specificity that shorthand reset the auto margin and the
   delete button snapped back beside the primary, which is exactly the
   placement Law 3 exists to prevent. */
.actions > .actions__destructive,
.actions__destructive {
    margin-right: auto;
}

/* ---------------------------------------------------------------------------
   Frontend Style Plan Law 7 and §9 — accessibility floor.
   --------------------------------------------------------------------------- */

/* Law 7 — every interactive element gets a 44x44px hit area, "not negotiable
   on any screen size". min-height rather than padding so it cannot be undone
   by a component that sets its own padding. */
.button,
.site-nav__toggle,
.recipe-library__filters-toggle,
.nav-menu--account summary,
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
select,
textarea {
    min-height: 44px;
}

/* Small buttons keep their tighter type and padding but not a smaller target. */
.button-small {
    min-height: 44px;
}

/* Checkboxes and radios are too small to hit reliably, and their labels are
   the real target — so the label carries the height and the control grows. */
input[type="checkbox"],
input[type="radio"] {
    width: 1.15rem;
    height: 1.15rem;
}

.recipe-form__checkbox-label,
.grocery-list-builder__status-option {
    min-height: 44px;
}

/* §9 — keyboard focus must be visible on every interactive element. There was
   no focus style at all, so this relied entirely on the browser default.
   :focus-visible keeps it off pointer interactions. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--r-sm);
}

/* §9 — prefers-reduced-motion removes transitions and animation. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ---------------------------------------------------------------------------
   Frontend Style Plan §6 — component contracts.
   The plan names these with a `--` modifier convention that the existing
   markup does not use. Rather than rename every call site, the contract name
   is declared alongside the name already in the views, so both resolve to one
   rule and new markup can use the documented name.
   --------------------------------------------------------------------------- */

/* §6 .button--ghost — transparent with a --c-line border, the default for
   secondary actions. */
.button--ghost,
.button-ghost {
    background: transparent;
    border-color: transparent;
    color: var(--accent);
}

.button--ghost:hover,
.button-ghost:hover {
    background: var(--surface-muted);
}

.button--danger,
.button-danger {
    background: transparent;
    border-color: var(--c-clay-border);
    color: var(--c-clay);
}

.button--danger:hover,
.button-danger:hover {
    background: var(--c-clay-surface);
    border-color: var(--c-clay);
}

/* §6 .card--sunk — the sunk surface, no shadow, for nesting inside a card.
   `.card .card` already did this implicitly; naming it makes the intent
   explicit and stops the nesting depth being the thing that carries meaning.
   Never nest a third level. */
.card--sunk,
.card .card {
    background: var(--input-surface);
    box-shadow: none;
}

/* §6 .badge — pill, state trio only, never --accent. --accent means "you can
   click this"; a badge is never clickable. */
.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    padding: 0.1rem 0.5rem;
    border-radius: var(--r-pill);
    border: 1px solid var(--border);
    background: var(--surface-muted);
    color: var(--muted);
    font-size: var(--t-eyebrow);
    font-weight: 600;
    vertical-align: middle;
}

.badge--current,
.badge--active {
    background: var(--c-sage-surface);
    border-color: var(--c-sage-border);
    color: var(--c-sage);
}

.badge--attention,
.badge--draft {
    background: var(--c-amber-surface);
    border-color: var(--c-amber-border);
    color: var(--c-amber);
}

.badge--error {
    background: var(--c-clay-surface);
    border-color: var(--c-clay-border);
    color: var(--c-clay);
}

/* §6 .field — label above, input, hint below, error below hint. */
.field,
.field__hint,
/* Errors are text, never colour alone (§6). */
.field__error {
    display: flex;
    align-items: flex-start;
    gap: var(--s-1);
    color: var(--c-clay);
    font-size: var(--t-small);
    font-weight: 600;
}

.field__error::before {
    content: "!";
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 1.1em;
    height: 1.1em;
    border-radius: var(--r-pill);
    border: 1px solid currentColor;
    font-size: 0.85em;
    line-height: 1;
}

/* §6 .empty — icon, one sentence naming what belongs here, one primary
   action. Never a bare "No results". */
.empty {
    display: grid;
    justify-items: center;
    gap: var(--s-3);
    padding: var(--s-6) var(--s-4);
    text-align: center;
    color: var(--muted);
}

.empty__icon {
    font-size: var(--t-display);
    line-height: 1;
}

.empty__title {
    color: var(--text);
    font-size: var(--t-section);
    font-weight: 600;
}

.empty__body {
    margin: 0;
    max-width: 42ch;
}

.empty .actions {
    justify-content: center;
    margin-top: var(--s-2);
}

.theme-preference-form {
    display: grid;
    gap: var(--s-4);
}

.inline-form {
    margin: 0;
}

.notice,
.alert,
.error-message,
.form-error {
    margin-top: 1rem;
    padding: 0.85rem 1rem;
    border-radius: var(--r-md);
    border: 1px solid var(--border);
}

.notice {
    background: var(--background);
}

.alert,
.error-message,
.form-error {
    display: grid;
    gap: var(--s-1);
    border-width: 2px;
    border-left-width: 0.5rem;
    font-weight: 600;
    line-height: 1.45;
}

.alert--error,
.error-message,
.form-error {
    background: var(--alert-error-surface);
    border-color: var(--alert-error-border);
    color: var(--alert-error-text);
    box-shadow: 0 0 0 3px var(--alert-error-shadow);
}

.alert__title {
    font-size: var(--t-eyebrow);
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.form-stack {
    display: grid;
    gap: var(--s-4);
}

.form-required,
.form-field__error {
    font-size: var(--t-small);
}

.form-required {
    color: var(--muted);
    font-weight: 400;
}

.form-field__error {
    color: var(--alert-error-text);
    font-weight: 600;
}

.workflow-summary {
    display: grid;
    gap: var(--s-1);
    color: var(--muted);
}

.stack-list {
    list-style: none;
    padding-left: 0;
    margin: 0;
    display: grid;
    gap: var(--s-3);
}

/* The three libraries use the same denser browsing rhythm without changing
   cards on forms, dashboards, or detail pages. Interactive targets retain the
   global 44px minimum below. */
.stack-list.library-list {
    gap: var(--s-2);
}

.library-list .stack-list__item {
    gap: var(--s-1);
    padding: var(--s-2) var(--s-3);
}

.library-results > h2 {
    margin-bottom: var(--s-2);
}

.grocery-detail-list {
    margin: 0;
    padding-left: 1.25rem;
    display: grid;
    gap: var(--s-3);
}

.stack-list__item {
    display: grid;
    gap: var(--s-2);
    padding: 0.9rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--background);
}

.stack-list__item--highlight {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 5%, var(--background));
    box-shadow: 0 0 0 1px var(--accent);
}

.grocery-detail-list__item {
    display: grid;
    gap: var(--s-1);
}

.stack-list__headline,
.stack-list__meta,
.grocery-print__summary {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-1) var(--s-2);
    align-items: baseline;
}

.stack-list__meta,
.stack-list__note {
    color: var(--muted);
}

/* §9 Law 6 — a list-card title clamps to two lines and carries a title
   attribute, so a 90-character recipe name cannot push the badge beside it out
   of the card. min-width: 0 is what lets the flex child actually shrink. */
.stack-list__title {
    min-width: 0;
}

/* The title is the only way into a library result, so its link owns a full
   touch target even in the dense list. Clamping lives on the link itself so
   expanding the hit area cannot reintroduce long-title overflow at 320px. */
.library-list .stack-list__title a {
    display: -webkit-box;
    min-width: 44px;
    min-height: 44px;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.grocery-detail-list__line {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--s-1) var(--s-3);
    align-items: baseline;
}

.grocery-detail-list__summary {
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-1) var(--s-2);
    align-items: baseline;
}

.grocery-detail-list__quantity,
.grocery-detail-list__note {
    color: var(--muted);
}

.grocery-detail-list__status {
    justify-self: end;
    text-align: right;
}

.grocery-detail-list__note {
    margin: 0;
}

.recipe-import-progress {
    display: grid;
    gap: var(--s-2);
    padding: 1rem;
    border-radius: var(--r-lg);
    border: 1px solid var(--border);
    background: color-mix(in srgb, var(--accent) 10%, var(--surface));
}

.recipe-generation-payload {
    margin: 0;
    max-height: 32rem;
    overflow: auto;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.recipe-import-progress[hidden] {
    display: none;
}

.recipe-import-progress__bar {
    position: relative;
    overflow: hidden;
    width: 100%;
    height: 0.7rem;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--accent) 18%, var(--surface));
}

/* --progress is the one per-request value the view sets inline. The finished
   recipes are solid up to it; the work still to come sweeps from it to the end
   of the track, so the bar reads as "this much is done, and the next one is
   being written" instead of going still between steps. */
.recipe-import-progress__bar-fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--progress, 0%);
    border-radius: inherit;
    background: linear-gradient(90deg, transparent 0%, var(--accent) 30%, var(--accent) 100%);
    transition: width 180ms ease;
}

.recipe-import-progress__bar-fill--indeterminate {
    left: -40%;
    width: 40%;
    animation: recipe-import-progress-slide 1.1s ease-in-out infinite;
}

.recipe-import-progress__bar-fill--paused {
    left: 0;
    min-width: 0.75rem;
    animation: none;
    opacity: 0.65;
}

/* The unfinished remainder of the track. The bar clips it, so it needs no
   radius of its own. */
.recipe-import-progress__bar-pending {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: var(--progress, 0%);
    overflow: hidden;
    transition: left 180ms ease;
}

.recipe-import-progress__bar-sweep {
    position: absolute;
    top: 0;
    bottom: 0;
    left: -45%;
    width: 45%;
    background: linear-gradient(
        90deg,
        transparent 0%,
        color-mix(in srgb, var(--accent) 45%, transparent) 50%,
        transparent 100%
    );
    animation: recipe-import-progress-sweep 1.5s ease-in-out infinite;
}

/* A stalled job is not thinking, so the remainder stops moving and dims to
   match the paused fill. */
.recipe-import-progress__bar-pending--paused .recipe-import-progress__bar-sweep {
    animation: none;
    opacity: 0.65;
}

@keyframes recipe-import-progress-slide {
    0% {
        transform: translateX(0);
    }

    100% {
        transform: translateX(350%);
    }
}

/* The sweep is 45% of the remainder and crosses it plus its own width, so it
   travels (100 + 45) / 45 of itself. */
@keyframes recipe-import-progress-sweep {
    0% {
        transform: translateX(0);
    }

    100% {
        transform: translateX(322%);
    }
}

.form-field > span:first-child {
    display: inline-block;
    max-width: 100%;
    padding: 0;
    border: none;
    border-radius: 0;
    background: transparent;
    color: var(--text);
    font-weight: 600;
    font-size: var(--t-body);
}

/* Law 4's rare opt-out: a genuinely tiny, always-inline pair (e.g. a
   stepper) that should never stack, combined with .actions in markup. */
.actions--inline {
    align-items: center;
}

.actions--inline > :where(.button),
.actions--inline > .inline-form > :where(.button) {
    flex: 0 1 auto;
}

.actions--inline > .inline-form > :where(.button) {
    width: auto;
}

.admin-grid {
    display: grid;
    gap: var(--s-5);
}

.repair-diff {
    display: grid;
    gap: var(--s-4);
}

.repair-diff__summary {
    color: var(--muted);
}

.repair-review__header {
    padding: var(--s-2) 0;
}

.repair-review__header h2 {
    margin: 0;
}

.repair-review__findings {
    display: grid;
    gap: var(--s-1);
    margin: 0 0 var(--s-4);
    padding: var(--s-3) var(--s-4);
    color: var(--muted);
    background: var(--surface-muted);
    border-left: 3px solid var(--c-clay);
    border-radius: var(--r-sm);
}

.repair-review__findings strong {
    color: var(--text);
}

.repair-review__findings ul {
    margin: var(--s-1) 0 0;
}

.repair-diff__legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    margin: 0;
    color: var(--muted);
    font-size: 0.9rem;
}

.repair-diff__legend--added {
    color: var(--c-sage);
}

.repair-diff__legend--removed {
    color: var(--c-clay);
}

.repair-review__validation {
    padding: var(--s-2) var(--s-3);
    color: var(--muted);
    background: var(--surface-muted);
    border-radius: var(--r-sm);
}

.repair-review__actions {
    display: grid;
    gap: var(--s-3);
    padding-top: var(--s-4);
    border-top: 1px solid var(--border);
}

.repair-review__actions p {
    margin: 0;
    color: var(--muted);
}

.repair-diff__row,
.repair-diff__unchanged {
    display: flex;
    gap: var(--s-2);
    align-items: flex-start;
    padding: var(--s-2) var(--s-3);
    border-radius: var(--r-sm);
    overflow-wrap: anywhere;
}

.repair-diff__row--removed {
    color: var(--c-clay);
    background: var(--c-clay-surface);
    border: 1px solid var(--c-clay-border);
    text-decoration: line-through;
}

.repair-diff__row--added {
    color: var(--c-sage);
    background: var(--c-sage-surface);
    border: 1px solid var(--c-sage-border);
}

.repair-diff__marker {
    flex: 0 0 1rem;
    font-weight: 700;
    text-decoration: none;
}

.repair-diff__unchanged {
    color: var(--muted);
}

.repair-recipe__identity {
    display: grid;
    gap: var(--s-2);
}

.repair-recipe__title {
    font-size: var(--t-title);
    font-weight: 700;
}

.repair-recipe__summary {
    color: var(--muted);
}

.repair-recipe__notes {
    white-space: pre-wrap;
}

.repair-recipe__facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-4);
    color: var(--muted);
}

.repair-recipe__fact {
    display: inline-flex;
    gap: var(--s-1);
    align-items: baseline;
}

.repair-recipe__section {
    display: grid;
    gap: var(--s-2);
    padding-top: var(--s-4);
    border-top: 1px solid var(--border);
}

.repair-recipe__section h4 {
    margin: 0;
    font-size: var(--t-section);
}

.admin-grid__stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: var(--s-4);
}

.admin-grid__stat {
    display: grid;
    gap: var(--s-1);
    padding: 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--background);
}

.admin-grid__stat-value {
    font-size: var(--t-title);
    font-weight: 700;
}

.admin-grid__stat-label {
    color: var(--muted);
}

.admin-list {
    list-style: none;
    padding-left: 0;
    margin: 0;
    display: grid;
    gap: var(--s-4);
}

.admin-list__item {
    display: grid;
    gap: var(--s-4);
    padding: 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--background);
}

.admin-list__header {
    display: grid;
    gap: var(--s-1);
}

.admin-list__meta {
    color: var(--muted);
}

.admin-list__controls {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    align-items: end;
}

.admin-list__controls .form-field {
    min-width: 12rem;
    flex: 1 1 12rem;
}

.admin-list__controls :where(.button) {
    width: auto;
}

.form-field input,
.form-field select,
.form-field textarea,
.search-form input {
    /* §9 wants 3:1 on a control boundary. --border is a decorative divider
       and measures ~1.4:1, so inputs use the stronger token. */
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    padding: 0.7rem 0.85rem;
    font: inherit;
    background: var(--input-surface);
    color: var(--text);
}

.form-field input,
.form-field select,
.form-field textarea {
    width: 100%;
}

.form-field textarea {
    min-height: 6rem;
    resize: vertical;
}

/* Autocomplete */
.autocomplete {
    position: relative;
    width: 100%;
}

.autocomplete-results {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1000;
    margin: 0.25rem 0 0 0;
    padding: 0;
    list-style: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    box-shadow: var(--e-raised);
    max-height: 300px;
    overflow-y: auto;
    display: none;
}

.autocomplete-results:not(:empty) {
    display: block;
}

.autocomplete-result {
    padding: 0.75rem 1rem;
    cursor: pointer;
    border-bottom: 1px solid var(--border);
    font-size: var(--t-body);
}

.autocomplete-result:last-child {
    border-bottom: none;
}

.autocomplete-result:hover,
.autocomplete-result.is-selected {
    background: var(--surface-muted);
    color: var(--accent);
}

.recipe-form {
    gap: var(--s-5);
    min-width: 0;
}

.recipe-form__header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--s-5);
    margin-top: 1.5rem;
}

.recipe-form__header-main {
    display: grid;
    gap: var(--s-1);
}

.recipe-form__import-notice {
    color: var(--muted);
    font-size: var(--t-body);
    margin: 0;
}

.recipe-form__header-grid {
    display: grid;
    gap: var(--s-5);
    margin-top: 1.25rem;
}

.recipe-form__header-grid .card {
    margin-top: 0;
}

@media (min-width: 60rem) {
    .recipe-form__header-grid {
        grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
        align-items: stretch;
    }
}

.recipe-form__title {
    margin: 0;
    font-size: var(--t-title);
}

.recipe-form__section {
    display: grid;
    gap: var(--s-4);
    min-width: 0;
    padding: 1.2rem;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--e-card);
}

.recipe-form__variants-card {
    min-width: 0;
}

.recipe-form__details {
    display: grid;
    gap: var(--s-4);
    min-width: 0;
}

.recipe-form__details-summary {
    display: flex;
    justify-content: space-between;
    align-items: start;
    gap: var(--s-4);
    cursor: pointer;
    list-style: none;
}

.recipe-form__details-summary::-webkit-details-marker {
    display: none;
}

.recipe-form__details-summary::after {
    content: 'Show';
    flex: 0 0 auto;
    padding: 0.35rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: var(--r-pill);
    background: var(--surface-muted);
    color: var(--muted);
    font-size: var(--t-small);
    font-weight: 600;
}

.recipe-form__details[open] .recipe-form__details-summary::after {
    content: 'Hide';
}

.recipe-form__details-copy,
.recipe-form__details-panel {
    display: grid;
    gap: var(--s-1);
    min-width: 0;
}

.recipe-form__details-title {
    font-size: var(--t-section);
    font-weight: 700;
}

.recipe-form__details-description {
    color: var(--muted);
    line-height: 1.5;
}

/* Generation diagnostics — the panels behind "Generation technical details"
   and its sibling disclosures. Their contents are the most technical copy on
   the site, so they get the most structure: a sunk card for the cost numbers,
   and one bordered row per log entry instead of a bare numbered list. */
.generation-diagnostics__meta {
    display: grid;
    gap: var(--s-4);
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    padding: var(--s-4);
}

.generation-diagnostics__meta > div {
    display: grid;
    gap: var(--s-1);
    min-width: 0;
}

.generation-diagnostics__meta strong {
    font-size: var(--t-section);
    font-variant-numeric: tabular-nums;
}

.generation-diagnostics__group {
    display: grid;
    gap: var(--s-2);
    min-width: 0;
}

.generation-diagnostics__list {
    display: grid;
    gap: var(--s-2);
    margin: 0;
    padding: 0;
    list-style: none;
    min-width: 0;
}

.generation-diagnostics__entry {
    display: grid;
    gap: var(--s-1);
    /* Beats the mobile `li + li` rule, which would double the grid gap. */
    margin-top: 0;
    padding: var(--s-3) var(--s-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--input-surface);
    min-width: 0;
    overflow-wrap: anywhere;
}

.generation-diagnostics__entry-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
}

.generation-diagnostics__time {
    color: var(--muted);
    font-size: var(--t-small);
    font-variant-numeric: tabular-nums;
}

.recipe-form__section-header {
    display: grid;
    gap: var(--s-1);
    min-width: 0;
}

.recipe-form__section-header h3 {
    margin: 0;
}

.recipe-form__section-header p {
    margin: 0;
    color: var(--muted);
}

.recipe-form__helper {
    font-size: var(--t-body);
}

.recipe-form__grid,
.recipe-form__steps {
    display: grid;
    gap: var(--s-4);
    min-width: 0;
}

.recipe-form__toolbar {
    display: flex;
    justify-content: flex-end;
}

.recipe-form__field {
    margin: 0;
    min-width: 0;
}

.recipe-form__checkbox-field {
    display: block;
}

.recipe-form__checkbox-label {
    display: inline-flex;
    align-items: flex-start;
    gap: var(--s-3);
    max-width: 100%;
}

.recipe-form__checkbox-label > span {
    min-width: 0;
}

.recipe-form__checkbox-label input {
    width: auto;
    margin-top: 0.2rem;
}

.recipe-form__checkbox-label strong,
.recipe-form__checkbox-label small {
    display: block;
}

.recipe-form__photo-field {
    display: grid;
    gap: var(--s-4);
}

.recipe-form__image-preview,
.recipe-photo {
    display: grid;
    gap: var(--s-3);
}

.recipe-form__image-preview-header {
    display: grid;
    gap: var(--s-4);
    grid-template-columns: minmax(0, 1fr);
}

.recipe-form__image-preview-header .recipe-form__step-title {
    margin-bottom: 0;
}

.recipe-form__image-preview-body {
    display: grid;
    gap: var(--s-4);
    align-items: start;
    grid-template-columns: minmax(0, 1fr);
}

.recipe-form__photo-remove-toggle {
    margin: 0;
    align-self: center;
}

.recipe-form__photo-remove-toggle .recipe-form__checkbox-label {
    justify-content: flex-start;
    text-align: left;
}

.recipe-form__photo-remove-toggle strong {
    color: var(--text);
}

.recipe-form__image-preview img,
.recipe-photo img {
    width: 100%;
    max-width: 26rem;
    border-radius: var(--r-lg);
    border: 1px solid var(--border);
    background: var(--surface);
}

.recipe-photo figcaption {
    color: var(--muted);
    font-size: var(--t-body);
}

.recipe-form__collection {
    display: grid;
    gap: var(--s-4);
}

.recipe-form__step {
    display: grid;
    gap: var(--s-3);
    background: var(--background);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 1rem;
}

.recipe-form__step-title {
    display: inline-flex;
    margin-bottom: 0.85rem;
    font-weight: 600;
    color: var(--muted);
}

.recipe-form__collection-header {
    display: flex;
    gap: var(--s-4);
    justify-content: space-between;
    align-items: flex-start;
}

.recipe-form__collection-header h4 {
    margin: 0;
}

.recipe-form__collection-header p {
    margin: 0.35rem 0 0;
    color: var(--muted);
}

.recipe-form__instruction-column {
    display: grid;
    gap: var(--s-4);
    align-content: start;
}

.recipe-form__instruction-sections {
    display: grid;
    gap: var(--s-5);
}

.recipe-form__ingredients-table-wrapper {
    overflow-x: auto;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
}

.recipe-form__ingredients-table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}

.recipe-form__ingredients-col--label {
    width: 3.5rem;
}

.recipe-form__ingredients-col--quantity {
    width: 7rem;
}

.recipe-form__ingredients-col--unit {
    width: 6rem;
}

.recipe-form__ingredients-col--actions {
    width: 3.5rem;
}

.recipe-form__ingredients-table th,
.recipe-form__ingredients-table td {
    padding: 0.85rem;
    border-bottom: 1px solid var(--border);
    text-align: left;
    vertical-align: middle;
}

.recipe-form__ingredients-table .recipe-form__row-label,
.recipe-form__ingredients-table .recipe-form__header--label,
.recipe-form__ingredients-table .recipe-form__header--action,
.recipe-form__ingredients-table .recipe-form__row-actions {
    text-align: center;
}

.recipe-form__row-actions .button {
    min-width: 0;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}

@media (min-width: 40rem) {
    .actions > :where(.button),
    .actions > .inline-form {
        flex: 0 1 auto;
    }

    .actions > .inline-form > :where(.button) {
        width: auto;
    }

    .recipe-form__ingredients-table .button__icon {
        display: flex;
    }

    .recipe-form__ingredients-table .button__text {
        display: none;
    }

    .recipe-form__ingredients-table .button {
        padding: 0.5rem;
        line-height: 1;
    }
}

.recipe-form__ingredients-table thead th {
    font-size: var(--t-small);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
    background: rgba(148, 163, 184, 0.08);
}

.recipe-form__ingredients-table tbody th {
    font-weight: 600;
    color: var(--text);
    white-space: nowrap;
    background: var(--surface-muted);
}

.recipe-form__ingredients-table tbody td {
    background: rgba(255, 255, 255, 0.18);
}

.recipe-form__row-actions {
    white-space: nowrap;
    width: 1%;
}

.recipe-form__ingredients-table tbody tr:last-child th,
.recipe-form__ingredients-table tbody tr:last-child td {
    border-bottom: none;
}

.recipe-form__ingredients-table input {
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    padding: 0.7rem 0.85rem;
    font: inherit;
    background: var(--input-surface);
    color: var(--text);
    width: 100%;
    min-width: 0;
}

.recipe-form__preparation-input {
    min-width: 0;
}

.recipe-form__preparation-hint {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    width: fit-content;
    max-width: 100%;
    padding: 0.4rem 0.65rem;
    border-radius: var(--r-pill);
    background: var(--surface-muted);
    color: var(--muted);
    font-size: var(--t-small);
}

.recipe-form__preparation-hint strong {
    color: var(--text);
}

.recipe-form__preparation-hint-sample {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: min(100%, 22rem);
}

.recipe-form__collection-card {
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 1rem;
    background: var(--input-surface);
}

.recipe-form__step-title {
    margin-bottom: 0.5rem;
}

.recipe-form__add-button,
.recipe-form__remove-button {
    width: auto;
    justify-self: start;
}

.recipe-form__step-remove-button {
    justify-self: end;
}

.role-badge {
    display: inline-flex;
    padding: 0.35rem 0.75rem;
    border-radius: var(--r-pill);
    border: 1px solid var(--border);
    margin-top: 0.75rem;
}

.meal-plan-builder__hint {
    margin-top: -0.25rem;
    color: var(--muted);
}

.meal-plan-builder__day-groups {
    display: grid;
    gap: var(--s-6);
}

.meal-plan-builder__day-date-group {
    display: flex;
    gap: var(--s-3);
    align-items: flex-start;
    justify-content: flex-start;
}

.meal-plan-builder__date-input {
    flex: 1;
    min-width: 0;
}

.meal-plan-builder__date-input:not([value]),
.meal-plan-builder__date-input[value=""] {
    opacity: 0.5;
}

[data-meal-plan-start]:not([value=""]) ~ .meal-plan-builder__day-groups .meal-plan-builder__date-input,
[data-meal-plan-start]:not(:placeholder-shown) ~ .meal-plan-builder__day-groups .meal-plan-builder__date-input {
    opacity: 1;
}

.meal-plan-builder__day-input {
    width: 4rem;
    flex-shrink: 0;
}

.meal-plan-builder__recipe-group {
    display: flex;
    gap: var(--s-2);
}

.meal-plan-builder__recipe-group input {
    flex-grow: 1;
}

.meal-plan-builder__privacy-badge {
    font-size: var(--t-eyebrow);
    padding: 0.1rem 0.4rem;
    border-radius: var(--r-sm);
    background: var(--surface-muted);
    color: var(--muted);
    border: 1px solid var(--border);
    margin-left: 0.5rem;
    vertical-align: middle;
}

.meal-plan-builder__privacy-badge--current {
    background: var(--c-sage-surface);
    color: var(--c-sage);
    border-color: var(--c-sage-border);
}

.meal-plan-builder__section,
.grocery-list-builder__section {
    display: grid;
    gap: var(--s-4);
}

.meal-plan-builder__section-header,
.grocery-list-builder__section-header,
.recipe-title-bar {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--s-4);
    flex-wrap: wrap;
}

.meal-plan-builder__section-shell,
.grocery-list-builder__section-shell {
    display: grid;
    gap: var(--s-4);
}

.meal-plan-builder__section-actions,
.grocery-list-builder__section-actions {
    margin-left: auto;
    justify-content: flex-end;
    flex-shrink: 0;
}

/* Law 6: text never moves a control. The heading/description column must be
   able to shrink below its content size so a long title wraps instead of
   pushing the actions column out of the row. */
.meal-plan-builder__section-header > div,
.grocery-list-builder__section-header > div {
    min-width: 0;
}

.meal-plan-builder__section-header > div h3,
.grocery-list-builder__section-header > div h3 {
    overflow-wrap: anywhere;
}

.meal-plan-builder__section-header > div > :last-child,
.grocery-list-builder__section-header > div > :last-child,
.recipe-title-bar__content > :last-child {
    margin-bottom: 0;
}

.meal-plan-builder__entry {
    gap: var(--s-4);
}

.meal-plan-builder__day-header {
    display: flex;
    justify-content: flex-start;
    align-items: flex-end;
    gap: var(--s-4);
    margin-bottom: 0.5rem;
}

.meal-plan-builder__day-meals {
    display: grid;
    gap: var(--s-4);
}

.meal-plan-builder__day-group.card {
    background: var(--surface);
    border-color: var(--border);
    box-shadow: var(--e-card);
}

.meal-plan-builder__day-groups .meal-plan-builder__entry {
    background: var(--surface-muted);
}

.card.grocery-list-builder__section-shell {
    background: var(--surface-muted);
    border-color: var(--surface-strong);
}

.meal-plan-builder__entry-grid {
    display: grid;
    gap: var(--s-4);
    grid-template-columns: 1fr auto;
}

.meal-plan-builder__slot-field {
    grid-column: 1 / -1;
}

.meal-plan-builder__protein-field {
    grid-column: 1;
}

.meal-plan-builder__randomize-field {
    grid-column: 2;
    align-self: end;
}

.meal-plan-builder__recipe-field {
    grid-column: 1 / -1;
}

.meal-plan-builder__randomize-field .button {
    width: auto;
    padding-left: 0.75rem;
    padding-right: 0.75rem;
}

.form-field__label-spacer {
    display: none;
}

.meal-plan-builder__entry-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 0.5rem;
}

.meal-plan-builder__day-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 1.5rem;
}

.grocery-list-builder__toolbar,
.grocery-list-builder__categories,
.grocery-list-builder__category,
.grocery-list-builder__category-items {
    display: grid;
    gap: var(--s-4);
}

.grocery-list-builder__toolbar {
    grid-template-columns: minmax(0, 1fr);
    align-items: end;
    width: 100%;
    min-width: 0;
}

.grocery-list-builder__add-category-field,
.grocery-list-builder__custom-category-field,
.grocery-list-builder__field,
.grocery-list-builder__category-editor > .form-field {
    margin-bottom: 0;
}

.grocery-list-builder__custom-category-field--hidden,
.grocery-list-builder__empty-state--hidden {
    display: none;
}

.grocery-list-builder__empty-state {
    margin: 0;
    color: var(--muted);
}

.card.grocery-list-builder__category {
    gap: var(--s-4);
    min-width: 0;
    background: var(--surface);
    border-color: var(--surface-strong);
}

.grocery-list-builder__category-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--s-4);
    flex-wrap: wrap;
}

.grocery-list-builder__category-title {
    margin: 0;
    flex: 0 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.grocery-list-builder__category-summary {
    flex: 0 1 auto;
    margin: 0;
    color: var(--muted);
}

.grocery-list-builder__category-header .actions {
    flex: 1 1 100%;
}

.grocery-list-builder__item {
    display: grid;
    gap: var(--s-4);
    padding: 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    min-width: 0;
    background: var(--input-surface);
}

.grocery-list-builder__item-row {
    display: grid;
    gap: var(--s-3);
    align-items: end;
    min-width: 0;
}

.grocery-list-builder__item-row--header {
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    align-items: start;
}

.grocery-list-builder__item-row--main {
    grid-template-columns: 1fr 1fr;
}

.grocery-list-builder__item-row--details {
    grid-template-columns: minmax(0, 1fr);
}

.grocery-list-builder__item-label {
    display: flex;
    align-items: flex-start;
    padding-top: 0.15rem;
    min-height: 100%;
}

.grocery-list-builder__row-label {
    font-weight: 700;
    color: var(--text);
}

.grocery-list-builder__field {
    min-width: 0;
}

.grocery-list-builder__field-label {
    display: block;
    margin-bottom: 0.35rem;
    font-size: var(--t-eyebrow);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

.grocery-list-builder__field input,
.grocery-list-builder__field select,
.grocery-list-builder__toolbar input,
.grocery-list-builder__toolbar select {
    width: 100%;
    min-width: 0;
}

.grocery-list-builder__field input,
.grocery-list-builder__field select {
    background: var(--background);
}

.grocery-list-builder__field--quantity input,
.grocery-list-builder__field--unit input {
    max-width: none;
}

.grocery-list-builder__field--name {
    grid-column: span 2;
}

.grocery-list-builder__field--status {
    min-width: 0;
}

.grocery-list-builder__status-options {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-4);
    min-height: 2.75rem;
    align-items: center;
}

.grocery-list-builder__status-option {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--t-body);
}

.grocery-list-builder__status-option input[type="checkbox"] {
    width: auto;
    margin: 0;
}

.grocery-list-builder__category-editor {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-3);
    min-width: 0;
    justify-self: end;
    width: 100%;
    max-width: 200px;
}

.grocery-list-builder__row-actions {
    display: flex;
    justify-content: flex-end;
    align-items: end;
    white-space: nowrap;
}

.grocery-list-builder__item-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--s-4);
    padding-top: 1rem;
    border-top: 1px dashed var(--border);
    flex-wrap: wrap;
}

.grocery-list-builder__item-actions :where(.button) {
    width: auto;
    flex: 0 1 auto;
}

.grocery-list-builder__toolbar .grocery-list-builder__section-actions {
    grid-column: 1 / -1;
    margin-left: 0;
    justify-content: flex-end;
    width: 100%;
}

.grocery-list-builder__row-actions :where(.button) {
    width: auto;
}

.recipe-form__collection-footer {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.5rem;
}

.recipe-form__step-actions {
    display: flex;
    justify-content: space-between;
    gap: var(--s-2);
    margin-top: 0.5rem;
}

.pagination-container {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-5);
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--border);
}

.library-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--s-3);
    padding: var(--s-3) 0;
}

.library-pagination--top {
    margin-bottom: var(--s-3);
    border-bottom: 1px solid var(--border);
}

.library-pagination--bottom {
    margin-top: var(--s-3);
    border-top: 1px solid var(--border);
}

.library-pagination__summary {
    flex: 1 0 100%;
    margin: 0;
    color: var(--muted);
}

.library-pagination__size {
    display: flex;
    flex: 0 0 auto;
    flex-wrap: nowrap;
    align-items: flex-end;
    gap: var(--s-2);
    min-width: 0;
}

.library-pagination__size label {
    display: grid;
    gap: var(--s-1);
    color: var(--muted);
    font-size: var(--t-small);
}

.library-pagination__size select,
.library-pagination__size .combo__control {
    width: calc(var(--s-6) * 2);
    min-width: 44px;
}

.library-pagination__size .button,
.recipe-form .recipe-form__row-actions .button {
    min-width: 44px;
}

/* The button component declares its own display value, so the browser's
   lower-priority default [hidden] rule cannot hide this fallback by itself. */
.library-pagination [data-submit-fallback][hidden] {
    display: none;
}

.library-pagination > .pagination {
    flex: 0 0 auto;
    min-width: 0;
    margin-left: auto;
    flex-wrap: nowrap;
}

.pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-1);
}

.pagination__link,
.pagination__current {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0 0.5rem;
    border-radius: var(--r-sm);
    font-size: var(--t-body);
    text-decoration: none;
    transition: all 0.2s ease;
}

.pagination__link {
    color: var(--text);
    background: var(--surface-muted);
}

.pagination__link:hover {
    background: var(--surface-strong);
    color: var(--accent);
}

.pagination__link--prev,
.pagination__link--next {
    flex: 0 0 44px;
    width: 44px;
    padding: 0;
    font-size: var(--t-section);
    font-weight: 500;
}

.pagination__current {
    background: var(--accent);
    color: var(--button-text);
    font-weight: 600;
}

.pagination__status {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    min-height: 44px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.library-pagination .pagination__status input[type="number"] {
    width: calc(var(--s-6) + var(--s-4));
    min-width: 44px;
    padding: 0 var(--s-1);
    text-align: center;
}

.pagination__dots {
    color: var(--muted);
    padding: 0 0.25rem;
}

.pagination__jump {
    display: flex;
    align-items: center;
    gap: var(--s-3);
}

.pagination__jump label {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--t-small);
    color: var(--muted);
}

.pagination__jump input[type="number"] {
    width: 4rem;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--input-surface);
    color: var(--text);
}

@media (min-width: 40rem) {
    /* .grocery-list-builder__toolbar and __item-row--main/--header keep the
       same 2-column shape used at the base mobile tier here, so no override
       is needed for them until the min-width: 60rem desktop tier below. */
    .grocery-list-builder__item-row--details {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* Flex rather than two fixed grid tracks, because the custom-category
       field beside this one is display:none unless "Custom" is chosen. The
       grid reserved its track anyway, so the category control was permanently
       half of a 200px cell — 94px, most of it padding, showing about three
       characters of "Refrigerated". Hidden siblings take no room in a flex
       row, so the common case gets the whole width. */
    .grocery-list-builder__category-editor {
        display: flex;
        flex-wrap: wrap;
    }

    /* 8rem is roughly "Refrigerated" plus the chevron. Two of these will not
       fit across a 200px cell, so when the custom field is showing they stack
       and both stay readable instead of both being cut to three letters. */
    .grocery-list-builder__category-editor > .form-field {
        flex: 1 1 8rem;
        min-width: 0;
    }

    .grocery-list-builder__item-label,
    .grocery-list-builder__row-actions {
        grid-column: span 2;
    }

    .grocery-list-builder__row-actions {
        justify-content: flex-start;
    }
}

.recipe-title-bar__content {
    display: grid;
    gap: var(--s-3);
    flex: 1 1 18rem;
    min-width: 0;
}

.recipe-title-bar__content h2,
.recipe-title-bar__content p {
    margin: 0;
    overflow-wrap: anywhere;
}

/* Narrow screens: the action group takes its own full-width row under the
   title. It must stay shrinkable here — Law 6's flex-shrink: 0 (applied at
   >=40rem below) pins the group to its content width, which on a phone is
   wider than the card and pushed the buttons past the card's right edge. */
.recipe-title-bar__actions {
    margin-top: 0;
    flex: 1 1 100%;
    min-width: 0;
}

@media (min-width: 40rem) {
    /* Law 6: once the group sits beside the title, a long title must not be
       able to squeeze it. */
    .recipe-title-bar__actions {
        flex: 0 0 auto;
    }
}

.meal-plan-builder__quick-add {
    margin-top: 1.5rem;
}

/* "Plan this recipe" on the recipe page — an occasional secondary action, so
   it reads as a toggle rather than a section. It previously sat in its own
   card with a --t-section heading, which gave it the same weight as the
   ingredients and instructions it sits between. */
/* Status on the left, the recipe's own toolbar on the right, in one slim row
   above a full-width title. */
.recipe-detail-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    flex-wrap: wrap;
}

.recipe-detail-topbar__actions {
    margin-top: 0;
    flex: 0 0 auto;
}

.recipe-detail-title {
    margin: var(--s-2) 0 0;
    overflow-wrap: anywhere;
}

/* Combobox: a text input with a chevron that opens the full list of known
   values. The popup borrows the .autocomplete-results shape so both dropdowns
   on the recipe form read as one control. */
.combo__control {
    position: relative;
    width: 100%;
}

/* The chevron sits inside the field, so the text needs to stop before it —
   and no further. Narrow fields have little enough room as it is, so a long
   value trails off rather than being cut mid-letter. */
.form-field .combo__input {
    padding-right: 2.25rem;
    text-overflow: ellipsis;
}

/* Narrow on purpose. A native select draws its arrow in about a sixth of an
   inch; a 44px button ate half of the grocery builder's 94px category field and
   left three characters visible. Law 7's 44px target is the field itself — the
   input is full width, 44px tall, and opens the list when clicked — so the
   chevron only has to be legible, not tappable. */
.combo__toggle {
    position: absolute;
    top: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 100%;
    padding: 0;
    border: none;
    border-radius: 0 var(--r-md) var(--r-md) 0;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
}

.combo__toggle:hover {
    color: var(--accent);
}

.combo__toggle svg {
    width: 1.15rem;
    height: 1.15rem;
    stroke: currentColor;
    transition: transform 0.15s ease;
}

.combo__input[aria-expanded="true"] ~ .combo__toggle svg {
    transform: rotate(180deg);
}

/* A <select> that the combobox script has taken over. It stays in the DOM
   holding the value — and stays a working native control until the script
   runs — so it is hidden rather than removed. */
.combo__source {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* The generated input is not inside .form-field in every view, so the field
   styling it would otherwise inherit is stated here. */
.combo__control > .combo__input {
    width: 100%;
    padding: 0.7rem 2.25rem 0.7rem 0.85rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    background: var(--input-surface);
    color: var(--text);
    font: inherit;
    min-height: 44px;
    text-overflow: ellipsis;
}

/* Short lists are click-to-open rather than type-to-filter, so the caret would
   be a lie. */
.combo__input[readonly] {
    cursor: pointer;
}

.combo__input:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.combo__option[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
}

.combo__list {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1000;
    margin: var(--s-1) 0 0 0;
    padding: 0;
    list-style: none;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    box-shadow: var(--e-raised);
    max-height: 300px;
    overflow-y: auto;
}

.combo__option {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: var(--s-2) var(--s-4);
    border-bottom: 1px solid var(--border);
    font-size: var(--t-body);
    cursor: pointer;
}

/* display:flex above would otherwise beat the user-agent rule for [hidden],
   which is how typing filters the list. */
.combo__option[hidden] {
    display: none;
}

.combo__option:last-child,
.combo__option.is-last {
    border-bottom: none;
}

.combo__option:hover,
.combo__option.is-active {
    background: var(--surface-muted);
    color: var(--accent);
}

/* Square icon-only button. Declared after Law 4's stacking rule so it keeps
   its own size instead of stretching to the full row on narrow screens. */
.button--icon {
    flex: 0 0 auto;
    width: 44px;
    min-width: 44px;
    padding: 0;
}

.button--icon svg {
    width: 1.3rem;
    height: 1.3rem;
}

/* Favourited reads as filled and accented, not merely outlined. */
.button--icon.is-active {
    border-color: var(--accent);
    background: var(--surface-muted);
    color: var(--accent);
}

/* The recipe's editor actions. The plan disclosure is flattened into this row
   by display: contents, so `order` places the toggle between Delete and Edit
   while its panel stays last in the markup and wraps to its own full line. */
.recipe-detail-actions {
    align-items: center;
}

.recipe-detail-actions .recipe-plan-quick-add {
    display: contents;
}

.recipe-detail-actions__delete {
    order: 1;
}

.recipe-detail-actions .recipe-plan-quick-add__toggle {
    order: 2;
    margin-top: 0;
}

.recipe-detail-actions__edit {
    order: 3;
}

.recipe-detail-actions .recipe-plan-quick-add__panel {
    order: 4;
    flex: 0 0 100%;
    max-width: 100%;
}

/* Chrome wraps everything after <summary> in a UA-generated
   ::details-content box, and display: contents on the <details> does not
   dissolve it — so that wrapper, not the panel, is the flex item. It
   defaults to order: 0, which sorted it ahead of the buttons and made the
   panel open *above* the row. Ordering the wrapper is the fix; engines
   without the pseudo-element fall back to the panel rule above. */
.recipe-detail-actions .recipe-plan-quick-add::details-content {
    order: 4;
    flex: 0 0 100%;
    max-width: 100%;
}

@media (min-width: 40rem) {
    /* Delete and Plan group left; Edit is pushed to the right edge. */
    .recipe-detail-actions__edit {
        margin-left: auto;
    }
}

.recipe-plan-quick-add {
    margin-top: var(--s-4);
}

.recipe-plan-quick-add__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    min-height: 44px;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: transparent;
    color: var(--muted);
    font-size: var(--t-small);
    font-weight: 600;
    cursor: pointer;
    list-style: none;
}

.recipe-plan-quick-add__toggle::-webkit-details-marker {
    display: none;
}

.recipe-plan-quick-add__toggle::before {
    content: "+";
    font-size: 1.1em;
    line-height: 1;
}

.recipe-plan-quick-add[open] .recipe-plan-quick-add__toggle::before {
    content: "−";
}

.recipe-plan-quick-add__toggle:hover {
    background: var(--surface-muted);
    color: var(--text);
}

.recipe-plan-quick-add__panel {
    display: grid;
    gap: var(--s-4);
    margin-top: var(--s-4);
    padding-left: var(--s-3);
    border-left: 2px solid var(--border);
}

.meal-plan-builder__quick-add-details {
    display: grid;
    gap: var(--s-4);
}

.meal-plan-builder__quick-add-details summary {
    cursor: pointer;
}

.meal-plan-builder__quick-add-summary {
    font-size: var(--t-section);
    font-weight: 700;
}

.meal-plan-builder__quick-add-details > :not(summary) {
    margin: 0;
}

.recipe-print {
    display: grid;
    gap: var(--s-5);
    margin-top: 1.5rem;
    padding: 1.75rem;
    background: var(--c-print-paper);
    color: var(--c-print-ink);
    border: 1px solid var(--c-print-line);
    border-radius: 10px;
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08);
}

.recipe-print__top {
    display: grid;
    gap: var(--s-5);
}

.recipe-print__headline {
    display: grid;
    gap: var(--s-3);
}

.recipe-print__title {
    margin: 0;
    font-size: var(--t-display);
    line-height: 1.1;
}

.recipe-print__lead,
.recipe-print__note,
.recipe-print__content section > :first-child {
    margin-top: 0;
}

.recipe-print__summary,
.recipe-print__ingredients,
.recipe-print__content {
    display: grid;
    gap: var(--s-3);
}

.recipe-print__summary-facts,
.recipe-print__ingredients ul,
.recipe-print__content ol {
    margin: 0;
}

.recipe-print__summary-facts {
    padding-left: 1.1rem;
}

.recipe-print__ingredients ul {
    padding-left: 2.5rem;
}

.recipe-print__summary-facts {
    display: grid;
    gap: var(--s-2) var(--s-4);
}

.recipe-print__summary-facts li:nth-child(odd) {
    margin-left: 1.4rem;
}

.recipe-print__summary-facts li + li {
    margin-top: 0;
}

.recipe-print__ingredients h2,
.recipe-print__content h2,
.recipe-print__content h3 {
    margin-bottom: 0;
}

.recipe-print__content {
    gap: var(--s-5);
}

.recipe-print__content section {
    display: grid;
    gap: var(--s-3);
}

.recipe-panel {
    display: grid;
    gap: var(--s-4);
}

.recipe-panel__header {
    display: grid;
    gap: var(--s-1);
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--border);
}

.recipe-panel__header h2 {
    margin: 0;
}

/* A header that carries a control opposite its title. Stacks below 40rem so the
   control never squeezes the heading (Law 6), side by side and centred above it. */
.recipe-panel__header--split {
    gap: var(--s-3);
    align-items: center;
}

.recipe-panel__heading {
    display: grid;
    gap: var(--s-1);
    min-width: 0;
}

/* Mobile-first: stacked and left-aligned under the heading, then side by side
   and right-aligned once there is room for both. */
@media (min-width: 40rem) {
    .recipe-panel__header--split {
        grid-template-columns: minmax(0, 1fr) auto;
    }

    .recipe-servings {
        justify-content: flex-end;
    }
}

.recipe-panel ul {
    margin: 0;
}

.recipe-source-value,
.recipe-reference-value {
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
}

.recipe-instructions {
    display: grid;
    gap: var(--s-4);
}

.recipe-instructions__group {
    display: grid;
    gap: var(--s-3);
}

.recipe-instructions__group h3,
.recipe-instructions__notes h3 {
    margin: 0;
}

/* §4.3 — the cooking-distance treatment. Read from three feet away with
   your hands in a bowl: larger type, generous line height, and a quiet
   margin numeral instead of a busy filled badge, so losing your place and
   finding it again costs nothing. */
.recipe-panel--ingredients ul {
    font-size: var(--t-cook);
    line-height: 1.7;
    font-variant-numeric: tabular-nums;
}

.recipe-panel--ingredients li + li {
    margin-top: var(--s-3);
}

/* The servings stepper sits in the panel header opposite the title. Every
   control is a direct flex child on one centre line, so the input and the
   buttons share a midline no matter which is taller. */
.recipe-servings {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
}

.recipe-servings__label,
.recipe-servings__suffix {
    color: var(--muted);
    white-space: nowrap;
}

.recipe-servings__input {
    width: 5rem;
    flex: 0 0 auto;
}

.recipe-servings__suffix {
    margin-right: var(--s-1);
}

.recipe-servings > .button {
    flex: 0 0 auto;
}

.recipe-instructions__list {
    margin: 0;
    padding: 0;
    list-style: none;
    counter-reset: recipe-step;
    display: grid;
    gap: var(--s-4);
}

.recipe-instructions__item {
    counter-increment: recipe-step;
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--s-3);
    align-items: start;
    padding: 0.8rem 0.9rem;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: linear-gradient(180deg, var(--surface-muted) 0%, var(--surface) 100%);
    font-size: var(--t-cook);
    line-height: 1.7;
}

.recipe-instructions__item::before {
    content: counter(recipe-step);
    display: inline-flex;
    align-items: flex-start;
    justify-content: center;
    min-width: 1.9rem;
    padding-top: 0.1em;
    font-family: var(--font-family-display);
    font-size: 1.75em;
    font-weight: 400;
    line-height: 1;
    color: var(--c-line);
    font-variant-numeric: tabular-nums;
}

.recipe-instructions__notes {
    padding-top: 0.25rem;
    border-top: 1px solid var(--border);
}

.recipe-instructions__notes p {
    margin-bottom: 0;
}

.status-grid {
    display: grid;
    gap: var(--s-4);
}

.account-home__card {
    background: var(--input-surface);
}

.account-home__form input,
.account-home__form select,
.account-home__form textarea {
    background: var(--background);
}

/* Mobile-first base for the sections below: narrow-screen values live here
   unconditionally; the min-width: 60rem tier near the end of this file
   restores the desktop table/card chrome these selectors had before the
   Frontend Style Plan §4.6 breakpoint consolidation. */
.nav-menu__panel {
    position: static;
    min-width: 0;
    width: 100%;
}

.card > .recipe-form {
    padding: 0;
    border-radius: 0;
    background: transparent;
}

.recipe-form__section {
    background: var(--surface-muted);
    box-shadow: none;
}

.recipe-form__ingredients-table-wrapper {
    overflow: visible;
    border: none;
    background: transparent;
}

.recipe-form__ingredients-table,
.recipe-form__ingredients-table thead,
.recipe-form__ingredients-table tbody,
.recipe-form__ingredients-table th,
.recipe-form__ingredients-table td {
    display: block;
    width: 100%;
}

.recipe-form__ingredients-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
}

.recipe-form__ingredients-table tbody {
    display: grid;
    gap: var(--s-4);
}

.recipe-form__ingredients-table tbody tr {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-4);
    min-width: 0;
    padding: 0.35rem;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
}

.recipe-form__ingredients-table tbody th,
.recipe-form__ingredients-table tbody td {
    padding: 0;
    border: none;
    background: transparent;
}

.recipe-form__row-label,
.recipe-form__ingredient-cell--name,
.recipe-form__ingredient-cell--preparation {
    grid-column: 1 / -1;
}

.recipe-form__row-actions--remove {
    grid-column: 1;
}

.recipe-form__row-actions--add {
    grid-column: 2;
}

.recipe-form__row-actions--remove::before,
.recipe-form__row-actions--add::before {
    display: none !important;
}

.recipe-form__ingredient-cell {
    display: grid;
    gap: var(--s-2);
}

.recipe-form__ingredient-cell::before,
.recipe-form__row-actions::before {
    content: attr(data-label);
    font-size: var(--t-eyebrow);
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--muted);
}

.recipe-form__row-actions {
    display: grid;
    gap: var(--s-2);
    justify-items: stretch;
}

.recipe-form__row-actions .button {
    width: 100%;
}

.grocery-list-builder__items-table-wrapper {
    overflow: visible;
    border: none;
    background: transparent;
}

.grocery-list-builder__items-table,
.grocery-list-builder__items-table thead,
.grocery-list-builder__items-table tbody,
.grocery-list-builder__items-table th,
.grocery-list-builder__items-table td {
    display: block;
    width: 100%;
}

.grocery-list-builder__items-table {
    min-width: 0;
}

.grocery-list-builder__items-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    border: 0;
}

.grocery-list-builder__items-table tbody {
    display: grid;
    gap: var(--s-4);
}

.grocery-list-builder__items-table tbody tr {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-4);
    min-width: 0;
    padding: 0.35rem;
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
}

.grocery-list-builder__items-table tbody th,
.grocery-list-builder__items-table tbody td {
    padding: 0;
    border: none;
    background: transparent;
}

.grocery-list-builder__row-label,
.grocery-list-builder__item-cell--name,
.grocery-list-builder__item-cell--notes,
.grocery-list-builder__item-cell--status,
.grocery-list-builder__row-actions {
    grid-column: 1 / -1;
}

.grocery-list-builder__item-cell,
.grocery-list-builder__row-actions {
    display: grid;
    gap: var(--s-2);
}

.grocery-list-builder__item-cell::before,
.grocery-list-builder__row-actions::before {
    content: attr(data-label);
    font-size: var(--t-eyebrow);
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--muted);
}

.grocery-list-builder__row-actions {
    justify-items: end;
}

.meal-plan-builder__section-shell,
.meal-plan-builder__entry,
.grocery-list-builder__section-shell,
.grocery-list-builder__category,
.grocery-list-builder__item {
    padding: 0.35rem;
}

.meal-plan-builder__section-shell,
.meal-plan-builder__entries,
.grocery-list-builder__toolbar,
.grocery-list-builder__categories,
.grocery-list-builder__category,
.grocery-list-builder__category-items {
    gap: var(--s-3);
}

.meal-plan-builder__section-header,
.grocery-list-builder__section-header {
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-3);
}

.grocery-list-builder__category-header {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    gap: var(--s-3);
    flex-wrap: wrap;
}

.meal-plan-builder__section-actions,
.grocery-list-builder__section-actions {
    width: 100%;
    margin-left: 0;
}

.meal-plan-builder__section-actions .button,
.grocery-list-builder__section-actions .button,
.grocery-list-builder__category-header .button,
.meal-plan-builder__entry-actions .button {
    width: 100%;
}

.recipe-form__section,
.recipe-form__details-summary,
.recipe-form__details-panel {
    gap: var(--s-3);
}

.recipe-form__section {
    padding: 0.35rem;
}

.recipe-form__instruction-sections {
    gap: var(--s-4);
}

.recipe-form__instruction-column.recipe-form__collection-card {
    padding: 0.35rem;
}

.recipe-form__steps {
    gap: var(--s-3);
}

.recipe-form__step {
    padding: 0.35rem;
    gap: var(--s-2);
}

.recipe-form__step textarea {
    min-height: 7.5rem;
}

.recipe-form__collection-header {
    flex-direction: column;
    gap: var(--s-3);
}

.recipe-form__collection-header .button {
    width: 100%;
}

.recipe-form__collection-card {
    padding: 0.35rem;
}

.recipe-form__details-summary {
    flex-direction: column;
}

.recipe-form__details-summary::after {
    align-self: flex-start;
}

ul {
    padding-left: 1.25rem;
}

li + li {
    margin-top: 0.55rem;
}

/* Desktop tier — Frontend Style Plan §4.6. Everything here must stay after
   every unconditional (mobile-first) rule above so it wins the cascade at
   min-width: 60rem, since matching declarations are otherwise resolved by
   source order. */
@media (min-width: 60rem) {
    .status-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .recipe-instructions {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
    }

    .recipe-print__summary-facts {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .recipe-print__ingredients ul {
        columns: 2;
        column-gap: var(--s-5);
    }

    .recipe-form__grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .recipe-form__grid--primary {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }

    .meal-plan-builder__entry-grid {
        grid-template-columns: auto 1fr auto;
    }

    .meal-plan-builder__slot-field,
    .meal-plan-builder__protein-field,
    .meal-plan-builder__randomize-field {
        grid-column: auto;
    }

    .meal-plan-builder__recipe-field {
        grid-column: 1 / -1;
    }

    .form-field__label-spacer {
        display: block;
        height: 1.2em;
        margin-bottom: 0.5rem;
    }

    .recipe-form__field--full {
        grid-column: 1 / -1;
    }

    .recipe-form__field--wide {
        grid-column: span 2;
    }

    .stack-list {
        gap: var(--s-4);
    }

    .nav-menu__panel {
        min-width: 14rem;
        position: absolute;
        width: auto;
    }

    /* Nav swap: hand off from the toggle-driven mobile nav to the inline
       desktop nav. */
    .site-nav.site-nav--desktop {
        display: flex;
        margin-left: auto;
    }

    .site-nav.site-nav--mobile {
        display: none;
    }

    .nav-menu {
        width: auto;
    }

    .page {
        padding: 2rem 1.5rem 3rem;
    }

    .site-header {
        flex-direction: row;
        align-items: center;
    }

    .site-header.card {
        gap: var(--s-4);
    }

    .card {
        padding: 1.4rem;
    }

    .card > .recipe-form {
        padding: 1.4rem;
        border-radius: var(--r-lg);
        background: var(--surface);
    }

    .recipe-form__section {
        background: var(--surface);
        box-shadow: var(--e-card);
        gap: var(--s-4);
        padding: 1.2rem;
    }

    .recipe-form__details-summary,
    .recipe-form__details-panel {
        gap: var(--s-4);
    }

    .recipe-form__details-panel {
        gap: var(--s-1);
    }

    .recipe-form__details-summary {
        flex-direction: row;
    }

    /* Variants keeps the standard .card padding — it was zero here, which is
       why the section rendered as a bare strip rather than a card. */

    .recipe-form__instruction-sections {
        gap: var(--s-5);
    }

    .recipe-form__instruction-column.recipe-form__collection-card {
        padding: 1rem;
    }

    .recipe-form__steps {
        gap: var(--s-4);
    }

    .recipe-form__step {
        padding: 1rem;
        gap: var(--s-3);
    }

    .recipe-form__collection-header {
        flex-direction: row;
        gap: var(--s-4);
    }

    .recipe-form__collection-header .button {
        width: auto;
    }

    .recipe-form__collection-card {
        padding: 1rem;
    }

    .grocery-list-builder__toolbar .grocery-list-builder__section-actions {
        grid-column: 3;
        margin-left: auto;
    }

    .grocery-list-builder__item {
        padding: 1rem;
    }

    .grocery-list-builder__item-row--main {
        grid-template-columns: minmax(0, 1fr) minmax(0, 8rem) minmax(0, 7rem);
    }

    .grocery-list-builder__item-row--details {
        grid-template-columns: minmax(0, 1.5fr) minmax(0, 120px) minmax(0, 1fr);
    }

    .grocery-list-builder__field--quantity input {
        max-width: 5.5rem;
    }

    .grocery-list-builder__field--unit input {
        max-width: 4.5rem;
    }

    .grocery-list-builder__field--name {
        grid-column: auto;
    }

    .meal-plan-builder__section-shell,
    .meal-plan-builder__entries,
    .grocery-list-builder__categories,
    .grocery-list-builder__category-items {
        gap: var(--s-4);
    }

    .meal-plan-builder__section-header,
    .grocery-list-builder__section-header {
        flex-direction: row;
        align-items: center;
    }

    .meal-plan-builder__section-actions,
    .grocery-list-builder__section-actions,
    .grocery-list-builder__category-header .button,
    .meal-plan-builder__entry-actions .button {
        width: auto;
    }

    /* Ingredients table: restore real <table> semantics and the desktop
       cell chrome that the mobile card layout above replaces. */
    .recipe-form__ingredients-table-wrapper {
        overflow-x: auto;
        overflow-y: visible;
        border: 1px solid var(--border);
        background: var(--surface);
    }

    .recipe-form__ingredients-table,
    .recipe-form__ingredients-table th,
    .recipe-form__ingredients-table td {
        width: auto;
    }

    .recipe-form__ingredients-table {
        display: table;
    }

    .recipe-form__ingredients-table thead {
        display: table-header-group;
        position: static;
        width: auto;
        height: auto;
        padding: 0;
        margin: 0;
        overflow: visible;
        clip: auto;
        border: 0;
    }

    .recipe-form__ingredients-table tbody {
        display: table-row-group;
    }

    .recipe-form__ingredients-table tbody tr {
        display: table-row;
        grid-template-columns: none;
        padding: 0;
        border: 0;
        border-radius: 0;
        background: none;
    }

    .recipe-form__ingredients-table th,
    .recipe-form__ingredients-table td {
        display: table-cell;
    }

    .recipe-form__ingredients-table tbody th,
    .recipe-form__ingredients-table tbody td {
        padding: 0.85rem;
        border-bottom: 1px solid var(--border);
    }

    .recipe-form__ingredients-table tbody th {
        background: var(--surface-muted);
    }

    .recipe-form__ingredients-table tbody td {
        background: rgba(255, 255, 255, 0.18);
    }

    .recipe-form__ingredient-cell::before,
    .recipe-form__row-actions::before {
        content: none;
    }

    /* Grocery items table: same table/card swap as above. */
    .grocery-list-builder__items-table-wrapper {
        overflow-x: auto;
        overflow-y: visible;
        border: 1px solid var(--border);
        background: var(--surface);
    }

    .grocery-list-builder__items-table,
    .grocery-list-builder__items-table th,
    .grocery-list-builder__items-table td {
        width: auto;
    }

    .grocery-list-builder__items-table {
        display: table;
    }

    .grocery-list-builder__items-table thead {
        display: table-header-group;
        position: static;
        width: auto;
        height: auto;
        padding: 0;
        margin: 0;
        overflow: visible;
        clip: auto;
        border: 0;
    }

    .grocery-list-builder__items-table tbody {
        display: table-row-group;
    }

    .grocery-list-builder__items-table tbody tr {
        display: table-row;
        grid-template-columns: none;
        padding: 0;
        border: 0;
        border-radius: 0;
        background: none;
    }

    .grocery-list-builder__items-table th,
    .grocery-list-builder__items-table td {
        display: table-cell;
    }

    .grocery-list-builder__items-table tbody th,
    .grocery-list-builder__items-table tbody td {
        padding: 0.85rem;
        border-bottom: 1px solid var(--border);
    }

    .grocery-list-builder__item-cell::before,
    .grocery-list-builder__row-actions::before {
        content: none;
    }
}

@media print {
    :root {
        --background: var(--c-print-paper);
        --surface: var(--c-print-paper);
        --text: var(--c-print-ink);
        --muted: var(--c-print-ink);
        --accent: var(--c-print-ink);
        --button-text: var(--c-print-ink);
        /* Deliberately paper, not a line: borders disappear into the page. */
        --border: var(--c-print-paper);
        --shadow: transparent;
    }

    body {
        background: var(--c-print-paper);
        color: var(--c-print-ink);
    }

    .page {
        padding: 0;
    }

    .shell,
    .shell--print {
        max-width: none;
    }

    .site-header,
    .actions,
    .button,
    .button-secondary,
    .button-small,
    .meal-plan-builder__quick-add,
    .recipe-photo,
    .recipe-photo + p {
        display: none !important;
    }

    .card,
    .recipe-print {
        margin-top: 0;
        padding: 0;
        background: var(--c-print-paper);
        border: none;
        border-radius: 0;
        box-shadow: none;
    }

    .recipe-print {
        gap: var(--s-4);
    }

    .recipe-print__top {
        display: grid;
        gap: var(--s-3);
        align-items: start;
    }

    .recipe-print__summary-facts {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .recipe-print__title {
        font-size: 22pt;
    }

    .recipe-print__summary-facts,
    .recipe-print__ingredients ul,
    .recipe-print__content ol,
    .recipe-print__content p {
        font-size: 10.5pt;
        line-height: 1.35;
    }

    .recipe-print__content ol,
    .recipe-print__summary-facts {
        padding-left: 1rem;
    }

    .recipe-print__summary-facts li:nth-child(odd) {
        margin-left: 1.5rem;
    }

    .recipe-print__ingredients ul {
        padding-left: 2.5rem;
        columns: 2;
        column-gap: var(--s-5);
    }

    .recipe-print__ingredients li {
        break-inside: avoid;
    }

    .recipe-print__content {
        gap: var(--s-3);
    }

    .recipe-print__content section {
        gap: var(--s-2);
        break-inside: avoid;
    }

    h1,
    h2,
    h3,
    p,
    ul,
    ol {
        color: var(--c-print-ink);
    }
}

.search-form {
    display: flex;
    gap: var(--s-2);
    flex-grow: 1;
    max-width: none;
    width: 100%;
    align-items: center;
}

.search-form input {
    flex-grow: 1;
    min-width: 0;
}

/* Frontend Style Plan Law 5 — the shared library controls band.
   Grid, not flex: the panel is promoted out of its <details> wrapper by
   display: contents below, and a flex-basis: 100% on a promoted item
   resolves against the wrong box (it measured 512px inside an 833px
   container), leaving the panel short. Grid line placement is absolute —
   `grid-column: 1 / -1` is the full row regardless of promotion.
   The search field owns a 1fr track so it absorbs all leftover width, which
   is what pins the button hard against the right edge; the button's track is
   `auto`, so it is only ever as wide as its own label. */
.library-controls {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-3);
    align-items: start;
}

@media (min-width: 40rem) {
    .library-controls {
        grid-template-columns: minmax(0, 1fr) auto;
    }
}

.library-controls .search-form {
    grid-column: 1;
    grid-row: 1;
    /* .search-form caps itself at 500px for the standalone case, which is a
       reading-measure limit for a lone input. Inside this band that cap is
       what made the field stop halfway across and left the buttons stranded
       mid-row, so it is explicitly lifted here. */
    max-width: none;
    width: auto;
}

/* The sort control shares the band with search: under it on narrow screens,
   beside it on wide ones, where the recipe library puts its filters toggle. */
.library-controls .library-sort {
    grid-column: 1;
    grid-row: 2;
    display: flex;
    align-items: center;
    gap: var(--s-2);
    min-width: 0;
}

.library-sort__label {
    color: var(--muted);
    white-space: nowrap;
}

@media (min-width: 40rem) {
    .library-controls .library-sort {
        grid-column: 2;
        grid-row: 1;
    }
}

/* The <details> generates no box, so its summary (the toggle button) and its
   content become independent grid items placed on the tracks above. The
   native open/closed behavior is unaffected — it keys off the [open]
   attribute on the DOM element, not on its box. */
.library-controls .recipe-form__details {
    display: contents;
}

/* Narrow: button and panel stack under the search field. */
.library-controls .recipe-form__details-panel {
    grid-column: 1;
    grid-row: 3;
    min-width: 0;
}

@media (min-width: 40rem) {
    .library-controls .recipe-form__details-panel {
        grid-column: 1 / -1;
        grid-row: 2;
    }
}

/* Chrome (and other engines implementing ::details-content) wrap everything
   after <summary> in a UA-generated box. `display: contents` on the <details>
   dissolves the <details> box but NOT that wrapper, so the wrapper — not the
   panel — is the real grid item, and it auto-placed into column 1. The panel
   inside it could then never exceed one column, which is why the open filter
   grid stopped short of the card's right edge. Placing the wrapper itself is
   the fix. Engines without this pseudo-element ignore the rule entirely and
   fall back to the panel rules above, where the panel IS the grid item. */
.library-controls .recipe-form__details::details-content {
    grid-column: 1;
    grid-row: 3;
    min-width: 0;
}

@media (min-width: 40rem) {
    .library-controls .recipe-form__details::details-content {
        grid-column: 1 / -1;
        grid-row: 2;
    }
}

/* The disclosure trigger is a single button — no separate "Filters" label —
   so its own text just says what clicking it will do. It sits directly
   beside the Search button, so it takes the same --r-md corner rather than
   the pill it would get as a standalone toggle. */
.recipe-library__filters-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    grid-column: 1;
    grid-row: 2;
    width: 100%;
    padding: 0.65rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface-muted);
    color: var(--muted);
    font-weight: 600;
    font-size: var(--t-small);
    cursor: pointer;
    list-style: none;
}

@media (min-width: 40rem) {
    .recipe-library__filters-toggle {
        grid-column: 2;
        grid-row: 1;
        width: max-content;
    }
}

.recipe-library__filters-toggle::-webkit-details-marker {
    display: none;
}

.recipe-library__filters-toggle-open {
    display: none;
}

.recipe-form__details[open] .recipe-library__filters-toggle-closed {
    display: none;
}

.recipe-form__details[open] .recipe-library__filters-toggle-open {
    display: inline;
}

@media (min-width: 40rem) {
    .search-form {
        max-width: 500px;
        width: auto;
    }
}
