/* Tokens live in tokens.css, imported ahead of this file by assets/app.js. */
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--ink);
       font: 400 15px/1.5 var(--f-sans);
       min-height: 100vh; display: flex; flex-direction: column;
       -webkit-font-smoothing: antialiased; }
main { flex: 1 0 auto; }
footer.site { margin-top: auto; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* `.container` is the name the redesign uses; `.wrap` is kept because six existing
   templates already emit it. Same rule, so the two can be mixed during the port. */
.wrap, .container { max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter); }
/* Icons — sized by the context that renders them, coloured by currentColor. */
.icon { width: 1em; height: 1em; flex: 0 0 auto; display: inline-block; vertical-align: middle; }

.skip-link { position: absolute; left: -9999px; }
.skip-link:focus { left: var(--s2); top: var(--s2); z-index: var(--z-skiplink); background: var(--card);
                   color: var(--ink); padding: var(--s2) var(--s3); border-radius: var(--r-md);
                   box-shadow: var(--sh-pop); font: var(--t-body); font-weight: 600; }

/* Site header — full-bleed gradient bar */
header.site { background: var(--header-grad); color: var(--on-dark); }
.header-in { min-height: var(--header-h); display: flex; align-items: center; gap: var(--s6); }
.brand-block { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
header.site .brand { display: inline-flex; align-items: center; gap: var(--s2);
                     color: var(--on-dark); font: 400 22px/1.5 var(--f-serif);
                     letter-spacing: -.02em; }
header.site .brand .icon { width: var(--brand-mark-w); height: 38px; }
/* Indented to the wordmark, not to the mark, which spans both lines. */
header.site .tagline { padding-left: calc(var(--brand-mark-w) + var(--s2)); }
header.site .brand:hover { text-decoration: none; opacity: .85; }
header.site .tagline { margin: 0; font: var(--t-body); color: var(--on-dark-2); }
header.site .tagline b { font-family: var(--f-mono); font-weight: 500;
                         font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, .82); }
header.site .site-nav { margin-left: auto; display: flex; gap: var(--s1); }
header.site .site-nav a { font: var(--t-nav); letter-spacing: .01em; color: var(--on-dark-2);
                          padding: 6px 11px; border-radius: var(--r-sm); white-space: nowrap;
                          transition: background .12s, color .12s; }
header.site .site-nav a:hover { color: var(--on-dark); text-decoration: none;
                                background: rgba(255, 255, 255, .06); }
header.site .site-nav a.active { background: var(--on-dark-3); color: var(--on-dark); font-weight: 600; }
/* The filter toggle. The checkbox is visually hidden rather than [hidden] so it
   keeps keyboard focus and an accessible name; the label in the header is the
   visible control, and :has() carries the state and focus ring across to it.
   On desktop :checked collapses the band; below 640px the media query flips it
   so :checked expands instead. */
.filters-open-state { position: absolute; width: 1px; height: 1px; margin: -1px;
                      padding: 0; border: 0; overflow: hidden; clip-path: inset(50%); }
.filters-open-state:checked ~ turbo-frame#voyages .filterbar .band-inner { display: none; }
body:has(.filters-open-state:checked) .hdr-icon { background: rgba(255, 255, 255, .28); }
body:has(.filters-open-state:focus-visible) .hdr-icon { outline: 2px solid #fff; outline-offset: 2px; }
/* Square glyph button that toggles the filter band (see .filters-open-state). */
.hdr-icon { display: inline-flex; align-items: center; justify-content: center; width: 34px;
            height: 34px; flex: 0 0 auto; border: 0; border-radius: var(--r-sm);
            background: var(--on-dark-3); color: var(--on-dark); cursor: pointer; font-size: 17px; }
.hdr-icon:hover { background: rgba(255, 255, 255, .2); }
@media (max-width: 900px) {
    /* 7+ nav pills wrapping would build a three-storey header — scroll them instead.
       padding-block, not the shorthand: .header-in is also .container, whose
       horizontal gutter must survive or the brand sits flush against the edge. */
    .header-in { min-height: 0; padding-block: 10px; flex-wrap: wrap; gap: var(--s3); }
    header.site .tagline { display: none; }
    header.site .site-nav { margin-left: 0; order: 3; width: 100%; overflow-x: auto;
                            scrollbar-width: none; }
    header.site .site-nav::-webkit-scrollbar { display: none; }
    .hdr-icon { margin-left: auto; }
}

/* SEO hub pages (operators, ships, months, regions, activities) */
.hub-intro { color: var(--muted); max-width: 760px; margin: 0 0 20px; }
.hub-description { max-width: 760px; margin: -8px 0 24px; font-size: 15px; line-height: 1.6; }
.hub-description p { margin: 0 0 10px; }
.hub-related h2 { font-size: 17px; margin: 26px 0 8px; }
.hub-links { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.hub-links li { background: var(--card); border: 1px solid var(--line); border-radius: 999px;
                padding: 6px 12px; font-size: 14px; }
.hub-links li a { color: var(--ink); font-weight: 500; }
.hub-links li a:hover { color: var(--accent); text-decoration: none; }
.hub-links .count { color: var(--muted); font-family: var(--f-mono); font-size: 11px;
                    font-variant-numeric: tabular-nums; margin-left: 4px; }
.hub-links.index { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px; }
.hub-links.index li { border-radius: 10px; padding: 12px 16px; display: flex;
                      justify-content: space-between; align-items: baseline; gap: 8px; }
.hub-cta { margin: 28px 0 0; font-weight: 600; }
.hub .cards { margin-top: 4px; }
.hub .pager { margin: 16px 0 0; }

/* Site footer — flat dark, not the header gradient: a second gradient at the
   bottom of the page reads as a mistake rather than a bookend. */
footer.site { background: var(--dark); color: var(--on-dark-2); margin-top: var(--s12);
              padding: var(--s8) 0 var(--s8); }
footer.site .footer-brand { margin: 0 0 var(--s4); font: 400 19px/1.3 var(--f-serif);
                            color: var(--on-dark); letter-spacing: -.01em; }
footer.site .footer-nav { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5);
                          margin-bottom: var(--s4); }
footer.site .footer-nav a { color: var(--on-dark); font: var(--t-body); font-weight: 500; }
footer.site .footer-note { margin: 0; font: var(--t-body); max-width: 720px; }
footer.site .footer-contact { margin: var(--s2) 0 0; font: var(--t-body); }
footer.site .footer-contact a { color: var(--on-dark); }

/* FAQ hub + entry pages */
.faq h2 { font-size: 19px; margin: 28px 0 10px; }
.faq-intro { color: var(--muted); max-width: 720px; margin: 0 0 8px; }
.faq-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.faq-link { background: var(--card); border: 1px solid var(--line); border-radius: 10px;
            padding: 12px 16px; }
.faq-link > a { font-weight: 600; font-size: 15px; color: var(--ink); }
.faq-link > a:hover { color: var(--accent); }
.faq-link .teaser { margin: 4px 0 0; font-size: 13px; color: var(--muted); line-height: 1.45; }
.faq-list.related .faq-link { padding: 10px 16px; }
.faq-entry .answer { max-width: 760px; font-size: 16px; line-height: 1.6; }
.faq-entry .answer p { margin: 0 0 12px; }
.faq-cta { margin: 20px 0 8px; font-weight: 600; }
.breadcrumbs { font-size: 13px; color: var(--muted); margin: 0 0 12px; }
.breadcrumbs a { color: var(--muted); }
.breadcrumbs a:hover { color: var(--accent); }
main { padding: 24px 0 48px; }
/* Editorial voice for page headings; the listing's h1 opts out via .results-h1. */
h1 { font: 400 30px/1.25 var(--f-serif); letter-spacing: -.015em; margin: 0 0 var(--s4); }
h2 { font-family: var(--f-serif); font-weight: 400; letter-spacing: -.01em; }
.hub-related h2, .faq h2 { font-size: 22px; }
.page-sub { color: var(--muted); font-size: 15px; margin: -10px 0 18px; max-width: 720px; }

/* Error pages (templates/bundles/TwigBundle/Exception/) */
.error-page { max-width: 640px; margin: var(--s12) auto var(--s16); text-align: center; }
.error-code { margin: 0 0 var(--s2); font: 600 13px/1 var(--f-mono); letter-spacing: var(--ls-caps);
              color: var(--muted); }
.error-page h1 { font: 400 32px/1.2 var(--f-serif); letter-spacing: -.01em; margin: 0 0 var(--s3); }
.error-lead { margin: 0 0 var(--s6); color: var(--muted); font-size: 15px; line-height: 1.6; }
.error-actions { display: flex; gap: var(--s5); align-items: center; justify-content: center;
                 margin: 0; font-size: 14px; }
.error-links { text-align: left; }

/* Full-bleed bands.
   The listing views neutralise the container on <main> so the white filter strip
   can span the viewport, and each band re-establishes the 1280px measure itself.
   Preferred over the margin-inline: calc(50% - 50vw) trick because 100vw includes
   the scrollbar on Windows/Linux, and the usual fix — overflow-x: hidden on html —
   creates a scroll container that silently kills position: sticky in descendants. */
body.view-listing main { padding: 0 0 var(--s16); }
body.view-listing main > .wrap { max-width: none; padding: 0; }
.band-inner { max-width: var(--container); margin: 0 auto; padding: 0 var(--gutter); }
.filterbar { background: var(--card); border-bottom: 1px solid var(--line); }
.results-band { padding: var(--s5) 0 0; }

/* Filters */
turbo-frame { display: block; }
/* Three rows, achieved purely by grid placement — the DOM order stays exactly as
   it was (search, five selects, price-range, check, tag-filter), so nothing in
   filters_controller.js or the Tom Select wiring has to move. */
/* Search takes a third of the row, as measured off the design; the five facets
   split the rest evenly. The mock draws them at differing widths, but sizing a
   select to its content would make the whole row jump every time a facet's
   longest value changes with the filters. */
.filters { display: grid; grid-template-columns: 2.5fr repeat(5, 1fr);
           gap: var(--s3) var(--s2); align-items: end; padding: var(--s4) 0 var(--s3); }
.filters > div { min-width: 0; }
.filters label, .filters .range-label { display: block; font: var(--t-label);
    text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--muted);
    margin-bottom: var(--s1); }
.filters select, .filters input[type=text], .filters input[type=number] {
    width: 100%; height: 38px; padding: 0 10px; border: 1px solid transparent;
    border-radius: var(--r-md); background: var(--field-bg); font: var(--t-body); color: var(--ink); }
.filters select { appearance: none; -webkit-appearance: none; padding-right: 28px; cursor: pointer;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2364748B' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 10px center; }
/* Leading search glyph, as a background so the input keeps its native behaviour. */
.filters #f-q { padding-left: 34px;
    background: var(--field-bg) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15' viewBox='0 0 24 24' fill='none' stroke='%2364748B' stroke-width='1.9' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='6.5'/%3E%3Cpath d='M16 16l4.5 4.5'/%3E%3C/svg%3E")
        no-repeat left 11px center; }
.filters select:focus, .filters input:focus {
    outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); }
/* padding-left, not the grid gap: the price value is flush to the right edge of
   the block beside it, so 8px alone reads as the two captions touching. */
.filters .check { grid-column: 6 / 7; justify-self: start; align-self: start;
                  padding-left: var(--s5); }
.filters .check-row { display: flex; align-items: center; gap: var(--s2);
                      margin-top: var(--s3); height: 20px; }
/* iOS-style switch, drawn entirely on the existing checkbox — no markup change. */
.filters .check input { appearance: none; -webkit-appearance: none; position: relative;
    width: 34px; height: 20px; flex: 0 0 auto; border-radius: var(--r-pill);
    background: var(--line); cursor: pointer; transition: background .15s; }
.filters .check input::after { content: ''; position: absolute; top: 2px; left: 2px;
    width: 16px; height: 16px; border-radius: 50%; background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .2); transition: transform .15s; }
.filters .check input:checked { background: var(--accent); }
.filters .check input:checked::after { transform: translateX(14px); }
.filters .check input:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-ring); }
.filters .check label { margin: 0; text-transform: none; letter-spacing: 0; font: var(--t-body);
                        color: var(--ink); cursor: pointer; }
/* Scoped to noscript: the form also contains the map trigger and the dialog's
   close button, which must not inherit the submit button's solid fill. */
.filters noscript button { background: var(--accent); color: var(--accent-ink); border: 0;
                  border-radius: var(--r-md); padding: 9px 18px; font: var(--t-body); cursor: pointer; }
.filters .price-range { grid-column: 1 / 6; align-self: start; display: grid;
                        /* not baseline: the 10px caption and the 14px value sit in
                           equal 18px line boxes, and baseline alignment nudged the
                           caption 1px off the availability one beside it */
                        grid-template-columns: 1fr auto; align-items: center; gap: 0 var(--s3); }
/* The price caption, its value and the availability caption share a line box,
   so the two blocks' second rows also start at the same y. */
.filters .price-range .range-label, .filters .check .range-label { margin: 0; line-height: 18px; }
.filters .price-range output { line-height: 18px; }
.filters .price-range output { justify-self: end; font: var(--t-price); font-size: 14px;
                               font-variant-numeric: tabular-nums; }
/* This div IS the noUi-target — price_range_controller builds the slider on it —
   so it must keep the 4px track height. Centre it against the 20px toggle beside
   it with margin, never by giving it a height: 12px row gap + (20-4)/2. */
.filters .price-range > div { grid-column: 1 / -1; margin-top: calc(var(--s3) + 8px); }

/* Tags share the last row with the map button: tags run to column 5, the button
   takes 5-7. These MUST stay above the media queries below — they have the same
   specificity as the responsive overrides, so if they sat further down the file
   they would silently win and the mobile layout would keep the desktop columns. */
.filters .tag-filter { min-width: 0; grid-column: 1 / 5; }
.filters .region-map-host { grid-column: 5 / 7; justify-self: end; align-self: end; }
.filters .tag-row { display: flex; flex-wrap: wrap; gap: 6px; }
.filters .tag-toggle { position: relative; display: inline-flex; margin: 0; cursor: pointer;
    text-transform: none; letter-spacing: 0; font-weight: 400; }
.filters .tag-toggle input { position: absolute; opacity: 0; pointer-events: none; }
.filters .tag-toggle span { display: inline-flex; align-items: baseline; gap: 5px;
    border: 1px solid var(--line); border-radius: var(--r-pill); background: #fff;
    padding: 4px 12px; font: var(--t-body); color: var(--ink);
    transition: background .12s, border-color .12s, color .12s; }
.filters .tag-toggle span small { font-family: var(--f-mono); font-size: 11px;
    font-variant-numeric: tabular-nums; color: var(--muted); }
.filters .tag-toggle:hover span { border-color: var(--accent); color: var(--accent); }
.filters .tag-toggle input:checked + span { background: var(--accent);
    border-color: var(--accent); color: var(--accent-ink); }
.filters .tag-toggle input:checked + span small { color: rgba(255, 255, 255, .75); }
.filters .tag-toggle input:focus-visible + span { border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring); }
/* Intermediate widths: six columns stop fitting well before the drawer kicks in. */
@media (max-width: 1100px) {
    .filters { grid-template-columns: repeat(3, 1fr); }
    .filters .price-range { grid-column: 1 / 3; }
    .filters .check { grid-column: 3 / 4; }
    .filters .tag-filter { grid-column: 1 / -1; }
    .filters .region-map-host { grid-column: 1 / -1; justify-self: end; }
}

/* Mobile: the whole filter band collapses behind the header's glyph button so the
   first screen shows voyages, not a form. The #filters-open checkbox sits outside
   the turbo frame (state survives frame reloads) and immediately before it, which
   is what the sibling combinator below depends on — do NOT wrap the frame. */
@media (max-width: 640px) {
    turbo-frame#voyages .filterbar .band-inner { display: none; }
    /* later than the desktop rule above, so on a phone :checked reveals rather
       than collapses */
    .filters-open-state:checked ~ turbo-frame#voyages .filterbar .band-inner { display: block; }
    .filters { grid-template-columns: repeat(2, 1fr); }
    .filters .price-range, .filters .check, .filters .tag-filter,
    .filters .region-map-host { grid-column: 1 / -1; }
    .filters .region-map-host { justify-self: stretch; }
    .map-btn { width: 100%; justify-content: center; }
    .filters .check { justify-self: start; }
    /* A 24px gutter each side eats too much of a phone screen. */
    .band-inner { padding: 0 var(--s4); }

    /* Compare tray: one compact row — chips and hint are desktop luxuries. */
    .compare-tray .ct-chips, .compare-tray .ct-hint { display: none; }
    .compare-tray .wrap { flex-wrap: nowrap; gap: 10px; }
    .compare-tray .ct-actions { margin-left: auto; }
}

/* Phone portrait: two columns leave ~160px per field, which is not enough for
   values like "HX (Hurtigruten Expeditions)". One field per row instead. */
@media (max-width: 480px) {
    .filters { grid-template-columns: 1fr; }
}

/* Tom Select theme (multi-value facet selects) */
@media (scripting: enabled) {
    /* hide the raw multi-select until Tom Select takes over — kills the
       tall-listbox flash on every frame refresh */
    .filters select[multiple]:not(.tomselected) { display: none; }
}
.filters .ts-wrapper { min-width: 0; }
/* Tom Select's internal search box is a bare <input>, which is implicitly
   type=text — so the field rule above was setting it to 38px and, with the
   control's own padding, rendering the select 10px taller than the search
   input beside it. */
.filters .ts-control input { height: auto; min-height: 0; }
.filters .ts-control { min-height: 38px; padding: 4px 28px 4px 9px; border: 1px solid transparent;
    border-radius: var(--r-md); font: var(--t-body); box-shadow: none; gap: 3px;
    background: var(--field-bg) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%2364748B' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E")
        no-repeat right 10px center; }
/* pills wrap onto new lines — the control grows downward */
.filters .ts-wrapper.multi .ts-control { flex-wrap: wrap; }
.filters .ts-wrapper.multi .ts-control > .item { max-width: 100%; white-space: normal;
    overflow-wrap: anywhere; text-align: left; }
.filters .ts-wrapper.focus .ts-control { border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring); }
.filters .ts-control input { font: var(--t-body); }
.filters .ts-wrapper.multi .ts-control > .item { background: var(--accent-soft); color: var(--accent);
    border-radius: var(--r-sm); padding: 1px 2px 1px 7px; font-size: 12px; }
.filters .ts-wrapper.multi .ts-control > .item .remove { border-left: 0; padding: 0 5px;
    border-radius: 0 6px 6px 0; }
.filters .ts-wrapper.multi .ts-control > .item .remove:hover { background: #d0e2ef; }
.ts-dropdown { border: 1px solid var(--line); border-radius: var(--r-md); font: var(--t-body);
    margin-top: 4px; box-shadow: var(--sh-pop); }
.ts-dropdown .option { padding: 7px 10px; }
.ts-dropdown .option.active { background: var(--accent-soft); color: var(--accent); }

/* Region map — SVG region filter in a modal (see _region_map.html.twig).
   The <dialog> keeps the .region-map class so all the shape/label rules below
   apply unchanged; only the trigger and the shell are new. */
.map-btn { display: inline-flex; align-items: center; gap: var(--s2);
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-md);
    padding: 8px 14px; font: var(--t-body); font-weight: 500; color: var(--ink);
    cursor: pointer; transition: border-color .12s, color .12s; }
.map-btn:hover { border-color: var(--accent); color: var(--accent); }
.map-btn .icon { width: 15px; height: 15px; color: var(--muted); }
.map-btn:hover .icon { color: var(--accent); }
.map-btn .on-count { background: var(--accent); color: var(--accent-ink);
    border-radius: var(--r-pill); font-size: 11px; line-height: 1; padding: 3px 7px; }
.region-dialog { max-width: min(96vw, 1040px); width: 100%; border: 0; padding: 0;
    border-radius: var(--r-lg); box-shadow: var(--sh-pop); color: var(--ink);
    background: var(--card); }
.region-dialog::backdrop { background: rgba(11, 29, 58, .45); }
.region-dialog-head { display: flex; align-items: center; justify-content: space-between;
    gap: var(--s4); padding: var(--s4) var(--s5) var(--s2); }
.region-dialog-head h2 { margin: 0; font: 400 20px/1.3 var(--f-serif); letter-spacing: -.01em; }
.dlg-x { display: inline-flex; align-items: center; justify-content: center; width: 30px;
    height: 30px; border: 0; border-radius: var(--r-sm); background: transparent;
    color: var(--muted); cursor: pointer; }
.dlg-x:hover { background: var(--bg); color: var(--ink); }
.dlg-x .icon { width: 17px; height: 17px; }
.region-map-body { padding: 0 var(--s3) var(--s3); overflow-x: auto; }
.region-map-body > svg { display: block; width: 100%; min-width: 860px; height: auto; }
.region-map .hint { margin: 6px 4px 2px; font-size: 12px; color: var(--muted); }

/* regions outside the map frames — pill toggles with the same data-region contract */
.region-map .far-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    margin: 10px 4px 0; }
.region-map .far-label { font-size: 12px; color: var(--muted); margin-right: 2px; }
.region-map .far-row .rg { display: inline-flex; align-items: baseline; gap: 5px;
    border: 1px solid var(--line); border-radius: 999px; background: var(--card);
    padding: 3px 11px; font-size: 12.5px; color: var(--ink); user-select: none; }
.region-map .far-row .rg small { font-size: 10.5px; color: var(--muted); }
.region-map .far-row .rg:hover, .region-map .far-row .rg:focus-visible {
    border-color: var(--accent); color: var(--accent); }
.region-map .far-row .rg.is-on { background: var(--accent); border-color: var(--accent);
    color: var(--accent-ink); }
.region-map .far-row .rg.is-on small { color: rgba(255, 255, 255, .75); }
.region-map .far-row .rg.is-off:not(.is-on) { color: #b3c2cc; background: transparent; }
.region-map .far-row .rg.is-off:not(.is-on) small { color: #b3c2cc; }

/* wildlife seen in the currently selected regions — links to /highlights/{slug} */
.region-map .map-wildlife { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    margin: 10px 4px 0; padding-top: 10px; border-top: 1px dashed var(--line); }
.region-map .wl-chip { display: inline-flex; border: 1px solid var(--line);
    border-radius: 999px; background: var(--card); padding: 3px 11px;
    font-size: 12.5px; color: var(--ink); }
.region-map .wl-chip:hover, .region-map .wl-chip:focus-visible {
    border-color: var(--accent); color: var(--accent); text-decoration: none; }

.region-map .panel { fill: #eaf2f7; stroke: var(--line); }
.region-map .land { fill: #dde8ee; stroke: #bfd2dd; stroke-width: 1.2; }
.region-map .isle { fill: #c6d8e2; stroke: #9fb9c9; stroke-width: 1; }
.region-map .crater { fill: #eaf2f7; }
.region-map .deco { fill: #a9bcc8; font-size: 11px; letter-spacing: .12em; }
.region-map .deco.big { font-size: 13px; }
.region-map .deco-line { fill: none; stroke: #c3d3dd; stroke-dasharray: 3 4; }
.region-map .inset-title { fill: var(--muted); font-size: 12.5px; font-weight: 600; }
.region-map text { font-size: 11px; fill: var(--ink); pointer-events: none; }
.region-map text.sm, .region-map .deco.sm { font-size: 9.5px; letter-spacing: 0; }
.region-map text .ct { font-size: 9px; font-weight: 700; fill: var(--muted); }

.region-map .rg { cursor: pointer; outline: none; }
.region-map .rg text { pointer-events: all; }
.region-map .rg .hit { fill: transparent; }
.region-map .rg .hitline { fill: none; stroke: transparent; stroke-width: 22; stroke-linecap: round; }
.region-map .rg .ring-hit { stroke-width: 12; }
.region-map .rg .lane-hit { stroke-width: 16; }
.region-map .rg .dot { fill: var(--accent); stroke: #fff; stroke-width: 1.5; }
.region-map .rg .zone { fill: var(--accent-soft); fill-opacity: .55; stroke: #aecbdd;
    stroke-dasharray: 4 3; }
.region-map .rg .shelf { fill: #f3f7fa; stroke: #aecbdd; stroke-dasharray: 4 3; }
.region-map .rg .lane { fill: none; stroke: #7ea8c4; stroke-width: 3; stroke-dasharray: 6 4;
    stroke-linecap: round; opacity: .7; }
.region-map .rg .armline { fill: none; stroke: var(--accent); stroke-width: 5;
    stroke-linecap: round; opacity: 0; }
.region-map .rg .latline { fill: none; stroke: #7ea8c4; stroke-width: 1.6; stroke-dasharray: 7 5; }
.region-map .rg .arm-hit { stroke-width: 26; }
.region-map .rg .chain-hit { stroke-width: 34; }

.region-map .rg:hover text, .region-map .rg:focus-visible text { fill: var(--accent); }
.region-map .rg:hover .dot { r: 6; }
.region-map .rg:hover .zone, .region-map .rg:focus-visible .zone { stroke: var(--accent);
    fill-opacity: .8; }
.region-map .rg:hover .isle { stroke: var(--accent); }
.region-map .rg:hover .clickland, .region-map .rg:focus-visible .clickland {
    fill: #cfe2ee; stroke: var(--accent); }
.region-map .rg:hover .armline { opacity: .35; }
.region-map .rg:hover .latline, .region-map .rg:hover .lane { stroke: var(--accent); opacity: 1; }
.region-map .rg:focus-visible .dot, .region-map .rg:focus-visible .isle { stroke: var(--accent);
    stroke-width: 2.5; }

.region-map .rg.is-on text { fill: var(--accent); font-weight: 700; }
.region-map .rg.is-on text .ct { fill: var(--accent); }
.region-map .rg.is-on .dot { r: 6.5; stroke: var(--accent); }
.region-map .rg.is-on .isle { fill: var(--accent); stroke: var(--accent); }
.region-map .rg.is-on .crater { fill: #fff; }
.region-map .rg.is-on .zone { fill: var(--accent); fill-opacity: .22; stroke: var(--accent);
    stroke-dasharray: none; }
.region-map .rg.is-on .shelf { stroke: var(--accent); stroke-dasharray: none; }
.region-map .rg.is-on .lane, .region-map .rg.is-on .latline { stroke: var(--accent);
    stroke-width: 4; opacity: 1; }
.region-map .rg.is-on .armline { opacity: .55; }
.region-map .rg.is-on .clickland { fill: #cfe2ee; stroke: var(--accent); stroke-width: 2; }

/* zero results under current filters: muted and inert (unless selected) */
.region-map .rg.is-off:not(.is-on) { pointer-events: none; }
.region-map .rg.is-off:not(.is-on) text { fill: #b3c2cc; }
.region-map .rg.is-off:not(.is-on) .dot { fill: #b9c6cf; }
.region-map .rg.is-off:not(.is-on) .isle { fill: #d4dfe6; stroke: #c2d0d9; }
.region-map .rg.is-off:not(.is-on) .zone,
.region-map .rg.is-off:not(.is-on) .shelf { fill-opacity: .25; stroke: #ccdae3; }
.region-map .rg.is-off:not(.is-on) .lane { stroke: #b9c6cf; }

/* noUiSlider theme */
.price-range .noUi-target { height: 4px; border: 0; box-shadow: none; background: var(--line);
    border-radius: var(--r-pill); }
.price-range .noUi-connect { background: var(--accent); }
.price-range .noUi-handle { width: 16px; height: 16px; right: -8px; top: -6px; border-radius: 50%;
    border: 2px solid var(--accent); background: #fff; box-shadow: none; cursor: grab; }
.price-range .noUi-handle:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-ring); }
.price-range .noUi-handle::before, .price-range .noUi-handle::after { display: none; }

/* Active filter chips */
.chips { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 14px; }
.chips a.chip { display: inline-flex; align-items: center; gap: 6px; background: var(--accent-soft);
    color: var(--accent); border-radius: 999px; padding: 4px 12px; font-size: 13px; }
.chips a.chip b { font-weight: 600; }
.chips a.chip:hover { text-decoration: none; background: #d6e7f3; }
.chips a.chip .x { font-weight: 700; opacity: .7; }
.chips a.clear-all { font: var(--t-body); color: var(--muted); margin-left: 4px; }

/* Count left, view switch dead-centre, sort tools right. The 1fr flanks keep
   the switch on the band's true centre line whatever the sides' widths; in the
   month views (no tools) the empty third column preserves the same geometry. */
.meta-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px;
            margin: 18px 0 12px; }
.meta-row > .view-switch { justify-self: center; }
.meta-row .meta-tools { justify-self: end; }
@media (max-width: 720px) {
    .meta-row { grid-template-columns: 1fr auto; }
    .meta-row > .view-switch { grid-column: 1 / -1; grid-row: 2; justify-self: center; }
}
/* The single <h1>, dressed as the design's plain result count. */
.results-h1 { margin: 0; font: var(--t-body); font-weight: 400; color: var(--muted); }
.results-h1 .num { font-family: var(--f-mono); font-variant-numeric: tabular-nums;
                   font-weight: 500; font-size: 14px; color: var(--ink); }
.meta-row .meta-tools { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }

/* List | Calendar | Timeline — segmented pill group */
.view-switch { display: inline-flex; gap: 2px; padding: 2px; border-radius: var(--r-sm);
               background: #E7ECF4; font: var(--t-body); }
.view-switch a, .view-switch .current { padding: 5px 14px; border-radius: var(--r-sm); }
.view-switch .current { background: var(--card); color: var(--ink); font-weight: 600;
                        box-shadow: var(--sh-segment); }
.view-switch a { color: var(--muted); background: transparent; }
.view-switch a:hover { text-decoration: none; color: var(--ink); }
.meta-row .sorter { display: flex; align-items: center; gap: var(--s2); font: var(--t-body);
                    color: var(--muted); }
/* Drawn as the design's underlined caps link, but still a native <select> so
   keyboard, screen readers and the frame's auto-submit keep working for free. */
.meta-row .sorter select { padding: 2px 16px 2px 0; border: 0; background: transparent;
    font: var(--t-caps); font-size: 11px; letter-spacing: var(--ls-label);
    text-transform: uppercase; color: var(--ink); text-decoration: underline;
    text-underline-offset: 3px; appearance: none; -webkit-appearance: none; cursor: pointer;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' stroke='%230D1B2A' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right center; }
.meta-row .sorter select:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Voyage cards
   The grid stays auto-fill rather than the prototype's fixed repeat(3, 1fr):
   at the 1232px inner width it yields exactly three columns, and it degrades to
   two and then one with no media query. Fixed columns are precisely why the
   prototype scrolls horizontally on a phone. */
/* min(320px, 100%) rather than a bare 320px: the floor would otherwise force a
   320px column inside a narrower container and push the card past the viewport
   on a 320px phone. */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr));
         gap: var(--s5); }
.vcard { position: relative; background: var(--card); border: 1px solid var(--line);
         border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--sh-card);
         display: flex; flex-direction: column;
         transition: box-shadow .15s ease, transform .15s ease; }
.vcard:hover { box-shadow: var(--sh-card-hover); transform: translateY(-2px); }
.vcard-media { position: relative; aspect-ratio: 3 / 2; overflow: hidden;
               background: var(--photo-placeholder); }
/* Absolutely positioned so the photo can never size its own container. In flow,
   aspect-ratio is only a *preferred* height, while the flex item's automatic
   min-height is its content — so a 4:3 or 5:4 source stretched the box past 3:2
   and cards in the same row ended up with different image heights. Out of flow,
   the ratio always wins and object-fit crops. Same approach as .ecard > img. */
.vcard-media img { position: absolute; inset: 0; display: block;
                   width: 100%; height: 100%; object-fit: cover; }
.vcard-media.placeholder { display: flex; align-items: center; justify-content: center;
    background: linear-gradient(160deg, #DFEAF1, #F0F5F8); color: #A8BCC9; }
.vcard-media.placeholder .icon { width: 40px; height: 56px; opacity: .55; }
.vcard-scrim { position: absolute; inset: 0; background: var(--scrim-card); pointer-events: none; }
.vcard-body { padding: var(--s4); display: flex; flex-direction: column; gap: var(--s2); flex: 1; }
.vcard-by { margin: 0; font: var(--t-caps); text-transform: uppercase;
            letter-spacing: var(--ls-label); }
/* The title's stretched link covers the whole card, so the real links inside the
   body have to be lifted above it or they stop being clickable. Easiest thing in
   this component to get wrong. */
.vcard-by a { position: relative; z-index: 2; color: var(--accent);
              text-decoration: underline; text-underline-offset: 2px; }
.vcard-by .sep { color: var(--muted); margin: 0 2px; }
/* Outside the <a> on purpose: in the design the underline runs under the words
   only, and the glyph stays neutral rather than picking up the link colour. */
.vcard-by .icon { width: 12px; height: 12px; margin-right: 4px; color: var(--muted); }
.vcard-title { margin: 0; font: var(--t-title); letter-spacing: -.01em; }
.vcard-title a { color: var(--ink); }
.vcard-title a::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.vcard-title a:hover { text-decoration: none; color: var(--accent); }
.vcard-dates, .vcard-port { margin: 0; display: flex; align-items: center; gap: 6px;
                            font: var(--t-body); color: var(--muted); }
.vcard-dates .icon, .vcard-port .icon { width: 13px; height: 13px; }
.vcard-dates .num { font: var(--t-num); font-variant-numeric: tabular-nums; }
.vcard-tags { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.vcard-tags li { font-size: 11px; line-height: 1.5; padding: 2px 8px; border-radius: var(--r-lg);
                 background: var(--teal-bg); color: var(--teal); border: 1px solid var(--teal-line); }
.vcard-foot { margin-top: auto; padding-top: var(--s3); border-top: 1px solid var(--line);
              display: flex; justify-content: space-between; align-items: flex-end; gap: var(--s2); }
.vcard-price .lbl { display: block; font: var(--t-label); letter-spacing: var(--ls-label);
                    text-transform: uppercase; color: var(--muted); }
.vcard-price b { display: block; font: var(--t-price); font-variant-numeric: tabular-nums;
                 letter-spacing: var(--ls-tight); }
.vcard-price small { display: block; margin-top: 1px; font: var(--t-body); font-size: 12px;
                     color: var(--muted); }
.vcard-price small .num { font-family: var(--f-mono); font-variant-numeric: tabular-nums;
                          color: var(--accent); }

/* Editorial promo cards — same grid cell as .vcard, photo edge to edge, copy
   over a scrim at the bottom. */
.ecard { position: relative; display: flex; border-radius: var(--r-lg); overflow: hidden;
         min-height: 420px; background: var(--dark); box-shadow: var(--sh-card);
         transition: box-shadow .15s ease, transform .15s ease; }
.ecard:hover { box-shadow: var(--sh-card-hover); transform: translateY(-2px); }
.ecard > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ecard-scrim { position: absolute; inset: 0; background: var(--scrim-editorial); }
.ecard-body { position: relative; margin-top: auto; width: 100%; padding: var(--s5);
              display: grid; grid-template-columns: 1fr auto; align-items: end;
              gap: 0 var(--s3); color: var(--on-dark); }
.ecard-eyebrow { grid-column: 1; margin: 0 0 var(--s1); font: var(--t-caps);
                 text-transform: uppercase; letter-spacing: var(--ls-caps);
                 color: rgba(255, 255, 255, .6); }
.ecard-title { grid-column: 1; margin: 0 0 6px; font: 400 18px/1.2 var(--f-serif);
               letter-spacing: -.01em; }
.ecard-title a { color: var(--on-dark); }
.ecard-title a::after { content: ''; position: absolute; inset: 0; }
.ecard-title a:hover { text-decoration: none; }
.ecard-blurb { grid-column: 1; margin: 0; font: var(--t-body); color: rgba(255, 255, 255, .75); }
.ecard-go { grid-column: 2; grid-row: 1 / -1; align-self: end; display: inline-flex;
            align-items: center; justify-content: center; width: 34px; height: 34px;
            border-radius: 50%; background: rgba(255, 255, 255, .2); color: var(--on-dark); }
.ecard:hover .ecard-go { background: rgba(255, 255, 255, .32); }
.ecard-go .icon { width: 17px; height: 17px; }

/* Highlight detail page */
.editorial { max-width: 760px; margin: 0 auto; }
.editorial-eyebrow { margin: 0 0 var(--s2); font: var(--t-caps); text-transform: uppercase;
                     letter-spacing: var(--ls-caps); color: var(--accent); }
.editorial h1 { font: 400 34px/1.2 var(--f-serif); letter-spacing: -.015em; margin: 0 0 var(--s3); }
.editorial-lead { margin: 0 0 var(--s6); font-size: 17px; line-height: 1.6; color: var(--muted); }
.editorial-hero { margin: 0 0 var(--s6); }
.editorial-hero img { display: block; width: 100%; height: auto; border-radius: var(--r-lg); }
.editorial-hero figcaption { margin-top: 6px; font-size: 12px; color: var(--muted); }
.editorial-hero figcaption a { color: var(--muted); text-decoration: underline; }
.editorial-body { font-size: 16px; line-height: 1.7; }
.editorial-body p { margin: 0 0 var(--s4); }
.editorial h2 { font: 400 24px/1.3 var(--f-serif); margin: var(--s10) 0 var(--s4); }
.editorial .cards { grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.editorial .ecard { min-height: 300px; }

/* Wildlife blocks on hub/voyage pages — compact editorial cards */
.hub-wildlife, .voyage-wildlife { margin-top: var(--s8); }
.hub-wildlife h2, .voyage-wildlife h2 { font: 400 24px/1.3 var(--f-serif); margin: 0 0 var(--s4); }
.hub-wildlife .cards, .voyage-wildlife .cards {
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.hub-wildlife .ecard, .voyage-wildlife .ecard { min-height: 300px; }

/* Status pills. The prototype only draws available and sold out; limited and
   unknown keep the same shape so all four VoyageStatus values stay legible. */
.badge { display: inline-flex; align-items: center; gap: 5px; font: var(--t-badge);
         padding: 3px 10px; border-radius: var(--r-pill); border: 1px solid transparent;
         white-space: nowrap; }
.badge::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.badge.available { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.badge.limited { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-line); }
.badge.sold_out { background: var(--bad-bg); color: var(--bad); border-color: var(--bad-line); }
.badge.unknown { background: var(--bg); color: var(--muted); border-color: var(--line); }
.tags { display: flex; flex-wrap: wrap; gap: 6px; }
.tags span { font-size: 12px; background: var(--bg); border-radius: var(--r-pill); padding: 2px 8px;
             color: var(--muted); }
.tags.addons span { background: var(--accent-soft); color: var(--accent); }

/* Calendar view */
.cal-months { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.cal-months a, .cal-months .current { padding: 6px 12px; border: 1px solid var(--line);
    border-radius: 999px; background: var(--card); font-size: 14px; color: var(--ink); }
.cal-months .current { background: var(--ink); color: #fff; border-color: var(--ink); }
.cal-months a:hover { text-decoration: none; background: var(--accent-soft); }
.cal-months small { font-size: 12px; opacity: .65; }

.calendar { background: var(--card); border: 1px solid var(--line); border-radius: 10px;
            overflow: hidden; }
.cal-head, .cal-week { display: grid; grid-template-columns: repeat(7, 1fr); }
.cal-head div { padding: 8px 10px; font-size: 12px; text-transform: uppercase;
    letter-spacing: .04em; color: var(--muted); background: var(--bg);
    border-bottom: 1px solid var(--line); }
.cal-day { min-height: 112px; min-width: 0; padding: 6px; display: flex; flex-direction: column;
           gap: 4px; border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.cal-week .cal-day:last-child { border-right: 0; }
.calendar .cal-week:last-child .cal-day { border-bottom: 0; }
.cal-day .d { font-size: 12px; color: var(--muted); }
.cal-day.out { background: var(--bg); }
.cal-day.out .d { opacity: .45; }
.cal-chip { display: block; background: var(--accent-soft); color: var(--accent);
            border-radius: 6px; padding: 3px 7px; font-size: 12px; line-height: 1.35; }
.cal-chip:hover { text-decoration: none; background: #d6e7f3; }
.cal-chip b { display: block; font-weight: 600; white-space: nowrap; overflow: hidden;
              text-overflow: ellipsis; }
.cal-chip span { opacity: .85; font-family: var(--f-mono); font-size: 11px;
                 font-variant-numeric: tabular-nums; }
.cal-chip.limited { background: #fdf1de; color: var(--warn); }
.cal-chip.limited:hover { background: #f8e6c6; }
.cal-chip.sold_out { background: #fbe5e3; color: var(--bad); }
.cal-chip.sold_out:hover { background: #f6d6d3; }
.cal-more summary { cursor: pointer; font-size: 12px; color: var(--accent); list-style: none; }
.cal-more summary::-webkit-details-marker { display: none; }
.cal-more summary:hover { text-decoration: underline; }
.cal-more[open] summary { display: none; }
.cal-more .cal-chip { margin-top: 4px; }

/* Timeline (Gantt) view — rows are ships, bars are voyages */
.tl-scroll { overflow-x: auto; background: var(--card); border: 1px solid var(--line);
             border-radius: 10px; }
.timeline { --day-w: 32px; --ship-w: 200px;
            min-width: calc(var(--ship-w) + var(--days) * var(--day-w)); }
.tl-row { display: flex; border-bottom: 1px solid var(--line); }
.timeline .tl-row:last-child { border-bottom: 0; }
.tl-ship { flex: 0 0 var(--ship-w); position: sticky; left: 0; z-index: 2;
           background: var(--card); padding: 8px 12px; border-right: 1px solid var(--line); }
.tl-ship b { display: block; font-size: 13px; line-height: 1.3; }
.tl-ship small { color: var(--muted); font-size: 11px; }
.tl-head .tl-ship { background: var(--bg); font-size: 11px; text-transform: uppercase;
                    letter-spacing: .04em; color: var(--muted); font-weight: 600; }
.tl-days { display: grid; grid-template-columns: repeat(var(--days), var(--day-w));
           flex: 1 0 auto; background: var(--bg); }
.tl-days div { padding: 8px 0 6px; font: var(--t-num); font-size: 11px;
               font-variant-numeric: tabular-nums; text-align: center; color: var(--muted); }
.tl-days div.we { background: rgba(20, 35, 46, .05); }
.tl-track { flex: 1 0 auto; padding: 4px 0;
    background-image: repeating-linear-gradient(to right,
        transparent 0, transparent calc(var(--day-w) - 1px),
        var(--line) calc(var(--day-w) - 1px), var(--line) var(--day-w));
    background-size: calc(var(--days) * var(--day-w)) 100%; background-repeat: no-repeat; }
.tl-lane { display: grid; grid-template-columns: repeat(var(--days), var(--day-w)); height: 32px; }
.tl-bar { display: flex; align-items: center; min-width: 0; margin: 2px;
          background: var(--accent-soft); color: var(--accent);
          border: 1px solid rgba(13, 92, 140, .3); border-radius: 6px;
          padding: 0 7px; font-size: 12px; }
.tl-bar span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
.tl-bar:hover { text-decoration: none; background: #d6e7f3; }
.tl-bar.limited { background: #fdf1de; color: var(--warn); border-color: rgba(178, 106, 0, .35); }
.tl-bar.limited:hover { background: #f8e6c6; }
.tl-bar.sold_out { background: #fbe5e3; color: var(--bad); border-color: rgba(179, 54, 45, .3); }
.tl-bar.sold_out:hover { background: #f6d6d3; }
.tl-bar.clip-s { border-top-left-radius: 0; border-bottom-left-radius: 0;
                 border-left-style: dashed; margin-left: 0; }
.tl-bar.clip-e { border-top-right-radius: 0; border-bottom-right-radius: 0;
                 border-right-style: dashed; margin-right: 0; }
.tl-hint { color: var(--muted); font-size: 13px; margin-top: 10px; }

/* Narrow screens: the grid degrades into a per-day agenda list */
.cal-agenda { display: none; }
@media (max-width: 720px) {
    .calendar { display: none; }
    .cal-agenda { display: block; }
    .agenda-day { margin-bottom: 16px; }
    .agenda-day h3 { font-size: 13px; text-transform: uppercase; letter-spacing: .04em;
                     color: var(--muted); margin: 0 0 6px; }
    .agenda-item { display: flex; justify-content: space-between; align-items: center; gap: 12px;
        background: var(--card); border: 1px solid var(--line); border-radius: 8px;
        padding: 10px 12px; margin-bottom: 6px; color: var(--ink); }
    .agenda-item:hover { text-decoration: none; border-color: var(--accent); }
    .agenda-item .what { min-width: 0; }
    .agenda-item .what b { display: block; font-size: 15px; }
    .agenda-item .what small { display: block; color: var(--muted); font-size: 13px;
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .agenda-item .price { font-weight: 700; font-size: 14px; white-space: nowrap; }
}

/* Pagination */
.pager { display: flex; gap: 8px; justify-content: center; margin-top: 24px; flex-wrap: wrap; }
.pager a, .pager span { padding: 6px 12px; border: 1px solid var(--line);
    border-radius: var(--r-md); background: var(--card); font: var(--t-num); font-size: 13px;
    font-variant-numeric: tabular-nums; }
.pager span.gap { border: 0; background: none; color: var(--muted); }
.pager span.current { background: var(--accent); color: #fff; border-color: var(--accent); }

/* Offers table (voyage detail) */
table.offers { width: 100%; border-collapse: collapse; background: var(--card);
               border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
table.offers th, table.offers td { padding: 10px 12px; text-align: left; font-size: 14px;
                                   border-bottom: 1px solid var(--line); }
table.offers th { background: var(--bg); font: var(--t-label); letter-spacing: var(--ls-label);
                  text-transform: uppercase;
                  letter-spacing: .04em; color: var(--muted); }
table.offers tr:last-child td { border-bottom: 0; }
table.offers tr.best td { background: #f0f8f3; }
table.offers .best-tag { display: inline-block; font-size: 11px; font-weight: 700; color: var(--ok);
    text-transform: uppercase; letter-spacing: .04em; margin-left: 6px; }
@media (max-width: 720px) {
    table.offers, table.offers tbody, table.offers tr, table.offers td { display: block; }
    table.offers thead { display: none; }
    table.offers tr { border-bottom: 6px solid var(--bg); padding: 6px 0; }
    table.offers tr:last-child { border-bottom: 0; }
    table.offers td { border: 0; padding: 4px 14px; }
    table.offers td[data-label]::before { content: attr(data-label); display: inline-block;
        min-width: 110px; font-size: 12px; text-transform: uppercase; letter-spacing: .04em;
        color: var(--muted); }
    table.offers td:empty { display: none; }
}

.btn { display: inline-block; background: var(--accent); color: #fff !important;
       border-radius: var(--r-md); padding: 9px 16px; font: var(--t-body); font-weight: 600;
       white-space: nowrap; }
.btn:hover { text-decoration: none; opacity: .92; }
.btn.ghost { background: transparent; color: var(--accent) !important; border: 1px solid var(--line); }
.btn.ghost:hover { border-color: var(--accent); opacity: 1; }
.btn.disabled { pointer-events: none; opacity: .45; cursor: default; }

/* Compare — card selector.
   The stretched link lives on .vcard-title (see the Voyage cards block); this pill
   sits above it at z-index 3, and the operator/ship links at z-index 2. */
.compare-check { position: absolute; top: var(--s2); right: var(--s2); z-index: 3;
    display: inline-flex; align-items: center; gap: 6px; font: var(--t-body);
    font-weight: 600; color: var(--ink);
    background: rgba(255, 255, 255, .92); border: 1px solid var(--line);
    border-radius: var(--r-pill);
    padding: 4px 10px 4px 7px; cursor: pointer; backdrop-filter: blur(2px); }
.compare-check:hover { border-color: var(--accent); }
.compare-check input { accent-color: var(--accent); margin: 0; cursor: pointer; }
.compare-check input:disabled { cursor: not-allowed; }
.compare-check:has(input:disabled) { opacity: .5; cursor: not-allowed; }

/* Compare — sticky tray */
.compare-tray { position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
    background: var(--card); border-top: 1px solid var(--line);
    box-shadow: 0 -4px 20px rgba(20, 35, 46, .12); padding: 10px 0;
    padding-bottom: calc(10px + env(safe-area-inset-bottom)); }
.compare-tray[hidden] { display: none; }
.compare-tray.pulse { animation: ct-pulse .4s ease; }
@keyframes ct-pulse { 0%, 100% { box-shadow: 0 -4px 20px rgba(20, 35, 46, .12); }
    40% { box-shadow: 0 -4px 24px rgba(179, 54, 45, .5); } }
.compare-tray .wrap { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.ct-lead { line-height: 1.2; }
.ct-lead b { font-size: 15px; }
.ct-hint { display: block; font-size: 12px; color: var(--muted); }
.ct-chips { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 auto; min-width: 0; }
.compare-chip { display: inline-flex; align-items: center; gap: 4px; max-width: 220px;
    font-size: 12px; background: var(--accent-soft); color: var(--ink);
    border-radius: 999px; padding: 3px 4px 3px 10px; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; }
.compare-chip-x { border: 0; background: none; cursor: pointer; font-size: 15px; line-height: 1;
    color: var(--muted); padding: 0 4px; }
.compare-chip-x:hover { color: var(--bad); }
.ct-actions { display: flex; gap: 8px; margin-left: auto; }

/* Compare — table page */
.compare-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.compare-head .count { color: var(--muted); font-size: 14px; }
.compare-empty svg { width: 56px; height: 56px; color: #a8bcc9; }
.compare-empty p { max-width: 420px; margin: 12px auto 20px; }
.compare-wrap { overflow-x: auto; margin: 16px 0; -webkit-overflow-scrolling: touch;
    background: var(--card); border: 1px solid var(--line); border-radius: 12px;
    box-shadow: 0 6px 18px rgba(20, 35, 46, .06); }
/* table-layout: fixed — every trip column gets the same width regardless of
   content (the min-width for N columns is set inline on the table) */
.compare-table { border-collapse: separate; border-spacing: 0; width: 100%; table-layout: fixed; }
/* row-label column stays put while the trip columns scroll under it */
.compare-table th[scope="row"] { position: sticky; left: 0; z-index: 2; background: var(--card);
    text-align: left; font-size: 12px; text-transform: uppercase;
    letter-spacing: .04em; color: var(--muted); font-weight: 600; white-space: nowrap;
    padding: 12px 14px 12px 16px; vertical-align: middle; width: 130px;
    border-bottom: 1px solid var(--line); box-shadow: 1px 0 0 var(--line); }
.compare-table td { border-left: 1px solid var(--line); border-bottom: 1px solid var(--line);
    padding: 12px 14px; vertical-align: top; font-size: 15px; overflow-wrap: anywhere; }
.compare-table tr:last-child :is(td, th) { border-bottom: 0; }
.compare-table .c-section :is(th, td) { background: var(--bg); padding-top: 8px; padding-bottom: 8px;
    font-size: 11px; }
.compare-table .c-photo td { vertical-align: bottom; border-bottom-color: transparent; padding-bottom: 4px; }
.compare-table .c-photo th { border-bottom-color: transparent; box-shadow: none; background: transparent; }
.c-thumb { display: block; width: 100%; height: 130px; border-radius: 8px; overflow: hidden;
    background: var(--accent-soft); }
.c-thumb.placeholder { display: flex; align-items: center; justify-content: center; color: var(--accent); }
.c-thumb.placeholder svg { width: 44px; height: 44px; }
.c-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform .2s ease; }
.c-thumb:hover img { transform: scale(1.03); }
.c-overline { display: block; margin-top: 10px; font-size: 11px; text-transform: uppercase;
    letter-spacing: .05em; color: var(--muted); }
.c-title { display: block; margin-top: 2px; font-weight: 700; font-size: 15px; color: inherit; line-height: 1.3; }
.c-remove { display: inline-block; margin-top: 6px; font-size: 12px; color: var(--muted); }
.c-remove:hover { color: var(--bad); text-decoration: none; }
.c-price.is-best { background: #f0f8f3; }
.c-price b { font: var(--t-price); font-size: 18px; font-variant-numeric: tabular-nums; }
.c-price small { display: block; color: var(--muted); font-size: 12px; }
.val-tag { display: inline-block; margin-top: 4px; font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .03em; color: var(--ok);
    background: #e2f3ea; border-radius: 999px; padding: 1px 8px; }
td .val-tag { margin-left: 6px; }
.c-price .val-tag { display: block; width: fit-content; margin-left: 0; }
.c-route { font-size: 13.5px; color: var(--muted); line-height: 1.45; }
.compare-table .muted { color: var(--muted); }
.compare-table .tags { margin: 0; }
.c-actions .btn { display: block; text-align: center; }
.c-actions .btn + .btn { margin-top: 6px; }

/* Compare — "AI match" rows inside the table (filled by ai_compare_controller) */
.compare-table tr.c-ai-row :is(td, th) { background: #f3f9ff; }
.compare-table .c-ai-head th { color: var(--accent); }
.c-ai-intro { font-size: 13.5px; }
.c-ai-cell { font-size: 13.5px; }
.ai-headline { margin: 0 0 8px; font-size: 13px; font-weight: 600; color: var(--accent); }
.c-ai-cell ul { margin: 0 0 10px; padding-left: 16px; }
.c-ai-cell li { margin: 3px 0; }
.ai-ideal { margin: 0; font-size: 13px; color: var(--muted);
    border-top: 1px dashed #cfe0ed; padding-top: 8px; }
.ai-ideal b { color: var(--ink); font-weight: 600; }
.c-ai-tip { font-size: 13px; }
.c-ai-tip:not(:empty)::before { content: "💡 "; }
/* skeleton loaders shown in the AI cells while /compare/ai runs */
.ai-skeleton { display: grid; gap: 8px; padding: 2px 0; }
.ai-skeleton span { height: 11px; border-radius: 6px;
    background: linear-gradient(90deg, #dde9f2 25%, #eef5fa 45%, #dde9f2 65%);
    background-size: 200% 100%; animation: ai-shimmer 1.2s linear infinite; }
.ai-skeleton span:nth-child(2) { width: 82%; }
.ai-skeleton span:nth-child(3) { width: 64%; }
@keyframes ai-shimmer { to { background-position: -200% 0; } }

/* Compare — route map thumbnails (click to zoom via glightbox) */
.c-map { display: block; height: 110px; border: 1px solid var(--line); border-radius: 8px;
    overflow: hidden; background: #fff; cursor: zoom-in; }
.c-map img { width: 100%; height: 100%; object-fit: contain; padding: 4px; }
.c-map:hover { border-color: var(--accent); }

/* Price trend chart */
.price-trend { background: var(--card); border: 1px solid var(--line); border-radius: 10px;
    padding: 16px; margin: 8px 0 24px; }
.pt-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.price-trend h2 { margin: 0; font-size: 18px; }
.pt-sub { margin: 2px 0 0; font-size: 13px; color: var(--muted); }
.pt-delta { font-family: var(--f-mono); font-weight: 600; font-size: 15px;
             font-variant-numeric: tabular-nums; text-align: right; line-height: 1.25;
             white-space: nowrap; }
.pt-delta small { display: block; font-size: 11px; font-weight: 400; color: var(--muted); }
.pt-delta.down { color: var(--ok); }
.pt-delta.up { color: var(--bad); }
.pt-delta.flat { color: var(--muted); }
.pt-plot { position: relative; margin: 14px 0 0; }
.pt-chart { display: block; width: 100%; height: 120px; overflow: visible; }
.pt-line-now { stroke: var(--accent); stroke-width: 2; stroke-dasharray: 3 4; opacity: .55;
               vector-effect: non-scaling-stroke; }
.pt-mark { position: absolute; transform: translate(-50%, calc(-100% - 8px)); white-space: nowrap;
           font-size: 11px; font-weight: 600; color: var(--ink); background: rgba(255, 255, 255, .92);
           border: 1px solid var(--line); border-radius: 6px; padding: 1px 6px; pointer-events: none; }
.pt-mark.below { transform: translate(-50%, 10px); }
.pt-mark.down { color: var(--ok); }
.pt-mark.up { color: var(--bad); }
.pt-axis { position: relative; height: 16px; margin: 2px 0 6px; font-size: 11px; color: var(--muted); }
.pt-axis span { position: absolute; transform: translateX(-50%); white-space: nowrap; }
.pt-axis .pt-axis-now { font-weight: 600; color: var(--accent); }
.pt-area { fill: var(--accent-soft); stroke: none; }
.pt-line { fill: none; stroke: var(--accent); stroke-width: 2; vector-effect: non-scaling-stroke;
    stroke-linejoin: round; stroke-linecap: round; }
/* Dots are HTML overlays, not SVG circles: the chart SVG stretches with
   preserveAspectRatio="none", which would smear a <circle> into an ellipse. */
.pt-dot { position: absolute; width: 8px; height: 8px; border-radius: 50%;
    background: var(--accent); transform: translate(-50%, -50%); }
.pt-dot-last { width: 10px; height: 10px; box-shadow: 0 0 0 2px var(--card); }
.pt-foot { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 8px;
    font-size: 13px; color: var(--muted); border-top: 1px solid var(--line); padding-top: 10px; }
.pt-foot b { color: var(--ink); }
.pt-note { margin: 8px 0 0; font-size: 12px; color: var(--muted); }

/* Detail media */
.media-row { display: flex; flex-wrap: wrap; gap: 16px; margin: 16px 0; }
.media-row figure { margin: 0; flex: 1 1 320px; }
.media-row img { display: block; width: 100%; border: 1px solid var(--line); border-radius: 10px;
                 object-fit: cover; background: var(--card); }
.media-row a { display: block; cursor: zoom-in; }
.media-hero img { height: 300px; }
.media-map img { height: 300px; object-fit: contain; padding: 8px; }
.media-map figcaption { font-size: 12px; color: var(--muted); text-align: center; margin-top: 4px; }

.days { background: var(--card); border: 1px solid var(--line); border-radius: 10px;
        margin: 0 0 24px; overflow: hidden; }
.days details { border-bottom: 1px solid var(--line); }
.days details:last-child { border-bottom: 0; }
.days summary { padding: 12px 16px; cursor: pointer; list-style: none; }
.days summary::-webkit-details-marker { display: none; }
.days summary::before { content: "▸ "; color: var(--muted); }
.days details[open] summary::before { content: "▾ "; }
.days p { margin: 0; padding: 0 16px 14px 32px; color: var(--muted); font-size: 15px; }
.facts { display: flex; flex-wrap: wrap; gap: 24px; background: var(--card); padding: 16px;
         border: 1px solid var(--line); border-radius: 10px; margin: 16px 0; }
.facts div b { display: block; font-size: 12px; text-transform: uppercase;
               letter-spacing: .04em; color: var(--muted); font-weight: 600; }
.empty { text-align: center; color: var(--muted); padding: 48px 0; }

/* Sticky cheapest-offer bar (voyage detail) */
body.has-offer-bar main { padding-bottom: 96px; }
.offer-bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
    background: var(--card); border-top: 1px solid var(--line);
    box-shadow: 0 -4px 16px rgba(20, 35, 46, .08); padding: 10px 0;
    padding-bottom: calc(10px + env(safe-area-inset-bottom)); }
.offer-bar .wrap { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.offer-bar .from { line-height: 1.3; }
.offer-bar .from b { font-size: 19px; }
.offer-bar .from small { display: block; font-size: 12px; color: var(--muted); }

/* Turbo progress bar */
.turbo-progress-bar { background: var(--accent); height: 3px; }

/* Loading state — Turbo puts [busy] on the frame while it fetches */
turbo-frame[busy] :is(.chips, .meta-row, .cards, .pager, .empty, .cal-months, .calendar, .cal-agenda, .tl-scroll) {
    opacity: .45; pointer-events: none; transition: opacity .2s ease; }
turbo-frame[busy]::after { content: ""; position: fixed; top: 50%; left: 50%;
    width: 36px; height: 36px; margin: -18px 0 0 -18px; z-index: 40;
    border: 3px solid var(--line); border-top-color: var(--accent); border-radius: 50%;
    animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* The redesign adds card lifts, the tray pulse, the AI shimmer and the frame
   spinner — honour the OS preference to switch them off. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
    .vcard:hover, .ecard:hover { transform: none; }
}

/* Back-office: scrape board and login.
   Both used to carry their own inline <style> block with a private blue
   (#1055c9) that predated the token layer. Folded in here so the last hardcoded
   palette in the codebase is gone. */
.admin-scrape h1 { margin: 0 0 var(--s1); }
.admin-scrape .subtitle { color: var(--muted); font: var(--t-body); margin: 0 0 var(--s5); }
.admin-scrape table { width: 100%; border-collapse: collapse; font: var(--t-body);
    background: var(--card); border: 1px solid var(--line); border-radius: var(--r-lg);
    overflow: hidden; }
.admin-scrape th, .admin-scrape td { padding: var(--s2) var(--s3); text-align: left;
    vertical-align: top; border-bottom: 1px solid var(--line); }
.admin-scrape th { font: var(--t-label); letter-spacing: var(--ls-label); text-transform: uppercase;
    color: var(--muted); background: var(--bg); white-space: nowrap; }
.admin-scrape td.num { text-align: right; font-family: var(--f-mono);
    font-variant-numeric: tabular-nums; }
.admin-scrape .muted { color: var(--muted); }
.admin-scrape .badge.success { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-line); }
.admin-scrape .badge.warning { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-line); }
.admin-scrape .badge.failed { background: var(--bad-bg); color: var(--bad); border-color: var(--bad-line); }
.admin-scrape .badge.running { background: var(--accent-soft); color: var(--accent);
    border-color: rgba(0, 95, 204, .2); }
.admin-scrape .badge.never { background: var(--bg); color: var(--muted); border-color: var(--line); }
.admin-scrape details { max-width: 26rem; }
.admin-scrape details summary { cursor: pointer; color: var(--bad); }
.admin-scrape details ul { margin: var(--s1) 0 0; padding-left: 1.1rem; }
.admin-scrape details li { margin-bottom: var(--s1); overflow-wrap: anywhere; }
.admin-scrape .stats { color: var(--muted); font-size: 12px; }
.admin-scrape button { padding: 5px var(--s3); border: 1px solid var(--accent);
    border-radius: var(--r-sm); background: var(--card); color: var(--accent);
    font: var(--t-body); font-weight: 600; cursor: pointer; }
.admin-scrape button:hover:not([disabled]) { background: var(--accent); color: var(--accent-ink); }
.admin-scrape button[disabled] { border-color: var(--line); color: var(--muted); cursor: not-allowed; }
.admin-scrape .flash { padding: var(--s3) var(--s4); border-radius: var(--r-md);
    margin-bottom: var(--s4); font: var(--t-body); }
.admin-scrape .flash.success { background: var(--ok-bg); color: var(--ok); }
.admin-scrape .flash.warning { background: var(--warn-bg); color: var(--warn); }

.login-box { max-width: 22rem; margin: var(--s16) auto; background: var(--card);
    border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s8);
    box-shadow: var(--sh-card); }
.login-box h1 { margin: 0 0 var(--s5); font-size: 24px; }
.login-box label { display: block; margin: var(--s4) 0 var(--s1); font: var(--t-label);
    letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); }
.login-box input { width: 100%; height: 40px; padding: 0 var(--s3); border: 1px solid var(--line);
    border-radius: var(--r-md); font-size: 15px; color: var(--ink); }
.login-box input:focus { outline: none; border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring); }
.login-box button { margin-top: var(--s6); width: 100%; padding: 10px; border: 0;
    border-radius: var(--r-md); background: var(--accent); color: var(--accent-ink);
    font-weight: 600; font-size: 15px; cursor: pointer; }
.login-box button:hover { opacity: .92; }
.login-box .error { background: var(--bad-bg); color: var(--bad); border: 1px solid var(--bad-line);
    padding: var(--s3) var(--s4); border-radius: var(--r-md); margin-bottom: var(--s4);
    font: var(--t-body); }
