/* home.css: the home page. The tour over the pinned 3D view (hero, stop cards, markers, rail, closing list with its finder),
   the Sperlonga band and the night ending (contact). Loaded on home pages only, after site.css.
   Look: "Whitewash and maiolica" (.review/directions/final). Every colour, font and size comes from T5's tokens in theme.css;
   the --h-* names below only add a fallback so this file also renders before those tokens exist.
   Motion: the scroll drives the cards and the camera; anything that moves on its own settles within seconds; reduced motion cuts
   straight to still frames. Never put opacity on .stop or .stops: an opacity ancestor removes the cards' glass blur. */

.page-home {
  --h-calce: var(--calce, #F6F4EF);
  --h-paper: var(--paper, #FFFFFF);
  --h-wall: var(--wall, #ECE8DF);
  --h-grout: var(--grout, #D9D3C6);
  --h-line: var(--line, #8C8574);
  --h-ink: var(--ink, #14213A);
  --h-ink-2: var(--ink-2, #4B5568);
  --h-cobalt: var(--cobalt, #1D4F91);
  --h-cobalt-deep: var(--cobalt-deep, #173F75);
  --h-cobalt-press: var(--cobalt-press, #11315C);
  --h-wash: var(--wash, #E8EEF7);
  --h-wash-press: var(--wash-press, #D9E4F3);
  --h-giallo: var(--giallo, #E6B547);
  --h-ocra: var(--ocra, #7A5206);
  --h-notte: var(--notte, #0E2340);
  --h-on-notte: var(--on-notte, #F6F4EF);
  --h-on-notte-2: var(--on-notte-2, #B9C3D3);
  --h-glass: var(--glass, rgb(248 246 241 / 0.96));
  --h-glass-solid: var(--glass-solid, #F8F6F1);
  --h-glass-edge: var(--glass-edge, rgb(255 255 255 / 0.7));
  --h-glass-blur: var(--glass-blur, blur(20px) saturate(0.7));
  --h-chip: var(--chip, rgb(246 244 239 / 0.92));
  --h-scrim-rgb: var(--scrim-rgb, 6 26 50);
  --h-scrim: var(--scrim-strength, 0.62);
  --h-font-display: var(--font-display, "Bodoni Moda", "Bodoni 72", Didot, Georgia, serif);
  --h-font-text: var(--font-text, var(--font-body, ui-sans-serif, system-ui, sans-serif));
  --h-fs-display: var(--fs-display, clamp(3rem, 1.7rem + 4.9vw, 5.5rem));
  --h-fs-h1: var(--fs-h1, clamp(2.75rem, 1.7rem + 4vw, 4.75rem));
  --h-fs-h2: var(--fs-h2, clamp(2.125rem, 1.7rem + 1.8vw, 3rem));
  --h-lh-head: var(--lh-head, 1.1);
  --h-fs-h2-card: var(--fs-h2-card, clamp(2.125rem, 1.85rem + 1.1vw, 2.75rem));
  --h-fs-lead: var(--fs-lead, clamp(1.125rem, 1.06rem + 0.28vw, 1.3125rem));
  --h-fs-small: var(--fs-small, 0.875rem);
  --h-fs-label: var(--fs-label, 0.75rem);
  --h-opsz-map: var(--opsz-map, 22);
  --h-opsz-page: var(--opsz-page, 30);
  --h-opsz-small: var(--opsz-small, 16);
  --h-r-1: var(--r-1, 3px);
  --h-r-2: var(--r-2, 4px);
  --h-r-3: var(--r-3, 8px);
  --h-shadow-2: var(--shadow-2, 0 1px 2px rgb(14 35 64 / 0.06), 0 8px 20px -8px rgb(14 35 64 / 0.20));
  --h-shadow-card: var(--shadow-card, 0 0 0 1px rgb(14 35 64 / 0.06), 0 2px 4px rgb(14 35 64 / 0.05), 0 28px 64px -24px rgb(14 35 64 / 0.50));
  --h-ease: var(--ease, cubic-bezier(0.2, 0.7, 0.2, 1));
  --h-ease-in-out: var(--ease-in-out, cubic-bezier(0.77, 0, 0.175, 1));
  --h-gutter: var(--gutter, clamp(16px, 5.5vw, 112px));
  --h-page: var(--page, 1216px);
  --h-ring: 0 0 0 2px var(--focus-gap, var(--h-calce)), 0 0 0 4px var(--focus, var(--h-cobalt));
}

/* The design system's facts line, at zero specificity: T5's .facts-line wins wherever it exists */
:where(.page-home .facts-wrap) { overflow: hidden; }
:where(.page-home .facts-line) { --sep: 1.15em; display: flex; flex-wrap: wrap; margin: 0 0 0 calc(-1 * var(--sep)); padding: 0; list-style: none; }
:where(.page-home .facts-line > *) { white-space: nowrap; }
:where(.page-home .facts-line > *)::before { content: "·"; display: inline-block; width: var(--sep); text-align: center; text-indent: 0; }
:where(.page-home .facts-line > .fact-long) { white-space: normal; padding-left: var(--sep); text-indent: calc(-1 * var(--sep)); }

/* ---------------------------------------------------------------------------------------------------------------- */
/* The tour: the stops come first in the DOM; the stage sits under them in the same grid cell and stays pinned */
.story { position: relative; display: grid; grid-template: 1fr / 1fr; background: #0d3a62; color: var(--h-ink); }
.story > .stage, .story > .stops { grid-area: 1 / 1; }
.stage { position: sticky; top: 0; align-self: start; z-index: 0; height: 100vh; height: 100lvh; min-height: 0; overflow: hidden;
  background: linear-gradient(#84a2c8 0%, #a2b4c4 20%, #9ea894 28%, #b6b6a2 45%, #8ea99c 70%, #6cb4b8 100%); } /* the opening frame's colours, until the poster is in */
/* The poster stays at full opacity; only the canvas fades in over it (T1 hides the poster when that fade ends) */
.scene-poster { position: absolute; inset: 0; }
.scene-poster img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* The posters are wider than the screens they cover (tools/scene/shoot-all.mjs), so cover crops only their sides, around
   the point the first frame's lens shift keeps still: the middle (tall) or 67 % across, where the old town is (wide) */
@media (orientation: landscape) { .scene-poster img { object-position: 67% 50%; } }
/* A one-finger sideways drag turns the view (scene.js); vertical swipes scroll the page and two fingers zoom it (WCAG 1.4.4) */
.scene-canvas { position: absolute; inset: 0; display: block; width: 100%; height: 100%; opacity: 0; transition: opacity 0.9s cubic-bezier(0.4, 0, 0.2, 1);
  touch-action: pan-y pinch-zoom; cursor: grab; }
.scene-canvas:active { cursor: grabbing; }
.scene-ready .scene-canvas { opacity: 1; }

/* Still frames of each stop when there is no 3D view (no WebGL, a lost GPU context): src/js/home.js swaps them */
.scene-stills { display: none; position: absolute; inset: 0; }
.scene-stills img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 0.7s var(--h-ease); }
.scene-stills img.is-front { opacity: 1; }
.scene-still-mode .scene-stills { display: block; }
.scene-still-mode :is(.scene-markers, .scene-labels) { visibility: hidden; }
.scene-lost .scene-poster { opacity: 1; }
.scene-lost :is(.scene-canvas, .scene-markers, .scene-labels) { visibility: hidden; }

/* The scrim under the opening words: rgb(6 26 50 / .62) under every hero line, so white text keeps 4.9:1 even over an
   all-white scene. home.js measures the copy (--hero-h); 460 px is the design system's stop. The scrim reaches a screen
   further down than the stage (--scrim-under, clipped), so it can rise with the words as the page scrolls (scrim-rise
   below, or home.js's --scrim-y where scroll-driven animations are missing): the words never climb out of it, however
   short the screen (400 % zoom, a phone held sideways). It fades as the words leave (below). */
.stage::after { content: ""; position: absolute; inset: 0 0 calc(-1 * var(--scrim-under)); z-index: 2; pointer-events: none;
  --scrim-under: 100lvh;
  --copy-x: max(var(--h-gutter), (100% - var(--h-page)) / 2);
  background: linear-gradient(to top, rgb(var(--h-scrim-rgb) / var(--h-scrim)) 0, rgb(var(--h-scrim-rgb) / var(--h-scrim)) calc(var(--scrim-under) + var(--hero-h, 460px)), rgb(var(--h-scrim-rgb) / 0) calc(var(--scrim-under) + var(--hero-h, 460px) + 300px));
  translate: 0 var(--scrim-y, 0px);
  -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(var(--copy-x) + 720px), transparent calc(var(--copy-x) + 1240px));
  mask-image: linear-gradient(to right, #000 0, #000 calc(var(--copy-x) + 720px), transparent calc(var(--copy-x) + 1240px));
  transition: opacity 0.8s ease; }
.story[data-stop]:not([data-stop="intro"]) .stage::after { opacity: 0; }

/* ---------------------------------------------------------------------------------------------------------------- */
/* Markers: positioned by scene.js; labels avoid [data-scene-obstacle] (T1). Hidden during the opening glide. */
.scene-markers, .scene-labels { position: absolute; inset: 0; pointer-events: none; visibility: hidden; transition: opacity 0.5s var(--h-ease); }
.scene-markers { z-index: 5; }
.scene-labels { z-index: 1; } /* under the hero scrim, so place names never compete with the opening words (they are hidden there: .place) */
/* Phones: the pins sit above the opening words, over the scrim (scene.js fitPhone). Where the words leave no room for them,
   those behind the words fade out (is-under-hero). */
/* Pins that step back fade, as the place names do (0.4 s): behind the opening words on small phones (is-under-hero),
   and behind a card, off screen or with no room for the label (is-off, is-label-off). scene.js keeps moving them while they
   fade. Each element's normal transition list includes opacity and visibility, so they fade back in too. */
.marker { transition: opacity 0.4s var(--h-ease), visibility 0s; }
.marker.is-under-hero:not(:focus-visible), .marker.is-off { opacity: 0; }
.marker.is-off { visibility: hidden; transition: opacity 0.4s var(--h-ease), visibility 0s 0.4s; }
.marker:is(.is-under-hero:not(:focus-visible), .is-off) :is(.marker-dot, .marker-label) { pointer-events: none; }
/* Shown once the 3D view is up, never over the poster or a still frame (a lost GPU context, still mode): those have
   their own pins or none */
.scene-ready:not(.scene-lost, .scene-still-mode) :is(.scene-markers, .scene-labels) { visibility: visible; }
/* The pins enter the opening glide already invisible (no fade from 1 to 0 that could show once scene-ready lands);
   only the fade back in, when is-opening goes, uses the 0.5 s transition above */
.is-opening :is(.scene-markers, .scene-labels) { opacity: 0; transition: none; }
.no-markers .scene-markers { display: none; }

.marker { position: absolute; left: 0; top: 0; color: var(--h-ink); text-decoration: none; will-change: translate; } /* scene.js sets translate */
.scene-ready:not(.is-opening) .marker { animation: marker-in 0.5s var(--h-ease) backwards; animation-delay: calc(0.1s + var(--i, 0) * 70ms); }
@keyframes marker-in { from { opacity: 0; scale: 0.9; } }
.marker-stem { position: absolute; z-index: 1; left: -0.5px; bottom: 0; width: 1px; background: #fff; box-shadow: 0 0 0 0.5px rgb(14 35 64 / 0.3); transition: opacity 0.3s ease, visibility 0s; }
.marker.is-below .marker-stem { bottom: auto; top: 0; }
.marker-dot { position: absolute; z-index: 1; left: -7px; top: -7px; width: 14px; height: 14px; border-radius: 50%; pointer-events: auto;
  background: var(--h-cobalt); box-shadow: 0 0 0 3px #fff, 0 2px 6px rgb(14 35 64 / 0.45); transition: opacity 0.3s ease; }
.marker-dot::before { content: ""; position: absolute; inset: -15px; } /* 44 px hit area where only the dot shows (phones) */
/* Phones, the opening view: pins too close to tap apart show as one, with the number of properties there (scene.js
   is-group, .marker-count; white on cobalt 8.6:1). The dot grows to 20 px, its hit area stays 44 px. */
.marker.is-group .marker-dot { left: -10px; top: -10px; width: 20px; height: 20px; }
.marker.is-group .marker-dot::before { inset: -12px; }
.marker-count { position: absolute; z-index: 2; left: -10px; top: -10px; display: none; width: 20px; height: 20px; place-items: center;
  pointer-events: none; color: #fff; font: 700 0.75rem / 1 var(--h-font-text); font-variant-numeric: tabular-nums lining-nums; }
.marker.is-group .marker-count { display: grid; }
/* One ring goes out from the selected pin, twice, then stops (renamed so it replays for each new selection) */
.marker-dot::after { content: ""; position: absolute; inset: -3px; border-radius: 50%; border: 2px solid var(--h-cobalt); opacity: 0; animation: none; }
.marker.is-selected .marker-dot::after { animation: ping-sel 1.6s var(--h-ease) 2 both; }
@keyframes ping-sel { 0% { opacity: 0.8; scale: 1; } 70%, 100% { opacity: 0; scale: 3.2; } }
.marker-label { position: absolute; z-index: 2; left: 0; top: 0; pointer-events: auto; display: inline-flex; align-items: center; gap: 8px;
  min-height: 36px; padding: 0 12px; white-space: nowrap; background: rgb(255 255 255 / 0.96); color: var(--h-ink); border-radius: var(--h-r-2);
  font: 600 0.875rem / 1 var(--h-font-text); box-shadow: 0 0 0 1px rgb(14 35 64 / 0.10), 0 6px 14px -6px rgb(14 35 64 / 0.45);
  transition: background-color 0.2s ease, color 0.2s ease, scale 0.2s var(--h-ease), box-shadow 0.2s ease, opacity 0.4s var(--h-ease), visibility 0s; }
/* No room for the label (scene.js): the label and its stem fade out, the dot stays */
.marker.is-label-off .marker-label { opacity: 0; visibility: hidden; pointer-events: none;
  transition: background-color 0.2s ease, color 0.2s ease, scale 0.2s var(--h-ease), box-shadow 0.2s ease, opacity 0.4s var(--h-ease), visibility 0s 0.4s; }
.marker.is-label-off .marker-stem { opacity: 0; visibility: hidden; transition: opacity 0.4s var(--h-ease), visibility 0s 0.4s; }
.marker-label::before { content: ""; flex: none; width: 7px; height: 7px; background: var(--h-cobalt); rotate: 45deg; transition: background-color 0.2s ease; }
.marker-label::after { content: ""; position: absolute; inset: -8px -4px; } /* 52 px tall hit area: still 44 px when a pin steps back to 0.85 */
.marker:is(.is-selected, .is-hot) .marker-label { background: var(--h-cobalt); color: #fff; }
.marker:is(.is-selected, .is-hot) .marker-label::before { background: #fff; }
/* Unselected pins step back without fading their labels (label text keeps full contrast) */
.story.has-selection .marker:not(.is-selected) :is(.marker-stem, .marker-dot) { opacity: 0.5; }
.story.has-selection .marker:not(.is-selected) .marker-label { scale: 0.9; }
.marker.is-dim :is(.marker-stem, .marker-dot) { opacity: 0.3; }
.marker.is-dim .marker-label { scale: 0.85; }
/* Focus and hover always bring a pin back to full strength (the selectors match the dimming rules' weight) */
.story.has-selection .marker:is(:focus-visible, :hover) :is(.marker-label, .marker-stem, .marker-dot),
.marker:is(:focus-visible, :hover) :is(.marker-label, .marker-stem, .marker-dot) { opacity: 1; scale: 1; }
.marker:hover:not(.is-selected, .is-hot) .marker-label { background: #fff; color: var(--h-cobalt); }
.marker:focus-visible { outline: none; box-shadow: none; }
.marker:focus-visible .marker-label { outline: 2px solid transparent; outline-offset: 2px; box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--h-cobalt); }
.marker:hover, .marker:focus-within { z-index: 5000 !important; }

/* Desktop: a photo preview over the hovered or focused pin (images load once the 3D view is up) */
.marker-peek { display: none; }
@media (hover: hover) and (pointer: fine) {
  .scene-ready .marker-peek { display: block; position: absolute; left: calc(50% + var(--peek-dx, 0px)); bottom: calc(100% + 8px); width: 200px; pointer-events: none;
    translate: -50% 6px; scale: 0.96; opacity: 0; border-radius: var(--h-r-3); background: var(--h-glass-solid); color: var(--h-ink-2);
    box-shadow: 0 10px 30px rgb(5 28 46 / 0.25); transition: opacity 0.14s ease, translate 0.14s ease, scale 0.14s ease; }
  .marker-peek.peek-below { bottom: auto; top: calc(100% + 8px); }
  .marker-peek img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; border-radius: var(--h-r-3) var(--h-r-3) 0 0; }
  /* The 8 px gap between the label and the preview: the pointer can cross it onto the preview (WCAG 1.4.13, hoverable) */
  .marker-peek::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 8px; }
  .marker-peek.peek-below::after { top: auto; bottom: 100%; }
  .marker-peek-line { display: block; padding: 8px 10px 9px; white-space: normal; font: 500 0.75rem / 1.35 var(--h-font-text); font-variant-numeric: tabular-nums; }
  /* Shown on hover and keyboard focus, and the pointer may move onto it; Escape dismisses it (.peek-off, home.js) */
  .marker:is(:hover, :focus-visible):not(.is-selected, .peek-off) .marker-peek { opacity: 1; translate: -50% 0; scale: 1; transition-delay: 80ms; pointer-events: auto; }
}

/* Place names over the map (scene.js) */
.place { position: absolute; left: 0; top: 0; white-space: nowrap; color: var(--h-ink); font: 600 0.75rem / 1 var(--h-font-text); letter-spacing: 0.14em; text-transform: uppercase;
  text-shadow: 0 0 2px #f7f3ea, 0 0 8px rgb(247 243 234 / 0.9), 0 0 14px rgb(247 243 234 / 0.5); transition: opacity 0.4s ease; }
.place-town { font-size: 1rem; letter-spacing: 0.24em; }
.place-lake, .place-beach { text-transform: none; letter-spacing: 0.01em; font-weight: 500; font-size: 0.8125rem; color: var(--h-cobalt-press); }
.place-far { text-transform: none; letter-spacing: 0.02em; color: var(--h-ink-2); }
.place.is-hidden { opacity: 0; }
/* The intro: no place names. Under the hero scrim they would be 2.1-2.5:1; they fade in as the tour starts and the scrim
   fades out (the same condition) */
.story:is(:not([data-stop]), [data-stop="intro"]) .place { opacity: 0; }

/* The map credit: a calce chip, 11 px */
.scene-credit { position: absolute; right: 12px; bottom: 12px; z-index: 6; max-width: calc(100% - 24px); margin: 0; padding: 3px 8px; border-radius: var(--h-r-1);
  background: var(--h-chip); color: var(--h-ink-2); font-size: 0.6875rem; line-height: 1.4; text-align: right; transition: opacity 0.2s var(--h-ease); }
/* While the opening words or the closing list pass over it (home.js), the credit steps back: it fades rather than show
   through their lines or be cut by the card's edge. Its links stay in the tab order, and keyboard focus brings it back. */
.scene-credit.is-under:not(:focus-within) { opacity: 0; pointer-events: none; }
/* The credit sits in the pinned stage, so a scroll can't bring it into view: keyboard focus on its links never scrolls the
   page for nothing (this cancels the page's anchor offset, site.css), and home.js keeps it clear of the header and cards */
.scene-credit a { position: relative; color: inherit; scroll-margin-top: calc(-1 * var(--anchor-clear, 5.5rem)); }
/* The links' targets never reach above the chip, where the selected pin may stand on a small phone (scene.js stopView);
   they are links inside a sentence, so their size may follow the line (WCAG 2.5.8) */
.scene-credit a::after { content: ""; position: absolute; inset: -3px -4px; }
/* The link to the sources and licences on /legal/ stays on one line, so its target is one box. It is always on the last
   line, so its target grows down to a 32 px band, below the chip's text */
.scene-credit .credit-sources { white-space: nowrap; }
.scene-credit .credit-sources::after { top: -2px; bottom: -15px; }

/* ---------------------------------------------------------------------------------------------------------------- */
/* Stops */
.stops { position: relative; z-index: 6; pointer-events: none; }
.stop { position: relative; display: flex; align-items: center; min-height: 100svh; padding: 6rem 0 2.5rem; pointer-events: none; }
/* A jump to a stop (the hero links, the header's links, the rail, keyboard focus) lands with the section's top at the top of
   the screen: each stop already pads its card clear of the header, so the page's anchor offset (site.css) is cancelled */
#scene .stop { scroll-margin-top: calc(-1 * var(--anchor-clear, 5.5rem)); }
.stop > * { pointer-events: auto; }

/* Hero */
.stop-intro { align-items: flex-end; padding-bottom: clamp(3rem, 9vh, 6rem); }
.hero-copy { max-width: 44rem; margin-left: max(var(--h-gutter), (100% - var(--h-page)) / 2); margin-right: var(--h-gutter); color: #fff;
  --focus: #fff; --focus-gap: rgb(var(--h-scrim-rgb)); }
/* The notice (Pages CMS "Avviso"), first line of the opening words: the fixed header would cover a bar above it.
   Ink on giallo wash (8.5:1), as on the other pages; it leaves with the opening words when the tour starts. */
.announcement-hero { width: fit-content; max-width: 36rem; margin: 0 0 18px; padding: 8px 14px; border-radius: var(--h-r-2, 4px);
  text-align: left; line-height: 1.45; box-shadow: inset 0 0 0 1px rgb(230 181 71 / 0.7), 0 1px 12px rgb(4 20 40 / 0.18);
  --focus: var(--h-cobalt); --focus-gap: var(--giallo-wash); }   /* the site's focus ring (site.css), cobalt on the wash */
.announcement-hero a { text-underline-offset: 0.2em; }
.hero-copy .eyebrow { margin: 0 0 12px; color: #fff; }
.hero-copy h1 { max-width: 9.2em; margin: 0 0 24px; font-size: var(--h-fs-display); line-height: 1.02; color: #fff;
  font-variation-settings: "opsz" var(--h-opsz-map); text-shadow: 0 2px 24px rgb(4 20 40 / 0.3); }
.hero-lead { max-width: 28em; margin: 0 0 28px; font-size: var(--h-fs-lead); line-height: 1.5; color: #fff; text-shadow: 0 1px 12px rgb(4 20 40 / 0.35); }
/* Narrow wide screens (900-1200 px): the opening view's first pins stand at about 60 % of the width (scene.js fitIntro caps
   the words' side there), so the lead's lines stop short of them and the pins' labels keep room beside the words */
@media (min-width: 56.25rem) and (max-width: 75rem) { .hero-lead { max-width: min(28em, 44vw); } }
.scroll-cue { display: inline-flex; align-items: center; gap: 14px; min-height: 44px; padding-right: 6px; border-radius: var(--h-r-2); color: #fff; font-weight: 600; text-decoration: none; }
.scroll-cue .rail { position: relative; width: 1px; height: 40px; overflow: hidden; background: rgb(255 255 255 / 0.5); }
/* The bead runs down the line twice (4.8 s), then rests in the middle */
.scroll-cue .rail::after { content: ""; position: absolute; left: -1px; top: 13px; width: 3px; height: 14px; background: var(--h-giallo); animation: cue 2.4s var(--h-ease-in-out) 1.2s 2 both; }
@keyframes cue { 0% { translate: 0 0; } 40% { translate: 0 30px; } 40.01% { translate: 0 -30px; } 100% { translate: 0 0; } }
.scroll-cue:hover span { text-decoration: underline; text-underline-offset: 6px; }
.hero-hint { margin: 4px 0 0; font-size: 0.92rem; color: #fff; text-shadow: 0 1px 10px rgb(4 20 40 / 0.4); }
.hero-hint a { padding-block: 14px; color: #fff; text-decoration-color: rgb(255 255 255 / 0.6); text-underline-offset: 0.22em; }
.hero-hint a:hover { text-decoration-color: #fff; }
/* No map to choose from (no JavaScript, no WebGL, Save-Data, a GPU context lost for 4 s): the hint offers the list alone */
.hero-hint-list { display: none; }
html:not(.js) .hero-hint-map, #scene:is(.scene-failed, .scene-still-mode) .hero-hint-map { display: none; }
html:not(.js) .hero-hint-list, #scene:is(.scene-failed, .scene-still-mode) .hero-hint-list { display: inline; }
.scene-load { margin-top: 12px; }
.page-home :is(.scene-load, .finder-reset, .index-reason)[hidden] { display: none; }
.scroll-cue:focus-visible, .hero-hint a:focus-visible { outline: 2px solid transparent; outline-offset: 2px; box-shadow: var(--h-ring); }
/* Entrance: the opening words arrive one after another */
.js .hero-copy > * { animation: hero-in 0.8s var(--h-ease) backwards; }
.js .hero-copy > :nth-child(2) { animation-delay: 80ms; }
.js .hero-copy > :nth-child(3) { animation-delay: 160ms; }
.js .hero-copy > :nth-child(4) { animation-delay: 240ms; }
.js .hero-copy > :nth-child(n + 5) { animation-delay: 320ms; }
@keyframes hero-in { from { opacity: 0; translate: 0 14px; } }
/* The heading is the page's largest paint: it rises with the others but is never transparent, so it counts at first paint */
.js .hero-copy > :where(h1) { animation-name: hero-rise; }
@keyframes hero-rise { from { translate: 0 14px; } }

/* Stop cards: glass over the map */
/* --card-pad-x and --card-pad-b are the card's side and bottom padding: the sticky action row of a card that scrolls inside
   itself (phones held sideways, below) reaches out to the card's edges with them */
.stop-card { box-sizing: border-box; width: min(28rem, calc(100% - 2rem)); margin-left: max(var(--h-gutter), (100% - var(--h-page)) / 2);
  --card-pad-x: 28px; --card-pad-b: 22px; padding: 28px var(--card-pad-x) var(--card-pad-b);
  color: var(--h-ink); background: var(--h-glass); -webkit-backdrop-filter: var(--h-glass-blur); backdrop-filter: var(--h-glass-blur);
  border: 1px solid var(--h-glass-edge); border-radius: var(--h-r-3); box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.85), var(--h-shadow-card); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .stop-card { background: var(--h-glass-solid); } }
.stop-top { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 12px; }
.stop-top .eyebrow, .stop-top .where { margin: 0; }
.stop-count { flex: none; font-size: 0.8125rem; font-weight: 600; font-variant-numeric: tabular-nums lining-nums; color: var(--h-ink-2); }
.stop-card h2 { margin: 0 0 16px; font-size: var(--h-fs-h2-card); line-height: 1.05; font-variation-settings: "opsz" var(--h-opsz-map);
  overflow-wrap: break-word; } /* a name wider than the narrowest card (320 px with wider text spacing) breaks, never spills */
.stop-card h2:focus { outline: none; box-shadow: none; }
.stop-card[tabindex="0"]:focus-visible { outline: 2px solid transparent; outline-offset: 2px; box-shadow: var(--h-ring); } /* a card that scrolls (home.js) */
.stop-photo { margin: 0 0 16px; }
.stop-photo img { display: block; width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; border-radius: var(--h-r-2); background: var(--h-wall); }
.stop-text { max-width: 36rem; margin: 0 0 12px; }
.stop-meta { margin: 0 0 16px; padding-top: 12px; border-top: 1px solid var(--h-grout); font-size: var(--h-fs-small); color: var(--h-ink-2); font-variant-numeric: tabular-nums lining-nums; }
.stop-cin { letter-spacing: 0.02em; }
.stop-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 20px; }
.stop-actions .button .icon { flex: none; width: 18px; height: 18px; margin-right: -4px; transition: translate 0.2s var(--h-ease); }
.stop-actions .button:hover .icon-go { translate: 3px 0; }
.stop-details .icon { display: none; }

/* Cards while the tour runs (browsers without scroll-driven animations, and reduced motion): the card at the centre is
   whole, the others are faint enough that no half-readable copy sits over the map. A focused card is always whole. */
.scene-ready .stop-card { opacity: 0.12; translate: 0 26px; transition: opacity 0.6s ease, translate 0.9s var(--h-ease); }
.scene-ready .stop.is-active .stop-card, .scene-ready .stop-card:focus-within { opacity: 1; translate: none; scale: none; }
.scene-ready .stop-intro .hero-copy { transition: opacity 0.6s ease, translate 0.9s var(--h-ease); }
.scene-ready .stop-intro:not(.is-active) .hero-copy:not(:focus-within) { opacity: 0; translate: 0 -24px; }

/* Arrival after a jump (marker, rail, link): the card's parts rise in one after another */
.stop.is-arriving .stop-card > * { animation: card-rise 420ms var(--h-ease) backwards; animation-delay: calc(var(--i, 0) * 40ms); }
@keyframes card-rise { from { opacity: 0; translate: 0 10px; } }

/* The closing list of the six houses, with its finder */
.stop-outro { align-items: center; }
.stop-index { width: min(37.5rem, calc(100% - 2rem)); padding: 28px 16px 12px; } /* wide enough for the stepper and both chips on one row */
.stop-index h2 { margin: 0 0 12px; padding: 0 12px; }
.index-list { margin: 0; padding: 0; list-style: none; }
.index-list li { position: relative; }
.index-list li + li { border-top: 1px solid rgb(140 133 116 / 0.28); }
.index-list a { display: grid; grid-template-columns: 52px minmax(0, 1fr) auto 18px; grid-template-rows: auto auto auto; column-gap: 16px; align-items: center; align-content: center;
  min-height: 76px; padding: 10px 12px; border-radius: var(--h-r-2); color: var(--h-ink); text-decoration: none; transition: background-color 0.2s var(--h-ease); }
.index-list a:hover, .index-list li.is-hot > a { background: var(--h-wash); }
.index-list img, .index-img-empty { grid-row: 1 / 4; width: 52px; height: 60px; object-fit: cover; border-radius: 26px 26px 2px 2px; transition: opacity 0.3s ease, filter 0.3s ease; } /* arched like the old town's doorways */
.index-img-empty { background: var(--h-wall, #ECE8DF); } /* a property saved without photos */
.index-name { grid-column: 2; grid-row: 1; align-self: end; font: 500 1.4375rem / 1.15 var(--h-font-display); font-variation-settings: "opsz" var(--h-opsz-small); }
.index-meta { grid-column: 2; grid-row: 2; align-self: start; font-size: var(--h-fs-small); color: var(--h-ink-2); }
.index-icon { display: inline-flex; align-items: center; }
.index-icon .icon { width: 16px; height: 16px; color: var(--h-ink-2); }
.index-reason { grid-column: 2; grid-row: 3; display: flex; align-items: center; gap: 6px; margin-top: 2px; font-size: 0.8125rem; font-weight: 500; line-height: 1.35; color: var(--h-ink); }
.index-reason::before { content: ""; flex: none; width: 14px; height: 14px; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='8.5'/%3E%3Cpath d='M12 7.6v5.2M12 16.2v.1'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='12' cy='12' r='8.5'/%3E%3Cpath d='M12 7.6v5.2M12 16.2v.1'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat; }
.index-dist { grid-column: 3; grid-row: 1 / 4; text-align: right; font-size: 0.8125rem; line-height: 1.3; color: var(--h-ink-2); white-space: nowrap; }
.index-note { margin: 10px 0 0; padding: 0 12px; font-size: 0.8125rem; line-height: 1.35; color: var(--h-ink-2); }
.index-dist b { display: block; font-size: 1rem; font-weight: 600; color: var(--h-ink); font-variant-numeric: tabular-nums lining-nums; }
.index-arrow { grid-column: 4; grid-row: 1 / 4; display: flex; color: var(--h-cobalt); transition: translate 0.2s var(--h-ease); }
.index-arrow .icon { width: 18px; height: 18px; }
@media (hover: hover) and (pointer: fine) { .index-list a:hover .index-arrow { translate: 3px 0; } }
.index-list a:focus-visible { outline: 2px solid transparent; outline-offset: -2px; box-shadow: inset 0 0 0 2px var(--h-cobalt), inset 0 0 0 4px var(--h-calce); }
/* Rows that don't match the finder: only the photo fades; every word keeps full contrast */
.index-list li.is-out img { opacity: 0.45; filter: grayscale(1); }

/* Finder: guests, pets and parking. Without JavaScript it stays hidden and the list is the plain list. */
html:not(.js) .finder-wrap { display: none; }
.finder-wrap { margin: 0 12px 10px; }
.finder-summary { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 44px; padding: 0 12px 0 14px; list-style: none; cursor: pointer;
  background: var(--h-paper); color: var(--h-cobalt); border: 1px solid var(--h-cobalt); border-radius: var(--h-r-2); font-weight: 600; font-size: 0.9375rem;
  transition: background-color 0.2s var(--h-ease); }
.finder-summary::-webkit-details-marker { display: none; }
.finder-summary:hover { background: var(--h-wash); }
.finder-summary:focus-visible { outline: 2px solid transparent; outline-offset: 2px; box-shadow: var(--h-ring); }
.finder-chevron { flex: none; transition: rotate 0.2s var(--h-ease); }
.finder-wrap[open] .finder-chevron { rotate: 180deg; }
.finder { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; padding: 12px 0 2px; }
.finder-guests { display: flex; align-items: center; gap: 10px; }
.finder-label { font-size: var(--h-fs-small); font-weight: 600; }
.stepper { display: inline-grid; grid-template-columns: 44px 2.5rem 44px; overflow: hidden; background: var(--h-paper); border: 1px solid var(--h-line); border-radius: var(--h-r-2);
  box-shadow: inset 0 1px 2px rgb(14 35 64 / 0.06); }
.stepper button { display: grid; place-items: center; width: 44px; min-height: 44px; padding: 0; border: 0; background: none; color: var(--h-cobalt); cursor: pointer; transition: background-color 0.2s var(--h-ease); }
.stepper button:hover { background: var(--h-wash); }
.stepper button:active { background: var(--h-wash-press); }
.stepper button[aria-disabled="true"] { color: #9C9688; background: none; cursor: not-allowed; } /* stays focusable, so focus never drops */
.stepper button:focus-visible { outline: 2px solid transparent; outline-offset: -2px; box-shadow: inset 0 0 0 2px var(--h-cobalt), inset 0 0 0 4px var(--h-paper); }
.stepper-value { display: grid; place-items: center; border-inline: 1px solid var(--h-grout); font-weight: 600; font-size: 1.0625rem; font-variant-numeric: tabular-nums lining-nums; }
.chip { display: inline-flex; align-items: center; gap: 0.45rem; min-height: 44px; padding: 0 0.75rem 0 0.65rem; cursor: pointer; user-select: none;
  background: var(--h-paper); color: var(--h-ink); border: 1px solid var(--h-line); border-radius: var(--h-r-1); font-size: 0.9375rem; font-weight: 500;
  transition: background-color 0.2s var(--h-ease), border-color 0.2s var(--h-ease); }
.chip:hover { border-color: var(--h-cobalt); }
.chip .icon { width: 18px; height: 18px; color: var(--h-cobalt); }
.chip input { flex: none; display: grid; place-items: center; width: 18px; height: 18px; margin: 0; -webkit-appearance: none; appearance: none; cursor: pointer;
  background: var(--h-paper); border: 1.5px solid var(--h-line); border-radius: 3px; }
.chip input:checked { background: var(--h-cobalt) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m6 12.5 4 4 8-9' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 14px no-repeat; border-color: var(--h-cobalt); }
.chip:has(input:checked) { background: var(--h-wash); border-color: var(--h-cobalt); color: var(--h-cobalt-press); }
.chip input:focus-visible { outline: none; box-shadow: none; }
.chip:has(input:focus-visible) { outline: 2px solid transparent; outline-offset: 2px; box-shadow: var(--h-ring); }
.finder-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; flex-basis: 100%; }
.finder-foot:has(.finder-count:empty):has(.finder-reset[hidden]) { margin-top: -10px; } /* the status line stays rendered, so it is announced */
.finder-count { margin: 0; font-size: var(--h-fs-small); font-weight: 500; color: var(--h-ink); font-variant-numeric: tabular-nums; }
/* Wide screens: the finder is always open, so its summary goes */
@media (min-width: 56.25rem) {
  .finder-summary { display: none; }
  .finder { padding-top: 0; }
}

/* Wide screens: a card fits between the header and the bottom of the screen (the stop's 6rem and 2.5rem). The photo is
   260 px tall on tall screens and gets shorter on short ones (1280x720, 1366x768: 570 px is the tallest card's other parts
   and the stop's padding); all of a screen's photos are the same height. Below 120 px it stops shrinking: a card that
   still doesn't fit (a laptop's 1366x657) starts under the header, never above it, and a jump to it lands there (home.js
   rail, scene.js pins).
   The map credit stays right of the cards' column (the closing list is the widest card, 37.5rem): a long credit wraps
   instead of running under a card. */
@media (min-width: 56.25rem) {
  .stop { align-items: safe center; }
  .stop-photo img { aspect-ratio: auto; height: clamp(120px, 100svh - 570px, 260px); }
  .scene-credit { max-width: calc(100% - max(var(--h-gutter), (100% - var(--h-page)) / 2) - min(37.5rem, 100% - 2rem) - 28px); }
}
@media (min-width: 56.25rem) and (max-height: 50rem) {
  .stop-index { padding-top: 22px; }
  .index-list a { min-height: 62px; padding-block: 5px; }
  .index-list img { width: 46px; height: 52px; border-radius: 23px 23px 2px 2px; }
  .index-list a { grid-template-columns: 46px minmax(0, 1fr) auto 18px; }
}

/* Tour rail (wide screens): one dot per stop; the bead follows --tour-p, the tour's progress that scene.js (or home.js)
   writes on the rail ([data-tour-progress]): 1 is the first stop, --rail-max + 1 the closing list (home.njk sets it) */
.tour-rail { display: none; }
@media (min-width: 56.25rem) and (min-height: 35rem) {
  .tour-rail { display: block; position: absolute; z-index: 7; top: 50%; right: max(var(--h-gutter), (100% - var(--h-page)) / 2); translate: 0 -50%;
    transition: opacity 0.3s var(--h-ease); }
  /* Before the first stop and at the closing list the rail steps back, but stays in the tab order and the accessibility
     tree: keyboard focus on one of its links brings it back */
  .story:is(:not([data-stop]), [data-stop="intro"], [data-stop="index"]) .tour-rail:not(:focus-within) { opacity: 0; pointer-events: none; }
  .tour-rail ol { position: relative; margin: 0; padding: 4px; list-style: none; background: var(--h-glass); border: 1px solid var(--h-glass-edge); border-radius: var(--h-r-3);
    box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.85), var(--h-shadow-2); }
  .tour-rail ol::before { content: ""; position: absolute; left: 50%; top: 27px; bottom: 27px; width: 2px; margin-left: -1px; background: var(--h-grout); }
  .tour-rail a { position: relative; display: grid; place-items: center; width: 44px; height: 44px; border-radius: var(--h-r-2); color: var(--h-ink); text-decoration: none; }
  .rail-dot { position: relative; width: 10px; height: 10px; border-radius: 50%; background: #fff; box-shadow: 0 0 0 2px var(--h-cobalt); }
  .rail-property .rail-dot { background: radial-gradient(circle, var(--h-cobalt) 0 2.4px, #fff 2.9px); }
  .rail-index .rail-dot { border-radius: 2px; }
  .rail-name { position: absolute; top: 50%; right: calc(100% + 12px); padding: 5px 9px; translate: 6px -50%; opacity: 0; pointer-events: none; white-space: nowrap;
    background: rgb(255 255 255 / 0.92); color: var(--h-ink); border-radius: var(--h-r-2); font: 600 0.8rem / 1.2 var(--h-font-text); box-shadow: var(--h-shadow-2);
    transition: opacity 0.2s ease, translate 0.25s var(--h-ease); }
  /* A dot's name shows on keyboard focus and under a real pointer, never by itself: the current stop is already marked by
     the bead, the card's heading and the selected pin. The pointer may move onto the shown name (the 12 px gap to the dot
     is bridged), and Escape dismisses it until the next hover or focus (.name-off, home.js): WCAG 1.4.13. */
  .rail-name::after { content: ""; position: absolute; left: 100%; top: 0; bottom: 0; width: 12px; }
  .tour-rail a:focus-visible:not(.name-off) .rail-name { opacity: 1; translate: 0 -50%; pointer-events: auto; }
  @media (hover: hover) { .tour-rail a:hover:not(.name-off) .rail-name { opacity: 1; translate: 0 -50%; pointer-events: auto; } }
  .tour-rail a:focus-visible { outline: 2px solid transparent; outline-offset: -2px; box-shadow: inset 0 0 0 2px var(--h-cobalt), inset 0 0 0 4px var(--h-calce); }
  .rail-bead { position: absolute; left: 50%; top: 20px; width: 14px; height: 14px; margin-left: -7px; border-radius: 50%; pointer-events: none;
    background: var(--h-giallo); box-shadow: 0 0 0 2px var(--h-cobalt), 0 1px 4px rgb(14 35 64 / 0.35);
    translate: 0 calc(clamp(0, var(--tour-p, 1) - 1, var(--rail-max, 8)) * 44px); }
  .tour-p-fallback .rail-bead { transition: translate 0.45s var(--h-ease); }
  .stop-count { display: none; }
}
/* Forced colours (Windows high contrast) turn backgrounds into Canvas and drop box-shadows, which took the rail's line,
   dots and bead with them. In the system's colours instead: the line and the dots' rings in CanvasText, a property's dot
   with a CanvasText centre, the closing list's dot square, and the bead a Highlight disc ringed in CanvasText. */
@media (forced-colors: active) {
  .tour-rail ol::before { background: CanvasText; }
  .rail-dot, .rail-property .rail-dot { border: 2px solid CanvasText; background: Canvas; }
  .rail-property .rail-dot::after { content: ""; position: absolute; inset: 1px; border-radius: 50%; background: CanvasText; }
  .rail-bead { background: Highlight; border: 2px solid CanvasText; }
}

/* ---------------------------------------------------------------------------------------------------------------- */
/* The hero scrim rises with the page's own scroll, a screen at most: it follows the opening words, so reduced motion keeps
   it too (where it fades as the tour starts, above). Its fade with the words is below. */
@supports (animation-timeline: scroll()) {
  .stage::after { animation: scrim-rise linear both; animation-timeline: scroll(root block); animation-range: 0 100lvh; }
}
/* Scroll-linked cards: each card rises in as it enters and leaves as it passes under the header, tied to the scroll itself */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .scene-ready .stop-card, .scene-ready .stop.is-active .stop-card { opacity: 1; translate: none; scale: none; transition: none; }
    .scene-ready .stop-card, .scene-ready .stop-intro .hero-copy { animation: tour-card linear both; animation-timeline: view(block 5rem 0px); }
    .scene-ready .stop-index { animation-name: tour-card-last; }
    /* Phones held sideways: the header scrolls away (T5), so cards dissolve at the top edge itself */
    @media (orientation: landscape) and (max-height: 31.25rem) {
      .scene-ready .stop-card, .scene-ready .stop-intro .hero-copy { animation-timeline: view(block 0px 0px); }
    }
    .scene-ready .stop-intro:not(.is-active) .hero-copy:not(:focus-within) { opacity: 1; translate: none; }
    .scene-ready .stop-card:focus-within, .scene-ready .stop-intro .hero-copy:focus-within { animation: none; opacity: 1; translate: none; scale: none; }
    .stage::after { animation: shade-out linear both, scrim-rise linear both; animation-timeline: scroll(root block), scroll(root block); animation-range: 0 70svh, 0 100lvh; }
    .story[data-stop]:not([data-stop="intro"]) .stage::after { opacity: 1; }
  }
}
/* The hero scrim fades with the opening words, on their own timeline: as they dissolve under the header (tour-card's
   range), or, with no 3D view (the words stay whole and scroll away), once they have left the screen. Keyboard focus in
   the words keeps them whole (above), and the scrim with them. */
@supports (animation-timeline: view()) and (timeline-scope: --a) {
  .story { timeline-scope: --hero, --hero-all; }
  .stop-intro .hero-copy { view-timeline: --hero block, --hero-all block; view-timeline-inset: 5rem 0px, 0px 0px; }
  @media (orientation: landscape) and (max-height: 31.25rem) { .stop-intro .hero-copy { view-timeline-inset: 0px 0px, 0px 0px; } }
  @media (prefers-reduced-motion: no-preference) {
    .stage::after { animation: shade-out linear both, scrim-rise linear both; animation-timeline: --hero-all, scroll(root block); animation-range: exit 100% exit 140%, 0 100lvh; }
    .scene-ready .stage::after { animation-timeline: --hero, scroll(root block); animation-range: exit 0% exit 20%, 0 100lvh; }
    .story:has(.stop-intro .hero-copy:focus-within) .stage::after { animation-name: none, scrim-rise; }
  }
}
/* A card rises in from below, stays whole while it is in view, and dissolves as its top meets the header (5rem inset),
   so no half-visible card ever sits under the header. The closing list is the last card and leaves with the whole stage,
   and on small phones it is taller than the screen: it only rises in. */
@keyframes tour-card {
  entry 0% { opacity: 0; translate: 0 56px; scale: 0.97; }
  entry 55% { opacity: 1; translate: 0 0; scale: 1; }
  exit 0% { opacity: 1; }
  exit 5%, exit 100% { opacity: 0; }
}
@keyframes tour-card-last {
  entry 0% { opacity: 0; translate: 0 56px; scale: 0.97; }
  entry 55% { opacity: 1; translate: 0 0; scale: 1; }
}
@keyframes shade-out { to { opacity: 0; } }
@keyframes scrim-rise { to { translate: 0 -100lvh; } }   /* -1 x --scrim-under */
@keyframes actions-edge { 0%, 85% { box-shadow: 0 -12px 14px -12px rgb(14 35 64 / 0.28); } 100% { box-shadow: 0 -12px 14px -12px rgb(14 35 64 / 0); } }

/* ---------------------------------------------------------------------------------------------------------------- */
/* Phones and narrow windows: compact cards at the bottom of the screen, the map above them. Short screens held sideways,
   however wide (932x430, a short desktop window), get the same compact cards; they stand beside the map (below). */
@media (max-width: 56.24rem), (orientation: landscape) and (max-height: 31.25rem) {
  .stop { align-items: flex-end; padding: 5rem 0 0.75rem; }
  .stop-card { width: auto; margin-inline: 0.75rem; }
  .stop-card-property, .stop-card-place { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); column-gap: 0.85rem;
    --card-pad-x: 0.95rem; --card-pad-b: 0.95rem; padding: 0.9rem var(--card-pad-x) var(--card-pad-b); }
  .stop-card-property > *, .stop-card-place > * { grid-column: 1 / -1; }
  /* A blur over a moving canvas costs the GPU every frame: phone cards are opaque instead (and no pin shows through) */
  .stop-card { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--h-glass-solid); }
  .stop-card .stop-photo { grid-column: 1; grid-row: 1 / span 2; align-self: start; margin: 0; }
  .stop-photo img { width: 5.5rem; height: 5.5rem; aspect-ratio: 1; }
  .stop-card .stop-top { grid-column: 2; grid-row: 1; align-self: end; align-items: flex-start; margin-bottom: 6px; }
  .stop-card h2 { grid-column: 2; grid-row: 2; align-self: start; margin: 0; font-size: 1.875rem; line-height: 1.05; }
  .stop-text { margin: 0.6rem 0 0.3rem; font-size: 0.95rem; line-height: 1.5; }
  /* A property's summary is cut at two lines: the whole text is on the property page. A place has no page of its own, so
     its text is never cut. */
  .stop-card-property .stop-text { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
  .stop-meta { margin: 0.4rem 0 0.6rem; padding-top: 0.5rem; font-size: 0.86rem; }
  .stop-card .where { min-width: 0; }
  /* A property's where-line takes the card's whole width, above the photo and the name: its street plaque stays on one
     line, as a glazed plaque is ("Via Cristoforo Colombo 230" fits from 320 px up, site.css tightens it below 360 px).
     Only a name too long even for that wraps inside the card, never past it. */
  .stop-card-property .stop-top { grid-column: 1 / -1; grid-row: 1; align-self: auto; }
  .stop-card-property .stop-photo { grid-row: 2; }
  .stop-card-property h2 { grid-row: 2; align-self: center; }
  /* Two actions on one row: the request button, and "Foto e dettagli" as a 44 px photos button (same name for screen readers) */
  .stop-actions { display: grid; grid-template-columns: minmax(0, 1fr) 44px; gap: 8px; }
  .stop-actions .button { justify-content: center; min-width: 0; white-space: normal; } /* a label that can't fit wraps, never cut */
  .stop-actions .stop-details { --h: 44px; --px: 0; --bg: var(--h-paper); --fg: var(--h-cobalt); --bd: var(--h-grout); width: 44px; min-height: 44px; padding: 0;
    background: var(--h-paper); color: var(--h-cobalt); border: 1px solid var(--h-grout); border-radius: var(--h-r-2); text-decoration: none; }
  .stop-actions .stop-details:hover { --bd: var(--h-cobalt); border-color: var(--h-cobalt); }
  .stop-details .icon { display: block; width: 20px; height: 20px; margin: 0; }
  .stop-details-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  /* The opening words never grow up under the map credit (top: 84px, up to three lines at 320 px, so down to 136 px), with
     the notice or the language line too: they start below it, and on a short phone the rest of them scrolls into view */
  .stop-intro { padding: 9.5rem 0 2rem; }
  .stop-outro { align-items: flex-end; }
  .stop-index { width: min(37.5rem, 100% - 1.5rem); margin-inline: 0.75rem; padding: 22px 8px 6px; } /* the full width up to the desktop list's, whatever its rows hold */
  .stop-index h2 { margin-bottom: 16px; padding: 0 10px; } /* the descenders of "Gli alloggi" clear the filter button and its focus ring */
  .finder-wrap { margin: 0 10px 8px; }
  /* A row: the name; the area on a line of its own; the guests with the parking and pet icons; the distance to the sea.
     The words take the whole width between the photo and the arrow, so from 320 px up no fact is cut, the icons stay
     beside the guests and every row has the same lines. A fact too long for its line wraps (.fact-long). */
  .index-list a { grid-template-columns: 44px minmax(0, 1fr) 16px; grid-template-rows: auto auto auto auto; column-gap: 12px; padding: 10px; }
  .index-list img, .index-img-empty { grid-row: 1 / 5; width: 44px; height: 54px; border-radius: 22px 22px 2px 2px; }
  .index-name { font-size: 1.1875rem; }
  .index-meta { line-height: 1.45; }
  .index-meta .index-area { flex-basis: 100%; }
  .index-dist { grid-column: 2; grid-row: 4; margin-top: 2px; text-align: left; white-space: normal; }
  .index-dist b { display: inline; }
  .index-arrow { grid-column: 3; grid-row: 1 / 5; }
  .index-arrow .icon { width: 16px; height: 16px; }
  /* At least 16rem wide, so on a 320 px phone the credit and its "Fonti e licenze" link still take three lines */
  .scene-credit { top: 84px; bottom: auto; right: 12px; max-width: max(72%, 16rem); }
  .credit-long { display: none; }
}
/* Phones held upright (and windows under 600 px wide, e.g. 400 % zoom): under the header for the opening words; during the
   tour, when the header has tucked away, just above the right end of the stop's card, so it never floats where the header
   was (home.js writes --credit-dy, with the same query). It settles before scene.js measures it again as an obstacle
   (450 ms after a stop changes). */
@media (max-width: 56.24rem) and (orientation: portrait), (max-width: 37.49rem) {
  .scene-credit { translate: 0 var(--credit-dy, 0px); transition: translate var(--dur-3, 0.36s) var(--h-ease), opacity 0.2s var(--h-ease); }
}
@media (max-width: 22.49rem) { .stop-actions .icon-go { display: none; } }
@media (max-width: 47.99rem) {
  /* The language line (site.js) on one row: its close button no longer wraps onto a 44 px row of its own, so the opening
     words leave room above them for the map's pins (scene.js fitPhone) */
  .hero-copy .lang-hint { flex-wrap: nowrap; gap: 0 10px; }
  .hero-copy .lang-hint p { flex: 0 1 auto; min-width: 0; }
  .hero-copy .lang-hint .lang-hint-actions { flex: none; }   /* the link and its close button never shrink or part */
  .hero-copy .lang-hint a { white-space: nowrap; }
  .stage::after { background: linear-gradient(to top, rgb(var(--h-scrim-rgb) / var(--h-scrim)) 0, rgb(var(--h-scrim-rgb) / var(--h-scrim)) calc(var(--scrim-under) + var(--hero-h, 370px)), rgb(var(--h-scrim-rgb) / 0) calc(var(--scrim-under) + var(--hero-h, 370px) + 210px));
    -webkit-mask-image: none; mask-image: none; }
  /* Only the selected pin shows its name; the others are dots (no stem up to a hidden name), each still a 44 px target */
  .marker:not(.is-selected, .is-hot, :focus-visible) :is(.marker-label, .marker-stem),
  .story.has-selection .marker:not(.is-selected, .is-hot, :focus-visible) .marker-stem { opacity: 0; pointer-events: none; }
}

/* Phones held upright, from 360 px wide: the opening words never start higher than 16rem (or 40 % of a short screen), so
   the map's pins keep their room above them (scene.js fitPhone needs a band of about 70 px under the credit). A notice
   (Pages CMS "Avviso") or the language line makes the words taller: the rest of them runs below the fold, where the intro
   scrolls to, instead of climbing into the pins' room and hiding them all. Below 360 px the credit takes three lines and
   no floor leaves the pins room on a 568 px screen: the words keep the 9.5rem top above. */
@media (min-width: 22.5rem) and (max-width: 56.24rem) and (orientation: portrait) {
  .stop-intro { padding-top: max(9.5rem, min(16rem, 40svh)); }
}

/* Short phones held upright: the opening words a little tighter, for the same reason (pins above them, not hidden behind
   them: below 700 px the heading at 42 px and the lead at 16 px keep its three lines and give the pins their room) */
@media (max-width: 47.99rem) and (max-height: 47.5rem) and (orientation: portrait) {
  .hero-copy h1 { margin-bottom: 14px; }
  .hero-lead { margin-bottom: 16px; }
}
@media (max-width: 47.99rem) and (max-height: 43.75rem) and (orientation: portrait) {
  .hero-copy h1 { font-size: 2.625rem; }
  .hero-lead { font-size: 1rem; }
}

/* Phones held sideways: the card beside the map, the header scrolls away (T5). Below 600 px wide (a desktop at 400 % zoom,
   1280x1024 -> 320x256) a card beside the map would be too narrow for its words: the cards stack as on phones held upright.
   scene.js frames the stops with the same query (stopView). */
@media (orientation: landscape) and (max-height: 31.25rem) and (min-width: 37.5rem) {
  .stop { align-items: center; padding: 3.6rem 0 0.6rem; }
  .stop-card, .stop-index { width: min(23rem, 48vw); margin-left: max(0.75rem, env(safe-area-inset-left)); max-height: calc(100svh - 4.4rem); overflow-y: auto; overscroll-behavior: contain; }
  /* A card taller than the screen scrolls inside itself: its buttons stay in sight at its foot, over the words scrolling
     under them (the card is opaque here), and the card itself is a tab stop while it scrolls with nothing to focus inside
     (home.js), so keys can scroll it. A sticky offset counts from inside the card's padding, so the row's is minus that
     padding: while the words scroll it sits where it rests at their end, its button the padding's height above the edge. */
  .stop-card .stop-meta { margin-bottom: 0; }
  /* A property's summary stands beside the photo, under the name (the where-line has the row above them to itself): the
     card stays short enough to show its facts line on most of these screens */
  .stop-card-property .stop-photo { grid-row: 2 / span 2; }
  .stop-card-property h2 { align-self: start; }
  .stop-card-property .stop-text { grid-column: 2; grid-row: 3; margin-top: 0.4rem; }
  .stop-card .stop-actions { position: sticky; z-index: 1; bottom: calc(-1 * var(--card-pad-b)); margin: 0 calc(-1 * var(--card-pad-x)) calc(-1 * var(--card-pad-b));
    padding: 0.6rem var(--card-pad-x) var(--card-pad-b); background: var(--h-glass-solid); }
  /* A faint edge above the row while words are still under it; it goes as the card reaches its end (no scroll, no edge) */
  @supports (animation-timeline: scroll()) {
    .stop-card .stop-actions { animation: actions-edge linear both; animation-timeline: scroll(nearest block); }
  }
  .hero-copy { max-width: 46vw; }
  /* The heading may use the copy's full width here, so "Case vacanza a Sperlonga e dintorni" takes two lines, not three */
  .hero-copy .eyebrow { margin-bottom: 6px; }
  .hero-copy h1 { max-width: none; margin-bottom: 8px; font-size: clamp(2rem, 8.5vh, 3rem); }
  .hero-lead { margin-bottom: 8px; font-size: 1rem; }
  /* The hint's link a little shorter (still a 39 px target), so the language line under it needs only 8 px to stay clear */
  .hero-hint { margin-top: 0; }
  .hero-hint a { padding-block: 10px; }
  .hero-copy .lang-hint { margin: 8px 0 0; }
  /* The opening words start below the header bar (it ends at 72–76 px), however tall the notice or the language line make them */
  .stop-intro, .stop-intro:has(.announcement-hero) { padding-top: 5.25rem; padding-bottom: 0.75rem; }   /* the credit is at the bottom here */
  /* The credit keeps 8 px clear of the opening words' column (46vw), so their last lines never cross it at rest */
  .scene-credit { top: auto; bottom: 0.4rem; max-width: min(48vw, 100% - var(--h-gutter) - 46vw - 20px); }
}

/* Phones held sideways under 700 px wide: a card narrower than the longest street plaque, which tightens as on narrow
   phones held upright (site.css) and so stays on one line */
@media (orientation: landscape) and (max-height: 31.25rem) and (min-width: 37.5rem) and (max-width: 43.75rem) {
  .stop-card .targa { padding-inline: 8px; font-size: 0.75rem; letter-spacing: 0.06em; }
}

/* ?poster, ?aerial and ?shot: the 3D view alone, for making pictures of it (tools/scene/README.md) */
.poster-mode .site-header, html:has(.story.poster-mode) .site-header, .poster-mode :is(.stops, .scene-labels, .scene-credit, .tour-rail, .scene-stills), .poster-mode .stage::after { display: none; }
/* The share image keeps the credit line (tools/scene/shoot-all.mjs) but not its link: a picture can't be clicked. It has no
   cards to keep clear of, so the line keeps the full width (wide screens wrap it beside the cards, above). */
.poster-mode .scene-credit .credit-sources { display: none; }
.poster-mode .scene-credit { max-width: calc(100% - 24px); }

/* ---------------------------------------------------------------------------------------------------------------- */
/* Sperlonga: a band on whitewash, with the photos uncropped */
.band { padding-block: clamp(64px, 8vw, 96px); }
.band-calce { background: var(--h-calce); color: var(--h-ink); }
.band-head { display: grid; gap: 16px 64px; align-items: end; margin-bottom: clamp(32px, 4vw, 48px); }
.band-head h2 { margin: 0; font-size: var(--h-fs-h2); line-height: var(--h-lh-head); font-variation-settings: "opsz" var(--h-opsz-page); }
.band-head .lead { margin: 0; color: var(--h-ink-2); }
@media (min-width: 60rem) { .band-head { grid-template-columns: auto minmax(0, 36rem); justify-content: space-between; } }
.photo-band { display: flex; gap: 12px; margin: 0; padding: 0 max(var(--h-gutter), (100% - var(--h-page)) / 2) 4px; list-style: none; overflow-x: auto;
  scroll-snap-type: x mandatory; scroll-padding-inline: max(var(--h-gutter), (100% - var(--h-page)) / 2); scrollbar-width: none; }
.photo-band::-webkit-scrollbar { display: none; }
.photo-band li { flex: 0 0 86%; scroll-snap-align: start; }
.photo-band figure { margin: 0; }
.photo-band img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--h-r-2); background: var(--h-wall); }
.photo-band figcaption { margin-top: 10px; font-size: var(--h-fs-small); line-height: 1.45; color: var(--h-ink-2); }
.photo-band:focus-visible { outline: 2px solid var(--h-cobalt); outline-offset: -2px; box-shadow: none; }
/* Wide screens: a still mosaic. The large photo and the two beside it are sized so that all three stay 4:3. */
@media (min-width: 60rem) {
  .photo-band { display: grid; grid-template-columns: repeat(32, minmax(0, 1fr)); gap: 24px 16px; width: min(100% - 2 * var(--h-gutter), var(--h-page)); margin-inline: auto; padding: 0; overflow: visible; }
  .photo-band li { grid-column: span 16; }
  .photo-band li:nth-child(1):nth-last-child(5) { grid-column: 1 / span 22; grid-row: 1 / span 2; }
  .photo-band li:nth-child(1):nth-last-child(5) figure { display: flex; flex-direction: column; height: 100%; }
  .photo-band li:nth-child(1):nth-last-child(5) img { flex: 1; min-height: 0; aspect-ratio: auto; }
  .photo-band li:nth-child(2):nth-last-child(4), .photo-band li:nth-child(3):nth-last-child(3) { grid-column: 23 / span 10; }
}

/* ---------------------------------------------------------------------------------------------------------------- */
/* The night ending: contact, then the footer on the same colour. The one tile band of the page sits on top of it. */
.contact { background: var(--h-notte); color: var(--h-on-notte); padding-bottom: clamp(48px, 6vw, 80px); --focus: var(--h-giallo); --focus-gap: var(--h-notte); }
:where(.contact > .tile-band) { height: 36px; background-color: var(--notte-2, #16305A); background-size: 36px 36px; background-position: center top; border-bottom: 1px solid rgb(185 195 211 / 0.18);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Cpath d='M0 22A22 22 0 0 0 22 0M26 0A22 22 0 0 0 48 22M48 26A22 22 0 0 0 26 48M22 48A22 22 0 0 0 0 26' fill='none' stroke='%236B80A6' stroke-width='1.6'/%3E%3Ccircle cx='24' cy='24' r='2.6' fill='%23E6B547'/%3E%3C/svg%3E"); }
.page-home .site-footer { margin-top: 0; } /* the footer continues the night band without a seam */
.page-home .site-footer .tile-band { display: none; } /* once per page: on the home page the band opens the contact instead */
/* The contact section right above already lists the same rows: the footer leaves them out on the home page */
.page-home .footer-contact { display: none; }
@media (min-width: 48rem) { .page-home .footer-grid { grid-template-columns: 1.3fr 2fr; } }
/* Once the contact rows stand in two columns (.contact-grid, below), the footer takes the same two columns and gap: the
   Alloggi list starts on the same line as the contact list above it, in the one night band */
@media (min-width: 60rem) { .page-home .footer-grid { grid-template-columns: 1fr 1fr; column-gap: 64px; } }
.contact-grid { display: grid; gap: 32px 64px; align-items: start; padding-top: clamp(64px, 8vw, 96px); }
@media (min-width: 60rem) { .contact-grid { grid-template-columns: 1fr 1fr; } }
.contact h2 { margin: 0 0 16px; font-size: var(--h-fs-h2); line-height: var(--h-lh-head); color: var(--h-on-notte); font-variation-settings: "opsz" var(--h-opsz-page); }
.contact .lead { max-width: 30rem; margin: 0; color: var(--h-on-notte-2); }
/* Phones held sideways: the section headings never outgrow the page's heading, made smaller there to fit the short screen */
@media (orientation: landscape) and (max-height: 31.25rem) and (min-width: 37.5rem) { .band-head h2, .contact h2 { font-size: min(var(--h-fs-h2), clamp(2rem, 8.5vh, 3rem)); } }
.contact .contact-list { display: block; margin: 0; padding: 0; list-style: none; border-top: 1px solid rgb(243 239 230 / 0.16); }
.contact .contact-list a { display: flex; justify-content: space-between; align-items: center; gap: 16px; min-height: 44px; padding: 1.1rem 0; margin: 0;
  border: 0; border-bottom: 1px solid rgb(243 239 230 / 0.16); border-radius: 0; background: none; color: var(--h-on-notte); text-decoration: none;
  font: 500 clamp(1.25rem, 2vw, 1.5rem) / 1.2 var(--h-font-text); font-variant-numeric: tabular-nums lining-nums; overflow-wrap: anywhere; }
/* ↗: each row opens another app (WhatsApp, the phone, email, maps) */
.contact .contact-list a::after { content: ""; flex: none; width: 22px; height: 22px; background: var(--h-giallo); transition: translate 0.2s var(--h-ease);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 17 17 7M8.5 7H17v8.5' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 17 17 7M8.5 7H17v8.5' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat; }
/* A row still waiting for the client's fact (an unlinked fill-in marker): the same rule and rhythm, no ↗ */
.contact .contact-list .contact-missing { padding: 1.1rem 0; border-bottom: 1px solid rgb(243 239 230 / 0.16); color: var(--h-on-notte-2);
  font: 500 clamp(1.25rem, 2vw, 1.5rem) / 1.2 var(--h-font-text); overflow-wrap: anywhere; }
.contact .contact-list a:hover, .contact .contact-list a:focus-visible { text-decoration: underline; text-decoration-color: var(--h-giallo); text-decoration-thickness: 1px; text-underline-offset: 0.3em; }
@media (hover: hover) and (pointer: fine) { .contact .contact-list a:hover::after { translate: 2px -2px; } }
.contact .contact-list a:focus-visible { outline: 2px solid transparent; outline-offset: 2px; box-shadow: 0 0 0 2px var(--h-notte), 0 0 0 4px var(--h-giallo); }

/* ---------------------------------------------------------------------------------------------------------------- */
/* Print: the opening words, every stop card and the list of the houses on white, one after another; no map, no rail */
@media print {
  .stage, .tour-rail, .scroll-cue, .hero-hint, .scene-load, .finder-wrap { display: none !important; }
  .story { display: block; background: none; color: #000; }
  .stop { display: block; min-height: 0; padding: 0 0 6mm; break-inside: avoid; }
  .stop-card, .stop-intro .hero-copy, .stop-card > * { animation: none !important; opacity: 1 !important; translate: none !important; scale: none !important; }
  .stop-card { max-height: none; overflow: visible; -webkit-backdrop-filter: none; backdrop-filter: none; background: #fff; box-shadow: none; border: 1px solid #ccc; }
  .hero-copy :is(h1, .eyebrow, .hero-lead) { color: #000; text-shadow: none; }
  .stop-card .stop-text { display: block; -webkit-line-clamp: none; line-clamp: none; overflow: visible; }
  .stop-actions .button { --bg: transparent; --fg: #000; --bd: #999; --shadow-own: none; }   /* no white text on an unprinted fill */
  .photo-band { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; overflow: visible; padding-inline: 0; }
  .photo-band li { break-inside: avoid; }
  .contact { background: none; color: #000; }
  .contact :is(h2, .lead, .contact-list a, .contact-missing) { color: #000; }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-cue .rail::after, .js .hero-copy > *, .marker, .marker-dot::after, .stop.is-arriving .stop-card > * { animation: none; }
  .stop-actions .button:hover .icon, .index-list a:hover .index-arrow, .contact .contact-list a:hover::after { translate: none; }
}
