/* ===========================================================================
   travel-explore.css — Flights / Hotels / Cruises / Tour Packages
   ===========================================================================
   Its own stylesheet and its own `tx-` namespace so nothing here can reach the
   V1 landing page above it. main.css is not modified by this feature.

   DARK MODE, AND WHY IT IS OPT-IN RATHER THAN @media
   The V1 landing page has NO dark mode — main.css contains zero
   prefers-color-scheme blocks and its palette is light by construction. If
   these sections honoured the OS preference on their own, a visitor with a dark
   system theme would get a light header, a light hero, one dark band, and a
   light footer. That is worse than no dark mode.

   So the dark palette below is complete and ready, but keyed to an explicit
   :root[data-theme="dark"] which nothing currently sets. The day the rest of
   the page gains dark mode it sets that attribute and these sections are
   already correct — no work here, and no half-dark page in the meantime.
   =========================================================================== */

.tx-scope {
  /* Local aliases of the page tokens, so the dark override below is one block
     instead of a repeat of every rule. */
  --tx-bg:        var(--bg, #F8FAFC);
  --tx-surface:   var(--white, #fff);
  --tx-surface-2: #F4F7FB;
  --tx-ink:       var(--navy, #0A2540);
  --tx-muted:     var(--muted, #5B6B82);
  --tx-line:      var(--line, rgba(10,37,64,.10));
  --tx-accent:    var(--coral, #FF4D4D);
  /* Ink ON an accent fill. It was #fff for a coral accent; the brand accent
     is gold now (~1.9:1 under white), so this follows --on-accent. */
  --tx-accent-ink:var(--on-accent, #12253F);
  --tx-gold:      var(--gold, #FFB020);
  --tx-emerald:   var(--emerald, #12B76A);
  --tx-shadow:    0 1px 2px rgba(10,37,64,.04), 0 8px 24px rgba(10,37,64,.06);
  --tx-shadow-lg: 0 2px 6px rgba(10,37,64,.06), 0 18px 44px rgba(10,37,64,.12);
  --tx-radius:    16px;
  /* The travelling band in a skeleton shimmer. A TOKEN because it has to
     reverse with the theme: a dark tint over a light placeholder reads as the
     sweep, and the same tint over the dark palette's #1A2439 computes to
     within a shade of the surface itself — an animation that runs and cannot
     be seen. */
  --tx-sk-hi:     rgba(10, 37, 64, .07);
}

/* Dark palette. Declared twice on purpose: the media query follows the OS, the
   attribute follows an explicit choice and must be able to beat it in BOTH
   directions (a visitor on a dark OS who picks light gets light). Combining
   them into one selector list is not possible across a media query. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .tx-scope {
    --tx-bg:        #0B1220;
    --tx-surface:   #131C2E;
    --tx-surface-2: #1A2439;
    --tx-ink:       #E8EEF7;
    --tx-muted:     #93A3BA;
    --tx-line:      rgba(232,238,247,.14);
    --tx-shadow:    0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.34);
    --tx-shadow-lg: 0 2px 6px rgba(0,0,0,.45), 0 18px 44px rgba(0,0,0,.5);
    --tx-sk-hi:     rgba(232,238,247,.10);
  }
}
:root[data-theme="dark"] .tx-scope {
  --tx-bg:        #0B1220;
  --tx-surface:   #131C2E;
  --tx-surface-2: #1A2439;
  --tx-ink:       #E8EEF7;
  --tx-muted:     #93A3BA;
  --tx-line:      rgba(232,238,247,.14);
  --tx-shadow:    0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.34);
  --tx-shadow-lg: 0 2px 6px rgba(0,0,0,.45), 0 18px 44px rgba(0,0,0,.5);
  --tx-sk-hi:     rgba(232,238,247,.10);
}

/* --------------------------------------------------------------------------
   Shell
   -------------------------------------------------------------------------- */
.tx-scope { background: var(--tx-bg); color: var(--tx-ink); }
/* #siteHeader is position:fixed and ~128px tall, so a section scrolled to
   block:'start' lands UNDER it and loses its heading. scroll-margin-top is what
   the nav links land against — without it "Flights" arrives on a decapitated
   section. */
/* scroll-margin-top matches the sticky offsets above: a static header leaves
   nothing overhead for an anchored scroll to duck under. */
.tx-section { max-width: 1200px; margin: 0 auto; padding: 56px 20px; scroll-margin-top: 16px; }
.tx-section + .tx-section { padding-top: 8px; }

.tx-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 22px; }
.tx-head h2 { font-size: clamp(22px, 3vw, 30px); line-height: 1.2; margin: 0 0 6px; color: var(--tx-ink); }
.tx-head p { margin: 0; color: var(--tx-muted); font-size: 14.5px; }

/* Says plainly that the content is demo data, so nobody mistakes a sample
   tariff for a quote. Removed per-section as each real source lands. */
.tx-sample {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  padding: 5px 11px; border-radius: 999px; font-size: 11.5px; font-weight: 800;
  letter-spacing: .04em; text-transform: uppercase;
  background: color-mix(in srgb, var(--tx-gold) 16%, transparent);
  color: #7A5200; border: 1px solid color-mix(in srgb, var(--tx-gold) 45%, transparent);
}
:root[data-theme="dark"] .tx-scope .tx-sample { color: var(--tx-gold); }

/* --------------------------------------------------------------------------
   Step 1 — the search panel
   -------------------------------------------------------------------------- */
.tx-search-panel {
  background: var(--tx-surface); border: 1px solid var(--tx-line);
  border-radius: var(--tx-radius); padding: 18px; margin-bottom: 20px;
  box-shadow: var(--tx-shadow);
}
.tx-trip { display: flex; gap: 18px; margin-bottom: 16px; }
.tx-radio { display: inline-flex; align-items: center; gap: 7px; font-size: 13.5px;
  font-weight: 700; color: var(--tx-ink); cursor: pointer; }
.tx-radio input { width: 16px; height: 16px; accent-color: var(--tx-accent); margin: 0; }

.tx-searchgrid { display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); align-items: end; }
.tx-sf label { display: block; font-size: 11px; font-weight: 800; letter-spacing: .05em;
  text-transform: uppercase; color: var(--tx-muted); margin-bottom: 6px; }
.tx-sf input, .tx-sf select {
  width: 100%; padding: 11px 12px; border-radius: 10px; border: 1.5px solid var(--tx-line);
  background: var(--tx-surface); color: var(--tx-ink);
  /* 16px — anything less and iOS zooms the page on focus. */
  font-size: 16px; font-family: inherit; outline: none; transition: border-color .18s ease;
}
.tx-sf input:focus, .tx-sf select:focus { border-color: var(--tx-accent); }
.tx-searchgo { min-height: 44px; }
@media (max-width: 560px) { .tx-searchgrid { grid-template-columns: 1fr 1fr; }
  .tx-searchgo { grid-column: 1 / -1; } }

/* The commercial facts under a flight's route line. */
.tx-facts { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.tx-fact { font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 999px;
  background: var(--tx-surface-2); color: var(--tx-muted); border: 1px solid var(--tx-line); }
.tx-fact.is-good { color: var(--tx-emerald);
  border-color: color-mix(in srgb, var(--tx-emerald) 45%, transparent); }
.tx-fact.is-warn { color: var(--tx-accent);
  border-color: color-mix(in srgb, var(--tx-accent) 45%, transparent); }
.tx-fare i { display: block; font-style: normal; font-size: 11px; color: var(--tx-muted); font-weight: 600; }

/* --------------------------------------------------------------------------
   Toolbar — search + sort
   -------------------------------------------------------------------------- */
.tx-toolbar { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.tx-search { position: relative; flex: 1 1 280px; min-width: 0; }
.tx-search svg { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; stroke: var(--tx-muted); fill: none; stroke-width: 2; }
.tx-search input {
  width: 100%; padding: 13px 14px 13px 42px; border-radius: 12px;
  border: 1.5px solid var(--tx-line); background: var(--tx-surface); color: var(--tx-ink);
  /* 16px, not 15px: anything smaller makes iOS zoom the page on focus. */
  font-size: 16px; font-family: inherit; outline: none; transition: border-color .2s ease;
}
.tx-search input:focus { border-color: var(--tx-accent); }
.tx-search input::placeholder { color: var(--tx-muted); }

.tx-sort { display: flex; align-items: center; gap: 8px; color: var(--tx-muted); font-size: 13.5px; font-weight: 700; }
.tx-sort select {
  padding: 12px 14px; border-radius: 12px; border: 1.5px solid var(--tx-line);
  background: var(--tx-surface); color: var(--tx-ink); font-size: 16px;
  font-family: inherit; font-weight: 600; cursor: pointer; outline: none;
}
.tx-sort select:focus { border-color: var(--tx-accent); }

/* --------------------------------------------------------------------------
   Filters
   -------------------------------------------------------------------------- */
.tx-layout { display: grid; grid-template-columns: 260px 1fr; gap: 22px; align-items: start; }

/* ===========================================================================
   THE SELECT-FLIGHT SCREEN — filters | flights | booking summary
   ===========================================================================
   Roughly 25 / 50 / 25 at 1440px, but stated in fr rather than percentages so
   the middle column absorbs the difference at every width in between: the
   flight card is the thing that must not be squeezed, and the two rails hold a
   fixed amount of text each.
   =========================================================================== */
/* align-items STRETCH, not start: the two rails need a full-height column to
   be sticky inside. See the .tx-rail-col note in flights.html. */
.tx-layout-3 { grid-template-columns: 256px minmax(0, 1fr) 280px; gap: 24px; align-items: stretch; }
.tx-rail-col { min-width: 0; }

/* THE MEASURE IS WIDER ON THIS SCREEN, and the arithmetic is the reason.
   .tx-section is capped at 1280px, which after 64px of padding and two 24px
   gaps leaves 1168px to split three ways. Two rails at 256+280 would have left
   the flight card 632px — under the 700px it needs to stay in its three-part
   horizontal shape, so every card collapsed into a vertical stack on a 1440px
   desktop. At 1360px the same split leaves 712px and the card keeps its shape.

   1360 is a deliberate ceiling rather than a full-bleed layout: it is what the
   three columns need and nothing more, so the page still has margins at
   1440px and does not sprawl on a 27-inch display. */
body.sp-continuation .tx-section:has(.tx-layout-3) { max-width: 1360px; }

/* A CONTAINER, NOT A MEDIA QUERY, and the distinction matters here.
   The flight card restacks when ITS COLUMN gets narrow, which on this screen
   happens at 1440px — the column is 596px wide once two rails are taking 572px
   between them. A viewport breakpoint cannot see that: it would keep the card
   in three columns on a wide desktop and push the price and both buttons out
   past the card's right edge, which is exactly what it did. */
.tx-results-col { min-width: 0; container-type: inline-size; container-name: txlist; }

/* The rule itself is at the FOOT of this file — see the note there. */

/* ---- Booking progress ---------------------------------------------------- */
.tx-stepper {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 20px; padding: 14px 18px;
  background: var(--tx-surface); border: 1px solid var(--tx-line);
  border-radius: var(--tx-radius); box-shadow: var(--tx-shadow);
  /* Seven steps do not fit a phone. They scroll INSIDE the bar rather than
     wrapping, so the row keeps its height and the page keeps its width. */
  overflow-x: auto; scrollbar-width: none;
}
.tx-stepper::-webkit-scrollbar { display: none; }
.tx-step { display: flex; align-items: center; gap: 9px; flex: none; }
.tx-step-dot {
  width: 25px; height: 25px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 800;
  background: color-mix(in srgb, var(--tx-ink) 8%, transparent);
  color: var(--tx-ink-3);
}
.tx-step-dot svg { width: 13px; height: 13px; }
.tx-step-label { font-size: 12.5px; font-weight: 700; color: var(--tx-ink-3); white-space: nowrap; }
.tx-step-line { flex: 1 1 18px; min-width: 14px; height: 2px; border-radius: 2px; background: var(--tx-line); }

/* Done is green, current is the brand's red, ahead stays grey — the three
   states a progress bar has to make obvious at a glance. */
.tx-step.is-done .tx-step-dot { background: #12B76A; color: #fff; }
.tx-step.is-done .tx-step-label { color: var(--tx-ink-2); }
.tx-step.is-done + .tx-step-line { background: #12B76A; }
.tx-step.is-current .tx-step-dot {
  background: var(--tx-accent); color: #fff;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--tx-accent) 18%, transparent);
}
.tx-step.is-current .tx-step-label { color: var(--tx-ink); font-weight: 800; }

/* ---- Booking summary rail ------------------------------------------------ */
.tx-summary-rail { min-width: 0; }
/* The panel sticks, not the column — the column is what gives it somewhere to
   stick.

   16px, not 148px. The 148 was clearance for the landing page's FIXED
   header; a results page paints `header.is-solid`, which is
   `position:static` and scrolls away with the page — so a panel pinning
   at 148 left that much empty viewport above it, under nothing. Same
   phantom header as the band's old top padding. */
.tx-summary { position: sticky; top: 16px; }
.tx-summary {
  background: var(--tx-surface); border: 1px solid var(--tx-line);
  border-radius: var(--tx-radius); box-shadow: var(--tx-shadow); overflow: hidden;
}
.tx-sum-head {
  padding: 14px 18px; border-bottom: 1px solid var(--tx-line);
  background: color-mix(in srgb, var(--tx-ink) 3%, transparent);
}
.tx-sum-head h2 { margin: 0; font-size: 14px; font-weight: 800; color: var(--tx-ink); }
.tx-sum-body { padding: 16px 18px; }
.tx-sum-route { font-size: 15px; font-weight: 800; color: var(--tx-ink); line-height: 1.45; }
.tx-sum-arrow { color: var(--tx-accent); margin: 0 4px; }
.tx-sum-meta { margin-top: 4px; font-size: 12.5px; font-weight: 600; color: var(--tx-ink-2); }
.tx-sum-rule { height: 1px; background: var(--tx-line); margin: 15px 0 13px; }
.tx-sum-sec {
  font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--tx-ink-3); margin-bottom: 9px;
}
.tx-sum-line {
  display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
  margin-bottom: 7px; font-size: 13px; color: var(--tx-ink-2);
}
.tx-sum-line b { font-weight: 700; color: var(--tx-ink); font-variant-numeric: tabular-nums; }
.tx-sum-total {
  display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
  margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--tx-line);
  font-size: 14px; font-weight: 800; color: var(--tx-ink);
}
/* The total is READ, not clicked, so it takes the panel's own ink and says
   so with weight instead of hue — the accent is this page's colour for
   things that need attention, and the running total is not one of them. */
.tx-sum-total span:last-child { font-size: 19px; font-weight: 800; color: var(--tx-ink); font-variant-numeric: tabular-nums; }
.tx-sum-note { margin: 9px 0 0; font-size: 11.5px; line-height: 1.5; color: var(--tx-ink-3); }
.tx-sum-empty {
  margin-top: 14px; padding: 13px 14px; border-radius: 10px;
  background: color-mix(in srgb, var(--tx-ink) 4%, transparent);
  font-size: 12.5px; line-height: 1.5; color: var(--tx-ink-2);
}

.tx-trust {
  list-style: none; margin: 0; padding: 13px 18px 15px;
  border-top: 1px solid var(--tx-line);
  background: color-mix(in srgb, var(--tx-ink) 2%, transparent);
}
.tx-trust li {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 600; color: var(--tx-ink-2);
}
.tx-trust li + li { margin-top: 7px; }
.tx-trust svg { width: 13px; height: 13px; flex: none; color: #12B76A; }

/* The card the traveller has opened is the one the rail is costing, so it says
   so rather than leaving them to match a number to a row. */
.tx-flight.is-open { border-color: color-mix(in srgb, var(--tx-accent) 45%, transparent); }

@media (max-width: 1180px) {
  /* The rail drops under the list before the filters do: the filters are used
     WHILE reading, the summary is read once. */
  .tx-layout-3 { grid-template-columns: 256px minmax(0, 1fr); }
  .tx-summary-rail { grid-column: 1 / -1; }
  .tx-summary { position: static; }
}
@media (max-width: 900px) {
  .tx-layout-3 { grid-template-columns: 1fr; }
  .tx-stepper { padding: 12px 14px; gap: 8px; }
  .tx-step-label { font-size: 12px; }
}
.tx-filters {
  background: var(--tx-surface); border: 1px solid var(--tx-line);
  border-radius: var(--tx-radius); padding: 18px; box-shadow: var(--tx-shadow);
  /* See the note on .tx-summary: the header on these pages is static, so
     there is nothing overhead to clear. */
  position: sticky; top: 16px;
  /* The panel scrolls INSIDE itself once it is taller than what is left of the
     viewport. Without this a long filter list simply runs off the bottom of a
     sticky box and its last group cannot be reached at all. */
  max-height: calc(100vh - 36px);
  overflow-y: auto; overscroll-behavior: contain;
}
/* A slim scrollbar, so the panel does not gain a chunky OS gutter that eats
   into the option labels. */
.tx-filters { scrollbar-width: thin; scrollbar-color: var(--tx-line) transparent; }
.tx-filters::-webkit-scrollbar { width: 8px; }
.tx-filters::-webkit-scrollbar-thumb { background: var(--tx-line); border-radius: 99px; }
.tx-filters::-webkit-scrollbar-track { background: transparent; }
/* .tx-fgroup, .tx-check and .tx-clear were the hand-written filter markup that
   filter-engine.js replaced. Nothing renders those class names any more, so
   the rules are gone with them rather than left to be inherited by accident. */

/* --------------------------------------------------------------------------
   Sticky search summary (hotels)

   Sits under .sp-header, which is itself sticky at top:0 and 72px tall, and
   below it in the stack so a scrolled page never shows the two overlapping.
   Collapsed to zero height until shown, so it takes no space and cannot be
   tabbed into while the real form is still on screen.
   -------------------------------------------------------------------------- */
.tx-summary-bar {
  position: sticky; top: 72px; z-index: 40;
  margin: 0 0 18px;
  max-height: 0; overflow: hidden; opacity: 0;
  transform: translateY(-6px);
  transition: max-height .24s ease, opacity .2s ease, transform .24s ease;
  pointer-events: none;
}
.tx-summary-bar.is-shown {
  max-height: 90px; opacity: 1; transform: none; pointer-events: auto;
}
.tx-summary-in {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 16px; border-radius: 12px;
  background: var(--tx-surface); border: 1px solid var(--tx-line);
  box-shadow: var(--tx-shadow-lg);
}
.tx-summary-text {
  margin: 0; flex: 1; min-width: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 0;
  font-size: 13.5px; font-weight: 700; color: var(--tx-ink);
}
/* Middot separators drawn in CSS, so the text itself stays a plain list of
   values that a screen reader reads without punctuation between every word. */
.tx-summary-text span + span::before {
  content: '·'; margin: 0 9px; color: var(--tx-muted); font-weight: 400;
}
.tx-summary-edit { flex: none; padding: 9px 16px; font-size: 13px; }

/* The bar's other face: the same criteria as a small form. Taller than the
   read-only line, so the shown height allows for it. */
.tx-summary-bar.is-shown { max-height: 260px; }
.tx-summary-in.is-editing {
  display: grid; align-items: end;
  grid-template-columns: minmax(160px, 1.6fr) repeat(2, minmax(130px, 1fr)) repeat(2, 84px) auto;
  gap: 10px;
}
.tx-sum-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.tx-sum-field label {
  font-size: 10.5px; font-weight: 800; letter-spacing: .05em;
  text-transform: uppercase; color: var(--tx-muted);
}
.tx-sum-field input {
  width: 100%; padding: 9px 10px; border-radius: 9px;
  border: 1.5px solid var(--tx-line); background: var(--tx-surface); color: var(--tx-ink);
  /* 16px so iOS does not zoom the page when one is focused. */
  font-size: 16px; font-family: inherit; outline: none;
}
.tx-sum-field input:focus { border-color: var(--tx-accent); }
.tx-sum-actions { display: flex; gap: 8px; }
.tx-sum-actions .tx-btn { padding: 10px 15px; font-size: 13px; }
.tx-sum-error {
  grid-column: 1 / -1; margin: 2px 0 0;
  font-size: 12.5px; font-weight: 700; color: var(--tx-accent);
}
@media (max-width: 860px) {
  .tx-summary-in.is-editing { grid-template-columns: 1fr 1fr; }
  .tx-sum-field:first-child { grid-column: 1 / -1; }
  .tx-sum-actions { grid-column: 1 / -1; }
  .tx-sum-actions .tx-btn { flex: 1; }
  .tx-summary-bar.is-shown { max-height: 420px; }
}
@media (max-width: 560px) {
  .tx-summary-bar.is-shown { max-height: 140px; }
  .tx-summary-text { font-size: 12.5px; }
  .tx-summary-edit { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .tx-summary-bar { transition: none; }
}

.tx-filter-toggle { display: none; }

/* --------------------------------------------------------------------------
   Filter panel (flight-filters.js)

   Its own `ff-` namespace rather than more `tx-fgroup` rules: the panel is
   rendered entirely by a module that knows nothing about this page, and the
   prefix is what keeps that boundary visible in the stylesheet too.
   -------------------------------------------------------------------------- */
.ff-top { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.ff-heading {
  margin: 0; font-size: 14px; font-weight: 800; color: var(--tx-ink);
  display: flex; align-items: center; gap: 8px;
}
.ff-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: 99px;
  background: var(--tx-accent); color: var(--tx-accent-ink);
  font-size: 11px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.ff-clear-all {
  margin-left: auto; background: none; border: none; padding: 4px 2px;
  font-family: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
  color: var(--tx-accent); text-decoration: underline; text-underline-offset: 3px;
}

.ff-group { border-top: 1px solid var(--tx-line); }
.ff-head { margin: 0; }
.ff-toggle {
  width: 100%; display: flex; align-items: center; gap: 8px;
  padding: 14px 0 12px; background: none; border: none; cursor: pointer;
  font-family: inherit; text-align: left; color: var(--tx-muted);
  font-size: 11.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
}
.ff-toggle:hover { color: var(--tx-ink); }
.ff-title { flex: 1; min-width: 0; }
.ff-caret { width: 16px; height: 16px; flex: none; transition: transform .2s ease; }
.ff-toggle[aria-expanded="false"] .ff-caret { transform: rotate(-90deg); }
.ff-body { padding-bottom: 14px; }

.ff-opts { display: flex; flex-direction: column; }
.ff-opt {
  display: flex; align-items: center; gap: 9px; padding: 6px 0;
  font-size: 14px; color: var(--tx-ink); cursor: pointer;
}
.ff-opt input { width: 16px; height: 16px; margin: 0; flex: none; accent-color: var(--tx-accent); cursor: pointer; }
.ff-opt-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.ff-opt-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ff-opt-note { font-size: 11px; color: var(--tx-muted); font-weight: 600; }
.ff-count { margin-left: auto; font-size: 12px; color: var(--tx-muted); font-variant-numeric: tabular-nums; }
/* Still selectable — it is how you get back to a wider result set — but shown
   as leading nowhere on its own. */
.ff-opt.is-empty { opacity: .45; }

.ff-logo {
  width: 20px; height: 20px; flex: none; border-radius: 5px; object-fit: contain;
  background: var(--tx-surface-2);
}
.ff-logo-code {
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 8.5px; font-weight: 800; color: var(--tx-muted);
  border: 1px solid var(--tx-line);
}

.ff-search { margin-bottom: 8px; }
.ff-search input {
  width: 100%; padding: 8px 10px; border-radius: 9px;
  border: 1.5px solid var(--tx-line); background: var(--tx-surface);
  color: var(--tx-ink); font-family: inherit; font-size: 13.5px; outline: none;
}
.ff-search input:focus { border-color: var(--tx-accent); }
.ff-none { margin: 6px 0; font-size: 13px; color: var(--tx-muted); }
.ff-clear-one {
  margin-top: 8px; background: none; border: none; padding: 2px;
  font-family: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
  color: var(--tx-accent); text-decoration: underline; text-underline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Dual-range slider

   Two overlaid <input type=range>. Each keeps its own thumb, so both are
   focusable and arrow-key operable — which a single custom-drawn track with
   div thumbs would have to reimplement badly. The inputs themselves are
   transparent; the visible track and fill are the div underneath.
   -------------------------------------------------------------------------- */
.ff-range-out {
  display: block; font-size: 13.5px; font-weight: 800; color: var(--tx-ink);
  font-variant-numeric: tabular-nums; margin-bottom: 10px;
}
.ff-range-track { position: relative; height: 22px; }
.ff-range-track::before {
  content: ''; position: absolute; left: 0; right: 0; top: 9px; height: 4px;
  border-radius: 99px; background: var(--tx-line);
}
.ff-range-fill {
  position: absolute; top: 9px; height: 4px; border-radius: 99px;
  background: var(--tx-accent);
}
.ff-range-track input[type="range"] {
  position: absolute; left: 0; top: 0; width: 100%; height: 22px; margin: 0;
  background: none; appearance: none; -webkit-appearance: none;
  /* The track must not swallow the other thumb's pointer events; only the
     thumbs themselves are interactive. */
  pointer-events: none;
}
.ff-range-track input[type="range"]:focus { outline: none; }
.ff-range-track input[type="range"]::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none; pointer-events: auto;
  width: 18px; height: 18px; border-radius: 50%; cursor: grab;
  background: var(--tx-surface); border: 2.5px solid var(--tx-accent);
  box-shadow: 0 1px 4px rgba(10, 37, 64, .25);
}
.ff-range-track input[type="range"]::-moz-range-thumb {
  pointer-events: auto;
  width: 18px; height: 18px; border-radius: 50%; cursor: grab; border: 2.5px solid var(--tx-accent);
  background: var(--tx-surface); box-shadow: 0 1px 4px rgba(10, 37, 64, .25);
}
.ff-range-track input[type="range"]:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--tx-accent); outline-offset: 2px;
}
.ff-range-track input[type="range"]:focus-visible::-moz-range-thumb {
  outline: 2px solid var(--tx-accent); outline-offset: 2px;
}
.ff-range-ends {
  display: flex; justify-content: space-between; margin-top: 6px;
  font-size: 11.5px; color: var(--tx-muted); font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
  .ff-caret { transition: none; }
}

/* --------------------------------------------------------------------------
   Flight card
   -------------------------------------------------------------------------- */
.tx-count-line { color: var(--tx-muted); font-size: 13.5px; margin: 0 0 12px; }
.tx-count-line b { color: var(--tx-ink); }

.tx-flight {
  display: grid; grid-template-columns: auto 1fr auto; gap: 20px; align-items: center;
  background: var(--tx-surface); border: 1px solid var(--tx-line);
  border-radius: var(--tx-radius); padding: 18px 20px; box-shadow: var(--tx-shadow);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.tx-flight + .tx-flight { margin-top: 12px; }
.tx-flight:hover { transform: translateY(-2px); box-shadow: var(--tx-shadow-lg); border-color: color-mix(in srgb, var(--tx-accent) 35%, var(--tx-line)); }

.tx-carrier { display: flex; align-items: center; gap: 12px; min-width: 168px; }
.tx-logo {
  width: 46px; height: 46px; flex: none; border-radius: 12px; background: var(--tx-surface-2);
  border: 1px solid var(--tx-line); display: grid; place-items: center; overflow: hidden;
}
.tx-logo img { width: 34px; height: 34px; object-fit: contain; }
.tx-logo .tx-logo-fb { font-size: 15px; font-weight: 800; color: var(--tx-muted); letter-spacing: .02em; }
.tx-carrier-name { font-weight: 700; font-size: 14.5px; color: var(--tx-ink); line-height: 1.3; }
.tx-carrier-no { font-size: 12.5px; color: var(--tx-muted); font-variant-numeric: tabular-nums; }

.tx-leg { display: grid; grid-template-columns: 1fr auto 1fr; gap: 14px; align-items: center; }
.tx-time { font-size: 21px; font-weight: 800; color: var(--tx-ink); line-height: 1.15; font-variant-numeric: tabular-nums; }
.tx-place { font-size: 13px; color: var(--tx-muted); margin-top: 2px; }
.tx-leg-end { text-align: right; }
.tx-leg-end .tx-time.tx-tbd { font-size: 15px; font-weight: 700; color: var(--tx-muted); }

.tx-path { display: grid; justify-items: center; gap: 4px; min-width: 108px; }
.tx-dur { font-size: 12px; font-weight: 700; color: var(--tx-muted); white-space: nowrap; }
.tx-line { width: 100%; height: 2px; background: var(--tx-line); position: relative; border-radius: 2px; }
/* The aeroplane at the end of the route line was content:'✈' — a text glyph
   that rendered as a different aircraft per platform and as a full-colour emoji
   on some. It is the jp-icons drawing now, so it matches the nav. */
.tx-line .jpi {
  position: absolute; right: -6px; top: 50%;
  /* 45deg, NOT 90. The mark used to be a hand-drawn aeroplane pointing straight
     up, and a quarter turn laid it along the route line. Lucide's Plane is
     already banked: its nose points up and to the RIGHT, about 45 degrees off
     vertical. Left at 90 it flew nose-down into the arrival airport. */
  transform: translateY(-50%) rotate(45deg);
  width: 16px; height: 16px; color: var(--tx-accent);
  background: var(--tx-surface); padding-left: 3px;
}
/* The card is the hover target — a 14px mark is not one. */
.tx-flight:hover .tx-line .jpi [data-jpi="glide"] { animation: jpiGlide .85s cubic-bezier(.22,1,.36,1); }
.tx-stops { font-size: 11.5px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--tx-emerald); }

.tx-act { display: grid; gap: 8px; justify-items: stretch; min-width: 150px; }
.tx-fare { text-align: right; font-size: 13px; color: var(--tx-muted); font-weight: 600; }
.tx-fare b { display: block; font-size: 19px; color: var(--tx-ink); font-weight: 800; }
.tx-btn {
  padding: 11px 16px; border-radius: 11px; font-weight: 800; font-size: 13.5px;
  font-family: inherit; cursor: pointer; border: 1.5px solid transparent;
  transition: transform .15s ease, background .2s ease, border-color .2s ease, color .2s ease;
  text-align: center; white-space: nowrap;
}
.tx-btn:active { transform: translateY(1px); }
.tx-btn-primary { background: var(--tx-accent); color: var(--tx-accent-ink); }
.tx-btn-primary:hover { background: var(--coral-dark, #E63946); }
.tx-btn-ghost { background: transparent; color: var(--tx-ink); border-color: var(--tx-line); }
.tx-btn-ghost:hover { border-color: var(--tx-accent); color: var(--tx-accent); }

.tx-status { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--tx-emerald); }
.tx-status::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* Expanding detail row */
.tx-details { grid-column: 1 / -1; margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--tx-line); display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 14px; }
.tx-detail span { display: block; font-size: 11.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--tx-muted); margin-bottom: 3px; }
.tx-detail b { font-size: 14px; color: var(--tx-ink); font-weight: 700; }
/* Fare rules reads as a sentence, not a fact — give it the whole row instead
   of a 140px column, and let it wrap normally rather than fighting <b>'s
   usual single-line labels. */
.tx-detail-wide { grid-column: 1 / -1; }
.tx-detail-wide b { font-weight: 600; line-height: 1.5; }

/* --------------------------------------------------------------------------
   Empty state + pagination
   -------------------------------------------------------------------------- */
.tx-empty { text-align: center; padding: 46px 20px; background: var(--tx-surface); border: 1px dashed var(--tx-line); border-radius: var(--tx-radius); color: var(--tx-muted); }
.tx-empty b { display: block; color: var(--tx-ink); font-size: 16px; margin-bottom: 6px; }
.tx-empty-acts { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 18px; }

/* --------------------------------------------------------------------------
   Load-failure states

   Shares .tx-empty's box but carries an illustration and its own copy. The
   mark is a line drawing rather than a picture: it has to read at 56px in
   both themes, and it inherits currentColor so it cannot be the one element
   that stays light on a dark page.
   -------------------------------------------------------------------------- */
.tx-state {
  grid-column: 1 / -1;
  text-align: center; padding: 46px 20px;
  background: var(--tx-surface); border: 1px dashed var(--tx-line);
  border-radius: var(--tx-radius); color: var(--tx-muted);
}
.tx-state-art {
  width: 56px; height: 56px; margin: 0 auto 14px; display: block;
  color: var(--tx-accent); opacity: .85;
}
.tx-state b { display: block; color: var(--tx-ink); font-size: 17px; margin-bottom: 6px; }
.tx-state p { margin: 0 auto; max-width: 46ch; font-size: 14px; line-height: 1.55; }
/* The empty state sits in a grid that may be several columns wide. */
.tx-empty { grid-column: 1 / -1; }

/* --------------------------------------------------------------------------
   Filter-panel skeleton

   The sidebar is as tall as the results and arrives at the same time, so
   leaving it blank while the grid shows card placeholders is the one half of
   the page that still jumps. Four groups of three, which is the shape of the
   panel that replaces it.
   -------------------------------------------------------------------------- */
.ff-sk-group { border-top: 1px solid var(--tx-line); padding: 14px 0; }
.ff-sk-group:first-child { border-top: none; }
.ff-sk-head { height: 11px; width: 44%; margin-bottom: 14px; }
.ff-sk-row { display: flex; align-items: center; gap: 9px; padding: 6px 0; }
.ff-sk-box { width: 16px; height: 16px; border-radius: 4px; flex: none; }
.ff-sk-txt { height: 12px; border-radius: 6px; flex: 1; }
.ff-sk-group .tx-sk-line,
.ff-sk-box, .ff-sk-txt {
  background: linear-gradient(90deg,
    var(--tx-surface-2) 25%, var(--tx-sk-hi) 37%, var(--tx-surface-2) 63%);
  background-size: 400% 100%;
  animation: txShimmer 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .ff-sk-box, .ff-sk-txt { animation: none; }
}

.tx-more { display: block; margin: 18px auto 0; padding: 12px 26px; border-radius: 12px; border: 1.5px solid var(--tx-line); background: var(--tx-surface); color: var(--tx-ink); font-weight: 800; font-size: 14px; font-family: inherit; cursor: pointer; transition: border-color .2s ease, color .2s ease; }
.tx-more:hover { border-color: var(--tx-accent); color: var(--tx-accent); }

/* --------------------------------------------------------------------------
   Cards: hotels / cruises / packages
   -------------------------------------------------------------------------- */
.tx-grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }
.tx-card {
  background: var(--tx-surface); border: 1px solid var(--tx-line); border-radius: var(--tx-radius);
  overflow: hidden; box-shadow: var(--tx-shadow); display: flex; flex-direction: column;
  transition: transform .18s ease, box-shadow .18s ease;
}
.tx-card:hover { transform: translateY(-3px); box-shadow: var(--tx-shadow-lg); }
.tx-media { position: relative; aspect-ratio: 4 / 3; background: var(--tx-surface-2); overflow: hidden; }
.tx-media img, .tx-media svg { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .35s ease; }
.tx-card:hover .tx-media img { transform: scale(1.04); }
.tx-badge { position: absolute; top: 10px; left: 10px; padding: 5px 10px; border-radius: 999px; background: rgba(10,37,64,.78); color: #fff; font-size: 11.5px; font-weight: 800; letter-spacing: .03em; backdrop-filter: blur(4px); }
.tx-body { padding: 15px 16px 16px; display: flex; flex-direction: column; gap: 7px; flex: 1; }
.tx-title { font-size: 16px; font-weight: 800; color: var(--tx-ink); line-height: 1.3; margin: 0; }
.tx-sub { font-size: 13px; color: var(--tx-muted); line-height: 1.5; margin: 0; }
.tx-stars { color: var(--tx-gold); font-size: 13px; letter-spacing: 1px; }
.tx-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.tx-chip { font-size: 11.5px; font-weight: 700; padding: 4px 9px; border-radius: 999px; background: var(--tx-surface-2); color: var(--tx-muted); border: 1px solid var(--tx-line); }
/* Free cancellation. Green because it is the reassurance a traveller scans a
   card for; shown only when the policy text actually says so, classified by
   the same reader the filter uses so badge and filter cannot disagree. */
.tx-chip.is-good {
  color: var(--tx-emerald);
  background: color-mix(in srgb, var(--tx-emerald) 12%, transparent);
  border-color: color-mix(in srgb, var(--tx-emerald) 40%, transparent);
}
.tx-foot { margin-top: auto; padding-top: 12px; display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; }
.tx-price span { display: block; font-size: 11.5px; color: var(--tx-muted); font-weight: 700; }
.tx-price b { font-size: 18px; color: var(--tx-ink); font-weight: 800; }
.tx-price i { font-style: normal; font-size: 12px; color: var(--tx-muted); font-weight: 600; }
/* Two buttons in a card footer (cruises: View Details + Book Now). They wrap
   under the price rather than squeezing it out on a narrow card. */
.tx-card-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.tx-card-actions .tx-btn { padding: 9px 13px; font-size: 12.5px; }

.tx-cancel-note { font-size: 12px; color: var(--tx-muted); line-height: 1.5; margin: 2px 0 0; }

/* --------------------------------------------------------------------------
   Hotel card

   The order the eye should take it: photograph, name, how good it is, where it
   is, what it costs. The score is a block rather than a sentence because it is
   the number properties get compared on.
   -------------------------------------------------------------------------- */
.tx-rate-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.tx-score { display: inline-flex; align-items: center; gap: 7px; }
.tx-score b {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; padding: 3px 7px; border-radius: 8px 8px 8px 2px;
  background: var(--tx-ink); color: var(--tx-surface);
  font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.tx-score > span { font-size: 11.5px; font-weight: 700; color: var(--tx-muted); }
.tx-chip.is-more { background: transparent; border-style: dashed; }

/* The strike-through only ever appears beside a real original price. */
.tx-was { display: block; font-size: 12px; color: var(--tx-muted); text-decoration-thickness: 1.5px; }
.tx-price span { display: block; font-size: 11.5px; color: var(--tx-muted); font-weight: 700; }
.tx-deal {
  position: absolute; top: 10px; right: 10px; padding: 5px 10px; border-radius: 999px;
  background: var(--tx-accent); color: var(--tx-accent-ink);
  font-size: 11.5px; font-weight: 800; letter-spacing: .03em;
}

/* Wishlist heart. Bottom-right of the photograph so it never collides with the
   distance badge (top-left) or a discount (top-right). */
.wl-btn {
  position: absolute; right: 10px; bottom: 10px; z-index: 2;
  width: 34px; height: 34px; display: grid; place-items: center;
  border: none; border-radius: 50%; cursor: pointer;
  background: rgba(255, 255, 255, .92);
  box-shadow: 0 2px 8px rgba(10, 37, 64, .22);
  transition: transform .16s ease, background .16s ease;
}
.wl-btn svg { width: 18px; height: 18px; fill: none; stroke: #5B6B82; stroke-width: 2; }
.wl-btn:hover { transform: scale(1.08); }
.wl-btn.is-on svg { fill: var(--tx-accent); stroke: var(--tx-accent); }
.wl-btn:focus-visible { outline: 2px solid var(--tx-accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .wl-btn { transition: none; } }

/* --------------------------------------------------------------------------
   Expanded details (hotel-details.js)

   One section per component, each rendered only when the payload can fill it.
   -------------------------------------------------------------------------- */
.tx-hotel-details {
  grid-column: 1 / -1; margin-top: 14px; padding-top: 14px;
  border-top: 1px dashed var(--tx-line);
}
.hd { display: flex; flex-direction: column; gap: 18px; }
.hd-h { margin: 0 0 8px; font-size: 12px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--tx-muted); }
.hd-prose { margin: 0; font-size: 13.5px; line-height: 1.6; color: var(--tx-ink); }

.hd-rooms { display: flex; flex-direction: column; gap: 10px; }
.hd-room {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 12px; border: 1px solid var(--tx-line); border-radius: 12px;
  background: var(--tx-surface-2);
}
.hd-room-main { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.hd-room-main h5 { margin: 0; font-size: 14px; font-weight: 800; color: var(--tx-ink); }
.hd-room-meta { margin: 0; font-size: 12px; color: var(--tx-muted); font-weight: 600; }
.hd-room-desc { margin: 0; font-size: 12.5px; color: var(--tx-ink); line-height: 1.5; }
.hd-room-cancel { margin: 2px 0 0; font-size: 11.5px; color: var(--tx-muted); line-height: 1.5; }
.hd-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.hd-room-buy {
  display: flex; flex-direction: column; align-items: flex-end; gap: 5px;
  text-align: right; white-space: nowrap; flex: none;
}
.hd-room-buy b { font-size: 16px; font-weight: 800; color: var(--tx-ink); }
.hd-room-buy > span { font-size: 11px; color: var(--tx-muted); font-weight: 700; }
.hd-left { font-size: 11px; font-weight: 800; color: var(--tx-accent); }

.hd-amenities, .hd-nearby, .hd-charges { margin: 0; padding: 0; list-style: none; }
.hd-amenities { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px 14px; }
.hd-amenities li, .hd-nearby li, .hd-charges li {
  font-size: 13px; color: var(--tx-ink); padding-left: 18px; position: relative; line-height: 1.5;
}
.hd-amenities li::before, .hd-nearby li::before, .hd-charges li::before {
  content: ''; position: absolute; left: 4px; top: 8px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--tx-accent);
}
.hd-nearby li { display: flex; justify-content: space-between; gap: 12px; }
.hd-nearby li span { color: var(--tx-muted); font-weight: 700; font-size: 12px; }

.hd-policies { margin: 0; display: flex; flex-direction: column; gap: 8px; }
.hd-policies > div { display: grid; grid-template-columns: 150px 1fr; gap: 12px; }
.hd-policies dt { font-size: 12px; font-weight: 800; color: var(--tx-muted); }
.hd-policies dd { margin: 0; font-size: 13px; color: var(--tx-ink); line-height: 1.5; }

.hd-map { border-radius: 12px; overflow: hidden; border: 1px solid var(--tx-line); }
.hd-map iframe { width: 100%; height: 220px; border: 0; display: block; }
.hd-map-pending {
  display: grid; place-items: center; gap: 4px; min-height: 140px;
  background: var(--tx-surface-2); text-align: center; padding: 20px;
}
.hd-map-pending p { margin: 0; font-weight: 800; color: var(--tx-ink); font-size: 13.5px; }
.hd-map-pending span { font-size: 12px; color: var(--tx-muted); font-variant-numeric: tabular-nums; }

@media (max-width: 560px) {
  .hd-room { flex-direction: column; align-items: stretch; }
  .hd-room-buy { align-items: flex-start; text-align: left; }
  .hd-policies > div { grid-template-columns: 1fr; gap: 2px; }
}
/* The .tx-room-opt* rules that were here belonged to the fixed four-section
   details panel hotel-details.js replaced; .hd-room above is its successor and
   already carries its own narrow-screen stacking. */

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 1000px) {
  .tx-layout { grid-template-columns: 1fr; }
  /* FLOATING, not a button in the toolbar. The toolbar scrolls away with the
     top of the page, and the filters are wanted most while deep in the list —
     which is exactly where a toolbar button cannot be reached. Centred at the
     bottom, where a thumb already is. */
  .tx-filter-toggle {
    display: inline-flex; align-items: center; gap: 7px;
    position: fixed; z-index: 88;
    left: 50%; transform: translateX(-50%);
    bottom: calc(18px + env(safe-area-inset-bottom, 0px));
    padding: 13px 22px; border-radius: 999px;
    border: none; background: var(--tx-ink); color: var(--tx-surface);
    font-weight: 800; font-size: 14.5px; font-family: inherit; cursor: pointer;
    box-shadow: 0 6px 22px rgba(10, 37, 64, .34);
    /* NOT THERE UNTIL THERE IS A LIST TO FILTER. Fixed to the bottom of the
       viewport, it covers whatever is there — and on first paint that is the
       date strip, dead centre, hiding the selected day. armFilterToggleReveal()
       in travel-explore.js adds `is-ready` once the results come into view and
       never takes it away. `visibility` rather than `display` so the button
       keeps its size and the fade has something to animate; pointer-events
       because a hidden-but-present control must not be clickable. */
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity .22s ease, visibility .22s;
  }
  .tx-filter-toggle.is-ready { opacity: 1; visibility: visible; pointer-events: auto; }
  @media (prefers-reduced-motion: reduce) {
    .tx-filter-toggle { transition: none; }
  }
  .tx-filter-toggle:hover { background: var(--tx-accent); color: var(--tx-accent-ink); }
  .tx-filter-toggle:focus-visible { outline: 2px solid var(--tx-accent); outline-offset: 3px; }
  /* The sheet has its own way out; two competing buttons at the bottom is one
     too many. */
  body.tx-sheet-open .tx-filter-toggle { display: none; }
  /* How many filters are on, so the button says something while it is the only
     thing on screen that could. */
  .tx-filter-toggle[data-count]::after {
    content: attr(data-count);
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 19px; height: 19px; padding: 0 5px; border-radius: 99px;
    background: var(--tx-accent); color: var(--tx-accent-ink);
    font-size: 11.5px; font-weight: 800;
  }

  /* ------------------------------------------------------------------------
     The filter panel becomes a BOTTOM SHEET.

     Not merely `display:block` in the flow, which is what it used to do: that
     pushed the results down the page, so opening the filters scrolled the
     thing being filtered out of sight, and closing them left the traveller
     somewhere they had not chosen to be. A sheet leaves the list exactly where
     it was underneath.

     It slides from the bottom because that is where a thumb is. The header and
     the "show results" footer stay put while the middle scrolls, so the way
     out is always reachable without scrolling back up through every group.
     ------------------------------------------------------------------------ */
  .tx-filters {
    position: fixed; z-index: 90;
    left: 0; right: 0; bottom: 0; top: auto;
    max-height: 86vh; margin: 0;
    border-radius: 18px 18px 0 0;
    border: 1px solid var(--tx-line); border-bottom: none;
    box-shadow: 0 -8px 40px rgba(10, 37, 64, .28);
    padding: 0 18px calc(76px + env(safe-area-inset-bottom, 0px));
    transform: translateY(101%);
    transition: transform .28s cubic-bezier(.22, .8, .3, 1);
    /* Not display:none — a hidden-by-transform sheet keeps its scroll position
       and can animate. visibility keeps it out of the tab order while closed. */
    visibility: hidden;
    overscroll-behavior: contain;
  }
  .tx-filters.tx-open { transform: none; visibility: visible; }

  /* The grab handle and title, pinned while the groups scroll under them. */
  .tx-filters::before {
    content: ''; display: block; width: 40px; height: 4px; border-radius: 99px;
    background: var(--tx-line); margin: 10px auto 4px;
    position: sticky; top: 0;
  }
  .ff-scope .ff-top {
    position: sticky; top: 0; z-index: 2;
    background: var(--tx-surface); padding: 8px 0 10px;
    border-bottom: 1px solid var(--tx-line); margin-bottom: 0;
  }

  /* Bigger targets: this is being used with a thumb. */
  .ff-opt { padding: 10px 0; }
  .ff-opt input { width: 20px; height: 20px; }
  .ff-toggle { padding: 16px 0 14px; }
  .ff-range-track { height: 34px; }
  .ff-range-track input[type="range"] { height: 34px; }
  .ff-range-track::before, .ff-range-fill { top: 15px; }
  .ff-range-track input[type="range"]::-webkit-slider-thumb { width: 24px; height: 24px; }
  .ff-range-track input[type="range"]::-moz-range-thumb { width: 24px; height: 24px; }

  /* The way out, always on screen. Its label is written by the page, so it
     says how many results the current selection actually yields. */
  .tx-sheet-done {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 91;
    display: none; padding: 12px 18px calc(12px + env(safe-area-inset-bottom, 0px));
    background: var(--tx-surface); border-top: 1px solid var(--tx-line);
  }
  .tx-filters.tx-open ~ .tx-sheet-done,
  .tx-sheet-done.is-shown { display: block; }
  .tx-sheet-done .tx-btn { width: 100%; padding: 14px; font-size: 15px; }

  .tx-sheet-scrim {
    position: fixed; inset: 0; z-index: 89;
    background: rgba(6, 12, 24, .45);
    opacity: 0; pointer-events: none; transition: opacity .24s ease;
  }
  .tx-sheet-scrim.is-shown { opacity: 1; pointer-events: auto; }
  body.tx-sheet-open { overflow: hidden; }
}
@media (max-width: 1000px) and (prefers-reduced-motion: reduce) {
  .tx-filters, .tx-sheet-scrim { transition: none; }
}
@media (max-width: 760px) {
  /* The header is shorter here, so the desktop 148px would leave a big gap. */
  .tx-section { padding: 40px 16px; scroll-margin-top: 84px; }
  /* The card becomes one column, so the carrier, the leg and the actions stack
     instead of being squeezed into three unreadable columns. */
  .tx-flight { grid-template-columns: 1fr; gap: 14px; padding: 16px; }
  .tx-carrier { min-width: 0; }
  .tx-act { min-width: 0; grid-template-columns: 1fr 1fr; align-items: center; }
  .tx-fare { grid-column: 1 / -1; text-align: left; }
  .tx-time { font-size: 19px; }
  .tx-grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
}
@media (max-width: 420px) {
  .tx-grid { grid-template-columns: 1fr; }
  .tx-leg { grid-template-columns: 1fr auto 1fr; gap: 8px; }
  .tx-path { min-width: 76px; }
}

@media (prefers-reduced-motion: reduce) {
  .tx-flight, .tx-card, .tx-media img, .tx-btn { transition: none; }
  .tx-flight:hover, .tx-card:hover { transform: none; }
}

/* ==========================================================================
   Flights search panel — airport picker, calendar, traveller popup
   ==========================================================================
   Same idiom as the tx-* rules above: the field wrapper is still .tx-sf, the
   buttons are still .tx-btn. Only the three widgets that had no equivalent
   get new classes.
   ========================================================================== */

.tx-searchgrid { position: relative; }
.tx-sf { position: relative; }

/* --- inline errors -------------------------------------------------------- */
.tx-err {
  margin: 5px 0 0; font-size: 12px; line-height: 1.35;
  color: var(--sp-coral-dark, #C0392B);
}
.tx-err:empty { display: none; }
.tx-sf .is-invalid,
.tx-ap-input.is-invalid,
.tx-cal-btn.is-invalid,
.tx-pax-trigger.is-invalid {
  border-color: var(--sp-coral-dark, #C0392B) !important;
  background: rgba(192, 57, 43, .04);
}

/* --- shared control surface ----------------------------------------------
   Matches the height and border of the .tx-sf selects/inputs already here, so
   a picker sits level with a plain field beside it. */
.tx-ap-input,
.tx-cal-btn,
.tx-pax-trigger {
  width: 100%; min-height: 42px; padding: 10px 12px;
  font: inherit; font-size: 14px; text-align: left;
  color: var(--sp-ink, #0A2540);
  background: var(--sp-surface, #fff);
  border: 1.5px solid var(--sp-line, rgba(10, 37, 64, .14));
  border-radius: 10px; cursor: pointer;
}
.tx-ap-input { cursor: text; }
.tx-ap-input:focus, .tx-cal-btn:focus-visible, .tx-pax-trigger:focus-visible {
  outline: none; border-color: var(--sp-accent, #FF4D4D);
}
.tx-cal-btn.is-empty { color: var(--sp-muted, #5B6B82); }

/* --- airport picker ------------------------------------------------------- */
.tx-ap-list {
  position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% - 14px);
  max-height: 300px; overflow-y: auto;
  background: var(--sp-surface, #fff);
  border: 1px solid var(--sp-line, rgba(10, 37, 64, .14));
  border-radius: 12px; box-shadow: 0 18px 44px rgba(10, 37, 64, .16);
}
.tx-ap-group {
  padding: 9px 12px 5px; font-size: 11px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--sp-muted, #5B6B82);
}
.tx-ap-opt { padding: 9px 12px; cursor: pointer; }
.tx-ap-opt b { display: block; font-size: 14px; font-weight: 700; }
.tx-ap-opt span { display: block; font-size: 12px; color: var(--sp-muted, #5B6B82); }
.tx-ap-opt:hover, .tx-ap-opt.is-active { background: var(--sp-accent-soft, rgba(255, 77, 77, .1)); }
.tx-ap-empty { padding: 14px 12px; font-size: 13px; color: var(--sp-muted, #5B6B82); }

/* --- swap ---------------------------------------------------------------- */
.tx-swap {
  align-self: end; margin-bottom: 22px;
  width: 38px; height: 38px; flex: none;
  display: grid; place-items: center; cursor: pointer;
  background: var(--sp-surface, #fff);
  border: 1.5px solid var(--sp-line, rgba(10, 37, 64, .14));
  border-radius: 50%; color: var(--sp-accent, #FF4D4D);
  transition: transform .18s ease, border-color .18s ease;
}
.tx-swap:hover { border-color: var(--sp-accent, #FF4D4D); transform: rotate(180deg); }
.tx-swap:focus-visible { outline: 2px solid var(--sp-accent, #FF4D4D); outline-offset: 2px; }

/* --- calendar ------------------------------------------------------------- */
.tx-cal-pop {
  position: absolute; z-index: 45; left: 0; top: calc(100% - 14px);
  width: 288px; padding: 12px;
  background: var(--sp-surface, #fff);
  border: 1px solid var(--sp-line, rgba(10, 37, 64, .14));
  border-radius: 14px; box-shadow: 0 18px 44px rgba(10, 37, 64, .16);
}
.tx-cal-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 8px; font-size: 14px;
}
.tx-cal-nav {
  width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  background: var(--sp-surface-2, #F1F5F9); border: none;
  font-size: 17px; line-height: 1; color: var(--sp-ink, #0A2540);
}
.tx-cal-nav:hover { background: var(--sp-accent-soft, rgba(255, 77, 77, .12)); }
.tx-cal-nav:focus-visible { outline: 2px solid var(--sp-accent, #FF4D4D); outline-offset: 1px; }

.tx-cal-dow, .tx-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.tx-cal-dow span {
  text-align: center; padding: 4px 0; font-size: 11px; font-weight: 700;
  color: var(--sp-muted, #5B6B82);
}
.tx-cal-cell {
  min-height: 34px; padding: 3px 0; border: none; border-radius: 8px; cursor: pointer;
  background: transparent; font: inherit; font-size: 13px;
  color: var(--sp-ink, #0A2540); display: grid; place-items: center; gap: 0;
}
.tx-cal-cell.is-empty { visibility: hidden; cursor: default; }
.tx-cal-cell:hover:not(:disabled) { background: var(--sp-accent-soft, rgba(255, 77, 77, .12)); }
.tx-cal-cell:disabled { color: rgba(10, 37, 64, .28); cursor: not-allowed; }
/* Weekends read differently because a weekend departure usually prices
   differently — the cue is the point, not decoration. */
.tx-cal-cell.is-weekend:not(:disabled) { color: var(--sp-accent, #FF4D4D); font-weight: 700; }
.tx-cal-cell.is-selected {
  background: var(--sp-accent, #FF4D4D) !important; color: var(--on-accent, #12253F); font-weight: 800;
}
.tx-cal-cell:focus-visible { outline: 2px solid var(--sp-accent, #FF4D4D); outline-offset: -2px; }
/* Reserved for real fares. search-widgets.js's calendar draws this whenever a
   panel passes a `noteFor(iso)`; nothing does yet, because there is no
   fare-by-date source to answer it. */
.tx-cal-fare { font-size: 9.5px; line-height: 1; color: var(--sp-emerald, #12B76A); }
.tx-cal-cell.is-selected .tx-cal-fare { color: #fff; }

/* --- traveller popup ------------------------------------------------------ */
.tx-pax-pop {
  position: absolute; z-index: 45; right: 0; top: calc(100% - 14px);
  width: 320px; padding: 14px;
  background: var(--sp-surface, #fff);
  border: 1px solid var(--sp-line, rgba(10, 37, 64, .14));
  border-radius: 14px; box-shadow: 0 18px 44px rgba(10, 37, 64, .16);
}
.tx-pax-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 9px 0;
  border-bottom: 1px solid var(--sp-line, rgba(10, 37, 64, .08));
}
.tx-pax-row b { display: block; font-size: 14px; }
.tx-pax-row span { display: block; font-size: 12px; color: var(--sp-muted, #5B6B82); }
.tx-pax-step { display: flex; align-items: center; gap: 10px; flex: none; }
.tx-pax-btn {
  width: 30px; height: 30px; border-radius: 50%; cursor: pointer;
  background: var(--sp-surface-2, #F1F5F9);
  border: 1.5px solid var(--sp-line, rgba(10, 37, 64, .14));
  font-size: 15px; line-height: 1; color: var(--sp-ink, #0A2540);
}
.tx-pax-btn:hover:not(:disabled) { border-color: var(--sp-accent, #FF4D4D); color: var(--sp-accent, #FF4D4D); }
.tx-pax-btn:disabled { opacity: .4; cursor: not-allowed; }
.tx-pax-btn:focus-visible { outline: 2px solid var(--sp-accent, #FF4D4D); outline-offset: 2px; }
.tx-pax-step output { min-width: 20px; text-align: center; font-weight: 800; font-size: 15px; }

.tx-pax-ages { padding: 10px 0; border-bottom: 1px solid var(--sp-line, rgba(10, 37, 64, .08)); }
.tx-pax-ages b, .tx-pax-cabin b {
  display: block; margin-bottom: 8px; font-size: 11px; font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase; color: var(--sp-muted, #5B6B82);
}
.tx-pax-agegrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.tx-pax-agegrid label { display: block; font-size: 11px; color: var(--sp-muted, #5B6B82); }
.tx-pax-agegrid select {
  width: 100%; margin-top: 3px; padding: 6px 8px; font: inherit; font-size: 13px;
  border: 1.5px solid var(--sp-line, rgba(10, 37, 64, .14)); border-radius: 8px;
  background: var(--sp-surface, #fff); color: var(--sp-ink, #0A2540);
}
.tx-pax-cabin { padding-top: 12px; }
.tx-pax-note { margin: 10px 0 0; font-size: 12.5px; }
.tx-pax-note:empty { display: none; }
.tx-pax-note.is-error { color: var(--sp-coral-dark, #C0392B); }
.tx-pax-done { width: 100%; margin-top: 12px; }

/* --- multi-city ----------------------------------------------------------- */
.tx-segments { display: grid; gap: 12px; }
.tx-seg {
  position: relative; padding: 12px; border-radius: 12px;
  background: var(--sp-surface-2, #F1F5F9);
  border: 1px solid var(--sp-line, rgba(10, 37, 64, .1));
}
.tx-seg-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
.tx-seg-remove {
  position: absolute; top: 8px; right: 10px;
  padding: 3px 10px; border-radius: 999px; cursor: pointer;
  font-size: 11.5px; font-weight: 700; color: var(--sp-muted, #5B6B82);
  background: none; border: 1px solid var(--sp-line, rgba(10, 37, 64, .14));
}
.tx-seg-remove:hover { border-color: var(--sp-coral-dark, #C0392B); color: var(--sp-coral-dark, #C0392B); }
.tx-seg-actions {
  display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; margin-top: 14px;
}
.tx-seg-actions .tx-pax { flex: 1 1 220px; }

/* --- search button, busy state ------------------------------------------- */
.tx-searchgo:disabled { opacity: .75; cursor: progress; }
.tx-spin {
  display: inline-block; width: 13px; height: 13px; margin-right: 7px;
  vertical-align: -1px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .45); border-top-color: #fff;
  animation: txSpin .7s linear infinite;
}
@keyframes txSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .tx-spin { animation-duration: 2s; } }

/* --- responsive ----------------------------------------------------------- */
@media (max-width: 860px) {
  .tx-seg-grid { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  /* Both popups become full-width sheets rather than 320px boxes that would
     hang off the side of a phone. */
  .tx-cal-pop, .tx-pax-pop { position: fixed; left: 10px; right: 10px; width: auto; top: auto; bottom: 10px; max-height: 76vh; overflow-y: auto; }
  .tx-swap { align-self: center; margin: 0 auto; transform: rotate(90deg); }
  .tx-swap:hover { transform: rotate(270deg); }
  .tx-pax-agegrid { grid-template-columns: 1fr; }
  .tx-seg-actions .tx-btn { width: 100%; }
}

/* ==========================================================================
   Hotels search panel
   ==========================================================================
   Reuses tx-ap-*, tx-cal-* and tx-pax-* from the flights panel — same widgets,
   same classes. Only what is particular to a stay is new: the night count, the
   per-room blocks and the date range shading.
   ========================================================================== */

.tx-hotelgrid { align-items: end; }

/* Nights, derived from the two dates rather than asked for. */
.tx-nights { max-width: 92px; }
.tx-nights-out {
  display: block; min-height: 42px; padding: 10px 12px;
  font-size: 15px; font-weight: 800; text-align: center;
  color: var(--sp-ink, #0A2540);
  background: var(--sp-surface-2, #F1F5F9);
  border: 1.5px solid var(--sp-line, rgba(10, 37, 64, .14));
  border-radius: 10px;
}

/* The stay, shaded between check-in and check-out. */
.tx-cal-cell.is-inrange:not(:disabled) {
  background: var(--sp-accent-soft, rgba(255, 77, 77, .12)); border-radius: 0;
}
.tx-cal-cell.is-rangeend:not(:disabled) {
  background: var(--sp-accent, #FF4D4D); color: var(--on-accent, #12253F); font-weight: 800;
}
.tx-cal-foot {
  margin-top: 8px; padding-top: 8px; font-size: 12px; text-align: center;
  color: var(--sp-muted, #5B6B82);
  border-top: 1px solid var(--sp-line, rgba(10, 37, 64, .08));
}

/* One block per room inside the Rooms & guests popup. */
.tx-room {
  padding: 10px 0;
  border-bottom: 1px solid var(--sp-line, rgba(10, 37, 64, .1));
}
.tx-room:first-child { padding-top: 0; }
.tx-room header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 4px;
}
.tx-room header b {
  font-size: 11px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--sp-muted, #5B6B82);
}
.tx-room-remove {
  padding: 2px 9px; border-radius: 999px; cursor: pointer;
  font-size: 11.5px; font-weight: 700; color: var(--sp-muted, #5B6B82);
  background: none; border: 1px solid var(--sp-line, rgba(10, 37, 64, .14));
}
.tx-room-remove:hover { border-color: var(--sp-coral-dark, #C0392B); color: var(--sp-coral-dark, #C0392B); }
.tx-room-add { width: 100%; margin-top: 12px; }

/* Group booking: an honest placeholder, not a disabled form. */
.tx-group-note { text-align: left; }
.tx-group-note .tx-btn { margin-top: 12px; display: inline-block; }

@media (max-width: 560px) {
  .tx-nights { max-width: none; }
  .tx-nights-out { text-align: left; }
}

/* ==========================================================================
   Packages search panel
   ==========================================================================
   Nothing new beyond a grid: the destination picker and the traveller popup
   are the shared widgets, and the three bands are plain selects already
   styled by .tx-sf.
   ========================================================================== */
.tx-pkggrid { align-items: end; }
.tx-pkggrid .tx-ap { min-width: 200px; }
@media (max-width: 560px) {
  .tx-pkggrid .tx-ap { min-width: 0; }
}

/* ==========================================================================
   Loading skeletons
   ==========================================================================
   Card-shaped, because what is coming is a list of cards. A spinner would
   say "wait"; this says "a list is arriving", and stops the layout jumping
   when it does.
   ========================================================================== */
.tx-skeleton { display: grid; gap: 14px; }
.tx-sk-card {
  padding: 18px; border-radius: 16px;
  background: var(--sp-surface, #fff);
  border: 1px solid var(--sp-line, rgba(10, 37, 64, .1));
}
.tx-sk-line {
  height: 12px; margin-bottom: 10px; border-radius: 6px;
  background: linear-gradient(90deg,
    var(--sp-surface-2, #F1F5F9) 25%,
    rgba(10, 37, 64, .07) 37%,
    var(--sp-surface-2, #F1F5F9) 63%);
  background-size: 400% 100%;
  animation: txShimmer 1.4s ease-in-out infinite;
}
.tx-sk-line:last-child { margin-bottom: 0; }
.tx-sk-line.w40 { width: 40%; }
.tx-sk-line.w55 { width: 55%; }
.tx-sk-line.w70 { width: 70%; }
@keyframes txShimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* --------------------------------------------------------------------------
   Flight skeleton

   Built on .tx-flight itself, so the placeholder inherits the real card's
   grid, padding, border and radius and the list does not resize when the two
   are swapped. Only the CONTENTS are grey bars; the box is the box.
   -------------------------------------------------------------------------- */
.tx-sk-list { display: block; }
.tx-sk-flight { pointer-events: none; }
/* The real card lifts on hover. A placeholder that did the same would invite a
   click on nothing. */
.tx-sk-flight:hover { transform: none; box-shadow: var(--tx-shadow); border-color: var(--tx-line); }
.tx-sk-col { display: flex; flex-direction: column; gap: 8px; }
.tx-sk-col .tx-sk-line { margin-bottom: 0; }

.tx-sk-logo {
  width: 46px; height: 46px; flex: none; border-radius: 12px;
  background: var(--tx-surface-2); border: 1px solid var(--tx-line);
}
/* Sized to the type it stands in for: 21px/800 times, a 19px fare. A bar that
   is not the height of its text is what makes the swap visible. */
.tx-sk-time  { height: 21px; width: 74px; border-radius: 7px; }
.tx-sk-dur   { height: 12px; width: 62px; }
.tx-sk-stops { height: 11px; width: 54px; }
.tx-sk-fare  { height: 19px; width: 88px; margin-left: auto; border-radius: 7px; }
.tx-sk-btn {
  height: 40px; border-radius: 11px;
  background: linear-gradient(90deg,
    var(--tx-surface-2) 25%, var(--tx-sk-hi) 37%, var(--tx-surface-2) 63%);
  background-size: 400% 100%;
  animation: txShimmer 1.4s ease-in-out infinite;
}
.tx-sk-rule { width: 100%; height: 2px; border-radius: 2px; background: var(--tx-line); }
.tx-sk-line.w45 { width: 45%; }
.tx-sk-line.w50 { width: 50%; }
.tx-sk-line.w60 { width: 60%; }
.tx-sk-line.w80 { width: 80%; }
/* The skeleton borrows the page palette rather than the hard-coded light one
   the generic cards above use, so it is right in either theme. */
.tx-scope .tx-sk-card { background: var(--tx-surface); border-color: var(--tx-line); }
.tx-scope .tx-sk-line {
  background: linear-gradient(90deg,
    var(--tx-surface-2) 25%, var(--tx-sk-hi) 37%, var(--tx-surface-2) 63%);
  background-size: 400% 100%;
}

/* --------------------------------------------------------------------------
   Hotel skeleton

   Same principle as the flight one above: built on .tx-card, so it lands in
   the same grid cell with the same border, radius and padding, and .tx-media
   keeps its 4/3 aspect-ratio so the image well is the height the photograph
   will be. Each bar is sized to the type it stands in for — a 16px/800 name,
   an 18px price — because a bar that is not the height of its text is what
   makes the swap visible.
   -------------------------------------------------------------------------- */
.tx-sk-hotel { pointer-events: none; }
/* The real card lifts and zooms its photograph on hover. A placeholder that
   did either would invite a click on nothing. */
.tx-sk-hotel:hover { transform: none; box-shadow: var(--tx-shadow); }

/* The image well. .tx-media already carries the aspect-ratio and the surface;
   this only adds the shimmer, so the well cannot be a different height from
   the photograph that replaces it. */
.tx-sk-media {
  background: linear-gradient(90deg,
    var(--tx-surface-2) 25%, var(--tx-sk-hi) 37%, var(--tx-surface-2) 63%);
  background-size: 400% 100%;
  animation: txShimmer 1.4s ease-in-out infinite;
}

/* Each bar stands the height of the line it replaces, so the two stack to the
   same total: a 16px/1.3 name renders 21, the star row 16, the location 20. */
.tx-sk-h-title { height: 19px; width: 82%; }
.tx-sk-h-stars { height: 15px; width: 58%; }
.tx-sk-h-sub   { height: 18px; width: 70%; }
.tx-sk-h-cancel { display: flex; flex-direction: column; gap: 6px; }
.tx-sk-h-note  { height: 12px; width: 100%; margin-bottom: 0; }
.tx-sk-h-cap   { height: 11px; width: 52px; }
.tx-sk-h-price { height: 20px; width: 86px; border-radius: 7px; }
.tx-sk-h-tax   { height: 11px; width: 68px; }

/* Amenity chips: the real ones are pill-shaped and sized by their word, so the
   three placeholders differ in width rather than sitting in a tidy row of
   identical stubs no real card would ever produce. */
/* Widths are those of the amenity labels the real cards actually carry —
   "Free Wi-Fi", "Pool", "Airport shuttle" — because four chips that all fit on
   one row make the placeholder a whole row shorter than the card. At this
   column width the fourth wraps, exactly as it does on the real one. */
.tx-sk-chip {
  display: block; height: 22px; width: 76px; border-radius: 999px;
  background: linear-gradient(90deg,
    var(--tx-surface-2) 25%, var(--tx-sk-hi) 37%, var(--tx-surface-2) 63%);
  background-size: 400% 100%;
  animation: txShimmer 1.4s ease-in-out infinite;
}
.tx-sk-chip.w-sm { width: 46px; }
.tx-sk-chip.w-lg { width: 98px; }

.tx-sk-h-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
/* The real buttons are .tx-btn at 9px/12.5px inside .tx-card-actions — shorter
   than the 40px list-view button .tx-sk-btn is sized for. */
.tx-sk-h-btn { height: 34px; width: 92px; border-radius: 11px; }

@media (prefers-reduced-motion: reduce) {
  .tx-sk-line, .tx-sk-btn, .tx-sk-media, .tx-sk-chip { animation: none; }
}

/* ==========================================================================
   Image gallery (hotel-gallery.js)

   Written for n photographs and correct at n = 1, which is what every
   property has today: the frame renders, and the thumbnails, arrows and
   counter simply are not emitted. Nothing here changes when the API starts
   returning eight.
   ========================================================================== */
.hg { display: flex; flex-direction: column; gap: 8px; }
.hg-stage {
  position: relative; border-radius: 12px; overflow: hidden;
  background: var(--tx-surface-2); aspect-ratio: 16 / 10;
}
.hg-frame { margin: 0; position: absolute; inset: 0; opacity: 0; transition: opacity .28s ease; }
.hg-frame.is-active { opacity: 1; }
.hg-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }

.hg-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 34px; height: 34px; display: grid; place-items: center;
  border: none; border-radius: 50%; cursor: pointer;
  background: rgba(255, 255, 255, .92); box-shadow: 0 2px 8px rgba(10, 37, 64, .22);
}
.hg-nav svg { width: 18px; height: 18px; fill: none; stroke: #0A2540; stroke-width: 2.2; }
.hg-prev { left: 10px; }
.hg-next { right: 10px; }
.hg-nav:focus-visible, .hg-expand:focus-visible, .hg-thumb:focus-visible {
  outline: 2px solid var(--tx-accent); outline-offset: 2px;
}

.hg-counter {
  position: absolute; left: 10px; bottom: 10px; margin: 0; z-index: 2;
  padding: 4px 9px; border-radius: 999px; background: rgba(10, 37, 64, .78);
  color: #fff; font-size: 11.5px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.hg-expand {
  position: absolute; right: 10px; bottom: 10px; z-index: 2;
  width: 32px; height: 32px; display: grid; place-items: center;
  border: none; border-radius: 8px; cursor: pointer;
  background: rgba(10, 37, 64, .78);
}
.hg-expand svg { width: 15px; height: 15px; fill: none; stroke: #fff; stroke-width: 2; }

.hg-thumbs { display: flex; gap: 7px; overflow-x: auto; padding-bottom: 2px; scrollbar-width: thin; }
.hg-thumb {
  flex: none; width: 62px; height: 46px; padding: 0; cursor: pointer;
  border: 2px solid transparent; border-radius: 8px; overflow: hidden;
  background: var(--tx-surface-2); opacity: .65; transition: opacity .18s ease, border-color .18s ease;
}
.hg-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hg-thumb.is-active { opacity: 1; border-color: var(--tx-accent); }
.hg-thumb:hover { opacity: 1; }

/* Fullscreen viewer. A dialog: Escape closes, arrows move, focus is trapped
   while open and restored to whatever opened it. */
.hg-overlay {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: 12px; padding: 20px; background: rgba(6, 12, 24, .93);
}
.hg-overlay[hidden] { display: none; }
.hg-overlay-stage { display: grid; place-items: center; max-height: 88vh; }
.hg-overlay-stage img { max-width: 100%; max-height: 88vh; border-radius: 10px; display: block; }
.hg-overlay .hg-nav { position: static; transform: none; width: 44px; height: 44px; }
.hg-close {
  position: absolute; top: 16px; right: 16px; z-index: 2;
  width: 40px; height: 40px; display: grid; place-items: center;
  border: none; border-radius: 50%; cursor: pointer; background: rgba(255, 255, 255, .14);
}
.hg-close svg { width: 20px; height: 20px; fill: none; stroke: #fff; stroke-width: 2.2; }
.hg-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.hg-overlay-counter {
  position: absolute; left: 0; right: 0; bottom: 18px; margin: 0; text-align: center;
  color: rgba(255, 255, 255, .82); font-size: 13px; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
/* The page must not scroll behind an open viewer. */
body.hg-locked { overflow: hidden; }

@media (max-width: 560px) {
  .hg-overlay { grid-template-columns: 1fr; padding: 12px; }
  .hg-overlay .hg-nav { position: absolute; top: 50%; transform: translateY(-50%); }
  .hg-overlay .hg-prev { left: 8px; }
  .hg-overlay .hg-next { right: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .hg-frame { transition: none; }
  .hg-thumb { transition: none; }
}

/* ===========================================================================
   The flight card, restacked by its COLUMN's width
   ===========================================================================
   Declared last on purpose. `.tx-flight` sets `grid-template-columns` several
   hundred lines above; a container query carries no extra specificity, so the
   same selector written earlier simply loses to it and the card stayed in
   three columns no matter how narrow its column got. Source order is the whole
   fix, and it belongs at the end of the file rather than next to the layout it
   serves.

   The container is .tx-results-col (declared with the layout above). At 1440px
   that column is 596px — two rails take 572px between them — which is why this
   is a container query and not a media query: the viewport is wide, the column
   is not, and only the column knows.
   =========================================================================== */
@container txlist (max-width: 700px) {
  .tx-flight { grid-template-columns: 1fr; gap: 14px; }
  .tx-carrier { min-width: 0; }
  .tx-act { min-width: 0; grid-template-columns: 1fr 1fr; align-items: center; }
  .tx-fare { grid-column: 1 / -1; text-align: left; }
}
