@charset "utf-8";
/* ============================================================================
   Components — the page archetypes that sit on top of site.css
   Loaded by hub, journal, editorial and food pages.

   Consolidates drift from the previous build, where the same component
   existed twice under different names:
       .pcard / .mcard   ->  .rack-card
       .rack  / .mrack   ->  .rack (with a density modifier)
   ========================================================================== */

@layer components {

  /* ---- page head (was .rack-hero) ------------------------------------- */
  .page-head { padding-block: var(--sp-8) var(--sp-1); }
  .page-head h1 { font-size: var(--text-3xl); }
  .page-head .sub,
  .page-head__sub {
    color: var(--ink-soft);
    font-style: italic;
    font-size: var(--text-md);
    margin-block-start: var(--sp-1);
    max-width: 54ch;
  }

  /* ---- rack of destination cards -------------------------------------- */
  .rack {
    --rack-min: 18.75rem;                 /* 300px */
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--rack-min), 100%), 1fr));
    gap: clamp(1.125rem, 0.7rem + 1.8vw, 1.75rem);
    margin-block: var(--sp-8) var(--sp-5);
    content-visibility: auto;
    contain-intrinsic-size: auto 800px;
  }
  .rack--tight { --rack-min: 15rem; }     /* was .mrack, 240px */

  .rack-card {
    position: relative;
    display: block;
    color: inherit;
    /* --mx/--my are written by site.js from pointer position (hover devices
       only, rAF-throttled). Everything stays on the compositor. */
    transform:
      translate3d(var(--mx, 0px), calc(var(--my, 0px) + var(--lift, 0px)), 0)
      rotate(var(--r, 0deg));
    transition: transform var(--dur-spring) var(--ease-spring);
  }
  .rack-card::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--radius-sm);
    box-shadow: 0 22px 44px rgb(34 30 25 / 0.34);
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
    pointer-events: none;
  }
  .rack-card:hover, .rack-card:focus-visible { --lift: -8px; --r: 0deg; }
  .rack-card:hover::after, .rack-card:focus-visible::after { opacity: 1; }
  .rack-card:active { --lift: -3px; transition-duration: var(--dur-instant); }

  .rack-card .cardwrap { position: relative; overflow: hidden; border-radius: var(--radius-sm); }
  .rack-card .base {
    width: 100%;
    height: auto;
    border-radius: var(--radius-sm);
    box-shadow: 0 12px 26px rgb(34 30 25 / 0.28);
  }

  /* The "Open →" affordance advances on approach. */
  .rack-card .go {
    position: absolute;
    right: 8%;
    bottom: 13%;
    font-family: var(--font-ui);
    font-size: var(--text-xs);
    color: var(--verm);
    white-space: nowrap;
    transform: translate3d(0, 0, 0);
    transition: transform var(--dur) var(--ease-lift), opacity var(--dur) var(--ease);
    opacity: 0.86;
  }
  .rack-card:hover .go, .rack-card:focus-visible .go {
    transform: translate3d(4px, 0, 0);
    opacity: 1;
  }

  /* Cards arrive on their own view timeline, staggering by position. */
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .rack-card {
        animation: card-in var(--dur-reveal) var(--ease-spring-soft) both;
        animation-timeline: view();
        animation-range: entry 0% cover 20%;
      }
    }
  }
  @keyframes card-in {
    from { opacity: 0; transform: translate3d(0, 22px, 0) rotate(0deg) scale(0.98); }
    to   { opacity: 1;
           transform: translate3d(var(--mx,0px), calc(var(--my,0px) + var(--lift,0px)), 0)
                      rotate(var(--r, 0deg)) scale(1); }
  }
  .rack-card .inphoto {
    position: absolute;
    left: var(--pc-l, 0);
    top: var(--pc-t, 0);
    width: var(--pc-w, 100%);
    height: var(--pc-h, auto);
    object-fit: cover;
    filter: sepia(0.1) saturate(1.05);
    border-radius: 1px;
  }

  /* Label sits on the card art. A text-shadow alone is not a reliable
     contrast guarantee over photography, so a soft scrim backs it. */
  .rack-card .label {
    position: absolute;
    left: 8%;
    bottom: 11%;
    max-width: 78%;
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--indigo);
    text-shadow: 0 1px 3px rgb(253 248 236 / 0.85);
    text-wrap: balance;
  }
  /* Centred caption variant — the old .mcard */
  .rack-card .cap {
    position: absolute;
    inset-inline: 0;
    bottom: 9%;
    text-align: center;
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--sepia-deep);
    text-shadow: 0 1px 3px rgb(253 248 236 / 0.85);
  }

  /* Not-yet-written destinations. aria-disabled carries this to assistive
     tech; the class alone only communicated it visually. */
  .rack-card.soon,
  .rack-card.is-soon {
    opacity: 0.66;
    cursor: default;
    pointer-events: none;
  }
  .rack-card.soon:hover,
  .rack-card.is-soon:hover { transform: rotate(var(--r, 0deg)); }
  .rack-card.soon .go { color: var(--ink-soft); }

  /* Below ~26rem the absolutely-positioned label and the "Open →" affordance
     collide on long destination names. Stack them under the card instead. */
  @container (max-width: 26rem) {
    .rack-card .label,
    .rack-card .go,
    .rack-card .cap {
      position: static;
      display: block;
      max-width: none;
      text-shadow: none;
      text-align: left;
    }
    .rack-card .label { margin-block-start: var(--sp-2); }
    .rack-card .go { margin-block-start: var(--sp-1); }
    .rack-card .cap { margin-block-start: var(--sp-2); text-align: center; }
  }
  .rack { container-type: inline-size; }
  .rack-card { container-type: inline-size; }

  /* ---- journal index -------------------------------------------------- */
  .entry {
    padding-block: var(--sp-6);
    border-block-end: 1px solid var(--line);
  }
  .entry:last-child { border-block-end: 0; }
  .entry h2 {
    font-size: var(--text-xl);
    margin-block: var(--sp-1) var(--sp-2);
  }
  .entry h2 a:hover { color: var(--verm); }
  .entry p { color: var(--ink-soft); max-width: var(--wrap-prose); }

  .date {
    display: block;
    margin-block-end: var(--sp-6);
    font-family: var(--font-ui);
    font-size: var(--text-2xs);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-soft);
  }
  .back {
    font-family: var(--font-ui);
    font-size: var(--text-xs);
    color: var(--verm);
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
  }

  /* ---- editorial ------------------------------------------------------ */
  .prose { max-width: var(--wrap-prose); }
  .prose p {
    margin-block-end: var(--sp-5);
    font-size: var(--text-md);
    line-height: 1.7;
  }
  .prose p:last-child { margin-block-end: 0; }

  .pull {
    border-inline-start: 3px solid var(--verm);
    padding: var(--sp-2) 0 var(--sp-2) var(--sp-5);
    margin-block-end: var(--sp-5);
    font-style: italic;
    color: var(--ink-soft);
  }

  .hero { padding-block: var(--sp-12) var(--sp-10); }
  .hero .wrap {
    display: grid;
    grid-template-columns: 1.3fr 1fr;
    gap: clamp(1.5rem, 0.5rem + 4vw, 3rem);
    align-items: center;
  }
  .panel {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: clamp(1.5rem, 0.5rem + 4vw, 2.75rem);
    align-items: center;
    padding-block: var(--sp-12);
  }
  .panel img {
    width: 100%;
    height: auto;
    border-radius: var(--radius-md);
    box-shadow: 0 10px 24px rgb(34 30 25 / 0.16);
  }
  /* Single breakpoint for both, so they can never disagree. */
  @media (max-width: 47.5rem) {
    .hero .wrap, .panel { grid-template-columns: 1fr; }
  }

  .quote {
    background: var(--indigo);
    color: #E8E2D2;
    text-align: center;
    padding: var(--sp-10) var(--sp-5);
  }
  .quote .disp,
  .quote blockquote {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-style: italic;
    max-width: 40ch;
    margin-inline: auto;
    text-wrap: balance;
  }

  .divider { text-align: center; padding-block-start: var(--sp-2); }
  .divider img { max-width: 21.25rem; width: 60%; margin-inline: auto; height: auto; }

  .btn, .linkout {
    position: relative;
    overflow: hidden;
    isolation: isolate;
    font-family: var(--font-ui);
    font-size: var(--text-sm);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--tap);
    padding-inline: var(--sp-5);
    border-radius: var(--radius-sm);
    transform: translate3d(var(--mx, 0px), var(--my, 0px), 0) scale(var(--press, 1));
    transition:
      background var(--dur) var(--ease),
      color var(--dur) var(--ease),
      transform var(--dur-spring) var(--ease-spring);
  }
  /* A sheen crossing the face, not a neon gradient border — the site's
     surfaces are paper and ink, and light moving over paper is the only
     gradient that belongs here. */
  .btn::before, .linkout::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(105deg,
      transparent 30%,
      rgb(255 255 255 / 0.30) 48%,
      transparent 66%);
    transform: translate3d(-120%, 0, 0);
    transition: transform var(--dur-slow) var(--ease-out-quart);
    pointer-events: none;
  }
  .btn:hover::before, .linkout:hover::before,
  .btn:focus-visible::before, .linkout:focus-visible::before {
    transform: translate3d(120%, 0, 0);
  }
  .btn:active, .linkout:active {
    --press: 0.965;
    transition-duration: var(--dur-instant);
  }
  .linkout {
    color: var(--verm);
    border: 1px solid var(--verm);
  }
  .linkout:hover {
    background: var(--verm);
    color: var(--card);
  }
  .btn {
    background: var(--indigo);
    color: var(--card);
    border: 1px solid var(--indigo);
  }
  .btn:hover {
    background: transparent;
    color: var(--indigo);
  }

  /* ---- embedded media ------------------------------------------------- */
  /* aspect-ratio replaces the padding-top:56.25% hack. */
  .vid {
    position: relative;
    aspect-ratio: 16 / 9;
    margin-block: var(--sp-2) var(--sp-5);
    border-radius: 4px;
    overflow: hidden;
    box-shadow: 0 8px 20px rgb(34 30 25 / 0.18);
    background: color-mix(in srgb, var(--ink) 8%, transparent);
  }
  .vid iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

  .spot {
    width: 100%;
    height: 22rem;
    border: 0;
    border-radius: 8px;
    margin-block: var(--sp-2) var(--sp-5);
  }
  @media (max-width: 30rem) { .spot { height: 15rem; } }

  /* ---- food destination lead card ------------------------------------- */
  .menu-shell { container-type: inline-size; container-name: menucard; }
  .menu-lead {
    position: relative;
    max-width: 35rem;
    margin-inline: auto;
    margin-block-end: var(--sp-2);
  }
  .menu-lead .base {
    width: 100%;
    height: auto;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-xl);
  }
  .menu-lead .lphoto {
    position: absolute;
    left: var(--pc-l, 0);
    top: var(--pc-t, 0);
    width: var(--pc-w, 100%);
    height: var(--pc-h, auto);
    object-fit: cover;
    border-radius: 1px;
  }
  .menu-lead .mtitle {
    position: absolute;
    left: 8%;
    right: 8%;
    bottom: 12.5%;
    text-align: center;
  }
  .menu-lead .mtitle .pl {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--sepia-deep);
  }
  .menu-lead .mtitle .rg {
    font-family: var(--font-hand);
    font-size: var(--text-2xs);
    color: var(--sepia);
  }
  /* Same stacked treatment as the travel postcard — and, like it, achieved
     by redefining custom properties rather than five !important overrides. */
  @container menucard (max-width: 34rem) {
    .menu-lead {
      background: var(--card-warm);
      border: 1px solid var(--line-soft);
      padding: var(--sp-3);
      border-radius: var(--radius-md);
      --pc-l: auto; --pc-t: auto; --pc-w: 100%; --pc-h: clamp(11rem, 48cqi, 14rem);
    }
    .menu-lead .base { display: none; }
    .menu-lead .lphoto { position: relative; }
    .menu-lead .mtitle { position: static; margin-block-start: var(--sp-2); }
  }
  @supports not (container-type: inline-size) {
    @media (max-width: 47.5rem) {
      .menu-lead { background: var(--card-warm); border: 1px solid var(--line-soft);
                   padding: var(--sp-3); --pc-l: auto; --pc-t: auto;
                   --pc-w: 100%; --pc-h: 13rem; }
      .menu-lead .base { display: none; }
      .menu-lead .lphoto { position: relative; }
      .menu-lead .mtitle { position: static; margin-block-start: var(--sp-2); }
    }
  }

  /* ---- record sleeve (music release) ---------------------------------- */
  .sleeve {
    display: grid;
    grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
    gap: clamp(1.5rem, 0.6rem + 3.5vw, 3rem);
    align-items: start;
    padding-block: var(--sp-8);
  }
  .sleeve__art {
    position: relative;
    transform: rotate(-1.2deg);
    transition: transform var(--dur) var(--ease);
  }
  .sleeve__art {
    transition: transform var(--dur-spring) var(--ease-spring);
  }
  .sleeve__art:hover { transform: rotate(0deg) translate3d(0, -4px, 0); }

  /* The release artwork carries its own gilt frame, so the card adds none —
     a mount around an already-framed picture reads as a mistake. */
  .sleeve__art img {
    width: 100%;
    height: auto;
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-xl);
  }

  /* Postmark stamp. The site's whole visual language is postcards, postmarks
     and pinned clippings, so an unreleased record gets franked rather than
     badged. Real text, not a background image — screen readers announce it. */
  .stamp {
    position: absolute;
    right: -0.5rem;
    bottom: 12%;
    display: grid;
    place-items: center;
    gap: 1px;
    padding: var(--sp-2) var(--sp-4);
    font-family: var(--font-ui);
    font-size: clamp(0.6875rem, 0.62rem + 0.35vw, 0.8125rem);
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--verm);
    background: rgb(253 248 236 / 0.82);
    border: 2px solid var(--verm);
    box-shadow: inset 0 0 0 1px rgb(253 248 236 / 0.9);
    border-radius: 2px;
    transform: rotate(-9deg);
    opacity: 0.88;
    pointer-events: none;
    white-space: nowrap;
  }
  .stamp small {
    font-size: 0.72em;
    letter-spacing: 0.14em;
    color: var(--sepia);
  }

  /* A release with no cover art yet still needs to hold its space rather
     than collapsing the grid. */
  .sleeve__art--pending {
    display: grid;
    place-items: center;
    aspect-ratio: 2 / 3;
    background:
      repeating-linear-gradient(45deg,
        transparent 0 10px,
        rgb(220 209 188 / 0.28) 10px 20px),
      var(--card-warm);
    border: 1px dashed var(--line);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
  }
  .sleeve__art--pending p {
    font-family: var(--font-hand);
    font-size: var(--text-xs);
    color: var(--sepia);
    text-align: center;
    padding: var(--sp-4);
    max-width: 22ch;
  }
  .sleeve__body > * + * { margin-block-start: var(--sp-5); }

  .tracklist { display: grid; gap: var(--sp-4); }
  .track {
    border: 1px solid var(--line-soft);
    background: var(--card-warm);
    border-radius: var(--radius-sm);
    padding: var(--sp-4);
    box-shadow: var(--shadow-sm);
    transform: translate3d(0, var(--lift, 0px), 0);
    transition:
      transform var(--dur-spring) var(--ease-spring),
      border-color var(--dur) var(--ease);
  }
  .track:hover, .track:focus-within {
    --lift: -3px;
    border-color: var(--line);
  }
  .track__title {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--indigo);
    display: block;
  }
  .track__note {
    font-family: var(--font-hand);
    font-size: var(--text-2xs);
    color: var(--sepia);
    display: block;
    margin-block: 2px var(--sp-3);
  }
  .track audio {
    width: 100%;
    height: 2.25rem;
    border-radius: var(--radius-sm);
  }
  @media (max-width: 47.5rem) {
    .sleeve { grid-template-columns: 1fr; }
    .sleeve__art { max-width: 18rem; margin-inline: auto; }
  }

  /* ---- press clippings ------------------------------------------------ */
  /* Pinned-clipping idiom rather than a list of links, so a page with four
     evergreen items still reads as a collection instead of a stub. */
  .clippings {
    display: grid;
    gap: clamp(1.25rem, 0.6rem + 2.5vw, 2rem);
    padding-block: var(--sp-6);
  }
  .clipping {
    display: grid;
    grid-template-columns: minmax(0, 9rem) minmax(0, 1fr);
    gap: clamp(1rem, 0.5rem + 2vw, 1.75rem);
    align-items: start;
    background: var(--card-warm);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    padding: clamp(1rem, 0.6rem + 1.4vw, 1.5rem);
    box-shadow: var(--shadow-md);
    position: relative;
    isolation: isolate;
    transform: translate3d(0, var(--lift, 0px), 0) rotate(var(--tilt, 0deg));
    transition: transform var(--dur-spring) var(--ease-spring);
  }
  .clipping:nth-child(odd)  { --tilt: -0.4deg; }
  .clipping:nth-child(even) { --tilt: 0.5deg; }
  .clipping:hover,
  .clipping:focus-within { --lift: -4px; --tilt: 0deg; }
  .clipping::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    box-shadow: var(--shadow-lg);
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
    pointer-events: none;
  }
  .clipping:hover::after, .clipping:focus-within::after { opacity: 1; }

  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .clipping {
        animation: clip-in var(--dur-reveal) var(--ease-spring-soft) both;
        animation-timeline: view();
        animation-range: entry 0% cover 24%;
      }
    }
  }
  @keyframes clip-in {
    from { opacity: 0; transform: translate3d(0, 18px, 0) rotate(0deg); }
    to   { opacity: 1; transform: translate3d(0, var(--lift, 0px), 0) rotate(var(--tilt, 0deg)); }
  }
  .clipping__shot img {
    width: 100%;
    height: auto;
    border: 1px solid var(--line);
    border-radius: 1px;
    filter: sepia(0.08) saturate(0.98);
  }
  /* When the screenshot has not been rescued yet the column simply closes up
     — the clipping still reads correctly with type alone. */
  .clipping:has(.clipping__shot[data-missing-image]),
  .clipping:not(:has(.clipping__shot)) { grid-template-columns: 1fr; }

  .clipping__outlet {
    font-family: var(--font-ui);
    font-size: var(--text-2xs);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--verm);
  }
  .clipping__title {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--indigo);
    margin-block: var(--sp-1) var(--sp-2);
    line-height: 1.2;
  }
  .clipping__body p { color: var(--ink-soft); max-width: 60ch; }
  .clipping__body > * + * { margin-block-start: var(--sp-3); }
  .clipping blockquote {
    border-inline-start: 3px solid var(--gold);
    padding-inline-start: var(--sp-4);
    font-style: italic;
    color: var(--sepia-deep);
    max-width: 52ch;
  }
  .clipping cite {
    display: block;
    font-family: var(--font-ui);
    font-size: var(--text-2xs);
    font-style: normal;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-block-start: var(--sp-2);
  }
  @media (max-width: 34rem) {
    .clipping { grid-template-columns: 1fr; }
    .clipping__shot { max-width: 11rem; }
  }

  /* ---- contact strip -------------------------------------------------- */
  .contact-strip {
    text-align: center;
    padding-block: var(--sp-10);
    border-block-start: 1px solid var(--line);
    margin-block-start: var(--sp-10);
  }
  .contact-strip p { color: var(--ink-soft); margin-block-end: var(--sp-4); }

  /* ---- section eyebrow -------------------------------------------------- */
  /* .tag is the same component as .eyebrow under an older name. It carried
     three different colours across the old forks (#B9C4D6, gold, indigo)
     depending on which page it landed on. Deriving from currentColor instead
     means it reads correctly on cream and on the dark homepage without any
     page needing to override it. */
  .tag {
    font-family: var(--font-ui);
    font-size: var(--text-2xs);
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: color-mix(in srgb, currentColor 62%, transparent);
  }

  /* ---- shop ------------------------------------------------------------ */
  /* Products are rendered at build time from shop.json into the site's own
     card language. No third-party embed, no runtime script, nothing that
     arrives styled by someone else. */
  .shop-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(17.5rem, 100%), 1fr));
    gap: clamp(1.25rem, 0.7rem + 2vw, 2rem);
    margin-block: var(--sp-8) var(--sp-6);
  }

  .product {
    position: relative;
    isolation: isolate;
    display: grid;
    grid-template-rows: auto 1fr auto;
    background: var(--card-warm);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    padding: var(--sp-3) var(--sp-3) var(--sp-5);
    box-shadow: var(--shadow-md);
    color: inherit;
    transform: translate3d(var(--mx, 0px), calc(var(--my, 0px) + var(--lift, 0px)), 0)
               rotate(var(--tilt, 0deg));
    transition: transform var(--dur-spring) var(--ease-spring),
                border-color var(--dur) var(--ease);
  }
  .product:nth-child(3n)   { --tilt: -0.7deg; }
  .product:nth-child(3n+1) { --tilt: 0.6deg; }
  .product::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    box-shadow: var(--shadow-lg);
    opacity: 0;
    transition: opacity var(--dur) var(--ease);
    pointer-events: none;
  }
  .product:hover, .product:focus-within {
    --lift: -6px; --tilt: 0deg;
    border-color: var(--line);
  }
  .product:hover::after, .product:focus-within::after { opacity: 1; }

  .product__frame {
    display: block;
    overflow: hidden;
    border-radius: 1px;
    background: color-mix(in srgb, var(--line) 30%, transparent);
    aspect-ratio: 1;
  }
  .product__frame img { width: 100%; height: 100%; object-fit: cover; }
  /* Until a photograph is dropped in, the frame holds its place rather than
     collapsing the card. */
  .product__frame:empty::after {
    content: "";
    display: block;
    height: 100%;
    background: repeating-linear-gradient(45deg, transparent 0 10px,
                rgb(220 209 188 / 0.3) 10px 20px);
  }

  .product__body { padding: var(--sp-4) var(--sp-1) var(--sp-2); }
  .product__title {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--indigo);
    line-height: 1.2;
  }
  .product__blurb {
    color: var(--ink-soft);
    font-size: var(--text-sm);
    margin-block-start: var(--sp-2);
  }
  .product__note {
    font-family: var(--font-hand);
    font-size: var(--text-2xs);
    color: var(--sepia);
    display: block;
    margin-block-start: var(--sp-2);
  }
  .product__go {
    justify-self: start;
    margin-inline: var(--sp-1);
    font-family: var(--font-ui);
    font-size: var(--text-xs);
    color: var(--verm);
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
  }
  .product__go::after {
    content: "→";
    margin-inline-start: 0.4em;
    transition: transform var(--dur) var(--ease-lift);
  }
  .product:hover .product__go::after { transform: translateX(4px); }

  /* The handover is stated plainly rather than hidden. Pretending the basket
     is ours would be a worse experience than saying where it goes. */
  .shop-note {
    max-width: var(--wrap-prose);
    margin: var(--sp-8) auto 0;
    padding-block-start: var(--sp-5);
    border-block-start: 1px solid var(--line);
    text-align: center;
    font-size: var(--text-sm);
    color: var(--ink-soft);
  }

  /* ---- food lead note -------------------------------------------------- */
  /* .lead-note carried real copy in the previous build but had no rule in any
     of the 13 forks, so it rendered as unstyled body text. */
  .lead-note {
    max-width: var(--wrap-prose);
    margin: var(--sp-5) auto var(--sp-2);
    text-align: center;
    font-size: var(--text-md);
    line-height: 1.7;
    color: var(--ink-soft);
  }
  .lead-note em, .lead-note i { color: var(--sepia); }

  /* ---- failed images -------------------------------------------------- */
  /* Set by site.js when an image 404s. Collapses the frame rather than
     leaving a broken-image glyph in the middle of an essay. */
  [data-missing-image] { display: none; }
  img[data-failed] { display: none; }
}
