/* ============================================================================
   Privat Flughafentransfer — design system
   ----------------------------------------------------------------------------
   One stylesheet, no framework, no build step. It replaces the previous
   style.css + swiss.css pair, which had left the site mid-migration between
   two visual languages with neither one finished.

   Order: tokens -> reset -> primitives -> layout -> components -> utilities.
   Components are defined once and used everywhere; if a page needs a variant,
   it gets a modifier class here rather than a near-copy in the template.

   Breakpoints are named after what they are for, not after devices:
     >= 640px  wide phone / small tablet portrait
     >= 900px  tablet landscape and small laptop — nav becomes horizontal
     >= 1180px full desktop
   Tablet portrait (768-899px) deliberately keeps the drawer navigation and a
   two-column grid rather than being handed a squeezed desktop bar.
============================================================================ */

/* ---------- Tokens ------------------------------------------------------- */
/*  COLOUR SYSTEM — three layers, one direction of travel.
    ---------------------------------------------------------------------------
    Layer 1 is the five-colour travel palette this brand family is built on.
    Layer 2 turns each hue into the tones it is actually safe to ship (a raw
    hue is rarely a legal text colour). Layer 3 assigns roles. Components
    reference layer 3 only, so a role moves in one place.

    ROLE MAP — the hierarchy this site is built on:
      SUNSHINE primary. The header, the hero, the band under it, the deep
               closing sections, the footer, form rules and headings, links,
               trust facts, brand marks. Sunshine is the *architecture* here,
               which is why it has a ramp that runs from a bright band all
               the way down to an espresso ground: a saturated warm hue
               cannot be a page surface, but the deep end of the same hue can,
               and it stays unmistakably warm.
      SEA      action, and nothing else. Book Transfer, every form submit, the
               selected option, the focus ring, the headline price. It is the
               only COOL colour on the entire site — every other pixel is
               warm — and that is what makes an action impossible to miss
               without having to be loud. Nothing that is not an action is
               ever blue.
      ISLAND   success and inclusion. "What's included", the end of the
               booking flow, positive status. Never a button.
      PASSION  attention, sparingly. Errors, validation, the one-line reason a
               vehicle is unavailable. It is a near neighbour of Sunshine on
               the wheel, so it never appears bare: always inside a red-tinted
               box with a red border, never as loose red wording on a warm
               ground. At most one element per screen.
      BEACH    the quiet end of the warm range. Alternating section grounds,
               the header bar, form and summary panels, chips, hairlines.

    Sibling-brand note: agnetours.com is built from the same five colours but
    maps Sea to the brand and Sunshine to the action. This site runs that
    exactly backwards — warm architecture, cool conversion — so the two share
    a palette and agree on nothing else. Reading them side by side, that site
    is a cool page with warm buttons and this one is a warm page with a cool
    button.                                                                  */
:root {
  /* ---- Layer 1: the palette, as sampled ---- */
  --island:   #00AE4F;
  --sunshine: #F59B21;
  --sea:      #006CAD;
  --passion:  #E01E15;
  --beach:    #C9B286;

  /* ---- Layer 2: tone ramps (every pairing below is contrast-checked) ---- */
  /* Sunshine, all the way down. The raw hue takes DARK ink, never white
     (2.19:1), so it is a band and a mark rather than a button; -900 and -950
     are the grounds that carry white body text. */
  --sun-950: #2B1B06;   /* footer */
  --sun-900: #3D2708;   /* deep closing sections — white on it is 14.07:1 */
  --sun-850: #4E340B;   /* top of the CTA-band gradient, dark buttons */
  --sun-800: #8A5A0A;   /* primary wording on a light ground — 5.92:1 */
  --sun-300: #F7C078;   /* primary wording on a deep warm ground — 8.56:1 */
  --sun-100: #FDEEDA;
  --sun-50:  #FEF7EC;
  /* Sea only ever has to do two things now — be a fill that holds white text,
     and be wording on a light ground — so it needs two tones and two tints. */
  --sea-700: #00568F;   /* Sea wording on white — 7.70:1; also the hover fill */
  --sea-100: #D3E5F1;
  --sea-50:  #EDF4F9;
  /* Island. -700 is the lowest green that still holds white text (4.63:1) and
     is also the darkest-safe icon tone on the light warm grounds this site is
     made of; the raw hue is for the deep warm sections only. */
  --isl-700: #00873D;
  --isl-800: #046A34;   /* green wording on a light ground — 6.74:1 */
  --isl-100: #D6F2E3;
  --isl-50:  #EBF9F1;
  /* Passion. The raw hue holds white text (4.81:1); -700 is red wording on
     white (6.96:1). Both are rationed — see the role map above. */
  --pas-700: #B3121A;
  --pas-100: #FADBDA;
  --pas-50:  #FDEDEC;
  /* Beach. Deliberately weak. These are grounds; if one of them ever reads as
     "a colour" rather than as paper, it has been taken too far. */
  --bch-200: #E9E1D1;
  --bch-100: #F5F0E6;
  --bch-50:  #FBF8F2;

  /* ---- Layer 3: roles ---- */
  /* Ink is warm now, not Sea-derived and not neutral black. On a page whose
     structure is amber, a cool-grey heading reads as a foreign object — the
     same argument that made it blue when the structure was blue. */
  --ink:            #241A0C;
  --ink-2:          #4A3E30;
  --ink-3:          #6E6152;   /* 6.01:1 on white, 5.29:1 on Beach-100 */
  /* Two line weights with two different jobs. --line is the hairline that
     draws cards and dividers; --line-strong outlines *controls*, so it has to
     clear the 3:1 non-text floor. Both are warm: interface belongs to the
     primary, and the primary is warm. */
  --line:           #E7E0D2;
  --line-strong:    #96876E;   /* 3.51:1 on white */
  --surface:        #FFFFFF;
  --surface-2:      var(--bch-50);
  --surface-3:      var(--bch-100);

  --brand:          var(--sunshine);
  --brand-strong:   var(--sun-800);
  --brand-deep:     var(--sun-850);
  --brand-ground:   var(--sun-900);
  --brand-abyss:    var(--sun-950);
  --brand-tint:     var(--sun-50);
  --brand-tint-2:   var(--sun-100);
  --on-brand:       var(--sun-950);   /* 7.60:1 on raw Sunshine */

  --action:         var(--sea);
  --action-hover:   var(--sea-700);
  --action-ink:     var(--sea-700);
  --action-tint:    var(--sea-50);
  --action-tint-2:  var(--sea-100);
  --on-action:      #FFFFFF;          /* 5.60:1 on Sea */

  --ok:             var(--isl-700);
  --ok-ink:         var(--isl-800);
  --ok-tint:        var(--isl-50);
  --ok-tint-2:      var(--isl-100);

  --attn-ink:       var(--pas-700);
  --attn-tint:      var(--pas-50);
  --attn-line:      var(--pas-100);
  /* Aliases. Both roles are Passion — a warning and an error are the same
     colour here because amber belongs to the brand and cannot also mean
     caution. */
  --warn:           var(--attn-ink);
  --err:            var(--attn-ink);
  --err-tint:       var(--attn-tint);

  /* Text on the deep warm grounds — warm-tinted, not neutral grey, or the
     espresso reads as a dirty black behind it. */
  --on-dark:        #FFFFFF;
  --on-dark-soft:   #E8D9C2;   /* 10.14:1 on -900 */
  --on-dark-mute:   #B9A585;   /* 6.96:1 on -950 */
  --on-dark-line:   rgba(255, 255, 255, .13);

  --radius:         10px;
  --radius-lg:      16px;
  --radius-pill:    999px;

  /* The only easing curve in the system. Everything else here transitions a
     colour, where the curve is invisible and bare `ease` is right. This one
     exists for the press state, which moves a real object and needs to
     decelerate rather than coast. Add a second curve only when something
     genuinely moves differently — not to give each component its own. */
  --ease-out:       cubic-bezier(0.23, 1, 0.32, 1);

  /* One shadow, two depths, tinted with the deep end of Sunshine rather than
     neutral black so a card lifting off a Beach ground stays warm. */
  --shadow:         0 1px 2px rgba(43, 27, 6, .07), 0 2px 8px rgba(43, 27, 6, .07);
  --shadow-lg:      0 2px 6px rgba(43, 27, 6, .08), 0 12px 32px rgba(43, 27, 6, .13);
  /* The action shadow exists so the one cool element on a warm page reads as
     raised without needing a border. Nothing else gets a tinted lift. */
  --shadow-action:  0 1px 2px rgba(0, 45, 74, .22), 0 6px 16px rgba(0, 108, 173, .30);

  /* 4px base. Named by role so a template never picks a raw pixel value. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 88px;

  --font: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
  --wrap: 1180px;
  --header-h: 64px;
}

/* ---------- Reset -------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
body {
  margin: 0;
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink-2);
  background: var(--surface);
  -webkit-font-smoothing: antialiased;
  /* NO overflow-x: hidden here, deliberately.
     It was in place as belt-and-braces against a wide child. Both things it
     was covering for — the header bar overflowing at 375px, and the price
     table leaking its scroll overflow — are now fixed at source, and every
     page measures 0 horizontal overflow at 320/768/834 without it. Keeping
     it would only hide the next real overflow bug instead of surfacing it.
     It also has a side effect worth not reintroducing: setting one axis to
     hidden makes the other compute to `auto`, which turns <body> into a
     scroll container and puts the sticky header on a shakier compositing
     path than it needs to be on. */
}
img, svg, video { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4 { color: var(--ink); margin: 0 0 var(--s3); line-height: 1.18; font-weight: 800; letter-spacing: -.018em; }
h1 { font-size: clamp(30px, 5.2vw, 50px); }
h2 { font-size: clamp(23px, 3.2vw, 33px); }
h3 { font-size: clamp(18px, 2vw, 21px); letter-spacing: -.012em; }
h4 { font-size: 16px; letter-spacing: -.008em; }
p  { margin: 0 0 var(--s4); }
p:last-child { margin-bottom: 0; }
ul, ol { margin: 0 0 var(--s4); padding-left: 1.25em; }
/* Links are the primary colour, per the role map — amber wording, not blue.
   Blue on this site means "this is a control you press", and a paragraph link
   is not that. The tone is --brand-strong because raw Sunshine as text is
   2.19:1; hover goes deeper still rather than lighter. */
a { color: var(--brand-strong); text-underline-offset: 3px; }
a:hover { color: var(--brand-deep); }
strong { color: var(--ink); font-weight: 700; }
hr { border: 0; border-top: 1px solid var(--line); margin: var(--s6) 0; }

/* Visible focus for every interactive element, including ones a browser
   would otherwise skip. Keyboard users need this more than the design needs
   the ring gone, so it is never removed — only styled. */
:focus-visible {
  outline: 3px solid var(--action);
  outline-offset: 2px;
  border-radius: 4px;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: var(--s4); top: -100px; z-index: 200;
  background: var(--ink); color: #fff; padding: var(--s3) var(--s4);
  border-radius: var(--radius); text-decoration: none; font-weight: 700;
}
.skip-link:focus { top: var(--s3); color: #fff; }

/* ---------- Layout ------------------------------------------------------- */
.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 var(--s4); }
@media (min-width: 640px) { .wrap { padding: 0 var(--s5); } }

.section       { padding: var(--s7) 0; }
.section--tight{ padding: var(--s6) 0; }
.section--alt  { background: var(--surface-2); }
/* Renamed from .section--ink: the dark section is Sea now rather than
   near-black, and a class called "ink" would keep suggesting otherwise.
   Both callers (pages/home.php, pages/about.php) were updated with it. */
/* Renamed from .section--sea: the deep section is espresso-amber now, the
   bottom of the Sunshine ramp, and the class should not claim otherwise.
   Both callers (pages/home.php, pages/about.php) were updated with it. */
.section--deep  { background: var(--brand-ground); color: var(--on-dark-soft); }
.section--deep h2, .section--deep h3 { color: #fff; }
@media (min-width: 900px) {
  .section        { padding: var(--s9) 0; }
  .section--tight { padding: var(--s7) 0; }
}

.stack > * + * { margin-top: var(--s4); }
.section-head { max-width: 62ch; margin-bottom: var(--s6); }
.section-head p { color: var(--ink-3); font-size: 17px; margin-bottom: 0; }

.eyebrow {
  display: block; font-size: 12.5px; font-weight: 800; letter-spacing: .1em;
  text-transform: uppercase; color: var(--brand-strong); margin-bottom: var(--s2);
}
.section--deep .eyebrow { color: var(--sun-300); }
.lead { font-size: clamp(17px, 2.1vw, 20px); color: var(--ink-2); max-width: 60ch; }

.grid { display: grid; gap: var(--s4); }
/* Grid items default to min-width:auto, so a single unbreakable string —
   johann@agnetours.com in the contact card — can push a column wider than
   its container and take the page with it. min-width:0 lets the column win. */
.grid > * { min-width: 0; }
.grid-2 { grid-template-columns: 1fr; }
.grid-3 { grid-template-columns: 1fr; }
.grid-4 { grid-template-columns: 1fr; }
@media (min-width: 640px) {
  .grid { gap: var(--s5); }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 900px) {
  .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ---------- Buttons ------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 48px;                 /* comfortably above the 44px touch target floor */
  padding: var(--s3) var(--s5);
  border: 1.5px solid transparent; border-radius: var(--radius);
  font: inherit; font-weight: 700; font-size: 16px; line-height: 1.2;
  text-decoration: none; cursor: pointer; text-align: center;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .15s ease,
              transform .12s var(--ease-out);
}
/* Press feedback. Until this existed the site answered a tap with nothing at
   all: hover does not exist on a phone, which is where most of this traffic
   books, so a 48px button could be pressed with no acknowledgement until the
   next page painted. 0.98 sits at the very top of the subtle range, chosen on
   device rather than from the spec: 0.97 read as mushy under a thumb. This is
   a form people are trying to finish, not a toy.

   There is deliberately no prefers-reduced-motion override, and adding one
   back would be noise. The global rule near the top of this file already
   flattens the duration, so a reduced-motion press snaps to 0.98 with no
   travel at all — which is the outcome such an override would be aiming for.
   At 2% there is nothing left worth halving. */
.btn:active { transform: scale(.98); }
.btn svg { width: 18px; height: 18px; flex: none; }
/* THE conversion button — Sea, white text, and the only cool-coloured
   object on a page that is warm everywhere else. Every route into the
   booking flow wears it: the header CTA, the hero picker submit, each step's
   Continue, the sticky bar, the CTA band. Nothing that is not booking is
   ever blue, and that rationing is the entire reason it works.

   On the two deep warm grounds it inverts to a white fill with Sea wording
   (see .cta-band below) — Sea on espresso measures 2.51:1 and would sink.
   That is the same button, not a second one: same hue, same word, the way an
   outline button is still the button it outlines. */
.btn--primary   { background: var(--action); color: var(--on-action); box-shadow: var(--shadow-action); }
.btn--primary:hover   { background: var(--action-hover); color: var(--on-action); }
.btn--dark      { background: var(--brand-deep); color: #fff; }
.btn--dark:hover{ background: var(--brand-ground); color: #fff; }
/* Secondary navigation dressed as a button — "all destinations", "see
   prices". It stays warm and never borrows blue: blue is reserved for the
   thing that starts a booking, and an outline button next to a filled one
   in the same hue would blunt both. */
.btn--outline   { background: transparent; color: var(--ink); border-color: var(--line-strong); }
.btn--outline:hover { border-color: var(--brand); color: var(--brand-strong); background: var(--brand-tint); }
/* The outline button as it appears on a deep warm ground. This used to be an
   inline style attribute in includes/ui.php's CTA band; it is a variant of a
   component, so it lives with the component. */
.btn--on-dark   { background: transparent; color: #fff; border-color: rgba(255,255,255,.38); }
.btn--on-dark:hover { background: rgba(255,255,255,.10); border-color: #fff; color: #fff; }
.btn--ghost     { background: transparent; color: var(--brand-strong); padding-left: 0; padding-right: 0; min-height: 44px; }
.btn--ghost:hover { color: var(--brand-deep); text-decoration: underline; }
/* WhatsApp's own green (#25D366 / #1FA855) gives white text only 3.09:1 —
   below AA for the 16px semibold this button uses. Darkened to the point
   where it still reads unmistakably as WhatsApp but clears 4.5:1 (5.07). */
.btn--wa        { background: #157F41; color: #fff; }
.btn--wa:hover  { background: #116E39; color: #fff; }
.btn--block     { width: 100%; }
.btn--lg        { min-height: 56px; font-size: 17px; padding-inline: var(--s6); }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .5; pointer-events: none; }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--s3); }

/* ---------- Header ------------------------------------------------------- */
/* Warm sand, with the primary colour laid across the top edge as a 4px
   band. Sunshine cannot be the header FILL — white on it is 2.19:1 and dark
   ink on a full-bleed orange bar is a budget airline, not a private transfer
   — so the primary states itself as a rule and a mark instead, and the bar
   itself stays quiet enough that the one blue button in it is the loudest
   thing on the page. The band is what a visitor sees first, before any
   word. */
/* The backdrop-filter is blur(4px) and nothing else. Both halves of that were
   measured, not guessed — on a position:sticky band the property is at its
   most expensive, because the backdrop changes on every scroll frame and the
   compositor re-filters a full-width strip for as long as the page moves.
   This band is on screen for the whole session, so what it does per frame
   should be the least that still reads as glass.

   The band is .94 opaque, so only 6% of the filtered backdrop reaches the
   screen. Compositing the real case — this site's #F5F0E6 ground with
   #2B1B06 body text passing underneath — the per-channel difference against
   an unfiltered backdrop comes out as:

     blur(10px) + saturate(140%)   11.5/255 peak   2.70/255 mean   <- was
     blur(10px)                    11.8/255        2.66/255
     blur(4px)                      9.6/255        2.13/255        <- is
     saturate(140%)                 0.6/255        0.15/255

   Two conclusions. `saturate(140%)` is gone because it moved 0.55/255 — 0.2%,
   through a 6% window on low-chroma content, which is nothing at all. And the
   radius dropped 10px -> 4px because 4px keeps 83% of the peak effect for 40%
   of the convolution; the difference between the two is well under the point
   where anyone could pick them apart, and the peak only ever lands on the
   leading edge of a dark glyph directly beneath the band.

   Be honest about the saving: the readback and the extra compositing layer
   are what a sticky backdrop-filter really costs, and those remain. Only the
   blur work shrinks. If this header ever shows up in a scroll profile again,
   removing the property outright is the next step, and it costs 2.13/255 of
   mean difference to take.

   Do NOT lower the alpha to make the blur read more strongly. A more
   transparent header would undo what the note above wants — a band quiet
   enough that its one blue button stays the loudest thing in it. */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(245, 240, 230, .94);
  backdrop-filter: blur(4px);
  border-bottom: 1px solid var(--bch-200);
  /* The band is an inset shadow rather than a border-top on purpose: a border
     would add 4px to the header's measured height, and --header-h (64/76px)
     is what the drawer's max-height and the scroll-padding are computed from.
     Painting it inside keeps every one of those numbers where it was. */
  box-shadow: inset 0 4px 0 var(--brand);
}
/* The focus ring is Sea by default, which is right on every light ground
   (5.60:1 on white). On the deep warm grounds it flips to the primary:
   Sunshine on -900 is 6.43:1, where Sea would be 2.51:1 and invisible. */
.site-footer :focus-visible,
.cta-band :focus-visible,
.section--deep :focus-visible { outline-color: var(--brand); }
.header-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s4); min-height: var(--header-h);
}
/* min-width:0 lets the brand give way rather than push the bar wider than
   the viewport — at 375px the logo, language switch and menu button together
   overflowed by 68px before this. */
.brand { display: flex; align-items: center; gap: var(--s3); text-decoration: none; min-width: 0; min-height: 44px; }
.brand-mark {
  width: 36px; height: 36px; flex: none; border-radius: 9px;
  background: var(--brand); color: var(--on-brand);
  display: grid; place-items: center;
}
.brand-mark svg { width: 21px; height: 21px; }
/* Three stacked lines, so a touch more leading than a single line would want
   — at 1.12 the licence and agency lines start to look glued together. */
.brand-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
/* A wordmark does not wrap. Squeezed between the nav and the CTA just above
   the desktop breakpoint it was breaking into "Privat / Flughafentransfer";
   the nav and CTA tighten instead (see the 900–1099px block below). */
.brand-name { font-weight: 800; font-size: 15px; color: var(--ink); letter-spacing: -.025em; white-space: nowrap; }
/* The wordmark accent is the primary, which is now the warm half. It could
   not have been this colour before the swap — it would have put the booking
   colour on something that is not a button. */
.brand-name b { color: var(--brand-strong); font-weight: 800; }
/* The operating agency sits between the brand and its licence, and carries
   slightly more weight than the licence line: it is a company name, not a
   reference number. */
.brand-org { font-size: 11px; font-weight: 600; color: var(--ink-2); letter-spacing: .005em; }
.brand-sub { font-size: 10.5px; font-weight: 600; color: var(--ink-3); letter-spacing: .03em; }
/* Both lower lines are trust signals rather than wayfinding, and both repeat
   in the footer and the imprint — so on the narrowest phones they yield the
   height and width instead of crowding a 64px bar with three lines. */
@media (max-width: 379px) {
  .brand-org, .brand-sub { display: none; }
}

.nav-desktop { display: none; }
.header-actions { display: flex; align-items: center; gap: var(--s2); }

.nav-toggle {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: 44px; min-width: 44px; padding: 0 var(--s3);
  background: transparent; border: 1.5px solid var(--line-strong);
  border-radius: var(--radius); color: var(--ink);
  font: inherit; font-weight: 700; font-size: 14px; cursor: pointer;
}
.nav-toggle svg { width: 22px; height: 22px; }
/* Icon-only on small phones; the button keeps its accessible name from the
   visually-hidden label, so nothing is lost to a screen reader. */
.nav-toggle .nav-toggle-text { display: none; }
@media (min-width: 420px) { .nav-toggle .nav-toggle-text { display: inline; } }

/* The drawer is a plain checkbox-free, JS-toggled panel; it degrades to an
   always-visible list if the script fails, which is better than a menu that
   cannot be opened. */
.nav-drawer {
  display: none;
  border-top: 1px solid var(--bch-200);
  /* Vertical only, on purpose. This element also carries .wrap in the markup,
     whose `padding: 0 var(--s4)` is where its horizontal gutter comes from —
     and a `padding` shorthand here silently zeroed it, which is what put the
     link text, the CTA and the language switch flush against the screen edge
     while every other surface on the site inset by 16px. padding-block
     touches one axis and leaves .wrap's gutter alone. */
  padding-block: var(--s4) var(--s5);
  background: var(--surface);
  max-height: calc(100dvh - var(--header-h));
  overflow-y: auto;
  /* The panel was the only thing on the site that teleported: display flipped
     none/block with nothing in between, on the primary mobile navigation.
     `display` is in the transition list with allow-discrete so the element
     stays rendered long enough for the fade to run — without it the browser
     removes the box on the first frame and there is nothing left to animate.
     8px of travel, not more: this drops from under a bar it is attached to,
     so it needs to look connected rather than thrown. */
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity .2s var(--ease-out), transform .2s var(--ease-out),
              display .2s allow-discrete;
}
.nav-drawer[data-open="true"] { display: block; opacity: 1; transform: none; }
/* Entry needs a start state or the drawer would only animate on the way out.
   @starting-style is what the first style resolution reads before the element
   becomes visible. */
@starting-style {
  .nav-drawer[data-open="true"] { opacity: 0; transform: translateY(-8px); }
}
/* The drawer ships open in the HTML and app.js closes it on load, so without
   this gate every single page load would play a drawer sweeping shut. The
   script adds [data-ready] two frames later; until then there is no
   transition to trigger. See the matching note in assets/app.js. */
.nav-drawer:not([data-ready]) { transition: none; }
.nav-drawer a.nav-link {
  display: flex; align-items: center; justify-content: space-between;
  min-height: 52px; padding: var(--s2) 0;
  font-size: 17px; font-weight: 700; color: var(--ink); text-decoration: none;
  border-bottom: 1px solid var(--line);
}
/* Every other icon in this file carries an explicit size; this one was missed,
   and the global `svg { height: auto }` reset then let it grow to fill the
   space `justify-content: space-between` handed it — a ~500px chevron per row.
   It went unseen because opening the drawer was never part of any check until
   the panel got an animation worth looking at. */
.nav-drawer a.nav-link svg { width: 18px; height: 18px; flex: none; }
.nav-drawer a.nav-link:hover { color: var(--brand-strong); }
.nav-drawer .btn { margin-top: var(--s4); }

/* The header bar is light and the footer is espresso, so this needs both
   treatments. Light is the default; the footer overrides it below. */
.lang-switch { display: flex; gap: var(--s1); background: var(--bch-200); padding: 3px; border-radius: var(--radius-pill); }
/* Below 640px the header has room for the brand and one control, and that
   control has to be the menu. The switcher moves into the drawer (and stays
   in the footer), so language is never more than one tap away. */
.header-actions .lang-switch { display: none; }
@media (min-width: 640px) { .header-actions .lang-switch { display: flex; } }
.nav-drawer .lang-switch { margin-top: var(--s4); }
@media (min-width: 640px) { .nav-drawer .lang-switch { display: none; } }
.lang-switch a {
  min-height: 44px; min-width: 44px; padding: 0 var(--s3);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); text-decoration: none; border-radius: var(--radius-pill);
}
.lang-switch a[aria-current="true"] { background: #fff; color: var(--ink); box-shadow: var(--shadow); }
.site-footer .lang-switch { background: rgba(255, 255, 255, .10); }
.site-footer .lang-switch a { color: var(--on-dark-soft); }
.site-footer .lang-switch a[aria-current="true"] { background: #fff; color: var(--brand-abyss); box-shadow: none; }

/* The header's own booking button is desktop-only: on phone and tablet the
   sticky bottom bar carries the same action, and two of them in view at once
   is one too many. */
.cta-desktop { display: none; }

@media (min-width: 900px) {
  :root { --header-h: 76px; }
  .cta-desktop { display: inline-flex; }
  .nav-toggle { display: none; }
  .nav-drawer { display: none !important; }
  .nav-desktop { display: flex; align-items: center; gap: var(--s5); }
  .nav-desktop a {
    font-size: 15px; font-weight: 700; color: var(--ink-2); text-decoration: none;
    padding: var(--s2) 0; border-bottom: 2px solid transparent;
  }
  /* The current-page marker underlines in the primary. Under the old
     hierarchy this had to be white — an amber underline would have been the
     booking colour on five items that are not booking. Now that navigation
     and the primary are the same role, the underline is simply the brand
     saying where you are, and the CTA keeps blue to itself. --brand-strong,
     not the raw hue: a raw Sunshine rule on sand is 1.95:1. */
  .nav-desktop a:hover, .nav-desktop a[aria-current="page"] { color: var(--ink); border-bottom-color: var(--brand-strong); }
  .brand-name { font-size: 18px; }
  .brand-org  { font-size: 11.5px; }
  .brand-sub  { font-size: 11px; }
}

/* The tight band: wide enough for a horizontal nav, not wide enough for it at
   full size next to a three-line lockup. Measured at 900px the bar wanted
   ~903px of content in 877px of room, which is what pushed the wordmark and
   the CTA onto second lines. Tightening the two elements that can afford it
   buys ~90px and leaves the lockup intact. */
@media (min-width: 900px) and (max-width: 1099px) {
  .nav-desktop { gap: var(--s4); }
  .nav-desktop a { font-size: 14.5px; }
  .cta-desktop { padding-inline: var(--s4); }
  .lang-switch a { min-width: 38px; padding: 0 var(--s2); }
}

/* The CTA says "Transfer buchen" where there is room and "Buchen" where there
   is not — the same action, not a different one, so the shorter label costs
   the reader nothing. */
.cta-desktop .cta-short { display: none; }
@media (max-width: 1099px) {
  .cta-desktop .cta-full  { display: none; }
  .cta-desktop .cta-short { display: inline; }
}

/* Mobile-only sticky booking bar. The single most important control on the
   site should never be more than a thumb away, and on a phone the header CTA
   scrolls out of reach the moment someone starts reading. */
.sticky-cta {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
  display: flex; gap: var(--s2); padding: var(--s2) var(--s3);
  padding-bottom: calc(var(--s2) + env(safe-area-inset-bottom, 0px));
  background: rgba(255,255,255,.97);
  border-top: 1px solid var(--bch-200);
  box-shadow: 0 -2px 14px rgba(0,42,71,.10);
}
.sticky-cta .btn { flex: 1; min-height: 50px; }
.sticky-cta .btn--wa { flex: 0 0 52px; padding: 0; }
body.has-sticky-cta { padding-bottom: 76px; }
@media (min-width: 900px) {
  .sticky-cta { display: none; }
  body.has-sticky-cta { padding-bottom: 0; }
}

/* ---------- Hero --------------------------------------------------------- */
/* Sunlight, not a dark band. The hero warms from the palest Sunshine tint
   into Beach, the white booking panel sits on top of it, and the one blue
   button inside that panel is the only cool thing in the viewport — which is
   exactly where the eye lands. The deep end of the ramp is saved for the
   close of the page (.section--deep, .cta-band, footer), so a visit runs
   light-warm at the top and deep-warm at the bottom rather than the reverse.

   The old hierarchy could not do this: with Sunshine as the action, a warm
   hero would have put the CTA's own colour behind the CTA. */
.hero {
  background: linear-gradient(168deg, var(--sun-50) 0%, var(--bch-100) 62%, var(--bch-200) 100%);
  border-bottom: 1px solid var(--bch-200);
  padding: var(--s6) 0 var(--s7);
}
.hero-grid { display: grid; gap: var(--s6); align-items: start; }
.hero h1 { margin-bottom: var(--s4); }
.hero .lead { color: var(--ink-2); }
.hero-points { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s3); }
.hero-points li { display: flex; gap: var(--s3); align-items: flex-start; font-size: 15.5px; color: var(--ink-2); }
/* What is included = Island. These three lines are the inclusion list, not
   navigation, so they are the one place green appears above the fold. The
   tone stepped down from the raw hue to -700 when the hero went light: raw
   Island on this ground is 2.93:1, -700 is 4.63:1. */
.hero-points svg { width: 20px; height: 20px; flex: none; color: var(--ok); margin-top: 1px; }
@media (min-width: 900px) {
  .hero { padding: var(--s8) 0; }
  /* Copy left, booking panel right — the panel gets the narrower, denser
     column because it is a form, not a reading surface. Named areas let the
     source stay in the order a phone should scroll it (copy, picker, points)
     while desktop reads copy over points beside a full-height picker. */
  .hero-grid {
    grid-template-columns: minmax(0, 1.05fr) minmax(360px, .95fr);
    grid-template-areas: "copy picker" "points picker";
    grid-template-rows: auto 1fr;
    gap: var(--s5) var(--s7);
  }
  .hero-copy   { grid-area: copy; }
  .hero-picker { grid-area: picker; }
  .hero-points { grid-area: points; }
}

/* ---------- Booking picker (hero + embedded) ----------------------------- */
.picker {
  background: var(--surface); border: 1px solid var(--line);
  /* The Sea rule across the top is the "main form heading" signal from the
     role map: it states who is asking before the heading does, and it keeps
     the panel reading as authority rather than as a floating widget. */
  border-top: 4px solid var(--brand);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  padding: var(--s5);
}
.picker h2 { font-size: 20px; margin-bottom: var(--s1); color: var(--brand-strong); }
.picker .picker-hint { font-size: 14px; color: var(--ink-3); margin-bottom: var(--s5); }
.picker-fields { display: grid; gap: var(--s4); }
@media (min-width: 480px) { .picker-fields .field-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); } }
.picker-foot { margin-top: var(--s5); }
.picker-note { display: flex; gap: var(--s2); align-items: center; justify-content: center; margin-top: var(--s3); font-size: 13px; color: var(--ink-3); }
.picker-note svg { width: 15px; height: 15px; flex: none; }


/* ---------- Quick booking band ------------------------------------------
   The conversion area above the route grids. It is deliberately built out of
   parts that already exist — .input, .select, .btn--primary, .price-from,
   .price-value, the Beach panel of .summary — so it reads as another section
   of this site rather than as an application embedded in it.

   Two columns on desktop: the question on the left, the answer on the right.
   One column on a phone, in that same order, so the price lands directly
   under the field that produced it instead of above it.                    */
.quickbook {
  display: grid; gap: var(--s5);
  background: var(--surface); border: 1px solid var(--line);
  border-top: 4px solid var(--brand);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  padding: var(--s5);
}
.qb-h2 { font-size: 22px; color: var(--brand-strong); margin-bottom: var(--s1); }
.qb-lead { font-size: 15.5px; color: var(--ink-3); margin-bottom: var(--s5); max-width: 46ch; }
.qb-form { display: grid; gap: var(--s4); }
.qb-submit { width: 100%; }

/* The combobox. The list is absolutely positioned so opening it does not
   push the price panel down the page — the one thing the visitor is looking
   at must not move while they type. */
.qb-combo { position: relative; }
.qb-list {
  position: absolute; z-index: 30; top: calc(100% + 4px); left: 0; right: 0;
  max-height: 292px; overflow-y: auto; margin: 0; padding: var(--s1); list-style: none;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
}
.qb-list:empty, .qb-list[hidden] { display: none; }
.qb-opt {
  display: flex; align-items: baseline; gap: var(--s3);
  padding: var(--s3); border-radius: var(--radius);
  font-size: 15px; color: var(--ink); cursor: pointer;
}
.qb-opt b { font-weight: 800; }
/* The region a hotel belongs to, shown beside it: the point of searching a
   hotel name is to be told which route it is on, so that answer travels with
   every suggestion rather than only appearing after selection. */
.qb-opt small { margin-left: auto; font-size: 13px; color: var(--ink-3); white-space: nowrap; }
.qb-opt[aria-selected="true"], .qb-opt:hover {
  background: var(--action-tint); color: var(--action-ink);
}
.qb-opt[aria-selected="true"] small, .qb-opt:hover small { color: var(--action-ink); }

/* Shortlist. Ordered by distance from the terminal — a fact, not a ranking
   of what other people booked. */
.qb-shortlist-label {
  display: block; font-size: 11.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--ink-3); margin-bottom: var(--s2);
}
.qb-pills { display: flex; flex-wrap: wrap; gap: var(--s2); }
.qb-pill {
  display: inline-flex; align-items: baseline; gap: var(--s2);
  min-height: 44px; padding: var(--s2) var(--s4);
  border: 1.5px solid var(--line); border-radius: var(--radius-pill);
  background: var(--surface-2); text-decoration: none;
  font-size: 14.5px; font-weight: 700; color: var(--ink);
  transition: border-color .15s ease, background-color .15s ease;
}
.qb-pill b { color: var(--action-ink); font-weight: 800; font-variant-numeric: tabular-nums; }
.qb-pill:hover { border-color: var(--action); background: var(--action-tint); color: var(--ink); }
.qb-pill[aria-current="true"] { border-color: var(--action); background: var(--action-tint); }

/* The answer panel. Beach ground, like every other summary on the site. */
.qb-panel {
  background: var(--bch-50); border: 1px solid var(--bch-200);
  border-radius: var(--radius-lg); padding: var(--s5);
  display: flex; flex-direction: column; gap: var(--s2);
}
.qb-panel .price-value { font-size: 34px; line-height: 1.1; }
.qb-route { font-size: 15px; font-weight: 700; color: var(--ink); margin: 0; }
.qb-vehicle { font-size: 14px; color: var(--ink-3); margin: 0; }
.qb-value {
  list-style: none; margin: var(--s3) 0 0; padding: var(--s4) 0 0;
  border-top: 1px dashed var(--line-strong);
  display: grid; gap: var(--s2);
}
.qb-value li { display: flex; gap: var(--s2); align-items: flex-start; font-size: 14.5px; color: var(--ink-2); }
/* Island, as everywhere else that means "what you get". */
.qb-value svg { width: 17px; height: 17px; flex: none; color: var(--ok); margin-top: 3px; }
.qb-policy {
  display: flex; gap: var(--s2); align-items: flex-start;
  margin: var(--s3) 0 0; font-size: 13.5px; color: var(--ink-3);
}
.qb-policy svg { width: 16px; height: 16px; flex: none; color: var(--brand-strong); margin-top: 2px; }

@media (min-width: 640px) {
  .qb-submit { width: auto; justify-self: start; }
}
@media (min-width: 900px) {
  .quickbook {
    grid-template-columns: minmax(0, 1.15fr) minmax(300px, .85fr);
    gap: var(--s7); padding: var(--s6);
    align-items: start;
  }
}
/* ---------- Forms -------------------------------------------------------- */
.field { display: block; }
/* `.field { display: block }` outranks the user-agent's `[hidden]` rule, so a
   field carrying the attribute stayed on screen anyway. The quick-booking
   band swaps two fields for one another and needs the attribute to actually
   mean something; without this both controls are visible at once. */
.field[hidden] { display: none; }
.field > label, .field-legend {
  display: block; font-size: 14px; font-weight: 700; color: var(--ink);
  margin-bottom: var(--s2);
}
.field-hint { display: block; font-size: 13px; color: var(--ink-3); margin-top: var(--s2); }
.field-error { display: block; font-size: 13.5px; font-weight: 600; color: var(--err); margin-top: var(--s2); }

.input, .select, .textarea {
  width: 100%; min-height: 50px; padding: var(--s3) var(--s4);
  font: inherit; font-size: 16px;  /* 16px keeps iOS from zooming on focus */
  color: var(--ink); background: var(--surface);
  border: 1.5px solid var(--line-strong); border-radius: var(--radius);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.textarea { min-height: 96px; padding-top: var(--s3); resize: vertical; }
.input:hover, .select:hover, .textarea:hover { border-color: var(--ink-3); }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--action);
  box-shadow: 0 0 0 3px var(--action-tint-2);
}
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
  border-color: var(--err); background: var(--err-tint);
}
.select {
  appearance: none; padding-right: 44px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23596C7E' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M5 8l5 5 5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; background-size: 18px;
}

/* Segmented control — used for trip type and anywhere a two-way choice
   should read as a switch rather than as two loose radios. */
.segmented { display: flex; gap: 0; border: 1.5px solid var(--line-strong); border-radius: var(--radius); overflow: hidden; }
/* Collapsed to a point, not just transparent: at their natural size these
   register as sub-minimum touch targets even though the label beside them is
   the real 48px control. */
.segmented input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.segmented label {
  flex: 1; display: flex; align-items: center; justify-content: center;
  min-height: 48px; padding: var(--s2) var(--s3);
  font-size: 15px; font-weight: 700; color: var(--ink-2); cursor: pointer;
  background: var(--surface); text-align: center;
}
.segmented label + label { border-left: 1.5px solid var(--line-strong); }
/* Selected = Sunshine, per the role map — but at tint strength with a
   full-strength bar under it, not as a solid fill. Measured on the live
   panel, a solid orange segment sits directly above the solid orange submit
   button and the two read as one block, which costs the button the thing it
   is there for. A selection is a state; the button is the action; the state
   gets the quieter half of the same colour. */
.segmented input:checked + label {
  background: var(--action-tint-2); color: var(--action-ink);
  box-shadow: inset 0 -3px 0 var(--action);
}
.segmented input:focus-visible + label { outline: 3px solid var(--action); outline-offset: -3px; }

/* Choice cards — vehicles and extras. The whole card is the control, so the
   touch target is the card, not a 16px radio dot. */
.choice { display: grid; gap: var(--s3); }
/* Same reason as `.grid > *` above, and the same one-line fix: a grid item's
   automatic minimum size is min-content, so the track was sized to the widest
   card rather than to the column. Measured at 320px that made every card
   336px wide and gave the page 57px of horizontal scroll — which also
   stretched the fixed sticky bar, since it resolves against the initial
   containing block. min-width:0 lets the column win instead. */
.choice > * { min-width: 0; }
.choice-card { position: relative; }
.choice-card input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.choice-card > label {
  display: flex; gap: var(--s4); align-items: flex-start;
  padding: var(--s4); cursor: pointer;
  background: var(--surface); border: 1.5px solid var(--line);
  /* opacity is in this list because the card is dimmed to .45 when the party
     outgrows the vehicle (see input:disabled below), and that is driven by JS
     while the customer is looking at it. Without the transition a card they
     were about to tap simply blinks out of reach. */
  border-radius: var(--radius); transition: border-color .15s ease, background-color .15s ease, opacity .15s ease;
}
.choice-card > label:hover { border-color: var(--line-strong); background: var(--surface-2); }
.choice-card input:checked + label { border-color: var(--action); background: var(--action-tint); box-shadow: inset 0 0 0 1px var(--action); }
.choice-card input:focus-visible + label { outline: 3px solid var(--action); outline-offset: 2px; }
.choice-card input:disabled + label { opacity: .45; cursor: not-allowed; }
.choice-body { flex: 1; min-width: 0; }
.choice-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2); font-weight: 800; color: var(--ink); font-size: 16px; }
/* The recommendation badge is the one chip on the site allowed to wrap.
   Chips are nowrap by default because a two-line "6 Koffer" pill is silly,
   but this one carries a sentence — "Empfohlen für 2 Personen" measures
   188px against the 171px a 320px card can give it — and an unbreakable
   sentence sets the minimum width of everything above it. Wrapping only
   happens where it has to; at 375px and up it still sits on one line. */
.choice-title .chip { white-space: normal; line-height: 1.3; text-align: left; }
/* display:block matters: these are sibling spans, and left inline they ran
   together into "6 Koffer Zu klein für 9 Personen" on one line. */
.choice-meta  { display: block; font-size: 13.5px; color: var(--ink-3); margin-top: 2px; }
/* The "too small" note is a reason, not a spec — it earns the warning colour
   so the disabled row explains itself at a glance. */
.choice-meta[data-fit-note]:not(:empty) { color: var(--warn); font-weight: 600; }
.choice-price { font-weight: 800; color: var(--action-ink); font-size: 17px; white-space: nowrap; text-align: right; }
.choice-icon  { width: 40px; height: 40px; flex: none; color: var(--brand-strong); }
.choice-qty   { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s3); }
.choice-qty select { min-height: 42px; width: auto; padding-right: 38px; }

/* Below 420px the row does not have room for icon + text + price. Letting the
   text column absorb the difference is not the answer — it collapses to about
   90px and "Empfohlen für 2 Personen" breaks into three lines inside its pill.
   The price drops onto its own line instead and the text gets the column back.
   Indented past the icon so it stays visually attached to the vehicle it
   belongs to rather than floating against the card edge. */
@media (max-width: 419px) {
  .choice-card > label { flex-wrap: wrap; }
  .choice-price {
    flex: 1 0 100%;
    text-align: left;
    padding-left: calc(40px + var(--s4));
  }
}

/* ---------- Stepper ------------------------------------------------------ */
.stepper { display: flex; gap: var(--s1); list-style: none; padding: 0; margin: 0 0 var(--s6); counter-reset: step; }
.stepper li {
  flex: 1; min-width: 0; position: relative;
  padding-top: var(--s5); font-size: 12.5px; font-weight: 700; color: var(--ink-3);
  border-top: 4px solid var(--line); text-align: left;
}
.stepper li + li { margin-left: var(--s2); }
/* Two colours, not three: the track behind you is the brand (stating
   progress) and the step you are on is the action (stating where to look).
   Both halves swapped hue with the roles; the logic did not move. The done
   bars take --brand-strong rather than the raw hue because a 4px rule has to
   clear 3:1 to mean anything, and raw Sunshine on white is 2.19:1.
   Island is deliberately absent — it is reserved for the end of the flow, and
   spending it on step 2 would leave that screen with nothing new to say. */
.stepper li[data-state="done"]    { border-top-color: var(--brand-strong); color: var(--ink-2); }
.stepper li[data-state="current"] { border-top-color: var(--action); color: var(--ink); }
.stepper li[data-state="current"]::after {
  content: ''; position: absolute; top: -4px; left: 0; width: 100%; height: 4px;
  background: var(--action);
}
.stepper .step-n { display: block; font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3); }
@media (max-width: 519px) {
  /* Four labels do not fit across a phone. Keep the progress bars, drop the
     words, and let the "Step 2 of 4" line above carry the meaning. */
  .stepper li { font-size: 0; padding-top: var(--s3); }
  .stepper .step-n { font-size: 0; }
}

/* ---------- Cards -------------------------------------------------------- */
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: var(--s5);
}
.card--flat { box-shadow: none; }
.card--pad-lg { padding: var(--s6); }
/* A card that is a form step, wearing the same Sea rule and Sea heading as
   the hero picker. The point is that "the box you fill in" looks identical
   whether it is the two-field picker on the homepage or step 4 of the
   booking flow — the customer meets one form, in one colour, four times. */
.card--form { border-top: 4px solid var(--brand); }
.card--form > h2 { color: var(--brand-strong); font-size: 21px; }

/* Destination card: name, the two facts that decide the booking, the price. */
.dest-card {
  display: flex; flex-direction: column; gap: var(--s3);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: var(--s5); text-decoration: none; color: inherit;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
a.dest-card:hover { border-color: var(--brand); box-shadow: var(--shadow-lg); color: inherit; }
.dest-card h3 { margin: 0; }
.dest-facts { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); font-size: 14px; color: var(--ink-3); }
.dest-facts span { display: inline-flex; align-items: center; gap: 6px; }
.dest-facts svg { width: 15px; height: 15px; flex: none; }
/* Label above the figure, not beside it. At four cards across, a single row
   put "Preis ab" and "Einfache Fahrt" hard against the number from both
   sides and the whole thing read as one run-on string. */
.dest-foot { margin-top: auto; padding-top: var(--s3); border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 2px; }
.price-from { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-3); }
/* Headline prices carry Sunshine's text tone — the deep amber, not the raw
   hue, which would be 2.19:1 here. Note where this stops: the price TABLE
   keeps ink figures. A whole column of amber is not emphasis, it is wallpaper,
   and the role map's "price highlights" means the one number a card is
   selling on, not every number on the site. */
.price-value { font-size: 26px; font-weight: 800; color: var(--action-ink); letter-spacing: -.025em; font-variant-numeric: tabular-nums; }

/* Vehicle card */
.veh-card { display: flex; flex-direction: column; gap: var(--s3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: var(--s5); text-decoration: none; color: inherit; transition: border-color .15s ease, box-shadow .15s ease; }
a.veh-card:hover { border-color: var(--brand); box-shadow: var(--shadow-lg); color: inherit; }
.veh-icon { width: 56px; height: 40px; color: var(--brand-strong); }
.veh-card h3 { margin: 0; }
.veh-tagline { font-size: 14.5px; color: var(--ink-3); margin: 0; }
.veh-specs { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: auto; padding-top: var(--s3); }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px var(--s3); border-radius: var(--radius-pill);
  background: var(--surface-3); color: var(--ink-2);
  font-size: 13px; font-weight: 700; white-space: nowrap;
}
.chip svg { width: 14px; height: 14px; }
.chip--brand { background: var(--brand-tint); color: var(--brand-strong); }
.chip--ok    { background: var(--ok-tint); color: var(--ok-ink); }

/* Trust items — no card chrome, just an icon and two lines. Boxing these
   would make six identical rectangles and bury the words. */
.trust-item { display: flex; gap: var(--s4); align-items: flex-start; }
.trust-item svg { width: 24px; height: 24px; flex: none; color: var(--brand-strong); margin-top: 2px; }
.trust-item h3 { font-size: 16.5px; margin-bottom: 4px; }
.trust-item p { font-size: 15px; color: var(--ink-3); margin: 0; }
.section--deep .trust-item svg { color: var(--sun-300); }
.section--deep .trust-item p { color: var(--on-dark-soft); }

/* The one place raw Sunshine covers real area. A thin full-bleed band of the
   primary directly under the hero: it separates a light-warm hero from a
   light-warm page, which a Beach strip could not do, and it is a statement
   rather than a control, so it never competes with the blue button. Dark ink
   on it, never white — 7.60:1 against 2.19:1. */
.trust-strip { background: var(--brand); color: var(--on-brand); font-size: 14px; font-weight: 700; }
.trust-strip .wrap { padding-block: var(--s3); display: flex; justify-content: center; text-align: center; }

/* Numbered steps (how it works) */
.steps { list-style: none; padding: 0; margin: 0; counter-reset: s; display: grid; gap: var(--s5); }
.steps li { counter-increment: s; display: grid; grid-template-columns: 40px 1fr; gap: var(--s4); align-items: start; }
.steps li::before {
  content: counter(s); grid-row: span 2;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--brand-tint-2); color: var(--brand-strong);
  display: grid; place-items: center; font-weight: 800; font-size: 16px;
}
.steps h3 { font-size: 17px; margin-bottom: 4px; }
.steps p { font-size: 15px; color: var(--ink-3); margin: 0; }
@media (min-width: 900px) { .steps { grid-template-columns: repeat(4, 1fr); gap: var(--s5); } }

/* ---------- Price table -------------------------------------------------- */
/* max-width and contain are both load-bearing, not belt-and-braces: without
   them the 638px price table leaked its scroll overflow past this container
   to the document on the vehicle pages, giving the whole page a 264px
   horizontal scroll at 375px wide. The box was already sized correctly — it
   was the overflow escaping to the initial containing block, which also
   stretched the fixed sticky CTA to 639px. */
.table-scroll {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  max-width: 100%; contain: paint;
  border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface);
}
table.price-table { width: 100%; border-collapse: collapse; min-width: 560px; font-size: 15px; }
.price-table caption { text-align: left; padding: var(--s4) var(--s5); font-size: 14px; color: var(--ink-3); border-bottom: 1px solid var(--line); }
.price-table th, .price-table td { padding: var(--s3) var(--s4); text-align: right; border-bottom: 1px solid var(--line); }
.price-table th:first-child, .price-table td:first-child { text-align: left; }
.price-table thead th { font-size: 12.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); background: var(--bch-100); font-weight: 800; white-space: nowrap; }
.price-table tbody tr:last-child td { border-bottom: 0; }
.price-table tbody tr:hover { background: var(--surface-2); }
.price-table td.num { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink); white-space: nowrap; }
.price-table a { font-weight: 700; text-decoration: none; }
.price-table a:hover { text-decoration: underline; }
.price-table .row-sub { display: block; font-size: 12.5px; font-weight: 500; color: var(--ink-3); }
/* The airport heading inside a vehicle page's fare table. It is a row, not a
   second <thead>, so the column headings above stay associated with every
   group beneath them — and it is tinted rather than ruled so it reads as a
   divider between groups instead of a fifth column heading. */
.price-table .price-group th {
  text-align: left; background: var(--surface-2);
  font-size: 12.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-2); font-weight: 800;
}
.price-table tbody + tbody .price-group th { border-top: 1px solid var(--line); }
.price-table .price-group a { color: inherit; }

/* Compact price list — the same data on a route page, where there are only
   four rows and a table would be overkill. */
.price-list { display: grid; gap: var(--s2); }
.price-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: var(--s3);
  align-items: center; padding: var(--s3) var(--s4);
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);
}
.price-row .pr-name { font-weight: 700; color: var(--ink); min-width: 0; }
.price-row .pr-name small { display: block; font-weight: 500; font-size: 13px; color: var(--ink-3); }
.price-row .pr-cell { text-align: right; font-variant-numeric: tabular-nums; }
.price-row .pr-cell small { display: block; font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); font-weight: 700; }
.price-row .pr-cell b { font-size: 16px; color: var(--action-ink); }

/* ---------- Summary panel ------------------------------------------------ */
.summary { background: var(--bch-50); border: 1px solid var(--bch-200); border-radius: var(--radius-lg); padding: var(--s5); }
.summary h3 { font-size: 17px; color: var(--brand-strong); }
.summary dl { margin: 0; display: grid; gap: var(--s2); }
.summary .row { display: flex; justify-content: space-between; gap: var(--s4); font-size: 15px; padding: var(--s2) 0; border-bottom: 1px dashed var(--line-strong); }
.summary .row dt { color: var(--ink-3); }
/* Email addresses and hotel names have no break opportunities of their own;
   without this they set the row's minimum width on a narrow phone. */
.summary .row dd { margin: 0; font-weight: 700; color: var(--ink); text-align: right; min-width: 0; overflow-wrap: anywhere; }
/* "Change" is a standalone control, not a link inside a sentence, so it gets
   a real target rather than the 21px an inline anchor would give it. */
.summary .row dd a {
  display: inline-flex; align-items: center; min-height: 44px;
  padding: 0 var(--s2); margin-left: var(--s2); font-weight: 700;
}
/* Below ~420px there is not enough room for a label and a value on one line:
   "E-Mail" wrapped to two lines and johann@agnetours.com broke mid-word.
   Stacking gives the value the full row, so both fit intact. */
@media (max-width: 419px) {
  .summary .row { flex-direction: column; align-items: flex-start; gap: 2px; }
  .summary .row dd { text-align: left; }
  .summary .row--total { flex-direction: row; align-items: baseline; justify-content: space-between; }
}
.summary .row--total { border-bottom: 0; border-top: 2px solid var(--brand-strong); margin-top: var(--s2); padding-top: var(--s3); font-size: 18px; }
.summary .row--total dd { font-size: 24px; font-weight: 800; letter-spacing: -.02em; color: var(--action-ink); }

/* The end of the booking flow — the one surface Island owns. Everything
   before it is Sunshine structure and Sea action, so the green arriving here
   reads as an outcome rather than as another step. It also carries the
   reference number, which is why the price figure inside it drops Sunshine:
   on this panel the number to remember is the reference, not the total. */
.summary--received { background: var(--ok-tint); border-color: var(--ok-tint-2); border-top: 4px solid var(--ok); }
.summary--received h3 { color: var(--ok-ink); }
.summary--received .row { border-bottom-color: var(--ok-tint-2); }
.summary--received .row--total { border-top-color: var(--ok); }
.summary--received .row--total dd,
.summary--received .price-value { color: var(--ok-ink); }

/* The only animated moment on the site, and the only one that is allowed to
   be. A customer books a transfer once; this panel is the payoff, arrives on
   a fresh page load after the POST redirect, and is the screen where someone
   decides whether they believe their booking actually worked. Everywhere
   else motion would tax a task people want finished — here there is no task
   left to finish.

   The panel rises as one object. Its contents fade in on a stagger but do
   NOT translate: a row sliding inside a panel that is itself sliding is
   compound motion, and it reads muddy rather than considered. One thing
   moves, the rest arrive.

   The reference number is delayed past the rows' start on purpose — it is
   the one thing the customer has to write down, so it lands first among the
   contents and holds the eye. Last row settles at 0.77s. */
@keyframes pft-rise { from { opacity: 0; transform: translateY(12px); } }
@keyframes pft-fade { from { opacity: 0; } }

.summary--received { animation: pft-rise .4s var(--ease-out) both; }
.summary--received .price-value,
.summary--received .row { animation: pft-fade .3s ease both; }
.summary--received .price-value            { animation-delay: .16s; }
.summary--received .row:nth-child(1)       { animation-delay: .22s; }
.summary--received .row:nth-child(2)       { animation-delay: .27s; }
.summary--received .row:nth-child(3)       { animation-delay: .32s; }
.summary--received .row:nth-child(4)       { animation-delay: .37s; }
.summary--received .row:nth-child(5)       { animation-delay: .42s; }
.summary--received .row:nth-child(6)       { animation-delay: .47s; }

/* Unlike the button press, this override earns its place: there is 12px of
   travel and three quarters of a second of sequencing to remove, so dropping
   both leaves something genuinely gentler rather than something identical.
   The !important is load-bearing twice over, which is why the shorthand is
   used rather than longhands. It out-specifies the global `*` rule at the top
   of this file that would otherwise flatten this to nothing — and because the
   shorthand also resets animation-delay to 0s under the same flag, it beats
   the :nth-child rules above, which are (0,2,0) against this block's (0,1,0)
   and would otherwise keep staggering a fade the reader asked not to be
   staggered. */
@media (prefers-reduced-motion: reduce) {
  .summary--received,
  .summary--received .price-value,
  .summary--received .row {
    animation: pft-fade .2s ease both !important;
  }
}

/* ---------- Notices ------------------------------------------------------ */
.notice { display: flex; gap: var(--s3); padding: var(--s4); border-radius: var(--radius); border: 1.5px solid; font-size: 15px; }
.notice svg { width: 20px; height: 20px; flex: none; margin-top: 1px; }
.notice ul { margin: var(--s2) 0 0; }
.notice--error { background: var(--attn-tint);  border-color: var(--attn-line);    color: var(--attn-ink); }
.notice--ok    { background: var(--ok-tint);    border-color: var(--ok-tint-2);    color: var(--ok-ink); }
/* Info is the one notice that takes the ACTION family rather than the
   primary. An amber info box on an amber-branded page beside a red error box
   reads as a second severity level, and it is not one — blue is unambiguous
   and is the convention every reader already has. */
.notice--info  { background: var(--action-tint); border-color: var(--action-tint-2); color: var(--action-ink); }
.notice strong { color: inherit; }

/* ---------- Breadcrumb --------------------------------------------------- */
.breadcrumb { padding: var(--s4) 0 0; font-size: 13.5px; color: var(--ink-3); }
.breadcrumb ol { list-style: none; display: flex; flex-wrap: wrap; gap: var(--s2); padding: 0; margin: 0; }
.breadcrumb li { display: flex; align-items: center; gap: var(--s2); }
.breadcrumb li + li::before { content: '/'; color: var(--line-strong); }
/* Breadcrumbs are real navigation on a deep route page, so they get a proper
   target height instead of the ~22px a bare inline anchor gives. */
.breadcrumb a { color: var(--ink-3); text-decoration: none; display: inline-flex; align-items: center; min-height: 44px; }
.breadcrumb a:hover { color: var(--brand-strong); text-decoration: underline; }
.breadcrumb [aria-current] { color: var(--ink-2); font-weight: 600; }

/* ---------- Page hero (sub pages) ---------------------------------------- */
/* Sub-page heroes are the Beach counterpart to the home hero's Sea: warm,
   quiet and light, so the immersive band stays a homepage event. Sea still
   leads here — it is in the eyebrow, the breadcrumb and the fact figures. */
.page-hero { background: var(--bch-50); border-bottom: 1px solid var(--bch-200); padding-bottom: var(--s7); }
.page-hero h1 { margin-top: var(--s4); }
.page-hero .lead { margin-top: var(--s4); }
.fact-row { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s5); }
.fact {
  background: var(--surface); border: 1px solid var(--bch-200); border-radius: var(--radius);
  padding: var(--s3) var(--s4); min-width: 120px;
}
.fact dt { font-size: 11.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-3); }
/* Distance, duration, licence number — these are the trust facts, so they
   are Sea rather than plain ink. */
.fact dd { margin: 2px 0 0; font-size: 18px; font-weight: 800; color: var(--brand-strong); letter-spacing: -.02em; }

/* ---------- Prose -------------------------------------------------------- */
.prose { max-width: 68ch; }
.prose h2 { margin-top: var(--s7); }
.prose h2:first-child { margin-top: 0; }
.prose h3 { margin-top: var(--s5); }
.prose p, .prose li { font-size: 17px; }
/* :not([class]) scopes this to plain prose lists. Without it, `.prose ul`
   (0,1,1) outranks any component class on a list inside prose (0,1,0) no
   matter what order the file is in — which turned the district chips on
   every route page into full-width stacked bars. Components style
   themselves; prose styles only what it authored. */
.prose ul:not([class]), .prose ol:not([class]) { display: grid; gap: var(--s2); }

/* FAQ uses <details>, so it opens without JavaScript and stays readable
   with the script blocked or still loading. */
/* interpolate-size is set here rather than on :root on purpose. It is what
   lets block-size animate to the `auto` keyword below, and it inherits — put
   it on the root and every keyword size in the document becomes animatable,
   which is a document-wide capability granted for one accordion. */
.faq { display: grid; gap: var(--s2); interpolate-size: allow-keywords; }
.faq details { border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
/* The caret has rotated over .15s since this component was written while the
   panel it points at appeared instantly — an animation half-done, which reads
   as broken rather than as restraint. This finishes it.

   ::details-content is Chrome/Edge 131+ and Safari 18.4+, not Firefox. Where
   it is unsupported the whole block is ignored and the panel snaps open
   exactly as it does today, so this is additive: no browser gets something
   worse than it had. That is the same bargain the rest of this site makes —
   it renders complete from the server and the extras layer on top.

   content-visibility is in the transition list with allow-discrete so the
   panel stays rendered while it collapses; without it the content is gone on
   the first frame and only the height would animate. */
.faq details::details-content {
  opacity: 0;
  block-size: 0;
  overflow: hidden;
  transition: opacity .2s var(--ease-out), block-size .2s var(--ease-out),
              content-visibility .2s allow-discrete;
}
.faq details[open]::details-content { opacity: 1; block-size: auto; }
.faq summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
  padding: var(--s4); min-height: 56px; cursor: pointer;
  font-weight: 700; color: var(--ink); font-size: 16.5px; list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ''; flex: none; width: 12px; height: 12px;
  border-right: 2.5px solid var(--ink-3); border-bottom: 2.5px solid var(--ink-3);
  transform: rotate(45deg) translateY(-3px); transition: transform .15s ease;
}
.faq details[open] summary::after { transform: rotate(-135deg) translateY(-2px); }
.faq details[open] { border-color: var(--brand-tint-2); }
.faq details[open] summary { border-bottom: 1px solid var(--line); color: var(--brand-strong); }
.faq .faq-body { padding: var(--s4); color: var(--ink-2); font-size: 16px; }
.faq .faq-body p:last-child { margin-bottom: 0; }

/* Area chips */
.chips { display: flex; flex-wrap: wrap; gap: var(--s2); padding: 0; margin: 0; list-style: none; }

/* ---------- CTA band ----------------------------------------------------- */
.cta-band { background: linear-gradient(150deg, var(--sun-850), var(--sun-950)); border-radius: var(--radius-lg); padding: var(--s6); text-align: center; color: var(--on-dark-soft); }
/* The inverted conversion button. Sea on espresso is 2.51:1, so on this one
   ground the fill and the wording trade places: white fill, Sea word, same
   button. */
.cta-band .btn--primary { background: #fff; color: var(--action-ink); box-shadow: none; }
.cta-band .btn--primary:hover { background: var(--action-tint-2); color: var(--action-ink); }
.cta-band h2 { color: #fff; max-width: 22ch; margin-inline: auto; }
.cta-band p { max-width: 52ch; margin-inline: auto; font-size: 16.5px; }
.cta-band .btn-row { justify-content: center; margin-top: var(--s5); }
@media (min-width: 900px) { .cta-band { padding: var(--s8) var(--s7); } }

/* ---------- Footer ------------------------------------------------------- */
.site-footer { background: var(--brand-abyss); color: var(--on-dark-mute); font-size: 15px; margin-top: var(--s8); }
.site-footer a { color: var(--on-dark-soft); text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }
.footer-grid { display: grid; gap: var(--s6); padding: var(--s7) 0; }
@media (min-width: 640px) { .footer-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 900px) { .footer-grid { grid-template-columns: 1.6fr 1fr 1fr 1fr; gap: var(--s6); } }
/* Five columns once there is room for them: the airport list is a column of
   its own, and at 900px it would squeeze the other four rather than sit
   beside them — so between 900 and 1100 it wraps onto its own row instead. */
@media (min-width: 1100px) { .footer-grid { grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr; } }
.footer-col h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .08em; color: #fff; margin-bottom: var(--s4); }
/* The airports heading links to /flughafen/. It must not read as dimmer than
   the headings beside it, so it takes the heading's colour rather than the
   footer's link colour — the hover underline still marks it as a link. */
.footer-col h3 a { color: inherit; }
.footer-col ul { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s3); }
.footer-about p { max-width: 38ch; margin-bottom: var(--s4); }
.footer-legalinfo { font-size: 13px; line-height: 1.7; color: var(--on-dark-mute); }
.footer-bottom {
  border-top: 1px solid var(--on-dark-line); padding: var(--s5) 0;
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5);
  align-items: center; justify-content: space-between; font-size: 13.5px;
}
.footer-contact { display: grid; gap: var(--s3); }
.footer-contact a { display: inline-flex; align-items: center; gap: var(--s2); min-height: 44px; font-weight: 700; }
/* Footer links are the site's main mobile navigation once someone has
   scrolled, so they get a full-row target rather than one sized to the word. */
.footer-col ul a { display: flex; align-items: center; min-height: 44px; }
.footer-contact svg { width: 18px; height: 18px; flex: none; }

/* ---------- Utilities ---------------------------------------------------- */
.mt-0 { margin-top: 0; }
.mt-4 { margin-top: var(--s4); }
.mt-5 { margin-top: var(--s5); }
.mt-6 { margin-top: var(--s6); }
.mb-0 { margin-bottom: 0; }
.text-center { text-align: center; }
.muted { color: var(--ink-3); }
.small { font-size: 14px; }
.nowrap { white-space: nowrap; }

/* ---------- Guide: index cards ------------------------------------------- */
/* Same shape as .dest-card — a whole-card link, a category eyebrow, a title
   and one line of teaser. The difference from a destination card is what sits
   at the foot: a route card closes on a price because that is the decision it
   supports, and an article card closes on the invitation to read. */
.post-card {
  display: flex; flex-direction: column; gap: var(--s2);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: var(--s5);
  text-decoration: none; color: var(--ink);
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.post-card:hover { border-color: var(--brand); box-shadow: var(--shadow); transform: translateY(-2px); }
@media (prefers-reduced-motion: reduce) { .post-card { transition: none; } .post-card:hover { transform: none; } }
.post-card-cat {
  font-size: 11.5px; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--brand-strong);
}
.post-card h3 { font-size: 19px; margin: 0; letter-spacing: -.015em; }
.post-card p { color: var(--ink-2); font-size: 15.5px; margin: 0; flex: 1; }
.post-card-more {
  display: inline-flex; align-items: center; gap: var(--s2);
  color: var(--action-ink); font-weight: 700; font-size: 15px; margin-top: var(--s2);
}
.post-card-more svg { width: 16px; height: 16px; }

/* ---------- Guide: article ----------------------------------------------- */
/* Body and sidebar. One column on phones with the sidebar BELOW the prose:
   a contents list above the article it indexes is a screenful of links before
   the first sentence, which is exactly the wrong order on the device where
   the first sentence matters most. */
.post-layout { display: grid; gap: var(--s7); align-items: start; }
@media (min-width: 900px) {
  .post-layout { grid-template-columns: minmax(0, 1fr) 300px; gap: var(--s8); }
  .post-aside { position: sticky; top: var(--s5); }
}
.post-body > *:first-child { margin-top: 0; }
/* Headings are anchor targets from the contents list, so they need to clear
   the sticky header when jumped to rather than landing underneath it. */
.post-body h2, .post-body h3 { scroll-margin-top: 90px; }

.post-aside { display: grid; gap: var(--s5); }
.post-toc {
  border-left: 3px solid var(--brand-tint-2); padding-left: var(--s4);
}
.post-toc h2 {
  font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); margin: 0 0 var(--s3);
}
.post-toc ol { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); counter-reset: toc; }
.post-toc li { counter-increment: toc; font-size: 15px; line-height: 1.45; }
.post-toc a { color: var(--ink-2); text-decoration: none; display: block; padding: 2px 0; }
.post-toc a:hover { color: var(--brand-strong); text-decoration: underline; }

.post-cta { background: var(--surface-2); }
.post-cta h2 { font-size: 18px; margin: 0 0 var(--s2); }
.post-cta p { margin-bottom: var(--s4); }

/* The pulled-out aside inside article prose. Warm tint rather than a border
   box, so it reads as a raised remark in the same voice rather than as a
   warning notice. */
.post-note {
  background: var(--brand-tint); border-left: 3px solid var(--brand);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--s4) var(--s5); margin: var(--s5) 0;
}
.post-note p { margin: 0; color: var(--ink-2); font-size: 16px; }

.post-date { margin-bottom: 0; }

/* ---------- Consent banner ------------------------------------------------
   Sits above the sticky booking CTA (z-index 90) rather than beside it, and
   clears its height below 900px, where that bar is on screen. Overlapping the
   two would put a dismissable panel on top of the primary action on exactly
   the viewport with least room. */
.consent {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 100;
  background: #fff;
  border-top: 1px solid var(--line);
  box-shadow: 0 -2px 18px rgba(43, 27, 6, .14);
  padding: var(--s4) 0;
  padding-bottom: calc(var(--s4) + env(safe-area-inset-bottom, 0px));
  /* Entrance only. It is set by app.js two frames after the element is
     unhidden; without [data-open] the panel is already in place, so a browser
     that never runs the script cannot leave it parked off-screen. */
  transform: translateY(100%);
  transition: transform .28s cubic-bezier(.22, .61, .36, 1);
}
.consent[data-open] { transform: translateY(0); }
body.has-sticky-cta .consent { bottom: 68px; }
@media (min-width: 900px) {
  body.has-sticky-cta .consent { bottom: 0; }
}
.consent-inner {
  width: min(1140px, 100% - 2 * var(--s4));
  margin: 0 auto;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s3) var(--s5);
}
.consent-text { flex: 1 1 340px; }
.consent-text h2 { font-size: 17px; margin: 0 0 var(--s1); }
.consent-text p  { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--ink-2); }
/* Both buttons the same width, so neither reads as the expected answer. */
.consent-actions { display: flex; gap: var(--s2); flex: 0 0 auto; }
.consent-actions .btn { min-width: 152px; justify-content: center; }
@media (max-width: 559px) {
  .consent-actions { width: 100%; }
  .consent-actions .btn { flex: 1; min-width: 0; }
}

/* A button that has to read as a link: it sits in a row of footer links and
   would look like a stray control otherwise. Kept a <button> because it acts
   on this page rather than navigating. */
.linkish {
  appearance: none; background: none; border: 0; padding: 0;
  font: inherit; color: inherit; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.linkish:hover { color: #fff; }
