/* ============================================================
   Tenerife Travel Guide - Phase 3 Design System
   IDENTITY: palette "Atlantic cobalt", approved at the Phase C gate. Four identity values:
   paper #EEF3F7 (sea haze), ink #15202B (Atlantic night), brand #0F4C81 (cobalt) and accent
   #D4521C (lava orange). Every other colour token is derived from those four.
   Measured when this block was written, and recomputed by build/check-contrast.mjs on every run:
   ink on paper 14.76:1, secondary text on paper 6.44:1, brand on paper 7.93:1, white on brand
   8.86:1. White on the identity orange measures 4.18:1, which is short of AA for a label, so the
   button fill is the same hue walked down to #AC3C0A (white on it 6.17:1, hover 8.32:1) and the
   bright #D4521C is spent on non-text marks only: rules, dots, the focus ring, the logo sun
   (3.74:1 on paper, 4.18:1 on white, 3.94:1 on ink, all above the 3:1 a mark needs).
   No colour literal lives outside a token block in this file except pure white and black, so a
   future clone cannot inherit a colour this palette does not define.
   Soft-square radii. Mobile-first, zero heavy deps, system fonts to protect LCP.

   Keep this header free of any other site's name. Unlike a JS comment, a CSS comment SHIPS:
   the built fingerprinted stylesheet is served verbatim on every page of the site.
   ============================================================ */
/* ---- COLOR TOKENS (light, purposeful) ---- */
:root {
  /* ---- Palette "Atlantic cobalt". IDENTITY marks the four approved values; everything else is
     derived from them. Values stay plain hex on purpose: build/check-contrast.mjs reads hex
     tokens out of this block and fails the run on any pair below its WCAG minimum. ---- */
  --ink:        #15202B;   /* IDENTITY - primary text (14.76:1 on --paper) */
  --ink-soft:   #4A5968;   /* derived: secondary text (6.44:1 on --paper, 5.40:1 on the wash band) */
  --paper:      #EEF3F7;   /* IDENTITY - page ground */
  --paper-2:    #E1EAF2;   /* derived: raised panels, table header tint */
  --surface:    #FFFFFF;   /* cards */
  --surface-2:  #F6F9FB;   /* derived: inputs and zebra rows, one step off --paper */
  --line:       #CBD8E3;   /* derived: hairline borders on light */
  --brand:      #0F4C81;   /* IDENTITY - links, structural text (7.93:1 on --paper) */
  --brand-bright:#1769AE;  /* derived: cobalt lifted for FILLS ON LIGHT */
  --signature:  #9CC8F0;   /* derived: cobalt lifted for DARK GROUNDS ONLY (1.58:1 on --paper, so
                              never text on light; 9.37:1 on the inverse band) */
  --brand-deep: #0A3559;   /* derived: deep fills, brand hover */
  --accent:     #AC3C0A;   /* derived: CTA fill and accent TEXT, the identity orange walked down
                              until a white label clears AA (6.17:1) */
  --accent-warm:#D4521C;   /* IDENTITY - lava orange, NON-TEXT marks only (3.74:1 on --paper) */
  --accent-deep:#8A3109;   /* derived: CTA hover (white 8.32:1) */
  --accent-ink: #ffffff;   /* text colour that sits on --accent */
  --accent-on-dark:#EE7A45;/* derived: orange lifted for dark grounds (5.89:1 on --ink) */
  --gold:       #845600;   /* real ratings only (5.68:1 on --paper) */
  --gold-soft:  #FDF0D2;   /* derived: amber wash for badge backgrounds (DARK text on top) */
  --gold-ink:   #5C3D00;   /* derived: text colour that sits on --gold-soft */
  --gold-on-dark:#F2CF7A;  /* derived: rating text over a dark hero or dark band */
  --focus:      #D4521C;   /* focus ring: a mark, not text, so the bright identity orange */
  /* footer: the one deepest surface, a cobalt navy below --brand-deep */
  --footer-bg:  #0C2238;
  --footer-ink: #B9CCDD;
  --footer-link:#EAF1F7;
  --footer-mute:#8FA9C0;
  --footer-line:#24405C;
  /* Fixed darks. Neither is ever redefined per colour scheme or per band: a scrim or a shadow
     built from --ink inverts wherever --ink flips to a light value. */
  --scrim-rgb:  8 18 28;
  --shade-rgb:  21 32 43;
  /* map base for the /map/ schematic: Atlantic water against pale volcanic land */
  --map-sea:    #A8CBE2;   /* derived: 1.55:1 against --map-land so the shore reads */
  --map-land:   #F5F4EC;   /* derived: pale land */
  --map-shore:  #0F4C81;   /* derived: --brand, for the shoreline */
  --ok:         #0F4C81;   /* derived: same hue as --brand */
  --warn:       #8A3109;   /* derived: same hue as --accent-deep */

  /* ---- TYPE - assertive sans display + neutral sans body. System stacks, no webfont
     (protects LCP); contrast comes from weight and scale, an editorial feel. ---- */
  --display: "Helvetica Neue", "Segoe UI", system-ui, Arial, sans-serif;
  --body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* modular scale */
  --step--1: clamp(.83rem, .8rem + .15vw, .92rem);
  --step-0:  clamp(1rem, .96rem + .2vw, 1.12rem);
  --step-1:  clamp(1.2rem, 1.1rem + .5vw, 1.5rem);
  --step-2:  clamp(1.45rem, 1.25rem + 1vw, 2.1rem);
  --step-3:  clamp(1.85rem, 1.5rem + 1.8vw, 3.1rem);
  --step-4:  clamp(2.4rem, 1.8rem + 3vw, 4.4rem);

  /* spacing scale */
  --s1:.4rem; --s2:.7rem; --s3:1.1rem; --s4:1.7rem; --s5:2.6rem; --s6:4rem; --s7:6rem;
  --maxw: 72rem;
  /* soft-square radii */
  --radius: 8px;
  --radius-sm: 5px;
  --shadow: 0 1px 2px rgb(var(--shade-rgb) / .05), 0 10px 28px rgb(var(--shade-rgb) / .09);
}

/* ---- RESET-ISH ---- */
*,*::before,*::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0; background: var(--paper); color: var(--ink);
  font-family: var(--body); font-size: var(--step-0); line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: var(--brand); text-underline-offset: 3px; }
h1,h2 { font-family: var(--display); line-height: 1.05; letter-spacing: -.025em; font-weight: 800; margin: 0 0 var(--s3); color: var(--ink); }
h3 { font-family: var(--display); line-height: 1.2; letter-spacing: -.01em; font-weight: 700; margin: 0 0 var(--s3); color: var(--ink); }
h1 { font-size: var(--step-4); }
h2 { font-size: var(--step-3); margin-top: var(--s6); }
h3 { font-size: var(--step-1); margin-top: var(--s4); }
p { margin: 0 0 var(--s3); max-width: 64ch; }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 4px; }

/* ---- LAYOUT ---- */
.wrap { width: min(100% - 2rem, var(--maxw)); margin-inline: auto; }
.wrap-narrow { width: min(100% - 2rem, 50rem); margin-inline: auto; }
.section { padding-block: var(--s6); }
.section + .section { padding-top: 0; }
.skip-link { position:absolute; left:-999px; top:0; background:var(--brand); color:#fff; padding:.6rem 1rem; z-index:50; font-weight:700; }
.skip-link:focus { left:8px; top:8px; }

/* ---- CTA ---- */
.btn {
  display:inline-flex; align-items:center; gap:.5rem; justify-content:center;
  font-family: var(--display); font-weight:700; font-size: var(--step-0);
  padding: .85rem 1.4rem; border-radius: 999px; text-decoration:none; border:0; cursor:pointer;
  min-height: 48px; transition: transform .08s ease, background .15s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--accent); color:var(--accent-ink); }
.btn-primary:hover { background: var(--accent-deep); color:var(--accent-ink); }
.btn-brand { background: var(--brand); color:#fff; }
.btn-brand:hover { background: var(--brand-deep); }
.btn-ghost { background: transparent; color: var(--ink); border:2px solid var(--line); }
.btn-ghost:hover { border-color: var(--ink-soft); }

/* ---- HEADER / NAV ---- */
.site-header { position: sticky; top:0; z-index:40; background: var(--paper); background: color-mix(in srgb, var(--paper) 90%, transparent); backdrop-filter: blur(10px); border-bottom:1px solid var(--line); }
/* NAV SIZING (rebuilt 2026-09-09). The previous pass added `white-space:nowrap` and stopped
   there. Measured over HTTP, that fix was treating the wrong thing: nowrap keeps a LABEL off a
   second line, but it cannot keep the ROW inside the content column, and the row had no room
   left. At 1440/1360/1280 the brand (326px) plus the ten items (810px) plus the gap came to
   exactly 1152px in a 1152px column: SLACK = 0. At 1100 it was -39px and at 1024 it was -105px,
   which pushed real horizontal page scroll (documentElement.scrollWidth 1114 on a 1024 viewport).
   With zero slack, anything that widens the row by a few pixels (browser zoom, a wider fallback
   face before the display font settles, a longer label) tips it over, which is what the
   screenshot shows. The old 1023px collapse point made it worse: the row already failed at
   about 1150px, so 1024-1150 was a dead zone, too narrow for the row and too wide for the menu.

   The row is now sized to FIT with real slack instead of being told not to wrap:
   the brand and the link type scale down between 1024 and 1440, the inter-item gap is fluid,
   the nav CTA is a compact pill rather than a full-size button, the brand never shrinks below
   its own text (so it can no longer be squeezed into the links), and the collapse point moved
   to 1023px only because the row genuinely fits from 1024 up now. Verified at 1024-1600 in 8px
   steps: minimum slack 44px. nowrap stays as a second line of defence. */
.nav { display:flex; align-items:center; justify-content:space-between; gap:clamp(.75rem, 1.4vw, 1.5rem); padding-block:.7rem; flex-wrap:nowrap; }
.brand { font-family:var(--display); font-weight:800; font-size:clamp(1.02rem, .62rem + .62vw, 1.35rem); color:var(--ink); text-decoration:none; letter-spacing:-.02em; display:inline-flex; align-items:center; gap:.45rem; flex:0 0 auto; white-space:nowrap; }
.brand-logo { width:28px; height:28px; border-radius:7px; display:block; flex:0 0 auto; }
.brand b { color: var(--accent); }
.nav-links { display:flex; gap:clamp(.62rem, .12rem + .78vw, 1.2rem); align-items:center; list-style:none; margin:0; padding:0; flex:0 1 auto; min-width:0; }
/* nowrap: "Day Trips" broke onto a second line at 1440 and pushed the whole row out of
   alignment, on every page. A nav label is never a wrapping phrase. */
.nav-links li { white-space:nowrap; flex:0 0 auto; }
.nav-links a { color:var(--ink); text-decoration:none; font-weight:600; font-size:clamp(.8rem, .68rem + .19vw, .92rem); white-space:nowrap; }
.nav-links a:hover { color:var(--accent); }
.nav-links a.btn-brand { color:#fff; }
.nav-links a.btn-primary { color:#fff; }
/* The shortlist pill was 159x51 in a row with no room to give. It stays the only filled target
   in the header, just sized for a nav bar rather than for a page body. */
.nav-links .btn { padding:.5rem clamp(.62rem, .2rem + .62vw, 1.05rem); min-height:0; border-radius:9px; line-height:1.15; }
.nav-toggle { display:none; background:none; border:0; font-size:1.6rem; min-height:48px; min-width:48px; cursor:pointer; color:var(--ink); }
/* Below 1024 the row cannot fit at any honest type size, so it becomes the menu. */
@media (max-width: 1023px) {
  .nav-toggle { display:block; }
  .nav-links { position:absolute; inset:100% 0 auto 0; flex-direction:column; align-items:flex-start; background:var(--paper-2); border-bottom:1px solid var(--line); padding:1rem; gap:1rem; display:none; }
  .nav-links[data-open="true"] { display:flex; }
  /* In the collapsed menu the links were 18px-tall tap targets. */
  .nav-links a { font-size:1rem; display:flex; align-items:center; min-height:44px; width:100%; }
  .nav-links .btn { padding:.7rem 1.1rem; justify-content:center; }
}
/* Belt and braces: even if a future nav item overruns the budget, the header clips instead of
   giving every page a horizontally scrolling body. */
.site-header { overflow-x: clip; }

/* ---- HTML SITEMAP: hundreds of links, so they read as columns rather than one long ribbon ---- */
.sitemap-list { list-style:none; margin:0 0 var(--s5); padding:0; columns: 2; column-gap: var(--s5); }
.sitemap-list li { break-inside:avoid; margin:0 0 .35rem; font-size:var(--step--1); line-height:1.45; }
.sitemap-list a { color:var(--ink); text-decoration:none; }
.sitemap-list a:hover { color:var(--accent); text-decoration:underline; }
@media (max-width: 640px) { .sitemap-list { columns: 1; } }

/* ---- BREADCRUMB ---- */
.crumbs { font-size: var(--step--1); color: var(--ink-soft); padding-block: var(--s3); }
.crumbs a { color: var(--ink-soft); }
.crumbs span[aria-current] { color: var(--ink); font-weight:600; }

/* ---- HERO: FULL-SCREEN BACKGROUND (first screen: photograph behind, headline overlaid) ---- */
/* Vertically CENTRED, not bottom-anchored (Oleg, 2026-09-09). `align-items:flex-end` pinned the
   headline, the lede, the proof line and the shortlist button to the bottom edge of an 88svh
   band, leaving a large empty stripe under the header and putting the CTA within a few pixels of
   the fold. Centring moves the whole block up: at 390 the button's bottom goes from 676px to
   about 565px in an 844px viewport, so it clears the fold by a wider margin than before. */
.hero-full {
  min-height: 88svh;
  background-size: cover; background-position: center;
  display: flex; align-items: center;
  color: #fff;
  border-bottom: 3px solid var(--accent-warm);
}
/* The padding that used to hold the copy off the bottom edge would now bias a centred block
   downwards, so it is balanced. */
.hero-full .hero-inner { padding-block: var(--s6); }
.hero-full-inner { padding-block: var(--s7) var(--s6); }
.hero-full h1 { color: #fff; max-width: 21ch; }
.hero-full .lede, .hero-full .hero-lede { color: rgba(255,255,255,.9); max-width: 56ch; font-size: var(--step-1); }
.hero-full .author-hook { color: rgba(255,255,255,.85); display:flex; align-items:center; gap:.6rem; font-size:var(--step--1); margin: var(--s3) 0 var(--s4); }
.hero-full .author-hook a { color: #fff; }
.hero-full .author-hook img { width:38px; height:38px; border-radius:50%; object-fit:cover; border:2px solid rgba(255,255,255,.5); }
.hero-full-ghost { color:#fff; border-color: rgba(255,255,255,.55); }
.hero-full-ghost:hover { border-color:#fff; }
.hero-full .cta-proof { color: rgba(255,255,255,.92); }
.hero-full .cta-proof .gold { color:var(--gold-on-dark); }
.hero-cta { margin: var(--s3) 0 0; }
/* 72svh, not 78: on a 390px screen the hero used most of the first screen and the shortlist CTA
   still fell below it. */
@media (max-width: 640px) { .hero-full { min-height: 72svh; background-position: 64% center !important; } .hero-cta .btn-xl { width:100%; text-align:center; } }

/* ---- HERO (money) - asymmetric split ---- */
.hero { display:grid; grid-template-columns: 1.15fr .85fr; gap: var(--s5); align-items:center; padding-top: var(--s5); }
/* this site's signature: media on the LEFT, taller than wide never; text column right */
.hero--flip { grid-template-columns: .9fr 1.1fr; }
.hero--flip .hero-media { order:-1; }
.eyebrow { font-family:var(--display); text-transform:uppercase; letter-spacing:.14em; font-size:var(--step--1); color:var(--gold); font-weight:700; margin-bottom:var(--s2); }
.hero h1 { margin-bottom: var(--s3); }
.hero .author-hook { display:flex; align-items:center; gap:.6rem; font-size:var(--step--1); color:var(--ink-soft); margin-bottom:var(--s4); }
.hero .author-hook img { width:38px; height:38px; border-radius:50%; object-fit:cover; border:2px solid var(--line); }
.hero-media { aspect-ratio:16/9; border-radius:var(--radius); overflow:hidden; background:var(--surface); box-shadow:var(--shadow); }
.hero-media img { width:100%; height:100%; object-fit:cover; }
@media (max-width: 820px){ .hero{ grid-template-columns:1fr; align-items:start; } .hero--flip .hero-media { order:0; } }

/* ---- QUICK ANSWER (answer-first) ---- */
.quick-answer { background: var(--surface); border:1px solid var(--line); border-left:5px solid var(--accent-warm); color: var(--ink); border-radius: var(--radius); padding: var(--s4) var(--s5); margin-top: var(--s5); box-shadow: var(--shadow); }
.quick-answer p { color:var(--ink); font-size: var(--step-1); max-width: 60ch; margin:0; }
.quick-answer .qa-label { font-family:var(--display); text-transform:uppercase; letter-spacing:.14em; font-size:.72rem; color:var(--gold); margin-bottom:var(--s2); font-weight:700; }

/* ---- KEY FACTS TABLE ---- */
.keyfacts { width:100%; border-collapse:collapse; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; }
.keyfacts th, .keyfacts td { text-align:left; padding:.8rem 1rem; border-bottom:1px solid var(--line); vertical-align:top; }
.keyfacts th { width:42%; font-family:var(--display); font-weight:700; color:var(--ink); background:var(--paper-2); }
.keyfacts tr:last-child th, .keyfacts tr:last-child td { border-bottom:0; }
@media (max-width:640px){
  .keyfacts { display:block; overflow-x:auto; -webkit-overflow-scrolling:touch; }
  .keyfacts th { min-width: 9rem; }
}

/* ---- PRODUCT CARD (money component) ---- */
.product-card { display:grid; grid-template-columns: 1fr 1.2fr; gap:0; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); }
.product-card .pc-media { background:var(--paper-2); overflow:hidden; }
.product-card .pc-media img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.product-card .pc-body { padding: var(--s4); display:flex; flex-direction:column; gap:var(--s2); }
.pc-tag { align-self:flex-start; background:var(--gold-soft); color:var(--gold-ink); font-weight:700; font-size:.72rem; text-transform:uppercase; letter-spacing:.06em; padding:.25rem .6rem; border-radius:999px; }
.pc-rating { font-weight:700; color:var(--ink); display:flex; align-items:center; gap:.45rem; flex-wrap:wrap; }
.pc-rating .src { font-weight:500; color:var(--ink-soft); font-size:var(--step--1); }

/* ---- STAR RATING (dependency-free) - set fill width inline: ratingValue/5*100% ---- */
.stars { position:relative; display:inline-block; vertical-align:middle; line-height:1; white-space:nowrap; font-size:1.05em; }
.stars::before { content:"★★★★★"; color:var(--line); letter-spacing:2px; }
.stars .stars-fill { position:absolute; top:0; left:0; overflow:hidden; white-space:nowrap; }
.stars .stars-fill::before { content:"★★★★★"; color:var(--gold); letter-spacing:2px; }
.stars-num { font-weight:700; color:var(--ink); }
.trust .stars { font-size:1.15rem; margin-top:.2rem; }
.pc-price { font-family:var(--display); font-size:var(--step-2); font-weight:800; color:var(--ink); }
.pc-price small { font-size:var(--step--1); color:var(--ink-soft); font-weight:500; }
@media (max-width:680px){ .product-card{ grid-template-columns:1fr; } .product-card .pc-media{ aspect-ratio:4/3; } }

/* ---- PRICING & INCLUSIONS ---- */
.incl { display:grid; grid-template-columns:1fr 1fr; gap:var(--s4); }
.incl ul { list-style:none; padding:0; margin:0; }
.incl li { padding:.35rem 0 .35rem 1.6rem; position:relative; }
.incl .yes::before { content:"✓"; position:absolute; left:0; color:var(--ok); font-weight:800; }
.incl .no::before  { content:"✕"; position:absolute; left:0; color:var(--accent); font-weight:800; }
@media (max-width:600px){ .incl{ grid-template-columns:1fr; } }

/* ---- ITINERARY ---- */
.timeline { list-style:none; margin:0; padding:0; border-left:3px solid var(--line); }
.timeline li { position:relative; padding:0 0 var(--s4) var(--s4); }
.timeline li::before { content:""; position:absolute; left:-9px; top:4px; width:15px; height:15px; border-radius:50%; background:var(--accent); border:3px solid var(--paper); }
.timeline .t { font-family:var(--display); font-weight:700; }

/* ---- PROS / CONS ---- */
.proscons { display:grid; grid-template-columns:1fr 1fr; gap:var(--s4); }
.proscons .col { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:var(--s4); }
.proscons h3 { margin-top:0; }
.proscons ul { padding-left:1.1rem; margin:0; }
.proscons li { margin-bottom:.5rem; }
.proscons .pros { border-top:4px solid var(--ok); }
.proscons .cons { border-top:4px solid var(--accent-warm); }
@media (max-width:600px){ .proscons{ grid-template-columns:1fr; } }

/* ---- AUTHOR EXPERIENCE ---- */
.author-block { background:var(--paper-2); border:1px solid var(--line); border-radius:var(--radius); padding:var(--s5); display:grid; grid-template-columns:84px minmax(0,1fr); gap:var(--s4); align-items:start; }
.author-block img.avatar { width:84px; height:84px; border-radius:50%; object-fit:cover; border:3px solid var(--surface); box-shadow:var(--shadow); }
.author-block .updated { font-size:var(--step--1); color:var(--ink-soft); margin-top:var(--s3); }
@media (max-width:600px){ .author-block{ grid-template-columns:1fr; } }

/* ---- TRUST STRIP ---- */
.trust { display:grid; grid-template-columns:repeat(4,1fr); gap:var(--s3); text-align:center; background:var(--paper-2); border:1px solid var(--line); border-radius:var(--radius); padding:var(--s4); }
.trust .num { font-family:var(--display); font-size:var(--step-2); font-weight:800; color:var(--gold); }
.trust .lbl { font-size:var(--step--1); color:var(--ink-soft); }
@media (max-width:640px){ .trust{ grid-template-columns:repeat(2,1fr); gap:var(--s4); } }

/* ---- FAQ (FAQPage) ---- */
.faq details { border:1px solid var(--line); border-radius:var(--radius-sm); background:var(--surface); padding:.4rem 1rem; margin-bottom:.7rem; }
.faq summary { font-family:var(--display); font-weight:700; cursor:pointer; padding:.6rem 0; list-style:none; color:var(--ink); }
.faq summary::-webkit-details-marker{ display:none; }
.faq summary::after { content:"+"; float:right; color:var(--accent); font-weight:800; }
.faq details[open] summary::after { content:"−"; }

/* ---- RELATED TOURS ---- */
.related { display:grid; grid-template-columns:repeat(2,1fr); gap:var(--s4); }
.related .rcard { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); }
.related .rcard .rc-media { aspect-ratio:4/3; background:var(--paper-2); }
.related .rcard .rc-media img { width:100%; height:100%; object-fit:cover; }
.related .rcard .rc-body { padding:var(--s3) var(--s4); }
@media (max-width:600px){ .related{ grid-template-columns:1fr; } }
/* 3-up card grid (homepage / hubs) */
.cards-3 { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s4); }
.cards-3 .rcard { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); display:flex; flex-direction:column; }
.cards-3 .rcard .rc-media { aspect-ratio:4/3; background:var(--paper-2); }
.cards-3 .rcard .rc-media img { width:100%; height:100%; object-fit:cover; }
.cards-3 .rcard .rc-body { padding:var(--s3) var(--s4) var(--s4); display:flex; flex-direction:column; gap:var(--s2); flex:1; }
.cards-3 .rcard .rc-body .btn { margin-top:auto; }
.cards-3 .rcard h3 { overflow-wrap:anywhere; }
@media (max-width:820px){ .cards-3{ grid-template-columns:1fr; } }

/* Product card upgrades: "best for" frame, benefit bullets, social proof, popular badge */
.pc-reviews { font-weight:500; color:var(--ink-soft); font-size:var(--step--1); }
.pc-bestfor { align-self:flex-start; margin:0; background:var(--gold-soft); color:var(--gold-ink);
  font-weight:700; font-size:.7rem; text-transform:uppercase; letter-spacing:.05em; padding:.28rem .65rem; border-radius:999px; }
.pc-points { list-style:none; padding:0; margin:.15rem 0 0; display:flex; flex-direction:column; gap:.4rem; }
.pc-points li { position:relative; padding-left:1.45rem; font-size:var(--step--1); color:var(--ink); line-height:1.4; }
.pc-points li::before { content:"✓"; position:absolute; left:0; top:-.02em; color:var(--accent); font-weight:800; }
.rc-media, .pc-media { position:relative; }
.pc-badge { position:absolute; top:.6rem; left:.6rem; z-index:1; background:var(--gold-soft); color:var(--gold-ink);
  font-weight:800; font-size:.7rem; text-transform:uppercase; letter-spacing:.05em; padding:.32rem .7rem; border-radius:999px; box-shadow:var(--shadow); }
.cards-3 .rcard.is-popular, .product-card.is-popular { border-color:var(--gold); box-shadow:0 0 0 2px var(--gold), var(--shadow); }

/* ---- CTA BANNER ---- */
.cta-banner { background:linear-gradient(120deg,var(--brand),var(--brand-deep)); border:1px solid var(--brand-deep); color:#fff; border-radius:var(--radius); padding:var(--s5); text-align:center; }
.cta-banner h2, .cta-banner h3 { color:#fff; margin-top:0; }
.cta-banner p { color:rgba(255,255,255,.82); max-width:52ch; margin-inline:auto; }

/* ---- AVAILABILITY WIDGET (date -> affiliate check) ---- */
.availability { display:flex; gap:.7rem; align-items:center; flex-wrap:wrap; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:var(--s3) var(--s4); box-shadow:var(--shadow); }
.availability .av-label { font-family:var(--display); font-weight:700; color:var(--ink); }
.availability .av-date { padding:.7rem .8rem; border:1px solid var(--line); border-radius:var(--radius-sm); font:inherit; min-height:48px; background:var(--surface-2); color:var(--ink); color-scheme:light; }
.availability .av-go, .availability .av-check { margin-left:auto; }
.availability .av-msg { width:100%; margin:.3rem 0 0; color:var(--ok); font-weight:600; }
@media (max-width:560px){ .availability .av-go, .availability .av-check { margin-left:0; width:100%; } }

/* ---- FOOTER (the one deepest surface, a cobalt navy; its own token family) ---- */
.site-footer { background:var(--footer-bg); color:var(--footer-ink); margin-top:var(--s7); padding-block:var(--s6) var(--s4); border-top:1px solid var(--line); }
.footer-grid { display:grid; grid-template-columns:2fr 1fr 1fr; gap:var(--s5); }
.site-footer a { color:var(--footer-link); text-decoration:none; }
.site-footer a:hover { text-decoration:underline; }
.site-footer h4, .site-footer .footer-h { font-family:var(--display); font-weight:700; font-size:var(--step-0); color:#fff; margin:0 0 var(--s2); }
.footer-nav ul { list-style:none; margin:0; padding:0; }
.footer-nav li { margin-bottom:.4rem; }
.socials { display:flex; gap:.8rem; margin-top:var(--s3); }
.socials a { width:40px; height:40px; display:grid; place-items:center; border:1px solid var(--footer-line); border-radius:50%; }
.socials a:hover { border-color:var(--accent-on-dark); color:var(--accent-on-dark); }
.disclosure { font-size:var(--step--1); color:var(--footer-mute); border-top:1px solid var(--footer-line); margin-top:var(--s5); padding-top:var(--s3); }
.disclosure p { max-width:none; }
@media (max-width:740px){ .footer-grid{ grid-template-columns:1fr; gap:var(--s4); } }

/* ---- BLOG ---- */
.blog-hero { margin:var(--s5) 0 0; }
.blog-hero img { width:100%; height:auto; aspect-ratio:16/9; object-fit:cover; border-radius:var(--radius); box-shadow:var(--shadow); }
.blog-post .author-hook { display:flex; align-items:center; gap:.6rem; font-size:var(--step--1); color:var(--ink-soft); margin-top:var(--s3); }
.blog-post .author-hook img { width:38px; height:38px; border-radius:50%; object-fit:cover; border:2px solid var(--line); }
.prose { margin-top:var(--s5); }
.prose > * { max-width:64ch; }
.prose h2 { margin-top:var(--s6); }
.prose h3 { margin-top:var(--s5); }
.prose img { width:100%; max-width:100%; height:auto; border-radius:var(--radius); margin:var(--s4) 0; box-shadow:var(--shadow); }
.prose figure { margin:var(--s4) 0; }
.prose figcaption { font-size:var(--step--1); color:var(--ink-soft); margin-top:.4rem; }
.prose ul, .prose ol { padding-left:1.3rem; }
.prose li { margin-bottom:.4rem; }
.prose blockquote { margin:var(--s4) 0; padding:.4rem 0 .4rem var(--s4); border-left:4px solid var(--accent-warm); color:var(--ink-soft); font-style:italic; }
.prose table { width:100%; max-width:100%; border-collapse:collapse; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); overflow:hidden; margin:var(--s4) 0; font-size:var(--step--1); }
.prose th, .prose td { text-align:left; padding:.7rem .9rem; border-bottom:1px solid var(--line); vertical-align:top; }
.prose thead th { background:var(--paper-2); font-family:var(--display); color:var(--ink); }
.prose tbody tr:last-child td { border-bottom:0; }
@media (max-width:640px){
  .prose table { display:block; overflow-x:auto; -webkit-overflow-scrolling:touch; }
.table-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; }
.table-scroll > table { min-width: 640px; }
}
.prose a { color:var(--accent); }
/* CRITICAL: .prose a (0,1,1) out-specifies .btn-primary (0,1,0); without these overrides a
   button inside an article body renders accent-on-accent = INVISIBLE TEXT. */
.prose a.btn-primary, .prose a.btn-primary:hover { color:#fff; }
.prose a.btn-brand { color:#fff; }
.prose a.btn-ghost { color:var(--ink); }
/* Hotel affiliate pill (Expedia via Travelpayouts): deliberately amber + outline so it reads as a
   secondary lodging action, distinct from the cyan GetYourGuide tour buttons. Explicit colors
   defeat the .prose a override trap above. */
.hotel-cta, .prose a.hotel-cta { display:inline-flex; align-items:center; gap:.35rem; font-family:var(--display); font-weight:800; font-size:var(--step--1); line-height:1; padding:.6rem 1.1rem; border-radius:10px; border:1px solid var(--gold); color:#fff; background:var(--gold); text-decoration:none; white-space:nowrap; box-shadow:var(--shadow); }
.hotel-cta:hover, .prose a.hotel-cta:hover { background:var(--gold-ink); color:#fff; text-decoration:none; }
.hotel-row { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:.6rem .9rem; margin:.2rem 0 var(--s3); padding:.7rem .95rem; background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm); }
.hotel-row .hr-name { font-weight:700; color:var(--ink); }
.blog-cta { background:var(--paper-2); border:1px solid var(--line); border-left:4px solid var(--accent-warm); border-radius:var(--radius); padding:var(--s4) var(--s5); margin:var(--s6) 0 0; }
.blog-cta h2 { margin-top:0; }
.blog-cta p { margin-bottom:var(--s3); }
.blog-cta-btns { display:flex; gap:.6rem; flex-wrap:wrap; align-items:center; }

/* ---- INFOGRAPHIC (generated SVG) ---- */
.infographic { margin:var(--s4) 0; }
.infographic img, .infographic svg { width:100%; height:auto; border-radius:var(--radius); border:1px solid var(--line); background:var(--surface); display:block; }
.infographic figcaption { margin-top:.6rem; text-align:center; color:var(--ink-soft); }
.embed + p, .embed + p.small, .video-grid + p { max-width:none; }

/* information-gain block: fresh, sourced data competitors don't carry */
.infogain { background:var(--paper-2); border:1px solid var(--line); border-left:5px solid var(--gold); border-radius:var(--radius); padding:var(--s4) var(--s4) var(--s3); margin:var(--s5) 0; }
.infogain .infogain-h { display:flex; align-items:center; flex-wrap:wrap; gap:.55rem; font-family:var(--display); font-weight:800; font-size:var(--step-1); line-height:1.1; margin:0 0 var(--s3); max-width:none; color:var(--ink); }
.infogain .tag { font-family:var(--body); font-size:var(--step--1); font-weight:700; color:var(--gold-ink); background:var(--gold-soft); padding:.18rem .55rem; border-radius:999px; letter-spacing:.03em; text-transform:uppercase; }
.infogain ul { list-style:none; margin:0; padding:0; display:grid; gap:var(--s3); }
.infogain li { position:relative; padding-left:1.5rem; max-width:66ch; }
.infogain li::before { content:""; position:absolute; left:0; top:.5em; width:.55rem; height:.55rem; background:var(--gold); border-radius:50%; }
.infogain .src { display:block; font-size:var(--step--1); color:var(--ink-soft); margin-top:.2rem; }
.infogain .src a { color:var(--ink-soft); }
.infogain .infogain-note { font-size:var(--step--1); color:var(--ink-soft); margin:var(--s3) 0 0; max-width:none; }

/* ---- EMBEDS (interactive map + videos) ---- */
.embed { position:relative; width:100%; aspect-ratio:16/9; border-radius:var(--radius); overflow:hidden; border:1px solid var(--line); box-shadow:var(--shadow); background:var(--surface); }
.embed iframe { position:absolute; inset:0; width:100%; height:100%; border:0; }
.video-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s4); }
.video-grid .embed { box-shadow:none; }
@media (max-width:820px){ .video-grid{ grid-template-columns:1fr; } }

/* ---- CONVERSION COMPONENTS (conversion-optimizer skill) ---- */
.btn-xl { font-size: var(--step-1); padding: 1.05rem 1.9rem; }
.cta-proof { display:block; font-size:var(--step--1); color:var(--ink-soft); margin:.55rem 0 0; max-width:none; }
.cta-proof .gold { color: var(--gold); font-weight:700; }
.cta-box { background:var(--paper-2); border:1px solid var(--line); border-top:3px solid var(--accent-warm);
  border-radius:var(--radius); padding:var(--s5); margin:var(--s5) 0; max-width:none; text-align:center; }
.cta-box .cb-h { font-family:var(--display); font-weight:800; font-size:var(--step-2); color:var(--ink); margin:0 0 .5rem; max-width:none; }
.cta-box p { max-width:44ch; margin-inline:auto; }
.cta-box p:last-child { margin-bottom:0; max-width:none; }
.cta-box .btn { margin-top:.5rem; }
.cta-box a.btn-primary, .cta-box a.btn-primary:hover { color:var(--accent-ink); }
@media (max-width:640px){ .cta-box { padding:var(--s4); } .cta-box .btn { width:100%; } }
/* sticky bar: fixed overlay (zero CLS), thin, revealed after first screen */
.sticky-cta { position:fixed; z-index:45; left:0; right:0; bottom:0; display:flex; align-items:center;
  justify-content:center; gap:.9rem; padding:.55rem .9rem calc(.55rem + env(safe-area-inset-bottom));
  background:rgba(255,255,255,.97); backdrop-filter:blur(8px); border-top:1px solid var(--line);
  transform:translateY(110%); transition:transform .25s ease; }
.sticky-cta.visible { transform:translateY(0); }
.sticky-cta .sc-proof { font-size:var(--step--1); color:var(--ink); white-space:nowrap; flex:0 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.sticky-cta .sc-proof .gold { color:var(--gold); font-weight:700; }
.sticky-cta .btn { min-height:44px; padding:.6rem 1.3rem; }
@media (min-width: 761px) {
  .sticky-cta { left:auto; right:1.2rem; bottom:1.2rem; border:1px solid var(--line);
    border-radius:999px; padding:.5rem .6rem .5rem 1.1rem; box-shadow:var(--shadow); }
  /* article pages (blog/guides): full-width bottom bar on desktop too, like mobile */
  body.article .sticky-cta { left:0; right:0; bottom:0; border-radius:0; border:0;
    border-top:1px solid var(--line); box-shadow:none; padding:.55rem .9rem; }
}
@media (prefers-reduced-motion: reduce) { .sticky-cta { transition:none; } }

/* ---- TESTIMONIALS (real GetYourGuide/Tripadvisor reviews, verbatim) ---- */
.reviews { display:grid; grid-template-columns:repeat(2,1fr); gap:var(--s4); }
.review-card { background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:var(--s4); display:flex; flex-direction:column; gap:var(--s2); }
.review-card .stars { font-size:.95rem; }
.review-card blockquote { margin:0; font-size:var(--step-0); color:var(--ink); max-width:none; }
.review-card blockquote p { max-width:none; margin:0; }
.review-meta { margin-top:auto; font-size:var(--step--1); color:var(--ink-soft); }
.review-meta strong { color:var(--ink); }
@media (max-width:760px){ .reviews{ grid-template-columns:1fr; } }

/* utility */
.lede { font-size:var(--step-1); color:var(--ink-soft); }
.small { font-size:var(--step--1); color:var(--ink-soft); }
.mt-0{margin-top:0}.mb-0{margin-bottom:0}.text-center{text-align:center}

/* ============================================================
   SITE-SPECIFIC LAYER , "Arctic Rust" additions for svalbardtours.org
   ============================================================ */

/* ---- FLIPPED SPLIT HERO (this site's first screen, approved 2026-08-07).
        Tall photo LEFT, headline + proof + XL CTA RIGHT. On mobile the H1 comes first
        and the photo drops below, so the primary-keyword H1 is above the fold. ---- */
.hero--split { display:grid; grid-template-columns: .85fr 1.15fr; gap: var(--s5);
  align-items:center; padding-block: var(--s5) var(--s6); }
.hero--split .hero-media { order:-1; aspect-ratio:3/4; border-radius:var(--radius);
  overflow:hidden; background:var(--paper-2); box-shadow:var(--shadow); }
.hero--split .hero-media img { width:100%; height:100%; object-fit:cover; }
.hero--split h1 { margin-bottom: var(--s3); max-width: 16ch; }
.hero--split .lede { max-width: 46ch; margin-bottom: var(--s4); }
.hero--split .hero-actions { display:flex; flex-wrap:wrap; gap:.7rem; align-items:center; }
.hero--split .author-hook { display:flex; align-items:center; gap:.6rem;
  font-size:var(--step--1); color:var(--ink-soft); margin-top:var(--s4); }
.hero--split .author-hook img { width:38px; height:38px; border-radius:50%; object-fit:cover; border:2px solid var(--line); }
@media (max-width: 860px) {
  .hero--split { grid-template-columns:1fr; gap:var(--s4); padding-block:var(--s4) var(--s5); }
  /* The media div is FIRST in the DOM so it can sit left on desktop. On mobile it has to
     drop BELOW the text, or the primary-keyword H1 is pushed off the first screen. */
  .hero--split .hero-media { order:2; aspect-ratio:4/3; }
  .hero--split h1 { max-width:none; }
}

/* ---- Editorial hairline under section headings (the industrial detail) ---- */
.section > h2:first-child, .rule-h2 { padding-bottom: var(--s2); border-bottom: 2px solid var(--ink);
  border-image: repeating-linear-gradient(90deg, var(--ink) 0 14px, var(--accent-warm) 14px 18px, var(--ink) 18px 32px) 1; }

/* ---- CATEGORY GRID (8 activity hubs on the homepage) ---- */
.cat-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s3); }
.cat-card { display:flex; flex-direction:column; gap:.3rem; background:var(--surface);
  border:1px solid var(--line); border-left:4px solid var(--brand); border-radius:var(--radius-sm);
  padding:var(--s3) var(--s4); text-decoration:none; color:var(--ink); transition:border-color .15s ease, transform .08s ease; }
.cat-card:hover { border-left-color:var(--accent-warm); transform:translateY(-2px); }
.cat-card .cc-name { font-family:var(--display); font-weight:800; font-size:var(--step-0); line-height:1.2; }
.cat-card .cc-meta { font-size:var(--step--1); color:var(--ink-soft); }
@media (max-width:900px){ .cat-grid{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:520px){ .cat-grid{ grid-template-columns:1fr; } }

/* ---- PLATFORM PILL (which marketplace a listing lives on) ---- */
.pc-platform { font-size:var(--step--1); font-weight:700; color:var(--ink-soft); }
.pc-platform .pf { display:inline-block; padding:.12rem .5rem; border-radius:999px;
  border:1px solid var(--line); background:var(--paper-2); color:var(--ink); font-size:.72rem;
  letter-spacing:.03em; text-transform:uppercase; }

/* ---- HONESTY CALLOUT (used wherever the site corrects a common claim) ---- */
.honesty { background:var(--surface); border:1px solid var(--line); border-left:5px solid var(--accent-warm);
  border-radius:var(--radius); padding:var(--s4); margin:var(--s5) 0; }
.honesty .h-h { font-family:var(--display); font-weight:800; font-size:var(--step-1);
  margin:0 0 var(--s2); max-width:none; color:var(--ink); }
.honesty p:last-child { margin-bottom:0; }
.prose .honesty p { max-width:none; }

/* ---- TOUR LIST (category hubs: a dense, scannable row per product) ---- */
.tour-list { display:grid; gap:var(--s3); }
.tour-row { display:grid; grid-template-columns:minmax(0,1fr) minmax(9rem,15rem); gap:var(--s3) var(--s4); align-items:start;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--radius); padding:var(--s4); }
.tour-row.is-lead { border-color:var(--gold); box-shadow:0 0 0 2px var(--gold-soft), var(--shadow); }
.tour-row h3 { margin:0 0 .35rem; font-size:var(--step-1); }
.tour-row h3 a { color:var(--ink); text-decoration:none; }
.tour-row .tr-meta { font-size:var(--step--1); color:var(--ink-soft); margin:0 0 .5rem; max-width:none; }
.tour-row .tr-desc { margin:0; max-width:58ch; }
.tour-row .tr-cta { display:flex; flex-direction:column; align-items:flex-end; gap:.4rem; text-align:right; }
.tr-price { font-family:var(--display); font-weight:800; font-size:var(--step-1); white-space:nowrap; }
.tr-price small { display:block; margin-top:2px; font-family:var(--body); font-size:var(--step--1); font-weight:500; color:var(--ink-soft); white-space:normal; }
@media (max-width:760px){
  .tour-row { grid-template-columns:1fr; }
  .tour-row .tr-cta { align-items:stretch; text-align:left; }
  .tour-row .tr-cta .btn { width:100%; }
}

/* ============================================================
   TOUR-PICKS GRID (depth-cro conversion layer, ported from niagarafallsboattours.org
   2026-08-15 and re-tokened for "Arctic Rust"). A 3-card grid dropped into an article body
   before its FAQ. Whole card converts: image, title and button all carry the sponsored link.
   ============================================================ */
.tour-picks {
  background:var(--paper-2); border:1px solid var(--line); border-radius:var(--radius);
  padding:var(--s4); margin:var(--s5) 0;
}
.tour-picks .tp-season {
  font-size:var(--step--1); color:var(--ink-soft); margin:0 0 var(--s3);
  border-left:3px solid var(--accent-warm); padding-left:.7rem;
}
.tour-picks .tp-grid { display:grid; gap:var(--s3); grid-template-columns:1fr; }
@media (min-width:700px) { .tour-picks .tp-grid { grid-template-columns:repeat(3,1fr); } }
.tour-picks .tp-card {
  background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm);
  overflow:hidden; display:flex; flex-direction:column;
}
.tour-picks .tp-media { display:block; aspect-ratio:4/3; overflow:hidden; }
.tour-picks .tp-media img { width:100%; height:100%; object-fit:cover; }
.tour-picks .tp-body { padding:var(--s3); display:flex; flex-direction:column; gap:.5rem; flex:1; }
.tour-picks .tp-chips { margin:0; display:flex; gap:.35rem; flex-wrap:wrap; }
.pc-chip { display:inline-block; font-size:.7rem; font-weight:700; letter-spacing:.02em;
  padding:.2rem .55rem; border-radius:999px; background:var(--paper-2); color:var(--ink-soft);
  border:1px solid var(--line); }
.tour-picks .tp-chips .pc-chip { white-space:normal; overflow-wrap:anywhere; max-width:100%; }
.tour-picks .tp-chips .pc-chip:first-child { color:var(--ink); }
.tour-picks .tp-title { font-weight:700; color:var(--ink); font-size:var(--step--1); line-height:1.35; margin:0; }
.tour-picks .tp-title a { color:var(--ink); text-decoration:none; }
.tour-picks .tp-title a:hover { color:var(--accent); text-decoration:underline; }
.tour-picks .tp-proof { font-size:var(--step--1); color:var(--ink-soft); margin:0; }
.tour-picks .tp-proof .gold { color:var(--gold); font-weight:700; }
.tour-picks .tp-proof-none { font-style:italic; }
.tour-picks .tp-reassure { margin:0; font-size:.74rem; color:var(--ink-soft); }
.tour-picks .tp-body .btn { margin-top:auto; width:100%; white-space:nowrap; font-size:var(--step--1); padding:.7rem .8rem; }
.tour-picks a.btn-primary, .tour-picks a.btn-primary:hover { color:#fff; }
/* BREAKOUT: .prose > * and .wrap-narrow cap children at ~64ch/50rem, and max-width beats width,
   so a 3-card grid gets crushed to ~150px columns unless the panel is lifted out of the measure
   (same pattern as .cta-box / .infogain). This is the CSS gotcha the depth-cro skill flags. */
.wrap-narrow .tour-picks, .prose .tour-picks, .blog-post .tour-picks {
  max-width:none;
  width:min(100vw - 2rem, 68rem);
  margin-left:50%;
  transform:translateX(-50%);
}
@media (max-width:700px) {
  .wrap-narrow .tour-picks, .prose .tour-picks, .blog-post .tour-picks { width:100%; margin-left:0; transform:none; }
}

/* ---- v2.1: card-style mid-article CTA (image + body) ---- */
.cta-box.cta-card { display:grid; grid-template-columns:230px 1fr; gap:var(--s4); align-items:center; max-width:none; }
.cta-box.cta-card .cb-media { display:block; }
.cta-box.cta-card .cb-media img { width:100%; height:100%; min-height:170px; object-fit:cover; border-radius:var(--radius-sm); display:block; }
.cta-box.cta-card .cb-body p { margin:0 0 var(--s2); max-width:none; margin-inline:0; text-align:left; }
.cta-box.cta-card .tp-proof { color:var(--ink-soft); font-size:var(--step--1); }
@media (max-width: 700px) { .cta-box.cta-card { grid-template-columns:1fr; } }
/* category card: who-it's-for line */
.cat-card .cc-who { display:block; margin-top:.35rem; color:var(--ink-soft); font-size:var(--step--1); font-weight:600; }


/* ---- v2.2 (Oleg, 2026-08-18): section intros span the container, rows + cat cards carry images ---- */
.section.wrap > p { max-width: none; }
/* THE THIRD COLUMN MUST BE CAPPED. It was `auto`, which sizes to content, so a long price note
   grew the CTA column until the middle column had almost no room: the lead card rendered its
   title one word per line with the note taking half the card. `auto` is safe only while every
   note is short, and 45 of 46 ran past 120 characters. A max width makes the layout independent
   of the copy, which is the property it needed all along. */
.tour-row.has-media { grid-template-columns: 220px minmax(0, 1fr) minmax(9rem, 15rem); }
.tour-row { grid-template-columns: minmax(0, 1fr) minmax(9rem, 15rem); }
.tour-row .tr-cta { min-width: 0; }
.tour-row .tr-price small { display:block; max-width: 15rem; margin-left:auto; }
.tour-row .tr-media { display:block; align-self:stretch; }
.tour-row .tr-media img { width:100%; height:100%; min-height:150px; object-fit:cover; border-radius:var(--radius-sm); display:block; }
@media (max-width: 860px) {
  .tour-row.has-media { grid-template-columns: 1fr; }
  .tour-row .tr-media img { min-height:0; max-height:210px; }
}
.cat-card { padding-top:0; overflow:hidden; }
.cat-card .cc-media { display:block; margin:0 calc(-1 * var(--s4)) var(--s2); }
.cat-card .cc-media img { width:100%; height:130px; object-fit:cover; display:block; }

/* homepage per-category anchor blocks */
.cat-block { margin-top: var(--s5); scroll-margin-top: 5rem; }
.cat-block > h3 { margin-bottom: var(--s1); }
.cat-block > p.small { margin-top: 0; color: var(--ink-soft); }

/* single mid-article conversion card (gradual blog placements) */
.tp-solo { margin: var(--s5) 0; }
.tp-solo .tp-grid { grid-template-columns: 1fr; max-width: 34rem; margin-inline: auto; }

/* ---- TOUR (MONEY) PAGES: booking panel above the fold (tour-page-cro spec) ---- */
.tour-hero { padding-top: var(--s4); }
.tour-hero-grid { display: grid; gap: var(--s4); align-items: start; }
@media (min-width: 860px) { .tour-hero-grid { grid-template-columns: 1.15fr 1fr; } }
.tour-hero .hero-media img { width: 100%; height: auto; border-radius: var(--radius); box-shadow: var(--shadow); }
.booking-panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: var(--s4); box-shadow: var(--shadow); }
.booking-panel h1 { font-size: var(--step-2); margin: 0 0 var(--s2); line-height: 1.15; }
.bp-rating { margin: 0 0 var(--s2); color: var(--ink-soft); font-size: var(--step--1); }
.bp-rating .gold { color: var(--gold); font-weight: 700; }
.bp-reason { margin: 0 0 var(--s2); font-size: var(--step-0); }
.bp-price { margin: 0 0 var(--s3); font-size: var(--step-1); font-family: var(--display); }
.bp-price small { display: block; font-size: var(--step--1); color: var(--ink-soft); font-family: var(--body); font-weight: 400; }
.btn-block { display: block; width: 100%; text-align: center; }
.bp-next { color: var(--ink-soft); margin: var(--s2) 0 var(--s3); }
.bp-facts { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2) var(--s3); margin: 0; border-top: 1px solid var(--line); padding-top: var(--s3); }
.bp-facts div { min-width: 0; }
.bp-facts dt { font-size: var(--step--1); color: var(--ink-soft); }
.bp-facts dd { margin: 0; font-size: var(--step--1); font-weight: 600; }

/* ============================================================
   CITY-GUIDE additions: free-entry treatment + wide comparison table
   ============================================================ */
.pc-free { font-weight:700; color:var(--ok); margin:.1rem 0; }
.bp-free { font-family:var(--display); font-weight:800; color:var(--ok); font-size:var(--step-1); margin:0 0 var(--s2); }
.table-scroll { overflow-x:auto; -webkit-overflow-scrolling:touch; margin:var(--s3) 0; }
.keyfacts.compare { min-width:680px; }
.keyfacts.compare th, .keyfacts.compare td { width:auto; white-space:nowrap; vertical-align:middle; }
.keyfacts.compare thead th { white-space:nowrap; }
.keyfacts.compare td:nth-child(2), .keyfacts.compare th:nth-child(2) { white-space:normal; min-width:12rem; }
.keyfacts.compare th[scope="row"] { background:var(--surface); font-weight:700; }
.keyfacts.compare th[scope="row"] a { color:var(--ink); text-decoration:none; }
.keyfacts.compare th[scope="row"] a:hover { color:var(--accent); text-decoration:underline; }
.keyfacts.compare .gold { color:var(--gold); font-weight:700; }
/* free-entry card / passes shell */
.passes-shell { background:var(--paper-2); border:1px solid var(--line); border-left:5px solid var(--accent-warm); border-radius:var(--radius); padding:var(--s4) var(--s5); margin:var(--s4) 0; }
.passes-shell h2 { margin-top:0; }

/* ---- DECISION PICKER (generic, build/launch-v2/gen-decision-picker.mjs) ---- */
.decision-picker { --dp-key: var(--brand, var(--sea, #0F4C81)); --dp-line: var(--line, #CBD8E3);
  --dp-surface: var(--surface, #fff); --dp-surface-2: var(--surface-2, var(--paper-2, #E1EAF2));
  --dp-ink: var(--ink, #15202B); --dp-soft: var(--ink-soft, #4A5968);
  background:var(--dp-surface); border:1px solid var(--dp-line); border-radius:var(--radius, 10px);
  padding:var(--s4, 1.5rem); box-shadow:var(--shadow, 0 1px 2px rgba(0,0,0,.06)); }
.decision-picker .dp-q { border:0; padding:0; margin:0 0 var(--s3, 1rem); min-width:0; }
.decision-picker .dp-q legend { font-family:var(--display, inherit); font-weight:800;
  font-size:var(--step-0, 1rem); color:var(--dp-ink); padding:0 0 .45rem; }
.decision-picker .dp-opts { display:flex; flex-wrap:wrap; gap:.5rem; }
.decision-picker .dp-opt { font-family:var(--body, inherit); font-size:var(--step--1, .9rem); font-weight:600;
  color:var(--dp-ink); background:var(--dp-surface); border:1.5px solid var(--dp-line); border-radius:999px;
  padding:.5rem .9rem; cursor:pointer; line-height:1.25; min-height:40px;
  transition:border-color .15s ease, background .15s ease, color .15s ease; }
.decision-picker .dp-opt:hover { border-color:var(--dp-key); color:var(--dp-key); }
.decision-picker .dp-opt.is-on { background:var(--dp-key); border-color:var(--dp-key); color:#fff; }
.decision-picker .dp-opt:focus-visible { outline:3px solid var(--accent, var(--dp-key)); outline-offset:2px; }
.decision-picker .dp-controls { margin:0 0 var(--s3, 1rem); }
.decision-picker .dp-summary { margin:0 0 var(--s3, 1rem); padding:.6rem .85rem; border-radius:var(--radius-sm, 6px);
  background:var(--dp-surface-2); color:var(--dp-ink); font-size:var(--step--1, .9rem); font-weight:600; max-width:none; }
.decision-picker .dp-results { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s3, 1rem); }
.decision-picker .dp-card { display:flex; flex-direction:column; border:1px solid var(--dp-line);
  border-radius:var(--radius, 10px); overflow:hidden; background:var(--dp-surface-2); }
.decision-picker[data-state="picked"] .dp-card { border-color:var(--dp-key); box-shadow:0 0 0 2px rgba(0,0,0,.06); }
.decision-picker .dp-card.dp-fold { display:none; }
/* [hidden] alone loses to the .dp-card display:flex rule above (author styles beat the UA stylesheet), so a
   card the script hides stays on screen: pin it, same fix as the exit popup's dismissal trap. */
.decision-picker .dp-card[hidden] { display:none !important; }

.decision-picker .dp-media { display:block; aspect-ratio:4/3; background:var(--dp-surface-2); }
.decision-picker .dp-media img { width:100%; height:100%; object-fit:cover; display:block; }
.decision-picker .dp-body { padding:var(--s3, 1rem); display:flex; flex-direction:column; gap:.4rem; flex:1; }
.decision-picker .dp-name { margin:0; font-size:var(--step-0, 1rem); line-height:1.25; }
.decision-picker .dp-name a { color:var(--dp-ink); text-decoration:none; }
.decision-picker .dp-name a:hover { color:var(--dp-key); text-decoration:underline; }
/* Rule 33 floor applies to the card's title LINK too, not only to the chips. Measured at
   39.2px on a 390px viewport (kualalumpuraquarium.com, 2026-09-05) while every chip sat at
   exactly 40px, so the highest-intent target in the block was the one under the floor. */
@media (max-width: 760px) {
  .decision-picker .dp-name a { display:block; min-height:44px; line-height:1.35; padding-block:9px; }
}
.decision-picker .dp-facts { margin:0; display:flex; flex-wrap:wrap; gap:.3rem; max-width:none; }
.decision-picker .dp-facts span { font-size:.72rem; font-weight:600; background:var(--dp-surface);
  color:var(--dp-soft); border:1px solid var(--dp-line); border-radius:999px; padding:.2rem .55rem; }
.decision-picker .dp-price { margin:0; font-size:var(--step--1, .9rem); max-width:none; }
.decision-picker .dp-price strong { font-family:var(--display, inherit); font-size:var(--step-0, 1rem); }
.decision-picker .dp-rev { color:var(--dp-soft); }
.decision-picker .dp-body .btn { margin-top:auto; text-align:center; }
@media (max-width:900px){ .decision-picker .dp-results { grid-template-columns:repeat(2,1fr); } }
@media (max-width:560px){
  .decision-picker { padding:var(--s3, 1rem); }
  .decision-picker .dp-results { grid-template-columns:1fr; }
  .decision-picker .dp-card { flex-direction:row; }
  .decision-picker .dp-media { width:38%; flex:none; aspect-ratio:1/1; }
}

/* ============================================================
   D1 BLOCK LAYER (compile tokens added in Phase D1)
   Count-aware card grid (rule 30) + photo collage (rule 32) +
   testimonials and hotels (homepage blueprint rule 26).
   Every colour is a palette token, so nothing here re-tunes the approved palette.
   ============================================================ */

/* ---- COUNT-AWARE CARD GRID (rule 30 / block library 4.5c.A) ----
   Two leftover cards sit two-per-row and one sits in a capped single column, so a short
   group never stretches across a three-column track and reads as "everything looks the same". */
.cards-3.cards-n2 { grid-template-columns:repeat(2,1fr); }
.cards-3.cards-n1 { grid-template-columns:minmax(0,1fr); max-width:420px; }
@media (max-width:1040px){ .cards-3, .cards-3.cards-n2 { grid-template-columns:repeat(2,1fr); } }
@media (max-width:760px){
  .cards-3, .cards-3.cards-n1, .cards-3.cards-n2 { grid-template-columns:1fr; max-width:none; }
}

/* ---- PHOTO COLLAGE (rule 32 / block library 4.5c.C) ---- */
.photo-collage { display:grid; grid-template-columns:repeat(4,1fr); grid-auto-rows:130px; gap:.6rem; margin:var(--s3) 0 var(--s4); }
.photo-collage .pcol-tile { position:relative; display:block; overflow:hidden; border-radius:var(--radius-sm); background:var(--paper-2); }
.photo-collage .pcol-tile.pcol-hero { grid-column:span 2; grid-row:span 2; }
.photo-collage .pcol-tile.pcol-wide { grid-column:span 2; }
.photo-collage .pcol-tile img { width:100%; height:100%; object-fit:cover; display:block; transition:transform .3s ease; }
.photo-collage .pcol-tile:hover img { transform:scale(1.04); }
.photo-collage .pcol-cap { position:absolute; left:0; right:0; bottom:0; padding:1.4rem .6rem .5rem; font-size:.78rem; font-weight:700; color:#fff;
  /* Scrim from the FIXED --scrim-rgb, held across the glyph band and then faded. Never from --ink:
     a text token flips per scheme and per band, and the scrim would invert under white text. */
  text-shadow:0 1px 2px rgb(var(--scrim-rgb) / .7);
  background:linear-gradient(to top, rgb(var(--scrim-rgb) / .86) 0%, rgb(var(--scrim-rgb) / .78) 58%, rgb(var(--scrim-rgb) / 0) 100%); line-height:1.25; }
@media (max-width:900px){
  .photo-collage { grid-template-columns:repeat(3,1fr); grid-auto-rows:110px; }
  .photo-collage .pcol-tile.pcol-wide { grid-column:span 1; }
}
@media (max-width:560px){
  .photo-collage { grid-template-columns:repeat(2,1fr); grid-auto-rows:100px; }
  .photo-collage .pcol-tile.pcol-wide { grid-column:span 2; }
  .photo-collage .pcol-tile:nth-child(n+7) { display:none; }
}
@media (prefers-reduced-motion: reduce){ .photo-collage .pcol-tile img { transition:none; } .photo-collage .pcol-tile:hover img { transform:none; } }

/* ---- TESTIMONIALS (the testimonials block, real reviews only) ---- */
.testimonials-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s3); }
.testimonial-card { background:var(--surface); border:1px solid var(--line); border-top:3px solid var(--gold);
  border-radius:var(--radius); padding:var(--s4); display:flex; flex-direction:column; gap:var(--s2); }
.testimonial-card .tc-stars { margin:0; color:var(--gold); letter-spacing:.08em; font-size:.95rem; }
.testimonial-card blockquote { margin:0; max-width:none; color:var(--ink); }
.testimonial-card .tc-attr { margin:auto 0 0; font-size:var(--step--1); color:var(--ink-soft); }
.testimonials-disclaimer { margin-top:var(--s3); font-size:var(--step--1); color:var(--ink-soft); }
@media (max-width:980px){ .testimonials-grid { grid-template-columns:repeat(2,1fr); } }
@media (max-width:640px){ .testimonials-grid { grid-template-columns:1fr; } }

/* ---- HOTELS (the hotels block, minted Travelpayouts links) ---- */
.hotel-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--s3); }
.hotel-card { background:var(--surface); border:1px solid var(--line); border-left:4px solid var(--brand);
  border-radius:var(--radius); padding:var(--s4); display:flex; flex-direction:column; gap:.45rem; }
.hotel-card h3 { margin:0; font-size:var(--step-0); line-height:1.25; }
.hotel-card .hc-tier { align-self:flex-start; font-size:.72rem; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
  color:var(--gold-ink); background:var(--gold-soft); border-radius:999px; padding:.14rem .6rem; }
.hotel-card .hc-area, .hotel-card .hc-rating { margin:0; font-size:var(--step--1); color:var(--ink-soft); }
.hotel-card .hc-rating .gold { color:var(--gold); font-weight:700; }
.hotel-card p { margin:0; font-size:var(--step--1); }
.hotel-card .btn { margin-top:auto; align-self:flex-start; }
@media (max-width:980px){ .hotel-grid { grid-template-columns:repeat(2,1fr); } }
@media (max-width:640px){ .hotel-grid { grid-template-columns:1fr; } }

/* === BEGIN rule37 section bands (build/launch-v2/apply-section-bands.mjs) === */
/* rule37:bands {"band-paper":{"key":"paper","bg":"#EEF3F7","fg":"#15202B","link":"#0F4C81"},"band-tint":{"key":"tint","bg":"#E1EAF2","fg":"#15202B","link":"#0F4C81"},"band-wash":{"key":"wash","bg":"#D3E1EE","fg":"#15202B","link":"#0F4C81"},"band-edge":{"key":"edge","bg":"#ffffff","fg":"#15202B","link":"#0F4C81"},"band-inverse":{"key":"inverse","bg":"#15202B","fg":"#EEF3F7","link":"#9CC8F0"}} */
/* Rule 37: adjacent sections never share a ground. The colour lives on the OUTER
   full-width <section>; .wrap moved to an inner div that only constrains width.
   Never reintroduce a box-shadow 0 0 0 100vmax full-bleed here: an outer box-shadow
   paints only outside the border box, so none of it lands behind the content. */
.section[class*="band-"]{background:var(--band-bg);color:var(--band-fg);position:relative;isolation:isolate;overflow-x:clip}
.section[class*="band-"]>.wrap{position:relative;z-index:1}
/* a banded section needs air on both sides, so the usual collapsed top padding is restored */
.section[class*="band-"]+.section[class*="band-"]{padding-top:var(--s6,4rem)}
.section[class*="band-"] a:not(.btn):not(.nav-link){color:var(--band-link)}
.section[class*="band-"] a:not(.btn):not(.nav-link):hover{color:var(--band-link-hover)}
.section[class*="band-"] :where(h1,h2,h3,h4,h5,h6){color:var(--band-fg)}
.band-edge{--band-bg:#ffffff;--band-fg:#15202B;--band-muted:#4A5968;--band-line:#CBD8E3;--band-link:#0F4C81;--band-link-hover:#8A3109;--band-rule:#845600;--band-cta-bg:#AC3C0A;--band-cta-fg:#ffffff;--band-surface:#EEF3F7;--paper:#ffffff;--surface:#EEF3F7;--surface-2:#F6F9FB}
.band-inverse :where(hr,table,th,td){border-color:var(--band-line)}
.band-inverse :where(.card,.rcard,.tcard,.stat-tile,.quick-answer-box,details,.tp-card){background:var(--band-surface);border-color:var(--band-line)}

/* the 5 treatments, driven only by this site's own palette tokens */
.band-paper{--band-bg:#EEF3F7;--band-fg:#15202B;--band-muted:#4A5968;--band-line:#CBD8E3;--band-link:#0F4C81;--band-link-hover:#8A3109;--band-rule:#845600;--band-cta-bg:#AC3C0A;--band-cta-fg:#ffffff;--band-surface:#ffffff}
.band-tint{--band-bg:#E1EAF2;--band-fg:#15202B;--band-muted:#4A5968;--band-line:#CBD8E3;--band-link:#0F4C81;--band-link-hover:#8A3109;--band-rule:#845600;--band-cta-bg:#AC3C0A;--band-cta-fg:#ffffff;--band-surface:#ffffff}
.band-wash{--band-bg:#D3E1EE;--band-fg:#15202B;--band-muted:#4A5968;--band-line:#B9CBDB;--band-link:#0F4C81;--band-link-hover:#8A3109;--band-rule:#845600;--band-cta-bg:#AC3C0A;--band-cta-fg:#ffffff;--band-surface:#ffffff;--surface:#ffffff;--line:#B9CBDB}
.band-edge{--band-bg:#ffffff;--band-fg:#15202B;--band-muted:#4A5968;--band-line:#CBD8E3;--band-link:#0F4C81;--band-link-hover:#8A3109;--band-rule:#845600;--band-cta-bg:#AC3C0A;--band-cta-fg:#ffffff;--band-surface:#EEF3F7;--paper:#ffffff;--surface:#EEF3F7;--surface-2:#F6F9FB}
.band-inverse{--band-bg:#15202B;--band-fg:#EEF3F7;--band-muted:#A9B9C8;--band-line:#2C3B4A;--band-link:#9CC8F0;--band-link-hover:#C4DEF6;--band-surface:#1F2C3A;--band-rule:#9CC8F0;--band-cta-bg:#EE7A45;--band-cta-fg:#15202B;--ink:#EEF3F7;--ink-soft:#A9B9C8;--paper:#15202B;--paper-2:#1F2C3A;--surface:#1F2C3A;--surface-2:#1F2C3A;--line:#2C3B4A;--brand:#9CC8F0;--brand-bright:#9CC8F0;--brand-deep:#9CC8F0;--accent:#EE7A45;--accent-warm:#EE7A45;--accent-deep:#9CC8F0;--accent-ink:#15202B;--gold:#F2CF7A;--gold-soft:#1F2C3A;--gold-ink:#EEF3F7;--focus:#EE7A45;--shadow:none}

/* selectors that used to match the combined .section.wrap element, re-pointed at the split form */
.section > .wrap > h2:first-child{padding-bottom: var(--s2); border-bottom: 2px solid var(--ink); border-image: repeating-linear-gradient(90deg, var(--ink) 0 14px, var(--accent-warm) 14px 18px, var(--ink) 18px 32px) 1;}
.section > .wrap > p{max-width: none;}

/* ---- motion layer. Both effects are decoration only: nothing here can leave real
   content hidden if the animation never runs (memory: a broad opacity:0 scroll reveal
   stays invisible forever for any renderer that does not scroll). ---- */
@keyframes r37-drift{from{transform:translate3d(-4%,0,0) scale(1.02);opacity:.5}to{transform:translate3d(4%,0,0) scale(1.09);opacity:.95}}
.band-wash::before{content:"";position:absolute;inset:-12% -8%;z-index:0;pointer-events:none;border-radius:inherit;background:radial-gradient(38% 60% at 22% 28%,color-mix(in srgb, var(--brand-bright) 15%, transparent) 0%,transparent 70%),radial-gradient(34% 56% at 78% 74%,color-mix(in srgb, var(--accent-warm) 9%, transparent) 0%,transparent 72%);animation:r37-drift 22s ease-in-out infinite alternate;will-change:transform,opacity}
@keyframes r37-pulse{0%{transform:scale(.55);opacity:.8}70%{transform:scale(2);opacity:0}100%{transform:scale(2);opacity:0}}
.map-pin,.spot-pin,.pin,[data-pulse]{position:relative}
.map-pin::after,.spot-pin::after,.pin::after,[data-pulse]::after{content:"";position:absolute;left:50%;top:50%;width:18px;height:18px;margin:-9px 0 0 -9px;border-radius:50%;border:2px solid color-mix(in srgb, var(--accent-warm) 85%, transparent);pointer-events:none;animation:r37-pulse 2.6s ease-out infinite}
[data-countup],.st-num,.stat-num,.kpi-num{font-variant-numeric:tabular-nums}
/* reduced motion: animation off, END STATE shown, nothing hidden */
@media (prefers-reduced-motion: reduce){
  .band-wash::before{animation:none;transform:none;opacity:.75}
  .map-pin::after,.spot-pin::after,.pin::after,[data-pulse]::after{animation:none;transform:translate(0,0) scale(1);opacity:.5}
  [data-countup],.st-num,.stat-num,.kpi-num{transition:none}
}
/* === END rule37 section bands === */

/* === BEGIN crowd calendar (homepage #when, build/compile.mjs > crowdCalendarHtml) ===
   The block sits on the inverse band, so every colour resolves from the band tokens with a
   light-band fallback: moving the section to another treatment needs no edit here. */
.crowd{--cw-fg:var(--band-fg,var(--ink));--cw-muted:var(--band-muted,var(--ink-soft));
  --cw-line:var(--band-line,var(--line));--cw-surface:var(--band-surface,var(--surface));
  --cw-accent:var(--band-rule,var(--gold));--cw-bar:rgb(var(--shade-rgb) / .30);
  display:grid;gap:var(--s4);margin-top:var(--s4)}
.band-inverse .crowd{--cw-bar:rgba(255,255,255,.42)}

.crowd-fig{margin:0;background:var(--cw-surface);border:1px solid var(--cw-line);border-radius:var(--radius);padding:var(--s3)}
.crowd-fig figcaption{margin-top:var(--s2);font-size:var(--step--1);color:var(--cw-muted);max-width:76ch}
.crowd-chart{display:block;width:100%;height:auto;overflow:visible}
.crowd-chart .ch-grid{stroke:var(--cw-line);stroke-width:1;opacity:.55}
.crowd-chart .ch-axis{stroke:var(--cw-line);stroke-width:1.5}
.crowd-chart .ch-bar{fill:var(--cw-bar)}
.crowd-chart .ch-bar.is-peak{fill:var(--cw-accent)}
.crowd-chart .ch-ylab,.crowd-chart .ch-xlab{fill:var(--cw-muted);font-size:15px;font-family:var(--body)}
.crowd-chart .ch-val{fill:var(--cw-fg);font-size:16px;font-weight:700;font-family:var(--body)}
.crowd-chart .ch-cap{stroke:var(--cw-accent);stroke-width:1.5;stroke-dasharray:6 5}
.crowd-chart .ch-caplab{fill:var(--cw-accent);font-size:14px;font-weight:700;font-family:var(--body)}

.crowd-now{background:var(--cw-surface);border:1px solid var(--cw-line);border-radius:var(--radius);padding:var(--s3)}
/* min-height reserves the tallest wording BEFORE the fetch resolves, so replacing the text
   with the live reading cannot shift a single pixel of the page below it. */
.cn-live{margin:0;font-size:var(--step-0);font-weight:600;min-height:5.4em}
.cn-refresh{margin:.5rem 0 0;font-size:var(--step--1);color:var(--cw-muted)}
.cn-live[data-state="live"]{font-weight:700}
.cn-gauge{margin-top:var(--s2);height:14px;border-radius:999px;background:var(--cw-bar);overflow:hidden}
.cn-gauge span{display:block;height:100%;width:var(--pct,2%);border-radius:inherit;background:var(--cw-accent);
  transition:width .6s ease;animation:cw-fill .9s cubic-bezier(.2,.7,.3,1) both;transform-origin:left center}
@keyframes cw-fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
.cn-scale{display:flex;justify-content:space-between;margin:.35rem 0 0;font-size:var(--step--1);color:var(--cw-muted)}
.cn-next{margin:var(--s3) 0 0;padding-top:var(--s2);border-top:1px solid var(--cw-line);font-size:var(--step--1)}

.crowd-cal{background:var(--cw-surface);border:1px solid var(--cw-line);border-radius:var(--radius);padding:var(--s3)}
.cc-lede{margin:0 0 var(--s3);font-size:var(--step--1);max-width:78ch}
.cc-controls{display:flex;flex-wrap:wrap;gap:.5rem}
.cc-chip{min-height:44px;padding:.5rem .95rem;border-radius:999px;border:1px solid var(--cw-line);
  background:transparent;color:var(--cw-fg);font:inherit;font-size:var(--step--1);font-weight:600;cursor:pointer}
.cc-chip:hover{border-color:var(--cw-accent)}
.cc-chip:focus-visible{outline:3px solid var(--cw-accent);outline-offset:2px}
.cc-chip.is-on{background:var(--cw-accent);border-color:var(--cw-accent);color:var(--band-cta-fg,var(--accent-ink))}
.cc-status{margin:var(--s2) 0 var(--s3);font-size:var(--step--1);color:var(--cw-muted);min-height:1.5em}
.cc-month + .cc-month{margin-top:var(--s3)}
.cc-mname{margin:0 0 .5rem;font-size:var(--step-0);letter-spacing:.02em}
.cc-grid{list-style:none;margin:0;padding:0;display:grid;gap:.4rem;grid-template-columns:repeat(auto-fill,minmax(88px,1fr))}
.cd-day{border:1px solid var(--cw-line);border-radius:var(--radius-sm);padding:.4rem .45rem;
  display:flex;flex-direction:column;gap:.1rem;font-size:.78rem;line-height:1.25;min-height:74px}
.cd-dnum{font-weight:800;font-size:1rem}
.cd-dow{color:var(--cw-muted);font-size:.7rem;text-transform:uppercase;letter-spacing:.05em}
.cd-ships{font-weight:600}
.cd-quiet{color:var(--cw-muted);font-weight:500}
.cd-pax{color:var(--cw-muted);font-variant-numeric:tabular-nums}
.cd-bar{display:block;height:4px;margin-top:auto;border-radius:999px;background:var(--cw-bar)}
.cd-bar::before{content:"";display:block;height:100%;width:var(--w,0%);border-radius:inherit;background:var(--cw-accent)}
.cd-none{opacity:.72}
.cd-busy{border-color:var(--cw-accent);border-width:2px}
@media (max-width:520px){
  .cc-grid{grid-template-columns:repeat(auto-fill,minmax(74px,1fr))}
  .cd-day{min-height:68px;font-size:.72rem}
}
@media (prefers-reduced-motion: reduce){
  .cn-gauge span{animation:none;transition:none}
}
/* === END crowd calendar === */

/* === BEGIN pin-map schematic + tour explorer (/map/, build/compile.mjs) ===
   NOTE ON SVG STYLING: these rules live HERE, in the site stylesheet, and never in a <style>
   element inside the SVG. A <style> inside an SVG is not scoped and leaks to the whole
   document, which is how a chart ends up restyling the nav. */
.map-embed{position:relative;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--surface-2)}
.map-embed iframe{display:block;width:100%;height:min(62vh,520px);border:0}

.pin-panel{margin-top:var(--s4)}
.pin-panel .pm-title{margin:0 0 .2rem;font-size:var(--step-1)}
.pin-panel .pm-sub{margin:0 0 var(--s3);color:var(--ink-soft);font-size:var(--step--1);max-width:74ch}
.pm-grid-2{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);gap:var(--s3);align-items:start}
.pin-map{display:block;width:100%;height:auto;border:1px solid var(--line);border-radius:var(--radius);background:var(--surface)}
/* Base geography. .pm-sea is the whole frame; .pm-land is painted over it with fill-rule
   evenodd so a lake inside an island reads as water again. There is no .pm-bg fallback any more:
   a build with no research/geo/coastline.json now FAILS rather than shipping pins on a blank
   tinted box, which is the exact defect this layer exists to end. */
.pin-map .pm-sea{fill:var(--map-sea)}
.pin-map .pm-land{fill:var(--map-land)}
.pin-map .pm-shore{fill:none;stroke:var(--map-shore);stroke-width:1.1;stroke-linejoin:round;stroke-linecap:round}
.pin-map .pm-border{fill:none;stroke:var(--ink-soft);stroke-width:1.4;stroke-dasharray:7 5;opacity:.55}
.pin-map .pm-sea-lab{fill:var(--map-shore);font-size:17px;font-weight:600;letter-spacing:.14em;
  font-family:var(--body);text-transform:uppercase;opacity:.75}
.pin-map .pm-grid{stroke:var(--ink-soft);stroke-width:.8;opacity:.28}
/* A badge may be nudged off its place to stop two numbers covering each other; .pm-true is
   the real coordinate and .pm-leader ties the badge back to it. */
.pin-map .pm-true{fill:var(--brand);stroke:var(--surface);stroke-width:1.4}
.pin-map .pm-leader{stroke:var(--brand);stroke-width:1.6;opacity:.75}
.pin-map .pm-scale line,.pin-map .pm-north line,.pin-map .pm-north path{stroke:var(--ink-soft);stroke-width:1.6;fill:var(--ink-soft)}
.pin-map .pm-scale-lab{fill:var(--ink-soft);font-size:14px;font-family:var(--body)}
.pin-map .pm-anchor circle{fill:var(--ink);opacity:.85}
.pin-map .pm-anchor-lab{fill:var(--ink);font-size:15px;font-weight:700;font-family:var(--body)}
.pin-map .pm-dot{fill:var(--brand);stroke:var(--surface);stroke-width:2.5;transition:fill .18s ease}
.pin-map .pm-num{fill:#fff;font-size:15px;font-weight:800;font-family:var(--body);pointer-events:none}
.pin-map .pm-halo{fill:none;stroke:var(--gold);stroke-width:2;opacity:0;transform-box:fill-box;transform-origin:center;
  animation:pm-pulse 3s ease-out infinite}
.pin-map .pm-pin:hover .pm-dot,.pin-map .pm-pin:focus .pm-dot{fill:var(--accent)}
.pin-map .pm-pin:focus{outline:none}
.pin-map .pm-pin:focus-visible .pm-dot{stroke:var(--accent-deep);stroke-width:4}
@keyframes pm-pulse{0%{opacity:.7;transform:scale(.7)}70%{opacity:0;transform:scale(1.5)}100%{opacity:0;transform:scale(1.5)}}

.pm-legend{list-style:none;margin:0;padding:0;display:grid;gap:.35rem}
.pm-item{display:block;font-size:var(--step--1);line-height:1.4}
.pm-item > a{display:inline-flex;align-items:center;gap:.5rem;min-height:40px;font-weight:700;text-decoration:none}
.pm-item > a:hover{text-decoration:underline}
.pm-badge{display:inline-grid;place-items:center;width:26px;height:26px;border-radius:50%;
  background:var(--brand);color:#fff;font-size:.78rem;font-weight:800;flex:none}
.pm-note{color:var(--ink-soft)}
.pm-credit{color:var(--ink-soft);margin-top:var(--s3);max-width:80ch}
@media (max-width:820px){ .pm-grid-2{grid-template-columns:1fr} }
@media (prefers-reduced-motion: reduce){ .pin-map .pm-halo{animation:none;opacity:0} }

/* ---- tour explorer ---- */
.tex{margin-top:var(--s3)}
.tex-controls{display:grid;gap:var(--s3);margin-bottom:var(--s2)}
.tex-facet{border:1px solid var(--line);border-radius:var(--radius);padding:.7rem .9rem 1rem;margin:0;background:var(--surface)}
.tex-facet legend{padding:0 .35rem;font-size:var(--step--1);font-weight:800;letter-spacing:.02em}
.tex-chips{display:flex;flex-wrap:wrap;gap:.45rem}
.tex-chip{min-height:44px;padding:.5rem .9rem;border-radius:999px;border:1px solid var(--line);
  background:var(--surface-2);color:var(--ink);font:inherit;font-size:var(--step--1);font-weight:600;cursor:pointer}
.tex-chip:hover{border-color:var(--brand)}
.tex-chip:focus-visible{outline:3px solid var(--brand);outline-offset:2px}
.tex-chip.is-on{background:var(--brand);border-color:var(--brand);color:#fff}
.tex-chip .tex-n{opacity:.7;font-weight:700;font-size:.75em}
.tex-sortwrap{display:flex;flex-wrap:wrap;align-items:center;gap:.6rem}
.tex-sortwrap label{font-size:var(--step--1);font-weight:700}
.tex-sort{min-height:44px;padding:.45rem .7rem;border:1px solid var(--line);border-radius:var(--radius-sm);
  background:var(--surface);color:var(--ink);font:inherit;font-size:var(--step--1)}
.tex-clear{min-height:44px}
.tex-note{color:var(--ink-soft);margin:.2rem 0 0;max-width:78ch}
.tex-status{margin:.5rem 0 var(--s2);font-size:var(--step--1);color:var(--ink-soft);min-height:1.5em}
.tex-table{min-width:720px}
.tex-table th[scope="col"]{width:auto;white-space:nowrap}
.tex-table .tex-name{width:30%}
.tex-table td,.tex-table th{padding:.6rem .8rem;font-size:var(--step--1)}
.tex-price{font-variant-numeric:tabular-nums;white-space:nowrap}
.tex-rev{color:var(--ink-soft)}
.tex-na{color:var(--ink-soft)}
.tex-empty{margin:var(--s3) 0 0;padding:var(--s3);border:1px dashed var(--line);border-radius:var(--radius);
  background:var(--surface-2);font-weight:600}
@media (min-width:820px){ .tex-controls{grid-template-columns:1fr 1fr;align-items:start} .tex-sortwrap{grid-column:1 / -1} }
/* === END pin-map schematic + tour explorer === */

/* ---- SECTION LINK LIST (homepage #kids / #rainy) --------------------------------------------
   These two approved sections shipped as a heading over a single paragraph that named articles
   it never linked to. They deliver a route onward, not a seventh copy of the product roster:
   one scannable line per article, title then what it is for. */
.section-links { list-style:none; margin:var(--s4) 0 0; padding:0; display:grid; gap:.5rem; }
.section-links li { margin:0; }
.section-links a {
  display:grid; grid-template-columns:minmax(0,1.3fr) minmax(0,1fr); align-items:baseline; gap:.35rem 1.2rem;
  padding:.7rem .95rem; background:var(--surface); border:1px solid var(--line);
  border-radius:var(--radius-sm); text-decoration:none; color:var(--ink);
}
.section-links a:hover { border-color:var(--brand); background:var(--paper-2); }
.section-links .sl-name { font-family:var(--display); font-weight:700; font-size:var(--step-0); line-height:1.3; }
.section-links .sl-note { color:var(--ink-soft); font-size:var(--step--1); line-height:1.4; text-align:right; }
@media (max-width:760px) {
  .section-links a { grid-template-columns:1fr; }
  .section-links .sl-note { text-align:left; }
}

/* ---- HOMEPAGE FAQ ---- */
.home-faq { margin-top:var(--s4); }
.home-faq .faq-more { margin:.35rem 0 .6rem; font-size:var(--step--1); }

/* DECISION PICKER: the compact server-rendered list (city-guide SKILL §11).
   The picker used to server-render a full image card per product, which on the donor site made
   the block 98 KB and a quarter of the homepage, duplicating the kinds grid directly below it.
   Every product is still here, still linked, still readable with scripting off; it is a row
   until the visitor answers a question, and only the matched few become cards. */
.decision-picker .dp-list { list-style:none; margin:0; padding:0; display:flex; flex-direction:column;
  border:1px solid var(--dp-line); border-radius:var(--radius-sm, 6px); overflow:hidden; background:var(--dp-surface); }
.decision-picker .dp-list[hidden] { display:none !important; }
.decision-picker .dp-row { display:grid; grid-template-columns:minmax(0,1.5fr) minmax(0,.9fr) 16rem 15.5rem;
  align-items:center; gap:.5rem .9rem; padding:.62rem .85rem; border-top:1px solid var(--dp-line); font-size:var(--step--1, .9rem); }
.decision-picker .dp-row:first-child { border-top:0; }
.decision-picker .dp-row:nth-child(even) { background:var(--dp-surface-2); }
.decision-picker .dp-row-name { color:var(--dp-ink); font-weight:700; text-decoration:none; min-width:0; }
.decision-picker .dp-row-name:hover { text-decoration:underline; }
.decision-picker .dp-row-facts { color:var(--dp-muted, inherit); min-width:0; display:flex; flex-wrap:wrap; gap:.5rem; }
.decision-picker .dp-row-facts span + span::before { content:"·"; margin-right:.5rem; opacity:.6; }
.decision-picker .dp-row-price { white-space:nowrap; }
.decision-picker .dp-row-cta { white-space:nowrap; font-weight:700; color:var(--dp-key); text-decoration:none;
  border:1px solid var(--dp-key); border-radius:999px; padding:.28rem .7rem; }
.decision-picker .dp-row-cta:hover { background:var(--dp-key); color:#fff; }
/* The results grid is empty and hidden until a question is answered, so it must not reserve space. */
.decision-picker .dp-results[hidden] { display:none !important; }
@media (max-width: 860px) {
  .decision-picker .dp-row { grid-template-columns:minmax(0,1fr) fit-content(11rem); row-gap:.3rem; }
  .decision-picker .dp-row-facts { grid-column:1 / -1; }
  .decision-picker .dp-row-cta { grid-column:2; grid-row:1; }
}

/* FLAGSHIP: the shore-conditions block. The live line RESERVES ITS HEIGHT so a successful fetch
   swaps text without moving anything below it. §12 is explicit that a blocked or slow API must
   produce zero layout shift, and a one-line paragraph that grows to two lines is a layout shift,
   so the reservation is two lines at the narrowest supported width. */
.surf { margin: var(--s4, 1.25rem) 0 0; }
.surf-table { width: 100%; border-collapse: collapse; font-size: var(--step--1, .9rem); }
.surf-table caption { text-align: left; padding-bottom: .5rem; color: var(--band-muted, var(--ink-soft)); }
.surf-table th, .surf-table td { padding: .55rem .7rem; border-bottom: 1px solid var(--band-line, var(--line)); text-align: left; vertical-align: top; }
.surf-table thead th { font-weight: 700; border-bottom-width: 2px; white-space: nowrap; }
.surf-table tbody th { font-weight: 700; white-space: nowrap; }
.surf-table td:nth-child(2), .surf-table td:nth-child(3) { white-space: nowrap; font-variant-numeric: tabular-nums; }
.surf-table td:last-child { min-width: 18ch; }
.surf-live {
  margin: var(--s3, 1rem) 0 .35rem;
  font-weight: 700;
  min-height: 3.1em;            /* two lines at 390px, so the live swap never reflows the page */
  display: flex; align-items: center;
}
.surf-src { margin: 0; color: var(--band-muted, var(--ink-soft)); }
@media (max-width: 640px) {
  .surf-table td:last-child { min-width: 0; }
  .surf-live { min-height: 4.2em; }
}

/* WHEN TO GO: the monthly sea-temperature chart. Hand-authored SVG, so it is styled here rather
   than inline, and it inherits the band tokens so it reads correctly on any section background.
   Note the SVG carries no <style> block of its own: an SVG <style> is NOT scoped and leaks into
   the whole page, which has broken sites in this pipeline before. */
.wtg-lead { font-size: var(--step-0); }
.wtg-fig { margin: var(--s4, 1.25rem) 0 .6rem; }
.wtg-chart { width: 100%; height: auto; display: block; }
/* THE WRAPPER ITSELF HAD NO RULE, and this is the miss build/check-styled.mjs exists to catch.
   Every `.wtg-*` child was styled while the bare `.wtg` container that holds them was not, so the
   "across the year" block inherited nothing: no ground of its own, no padding, no separation from
   the section above it. A class name is a promise that something styles it, and unstyled
   structural markup renders as a bare browser default, which looks broken while passing every
   text-reading gate. The same class was reported unstyled on an earlier site in this lineage and
   was never closed, so it arrived here through the clone. */
.wtg {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s4);
  margin: var(--s5) 0;
}
.wtg > :first-child { margin-top: 0; }
.wtg > :last-child  { margin-bottom: 0; }

.wtg-bar { fill: var(--band-link, var(--brand)); }
.wtg-bar.is-peak { fill: var(--band-cta-bg, var(--accent)); }
.wtg-val { font-size: 11px; font-weight: 700; fill: var(--band-fg, var(--ink)); }
.wtg-mon { font-size: 11px; fill: var(--band-muted, var(--ink-soft)); }
.wtg-ax  { font-size: 10px; fill: var(--band-muted, var(--ink-soft)); }
.wtg-grid { stroke: var(--band-line, var(--line)); stroke-width: 1; }
.wtg-fig figcaption { font-size: var(--step--1, .9rem); color: var(--band-muted, var(--ink-soft)); margin-top: .5rem; }
.wtg-src { color: var(--band-muted, var(--ink-soft)); margin: 0; }
.hotel-more { margin-top: var(--s3, 1rem); }
@media (max-width: 640px) { .wtg-val { font-size: 9px; } .wtg-mon { font-size: 9px; } }

/* ---- Stop map: styled as a cartographic figure -------------------------------------------------
   A map's ground is the reference every other colour is judged against, so the map gets its OWN
   small palette rather than the site surface: a pale neutral land, Atlantic blues for water and
   coast, cool greys for the street hierarchy, and the brand cobalt spent on the subject and the
   pins. That is the cartographic convention: everything recedes except the subject.

   Kept deliberately: the six-slot pin series (separated hues, each clearing 4.5:1 under white
   numerals), the scroll mask on the visible box, the legend outside the scroller. */
:root {
  /* Pin series: six separated hues. The closest pair (cobalt and sea teal) sits 49 RGB units
     apart, and every slot clears 4.5:1 under a white numeral. */
  --map-1: #0F4C81;  /* cobalt, the brand */
  --map-2: #AC3C0A;  /* lava orange */
  --map-3: #845600;  /* ochre */
  --map-4: #1A7466;  /* sea teal */
  --map-5: #3F6B2A;  /* laurel green */
  --map-6: #6A3F78;  /* plum */

  /* The map's own ground palette, cool-neutral so water and green read true on it. */
  --mp-land:    #F6F7F3;
  --mp-water:   #B5D3E8;
  --mp-coast:   #4F84AC;
  --mp-green:   #D5E3CF;
  --mp-grid:    #E6ECF0;
  --mp-edge:    #D5DEE6;
  --mp-road:    #A9B4BE;
  --mp-road-mj: #7E8A96;
  --mp-trail:   #BDAA84;
  --mp-ink:     #3B4A58;
}

/* ---- The figure itself ---- */
.stp-map { width:100%; height:auto; display:block; }
.stp-frame { fill:var(--mp-land); stroke:var(--mp-edge); stroke-width:1.25; }
.stp-grid line { stroke:var(--mp-grid); stroke-width:1; }
.stp-leader line { stroke:var(--mp-ink); stroke-width:1; opacity:.45; stroke-dasharray:2 2; }

/* ---- Basemap, real OSM geometry. Weight order runs lightest to heaviest so the eye lands on the
   tram line last and stays there: water, green, trails, lanes, streets, coast, funicular. ---- */
.bm-water path { fill:var(--mp-water); stroke:none; }
.bm-green path { fill:var(--mp-green); stroke:none; }
.bm-trail path { fill:none; stroke:var(--mp-trail); stroke-width:.85; opacity:.55; stroke-dasharray:3.5 3; stroke-linecap:round; }
.bm-lane path { fill:none; stroke:var(--mp-road); stroke-width:.9; opacity:.55; }
.bm-road-minor path { fill:none; stroke:var(--mp-road); stroke-width:1.3; opacity:.85; }
.bm-road-major path { fill:none; stroke:var(--mp-road-mj); stroke-width:2.2; opacity:.95; stroke-linecap:round; }
.bm-coast path { fill:none; stroke:var(--mp-coast); stroke-width:1.6; stroke-linecap:round; stroke-linejoin:round; }
/* Linear water (rivers, canals). Drawn as a stroked line, not a fill, because a river is a way
   and not a polygon. Slightly narrower than the shoreline so the lake still reads as the dominant
   edge, and in the water colour so the eye groups it with the lake rather than with the roads. */
.bm-river path { fill:none; stroke:var(--mp-water); stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }
/* The funicular is the subject. A pale casing under it is the standard way to lift a route line
   off a busy street network without thickening it. */
.bm-funicular path { fill:none; stroke:var(--map-1); stroke-width:3.2; stroke-linecap:round; stroke-linejoin:round;
  filter: drop-shadow(0 0 1.6px rgba(255,255,255,.95)); }
.bm-fun-join { stroke:var(--map-1); stroke-width:2.4; stroke-dasharray:4 3.5; opacity:.7; stroke-linecap:round; }
/* The national park is a landmark boundary, not a road or a filled area: no fill, a dashed line
   so it reads as an outline, in the brand cobalt so it stands apart from the grey road hierarchy
   and the tan trail dashes. */
.bm-park path { fill:none; stroke:var(--brand); stroke-width:1.5; stroke-dasharray:6 3; opacity:.8; stroke-linejoin:round; }

/* ---- Pins and labels ---- */
.stp-lbl { font-size:12.5px; fill:var(--mp-ink); font-weight:650; letter-spacing:.005em;
  paint-order:stroke fill; stroke:var(--mp-land); stroke-width:2.2px; stroke-linejoin:round; }
.stp-lbl-home { font-size:14.5px; fill:var(--ink); font-weight:800; letter-spacing:-.005em;
  paint-order:stroke fill; stroke:var(--mp-land); stroke-width:2.8px; stroke-linejoin:round; }
/* Town names are backdrop, not the point of the plate (pin labels own that, above): smaller,
   lighter, no letter-spacing bump. A city still gets a bolder weight so the two ranks read apart
   even though both sit at the same reduced opacity below (a town carries no group of its own, so
   it never sits in the is-on/not(is-on) filter state, only in this permanently-quiet one). */
.bm-town { font-size:9.5px; fill:var(--mp-ink); font-weight:500;
  paint-order:stroke fill; stroke:var(--mp-land); stroke-width:1.8px; stroke-linejoin:round; }
.bm-town-city { font-weight:750; }
.stp-num { font-size:10px; font-weight:700; fill:#fff; paint-order:fill; }
.stp-dot { stroke:var(--mp-land); stroke-width:1.75; }
/* The true coordinate, drawn only when the numbered badge has been nudged off it so a crowded
   neighbour stays readable: a small solid ink dot, independent of group colour. Radius (2.4px)
   is set inline in the markup, not here. */
.stp-true { fill:var(--ink); }
/* THE FILTER MUST MOVE THE MAP. The picker JS has always toggled `is-on` on every pin, but no
   rule ever styled it, so clicking a group narrowed the card list while the plate stayed pixel
   for pixel identical. Putting the group colour inside the filter chips made that worse: the
   chips then PROMISED they drive the map. Dim, never hide: the points stay on the plate so the
   spatial picture is preserved and the block still works with scripting off. */
.stp-pin .stp-dot, .stp-pin .stp-num, .stp-labels text { transition: opacity .16s ease; }
.stp-pin:not(.is-on) .stp-dot { opacity:.16; }
.stp-pin:not(.is-on) .stp-num { opacity:.2; }
/* Broadened from `text[data-group]:not(.is-on)`: a town name never carries a group or an is-on
   class (it is not part of any filter), so the narrower rule left it at full strength beside
   pins the filter had just faded, which reads as more important than the thing the filter is
   pointing at. The filter dims, never hides, so this fades it instead of leaving it alone. */
.stp-labels text:not(.is-on) { opacity:.22; }
/* Except a town name: it is never "filtered out" (it has no group to lose), so leaving it at the
   same .22 as a de-selected pin would make it functionally unreadable rather than muted. Equal
   specificity to the rule above, declared after it, so these two win for these two classes only;
   every other `.stp-labels text` still dims fully with its pin. */
.stp-labels text.bm-town { opacity:.7; }
.stp-labels text.bm-town-city { opacity:.85; }
/* A label the scroller's edge would bisect is HIDDEN, not clipped. Specificity is deliberate:
   `.stp-map` in front makes this (0,3,1) so it beats the dimming rules above at (0,2,1). */
.stp-map .stp-labels text.is-cut { opacity:0; }
@media (prefers-reduced-motion: reduce) {
  .stp-pin .stp-dot, .stp-pin .stp-num, .stp-labels text { transition:none; }
}
.stp-pin { cursor:pointer; }
.stp-pin .stp-dot { transition: r .14s ease, stroke-width .14s ease; }
.stp-pin:hover .stp-dot, .stp-pin:focus-visible .stp-dot { stroke:var(--ink); stroke-width:2.5; }
.stp-pin:focus-visible { outline:3px solid var(--focus); outline-offset:2px; }
@media (prefers-reduced-motion: reduce) { .stp-pin .stp-dot { transition:none; } }
.stp-home circle:first-child { fill:var(--mp-land); stroke:var(--map-1); stroke-width:2; }
.stp-home-dot { fill:var(--map-1); }

.stp-g0 .stp-dot { fill:var(--map-1); } .stp-legend .stp-g0 .stp-key { background:var(--map-1); }
.stp-g1 .stp-dot { fill:var(--map-2); } .stp-legend .stp-g1 .stp-key { background:var(--map-2); }
.stp-g2 .stp-dot { fill:var(--map-3); } .stp-legend .stp-g2 .stp-key { background:var(--map-3); }
.stp-g3 .stp-dot { fill:var(--map-4); } .stp-legend .stp-g3 .stp-key { background:var(--map-4); }
.stp-g4 .stp-dot { fill:var(--map-5); } .stp-legend .stp-g4 .stp-key { background:var(--map-5); }
.stp-g5 .stp-dot { fill:var(--map-6); } .stp-legend .stp-g5 .stp-key { background:var(--map-6); }

/* ---- Dead pins: no page to link to (both airports, the ferry port, the cruise port, two
   marinas). It must still read as a real, deliberate location marker: same dot, same numeral,
   same size as every other pin. It never fades via opacity (that already means "filtered out"
   on this plate, i.e. temporary and wrong here) and it never turns red or grey (that reads as an
   error or a disabled control, and this is neither): it is neutral map ink instead of a group
   hue, and it drops the pointer cursor and the hover highlight because there is nothing to
   activate. Declared after the group-colour block above on purpose: equal specificity, so this
   overrides `.stp-gN .stp-dot` regardless of which group the dead pin belongs to. */
.stp-pin-dead .stp-dot { fill:var(--mp-ink); }
.stp-pin-dead { cursor:default; }
.stp-pin-dead:hover .stp-dot { stroke:var(--mp-land); stroke-width:1.75; }

/* ---- Container: the map reads as a card, like every other block on the page ---- */
.stp-wrap { position:relative; margin-top:var(--s3); }
.ix .ix-map, .ix-map { border-radius:var(--radius); background:var(--mp-land);
  box-shadow:0 1px 2px rgb(var(--shade-rgb) / .05), 0 8px 24px rgb(var(--shade-rgb) / .07); }

/* ---- Legend: chips, not loose dots ---- */
.stp-legend { list-style:none; margin:1.6rem 0 0; padding:0; display:flex; flex-wrap:wrap;
  gap:.45rem .5rem; align-items:center; position:relative; }
.stp-legend li { display:inline-flex; align-items:center; gap:.42rem; font-size:var(--step--2, .72rem);
  color:var(--ink-soft); font-weight:500; line-height:1.3; }
/* The group colour now lives INSIDE the filter chip, so one row both explains and filters. The
   legend used to repeat the same four words 60px above an identical pill row. */
.stp-chip .stp-key { width:10px; height:10px; border-radius:50%; display:inline-block; flex:0 0 auto;
  margin-right:.45rem; box-shadow:0 0 0 1.5px rgba(255,255,255,.6) inset; }
.stp-chip { display:inline-flex; align-items:center; }
.stp-g0 .stp-key { background:var(--map-1); } .stp-g1 .stp-key { background:var(--map-2); }
.stp-g2 .stp-key { background:var(--map-3); } .stp-g3 .stp-key { background:var(--map-4); }
.stp-g4 .stp-key { background:var(--map-5); } .stp-g5 .stp-key { background:var(--map-6); }
/* In-plate furniture: a scale bar that scales with the SVG, unlike a fixed-width HTML bar. */
.stp-bar-line { stroke:var(--mp-ink); stroke-width:1.6; opacity:.75; }
.stp-north-head { fill:var(--mp-ink); opacity:.75; }
.stp-furn-lbl { font-weight:600; opacity:.85; stroke-width:0; }
/* Furniture rows read as notes, not as another category chip. */
.stp-legend li.stp-attrib { opacity:.75; }
/* Specificity matters: `.stp-legend li { display:inline-flex }` is (0,1,1) and outranked a bare
   `.stp-swipe { display:none }` at (0,1,0), so the hint rendered on every desktop width and told
   readers to swipe a map that does not scroll. Both rules are qualified to match. */
.stp-legend li.stp-swipe { display:none; }

/* ---- Narrow screens ---- */
@media (max-width: 620px) {
  /* A mask on the SCROLL BOX, anchored to the visible box. An ::after overlay was wrong: it is a
     child of the scroller, so it travelled with the content and landed as a bleach column down
     the middle of the map at the offset the page actually loads in. */
  .ix-map {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 22px, #000 calc(100% - 22px), transparent 100%);
  }
  .stp-lbl { font-size:11.5px; }
  .stp-legend li.stp-swipe { display:inline-flex; }
  .stp-legend { gap:.4rem .45rem; }
}

/* ---- Dark theme: the map keeps its own ground, darkened, so it never becomes a glowing white
   slab on a dark page and the natural tones stay believable. ---- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --mp-land:    #18222C;
    --mp-water:   #173A55;
    --mp-coast:   #4B86B4;
    --mp-green:   #22332A;
    --mp-grid:    #222D38;
    --mp-edge:    #2A3642;
    --mp-road:    #566370;
    --mp-road-mj: #7A8896;
    --mp-trail:   #7C7150;
    --mp-ink:     #CCD8E3;
  }
  :root:not([data-theme="light"]) .stp-lbl-home { fill:var(--signature); }
  :root:not([data-theme="light"]) .stp-home circle:first-child { stroke:var(--signature); }
  :root:not([data-theme="light"]) .stp-home-dot { fill:var(--signature); }
  :root:not([data-theme="light"]) .stp-pin:hover .stp-dot { stroke:var(--signature); }
}
:root[data-theme="dark"] {
  --mp-land:    #18222C;
  --mp-water:   #173A55;
  --mp-coast:   #4B86B4;
  --mp-green:   #22332A;
  --mp-grid:    #222D38;
  --mp-edge:    #2A3642;
  --mp-road:    #566370;
  --mp-road-mj: #7A8896;
  --mp-trail:   #7C7150;
  --mp-ink:     #CCD8E3;
}
:root[data-theme="dark"] .stp-lbl-home { fill:var(--signature); }
:root[data-theme="dark"] .stp-home circle:first-child { stroke:var(--signature); }
:root[data-theme="dark"] .stp-home-dot { fill:var(--signature); }
:root[data-theme="dark"] .stp-pin:hover .stp-dot { stroke:var(--signature); }


/* ============================================================
   UX PASS (Oleg, 2026-09-16). Three defects, each MEASURED in a real browser at 390 and 1280,
   not eyeballed. The measurements are recorded because "looks fine" is how each of these shipped.
   ============================================================ */

/* 1. TABLES THAT OVERFLOW WITH NO SCROLL.
      `.table-wrap` is emitted by compile.mjs three times and had NO CSS RULE AT ALL, so at 390px
      the season table measured 445px wide inside a 358px container with overflow:visible. The
      sibling class `.table-scroll` had the rule; this one was never given it. Same treatment. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: var(--s3) 0; }
.table-wrap > table { min-width: 640px; }
/* A scrollable table must LOOK scrollable or nobody scrolls it. */
.table-wrap, .table-scroll { position: relative; scrollbar-width: thin; }
.table-wrap::-webkit-scrollbar, .table-scroll::-webkit-scrollbar { height: 8px; }
.table-wrap::-webkit-scrollbar-thumb, .table-scroll::-webkit-scrollbar-thumb {
  background: var(--line); border-radius: 4px; }

/* Data tables: tinted header, zebra rows, rounded container, numerals aligned. */
.datatable { border-collapse: separate; border-spacing: 0; width: 100%; }
.datatable caption { text-align: left; color: var(--ink-soft); font-size: var(--step--1);
  margin-bottom: var(--s2); line-height: 1.5; }
.datatable thead th { background: var(--paper-2); color: var(--ink); font-weight: 700;
  text-align: left; padding: .7rem .8rem; border-bottom: 2px solid var(--line);
  position: sticky; top: 0; z-index: 1; }
.datatable tbody th[scope="row"] { font-weight: 700; color: var(--brand); white-space: nowrap; }
.datatable tbody td, .datatable tbody th { padding: .65rem .8rem; border-bottom: 1px solid var(--line);
  vertical-align: top; }
.datatable tbody tr:nth-child(even) { background: color-mix(in srgb, var(--paper-2) 45%, transparent); }
.datatable tbody tr:last-child td, .datatable tbody tr:last-child th { border-bottom: 0; }
/* Figures read as a column when they share a width and sit on the same baseline. */
.datatable td:not(:last-child) { font-variant-numeric: tabular-nums; white-space: nowrap; }
.datatable td:last-child { min-width: 22ch; white-space: normal; color: var(--ink-soft); }

/* 2. THE TWELVE CATEGORY BLOCKS RAN TOGETHER.
      `.cat-block` carried only `margin-top`, no border, background or padding, so twelve stacked
      groups read as one undifferentiated column of cards. Each is now a defined panel: the cards
      inside stay white (--surface) against a tinted ground, which is what separates them. */
.cat-block { margin-top: var(--s5); padding: var(--s4); border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--paper-2); scroll-margin-top: 5rem; }
.cat-block > h3 { margin: 0 0 var(--s1); padding-left: .65rem; border-left: 4px solid var(--brand);
  line-height: 1.25; }
.cat-block > p.small { margin: 0 0 var(--s3); color: var(--ink-soft); max-width: 68ch; }
.cat-block .cards-3 .rcard { background: var(--surface); }
@media (max-width: 640px) { .cat-block { padding: var(--s3); } }

/* 3. PICKER ROWS DID NOT ALIGN WITH EACH OTHER.
      The grid was `minmax(0,1.5fr) minmax(0,1.4fr) auto auto`, and an `auto` track sizes to ITS
      OWN row's content, so every row computed different column widths. Measured at 1280px:
      `facts` began at 489px on row 0 but 494px on rows 1 and 2; `price` at 847 vs 856. Fixed
      tracks make every row share one set of columns. */
.decision-picker .dp-row { grid-template-columns:
  minmax(0, 1.5fr) minmax(0, 1.4fr) minmax(8.5rem, 9.5rem) minmax(9rem, 10rem); }
.decision-picker .dp-row-price { text-align: right; font-variant-numeric: tabular-nums; }
.decision-picker .dp-row-cta { text-align: center; }

/* Mobile: at 390px `price` was stranded alone on a third row at the far left while `cta` sat up
   beside the title. Name and facts take the full width; price and cta share the closing row. */
@media (max-width: 640px) {
  /*  here sized the CTA to its own row's label, so the column wandered 9px between rows
     (measured: cta at left 216 on row 0, 207 on rows 1 and 2). A fixed track pins it. */
  /* minmax(0,1fr), NOT 1fr. `1fr` means `minmax(auto, 1fr)`, so a track grows past its share when
     its content demands it: the first row's price carries a 5-digit review count and pushed
     column 1 from 144px to 152px, so that one row sat 8px out of line with every row below it.
     A 0 floor lets the track take exactly its share whatever is in it. */
  .decision-picker .dp-row { grid-template-columns: minmax(0, 1fr) 9rem; row-gap: .45rem; align-items: center; }
  .decision-picker .dp-row-name  { grid-column: 1 / -1; grid-row: 1; }
  .decision-picker .dp-row-facts { grid-column: 1 / -1; grid-row: 2; }
  .decision-picker .dp-row-price { grid-column: 1; grid-row: 3; text-align: left; }
  .decision-picker .dp-row-cta   { grid-column: 2; grid-row: 3; }
}


/* ============================================================
   UX PASS 2 (Oleg, 2026-09-16, second report). Both defects MEASURED on the live page.
   ============================================================ */

/* 1. "TOUR CARDS ARE STUCK TOGETHER", and they literally were.
      Inside a .cat-block the lead .tour-list sits directly above the .cards-3 grid. .tour-list
      carries `gap` for its own children, but it has exactly ONE child, and neither it nor
      .cards-3 carried any margin between them. Measured on the live page: the gap between the
      lead card's bottom edge and the first row of the grid was **0px**, so two bordered white
      cards touched. The grid's own 27px internal gaps looked fine in isolation, which is why
      measuring the grid never surfaced this: the defect was BETWEEN two siblings, not inside one. */
.cat-block > .tour-list + .cards-3,
.cat-block > .cards-3 + .tour-list { margin-top: var(--s4); }
.cat-block > .tour-list { margin-bottom: 0; }
/* Give the block a real vertical rhythm while we are here: the heading sat 6px off its blurb. */
.cat-block > h3 { margin: 0 0 var(--s2); }
.cat-block > p.small { margin: 0 0 var(--s4); }
.cat-block .cards-3 { gap: var(--s4); }
/* A card should lift slightly on hover so a grid of them reads as separate objects. */
.cat-block .cards-3 .rcard { transition: transform .15s ease, box-shadow .15s ease; }
@media (hover: hover) {
  .cat-block .cards-3 .rcard:hover { transform: translateY(-2px);
    box-shadow: 0 4px 14px rgb(var(--shade-rgb) / .10); }
}
@media (prefers-reduced-motion: reduce) {
  .cat-block .cards-3 .rcard { transition: none; }
  .cat-block .cards-3 .rcard:hover { transform: none; }
}

/* 2. THE COMPARISON TABLE WAS THE UNSTYLED ONE.
      The kinds heading sits above the category blocks, and the actual comparison table renders
      further down as `.keyfacts.compare`, NOT `.datatable`, so the data-table treatment added in
      the first pass never reached it. Measured on the donor: 52 rows, 5 columns, and an even-row
      background of rgba(0,0,0,0), meaning no zebra and no sticky header. Fifty rows unstriped is
      a wall, and a wall reads as unstyled however valid the markup is. This site renders 45
      products into the same table, so the treatment below is load-bearing here too.
      A class name is a promise: build/check-styled.mjs asserts every structural class the pages
      emit has a CSS rule, because `.table-wrap`/`.datatable` once shipped with neither. */
.keyfacts.compare thead th { position: sticky; top: 0; z-index: 2; background: var(--paper-2);
  border-bottom: 2px solid var(--line); box-shadow: 0 1px 0 var(--line); }
.keyfacts.compare tbody tr:nth-child(even) > * {
  background: color-mix(in srgb, var(--paper-2) 42%, transparent); }
.keyfacts.compare tbody tr:hover > * {
  background: color-mix(in srgb, var(--brand) 8%, transparent); }
/* The row header carries its own background, so it must win over the zebra to stay readable. */
.keyfacts.compare tbody tr:nth-child(even) th[scope="row"] {
  background: color-mix(in srgb, var(--paper-2) 42%, var(--surface)); }
/* Figures line up as a column only with tabular numerals. */
.keyfacts.compare td { font-variant-numeric: tabular-nums; }
.keyfacts.compare tbody tr > * { border-bottom: 1px solid var(--line); }
.keyfacts.compare tbody tr:last-child > * { border-bottom: 0; }


/* ============================================================
   THE "EIGHT KINDS" INDEX, and the bug class these rules exist to survive.
   On the code donor the equivalent block shipped as 36 bare spans, and the ROOT CAUSE was not
   styling. compile.mjs wraps each entry in <a class="cat-card">, but there the category URLs
   pointed at a separate money page's #slug while those anchor ids were rendered on the HOMEPAGE.
   Every link was dead, the dead-link pass correctly unwrapped them, and with the wrapper gone
   every `.cat-card .cc-*` rule stopped matching: text on a background, no padding, no card.
   TWO REASONS IT CANNOT RECUR HERE, and both are worth stating because the failure was invisible
   to every text-reading gate. First, this site has ONE money page and it IS the homepage, so a
   category anchor and its target are on the same document. Second, build/compile.mjs now strips
   the #fragment before testing whether a href exists, which is the fix the donor lacked: without
   it a perfectly good /page/#anchor reads as dead and gets unwrapped.
   The rules below make the restored card earn the word "Compared" in the heading above it.
   ============================================================ */

.cat-grid { gap: var(--s4); align-items: stretch; }
.cat-card { position: relative; gap: .45rem; padding: var(--s4);
  box-shadow: 0 1px 2px rgb(var(--shade-rgb) / .05); }
.cat-card:hover { box-shadow: 0 6px 18px rgb(var(--shade-rgb) / .10); border-color: var(--line); }

/* The name is the thing being compared, so it leads and it carries the brand colour. */
.cat-card .cc-name { color: var(--brand); font-size: var(--step-1); letter-spacing: -.01em;
  padding-right: 1.25rem; }

/* CLAMP THE BLURB. Each entry carries roughly sixty words, so twelve of them stacked is the wall
   of prose that made this read as unstyled even once the card came back. Three lines is enough to
   tell the categories apart, and the full text still appears under the matching heading below. */
.cat-card .cc-meta { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden; line-height: 1.5; }

/* The option count is DATA, not a sentence: give it a chip and push it to the card's foot so the
   twelve counts line up down the column and can actually be compared at a glance. */
.cat-card .cc-who { margin-top: auto; padding-top: .6rem; align-self: flex-start;
  font-weight: 700; color: var(--brand); background: var(--paper-2);
  border-radius: 999px; padding: .2rem .7rem; font-size: var(--step--1); }

/* A link should look like one. */
.cat-card::after { content: "\2192"; position: absolute; top: var(--s4); right: var(--s4);
  color: var(--brand); opacity: .55; font-size: var(--step-0); transition: transform .15s ease; }
.cat-card:hover::after { transform: translateX(3px); opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .cat-card, .cat-card::after { transition: none; }
  .cat-card:hover { transform: none; }
  .cat-card:hover::after { transform: none; }
}


/* ---- PORTED LAYOUT FIXES (2026-09-14) ---- */
/* Each rule below fixes a defect that shipped live and passed every text gate.
   build/qa-layout.py is the gate that now catches this whole class. */

/* Chips carry SENTENCES on these sites, not one-word labels, so they must wrap. Left at nowrap a
   chip rendered 609px wide inside a 332px card and its tail was cut off. */
.pc-chip { white-space: normal; overflow-wrap: anywhere; max-width: 100%; }

/* A link-card whose inner layout is two columns (name left, note right) must stack when the CARD
   is narrow, which is not the same question as when the WINDOW is narrow. Keyed to the viewport
   it never fired inside a 275px card at a 1440px window, and titles wrapped one word per line. */
.section-links li { container-type: inline-size; }
@container (max-width: 360px) {
  .section-links a { grid-template-columns: 1fr; }
  .section-links .sl-note { text-align: left; }
}

/* .btn-ghost is styled for a light ground. On a dark hero band its ink is the same colour as the
   overlay behind it and the button is invisible. */
.hero-full .btn-ghost, .band-dark .btn-ghost, [data-band="dark"] .btn-ghost {
  color: #fff; border-color: rgba(255,255,255,.55); background: rgba(255,255,255,.06);
}
.hero-full .btn-ghost:hover, .band-dark .btn-ghost:hover, [data-band="dark"] .btn-ghost:hover {
  border-color: #fff; background: rgba(255,255,255,.14);
}

/* Article tables are styled by `.prose table`. Nothing on a homepage is inside .prose, so a
   homepage table shipped with no border, no radius and a transparent header. */
/* Scoped to `main table` on purpose, NOT to a wrapper class. The first attempt keyed off
   `.table-scroll`, and the donor wraps its homepage tables in `.table-wrap` instead, so two
   unstyled tables sailed straight through. An element-only selector cannot miss a wrapper it has
   not heard of, and it is weak enough (0,0,2) that every existing class rule still wins:
   `.prose table` (0,1,1) keeps styling article tables and `.keyfacts` (0,1,0) keeps its own. */
main table {
  width: 100%; border-collapse: collapse; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}
main table > thead > tr > th, main table > tr > th {
  background: var(--paper-2); color: var(--ink); text-align: left;
}

/* `.keyfacts th { width:42% }` is written for a TWO-column key/value table. Applied to a wide
   data table it starves the last column and one cell wraps one word per line. */
.keyfacts[class*="-table"] th, .keyfacts[class*="-table"] td { width: auto; vertical-align: top; }

/* Picker rows: each row is its own grid, so `auto` tracks made every row compute different
   column widths and nothing lined up. A product with no duration renders NO facts cell, which
   shifted its price and button into the wrong columns entirely. Pin each cell to its column. */
@media (min-width: 861px) {
  .decision-picker .dp-row { grid-template-columns: minmax(0,1.5fr) minmax(0,.9fr) 16rem 15.5rem; }
}
.decision-picker .dp-row-name  { grid-column: 1; }
.decision-picker .dp-row-facts { grid-column: 2; overflow-wrap: anywhere; }
.decision-picker .dp-row-price { grid-column: 3; justify-self: end; text-align: right;
  white-space: normal; overflow-wrap: anywhere; }
.decision-picker .dp-row-cta   { grid-column: 4; justify-self: stretch; text-align: center; }
@media (max-width: 860px) {
  /* Explicit ROWS as well as columns: two cells sharing a column with no row named let grid
     auto-placement drop them into the SAME cell, printing one on top of the other. */
  .decision-picker .dp-row { grid-template-columns: minmax(0,1fr) fit-content(11rem); grid-template-rows: auto auto auto; row-gap: .3rem; }
  .decision-picker .dp-row-name  { grid-column: 1 / -1; grid-row: 1; }
  .decision-picker .dp-row-facts { grid-column: 1; grid-row: 2; }
  .decision-picker .dp-row-price { grid-column: 1; grid-row: 3; justify-self: start; text-align: left; }
  .decision-picker .dp-row-cta   { grid-column: 2; grid-row: 2 / span 2; justify-self: end; align-self: center; }
}

/* ---- SERVICE CARD ({{ service_card:<key> }}, a Travelpayouts service the page is ABOUT) ----
   Distinct from .tp-card, which is a TOUR-PICKS product card. Two different things, two different
   prefixes, on purpose: sharing the `tp` prefix is how one stylesheet rule ends up silently
   restyling the other. This one is an aside inside prose, so it reads as a recommendation the
   article is making rather than as an ad break: tinted ground, brand rule down the leading edge,
   no image, one button. */
.service-card {
  margin: var(--s5) 0;
  padding: var(--s4);
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-left: 4px solid var(--brand);
  border-radius: var(--radius);
}
.service-card .sc-kicker {
  margin: 0 0 .35rem;
  font-size: var(--step--1);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.service-card h3 { margin: 0 0 .5rem; font-size: var(--step-1); }
.service-card p  { margin: 0 0 var(--s3); }
.service-card .btn { margin: 0; }
@media (max-width: 560px) {
  .service-card { padding: var(--s3); }
  .service-card .btn { display: block; text-align: center; }
}


/* The product one-liner, moved out of the chip row (a chip is a label, not a sentence).
   Clamped so cards in a row stay level regardless of how long an operator's blurb runs. */
.tour-picks .tp-note{
  margin:.35rem 0 0; font-size:.82rem; line-height:1.45; color:var(--ink-soft);
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; line-clamp:3;
  overflow:hidden;
}

/* ---- LAYOUT RULES, CONFIRMED AT THE PALETTE PASS ----
   Each rule here closes a gap found while checking the learned layout rules against this file. */

/* Picker rows share ONE set of column tracks through subgrid, so a row with no facts cell or a
   longer price can never compute columns of its own. This block sits AFTER every base .dp-row
   rule on purpose: a later grid-template-columns of equal specificity would replace `subgrid`
   without any error. Cells stay pinned to their columns by class (rules above). Where subgrid
   is missing, the fixed tracks above still apply. */
@supports (grid-template-columns: subgrid) {
  .decision-picker .dp-list { display:grid; column-gap:.9rem;
    grid-template-columns: minmax(0,1.5fr) minmax(0,.9fr) fit-content(16rem) fit-content(15.5rem); }
  .decision-picker .dp-list > .dp-row { grid-column: 1 / -1; grid-template-columns: subgrid; }
  @media (max-width: 860px) {
    .decision-picker .dp-list { grid-template-columns: minmax(0,1fr) fit-content(11rem); }
  }
}
/* On a narrow row a long button label wraps inside its bounded track instead of widening it. */
@media (max-width: 860px) {
  .decision-picker .dp-row-cta { white-space: normal; text-align: center; }
}

/* Card inner layout answers to the CARD, not the window: a tour row inside a narrow column
   stacks even when the viewport is wide. */
.tour-list { container-type: inline-size; }
@container (max-width: 640px) {
  .tour-row, .tour-row.has-media { grid-template-columns: 1fr; }
  .tour-row .tr-cta { align-items: stretch; text-align: left; }
  .tour-row .tr-cta .btn { width: 100%; }
  .tour-row .tr-price small { margin-left: 0; max-width: none; }
}
/* A full-width card button never needs nowrap, and the longest label would overflow a
   narrow card with it. */
.tour-picks .tp-body .btn { white-space: normal; text-wrap: balance; }

/* Two card groups in a row always get explicit air between them. Zero specificity, so every
   component rule above still wins. */
:where(.cards-3, .tour-list, .tp-grid, .hotel-grid, .cat-grid, .testimonials-grid, .photo-collage, .reviews)
  + :where(.cards-3, .tour-list, .tp-grid, .hotel-grid, .cat-grid, .testimonials-grid, .photo-collage, .reviews) { margin-top: var(--s4); }

/* Hero lede on a phone stays at body size so the primary button holds above the fold at 390px. */
@media (max-width: 640px) { .hero-full .lede, .hero-full .hero-lede { font-size: var(--step-0); } }


/* ============================================================
   MARKUP THE COMPILER EMITS THAT HAD NO RULE BEHIND IT.
   Every class in this block was live in the built pages and rendered as a bare browser default.
   Tokens only, so each rule follows the band it sits in (a band redefines the tokens as a set).
   Widths were MEASURED at 390, 768 and 1280 in a headless browser against a fixture copy of the
   built pages; the figures are in research/L3e-report.md.
   ============================================================ */

/* ---- AREA CHIPS. A chip is a short AREA label. It wraps honestly and never clips: no nowrap,
   no ellipsis, and the radius is a soft rectangle so a two-line chip still reads as one shape. */
.rc-chips { display:flex; flex-wrap:wrap; gap:.35rem; margin:0; padding:0; min-width:0; list-style:none; }
.chip { display:inline-block; box-sizing:border-box; max-width:100%; min-width:0;
  font-size:.72rem; font-weight:700; letter-spacing:.02em; line-height:1.3;
  padding:.22rem .6rem; border-radius:.85rem; background:var(--paper-2); color:var(--ink-soft);
  border:1px solid var(--line); white-space:normal; overflow-wrap:anywhere; }

/* ---- THE PRODUCT ONE-LINER. A paragraph, clamped to three lines so cards in a row stay level
   however long one product's line runs. */
.rc-line { margin:0; line-height:1.45; overflow-wrap:anywhere;
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:3; line-clamp:3; overflow:hidden; }

/* ---- UNFINISHED COPY. Loud on purpose: a placeholder that blends in ships. Hazard stripes in the
   two CTA oranges with the label colour that is measured against both, so it stays legible on
   every band, the dark one included, because it carries its own ground. */
mark.placeholder { display:inline; padding:.12em .4em; border-radius:3px; font-weight:800; font-style:normal;
  color:var(--accent-ink);
  background:repeating-linear-gradient(135deg, var(--accent) 0 12px, var(--accent-deep) 12px 24px);
  outline:2px dashed var(--accent-warm); outline-offset:1px;
  -webkit-box-decoration-break:clone; box-decoration-break:clone;
  white-space:normal; overflow-wrap:anywhere; text-shadow:none; letter-spacing:0; text-transform:none; }

/* ---- COMPACT PRODUCT LIST (.plist): name, one line of facts, price and rating, one button.
   Table-like alignment comes from a grid on the LIST with subgrid rows (block further down).
   This base rule is the fallback: fixed, bounded tracks, so rows still line up without subgrid.
   THREE tracks, not four. The money page sets these lists in an 800px column, and four tracks
   there measured the name at 179px of 798 (22%), so most names broke onto two lines at random.
   Name over facts in one flexible track, then price, then the button: every row is the same
   two lines tall and the name holds the widest track.
   Every cell is pinned to its column AND its row by class, so a product with no facts line or no
   price can never slide a later cell into the wrong column. */
ul.plist, ol.plist { list-style:none; margin:var(--s3) 0 0; padding:0; max-width:none;
  border:1px solid var(--line); border-radius:var(--radius-sm); overflow:hidden; background:var(--surface); }
.plist > .plist-row { display:grid; margin:0; padding:.62rem .85rem; align-items:center;
  grid-template-columns:minmax(0,1fr) minmax(0,14rem) 10.5rem; grid-template-rows:auto auto;
  column-gap:.9rem; row-gap:.15rem; border-top:1px solid var(--line);
  font-size:var(--step--1); line-height:1.4; color:var(--ink); }
.plist > .plist-row:first-child { border-top:0; }
.plist > .plist-row:nth-child(even) { background:var(--surface-2); }
.plist-name  { grid-column:1; grid-row:1; min-width:0; font-weight:700; color:var(--ink);
  text-decoration:none; overflow-wrap:anywhere; }
.plist .plist-row a.plist-name { color:var(--ink); }
.plist .plist-row a.plist-name:hover { color:var(--brand); text-decoration:underline; }
.plist-facts { grid-column:1; grid-row:2; min-width:0; overflow-wrap:anywhere; }
.plist-proof { grid-column:2; grid-row:1 / span 2; align-self:center; min-width:0; justify-self:end; text-align:right;
  display:flex; flex-wrap:wrap; justify-content:flex-end; align-items:baseline; gap:.1rem .55rem;
  font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.plist-proof > * { min-width:0; }
.plist-proof .gold { color:var(--gold); font-weight:700; }
.plist-cta { grid-column:3; grid-row:1 / span 2; align-self:center; justify-self:stretch; box-sizing:border-box; min-width:0;
  display:inline-flex; align-items:center; justify-content:center; text-align:center;
  min-height:2.25rem; padding:.3rem .8rem; border:1px solid var(--brand); border-radius:999px;
  font-weight:700; line-height:1.2; text-decoration:none; white-space:normal; }
.plist .plist-row a.plist-cta { color:var(--brand); }
.plist .plist-row a.plist-cta:hover { background:var(--brand); color:var(--surface); }
.plist-name:focus-visible, .plist-cta:focus-visible { outline:3px solid var(--focus); outline-offset:2px; }
/* Tablet: the text cells stack in one column (always well over 60% of the row) and the button
   keeps a bounded column of its own, centred on the row. */
@media (max-width: 980px) {
  .plist > .plist-row { grid-template-columns:minmax(0,1fr) 10.5rem; grid-template-rows:auto auto auto; }
  .plist-name  { grid-column:1; grid-row:1; }
  .plist-facts { grid-column:1; grid-row:2; }
  .plist-proof { grid-column:1; grid-row:3; justify-self:start; justify-content:flex-start; text-align:left; }
  .plist-cta   { grid-column:2; grid-row:1 / span 3; align-self:center; min-height:44px; }
}
/* Phone: one column. The name owns the full row and the button runs full width beneath. */
@media (max-width: 640px) {
  .plist > .plist-row { grid-template-columns:minmax(0,1fr); grid-template-rows:none; row-gap:.35rem; padding:.8rem .85rem; }
  .plist-proof { grid-row:3; }
  .plist-cta { grid-column:1; grid-row:4; align-self:stretch; width:100%; margin-top:.2rem; }
}
/* Subgrid: every row borrows the LIST's tracks, so the price column is one column down the whole
   list, sized to its widest entry. Declared AFTER the base row rule on purpose: a later
   grid-template-columns of equal specificity would silently replace `subgrid`. */
@supports (grid-template-columns: subgrid) {
  ul.plist, ol.plist { display:grid; column-gap:.9rem;
    grid-template-columns:minmax(0,1fr) fit-content(14rem) fit-content(10.5rem); }
  .plist > .plist-row { grid-column:1 / -1; grid-template-columns:subgrid; }
  @media (max-width: 980px) {
    ul.plist, ol.plist { grid-template-columns:minmax(0,1fr) fit-content(10.5rem); }
    .plist > .plist-row { grid-template-columns:subgrid; }
  }
  @media (max-width: 640px) {
    ul.plist, ol.plist { grid-template-columns:minmax(0,1fr); }
    .plist > .plist-row { grid-template-columns:subgrid; }
  }
}

/* ---- AREA BLOCK: the same panel language as the kind blocks (tinted ground, white list inside,
   brand rule down the heading). */
.area-block { margin-top:var(--s5); padding:var(--s4); border:1px solid var(--line);
  border-radius:var(--radius); background:var(--paper-2); scroll-margin-top:5rem; min-width:0; }
.area-block > h3 { margin:0 0 var(--s2); padding-left:.65rem; border-left:4px solid var(--brand);
  line-height:1.25; overflow-wrap:anywhere; }
.area-block > p { margin:0 0 var(--s3); max-width:68ch; }
.area-block > .plist { margin-top:var(--s3); }
@media (max-width: 640px) { .area-block { padding:var(--s3); } }

/* ---- MONEY-PAGE DATA BLOCK WRAPPER. It only has to give its block air and let a wide child
   scroll: min-width:0 is what stops a 46rem table from widening the page instead. */
.md-block { margin:var(--s4) 0; min-width:0; max-width:100%; }
.md-block > :first-child { margin-top:0; }
.md-block > :last-child { margin-bottom:0; }

/* ---- THE WIDE COMPARISON TABLE. Tinted header and zebra come from .keyfacts.compare above.
   Added here: separate borders so the container can be rounded (a collapsed table ignores
   border-radius), corner cells rounded to match, a sticky first column with an opaque ground so
   scrolled cells pass UNDER the name, and a first column that wraps on a phone so it cannot eat
   the whole viewport. overflow is visible on the table itself: a clipping table becomes the
   sticky column's scroll container and the column then never sticks. */
.table-scroll > table.compare-wide, table.compare-wide { width:100%; min-width:46rem;
  border-collapse:separate; border-spacing:0; overflow:visible; background:var(--surface);
  border:1px solid var(--line); border-radius:var(--radius); font-size:var(--step--1); }
table.compare-wide thead tr:first-child > :first-child { border-top-left-radius:calc(var(--radius) - 1px); }
table.compare-wide thead tr:first-child > :last-child  { border-top-right-radius:calc(var(--radius) - 1px); }
table.compare-wide tbody tr:last-child > :first-child  { border-bottom-left-radius:calc(var(--radius) - 1px); }
table.compare-wide tbody tr:last-child > :last-child   { border-bottom-right-radius:calc(var(--radius) - 1px); }
table.compare-wide thead th:first-child,
table.compare-wide tbody th[scope="row"] { position:sticky; left:0; z-index:1; box-shadow:1px 0 0 var(--line); }
table.compare-wide thead th:first-child { z-index:3; }
/* The shared hover tint is translucent, which would let scrolled figures show through the name. */
table.compare-wide tbody tr:hover > th[scope="row"] { background:color-mix(in srgb, var(--brand) 8%, var(--surface)); }
table.compare-wide td { font-variant-numeric:tabular-nums; }
@media (max-width: 640px) {
  table.compare-wide thead th:first-child,
  table.compare-wide tbody th[scope="row"] { white-space:normal; overflow-wrap:anywhere; min-width:9.5rem; max-width:11rem; }
}

/* ---- WHERE TO STAY: four base groups of three hotels. No price or rating styling exists here
   because the block shows neither. The locality line wraps in full: one hotel sits outside the
   strip its group is named for, and that qualifier is the point of the line. */
.hotel-bases { display:grid; grid-template-columns:minmax(0,1fr); gap:var(--s5); margin-top:var(--s4); }
.hotel-base { min-width:0; scroll-margin-top:5rem; }
.hotel-base + .hotel-base { padding-top:var(--s4); border-top:1px solid var(--line); }
.hb-name { margin:0 0 var(--s3); padding-left:.65rem; border-left:4px solid var(--brand);
  font-size:var(--step-1); line-height:1.25; overflow-wrap:anywhere; }
.hotel-card h4 { margin:0; font-size:var(--step-0); line-height:1.25; overflow-wrap:anywhere; }
.hotel-base .hotel-card .hc-area { font-weight:600; line-height:1.4; white-space:normal; overflow-wrap:anywhere; }

/* ---- GETTING AROUND: the link list. Both tracks of each link are bounded (a flexible text track
   with a zero floor beside a fixed arrow track), never `1fr auto`. */
ul.ga-links { list-style:none; margin:var(--s4) 0 0; padding:0; display:grid; gap:.5rem .9rem;
  grid-template-columns:repeat(2, minmax(0,1fr)); }
ul.ga-links > li { margin:0; min-width:0; }
ul.ga-links > li > a { display:grid; grid-template-columns:minmax(0,1fr) 1.25rem; align-items:center;
  gap:.6rem; min-height:48px; box-sizing:border-box; padding:.7rem .95rem; background:var(--surface);
  border:1px solid var(--line); border-radius:var(--radius-sm); text-decoration:none;
  font-weight:700; line-height:1.3; overflow-wrap:anywhere; }
ul.ga-links > li > a::after { content:"\2192"; justify-self:end; color:var(--accent-warm); font-weight:700; }
ul.ga-links > li > a:hover { border-color:var(--brand); background:var(--paper-2); }
@media (max-width: 700px) { ul.ga-links { grid-template-columns:minmax(0,1fr); } }

/* ---- PICKER: the fold and the way past it.
   Rows after the opening set are folded on the idle list. They are folded ONLY while the
   "See all options" link is present as a later sibling of the list, so the fold can never exist
   without its way out: drop the link and every row shows again. The link goes to the money page,
   which lists every product in plain server-rendered rows, so nothing depends on script.
   display:none removes a row for everyone alike, sighted or not, and both reach it by the same
   link. A browser without :has() ignores the rule and shows every row, which is the safe side. */
.decision-picker .dp-list:has(~ .dp-all) > .dp-row-fold { display:none; }
.dp-all { margin:var(--s3) 0 0; text-align:center; font-weight:700; }
.dp-all a { display:inline-flex; align-items:center; justify-content:center; min-height:44px;
  padding:.2rem .6rem; text-decoration:underline; text-underline-offset:.18em; overflow-wrap:anywhere; }
/* The script hides the list once a question is answered; the link stays, it is still true. */

/* ---- SMALL BUTTON. Emitted on the picker reset and the map, never defined: it rendered at the
   full 48px CTA size beside the questions. */
.btn-sm { font-size:var(--step--1); padding:.5rem 1rem; min-height:40px; }

/* ---- MAP STOP LIST. Sixty-five of the seventy-one stops carry no link yet, and the row styling
   lived on the ANCHOR only, so an unlinked stop rendered as two bare inline spans run together.
   The row box now belongs to the list item; a linked or unwrapped stop lays its two spans out on
   the same two bounded tracks. */
.section-links.stp-cards > li { position:relative; display:grid; align-items:baseline;
  grid-template-columns:minmax(0,1.3fr) minmax(0,1fr); gap:.35rem 1.2rem; padding:.7rem .95rem;
  background:var(--surface); border:1px solid var(--line); border-radius:var(--radius-sm); }
.section-links.stp-cards > li > a,
.section-links.stp-cards > li > .sl-dead { grid-column:1 / -1; display:grid; align-items:baseline;
  grid-template-columns:minmax(0,1.3fr) minmax(0,1fr); gap:.35rem 1.2rem;
  padding:0; border:0; background:none; border-radius:0; }
.section-links.stp-cards > li > a::after { content:""; position:absolute; inset:0; }
.section-links.stp-cards > li:hover:has(> a) { border-color:var(--brand); background:var(--paper-2); }
.section-links .sl-dead { color:var(--ink); }
.section-links.stp-cards .sl-name, .section-links.stp-cards .sl-note { min-width:0; overflow-wrap:anywhere; }
@media (max-width: 760px) {
  .section-links.stp-cards > li,
  .section-links.stp-cards > li > a,
  .section-links.stp-cards > li > .sl-dead { grid-template-columns:minmax(0,1fr); }
}
.map-close { margin:var(--s4) 0 var(--s3); max-width:68ch; }

/* ---- MARKUP LANE L3d ADDED TO THE COMPILER WHILE THIS BLOCK WAS WRITTEN (read from
   build/compile.mjs, no built page carried it yet, so these rules are audited statically and
   must be measured after the next build). ---- */

/* Price basis. A price that is per buggy, per boat or per group is the one figure on the page
   that can mislead, so the basis is a marked chip beside the number, never plain text. */
.price-basis { display:inline-block; vertical-align:middle; margin-left:.35rem; padding:.1rem .5rem;
  border-radius:999px; background:var(--gold-soft); color:var(--gold-ink); font-family:var(--body);
  font-size:.72rem; font-weight:700; letter-spacing:.02em; line-height:1.35; white-space:normal; overflow-wrap:anywhere; }
.tp-price { margin:0; font-size:var(--step--1); font-weight:700; color:var(--ink); }
.tp-price[data-basis], .bp-price[data-basis] > strong { background:var(--gold-soft); color:var(--gold-ink);
  padding:.1rem .45rem; border-radius:var(--radius-sm); }
.tour-picks .tp-body .tp-price[data-basis] { align-self:flex-start; }

/* Table booking column: the last cell of every comparison row is a small outlined button. */
.tbl-cta-cell { text-align:right; white-space:nowrap; }
td.tbl-cta-cell > a.tbl-cta { display:inline-flex; align-items:center; justify-content:center; min-height:2.25rem;
  padding:.3rem .8rem; border:1px solid var(--brand); border-radius:999px; color:var(--brand);
  font-weight:700; line-height:1.2; text-decoration:none; white-space:nowrap; }
td.tbl-cta-cell > a.tbl-cta:hover { background:var(--brand); color:var(--surface); }
td.tbl-cta-cell > a.tbl-cta:focus-visible { outline:3px solid var(--focus); outline-offset:2px; }
.visually-hidden { position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0; }

/* Booking panels on /tours/ topic pages: one lead panel, then a grid of compact ones. The lead
   panel puts its photo beside the copy on a wide screen; explicit rows with a zero row gap let
   the photo span every row while the items keep their own margins. */
.booking-panel .bp-media { margin:0 0 var(--s3); border-radius:var(--radius-sm); overflow:hidden; background:var(--paper-2); }
.booking-panel .bp-media img { display:block; width:100%; height:auto; aspect-ratio:4 / 3; object-fit:cover; }
.bp-kicker { margin:0 0 .2rem; font-family:var(--display); font-weight:700; font-size:var(--step--1);
  letter-spacing:.04em; text-transform:uppercase; color:var(--brand); overflow-wrap:anywhere; }
.bp-title, .tour-official { margin:0 0 var(--s2); font-family:var(--display); font-weight:800;
  font-size:var(--step-1); line-height:1.2; color:var(--ink); overflow-wrap:anywhere; }
.bp-price.bp-price-free { color:var(--brand); }
.bp-price.bp-price-free > strong { font-size:var(--step-1); }
.booking-panel.bp-compact { padding:var(--s3); }
.booking-panel.bp-compact .bp-title { font-size:var(--step-0); }
.booking-panel.bp-compact .bp-media { margin-bottom:var(--s2); }
.bp-grid { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:var(--s4); margin-top:var(--s4); }
.bp-grid.bp-grid-n2 { grid-template-columns:repeat(2, minmax(0,1fr)); }
.bp-grid.bp-grid-n1 { grid-template-columns:minmax(0,1fr); max-width:32rem; }
@media (max-width: 1040px) { .bp-grid, .bp-grid.bp-grid-n2 { grid-template-columns:repeat(2, minmax(0,1fr)); } }
@media (max-width: 640px)  { .bp-grid, .bp-grid.bp-grid-n1, .bp-grid.bp-grid-n2 { grid-template-columns:minmax(0,1fr); max-width:none; } }
@media (min-width: 901px) {
  .booking-panel.bp-lead { display:grid; grid-template-columns:minmax(0,5fr) minmax(0,7fr);
    grid-template-rows:repeat(9, auto); column-gap:var(--s4); row-gap:0; align-content:start; }
  .booking-panel.bp-lead > .bp-media { grid-column:1; grid-row:1 / -1; margin:0; align-self:start; }
  .booking-panel.bp-lead > :not(.bp-media) { grid-column:2; min-width:0; }
}
.topic-book { padding-top:var(--s5); }
.topic-related { padding-top:var(--s5); }
.topic-neighbours-note { margin:0 0 var(--s3); max-width:68ch; color:var(--ink-soft); }
.tr-price.tr-price-free { white-space:normal; font-size:var(--step-0); color:var(--brand); }
/* The live block reserves its slot before the feed lane fills it, so the page does not jump. */
.live-now { margin:var(--s3) 0 0; min-height:3.5rem; }
.live-now > p { margin:0; }
