/* ══════════════════════════════════════════════════════════════════════
   Classical design system — ported subset for Phase 2.5 (base templates &
   static assets). Source of truth for the full system is
   mockups/design_handoff_weekly_meal_planner/styles.css - this file is a
   deliberately curated subset of it, not a full copy.

   Ported here: every design token (:root custom properties), base
   typography (headings/body/links/rules), buttons, form input/label
   primitives, header/nav styling, and (Phase 3.1) the card/tag/elevation
   components - everything the shared base template and today's built
   pages (login/register/home/family, recipe library) actually use.

   Phase 3.1 addition: .card / .card-kicker / .card-title / .card-meta,
   .tag / .tag-accent / .tag-accent-2 / .tag-neutral / .tag-outline, and
   .elev-sm / .elev-md / .elev-lg, ported verbatim from the mockup's
   styles.css for the recipe library's card grid. Also added
   .chip-checkbox, which is NOT a literal mockup class - the mockup's own
   sidebar filter chips (`.tag.chip`, defined inline in the .dc.html file
   rather than styles.css) are onClick-driven spans that toggle an
   `active` class via JS. Step 3.1's brief is a plain GET-form filter UI
   with no JS, so .chip-checkbox reproduces the same visual (outline chip,
   accent-tinted when active) using a hidden checkbox + `:has(input:checked)`
   instead - same look, no script. Recipe-library-specific layout (sidebar
   width, grid, photo placeholder) lives in static/css/recipes.css instead
   of here since it's one page's layout, not a shared component - see that
   file's header comment.

   Phase 3.2 addition: .plate (recipe photo mat, ported verbatim from the
   mockup's styles.css) and .no-print (mirrors the mockup's own print
   utility class - toolbar/nav chrome hidden on paper) for the recipe
   detail/print view.

   Phase 4.1 addition: .radio / .seg / .seg-opt, ported verbatim from the
   mockup's styles.css. The mockup itself only used these for its
   Viewer/Editor role-toggle demo control (not present in the real app -
   role comes from auth, see accounts/permissions.py) - here they're
   repurposed for the add/edit recipe form's single-select tag facets
   (meal_type/cuisine/season/category), same "reuse an existing hidden
   input-driven component instead of inventing a new one" approach as
   Phase 3.1's .chip-checkbox for the library's filter chips. Only .seg/
   .seg-opt are actually used by that form (radio inputs wrapped in
   .seg-opt spans, grouped in a .seg container); .radio is ported
   alongside since the mockup documents the pair together and it's a
   trivial single extra component to keep available for the next screen
   that needs a standalone (non-segmented) radio control - see
   recipes.css for where .seg is actually used.

   Phase 4.3 addition: .dialog / .dialog-title / .dialog-body /
   .dialog-actions, ported from the mockup's styles.css for the recipe
   delete-confirm page (see below) - .dialog-backdrop is NOT ported since
   this app renders the confirm as its own page rather than a JS-toggled
   overlay (Phase 6's assign-recipe modal is the more likely place that
   one ends up mattering, if it ever needs a true overlay).

   Phase 6 addition: .btn-icon, ported verbatim for the weekly planner's
   prev/next week buttons. That screen's assign-recipe modal did NOT end up
   needing .dialog-backdrop after all (see the Phase 4.3 note above): it's a
   native <dialog>, whose ::backdrop pseudo-element replaces the mockup's
   backdrop div - static/css/planner.css styles it there.

   Still NOT ported (left for the phase that builds the screen that needs
   it):
     - .table (not used by any of the mockup's 6 screens - nothing to
       port yet; add it when a screen actually needs a tabular layout)
   ══════════════════════════════════════════════════════════════════════ */

@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;600&family=Lora:wght@400;600&display=swap');

:root {
  --color-bg: #f3f2f2;
  --color-surface: #eae9e9;
  --color-text: #201f1d;
  --color-accent: #b68235;
  --color-accent-2: #ac803e;
  --color-divider: color-mix(in srgb, #201f1d 16%, transparent);

  /* Tonal ramps - generated in OKLCH on one shared lightness scale, so the
     same step of any role matches the others in visual value. */
  --color-neutral-100: #f8f4f4;
  --color-neutral-200: #eae7e7;
  --color-neutral-300: #d7d3d3;
  --color-neutral-400: #bab6b6;
  --color-neutral-500: #9b9797;
  --color-neutral-600: #7d7979;
  --color-neutral-700: #605d5d;
  --color-neutral-800: #444141;
  --color-neutral-900: #2d2b2b;

  --color-accent-100: #fff3e4;
  --color-accent-200: #ffe3bf;
  --color-accent-300: #facb8d;
  --color-accent-400: #e1ad66;
  --color-accent-500: #c28d41;
  --color-accent-600: #a06f24;
  --color-accent-700: #7d5411;
  --color-accent-800: #5a3b0a;
  --color-accent-900: #3a270d;

  --color-accent-2-100: #fff3e4;
  --color-accent-2-200: #ffe3be;
  --color-accent-2-300: #f5cd96;
  --color-accent-2-400: #dbaf70;
  --color-accent-2-500: #bc8f4e;
  --color-accent-2-600: #9b7232;
  --color-accent-2-700: #79561f;
  --color-accent-2-800: #573d14;
  --color-accent-2-900: #382810;

  --font-heading: "Cormorant Garamond", system-ui, sans-serif;
  --font-heading-weight: 600;
  --font-body: "Lora", system-ui, sans-serif;

  --space-1: 4.6px;
  --space-2: 9.2px;
  --space-3: 13.8px;
  --space-4: 18.4px;
  /* --space-5 is missing from the mockup's own styles.css token export
     (only 1/2/3/4/6/8 are defined there) despite var(--space-5) being
     used 6 times in Weekly Meal Planner.dc.html's inline styles - the
     library/dashboard card grid gaps and the recipe detail screen's
     margin-top under the photo among them. Every other defined step
     is exactly 4.6px * N (1=4.6, 2=9.2, 3=13.8, 4=18.4, 6=27.6, 8=36.8),
     so 23px fills the gap consistent with that scale rather than
     silently falling back to `gap: normal` / no gap wherever the mockup
     markup references it (Phase 3.2 finding - see recipes.css/detail.html).
   */
  --space-5: 23px;
  --space-6: 27.6px;
  --space-8: 36.8px;

  --radius-sm: 2px;
  --radius-md: 4px;
  --radius-lg: 7px;

  --shadow-sm: 0 1px 2px color-mix(in srgb, #2d2b2b 14%, transparent);
  --shadow-md: 0 3px 10px color-mix(in srgb, #2d2b2b 16%, transparent);
  --shadow-lg: 0 12px 32px color-mix(in srgb, #2d2b2b 22%, transparent);
}

/* — base typography & elements — */
*, *::before, *::after { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  font-weight: 400;
}
h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  line-height: 1.12; letter-spacing: -0.015em; margin: 0 0 var(--space-2);
}
h1 { font-size: 42px; }
h2 { font-size: 32px; }
h3 { font-size: 25px; }
h4 { font-size: 20px; }
h5 { font-size: 16px; }
h6 { font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase; }
p { margin: 0 0 var(--space-3); }
a { color: var(--color-accent); text-underline-offset: 3px; }
img { display: block; max-width: 100%; }
figure { margin: 0; }
figcaption {
  font-size: 11px; margin-top: var(--space-1);
  /* 65%, not the mockup's original 55% - see .text-muted below for why. */
  color: color-mix(in srgb, var(--color-text) 65%, transparent);
}
/* The mockup's own token here (55%) computes to ~3.6:1 against
   --color-bg / --color-surface - below WCAG AA's 4.5:1 minimum for
   normal-size text. Bumped to 65% (~4.9:1 / ~4.75:1 respectively) per
   this project's standing full-brightness/contrast-checked UI-text
   guidance, which takes priority over faithfully reproducing the
   mockup's own choice here. figcaption and .field .helptext above/below
   share this same 55% value for the same "muted secondary text" role,
   so they're bumped identically for the same reason. */
.text-muted { color: color-mix(in srgb, var(--color-text) 65%, transparent); }
:focus { outline: none; }
:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
::selection { background: color-mix(in srgb, var(--color-accent) 30%, transparent); }

/* — rules — */
.hr {
  height: 1px; border: 0; margin: var(--space-4) 0;
  background: var(--color-divider);
}

/* — buttons — */
/* `font-weight: 700` and `font-size: 15px` below are a deliberate
   deviation from --font-heading-weight (600) / 14px used by every other
   heading-styled element (h1-h6, .card-title, .dialog-title, .nav-brand)
   - not drift. Per direct user feedback on the login/register buttons'
   legibility ("need to be around 1px bigger and possibly with thicker
   font"), buttons intentionally render bolder/larger than the rest of
   the heading system. Keep this as-is; don't "fix" it back to the token. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  cursor: pointer; text-decoration: none;
  font-family: var(--font-heading); font-weight: 700;
  font-size: 15px; line-height: 1.2; color: var(--color-text);
  background: transparent; border: 1px solid transparent;
  padding: var(--space-2) calc(var(--space-3) * 1.2);
  border-radius: var(--radius-md);
}
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
/* `.btn { display: inline-flex }` above is an author rule, so it beats the
   UA stylesheet's `[hidden] { display: none }` - without this, setting
   `button.hidden = true` from JS changes the attribute and nothing else.
   Phase 7 addition: the shopping list's servings slider ships with a
   no-JS "Apply" submit button that shopping.js hides once it takes over.
   Same class of clash planner.css documents for the assign dialog and its
   filtered rows; fixed once here rather than per-screen because it
   applies to every .btn in the app. */
.btn[hidden] { display: none; }
.btn-primary { color: var(--color-accent); border-color: var(--color-accent); }
.btn-primary:hover { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
.btn-primary:active { background: color-mix(in srgb, var(--color-accent) 22%, transparent); }
.btn-secondary { border-color: var(--color-divider); }
.btn-secondary:hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-secondary:active { background: color-mix(in srgb, var(--color-text) 14%, transparent); }
.btn-ghost { color: var(--color-accent); padding-inline: var(--space-1); }
.btn-ghost:hover { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }
.btn-ghost:active { background: color-mix(in srgb, var(--color-accent) 18%, transparent); }
.btn-block { width: 100%; margin-top: var(--space-2); }
/* Phase 6 addition, ported verbatim from the mockup's styles.css - the
   planner's prev/next week buttons are the first square icon-sized
   buttons in the app. */
.btn-icon { width: 36px; height: 36px; padding: 0; }

/* — forms —
   Today's pages render Django forms with a manual {% for field in form %}
   loop (see accounts/templates/*) wrapping each field in a `.field` div,
   rather than `.input`/`.field` classes on the widgets themselves - so
   these rules target the raw input/textarea/select elements directly and
   need no changes to accounts/forms.py widget attrs. */
.field { margin-bottom: var(--space-3); }
.field > label {
  display: block; font-size: 12px; margin-bottom: 5px;
  color: color-mix(in srgb, var(--color-text) 70%, transparent);
}
input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="search"], input[type="file"], textarea, select {
  width: 100%; min-height: 36px; padding: 6px 10px; font: inherit;
  font-size: 14px; color: var(--color-text); caret-color: var(--color-accent);
  background: transparent;
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
/* input[type="file"] gets the same box as every other field above, but
   its native "Choose File" button portion doesn't respect width: 100%
   the way a plain text input does - left unset, its intrinsic content
   width can push past the field's own border, which is what caused a
   1px horizontal-overflow bug on the add/edit recipe form's Photo field
   at 768px viewport width (#id_photo's right edge sitting at 769.4px
   against a 768px viewport). max-width: 100% plus the box-sizing:
   border-box every element already gets (top of this file) keeps the
   whole control - including the button - inside the field's own bounds. */
input[type="file"] { max-width: 100%; }
input[type="text"]:hover, input[type="email"]:hover, input[type="password"]:hover,
input[type="number"]:hover, input[type="search"]:hover, input[type="file"]:hover, textarea:hover, select:hover {
  border-color: color-mix(in srgb, var(--color-text) 45%, transparent);
}
input[type="text"]:focus-visible, input[type="email"]:focus-visible,
input[type="password"]:focus-visible, input[type="number"]:focus-visible,
input[type="search"]:focus-visible, input[type="file"]:focus-visible, textarea:focus-visible, select:focus-visible {
  border-color: var(--color-accent); outline-offset: 0;
}
textarea { min-height: 90px; resize: vertical; }
.field .helptext { display: block; font-size: 12px; margin-top: 4px; color: color-mix(in srgb, var(--color-text) 65%, transparent); }
.field .errorlist { list-style: none; margin: 4px 0 0; padding: 0; font-size: 12px; color: #8a3b2c; }

/* — cards — (Phase 3.1, ported verbatim from the mockup's styles.css) */
.card {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: var(--space-3); border-radius: var(--radius-md); background: transparent; border: 1px solid var(--color-divider);
}
.card-kicker { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-accent); }
.card-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 17px; line-height: 1.2;
}
.card-meta {
  display: flex; align-items: center; gap: 6px; font-size: 11px;
  /* 65%, not the mockup's original 50% (~3.15:1) - same WCAG AA
     contrast fix and rationale as .text-muted above. */
  color: color-mix(in srgb, var(--color-text) 65%, transparent);
}
.elev-sm { box-shadow: var(--shadow-sm); }
.elev-md { box-shadow: var(--shadow-md); }
.elev-lg { box-shadow: var(--shadow-lg); }

/* — photo mat — (Phase 3.2, ported verbatim from the mockup's styles.css) */
.plate {
  filter: sepia(0.22) saturate(0.82) contrast(1.05);
  box-sizing: border-box;
  border: 6px solid var(--color-surface);
  outline: 1px solid var(--color-divider);
}

/* — tags — (Phase 3.1, ported verbatim from the mockup's styles.css) */
.tag {
  display: inline-flex; align-items: center; font-size: 11px;
  letter-spacing: 0.02em; padding: 3px 10px;
  border-radius: calc(var(--radius-md) * 0.75);
}
.tag-accent { background: var(--color-accent-100); color: var(--color-accent-800); }
.tag-accent-2 { background: var(--color-accent-2-100); color: var(--color-accent-2-800); }
.tag-neutral { background: var(--color-neutral-100); color: var(--color-neutral-800); }
.tag-outline { border: 1px solid var(--color-accent); color: var(--color-accent); }

/* — radio / segmented control — (Phase 4.1, ported verbatim from the
   mockup's styles.css) */
.radio { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 14px; }
.radio input, .seg-opt input {
  position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.radio .dot {
  width: 16px; height: 16px; flex: none; border-radius: 50%;
  border: 1.5px solid var(--color-divider);
}
.radio:hover .dot { border-color: var(--color-accent); }
.radio input:checked + .dot {
  border-color: var(--color-accent); background: var(--color-accent);
  box-shadow: inset 0 0 0 4px var(--color-bg);
}
.radio input:focus-visible + .dot { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.seg {
  display: inline-flex; overflow: hidden;
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
}
.seg-opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; font-size: 13px; cursor: pointer;
}
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt:has(input:checked) { color: var(--color-accent); box-shadow: inset 0 0 0 1px var(--color-accent); }
.seg-opt:not(:has(input:checked)):hover { background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.seg-opt:has(input:focus-visible) { outline: 2px solid var(--color-accent); outline-offset: -2px; }

/* — filter chips (checkbox-driven, see header comment) — */
.chip-checkbox {
  position: relative; display: inline-flex; align-items: center; cursor: pointer;
  user-select: none; border: 1px solid var(--color-divider);
}
.chip-checkbox:hover { border-color: var(--color-accent); }
.chip-checkbox input {
  position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
}
.chip-checkbox:has(input:checked) {
  border-color: var(--color-accent); background: var(--color-accent-100); color: var(--color-accent-800);
}

/* — dialog — (Phase 4.3, ported verbatim from the mockup's styles.css).
   The mockup uses .dialog-backdrop/.dialog as a JS-toggled overlay
   (deleteConfirmOpen); Step 4.3's delete-confirm flow is a real
   GET-confirm/POST-delete *page* instead (no JS), so only .dialog/
   .dialog-title/.dialog-body/.dialog-actions are used here, centered on
   the page via .container - .dialog-backdrop is left unported since
   there's no overlay to dim (nothing else it's needed for yet either). */
.dialog {
  width: min(440px, 100%); display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4); border-radius: var(--radius-lg);
  background: var(--color-surface); box-shadow: var(--shadow-lg); border: 1px solid var(--color-divider);
}
.dialog-title {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 20px;
}
.dialog-body { font-size: 14px; opacity: 0.85; }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-2); }
.dialog-actions form { margin: 0; }

/* — navigation — */
.nav {
  display: flex; align-items: center; gap: var(--space-4);
  /* The nav is the one row in this app whose width is driven by content
     it can't shorten (the signed-in user's email, an unverified-email
     notice, one link per screen), so at phone widths it was pushing the
     whole document sideways - every page picked up a horizontal scrollbar,
     not just the one being looked at. Found at 390px while QA'ing the
     shopping list, which adds a seventh item to the row; wrapping is the
     whole fix and needs no per-page work. */
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-divider);
}
.nav a { color: inherit; text-decoration: none; font-size: 15px; font-weight: 500; }
/* .nav-brand is itself an <a> inside .nav, so it also matches ".nav a"
   above - ".nav .nav-brand" (2 classes) beats ".nav a" (1 class + 1
   element) on specificity regardless of source order, so this actually
   wins rather than silently losing to the plain-nav-link rule. */
.nav .nav-brand {
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 22px; margin-right: auto; color: var(--color-text); text-decoration: none;
}
.nav a:hover, .nav a[aria-current='page'] { color: var(--color-accent); }
.nav form { margin: 0; }

/* Small utility (not a mockup class) for a one-button POST form sitting
   inline next to text in a list row (member/invitation action rows) -
   forms are block-level by default, which would otherwise stack each
   action onto its own line. */
.inline-form { display: inline; margin-left: var(--space-2); }
.inline-form .btn { padding: 3px var(--space-2); font-size: 12px; }

/* — layout primitives —
   Not literal mockup classes (the mockup's screens each hardcode their own
   max-width per screen - 1280px library, 840px recipe detail, 760px form,
   900px shopping list); .container is a general-purpose default in that
   same spirit for pages that don't have a mockup screen of their own yet. */
.container { max-width: 720px; margin: 0 auto; padding: var(--space-8) var(--space-4); }

/* — messages (Django messages framework) —
   No equivalent component in the mockup (none of its 6 screens show a
   flash-message state); minimal styling added here, built only from
   existing tokens, so alerts don't render as a bare bulleted list. */
.messages { list-style: none; margin: 0 0 var(--space-4); padding: 0; }
.messages li {
  padding: var(--space-2) var(--space-3); margin-bottom: var(--space-2);
  border: 1px solid var(--color-divider); border-radius: var(--radius-md);
  font-size: 14px;
}

/* — print —
   .no-print mirrors the mockup's own convention: pages that need print
   support (recipe detail - Phase 3.2; shopping list - Phase 7.3) mark
   their on-screen-only chrome (toolbars, buttons) with this class. Global
   chrome (nav/header) lives here since it's shared across every page;
   page-specific print polish (e.g. recipe detail's two-column layout
   collapsing to one) lives in that page's own stylesheet instead. */
@media print {
  header, .no-print {
    display: none !important;
  }
  body {
    background: #fff;
  }
  a {
    color: inherit;
    text-decoration: none;
  }
}
