/* ==========================================================================
   Miam design system.

   Everything is expressed through custom properties: a raw palette, then the
   semantic tokens the components actually use. Only the semantic block is
   redefined for the dark scheme, so a component is written once and themed
   for free.

   No external resource: system fonts, emoji, CSS gradients.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Reset
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

img,
svg {
    display: block;
    max-width: 100%;
}

button,
input,
select,
textarea {
    font: inherit;
    color: inherit;
}

[hidden] {
    display: none !important;
}

/* --------------------------------------------------------------------------
   2. Design tokens
   -------------------------------------------------------------------------- */

:root {
    /* Cream: page and surface backgrounds. */
    --cream-50: #fffaf3;
    --cream-100: #fdf4e8;
    --cream-200: #f8e9d6;
    --cream-300: #efdac1;
    --cream-400: #e3c6a5;

    /* Terracotta: the warm primary accent. */
    --terracotta-50: #fdeee7;
    --terracotta-100: #fadbcd;
    --terracotta-200: #f3b89f;
    --terracotta-300: #e8906d;
    --terracotta-400: #d96f48;
    --terracotta-500: #c2552f;
    --terracotta-600: #a44424;
    --terracotta-700: #83351b;

    /* Saffron: secondary accent, badges and highlights. */
    --saffron-50: #fdf5df;
    --saffron-100: #fbebbd;
    --saffron-200: #f6d786;
    --saffron-300: #eebd4a;
    --saffron-500: #b47f16;

    /* Sage: quiet accent, links and confirmations. */
    --sage-100: #d9e9e0;
    --sage-300: #85b39d;
    --sage-500: #43806a;
    --sage-600: #336352;

    /* Ink: text. */
    --ink-900: #2a1f18;
    --ink-700: #4a382c;
    --ink-500: #7a6455;
    --ink-300: #a4907f;

    /* Brick: gentle alerts. */
    --brick-100: #fbe0dd;
    --brick-500: #b4402f;
    --brick-700: #8d2f21;

    /* Radii. */
    --radius-xs: 6px;
    --radius-sm: 10px;
    --radius-md: 14px;
    --radius-lg: 20px;
    --radius-pill: 999px;

    /* Typography: system stack only, no web font is ever downloaded. */
    --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --size-xs: 0.75rem;
    --size-sm: 0.83rem;
    --size-md: 0.94rem;
    --size-base: 1rem;
    --size-lg: 1.13rem;
    --size-xl: 1.35rem;
    --size-2xl: 1.65rem;
    --size-3xl: 2rem;

    /* Spacing. */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;

    /* Layout. Nothing is reserved above the page: the cook mode, the only
       screen still carrying a header, raises the height further down. */
    --layout-width: 1100px;
    --header-height: 0px;
    /* The bottom bar carries icons alone, so it is as tall as a comfortable tap
       and nothing more. Everything reserving room for it reads this. */
    --nav-height: 48px;
    --nav-height-wide: 0px;

    /* Motion. A hover or a press is over almost before it is seen; a panel crossing the
       whole screen needs long enough for the eye to follow where it came from. */
    --ease: cubic-bezier(0.4, 0, 0.2, 1);
    --duration: 0.18s;
    --duration-panel: 0.26s;

    /* Semantic tokens: light scheme. */
    --surface-page: var(--cream-100);
    --surface-raised: #fffdfa;
    --surface-sunken: var(--cream-50);
    --surface-hover: var(--cream-200);

    --text-strong: var(--ink-900);
    --text-muted: var(--ink-500);
    --text-faint: var(--ink-300);
    --text-on-accent: #ffffff;

    --border: #e9d9c4;
    --border-strong: var(--cream-400);

    --accent: var(--terracotta-500);
    --accent-strong: var(--terracotta-600);
    --accent-soft: var(--terracotta-50);
    --accent-ring: rgba(194, 85, 47, 0.16);
    /* The hairline a tinted mark is edged with: quieter than a border, and
       redefined by the dark scheme where the light palette would glare. */
    --accent-edge: var(--terracotta-100);

    --link: var(--sage-500);
    --link-strong: var(--sage-600);

    --danger: var(--brick-500);
    --danger-soft: var(--brick-100);

    --warning: var(--saffron-500);
    --warning-soft: var(--saffron-50);
    --warning-border: var(--saffron-200);

    --overlay: rgba(42, 31, 24, 0.34);

    --shadow-xs: 0 1px 2px rgba(88, 55, 30, 0.06);
    --shadow-sm: 0 1px 3px rgba(88, 55, 30, 0.08), 0 1px 2px rgba(88, 55, 30, 0.05);
    --shadow-md: 0 10px 22px -6px rgba(88, 55, 30, 0.14), 0 4px 8px -4px rgba(88, 55, 30, 0.08);
    --shadow-accent: 0 4px 14px -4px rgba(194, 85, 47, 0.45);
}

@media (min-width: 900px) {
    :root {
        --nav-height: 0px;
        --nav-height-wide: 48px;
    }
}

/* Dark scheme. The same warm family, pulled down to a deep roasted brown so the
   accent keeps enough contrast to stay readable. */
@media (prefers-color-scheme: dark) {
    :root[data-theme="auto"] {
        --surface-page: #17120f;
        --surface-raised: #211a15;
        --surface-sunken: #1d1713;
        --surface-hover: #2c231d;

        --text-strong: #f4ebe1;
        --text-muted: #c3b2a4;
        --text-faint: #8d7a6c;
        --text-on-accent: #24170f;

        --border: #382d25;
        --border-strong: #4b3c32;

        --accent: var(--terracotta-300);
        --accent-strong: var(--terracotta-200);
        --accent-soft: #2f211a;
        --accent-ring: rgba(232, 144, 109, 0.24);
        --accent-edge: #442d22;

        --link: var(--sage-300);
        --link-strong: var(--sage-100);

        --danger: #eb8a7b;
        --danger-soft: #3a201c;

        --warning: var(--saffron-300);
        --warning-soft: #302512;
        --warning-border: #574119;

        --overlay: rgba(0, 0, 0, 0.62);

        --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
        --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.35);
        --shadow-md: 0 10px 22px -6px rgba(0, 0, 0, 0.6), 0 4px 8px -4px rgba(0, 0, 0, 0.45);
        --shadow-accent: 0 4px 14px -4px rgba(0, 0, 0, 0.55);
    }
}

:root[data-theme="dark"] {
    --surface-page: #17120f;
    --surface-raised: #211a15;
    --surface-sunken: #1d1713;
    --surface-hover: #2c231d;

    --text-strong: #f4ebe1;
    --text-muted: #c3b2a4;
    --text-faint: #8d7a6c;
    --text-on-accent: #24170f;

    --border: #382d25;
    --border-strong: #4b3c32;

    --accent: var(--terracotta-300);
    --accent-strong: var(--terracotta-200);
    --accent-soft: #2f211a;
    --accent-ring: rgba(232, 144, 109, 0.24);
    --accent-edge: #442d22;

    --link: var(--sage-300);
    --link-strong: var(--sage-100);

    --danger: #eb8a7b;
    --danger-soft: #3a201c;

    --warning: var(--saffron-300);
    --warning-soft: #302512;
    --warning-border: #574119;

    --overlay: rgba(0, 0, 0, 0.62);

    --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.35);
    --shadow-md: 0 10px 22px -6px rgba(0, 0, 0, 0.6), 0 4px 8px -4px rgba(0, 0, 0, 0.45);
    --shadow-accent: 0 4px 14px -4px rgba(0, 0, 0, 0.55);
}

/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */

html {
    -webkit-text-size-adjust: 100%;
    scroll-padding-top: calc(var(--header-height) + var(--space-6));
    color-scheme: light;
}

@media (prefers-color-scheme: dark) {
    html[data-theme="auto"] {
        color-scheme: dark;
    }
}

html[data-theme="dark"] {
    color-scheme: dark;
}

body {
    min-height: 100vh;
    font-family: var(--font-sans);
    font-size: var(--size-base);
    line-height: 1.6;
    color: var(--text-strong);
    background: var(--surface-page);
    -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
h4 {
    font-weight: 700;
    line-height: 1.25;
    letter-spacing: -0.015em;
}

a {
    color: var(--link);
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

a:hover {
    color: var(--link-strong);
}

::selection {
    background: var(--terracotta-100);
    color: var(--ink-900);
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition: none !important;
        animation: none !important;
    }
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* --------------------------------------------------------------------------
   4. Application shell
   -------------------------------------------------------------------------- */

/* The cook mode brings the only header left, on every width, and is therefore
   the only screen reserving a height under it. The height is written on the
   root element rather than on the body, so that the scroll padding, which the
   body cannot reach, is told about it too. */
:root:has(body.cooking) {
    --header-height: 56px;
}

@media (min-width: 900px) {
    :root:has(body.cooking) {
        --header-height: 62px;
    }
}

/* The frame the cook header is built on: the shell owns the bar, the cook
   stylesheet dresses it. */
.app-header {
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--surface-raised);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-xs);
}

.app-header-inner {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    max-width: var(--layout-width);
    min-height: var(--header-height);
    margin: 0 auto;
    padding: 0 var(--space-4);
}

/* Main navigation: a bottom bar on phones, a top bar from 900px on. */
.app-nav {
    position: fixed;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 45;
    background: var(--surface-raised);
    border-top: 1px solid var(--border);
    box-shadow: 0 -2px 10px rgba(88, 55, 30, 0.07);
    padding-bottom: env(safe-area-inset-bottom, 0);
}

.app-nav-inner {
    display: flex;
    align-items: stretch;
    max-width: var(--layout-width);
    margin: 0 auto;
}

.nav-item {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: var(--nav-height);
    padding: 0.35rem 0.25rem;
    border-top: 3px solid transparent;
    color: var(--text-faint);
    font-size: var(--size-xs);
    font-weight: 600;
    text-decoration: none;
    transition: color var(--duration) var(--ease), background var(--duration) var(--ease);
}

/* One box for both entries, whether an emoji or the icon of the application fills
   it: the two sit on the same line instead of a hair apart. The rounded corners
   are what makes the terracotta square read as an application icon. */
.nav-icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    font-size: 1.25rem;
    line-height: 1;
}

.nav-icon img {
    width: 100%;
    height: 100%;
    border-radius: var(--radius-xs);
    object-fit: cover;
}

/* On a phone the words are dropped: two destinations are told apart by their
   icons alone, and the band the bar takes from the page shrinks with them. */
.nav-label {
    display: none;
}

a.nav-item:hover {
    color: var(--text-muted);
    background: var(--surface-sunken);
}

.nav-item.is-current {
    color: var(--accent-strong);
    border-top-color: var(--accent);
    background: var(--accent-soft);
}

@media (min-width: 900px) {
    .app-nav {
        position: sticky;
        top: 0;
        bottom: auto;
        z-index: 39;
        border-top: none;
        border-bottom: 1px solid var(--border);
        box-shadow: var(--shadow-xs);
    }

    .app-nav-inner {
        gap: var(--space-1);
        padding: 0 var(--space-4);
    }

    .nav-item {
        flex: 0 0 auto;
        flex-direction: row;
        gap: 0.45rem;
        min-height: var(--nav-height-wide);
        padding: 0 var(--space-4);
        border-top: none;
        border-bottom: 3px solid transparent;
        border-radius: var(--radius-sm) var(--radius-sm) 0 0;
        font-size: var(--size-md);
    }

    .nav-item.is-current {
        border-bottom-color: var(--accent);
        background: transparent;
    }

    .nav-icon {
        width: 1.35rem;
        height: 1.35rem;
        font-size: 1.05rem;
    }

    /* Room enough to read: the top bar keeps the words beside the icons. */
    .nav-label {
        display: inline;
    }
}

.app-main {
    width: 100%;
    max-width: var(--layout-width);
    margin: 0 auto;
    padding: var(--space-4) var(--space-4) calc(var(--nav-height) + var(--space-7));
}

@media (min-width: 900px) {
    .app-main {
        padding: var(--space-6) var(--space-5) var(--space-7);
    }
}

.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    margin-bottom: var(--space-5);
}

.page-title {
    font-size: var(--size-2xl);
}

.page-subtitle {
    color: var(--text-muted);
    font-size: var(--size-md);
}

/* --------------------------------------------------------------------------
   5. Components
   -------------------------------------------------------------------------- */

.card {
    padding: var(--space-5);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

.empty-state {
    padding: var(--space-7) var(--space-4);
    background: var(--surface-sunken);
    border: 1.5px dashed var(--border-strong);
    border-radius: var(--radius-md);
    color: var(--text-muted);
    text-align: center;
}

.empty-state::before {
    content: "🥘";
    display: block;
    margin-bottom: var(--space-2);
    font-size: 2.4rem;
    opacity: 0.7;
}

.empty-state-title {
    margin-bottom: var(--space-2);
    color: var(--text-strong);
    font-size: var(--size-lg);
}

.empty-state-text {
    max-width: 34rem;
    margin: 0 auto;
    font-size: var(--size-md);
}

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    min-height: 44px;
    padding: 0.55rem 1rem;
    background: var(--surface-raised);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-xs);
    color: var(--text-strong);
    font-size: var(--size-md);
    font-weight: 650;
    line-height: 1.2;
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--duration) var(--ease), border-color var(--duration) var(--ease),
        transform var(--duration) var(--ease);
}

.button:hover {
    background: var(--surface-hover);
    border-color: var(--terracotta-200);
    color: var(--text-strong);
}

.button:active {
    transform: scale(0.97);
}

.button.is-primary {
    background: var(--accent);
    border-color: var(--accent);
    box-shadow: var(--shadow-accent);
    color: var(--text-on-accent);
}

.button.is-primary:hover {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
    color: var(--text-on-accent);
}

.button.is-block {
    width: 100%;
}

.button.is-small {
    min-height: 36px;
    padding: 0.35rem 0.7rem;
    border-radius: var(--radius-xs);
    font-size: var(--size-sm);
}

.button.is-danger {
    background: transparent;
    border-color: var(--danger);
    box-shadow: none;
    color: var(--danger);
}

.button.is-danger:hover {
    background: var(--danger-soft);
    border-color: var(--danger);
    color: var(--danger);
}

/* A label too long for one line: the choice offered against a near duplicate. */
.button.is-multiline {
    height: auto;
    line-height: 1.35;
    text-align: center;
    white-space: normal;
}

/* --------------------------------------------------------------------------
   5b. Chips and badges
   -------------------------------------------------------------------------- */

.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.28rem 0.7rem;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    color: var(--text-muted);
    font-size: var(--size-sm);
    font-weight: 600;
    line-height: 1.35;
    white-space: nowrap;
}

.chip.is-active {
    background: var(--accent-soft);
    border-color: var(--terracotta-200);
    color: var(--accent-strong);
}

/* The count a filter chip carries, dimmer than the label it follows. */
.chip-count {
    color: var(--text-faint);
    font-size: var(--size-xs);
    font-variant-numeric: tabular-nums;
}

.chip.is-active .chip-count {
    color: inherit;
    opacity: 0.7;
}

.chip.is-mini {
    gap: 0.25rem;
    padding: 0.08rem 0.5rem;
    background: var(--surface-sunken);
    font-size: var(--size-xs);
    font-weight: 600;
    white-space: normal;
}

.badge {
    display: inline-block;
    padding: 0.1rem 0.45rem;
    background: var(--saffron-100);
    border-radius: var(--radius-xs);
    color: var(--saffron-500);
    font-size: var(--size-xs);
    font-weight: 700;
    letter-spacing: 0.02em;
}

/* --------------------------------------------------------------------------
   6. Forms
   -------------------------------------------------------------------------- */

.field {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    margin-bottom: var(--space-4);
}

.field > label {
    color: var(--text-muted);
    font-size: var(--size-xs);
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.field input {
    width: 100%;
    min-height: 44px;
    padding: 0.6rem 0.8rem;
    background: var(--surface-raised);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-strong);
    font-size: var(--size-md);
    transition: border-color var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
}

.field input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring);
}

.form-error {
    display: flex;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--danger-soft);
    border-left: 3px solid var(--danger);
    border-radius: var(--radius-xs);
    color: var(--danger);
    font-size: var(--size-md);
    font-weight: 600;
}

/* --------------------------------------------------------------------------
   7. Authentication screens
   -------------------------------------------------------------------------- */

.auth {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: var(--space-5) var(--space-4);
    background:
        radial-gradient(80rem 40rem at 50% -10rem, var(--accent-soft), transparent 70%),
        var(--surface-page);
}

.auth-card {
    width: 100%;
    max-width: 26rem;
    padding: var(--space-6) var(--space-5);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
}

.auth-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin-bottom: var(--space-5);
    color: var(--text-strong);
    font-size: var(--size-xl);
    font-weight: 800;
    letter-spacing: -0.02em;
    text-decoration: none;
}

.auth-brand::before {
    content: "🍲";
    font-size: 1.5em;
    line-height: 1;
}

.auth-title {
    margin-bottom: var(--space-2);
    font-size: var(--size-xl);
}

.auth-intro {
    margin-bottom: var(--space-5);
    color: var(--text-muted);
    font-size: var(--size-md);
}

.auth-submit {
    margin-top: var(--space-5);
}

/* --------------------------------------------------------------------------
   8. Dialogs

   Native `dialog`, opened by the small helper in `app.js`. The top layer takes
   care of the stacking, so no z-index is needed anywhere here.
   -------------------------------------------------------------------------- */

.modal {
    /* The reset zeroes every margin, which is what the browser centres a dialog with. */
    margin: auto;
    width: min(560px, calc(100% - 2rem));
    max-height: calc(100dvh - 4rem);
    padding: var(--space-5);
    overflow-y: auto;
    background: var(--surface-raised);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    color: var(--text-strong);
}

.modal::backdrop {
    background: var(--overlay);
}

.modal-title {
    margin-bottom: var(--space-2);
    font-size: var(--size-xl);
}

.modal-intro {
    color: var(--text-muted);
    font-size: var(--size-md);
}

.modal-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--space-2);
    margin-top: var(--space-5);
}

/* --------------------------------------------------------------------------
   9. Foods screen
   -------------------------------------------------------------------------- */

.catalog-counter {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem var(--space-3);
    margin-bottom: var(--space-4);
    color: var(--text-muted);
    font-size: var(--size-md);
}

.catalog-counter-number {
    color: var(--text-strong);
    font-size: var(--size-2xl);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
    line-height: 1.1;
}

.foods-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}

.foods-search {
    display: flex;
    flex: 1 1 15rem;
    gap: var(--space-2);
}

/*
   `food-autocomplete.js` wraps the field it takes over, so the wrapper inherits the place the field held
   in the row. Both states must render the same: `flex` rules the field while it is still a direct child,
   `width` once it is not.
*/
.foods-search .autocomplete {
    flex: 1;
    min-width: 0;
}

.foods-search input {
    flex: 1;
    width: 100%;
    min-width: 0;
    min-height: 44px;
    padding: 0.6rem 0.8rem;
    background: var(--surface-raised);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-strong);
    font-size: var(--size-md);
}

.foods-search input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring);
}

/* Creation panel: a button next to the search box, a full row once unfolded. */
.food-create {
    flex: 0 0 auto;
}

.food-create[open] {
    flex: 1 1 100%;
}

summary {
    list-style: none;
}

summary::-webkit-details-marker {
    display: none;
}

.food-create-open {
    width: 100%;
}

.food-create-panel {
    margin-top: var(--space-3);
    padding: var(--space-5);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

@media (min-width: 900px) {
    .food-create-panel {
        max-width: 30rem;
    }
}

.panel-title {
    margin-bottom: var(--space-4);
    font-size: var(--size-lg);
}

.field-help {
    color: var(--text-faint);
    font-size: var(--size-xs);
}

.create-author {
    margin-bottom: var(--space-3);
    color: var(--text-faint);
    font-size: var(--size-xs);
}

.foods-filters {
    display: flex;
    gap: var(--space-2);
    margin-bottom: var(--space-5);
    padding-bottom: 0.3rem;
    overflow-x: auto;
    scrollbar-width: none;
}

.foods-filters::-webkit-scrollbar {
    display: none;
}

@media (min-width: 640px) {
    .foods-filters {
        flex-wrap: wrap;
        overflow-x: visible;
    }
}

/* Dense list of foods. */
.food-list {
    list-style: none;
    overflow: hidden;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

.food + .food {
    border-top: 1px solid var(--border);
}

.food-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.3rem var(--space-3);
    width: 100%;
    padding: 0.6rem var(--space-4);
    cursor: pointer;
    transition: background var(--duration) var(--ease);
}

.food-head:hover {
    background: var(--surface-sunken);
}

.food-row[open] .food-head {
    background: var(--accent-soft);
}

.food-name {
    font-weight: 700;
    line-height: 1.3;
}

.food-plural {
    margin-left: 0.4rem;
    color: var(--text-faint);
    font-size: var(--size-xs);
}

.food-aliases {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
}

.food-no-alias {
    color: var(--text-faint);
    font-size: var(--size-xs);
}

.food-end {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* How many recipes name the food: read at a glance, never louder than its name. */
.food-usage {
    color: var(--text-faint);
    font-size: var(--size-xs);
    white-space: nowrap;
}

.chevron {
    color: var(--text-faint);
    font-size: 0.9rem;
    line-height: 1;
    transition: transform var(--duration) var(--ease);
}

.food-row[open] .chevron {
    color: var(--accent);
    transform: rotate(180deg);
}

@media (min-width: 760px) {
    .food-head {
        grid-template-columns: 15rem minmax(0, 1fr) auto;
    }

    .food-aliases {
        grid-column: 2;
    }
}

.food-detail {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-4) var(--space-5);
    background: var(--surface-sunken);
    border-top: 1px solid var(--border);
}

.detail-title {
    margin-bottom: var(--space-2);
    color: var(--accent);
    font-size: var(--size-xs);
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.detail-help {
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.chip-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
}

.food-list-footer {
    margin-top: var(--space-3);
    color: var(--text-faint);
    font-size: var(--size-sm);
}

/* Administrator actions. Each one unfolds under its own button. */
.food-admin {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--space-2);
}

.food-action[open] {
    flex: 1 1 100%;
}

.food-action[open] > form {
    margin-top: var(--space-3);
    padding: var(--space-4);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.food-action.is-danger-action[open] > form {
    border-color: var(--danger);
}

.admin-note {
    color: var(--text-faint);
    font-size: var(--size-xs);
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

/* Merge dialog. */
.merge-flow {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-4) 0;
}

.merge-card {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
    padding: var(--space-3);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    text-align: center;
}

.merge-card.is-kept {
    background: var(--accent-soft);
    border-color: var(--terracotta-200);
}

.merge-role {
    color: var(--text-faint);
    font-size: var(--size-xs);
}

.merge-name {
    font-weight: 700;
    line-height: 1.3;
    overflow-wrap: anywhere;
}

.merge-arrow {
    color: var(--accent);
    font-size: 1.3rem;
    text-align: center;
}

.consequences {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    list-style: none;
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.consequences li {
    display: flex;
    gap: var(--space-2);
    align-items: flex-start;
}

.consequences li::before {
    content: "→";
    flex: 0 0 auto;
    color: var(--accent);
    font-weight: 700;
}

/* Near duplicate: the choice the server asks for before writing anything. */
.near-duplicate {
    margin-bottom: var(--space-3);
    padding: var(--space-3);
    background: var(--warning-soft);
    border: 1.5px solid var(--warning-border);
    border-radius: var(--radius-sm);
    font-size: var(--size-sm);
}

.near-duplicate-title {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    color: var(--warning);
    font-size: var(--size-md);
    font-weight: 800;
}

.near-duplicate-title::before {
    content: "⚠️";
    font-size: 0.95em;
}

.near-duplicate-card {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
    padding: var(--space-3);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}

.near-duplicate-name {
    font-size: var(--size-lg);
    font-weight: 700;
    line-height: 1.2;
}

.near-duplicate-meta {
    color: var(--text-faint);
    font-size: var(--size-xs);
}

.near-duplicate-choice {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* --------------------------------------------------------------------------
   10. Autocomplete suggestions

   The list the food fields grow underneath themselves. It is built by
   `food-autocomplete.js`, which owns the markup below.
   -------------------------------------------------------------------------- */

.autocomplete {
    position: relative;
}

.suggestions {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    left: 0;
    z-index: 20;
    list-style: none;
    max-height: 17rem;
    overflow: hidden auto;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
}

.suggestion {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: 0.45rem 0.6rem;
    border-bottom: 1px solid var(--border);
    cursor: pointer;
}

.suggestion:last-child {
    border-bottom: none;
}

.suggestion:hover,
.suggestion.is-highlighted {
    background: var(--accent-soft);
    box-shadow: inset 3px 0 0 var(--accent);
}

.suggestion-body {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
}

.suggestion-name {
    font-weight: 650;
    line-height: 1.35;
}

.suggestion-alias {
    color: var(--text-muted);
    font-size: var(--size-xs);
}

.suggestion-flag {
    flex: 0 0 auto;
    padding: 0.05rem 0.35rem;
    background: var(--warning-soft);
    border-radius: var(--radius-xs);
    color: var(--warning);
    font-size: var(--size-xs);
    font-weight: 700;
}

/* --------------------------------------------------------------------------
   11. Library
   -------------------------------------------------------------------------- */

.library-search {
    margin-bottom: var(--space-5);
}

.search-box {
    display: flex;
    gap: var(--space-2);
    max-width: 620px;
}

/*
   `food-autocomplete.js` wraps the field it takes over, so the wrapper inherits the place the field held
   in the row. Both states must render the same: `flex` rules the field while it is still a direct child,
   `width` once it is not.
*/
.search-box .autocomplete {
    flex: 1;
    min-width: 0;
}

.search-box input {
    flex: 1;
    width: 100%;
    min-width: 0;
    min-height: 44px;
    padding: 0.6rem 0.9rem;
    background: var(--surface-raised);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-pill);
    color: var(--text-strong);
    font: inherit;
}

.search-box input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 4px var(--accent-ring);
    outline: none;
}

/* The tab chooses the subset, the chips below filter inside it: two different jobs, two shapes. */
.tabs {
    display: flex;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    scrollbar-width: none;
}

.tabs::-webkit-scrollbar {
    display: none;
}

.tab {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: -1px;
    padding: 0.5rem 0.15rem 0.6rem;
    border-bottom: 2px solid transparent;
    color: var(--text-muted);
    font-size: var(--size-md);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: color var(--duration) var(--ease), border-color var(--duration) var(--ease);
}

.tab:hover {
    color: var(--text-strong);
}

.tab.is-current {
    border-bottom-color: var(--accent);
    color: var(--accent-strong);
}

.tab-count {
    color: var(--text-faint);
    font-size: var(--size-xs);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.category-filters {
    display: flex;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
    overflow-x: auto;
    scrollbar-width: none;
}

.category-filters::-webkit-scrollbar {
    display: none;
}

.category-filters .chip {
    text-decoration: none;
}

.library-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

.section-title {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
    font-size: var(--size-lg);
}

.section-title .count {
    color: var(--text-muted);
    font-size: var(--size-sm);
    font-weight: 600;
}

.sort-form {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.sort-form label {
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.select {
    min-height: 36px;
    padding: 0.35rem 0.6rem;
    background: var(--surface-raised);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-strong);
    font: inherit;
    font-size: var(--size-sm);
}

/* The submit button beside the select only exists for a browser running no script. */
.has-script .sort-submit {
    display: none;
}

/* Recipe cards. */
.recipe-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
}

@media (min-width: 560px) {
    .recipe-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (min-width: 900px) {
    .recipe-grid {
        grid-template-columns: repeat(3, 1fr);
        gap: var(--space-5);
    }
}

.recipe-tile {
    position: relative;
}

.recipe-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    color: inherit;
    overflow: hidden;
    text-decoration: none;
    transition: transform var(--duration) var(--ease), box-shadow var(--duration) var(--ease),
        border-color var(--duration) var(--ease);
}

.recipe-card:hover {
    transform: translateY(-2px);
    border-color: var(--terracotta-200);
    box-shadow: var(--shadow-md);
    color: inherit;
}

/* The empty photo slot: a warm gradient rather than a grey rectangle, since a household writes its
   recipes down long before it photographs them. */
.thumb {
    position: relative;
    aspect-ratio: 4 / 3;
    background: linear-gradient(140deg, var(--terracotta-100) 0%, var(--saffron-100) 55%, var(--cream-200) 100%);
    overflow: hidden;
}

.thumb::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(circle at 30% 25%, rgba(255, 255, 255, 0.5), transparent 60%);
    pointer-events: none;
}

.thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.thumb.is-warm {
    background: linear-gradient(140deg, var(--terracotta-200) 0%, var(--terracotta-100) 60%, var(--saffron-100) 100%);
}

.thumb.is-golden {
    background: linear-gradient(140deg, var(--saffron-200) 0%, var(--saffron-100) 55%, var(--cream-100) 100%);
}

.thumb.is-herb {
    background: linear-gradient(140deg, var(--sage-100) 0%, var(--cream-200) 60%, var(--saffron-100) 100%);
}

.thumb.is-cream {
    background: linear-gradient(140deg, var(--cream-300) 0%, var(--cream-100) 60%, var(--surface-raised) 100%);
}

.card-body {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4) var(--space-4);
}

.card-title {
    font-size: var(--size-lg);
    font-weight: 700;
    line-height: 1.25;
}

.card-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.card-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem var(--space-3);
    margin-top: auto;
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.card-meta > span {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}

/* The star sits on the card rather than in it: a form nested in a link is neither. */
.favorite-form {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    z-index: 1;
}

.favorite-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    padding: 0;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-xs);
    color: var(--text-faint);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.favorite-button:hover {
    border-color: var(--saffron-200);
    color: var(--saffron-300);
}

.favorite-button.is-on {
    border-color: var(--saffron-200);
    color: var(--saffron-300);
}

/* The way into the editor, always within a thumb's reach. */
.floating-action {
    position: fixed;
    right: var(--space-4);
    bottom: calc(var(--nav-height) + var(--space-4));
    z-index: 30;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.85rem 1.15rem;
    background: var(--accent);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-accent), var(--shadow-md);
    color: var(--text-on-accent);
    font-size: var(--size-md);
    font-weight: 700;
    text-decoration: none;
    transition: transform var(--duration) var(--ease), background var(--duration) var(--ease);
}

.floating-action:hover {
    transform: translateY(-2px);
    background: var(--accent-strong);
    color: var(--text-on-accent);
}

.floating-action-icon {
    font-size: 1.1rem;
    line-height: 1;
}

@media (min-width: 900px) {
    .floating-action {
        right: var(--space-6);
        bottom: var(--space-6);
    }
}

/* --------------------------------------------------------------------------
   12. Recipe page
   -------------------------------------------------------------------------- */

.recipe-header {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}

/* One band, one shape, everywhere: the sides are the ones the printed page gives a photograph, so a
   recipe read on a screen and the document it is printed and downloaded as show the same picture.
   Nothing about the width of the screen changes it. */
.recipe-photo {
    position: relative;
    aspect-ratio: 178 / 70;
    border-radius: var(--radius-md);
    background: linear-gradient(140deg, var(--terracotta-100) 0%, var(--saffron-100) 55%, var(--cream-200) 100%);
    overflow: hidden;
}

.recipe-photo.is-empty {
    aspect-ratio: auto;
    height: 84px;
}

/* Both copies lie in the band rather than in the flow: the band holds its own height, and the order
   they are written in is the order they are drawn in. */
.recipe-photo img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* A photograph whose shape is far from the band would lose most of itself to the crop. Those are shown
   whole, over a blurred copy of themselves filling what is left of the band. The copy is drawn larger
   than the band on purpose: a blur bleeds the edges of a picture into nothing, and at its own size a
   dark rim would show all around. */
.photo-backdrop {
    display: none;
    transform: scale(1.15);
    filter: blur(40px) brightness(0.55);
}

.recipe-photo.is-contained img {
    object-fit: contain;
}

.recipe-photo.is-contained .photo-backdrop {
    display: block;
    object-fit: cover;
}

.recipe-title {
    font-size: var(--size-2xl);
    line-height: 1.15;
}

.recipe-categories,
.recipe-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.recipe-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem var(--space-4);
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.recipe-meta > span {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

.recipe-actions .favorite-form {
    position: static;
}

.recipe-actions .favorite-button {
    width: auto;
    height: auto;
    min-height: 44px;
    gap: 0.45rem;
    padding: 0.55rem 1rem;
    border-width: 1.5px;
    border-color: var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-strong);
    font-size: var(--size-md);
    font-weight: 650;
}

.recipe-actions .favorite-button.is-on {
    border-color: var(--saffron-200);
    color: var(--saffron-500);
}

/* Servings. The desktop reads them from a bar under the navigation; on a phone the drawer carries them. */
.servings-bar {
    display: none;
}

.servings-block {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}

.servings {
    display: inline-flex;
    align-items: stretch;
    background: var(--surface-raised);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-xs);
    overflow: hidden;
}

.servings-step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.7rem;
    color: var(--accent);
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1;
    text-decoration: none;
    transition: background var(--duration) var(--ease);
}

.servings-step:hover {
    background: var(--accent-soft);
    color: var(--accent);
}

.servings-step.is-disabled {
    color: var(--text-faint);
    pointer-events: none;
}

.servings-value {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    min-width: 8.5rem;
    padding: 0.5rem 0.4rem;
    border-right: 1px solid var(--border);
    border-left: 1px solid var(--border);
    color: var(--text-muted);
    font-size: var(--size-md);
    text-align: center;
}

.servings-number {
    color: var(--text-strong);
    font-size: var(--size-lg);
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

.servings-info {
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.button.is-quiet {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    color: var(--text-muted);
}

.button.is-quiet:hover {
    background: var(--surface-hover);
    border-color: transparent;
}

.button.is-disabled {
    opacity: 0.45;
    pointer-events: none;
}

/* Only shown when a locked quantity is read for more than twice the servings. */
.scale-warning {
    display: flex;
    gap: var(--space-2);
    margin-bottom: var(--space-5);
    padding: var(--space-3) var(--space-4);
    background: var(--warning-soft);
    border: 1px solid var(--warning-border);
    border-left: 4px solid var(--warning);
    border-radius: var(--radius-md);
    color: var(--warning);
    font-size: var(--size-sm);
    line-height: 1.5;
}

.scale-warning[hidden] {
    display: none;
}

.recipe-columns {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-6);
}

.recipe-column,
.steps-column {
    min-width: 0;
}

.section + .section {
    margin-top: var(--space-6);
}

.section .section-title {
    margin-bottom: var(--space-3);
}

/* Ingredient list. Rendered twice, styled once. */
.ingredients {
    padding: var(--space-2) var(--space-4);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    list-style: none;
}

.ingredients li {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--border);
}

.ingredients li:last-child {
    border-bottom: none;
}

.ingredients li.group-title {
    display: block;
    padding: var(--space-3) 0 0.35rem;
    border-bottom: none;
    color: var(--accent);
    font-size: var(--size-xs);
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.ingredients li.group-title:first-child {
    padding-top: var(--space-2);
}

.check {
    display: flex;
    flex: 1;
    align-items: baseline;
    gap: var(--space-3);
    min-width: 0;
    padding: 0.15rem 0;
    cursor: pointer;
}

.check input[type="checkbox"] {
    flex: 0 0 auto;
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--accent);
}

.ingredient-quantity {
    flex: 0 0 5.5rem;
    color: var(--accent-strong);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.ingredient-name {
    flex: 1;
    min-width: 0;
}

.ingredient-note {
    color: var(--text-faint);
    font-size: var(--size-sm);
}

.lock {
    font-size: 0.85em;
    opacity: 0.75;
}

/* A checked line: struck through quietly, wherever it is read. */
.is-checked .ingredient-quantity,
.is-checked .ingredient-name,
.is-checked .ingredient-note {
    color: var(--text-faint);
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}

/* Some of the mentions crossed out and not the others: the line says how far it is instead of
   claiming to be done. The checkbox itself carries the dash a half-checked box is drawn with. */
.is-partial .ingredient-quantity,
.is-partial .ingredient-name {
    text-decoration: underline dashed var(--text-faint);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}

.ingredient-tally {
    display: inline-block;
    margin-left: 0.3rem;
    padding: 0.05em 0.35em;
    background: var(--accent-soft);
    border-radius: var(--radius-xs);
    color: var(--accent-strong);
    font-size: 0.72em;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    text-decoration: none;
    vertical-align: 0.05em;
}

.list-note {
    margin-top: var(--space-2);
    color: var(--text-faint);
    font-size: var(--size-sm);
}

/* Steps. */
.steps {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    list-style: none;
}

.step {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
}

.step-number {
    display: flex;
    flex: 0 0 2rem;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    background: var(--accent);
    border-radius: var(--radius-pill);
    color: var(--text-on-accent);
    font-size: var(--size-md);
    font-weight: 800;
    line-height: 1;
}

.step-body {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
}

/* Being read, a step gives its whole width to the sentence: the number floats over the corner of the
   card instead of holding a column of its own. The editor arranges its own steps, and says so in its
   own stylesheet, so nothing here reaches them. */
.steps-column .step {
    position: relative;
    display: block;
}

.steps-column .step-number {
    position: absolute;
    top: -0.55rem;
    left: -0.55rem;
    z-index: 1;
    width: 1.9rem;
    height: 1.9rem;
}

.step-text {
    line-height: 1.8;
}

.step-photo img {
    max-width: 260px;
    border-radius: var(--radius-sm);
}

/* The quantity written inside a sentence: what makes rescaling rewrite the
   steps too. The colors are the semantic tokens, so the dark scheme keeps the
   words readable where the raw palette used to muddy them, and a portion long
   enough to wrap keeps its tint and ring on every fragment. The tint hugs its
   words, exactly as it does in the editor: padding wide enough to read as a
   space would show one the sentence does not hold. */
.token {
    display: inline;
    padding: 0.08em 0.18em;
    margin: 0 0.02em;
    background: var(--accent-soft);
    border-radius: var(--radius-xs);
    box-shadow: inset 0 0 0 1px var(--accent-edge);
    color: var(--accent-strong);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}

.token.is-locked {
    background: var(--surface-hover);
    box-shadow: inset 0 0 0 1px var(--border-strong);
    color: var(--text-strong);
}

.token.is-locked::before {
    content: "🔒";
    margin-right: 0.28em;
    font-size: 0.78em;
    opacity: 0.75;
}

.token.is-rounded::after {
    content: attr(data-rounded-label);
    margin-left: 0.35em;
    padding: 0.05em 0.35em;
    background: var(--saffron-100);
    border-radius: var(--radius-xs);
    color: var(--saffron-500);
    font-size: 0.7em;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    white-space: nowrap;
}

.token.is-checked {
    opacity: 0.5;
}

.token.is-checked::before {
    content: "✓";
    margin-right: 0.25em;
    font-weight: 700;
}

.token.is-locked.is-checked::before {
    content: "🔒 ✓";
}

@media (min-width: 900px) {
    .recipe-title {
        font-size: var(--size-3xl);
    }

    .recipe-photo {
        border-radius: var(--radius-lg);
    }

    .recipe-photo.is-empty {
        height: 110px;
    }

    /* The stepper stays under the eye while the steps scroll. */
    .servings-bar {
        position: sticky;
        top: var(--nav-height-wide);
        z-index: 20;
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-3);
        margin-bottom: var(--space-5);
        padding: var(--space-3) 0;
        background: var(--surface-page);
        border-bottom: 1px solid var(--border);
    }

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

    /* Ingredients stay in sight while the steps are read, and a list longer than the screen scrolls
       on its own rather than dragging the whole recipe along with it. */
    .recipe-column {
        --column-top: calc(var(--nav-height-wide) + 118px);

        position: sticky;
        top: var(--column-top);
        max-height: calc(100vh - var(--column-top) - var(--space-5));
        /* Room on the sides for the shadow of the list, which the overflow would cut off. */
        margin: 0 -4px;
        padding: 0 4px;
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-width: thin;
        scrollbar-color: var(--border-strong) transparent;
    }

    .recipe-column::-webkit-scrollbar {
        width: 8px;
    }

    .recipe-column::-webkit-scrollbar-thumb {
        background: var(--border-strong);
        border: 2px solid transparent;
        border-radius: var(--radius-pill);
        background-clip: content-box;
    }

    .step {
        gap: var(--space-4);
        padding: var(--space-5);
    }

    .step-photo img {
        max-width: 300px;
    }
}

/* --------------------------------------------------------------------------
   13. Ingredient panel
   -------------------------------------------------------------------------- */

/* The component is written once, whatever the width: the narrow layout leans on it, the wide one
   hides it behind the sticky column, and the cook mode brings it back on every screen.

   Folded, it is a band saying how far the list has been crossed out and, in words, what a tap on it
   does. Opened, it is the list over the whole screen. There is nothing in between. */
.drawer {
    position: sticky;
    top: var(--header-height);
    z-index: 41;
    display: block;
    background: var(--surface-raised);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-sm);
}

/* Opened, the panel has to pass over the navigation bar, which the folded band never does. It is
   still passing over it on the way out, so the band waits for the slide to be over to come down. */
.drawer.is-open,
.drawer.is-moving {
    z-index: 50;
}

/* The whole band is the button: the count on one side, what a tap does on the other. */
.drawer-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    width: 100%;
    min-height: 40px;
    padding: 0.45rem var(--space-4);
    background: transparent;
    border: none;
    color: var(--text-strong);
    font: inherit;
    font-size: var(--size-md);
    font-weight: 700;
    text-align: left;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
}

.drawer-count {
    font-variant-numeric: tabular-nums;
}

/* Words rather than a glyph to decipher, in the shape of something one presses. */
.drawer-see {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.55rem;
    background: var(--accent-soft);
    border: 1px solid var(--accent-ring);
    border-radius: var(--radius-pill);
    color: var(--accent-strong);
    font-size: var(--size-sm);
    font-weight: 600;
    transition: background var(--duration) var(--ease), border-color var(--duration) var(--ease),
        color var(--duration) var(--ease);
}

/* The cook mode reads the band on a desktop too, where a pointer expects an answer. */
.drawer-bar:hover .drawer-see {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
}

/* An arrow with a shaft: a bare head said too little about where the list was coming from. */
.drawer-arrow {
    flex: 0 0 auto;
    width: 1rem;
    height: 1rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: transform var(--duration-panel) ease-out;
}

/* Turned back on itself while the list is out, so the way back is the way it came. */
.drawer.is-open .drawer-arrow {
    transform: rotate(180deg);
}

/* The panel is laid out at all times, one screen above the top of the screen, because nothing
   slides from a display of none. Hidden, it takes no tap and no tab stop; visibility waits for
   the slide to be over before it goes, and comes back before the slide in. */
.drawer-panel {
    position: fixed;
    inset: 0;
    z-index: 1;
    height: 100dvh;
    background: var(--surface-page);
    overflow-y: auto;
    overscroll-behavior: contain;
    visibility: hidden;
    opacity: 0;
    transform: translateY(-100%);
    transition: transform var(--duration-panel) ease-out, opacity var(--duration-panel) ease-out,
        visibility 0s linear var(--duration-panel);
}

.drawer.is-open .drawer-panel {
    visibility: visible;
    opacity: 1;
    transform: translateY(0);
    transition: transform var(--duration-panel) ease-out, opacity var(--duration-panel) ease-out,
        visibility 0s;
}

.drawer-header {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: var(--surface-page);
    border-bottom: 1px solid var(--border);
}

.drawer-close {
    flex: 0 0 auto;
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.2rem;
    height: 2.2rem;
    background: var(--surface-raised);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    color: var(--text-muted);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.drawer-close:hover {
    background: var(--surface-hover);
    color: var(--text-strong);
}

/* Inside the panel the list is already framed by the screen: it drops its own frame. */
.drawer-panel .ingredients {
    padding: var(--space-2) var(--space-4) var(--space-6);
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
}

.drawer-panel .ingredients li {
    align-items: center;
    gap: var(--space-2);
    padding: 0.15rem 0;
}

.drawer-panel .check {
    align-items: center;
    padding: 0.35rem 0;
}

.drawer-panel .ingredient-quantity {
    flex: 0 0 4.6rem;
}

/* The page under the panel must not scroll under the finger. */
body.drawer-open {
    overflow: hidden;
}

@media (max-width: 899px) {
    /* The panel replaces both the sticky bar and the ingredient column. */
    .recipe-column {
        display: none;
    }
}

/* On a wide screen the sticky column says everything the panel would. */
@media (min-width: 900px) {
    .drawer {
        display: none;
    }
}

/* --------------------------------------------------------------------------
   14. Sharing and the pages a link leads to
   -------------------------------------------------------------------------- */

.modal-head {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}

.modal-head .modal-title {
    margin-bottom: 0.1rem;
}

.icon-button {
    flex: 0 0 auto;
    margin-left: auto;
    width: 2rem;
    height: 2rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-muted);
    cursor: pointer;
}

.icon-button:hover {
    background: var(--surface-sunken);
    color: var(--text-strong);
}

.separator {
    height: 1px;
    margin: var(--space-4) 0;
    border: none;
    background: var(--border);
}

.field-label {
    display: block;
    margin-bottom: 0.35rem;
    color: var(--text-muted);
    font-size: var(--size-sm);
    font-weight: 650;
}

/* The state of the public link: the lamp says it at a glance, the words say it out loud. */
.share-state {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
}

.share-lamp {
    flex: 0 0 auto;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    background: var(--border-strong);
}

.share-state.is-open .share-lamp {
    background: var(--accent);
}

.share-state-text {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    line-height: 1.3;
}

.share-state-value {
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.share-state form {
    margin-left: auto;
}

.share-address {
    margin-top: var(--space-4);
}

.share-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* Two buttons of their own width, which a narrow dialog stacks rather than squeezes. */
.share-document .share-row {
    flex-wrap: wrap;
}

.share-url {
    flex: 1;
    min-width: 0;
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-strong);
    font-family: inherit;
    font-size: var(--size-sm);
    font-variant-numeric: tabular-nums;
}

.share-address .field-help,
.share-document .field-help,
.share-held .field-help,
.share-danger .field-help {
    margin-top: 0.4rem;
}

.share-danger {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.share-danger .field-help {
    flex: 1 0 100%;
}

/* A recipe already open to the outside says so on the button that opens the dialog. */
.button.share.is-shared {
    border-color: var(--accent);
    color: var(--accent-strong);
}

/* Nothing is fixed at the bottom of a guest page but the drawer, so the padding the navigation
   bar reserved is given back to the recipe. */
@media (max-width: 899px) {
    body.guest .app-main {
        padding-bottom: var(--space-6);
    }
}

/* Printing is the document: the button of a recipe hands the browser the very file the household
   downloads. A reader who asks its browser for the page instead gets the page, less the furniture
   that floats over it. */
@media print {
    .app-nav, .floating-action, .drawer { display: none; }
}

/* --------------------------------------------------------------------------
   15. Settings screen
   -------------------------------------------------------------------------- */

/* A reading column: seven sections one under the other, none of them wider than a sentence. */
.settings {
    max-width: 680px;
}

/* A card holding rows carries no padding of its own: each row carries its own. */
.card.is-list {
    padding: 0;
    overflow: hidden;
}

.settings-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border);
}

.card.is-list > *:last-child {
    border-bottom: none;
}

.settings-row-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.settings-row-name {
    font-weight: 650;
}

.settings-row-note {
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.settings-row-end {
    flex-shrink: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* The initial of an account, so a list of names is read at a glance. */
.avatar {
    flex: 0 0 2.4rem;
    width: 2.4rem;
    height: 2.4rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-pill);
    background: linear-gradient(140deg, var(--sage-300), var(--sage-600));
    color: #fff;
    font-weight: 800;
}

.avatar.is-admin {
    background: linear-gradient(140deg, var(--terracotta-400), var(--terracotta-600));
}

/* A form inside a card of rows: its own band, so it never reads as one more row. */
.settings-form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3);
    padding: var(--space-4);
    border-bottom: 1px solid var(--border);
    background: var(--surface-sunken);
}

.settings-form .field {
    flex: 1 1 10rem;
    margin: 0;
}

.card-note {
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--border);
    background: var(--surface-sunken);
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.card-help {
    margin-top: var(--space-3);
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.card.is-list .form-error {
    margin: 0;
    padding: var(--space-3) var(--space-4);
}

/* Colour scheme: three options, each showing what it does. */
.theme-choice {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
}

.theme-option {
    position: relative;
    display: block;
    cursor: pointer;
}

.theme-option input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.theme-option-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-2);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-muted);
    font-size: var(--size-sm);
    font-weight: 650;
}

.theme-option input:checked ~ .theme-option-body {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent-strong);
}

.theme-option input:focus-visible ~ .theme-option-body {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.theme-preview {
    width: 100%;
    height: 2.6rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
}

.theme-preview.is-light {
    background: linear-gradient(160deg, #fdf4e8, #f2e3cd);
}

.theme-preview.is-dark {
    background: linear-gradient(160deg, #3b2c22, #221a14);
}

.theme-preview.is-auto {
    background: linear-gradient(105deg, #fdf4e8 0 50%, #2f241c 50% 100%);
}

/* The button beside the radios only exists for a browser running no script. */
.has-script .theme-submit {
    display: none;
}

/* Categories: the row is a rename form, the end of it is what can be done to the row. */
.category-row {
    gap: var(--space-2);
}

.category-rename {
    flex: 1 1 12rem;
    display: flex;
    gap: var(--space-2);
}

.category-rename input {
    flex: 1;
    min-width: 0;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--text-strong);
    font: inherit;
}

.category-usage,
.counter {
    color: var(--text-faint);
    font-size: var(--size-sm);
}

.settings-row-end form {
    display: inline-flex;
}

/* Installing the application on the device it is read on. */
.install-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4);
    border: 1.5px solid var(--terracotta-200);
    border-radius: var(--radius-md);
    background: linear-gradient(140deg, var(--terracotta-50), var(--cream-50) 70%);
}

.install-icon {
    flex: 0 0 3rem;
    width: 3rem;
    height: 3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-md);
    background: linear-gradient(140deg, var(--terracotta-300), var(--terracotta-600));
    font-size: 1.5rem;
}

.install-text {
    flex: 1 1 11rem;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.install-title {
    font-weight: 700;
}

.install-help,
.install-manual {
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.export-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-4);
    border-bottom: 1px solid var(--border);
}

.version {
    font-variant-numeric: tabular-nums;
}

.settings-footer {
    margin-top: var(--space-6);
}

@media (max-width: 420px) {
    .theme-choice {
        grid-template-columns: 1fr;
    }

    .theme-preview {
        height: 2rem;
    }
}

/* --- Paste import: home cards and the doubled floating action ------------ */

.import-cards {
    display: grid;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.import-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.import-card.is-pending {
    opacity: 0.75;
}

.import-card-title {
    font-weight: 700;
}

.import-card-meta {
    color: var(--text-muted);
    font-size: var(--size-sm);
}

.import-card-actions {
    display: flex;
    gap: var(--space-1);
    align-items: center;
}

.import-diagnostic-meta,
.import-diagnostic-note {
    color: var(--text-muted);
    font-size: var(--size-sm);
}

/* The summary is a button, the way every other one of this application is:
   nothing else would say it opens, since the marker is reset away. */
.import-diagnostic-prompt {
    margin-top: var(--space-4);
}

/* A prompt runs to several thousand characters: it scrolls inside its own
   block, so the page never scrolls sideways. */
.import-diagnostic-block {
    overflow-x: auto;
    padding: var(--space-2);
    border-radius: var(--radius-md);
    background: var(--surface-sunken, var(--surface));
    font-size: var(--size-sm);
    white-space: pre-wrap;
    word-break: break-word;
}

.floating-action-stack {
    position: fixed;
    right: var(--space-4);
    bottom: calc(var(--nav-height) + var(--space-4));
    z-index: 30;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-2);
}

/* Inside the stack the buttons keep their look but let the stack place them. */
.floating-action-stack .floating-action {
    position: static;
}

.floating-action.is-secondary {
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow-md);
}

.import-review-banner {
    padding: var(--space-2);
    border-left: 3px solid var(--accent);
    background: var(--surface);
    border-radius: var(--radius-sm);
    margin-bottom: var(--space-2);
}

.import-review-source {
    margin-bottom: var(--space-3);
}

.import-review-source pre {
    white-space: pre-wrap;
    padding: var(--space-2);
    background: var(--surface);
    border-radius: var(--radius-sm);
    font-size: var(--size-sm);
}

.import-review-feedback {
    margin-bottom: var(--space-3);
}

/* The note is written where the proposal is read, so the field wears the
   skin every other field of the application wears. */
.import-review-feedback textarea {
    width: 100%;
    padding: 0.6rem 0.8rem;
    background: var(--surface-raised);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-strong);
    font-size: var(--size-md);
    resize: vertical;
}

.import-review-feedback textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring);
}
