/* Recipes index — shared chrome for /recipes (Explore) and /saved.

   Slice C PR1 made /recipes a masonry surface; /saved kept its
   uniform-grid + htmx chrome. The shared base class .recipes-grid is
   uniform-grid by default. The .recipes-grid--masonry modifier opts
   into the masonry idiom (4-col flow + boundary-flip via container
   queries + JS-driven row spans in explore-layout.js).

   The Saved-page chrome (search bar, sort, load-more, paste-form
   wrappers) lives in components/saved.css — single-consumer, cleanly
   home-named so it doesn't accrete back into this file. The .empty
   class lives here because /recipes uses it for its honest empty
   state ("Nothing here yet." per bible §1.5).

   Cascade-layer note: lives in `components` so reset/layout never
   overrides it, and utility classes always do. */

@layer components {
  /* .recipes-index takes the shared .page-shell utility in the template
     for centering + the --content-max tier (1280px). Per spacing-foundation
     D2 this narrows Explore / the author hub from the prior 1600px literal.
     The container-type stays here — .page-shell sizes the column, the
     container-query context is a recipes-index concern. */
  .recipes-index {
    container-type: inline-size;
  }

  .recipes-index__header {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0 0 var(--space-8);
  }

  .recipes-index__heading {
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: clamp(2.5rem, 5.2vw, 3.5rem);
    letter-spacing: -0.02em;
    line-height: 1.05;
    color: var(--md-on-surface);
    margin: 0;
    text-wrap: balance;
  }

  .recipes-index__filters {
    margin: calc(-1 * var(--space-4)) 0 var(--space-6);
  }

  .recipes-index__filter-strip {
    display: flex;
    gap: var(--space-2);
    min-width: 0;
    scrollbar-width: none;
  }

  .recipes-index__filter-strip::-webkit-scrollbar {
    display: none;
  }

  .recipes-index__filter-strip {
    flex-wrap: nowrap;
    overflow-x: auto;
    /* 2px lead is a sub-token micro-gap (allowlisted); trail snapped 6px -> --space-2. */
    padding-block: 2px var(--space-2);
    scroll-padding-inline: var(--space-2);
  }

  .recipes-index__filter-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    flex: 0 0 auto;
    min-height: 34px;
    max-width: 100%;
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--md-border-muted);
    border-radius: var(--radius-pill);
    background: var(--md-surface);
    color: var(--md-on-surface);
    font-family: var(--font-body);
    font-size: var(--text-sm);
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    box-shadow: none;
    white-space: nowrap;
  }

  .recipes-index__filter-chip:hover {
    border-color: var(--md-border-strong);
    background: var(--md-surface-container);
  }

  .recipes-index__filter-chip.is-active {
    border-color: transparent;
    background: var(--md-primary-container);
    color: var(--md-on-primary-container);
  }

  .recipes-index__filter-chip span:first-child {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .recipes-index__filter-count {
    flex: 0 0 auto;
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--md-on-surface-variant);
  }

  .recipes-index__filter-chip.is-active .recipes-index__filter-count {
    color: var(--md-on-primary-container);
  }

  .recipes-index__result-count {
    margin: calc(-1 * var(--space-2)) 0 var(--space-4);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--md-on-surface-variant);
    text-transform: uppercase;
  }

  /* ==============================================
     CARD GRID — shared gutter + breakpoint canon (spacing-foundation D6)

     /recipes (masonry, container queries) and /saved (uniform grid,
     media queries) keep their separate mechanisms but reflow at the
     SAME observed viewport widths with a MATCHING column count.

     Column-count transitions are pinned to the canonical viewport
     breakpoints --bp-sm/md/lg (640 / 1024 / 1280):

       viewport      < 640   640–1023  1024–1279   >= 1280
       columns         1         2          3          4

     - /saved uses those viewport literals directly in its @media block.
     - /recipes' @container thresholds are DERIVED so the container-query
       transition lands at the same viewport widths — see the derivation
       comment on the @container block below.

     The --gutter ladder is tokenized and SHARED: gutter is keyed to the
     column count, so both grids carry the identical gap at the identical
     column count. 1col -> --space-3, 2col -> --space-4, 3col -> --space-5,
     4col -> --space-6 (the former raw 12/16/20/24 ladder, each an exact
     --space-* token).
     ============================================== */

  /* ==============================================
     UNIFORM GRID — base .recipes-grid (used by /saved)
     ============================================== */
  .recipes-grid {
    /* --gutter steps with the column count — see the shared-gutter note
       above. 1-column default is --space-3 (12px). */
    --gutter: var(--space-3);
    display: grid;
    gap: var(--gutter);
    grid-template-columns: 1fr;
    list-style: none;
    padding: 0;
    margin: 0;
  }

  /* Grid items default to min-content; let cards shrink to 0 so long
     titles or wide images don't push the grid past viewport. */
  .recipes-grid > li {
    min-width: 0;
  }

  /* Column-count + gutter stepped on the canonical --bp-* viewport
     literals (640 / 1024 / 1280). No 5-column step: the masonry tops out
     at 4 columns, so a 5-column /saved would break the matching-count
     contract (I-AC6). */
  @media (min-width: 640px) {
    .recipes-grid {
      --gutter: var(--space-4);
      grid-template-columns: repeat(2, 1fr);
    }
  }
  @media (min-width: 1024px) {
    .recipes-grid {
      --gutter: var(--space-5);
      grid-template-columns: repeat(3, 1fr);
    }
  }
  @media (min-width: 1280px) {
    .recipes-grid {
      --gutter: var(--space-6);
      grid-template-columns: repeat(4, 1fr);
    }
  }

  /* ==============================================
     MASONRY — .recipes-grid--masonry (masonry surfaces)
     ============================================== */
  .recipes-grid--masonry {
    --gutter: var(--space-6);
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    grid-auto-rows: 8px;
    grid-auto-flow: dense;
    gap: var(--gutter);
  }

  .recipes-grid--sparse {
    justify-content: start;
  }

  .recipes-grid--sparse[data-card-count="1"] {
    max-width: min(100%, 23rem);
  }

  .recipes-grid--sparse[data-card-count="2"] {
    max-width: min(100%, 48rem);
  }

  /* Container queries flip column count + gutter. Targets the descendant
     element (not the @container host .recipes-index) since a container
     can't query its own size. JS-driven inline grid-template-columns
     (explore-layout.js destColCount) takes precedence to avoid jitter
     mid-resize; these @container rules are the no-JS fallback and MUST
     stay in lockstep with the JS thresholds.

     DERIVATION (spacing-foundation D6 — the same-viewport-width contract).
     Container queries measure the CONTAINER (.recipes-index inline-size),
     not the viewport, so the masonry thresholds cannot reuse /saved's
     640/1024/1280 viewport literals — they must be the container width
     observed AT those viewports. The chain from viewport edge to the
     .recipes-index container:

       container = page-shell width
                 = min(.app-shell__main content-box, --content-max 1280)

       .app-shell__main content-box:
         viewport < 1024 (no sidebar):  V - 2 * --space-page
         viewport >= 1024 (sidebar):    (V - --sidebar-width) - 2 * --space-page
       --space-page = clamp(16px, 5vw, 48px); --sidebar-width = 112px.

     Container width at each /saved viewport transition:
       V = 640  -> --space-page 32 -> container 640 - 64        = 576
       V = 1024 -> sidebar+48 pad  -> container 1024 - 112 - 96 = 816
       V = 1280 -> sidebar+48 pad  -> container 1280 - 112 - 96 = 1072

     Each @container max-width is set to the MIDPOINT between the two
     container widths bracketing the transition, so sub-pixel rounding
     can't land on the wrong side:
       1<->2 : V=639 c=575.1, V=640 c=576.0  -> midpoint 575.5
       3<->4 : V=1279 c=1071, V=1280 c=1072  -> midpoint 1071.5

     The 2<->3 boundary sits ON the sidebar discontinuity: as the viewport
     crosses 1024 the sidebar appears and the container DROPS 927 -> 816.
     No single threshold separates "2 cols at V=1023 (c=927)" from "3 cols
     at V=1024 (c=816)". We honor the DESKTOP side — threshold 815 puts
     3 cols at every container > 815, so V=1024+ matches /saved exactly
     (and the I-AC6 sample at 1024 is correct). Residual: in the no-sidebar
     band V in [906, 1023] the masonry leads /saved by one column (3 vs 2).
     This is inherent to keeping two coordinate systems (D6) across the
     sidebar step; none of the I-AC6 sample widths fall in that band. */
  @container (max-width: 1071.5px) {
    .recipes-grid--masonry {
      --gutter: var(--space-5);
      grid-template-columns: repeat(3, 1fr);
    }
  }
  @container (max-width: 815px) {
    .recipes-grid--masonry {
      --gutter: var(--space-4);
      grid-template-columns: repeat(2, 1fr);
    }
  }
  @container (max-width: 575.5px) {
    .recipes-grid--masonry {
      --gutter: var(--space-3);
      grid-template-columns: 1fr;
    }
  }

  /* ==============================================
     EXPLORE EMPTY STATE
     Bible §1.5 honest end: heading + one calm subordinate line.
     Left-aligned under the heading per alignment Q9.
     ============================================== */
  .recipes-index__empty {
    margin: 0;
    font-family: var(--font-body);
    font-size: var(--text-base);
    color: var(--md-on-surface-variant);
    max-width: 32ch;
    /* Inherit .recipes-index__header's bottom-margin rhythm above; no
       top-margin so the line sits flush against that rhythm. */
  }
}
