/*
 * navshell.css — pill-based primary nav, lifted from
 * Mirepoix_Design_System-2026-05-27/ui_kits/mirepoix/styles.css (nav sections).
 *
 * Adaptations from the source:
 *   - All hardcoded colors replaced with token references (--ground,
 *     --red, --red-soft, --red-deep, --red-glow, --pill-bg, etc.).
 *     PR 1 added these tokens to base.css as aliases or new entries.
 *   - Drops anything that touches .appwrap or .paper (page chrome
 *     is out of scope for this port).
 *   - data-cramped is read from .navshell itself (no .appwrap host).
 *   - .navshell uses position: sticky; top: 0; z-index: 30; instead of
 *     the design system's absolute-inside-appwrap positioning.
 *
 * Collaborators:
 *   - base.css (token vocabulary)
 *   - app/views/shared/_navshell.html.erb (markup)
 *   - app/views/shared/_brand_menu.html.erb (popover)
 *   - app/javascript/controllers/nav_shell_controller.js (width measurement)
 *   - app/javascript/controllers/brand_menu_controller.js (popover)
 *
 * Reference (read-only): the design-system styles.css uses comment-divider
 * markers (── pill primitive ──, ── pill variant: brand ──, etc.) to signpost
 * each section; this file preserves them. Do not write `*` `/` next to each
 * other inside this comment — it terminates the outer block early and the
 * parser eats the next rule.
 */

/* ============================================================================
 * .pill — the one chrome primitive every nav surface inherits.
 * Frosted-paper capsule with a top-edge highlight, paper-noise overlay,
 * and the system's offset-SE shadow.
 * ========================================================================= */

/* .brand-host wraps the brand pill and its popover so the brand-menu
 * Stimulus controller has both as descendants. display: contents keeps
 * the wrapper layout-transparent — the surrounding slot sees the button
 * and popover as direct children, so positioning isn't affected. */
.brand-host {
  display: contents;
}

.pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  background: var(--pill-bg);
  backdrop-filter: blur(20px) saturate(1.15);
  -webkit-backdrop-filter: blur(20px) saturate(1.15);
  border: 1px solid var(--pill-border);
  border-radius: var(--radius-pill);
  box-shadow: var(--pill-shadow);
  isolation: isolate;
  color: var(--text);
  font: inherit;
  -webkit-tap-highlight-color: transparent;
}
.pill::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("/assets/paper-noise-94045ff2.png");
  background-size: 200px 200px;
  border-radius: inherit;
  opacity: 0.05;
  mix-blend-mode: multiply;
  pointer-events: none;
}

/* =============================================================================
 * NAV BUTTON STATE SYSTEM
 *
 * Four button types share one interactive vocabulary:
 *   .pill--brand       — the Mirepoix wordmark
 *   .destination       — destinations inside the host destinations pill
 *   .pill--icon        — the search and help icon buttons
 *   .brandmenu__item   — rows inside the brand-menu popover
 *
 * Shared treatments (this block):
 *   :hover  → layer a red-soft background tint as a gradient image, so it
 *             stacks on top of the existing paper bg-color on free-standing
 *             pills, and stands on its own on transparent nested buttons.
 *             Skipped on the current destination (its inset-card chrome
 *             would fight the hover bg).
 *
 * Per-variant treatments (later blocks):
 *   inner halo glows  — each button has its own anatomy of icons/text
 *   focus indicator   — outline ring on outer pills, emboss on menu rows
 *   press deboss      — same recipe (--shadow-deboss / --shadow-deboss-deep)
 *   inner content shift on press — translate or padding shift
 *   open / current / modal-active — each is structurally different
 *
 * Tokens carry the shadow + halo recipes; dark-mode overrides flow through
 * the tokens, so the per-variant rules never need their own dark fork. */

/* :hover — shared background tint */
@media (hover: hover) {
  :is(.pill--brand,
      .destination:not([aria-current="page"]),
      .pill--icon,
      .brandmenu__item):hover {
    background-image: linear-gradient(var(--red-soft), var(--red-soft));
  }
}
.pill--brand.is-hover,
.destination.is-hover,
.pill--icon.is-hover,
.brandmenu__item.is-hover {
  background-image: linear-gradient(var(--red-soft), var(--red-soft));
}

/* ─── Pill variant: brand ───────────────────────────────────── */

/* Base */
.pill--brand {
  height: 48px;
  padding: 0 14px 0 10px;
  gap: 8px;
  cursor: pointer;
  transition:
    color var(--duration-fast),
    background var(--duration-fast),
    box-shadow var(--duration-fast);
}
.brand__mark {
  width: 20px; height: 20px;
  display: block;
  /* Inner radius rhymes with the pill's full-round outer edge — a soft
   * concentric corner (outer 24px radius − 14px mark inset ≈ 10px, eased
   * to 5px so the mark still reads as a square color-field, just softened
   * at the corners). */
  border-radius: 5px;
  overflow: hidden;
  transition: transform var(--duration-fast), filter var(--duration-fast);
}
/* Mark internals are opaque quadrant fills baked into the SVG assets.
 * iOS Safari flattens semi-transparent overlapping SVG bars inside this
 * pill chrome, even when the SVG is loaded through <img>. */
/* Wordmark — lowercase italic Instrument Serif, bumped a touch and tracked
 * loose. Lowercase keeps it intimate (a name spoken across a kitchen, not
 * announced); italic carries the editorial-cozy voice the whole kit uses for
 * its display type. Inherits currentColor from the pill so the hover/open
 * red treatment applies to the whole wordmark. */
.brand__name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: 1.45rem;
  line-height: 1;
  letter-spacing: 0.005em;
  font-feature-settings: "kern" 1, "liga" 1, "dlig" 1;
  /* Match the rest of the navbar's ink weight (destinations + icons use
   * --text-soft); hover/open still flips to --red via the inherited color
   * cascade below. */
  color: var(--text-soft);
  /* The wordmark is all lowercase, so its bounding box (which includes the
   * descender on 'p') sits low — visually the x-height centerline lands
   * below the pill's centerline. Lift it so the cross of the 'x' aligns
   * with the pill's geometric center. */
  transform: translateY(-1.5px);
  transition: transform var(--duration-fast), text-shadow var(--duration-fast);
}
.brand__caret {
  /* Composed transform: rotate is set by [aria-expanded="true"] in the
   * open state below, translateY is set by :active in the press state.
   * Each modifies one custom property, so press + open stack instead of
   * clobbering each other.
   *
   * The glyph is a downward chevron at rest, morphing to × on open via
   * two motions running together on the inner <g>:
   *   1. The two strokes' bottom endpoints slide apart horizontally to
   *      cross at the centre (positions change, orientation doesn't).
   *   2. The group spins 90° clockwise — the rotation is purely a
   *      flourish (the strokes are already at ±45° so a 90° turn leaves
   *      them at ±45°, still forming an ×), but with --ease-bounce it
   *      overshoots ~10°, rubber-bands back, and gives the morph its
   *      physical, momentum-laden feel. Lifted from LoveFrom's info
   *      button — a one-stroke i→× spin with the same overshoot.
   * overflow: visible so the spring's overshoot doesn't clip strokes
   * against the SVG viewport edge. */
  --caret-y: 0px;
  color: var(--text-light);
  margin-left: 2px;
  overflow: visible;
  transform: translateY(var(--caret-y));
  transition:
    transform var(--duration-fast),
    color var(--duration-fast),
    filter var(--duration-fast);
}
.brand__caret-g {
  transform-box: fill-box;
  transform-origin: center;
  transform: rotate(0deg);
  transition: transform 0.42s var(--ease-bounce);
}
.brand__caret-l,
.brand__caret-r {
  transition: transform 0.42s var(--ease-bounce);
}
@media (prefers-reduced-motion: reduce) {
  /* Flatten the spring — straight cross-fade-style transition, no overshoot. */
  .brand__caret-g,
  .brand__caret-l,
  .brand__caret-r {
    transition-duration: var(--duration-fast);
    transition-timing-function: ease-out;
  }
}

/* States — brand pill.
 *   :hover         → wordmark, mark, and caret turn red with a backlit-paper
 *                    halo. Background tint is handled by the shared :hover
 *                    rule above.
 *   :focus-visible → red outline, offset so the pill silhouette stays
 *   :active        → red-soft-2 fill + deboss-deep shadow. Inner contents
 *                    (mark, name, caret) translate down 1 px so they settle
 *                    into the recess. The caret uses custom-prop composition
 *                    so the press translate stacks with the open rotate.
 *   [aria-expanded="true"] → same red + halo as hover, plus the caret flips.
 *                    The caret rotation is the differentiator from hover.
 */

/* :hover — halos on inner elements (background tint is shared above) */
@media (hover: hover) {
  .pill--brand:hover { color: var(--red); }
  .pill--brand:hover .brand__mark { filter: var(--halo-icon); }
  .pill--brand:hover .brand__name { text-shadow: var(--halo-text); }
  .pill--brand:hover .brand__caret {
    color: var(--red);
    filter: var(--halo-icon);
  }
}
.pill--brand.is-hover { color: var(--red); }
.pill--brand.is-hover .brand__mark { filter: var(--halo-icon); }
.pill--brand.is-hover .brand__name { text-shadow: var(--halo-text); }
.pill--brand.is-hover .brand__caret {
  color: var(--red);
  filter: var(--halo-icon);
}

/* :focus-visible */
.pill--brand:focus-visible,
.pill--brand.is-focus {
  outline: 2px solid var(--red);
  outline-offset: 2px;
}

/* :active — deboss-deep + inner content settles 1 px down */
.pill--brand:active,
.pill--brand.is-pressed {
  background: var(--red-soft-2);
  box-shadow: var(--shadow-deboss-deep);
}
.pill--brand:active .brand__mark,
.pill--brand.is-pressed .brand__mark {
  transform: translateY(1px);
}
.pill--brand:active .brand__name,
.pill--brand.is-pressed .brand__name {
  /* Compose with the resting -1.5px lift so press still settles 1 px down. */
  transform: translateY(-0.5px);
}
.pill--brand:active .brand__caret,
.pill--brand.is-pressed .brand__caret { --caret-y: 1px; }

/* [aria-expanded="true"] — menu open */
.pill--brand[aria-expanded="true"],
.pill--brand.is-open { color: var(--red); }
.pill--brand[aria-expanded="true"] .brand__mark,
.pill--brand.is-open .brand__mark { filter: var(--halo-icon); }
.pill--brand[aria-expanded="true"] .brand__name,
.pill--brand.is-open .brand__name { text-shadow: var(--halo-text); }
.pill--brand[aria-expanded="true"] .brand__caret,
.pill--brand.is-open .brand__caret {
  color: var(--red);
  filter: var(--halo-icon);
}
.pill--brand[aria-expanded="true"] .brand__caret-g,
.pill--brand.is-open .brand__caret-g { transform: rotate(90deg); }
.pill--brand[aria-expanded="true"] .brand__caret-l,
.pill--brand.is-open .brand__caret-l { transform: translateX(1.5px); }
.pill--brand[aria-expanded="true"] .brand__caret-r,
.pill--brand.is-open .brand__caret-r { transform: translateX(-1.5px); }

/* ─── Pill variant: destinations (the floating menu plate) ─── */

/* Base */
.pill--destinations {
  height: 48px;
  /* padding(3) + border(1) = 4px ring on every side, matching the inter-item gap.
   * Inner padding-box is exactly 40px, the destination button's height. */
  padding: 3px;
  gap: 4px;
  /* override .pill's inline-flex with proper flex so children layout cleanly */
  display: flex;
}

.destination {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  background: none;
  border: none;
  border-radius: var(--radius-pill);
  padding: 2px 14px 3px;
  min-width: 60px;
  color: var(--text-soft);
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  -webkit-tap-highlight-color: transparent;
  transition:
    background var(--duration-fast),
    color var(--duration-fast),
    box-shadow var(--duration-fast),
    text-shadow var(--duration-fast);
}
.destination__icon {
  display: flex;
  transition: filter var(--duration-fast), transform var(--duration-fast);
}
.destination__icon > svg { display: block; }
.destination__label {
  line-height: 1;
  transition: transform var(--duration-fast);
}

/* States — destination.
 *   :hover         → red text/icon + backlit-paper halo. Background tint is
 *                    handled by the shared :hover rule above (which skips
 *                    the current page so its inset-card chrome isn't fought
 *                    by the hover bg).
 *   :focus-visible → red outline + red text, offset 1 px because the parent
 *                    pill is tight (2 px offset would clip the next sibling)
 *   :active        → red-soft-2 bg + red-deep text + deboss. Inner icon +
 *                    label translate down 1 px. Wins over [aria-current]
 *                    so the current button still gives press feedback.
 *   [aria-current="page"] → emboss — a recessed paper plate inside the host
 *                    pill.
 */

/* :hover — halos on text and icon. Applies even when the destination is
 * the current page — the halo glow is a touch of life on top of the emboss
 * (and matches every other interactive button in the system). Only the bg
 * tint above stays gated off, because it would flatten the inset card. */
@media (hover: hover) {
  .destination:hover {
    color: var(--red);
    text-shadow: var(--halo-text);
  }
  .destination:hover .destination__icon {
    filter: var(--halo-icon);
  }
}
.destination.is-hover {
  color: var(--red);
  text-shadow: var(--halo-text);
}
.destination.is-hover .destination__icon { filter: var(--halo-icon); }

/* :focus-visible */
.destination:focus-visible,
.destination.is-focus {
  outline: 2px solid var(--red);
  outline-offset: 1px;
  color: var(--red);
}

/* :active — deboss + inner content settles 1 px down. The
 * [aria-current="page"]:active selector is at specificity (0,2,1) so it
 * beats the [aria-current] (0,2,0) emboss below — clicking the current page
 * (a no-op) still gives press feedback. */
.destination:active,
.destination[aria-current="page"]:active,
.destination.is-pressed {
  background: var(--red-soft-2);
  color: var(--red-deep);
  box-shadow: var(--shadow-deboss);
}
.destination:active .destination__icon,
.destination:active .destination__label,
.destination[aria-current="page"]:active .destination__icon,
.destination[aria-current="page"]:active .destination__label,
.destination.is-pressed .destination__icon,
.destination.is-pressed .destination__label {
  transform: translateY(1px);
}

/* [aria-current="page"] — the current page */
.destination[aria-current="page"],
.destination.is-current {
  color: var(--red);
  background: var(--content-card-bg);
  box-shadow: var(--shadow-emboss);
}

/* ─── Pill variant: icon-only square (search, help) ─────────── */

/* Base */
.pill--icon {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: var(--text-soft);
  padding: 0;
  transition:
    color var(--duration-fast),
    background var(--duration-fast),
    box-shadow var(--duration-fast);
}
.pill--icon > svg,
.pill--icon > [data-icon] > svg { display: block; }
.pill--icon > [data-icon],
.pill--icon > svg {
  transition: filter var(--duration-fast), transform var(--duration-fast);
}

/* Label modifier — pill--icon with a visible text label alongside its icon.
 * Used for the Sign-in pill in the right cluster (icon-only is harder to
 * discover for non-recurring controls). Overrides the fixed 48 px width and
 * zero padding so the label and icon both have room. */
.pill--icon.pill--with-label {
  width: auto;
  padding: 0 14px;
  gap: 8px;
}
.pill__label {
  font-family: var(--font-body);
  font-size: var(--type-sm);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: inherit;
}

/* States — icon pill (search, help).
 *   :hover         → icon turns red with a backlit-paper halo. Background
 *                    tint is shared above.
 *   :focus-visible → red outline + red icon, offset 2 px
 *   :active        → red-soft-2 bg + deboss-deep. Icon translates down 1 px
 *                    into the recess. Wins over [aria-expanded] so press
 *                    feedback fires even when the overlay is open.
 *   [aria-expanded="true"] → "modal open" state. A persistent lighter deboss
 *                    with red-soft fill so the pill reads as a held-down
 *                    toggle while its overlay is showing. navshell.js
 *                    mirrors aria-expanded onto the trigger. (Only the
 *                    search button uses this; help has no overlay.)
 */

/* :hover — icon halo (bg tint shared above) */
@media (hover: hover) {
  .pill--icon:hover { color: var(--red); }
  .pill--icon:hover > [data-icon] > svg,
  .pill--icon:hover > svg { filter: var(--halo-icon); }
}
.pill--icon.is-hover { color: var(--red); }
.pill--icon.is-hover > [data-icon] > svg,
.pill--icon.is-hover > svg { filter: var(--halo-icon); }

/* :focus-visible */
.pill--icon:focus-visible,
.pill--icon.is-focus {
  outline: 2px solid var(--red);
  outline-offset: 2px;
  color: var(--red);
}

/* :active — full pill deboss + inner icon settles down */
.pill--icon:active,
.pill--icon[aria-expanded="true"]:active,
.pill--icon.is-pressed {
  color: var(--red-deep);
  background: var(--red-soft-2);
  box-shadow: var(--shadow-deboss-deep);
}
.pill--icon:active > [data-icon],
.pill--icon:active > svg,
.pill--icon[aria-expanded="true"]:active > [data-icon],
.pill--icon[aria-expanded="true"]:active > svg,
.pill--icon.is-pressed > [data-icon],
.pill--icon.is-pressed > svg {
  transform: translateY(1px);
}

/* [aria-expanded="true"] — modal open (held-down toggle) */
.pill--icon[aria-expanded="true"],
.pill--icon.is-active {
  color: var(--red);
  background: var(--red-soft);
  box-shadow: var(--shadow-deboss-modal);
}

/* ============================================================================
 * .navshell — the nav layout shell.
 *
 * Two slot containers:
 *   .navshell__top   — top strip (always renders brand + help, optionally
 *                      destinations + search when not cramped)
 *   .navshell__dock  — bottom floating dock (holds destinations + search
 *                      when cramped, otherwise empty/invisible)
 *
 * JS toggles `data-cramped` on .navshell and moves pills between slots.
 * CSS nav layout keys off the .navshell attribute.
 * ========================================================================= */

.navshell {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  pointer-events: none;
}
.navshell * { pointer-events: auto; }

/* Remove the default anchor underline on nav links (destinations,
 * brand link, sign-in pill, help, brandmenu items). The brand menu
 * popover lives inside .navshell via the .brand-host display:contents
 * wrapper, so this selector covers it too. Hover/focus states paint
 * their own affordance — halo, emboss, deboss — instead. */
.navshell a { text-decoration: none; }

/* While JS is measuring on first paint, keep things invisible so the user
 * doesn't see a flash of the wrong layout. The JS clears this on the next
 * frame after applying the right layout. */
.navshell[data-measuring="true"] { visibility: hidden; }
.navshell[data-measuring="true"] .navshell__top { width: max-content; }

/* Top strip — three columns: brand | (destinations centered) | (search + help).
 *
 * max-width tracks the content card. Under the universal border-box reset,
 * main's max-width is its outer extent, so main at max-width 41rem caps its
 * visible card edges at 41rem (content area ~35rem after 3rem side padding).
 * Add the nav's own padding on each side and the strip caps at the same
 * horizontal extent as main's visible card edges.
 * margin-inline: auto centers it inside a wider viewport. Padding and gap
 * both key off --gingham-gap so the pills land flush with the content
 * card's outer edges and the same spacing token drives both surfaces.
 *
 * --gingham-gap shrinks at the narrow breakpoint (max-width: 720px) in
 * base.css, so the strip's padding + horizontal cap track that change
 * automatically — no second media query needed. */
.navshell__top {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--gingham-gap);
  padding: var(--gingham-gap);
  max-width: calc(41rem + 2 * var(--gingham-gap));
  margin-inline: auto;
  box-sizing: border-box;
  pointer-events: none;        /* the strip itself doesn't catch clicks */
}
.navshell__top > * { pointer-events: auto; }

.navshell__slot--brand   { justify-self: start;  position: relative; }
.navshell__slot--center  { justify-self: center; }
.navshell__slot--right   {
  justify-self: end;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Note: nav pills are a single uniform size (48px) on desktop and in the
 * mobile dock. One size means one set of paddings/radii to keep aligned. */

/* ─── Brand menu popover ────────────────────────────────────── */

.brandmenu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: var(--z-nav-overflow);
  width: 268px;
  background: var(--content-card-bg);
  border: 1px solid var(--rule);
  border-radius: 8px;
  box-shadow: var(--shadow-popover);
  padding: 6px;
  animation: brandmenu-in 0.18s var(--ease-curtain);
}
.brandmenu[hidden] { display: none; }
@keyframes brandmenu-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0);    }
}
.brandmenu__header {
  padding: 10px 12px 12px;
  border-bottom: 1px solid var(--rule-faint);
  margin-bottom: 6px;
}
.brandmenu__kitchen {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.15rem;
  color: var(--text);
  line-height: 1;
}
.brandmenu__user {
  font-family: var(--font-body);
  font-size: 0.72rem;
  color: var(--text-light);
  letter-spacing: 0.04em;
  margin-top: 4px;
}
.brandmenu__list { list-style: none; padding: 0; margin: 0; }

/* Base */
.brandmenu__item {
  display: grid;
  grid-template-columns: 24px 1fr;
  grid-template-rows: auto auto;
  gap: 1px 12px;
  align-items: center;
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  padding: 8px 10px;
  border-radius: 5px;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    background var(--duration-fast),
    box-shadow var(--duration-fast),
    color var(--duration-fast),
    text-shadow var(--duration-fast),
    padding var(--duration-fast);
}
.brandmenu__icon {
  grid-row: 1 / span 2;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-soft);
  width: 24px; height: 24px;
  transition: color var(--duration-fast), filter var(--duration-fast);
}
.brandmenu__icon > svg { display: block; }
.brandmenu__label {
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--text);
  grid-column: 2;
  transition: color var(--duration-fast), text-shadow var(--duration-fast);
}
.brandmenu__hint {
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.78rem;
  color: var(--text-light);
  grid-column: 2;
  line-height: 1.2;
}

/* Quiet variant base — Sign out is visually demoted from the regular items. */
.brandmenu__item--quiet .brandmenu__label { color: var(--text-soft); }

/* States — brand menu item.
 *   :hover         → label/icon turn red with backlit halo. Background tint
 *                    is handled by the shared :hover rule above.
 *   :focus-visible → embossed paper card — raised box-shadow + 1 px inset
 *                    border + red text/icon. Rhymes with the destination
 *                    [aria-current="page"] card-on-pill look. No left bar,
 *                    no halo — the raised treatment is the affordance.
 *   :active        → red-soft-2 + deboss. Inner contents settle 1 px down
 *                    via a padding shift (top +1, bottom -1) so the whole
 *                    row moves uniformly as a single grid reflow. No
 *                    per-child transforms (those drifted at different rates
 *                    because of how filters and text-shadows rasterize).
 *
 * The quiet variant (Sign out) keeps a neutral vocabulary: warm-gray hover
 * (via its own override, since the shared red-soft hover would pull brand
 * red onto a destructive action), neutral embossed focus, rule-faint press;
 * no red glow anywhere.
 */

/* :hover — halos on label and icon (bg tint shared above) */
@media (hover: hover) {
  .brandmenu__item:hover .brandmenu__label {
    color: var(--red);
    text-shadow: var(--halo-text);
  }
  .brandmenu__item:hover .brandmenu__icon {
    color: var(--red);
    filter: var(--halo-icon);
  }
  /* Quiet variant overrides the shared red-soft tint with a neutral
   * warm-gray and skips the halo. */
  .brandmenu__item--quiet:hover {
    background-image: linear-gradient(var(--surface-alt), var(--surface-alt));
  }
  .brandmenu__item--quiet:hover .brandmenu__label,
  .brandmenu__item--quiet:hover .brandmenu__icon {
    color: var(--text);
    text-shadow: none;
    filter: none;
  }
}
.brandmenu__item.is-hover .brandmenu__label {
  color: var(--red);
  text-shadow: var(--halo-text);
}
.brandmenu__item.is-hover .brandmenu__icon {
  color: var(--red);
  filter: var(--halo-icon);
}
.brandmenu__item--quiet.is-hover {
  background-image: linear-gradient(var(--surface-alt), var(--surface-alt));
}
.brandmenu__item--quiet.is-hover .brandmenu__label,
.brandmenu__item--quiet.is-hover .brandmenu__icon {
  color: var(--text);
  text-shadow: none;
  filter: none;
}

/* :focus-visible — embossed paper card. Uses --surface-alt (slightly
 * different from the popover's --content-card-bg in both light and dark
 * modes) so the raised card actually reads against the popover. The
 * bordered emboss adds a subtle 1 px inset edge so the silhouette is
 * legible even when the bg contrast is low. */
.brandmenu__item:focus-visible,
.brandmenu__item.is-focus,
.brandmenu__item--quiet:focus-visible,
.brandmenu__item--quiet.is-focus {
  outline: none;
  background: var(--surface-alt);
  box-shadow: var(--shadow-emboss-bordered);
}
.brandmenu__item:focus-visible,
.brandmenu__item.is-focus { color: var(--red); }
.brandmenu__item:focus-visible .brandmenu__label,
.brandmenu__item:focus-visible .brandmenu__icon,
.brandmenu__item.is-focus .brandmenu__label,
.brandmenu__item.is-focus .brandmenu__icon { color: var(--red); }
.brandmenu__item--quiet:focus-visible .brandmenu__label,
.brandmenu__item--quiet:focus-visible .brandmenu__icon,
.brandmenu__item--quiet.is-focus .brandmenu__label,
.brandmenu__item--quiet.is-focus .brandmenu__icon {
  color: var(--text);
  text-shadow: none;
  filter: none;
}

/* :active — deboss + padding shift (top +1, bottom -1) so the whole grid
 * content reflows down 1 px as one unit. */
.brandmenu__item:active,
.brandmenu__item.is-pressed {
  padding: 9px 10px 7px;
  background: var(--red-soft-2);
  box-shadow: var(--shadow-deboss);
}
.brandmenu__item:active .brandmenu__label,
.brandmenu__item:active .brandmenu__icon,
.brandmenu__item.is-pressed .brandmenu__label,
.brandmenu__item.is-pressed .brandmenu__icon { color: var(--red-deep); }

.brandmenu__item--quiet:active,
.brandmenu__item--quiet.is-pressed {
  padding: 9px 10px 7px;
  background: var(--rule-faint);
  box-shadow: var(--shadow-deboss);
}
.brandmenu__item--quiet:active .brandmenu__label,
.brandmenu__item--quiet:active .brandmenu__icon,
.brandmenu__item--quiet.is-pressed .brandmenu__label,
.brandmenu__item--quiet.is-pressed .brandmenu__icon { color: var(--text); }
.brandmenu__divider {
  height: 1px;
  background: var(--rule-faint);
  margin: 6px 0;
}

/* ─── Search overlay ──────────────────────────────────────────
 *
 * The markup uses native <dialog class="search-overlay"> opened via
 * showModal() (search_overlay_controller.js). The browser places the
 * dialog in the top layer and emits a ::backdrop pseudo-element for
 * the dim layer. We unset the dialog's native chrome and style the
 * inner .search-panel as the floating paper card.
 *
 * Class names match the existing ERB partial and the dynamic class
 * names emitted by the controller (.search-result, .search-result-title,
 * .search-result-category, .search-no-results, .selected).
 * ─────────────────────────────────────────────────────────── */

dialog.search-overlay {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  border: none;
  background: transparent;
  padding: 14% 16px 24px;
  box-sizing: border-box;
  z-index: var(--z-overlay);
  overflow: visible;
  display: flex;
  align-items: flex-start;
  justify-content: center;
}
dialog.search-overlay:not([open]) { display: none; }
dialog.search-overlay::backdrop {
  background: rgba(40, 24, 22, 0.42);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  animation: fade-in var(--duration-normal) var(--ease-curtain);
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.search-panel {
  position: relative;
  width: 100%;
  max-width: 32rem;
  background: var(--content-card-bg);
  border: 1px solid var(--rule);
  border-radius: 12px;
  box-shadow: var(--shadow-popover-lg);
  overflow: hidden;
  animation: paper-in var(--duration-curtain) var(--ease-curtain);
}
@keyframes paper-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0);    }
}
.search-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("/assets/paper-noise-94045ff2.png");
  background-size: 200px 200px;
  opacity: 0.05;
  pointer-events: none;
}
.search-panel > * { position: relative; }

.search-input-wrapper {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--rule-faint);
}
.search-icon {
  color: var(--text-light);
  display: flex;
  align-items: center;
  font-size: 1.2rem;
  line-height: 1;
}
.search-icon > svg { display: block; }

/* Container for grocery quick-add chips and similar context pills.
 * Empty by default; populated by search_overlay_controller. */
.search-pill-area {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.search-pill-area:empty { display: none; }

.search-input {
  all: unset;
  flex: 1;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.35rem;
  color: var(--text);
  min-width: 0;
  line-height: 1.2;
}
.search-input::placeholder { color: var(--text-light); }

.search-results {
  list-style: none;
  margin: 0;
  padding: 6px 6px 10px;
  max-height: 60vh;
  overflow-y: auto;
}
.search-results:empty { display: none; }

.search-result {
  display: flex;
  flex-direction: column;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 10px 14px;
  border-radius: 6px;
  cursor: pointer;
  font: inherit;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--duration-fast);
}
.search-result:hover,
.search-result:focus-visible,
.search-result.selected {
  background: var(--red-soft);
  outline: none;
}
.search-result-title {
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--red);
  line-height: 1.2;
}
.search-result-category {
  font-family: var(--font-body);
  font-size: 0.74rem;
  color: var(--text-light);
  letter-spacing: 0.04em;
  margin-top: 2px;
}
.search-no-results {
  padding: 16px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 0.95rem;
  color: var(--text-light);
  text-align: center;
}

/* Grocery quick-add section — emitted by the controller above or below
 * the recipe results depending on tier of match. */
.grocery-section--above,
.grocery-section--below {
  list-style: none;
  margin: 0;
  padding: 0;
}
.grocery-section--above {
  border-bottom: 1px solid var(--rule-faint);
  padding-bottom: 6px;
  margin-bottom: 6px;
}
.grocery-section--below {
  border-top: 1px solid var(--rule-faint);
  padding-top: 6px;
  margin-top: 6px;
}

/* Cramped layout — when nav_shell_controller flips data-cramped="true",
 * the dock becomes visible and the body needs bottom padding to clear it.
 * (In the design system, .appwrap[data-cramped="true"] handled this; we
 * apply it to body via :has() since the navshell sits at body level.)
 */
body:has(.navshell[data-cramped="true"]) {
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 80px);
}

/* Dock shrink-wraps to its content (destinations pill + search) and centers
 * via the 50%/translateX(-50%) idiom. `left: 0; right: 0` + padding was
 * pulling the dock to full viewport width, which on narrow iPhones added
 * 32px of horizontal padding that pushed the search pill off the right edge
 * (4 destinations × ~80px + gap + 48px search already approaches a 375px
 * viewport). Lifted from the design-system source. */
.navshell[data-cramped="true"] .navshell__dock {
  position: fixed;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: var(--z-nav);
}

/* Dock-mode destination overrides. The design-system source was authored
 * against the browser default 16px root; the live app's 18px root (set in
 * base.css for readability) blows every rem in the destinations pill up
 * by ~13%, pushing the dock content past iPhone viewport widths. Override
 * label font-size and horizontal padding with absolute px so the cramped
 * dock matches the reference's effective dimensions regardless of root.
 * Top-strip destinations keep the rem-based sizing — they only render at
 * desktop widths where the extra slack doesn't matter. */
.navshell[data-cramped="true"] .destination {
  padding: 2px 10px 3px;
  min-width: 52px;
}
.navshell[data-cramped="true"] .destination__label {
  font-size: 10px;
}

.navshell:not([data-cramped="true"]) .navshell__dock {
  display: none;
}
