/* Recipe preview pane.

   Production progressive enhancement for /recipes. The card links
   remain real /recipes/{slug} anchors; this component owns only the
   enhanced read-and-decide preview surface.

   B2 (#300) ports the converged side-sheet design from
   ~/Developer/recipe-masonry/recipe-detail-sidesheet.html: a contained
   hero, an abbreviated byline, Save + "See full recipe", a static
   facts bar, and a one-rail icon-tile ingredient list.

   #329 PR3 (bug #9) deleted the in-pane expanded full-detail state.
   "See full recipe" is now a plain page anchor to /recipes/{slug}
   (with ?focus=<slug> for return-scroll restore). The .is-expanded
   modifier, the .recipe-detail--in-pane wrapper, the body.recipe-
   preview-expanded class, the data-recipe-preview-expand /
   data-recipe-preview-collapse / data-recipe-preview-expanded
   hooks, and the --recipe-preview-expanded-width token are all
   gone -- nothing in the pane now widens it.

   The selectors recipe-preview.js depends on (.recipe-preview,
   .recipe-preview__scroll, .recipe-preview__title, .recipe-preview__body,
   [data-recipe-preview-panel], [data-recipe-preview-close],
   .is-open / .is-entering / .is-switching) are load-bearing --
   update the JS in lockstep if any of them changes. */

@layer components {
  .recipe-preview-slot {
    display: contents;
  }

  .recipes-index {
    --recipe-preview-width: 540px;
    --recipe-preview-gap: var(--space-6);
    transition: padding-inline-end var(--motion-spring-default-spatial-duration)
      var(--motion-spring-default-spatial);
  }

  @media (min-width: 769px) {
    .recipes-index.is-preview-open {
      padding-inline-end: calc(var(--recipe-preview-width) + var(--recipe-preview-gap));
    }
  }

  .recipe-card.is-preview-selected {
    outline: 2px solid var(--md-primary);
    outline-offset: 3px;
    border-radius: var(--radius-lg);
  }

  .recipe-card.is-preview-selected::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow: inset 0 0 0 1px var(--md-primary-container);
    pointer-events: none;
  }

  .recipe-preview {
    position: fixed;
    inset: 0 0 0 auto;
    z-index: 50;
    width: var(--recipe-preview-width, 540px);
    max-width: 92vw;
    background: var(--md-surface);
    border-inline-start: 1px solid var(--md-border-muted);
    box-shadow: var(--shadow-xl);
    transform: translateX(100%);
    transition: transform var(--motion-spring-default-spatial-duration)
      var(--motion-spring-default-spatial);
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }

  .recipe-preview.is-open {
    transform: translateX(0);
  }

  /* Sticky top bar: the close affordance is the only chrome on the
     bar (Save lives in the action row inside the body now). Subtle
     backdrop blur so the bar reads cleanly when the body scrolls
     under it. */
  .recipe-preview__topbar {
    position: sticky;
    inset-block-start: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    padding: var(--space-3) var(--space-4);
    background: color-mix(in srgb, var(--md-surface) 92%, transparent);
    backdrop-filter: blur(8px);
    border-block-end: 1px solid var(--md-border-muted);
  }

  .recipe-preview__icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--md-on-surface-variant);
    cursor: pointer;
    transition:
      background var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .recipe-preview__icon-btn:hover {
    background: var(--md-surface-container);
    color: var(--md-on-surface);
  }

  .recipe-preview__icon-btn:focus-visible {
    outline: none;
    box-shadow:
      0 0 0 3px var(--md-primary-container),
      0 0 0 4px var(--md-primary);
  }

  .recipe-preview__scroll {
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    min-height: 0;
    flex: 1;
  }

  /* Contained hero -- sized to the pane, locked aspect. Honest no-
     image treatment is achieved by omitting the <header> entirely
     in the template; no placeholder chrome here. */
  .recipe-preview__hero {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    background: var(--md-surface-container);
    overflow: hidden;
  }

  /* Hero img cross-fade on swap (#329 PR1, bug #7b).
     Each swap replaces the panel partial wholesale, so the hero <img>
     is a fresh DOM node. Without an explicit fade, the image hard-cuts
     -- jarring against the body fade-out / fade-in around it. The
     image starts at opacity 0; recipe-preview.js adds .is-loaded once
     the image has decoded (or immediately if cached), triggering the
     opacity transition. Duration matches the body fade tier
     (--motion-spring-fast-effects, ~180ms) per bible §3.2. */
  .recipe-preview__hero-image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity var(--motion-spring-fast-effects-duration)
      var(--motion-spring-fast-effects);
  }

  .recipe-preview__hero-image.is-loaded {
    opacity: 1;
  }

  .recipe-preview__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    padding: var(--space-6);
    opacity: 1;
    transition: opacity var(--motion-spring-fast-effects-duration)
      var(--motion-spring-fast-effects);
  }

  .recipe-preview.is-switching .recipe-preview__body {
    opacity: 0;
  }

  .recipe-preview.is-entering .recipe-preview__body > * {
    animation: recipe-preview-enter 260ms var(--motion-spring-default-spatial) both;
  }

  .recipe-preview.is-entering .recipe-preview__body > :nth-child(2) { animation-delay: 25ms; }
  .recipe-preview.is-entering .recipe-preview__body > :nth-child(3) { animation-delay: 50ms; }
  .recipe-preview.is-entering .recipe-preview__body > :nth-child(4) { animation-delay: 75ms; }
  .recipe-preview.is-entering .recipe-preview__body > :nth-child(n+5) { animation-delay: 100ms; }

  @keyframes recipe-preview-enter {
    from {
      opacity: 0;
      transform: translateY(6px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  .recipe-preview__kicker {
    margin: 0;
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--md-on-surface-variant);
  }

  /* The title rule is scoped to descend from .recipe-preview so the
     dual-classed h1 inside recipe_detail_body.html (where the title
     carries both .recipe-detail__title and .recipe-preview__title --
     load-bearing for recipe-preview.js's focusPreview() selector,
     Slice 2 #304) does NOT restyle the standalone /recipes/{slug}
     page. On the page, only .recipe-detail__title applies; in the
     pane, both apply with .recipe-preview .recipe-preview__title
     taking precedence on the shared declarations. */
  .recipe-preview .recipe-preview__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: clamp(1.6rem, 4vw, 2.1rem);
    line-height: 1.1;
    font-weight: 500;
    letter-spacing: -0.02em;
    color: var(--md-on-surface);
    text-wrap: balance;
    overflow-wrap: anywhere;
  }

  .recipe-preview__desc {
    margin: 0;
    color: var(--md-on-surface-variant);
    font-family: var(--font-heading);
    font-style: italic;
    font-size: var(--text-base);
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* Abbreviated one-line byline. Distinct shape from the full page's
     creator block: no avatar, no bio, no "More recipes from ..." link
     -- those live on the full /recipes/{slug} page. */
  .recipe-preview__byline {
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--md-on-surface);
  }

  .recipe-preview__byline-name {
    font-weight: 600;
  }

  .recipe-preview__byline-verified {
    display: inline-flex;
    align-items: center;
    color: var(--md-primary);
  }

  .recipe-preview__byline-verified .icon {
    width: 14px;
    height: 14px;
  }

  /* Action row: the labelled Save button + the See-full-recipe
     secondary outline button. The Save button reuses the shared
     recipe_save_button partial (same component the full /recipes/{slug}
     page uses); the secondary button is local to the preview. */
  .recipe-preview__actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
  }

  .recipe-preview__btn-secondary {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    border-radius: var(--radius-md);
    border: 1px solid var(--md-border-muted);
    background: var(--md-surface);
    color: var(--md-on-surface);
    font-size: var(--text-sm);
    font-weight: 600;
    text-decoration: none;
    transition:
      border-color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      transform var(--motion-spring-fast-spatial-duration) var(--motion-spring-fast-spatial);
  }

  .recipe-preview__btn-secondary:hover {
    border-color: var(--md-on-surface-variant);
  }

  .recipe-preview__btn-secondary:active {
    transform: scale(0.98);
  }

  .recipe-preview__btn-secondary:focus-visible {
    outline: none;
    box-shadow:
      0 0 0 3px var(--md-primary-container),
      0 0 0 4px var(--md-primary);
  }

  .recipe-preview__btn-secondary .icon {
    width: 16px;
    height: 16px;
    color: var(--md-on-surface-variant);
  }

  /* Single-line factsbar (#329 PR2, bug #8) -- PREP 15m · TOTAL 25m ·
     SERVES 4. Replaces the prior 2x2 label-on-top / value-below stack.
     Monospace caps labels paired with heading-face values, mid-dot
     separators owned by the following triplet (so dropping a middle
     triplet doesn't orphan a separator). Wraps to two lines on
     narrow widths; the structure stays tight per the spec. */
  .recipe-preview__factsbar {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2) var(--space-3);
    padding-block: var(--space-4);
    border-block-start: 1px solid var(--md-border-muted);
    border-block-end: 1px solid var(--md-border-muted);
    line-height: 1.2;
  }

  .recipe-preview__fact {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-2);
  }

  .recipe-preview__fact-label {
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--md-on-surface-variant);
  }

  .recipe-preview__fact-value {
    font-family: var(--font-heading);
    font-size: var(--text-base);
    font-weight: 500;
    color: var(--md-on-surface);
    font-variant-numeric: tabular-nums;
  }

  .recipe-preview__fact-sep {
    color: var(--md-on-surface-variant);
    font-size: var(--text-base);
    line-height: 1;
    user-select: none;
  }

  /* Section heading shared by Ingredients + Nutrition. */
  .recipe-preview__section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
  }

  .recipe-preview__section-head {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding-block-end: var(--space-2);
    border-block-end: 1px solid var(--md-border-muted);
  }

  .recipe-preview__section-heading {
    margin: 0;
    font-family: var(--font-heading);
    font-size: var(--text-xl);
    font-weight: 500;
    letter-spacing: -0.015em;
    color: var(--md-on-surface);
  }

  .recipe-preview__section-count {
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    font-weight: 500;
    color: var(--md-on-surface-variant);
  }

  /* +N ingredient row (#329 PR2, bug #8) -- a fixed left-aligned row
     of up to three 1:1 anchor cells with canonical ingredient icons,
     plus a fourth +N overflow cell when ingredients > 3. Counts <=3
     render 2 or 3 cells with no overflow. Every cell anchors to the
     durable /recipes/{slug} page -- this is a glance, not a list.
     No horizontal scroll; the row never overflows.
     Sizing: 96px desktop, 80px mobile -- pixel literals to match the
     spec range exactly. Matches the existing component-local pixel-
     literal convention (`--recipe-preview-width`, the prior tile width)
     rather than fabricating a global token for a one-off card size. */
  .recipe-preview {
    --recipe-preview-ingcell-size: 96px;
  }

  .recipe-preview__ingrow {
    display: flex;
    flex-wrap: nowrap;
    gap: var(--space-2);
    margin: 0;
    padding: var(--space-1) 0;
  }

  .recipe-preview__ingcell {
    flex: 0 0 var(--recipe-preview-ingcell-size);
    width: var(--recipe-preview-ingcell-size);
    height: var(--recipe-preview-ingcell-size);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--md-surface-container-low);
    border: 1px solid var(--md-border-muted);
    border-radius: var(--radius-md);
    color: var(--md-on-surface);
    text-decoration: none;
    transition:
      border-color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      transform var(--motion-spring-fast-spatial-duration) var(--motion-spring-fast-spatial);
  }

  .recipe-preview__ingcell:hover {
    border-color: var(--md-on-surface-variant);
  }

  .recipe-preview__ingcell:active {
    transform: scale(0.98);
  }

  .recipe-preview__ingcell:focus-visible {
    outline: none;
    box-shadow:
      0 0 0 3px var(--md-primary-container),
      0 0 0 4px var(--md-primary);
  }

  .recipe-preview__ingcell-icon {
    width: 64px;
    height: 64px;
    object-fit: contain;
    filter: drop-shadow(0 2px 4px rgba(24, 24, 27, 0.18));
  }

  .recipe-preview__ingcell-icon--placeholder {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--md-on-surface-variant);
    filter: none;
  }

  .recipe-preview__ingcell-icon--placeholder .icon {
    width: 32px;
    height: 32px;
  }

  /* +N overflow cell -- same border/radius/surface as the icon cells;
     no icon, monospace count anchored to the same recipe URL per spec. */
  .recipe-preview__ingcell--overflow {
    background: var(--md-surface);
  }

  .recipe-preview__ingcell-count {
    font-family: var(--font-mono);
    font-size: var(--text-xl);
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--md-on-surface-variant);
    font-variant-numeric: tabular-nums;
  }

  /* Nutrition designed slot. Data-pending: never with fabricated
     numbers (non-negotiable #2). */
  .recipe-preview__nutrition {
    display: flex;
    align-items: center;
    gap: var(--space-5);
  }

  .recipe-preview__donut {
    position: relative;
    width: 120px;
    height: 120px;
    flex-shrink: 0;
  }

  .recipe-preview__donut-center {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .recipe-preview__donut-dash {
    font-family: var(--font-heading);
    font-size: 1.6rem;
    font-weight: 500;
    color: var(--md-on-surface-variant);
  }

  .recipe-preview__nutrition-pending {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--md-on-surface-variant);
    line-height: 1.45;
  }

  @media (min-width: 769px) and (max-width: 1024px) {
    .recipes-index {
      --recipe-preview-width: 460px;
    }
  }

  @media (max-width: 768px) {
    body.recipe-preview-open {
      overflow: hidden;
    }

    .recipe-preview {
      inset: 0;
      width: 100vw;
      max-width: 100vw;
      border-inline-start: 0;
      border-radius: 0;
      /* Shrink ingredient cells on mobile per spec (#329 PR2). */
      --recipe-preview-ingcell-size: 80px;
    }

    .recipe-preview__hero {
      aspect-ratio: 16 / 9;
    }

    .recipe-preview__body {
      padding: var(--space-5) var(--space-4) calc(var(--space-9) + var(--space-6));
    }

    .recipe-preview .recipe-preview__title {
      font-size: clamp(1.7rem, 7vw, 2.2rem);
    }

    .recipe-preview__ingcell-icon {
      width: 52px;
      height: 52px;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .recipes-index,
    .recipe-preview,
    .recipe-preview__body,
    .recipe-preview__body > *,
    .recipe-preview__hero-image {
      animation: none !important;
      transition: none !important;
    }
    /* The opacity:0 default would leave the hero invisible in
       reduced-motion -- restore visibility without the fade. */
    .recipe-preview__hero-image {
      opacity: 1;
    }
  }
}
