@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__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;
    margin-left: auto;
    border-radius: var(--radius-sm);
    color: inherit;
  }

  .header__toggle-bars,
  .header__toggle-bars::before,
  .header__toggle-bars::after {
    display: block;
    width: 1.25rem;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
  }

  .header__toggle-bars::before,
  .header__toggle-bars::after {
    content: "";
    position: relative;
  }

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

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

  .header__cta {
    display: none;
  }

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

  /* Full-bleed dropdown under the header. Resets the UA [popover] defaults
     (margin: auto; width: fit-content) that otherwise float a mid-screen box. */
  .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;
    /* Transform-only enter (no fade) so the closed state stays display-driven. */
    transition:
      display 200ms allow-discrete,
      overlay 200ms allow-discrete,
      transform 200ms ease;
  }

  .nav:not(:popover-open) {
    transform: translateY(-0.5rem);
  }

  .nav:popover-open {
    transform: translateY(0);
  }

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

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

  .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__eyebrow {
    color: var(--accent-ink);
    font-size: var(--step-0);
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
  }

  .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;
    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);
  }

  /* @starting-style gives the open a transform-only entry with no script and no
     class toggle. `overlay` has to be in the transition list or the dialog
     leaves the top layer the instant it closes and the exit is never seen. */
  .lightbox {
    width: min(92vw, 68rem);
    max-height: 90dvh;
    padding: var(--space-4);
    border: 0;
    border-radius: var(--radius-md);
    background: var(--ink);
    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;
  }

  .lightbox[open] {
    transform: none;
  }

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

  .lightbox__bar {
    display: flex;
    gap: var(--space-4);
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-3);
  }

  .lightbox__count {
    color: var(--sand-dim);
    font-size: var(--step--1);
    font-variant-numeric: tabular-nums;
  }

  .lightbox__steps {
    display: flex;
    gap: var(--space-2);
    justify-content: center;
    margin-top: var(--space-3);
  }

  .lightbox__step {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border: 1px solid var(--sand-dim);
    border-radius: var(--radius-pill);
    color: inherit;
    transition: background-color 180ms ease;
  }

  .lightbox__step:hover {
    background: var(--ink-lift);
  }

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

  /* Subtracts the chrome rather than guessing a dvh fraction: the count bar and
     the step buttons are 2.75rem each plus their gaps, and the frame's own
     padding sits outside that. Guessing overflowed the dialog on a short
     landscape phone, where the shortfall is worst. */
  .lightbox__img {
    width: 100%;
    max-height: calc(90dvh - 10rem);
    object-fit: contain;
  }

  .lightbox__close {
    min-height: 2.75rem;
    padding: var(--space-2) var(--space-4);
    border: 1px solid var(--sand-dim);
    border-radius: var(--radius-pill);
    color: inherit;
    font-size: var(--step--1);
    transition: background-color 180ms ease;
  }

  .lightbox__close:hover {
    background: var(--ink-lift);
  }

  /* ---------------------------------------------------------------- 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__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);
  }

  .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%;
  }

  .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__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__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-radius: var(--radius-pill);
    color: var(--ink);
    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: block;
      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 {
      transform: none;
    }

    .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__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.hero_motion: drift — P-hero-drift

   A single slow push into the hero photograph on load, ending at rest.

   Once, not looping: a hero that breathes forever pulls the eye off the copy
   sitting next to it, and it is the tell that separates a template from a
   photograph somebody chose. Twenty-two seconds and six percent is far enough
   to register as depth and not far enough to notice as movement.

   99-motion.css collapses this to its finished state under
   prefers-reduced-motion, which leaves a very slightly cropped still. */
@layer components {
  .hero__media {
    overflow: hidden;
  }

  .hero__media > .pic > img {
    animation: hero-drift 22s ease-out both;
  }

  @keyframes hero-drift {
    from {
      transform: scale(1);
    }

    to {
      transform: scale(1.06);
    }
  }
}

/* chrome.progress: bar — P-scroll-progress

   A hairline across the top of the viewport showing how far down the page the
   reader is. On a single-page site carrying a full menu, that is the one piece
   of orientation the design otherwise withholds.

   Drawn on `body::after` rather than a real element: it is decoration with no
   semantics, and giving it markup would mean an empty div in every page's
   source and an `aria-hidden` to remember. Inside the @supports block only, so
   a browser without scroll timelines paints nothing rather than a bar stuck at
   zero width. */
@layer components {
  @supports (animation-timeline: scroll()) {
    body::after {
      content: "";
      position: fixed;
      z-index: 45;
      inset: 0 0 auto;
      height: 0.1875rem;
      background: var(--accent);
      transform: scaleX(0);
      transform-origin: 0 50%;
      animation: scroll-progress linear both;
      animation-timeline: scroll(root block);
    }

    @keyframes scroll-progress {
      to {
        transform: scaleX(1);
      }
    }
  }
}

/* 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.page_transition: fade — P-page-transition

   Cross-document view transitions. Navigating from the home page to the privacy
   or terms page cross-fades instead of flashing white.

   Two lines of CSS, no JavaScript, no markup, no bytes worth measuring. On a
   genuinely multi-page static build this is the highest ratio of perceived
   quality to risk available: a browser without support performs an ordinary
   instant navigation, which is exactly what happens today.

   The opt-in sits outside the reduced-motion query on purpose. On its own it
   produces an instant cut, which is the correct behaviour under that
   preference; only the timing customisation below needs gating.

   Unlayered because @view-transition is a top-level rule that configures the
   document rather than styling an element. Same reasoning as 99-motion.css. */
@view-transition {
  navigation: auto;
}

@layer components {
  @media (prefers-reduced-motion: no-preference) {
    /* The default fill would stretch a photograph mid-transition. */
    ::view-transition-old(root),
    ::view-transition-new(root) {
      animation-duration: 240ms;
      object-fit: cover;
    }
  }
}

/* patterns.gallery: P-gallery-hero-grid

   One photograph carries the section and the rest support it.

   The skin for a library that is all one shape — guest photos off a maps
   listing are almost always 4:3, and justified rows of identical tiles are a
   grid with extra arithmetic. Hierarchy has to be imposed here because the
   photographs will not supply it. */
@layer components {
  .gallery__grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-3);
  }

  /* Full width on its own row: at two columns there is nothing beside a 2x2
     feature to give its rows a height, so they would collapse. */
  .gallery__cell:first-child {
    grid-column: span 2;
  }

  @media (min-width: 48rem) {
    .gallery__grid {
      grid-template-columns: repeat(4, 1fr);
    }

    .gallery__cell:first-child {
      grid-row: span 2;
    }

    /* The two rows are sized by the ordinary tiles beside it, so the feature
       drops its own ratio and fills whatever they came to. */
    .gallery__cell:first-child .tile {
      aspect-ratio: auto;
      height: 100%;
    }
  }
}

/* Paper Shoal. Self-hosted fonts; no third-party origin at runtime. */

@font-face {
  font-family: "Instrument Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/instrument-serif-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: "Instrument Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/instrument-serif-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: Outfit;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/outfit-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: Outfit;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/outfit-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: Outfit;
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/outfit-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: Outfit;
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/outfit-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: #F3F0E8;
  --sand-deep: #E5DFD2;
  --sand-dim: #C5CBD4;
  --surface: #FFFEFA;
  --ink: #1A2744;
  --ink-deep: #121C33;
  --ink-lift: #2A3A5C;
  --sea: #243B5C;
  --sea-deep: #1A2D4A;
  --sea-tint: #E4E8F0;
  --sea-tint-deep: #D0D7E5;
  --accent: #C48A2E;
  --accent-ink: #6B4A12;
  --text: #1A2438;
  --text-muted: #4A5568;
  --line: #D4CEC2;
  --line-soft: #E8E3D8;
  --zebra: #F0EDE5;

  --focus: var(--sea);
  --scrim: rgb(18 28 51 / 0.55);
  --scrim-deep: rgb(18 28 51 / 0.86);

  --font-display: "Instrument Serif", Georgia, serif;
  --font-body: Outfit, "Segoe UI", system-ui, sans-serif;

  /* Taller display step than daylight-coast; body stays close. */
  --step--1: clamp(0.8125rem, 0.79rem + 0.12vw, 0.875rem);
  --step-0: clamp(1rem, 0.97rem + 0.16vw, 1.0625rem);
  --step-1: clamp(1.125rem, 1.06rem + 0.32vw, 1.3125rem);
  --step-2: clamp(1.375rem, 1.22rem + 0.75vw, 1.75rem);
  --step-3: clamp(1.75rem, 1.44rem + 1.5vw, 2.5rem);
  --step-4: clamp(2.25rem, 1.65rem + 2.9vw, 3.5rem);

  /* Signature: vertical slats in oak-gold. */
  --slat: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="10" viewBox="0 0 12 10"><path d="M2 1 V9 M6 1 V9 M10 1 V9" fill="none" stroke="%23C48A2E" stroke-width="1.5" stroke-linecap="round"/></svg>');
  /* The slat SVG is 10px tall. Every placement clears or draws exactly this,
     so the height lives here rather than being restated per rule. */
  --slat-h: 0.625rem;
}

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

  .hero__title,
  .cta__title,
  .doc__title {
    letter-spacing: -0.02em;
    text-wrap: balance;
  }

  .hero__title {
    line-height: 1.12;
  }

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

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

  /* Placement two: the top edge of every muted band. */
  .section--band {
    position: relative;
  }

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

  /* ---- P-hero-split: type on parchment, photo untouched; mobile copy-first */

  .hero--split {
    background: linear-gradient(var(--sand), var(--sand-deep));
  }

  /* Inset, not bled, and the radius is the lesser reason: a photo running into
     the viewport corner has no corner to round, and it also sat with no air on
     any side while every other photograph on the page had a margin of parchment.
     One --gutter, so the inset is a distance the page already holds off its own
     edges rather than a number chosen for this photo. */
  .hero--split .hero__media {
    order: 0;
    aspect-ratio: 4 / 3;
    margin: var(--gutter);
    border-radius: var(--radius-media);
    overflow: hidden;
  }

  .hero--split .hero__media .pic,
  .hero--split .hero__media img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 42%;
  }

  .hero--split .hero__copy { color: var(--text); }
  .hero--split .hero__eyebrow { color: var(--accent-ink); }
  .hero--split .hero__title { max-width: 18ch; color: var(--ink); }
  .hero--split .hero__lead { color: var(--text-muted); }

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

  @media (min-width: 64rem) {
    .hero--split .hero__media {
      order: 0;
      aspect-ratio: auto;
      min-height: 32rem;
    }
  }

  /* ---- P-reviews-featured-list ----------------------------------------- */

  .reviews__featured {
    display: grid;
    gap: var(--space-4);
    margin: 0 0 var(--space-6);
    padding: 0 var(--space-4) var(--space-6);
    border-bottom: 1px solid var(--line);
  }

  .reviews__featured-text {
    margin: 0;
    max-width: 36ch;
    font-family: var(--font-display);
    font-size: var(--step-2);
    line-height: 1.3;
    color: var(--ink);
  }

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

  .reviews__list {
    display: grid;
    gap: var(--space-5);
    margin: 0;
    padding: 0;
    list-style: none;
  }

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

  .reviews__item-text {
    margin: 0;
    color: var(--text-muted);
    font-size: var(--step-0);
  }

  @media (min-width: 64rem) {
    .reviews--featured .wrap {
      display: grid;
      grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
      column-gap: var(--space-8);
      align-items: start;
    }

    .reviews--featured .head,
    .reviews--featured .reviews__more { grid-column: 1 / -1; }

    .reviews__featured {
      margin-bottom: 0;
      padding: 0 var(--space-8) 0 var(--space-4);
      border-bottom: 0;
      border-right: 1px solid var(--line);
    }
  }

  /* ---- P-story-about-banner -------------------------------------------- */

  .story-about {
    display: grid;
    gap: var(--space-6);
    align-items: center;
  }

  .story-about__copy {
    display: grid;
    gap: var(--space-4);
    justify-items: center;
    text-align: center;
    max-width: var(--measure-text);
    margin-inline: auto;
  }

  .story-about__copy .eyebrow,
  .story-about__copy .h2 {
    text-wrap: balance;
  }

  .story-about__body {
    display: grid;
    gap: var(--space-3);
    color: var(--text-muted);
  }

  .story-about__phone {
    margin-top: var(--space-2);
  }

  .story-about__notes {
    display: grid;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-4);
    border-top: 3px solid var(--accent);
    background: var(--surface);
    font-size: var(--step--1);
    text-align: start;
  }

  .story-about__banner {
    position: relative;
    aspect-ratio: 1;
    max-width: 28rem;
    margin-inline: auto;
    border-radius: var(--radius-media);
    overflow: hidden;
  }

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

  @media (min-width: 64rem) {
    .story-about {
      grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
      gap: var(--space-8);
      text-align: start;
    }

    .story-about__copy {
      justify-items: start;
      text-align: start;
      margin-inline: 0;
    }

    .story-about__banner {
      margin-inline: 0;
      max-width: none;
    }
  }

  /* ---- P-picks-scroll-shelf -------------------------------------------- */

  .order-shelf__track {
    display: flex;
    gap: var(--space-4);
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    padding-block: var(--space-2) var(--space-3);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    -webkit-overflow-scrolling: touch;
  }

  .order-shelf__slide {
    flex: 0 0 min(78vw, 18.75rem);
    scroll-snap-align: start;
    display: grid;
    gap: var(--space-4);
    min-width: 0;
    padding: var(--space-4);
    border-top: 4px solid var(--accent);
    background: var(--surface);
  }

  .order-shelf__slide:first-child {
    flex-basis: min(82vw, 21.25rem);
  }

  .order-shelf__media {
    overflow: hidden;
    border-radius: var(--radius-sm);
  }

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

  .order-shelf__price {
    margin-top: auto;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--accent-ink);
  }

  @media (min-width: 56.25rem) {
    .order-shelf__track {
      margin-inline: 0;
      padding-inline: 0;
      padding-block: 0;
      overflow-x: visible;
      scroll-snap-type: none;
    }

    .order-shelf__slide,
    .order-shelf__slide:first-child {
      flex: 1 1 0;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .order-shelf__media img {
      transform: none;
    }
  }
}

/* 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;
  }
}
