/* Public stylesheet — hand-written, no build step, no framework.
 *
 * Rules that are load-bearing rather than taste:
 *  - NO inline styles anywhere in the markup, so the focal point of a photo is one of
 *    nine utility classes and the LQIP is a real <img>. That is what keeps the CSP at
 *    style-src 'self' (technical §12).
 *  - prefers-reduced-motion is a hard kill switch, not a softening.
 *  - No parallax at all: it costs scroll-linked layout work on exactly the phones this
 *    site is built for and buys almost nothing on a page that is already photography.
 *  - Display text over photographs carries font-weight >= 500 and a text-shadow, on top
 *    of the measured scrim — a 90th-percentile luminance can still be beaten by a small
 *    specular highlight behind a single word (design §7).
 */

/* ------------------------------------------------------------------ foundations */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: var(--lh-body);
  color: var(--c-ink-700);
  background: #fff;
}
h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--c-ink-900);
  line-height: var(--lh-snug);
  letter-spacing: -0.015em;
  margin: 0 0 var(--s-4);
  font-weight: 600;
}
h1 { font-size: var(--step-4); line-height: var(--lh-tight); }
h2 { font-size: var(--step-3); }
h3 { font-size: var(--step-2); }
p { margin: 0 0 var(--s-4); }
img { max-width: 100%; height: auto; }

a { color: var(--c-fjord-600); text-underline-offset: 3px; }
a:hover { color: var(--c-fjord-700); }

/* Visible focus everywhere. WCAG 2.2 AA is the floor, not the target. */
:focus-visible {
  outline: 3px solid var(--c-fjord-400);
  outline-offset: 2px;
  border-radius: var(--r-1);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip-link {
  position: absolute; left: var(--s-4); top: -100px; z-index: 100;
  background: #fff; color: var(--c-fjord-600);
  padding: var(--s-3) var(--s-4); border-radius: var(--r-2);
  transition: top var(--motion);
}
.skip-link:focus { top: var(--s-3); }

.container {
  width: 100%; max-width: var(--container);
  margin-inline: auto; padding-inline: var(--gutter);
}
.container--prose { max-width: calc(var(--measure) + 2 * var(--gutter)); }
.section { padding-block: var(--section-y); }
.section--mist { background: var(--c-mist-50); }
.section__heading { font-size: var(--step-3); }
.lead { font-size: var(--step-1); color: var(--c-ink-700); }
.meta { font-size: var(--step--1); color: var(--c-ink-500); }
.eyebrow {
  font-size: var(--step--1); letter-spacing: .08em; text-transform: uppercase;
  font-weight: 600; color: var(--c-sand-600); margin-bottom: var(--s-2);
}
.prose { max-width: var(--measure); }
.prose h3 { margin-top: var(--s-6); }

/* ---- tables in prose --------------------------------------------------------------
   There is one table on this site and it is the species/minimum-size/closed-season
   list on the export-rules page, read by a guest deciding what is legal to take home.

   It has to survive a 390px phone without a horizontal scrollbar hiding the third
   column, because a column a reader does not know exists is worse than a cramped one.
   `table-layout: auto` with wrapping cells does that; the wrapper only scrolls if a
   single word cannot fit, which for these three columns it cannot.

   Zebra striping rather than borders on every cell: the row is the unit of meaning
   here — one species, its size, its caveat — and the eye has to keep the row together
   across the full width of a phone. */
.prose__scroll { overflow-x: auto; }
.prose table {
  width: 100%; border-collapse: collapse; margin: var(--s-5) 0;
  font-size: var(--step--1);
}
.prose th, .prose td {
  padding: var(--s-2) var(--s-3);
  text-align: start; vertical-align: top;
  border-bottom: 1px solid var(--c-line);
  /* Second guard, with the scroll wrapper: a long unbreakable string breaks rather
     than widening the table past the phone. */
  overflow-wrap: anywhere;
}
.prose thead th {
  border-bottom: 2px solid var(--c-ink-300);
  font-weight: 700; color: var(--c-ink-900);
  /* Long enough to say "Minstemål her" without becoming three stacked words. */
  white-space: nowrap;
}
.prose tbody tr:nth-child(odd) { background: var(--c-mist-50); }
.prose td:first-child { font-weight: 600; color: var(--c-ink-900); }
.prose-invert, .prose-invert a { color: rgba(255, 255, 255, .92); }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 48px; padding: var(--s-3) var(--s-5);
  border-radius: var(--r-pill); border: 2px solid transparent;
  font-weight: 600; font-size: var(--step-0); text-decoration: none;
  transition: background-color var(--motion), color var(--motion), border-color var(--motion);
}
/* Rowan is the booking accent and appears at most once per screen. It sits on the
   PHONE call, not on the enquiry button: a tap-to-call works for a same-day tourist
   right now; the form is a next-day channel (design §4). */
.btn--rowan { background: var(--c-rowan-600); color: #fff; }
.btn--rowan:hover { background: var(--c-rowan-700); color: #fff; }
.btn--ghost { border-color: rgba(255, 255, 255, .8); color: #fff; }
.btn--ghost:hover { background: rgba(255, 255, 255, .14); color: #fff; }
.btn--ghost-ink { border-color: var(--c-fjord-600); color: var(--c-fjord-600); background: #fff; }
.btn--quiet { border-color: var(--c-line); color: var(--c-fjord-600); background: var(--c-mist-50); }
.btn--sm { min-height: 44px; padding: var(--s-2) var(--s-4); font-size: var(--step--1); }

/* ------------------------------------------------------------------ photographs */

.pic { display: block; }
.pic__img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* The 9-point focal grid as utility classes, not inline styles. Nine tap targets also
   beat a draggable dot on a phone (technical §9). */
.focal--top-left { object-position: 0% 0%; }
.focal--top { object-position: 50% 0%; }
.focal--top-right { object-position: 100% 0%; }
.focal--left { object-position: 0% 50%; }
.focal--center { object-position: 50% 50%; }
.focal--right { object-position: 100% 50%; }
.focal--bottom-left { object-position: 0% 100%; }
.focal--bottom { object-position: 50% 100%; }
.focal--bottom-right { object-position: 100% 100%; }

.lqip {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; filter: blur(12px); transform: scale(1.05); z-index: 0;
}

/* Two-ended scrim over every photo.
 *
 * THE OVERLAY IS ATTACHED TO THE BAND, NOT TO THE CLASS THE SERVER SENDS. Every
 * surface that puts display type over a photograph paints it unconditionally, and the
 * `.scrim--*` classes only ADJUST the strength that is already there.
 *
 * That inversion is deliberate and it is the last line of the §6.4 contrast argument.
 * The strength is measured at ingest from the photo's own 90th-percentile luminance
 * and resolved by `scrim_for()`, but two bugs in one day showed how quietly that
 * chain breaks: the resolver's answer was dropped in transit (the raw enum reached
 * the markup, aliasing every photo to `normal`), and a row whose column defaults had
 * not been applied resolved to `None`, which would have rendered `class="scrim--None"`
 * — matching no rule, creating no ::after, and putting white display type on an
 * unscrimmed photograph.
 *
 * Both were failures of a VALUE. A missing or unrecognised value can no longer remove
 * the overlay, because nothing about its existence depends on the value any more. The
 * worst a broken class name can now do is leave the strength at normal. */
.hero--photo::after, .band-full::after, .page-hero::after,
.scrim--light::after, .scrim--normal::after, .scrim--strong::after,
.scrim--auto::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom, var(--scrim-a) 0%, var(--scrim-b) 45%, var(--scrim-c) 100%);
  opacity: 1;
}
/* Lighter than the default, and only ever chosen from a measurement or a deliberate
   override — never from a value falling through. */
.scrim--light::after { opacity: .72; }
/* `scrim--auto` should never appear in rendered HTML: the server resolves the
   strength. It is kept, aliased to the default, for markup that bypasses
   content.picture() — this alias is what hid the unwired measurement, so if it does
   reappear the page is at least no lighter than it is now. */
.scrim--strong::after { filter: saturate(.9) brightness(.86); }

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

.site-header {
  position: fixed; inset-inline: 0; top: 0; z-index: 50;
  background: var(--header-glass);
  -webkit-backdrop-filter: blur(10px) saturate(1.1);
  backdrop-filter: blur(10px) saturate(1.1);
  transition: background-color var(--motion), box-shadow var(--motion);
}
/* Goes opaque after 80px of scroll (design §8.7) — opaque INK, not white, so the nav
   keeps one colour scheme on every page and in both states. */
.site-header.is-solid { background: var(--c-ink-900); box-shadow: 0 1px 0 rgba(15,30,43,.6); }
.site-header__inner {
  display: flex; align-items: center; gap: var(--s-4);
  min-height: var(--header-h);
  max-width: var(--container-wide); margin-inline: auto; padding-inline: var(--gutter);
}
.brand { margin-inline-end: auto; text-decoration: none; }
.brand__name {
  font-family: var(--font-display); font-weight: 600; font-size: var(--step-1);
  color: #fff; text-shadow: 0 1px 3px rgba(15, 30, 43, .5);
}

.nav { display: none; }
.nav__list, .lang-switch, .sheet__list, .site-footer__links, .stats__list,
.grid, .shelf, .video-grid, .maplinks, .ticks, .chips, .departures__list {
  list-style: none; margin: 0; padding: 0;
}
.nav__list { display: flex; gap: var(--s-5); }
.nav__link, .lang-switch__link {
  color: #fff; text-decoration: none; font-weight: 500;
  text-shadow: 0 1px 3px rgba(15, 30, 43, .5);
}
.nav__link:hover { text-decoration: underline; }
.lang-switch { display: none; gap: var(--s-3); font-size: var(--step--1); }
.lang-switch__link { display: inline-flex; align-items: center; gap: .35em; }
.lang-switch__link.is-current { text-decoration: underline; font-weight: 700; }

/* ---- flags ------------------------------------------------------------------------
   Defined once at the top of <body> and referenced with <use>. The sprite itself must
   take no space and must not be reachable: `display: none` on it would make Safari
   refuse to render the symbols at all, so it is collapsed instead. */
.flag-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
/* Sized in `em`, so a flag tracks the text it sits beside instead of needing a second
   set of breakpoints. The hairline keeps the white band of a Norwegian or Dutch flag
   from bleeding into a pale background. */
.flag {
  width: 1.5em; height: 1.125em; flex: none; border-radius: 2px;
  box-shadow: 0 0 0 1px rgb(0 0 0 / .18);
}
.flag--row { margin-inline-end: .6em; width: 1.6em; height: 1.2em; }

.hamburger {
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: 48px; min-width: 48px; padding: var(--s-2) var(--s-3);
  background: transparent; border: 0; color: #fff; font: inherit; cursor: pointer;
  text-shadow: 0 1px 3px rgba(15, 30, 43, .5);
}
.hamburger__bars, .hamburger__bars::before, .hamburger__bars::after {
  display: block; width: 22px; height: 2px; background: currentColor; content: "";
}
.hamburger__bars { position: relative; }
.hamburger__bars::before { position: absolute; top: -7px; }
.hamburger__bars::after { position: absolute; top: 7px; }

/* Full-screen sheet below 1024px, 48px+ rows, language as full text labels. */
.sheet {
  position: fixed; inset: 0; z-index: 60; background: #fff;
  padding: var(--s-4) var(--gutter); overflow-y: auto;
}
.sheet[hidden] { display: none; }
/* Set by the sheet script as a class, never as an inline style. */
.is-locked { overflow: hidden; }
.sheet__head { display: flex; align-items: center; justify-content: space-between; min-height: 48px; }
.sheet__title { font-family: var(--font-display); font-size: var(--step-2); }
.sheet__close { min-height: 48px; padding-inline: var(--s-4); border: 1px solid var(--c-line);
  border-radius: var(--r-pill); background: #fff; font: inherit; cursor: pointer; }
.sheet__link {
  display: flex; align-items: center; min-height: 56px;
  border-bottom: 1px solid var(--c-line); text-decoration: none;
  color: var(--c-ink-900); font-size: var(--step-1);
}
.sheet__list--lang { margin-top: var(--s-5); }

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

/* 86svh on phones with NO fixed floor: the 14% leak cues the scroll, and a 520px
   min-height would cancel it entirely on the 360x640 handset the research names. */
.hero {
  position: relative; display: flex; align-items: flex-end;
  min-height: 86svh;
  margin-top: calc(-1 * var(--header-h));
  padding: var(--s-8) 0 var(--s-7);
  background: var(--c-ink-900);
  overflow: hidden;
}
.hero__media, .hero .lqip { position: absolute; inset: 0; z-index: 0; }
.hero__media .pic__img { height: 100%; }
/* Text bottom-left, never centred. */
.hero__inner {
  position: relative; z-index: 2;
  width: 100%; max-width: var(--container-wide);
  margin-inline: auto; padding-inline: var(--gutter);
  color: #fff;
}
.hero__title {
  font-size: var(--step-6); line-height: var(--lh-tight);
  max-width: 18ch; color: #fff; font-weight: 500;
  text-shadow: 0 2px 12px rgba(15, 30, 43, .45);
  margin-bottom: var(--s-3);
}
.hero__sub {
  font-size: var(--step-1); color: rgba(255, 255, 255, .88);
  max-width: 46ch; text-shadow: 0 1px 8px rgba(15, 30, 43, .45);
}
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); margin-top: var(--s-5); }
.hero__quote {
  margin: var(--s-5) 0 0; max-width: 46ch;
  color: rgba(255, 255, 255, .82); font-size: var(--step-0);
}
.hero__quote cite { display: block; font-size: var(--step--1); font-style: normal; }
/* No photo yet? The hero is still a hero: type and colour carry it (design §10.2). */
.hero:not(.hero--photo) { min-height: 60svh; background: var(--c-ink-900); }

/* ---- "Start bildet under menyen" (per photo, set in /admin/forsiden) --------------
   The full-bleed hero runs under the fixed header, which is the design and is wrong
   for a close catch shot: the faces sit high in the frame and the menu bar lands
   across them. Moving the focal point down only pushes them further under it.

   TWO rules, and they are one change. Cancelling the negative margin without making
   the header opaque leaves the header's white type — shadowed, sized for a photograph
   behind it — sitting on the page background, which is near-white. The bar has to
   become a bar the moment it stops being an overlay.

   The min-height loses the header's height at the same time, so the hero plus the bar
   still add up to the one screen the design asks for instead of pushing 72px of the
   photo below the fold. */
.hero-below-menu .hero {
  margin-top: 0;
  min-height: calc(86svh - var(--header-h));
}
.hero-below-menu .site-header { background: var(--c-ink-900); }

/* --------------------------------------------------------------- dagens turer */

/* The NEUTRAL state is the steady state and is styled as ordinary body text. Grey or
   red reads as "closed", and we are not closed — we are at sea (technical §9b). */
.departures { padding-block: var(--s-6); border-bottom: 1px solid var(--c-line); }
.departures__heading { font-size: var(--step-2); margin-bottom: var(--s-4); }
.departures__list { display: flex; flex-wrap: wrap; gap: var(--s-4); }
.departure {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--c-line); border-radius: var(--r-2);
  background: #fff;
}
.departure__slot { font-weight: 600; color: var(--c-ink-900); }
.departure__window { color: var(--c-ink-700); }
.departure__state { font-weight: 600; }
.departure__set { font-size: var(--step--1); color: var(--c-ink-500); width: 100%; }
/* Only the tapped states are colour-coded. */
.departure--yes { border-color: var(--c-fjord-600); background: var(--c-fjord-100); }
.departure--yes .departure__state { color: var(--c-fjord-700); }
.departure--no .departure__state, .departure--away .departure__state { color: var(--c-rowan-700); }
.departure--full { background: var(--c-sand-100); border-color: var(--c-sand-300); }
.departure--full .departure__state { color: var(--c-sand-600); }
.departures__standing { margin: var(--s-4) 0 0; max-width: var(--measure); }
.departures__call { font-weight: 600; white-space: nowrap; }

/* ---------------------------------------------------------------- practical strip */

.practical { padding-block: var(--s-6); }
.practical__inner { display: grid; gap: var(--s-4); grid-template-columns: 1fr; }
.practical__item { display: flex; flex-direction: column; gap: var(--s-1); }
.practical__label {
  font-size: var(--step--1); text-transform: uppercase; letter-spacing: .06em;
  color: var(--c-ink-500);
}
.practical__value { font-weight: 500; color: var(--c-ink-900); }

/* ---------------------------------------------------------------------- prices */

.price-cards { display: grid; gap: var(--s-5); grid-template-columns: 1fr; }
.price-card {
  border: 1px solid var(--c-line); border-radius: var(--r-3);
  padding: var(--s-6); background: var(--c-mist-50);
}
.price-card__title { font-size: var(--step-2); margin-bottom: var(--s-2); }
.price-card__amount { font-size: var(--step-3); font-family: var(--font-display); color: var(--c-ink-900); margin-bottom: var(--s-2); }
.price-card__unit { font-family: var(--font-body); font-size: var(--step--1); color: var(--c-ink-500); display: inline-block; }
.price-card__line { margin-bottom: var(--s-2); }
/* The per-head arithmetic: kr 6 000 for the boat is kr 1 500 each for four. */
.price-card__each { font-weight: 600; color: var(--c-fjord-700); }
.price-cards__vat { font-size: var(--step--1); color: var(--c-ink-500); margin-top: var(--s-4); }

/* ---------------------------------------------------------------------- notice */

.notice {
  background: var(--c-warn-bg);
  border-block: 1px solid var(--c-warn-line);
  padding-block: var(--s-6);
  color: var(--c-warn-fg);
}
.notice__heading { font-size: var(--step-2); color: var(--c-warn-fg); }
.notice__body p:last-child { margin-bottom: 0; }
.notice__link { margin-top: var(--s-3); }
.notice__link a, .notice a { color: var(--c-warn-fg); font-weight: 600; }
.notice__updated { font-size: var(--step--1); margin: var(--s-3) 0 0; }

/* ----------------------------------------------------------------------- stats */

.stats__list { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s-5); }
.stat { display: flex; flex-direction: column; gap: var(--s-1); }
.stat__value { font-family: var(--font-display); font-size: var(--step-5); line-height: 1; color: var(--c-fjord-600); }
.stat__label { font-size: var(--step--1); color: var(--c-ink-500); }

/* --------------------------------------------------------- galleries and shelves */

/* Cells are capped at each image's NATIVE width. 62 of the migrated images sit in the
   600-1399px tier; stretching a 720px file into a 460px retina cell would make the
   weakest part of the library the most visible (design §10.2). The grid reflows. */
.grid, .shelf {
  display: grid; gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr));
  align-items: start;
}
.shelf { grid-template-columns: repeat(auto-fill, minmax(min(180px, 100%), 1fr)); }
.cell--cap-180 { max-width: 180px; }
.cell--cap-240 { max-width: 240px; }
.cell--cap-320 { max-width: 320px; }
.cell--cap-460 { max-width: 460px; }
.grid__btn {
  display: block; width: 100%; padding: 0; border: 0; background: none; cursor: zoom-in;
  border-radius: var(--r-2); overflow: hidden;
}
.grid__pic .pic__img, .album-card__pic .pic__img { aspect-ratio: 4 / 3; }
.shelf__caption { font-size: var(--step--1); color: var(--c-ink-500); margin: var(--s-2) 0 0; }
.album-card {
  display: flex; flex-direction: column; gap: var(--s-2);
  text-decoration: none; color: var(--c-ink-900);
  border: 1px solid var(--c-line); border-radius: var(--r-2); overflow: hidden;
  padding-bottom: var(--s-3);
}
.album-card__title { font-weight: 600; padding-inline: var(--s-3); }
.album-card__count { font-size: var(--step--1); color: var(--c-ink-500); padding-inline: var(--s-3); }

.lightbox { border: 0; padding: 0; background: var(--c-ink-900); max-width: 100vw; max-height: 100dvh; }
.lightbox::backdrop { background: rgba(15, 30, 43, .92); }
.lightbox__img { display: block; max-width: 100vw; max-height: 82dvh; margin-inline: auto; }
.lightbox__bar { display: flex; justify-content: space-between; align-items: center; gap: var(--s-4);
  padding: var(--s-3) var(--s-4); color: #fff; }
.lightbox__close, .lightbox__prev, .lightbox__next {
  min-width: 48px; min-height: 48px; border-radius: var(--r-pill);
  border: 1px solid rgba(255, 255, 255, .4); background: transparent; color: #fff;
  font: inherit; cursor: pointer;
}
.lightbox__caption { font-size: var(--step--1); color: rgba(255, 255, 255, .82); }

/* ---------------------------------------------------------------------- videos */

.video-grid { display: grid; gap: var(--s-5); grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); }
.video { margin: 0; }
.video__facade {
  position: relative; display: block; width: 100%; aspect-ratio: 16 / 9;
  padding: 0; border: 0; cursor: pointer; overflow: hidden;
  border-radius: var(--r-2); background: var(--c-ink-900); color: #fff;
}
.video__poster { position: absolute; inset: 0; }
.video__poster .pic__img { height: 100%; }
.video__card {
  display: flex; align-items: center; justify-content: center; height: 100%;
  padding: var(--s-6) var(--s-4) var(--s-8); font-family: var(--font-display);
  font-size: var(--step-1); color: #fff; text-align: center;
}
.video__play { position: absolute; inset-block-end: var(--s-3); inset-inline-start: var(--s-3); color: #fff; }
.video__meta { display: flex; flex-direction: column; gap: var(--s-1); padding-top: var(--s-2); }
.video__title { font-weight: 600; color: var(--c-ink-900); }
.video__notice { font-size: var(--step--1); color: var(--c-ink-500); }
.video__frame { width: 100%; aspect-ratio: 16 / 9; border: 0; border-radius: var(--r-2); }

/* -------------------------------------------------------------- bands and guide */

.band-full {
  position: relative; display: flex; align-items: flex-end;
  min-height: 56svh; overflow: hidden; background: var(--c-ink-900);
}
.band-full__media, .band-full .lqip { position: absolute; inset: 0; z-index: 0; }
.band-full__media .pic__img { height: 100%; }
.band-full__inner {
  position: relative; z-index: 2; width: 100%; max-width: var(--container-wide);
  margin-inline: auto; padding: var(--s-7) var(--gutter);
}
.band-full__title {
  font-size: var(--step-5); color: #fff; font-weight: 500; max-width: 20ch;
  text-shadow: 0 2px 12px rgba(15, 30, 43, .45);
}
.guide__inner { display: grid; gap: var(--s-6); }
.guide__media .pic__img { aspect-ratio: 4 / 5; border-radius: var(--r-3); }
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chip {
  padding: var(--s-1) var(--s-3); border-radius: var(--r-pill);
  background: var(--c-fjord-100); color: var(--c-fjord-700); font-size: var(--step--1);
}
.split { display: grid; gap: var(--s-6); }
.split__media .pic__img { aspect-ratio: 4 / 3; border-radius: var(--r-3); }

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

.cta-strip { background: var(--c-ink-900); color: #fff; padding-block: var(--s-8); }
.cta-strip__heading { color: #fff; font-size: var(--step-3); }
.cta-strip__actions { display: flex; flex-wrap: wrap; gap: var(--s-3); }

/* ------------------------------------------------------------------ page heads */

.page-head { padding-block: calc(var(--header-h) + var(--s-7)) var(--s-6); }
.page-hero {
  position: relative; display: flex; align-items: flex-end; min-height: 56svh;
  margin-top: calc(-1 * var(--header-h)); overflow: hidden; background: var(--c-ink-900);
}
.page-hero__media, .page-hero .lqip { position: absolute; inset: 0; z-index: 0; }
.page-hero__media .pic__img { height: 100%; }
.page-hero__inner {
  position: relative; z-index: 2; width: 100%; max-width: var(--container-wide);
  margin-inline: auto; padding: var(--s-7) var(--gutter);
}
.page-hero__title { color: #fff; font-size: var(--step-5); font-weight: 500; text-shadow: 0 2px 12px rgba(15, 30, 43, .45); }
.page-hero__sub { color: rgba(255, 255, 255, .88); max-width: 46ch; }

.lang-fallback {
  background: var(--c-mist-100); border-bottom: 1px solid var(--c-line);
  padding-block: var(--s-3); font-size: var(--step--1); color: var(--c-ink-500);
}

/* ------------------------------------------------------------- tables and lists */

.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; min-width: 34rem; }
.table th, .table td { text-align: left; padding: var(--s-3); border-bottom: 1px solid var(--c-line); vertical-align: top; }
.table thead th { font-size: var(--step--1); text-transform: uppercase; letter-spacing: .06em; color: var(--c-ink-500); }
.ticks { display: grid; gap: var(--s-3); max-width: var(--measure); }
.ticks li { padding-left: var(--s-5); position: relative; }
.ticks li::before { content: "—"; position: absolute; left: 0; color: var(--c-sand-600); }

/* ----------------------------------------------------------------- find us page */

.maps { display: grid; gap: var(--s-5); margin-bottom: var(--s-7); }
.maps__figure { margin: 0; }
.maps__figure img { border-radius: var(--r-3); border: 1px solid var(--c-line); width: 100%; }
.find__cols { display: grid; gap: var(--s-7); }
.maplinks { display: grid; gap: var(--s-3); }
.coords code { font-size: var(--step-1); user-select: all; }

/* ------------------------------------------------------------------------ form */

.form__field { display: flex; flex-direction: column; gap: var(--s-2); max-width: 34rem; }
.form__field label { font-weight: 500; color: var(--c-ink-900); }
.form input[type="text"], .form input[type="email"], .form input[type="tel"], .form textarea {
  font: inherit; padding: var(--s-3); min-height: 48px;
  border: 1px solid var(--c-line); border-radius: var(--r-2);
  background: var(--c-mist-100); color: var(--c-ink-900);
}
.form textarea { min-height: 9rem; }
.form__legal { font-size: var(--step--1); color: var(--c-ink-500); max-width: 34rem; }
.form__error { color: var(--c-rowan-700); font-size: var(--step--1); }
.flash {
  background: var(--c-fjord-100); border: 1px solid var(--c-fjord-400);
  border-radius: var(--r-2); padding: var(--s-4); color: var(--c-ink-900);
}
/* The honeypot. Hidden in CSS because an inline style attribute would break the CSP. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

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

.site-footer { background: var(--c-ink-900); color: rgba(255, 255, 255, .82); padding-block: var(--s-8); }
.site-footer a { color: #fff; }
.site-footer__inner {
  display: grid; gap: var(--s-6);
  max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter);
}
.site-footer__name { font-family: var(--font-display); font-size: var(--step-1); color: #fff; }
.site-footer__links { display: grid; gap: var(--s-2); }
.site-footer__meta { font-size: var(--step--1); color: rgba(255, 255, 255, .6); }

/* Sticky mobile call bar — the highest-value conversion element on the site. */
.sticky-cta {
  position: fixed; inset-inline: 0; bottom: 0; z-index: 40;
  display: flex; gap: var(--s-3); justify-content: center;
  padding: var(--s-3) var(--gutter);
  background: rgba(255, 255, 255, .96); border-top: 1px solid var(--c-line);
}
.sticky-cta[hidden] { display: none; }

/* --------------------------------------------------------------- breakpoints */

@media (min-width: 640px) {
  .practical__inner { grid-template-columns: repeat(2, 1fr); }
  .price-cards { grid-template-columns: repeat(2, 1fr); }
  .maps { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; align-items: center; }
  .split--reverse .split__text { order: 2; }
  .guide__inner { grid-template-columns: 2fr 3fr; align-items: center; }
  .find__cols { grid-template-columns: 3fr 2fr; }
  .site-footer__inner { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 1024px) {
  /* 100svh desktop, clamped 600-920px. */
  .hero { min-height: clamp(600px, 100svh, 920px); }
  /* Same clamp, minus the bar that is now above the photo rather than over it. */
  .hero-below-menu .hero { min-height: clamp(600px, calc(100svh - var(--header-h)), 920px); }
  .hero__inner { padding-inline-start: clamp(var(--gutter), 6vw, 120px); }
  .nav, .lang-switch { display: flex; }
  .hamburger { display: none; }
  .practical__inner { grid-template-columns: repeat(5, 1fr); }
  .stats__list { grid-template-columns: repeat(4, 1fr); }
  .band-full { min-height: 72svh; }
  /* The sticky bar is a phone affordance; on desktop the header CTA is always visible. */
  .sticky-cta { display: none; }
}

@media (min-width: 1600px) {
  .container { max-width: var(--container-wide); }
}

/* prefers-reduced-motion is a HARD kill switch, not a softening (design §8.6). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* -------------------------------------------------------------------- map facade */
/* Aspect-ratio box so the map is responsive with no JS resizing. Taller on a phone
   than a wide desktop column, because a map read at arm's length needs the height. */
.mapembed {
  position: relative; aspect-ratio: 4 / 3; width: 100%;
  border-radius: var(--r-md); overflow: hidden;
  background: var(--c-mist-100); border: 1px solid var(--c-line);
}
@media (min-width: 768px) { .mapembed { aspect-ratio: 16 / 10; } }
.mapembed iframe { width: 100%; height: 100%; border: 0; display: block; }
.mapembed__load {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-2); width: 100%; height: 100%; min-height: 48px;
  border: 0; cursor: pointer; background: var(--c-mist-100); color: var(--c-ink-700);
  font: inherit; text-align: center; padding: var(--s-5);
}
.mapembed__load:hover { background: var(--c-fjord-100); }
.mapembed__pin { font-size: var(--step-3); line-height: 1; }
.mapembed__label { font-weight: 600; font-size: var(--step-1); }
.mapembed__note { font-size: var(--step--1); color: var(--c-ink-500); max-width: 34ch; }

/* The locally-stored preview under the facade. `position: absolute` rather than a
   background-image: a real <img> gets alt text and lazy loading, and the map is
   information, not decoration. */
/* `display: contents` on the <picture>: as an inline wrapper it would otherwise
   contribute a line box of its own and push the button out of the frame. */
.mapembed picture { display: contents; }
.mapembed__preview {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* Over a photograph of a map, a flat fill would hide the very thing it is previewing,
   and plain text on map detail fails contrast wherever the map happens to be light.
   So: the button surface goes transparent and the words move onto their own panel. */
/* `position: relative` is load-bearing, not cosmetic: the preview is absolutely
   positioned, so without a stacking context of its own the button paints UNDERNEATH
   the map and the whole facade becomes an image with an invisible, unlabelled tap
   target on top of it. */
.mapembed__load--over {
  position: relative; z-index: 1; background: transparent; padding: var(--s-3);
}
.mapembed__load--over:hover { background: rgb(255 255 255 / .18); }
.mapembed__load--over .mapembed__panel {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
  padding: var(--s-3) var(--s-4); max-width: 40ch;
  background: rgb(255 255 255 / .93); border-radius: var(--r-md);
  box-shadow: 0 1px 6px rgb(0 0 0 / .25);
}
.mapembed__credit {
  margin-top: var(--s-1); font-size: var(--step--1); color: var(--c-ink-500);
}
.mapembed__credit a { color: inherit; }
