/* /css/site.css -- FindMyFerry shared stylesheet.
 *
 * Hand-maintained SOURCE file (checked into git -- this is not build output
 * and is not gitignored). Both builders link to it with a content-hash query
 * string for cache-busting (see css_version() in build-routes.py, reused by
 * build-pages.py) and no longer inline any CSS of their own. Refs #10.
 *
 * TWO page families share this one file, and they are NOT visually
 * identical: build-routes.py's TEMPLATE (78 route pages) and build-pages.py's
 * page() shell (operators/ports/guides/legal/404/stats) independently grew
 * ~12 selectors with the SAME name but DIFFERENT values -- .btn, footer,
 * main, h1/h2/h3, .foot-grid, .foot-bottom, .foot-col h4, .nav-links,
 * .nav-links a, .nav-actions .signin, .brand among them. A naive unscoped
 * merge would let whichever family's rule sorts last in the file win on
 * BOTH page types, corrupting one of them. So every rule below is scoped
 * under the <body> class its page family sets:
 *
 *   body.tpl-route  -- build-routes.py TEMPLATE pages (routes/<slug>/)
 *   body.tpl-page   -- build-pages.py page() shell pages
 *
 * :root is the one unscoped exception -- custom properties cascade per
 * property, not per rule, so ONE :root feeds both families safely. There is
 * exactly one in this file and none anywhere else in the repo; see the
 * palette section below for what that replaced.
 *
 * This is a byte-for-byte move, not a redesign: every declaration below is
 * unchanged from the inline blocks it replaced (only a "body.tpl-route " /
 * "body.tpl-page " scope prefix was mechanically inserted before each
 * selector). See the issue #10 report for the round-trip proof.
 *
 * Editing rules going forward:
 *   - Do not reintroduce inline <style> blocks in either builder.
 *   - Do not merge a selector across the two scopes unless you have checked
 *     both families' original values actually agreed (most don't -- see the
 *     list above).
 *   - Flat-UI / GPU rule still applies here: no gradients, backdrop-filter,
 *     filter:blur, position:sticky, overflow:clip, or large blurred shadows
 *     (they corrupt rendering on the reference Xiaomi/MIUI Chrome device).
 */

/* ============================== shared rules ================================
 * Header, nav, footer, .wrap, body base, skip-link and mobile-nav rules
 * that body.tpl-route and body.tpl-page AGREE on. These were previously
 * duplicated as two scoped copies (one per family) plus three more inline
 * copies in index.html, search/index.html and build-routes.py's
 * INDEX_TEMPLATE -- and they drifted (issue #74). Now they live here once,
 * as a shared selector list. Genuinely per-family differences (the .btn
 * hover lift, the footer margin-top, the h1 line-height) stay in the
 * scoped sections below.
 *
 * A scope prefix must never be applied to a bare `body`/`html` selector
 * (body.tpl-route body { … } can never match -- see issue #10). The shared
 * list below uses `body.tpl-route, body.tpl-page` as the selector, never
 * a bare `body`.
 * ========================================================================= */
  body.tpl-route, body.tpl-page { box-sizing: border-box; }
  body.tpl-route, body.tpl-page * { box-sizing: border-box; }
  body.tpl-route, body.tpl-page { margin:0; font-family:"Inter",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif; color:var(--ink); background:var(--foam); line-height:1.6; -webkit-font-smoothing:antialiased; }
  body.tpl-route a, body.tpl-page a { color:inherit; text-decoration:none; }
  body.tpl-route .wrap, body.tpl-page .wrap { width:min(1000px,92vw); margin-inline:auto; }

  /* header / nav -- identical across both families */
  body.tpl-route header, body.tpl-page header { position:relative; z-index:50; background:#062a45; border-bottom:1px solid rgba(255,255,255,.08); }
  body.tpl-route .nav, body.tpl-page .nav { display:flex; align-items:center; justify-content:space-between; height:74px; }
  body.tpl-route .brand, body.tpl-page .brand { display:flex; align-items:center; gap:.65rem; color:#fff; font-weight:700; font-size:1.18rem; letter-spacing:-0.01em; }
  body.tpl-route .brand .mark, body.tpl-page .brand .mark { width:38px; height:38px; border-radius:11px; background:var(--sea); display:grid; place-items:center; }
  body.tpl-route .brand .mark svg, body.tpl-page .brand .mark svg { width:22px; height:22px; }
  body.tpl-route .nav-links, body.tpl-page .nav-links { display:flex; align-items:center; gap:2rem; }
  body.tpl-route .nav-links a, body.tpl-page .nav-links a { color:rgba(255,255,255,.82); font-size:.95rem; font-weight:500; transition:color .15s; }
  body.tpl-route .nav-links a:hover, body.tpl-page .nav-links a:hover { color:#fff; }
  body.tpl-route .nav-links a:focus-visible, body.tpl-page .nav-links a:focus-visible { outline:2px solid var(--sea); outline-offset:2px; }
  body.tpl-route .nav-actions .signin, body.tpl-page .nav-actions .signin { color:rgba(255,255,255,.85); font-weight:500; font-size:.95rem; }
  body.tpl-route .nav-toggle, body.tpl-page .nav-toggle { display:none; align-items:center; justify-content:center; width:40px; height:40px; padding:0; border:0; border-radius:8px; background:transparent; color:#fff; cursor:pointer; }
  body.tpl-route .nav-toggle:hover, body.tpl-page .nav-toggle:hover { background:rgba(255,255,255,.08); }
  body.tpl-route .nav-toggle[aria-expanded="true"], body.tpl-page .nav-toggle[aria-expanded="true"] { background:rgba(255,255,255,.14); }
  body.tpl-route .nav-toggle:focus-visible, body.tpl-page .nav-toggle:focus-visible { outline:2px solid var(--sea); outline-offset:2px; }

  /* skip link (Refs #18) -- position:absolute, not fixed, per the flat-UI/GPU rule */
  body.tpl-route .skip-link, body.tpl-page .skip-link {
    position: absolute; top: -3.5rem; left: 0; z-index: 100;
    background: var(--deep); color: #fff; font-weight: 600; font-size: .92rem;
    padding: .75rem 1.3rem; border-radius: 0 0 10px 0;
  }
  body.tpl-route .skip-link:focus, body.tpl-page .skip-link:focus { top: 0; }
  body.tpl-route .skip-link:focus-visible, body.tpl-page .skip-link:focus-visible { outline: 2px solid var(--sea); outline-offset: 2px; }

  /* footer -- identical across both families */
  body.tpl-route footer, body.tpl-page footer { background:var(--deep); color:rgba(255,255,255,.7); padding:4rem 0 2rem; }
  body.tpl-route .foot-grid, body.tpl-page .foot-grid { display:grid; grid-template-columns:1.6fr 1fr 1fr 1fr; gap:2.5rem; padding-bottom:3rem; border-bottom:1px solid rgba(255,255,255,.1); }
  body.tpl-route .foot-brand .brand, body.tpl-page .foot-brand .brand { margin-bottom:1rem; }
  body.tpl-route .foot-brand p, body.tpl-page .foot-brand p { max-width:34ch; color:rgba(255,255,255,.6); font-size:.95rem; }
  body.tpl-route .foot-col h4, body.tpl-page .foot-col h4 { color:#fff; font-size:.82rem; text-transform:uppercase; letter-spacing:.12em; margin:0 0 1.1rem; font-family:"Inter"; font-weight:600; }
  body.tpl-route .foot-col a, body.tpl-page .foot-col a { display:block; color:rgba(255,255,255,.68); font-size:.95rem; padding:.32rem 0; transition:color .15s; }
  body.tpl-route .foot-col a:hover, body.tpl-page .foot-col a:hover { color:var(--aqua); }
  body.tpl-route .foot-bottom, body.tpl-page .foot-bottom { display:flex; justify-content:space-between; align-items:center; padding-top:1.8rem; font-size:.86rem; color:rgba(255,255,255,.5); flex-wrap:wrap; gap:1rem; }

  /* mobile nav -- identical across both families (Refs #15) */
  @media (max-width: 940px) {
    body.tpl-route .nav-toggle, body.tpl-page .nav-toggle { display:inline-flex; }
    body.tpl-route .nav, body.tpl-page .nav { flex-wrap:wrap; height:auto; min-height:74px; }
    body.tpl-route .nav-links, body.tpl-page .nav-links { display:flex; flex-direction:column; align-items:stretch; width:100%; gap:0; padding:.25rem 0 1rem; }
    body.tpl-route .nav-links a, body.tpl-page .nav-links a { padding:.7rem .1rem; border-top:1px solid rgba(255,255,255,.1); }
    /* js/nav.js sets [hidden] on .nav-links regardless of viewport width
       (Refs #15) -- keep this override IN this media query, not outside
       it: above 940px the ".nav-links { display:flex }" rule above
       (author CSS) beats the UA stylesheet's "[hidden] { display:none }",
       so desktop nav stays visible/focusable with no width check in the JS. */
    body.tpl-route .nav-links[hidden], body.tpl-page .nav-links[hidden] { display:none; }
    body.tpl-route .foot-grid, body.tpl-page .foot-grid { grid-template-columns:1fr 1fr; gap:2rem; }
  }
  @media (max-width: 560px) {
    body.tpl-route .foot-grid, body.tpl-page .foot-grid { grid-template-columns:1fr; }
    body.tpl-route .nav-actions .signin { display:none; }
  }

/* ================================ palette =================================
 * THE palette. One :root for the whole site -- every page family, both
 * builders, all three hand-maintained shells.
 *
 * There used to be six copies (css/site.css route scope + page scope,
 * index.html, search/index.html, map/index.html, build-routes.py's
 * INDEX_TEMPLATE). Eleven colours agreed across all six by luck and constant
 * hand-syncing; --shadow/--shadow-sm did not, --teal-d existed in three of
 * them, and --radius/--white/--sand/--sea-d/--rt-* in one. A colour change
 * made in one copy silently drifted the rest.
 *
 * Unscoped on purpose, and the only unscoped rule in this file: custom
 * properties cascade per *property*, so one :root feeds both body.tpl-route
 * and body.tpl-page without the collision the scoping comment above warns
 * about. tests/test_contrast.py computes the WCAG ratios from these exact
 * values, and fails if any other file grows a palette :root again.
 *
 * --coral/--coral-d were darkened for #14 (white text on coral was 2.80:1,
 * coral-d hover 3.43:1 -- both fail WCAG AA 4.5:1). Same hue, lower
 * lightness: coral now 4.56:1, coral-d 5.68:1 against white. --teal stays
 * bright because it is only ever decorative (icons, borders, bullets);
 * --teal-d is the darker text-safe variant for the spots teal carries real
 * text (.stepper button:hover's glyph inside a white .popover, .step .num):
 * 5.05:1 against white, 4.61:1 even against --foam.
 * ========================================================================= */
  :root {
    --deep:#04263f;
    --navy:#0a3a5c;
    --sea:#0e7ca3; --sea-d:#0b6382;
    --teal:#12b0c4; --teal-d:#0d7a88;
    --aqua:#5fd3d6;
    --foam:#eaf7f8;
    --sand:#f6efe4;
    --coral:#d1452f; --coral-d:#b83b2c;
    --ink:#0d2231;
    --muted:#5b7183;
    --white:#ffffff;
    --line:rgba(10,58,92,.12);
    --radius:20px;
    --shadow:0 8px 24px -16px rgba(4,38,63,.30);
    --shadow-sm:0 2px 8px -4px rgba(4,38,63,.16);

    /* Route-card banner badges (.rt-1--.rt-6, Refs #25). Used by index.html
       alone, but declared here with everything else -- a variable that exists
       in one file is exactly how the six-copy palette started. The badge text
       does not sit directly on these: .route-top::after lays a
       rgba(4,38,63,.22) scrim over the banner, then the badge's own <span>
       paints a rgba(255,255,255,.22) chip under the white text, so the colour
       behind the text is a two-layer composite whose net effect is to LOWER
       contrast versus the raw hex. tests/test_contrast.py models both
       overlays and checks the composited result. --rt-3/--rt-4 reference
       --navy/--coral-d, which their old literals already equalled. */
    --rt-1:#0b6382;
    --rt-2:#0a6570;
    --rt-5:#06507a;
    --rt-6:#236275;
  }
  /* The one genuine per-family difference. Route pages have always drawn a
     much larger, softer shadow than the rest of the site. Kept as an explicit
     override rather than flattened, so this commit changes no pixels --
     but note the blur radius: README's flat-UI rule lists "large blurred
     box-shadow" among the things that corrupt on scroll on the reference
     device (Xiaomi/MIUI Chrome), and 60px is the largest on the site by a
     factor of two and a half. Worth revisiting on its own. */
  body.tpl-route { --shadow:0 24px 60px -28px rgba(4,38,63,.45); --shadow-sm:0 10px 30px -18px rgba(4,38,63,.4); }

/* ============================== route pages ===============================
 * build-routes.py TEMPLATE -- the 78 routes/<slug>/ pages. Originally a
 * byte-identical 15,842-byte inline block duplicated on every one of them.
 * ========================================================================= */
  /* box-sizing, body base, a, .wrap, header, nav, footer, skip-link and mobile
     nav are in the shared section above (body.tpl-route, body.tpl-page). Only
     the genuinely route-specific rules live here. */
  body.tpl-route h1,body.tpl-route h2,body.tpl-route h3 { font-family:"Fraunces",Georgia,serif; font-weight:600; letter-spacing:-.01em; margin:0; line-height:1.1; }
  body.tpl-route .btn { display:inline-flex; align-items:center; gap:.5rem; font-weight:600; font-size:.98rem; padding:.85rem 1.5rem; border-radius:999px; cursor:pointer; border:1px solid transparent; transition:transform .18s, background .18s; white-space:nowrap; }
  body.tpl-route .btn-primary { background:var(--coral); color:#fff; }
  body.tpl-route .btn-primary:hover { background:var(--coral-d); transform:translateY(-2px); }
  body.tpl-route .btn-dark { background:var(--deep); color:#fff; }
  body.tpl-route .btn-dark:hover { background:var(--navy); transform:translateY(-2px); }

  /* route hero (holds the booking pill). When a per-route photo is present
     * build-routes.py sets an inline background-image on .route-hero; the
     * ::before scrim below lays a solid translucent dark layer over it so the
     * white text/pill stay legible on any photo. Flat-UI rule: the scrim is a
     * solid rgba, NOT a gradient. No photo => the solid #0a3a5c background
     * shows (the scrim is transparent so the navy reads at full strength).
     *
     * aspect-ratio:2/1 keeps the hero a consistent 2:1 banner whatever the
     * content height; the .wrap is vertically centred so the crumbs/h1/blurb/
     * pill sit in the middle of that box instead of crowding the top. A
     * min-height floor stops the ratio from crushing the content on a narrow
     * phone (2:1 of ~92vw leaves little room for the pill), and display:flex
     * on .route-hero is what makes the inner .wrap actually centre. */
  /* width:100% is load-bearing. aspect-ratio resolves in whichever axis is
     left auto, and min-height makes the block size definite — so without it
     this sized the hero's *inline* axis from its block axis: 320 * 2/1 =
     640px inside a 375px viewport, putting every route page into horizontal
     scroll on a phone. Desktop never showed it (at 1000px the ratio gives
     500px and the 320px floor never applies). Live for two days before
     tests_browser/test_no_horizontal_overflow.py went in and named it. */
  body.tpl-route .route-hero { position: relative; color:#fff;
    background: #0a3a5c center/cover no-repeat;
    width: 100%; aspect-ratio: 3/1; min-height: 320px;
    display: flex; align-items: center;
    padding: clamp(2.5rem,6vw,4rem) 0 clamp(3rem,7vw,4.5rem); }
  body.tpl-route .route-hero::before { content:""; position:absolute; inset:0;
    background: rgba(4,38,63,.55); }
  body.tpl-route .route-hero .wrap { position: relative; }
  body.tpl-route .route-hero .crumbs { font-size:.85rem; color:rgba(255,255,255,.6); margin-bottom:1rem; }
  body.tpl-route .route-hero .crumbs a { color:rgba(255,255,255,.85); font-weight:600; }
  body.tpl-route .route-hero .crumbs a:hover { color:#fff; }
  body.tpl-route .route-hero h1 { font-size: clamp(2.1rem,5vw,3.4rem); margin:.7rem 0 .5rem; display:flex; align-items:center; gap:.8rem; flex-wrap:wrap; }
  body.tpl-route .route-hero h1 svg { color:var(--aqua); flex:none; }
  body.tpl-route .route-hero p { color:rgba(255,255,255,.82); max-width:60ch; font-size:1.05rem; margin:0; }
  body.tpl-route .book-opts { margin-top:1.2rem; display:flex; align-items:center; gap:.6rem; flex-wrap:wrap; }
  body.tpl-route .bo-label { color:rgba(255,255,255,.82); font-size:.9rem; font-weight:600; margin-right:.2rem; }
  body.tpl-route .book-opts .btn { padding:.7rem 1.2rem; font-size:.92rem; }
  body.tpl-route .btn-ghost-hero { background:rgba(255,255,255,.12); color:#fff; border:1px solid rgba(255,255,255,.35); }
  body.tpl-route .btn-ghost-hero:hover { background:rgba(255,255,255,.2); }

  /* ---- Booking pill (same as homepage/search) ---- */
  body.tpl-route .search-shell { position: relative; margin-top: clamp(1.6rem,3vw,2.2rem); }
  body.tpl-route .search { background:#fff; border-radius:20px; padding:1.1rem; display:grid; grid-template-columns:2.1fr 1fr 1.15fr auto; gap:.6rem; align-items:end; }
  body.tpl-route .field { display:flex; flex-direction:column; gap:.3rem; padding:.55rem .8rem; border-radius:12px; position:relative; }
  /* Dividers as inset pseudo-elements, not border-left on the field.
     A border spans the field's own height, and the grid is align-items:end,
     so each field is a different height and the separators came out ragged —
     plus the first field had none at all, which is why the box looked like it
     had lines only down one side. These are a fixed inset from the top and
     bottom of the row, so every divider is the same length whatever the
     fields do. */
  body.tpl-route .field { position: relative; }
  body.tpl-route .field + .field::before {
    content: ""; position: absolute; left: 0; top: 18%; bottom: 18%;
    width: 1px; background: var(--line);
  }
  body.tpl-route .field label { font-size:.72rem; text-transform:uppercase; letter-spacing:.1em; color:var(--muted); font-weight:600; }
  body.tpl-route .field .value { display:flex; align-items:center; gap:.5rem; font-weight:600; font-size:1rem; color:var(--ink); position:relative; }
  body.tpl-route .field .value svg { color:var(--sea); flex:none; }
  body.tpl-route .search .btn-primary { justify-content:center; height:100%; padding-inline:1.6rem; }
  body.tpl-route .field .value .chev { color:var(--muted); flex:none; }
  body.tpl-route .field:not(.trip-field):focus-within { background:var(--foam); }
  body.tpl-route .picker-trigger { cursor:pointer; user-select:none; }
  body.tpl-route .picker-trigger span { font-weight:600; flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  body.tpl-route .trip-field { padding:0 !important; }
  body.tpl-route .trip { display:flex; align-items:stretch; }
  body.tpl-route .trip .endpoint { position:relative; flex:1 1 0; min-width:0; padding:.55rem .8rem; border-radius:12px; display:flex; flex-direction:column; gap:.3rem; }
  body.tpl-route .trip .endpoint:focus-within, body.tpl-route .trip .endpoint:hover { background:var(--foam); }
  body.tpl-route .trip .endpoint label { font-size:.72rem; text-transform:uppercase; letter-spacing:.1em; color:var(--muted); font-weight:600; }
  body.tpl-route .trip .swap { align-self:center; flex:none; width:42px; height:42px; margin:0 .55rem; border-radius:50%; border:1px solid var(--line); background:#fff; color:var(--sea); display:grid; place-items:center; cursor:pointer; transition:color .15s,border-color .15s; }
  body.tpl-route .trip .swap:hover { color:var(--teal); border-color:var(--teal); }
  body.tpl-route .trip .swap svg { transition:transform .3s ease; }
  body.tpl-route .trip .swap.spin svg { transform:rotate(180deg); }
  body.tpl-route .popover { position:absolute; top:calc(100% + 12px); right:0; z-index:60; background:#fff; color:var(--ink); border:1px solid var(--line); border-radius:16px; box-shadow:var(--shadow); padding:1rem; width:300px; max-width:min(90vw,340px); }
  body.tpl-route .popover[hidden] { display:none; }
  body.tpl-route .port-pop { left:0; right:auto; width:250px; padding:.4rem; }
  body.tpl-route .port-search { display:flex; align-items:center; gap:.45rem; padding:.35rem .55rem; border-bottom:1px solid var(--line); margin-bottom:.3rem; }
  body.tpl-route .port-search svg { color:var(--muted); flex:none; }
  body.tpl-route .port-search input { border:0; outline:0; background:transparent; font:inherit; font-weight:600; color:var(--ink); width:100%; min-width:0; }
  body.tpl-route .port-search input:focus-visible { outline:2px solid var(--sea); outline-offset:2px; border-radius:4px; }
  body.tpl-route .port-search input::placeholder { color:var(--muted); font-weight:500; }
  body.tpl-route .port-list { max-height:250px; overflow-y:auto; }
  body.tpl-route .port-group { font-size:.64rem; text-transform:uppercase; letter-spacing:.09em; color:var(--muted); font-weight:700; padding:.5rem .7rem .25rem; }
  body.tpl-route .port-empty { padding:.9rem .7rem; color:var(--muted); text-align:center; font-size:.9rem; }
  body.tpl-route .port-pop button.port { display:flex; align-items:center; gap:.6rem; width:100%; text-align:left; border:0; background:transparent; border-radius:10px; padding:.58rem .7rem; font:inherit; font-weight:600; color:var(--ink); cursor:pointer; }
  body.tpl-route .port-pop button.port span { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  body.tpl-route .port-pop button.port .pico { color:var(--sea); flex:none; }
  body.tpl-route .port-pop button.port .tick { color:var(--sea); flex:none; opacity:0; }
  body.tpl-route .port-pop button.port:hover:not(:disabled) { background:var(--foam); }
  body.tpl-route .port-pop button.port.sel { color:var(--sea); }
  body.tpl-route .port-pop button.port.sel .tick { opacity:1; }
  body.tpl-route .port-pop button.port:disabled { color:#c3cfd8; cursor:not-allowed; }
  body.tpl-route .port-pop button.port:disabled .pico { color:#c9d5dd; }
  body.tpl-route .cal-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:.7rem; }
  body.tpl-route .cal-title { font-weight:700; font-size:.98rem; }
  body.tpl-route .cal-nav { width:32px; height:32px; border-radius:9px; border:1px solid var(--line); background:#fff; color:var(--ink); font-size:1.1rem; line-height:1; display:grid; place-items:center; cursor:pointer; }
  body.tpl-route .cal-nav:hover:not(:disabled) { background:var(--foam); }
  body.tpl-route .cal-nav:disabled { opacity:.35; cursor:not-allowed; }
  body.tpl-route .cal-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:2px; }
  body.tpl-route .cal-grid .dow { text-align:center; font-size:.66rem; font-weight:700; color:var(--muted); text-transform:uppercase; padding:.35rem 0; }
  body.tpl-route .cal-grid button.day { aspect-ratio:1; border:0; background:transparent; border-radius:9px; font:inherit; font-size:.9rem; color:var(--ink); cursor:pointer; }
  body.tpl-route .cal-grid button.day:hover:not(:disabled) { background:var(--foam); }
  body.tpl-route .cal-grid button.day.today:not(.sel) { box-shadow:inset 0 0 0 1.5px var(--teal); }
  body.tpl-route .cal-grid button.day.sel { background:var(--sea); color:#fff; font-weight:700; }
  body.tpl-route .cal-grid button.day:disabled { color:#c3cfd8; cursor:not-allowed; }
  body.tpl-route .pax-pop { width:320px; }
  body.tpl-route .stepper-row { display:flex; align-items:center; justify-content:space-between; padding:.65rem 0; }
  body.tpl-route .stepper-row + .stepper-row { border-top:1px solid var(--line); }
  body.tpl-route .sr-label b { display:block; font-weight:600; font-size:.96rem; }
  body.tpl-route .sr-label small { color:var(--muted); font-size:.78rem; }
  body.tpl-route .stepper { display:flex; align-items:center; gap:.8rem; }
  body.tpl-route .stepper button { width:32px; height:32px; border-radius:50%; border:1px solid var(--line); background:#fff; color:var(--sea); font-size:1.2rem; line-height:1; cursor:pointer; display:grid; place-items:center; }
  body.tpl-route .stepper button:hover:not(:disabled) { border-color:var(--teal); color:var(--teal-d); }
  body.tpl-route .stepper button:disabled { opacity:.35; cursor:not-allowed; }
  body.tpl-route .stepper .count { min-width:1.3rem; text-align:center; font-weight:700; }
  body.tpl-route .veh-title { font-size:.72rem; text-transform:uppercase; letter-spacing:.1em; color:var(--muted); font-weight:600; margin:1rem 0 .55rem; }
  body.tpl-route .veh-opts { display:grid; grid-template-columns:repeat(3,1fr); gap:.45rem; }
  body.tpl-route .veh-opts button { border:1px solid var(--line); background:#fff; border-radius:11px; cursor:pointer; padding:.55rem .2rem; display:flex; flex-direction:column; align-items:center; gap:.3rem; font-size:.72rem; font-weight:600; color:var(--muted); text-align:center; }
  body.tpl-route .veh-opts button svg { color:var(--sea); }
  body.tpl-route .veh-opts button.sel { border-color:var(--teal); background:var(--foam); color:var(--ink); }
  body.tpl-route .veh-opts button.sel svg { color:var(--teal); }
  body.tpl-route .pop-done { margin-top:1rem; width:100%; justify-content:center; }

  /* ---- useful info below the pill ---- */
  body.tpl-route main { display:block; background:var(--foam); padding: clamp(2.5rem,6vw,4rem) 0 clamp(3rem,7vw,4.5rem); }
  body.tpl-route .info section + section { margin-top: clamp(2.2rem,5vw,3.2rem); }
  body.tpl-route .info h2 { font-size:clamp(1.4rem,3vw,1.9rem); margin-bottom:1.1rem; }
  body.tpl-route .fact-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(165px,1fr)); gap:.8rem; }
  body.tpl-route .fact-card { background:#fff; border:1px solid var(--line); border-radius:14px; padding:1rem 1.1rem; display:flex; align-items:flex-start; gap:.7rem; }
  body.tpl-route .fact-card svg { color:var(--sea); flex:none; margin-top:2px; }
  body.tpl-route .fact-card span { display:block; font-size:.68rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); font-weight:600; margin-bottom:.1rem; }
  body.tpl-route .fact-card b { font-size:1rem; line-height:1.25; }
  body.tpl-route .about p { color:var(--ink); max-width:70ch; margin:0 0 1rem; }
  body.tpl-route .about p:last-child { margin:0; }
  body.tpl-route .faq details { background:#fff; border:1px solid var(--line); border-radius:12px; margin-bottom:.6rem; }
  body.tpl-route .faq summary { cursor:pointer; padding:.9rem 1.1rem; font-weight:600; list-style:none; display:flex; justify-content:space-between; align-items:center; gap:1rem; }
  body.tpl-route .faq summary::-webkit-details-marker { display:none; }
  body.tpl-route .faq summary::after { content:"+"; color:var(--sea); font-size:1.4rem; font-weight:400; line-height:1; flex:none; }
  body.tpl-route .faq details[open] summary::after { content:"−"; }
  body.tpl-route .faq details[open] summary { border-bottom:1px solid var(--line); }
  body.tpl-route .faq .fa-body { padding:.75rem 1.1rem 1rem; color:var(--muted); max-width:72ch; }
  body.tpl-route .op-card { background:#fff; border:1px solid var(--line); border-radius:14px; padding:1.1rem 1.2rem; display:flex; align-items:flex-start; gap:.9rem; }
  body.tpl-route .op-card + .op-card { margin-top:.7rem; }
  body.tpl-route .op-logo { width:46px; height:46px; border-radius:12px; background:var(--sea); color:#fff; display:grid; place-items:center; font-weight:700; font-size:.95rem; flex:none; }
  /* A real logo brings its own background; the initials square's teal
     would otherwise show as a border around it. overflow:hidden clips
     the image to the same rounded corners. */
  body.tpl-route .op-logo.has-logo { background:none; overflow:hidden; }
  body.tpl-route .op-logo img { width:100%; height:100%; object-fit:contain; display:block; }
  body.tpl-route .op-card b { font-size:1.05rem; }
  body.tpl-route .op-card .op-tag { display:block; color:var(--sea); font-size:.8rem; font-weight:600; margin:.05rem 0 .4rem; }
  body.tpl-route .op-card p { margin:0; color:var(--muted); max-width:66ch; }
  body.tpl-route .tips-list { list-style:none; padding:0; margin:0; display:grid; gap:.6rem; }
  body.tpl-route .tips-list li { position:relative; padding-left:1.6rem; color:var(--ink); max-width:72ch; }
  body.tpl-route .tips-list li::before { content:""; position:absolute; left:0; top:.55rem; width:8px; height:8px; border-radius:50%; background:var(--teal); }
  body.tpl-route .times-note { color:var(--muted); margin:0 0 1rem; max-width:70ch; }
  body.tpl-route .time-chips { display:flex; flex-wrap:wrap; gap:.5rem; }
  body.tpl-route .time-chip { background:#fff; border:1px solid var(--line); border-radius:10px; padding:.5rem .85rem; font-weight:700; font-variant-numeric:tabular-nums; color:var(--ink); }
  body.tpl-route .rel-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:.8rem; }
  body.tpl-route .rel-card { background:#fff; border:1px solid var(--line); border-radius:14px; padding:1rem 1.1rem; display:block; transition:border-color .15s; }
  body.tpl-route .rel-card:hover { border-color:var(--sea); }
  body.tpl-route .rel-route { font-family:"Fraunces",serif; font-weight:600; font-size:1.08rem; display:flex; align-items:center; gap:.5rem; }
  body.tpl-route .rel-route svg { color:var(--teal); flex:none; }
  body.tpl-route .rel-meta { color:var(--muted); font-size:.85rem; margin-top:.3rem; }
  body.tpl-route .updated { color:var(--muted); font-size:.85rem; margin:2.2rem 0 0; }
  body.tpl-route .quick-picks { display:flex; gap:.4rem; margin-bottom:.7rem; }
  body.tpl-route .quick-picks button { font:inherit; font-size:.8rem; font-weight:600; color:var(--navy); background:#fff; border:1px solid var(--line); border-radius:999px; padding:.3rem .8rem; cursor:pointer; }
  body.tpl-route .quick-picks button:hover { border-color:var(--sea); color:var(--sea); }
  body.tpl-route .port-pop button.port.act { background:var(--foam); }

  /* footer + mobile nav are in the shared section above. Only the
     route-specific search-grid stacking lives here.

     The 940px step is what body.tpl-page's pill has always had and this one
     did not: four columns plus an auto-width button went straight to one
     column at 560px, so between 561px and about 900px the Compare button
     simply did not fit the .wrap and hung off the side of the page — 27px
     over at 768px. Found by tests_browser/test_no_horizontal_overflow.py. */
  @media (max-width: 940px) {
    body.tpl-route .search { grid-template-columns:1fr 1fr; }
    body.tpl-route .trip-field { grid-column:1 / -1; }
    body.tpl-route .search .btn-primary { grid-column:1 / -1; }
    /* stacked: the border-top below is the separator */
    body.tpl-route .field + .field::before { display:none; }
  }
  @media (max-width: 560px) {
    body.tpl-route .search { grid-template-columns:1fr; gap:0; padding:.5rem; }
    body.tpl-route .search > .field { padding:.9rem .75rem; border-radius:10px; }
    body.tpl-route .search > .field + .field { border-top:1px solid var(--line); border-radius:0; }
    /* Stacked here, so the vertical divider must go. */
    body.tpl-route .field + .field::before { display:none; }
    body.tpl-route .search .btn-primary { margin-top:.55rem; height:52px; }
    body.tpl-route .trip { position:relative; flex-direction:column; align-items:stretch; gap:0; }
    body.tpl-route .trip .endpoint { padding:.9rem 3.6rem .9rem .75rem; border-radius:0; }
    body.tpl-route .trip .endpoint + .endpoint { border-top:1px solid var(--line); }
    body.tpl-route .trip .swap { position:absolute; right:.5rem; top:50%; margin:0; transform:translateY(-50%) rotate(90deg); }
    body.tpl-route .popover { left:0; right:0; width:auto; max-width:none; }
  }
/* =========================== page() shell ==================================
 * build-pages.py page() -- operators/ ports/ guides/ legal/ 404/ stats/.
 * Originally the STYLE constant, inlined identically into every one of
 * those pages (~85 of them).
 * ========================================================================= */

  /* palette: the one :root at the top of this file. */
   /* box-sizing, body base, a, .wrap, header, nav, skip-link, footer and
      mobile nav are in the shared section above (body.tpl-route,
      body.tpl-page). Only the genuinely page-specific rules live here. */
   body.tpl-page h1,body.tpl-page h2,body.tpl-page h3 { font-family:"Fraunces",Georgia,serif; font-weight:600; letter-spacing:-.01em; margin:0; line-height:1.15; }
   body.tpl-page .heroband { background:#0a3a5c; color:#fff; padding:clamp(2rem,5vw,3.2rem) 0; }
  body.tpl-page .heroband h1 { font-size:clamp(1.9rem,4.4vw,2.9rem); margin:.7rem 0 .5rem; }
  body.tpl-page .heroband p { color:rgba(255,255,255,.82); max-width:64ch; margin:0; font-size:1.05rem; }
  body.tpl-page main { padding:clamp(2rem,5vw,3.4rem) 0 clamp(3rem,6vw,4.5rem); }
  body.tpl-page .crumbs { font-size:.85rem; color:var(--muted); margin-bottom:1.4rem; }
  body.tpl-page .crumbs a { color:var(--sea); font-weight:600; }
  body.tpl-page .grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(260px,1fr)); gap:.8rem; }
  body.tpl-page .card { background:#fff; border:1px solid var(--line); border-radius:14px; padding:1.1rem 1.2rem; display:block; transition:border-color .15s, transform .15s; }
  body.tpl-page .card:hover { border-color:var(--sea); transform:translateY(-2px); }
  body.tpl-page .card h3 { font-size:1.12rem; }
  body.tpl-page .card .meta { color:var(--muted); font-size:.85rem; margin-top:.35rem; }
  body.tpl-page .card .tag { display:inline-block; color:var(--sea); font-size:.78rem; font-weight:600; margin-top:.2rem; }
  body.tpl-page .op-row { display:flex; flex-wrap:wrap; gap:.5rem; margin-top:.5rem; }
  body.tpl-page .op-chip { display:inline-flex; align-items:center; background:#fff; border:1px solid var(--line); border-radius:999px; padding:.45rem 1rem; font-weight:600; font-size:.92rem; color:var(--navy); }
  body.tpl-page .op-chip:hover { border-color:var(--sea); color:var(--sea); }
  body.tpl-page .official { margin-top:.6rem; }
  body.tpl-page .official a { color:var(--sea); font-weight:600; }
  body.tpl-page .tips { list-style:none; padding:0; margin:0; display:grid; gap:.55rem; }
  body.tpl-page .tips li { position:relative; padding-left:1.5rem; color:var(--ink); max-width:70ch; }
  body.tpl-page .tips li::before { content:""; position:absolute; left:0; top:.55rem; width:8px; height:8px; border-radius:50%; background:var(--teal); }
  body.tpl-page .prose { max-width:72ch; }
  body.tpl-page .prose h2 { font-size:clamp(1.3rem,2.6vw,1.7rem); margin:2rem 0 .8rem; }
  body.tpl-page .prose p, body.tpl-page .prose li { color:var(--ink); }
  body.tpl-page .prose p { margin:0 0 1rem; }
  body.tpl-page .prose ul { padding-left:1.2rem; }
  body.tpl-page .prose a { color:var(--sea); font-weight:600; }
  body.tpl-page .section + .section { margin-top:2.4rem; }
  body.tpl-page .section h2 { font-size:clamp(1.25rem,2.6vw,1.6rem); margin-bottom:1rem; }
  body.tpl-page .rt-line { font-family:"Fraunces",serif; font-size:1.08rem; font-weight:600; display:flex; align-items:center; gap:.5rem; }
  body.tpl-page .rt-line svg { color:var(--teal); flex:none; }
   body.tpl-page .btn { display:inline-flex; align-items:center; gap:.5rem; font-weight:600; font-size:.95rem; padding:.7rem 1.3rem; border-radius:999px; border:1px solid transparent; background:var(--coral); color:#fff; }
   body.tpl-page .btn:hover { background:var(--coral-d); }
   /* footer + .foot-grid/.foot-col/.foot-bottom are in the shared section
      above. Only the page-specific margin-top override lives here: route
      pages have no margin-top on footer (the hero section provides the
      gap), page() pages add it so the footer clears the <main> content. */
   body.tpl-page footer { margin-top:clamp(2.5rem,6vw,4rem); }
   body.tpl-page #cookie-bar { position:fixed; left:0; right:0; bottom:0; z-index:200; transform:translateZ(0); will-change:transform; background:#062a45; color:rgba(255,255,255,.9); padding:.55rem .8rem; display:none; }
  body.tpl-page #cookie-bar.show { display:block; }
  body.tpl-page .cb-in { width:min(1000px,94vw); margin-inline:auto; display:flex; align-items:center; gap:.7rem; flex-wrap:wrap; justify-content:space-between; font-size:.85rem; line-height:1.4; }
  body.tpl-page .cb-in a { color:var(--aqua); font-weight:600; }
   body.tpl-page .cb-in button { font:inherit; font-weight:700; cursor:pointer; border:0; border-radius:999px; min-height:44px; padding:.5rem 1.2rem; background:var(--coral); color:#fff; }
   /* mobile nav + foot-grid responsive rules are in the shared section above. */

/* ======================= search pill (body.tpl-page) =======================
 * The route-search widget rendered from partials/pill.html into index.html
 * and search/index.html. Refs #13.
 *
 * The markup has been single-sourced since #12; the CSS was left behind, in
 * two copies that were 320 identical declarations across 86 selectors, and
 * had already drifted in six places. This is that half.
 *
 * Only six selectors genuinely differ between the two pages, and they differ
 * for one reason: on the homepage the pill sits on the dark hero, everywhere
 * else on --foam. That is now an explicit `on-dark` modifier on .search-shell
 * rather than two divergent copies -- so "this rule is different because the
 * background is dark" is visible in the markup instead of being something you
 * have to diff two files to discover.
 *
 * body.tpl-route has its own pill CSS further up (route pages embed the same
 * widget in a different shell); it is NOT this block. Keep the two in step by
 * hand -- tests/test_search_box_dividers.py checks the one rule that has
 * actually drifted between them before.
 * ========================================================================= */
  body.tpl-page .search-shell { position:relative; z-index:10; }
  body.tpl-page .search { background:#fff; border-radius:var(--radius); box-shadow:var(--shadow); padding:1.1rem; display:grid; grid-template-columns:2fr .95fr .95fr 1.1fr auto; gap:.6rem; align-items:end; }

  /* Dividers as inset pseudo-elements, not border-left on the field. A border
     spans the field's own height, and the grid is align-items:end, so each
     field is a different height and the separators came out ragged -- plus the
     first field had none at all, which is why the box looked like it had lines
     only down one side. These are a fixed inset from the top and bottom of the
     row, so every divider is the same length whatever the fields do. */
  body.tpl-page .field { display:flex; flex-direction:column; gap:.3rem; padding:.55rem .8rem; border-radius:12px; position:relative; }
  body.tpl-page .field + .field::before { content:""; position:absolute; left:0; top:18%; bottom:18%; width:1px; background:var(--line); }
  body.tpl-page .field label { font-size:.72rem; text-transform:uppercase; letter-spacing:.1em; color:var(--muted); font-weight:600; }
  body.tpl-page .field .value { display:flex; align-items:center; gap:.5rem; font-weight:600; font-size:1rem; color:var(--ink); position:relative; }
  body.tpl-page .field .value svg { color:var(--sea); flex:none; }
  body.tpl-page .field .value span.sub { color:var(--muted); font-weight:500; font-size:.82rem; }
  body.tpl-page .field .value .chev { color:var(--muted); flex:none; }
  body.tpl-page .field:not(.trip-field):focus-within { background:var(--foam); }
  /* more specific than body.tpl-page .btn, which both shells narrow further */
  body.tpl-page .search .btn-primary { justify-content:center; height:100%; padding-inline:1.6rem; }
  body.tpl-page .picker-trigger { cursor:pointer; user-select:none; }
  body.tpl-page .picker-trigger span { font-weight:600; flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  body.tpl-page .picker-trigger span.muted { color:var(--muted); font-weight:500; }

  /* one-way / return toggle */
  body.tpl-page .trip-toggle { display:inline-flex; gap:.2rem; background:#fff; border:1px solid var(--line); border-radius:999px; padding:.25rem; margin-bottom:.8rem; box-shadow:var(--shadow-sm); }
  body.tpl-page .tt-btn { border:0; background:transparent; padding:.4rem 1.1rem; border-radius:999px; font:inherit; font-weight:600; font-size:.9rem; color:var(--muted); cursor:pointer; }
  body.tpl-page .tt-btn.sel { background:var(--sea); color:#fff; }
  /* Was opacity:.6, which composited --muted down to #9daab5 = 2.37:1 against
     white -- a real AA failure axe-core caught. The disabled look now comes
     from muting the icon only; the text keeps a readable colour. Refs #21. */
  body.tpl-page .ret-field.disabled .value { color:var(--muted); }
  body.tpl-page .ret-field.disabled .value svg { opacity:.55; }

  /* trip: from / swap / to */
  body.tpl-page .trip-field { padding:0 !important; }
  body.tpl-page .trip { display:flex; align-items:stretch; }
  body.tpl-page .trip .endpoint { position:relative; flex:1 1 0; min-width:0; padding:.55rem .8rem; border-radius:12px; display:flex; flex-direction:column; gap:.3rem; }
  body.tpl-page .trip .endpoint:focus-within, body.tpl-page .trip .endpoint:hover { background:var(--foam); }
  body.tpl-page .trip .endpoint label { font-size:.72rem; text-transform:uppercase; letter-spacing:.1em; color:var(--muted); font-weight:600; }
  body.tpl-page .trip .swap { align-self:center; flex:none; width:42px; height:42px; margin:0 .55rem; border-radius:50%; border:1px solid var(--line); background:#fff; color:var(--sea); display:grid; place-items:center; cursor:pointer; box-shadow:0 6px 16px -6px rgba(4,38,63,.3); transition:color .15s, border-color .15s, box-shadow .15s; }
  body.tpl-page .trip .swap:hover { color:var(--teal); border-color:var(--teal); }
  body.tpl-page .trip .swap svg { transition:transform .3s ease; }
  body.tpl-page .trip .swap.spin svg { transform:rotate(180deg); }

  /* popovers */
  body.tpl-page .popover { position:absolute; top:calc(100% + 12px); right:0; z-index:60; background:#fff; color:var(--ink); border:1px solid var(--line); border-radius:16px; box-shadow:var(--shadow); padding:1rem; width:300px; max-width:min(90vw,340px); }
  body.tpl-page .popover[hidden] { display:none; }

  /* port list dropdown */
  body.tpl-page .port-pop { left:0; right:auto; width:250px; padding:.4rem; }
  body.tpl-page .port-search { display:flex; align-items:center; gap:.45rem; padding:.35rem .55rem; border-bottom:1px solid var(--line); margin-bottom:.3rem; }
  body.tpl-page .port-search svg { color:var(--muted); flex:none; }
  body.tpl-page .port-search input { border:0; outline:0; background:transparent; font:inherit; font-weight:600; color:var(--ink); width:100%; min-width:0; }
  body.tpl-page .port-search input:focus-visible { outline:2px solid var(--sea); outline-offset:2px; border-radius:4px; }
  body.tpl-page .port-search input::placeholder { color:var(--muted); font-weight:500; }
  body.tpl-page .port-list { max-height:250px; overflow-y:auto; }
  body.tpl-page .port-group { font-size:.64rem; text-transform:uppercase; letter-spacing:.09em; color:var(--muted); font-weight:700; padding:.5rem .7rem .25rem; }
  body.tpl-page .port-empty { padding:.9rem .7rem; color:var(--muted); text-align:center; font-size:.9rem; }
  body.tpl-page .port-pop button.port { display:flex; align-items:center; gap:.6rem; width:100%; text-align:left; border:0; background:transparent; border-radius:10px; padding:.58rem .7rem; font:inherit; font-weight:600; color:var(--ink); cursor:pointer; }
  body.tpl-page .port-pop button.port span { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  body.tpl-page .port-pop button.port .pico { color:var(--sea); flex:none; }
  body.tpl-page .port-pop button.port .tick { color:var(--sea); flex:none; opacity:0; }
  body.tpl-page .port-pop button.port:hover:not(:disabled) { background:var(--foam); }
  body.tpl-page .port-pop button.port.act { background:var(--foam); }
  body.tpl-page .port-pop button.port.sel { color:var(--sea); }
  body.tpl-page .port-pop button.port.sel .tick { opacity:1; }
  body.tpl-page .port-pop button.port:disabled { color:#c3cfd8; cursor:not-allowed; }
  body.tpl-page .port-pop button.port:disabled .pico { color:#c9d5dd; }
  body.tpl-page .quick-picks { display:flex; gap:.4rem; margin-bottom:.7rem; }
  body.tpl-page .quick-picks button { font:inherit; font-size:.8rem; font-weight:600; color:var(--navy); background:#fff; border:1px solid var(--line); border-radius:999px; padding:.3rem .8rem; cursor:pointer; }
  body.tpl-page .quick-picks button:hover { border-color:var(--sea); color:var(--sea); }

  /* calendar */
  body.tpl-page .cal-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:.7rem; }
  body.tpl-page .cal-title { font-weight:700; font-size:.98rem; }
  body.tpl-page .cal-nav { width:32px; height:32px; border-radius:9px; border:1px solid var(--line); background:#fff; color:var(--ink); font-size:1.1rem; line-height:1; display:grid; place-items:center; cursor:pointer; }
  body.tpl-page .cal-nav:hover:not(:disabled) { background:var(--foam); }
  body.tpl-page .cal-nav:disabled { opacity:.35; cursor:not-allowed; }
  body.tpl-page .cal-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:2px; }
  body.tpl-page .cal-grid .dow { text-align:center; font-size:.66rem; font-weight:700; color:var(--muted); text-transform:uppercase; padding:.35rem 0; }
  body.tpl-page .cal-grid button.day { aspect-ratio:1; border:0; background:transparent; border-radius:9px; font:inherit; font-size:.9rem; color:var(--ink); cursor:pointer; }
  body.tpl-page .cal-grid button.day:hover:not(:disabled) { background:var(--foam); }
  body.tpl-page .cal-grid button.day.today:not(.sel) { box-shadow:inset 0 0 0 1.5px var(--teal); }
  body.tpl-page .cal-grid button.day.sel { background:var(--sea); color:#fff; font-weight:700; }
  body.tpl-page .cal-grid button.day:disabled { color:#c3cfd8; cursor:not-allowed; }

  /* passengers & vehicle */
  body.tpl-page .pax-pop { width:320px; }
  body.tpl-page .stepper-row { display:flex; align-items:center; justify-content:space-between; padding:.65rem 0; }
  body.tpl-page .stepper-row + .stepper-row { border-top:1px solid var(--line); }
  body.tpl-page .sr-label b { display:block; font-weight:600; font-size:.96rem; }
  body.tpl-page .sr-label small { color:var(--muted); font-size:.78rem; }
  body.tpl-page .stepper { display:flex; align-items:center; gap:.8rem; }
  body.tpl-page .stepper button { width:32px; height:32px; border-radius:50%; border:1px solid var(--line); background:#fff; color:var(--sea); font-size:1.2rem; line-height:1; cursor:pointer; display:grid; place-items:center; }
  body.tpl-page .stepper button:hover:not(:disabled) { border-color:var(--teal); color:var(--teal-d); }
  body.tpl-page .stepper button:disabled { opacity:.35; cursor:not-allowed; }
  body.tpl-page .stepper .count { min-width:1.3rem; text-align:center; font-weight:700; }
  body.tpl-page .veh-title { font-size:.72rem; text-transform:uppercase; letter-spacing:.1em; color:var(--muted); font-weight:600; margin:1rem 0 .55rem; }
  body.tpl-page .veh-opts { display:grid; grid-template-columns:repeat(3,1fr); gap:.45rem; }
  body.tpl-page .veh-opts button { border:1px solid var(--line); background:#fff; border-radius:11px; cursor:pointer; padding:.55rem .2rem; display:flex; flex-direction:column; align-items:center; gap:.3rem; font-size:.72rem; font-weight:600; color:var(--muted); text-align:center; }
  body.tpl-page .veh-opts button svg { color:var(--sea); }
  body.tpl-page .veh-opts button.sel { border-color:var(--teal); background:var(--foam); color:var(--ink); }
  body.tpl-page .veh-opts button.sel svg { color:var(--teal); }
  body.tpl-page .pop-done { margin-top:1rem; width:100%; justify-content:center; }

  /* --- on the dark hero (homepage only) ----------------------------------
     The six selectors that actually differ. .search-shell.on-dark is a
     modifier the homepage puts on the wrapper; every other page gets the
     block above unchanged. */
  body.tpl-page .search-shell.on-dark .search { transform:translateY(40px); }
  body.tpl-page .search-shell.on-dark .trip-toggle { background:rgba(255,255,255,.12); border-color:rgba(255,255,255,.22); box-shadow:none; }
  body.tpl-page .search-shell.on-dark .tt-btn { color:rgba(255,255,255,.78); }
  body.tpl-page .search-shell.on-dark .tt-btn.sel { background:#fff; color:var(--ink); }
  body.tpl-page .search-shell.on-dark .recent { display:flex; align-items:center; gap:.45rem; flex-wrap:wrap; margin-bottom:.8rem; font-size:.85rem; color:rgba(255,255,255,.7); }
  body.tpl-page .search-shell.on-dark .recent[hidden] { display:none; }
  body.tpl-page .search-shell.on-dark .recent a { background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.28); color:#fff; font-weight:600; border-radius:999px; padding:.3rem .85rem; font-size:.84rem; }
  body.tpl-page .search-shell.on-dark .recent a:hover { background:rgba(255,255,255,.2); }

  @media (max-width: 940px) {
    body.tpl-page .search { grid-template-columns:1fr 1fr; }
    body.tpl-page .trip-field { grid-column:1 / -1; }
    body.tpl-page .search .btn-primary { grid-column:1 / -1; }
    /* stacked: the border-top below is the separator */
    body.tpl-page .field + .field::before { display:none; }
  }
  @media (max-width: 560px) {
    /* one clean card with hairline separators */
    body.tpl-page .search { grid-template-columns:1fr; gap:0; padding:.5rem; }
    body.tpl-page .search-shell.on-dark .search { transform:translateY(28px); }
    body.tpl-page .search > .field { padding:.9rem .75rem; border-radius:10px; }
    body.tpl-page .search > .field + .field { border-top:1px solid var(--line); border-radius:0; }
    body.tpl-page .search .btn-primary { margin-top:.55rem; height:52px; }
    /* From / To stack; swap is a round button pinned to the right edge */
    body.tpl-page .trip { position:relative; flex-direction:column; align-items:stretch; gap:0; }
    body.tpl-page .trip .endpoint { padding:.9rem 3.6rem .9rem .75rem; border-radius:0; }
    body.tpl-page .trip .endpoint + .endpoint { border-top:1px solid var(--line); }
    body.tpl-page .trip .swap { position:absolute; right:.5rem; top:50%; margin:0; transform:translateY(-50%) rotate(90deg); }
    /* popovers span the field width */
    body.tpl-page .popover { left:0; right:0; width:auto; max-width:none; }
  }

/* ============================ reduced motion ===============================
 * Refs #19 -- one block for BOTH page families (route + page), covering
 * every transition/hover-transform this file declares. Only ever *removes*
 * motion (transition:none, transform:none on the hover/active end-state) --
 * never adds an effect and never touches colour. Every selector below is
 * still correctly scoped under body.tpl-route/body.tpl-page, so this passes
 * test_no_selector_can_never_match same as every other rule in the file.
 * The one exception, deliberately left alone: #cookie-bar's
 * transform:translateZ(0);will-change:transform -- that's the flat-UI GPU
 * compositing hint for position:fixed (README's Flat-UI rule), a static
 * hint with no transition attached, not an animation to disable.
 * ========================================================================= */
  @media (prefers-reduced-motion: reduce) {
    body.tpl-route .btn,
    body.tpl-route .nav-links a,
    body.tpl-route .trip .swap,
    body.tpl-route .trip .swap svg,
    body.tpl-route .rel-card { transition: none; }
    body.tpl-route .btn-primary:hover,
    body.tpl-route .btn-dark:hover { transform: none; }

    body.tpl-page .card { transition: none; }
    body.tpl-page .card:hover { transform: none; }
  }
