@layer reset, base, layout, components, theme;

/* Structural tokens: spacing, measure and chrome sizes. Colour and type scale
   are the theme's job, so they live in themes/<name>/theme.css. The --space-*
   ladder is the one scripts/css-audit.py resolves when checking box insets. */
:root {
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 2.75rem;
  --space-8: 4rem;
  --space-9: 5.5rem;

  --gutter: var(--space-5);
  --measure-text: 38rem;
  --measure-wide: 74rem;
  --section-y: clamp(3rem, 2rem + 5vw, 5.5rem);

  --header-h: 4.25rem;

  /* Sticky chrome contract. Nothing below the header may hardcode a height, do
     arithmetic on one, or measure the header at runtime — CSS cannot stack
     sticky boxes automatically (css-position-3: "Multiple sticky positioned
     boxes in the same container are offset independently, and therefore might
     overlap"), so a shared number has to exist. It exists here, once.

     --chrome-h    how far down the viewport the header's PAINT reaches. The
                   header owns it: whichever nav module changes the header's
                   shape restates this in the same file, and nobody else does.
                   The paint, not the box — a floating bar's box carries
                   transparent inset the eye does not read as header, and
                   charging that to the chrome is a phantom gap.
     --chrome-gap  air between the chrome and the first thing under it. A pure
                   design decision, deliberately not folded into --chrome-h,
                   because mixing a measurement with a preference is what makes
                   spacing untunable. This is the knob. Turn it here.
     --below-chrome the first y that clears the chrome. Every sticky rail and
                   every anchor offset uses this verbatim.

     Adding a sticky element? `top: var(--below-chrome)`. That is the whole API,
     and it stays correct when a theme swaps the nav for a different shape. */
  --chrome-h: var(--header-h);
  --chrome-gap: 0rem;
  --below-chrome: calc(var(--chrome-h) + var(--chrome-gap));
  /* Sticky jump control under the header. Mobile = the closed <details> field;
     desktop = one pill row. site.js overwrites --jump-h with the measured height. */
  --menu-jump-h: 4.5rem;
  /* Apple HIG / WCAG 2.5.5 enhanced: stand-alone controls ≥ 44px. */
  --touch-min: 2.75rem;
  /* Floating pill + 12px inset + capped safe-area. Constant, never toggled. */
  --dock-clearance: 5.25rem;

  --radius-sm: 0.375rem;
  --radius-md: 0.75rem;
  --radius-lg: 1.25rem;
  --radius-pill: 999px;

  /* How a photograph's corners look, in one place: photo surfaces consume this
     and never pick their own. Three of them had picked three different answers,
     so no theme could restyle them together. It fixes the value, not the guest
     list — a full-bleed hero still takes no radius (a corner cut against the
     viewport edge reads as a mistake), and a 4rem thumbnail may keep the
     smaller one. */
  --radius-media: var(--radius-md);
}

@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  /* :where() keeps specificity at zero, so component rules never need to fight
     the reset. A blanket `* { margin: 0 }` would also hit form controls. */
  :where(body, h1, h2, h3, h4, p, figure, blockquote, dl, dt, dd, ul, ol) {
    margin: 0;
  }

  :where(ul, ol) {
    padding-left: 0;
    list-style: none;
  }

  html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--below-chrome) + var(--space-4));
  }

  body {
    min-height: 100dvh;
  }

  :where(img, picture, video, svg) {
    display: block;
    max-width: 100%;
  }

  :where(input, button, textarea, select) {
    font: inherit;
    color: inherit;
  }

  :where(button) {
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
  }

  :where(a) {
    color: inherit;
  }

  :where(summary) {
    cursor: pointer;
  }

  :where(summary)::-webkit-details-marker {
    display: none;
  }

  :where(h1, h2, h3, h4) {
    font-weight: inherit;
    font-size: inherit;
    text-wrap: balance;
  }

  :where(p, li, dd, figcaption) {
    text-wrap: pretty;
    overflow-wrap: break-word;
  }

  /* Without this every tap waits ~300ms for a possible double-tap. */
  :where(a, button, summary, label, select, [role="button"]) {
    touch-action: manipulation;
  }

  :where(:focus-visible) {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
  }
}

@layer base {
  body {
    background: var(--sand);
    color: var(--text);
    font-family: var(--font-body);
    font-size: var(--step-0);
    line-height: 1.6;
    /* Room for the mobile action bar so it never covers the last element. */
    padding-bottom: var(--dock-clearance);
  }

  .skip-link {
    position: fixed;
    z-index: 60;
    top: var(--space-2);
    left: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    background: var(--ink);
    color: var(--sand);
    /* Moves via transform, not `top`, so it does not force layout each frame. */
    transform: translateY(-200%);
    transition: transform 180ms ease;
  }

  .skip-link:focus-visible {
    transform: translateY(0);
  }

  .u-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  a {
    text-underline-offset: 0.18em;
  }

  .link {
    color: var(--sea-deep);
    text-decoration-thickness: from-font;
    transition: color 180ms ease;
  }

  .link:hover {
    color: var(--ink);
  }
}

@layer layout {
  .wrap {
    width: min(100% - var(--gutter) * 2, var(--measure-wide));
    margin-inline: auto;
  }

  .section {
    padding-block: var(--section-y);
  }

  .section--band {
    background: var(--sand-deep);
  }

  .head {
    max-width: var(--measure-text);
    margin-bottom: var(--space-6);
  }

  .head__lead {
    margin-top: var(--space-3);
    color: var(--text-muted);
    font-size: var(--step-1);
  }

  .eyebrow {
    margin-bottom: var(--space-2);
    color: var(--accent-ink);
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
  }

  .h2 {
    font-family: var(--font-display);
    font-size: var(--step-3);
    line-height: 1.08;
  }

  .h3 {
    font-family: var(--font-display);
    font-size: var(--step-2);
    line-height: 1.15;
  }
}

@layer components {
  /* ---------------------------------------------------------------- images */

  /* `<picture>` is inline by default, so a child sized with height:100% would
     collapse it to the image's natural height and leave a colour gap. */
  .pic {
    display: block;
    width: 100%;
    height: 100%;
  }

  .pic > img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* ---------------------------------------------------------------- buttons */

  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 3rem;
    padding: var(--space-3) var(--space-5);
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font-size: var(--step-0);
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    transition: background-color 180ms ease, border-color 180ms ease,
      color 180ms ease, transform 180ms ease;
  }

  .btn:active {
    transform: translateY(1px);
  }

  .btn--primary {
    background: var(--sea);
    color: var(--surface);
  }

  .btn--primary:hover {
    background: var(--sea-deep);
  }

  .btn--ghost {
    border-color: var(--sea);
    color: var(--sea-deep);
  }

  .btn--ghost:hover {
    background: var(--sea-tint);
  }

  /* ---------------------------------------------------------------- header */

  .header {
    position: sticky;
    z-index: 40;
    top: 0;
    background: var(--ink);
    color: var(--sand);
  }

  .header__bar {
    display: flex;
    gap: var(--space-4);
    align-items: center;
    min-height: var(--header-h);
  }

  .brand {
    display: inline-flex;
    gap: var(--space-3);
    align-items: center;
    margin-right: auto;
    text-decoration: none;
  }

  .brand__mark {
    display: grid;
    flex: 0 0 auto;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border: 1px solid var(--accent);
    border-radius: 50%;
    color: var(--accent);
    font-family: var(--font-display);
    font-size: var(--step-0);
    letter-spacing: 0.04em;
  }

  .brand__mark--lg {
    width: 3.5rem;
    height: 3.5rem;
    font-size: var(--step-1);
  }

  .brand__logo {
    display: block;
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2.5rem;
    object-fit: contain;
  }

  .brand__logo--lg {
    width: 4.5rem;
    height: 4.5rem;
    margin-inline: auto;
  }

  .brand__text {
    display: grid;
  }

  .brand__name {
    font-family: var(--font-display);
    font-size: var(--step-1);
    line-height: 1.1;
  }

  .brand__place {
    color: var(--sand-dim);
    font-size: var(--step--1);
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  .header__toggle {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border-radius: var(--radius-sm);
    color: inherit;
  }

  .header__toggle-bars {
    position: relative;
    display: block;
    width: 1.25rem;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
    transition: background-color 180ms ease;
  }

  .header__toggle-bars::before,
  .header__toggle-bars::after {
    content: "";
    position: absolute;
    left: 0;
    width: 100%;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
    transition: transform 180ms ease;
  }

  .header__toggle-bars::before {
    top: -6px;
  }

  .header__toggle-bars::after {
    top: 6px;
  }

  /* Native popover has no open class on the invoker. :has() is the state. */
  .header:has(.nav:popover-open) .header__toggle-bars {
    background: transparent;
  }

  .header:has(.nav:popover-open) .header__toggle-bars::before {
    transform: translateY(6px) rotate(45deg);
  }

  .header:has(.nav:popover-open) .header__toggle-bars::after {
    transform: translateY(-6px) rotate(-45deg);
  }

  .header__cta {
    display: none;
  }

  /* audit-ok: chip frame only; 44px target is on .lang__link */
  .lang {
    display: inline-flex;
    flex: 0 0 auto;
    gap: var(--space-1);
    align-items: center;
    height: 2.5rem;
    padding: var(--space-1);
    border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
    border-radius: var(--radius-sm);
    white-space: nowrap;
  }

  .lang__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.5rem;
    min-height: 2.2rem;
    padding: 0 var(--space-2);
    border-radius: calc(var(--radius-sm) - 2px);
    color: inherit;
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: 0.06em;
    text-decoration: none;
  }

  a.lang__link:hover {
    background: color-mix(in srgb, currentColor 12%, transparent);
  }

  .lang__link.is-current {
    background: var(--sand);
    color: var(--ink);
  }

  /* ---------------------------------------------------------------- nav */

  /* Sheet under the header, not over it. A second close control in the top
     layer sits on the hamburger; the popover leaves the top layer and the
     same pointerup hits the toggle, so the menu flashes shut and open.
     Closed state stays UA `display: none`. Do not transition display/overlay
     on the way out — that keeps a full box in the page after top-layer. */
  .nav {
    position: fixed;
    inset: var(--below-chrome) 0 auto;
    width: 100%;
    max-width: none;
    height: fit-content;
    max-height: calc(100dvh - var(--below-chrome));
    margin: 0;
    padding: var(--space-5) var(--gutter);
    overflow: auto;
    border: 0;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    background: var(--ink);
    color: var(--sand);
    overscroll-behavior: contain;
  }

  .nav:popover-open {
    display: grid;
    align-content: start;
    gap: var(--space-4);
    transform: translateY(0);
    transition: transform 200ms ease;
  }

  @starting-style {
    .nav:popover-open {
      transform: translateY(-0.5rem);
    }
  }

  /* UA ::backdrop is transparent. A painted backdrop is full-viewport in the
     top layer, behind the popover only (MDN Popover API), so it sits on the
     header X and fails WCAG 1.4.11 non-text contrast. The sheet is opaque. */
  .nav::backdrop {
    background: transparent;
  }

  .nav:popover-open .lang {
    justify-self: start;
  }

  .nav__list {
    display: grid;
    gap: var(--space-2);
  }

  .nav__link {
    display: block;
    min-height: 3rem;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-sm);
    font-size: var(--step-1);
    text-decoration: none;
    transition: background-color 180ms ease, color 180ms ease;
  }

  .nav__link:hover {
    background: var(--ink-lift);
    color: var(--accent);
  }

  /* ---------------------------------------------------------------- hero */

  .hero__grid {
    display: grid;
  }

  .hero__media {
    /* The slot owns the ratio; the art-directed sources match it per breakpoint. */
    aspect-ratio: 3 / 2;
    order: -1;
  }

  .hero__copy {
    display: grid;
    justify-items: start;
    gap: var(--space-4);
    width: min(100% - var(--gutter) * 2, var(--measure-wide));
    margin-inline: auto;
    padding-block: var(--space-5);
  }

  .hero__title {
    max-width: 22ch;
    font-family: var(--font-display);
    font-size: var(--step-4);
    line-height: 1.02;
  }

  .hero__lead {
    max-width: var(--measure-text);
    color: var(--text-muted);
    font-size: var(--step-1);
  }

  .hero__actions {
    display: flex;
    flex-wrap: wrap;
    /* The split hero's grid stretches this row to the media column's height;
       keep the buttons at their own height instead of stretching with it. */
    align-items: start;
    gap: var(--space-3);
  }

  .rating {
    display: inline-flex;
    gap: var(--space-2);
    align-items: baseline;
    font-size: var(--step--1);
    text-decoration: none;
  }

  .rating__stars {
    color: var(--accent-ink);
  }

  .rating__value {
    font-size: var(--step-0);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
  }

  .rating__count {
    color: var(--text-muted);
  }

  /* Fact strip under the hero. Full-bleed band; the list sits in .wrap. */
  .strip {
    padding-block: var(--space-5);
    background: var(--ink);
    color: var(--sand);
  }

  .strip__list {
    display: grid;
    gap: 1px;
  }

  .strip__item {
    display: grid;
    gap: 2px;
    padding: var(--space-3) var(--space-4);
  }

  .strip__label {
    color: var(--accent);
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
  }

  .strip__value {
    font-size: var(--step-0);
    font-variant-numeric: tabular-nums;
  }

  /* ---------------------------------------------------------------- story */

  .story__grid {
    display: grid;
    gap: var(--space-6);
  }

  .story__copy {
    display: grid;
    gap: var(--space-4);
    align-content: start;
    max-width: var(--measure-text);
  }

  .story__notes {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-4);
    border-left: 3px solid var(--accent);
    background: var(--surface);
    font-size: var(--step--1);
  }

  .story__media {
    position: relative;
    aspect-ratio: 3 / 4;
    max-width: 26rem;
    margin-inline: auto;
    border-radius: var(--radius-media);
    overflow: hidden;
  }

  .story__caption {
    padding: var(--space-3) var(--space-4);
    background: var(--ink);
    color: var(--sand);
    font-size: var(--step--1);
  }

  /* ---------------------------------------------------------------- picks */

  .picks__grid {
    display: grid;
    gap: var(--space-6);
    /* auto-fit avoids the half-viewport card jump a rigid 3/2/1 ladder causes. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  }

  .pick {
    display: grid;
    container-type: inline-size;
    gap: var(--space-4);
    /* A grid item's automatic minimum size is its content, so an image can push
       the column wider than its track. minmax(0, ...) above plus this fixes it. */
    min-width: 0;
  }

  .pick__media {
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-media);
    overflow: hidden;
  }

  /* Flex, not grid: auto grid rows stretch, which pushed the name of a card
     with a shorter note away from its image. Here the name stays under the
     image and only the price is pushed down. */
  .pick__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .pick__name {
    font-family: var(--font-display);
    font-size: var(--step-2);
    line-height: 1.1;
  }

  .pick__note {
    color: var(--text-muted);
  }

  .pick__price {
    /* Prices line up across cards whose notes run to different lengths. */
    margin-block-start: auto;
    color: var(--sea-deep);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
  }

  @container (min-width: 22rem) {
    .pick__name {
      font-size: var(--step-2);
    }
  }

  /* ---------------------------------------------------------------- menu */

  /* Compact sticky jump: a <details> of links, not a <select>. Focusing a form
     control inside stuck sticky chrome scrolls the page to its unstuck position
     in Chromium (csswg-drafts#3009), and a select that navigates on change fails
     WCAG 3.2.2. Hidden on desktop, where the pill rail owns this slot.
     See docs/patterns/interactions/P-menu-jump-disclosure.md. */
  .menu__jump {
    position: sticky;
    z-index: 30;
    top: var(--below-chrome);
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-2);
    background: var(--sand);
  }

  .menu__jump-field {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: var(--space-3);
    min-height: var(--touch-min);
    /* ≥12px inset: css-audit bordered-box rule (section 7b). */
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface);
    cursor: pointer;
    list-style: none;
  }

  .menu__jump-field::-webkit-details-marker {
    display: none;
  }

  .menu__jump[open] .menu__jump-field {
    border-color: var(--sea);
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  .menu__jump-icon {
    display: grid;
    place-items: center;
    width: 2.375rem;
    height: 2.375rem;
    border-radius: var(--radius-sm);
    background: var(--sea-tint);
    color: var(--sea-deep);
  }

  .menu__jump-copy {
    min-width: 0;
  }

  .menu__jump-label {
    display: block;
    margin: 0 0 2px;
    color: var(--accent-ink);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
  }

  .menu__jump-value {
    display: block;
    overflow: hidden;
    color: var(--ink);
    font-size: 1.02rem;
    font-weight: 600;
    line-height: 1.25;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .menu__jump-go {
    display: grid;
    place-items: center;
    width: var(--touch-min);
    height: var(--touch-min);
    border-radius: var(--radius-sm);
    background: var(--sea);
    color: var(--sand);
    transition: transform 180ms ease;
  }

  .menu__jump[open] .menu__jump-go {
    transform: rotate(180deg);
  }

  .menu__jump-list {
    margin: 0;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--sea);
    border-top: 0;
    border-radius: 0 0 var(--radius-md) var(--radius-md);
    background: var(--surface);
    max-height: min(60dvh, 24rem);
    overflow: auto;
    overscroll-behavior: contain;
  }

  .menu__jump-option {
    display: flex;
    align-items: center;
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--ink);
    font-weight: 600;
    text-decoration: none;
  }

  .menu__jump-option:hover,
  .menu__jump-option:focus-visible {
    background: var(--sea-tint);
    color: var(--sea-deep);
  }

  /* Desktop pills: hidden on mobile, where the jump field owns the sticky slot. */
  .menu__pills {
    display: none;
  }

  .menu__pills-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    min-height: var(--touch-min);
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    background: var(--surface);
    font-size: var(--step--1);
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    text-decoration: none;
    transition: background-color 180ms ease, border-color 180ms ease, color 180ms ease;
  }

  .pill:hover {
    border-color: var(--sea);
    background: var(--sea-tint);
    color: var(--sea-deep);
  }

  .menu__list {
    display: grid;
    gap: var(--space-3);
  }

  /* The inset lives on .cat__head and .row. Padding on the container would inset
     the zebra stripes from the card edge, the bug the menu pattern warns about. */
  /* audit-ok: inset is on the children, by design */
  .cat {
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-md);
    background: var(--surface);
    overflow: hidden;
    /* Only the jump bar: html's scroll-padding-top already clears the header. */
    scroll-margin-top: calc(var(--jump-h, var(--menu-jump-h)) + var(--space-3));
  }

  /* Category header is the primary tap target: ≥44px (Apple HIG / WCAG 2.5.5). */
  .cat__head {
    display: flex;
    gap: var(--space-4);
    align-items: center;
    justify-content: space-between;
    min-height: var(--touch-min);
    padding: var(--space-4) var(--space-5);
    background: var(--sea-tint);
    cursor: pointer;
    list-style: none;
    transition: background-color 180ms ease;
  }

  .cat__head::-webkit-details-marker {
    display: none;
  }

  .cat__head:hover {
    background: var(--sea-tint-deep);
  }

  .cat__title {
    font-family: var(--font-display);
    font-size: var(--step-2);
    line-height: 1.1;
  }

  .cat__count {
    color: var(--text-muted);
    font-size: var(--step--1);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* Ledger rows: not interactive, so they stay denser than 44px. */
  .rows {
    container-type: inline-size;
    padding: 0;
  }

  .row {
    display: flex;
    gap: var(--space-4);
    align-items: baseline;
    padding: var(--space-3) var(--space-5);
  }

  .row:nth-child(even) {
    background: var(--zebra);
  }

  .row__name {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
  }

  .row__desc {
    display: block;
    color: var(--text-muted);
    font-size: var(--step--1);
    font-weight: 400;
  }

  .row__price {
    flex: 0 0 auto;
    color: var(--sea-deep);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  /* ---------------------------------------------------------------- gallery */

  .gallery__grid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  }

  .gallery__cell {
    min-width: 0;
  }

  .tile {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-media);
    overflow: hidden;
  }

  .tile > .pic > img {
    transition: transform 220ms ease;
  }

  .tile:hover > .pic > img,
  .tile:focus-visible > .pic > img {
    transform: scale(1.04);
  }

  /* Stage lightbox (P-lightbox-dialog): the photo is the page, chrome sits on
     the edges. Transparent dialog + ::backdrop scrim, not a padded card.
     Load-bearing: `display: grid` lives only on [open]. On the closed dialog
     it beats the UA `display: none`, so the arrows stay on the page and eat
     clicks after close ([Chrome entry/exit](https://developer.chrome.com/blog/entry-exit-animations)).
     `overlay` stays in the transition list so the exit is seen. The dialog
     takes programmatic focus; its close button keeps the visible ring. */
  .lightbox {
    grid-template-columns: var(--touch-min) minmax(0, 1fr) var(--touch-min);
    grid-template-rows: var(--touch-min) minmax(0, 1fr);
    column-gap: var(--space-2);
    row-gap: var(--space-3);
    align-items: center;
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    padding: var(--space-3);
    border: 0;
    background: transparent;
    color: var(--sand);
    overscroll-behavior: contain;
    transform: translateY(0.75rem) scale(0.985);
    transition:
      transform 200ms ease,
      overlay 200ms allow-discrete,
      display 200ms allow-discrete;
  }

  /* audit-ok: dialog takes programmatic focus; close keeps the ring */
  .lightbox:focus {
    outline: none;
  }

  .lightbox[open] {
    display: grid;
    transform: none;
  }

  @starting-style {
    .lightbox[open] {
      transform: translateY(0.75rem) scale(0.985);
    }
  }

  .lightbox::backdrop {
    background: color-mix(in srgb, var(--ink-deep) 92%, transparent);
  }

  .lightbox__count {
    grid-column: 1 / 3;
    grid-row: 1;
    margin: 0;
    color: var(--sand-dim);
    font-size: var(--step--1);
    font-variant-numeric: tabular-nums;
    text-shadow: 0 0 0.4rem var(--ink-deep);
  }

  .lightbox__close,
  .lightbox__step {
    display: grid;
    place-items: center;
    width: var(--touch-min);
    height: var(--touch-min);
    border: 0;
    border-radius: var(--radius-pill);
    color: var(--sand);
    background: color-mix(in srgb, var(--ink-deep) 45%, transparent);
    filter: drop-shadow(0 0 0.35rem var(--ink-deep));
    transition: background-color 180ms ease;
  }

  .lightbox__close {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }

  .lightbox__close:hover,
  .lightbox__step:hover {
    background: color-mix(in srgb, var(--ink-deep) 72%, transparent);
  }

  .lightbox__step--prev {
    grid-column: 1;
    grid-row: 2;
  }

  .lightbox__step--next {
    grid-column: 3;
    grid-row: 2;
  }

  .lightbox__stage {
    grid-column: 2;
    grid-row: 2;
    display: grid;
    min-width: 0;
    min-height: 0;
    margin: 0;
    place-items: center;
  }

  .lightbox__img {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
  }

  /* Native order wizard. Focus stays inside the dialog via showModal(). */
  .order {
    position: relative;
    width: min(92vw, 32rem);
    max-height: 90dvh;
    padding: var(--space-6);
    border: 0;
    border-radius: var(--radius-md);
    background: var(--surface);
    color: var(--text);
    overflow: auto;
    overscroll-behavior: contain;
  }

  .order::backdrop {
    background: var(--scrim-deep);
  }

  .order__close {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: inherit;
    font-size: var(--step-2);
    line-height: 1;
    cursor: pointer;
  }

  .order__progress {
    margin: 0 var(--space-7) var(--space-2) 0;
    color: var(--text-muted);
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  .order__title {
    margin: 0 var(--space-7) var(--space-4) 0;
    font-family: var(--font-display);
    font-size: var(--step-2);
  }

  .order__body {
    display: grid;
    gap: var(--space-4);
  }

  .order__body textarea,
  .order__body input,
  .order__body select {
    width: 100%;
    min-height: 2.75rem;
    padding: var(--space-3);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--sand);
    color: var(--ink);
    font: inherit;
  }

  .order__body textarea {
    min-height: 8rem;
    resize: vertical;
  }

  .order__error:empty {
    display: none;
  }

  .order__error {
    margin: var(--space-3) 0 0;
    color: var(--accent-ink);
    font-size: var(--step--1);
  }

  .order__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    justify-content: flex-end;
    margin-top: var(--space-5);
  }

  .order__summary {
    display: grid;
    gap: var(--space-3);
    margin: 0;
  }

  .order__summary > div {
    display: grid;
    gap: 2px;
  }

  .order__summary dt {
    color: var(--text-muted);
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  .order__note {
    color: var(--text-muted);
    font-size: var(--step--1);
  }

  /* ---------------------------------------------------------------- reviews */

  .quotes {
    display: grid;
    gap: var(--space-6);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  }

  .quote {
    display: grid;
    gap: var(--space-3);
    align-content: start;
    min-width: 0;
    padding-inline: var(--space-4);
    border-left: 2px solid var(--accent);
  }

  .quote__text {
    font-family: var(--font-display);
    font-size: var(--step-1);
    line-height: 1.4;
  }

  .quote__by {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: baseline;
    font-size: var(--step--1);
  }

  .quote__author {
    font-weight: 600;
  }

  .quote__role {
    color: var(--text-muted);
  }

  .quote__stars {
    color: var(--accent-ink);
    letter-spacing: 0.1em;
  }

  .reviews__more {
    margin-top: var(--space-6);
  }

  /* A link on its own line is a tap target, not prose: pad it past the 24px
     WCAG 2.2 floor without changing the text size. */
  .reviews__more .link {
    display: inline-block;
    /* audit-ok: sized to the 24px floor at this font size, not to the rhythm */
    padding-block: 0.45rem;
  }

  /* ---------------------------------------------------------------- visit */

  /* Eyebrow + heading (+ optional booking lead) span the section; the two-column
     grid below holds contact details and the map panel (Familija shape). */
  .visit__intro {
    display: grid;
    gap: var(--space-3);
    max-width: var(--measure-text);
    margin-bottom: var(--space-6);
  }

  /* Delivery copy + optional coverage drawing, above Find us. */
  .visit__top--with-map {
    margin-bottom: var(--space-8);
  }

  /* audit-ok: coverage map is the content, not copy needing inset */
  .visit__coverage-map {
    margin: 0;
    overflow: hidden;
    min-width: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface);
  }

  .visit__coverage-map .pic,
  .visit__coverage-map img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
  }

  .visit__grid {
    display: grid;
    gap: var(--space-6);
  }

  .visit__copy {
    display: grid;
    gap: var(--space-5);
    align-content: start;
  }

  .visit__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
  }

  /* NAP is the left column's primary content; no top rule once the heading
     lives above the grid. */
  .visit__details {
    min-width: 0;
  }

  .visit__panel-title {
    margin-bottom: var(--space-4);
    font-family: var(--font-display);
    font-size: var(--step-2);
  }

  /* One bordered unit: map flush on top, CTA strip below, no inner seam.
     Inset lives on .visit__panel-foot so the iframe can share the container's
     top corners edge-to-edge. */
  /* audit-ok: inset is on the children; map must be flush */
  .visit__panel {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-width: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    background: var(--surface);
  }

  /* Media slot: the iframe fills the box edge-to-edge; padding would letterbox it.
     Mobile keeps 4 / 3; desktop drops the ratio so the map absorbs row slack. */
  /* audit-ok: map iframe is the content, not copy needing inset */
  .visit__map {
    position: relative;
    flex: 1 1 auto;
    aspect-ratio: 4 / 3;
    min-height: 12rem;
    overflow: hidden;
    background: var(--surface);
  }

  .visit__map iframe {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
  }

  .visit__panel-foot {
    flex: 0 0 auto;
    padding: var(--space-4) var(--space-5);
  }

  .visit__maps-cta {
    width: 100%;
  }

  .visit__list {
    margin-bottom: var(--space-6);
  }

  .visit__list-title {
    margin-bottom: var(--space-3);
    font-weight: 600;
  }

  .visit__list ul {
    display: grid;
    gap: var(--space-2);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .visit__list li {
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
  }

  .visit__map--photo .pic,
  .visit__map--photo img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .nap {
    display: grid;
    gap: var(--space-3);
  }

  .nap > dt {
    color: var(--text-muted);
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
  }

  .nap > dd {
    display: grid;
    gap: 2px;
    font-variant-numeric: tabular-nums;
  }

  .nap__note {
    color: var(--text-muted);
    font-size: var(--step--1);
  }

  /* ---------------------------------------------------------------- faq */

  /* One reading measure, centred by .wrap's own auto margins. Nothing in this
     section is wide — the head and the questions are both one measure — so a
     wide inner just parked the whole column against the left of a container
     twice its width. Capping the inner is what centres it; capping the children
     individually cannot, and reads as deliberate left alignment. */
  .faq__inner {
    max-width: var(--measure-text);
  }

  .faq__list {
    display: grid;
    gap: var(--space-2);
  }

  .qa {
    border-bottom: 1px solid var(--line);
  }

  .qa__q {
    display: flex;
    gap: var(--space-4);
    align-items: baseline;
    justify-content: space-between;
    padding: var(--space-4) var(--space-3);
    font-size: var(--step-1);
    font-weight: 600;
    transition: color 180ms ease;
  }

  .qa__q::after {
    content: "+";
    color: var(--accent-ink);
    font-size: var(--step-2);
    line-height: 1;
  }

  .qa[open] > .qa__q::after {
    content: "\2212";
  }

  .qa__q:hover {
    color: var(--sea-deep);
  }

  .qa__a {
    padding: 0 var(--space-3) var(--space-4);
    color: var(--text-muted);
  }

  /* ---------------------------------------------------------------- cta */

  .cta {
    padding-block: var(--section-y);
    background: var(--ink);
    color: var(--sand);
  }

  .cta__inner {
    display: grid;
    justify-items: center;
    gap: var(--space-4);
    text-align: center;
  }

  .cta__title {
    max-width: 26ch;
    font-family: var(--font-display);
    font-size: var(--step-3);
    line-height: 1.06;
  }

  .cta__lead {
    max-width: var(--measure-text);
    color: var(--sand-dim);
  }

  .cta__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    justify-content: center;
  }

  .cta .btn--ghost {
    border-color: var(--sand-dim);
    color: var(--sand);
  }

  .cta .btn--ghost:hover {
    background: var(--ink-lift);
  }

  /* ---------------------------------------------------------------- footer */

  .footer {
    padding-block: var(--space-8) var(--space-6);
    background: var(--ink-deep);
    color: var(--sand);
  }

  .footer__inner {
    display: grid;
    gap: var(--space-7);
  }

  .footer__brand {
    display: grid;
    justify-items: center;
    gap: var(--space-3);
    text-align: center;
  }

  .footer__name {
    font-family: var(--font-display);
    font-size: var(--step-2);
  }

  .footer__blurb {
    max-width: 44ch;
    color: var(--sand-dim);
    font-size: var(--step--1);
  }

  .footer__socials {
    display: flex;
    gap: var(--space-2);
  }

  .footer__social {
    display: grid;
    place-items: center;
    width: var(--touch-min);
    height: var(--touch-min);
    color: var(--sand-dim);
    transition: color 180ms ease;
  }

  .footer__social:hover {
    color: var(--sand);
  }

  .footer__social svg {
    width: var(--step-1);
    height: var(--step-1);
  }

  .footer__cols {
    display: grid;
    gap: var(--space-6);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
    text-align: center;
  }

  .footer__title {
    margin-bottom: var(--space-3);
    color: var(--accent);
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
  }

  .footer__list {
    display: grid;
    gap: var(--space-2);
  }

  .footer__link {
    display: inline-block;
    padding-block: var(--space-1);
    color: var(--sand-dim);
    text-decoration: none;
    transition: color 180ms ease;
  }

  .footer__link:hover {
    color: var(--sand);
    text-decoration: underline;
  }

  .footer__oib {
    color: var(--sand-dim);
    font-size: var(--step--1);
    text-align: center;
  }

  .footer__fine {
    padding-top: var(--space-5);
    border-top: 1px solid var(--ink-lift);
    color: var(--sand-dim);
    font-size: var(--step--1);
    text-align: center;
  }

  /* ---------------------------------------------------------------- dock */

  /* Floating pill, inset from the edges. Pointer-events only on the inner so
     the full-bleed positioning layer does not steal taps. */
  .dock {
    position: fixed;
    z-index: 50;
    inset: auto 0 0;
    padding: 0 var(--space-3)
      max(var(--space-2), min(env(safe-area-inset-bottom, 0px), var(--space-3)));
    pointer-events: none;
    visibility: hidden;
    transform: translateY(calc(100% + 0.375rem));
    transition: transform 200ms ease, visibility 200ms;
  }

  .dock.is-visible {
    visibility: visible;
    transform: translateY(0);
  }

  .dock.is-visible .dock__inner {
    pointer-events: auto;
  }

  .dock__inner {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    justify-content: space-between;
    max-width: 25rem;
    margin-inline: auto;
    /* audit-ok: 5px around the 2.75rem items keeps the pill at 54px; --space-2 gives 60px */
    padding: 0.3125rem var(--space-3);
    border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent);
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--surface) 97%, transparent);
    box-shadow:
      0 8px 28px color-mix(in srgb, var(--ink-deep) 18%, transparent),
      0 1px 0 color-mix(in srgb, var(--surface) 85%, transparent) inset;
    backdrop-filter: blur(12px);
  }

  .dock__item {
    display: flex;
    flex: 1 1 0;
    flex-direction: column;
    gap: 2px;
    align-items: center;
    justify-content: center;
    min-width: 2.75rem;
    min-height: 2.75rem;
    padding: var(--space-1) 2px;
    border: 0;
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--ink);
    font: inherit;
    cursor: pointer;
    text-decoration: none;
    transition: background-color 150ms ease, transform 150ms ease;
  }

  .dock__item:active {
    background: var(--sand-deep);
    transform: scale(0.96);
  }

  .dock__icon {
    display: grid;
    place-items: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--ink) 8%, transparent);
    color: var(--sea);
  }

  .dock__icon svg {
    display: block;
    width: 1.125rem;
    height: 1.125rem;
  }

  .dock__label {
    color: var(--sea-deep);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1.1;
  }

  .dock__item--primary {
    flex: 0 1 auto;
    min-width: 5.5rem;
    margin-inline: 1px;
    /* audit-ok: optical pad for the icon+label stack; min-height already sets the target */
    padding: 0.4375rem var(--space-4);
    background: linear-gradient(180deg, var(--sea) 0%, var(--sea-deep) 100%);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--sea) 34%, transparent);
    color: var(--surface);
  }

  .dock__item--primary .dock__icon {
    background: color-mix(in srgb, var(--ink-deep) 12%, transparent);
    color: var(--surface);
  }

  .dock__item--primary .dock__label {
    color: var(--surface);
    font-size: 0.72rem;
  }

  .dock__item--primary:active {
    background: var(--sea-deep);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--sea) 28%, transparent);
  }

  /* ---------------------------------------------------------------- doc pages */

  .doc {
    padding-block: var(--section-y);
  }

  .doc__inner {
    display: grid;
    gap: var(--space-4);
    max-width: var(--measure-text);
  }

  .doc__title {
    font-family: var(--font-display);
    font-size: var(--step-3);
    line-height: 1.06;
  }

  .doc__h2 {
    margin-top: var(--space-4);
    font-family: var(--font-display);
    font-size: var(--step-2);
  }

  .doc__meta {
    color: var(--text-muted);
    font-size: var(--step--1);
  }

  .doc__list {
    display: grid;
    gap: var(--space-2);
    padding-left: var(--space-5);
    list-style: disc;
  }

  .doc__back {
    margin-top: var(--space-5);
  }

  /* ---------------------------------------------------------------- desktop */

  @media (min-width: 48rem) {
    .hero__media {
      aspect-ratio: 16 / 9;
    }

    .hero__copy {
      padding-block: var(--space-7);
    }

    .strip__list {
      grid-auto-columns: 1fr;
      grid-auto-flow: column;
    }

    .strip__item {
      border-left: 1px solid var(--ink-lift);
    }

    .strip__item:first-child {
      border-left: 0;
    }
  }

  @media (min-width: 64rem) {
    .header__toggle {
      display: none;
    }

    .header__cta {
      display: inline-flex;
    }

    /* Beats the UA `[popover]:not(:popover-open) { display: none }` rule so the
       same markup is a dropdown on mobile and a plain nav here. */
    .nav {
      position: static;
      display: flex;
      gap: var(--space-3);
      align-items: center;
      width: auto;
      max-width: none;
      height: auto;
      max-height: none;
      margin: 0;
      padding: 0;
      overflow: visible;
      border-radius: 0;
      background: none;
      transform: none;
      transition: none;
    }

    .nav:not(:popover-open),
    .nav:popover-open {
      display: flex;
      transform: none;
      transition: none;
    }

    .nav .lang {
      order: 1;
    }

    .nav__list {
      display: flex;
      gap: var(--space-2);
    }

    .nav__link {
      min-height: 2.5rem;
      padding: var(--space-2) var(--space-3);
      font-size: var(--step-0);
    }

    .hero__grid {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
      align-items: stretch;
    }

    .hero__media {
      aspect-ratio: auto;
      order: 0;
      min-height: 32rem;
    }

    .hero__copy {
      width: auto;
      margin: 0;
      padding: var(--space-9) var(--space-7) var(--space-9)
        max(var(--gutter), calc((100vw - var(--measure-wide)) / 2));
    }

    .story__grid {
      grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
      gap: var(--space-8);
      align-items: start;
    }

    /* Contact details vs map panel. Narrower copy (1fr) / wider map (1.2fr);
       equal-height stretch lets the map fill the row without a dead band. */
    .visit__top--with-map {
      display: grid;
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
      gap: var(--space-8);
      align-items: start;
    }

    .visit__top--with-map .visit__intro {
      margin-bottom: 0;
    }

    .visit__top--with-map .visit__list {
      grid-column: 1;
      margin-bottom: 0;
    }

    .visit__top--with-map .visit__coverage-map {
      grid-column: 2;
      grid-row: 1 / span 2;
    }

    .visit__grid {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
      gap: var(--space-8);
      align-items: stretch;
    }

    .visit__panel {
      min-height: 100%;
    }

    .visit__map {
      aspect-ratio: auto;
      min-height: 14rem;
    }

    .story__media {
      margin-inline: 0;
    }

    /* Desktop: pills; the jump field is for narrow viewports only. */
    .menu__jump {
      display: none;
    }

    .menu__pills {
      display: block;
      position: sticky;
      z-index: 30;
      top: var(--below-chrome);
      margin-bottom: var(--space-5);
      padding-block: var(--space-2);
      background: var(--sand);
    }

    .dock {
      display: none;
    }

    body {
      padding-bottom: 0;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .dock {
      transition: none;
      transform: none;
    }

    .menu__jump-go {
      transition: none;
    }
  }
}

/* chrome.nav: floating-pill — P-nav-floating-pill

   The bar detaches from the page edges and rides as a rounded capsule. One
   sticky element still, so the sticky budget is unchanged.

   This module restates two values from the sticky chrome contract in
   00-tokens.css, and is allowed to because it is the header: --chrome-h is the
   header's paint depth and the header is what changed shape. Nothing outside
   this file learns a header height.

   --header-inset is declared once and consumed twice, by the padding that makes
   the capsule float and by the --chrome-h that tells everything else where it
   stopped. One edit moves both, which is the only reason those two can never
   disagree again.

   Both go on :root, unlayered, and that is not a style choice. 00-tokens.css
   declares them on an unlayered :root, and an unlayered declaration beats
   anything in a layer no matter how specific, so the same override inside
   @layer components below would silently lose — and worse, it would still reach
   the header's own descendants, so the nav dropdown would look right while
   every sticky rail outside the header kept the old value. Source order settles
   two unlayered rules, and chrome modules concatenate after the tokens. */
:root {
  --header-inset: var(--space-3);
  --chrome-h: calc(var(--header-h) + var(--header-inset));
  --chrome-gap: var(--space-1);
}

@layer components {
  .header {
    background: none;
    /* Padding here, never margin on the bar. A margin on the only child
       collapses straight through a header with no border, no padding and no
       BFC: the header's border box shrinks to the capsule while its margin box
       keeps the inset, and sticky pins the MARGIN box. The capsule then paints
       one inset below where the header's own box says it begins, so anything
       derived from the header's top comes up short — which is exactly how this
       shipped overlapping the menu rail. Padding puts the inset inside the
       border box, where it is visible to everything that asks. */
    padding-block: var(--header-inset);
    /* A transparent box still takes taps, and most of this one is transparent:
       the band below the capsule at every width, and on desktop the whole area
       either side of a content-sized one. Left alone it swallows every click
       that lands there, the sticky menu rail parked underneath included.
       Restored on the bar, which the nav popover inherits. */
    pointer-events: none;
  }

  .header__bar {
    pointer-events: auto;
    padding-inline: var(--space-4);
    border-radius: var(--radius-pill);
    background: var(--ink);
    box-shadow: 0 0.75rem 2rem -0.75rem var(--scrim);
  }

  /* The capsule is content-sized on desktop rather than the full measure: a
     pill spanning the viewport is a strip with rounded ends, which is the one
     way this pattern reads as a mistake. */
  @media (min-width: 64rem) {
    .header__bar {
      width: fit-content;
      max-width: min(100% - var(--gutter) * 2, var(--measure-wide));
    }
  }
}

/* chrome.placeholder: blur — P-media-blur-up

   Every slot paints a tiny blurred copy of its own photograph until the real
   file arrives, so a scrolled-to image resolves out of its own colours instead
   of flashing the page background.

   There is no reveal to run and nothing to fade. An <img> paints nothing until
   it has decoded, so the backdrop simply shows through and is covered the
   moment the file lands. No opacity, no transition, no state that can strand an
   image invisible if a script never runs.

   The data URI is written per element by core/images.py; a slot without one
   leaves var(--lqip) invalid, which resolves to no background at all. */
@layer components {
  .pic {
    background-image: var(--lqip);
    background-position: center;
    background-size: cover;
  }
}

/* chrome.reveal: fade — P-reveal-fade

   The one everybody means by "content appears as you scroll", and the one this
   repo already shipped by hand on sites/blacksky: a block sits hidden until it
   crosses into view, then rises and fades in over 550ms and stops. That
   duration is blacksky's, and it sits just under the 600ms ceiling
   scripts/audit.py holds non-interactive motion to.

   It is triggered, not scroll-linked, and that is the entire point. `settle`
   and `wipe` drive their animation off `animation-timeline: view()`, which maps
   the effect onto scroll *position* — so a block taller than the viewport holds
   a part-transparent state for as long as it is on screen, and reads as washed
   out rather than as a reveal. A trigger plus a plain transition cannot do
   that: once it fires it runs to completion on its own clock and ends opaque.
   It also works in Firefox, which has no scroll-driven animations at all.

   `opacity: 0` in a style rule is what scripts/design-audit.py bans, because a
   rule that hides content hands the job of showing it again to something that
   might not run. This is the one shape where that cannot happen: `.reveal` is
   applied by core/js/site.js and appears in no built page, so markup that
   reaches a browser with a broken or blocked script carries nothing that could
   hide it, and the failure path is the whole page visible with no motion. The
   audit verifies that invariant against the built HTML rather than trusting
   this comment.

   No selector list here, unlike its two siblings: site.js chooses the elements,
   because it also has to skip whatever is already on screen at load. Hiding
   those would make the first viewport blink out and fade back in. */
@layer components {
  .reveal {
    opacity: 0;
    transform: translateY(1.75rem);
    transition:
      opacity 550ms ease,
      transform 550ms cubic-bezier(0.22, 0.61, 0.36, 1);
  }

  .reveal.is-in {
    opacity: 1;
    transform: none;
  }
}

/* chrome.texture: grain — P-surface-grain

   A fixed film grain over the whole viewport at a few percent.

   The cheapest thing on this list and, on warm dining photography, the most
   effective. It does what a print stock does: breaks up flat colour fields,
   kills the banding a dark gradient scrim leaves across a plate, and stops a
   large photograph reading as a stock JPEG. Nobody notices it; they notice its
   absence.

   Pure CSS, no image request, no JavaScript, works in every browser, and has no
   accessibility surface at all — it is a non-interactive pseudo-element that
   the accessibility tree never sees. The noise is generated by an SVG filter in
   a data URI, so there is no asset to ship or fingerprint. */
@layer components {
  body::before {
    content: "";
    position: fixed;
    z-index: 59;
    inset: 0;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
    /* Above the dock and below the skip link, so neither is affected. */
    opacity: 0.05;
    pointer-events: none;
  }
}

/* chrome.gallery: justified — P-gallery-justified

   Rows of varying widths, flush at both edges, every photograph keeping its own
   shape. The arithmetic is done at build time: core/images.py reads each source
   file's real dimensions and the template writes --ar per cell, so there is no
   measuring pass, no reflow on load and no JavaScript.

   Why it lines up: a row's free space is shared out in proportion to flex-grow,
   and both grow and basis are set to the item's aspect ratio. Every item in a
   row therefore scales by the same factor, which means one row height. */
@layer components {
  .gallery__grid {
    /* Target row height. The real height lands near this once a row is
       justified, and varies by a few percent between rows, which is the point:
       identical rows are a grid, not a justified gallery. */
    --jg-row: clamp(8.5rem, 30vw, 13rem);

    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
  }

  .gallery__cell {
    flex: var(--ar, 1.333) 1 calc(var(--ar, 1.333) * var(--jg-row));
  }

  /* Without this the final row stretches its one or two survivors across the
     full width and they tower over every row above. A spacer that takes all
     remaining space leaves them at their natural size instead. */
  .gallery__grid::after {
    content: "";
    flex-grow: 1000000;
  }

  .tile {
    aspect-ratio: var(--ar, 4 / 3);
  }
}

/* Mosaic Forno. Self-hosted fonts, so no third-party origin at runtime and no
   Google Fonts request to disclose. Palette sampled from the client's own
   photographs: the majolica oven dome and the room around it. */

@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/bricolage-grotesque-700-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/bricolage-grotesque-700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: Petrona;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/petrona-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: Petrona;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/petrona-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: Petrona;
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/petrona-600-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: Petrona;
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/petrona-600-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --sand: #F6F0E1;
  --sand-deep: #EADFC6;
  --sand-dim: #C6BBA2;
  --surface: #FFFCF3;
  --ink: #2C3A86;
  --ink-deep: #1D2760;
  --ink-lift: #43519B;
  --sea: #2A3781;
  --sea-deep: #1D2760;
  --sea-tint: #E5E7F5;
  --sea-tint-deep: #CDD2EB;
  --accent: #B24810;
  --accent-ink: #7E3406;
  --sun: #F0B92C;
  --sun-deep: #C8930C;
  --text: #241E18;
  --text-muted: #5C5145;
  --line: #C4B79C;
  --line-soft: #E4DAC3;
  --zebra: #F1EAD9;

  /* Ember, not cobalt: the focus ring has to be findable against a page whose
     chrome, links and ghost buttons are all already cobalt. */
  --focus: var(--accent);
  /* color-mix off the declared token, never a hand-mixed rgba: an alpha
     literal is where a donor palette survives a token swap. */
  --scrim: color-mix(in srgb, var(--ink-deep) 55%, transparent);
  --scrim-deep: color-mix(in srgb, var(--ink-deep) 86%, transparent);

  --font-display: "Bricolage Grotesque", "Segoe UI", system-ui, sans-serif;
  --font-body: Petrona, Georgia, "Times New Roman", serif;

  /* Fluid type. Petrona has a modest x-height for a text serif, so step-0 sits
     a shade above the 16px default; the display steps are pulled tall because
     the hero band has no photograph competing with them. */
  --step--1: clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);
  --step-0: clamp(1.0625rem, 1.02rem + 0.2vw, 1.125rem);
  --step-1: clamp(1.1875rem, 1.11rem + 0.38vw, 1.375rem);
  --step-2: clamp(1.4375rem, 1.25rem + 0.9vw, 1.875rem);
  --step-3: clamp(1.8125rem, 1.44rem + 1.8vw, 2.75rem);
  --step-4: clamp(2.375rem, 1.5rem + 4.2vw, 4.25rem);

  /* --chrome-h is deliberately NOT restated here, and that took a wrong turn
     to establish. An earlier revision painted the header band and pushed the
     paint depth to inset + bar + inset, which does need a restatement. The
     two-state header above does not: in the stuck state, which is the only
     state with anything sticky underneath it, the band is transparent again
     and the paint stops at the bar's bottom edge, exactly where
     nav-floating-pill.css already says it does. The full-bleed band only
     paints at scroll position zero, where nothing is stuck. */


  /* Signature mark: the oven dome is broken tile, so the rule is broken tile.
     Six irregular quadrilaterals over 72px, none the same width, none square
     to the baseline. A regular repeat would read as a border pattern; the
     unevenness is what makes it read as mosaic. */
  --shard: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="72" height="12" viewBox="0 0 72 12"><path d="M0 2 L9 0 L11 9 L1 11 Z" fill="%232C3A86"/><path d="M13 1 L22 3 L20 12 L12 10 Z" fill="%23F0B92C"/><path d="M24 0 L31 2 L33 11 L25 9 Z" fill="%23CDD2EB"/><path d="M35 3 L45 0 L46 10 L36 12 Z" fill="%232C3A86"/><path d="M48 1 L55 0 L57 9 L49 11 Z" fill="%23F0B92C"/><path d="M59 2 L68 1 L70 10 L60 12 Z" fill="%23CDD2EB"/></svg>');
  /* The shard SVG is 12px tall. Both placements clear or draw exactly this,
     so the height lives here rather than being restated per rule. */
  --shard-h: 0.75rem;
}

/* Below 64rem the header keeps its full-bleed band in BOTH states, so its
   paint runs the height of the whole box: inset + bar + inset. Above 64rem the
   band clears the moment anything is stuck and only the capsule paints, which
   is exactly what nav-floating-pill.css already says --chrome-h is.

   So the restatement is scoped to the widths where it is true, rather than set
   globally. Left at the module's value on a phone, the sticky menu jump
   disclosure and the nav popover both park 8px too high and tuck under the
   band's bottom edge. Unlayered, and after the chrome modules in
   concatenation order, which is how it wins. */
@media (max-width: 63.99rem) {
  :root {
    --chrome-h: calc(var(--header-h) + var(--header-inset, 0rem) * 2);
  }
}

@layer theme {
  ::selection {
    background: var(--sun);
    color: var(--ink-deep);
  }

  /* ------------------------------------------------------------- header */

  /* Two states. At the top of the page the header is one full-bleed blue bar
     touching both edges at every width; once the page scrolls, site.js sets
     data-nav="stuck" on <html> and on desktop the same box becomes a floating
     capsule. On phones it stays edge to edge in both states, because a capsule
     inset from a 390px viewport is the shape that made this look unmoored in
     the first place.

     Written round this way on purpose: the built markup carries no data-nav,
     so the resting rules below are the solid bar, and a browser with a blocked
     script keeps a complete header rather than a half-styled one.

     Painting the band is safe for interaction. nav-floating-pill.css sets
     `pointer-events: none` on .header precisely so its transparent area does
     not eat clicks meant for the sticky menu rail underneath, and a background
     does not change that.

     The header's BOX never changes size between the states; only which of the
     two elements carries the paint moves. That is what keeps the --chrome-h
     restatement on :root above honest for the rail. */
  .header {
    background-color: color-mix(in srgb, var(--ink) 92%, transparent);
    backdrop-filter: blur(10px);
    transition: background-color 220ms ease;
  }

  .header__bar {
    border-radius: 0;
    background: none;
    box-shadow: none;
    transition: background-color 220ms ease, border-radius 220ms ease;
  }

  /* Their sign is a hand painted plate, so it is worth rendering at a size
     you can actually read: at core's 40px the pizzaiolo is a yellow smudge.
     The cream ring is what makes it read as a plate pinned to the capsule
     rather than a hole cut in it. */
  .brand__logo {
    width: 3.25rem;
    height: 3.25rem;
    border-radius: var(--radius-pill);
    box-shadow: 0 0 0 2px var(--sand);
  }

  /* --------------------------------------------------------------- type */

  /* Bricolage Grotesque is drawn loose; the display sizes want pulling in. */
  .hero__title,
  .cta__title,
  .doc__title {
    letter-spacing: -0.022em;
    text-wrap: balance;
  }

  .h2,
  .h3,
  .cat__title,
  .pick__name {
    letter-spacing: -0.012em;
  }

  /* A serif body wants a little more leading than core's default. */
  .story__copy p,
  .qa__a p,
  .visit__intro p {
    line-height: 1.62;
  }

  /* --------------------------------------------------- signature: shards */

  /* Placement one: crowning the hero fact strip. */
  .strip {
    background-color: var(--ink);
    background-image: var(--shard);
    background-repeat: repeat-x;
    background-position: top left;
    padding-top: calc(var(--space-5) + var(--shard-h));
  }

  .strip__label {
    color: var(--sun);
  }

  .strip__value {
    color: var(--surface);
  }

  /* Placement two: the top edge of every muted band. Twice only, so it stays
     a mark rather than wallpaper. */
  .section--band {
    position: relative;
  }

  .section--band::before {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: var(--shard-h);
    background-image: var(--shard);
    background-repeat: repeat-x;
  }

  /* ------------------------------------------------------------ buttons */

  /* Ember primary. Core's cobalt would disappear into a cobalt page, and the
     one warm mark on a cool chrome is what the eye goes to first. */
  .btn--primary {
    background: var(--accent);
    color: var(--surface);
  }

  .btn--primary:hover {
    background: var(--accent-ink);
  }

  /* --------------------------------- hero: P-hero-below-image (archetype C) */

  /* Photo on top, copy on a solid band under it. The library is 35 bright
     portrait phone snaps under house lights: a scrim heavy enough to make
     type legible over one of them turns the food to mud, and there is no
     landscape frame strong enough to carry a 21:9 crop. */
  .hero--below .hero__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Capped to the same band as the copy, not run to the viewport edge. The
     whole photo library tops out at 1200px and the built hero file is
     1280x549, so a full-bleed band paints it at 2.0x upscale on a 2560
     screen and 2.7x on a 3440, and the crop drifts from 3.3:1 to 9:1 as it
     widens. At --measure-wide the ratio is a constant 3.08:1 and the paint
     never exceeds the file. Adding srcset widths cannot fix this: the pixels
     do not exist anywhere in the library. */
  /* The box takes the ratio of the file that is actually served, so the
     browser crops nothing a second time. core's picture_art hands this hero
     `hero-mobile` (3:2) below 48rem and `hero-full` (21:9) above it; the box
     was a fixed height that computed to 3.08:1, wider than either, so cover
     ate another 24% of the frame's height on top of the crop the build had
     already made. Ratio, not height, is what keeps the two in step. */
  .hero--below .hero__media {
    order: -1;
    aspect-ratio: 3 / 2;
    height: auto;
    min-height: 0;
    overflow: clip;
  }

  /* padding-inline: 0 is load-bearing, and it is not obvious why.
     core/css/03-components.css sets a physical `padding` SHORTHAND on
     .hero__copy at >=64rem whose inline-start is
     max(--gutter, (100vw - --measure-wide) / 2) — the split hero's own
     centring. `padding-block` below is a block-axis shorthand and cannot
     reset the inline halves of that rule, and @layer order resolves per
     DECLARATION, not per rule, so the inline padding survives into this
     theme and stacks on the width + margin-inline centring here. That double
     count is 48px at 1280, 368px at 1920, 688px at 2560 and 1128px at 3440,
     where the children then overflow the box. It is invisible at every
     viewport the layout gate runs. */
  .hero--below .hero__copy {
    width: min(100% - var(--gutter) * 2, var(--measure-wide));
    margin-inline: auto;
    padding-block: var(--space-7) var(--space-8);
    padding-inline: 0;
    background: none;
  }

  .hero--below {
    background: var(--surface);
  }

  .hero--below .hero__lead {
    max-width: var(--measure-text);
    color: var(--text-muted);
    font-size: var(--step-1);
  }

  /* ------------------------------- story: P-story-document-wrap */

  /* The figure is floated into the prose by the theme's own story.html
     override; a float only shortens the line boxes that follow it in source
     order, so the move is a template job, not a CSS one. */
  /* The cap goes on the CONTAINER, not on the child. .story__grid IS the
     .wrap, so it defaulted to --measure-wide (1184px) while its only child
     was 608px, which is css-best-practices 7d word for word: a box whose
     dimensions do not describe what it holds breaks something at a distance.
     Here it left 1264px of empty ground beside the prose at 2560. A
     max-width on .story__copy could never fix that, because only the
     parent's own box can place the child. */
  .story .story__grid {
    display: block;
  }

  /* One prose edge on the page, with no arithmetic to get it.

     .wrap already computes the band: `min(100% - gutter*2, --measure-wide)`
     with auto margins. The bug was narrowing the wrap itself. Core does that
     to .faq__inner, and this theme briefly did it to .story__grid; a narrowed
     wrap then re-centres on its own smaller width and lands 288px right of
     every other section at 1440. Restating the wrap's offset to drag it back
     was the wrong repair, and it was where the hand arithmetic came from.

     Leave the wrap a wrap and cap the CHILD instead, which is exactly what
     .head already does in six other sections on this page. */
  .faq__inner {
    max-width: none;
  }

  .faq__list,
  .story .story__copy {
    max-width: var(--measure-text);
  }

  /* flow-root, not block: it wraps its own lines around the child float AND
     contains it, so a figure taller than the prose cannot escape into the
     next section. Not overflow: hidden, which also builds a scrollport. */
  .story .story__copy {
    display: flow-root;
  }

  /* Nothing overrides .faq__inner. Core already caps and centres it, which
     is the same shape the story grid above now has, and the two are the
     page's only standalone prose columns. An earlier attempt here set
     `margin-inline: 0` to force one left edge across the whole page; it put
     the FAQ at x=0, flush against the viewport with no gutter at all. The
     page has two axes on purpose: a standalone prose column centres, and
     anything sitting above or beside full-width content starts at the
     band's inline edge. */

  /* Turning the grid off threw away its gap, and 01-reset.css zeroes p
     margins: without this every paragraph collapses into one wall of text. */
  .story__copy > p,
  .story__copy > .h2 {
    margin-block-end: var(--space-4);
  }

  /* 38% of a 38rem measure leaves about 40 characters beside the photo, which
     is what a document wrap looks like. Taken as a percentage of the
     container, never as a rem number. */
  .story .story__media {
    float: inline-end;
    width: 38%;
    aspect-ratio: auto;
    overflow: visible;
    margin-block: var(--space-1) var(--space-5);
    margin-inline: var(--space-6) 0;
  }

  .story__media .pic {
    height: auto;
  }

  /* The ratio moves onto the image so the caption can sit under the photo
     instead of being clipped away by the figure's overflow. */
  .story__media img {
    aspect-ratio: 3 / 4;
    height: auto;
    border-radius: var(--radius-media);
  }

  /* Core's caption is an ink bar sized for a full column; under a 14rem float
     it reads as a black brick. */
  .story .story__caption {
    padding: var(--space-2) 0 0;
    border-top: 1px solid var(--line);
    background: none;
    color: var(--text-muted);
  }

  /* Release the float on phones. At 390 the container is about 21rem, so a
     38% float plus its 2rem inset leaves roughly 11rem of text: two-word
     lines, ragged on both sides. The image keeps 3/4 either way, because
     switching ratios by breakpoint would need a second art-directed source
     the story slot does not render. */
  @media (max-width: 47.99rem) {
    .story .story__media {
      float: none;
      width: 100%;
      margin-inline: 0;
    }
  }

  /* ------------------------------------- picks: P-picks-zigzag */

  .picks__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-7);
  }

  /* Matches the `pick-square` slot. Left at core's 4:3 the browser would put
     the square file back through a landscape box and undo the point of it. */
  .pick__media {
    aspect-ratio: 1 / 1;
  }

  .pick__price {
    color: var(--accent-ink);
    font-family: var(--font-display);
    font-size: var(--step-1);
  }

  /* ---------------------------------------- menu: P-menu-sticky-rail */

  .cat__title {
    color: var(--ink-deep);
  }

  .row__price {
    color: var(--accent-ink);
  }

  /* ------------------------------- reviews: P-reviews-quote-stack skin */

  /* Cardless: the quotes are set as a stack of pulled lines with a hairline
     rather than boxes, and the hairline is the line colour, not the accent. A
     coloured edge repeated once per quote is the side-tab tell. */
  .quote {
    border-left: 1px solid var(--line);
  }

  /* No italic here. Core sets .quote__text in the display face, and Bricolage
     Grotesque ships upright only in this theme, so font-style: italic would
     have asked the browser to slant it synthetically. */
  .quote__stars {
    color: var(--sun-deep);
  }

  /* ------------------------------------------------------- odds and ends */

  .footer {
    background: var(--ink-deep);
  }

  @media (min-width: 48rem) {
    /* The cap starts here, not at the base. Its job is to stop a 1280px file
       being stretched across a 2560px screen, and on a phone there is nothing
       to stop: the photo is being downscaled at every width below this, so
       full bleed costs no sharpness and an inset band reads as a timid card. */
    .hero--below .hero__media {
      aspect-ratio: 21 / 9;
      width: min(100% - var(--gutter) * 2, var(--measure-wide));
      margin-inline: auto;
      border-radius: var(--radius-media);
      /* The hero is not a .section, so it carries no --section-y and the photo
         started flush against the header's bottom edge: a rounded, inset band
         welded to a solid bar. The gap only belongs where the photo is inset;
         below this breakpoint it runs full bleed and sitting tight under the
         bar is the right look. */
      margin-block-start: var(--space-5);
    }

  }

  @media (min-width: 56rem) {
    /* Zigzag. Placed by grid column, never by `order`: source order stays
       media then copy on every row, so the reading order is consistent and
       WCAG 1.3.2 is not in play. */
    /* 18rem, which is what the `pick-square` slot's own `sizes` promises the
       browser. The theme claims that slot through PICK_SLOTS in chrome.py:
       every dish photograph in this library is a portrait phone shot, and a
       4:3 crop of one cuts the pizza's crust off top and bottom. Square
       contains it. 18rem rather than a larger column because a square photo
       is taller than the 4:3 one it replaced, and three stacked rows of them
       put this section back over the proportion budget. */
    .pick {
      grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
      gap: var(--space-7);
      align-items: center;
    }

    .pick:nth-child(even) {
      grid-template-columns: minmax(0, 1fr) minmax(0, 18rem);
    }

    .pick:nth-child(even) .pick__media {
      grid-column: 2;
      grid-row: 1;
    }

    .pick:nth-child(even) .pick__body {
      grid-column: 1;
      grid-row: 1;
    }
  }

  @media (min-width: 64rem) {
    /* Stuck: the paint moves off the band and onto the bar, which is where
       nav-floating-pill.css already sizes it to its content. Desktop only.
       Below 64rem the module leaves the bar full width, so a pill radius here
       would round two corners against the viewport edge and clip them. */
    /* Not fully transparent: a floating capsule means the page scrolls behind
       it, and whatever passes gets cut in a hard line at the band's edge. The
       cobalt fact strip going under was the worst case, reading as a second
       broken bar above the pill. A short fade from the page ground dissolves
       whatever is passing instead of slicing it, and still leaves the capsule
       looking detached, which is the point of the state. */
    [data-nav="stuck"] .header {
      background-color: transparent;
      background-image: linear-gradient(
        to bottom,
        var(--sand),
        color-mix(in srgb, var(--sand) 72%, transparent) 60%,
        transparent
      );
      backdrop-filter: none;
    }

    [data-nav="stuck"] .header__bar {
      border-radius: var(--radius-pill);
      background-color: color-mix(in srgb, var(--ink) 92%, transparent);
      backdrop-filter: blur(10px);
      box-shadow: 0 0.75rem 2rem -0.75rem var(--scrim);
    }

    /* Only the padding changes here. The one-column grid, the media order and
       the media height above are not restated: @layer theme outranks
       @layer components outright, so a base rule here already beats core's
       own 64rem split-hero block without a matching query. */
    .hero--below .hero__copy {
      padding-block: var(--space-8) var(--space-9);
    }

    /* The rail is the pills nav turned on its side. No overflow, contain or
       container-type on this box: each one breaks the sticky rail below it. */
    /* The rows column is capped at 40rem, not left as 1fr. A menu row is
       `display: flex` with the name on `flex: 1 1 auto`, so the name box
       grows to swallow whatever it is given: at 1fr inside the 74rem wrap
       that was an 894px row with 650px of nothing between "Margherita" and
       its price. A ledger wants a reading measure. Capped on the container
       rather than on .row or .row__name, per css-best-practices 7d. */
    .menu .wrap {
      display: grid;
      grid-template-columns: minmax(0, 14rem) minmax(0, 40rem);
      gap: var(--space-8);
    }

    .menu .head {
      grid-column: 1 / -1;
    }

    /* Core already made this sticky at top: var(--below-chrome); that is not
       restated. align-self is the whole change: a stretched grid item spans
       its containing block and so has zero travel, which looks exactly like
       broken sticky with a perfectly correct top. */
    .menu .menu__pills {
      align-self: start;
      background: none;
    }

    .menu .menu__pills-list {
      flex-direction: column;
      gap: var(--space-1);
    }

    .menu .pill {
      width: 100%;
      justify-content: flex-start;
      text-align: start;
      white-space: normal;
    }

    /* --jump-h is measured from the pills and is ~30rem once they are a
       column, but a side rail covers no heading. html's scroll-padding-top
       clears the header on the CSS path; this is what stickyOffset() adds to
       the header height on the JS path, so it hands back the chrome gap plus
       a buffer for the floating capsule. */
    .menu .cat {
      scroll-margin-top: calc(var(--chrome-gap) + var(--space-4));
    }

  }
}

/* Deliberately unlayered.

   Unlayered rules beat every @layer regardless of source order, so this single
   block neutralises motion across every component without needing !important
   and without per-component reduced-motion rules. Keep it last in the build. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 1ms;
    animation-iteration-count: 1;
    transition-duration: 1ms;
    /* A scroll-driven animation takes its progress from the scroll position,
       so animation-duration does not reach it. Handing it back the document
       timeline lets the 1ms above apply, and `both` fill then leaves the
       element at its finished state: settled, in place, never mid-reveal. */
    animation-timeline: auto;
    animation-range: normal;
  }

  html {
    scroll-behavior: auto;
  }
}
