/* ============================================
   Nakamura Tours & Travels Ã¢â‚¬â€ Main stylesheet
============================================= */

/* ---------- 1. Reset ---------- */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  overflow-x: hidden;
  max-width: 100%;
}
body {
  font-family: var(--font-base);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--color-text);
  background: var(--color-bg);
  /* clip (not hidden) prevents sidescroll WITHOUT breaking position: sticky */
  overflow-x: clip;
  transition:
    background-color 0.3s ease,
    color 0.3s ease;
}
img,
svg,
video {
  display: block;
  max-width: 100%;
  height: auto;
}
a {
  text-decoration: none;
  color: inherit;
}
ul,
ol {
  list-style: none;
}
button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}
button {
  cursor: pointer;
  border: none;
  background: none;
}
.ico {
  flex-shrink: 0;
}

/* ---------- 2. Tokens ---------- */
:root {
  --color-primary: #6f48a9;
  --color-primary-2: #9a6ac2;
  --color-primary-dark: #522b8d;
  --color-accent: #2563eb;
  /* RGB triplets of the brand colours, for rgba() tints and scrims.
     theme-vars.blade.php overrides these alongside the hex values when the
     admin panel publishes a palette â€” never edit one without the other. */
  --color-primary-rgb: 111, 72, 169;
  --color-bg-rgb: 255, 255, 255;
  --color-bg-alt-rgb: 246, 245, 251;
  /* Gradient family â€” the topbar, buttons and footer use their own more
     vivid purple ramp, deliberately NOT derived from --color-primary (the
     original design shipped these exact stops). Published by the panel as
     theme.site.color_grad_*; defaults here are the shipped design. Not
     overridden in dark mode, same as the original hardcoded values. */
  --grad-btn-1: #7c3aed;
  --grad-btn-2: #4c1d95;
  --grad-btn-2-rgb: 76, 29, 149;
  --grad-deep: #5b21b6;
  --grad-topbar-1: #3b0a8f;
  --grad-topbar-2: #6d28d9;
  --grad-footer-1: #4c1d95;
  --grad-footer-2: #7c3aed;
  --grad-footer-3: #b88fe0;
  --grad-footer-1-dm: #25105f;
  --grad-footer-2-dm: #4c1d95;
  --color-text: #1f2937;
  --color-text-muted: #6b7280;
  --color-bg: #ffffff;
  --color-bg-alt: #f6f5fb;
  --color-border: #e5e7eb;
  /* semantic surfaces (flip in dark mode) */
  --surface: #ffffff;
  --surface-2: #faf9ff;
  --surface-3: #efe7fb;
  --heading: #1b1147;
  --border-soft: #efeafc;
  --font-base: "Poppins", system-ui, -apple-system, sans-serif;
  --container-max: 1200px;
  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --shadow-sm: 0 2px 8px rgba(17, 12, 46, 0.06);
  --shadow-md: 0 10px 30px rgba(var(--grad-btn-2-rgb), 0.08);
  --shadow-lg: 0 20px 50px rgba(var(--grad-btn-2-rgb), 0.14);
  --transition: 0.25s ease;
  /* Was declared ONLY on .flightsearch, so every `var(--ease)` outside that
     widget resolved to nothing â€” and an invalid var() in a shorthand kills the
     whole declaration, so those rules silently had no transition at all (the
     nav scrim's fade-in, and the mobile menu's accordion). Hoisted here; the
     .flightsearch copy is identical and still wins inside the widget. */
  --ease: cubic-bezier(0.2, 0.9, 0.25, 1);
}

/* ---------- Dark theme ---------- */
[data-theme="dark"] {
  --color-primary: #9a6ac2;
  --color-primary-2: #b88fe0;
  --color-primary-rgb: 154, 106, 194;
  --color-bg-rgb: 20, 18, 28;
  --color-bg-alt-rgb: 27, 24, 37;
  --color-text: #e6e3f0;
  --color-text-muted: #a4a0b8;
  --color-bg: #14121c;
  --color-bg-alt: #1b1825;
  --color-border: #2c2838;
  --surface: #211d2c;
  --surface-2: #1c1927;
  --surface-3: #2a2438;
  --heading: #f2effa;
  --border-soft: #322c42;
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 10px 30px rgba(0, 0, 0, 0.45);
  --shadow-lg: 0 20px 50px rgba(0, 0, 0, 0.55);
}
[data-theme="dark"] .flightsearch {
  --ink: #ece9f6;
  --ink-2: #b6b1cd;
  --muted: #8e88a8;
  --line: rgba(255, 255, 255, 0.14);
  --line-soft: rgba(255, 255, 255, 0.08);
  --glass: rgba(40, 35, 54, 0.92);
  --field-hi: #332c44;
  --seg-bg: rgba(255, 255, 255, 0.05);
  --v1: var(--color-primary);
  --v2: var(--color-primary-2);
}
[data-theme="dark"] body {
  background: var(--color-bg);
}
[data-theme="dark"] .flightsearch input::placeholder {
  color: var(--muted);
}
/* trip-type pills: dark surface so the light text is readable */
[data-theme="dark"] .flightsearch .trip label,
[data-theme="dark"] .flightsearch .trip label:hover {
  background: var(--field-hi);
  border-color: var(--line);
  color: var(--ink);
}
[data-theme="dark"] .flightsearch .trip .dot {
  background: var(--field-hi);
}
[data-theme="dark"] .header {
  background: rgba(var(--color-bg-rgb), 0.95);
}
[data-theme="dark"] .hero {
  background: linear-gradient(180deg, var(--color-bg) 0%, var(--color-bg-alt) 60%, var(--color-bg) 100%);
}
[data-theme="dark"] .nav-toggle span {
  background: #d9d3ec;
}
[data-theme="dark"] .nav__link {
  color: var(--color-text);
}
/* use the white logo everywhere in dark mode (same as footer) */
[data-theme="dark"] .logo__img,
[data-theme="dark"] .auth__logo,
/* The flight-search progress overlay's brand mark (partials/flight-progress).
   Its card is background: var(--surface), which is #211d2c in dark mode, so
   the stock dark-ink logo sat almost invisible on it.
   Listed HERE rather than in its own rule beside the other .flightp__ styles:
   this is the one place that decides what "the brand logo in dark mode" means,
   and a second copy is how the two drift apart.
   NOTE: the COMMON preloader (.ntt-preloader-icon, layouts/site.blade.php) is
   deliberately NOT in this list — it shows the admin's own "Loading icon"
   upload, which is a separate asset and is left exactly as it is. */
[data-theme="dark"] .flightp__logo {
  filter: brightness(0) invert(1);
}
/* ...unless a real dark-mode logo was uploaded, in which case it is used as it
   is. Same rule as the header's, and deliberately right here beside it: this
   block is the one place that decides what "the brand logo in dark mode"
   means, and a second copy is how the two drift apart. */
.flightp__logowrap { display: contents; }

/* DOUBLED CLASS, ON PURPOSE — .flightp__logo.flightp__logo--dark, not
   .flightp__logo--dark alone.

   The base rule `.flightp__logo { display: block }` lives ~10,700 lines below
   this block. At equal specificity the later rule wins, so a single class here
   lost every time and BOTH logos rendered, stacked. One extra class outranks
   it wherever it sits.

   These rules stay up here with the dark-mode filter they belong to: this is
   the one place that decides what "the brand logo in dark mode" means, and a
   second copy beside the .flightp__ styles is how the two drift apart. */
.flightp__logo.flightp__logo--dark { display: none; }
[data-theme="dark"] .logo--has-dark .flightp__logo { filter: none; }
[data-theme="dark"] .logo--has-dark .flightp__logo.flightp__logo--light { display: none; }
[data-theme="dark"] .logo--has-dark .flightp__logo.flightp__logo--dark { display: block; }
[data-theme="dark"] .page-hero {
  /* Only the scrim changes in dark mode â€” the photo stays the admin's, same
     rule as the homepage hero. Hardcoding the bundled asset here would throw
     away real content whenever a visitor switched theme. */
  background:
    linear-gradient(180deg, rgba(var(--color-bg-rgb), 0.78), rgba(var(--color-bg-rgb), 0.92)),
    var(--page-hero-image, url("../assets/page-bg-img.png")) center/cover no-repeat;
  border-bottom-color: var(--color-border);
}
[data-theme="dark"] .page-hero__title {
  color: var(--heading);
}
[data-theme="dark"] .page-hero__sub {
  color: var(--color-text-muted);
}
/* lift the hardcoded dark texts in dark mode */
[data-theme="dark"] .hero__sub,
[data-theme="dark"] .review__text {
  color: var(--color-text-muted);
}
[data-theme="dark"] .faq__item summary {
  color: var(--heading);
}
/* Dark-mode hero scrim.
   ONLY the gradient changes â€” it is the readability layer over the photo, and
   light mode has its own equivalent. The photo itself stays var(--hero-image),
   the admin-uploaded slide, exactly as in light mode. This rule used to
   hardcode dark-hero-img.webp here, which silently threw away real content
   whenever a visitor switched theme. The bundled asset survives only as the
   fallback for when no slide image has been set. */
[data-theme="dark"] .hero::before {
  background-image:
    linear-gradient(
      100deg,
      rgba(var(--color-bg-rgb), 0.95) 0%,
      rgba(var(--color-bg-rgb), 0.8) 28%,
      rgba(var(--color-bg-rgb), 0.4) 50%,
      rgba(var(--color-bg-rgb), 0.05) 72%,
      rgba(var(--color-bg-rgb), 0) 100%
    ),
    var(--hero-image, url("../assets/dark-hero-img.webp"));
}

/* ---------- 3. Layout helpers ---------- */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: 1.25rem;
}
.section {
  padding-block: 3.5rem;
}
.section__head {
  text-align: center;
  margin-bottom: 2.5rem;
}
.section__title {
  font-size: 2.2rem;
  font-weight: 700;
}
.section__title span {
  color: var(--color-primary);
}
.section__sub {
  color: var(--color-text-muted);
  margin-top: 0.4rem;
}
.block-title {
  font-size: 1.5rem;
  font-weight: 700;
}
.block-title--center {
  text-align: center;
  margin-bottom: 2.25rem;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--color-primary);
  background: #efe9fd;
  background: color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
  padding: 0.4rem 0.9rem;
  border-radius: 999px;
  margin-bottom: 1rem;
}

/* ---------- 4. Top bar ---------- */
.topbar {
  position: relative;
  z-index: 60;
  background: linear-gradient(90deg, var(--grad-topbar-1), var(--grad-topbar-2));
  color: #e8e2fb;
  font-size: 0.78rem;
}
.topbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 38px;
}
.topbar__tagline {
  display: flex;
  align-items: center;
  gap: 0.45rem;
}
.topbar__links {
  display: flex;
  gap: 1.4rem;
}
.topbar__links a {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  opacity: 0.9;
  transition: var(--transition);
}
.topbar__links a:hover {
  opacity: 1;
}

/* Dark / light mode switcher Ã¢â‚¬â€ in top bar (desktop) */
.theme-toggle {
  position: relative;
  width: 46px;
  height: 22px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.25);
  border: 1px solid rgba(255, 255, 255, 0.4);
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  padding: 0;
  transition: background 0.25s ease;
}
.theme-toggle:hover {
  background: rgba(255, 255, 255, 0.38);
}
.theme-toggle.is-on {
  background: rgba(255, 255, 255, 0.45);
}
/* when relocated into the (light/dark) header, use a visible track */
.header .theme-toggle {
  background: rgba(var(--color-primary-rgb), 0.16);
  border-color: rgba(var(--color-primary-rgb), 0.3);
}
.header .theme-toggle:hover {
  background: rgba(var(--color-primary-rgb), 0.26);
}
.header .theme-toggle.is-on {
  background: rgba(var(--color-primary-rgb), 0.32);
}
[data-theme="dark"] .header .theme-toggle {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.2);
}
.theme-toggle__thumb {
  position: absolute;
  top: 50%;
  left: 2px;
  transform: translateY(-50%);
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: #fff;
  color: var(--color-primary);
  display: grid;
  place-items: center;
  transition: left 0.25s ease;
}
.theme-toggle.is-on .theme-toggle__thumb {
  left: 26px;
}
.theme-toggle__thumb svg {
  width: 12px;
  height: 12px;
}
.theme-toggle .ico-moon {
  display: none;
}
.theme-toggle.is-on .ico-sun {
  display: none;
}
.theme-toggle.is-on .ico-moon {
  display: block;
}
/* ---------- Live chat (floating) ---------- */
.livechat {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 96;
}
.livechat__btn {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  box-shadow: 0 12px 26px -8px rgba(34, 28, 70, 0.55);
  transition:
    transform 0.2s ease,
    box-shadow 0.2s ease;
}
.livechat__btn:hover {
  transform: translateY(-2px);
}
.livechat__btn .ico-close {
  display: none;
}
.livechat.is-open .livechat__btn .ico-chat {
  display: none;
}
.livechat.is-open .livechat__btn .ico-close {
  display: block;
}
.livechat__panel {
  position: absolute;
  right: 0;
  bottom: 72px;
  width: 320px;
  max-width: calc(100vw - 40px);
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: 16px;
  box-shadow: 0 24px 60px -20px rgba(34, 28, 70, 0.45);
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  transform: translateY(12px);
  transition:
    opacity 0.2s ease,
    transform 0.2s ease,
    visibility 0.2s;
}
.livechat.is-open .livechat__panel {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.livechat__head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.9rem 1rem;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
}
.livechat__avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.2);
}
.livechat__head b {
  display: block;
  font-size: 0.92rem;
}
.livechat__head small {
  font-size: 0.74rem;
  opacity: 0.9;
}
.livechat__body {
  padding: 1rem;
  background: var(--color-bg-alt);
  max-height: 230px;
  overflow-y: auto;
}
.livechat__msg {
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: 4px 14px 14px 14px;
  padding: 0.7rem 0.9rem;
  font-size: 0.86rem;
  color: var(--color-text);
  max-width: 85%;
  box-shadow: var(--shadow-sm);
}
.livechat__msg + .livechat__msg {
  margin-top: 0.6rem;
}
.livechat__msg--me {
  margin-left: auto;
  border-radius: 14px 4px 14px 14px;
  background: var(--surface-3);
  border-color: #ddd0f3;
  border-color: color-mix(in srgb, var(--grad-btn-1) 26%, #fff);
}
.livechat__input {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem;
  border-top: 1px solid var(--color-border);
}
.livechat__input input {
  flex: 1;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 0.55rem 0.95rem;
  font-size: 0.85rem;
  outline: none;
}
.livechat__input input:focus {
  border-color: var(--color-primary);
}
.livechat__input button {
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
}

/* Currency / Language selector */
.prefs {
  position: relative;
}
.prefs__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: inherit;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 500;
  opacity: 0.9;
  transition: var(--transition);
}
.prefs__toggle:hover {
  opacity: 1;
}
/* The state badge: selected language code / selected currency symbol.

   PHONE ONLY. It is switched on in the <=768px block, where the top bar is
   gone and this control is an icon-only circle; above that width the desktop
   top bar keeps its globe and its "JPY Â· EN" text, untouched.

   Tabular figures and a fixed line-height so the chip does not resize as the
   selection changes â€” the button must not jitter when someone switches.

   STACKED, not inline. Four glyphs side by side in a 33px circle left nothing
   for any of them â€” the currency in particular came out tiny â€” and the
   separator had to shout to be seen at all. Stacking gives each line the full
   width of the button, so both can be bigger AND calmer, and the arrangement
   itself separates them so no divider is needed. */
.prefs__mark {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /*gap: 1px;*/
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.prefs__mark-lang {
  text-transform: uppercase;
  /* Two caps read wide; a touch smaller and tracked out balances them against
     the single currency glyph below. */
  font-size: 0.92em;
  letter-spacing: 0.04em;
}
/* The stack is the separator. Kept in the markup so a divider can be brought
   back by styling alone. */
.prefs__mark-sep {
  display: none;
}
/* Currency glyphs draw smaller than Latin caps at the same point size, which
   is why the symbol looked shrunken beside "EN". */
.prefs__mark-cur {
  font-size: 1.18em;
}
/* Per-currency correction, via the code class each half carries. The Bengali
   Taka is drawn notably small for its point size; add a line here for any
   other symbol that comes out light. */
.prefs__mark-cur.bdt {
  font-size: 1.1em;
  font-weight: 800;
}
.prefs__caret {
  transition: transform var(--transition);
}
.prefs.is-open .prefs__caret {
  transform: rotate(180deg);
}
.prefs__pop {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  width: 240px;
  z-index: 70;
  background: var(--surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 8px;
  display: none;
}
.prefs.is-open .prefs__pop {
  display: block;
}
.prefs__tabs {
  display: flex;
  background: #f1ecfb;
  background: color-mix(in srgb, var(--grad-btn-1) 10%, #fff);
  border-radius: 9px;
  padding: 3px;
  margin-bottom: 6px;
}
.prefs__tab {
  flex: 1;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.45rem;
  border-radius: 7px;
  color: var(--color-text-muted);
  transition: var(--transition);
}
.prefs__tab.is-active {
  background: var(--surface);
  color: var(--color-primary);
  box-shadow: var(--shadow-sm);
}
.prefs__pane {
  display: none;
}
.prefs__pane.is-active {
  display: block;
}
.prefs__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  width: 100%;
  text-align: left;
  padding: 0.5rem 0.6rem;
  border-radius: 8px;
  color: var(--color-text);
  transition: var(--transition);
}
.prefs__opt:hover {
  background: #f6f4fc;
  background: color-mix(in srgb, var(--grad-btn-1) 6%, #fff);
}
.prefs__opt b {
  font-size: 0.82rem;
  font-weight: 600;
}
.prefs__opt span {
  font-size: 0.72rem;
  color: var(--color-text-muted);
}
.prefs__opt.is-active {
  background: #efe9fd;
  background: color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
}
.prefs__opt.is-active b {
  color: var(--color-primary);
}

/* ---------- 5. Header ---------- */
.header {
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  position: sticky;
  top: 0;
  z-index: 50;
}
.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 78px;
  gap: 1rem;
}

.logo {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.logo__img {
  height: 46px;
  width: auto;
  object-fit: contain;
}
.logo--light .logo__img {
  filter: brightness(0) invert(1);
  height: 42px;
}

/* ---------- the optional dark-mode logo ----------

   With no dark logo uploaded, nothing below applies and the two filter rules
   above and at the top of this file keep doing exactly what they always have:
   flatten the light artwork to solid white wherever the ground is dark.

   Upload one and `logo--has-dark` appears on the wrapper. Then the filter is
   cancelled — a real dark-mode logo is already the right colours, and
   flattening it would throw away the very thing it was uploaded for — and the
   two <img>s swap by theme.

   THE SWAP IS IN CSS, NOT PHP, because the theme toggle flips data-theme
   without a reload: a server-side choice would leave the wrong artwork on
   screen until the next navigation. */
.logo__img--dark { display: none; }

/* The footer is dark in BOTH themes, so it takes the dark logo unconditionally
   — its Blade puts that file straight into the single <img>, and all this has
   to do is stop flattening it. */
.logo--light.logo--has-dark .logo__img { filter: none; }

[data-theme="dark"] .logo--has-dark .logo__img { filter: none; }
[data-theme="dark"] .logo--has-dark .logo__img--light { display: none; }
[data-theme="dark"] .logo--has-dark .logo__img--dark { display: block; }

.nav {
  display: flex;
  gap: 2rem;
}
.nav__link {
  font-size: 0.95rem;
  font-weight: 500;
  color: #374151;
  position: relative;
  padding-block: 0.3rem;
  transition: var(--transition);
}
/* eSIM link is for the mobile menu only */
.nav__link--mob {
  display: none;
}
.nav__link:hover {
  color: var(--color-primary);
}
.nav__link.is-active {
  color: var(--color-primary);
  font-weight: 600;
}
.nav__link.is-active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  background: var(--color-primary);
  border-radius: 2px;
}

.header__right {
  display: flex;
  align-items: center;
  gap: 1rem;
}
/* No call-to-action button: the right cluster has no width left, so an auto
   left margin absorbs the free space and keeps the nav flush right rather
   than floating in the middle of the gap. Desktop only â€” on mobile the nav is
   a dropdown panel and must not be pushed anywhere. */
/* 992px: the nav collapses into the dropdown at max-width 991.98px, so this
   must cover exactly the range where the nav is still an inline row. Below it
   the nav is an absolutely-positioned panel and the toggle occupies the right
   cluster, so neither rule should apply. */
@media (min-width: 992px) {
  .header__inner--no-cta .nav {
    margin-left: auto;
  }
  /* Kill the flex gap so the nav's last item lands on the SAME right edge as
     the topbar's last control. The right cluster still exists (it holds the
     profile menu and the mobile toggle) but has no width at this breakpoint,
     yet `gap` was still reserving 1rem after the nav and pushing it inboard.
     Dropping column-gap costs nothing here: the nav's margin-left:auto above
     already provides all the logo-to-nav separation. */
  .header__inner--no-cta {
    column-gap: 0;
  }
}
.header__profile {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  transition: var(--transition);
}
.header__profile:hover {
  transform: translateY(-1px);
  box-shadow: 0 8px 18px -6px var(--color-primary);
}

/* Profile dropdown */
.profile {
  position: relative;
}
.profile__pop {
  position: absolute;
  top: calc(100% + 12px);
  right: 0;
  width: 200px;
  z-index: 60;
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 6px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition:
    opacity 0.18s var(--transition),
    transform 0.18s var(--transition),
    visibility 0.18s;
}
.profile.is-open .profile__pop {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.profile__item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.6rem 0.7rem;
  border-radius: 9px;
  font-size: 0.9rem;
  font-weight: 500;
  color: #374151;
  transition: var(--transition);
}
.profile__item svg {
  color: var(--color-primary);
}
.profile__item:hover {
  background: #f6f4fc;
  background: color-mix(in srgb, var(--grad-btn-1) 6%, #fff);
  color: var(--color-primary);
}
.profile__item--danger {
  color: #dc2626;
}
.profile__item--danger svg {
  color: #dc2626;
}
.profile__item--danger:hover {
  background: #fef2f2;
  color: #dc2626;
}
.profile__divider {
  display: block;
  height: 1px;
  background: var(--color-border);
  margin: 4px 6px;
}

/* Login / Register button */
.btn-login {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  padding: 0.45rem 0.95rem;
  border-radius: 999px;
  box-shadow: 0 8px 18px -8px var(--color-primary);
  transition: var(--transition);
}
.btn-login svg {
  width: 15px;
  height: 15px;
}
.btn-login:hover {
  transform: translateY(-1px);
  box-shadow: 0 12px 22px -8px var(--color-primary);
}

/* ---------- Auth modal ---------- */
.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
}
.modal.is-open {
  display: flex;
}
.modal__overlay {
  position: absolute;
  inset: 0;
  background: rgba(34, 28, 70, 0.55);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  animation: fade 0.2s ease;
}
.modal__card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 420px;
  max-height: 92vh;
  overflow-y: auto;
  background: var(--surface);
  border-radius: 20px;
  box-shadow: 0 40px 80px -30px rgba(34, 28, 70, 0.6);
  padding: 1.75rem;
  /* NOT `var(--transition)` here. That variable is `0.25s ease`, and in the
     `animation` shorthand a SECOND time value is the DELAY — so this read as
     `pop 0.22s 0.25s ease`: a quarter-second delay during which the card
     painted fully visible, then snapped to pop's `from` state (opacity 0) and
     faded in. Appear, vanish, appear — the blink. `--transition` is a
     duration+easing pair meant for `transition:`, where a second time value is
     also a delay; it simply must not be dropped into `animation:`.
     `backwards` additionally holds the `from` state if a delay is ever added. */
  animation: pop 0.22s ease backwards;
}
@keyframes fade {
  from {
    opacity: 0;
  }
}
@keyframes pop {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.98);
  }
}
.modal__close {
  position: absolute;
  top: 0.9rem;
  right: 1rem;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--color-text-muted);
  display: grid;
  place-items: center;
  transition: var(--transition);
}
.modal__close:hover {
  background: #f1ecfb;
  background: color-mix(in srgb, var(--grad-btn-1) 10%, #fff);
  color: var(--color-primary);
}

.auth__head {
  text-align: center;
  margin-bottom: 1.2rem;
}
.auth__logo {
  height: 38px;
  width: auto;
  margin: 0 auto 0.5rem;
}
.auth__welcome {
  font-size: 0.9rem;
  color: var(--color-text-muted);
}
.auth__tabs {
  display: flex;
  background: #f1ecfb;
  background: color-mix(in srgb, var(--grad-btn-1) 10%, #fff);
  border-radius: 11px;
  padding: 4px;
  margin-bottom: 1.3rem;
}
.auth__tab {
  flex: 1;
  font-weight: 600;
  font-size: 0.92rem;
  padding: 0.6rem;
  border-radius: 8px;
  color: var(--color-text-muted);
  transition: var(--transition);
}
.auth__tab.is-active {
  background: var(--surface);
  color: var(--color-primary);
  box-shadow: var(--shadow-sm);
}
.auth__form {
  display: none;
  flex-direction: column;
  gap: 0.9rem;
}
.auth__form.is-active {
  display: flex;
  animation: fade 0.2s ease;
}
.auth__field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.auth__field span {
  font-size: 0.8rem;
  font-weight: 600;
  color: #374151;
}
.auth__field input {
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 0.7rem 0.9rem;
  font-size: 0.92rem;
  transition: var(--transition);
}
.auth__field input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(109, 40, 217, 0.12);
}
.auth__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.82rem;
}
.auth__check {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--color-text-muted);
  cursor: pointer;
}
.auth__forgot {
  color: var(--color-primary);
  font-weight: 600;
}
.auth__submit {
  margin-top: 0.3rem;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  color: #fff;
  font-weight: 700;
  font-size: 0.95rem;
  padding: 0.8rem;
  border-radius: 11px;
  box-shadow: 0 10px 22px -10px var(--color-primary);
  transition: var(--transition);
}
.auth__submit:hover {
  transform: translateY(-1px);
}
.auth__alt {
  text-align: center;
  font-size: 0.86rem;
  color: var(--color-text-muted);
}
.auth__link {
  color: var(--color-primary);
  font-weight: 700;
}

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 6px;
}
.nav-toggle span {
  width: 24px;
  height: 2px;
  background: var(--heading);
  border-radius: 2px;
  transition:
    transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
    opacity 0.2s linear;
}
/* Open: the burger becomes a cross. The outer bars travel to the middle bar's
   line and rotate across it; the middle bar fades as they arrive.

   7px is the bar height (2) plus the flex gap (5) â€” the distance from an outer
   bar's centre to the middle one's. It stays 7 at every breakpoint because the
   narrow-screen rules only change the bar WIDTH, never the height or the gap.
   Translate is written before rotate so each bar moves to the centre first and
   then turns about its own middle; reversing them would swing it out on an arc
   and land somewhere else. */
.nav-toggle.is-open span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.nav-toggle.is-open span:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}
.nav-toggle.is-open span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}
@media (prefers-reduced-motion: reduce) {
  .nav-toggle span {
    transition: none;
  }
}

/* ---------- 6. Hero ---------- */
.hero {
  position: relative;
  padding-bottom: 3rem;
  background: linear-gradient(180deg, #eaf2ff 0%, #eef1fb 55%, var(--color-bg-alt) 100%);
}
/* Fixed-height background layer Ã¢â‚¬â€ stays the same when the form grows */
.hero::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 700px;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(
      100deg,
      rgba(244, 248, 255, 0.97) 0%,
      rgba(244, 248, 255, 0.85) 28%,
      rgba(244, 248, 255, 0.45) 50%,
      rgba(244, 248, 255, 0.05) 72%,
      rgba(244, 248, 255, 0) 100%
    ),
    var(--hero-image, url("../assets/flight-hero-image.png"));
  background-repeat: no-repeat, no-repeat;
  background-position: center, bottom;
  background-size: cover, cover;
}
.hero .container {
  position: relative;
  z-index: 1;
}
/* mobile-only flowing plane image (desktop uses the ::before background) */
.hero__plane {
  display: none;
  width: 100%;
  max-width: 420px;
  margin: 0 auto 0.5rem;
}
.hero__top {
  display: flex;
  align-items: center;
  min-height: 340px;
  padding-top: 7.5rem;
}
.hero__copy {
  max-width: 640px;
}

/* ===== HERO COPY: READABLE OVER ANY PHOTO ==================================
   The hero image is admin-uploaded, so it can be anything — a bright sky or a
   night skyline — and the copy has to survive both.

   .hero::before already lays a scrim in the THEME's own tone, but it is
   directional: 0.97 at the left edge, 0.45 by halfway, 0.05 by 72%. The copy
   block is 640px wide, which on a 992px viewport reaches about 66% across, so
   its last third sat over the bare photo. Dark text on a dark photo, or light
   text on a bright one, and the end of the sentence disappeared.

   Measured, for the worst case of a pure black photo in light mode and a pure
   white one in dark mode, the scrim directly behind the text has to hold:
       light  0.79 for the sub-line, 0.41 for the title
       dark   0.84 for the sub-line, 0.50 for the title
   0.93 below clears all four with room to spare.

   Carried on the COPY rather than by strengthening .hero::before, because
   raising that gradient to 0.9 out to 68% would wash out most of the picture.
   This wash is only as wide as the words; the photo to the right of them is
   untouched. It is a background, not a pseudo-element, so it paints behind the
   text with no z-index to keep in step with the slider's stacking.

   Feathered with a radial so there is no rectangle edge: solid across the
   words, gone by the block's corners. --hero-scrim-rgb is the theme's own
   surface, so the wash reads as part of the existing scrim rather than a panel
   laid on top. */
.hero__copy {
  --hero-scrim-rgb: 244, 248, 255;
  /* The halo behind the letterforms on phones. Light here because the copy is
     dark in this theme; the dark theme flips it below. */
  --hero-halo: 255, 255, 255;
  position: relative;
}
/* The wash is a pseudo-element, not a background on .hero__copy itself.
   As a background it was CLIPPED by the block's own box: a 135%-wide radial is
   still about 0.6 opaque where the box ends, so the fade had nowhere to finish
   and left a rectangle edge — measured as a 42-step jump in blue at the right
   boundary, which is exactly the "panel pasted on the photo" look this is
   meant to avoid.

   Given its own box, inset well past the words, the gradient reaches zero
   BEFORE anything clips it. z-index:-1 keeps it behind the copy while staying
   inside .hero .container's stacking context, so it cannot fall behind
   .hero::before and take the photo with it. */
.hero__copy::before {
  content: "";
  position: absolute;
  inset: -1.6rem -7rem -1.8rem -2.4rem;
  z-index: -1;
  pointer-events: none;
  /* Sized explicitly, and the vertical radius is deliberately enormous.
     A radial gradient measures distance as an ELLIPSE — sqrt((dx/rx)² +
     (dy/ry)²) — so with a normal vertical radius the LAST line of copy, which
     sits low in the block, is diagonally far from a centre at 50% height and
     lands much further along the ramp than its horizontal position suggests.
     Measured with farthest-side: the sub-line was down to 0.73 alpha where the
     maths said 0.90, and it failed while the title above it passed.
     A 320% vertical radius makes dy/ry ≈ 0, so this behaves as a horizontal
     fade for every line equally, while still feathering top and bottom rather
     than ending in a straight edge.

     The horizontal radius is 66%, not 72%: at 72% the ramp reached zero just
     BEYOND the pseudo-element's own right edge, so it was clipped while still
     about 0.2 opaque and left a 20-step line in the pixels. The fade has to
     finish inside the box it is painted in.

     DELIBERATELY LIGHT. This was 0.95 and it did make the text readable — by
     covering the photograph, which is the point of the hero. The wash now only
     takes the edge off a busy image; legibility itself comes from the halo on
     .hero__copy > *, which is painted WITH the glyphs and so does not depend on
     what the photo is doing at that pixel. */
  /*background: radial-gradient(
    66% 320% ellipse at 30% 50%,
    rgba(var(--hero-scrim-rgb), 0.34) 0%,
    rgba(var(--hero-scrim-rgb), 0.32) 70%,
    rgba(var(--hero-scrim-rgb), 0.24) 85%,
    rgba(var(--hero-scrim-rgb), 0.10) 95%,
    rgba(var(--hero-scrim-rgb), 0) 100%
  );*/
}
/* THE HALO — at every width, because the photo is behind the copy at every
   width: through .hero::before on desktop, and as the full-bleed .hero__plane
   below 992px. Scoping it to phones left the desktop relying on a heavy wash,
   and the only way that wash could win was by hiding the picture.

   Four tight offsets plus a soft bloom: an outline in all directions rather
   than a drop shadow, so a glyph sitting on a busy edge in the photo keeps its
   own edge. --hero-halo is the OPPOSITE tone to the text, per theme, which is
   what lets one rule serve a dark photo and a bright one equally.

   text-shadow rather than -webkit-text-stroke + paint-order: a stroke eats into
   the letterforms at this weight, and Anton has thin counters that close up. A
   shadow leaves the glyph shape alone. */
.hero__copy > * {
  text-shadow:
    0 0 3px rgba(var(--hero-halo), 0.95),
    1px 1px 2px rgba(var(--hero-halo), 0.9),
    -1px 1px 2px rgba(var(--hero-halo), 0.9),
    1px -1px 2px rgba(var(--hero-halo), 0.9),
    -1px -1px 2px rgba(var(--hero-halo), 0.9),
    0 0 12px rgba(var(--hero-halo), 0.65);
}
[data-theme="dark"] .hero__copy {
  --hero-scrim-rgb: var(--color-bg-rgb);
  --hero-halo: 10, 9, 14;
}
/* dashed plane accent above heading */
.hero__kicker {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--color-primary);
  margin: 1.1rem 0 0.2rem;
}
.hero__kicker-line {
  width: 44px;
  border-top: 2px dashed currentColor;
  opacity: 0.55;
}
/* Upright, not tilted. The 8deg rotation this used to carry threw the plane's
   straight edges off the pixel grid and softened them â€” the one thing an
   18px icon cannot afford. Nose points straight up, matching every other
   plane on the page. */
.hero__kicker svg {
  display: block;
  transform: none;
}
/* EXPLORE THE WORLD Ã¢â‚¬â€ small dark line */
.hero__eyebrow {
  display: block;
  font-family: "Oswald", sans-serif;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 1.45rem;
  color: var(--heading);
  margin-bottom: 1rem;
}
/* WITH NAKAMURA Ã¢â‚¬â€ large heavy condensed, "Nakamura" purple */
.hero__title {
  font-family: "Anton", "Oswald", sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 4px;
  font-size: 4.6rem;
  line-height: 1;
  color: var(--heading);
}
.hero__title .grad {
  background: linear-gradient(
    90deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.hero__sub {
  margin-top: 1.1rem;
  color: #475066;
  font-size: 1rem;
}

/* =========================================================
   Search widget Ã¢â‚¬â€ Flights / Hotels / eSIM (ported component)
   All selectors scoped under .flightsearch to avoid clashes
========================================================= */
.flightsearch {
  --ink: #221c46;
  --ink-2: #5a5380;
  --muted: #9a93bc;
  --line: rgba(34, 28, 70, 0.12);
  --line-soft: rgba(34, 28, 70, 0.07);
  --glass: rgba(255, 255, 255, 0.82);
  --field-hi: #ffffff;
  --seg-bg: #f4f0fb;
  --v1: var(--grad-btn-2);
  --v2: var(--color-primary);
  --accent-soft: rgba(109, 40, 217, 0.1);
  --rad: 999px;
  --ease: cubic-bezier(0.2, 0.9, 0.25, 1);
  position: relative;
  z-index: 2;
  margin-top: 4.8rem;
  color: var(--ink);
}

/* floating tab pill */
.flightsearch .tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2px;
  width: max-content;
  max-width: 100%;
  margin: 0 auto 14px;
  padding: 5px;
  background: var(--glass);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border: 1px solid var(--line);
  border-radius: var(--rad);
  box-shadow: 0 12px 30px -16px rgba(34, 28, 70, 0.4);
}
.flightsearch .tab {
  border: 0;
  background: transparent;
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  font-size: 14.5px;
  color: var(--ink-2);
  padding: 11px 22px;
  border-radius: var(--rad);
  display: flex;
  align-items: center;
  gap: 8px;
  transition: color 0.2s var(--ease);
}
/* 18px, not 17: the icons are drawn on a 24-unit grid, so 18 is an exact 3/4
   scale and the shape's own half-unit coordinates land on clean .5 device
   pixels instead of the 0.708 ratio 17px produced. `block` keeps the SVG off
   the text baseline, which was adding a fractional vertical offset of its own. */
.flightsearch .tab svg {
  display: block;
  width: 18px;
  height: 18px;
}
/* THE SAME GROUND AS "MANAGE BOOKING" (.btn-login).

   The widget's own --v1/--v2 pair runs grad-btn-2 -> color-primary at 120deg,
   which is darker and tilted differently from the header's call to action, so
   the two primary buttons on the same screen did not look related. The header
   pair is named directly rather than by redefining --v1/--v2: those two tokens
   drive about forty rules across this widget — field text, borders, the radio
   dot, the datepicker — and moving them would restyle all of it.

   Shadow tinted with --color-primary, as .btn-login does, so the lift matches
   too. */
.flightsearch .tab[aria-selected="true"] {
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  box-shadow: 0 8px 18px -8px var(--color-primary);
}
.flightsearch .tab .new {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.06em;
  background: var(--surface);
  color: var(--v2);
  border-radius: 20px;
  padding: 2px 6px;
}
.flightsearch .tab[aria-selected="true"] .new {
  background: rgba(255, 255, 255, 0.25);
  color: #fff;
}

/* trip toggle */
.flightsearch .triprow {
  display: flex;
  justify-content: center;
  margin: 4px 0 -18px;
  position: relative;
  z-index: 3;
}
.flightsearch .trip {
  display: inline-flex;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
}
.flightsearch .trip label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  user-select: none;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-2);
  padding: 7px 14px 7px 11px;
  border-radius: var(--rad);
  background: rgb(255, 255, 255);
  border: 1.5px solid var(--line);
  transition: all 0.2s var(--ease);
}
.flightsearch .trip input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  /* Size it down to a pixel. `position: absolute` with no offsets leaves the
     input at its static position but at an input's DEFAULT width (~298px),
     which stuck out past the panel and inflated body.scrollWidth â€” invisible
     on screen (opacity 0, and an ancestor clips the paint) but full-page
     screenshots measure that box, so the homepage captured ~1.4x too wide
     with the page stranded in the left portion of the image. Kept focusable
     (not display:none) so the radio group still works from the keyboard. */
  width: 1px;
  height: 1px;
  margin: 0;
}
.flightsearch .trip .dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex: none;
  border: 2px solid #c5bee3;
  background: var(--surface);
  transition: all 0.2s var(--ease);
}
.flightsearch .trip label:hover {
  border-color: #c5bee3;
  background: var(--surface);
}
.flightsearch .trip label:has(input:checked) {
  color: var(--v1);
  border-color: var(--v1);
  background: #f9f5ff;
  background: color-mix(in srgb, var(--grad-btn-1) 5%, #fff);
  box-shadow: 0 8px 18px -11px var(--v1);
}
.flightsearch .trip label:has(input:checked) .dot {
  border-color: var(--v1);
  background: radial-gradient(circle at center, var(--v1) 0 40%, #fff 44%);
}

/* --- dark mode: the checked pill matches the ACTIVE TAB ------------------
   PLACED HERE, not up in the [data-theme="dark"] block at the top of the file,
   and that position is load-bearing. Both this and the light rule above use
   :has(), whose specificity engines have handled inconsistently; when the two
   are treated as equal, SOURCE ORDER decides — and from the top of the file
   the light rule won, so the pill kept the light rule's colour and shadow
   while only taking this rule's background. Sitting after it, this wins
   outright regardless of how :has() is weighed. */
/* The checked trip pill matches the ACTIVE TAB (Flight / Hotel / e-SIM) in
   dark mode — same gradient, same white text, same lift. It used to be a faint
   rgba(154,106,194,.22) tint with #e9def7 text, which read as "slightly
   emphasised" next to a tab that reads as "selected": two controls one above
   the other, both showing a current choice, in two different visual languages.
   Values are copied from .flightsearch .tab[aria-selected="true"] rather than
   invented, so the pair stays in step if the tab is ever restyled.
   Light mode is deliberately untouched — its soft tint works there, because
   the surface behind it is white rather than near-black. */
[data-theme="dark"] .flightsearch .trip label:has(input:checked) {
  /* !important on colour and shadow only, and it is NOT decoration.
     This rule is more specific than the light rule above it AND comes later,
     so it should win outright — and its `background` does. Its `color` and
     `box-shadow` did not: the light rule's values kept computing, verified by
     enumerating the matched rules in the browser (this rule matched, was last,
     and still lost those two properties) and by ruling out an inline style, a
     transition mid-sample and a stale stylesheet. That looks like a `:has()`
     cascade quirk in the engine rather than anything in this file. Rather than
     leave the pill half-styled, the two affected properties are forced.
     If a future engine resolves :has() cascading properly, these can go. */
  color: #fff !important;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  border-color: transparent;
  box-shadow: 0 8px 18px -8px var(--color-primary) !important;
}
/* The radio dot sits ON that gradient now, so it has to be white — the dark
   default (background: var(--field-hi)) would be a dark blob on a bright pill. */
[data-theme="dark"] .flightsearch .trip label:has(input:checked) .dot {
  border-color: #fff;
  background: radial-gradient(circle at center, #fff 0 40%, transparent 44%);
}

.flightsearch .trip input:focus-visible + .dot {
  outline: 2px solid var(--v1);
  outline-offset: 2px;
}

/* connected pill bar */
.flightsearch .bar {
  display: flex;
  align-items: stretch;
  gap: 5px;
  /* Glass lives on ::before, NOT here. A non-none backdrop-filter creates a
     stacking context, which trapped the field popovers inside the bar: once
     they opened UPWARD they were painted under .triprow (z-index: 3) and the
     One-way / Round trip pills sat on top of the airport list and calendar.
     On the pseudo-element the effect is identical and .bar stays a plain
     positioned box, so .ap-pop (z-index 45) outranks .triprow again.
     Any future glass container that hosts a popover needs the same treatment
     â€” see .mc-seg. */
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow:
    0 30px 70px -30px rgba(34, 28, 70, 0.5),
    0 6px 18px -10px rgba(34, 28, 70, 0.25);
  padding: 35px 7px 7px;
  position: relative;
}
.flightsearch .bar::before {
  content: "";
  position: absolute;
  /* -1px so the glass reaches under the 1px border, exactly as a
     background on the element itself would (background-clip: border-box). */
  inset: -1px;
  border-radius: inherit;
  background: var(--glass);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  pointer-events: none;
}
.flightsearch .seg {
  flex: 1 1 0;
  min-width: 0;
  position: relative;
  border-radius: 11px;
  cursor: pointer;
  padding: 11px 16px;
  background: var(--seg-bg);
  /* Each picker is a CHILD of the field that owns it, so on a touch screen
     the field's tap highlight flashed across the open popup as a translucent
     slab. The rows inside carry their own :hover/.sel feedback, so nothing is
     lost by turning the default flash off. */
  -webkit-tap-highlight-color: transparent;
  transition:
    background 0.18s var(--ease),
    box-shadow 0.18s var(--ease);
}
.flightsearch .seg + .seg::before {
  content: "";
  display: none;
  position: absolute;
  left: 0;
  top: 20%;
  height: 60%;
  width: 1px;
  background: var(--line);
}
/* Highlight the field being worked in â€” but only for input types where that
   is a real, TRANSIENT state.

   This was `:hover, :focus-within`, and both latch on a touch screen. A `.seg`
   carries tabindex="0", so tapping it focuses it, and `:focus-within` then
   held the white `--field-hi` background for the rest of the session: a field
   you had filled in looked permanently unlike one you had not (Depart and To
   went white while From and Travellers stayed tinted). Mobile browsers latch
   `:hover` after a tap in much the same way.

   `:focus-visible` is the fix rather than dropping focus styling altogether â€”
   it matches keyboard focus and not a pointer tap, so keyboard users keep the
   affordance and a tap leaves the field looking like its neighbours. */
@media (hover: hover) and (pointer: fine) {
  .flightsearch .seg:hover {
    background: var(--field-hi);
    box-shadow: 0 8px 22px -14px rgba(34, 28, 70, 0.4);
  }
  .flightsearch .seg:hover::before,
  .flightsearch .seg:hover + .seg::before {
    opacity: 0;
  }
}
.flightsearch .seg:focus-visible {
  background: var(--field-hi);
  box-shadow: 0 8px 22px -14px rgba(34, 28, 70, 0.4);
}
.flightsearch .seg:focus-visible::before,
.flightsearch .seg:focus-visible + .seg::before {
  opacity: 0;
}
.flightsearch .seg .lab {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 3px;
}
.flightsearch .seg input,
.flightsearch .seg select {
  width: 100%;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  outline: none;
  cursor: inherit;
}
.flightsearch .seg input::placeholder {
  color: #b9b2d6;
  font-weight: 600;
}
.flightsearch .seg .val {
  /* MUST be a block box: text-overflow only applies to block containers, so
     as an inline <span> the ellipsis below never engaged and a long value
     ("1 traveller - Economy") spilled out of its field on narrow screens. */
  display: block;
  min-width: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Same treatment for the real inputs (From / To once typed in): they clip on
   their own, but without this the text simply cuts mid-glyph. */
.flightsearch .seg input {
  text-overflow: ellipsis;
}
/* From / To value: city - country on top, airport name below */
/* once an airport is picked, hide the FROM / TO label and center the value */
.flightsearch .ap-seg:not(.muted-val) {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.flightsearch .ap-seg:not(.muted-val) .lab {
  display: none;
}
.flightsearch .ap-seg .ap-display {
  white-space: normal;
  overflow: visible;
  line-height: 1.25;
}
.flightsearch .ap-seg .ap-display .ap-city {
  display: block;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.flightsearch .ap-seg .ap-display .ap-sub {
  display: block;
  font-size: 11px;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 1px;
}
.flightsearch .seg.muted-val .val {
  color: #8e86ab;
  font-weight: 600;
}

/* swap between From & To */
.flightsearch .seg.from {
  padding-right: 30px;
}
.flightsearch .swap {
  position: absolute;
  right: -15px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  display: grid;
  place-items: center;
  cursor: pointer;
  box-shadow: 0 4px 10px -4px rgba(34, 28, 70, 0.3);
  transition:
    transform 0.45s var(--ease),
    color 0.15s,
    border-color 0.15s;
}
.flightsearch .swap:hover {
  color: var(--v1);
  border-color: var(--v1);
}
.flightsearch .swap.spin {
  transform: translateY(-50%) rotate(180deg);
}
.flightsearch .swap svg {
  width: 15px;
  height: 15px;
  transform: rotate(90deg);
}
.flightsearch .seg.disabled {
  opacity: 0.4;
  pointer-events: none;
}
/* One-way: the Return field is hidden outright (see applyTrip in main.js). */
.flightsearch .seg.is-hidden {
  display: none;
}

/* go button */
.flightsearch .go {
  flex: none;
  align-self: center;
  /* Positioned so it paints ABOVE .bar::before (the glass layer). A static
     child sits in an earlier paint layer than any positioned sibling, so
     without this the glass covers the button â€” invisible on phones, where
     that layer is opaque. .seg is already position: relative for its own
     reasons, which is why the fields were unaffected. */
  position: relative;
  height: 58px;
  border: 0;
  cursor: pointer;
  border-radius: 11px;
  margin-left: 4px;
  /* Matches .btn-login — see the note on the active tab above. */
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  color: #fff;
  font: inherit;
  font-weight: 800;
  font-size: 15px;
  padding: 0 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  box-shadow: 0 12px 26px -12px var(--color-primary);
  transition:
    transform 0.12s var(--ease),
    box-shadow 0.2s var(--ease);
}
.flightsearch .go:hover {
  transform: translateY(-1px);
  box-shadow: 0 18px 32px -14px var(--color-primary);
}
.flightsearch .go:active {
  transform: translateY(0);
}
.flightsearch .go svg {
  width: 18px;
  height: 18px;
}

/* passengers popover */
.flightsearch .pop {
  position: absolute;
  /* Opens ABOVE the field: below, the panel covered the content under
     the widget and ran off the fold on short viewports. */
  bottom: calc(100% + 12px);
  right: 0;
  z-index: 30;
  width: 300px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 8px;
  box-shadow: 0 30px 60px -24px rgba(34, 28, 70, 0.5);
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition:
    opacity 0.18s var(--ease),
    transform 0.18s var(--ease),
    visibility 0.18s;
}
.flightsearch .pop.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.flightsearch .prow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 11px 12px;
}
.flightsearch .prow + .prow {
  border-top: 1px solid var(--line-soft);
}
.flightsearch .prow .t {
  font-weight: 700;
  font-size: 14px;
}
.flightsearch .prow .s {
  font-size: 12px;
  color: var(--muted);
  font-weight: 600;
}
.flightsearch .step {
  display: flex;
  align-items: center;
  gap: 13px;
}
.flightsearch .step button {
  width: 31px;
  height: 31px;
  border-radius: 10px;
  border: 1.5px solid var(--line);
  background: var(--surface);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  color: var(--ink);
  display: grid;
  place-items: center;
  transition: all 0.15s var(--ease);
}
.flightsearch .step button:hover:not(:disabled) {
  border-color: var(--v1);
  color: var(--v1);
}
.flightsearch .step button:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.flightsearch .step .v {
  min-width: 16px;
  text-align: center;
  font-weight: 800;
}
.flightsearch .cabin {
  padding: 10px 12px;
  border-top: 1px solid var(--line-soft);
  margin-top: 2px;
}
.flightsearch .cabin .lab {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 7px;
  display: block;
}
.flightsearch .cab-opts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
}
.flightsearch .cab-opts label {
  text-align: center;
  font-size: 13px;
  font-weight: 700;
  padding: 9px;
  border-radius: 11px;
  border: 1.5px solid var(--line);
  cursor: pointer;
  color: var(--ink-2);
  transition: all 0.15s;
}
.flightsearch .cab-opts input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  /* Size it down to a pixel. `position: absolute` with no offsets leaves the
     input at its static position but at an input's DEFAULT width (~298px),
     which stuck out past the panel and inflated body.scrollWidth â€” invisible
     on screen (opacity 0, and an ancestor clips the paint) but full-page
     screenshots measure that box, so the homepage captured ~1.4x too wide
     with the page stranded in the left portion of the image. Kept focusable
     (not display:none) so the radio group still works from the keyboard. */
  width: 1px;
  height: 1px;
  margin: 0;
}
.flightsearch .cab-opts label:has(input:checked) {
  border-color: var(--v1);
  background: var(--accent-soft);
  color: var(--v1);
}
.flightsearch .pax-done {
  display: block;
  width: calc(100% - 24px);
  margin: 10px 12px 12px;
  padding: 11px;
  border-radius: 12px;
  font: inherit;
  font-weight: 700;
  font-size: 14px;
  color: #fff;
  background: linear-gradient(120deg, var(--v1), var(--v2));
  box-shadow: 0 10px 20px -10px var(--v2);
  transition: transform 0.15s var(--ease);
}
.flightsearch .pax-done:hover {
  transform: translateY(-1px);
}

/* custom date picker */
.flightsearch .date-seg {
  cursor: pointer;
}
:is(.flightsearch, .ntt-dp) .dp-pop {
  position: absolute;
  /* Opens ABOVE the field: below, the panel covered the content under
     the widget and ran off the fold on short viewports. */
  bottom: calc(100% + 12px);
  left: 0;
  z-index: 40;
  width: 300px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 18px;
  padding: 14px;
  box-shadow: 0 30px 60px -24px rgba(34, 28, 70, 0.5);
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition:
    opacity 0.18s var(--ease),
    transform 0.18s var(--ease),
    visibility 0.18s;
}
:is(.flightsearch, .ntt-dp) .dp-pop.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
:is(.flightsearch, .ntt-dp) .dp-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}
:is(.flightsearch, .ntt-dp) .dp-head .m {
  font-weight: 700;
  font-size: 15px;
  color: var(--ink);
}
:is(.flightsearch, .ntt-dp) .dp-arrow {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  border: 1.5px solid var(--line);
  background: var(--surface);
  cursor: pointer;
  color: var(--ink);
  font-size: 18px;
  line-height: 1;
  display: grid;
  place-items: center;
  transition: all 0.15s var(--ease);
}
:is(.flightsearch, .ntt-dp) .dp-arrow:hover:not(:disabled) {
  border-color: var(--v1);
  color: var(--v1);
}
:is(.flightsearch, .ntt-dp) .dp-arrow:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}
:is(.flightsearch, .ntt-dp) .dp-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
:is(.flightsearch, .ntt-dp) .dp-grid .wd {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  text-align: center;
  padding: 4px 0 8px;
  letter-spacing: 0.02em;
}
:is(.flightsearch, .ntt-dp) .dp-day {
  aspect-ratio: 1;
  border: 0;
  background: transparent;
  border-radius: 10px;
  cursor: pointer;
  font: inherit;
  font-weight: 600;
  font-size: 13.5px;
  color: var(--ink);
  display: grid;
  place-items: center;
  transition:
    background 0.12s var(--ease),
    color 0.12s;
}
:is(.flightsearch, .ntt-dp) .dp-day:hover:not(:disabled) {
  background: var(--accent-soft);
  color: var(--v1);
}
:is(.flightsearch, .ntt-dp) .dp-day.out {
  color: #c4bedd;
  font-weight: 500;
}
:is(.flightsearch, .ntt-dp) .dp-day.today {
  box-shadow: inset 0 0 0 1.5px var(--line);
}
:is(.flightsearch, .ntt-dp) .dp-day.sel {
  background: linear-gradient(135deg, var(--v1), var(--v2));
  color: #fff;
  font-weight: 800;
  box-shadow: 0 6px 14px -6px var(--v1);
}
:is(.flightsearch, .ntt-dp) .dp-day:disabled {
  opacity: 0.28;
  cursor: not-allowed;
}

/* airport autocomplete dropdown */
.flightsearch .ap-seg {
  cursor: pointer;
}
.flightsearch .ap-pop {
  position: absolute;
  /* Opens ABOVE the field: below, the panel covered the content under
     the widget and ran off the fold on short viewports. */
  bottom: calc(100% + 12px);
  left: 0;
  z-index: 45;
  width: 360px;
  max-width: 88vw;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 8px;
  box-shadow: 0 30px 60px -24px rgba(34, 28, 70, 0.5);
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition:
    opacity 0.18s var(--ease),
    transform 0.18s var(--ease),
    visibility 0.18s;
}
.flightsearch .ap-pop.open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.flightsearch .ap-search {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: 11px;
  margin-bottom: 6px;
}
.flightsearch .ap-search svg {
  width: 16px;
  height: 16px;
  color: var(--muted);
  flex: none;
}
.flightsearch .ap-search input {
  flex: 1;
  border: 0;
  outline: none;
  background: transparent;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.flightsearch .ap-search input::placeholder {
  color: #b9b2d6;
  font-weight: 500;
}
.flightsearch .ap-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 282px;
  overflow-y: auto;
}
.flightsearch .ap-list::-webkit-scrollbar {
  width: 8px;
}
.flightsearch .ap-list::-webkit-scrollbar-thumb {
  background: var(--line);
  border-radius: 8px;
}
.flightsearch .ap-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 11px;
  cursor: pointer;
  transition: background 0.12s var(--ease);
}
.flightsearch .ap-item:hover,
.flightsearch .ap-item.sel {
  background: var(--accent-soft);
}
/* The airport the OTHER field already holds. Shown rather than hidden so the
   list does not silently lose a row the visitor is looking for â€” its own
   sub-line explains why it cannot be picked. */
.flightsearch .ap-item.is-taken {
  opacity: 0.45;
  cursor: not-allowed;
}
.flightsearch .ap-item.is-taken:hover {
  background: transparent;
}
.flightsearch .ap-item.is-taken small {
  font-style: italic;
}
.flightsearch .ap-ico {
  width: 18px;
  color: var(--v2);
  flex: none;
  display: grid;
  place-items: center;
}
/* Fills the 18px cell exactly (3/4 of the 24-unit grid) â€” at 16px the icon
   sat in an 18px box on a fractional scale and read as slightly soft. */
.flightsearch .ap-ico svg {
  display: block;
  width: 18px;
  height: 18px;
}
.flightsearch .ap-main {
  flex: 1;
  min-width: 0;
}
.flightsearch .ap-main b {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.flightsearch .ap-main small {
  display: block;
  font-size: 12px;
  color: var(--muted);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.flightsearch .ap-code {
  font-size: 13px;
  font-weight: 800;
  color: var(--v2);
  letter-spacing: 0.04em;
  flex: none;
}
.flightsearch .ap-empty {
  padding: 16px 12px;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
}

/* multi-city stacked pills */
.flightsearch #multi-view {
  padding-top: 30px;
}
.flightsearch .mc-seg {
  display: flex;
  align-items: stretch;
  gap: 5px;
  /* Multi-city is commented out of home.blade.php today. If it comes back,
     move this glass to a ::before like .bar does above â€” otherwise its
     popovers get trapped in the same stacking context. */
  background: var(--glass);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 6px;
  margin-bottom: 10px;
  box-shadow: 0 14px 36px -22px rgba(34, 28, 70, 0.45);
}
.flightsearch .mc-seg .seg {
  flex: 1;
}
.flightsearch .mc-remove {
  flex: none;
  align-self: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
  display: grid;
  place-items: center;
  margin: 0 6px;
  transition: all 0.15s var(--ease);
}
.flightsearch .mc-remove:hover {
  border-color: var(--v2);
  color: var(--v2);
}
.flightsearch .mc-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 6px;
  flex-wrap: wrap;
}
.flightsearch .add {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  font-size: 14px;
  color: var(--v1);
  background: rgba(255, 255, 255, 0.7);
  border: 1.5px dashed var(--v1);
  border-radius: var(--rad);
  padding: 11px 18px;
  transition: all 0.15s var(--ease);
}
.flightsearch .add:hover {
  background: var(--surface);
}
.flightsearch .add svg {
  width: 16px;
  height: 16px;
}
.flightsearch .mc-right {
  display: flex;
  gap: 10px;
  align-items: stretch;
  margin-left: auto;
  position: relative;
}
.flightsearch .mc-pax {
  position: relative;
  background: var(--glass);
  border: 1px solid var(--line);
  border-radius: var(--rad);
  padding: 11px 20px;
  cursor: pointer;
  min-width: 190px;
}
.flightsearch .mc-pax .lab {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 2px;
}
.flightsearch .mc-pax .val {
  display: block;
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
}
/* multi-city footer: equal height + matching pill radius for all 3 controls */
.flightsearch .mc-foot {
  align-items: stretch;
}
.flightsearch .mc-foot .add {
  padding-top: 0;
  padding-bottom: 0;
  border-radius: 11px;
}
.flightsearch .mc-foot .mc-pax {
  display: flex;
  flex-direction: column;
  justify-content: center;
  border-radius: 11px;
}
.flightsearch .mc-foot .go {
  height: auto;
  min-height: 52px;
  align-self: stretch;
  border-radius: 11px;
  margin-left: 0;
}

/* quick chips */
.flightsearch .quick {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}
.flightsearch .quick .h {
  font-size: 13px;
  color: var(--ink-2);
  font-weight: 700;
  align-self: center;
  margin-right: 2px;
}
.flightsearch .chip {
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid var(--line-soft);
  border-radius: var(--rad);
  padding: 8px 15px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-2);
  cursor: pointer;
  transition: all 0.15s var(--ease);
}
.flightsearch .chip:hover {
  border-color: var(--v1);
  color: var(--v1);
}
.flightsearch .chip.on {
  background: linear-gradient(120deg, var(--v1), var(--v2));
  color: #fff;
  border-color: transparent;
}

.flightsearch .panel {
  display: none;
}
.flightsearch .panel.active {
  display: block;
  animation: rise 0.3s var(--ease);
}
@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.flightsearch .bar--soon {
  display: block;
  padding: 0;
}
.flightsearch .soon {
  text-align: center;
  padding: 2.6rem 1.5rem;
  /* Lift above .bar::before, the frosted-glass layer. That pseudo is
     absolutely positioned with z-index auto, so it paints AFTER in-flow
     inline content â€” its translucent background and blur(18px) landed on top
     of this text and washed it out completely (the Hotels tab looked empty).
     Every other .bar child (.seg, .go, â€¦) already sets exactly this; the
     coming-soon block was the one that didn't. */
  position: relative;
  z-index: 1;
}
.soon__icon {
  width: 64px;
  height: 64px;
  margin: 0 auto 1.1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--color-primary);
  background: var(--surface-3);
}
.soon__title {
  font-size: 1.35rem;
  font-weight: 700;
  color: var(--heading);
}
.soon__text {
  margin-top: 0.5rem;
  color: var(--color-text-muted);
  font-size: 0.95rem;
  max-width: 420px;
  margin-inline: auto;
}
@media (max-width: 600px) {
  .flightsearch .soon {
    padding: 1.8rem 1rem;
  }
  .soon__title {
    font-size: 1.15rem;
  }

  .logo__img {
    height: 42px;
  }

  .offerd__badge {
    top: 6px!important;
    left: 6px!important;
  }

  .flightl--inline .flightl__region{
    padding-top: 20px!important;
  }
}

/* search component responsive Ã¢â‚¬â€ pill becomes stacked rows */
@media (max-width: 880px) {
  .flightsearch .bar {
    flex-wrap: wrap;
    border-radius: 14px;
    gap: 4px;
  }
  .flightsearch .seg {
    flex: 1 1 44%;
  }
  .flightsearch .seg + .seg::before {
    display: none;
  }
  /* .flightsearch .seg.from {
    padding-right: 20px;
  } */
  .flightsearch .swap {
    right: 8px;
  }
  .flightsearch .go {
    flex: 1 1 100%;
    justify-content: center;
    padding: 15px;
    margin-left: 0;
  }
  .flightsearch .pop,
  :is(.flightsearch, .ntt-dp) .dp-pop,
  .flightsearch .ap-pop {
    left: 0;
    right: auto;
    max-width: calc(100vw - 2.5rem);
  }
  .flightsearch .mc-seg {
    flex-wrap: wrap;
  }
  .flightsearch .mc-seg .seg {
    flex: 1 1 44%;
  }
  .flightsearch .mc-remove {
    order: 3;
  }
}
@media (max-width: 600px) {
  /* The trip pills deliberately keep their FULL desktop size and their tuck
     onto the card here â€” see the base .triprow / .trip rules. They used to be
     shrunk (12px text, 12px dot, tighter padding) and pushed 12px clear of the
     card, which made the control look like a different, detached widget on a
     phone. That compaction existed to fit THREE pills on one row; Multi-city
     has been commented out in pages/home.blade.php for a long time, and the
     remaining two fit at full size down to 320px with room to spare.
     `nowrap` stays: it is what guarantees that. */
  .flightsearch .trip {
    flex-wrap: nowrap;
  }
  .flightsearch .trip label {
    white-space: nowrap;
  }
  /* compact stacked search card */
  .flightsearch .bar {
    flex-direction: column;
    align-items: stretch;
    gap: 5px;
    border-radius: 18px;
    padding: 6px;
  }
  /* Phones drop the glass for a solid card. The override has to land on
     ::before, which is where the glass moved to â€” setting it on .bar alone
     would just paint an opaque layer UNDERNEATH the still-blurred pseudo. */
  .flightsearch .bar::before {
    background: var(--surface);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .flightsearch .seg {
    flex: 1 1 auto;
    width: 100%;
    padding: 11px 16px;
    border-radius: 12px;
    /* One height for every field, whatever it holds. A picked airport hides
       its FROM / TO label and shows a two-line value instead, which came out
       11px shorter than the date and traveller fields stacked beneath it â€”
       visibly ragged. Centring means the shorter states sit in the middle of
       the taller box instead of riding its top edge. min-height, not height,
       so a wrapped label can still grow the field. */
    min-height: 67px;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  /* tinted blocks + gap already separate the stacked fields */
  .flightsearch .seg + .seg {
    border-top: none;
  }
  /* swap floats on the From / To divider, right aligned */
  .flightsearch .seg.from {
    padding-right: 16px;
  }
  .flightsearch .swap {
    position: absolute;
    top: auto;
    bottom: -16px;
    right: 14px;
    left: auto;
    transform: rotate(90deg);
    width: 36px;
    height: 36px;
    background: var(--surface);
    border: 1px solid var(--line);
    box-shadow: 0 6px 14px -4px rgba(34, 28, 70, 0.4);
  }
  .flightsearch .swap.spin {
    transform: rotate(270deg);
  }
  /* full-width search button */
  .flightsearch .go {
    width: 100%;
    height: 52px;
    margin: 8px 0 0;
    justify-content: center;
    border-radius: 12px;
  }
  .flightsearch .tabs {
    width: 100%;
    justify-content: space-between;
  }
  .flightsearch .tab {
    flex: 1;
    justify-content: center;
    padding: 10px 8px;
    font-size: 13px;
  }
  .flightsearch .mc-seg {
    flex-direction: column;
  }
  .flightsearch .mc-seg .seg {
    flex: 1 1 auto;
  }
  /* footer: [Add flight] + [Travellers] share a row, Search full-width below */
  .flightsearch .mc-foot {
    flex-wrap: wrap;
    gap: 8px;
    align-items: stretch;
  }
  .flightsearch .mc-right {
    display: contents;
  }
  .flightsearch .add {
    flex: 1 1 45%;
    justify-content: center;
    white-space: nowrap;
    gap: 5px;
    font-size: 12px;
    padding: 10px 8px;
    border-radius: 12px;
  }
  .flightsearch .add svg {
    width: 14px;
    height: 14px;
  }
  .flightsearch .mc-pax {
    flex: 1 1 45%;
    min-width: 0;
    margin-left: 0;
    padding: 8px 12px;
    border-radius: 12px;
  }
  .flightsearch .mc-pax .lab {
    font-size: 10px;
  }
  .flightsearch .mc-pax .val {
    font-size: 12.5px;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .flightsearch .mc-foot .go {
    flex: 1 1 100%;
  }
  /* ---- Every picker becomes a bottom sheet -----------------------------
     A 300â€“360px popover pinned to its field is a desktop idiom: on a phone it
     covers the form it belongs to, and it opens wherever that field happens
     to sit. Airports, dates, travellers and the eSIM option lists (.opt-pop
     is an .ap-pop) all slide up from the bottom edge instead.

     `position: fixed` is safe here, but only by inspection: nothing in the
     ancestor chain sets transform / filter / backdrop-filter / will-change /
     contain, any of which would make that ancestor the containing block and
     strand the sheet inside the widget. `.tabs` DOES set backdrop-filter â€”
     it is a sibling, not an ancestor. Re-check before adding a blur or an
     animation to any wrapper around .flightsearch.

     Sliding is transform-only. Transform and opacity are the two properties a
     compositor can animate without repainting, and a sheet that fades as well
     as moves reads as sluggish at this size. */
  .flightsearch .pop,
  :is(.flightsearch, .ntt-dp) .dp-pop,
  .flightsearch .ap-pop {
    position: fixed;
    top: auto;
    right: 0;
    bottom: 0;
    left: 0;
    width: auto;
    /* dvh so the sheet is measured against the visible viewport rather than
       the tallest-possible one; the vh line above it is the fallback. */
    max-height: 86vh;
    max-height: 86dvh;
    max-width: none;
    display: flex;
    flex-direction: column;
    z-index: 120;
    padding: 18px 16px calc(16px + env(safe-area-inset-bottom, 0px));
    border: 0;
    border-radius: 22px 22px 0 0;
    box-shadow: 0 -18px 44px -18px rgba(34, 28, 70, 0.45);
    opacity: 1;
    transform: translateY(100%);
    transition:
      transform 0.32s cubic-bezier(0.32, 0.72, 0, 1),
      visibility 0.32s;
  }
  .flightsearch .pop.open,
  :is(.flightsearch, .ntt-dp) .dp-pop.open,
  .flightsearch .ap-pop.open {
    transform: translateY(0);
  }
  /* Grab handle â€” the affordance that says "this came from the bottom and
     belongs to the screen edge", not a control (nothing listens to it). */
  .flightsearch .pop::before,
  :is(.flightsearch, .ntt-dp) .dp-pop::before,
  .flightsearch .ap-pop::before {
    content: "";
    flex: none;
    width: 40px;
    height: 4px;
    margin: -8px auto 12px;
    border-radius: 99px;
    background: var(--line);
  }
  /* Airports: the search box stays put and only the results scroll, so the
     field you are typing into cannot slide out from under the caret. */
  .flightsearch .ap-pop .ap-search {
    flex: none;
  }
  .flightsearch .ap-pop .ap-list {
    flex: 1 1 auto;
    max-height: none;
  }
  /* Dates and travellers are short enough to scroll as one block. */
  :is(.flightsearch, .ntt-dp) .dp-pop,
  .flightsearch .pop {
    overflow-y: auto;
  }
  :is(.flightsearch, .ntt-dp) .dp-pop > *,
  .flightsearch .pop > * {
    flex: none;
  }
}

/* ---- Popover flipped below its field (desktop/tablet only) -------------
   The three pickers default to opening ABOVE, which suits the widget's usual
   spot low in the hero. main.js adds `is-below` when it measures more room
   underneath â€” after the page scrolls, on a short viewport, or when a long
   airport list simply will not fit above.

   Deliberately gated at 601px: from 600px down every picker is a bottom sheet
   pinned to the screen edge (position: fixed), and these rules would fight it.
   They also out-specify the sheet rules (three classes vs two), so without the
   query they would win inside the sheet's own media block.

   Default stays "above", so a browser that never runs the script behaves
   exactly as it shipped. */
@media (min-width: 601px) {
    .flightsearch .pop.is-below,
    :is(.flightsearch, .ntt-dp) .dp-pop.is-below,
    .flightsearch .ap-pop.is-below {
        top: calc(100% + 12px);
        bottom: auto;
    }
    /* Closed state slides from the side it will appear on: down-opening
       panels come from slightly above, not below. */
    .flightsearch .pop.is-below:not(.open),
    :is(.flightsearch, .ntt-dp) .dp-pop.is-below:not(.open),
    .flightsearch .ap-pop.is-below:not(.open) {
        transform: translateY(-6px);
    }
}

/* ---- Scrim behind a mobile bottom sheet --------------------------------
   Created and removed by main.js (it watches the pickers' `open` class), so
   there is no scrim on desktop where the pickers are ordinary popovers.
   Clicking it closes the sheet for free: main.js already closes every picker
   on a document click, and the scrim is outside the field, so that handler
   sees the click as "outside".

   Placement is the whole trick, and it took measuring to get right â€” there
   are TWO nested stacking contexts between the sheet and the page:

     .hero .container   z-index: 1   caps the widget below the sticky header
                                     (50) and the live-chat button (96)
     .flightsearch      z-index: 2   scopes the sheet's own z-index: 120

   So a scrim on <body>, or even on .container, paints straight over the sheet
   however high the sheet's z-index goes. Measured, not guessed:
   elementFromPoint in the middle of an open sheet returned the scrim, i.e.
   the sheet was dimmed and could not be tapped.

   main.js therefore appends the scrim INSIDE .flightsearch â€” one context with
   the sheet, so 110 < 120 finally means what it looks like â€” and lifts
   .container over the header while a sheet is open. Everything then lands in
   one pass: sheet over scrim, scrim over the widget, the hero copy, the
   header AND the live-chat button. */
.hero .container.fs-sheet-lift {
    z-index: 130;
}
.fs-sheet-scrim {
    position: fixed;
    inset: 0;
    z-index: 110;
    background: rgba(20, 16, 40, 0.45);
    opacity: 0;
    transition: opacity 0.32s cubic-bezier(0.32, 0.72, 0, 1);
    -webkit-tap-highlight-color: transparent;
}
.fs-sheet-scrim.is-on {
    opacity: 1;
}
/* The page must not scroll under an open sheet â€” handled by nttScrollLock() in
   main.js rather than `overflow: hidden` here, which iOS Safari ignores for a
   fixed overlay. Shared with the mobile menu and reference-counted, so closing
   one while the other is open does not release the page. */
@media (prefers-reduced-motion: reduce) {
    .fs-sheet-scrim,
    .flightsearch .pop,
    :is(.flightsearch, .ntt-dp) .dp-pop,
    .flightsearch .ap-pop {
        transition: none;
    }
}

/* ---------- 7. Offers ---------- */
.offers {
  background: var(--color-bg-alt);
}
.offers__carousel {
  position: relative;
}
.offers__viewport {
  /* clip only left/right (to hide sliding cards) â€” let the vertical
     shadow show, and reveal a little of the side shadow too */
  overflow: visible;
  clip-path: inset(-45px -16px);
  /* clip-path hides the off-stage clones but does NOT bound layout, so the
     track (real slides + clones, ~3600px) still counted toward the document
     content box. Nothing scrolled to it, but a FULL-PAGE SCREENSHOT measures
     that box â€” so the homepage captured ~3648px wide with the page stranded
     in the left third, while every other page captured correctly.
     `overflow-x: clip` bounds it for real; unlike `hidden` it pairs legally
     with `visible` on the other axis, so the vertical shadow still shows, and
     the 16px clip margin reproduces the horizontal bleed of the inset above.
     Unsupported in pre-16 Safari, where the declaration is simply dropped and
     the old (visually identical) behaviour remains. */
  overflow-x: clip;
  overflow-clip-margin: 16px;
}
.offers__track {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  transition: transform 0.5s var(--ease, cubic-bezier(0.2, 0.9, 0.25, 1));
  will-change: transform;
}
/* slides per view: 3 (desktop) */
.offers__track .offer {
  flex: 0 0 calc((100% - 3rem) / 3);
}
/* the creative renders as a real <img> at its natural ratio â€” cover-cropping
   cut the text off panel-uploaded artwork (standard size: 1859x846) */
.offer img {
  width: 100%;
  height: auto;
  display: block;
}
.carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface);
  color: var(--color-primary);
  box-shadow: var(--shadow-md);
  transition: var(--transition);
}
.carousel-arrow--prev {
  left: -22px;
}
.carousel-arrow--next {
  right: -22px;
}
.carousel-arrow:hover {
  background: var(--color-primary);
  color: #fff;
}

.offer {
  position: relative;
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  background-color: #f6f4fc;
  background-color: color-mix(in srgb, var(--grad-btn-1) 6%, #fff);
  cursor: pointer;
  transition:
    transform 0.2s ease,
    box-shadow 0.2s ease;
}
.offer:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}
.offer__badge {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  align-self: flex-start;
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  color: #fff;
  background: var(--color-primary);
  padding: 0.3rem 0.65rem;
  border-radius: 999px;
  margin-bottom: 0.85rem;
}
.offer--umrah .offer__badge {
  background: var(--color-accent);
}
.offer__text {
  position: relative;
  z-index: 1;
  max-width: 58%;
}
.offer__text h3 {
  font-size: 1.15rem;
  font-weight: 800;
  line-height: 1.15;
  color: var(--heading);
}
.offer__script {
  font-family: "Pacifico", cursive;
  font-size: 0.95rem;
  font-weight: 400;
  display: block;
  margin-bottom: 0.15rem;
  color: var(--grad-deep);
}
.offer__text em {
  font-style: normal;
  color: var(--color-primary);
}
.offer--umrah .offer__save {
  color: var(--color-accent);
  font-size: 1.15rem;
  font-weight: 800;
  line-height: 1.15;
}
.offer--umrah .offer__pkg {
  color: #1f2533;
  font-size: 1.05rem;
  font-weight: 700;
  margin-bottom: 0.6rem;
}
.offer--umrah .offer__from {
  display: inline-block;
  background: var(--color-accent);
  color: #fff;
  font-size: 0.68rem;
  padding: 0.28rem 0.65rem;
  border-radius: 6px;
}
.offer__hl {
  color: var(--color-primary);
  font-weight: 600;
  font-size: 0.9rem;
  margin-top: 0.35rem;
}
.offer__text small {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.74rem;
  color: #475066;
  max-width: 100%;
}

.dots {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 1.8rem;
}
.dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #cfc7e8;
  transition: var(--transition);
  cursor: pointer;
}
.dot.is-active {
  background: var(--color-primary);
  width: 24px;
  border-radius: 999px;
}

/* ---------- 8. Top flight deals ---------- */
.deals__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 2rem;
}
.deals__heading {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}
.deals__icon {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 16px;
  display: grid;
  place-items: center;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  box-shadow: 0 10px 22px -8px var(--color-primary);
}
.deals__heading .block-title {
  font-size: 2rem;
  line-height: 1.1;
  color: var(--heading);
}
.deals__sub {
  margin-top: 0.4rem;
  font-size: 0.95rem;
  color: var(--color-text-muted);
}
.deals__sub span {
  color: var(--color-primary);
  font-weight: 600;
}
.deals__viewall {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--color-primary);
  font-weight: 700;
  font-size: 0.95rem;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  padding: 0.7rem 1.2rem;
  border-radius: 12px;
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
.deals__viewall:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-md);
}
/* bottom "View all" buttons appear only on mobile */
.deals__viewall--bottom,
.link-more--bottom {
  display: none;
}

.deals__grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1.1rem;
}
.deal {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid #eee9f7;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
  border-radius: 18px;
  background: var(--surface);
  padding: 1rem;
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
.deal:hover {
  transform: translateY(-4px);
  border-color: #c9b6f2;
  border-color: color-mix(in srgb, var(--grad-btn-1) 40%, #fff);
  box-shadow: 0 16px 36px -16px rgba(109, 40, 217, 0.45);
}
.deal__top {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.9rem;
}
.deal__logo {
  height: 22px;
  width: auto;
  max-width: 70%;
  object-fit: contain;
}
.deal__logo[alt="Cathay Pacific"] {
  height: 17px;
}
.deal__logo[alt="Singapore Airlines"] {
  height: 24px;
}
.deal__route {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.deal__code {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--heading);
  background: var(--surface);
  border: 1px solid var(--color-border);
  border-radius: 9px;
  padding: 0.4rem 0.6rem;
  min-width: 50px;
  text-align: center;
}
/* dashed line with the arrow in the middle: NRT --->--- KUL */
.deal__mid {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
}
.deal__mid::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  border-top: 1.5px dashed #cdc6e6;
}
.deal__plane {
  position: relative;
  color: var(--color-primary);
  flex-shrink: 0;
  background: var(--surface);
  padding: 0 5px;
}
.deal__cities {
  display: flex;
  justify-content: space-between;
  font-size: 0.78rem;
  color: var(--color-text-muted);
  margin: 0.45rem 0.2rem 0;
}
.deal__art {
  position: relative;
  /* Natural height, not a fixed 116px cover-crop: the artwork renders as a
     real <img> so a panel upload whose ratio differs from the old box is
     shown whole instead of trimmed. min-height keeps the card's shape while
     the image is still loading, and on a deal that has no artwork at all. */
  min-height: 104px;
  margin: 0.7rem 0;
  border-radius: 12px;
  overflow: hidden;
  background-color: #fff;
}
.deal__art img {
  width: 100%;
  height: auto;
  display: block;
}
/* whitish gradient so the image blends into the card on top AND bottom */
.deal__art::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    #fff 0%,
    rgba(255, 255, 255, 0.35) 16%,
    rgba(255, 255, 255, 0) 38%,
    rgba(255, 255, 255, 0) 58%,
    rgba(255, 255, 255, 0.8) 88%,
    #fff 100%
  );
}
.deal__stop {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--color-primary);
  background: rgba(255, 255, 255, 0.9);
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  box-shadow: var(--shadow-sm);
}
.deal__cabin {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-primary);
  margin-bottom: 0.6rem;
}
.deal__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: auto;
}
.deal__price {
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--color-primary);
}
.deal__go {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  color: var(--color-primary);
  background: var(--surface);
  border: 1px solid #e7e0f7;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 18%, #fff);
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
.deal__go svg {
  width: 15px;
  height: 15px;
}
.deal__go:hover,
.deal:hover .deal__go {
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-deep));
  border-color: transparent;
}

/* features bar */
.deals__features {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
  margin-top: 2rem;
  padding: 1.2rem 1.4rem;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: 18px;
}
.feat {
  display: flex;
  align-items: center;
  gap: 0.8rem;
}
.feat + .feat {
  border-left: 1px solid var(--color-border);
  padding-left: 1rem;
}
.feat__icon {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  color: var(--color-primary);
  background: #efe9fd;
  background: color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
}
.feat b {
  display: block;
  font-size: 0.92rem;
  color: var(--heading);
}
.feat small {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
/* shared "view all" link (news section) */
.link-more {
  color: var(--color-primary);
  font-weight: 600;
  font-size: 0.9rem;
  display: inline-flex;
  gap: 0.3rem;
}

/* ---------- 9. Why choose ---------- */
.why__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.25rem;
}
.why__item {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  background: #f6f4fc;
  background: color-mix(in srgb, var(--grad-btn-1) 6%, #fff);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 1.25rem 1.3rem;
  text-align: left;
  transition: var(--transition);
}
.why__item:hover {
  box-shadow: var(--shadow-sm);
  border-color: #ddd2f7;
  border-color: color-mix(in srgb, var(--grad-btn-1) 24%, #fff);
}
.why__icon {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  color: var(--color-primary);
  background: #e9e2fb;
  background: color-mix(in srgb, var(--grad-btn-1) 15%, #fff);
}
.why__text {
  min-width: 0;
}
.why__item h3 {
  font-size: 1rem;
  font-weight: 700;
  color: #1f2533;
  margin-bottom: 0.25rem;
}
.why__item p {
  font-size: 0.83rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}

/* ---------- 10. News ---------- */
.news__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1.25rem;
}
.post {
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
.post:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.post__img {
  position: relative;
  height: 150px;
}
.post__img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The image link fills the frame so the whole picture is clickable. */
.post__imglink {
  display: block;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.post__imglink img {
  transition: transform 0.4s ease;
}
.post:hover .post__imglink img {
  transform: scale(1.05);
}
.post__cat {
  position: absolute;
  top: 0.8rem;
  left: 0.8rem;
  /* Above the image link, which covers the whole frame â€” without this the
     chip is painted over and its own click never lands. */
  z-index: 2;
  background: var(--color-primary);
  color: #fff;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  padding: 0.3rem 0.6rem;
  border-radius: 6px;
  transition: var(--transition);
}
a.post__cat:hover {
  background: var(--color-primary-dark);
  transform: translateY(-1px);
}
.post__body {
  padding: 1rem;
}
.post__body h3 {
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.35;
  margin-bottom: 0.6rem;
}
.post__date {
  font-size: 0.78rem;
  color: var(--color-text-muted);
}

/* ---------- 11. Footer ---------- */
/* Gradient shape (160deg, mid at 50%) matches the PRODUCTION site â€” the
   static theme this app was ported from shipped an older, darker ramp. */
.footer {
  background: linear-gradient(160deg, var(--grad-footer-1), var(--grad-footer-2) 50%, var(--grad-footer-3));
  /* Themable, with the shipped lavender as the fallback — the footer's text
     sits over a gradient, and a brand whose ramp ends lighter than this one's
     has to be able to lift it. Unset, this renders exactly as it always has. */
  color: var(--footer-text, #f3ecfd);
}
[data-theme="dark"] .footer {
  background: linear-gradient(135deg, var(--grad-footer-1-dm), var(--grad-footer-2-dm));
}
.footer__grid {
  display: grid;
  grid-template-columns: 1.8fr 1fr 1.6fr;
  gap: 2rem;
  padding-block: 3rem;
}
.logo--light .logo__text strong {
  color: #fff;
}
.footer__desc {
  font-size: 0.85rem;
  margin: 1rem 0;
  max-width: 320px;
  line-height: 1.7;
}
.socials {
  display: flex;
  gap: 0.6rem;
}
.socials a {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  transition: var(--transition);
}
.socials a:hover {
  background: var(--color-primary-2);
}
.footer__col h4 {
  color: #fff;
  font-size: 1rem;
  font-weight: 600;
  margin-bottom: 1.1rem;
}
.footer__col ul li {
  margin-bottom: 0.6rem;
}
.footer__col ul a {
  font-size: 0.88rem;
  transition: var(--transition);
}
.footer__col ul a:hover {
  color: #fff;
  padding-left: 3px;
}
.footer__contact li {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.88rem;
  margin-bottom: 0.7rem;
}
.footer__contact svg {
  color: #fff;
  flex-shrink: 0;
}
.footer__news p {
  font-size: 0.85rem;
  margin-bottom: 1rem;
  line-height: 1.6;
}
.subscribe {
  display: flex;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 10px;
  padding: 0.3rem;
}
.subscribe input {
  flex: 1;
  background: none;
  border: none;
  outline: none;
  color: #fff;
  padding: 0.5rem 0.7rem;
}
.subscribe input::placeholder {
  color: #b3a9d6;
}
.subscribe button {
  width: 40px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-deep));
}

.footer__bar {
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.footer__bar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1.2rem;
  font-size: 0.82rem;
}
.footer__legal {
  display: flex;
  gap: 1.2rem;
}
.footer__legal a {
  /* The lightest corner of the gradient lands on this row, so it is the first
     thing to go unreadable on a brand with a paler ramp. Same fallback rule as
     the footer's body colour above. */
  color: var(--footer-link, #cfc6ea);
  transition: var(--transition);
}
.footer__legal a:hover {
  color: #fff;
}
.pay {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}
.pay__chip {
  background: var(--surface);
  color: #1f2533;
  font-weight: 700;
  font-size: 0.7rem;
  padding: 0.3rem 0.55rem;
  border-radius: 5px;
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
.pay__chip--pp {
  color: #1d4ed8;
}
.pay__chip--mc {
  gap: 0;
}
.pay__chip--mc i {
  width: 14px;
  height: 14px;
  border-radius: 50%;
}
.pay__chip--mc i:first-child {
  background: #eb001b;
}
.pay__chip--mc i:last-child {
  background: #f79e1b;
  margin-left: -6px;
  mix-blend-mode: multiply;
}

/* ---------- 12. Responsive ---------- */
@media (max-width: 1024px) {
  .deals__grid {
    grid-template-columns: repeat(3, 1fr);
  }
  .why__grid,
  .news__grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .deals__features {
    grid-template-columns: 1fr 1fr;
    gap: 1rem 1.4rem;
  }
  .feat:nth-child(odd) {
    border-left: none;
    padding-left: 0;
  }
  .footer__grid {
    grid-template-columns: 1fr 1fr 1fr;
  }
  .hero__title {
    font-size: 3.4rem;
  }
}
/* THE MENU BREAKPOINT. 991.98px, and three other blocks are keyed to it: the
   `min-width: 992px` counterparts that undo it, the .navtools card below, and
   the rule that hides the top bar. They have to agree.

   It was 860px here while the tools moved into the menu at 768px, and the gap
   between the two was a real hole: from 769px to 860px the burger was showing,
   so the top bar was the only place left for Appearance, Currency and Language
   — and the top bar is hidden. Those three controls simply were not reachable
   at those widths. */
@media (max-width: 991.98px) {
  .nav-toggle {
    display: flex;
  }
  /* The DROPDOWN is the wrapper now, not the nav: it holds the links panel and
     the tools card below it, so the two can be separate surfaces with a gap
     between them. Everything that used to live on `.nav` â€” absolute placement,
     the slide/fade, the z-index â€” moved up here.

     Driven by `body.nav-open` rather than `.nav.is-open`, because the class
     that opens the menu sits on the nav INSIDE this element and cannot style
     its own parent. main.js already sets both. */
  .header__drop {
    display: block;
    position: absolute;
    top: 78px;
    left: 0;
    right: 0;
    transform: translateY(-10px);
    opacity: 0;
    visibility: hidden;
    transition: var(--transition);
    z-index: 40;
  }
  body.nav-open .header__drop {
    transform: translateY(0);
    opacity: 1;
    visibility: visible;
  }
  .nav {
    display: flex;
    background: var(--surface);
    flex-direction: column;
    gap: 0;
    padding: 0.5rem 1.25rem;
    box-shadow: var(--shadow-md);
    border-top: 1px solid var(--color-border);
  }
  /* The dropdown carries the links AND the tools card, so on a short phone it
     can be taller than the space below the header. It scrolls itself rather
     than being clipped â€” the page behind is frozen by nttScrollLock, and
     `overscroll-behavior: contain` stops a flick at the end of the list from
     chaining out to that frozen body. dvh where supported, so a mobile
     browser's collapsing address bar cannot cut the last row off.

     On the WRAPPER, not the nav: the card sits outside the nav now, and a
     scroll container on the nav alone would leave the card unreachable. */
  .header__drop {
    max-height: calc(100vh - 78px);
    max-height: calc(100dvh - 78px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  /* The floating chat button is z-96 â€” above the menu's scrim, so while the
     menu is open it hovered over the dimmed page and, now that the panel
     reaches further down, over the panel's own controls. Behind the scrim it
     recedes with everything else it belongs to. */
  body.nav-open .livechat {
    z-index: 38;
  }
  /* Everything behind the open menu is pushed back with a dim + blur, so the
     panel reads as a layer rather than a list floating over live content.

     z-index 39 is deliberate: it sits UNDER the sticky header (50), so the
     header and the panel inside it stay crisp above the scrim while the rest
     of the page recedes. Lift it past 50 and it fogs the menu itself.

     A pseudo-element rather than a real node, so there is nothing to create,
     remove, or leave behind on a bfcache restore. main.js closes the menu on a
     click outside .header â€” precisely the region this covers. */
  body.nav-open::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 39;
    background: rgba(20, 16, 40, 0.35);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    animation: nttNavScrim 0.25s var(--ease) both;
  }
  /* The scroll lock lives in main.js (nttScrollLock), not here: `overflow:
     hidden` on <body> does not hold on iOS Safari, which needs the body pinned
     and offset by the current scroll. */
  @keyframes nttNavScrim {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
  .nav__link {
    padding-block: 0.7rem;
    border-bottom: 1px solid var(--color-border);
  }

  .nav__link:last-of-type {
    border-bottom: 0;
  }
  /* mobile menu shows Flights / Hotels / eSIM only */
  .nav__link--mob {
    display: block;
  }
  .nav__link--desk {
    display: none;
  }
  /* mobile hero: real flowing plane image instead of the tall background */
  .hero::before {
    display: none;
  }
  .hero {
    background: linear-gradient(180deg, var(--color-bg) 0%, #f1f3fb 55%, var(--color-bg-alt) 100%);
  }
  .hero__plane {
    display: block;
    max-width: none;
    width: 100vw;
    margin-left: 50%;
    transform: translateX(-50%);
    margin-bottom: 0;
    /* fade the bottom edge so the image blends into the section background */
    -webkit-mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);
  }
  .hero__top {
    display: block;
    min-height: auto;
    padding-top: 0.4rem;
    text-align: center;
  }
  .hero__copy {
    max-width: 100%;
  }
  /* The WASH is off here: .hero::before is gone and the copy mostly clears the
     plane image, so a panel would only tint a flat background. Phones overlap
     the image properly and get a light one back; see the 600px block. */
  .hero__copy::before {
    display: none;
  }


  .hero__kicker {
    justify-content: center;
  }
  .flightsearch {
    margin-top: 1.5rem;
  }
  /* offers carousel: 2 per view on tablet */
  .offers__track .offer {
    flex: 0 0 calc((100% - 1.5rem) / 2);
  }
}

/* ---------- Mobile menu tools (<= 768px) ---------------------------------
   Below 769px the top bar is gone and main.js moves its two controls â€” the
   theme switch and the currency/language selector â€” into the MENU PANEL,
   under the nav links, rather than adding two more icon-only circles beside
   the CTA and the burger.

   They are the same DOM nodes as on desktop, re-faced here as a COMPACT list:
   one 44px row each, showing what is in force rather than every option.

     Appearance   [ small pill switch ]      <- the shipped 46x22 toggle
     Currency     BDT                    v   <- tap to reveal the options
     Language     EN                     v

   Options stay collapsed. Expanded, they are small chips under their row, and
   picking one collapses it again â€” the menu is already a scrolling panel, and
   three open option lists inside it pushed the whole thing past a phone
   screen. The desktop POPOVER is not used here at all: an absolutely
   positioned panel inside a scrollable, clipped menu either gets cut off or
   escapes it, and it would be a second dismissible layer competing with the
   menu for the next tap.

   Hidden above 768px: between 769 and 860 the menu is still a panel but the
   TOP BAR is back and owns both controls, so showing this too would render the
   same state twice. */
.navtools {
  display: none;
}
/* Above 991.98px there is no dropdown: the wrapper must not exist as a box, or
   it would become the flex child of .header__inner and the nav would lose its
   own place in the header row. `display: contents` lifts its children into that
   row exactly as before.

   min-width, NOT an unscoped rule: this section of the file comes AFTER the
   menu block that makes the wrapper the dropdown, so at equal specificity an
   unscoped `display: contents` would win there too â€” the nav and the tools
   card collapsed into two narrow columns of the header row. */
@media (min-width: 992px) {
  .header__drop {
    display: contents;
  }
}
/* The pane rows and their icons belong to the mobile menu ONLY. Without this
   they also render inside the desktop popover, under the tab strip that
   already names those panes â€” "Currency  BDT  v" sat above the currency list. */
.prefs__pane-title,
.navtools__ico {
  display: none;
}

/* Keyed to THE MENU BREAKPOINT above (991.98px), not to a phone width: these
   controls live in the menu exactly when the menu exists. */
@media (max-width: 991.98px) {
  /* A CARD of its own, floating below the links sheet with a gap between them
     â€” preferences are settings, not three more menu entries, and sharing one
     surface with the links made them read as exactly that. Inset to the same
     1.25rem as the links above it so the two still line up. */
  .navtools {
    display: block;
    margin: 0.7rem 1.25rem 1rem;
    padding: 0.15rem 0.9rem;
    border: 1px solid var(--color-border);
    border-radius: 18px;
    background: var(--surface);
    box-shadow: var(--shadow-md);
  }
  /* One row shape for all three, so "Appearance" and the two selector rows
     line up as a single list. */
  .navtools__row,
  .navtools .prefs__pane-title {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    width: 100%;
    min-height: 52px;
    padding: 0.25rem 0;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--color-text);
    text-align: left;
  }
  /* Tinted icon tile â€” the one element that makes these read as a designed
     settings list rather than three lines of text. */
  .navtools__ico {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 10px;
    color: var(--color-primary);
    background: color-mix(in srgb, var(--color-primary) 11%, var(--surface));
  }
  /* A divider above EVERY pane, so Appearance | Currency | Language are
     separated the same way. It cannot be `.prefs__pane + .prefs__pane`: that
     skips the first pane, which is the one that follows the Appearance row â€”
     and the row is not its sibling anyway (the panes are two levels down,
     inside .prefs > .prefs__pop), so no sibling combinator can reach across
     that boundary. */
  .navtools .prefs__pane {
    border-top: 1px solid var(--color-border);
  }
  .navtools__label,
  .navtools .prefs__pane-name {
    flex: 1 1 auto;
    min-width: 0;
  }
  .navtools__slot[data-slot="theme"] {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
  }

  /* ---- Currency / language: compact disclosure rows ---- */
  .navtools .prefs {
    position: static;
    width: 100%;
  }
  /* The trigger and the tab strip both have no job: each pane now carries its
     own row, and that row is the trigger. */
  .navtools .prefs__toggle,
  .navtools .prefs__tabs {
    display: none;
  }
  .navtools .prefs__pop {
    position: static;
    display: block;
    width: auto;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  /* Both panes render â€” `.is-active` is the tab strip's state and there is no
     tab strip here â€” but each shows only its row until expanded. */
  .navtools .prefs__pane,
  .navtools .prefs__pane.is-active {
    display: block;
  }
  .navtools .prefs__pane-value {
    flex: 0 0 auto;
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--color-primary);
    letter-spacing: 0.01em;
  }
  .navtools .prefs__pane-caret {
    flex: 0 0 auto;
    color: var(--color-text-muted);
    transition: transform var(--transition);
  }
  .navtools .prefs__pane.is-expanded .prefs__pane-caret {
    transform: rotate(180deg);
  }
  /* Options: a wrapping strip of small chips â€” the code only, since the row
     above already names what is set.

     The ACCORDION animates on the wrapper, not on the chips. `display` cannot
     be transitioned, and the chips have no fixed height to animate to (they
     wrap, so the height depends on how many codes the admin publishes), which
     rules out a hardcoded max-height. The 0fr -> 1fr grid row resolves to
     exactly the height the content needs, so any number of chips opens and
     closes smoothly; the inner element clips what overflows mid-transition.
     Browsers too old to animate grid-template-rows simply snap â€” the previous
     behaviour, never a broken one. */
  .navtools .prefs__opts {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.3s var(--ease);
  }
  .navtools .prefs__pane.is-expanded .prefs__opts {
    grid-template-rows: 1fr;
  }
  .navtools .prefs__opts-in {
    /* min-height:0 is required: a grid item's default `auto` minimum refuses
       to shrink below its content, which pins the row open at full height. */
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    /* Padding is NOT clipped by the 0fr row â€” height goes to 0 but padding is
       added on top of it, which left an 8.8px sliver under the closed row. So
       it is 0 when closed and transitions in step with the row instead. */
    padding-bottom: 0;
    opacity: 0;
    transition: opacity 0.18s var(--ease), padding-bottom 0.3s var(--ease);
  }
  .navtools .prefs__pane.is-expanded .prefs__opts-in {
    padding-bottom: 0.55rem;
    opacity: 1;
    /* Fades in just after the row starts opening, so the chips do not appear
       to slide out from behind the row above. */
    transition-delay: 0.07s, 0s;
  }
  .navtools .prefs__opt {
    display: inline-flex;
    width: auto;
    gap: 0.35rem;
    padding: 0.4rem 0.7rem;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--surface);
  }
  .navtools .prefs__opt b {
    font-size: 0.78rem;
  }
  /* The long name ("Bangladeshi Taka") would make every chip a full-width row;
     the code is the identifier people scan for here. */
  .navtools .prefs__opt span {
    display: none;
  }
  /* The base rules mix toward #fff, which is wrong once the page is dark â€”
     mix toward the surface instead so the tint follows the theme. */
  .navtools .prefs__opt.is-active {
    border-color: var(--color-primary);
    background: color-mix(in srgb, var(--color-primary) 12%, var(--surface));
  }
  /* Same courtesy as the burger and the search sheets: the accordion still
     opens and closes, it just does it instantly. */
  @media (prefers-reduced-motion: reduce) {
    .navtools .prefs__opts,
    .navtools .prefs__opts-in,
    .navtools .prefs__pane-caret {
      transition: none;
    }
  }
  .navtools .prefs__opt.is-active b {
    color: var(--color-primary);
  }
  .navtools .prefs__opt:hover {
    background: color-mix(in srgb, var(--color-primary) 6%, var(--surface));
  }
}
/* ---------- Compact header controls (<= 768px) ----------------------------
   The top bar is dropped at this width. The theme switch and the
   currency/language selector now live in the menu panel (above), so what is
   left in the header's right cluster is the CTA and the burger â€” both still
   rendered as icon-only round buttons.

   The .theme-toggle / .prefs rules below are KEPT: main.js falls back to this
   cluster if a page ever renders without the menu slots, and the sizing has to
   be right if it does.

   Scoped by the media query alone rather than a class main.js adds, so the CTA
   is already the right shape on the first paint instead of reflowing once the
   script runs. */
/* The top bar goes with the menu: below 992px its contents (Appearance,
   Currency, Language) are in the menu card instead, so leaving the bar up
   would show them twice — and the header is the only bar the visitor gets.

   Its own block at THE MENU BREAKPOINT rather than inside the 768px block it
   used to share: everything else in there is about compacting the header for a
   PHONE — shrinking the login button, dropping its label — which is a
   different question from where the menu lives, and should not start happening
   to 900px tablets. */
@media (max-width: 991.98px) {
  .topbar {
    display: none;
  }
}

@media (max-width: 768px) {
  /* The logo is the one cell allowed to shrink. It is admin-supplied, so its
     natural width is unknown, and a flex item defaults to min-width:auto â€”
     a wide file would refuse to give and push the controls off the right
     edge instead. */
  .header__inner {
    gap: 0.75rem;
  }
  .logo {
    min-width: 0;
  }
  .logo__img {
    max-width: 100%;
  }
  .header__right {
    flex: 0 0 auto;
    gap: 0.5rem;
  }

  /* One footprint for every control in the cluster. The burger is sized with
     the others but keeps its own gap â€” it is a COLUMN of three bars, and
     folding it into the gap:0 rule below collapses them into a single dash. */
  .header__right .btn-login,
  .header__right .prefs__toggle,
  .header__right .theme-toggle,
  .header__right .nav-toggle {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    padding: 0;
    justify-content: center;
  }
  .header__right .btn-login,
  .header__right .prefs__toggle,
  .header__right .theme-toggle {
    gap: 0;
    border-radius: 50%;
  }
  .header__right .nav-toggle {
    align-items: center;
    gap: 5px;
  }

  /* Labels are HIDDEN, not removed: each button keeps its text as its
     accessible name ("Manage Booking", "JPY Â· EN") with nothing to show for
     it in the layout. Same technique as .visually-hidden. */
  .header__right .btn-login__text,
  .header__right .prefs__label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  .header__right .prefs__caret {
    display: none;
  }

  .header__right .btn-login svg {
    width: 17px;
    height: 17px;
  }
  .header__right .prefs__toggle {
    color: var(--color-primary);
    background: rgba(var(--color-primary-rgb), 0.14);
    border: 1px solid rgba(var(--color-primary-rgb), 0.3);
    opacity: 1;
  }
  /* Icon-only button, and here the "icon" IS the state badge: the globe says
     nothing about what is selected once the "JPY Â· EN" text is hidden. The
     globe steps aside for it, and the badge is sized to fit four glyphs in a
     40px circle rather than to match the 18px icons either side. */
  .header__right .prefs__toggle .ico {
    display: none;
  }
  .header__right .prefs__mark {
    display: inline-flex;
    font-size: 11.5px;
  }

  /* Theme switch: the 46x22 sliding pill becomes a plain icon button â€”
     beside three 40px circles the track reads as a stray control. The thumb
     stops being a positioned white disc and is just the icon. */
  .header__right .theme-toggle__thumb,
  .header__right .theme-toggle.is-on .theme-toggle__thumb {
    position: static;
    left: auto;
    transform: none;
    width: auto;
    height: auto;
    background: none;
  }
  .header__right .theme-toggle__thumb svg {
    width: 18px;
    height: 18px;
  }
  /* On the dark translucent track the mid-purple --color-primary is too close
     to the surface behind it; the lighter step keeps the icon readable. */
  [data-theme="dark"] .header__right .theme-toggle__thumb,
  [data-theme="dark"] .header__right .prefs__toggle {
    color: var(--color-primary-2);
  }

  /* The selector's popover hangs off the HEADER, not off its own 40px button:
     anchored to the button's right edge, a 240px panel runs past the left edge
     of the screen on a 320px phone. .prefs going static hands the containing
     block to .header (sticky, so it is positioned). */
  .header__right .prefs {
    position: static;
  }
  .header__right .prefs__pop {
    top: calc(100% + 8px);
    right: 1.25rem;
    left: auto;
    width: min(280px, calc(100vw - 2.5rem));
  }
}

/* Phones: same cluster, one size down. Four controls plus the logo is the
   whole header width here, so the buttons give before the logo does. */
@media (max-width: 600px) {
  .header__inner {
    gap: 0.5rem;
  }
  .header__right {
    gap: 0.375rem;
  }
  .header__right .btn-login,
  .header__right .prefs__toggle,
  .header__right .theme-toggle,
  .header__right .nav-toggle {
    width: 36px;
    height: 36px;
  }
  .header__right .nav-toggle span {
    width: 21px;
  }
  .header__right .btn-login svg {
    width: 16px;
    height: 16px;
  }
  .header__right .theme-toggle__thumb svg {
    width: 17px;
    height: 17px;
  }
  .header__right .prefs__mark {
    font-size: 11px;
  }
}

/* Narrow phones: one more step down, and the logo starts giving too */
@media (max-width: 420px) {
  .header__right {
    gap: 0.3rem;
  }
  .header__right .btn-login,
  .header__right .prefs__toggle,
  .header__right .theme-toggle,
  .header__right .nav-toggle {
    width: 33px;
    height: 33px;
  }
  .header__right .nav-toggle span {
    width: 20px;
  }
  .header__right .btn-login svg {
    width: 15px;
    height: 15px;
  }
  .header__right .theme-toggle__thumb svg {
    width: 16px;
    height: 16px;
  }
  /* 33px circle: the badge is the tightest thing in the cluster, so it steps
     down further than the icons beside it. */
  .header__right .prefs__mark {
    font-size: 10px;
    gap: 0;
  }
  .logo__img {
    height: 38px;
  }
}

@media (max-width: 680px) {
  /* The top bar's own small-screen rules used to live here; it is now hidden
     outright from 768px down, so nothing below that width can reach them. */
  .deals__grid {
    grid-template-columns: 1fr 1fr;
  }
  .footer__grid {
    grid-template-columns: 1fr 1fr;
  }
}
@media (max-width: 600px) {
  .section {
    padding-block: 2.5rem;
  }
  /* offers carousel: 1 per view on mobile, arrows hidden (swipe + dots) */
  .offers__track .offer {
    flex: 0 0 100%;
  }
  .offers__carousel .carousel-arrow {
    display: none;
  }

  /* Top Flight Deals & Latest Updates become swipe carousels on mobile */
  .deals__grid,
  .news__grid {
    display: flex;
    gap: 1rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin: 0;
    /* small left gap so the first card isn't flush to the edge */
    padding: 16px 3px 14px;
    scroll-padding-left: 3px;
  }
  .deals__grid::-webkit-scrollbar,
  .news__grid::-webkit-scrollbar {
    display: none;
  }
  .deals__grid .deal {
    flex: 0 0 80%;
    scroll-snap-align: start;
  }
  .news__grid .post {
    flex: 0 0 78%;
    scroll-snap-align: start;
  }
  /* deals & news headers: centered title + button */
  .deals__head {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 1rem;
  }
  .deals__heading {
    flex-direction: column;
    align-items: center;
    text-align: center;
  }
  .deals__heading .block-title {
    font-size: 1.6rem;
  }
  .deals__viewall {
    align-self: center;
    justify-content: center;
  }
  /* move the "View all" buttons below the cards on mobile */
  .deals__head .deals__viewall,
  .news .deals__head .link-more {
    display: none;
  }
  .deals__viewall--bottom {
    display: flex;
    width: fit-content;
    margin: 1.25rem auto 0;
  }
  .link-more--bottom {
    display: inline-block;
    width: 100%;
    margin-top: 1.1rem;
    text-align: center;
  }
  .deals__features {
    grid-template-columns: 1fr 1fr;
    gap: 1.6rem 1rem;
    padding: 1.5rem 1.2rem;
  }
  .feat,
  .feat + .feat {
    border-left: none;
    padding-left: 0;
  }
  /* icon on top, centered text */
  .feat {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 0.55rem;
  }
  /* The CTA's compact-pill rules used to live here. From 768px down it is an
     icon-only circle with its whole label visually hidden, so sizing the pill
     and hiding just the second word no longer apply. */
  /* Phones: the hero copy reads as a LEFT-aligned block on the container
     edge (centred text over a full-bleed photo had nothing to line up with),
     and every band above the search widget is tightened so the widget starts
     as near the top as the design allows. On a phone the search form is the
     reason people are on this page, and it used to open below the fold â€”
     even the Search button fell off a 844px-tall screen. */
  /* PHONES PUT THE COPY BACK ON THE PHOTO. .hero__top below is lifted -90px
     with z-index:1 so the title overlaps the full-bleed plane image.

     THE PICTURE STAYS VISIBLE. A 0.95 wash was tried here and did make the
     text readable, but by covering the photograph — which is the point of the
     hero. So the job is split: a LIGHT gradient takes the edge off the busiest
     part of the image, and the legibility itself comes from a halo drawn
     around the glyphs (.hero__copy > * below). The halo works against any
     image because it is painted with the text, not behind it, so it does not
     depend on what the photo happens to be doing at that pixel.

     Linear and vertical: the copy is full-bleed here, so there is no
     horizontal room to feather into, and a linear gradient lands exactly 0 at
     both edges of its own box. */
  .hero__copy::before {
    display: block;
    inset: -2.4rem -50vw -2.6rem -50vw;
    background: linear-gradient(
      to bottom,
      rgba(var(--hero-scrim-rgb), 0) 0%,
      rgba(var(--hero-scrim-rgb), 0.24) 22%,
      rgba(var(--hero-scrim-rgb), 0.34) 50%,
      rgba(var(--hero-scrim-rgb), 0.24) 78%,
      rgba(var(--hero-scrim-rgb), 0) 100%
    );
  }
  .hero__top {
    position: relative;
    margin-top: -90px;
    /* With the kicker gone the title sat straight on the search tabs. This is
       the whole gap between them now, so it is a real margin rather than
       leaning on .flightsearch's own 0.5rem. */
    margin-bottom: 40px;
    padding-top: 0;
    z-index: 1;
    text-align: left;
  }
  /* The dashed plane divider is pure decoration and cost ~26px directly above
     the search widget â€” dropped on phones rather than shrunk. */
  .hero__kicker {
    display: none;
  }
  /* Decoration, and the single largest block of space above the fold. Capped
     rather than left to its own aspect ratio, which spent ~150px of a phone
     screen on sky. `cover` + object-position crops that sky and keeps the
     aircraft itself in frame; the existing bottom mask still does the fade. */
  .hero__plane {
    height: 140px;
    object-fit: cover;
    object-position: 50% 40%;
  }
  .flightsearch {
    margin-top: 0.5rem;
  }
  .hero__eyebrow {
    font-size: 1rem;
    margin-bottom: 0.5rem;
  }
  .hero__title {
    font-size: 2.6rem;
  }
  .hero__sub {
    display: none;
  }
  .section__title {
    font-size: 1.7rem;
  }
  /* CTA Ã¢â‚¬â€ darken more so white text stays readable over the image */
  .cta {
    min-height: auto;
    padding: 1.8rem 1.4rem;
    background-image:
      linear-gradient(
        90deg,
        rgba(var(--grad-btn-2-rgb), 0.96) 0%,
        rgba(var(--grad-btn-2-rgb), 0.78) 60%,
        rgba(var(--grad-btn-2-rgb), 0.45) 100%
      ),
      url("../assets/cta-bg-image.png");
  }
  .cta__title {
    font-size: 1.55rem;
  }
}
@media (max-width: 480px) {
  .section__title {
    font-size: 1.55rem;
  }
  .hero__title {
    font-size: 1.8rem;
  }
  .why__grid,
  .news__grid,
  .deals__grid {
    grid-template-columns: 1fr;
  }
  .footer__grid {
    grid-template-columns: 1fr;
    gap: 1.25rem;
    padding-block: 2rem;
  }
  .footer__col h4 {
    margin-bottom: 0.7rem;
  }
  .footer__col ul li {
    margin-bottom: 0.45rem;
  }
  .footer__desc {
    margin: 0.7rem 0;
  }
  .cta__actions {
    gap: 0.7rem;
  }
  .footer__bar-inner {
    flex-direction: column;
    gap: 0.8rem;
    text-align: center;
  }
}

/* ---------- CTA banner ---------- */
.cta-section {
  padding-block: 1rem 3.5rem;
}
.cta {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-lg);
  min-height: 220px;
  display: flex;
  align-items: center;
  padding: 2.5rem;
  color: #fff;
  background-color: var(--grad-deep);
  background-image:
    linear-gradient(
      90deg,
      rgba(var(--grad-btn-2-rgb), 0.96) 0%,
      rgba(var(--grad-btn-2-rgb), 0.7) 42%,
      rgba(var(--grad-btn-2-rgb), 0.15) 70%,
      rgba(var(--grad-btn-2-rgb), 0) 100%
    ),
    url("../assets/cta-bg-image.png");
  background-repeat: no-repeat, no-repeat;
  background-position:
    center,
    right center;
  background-size: cover, cover;
  box-shadow: var(--shadow-md);
}
.cta__eyebrow {
  display: inline-block;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  opacity: 0.92;
  margin-bottom: 0.6rem;
}
.cta__title {
  font-size: 2.1rem;
  font-weight: 800;
  line-height: 1.15;
}
.cta__title span {
  color: #fbbf24;
}
.cta__actions {
  display: flex;
  align-items: center;
  gap: 1.2rem;
  margin-top: 1.4rem;
  flex-wrap: wrap;
}
.cta__btn {
  display: inline-flex;
  align-items: center;
  background: var(--surface);
  color: var(--color-primary-dark);
  font-weight: 700;
  font-size: 0.95rem;
  padding: 0.7rem 1.8rem;
  border-radius: 999px;
  transition: var(--transition);
}
.cta__btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);
}
.cta__note {
  font-size: 0.9rem;
  opacity: 0.92;
}

/* ---------- Testimonials (carousel) ---------- */
.reviews {
  background: var(--color-bg-alt);
}
.reviews__carousel {
  position: relative;
}
.reviews__viewport {
  overflow: visible;
  clip-path: inset(-45px -16px);
  /* Same clones-inflate-the-screenshot fix as .offers__viewport above. */
  overflow-x: clip;
  overflow-clip-margin: 16px;
}
.reviews__track {
  display: flex;
  gap: 1.4rem;
  transition: transform 0.5s var(--ease, cubic-bezier(0.2, 0.9, 0.25, 1));
  will-change: transform;
}
.reviews__track {
  align-items: stretch;
}
.reviews__track .review {
  flex: 0 0 calc((100% - 2.8rem) / 3);
}
.review {
  /* Column + margin-top:auto on .review__user pins the reviewer to the BOTTOM
     of every card, so names line up across a row whatever the comment length
     (and they stay lined up when a card is expanded by Show more).
     Plain flexbox â€” supported everywhere including older iOS Safari. */
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 18px;
  padding: 1.6rem;
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
.review:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.review__stars {
  color: #f5b50a;
  font-size: 1.05rem;
  letter-spacing: 2px;
  margin-bottom: 0.8rem;
}
.review__text {
  color: #374151;
  font-size: 0.96rem;
  line-height: 1.7;
  margin-bottom: 1.3rem;
}
.review__user {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  /* Pushes the reviewer to the bottom of the card â€” see .review. */
  margin-top: auto;
}
.review__avatar {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 0.9rem;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
}
.review__user b {
  display: block;
  font-size: 0.95rem;
  color: var(--heading);
}
.review__user small {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
.reviews__more {
  text-align: center;
  margin-top: 2rem;
}
.btn-outline {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 700;
  font-size: 0.95rem;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  border: 1.5px solid transparent;
  padding: 0.8rem 1.8rem;
  border-radius: 999px;
  box-shadow: 0 12px 24px -10px var(--color-primary);
  transition: var(--transition);
}
.btn-outline:hover {
  color: var(--color-primary);
  background: var(--surface);
  border-color: #d9cdf3;
  border-color: color-mix(in srgb, var(--grad-btn-1) 27%, #fff);
  transform: translateY(-1px);
  box-shadow: var(--shadow-sm);
}
@media (max-width: 880px) {
  .reviews__track .review {
    flex: 0 0 calc((100% - 1.4rem) / 2);
  }
}
@media (max-width: 600px) {
  .reviews__track .review {
    flex: 0 0 100%;
  }
  .reviews__carousel .carousel-arrow {
    display: none;
  }
}

/* ---------- FAQ ---------- */
.faq__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem 1.5rem;
}
.faq__col {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.faq__item {
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 0 1.2rem;
  transition: var(--transition);
}
.faq__item[open] {
  background: var(--surface);
  border-color: #ddd2f7;
  border-color: color-mix(in srgb, var(--grad-btn-1) 24%, #fff);
  box-shadow: var(--shadow-sm);
}
.faq__item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  list-style: none;
  cursor: pointer;
  padding: 1.05rem 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--heading);
}
.faq__item summary::-webkit-details-marker {
  display: none;
}
.faq__chev {
  flex-shrink: 0;
  color: var(--color-primary);
  display: grid;
  place-items: center;
  transition: transform var(--transition);
}
.faq__item[open] .faq__chev {
  transform: rotate(180deg);
}
/* While collapsing, [open] is still set (the row has to stay visible until
   the height animation ends), so the chevron is pointed back down by class
   instead â€” otherwise it would flip only after the row had finished
   closing. */
.faq__item.is-closing .faq__chev {
  transform: rotate(0deg);
}
/* The answer fades with the height instead of being revealed as a solid
   block, which is what makes the open read as smooth rather than as a jump. */
.faq__item p {
  opacity: 0;
  transition: opacity 0.22s ease 0.04s;
}
.faq__item[open] p {
  opacity: 1;
}
.faq__item.is-closing p {
  opacity: 0;
  transition-delay: 0s;
}
.faq__item p {
  margin: 0;
  padding: 0 0 1.1rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--color-text-muted);
}

@media (max-width: 768px) {
  .faq__grid {
    grid-template-columns: 1fr;
  }
  .cta {
    padding: 2rem 1.5rem;
  }
  .cta__title {
    font-size: 1.7rem;
  }
}

/* ============================================
   Inner pages (Contact / About / Company)
============================================= */
.page-hero {
  position: relative;
  background:
    linear-gradient(
      180deg,
      rgba(244, 248, 255, 0.7),
      rgba(var(--color-bg-alt-rgb), 0.92)
    ),
    var(--page-hero-image, url("../assets/page-bg-img.png")) center/cover no-repeat;
  color: var(--heading);
  padding: 3.5rem 0 4rem;
  text-align: center;
  overflow: hidden;
  border-bottom: 1px solid #eceaf6;
  border-bottom: 1px solid color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
}
.page-hero .container {
  position: relative;
  z-index: 1;
}
.crumb {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin-bottom: 0.8rem;
}
.crumb a {
  color: var(--color-primary);
  font-weight: 600;
}
.crumb span {
  margin: 0 0.3rem;
  opacity: 0.7;
}
.page-hero__title {
  font-size: 2.6rem;
  font-weight: 800;
  line-height: 1.1;
  color: var(--heading);
}
.page-hero__title span {
  color: var(--color-primary);
}
.page-hero__sub {
  max-width: 620px;
  margin: 0.8rem auto 0;
  color: #475066;
  font-size: 1rem;
}

.contact__grid {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: 2rem;
  align-items: start;
}
.contact__info .pill {
  margin-bottom: 0.8rem;
}
.contact__title {
  font-size: 1.9rem;
  font-weight: 700;
  color: var(--heading);
  line-height: 1.15;
}
.contact__lead {
  margin: 0.7rem 0 1.6rem;
  color: var(--color-text-muted);
}
.contact__list {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.contact__list li {
  display: flex;
  align-items: center;
  gap: 0.9rem;
}
.contact__ic {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  color: var(--color-primary);
  background: #efe9fd;
  background: color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
}
.contact__list b {
  display: block;
  font-size: 0.8rem;
  color: var(--color-text-muted);
  font-weight: 600;
}
.contact__list a,
.contact__list div > span {
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--heading);
}
.contact__list a:hover {
  color: var(--color-primary);
}
.contact__socials {
  display: flex;
  gap: 0.6rem;
  margin-top: 1.6rem;
}
.contact__socials a {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  color: var(--color-primary);
  background: #f1ecfb;
  background: color-mix(in srgb, var(--grad-btn-1) 10%, #fff);
  transition: var(--transition);
}
.contact__socials a:hover {
  background: var(--color-primary);
  color: #fff;
}

.contact__form {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 20px;
  box-shadow: var(--shadow-md);
  padding: 1.8rem;
}
.contact__form h3 {
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--heading);
  margin-bottom: 1.2rem;
}
.contact__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
.contact__field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-bottom: 1rem;
}
.contact__field span {
  font-size: 0.8rem;
  font-weight: 600;
  color: #374151;
}

/* ---------- the contact form in dark mode ----------

   Two colours on this form were fixed slate and fixed pink, so on the dark
   card they measured 1.60:1 and 3.06:1 — the labels were barely there, and the
   validation errors, which are the one thing on a form a visitor MUST be able
   to read, were not much better.

   Light mode is untouched: these rules only exist inside [data-theme="dark"],
   and the values above are still exactly what a light page renders. */
[data-theme="dark"] .contact__field span {
  /* The theme's own body colour — it already flips with the theme, and a
     form label is ordinary text, not a brand colour. 13.01:1 here. */
  color: var(--color-text);
}
[data-theme="dark"] .contact__error,
[data-theme="dark"] .contact__errors,
[data-theme="dark"] .contact__req {
  /* The same pink lifted for a dark ground — 7.86:1, and the exact value the
     admin panel already uses for danger text on its own dark surface, so the
     two sides of the product agree on what "error" looks like. */
  color: #ff92b6;
}
.contact__field input,
.contact__field textarea {
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 0.7rem 0.9rem;
  font-size: 0.92rem;
  font-family: inherit;
  resize: vertical;
  transition: var(--transition);
}
.contact__field input:focus,
.contact__field textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(109, 40, 217, 0.12);
}
.contact__submit {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  color: #fff;
  font-weight: 700;
  font-size: 0.95rem;
  padding: 0.85rem 1.6rem;
  border-radius: 12px;
  box-shadow: 0 10px 22px -10px var(--color-primary);
  transition: var(--transition);
}
.contact__submit:hover {
  transform: translateY(-1px);
}
.contact__note {
  margin-top: 0.9rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: #16a34a;
}

.contact__map {
  margin-top: 2.5rem;
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-sm);
  line-height: 0;
}
.contact__map iframe {
  width: 100%;
  height: 360px;
  border: 0;
}

@media (max-width: 860px) {
  .contact__grid {
    grid-template-columns: 1fr;
  }
}
@media (max-width: 560px) {
  .page-hero__title {
    font-size: 2rem;
  }
  .contact__row {
    grid-template-columns: 1fr;
  }
  .contact__form {
    padding: 1.3rem;
  }
  .contact__map iframe {
    height: 280px;
  }
}

/* ============================================
   About page
============================================= */
/* Our Story */
.story__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3rem;
  align-items: center;
}
.story__media {
  position: relative;
  border-radius: 18px;
  overflow: hidden;
  box-shadow: var(--shadow-md);
}
.story__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.story__badge {
  position: absolute;
  left: 0;
  bottom: 28px;
  display: flex;
  flex-direction: column;
  line-height: 1.05;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  padding: 0.65rem 1.4rem 0.7rem 1.1rem;
  border-radius: 0 12px 12px 0;
  box-shadow: 0 10px 22px -10px var(--color-primary);
}
.story__badge small {
  font-size: 0.78rem;
  font-weight: 500;
  opacity: 0.92;
}
.story__badge b {
  font-size: 1.7rem;
  font-weight: 800;
}
.story__eyebrow,
.diff__eyebrow {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--color-primary);
  margin-bottom: 0.7rem;
}
.story__title {
  font-size: 2rem;
  font-weight: 800;
  line-height: 1.15;
  color: var(--heading);
}
.story__title span {
  color: var(--color-primary);
}
.story__copy p {
  margin-top: 1rem;
  color: var(--color-text-muted);
  line-height: 1.7;
}
.story__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.6rem;
  padding: 0.8rem 1.5rem;
  border-radius: 999px;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  box-shadow: 0 12px 24px -10px var(--color-primary);
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease;
}
.story__btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 28px -10px var(--color-primary);
}

/* Mission / Vision */
.mv__panel {
  display: grid;
  /* auto-fit, not a fixed 2: the panel allows up to 6 cards here (Pages >
     About Us Page > Mission & Vision), so the row has to reflow rather than
     strand a third card. */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
  background: var(--surface-3);
  border: 1px solid #e7dcf6;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 18%, #fff);
  border-radius: var(--radius-lg, 20px);
  padding: 1.6rem;
}
.mv__card {
  display: flex;
  gap: 1.1rem;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 1.6rem;
}
.mv__icon {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
}
.mv__text h3 {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--heading);
  margin-bottom: 0.4rem;
}
.mv__text p {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  line-height: 1.6;
}

/* The Nakamura Difference */
.diff__head {
  text-align: center;
  margin-bottom: 2.25rem;
}
.diff__title {
  font-size: 1.9rem;
  font-weight: 800;
  color: var(--heading);
}
.diff__title span {
  color: var(--color-primary);
}
.diff__sub {
  margin-top: 0.5rem;
  color: var(--color-text-muted);
}
.diff__grid {
  display: grid;
  /* Same reason as .mv__panel â€” the panel allows up to 8 cards here. */
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1.4rem;
}
.diff__card {
  text-align: center;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 2rem 1.4rem;
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease,
    border-color 0.18s ease;
}
.diff__card:hover {
  transform: translateY(-4px);
  border-color: #d9cdf3;
  border-color: color-mix(in srgb, var(--grad-btn-1) 27%, #fff);
  box-shadow: var(--shadow-md);
}
.diff__icon {
  width: 54px;
  height: 54px;
  margin: 0 auto 1.1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--color-primary);
  background: var(--surface-3);
}
.diff__card h3 {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--heading);
  margin-bottom: 0.5rem;
}
.diff__card p {
  font-size: 0.88rem;
  color: var(--color-text-muted);
  line-height: 1.6;
}

/* ============================================
   Company Information page
============================================= */
.company__reg {
  display: grid;
  /* auto-fit, not a fixed 2: the panel allows up to 8 licence cards
     (Pages > Company Information > Registration & Licences). */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.2rem;
  margin-bottom: 2.2rem;
}
.reg-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 1.3rem 1.4rem;
}
.reg-card__ic {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  color: var(--color-primary);
  background: #efe9fd;
  background: color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
}
.reg-card small {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--color-text-muted);
}
.reg-card b {
  font-size: 1.1rem;
  color: var(--heading);
  letter-spacing: 0.02em;
}
.company__offices {
  display: grid;
  /* Same reason as .company__reg â€” up to 8 offices. */
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1.4rem;
}
.office {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: 18px;
  box-shadow: var(--shadow-sm);
  padding: 1.6rem;
}
.office__head {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-bottom: 1rem;
}
.office__head .reg-card__ic {
  width: 42px;
  height: 42px;
}
.office__head h3 {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--heading);
}
.office address {
  font-style: normal;
  color: #374151;
  line-height: 1.8;
  font-size: 0.96rem;
}
.office address strong {
  color: var(--heading);
}

@media (max-width: 860px) {
  .story__grid,
  .mv__panel,
  .company__reg,
  .company__offices {
    grid-template-columns: 1fr;
  }
  .story__grid {
    gap: 2rem;
  }
  .story__media {
    order: -1;
  }
  .diff__grid {
    grid-template-columns: 1fr 1fr;
  }
}
@media (max-width: 480px) {
  .diff__grid {
    grid-template-columns: 1fr 1fr;
  }
  .mv__card {
    flex-direction: column;
    gap: 0.8rem;
  }
}

/* ============================================
   Legal / policy pages (Refund, Terms, Privacy)
   â€” now admin-authored "dynamic pages", same markup
============================================= */

/* Optional banner above the card, blog-detail style. Fixed 21:9 crop so a
   portrait upload cannot push the whole page below the fold. */
.policy__cover {
  width: 100%;
  margin: 0 auto 1.6rem;
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-md);
  line-height: 0;
}
.policy__cover img {
  width: 100%;
  height: auto;
  aspect-ratio: 21 / 9;
  object-fit: cover;
  display: block;
}
@media (max-width: 640px) {
  /* A 21:9 strip is a sliver on a phone; give it more height. */
  .policy__cover img {
    aspect-ratio: 16 / 10;
  }
}

/* Section icons come from the panel as Bootstrap Icons classes rather than the
   inline 22px SVGs these pages originally shipped â€” match that optical size. */
.policy__ic .bi {
  font-size: 1.32rem;
  line-height: 1;
}

.policy__card {
  width: 100%;
  margin: 0 auto;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  padding: 0.6rem 2.2rem;
}
.policy__item {
  display: flex;
  gap: 1.1rem;
  padding: 1.7rem 0;
  border-bottom: 1px solid var(--border-soft);
}
.policy__item:last-child {
  border-bottom: none;
}
.policy__lead {
  color: var(--color-text-muted);
  font-size: 0.95rem;
  line-height: 1.75;
  padding: 1.7rem 0;
  border-bottom: 1px solid var(--border-soft);
}
/* The intro is admin-authored HTML now, so it arrives as one or more <p>
   rather than as bare text. Inherit the block's own type, and space the
   paragraphs from each other without adding a gap at either end. */
.policy__lead p {
  color: inherit;
  font-size: inherit;
  line-height: inherit;
}
.policy__lead p + p { margin-top: 0.7rem; }

.policy__closing {
  padding: 1.6rem 0 0.2rem;
}
.policy__closing p {
  color: var(--color-text-muted);
  font-size: 0.92rem;
  line-height: 1.7;
}
.policy__closing p + p {
  margin-top: 0.7rem;
}
.policy__ic {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--color-primary);
  background: var(--surface-3);
}
.policy__body h3 {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--color-primary);
  margin-bottom: 0.55rem;
}
.policy__body > p {
  color: var(--color-text-muted);
  font-size: 0.92rem;
  line-height: 1.7;
}
.policy__body > p + p {
  margin-top: 0.75rem;
}
.policy__body > p + .policy__list {
  margin-top: 0.75rem;
}
/* Links inside ADMIN-AUTHORED body copy â€” every region that prints panel HTML
   with {!! !!}. Kept as one rule so a new region cannot be added and quietly
   miss the brand colour, which is how the closing note's mailto link ended up
   rendering as plain body text. `.rich-body a` (blog post + offer detail)
   has its own rule further down; it adds an underline these do not. */
.policy__body a,
.policy__lead a,
.policy__closing a {
  color: var(--color-primary);
  font-weight: 600;
}
/* `.policy__list` is the class the original hand-written pages used. A body
   edited in the panel comes back from CKEditor as a BARE <ul> â€” the editor
   drops classes it does not know about â€” so both must look the same, or a
   page would silently restyle itself the first time someone edited it. */
.policy__list,
.policy__body ul,
.policy__body ol {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
}
.policy__body > p + ul,
.policy__body > p + ol {
  margin-top: 0.75rem;
}
.policy__body ol {
  list-style: decimal;
  padding-left: 1.15rem;
}
.policy__body ol li {
  color: var(--color-text-muted);
  font-size: 0.92rem;
  line-height: 1.6;
}
.policy__list li,
.policy__body ul li {
  position: relative;
  padding-left: 1.15rem;
  color: var(--color-text-muted);
  font-size: 0.92rem;
  line-height: 1.6;
}
.policy__list li::before,
.policy__body ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--color-primary);
}
/* CKEditor wraps a table in <figure class="table">; keep it scrollable rather
   than letting a wide table push the whole card sideways. */
.policy__body figure.table,
.policy__body > table {
  overflow-x: auto;
  margin-top: 0.9rem;
}
.policy__body table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}
.policy__body th,
.policy__body td {
  border: 1px solid var(--border-soft);
  padding: 0.5rem 0.7rem;
  text-align: left;
  color: var(--color-text-muted);
}
.policy__body th {
  font-weight: 700;
  color: var(--color-text);
}
.policy__body blockquote {
  border-left: 3px solid var(--color-primary);
  padding-left: 0.9rem;
  margin-top: 0.8rem;
  color: var(--color-text-muted);
  font-size: 0.92rem;
  line-height: 1.7;
}
.policy__body h2,
.policy__body h4,
.policy__body h5 {
  font-size: 0.98rem;
  font-weight: 700;
  color: var(--color-text);
  margin: 0.9rem 0 0.4rem;
}
/* "Need help?" footer row inside the card */
.policy__help {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  padding: 1.7rem 0 0.4rem;
  border-top: 1px solid var(--border-soft);
}
.policy__help-text {
  flex: 1;
  min-width: 0;
}
.policy__help-text b {
  display: block;
  color: var(--heading);
  font-size: 0.98rem;
}
.policy__help-text span {
  color: var(--color-text-muted);
  font-size: 0.88rem;
}
.policy__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
  padding: 0.7rem 1.4rem;
  border-radius: 999px;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  box-shadow: 0 12px 24px -10px var(--color-primary);
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease;
}
.policy__btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 28px -10px var(--color-primary);
}
/* "View more" toggle Ã¢â‚¬â€ mobile only */
.policy__toggle {
  display: none;
  align-items: center;
  gap: 0.5rem;
  margin: 1.4rem auto 0;
  padding: 0.7rem 1.6rem;
  border-radius: 999px;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  box-shadow: 0 12px 24px -10px var(--color-primary);
}
.policy__toggle svg {
  transition: transform 0.2s ease;
}
.policy__toggle.is-open svg {
  transform: rotate(180deg);
}

@media (max-width: 600px) {
  .policy__card {
    padding: 0.4rem 1.2rem;
  }
  .policy__item {
    padding: 1.3rem 0;
    gap: 0.8rem;
  }
  .policy__ic {
    width: 38px;
    height: 38px;
  }
  .policy__help {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.9rem;
  }
  /* collapse long policy content behind a "View more" button */
  .policy__card.is-collapsed {
    max-height: 78vh;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(180deg, #000 80%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 80%, transparent 100%);
  }
  .policy__toggle {
    display: flex;
  }
}

/* ============================================
   Offer details page
============================================= */
.offerd__crumb {
  margin-bottom: 1.4rem;
}
.offerd__card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
.offerd__banner {
  position: relative;
  overflow: hidden;
}
.offerd__banner img {
  /* natural height â€” a fixed clamp()+cover window cropped the top and
     bottom off panel-uploaded creatives (standard size: 1859x846) */
  width: 100%;
  height: auto;
  display: block;
}
.offerd__badge {
  position: absolute;
  top: 18px;
  left: 18px;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  color: #fff;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.42rem 0.95rem;
  border-radius: 999px;
  box-shadow: var(--shadow-sm);
}
.offerd__content {
  max-width: 1000px;
  margin: 0 auto;
  padding: 2.8rem 1rem;
}
.offerd__title {
  font-size: 2rem;
  font-weight: 800;
  color: var(--heading);
  line-height: 1.2;
}
.offerd__body {
  margin-top: 1.2rem;
}
.offerd__body > * {
  margin-top: 1rem;
}
.offerd__body > *:first-child {
  margin-top: 0;
}
.offerd__body p {
  color: var(--color-text-muted);
  line-height: 1.75;
}
.offerd__subhead {
  margin-top: 1.7rem;
  font-size: 1.12rem;
  font-weight: 700;
  color: var(--heading);
}
.offerd__list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.offerd__list li {
  position: relative;
  padding-left: 1.2rem;
  color: var(--color-text-muted);
  line-height: 1.6;
}
.offerd__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--color-primary);
}
.offerd__details {
  border-top: 1px solid var(--border-soft);
}
.offerd__detrow {
  display: flex;
  justify-content: space-between;
  gap: 1.2rem;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.92rem;
}
.offerd__detrow span {
  color: var(--color-text-muted);
  flex-shrink: 0;
}
.offerd__detrow b {
  color: var(--heading);
  font-weight: 600;
  text-align: right;
}
.offerd__body strong {
  color: var(--heading);
  font-weight: 700;
}
/* multi-column timing table */
.offerd__tablewrap {
  overflow-x: auto;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
}
.offerd__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  min-width: 460px;
}
.offerd__table th {
  text-align: left;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  padding: 0.75rem 1rem;
  white-space: nowrap;
}
.offerd__table td {
  padding: 0.7rem 1rem;
  color: var(--color-text-muted);
  border-top: 1px solid var(--border-soft);
}
.offerd__table tbody tr:nth-child(even) {
  background: var(--surface-2);
}
.offerd__table td:first-child {
  color: var(--heading);
  font-weight: 600;
}
.offerd__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.8rem;
  padding: 0.85rem 1.9rem;
  border-radius: 999px;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  box-shadow: 0 12px 24px -10px var(--color-primary);
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease;
}
.offerd__btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 28px -10px var(--color-primary);
}
@media (max-width: 600px) {
  .offerd__content {
    padding: 1.3rem 1.2rem 1.5rem;
  }
  .offerd__title {
    font-size: 1.6rem;
  }
}

/* ---------- Hero slider (2+ admin-managed slides) ----------
   Only engages when there is MORE THAN ONE slide; a single slide renders the
   original static markup and never reaches these rules. Slides are stacked in
   the same grid cell so the section keeps the height of its tallest slide and
   nothing jumps as they rotate â€” and because each slide reuses .hero__copy
   verbatim, every existing responsive rule still applies. */
.hero__slider {
  display: grid;
}
.hero__slider > .hero__slide {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.7s ease;
}
.hero__slider > .hero__slide.is-active {
  opacity: 1;
  visibility: visible;
}
/* Auto-rotation is decorative; respect a user who has asked for less motion. */
@media (prefers-reduced-motion: reduce) {
  .hero__slider > .hero__slide {
    transition: none;
  }
}

/* Fractional review stars: a muted 5-star track with a gold copy clipped to
   the rating percentage on top. Overlay rather than per-star glyphs because
   it renders ANY fraction (4.5, 3.8) with one rule and stays aligned since
   both layers are the identical string in the identical font. */
.review__stars {
  position: relative;
  display: inline-block;
  white-space: nowrap;
  letter-spacing: 0;
}
/* One positioning context PER STAR, so a 50% fill is exactly half of that
   glyph. Gaps come from margin, never letter-spacing â€” spacing inside the
   clipped box would shift where the half lands. */
.review__star {
  position: relative;
  display: inline-block;
  color: #d8d3e4;
  letter-spacing: 0;
}
.review__star + .review__star {
  margin-left: 2px;
}
.review__star-fill {
  position: absolute;
  top: 0;
  left: 0;
  overflow: hidden;
  width: 0;
  color: #f5b50a;
  white-space: nowrap;
}

/* ==========================================================================
   COMING SOON  (pages/coming-soon.blade.php â€” Hotels, eSIM)
   Single centred column on a soft gradient field. Uses the shared theme
   tokens throughout so it re-colours with the rest of the site (including
   the admin-published palette) and needs no dark-mode overrides beyond the
   two below.
   ========================================================================== */
.pagesoon {
  position: relative;
  overflow: hidden;
  /* Fills the viewport below the header so the short content sits centred
     rather than crowding the footer. min-height, not height â€” the block still
     grows if a longer product name wraps the heading. */
  display: grid;
  align-content: center;
  min-height: calc(100vh - 190px);
  padding: clamp(3rem, 8vw, 5.5rem) 0;
  background:
    radial-gradient(
      120% 90% at 50% -10%,
      var(--surface-3) 0%,
      var(--surface-2) 45%,
      var(--surface) 100%
    );
}
/* Decorative blobs. Kept behind the content with a z-index rather than
   negative stacking, so they can never sit under the page background. */
.pagesoon__glow {
  position: absolute;
  z-index: 0;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.5;
  pointer-events: none;
}
.pagesoon__glow--a {
  top: -140px;
  left: -110px;
  width: 420px;
  height: 420px;
  background: var(--color-primary-2);
}
.pagesoon__glow--b {
  right: -140px;
  bottom: -170px;
  width: 460px;
  height: 460px;
  background: var(--color-accent);
  opacity: 0.28;
}
.pagesoon__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  max-width: 820px;
}
.crumb--soon {
  margin-bottom: 1.8rem;
}
.crumb--soon a {
  color: var(--color-primary);
  font-weight: 600;
}

.pagesoon__icon {
  display: grid;
  place-items: center;
  width: 78px;
  height: 78px;
  border-radius: 24px;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  box-shadow: 0 18px 38px -14px var(--color-primary);
}
.pagesoon__icon svg {
  width: 38px;
  height: 38px;
}

.pagesoon__badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.5rem;
  padding: 0.42rem 1rem;
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-primary);
}
.pagesoon__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-primary);
  animation: pagesoon-pulse 1.8s ease-in-out infinite;
}
@keyframes pagesoon-pulse {
  0%,
  100% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.45);
    opacity: 0.45;
  }
}

.pagesoon__title {
  margin-top: 1.1rem;
  font-size: clamp(2rem, 5.2vw, 3.2rem);
  line-height: 1.16;
  font-weight: 800;
  color: var(--heading);
}
.pagesoon__title span {
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
.pagesoon__sub {
  margin-top: 1rem;
  max-width: 620px;
  font-size: 1.03rem;
  line-height: 1.75;
  color: var(--color-text-muted);
}

.pagesoon__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.85rem;
  margin-top: 2rem;
}
.pagesoon__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.85rem 1.9rem;
  border-radius: 999px;
  border: 1.5px solid transparent;
  font-weight: 700;
  font-size: 0.95rem;
  transition: var(--transition);
}
.pagesoon__btn--primary {
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  box-shadow: 0 14px 26px -12px var(--color-primary);
}
.pagesoon__btn--primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 30px -12px var(--color-primary);
}
.pagesoon__btn--ghost {
  color: var(--color-primary);
  background: var(--surface);
  border-color: var(--border-soft);
  box-shadow: var(--shadow-sm);
}
.pagesoon__btn--ghost:hover {
  border-color: var(--color-primary-2);
  transform: translateY(-2px);
}

/* Dark mode: the tokens already flip, so only the blobs (too hot against a
   dark field) and the gradient backdrop need toning down. */
[data-theme="dark"] .pagesoon {
  background: radial-gradient(
    120% 90% at 50% -10%,
    rgba(var(--color-primary-rgb), 0.28) 0%,
    var(--surface-2) 55%,
    var(--surface) 100%
  );
}
[data-theme="dark"] .pagesoon__glow {
  opacity: 0.22;
}

/* Contact form cooldown â€” the wait between sends (config/contact.php). */
.contact__submit[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
  filter: grayscale(0.35);
}
.contact__cooldown {
  margin-top: 0.75rem;
  font-size: 0.88rem;
  line-height: 1.6;
  color: var(--color-text-muted);
}
.contact__cooldown b {
  color: var(--color-primary);
  font-variant-numeric: tabular-nums;
}

/* Contact form inline validation */
.contact__error {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.8rem;
  line-height: 1.4;
  color: #c22f6b;
  min-height: 0;
}
.contact__field input.is-invalid,
.contact__field textarea.is-invalid {
  border-color: #c22f6b;
  box-shadow: 0 0 0 3px rgba(194, 47, 107, 0.12);
}
.contact__errors {
  margin-top: 0.75rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius-sm);
  background: rgba(194, 47, 107, 0.08);
  color: #c22f6b;
  font-size: 0.86rem;
  line-height: 1.55;
}

/* Required-field marker on the contact form */
.contact__req {
  color: #c22f6b;
  font-weight: 700;
  margin-left: 1px;
}

/* Contact form unavailable notice (backend not reachable/configured) */
.contact__notice {
  margin: 0 0 1rem;
  padding: 0.8rem 1rem;
  border-radius: var(--radius-sm);
  border: 1px solid rgba(194, 47, 107, 0.25);
  background: rgba(194, 47, 107, 0.07);
  color: var(--color-text);
  font-size: 0.88rem;
  line-height: 1.6;
}

/* Debug-only banner: the page is rendering bundled fallback content because
   the admin panel could not be reached. Never shown when APP_DEBUG is off. */
.ntt-datawarn {
  position: sticky;
  top: 0;
  z-index: 999;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 0.85rem;
  padding: 0.6rem 1rem;
  background: #7a1230;
  color: #fff;
  font-size: 0.82rem;
  line-height: 1.5;
  text-align: left;
}
.ntt-datawarn strong { font-weight: 700; }
.ntt-datawarn small { opacity: 0.75; }

/* Bootstrap-Icons glyph inside an eyebrow pill.
   Sized to match the 12px inline SVG it replaced so swapping the icon source
   shifts nothing. line-height:1 stops the font's own metrics adding height. */
.pill .bi {
  font-size: 12px;
  line-height: 1;
  display: inline-block;
}

/* Section-heading icon (Bootstrap Icons glyph next to a .block-title).
   Sized in em so it tracks the heading at every breakpoint instead of needing
   its own media queries, and vertically nudged to sit on the cap height. */
.block-title__ico {
  font-size: 0.85em;
  line-height: 1;
  margin-right: 0.4rem;
  color: var(--color-primary);
  vertical-align: baseline;
}
/* The deals badge is a fixed 40px purple tile â€” the glyph fills it like the
   24px SVG it replaced, and inherits the tile's white colour. */
.deals__icon .bi {
  font-size: 22px;
  line-height: 1;
}

/* Centred section headings are close to the width limit on a phone: adding the
   icon inline tipped "Why Choose Nakamura Tours?" and "Frequently Asked
   Questions" onto a second line, pushing everything below them down ~38px.
   Tighten the glyph on narrow screens so the heading keeps its original line
   count instead of reflowing the page. */
@media (max-width: 600px) {
  /* Even at 0.7em the glyph still tipped these headings onto a second line
     (they sit right at the width limit on a 390px screen), which pushed the
     whole page down ~38px. The heading text matters more than a decorative
     icon, so on phones the centred variant drops the glyph and the layout
     stays byte-identical to before. Inline headings (deals, blog) keep theirs
     at every width because they do not wrap. */
  .block-title--center .block-title__ico {
    display: none;
  }
}

/* Glyph inside the Why-Choose card badge â€” matches the 20px SVG it replaced. */
.feat__icon .bi {
  font-size: 20px;
  line-height: 1;
}

/* Blog card titles: exactly two lines, then an ellipsis.
   -webkit-line-clamp is the cross-browser way to do this â€” despite the vendor
   prefix it is implemented by Chrome, Edge, Safari, Firefox and every mobile
   browser, so it is safe on all of them; `line-clamp` is the unprefixed
   standard for newer engines and is listed alongside it. The clamp needs
   display:-webkit-box + box-orient:vertical to take effect.

   min-height reserves both lines even when a title only needs one, so the
   dates underneath line up across the row instead of sitting at different
   heights (which is what looked odd). 2 lines x 1.35 line-height = 2.7em.

   The max-height fallback matters for anything that ignores the clamp
   entirely (very old engines, some in-app webviews): the title is still cut to
   two lines' worth of height, it just loses the "..." rather than overflowing
   the card. */
.post__body h3 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  min-height: 2.7em;
  max-height: 2.7em;
}

/* ==========================================================================
   Minimum 16px on every text field at 1024px and below.

   iOS Safari zooms the whole page when a focused field computes to less than
   16px, and leaves the layout shifted afterwards. The only fix is the field
   size itself.

   Scoped by WIDTH, not by an iOS feature test. An earlier version used
   @supports (-webkit-touch-callout: none) to limit the change to iOS, but that
   is fragile: it silently does nothing in engines that do not expose that
   property, so fields still measured 13.5px on a real phone. A width query is
   unambiguous and applies on every browser and OS, which is the point.

   !important is required: framework rules (.form-control and friends) are more
   specific than a bare element selector, and this is behavioural, not cosmetic.

   Excluded: checkbox/radio/range/color (no typed text, never trigger a zoom),
   and rich text editors, which are contenteditable and are not matched by
   input/textarea/select at all.
   ========================================================================== */
@media (max-width: 1024px) {
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]),
    textarea,
    select {
        font-size: 16px !important;
    }
}

/* Footer newsletter signup feedback */
.subscribe input.is-invalid {
  border-color: #ffb4c8;
  box-shadow: 0 0 0 3px rgba(255, 180, 200, 0.25);
}
.subscribe__error,
.subscribe__note {
  margin-top: 0.6rem;
  font-size: 0.82rem;
  line-height: 1.5;
}
.subscribe__error { color: #ffc9d8; }
.subscribe__note { color: #d9ffe9; }

/* ==========================================================================
   SUBMIT PRELOADER â€” same design as the ntt-web admin theme, rebuilt against
   this site's tokens (the admin rules are scoped to body.ntt-web and its own
   --ntt-* variables, neither of which exist here).
   ========================================================================== */
.ntt-preloader {
  position: fixed;
  inset: 0;
  z-index: 200000;
  display: none;
  align-items: center;
  justify-content: center;
  /* "Blur mirror": the page behind stays visible but is blurred and slightly
     desaturated, with a dark scrim for contrast. -webkit- prefix first for
     iOS Safari, which has shipped backdrop-filter ONLY prefixed since 9. */
  background: rgba(21, 16, 31, 0.38);
  -webkit-backdrop-filter: blur(14px) saturate(115%);
  backdrop-filter: blur(14px) saturate(115%);
  opacity: 0;
  transition: opacity 0.22s ease;
}
/* No backdrop-filter (older Android WebView, Firefox with the flag off): a
   heavier scrim, so the overlay still reads as a modal layer instead of a
   barely-there tint over live content. Written as `not (...)` on the
   PREFIXED property too, or Safari matches this and loses its blur. */
@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .ntt-preloader {
    background: rgba(21, 16, 31, 0.82);
  }
}
/* !important is REQUIRED, not decoration: the element carries an inline
   style="display: none;" (its no-CSS fallback), and an inline style beats a
   class rule. Without this the class was added, the tests saw it, and the
   overlay still never rendered. The ntt-web admin rule has the same
   !important for the same reason â€” dropping it was the bug. */
.ntt-preloader.is-active { display: flex !important; opacity: 1; }
.ntt-preloader-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}
/* The mark: a rotating brand-gradient ring with the logo held still inside
   it. Two elements because the ring spins and the icon must not. */
.ntt-preloader-orb {
  position: relative;
  width: 96px;
  height: 96px;
}
.ntt-preloader-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  /* conic-gradient: Safari 12.1+ / iOS 12.2+. The @supports block below
     covers anything older with a plain bordered spinner. */
  background: conic-gradient(
    from 0deg,
    var(--grad-btn-1),
    var(--color-primary-2),
    var(--grad-btn-2),
    var(--color-primary-2),
    var(--grad-btn-1)
  );
  animation: ntt-preloader-spin 1.1s linear infinite;
  /* Promotes the ring to its own layer so iOS composites the rotation on the
     GPU instead of repainting the blurred backdrop behind it every frame. */
  will-change: transform;
}
/* Punches the centre out to leave a RING. An inner circle, not mask-image:
   masks are still prefixed and patchy on older WebKit, and this is exact. */
.ntt-preloader-ring::after {
  content: "";
  position: absolute;
  inset: 5px;
  border-radius: 50%;
  background: var(--surface);
}
.ntt-preloader-icon {
  position: absolute;
  inset: 16px;
  width: calc(100% - 32px);
  height: calc(100% - 32px);
  border-radius: 50%;
  object-fit: contain;
}
@supports not (background: conic-gradient(red, blue)) {
  .ntt-preloader-ring {
    background: none;
    border: 4px solid rgba(124, 58, 237, 0.22);
    border-top-color: var(--color-primary);
  }
  .ntt-preloader-ring::after {
    display: none;
  }
}
.ntt-preloader-spinner {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 3px solid var(--border-soft);
  border-top-color: var(--color-primary);
  animation: ntt-preloader-spin 0.7s linear infinite;
}
.ntt-preloader-text {
  font-size: 13px;
  font-weight: 700;
  /* Sits on the dark scrim, not on a card, so it is light in BOTH themes â€”
     a token that flips would go dark-on-dark here. */
  color: #fff;
  letter-spacing: 0.03em;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.35);
}
@keyframes ntt-preloader-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .ntt-preloader-spinner { animation-duration: 2s; }
  /* Slowed, not stopped: a motionless ring gives no sign the page is still
     working, which is worse than a gentle one for someone who asked for
     less motion. The overlay fade is dropped entirely. */
  .ntt-preloader-ring { animation-duration: 2.4s; }
  .ntt-preloader { transition: none; }
}
/* A disabled submit must still read as the same control, just inert. */
.contact__submit[disabled],
.subscribe button[disabled] { opacity: 0.6; cursor: progress; }

/* ============================================
   Offers list page (/offers)
============================================= */
/* Pages that OPEN with a breadcrumb sit tight under the header â€” the
   generic .section 3.5rem top padding read as a hole between the two. */
.offerd.section,
.offerl.section {
  padding-top: 1.25rem;
}
.offerl__crumb {
  margin-bottom: 1.4rem;
}
.offerl__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
  margin-top: 2.2rem;
}
.offerl-card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition:
    transform 0.2s ease,
    box-shadow 0.2s ease;
}
.offerl-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.offerl-card__img {
  width: 100%;
  height: auto;
  display: block;
}
/* placeholder tile â€” only rendered when an offer has no image at all */
.offerl-card__media {
  aspect-ratio: 1859 / 846;
  background-color: #f6f4fc;
  background-color: color-mix(in srgb, var(--grad-btn-1) 6%, var(--surface));
}
.offerl-card__body {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  flex: 1;
  padding: 1.1rem 1.2rem 1.3rem;
}
.offerl-card__validity {
  align-self: flex-start;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 10%, var(--surface));
  border-radius: 999px;
  padding: 0.24rem 0.7rem;
}
.offerl-card__title {
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.35;
  color: var(--heading);
}
.offerl-card__excerpt {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.offerl-card__more {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: auto;
  padding-top: 0.35rem;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-primary);
  transition: gap 0.18s ease;
}
.offerl-card:hover .offerl-card__more {
  gap: 0.65rem;
}
.offerl__empty {
  margin-top: 2.2rem;
  padding: 3.2rem 1.2rem;
  text-align: center;
  color: var(--color-text-muted);
  background: var(--surface);
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-lg);
}
.offerl__empty strong {
  color: var(--heading);
}

/* pagination */
.pager {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.45rem;
  margin-top: 2.6rem;
}
.pager__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 42px;
  height: 42px;
  padding: 0 0.85rem;
  border-radius: 999px;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--color-text);
  background: var(--surface);
  border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
.pager__link--arrow {
  padding: 0;
  color: var(--color-primary);
}
a.pager__link:hover {
  color: var(--color-primary);
  border-color: color-mix(in srgb, var(--grad-btn-1) 27%, var(--surface));
  transform: translateY(-1px);
}
.pager__link.is-active {
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  border-color: transparent;
  box-shadow: 0 10px 20px -10px var(--color-primary);
}
.pager__link.is-disabled {
  opacity: 0.4;
  box-shadow: none;
}
.pager__gap {
  color: var(--color-text-muted);
  padding: 0 0.2rem;
}
@media (max-width: 980px) {
  .offerl__grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 640px) {
  .offerl__grid {
    grid-template-columns: 1fr;
    gap: 1.2rem;
  }
}

/* ============================================
   Admin-authored (CKEditor) content â€” shared by the offer detail and blog
   post pages. One HTML fragment rendered inside .rich-body; these rules
   restyle its generic tags to match the typed-block design above.
   Both pages keep their own BEM class alongside .rich-body for targeting.
============================================= */
.rich-body > * {
  margin-top: 1rem;
}
.rich-body > *:first-child {
  margin-top: 0;
}
.rich-body p {
  color: var(--color-text-muted);
  line-height: 1.75;
}
.rich-body strong {
  color: var(--heading);
  font-weight: 700;
}
.rich-body h2,
.rich-body h3,
.rich-body h4 {
  margin-top: 1.7rem;
  font-size: 1.12rem;
  font-weight: 700;
  color: var(--heading);
}
.rich-body h2 {
  font-size: 1.25rem;
}
.rich-body ul,
.rich-body ol {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.rich-body ul li,
.rich-body ol li {
  position: relative;
  padding-left: 1.2rem;
  color: var(--color-text-muted);
  line-height: 1.6;
}
.rich-body ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--color-primary);
}
.rich-body ol {
  counter-reset: offerd-ol;
}
.rich-body ol li {
  counter-increment: offerd-ol;
  padding-left: 1.5rem;
}
.rich-body ol li::before {
  content: counter(offerd-ol) ".";
  position: absolute;
  left: 0;
  color: var(--color-primary);
  font-weight: 700;
  font-size: 0.9em;
}
.rich-body a {
  color: var(--color-primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.rich-body img {
  border-radius: var(--radius-md);
}
.rich-body blockquote {
  border-left: 3px solid var(--color-primary);
  padding: 0.2rem 0 0.2rem 1rem;
  color: var(--color-text-muted);
  font-style: italic;
}
/* CKEditor wraps every table in <figure class="table"> */
.rich-body figure.table {
  margin: 1.2rem 0 0;
  overflow-x: auto;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
}
.rich-body table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}
.rich-body table th,
.rich-body table td {
  text-align: left;
  padding: 0.72rem 1rem;
  border-top: 1px solid var(--border-soft);
}
.rich-body table tr:first-child th,
.rich-body table tr:first-child td {
  border-top: 0;
}
/* column headers (matrix-style tables) get the gradient strip */
.rich-body table thead th {
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  white-space: nowrap;
}
/* row headers (label/value tables â€” the shape the panel's content starter
   uses) read as labels, mirroring .offerd__detrow */
.rich-body table tbody th {
  width: 34%;
  min-width: 140px;
  color: var(--heading);
  font-weight: 600;
  background: var(--surface-2);
}
.rich-body table td {
  color: var(--color-text-muted);
}

/* ============================================
   Blog â€” list (/blog), post (/blog/{slug}) and the shared sidebar
============================================= */
/* Screen-reader-only label. Needed by the sidebar search input, which is
   visually labelled by its placeholder and icon alone. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Pages that OPEN with a breadcrumb sit tight under the header. */
.blogl.section,
.blogd.section {
  padding-top: 1.25rem;
}
.blogl__crumb,
.blogd__crumb {
  margin-bottom: 1.4rem;
}

/* Article column plus sidebar. The sidebar is second in the DOM so a phone
   (and a screen reader) reaches the content first. */
.blogl__layout,
.blogd__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 2rem;
  align-items: start;
  margin-top: 2.2rem;
}
.blogl__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1.5rem;
}

/* ---------- card ---------- */
.blogc {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition:
    transform 0.2s ease,
    box-shadow 0.2s ease;
}
.blogc:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.blogc__media {
  display: block;
  overflow: hidden;
}
.blogc__media img {
  width: 100%;
  height: auto;
  display: block;
  transition: transform 0.4s ease;
}
.blogc:hover .blogc__media img {
  transform: scale(1.04);
}
.blogc__placeholder {
  display: block;
  aspect-ratio: 1200 / 800;
  background: color-mix(in srgb, var(--grad-btn-1) 6%, var(--surface));
}
.blogc__body {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  flex: 1;
  padding: 1.1rem 1.2rem 1.3rem;
}
.blogc__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}
.blogc__cat {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 10%, var(--surface));
  border-radius: 999px;
  padding: 0.26rem 0.7rem;
  transition: var(--transition);
}
a.blogc__cat:hover {
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
}
.blogc__date {
  font-size: 0.78rem;
  color: var(--color-text-muted);
}
.blogc__title {
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1.35;
  color: var(--heading);
}
.blogc__title a:hover {
  color: var(--color-primary);
}
.blogc__excerpt {
  font-size: 0.9rem;
  color: var(--color-text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.blogc__more {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: auto;
  padding-top: 0.35rem;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-primary);
  transition: gap 0.18s ease;
}
.blogc:hover .blogc__more {
  gap: 0.65rem;
}

/* ---------- result bar and empty state ---------- */
.blogl__resultbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-bottom: 1.4rem;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--border-soft);
}
.blogl__result {
  font-size: 0.92rem;
  color: var(--color-text-muted);
}
.blogl__result strong {
  color: var(--heading);
}
.blogl__clear {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-primary);
}
.blogl__clear:hover {
  text-decoration: underline;
  text-underline-offset: 3px;
}
.blogl__empty {
  padding: 3.2rem 1.2rem;
  text-align: center;
  color: var(--color-text-muted);
  background: var(--surface);
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-lg);
}
.blogl__empty strong {
  color: var(--heading);
}
.blogl__empty a {
  color: var(--color-primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ---------- sidebar ---------- */
.blogsb {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  /* Follows the reader down a long article; the top offset clears the
     sticky header. Released below the breakpoint, where it stacks. */
  position: sticky;
  top: 96px;
}
.blogsb__card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.15rem 1.2rem 1.3rem;
}
.blogsb__title {
  font-size: 0.98rem;
  font-weight: 800;
  color: var(--heading);
  margin-bottom: 0.9rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--border-soft);
}
.blogsb__search {
  position: relative;
  display: flex;
}
.blogsb__search input {
  width: 100%;
  padding: 0.7rem 2.9rem 0.7rem 0.95rem;
  font-size: 0.9rem;
  color: var(--color-text);
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: 999px;
  transition: var(--transition);
}
.blogsb__search input:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--grad-btn-1) 45%, var(--surface));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 12%, transparent);
}
.blogsb__search button {
  position: absolute;
  top: 50%;
  right: 5px;
  transform: translateY(-50%);
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  transition: var(--transition);
}
.blogsb__search button:hover {
  filter: brightness(1.08);
}
.blogsb__cats {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.blogsb__cat {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  border-radius: 10px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-text-muted);
  transition: var(--transition);
}
.blogsb__cat:hover {
  color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 7%, var(--surface));
}
.blogsb__cat.is-active {
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
}
.blogsb__count {
  flex-shrink: 0;
  font-style: normal;
  font-size: 0.74rem;
  font-weight: 700;
  min-width: 24px;
  text-align: center;
  padding: 0.12rem 0.42rem;
  border-radius: 999px;
  color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 11%, var(--surface));
}
.blogsb__cat.is-active .blogsb__count {
  color: #fff;
  background: rgba(255, 255, 255, 0.24);
}
.blogsb__recent {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
.blogsb-mini {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.blogsb-mini__img {
  flex-shrink: 0;
  width: 68px;
  height: 52px;
  object-fit: cover;
  border-radius: 10px;
}
.blogsb-mini__body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}
.blogsb-mini__title {
  font-size: 0.86rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--heading);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.blogsb-mini:hover .blogsb-mini__title {
  color: var(--color-primary);
}
.blogsb-mini__date {
  font-size: 0.74rem;
  color: var(--color-text-muted);
}
.blogsb__cta {
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  border-color: transparent;
}
.blogsb__cta-title {
  font-size: 1.05rem;
  font-weight: 800;
}
.blogsb__cta-text {
  margin-top: 0.4rem;
  font-size: 0.87rem;
  color: rgba(255, 255, 255, 0.86);
}
.blogsb__cta-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 1rem;
  padding: 0.6rem 1.2rem;
  border-radius: 999px;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--color-primary);
  background: #fff;
  transition: gap 0.18s ease;
}
.blogsb__cta-btn:hover {
  gap: 0.7rem;
}

/* ---------- post ---------- */
.blogd__main {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.8rem;
}
.blogd__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.7rem;
}
.blogd__title {
  margin-top: 0.85rem;
  font-size: 2rem;
  font-weight: 800;
  line-height: 1.2;
  color: var(--heading);
}
.blogd__banner {
  margin-top: 1.4rem;
  border-radius: var(--radius-md);
  overflow: hidden;
}
/* Natural height, same rule as the offer banner: a fixed crop window cut
   panel-uploaded artwork. */
.blogd__banner img {
  width: 100%;
  height: auto;
  display: block;
}
.blogd__body {
  margin-top: 1.5rem;
}
.blogd__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2rem;
  padding-top: 1.3rem;
  border-top: 1px solid var(--border-soft);
}
.blogd__back {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--color-text-muted);
  transition: var(--transition);
}
.blogd__back:hover {
  color: var(--color-primary);
}
.blogd__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1.6rem;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 700;
  color: #fff;
  background: linear-gradient(
    135deg,
    var(--color-primary),
    var(--color-primary-2)
  );
  box-shadow: 0 12px 24px -12px var(--color-primary);
  transition:
    transform 0.18s ease,
    box-shadow 0.18s ease;
}
.blogd__cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 16px 28px -12px var(--color-primary);
}
.blogd__related {
  margin-top: 3rem;
}
/* Three across beneath a post; two in the list page's narrower column. */
.blogd__related .blogl__grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 1080px) {
  .blogl__layout,
  .blogd__layout {
    grid-template-columns: minmax(0, 1fr);
  }
  /* Released from sticky and laid out two-up, so the tools read as a row
     under the article rather than one very long trailing column. */
  .blogsb {
    position: static;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
  }
}
@media (max-width: 760px) {
  .blogl__grid,
  .blogd__related .blogl__grid {
    grid-template-columns: minmax(0, 1fr);
  }
  .blogsb {
    grid-template-columns: minmax(0, 1fr);
  }
  .blogd__main {
    padding: 1.2rem;
  }
  .blogd__title {
    font-size: 1.55rem;
  }
}

/* ---------- Search widget: per-field validation messages ----------
   The message lives INSIDE its own field and expands it, rather than being
   absolutely positioned: an absolute message would sit on top of the next
   stacked field on a phone, where the bar is a vertical column.

   max-height rather than height:auto because auto is not animatable; the
   value only has to exceed a two-line message, and the element is
   overflow:hidden so an over-estimate costs nothing.                      */
.flightsearch .seg__err {
  display: block;
  max-height: 0;
  margin-top: 0;
  opacity: 0;
  overflow: hidden;
  transform: translateY(-4px);
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.35;
  color: #c22f6b;
  transition:
    max-height 0.28s var(--ease, ease),
    margin-top 0.28s var(--ease, ease),
    opacity 0.2s ease,
    transform 0.28s var(--ease, ease);
}
.flightsearch .seg.has-error .seg__err {
  max-height: 44px;
  margin-top: 5px;
  opacity: 1;
  transform: translateY(0);
}
/* The ring is an inset shadow, not a border: a border would change the
   field's box size and shift every neighbour by a pixel as it appears. */
.flightsearch .seg.has-error {
  box-shadow: inset 0 0 0 1.5px #c22f6b;
}
/* One soft shake on appearance â€” enough to draw the eye without the field
   bouncing around. Honoured only when the visitor allows motion. */
@media (prefers-reduced-motion: no-preference) {
  .flightsearch .seg.has-error {
    animation: segShake 0.36s var(--ease, ease);
  }
}
@keyframes segShake {
  0%,
  100% {
    transform: translateX(0);
  }
  25% {
    transform: translateX(-3px);
  }
  60% {
    transform: translateX(3px);
  }
}
/* Dark mode: the light-mode red is too dim on a dark field. */
[data-theme="dark"] .flightsearch .seg__err {
  color: #ff8fb3;
}
[data-theme="dark"] .flightsearch .seg.has-error {
  box-shadow: inset 0 0 0 1.5px #ff8fb3;
}

/* ---------- eSIM option picker ----------
   Deliberately thin: the panel and its rows reuse .ap-pop / .ap-list /
   .ap-item â€” the widget's single dropdown skin, shared with the airport
   picker â€” so the two tabs cannot drift apart. Only the chevron, the check
   mark and the room they need are new here. */
.flightsearch .opt-seg {
  /* room for the chevron so a long value never runs under it */
  padding-right: 34px;
}
.flightsearch .opt-caret {
  position: absolute;
  top: 50%;
  right: 12px;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  color: var(--muted);
  pointer-events: none;
  transition: transform 0.2s var(--ease, ease), color 0.2s ease;
}
.flightsearch .opt-caret svg {
  width: 15px;
  height: 15px;
}
.flightsearch .opt-seg:hover .opt-caret {
  color: var(--v2);
}
.flightsearch .opt-seg.is-open .opt-caret {
  transform: translateY(-50%) rotate(180deg);
  color: var(--v2);
}
/* The option list is narrower than the airport one: its rows are a couple of
   words, not city + airport + country. Matching the field keeps the panel
   visually attached to the control it belongs to. */
.flightsearch .opt-pop {
  width: max(200px, 100%);
}
/* The airport list's 282px cap is tuned for a long, obviously-scrollable
   list. These lists are short, and that cap clipped the last row by a few
   pixels â€” which reads as a rendering bug, not as "scroll for more". Raised
   just enough to show a five-option list whole; longer lists still scroll. */
.flightsearch .opt-pop .ap-list {
  max-height: 340px;
}
.flightsearch .opt-check {
  flex: none;
  display: grid;
  place-items: center;
  color: var(--v2);
}
.flightsearch .opt-check svg {
  width: 15px;
  height: 15px;
}

/* ---------- FAQ list page (/faqs) ----------
   The accordion rows themselves are .faq__item, shared with the homepage
   section â€” only the page shell is new here. */
.faql.section {
  padding-top: 1.25rem;
}
.faql__crumb {
  margin-bottom: 1.4rem;
}
/* One column, and narrow enough that a long answer stays readable. The
   homepage uses two columns to stay compact; on a page of 20 questions that
   would send the reader back up the page every time an answer expands. */
.faql__list {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  max-width: 860px;
  margin: 2.2rem auto 0;
}
.faql__empty {
  max-width: 860px;
  margin: 2.2rem auto 0;
  padding: 3.2rem 1.2rem;
  text-align: center;
  color: var(--color-text-muted);
  background: var(--surface);
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-lg);
}
.faql__empty strong {
  color: var(--heading);
}
.faql__empty a {
  color: var(--color-primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* Closing prompt: someone who read every answer and is still here needs a
   way to ask, not another list. */
.faql__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  margin-top: 2.6rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border-soft);
}
.faql__cta p {
  font-size: 1rem;
  font-weight: 600;
  color: var(--heading);
}

/* ---------- Flight deals list page (/flight-deals) ----------
   Cards are .deal, shared with the homepage teaser â€” only the page shell and
   the grid's wrapping behaviour are new. */
.deall.section {
  padding-top: 1.25rem;
}
.deall__crumb {
  margin-bottom: 1.4rem;
}
/* The homepage pins five across because it shows exactly five. A page of
   deals has an arbitrary count, so the grid fills by size instead and the
   last row never stretches into odd wide cards. */
.deall__grid {
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 1.25rem;
  margin-top: 2.2rem;
}
.deall__empty {
  max-width: 720px;
  margin: 2.2rem auto 0;
  padding: 3.2rem 1.2rem;
  text-align: center;
  color: var(--color-text-muted);
  background: var(--surface);
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-lg);
}
.deall__empty strong {
  color: var(--heading);
}
.deall__empty a {
  color: var(--color-primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* Prices are "starting from" figures; saying so once at the bottom is more
   honest than an asterisk on every card. */
.deall__note {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  margin-top: 2.6rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border-soft);
  text-align: center;
}
.deall__note p {
  max-width: 560px;
  font-size: 0.9rem;
  color: var(--color-text-muted);
}
/* Trip type sits beside the cabin, quieter than it â€” cabin is what people
   scan for, trip type is the qualifier. */
.deal__trip {
  margin-left: auto;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--color-text-muted);
  background: var(--surface-2);
  border-radius: 999px;
  padding: 0.15rem 0.5rem;
}
@media (max-width: 600px) {
  .deall__grid {
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 1rem;
  }
}

/* The inline icon in a list-page heading (/blog, /faqs, /flight-deals) takes
   the brand colour. Left to inherit it rendered flat black against the navy
   heading, reading as a stray glyph rather than part of the title. Scoped to
   a DIRECT child so the pill icons in homepage section heads are untouched. */
.section__title > .ico {
  color: var(--color-primary);
  vertical-align: -0.06em;
}

/* ---------- About Us: panel icons ----------
   The static page drew inline <svg> in these two slots; the panel stores
   Bootstrap Icons class names (bi-*), so they render as <i> glyphs instead.
   An <i> has no intrinsic size, so without a font-size it collapses to the
   inherited body size and floats small inside its 48/54px disc. */
.mv__icon i,
.diff__icon i {
  font-size: 22px;
  line-height: 1;
}
.diff__icon i {
  font-size: 24px;
}

/* ---------- Company Information: panel-driven card bits ----------
   The static page drew inline <svg> in .reg-card__ic; the panel stores
   Bootstrap Icons class names (bi-*), so they render as <i> glyphs. An <i>
   has no intrinsic size, so without a font-size it collapses to the inherited
   body size and floats small inside its 48px tile. */
.reg-card__ic i {
  font-size: 21px;
  line-height: 1;
}
/* The optional short line under a licence number â€” a NEW slot the panel adds
   (Short description). Hidden entirely when the admin leaves it empty. */
.reg-card__note {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.8rem;
  font-weight: 400;
  line-height: 1.45;
  color: var(--color-text-muted);
}

/* ---------- Reviews list page (/reviews) ----------
   The cards themselves are .review, shared with the homepage carousel â€” only
   the page shell and the grid are new here. */
.reviewl.section {
  padding-top: 1.25rem;
}
.reviewl__crumb {
  margin-bottom: 1.4rem;
}
/* Headline figure above the grid. */
.reviewl__summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 1.6rem;
}
.reviewl__score {
  font-size: 2.4rem;
  font-weight: 800;
  line-height: 1;
  color: var(--color-primary);
}
.reviewl__outof {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--heading);
}
.reviewl__count {
  width: 100%;
  text-align: center;
  font-size: 0.9rem;
  color: var(--color-text-muted);
}
/* A page of reviews is a grid, not the homepage's swipe track. Rows stretch
   so cards in a row match height regardless of comment length.

   This base rule is the narrow-screen one and is deliberately left as `1fr`:
   below ~1000px the row only ever holds one or two cards and they already fill
   it, so a card should use the full width rather than sit boxed in the middle
   of a phone screen. The centring problem does not exist down here. */
.reviewl__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 1.5rem;
  margin-top: 2rem;
  align-items: stretch;
}
/* From ~1000px up the row has room for three cards, and with only two reviews
   published auto-fill kept the third track and left a visibly empty gap to
   their right. Two changes fix that, and both are needed:

     auto-FIT  collapses the track nothing occupies;
     the 348px cap  stops the survivors absorbing the freed space â€” `1fr`
                    tracks swallow every spare pixel, leaving justify-content
                    nothing to centre.

   348px is chosen so a FULL row still lays out three across at the 1200px
   container (3x348 + 2 gaps = 1092, inside the 1160 available); a larger cap
   makes the browser count tracks by the cap and would drop desktop to two
   columns. Full rows are ~23px narrower than before as a result.

   A TRAILING part row (5 reviews over 3 columns) still aligns left, because
   those tracks are held by the row above â€” correct, since centring it would
   knock those cards out of line with the row above them. */
@media (min-width: 1000px) {
  .reviewl__grid {
    grid-template-columns: repeat(auto-fit, minmax(300px, 348px));
    justify-content: center;
  }
}
.reviewl__grid .review {
  display: flex;
  flex-direction: column;
}
/* Pushes the reviewer to the bottom so every card's name row lines up. */
.reviewl__grid .review__user {
  margin-top: auto;
}
.reviewl__empty {
  max-width: 720px;
  margin: 2.2rem auto 0;
  padding: 3.2rem 1.2rem;
  text-align: center;
  color: var(--color-text-muted);
  background: var(--surface);
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-lg);
}
.reviewl__empty strong {
  color: var(--heading);
}
.reviewl__empty a {
  color: var(--color-primary);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.reviewl__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
  margin-top: 2.6rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border-soft);
}
.reviewl__cta p {
  font-size: 1rem;
  font-weight: 600;
  color: var(--heading);
}
/* A real reviewer photo, when the panel has one. Same 44px disc the initials
   avatar occupies, so a mixed list stays aligned. */
.review__avatar--photo {
  object-fit: cover;
  padding: 0;
}

/* ---------- Review text: Show more / Show less ----------
   The text is clamped by CHARACTER COUNT (not line-clamp) because the
   requirement is a fixed 130-character preview, and -webkit-line-clamp counts
   lines, which vary with width and script. main.js does the counting; this is
   only the button and the expand animation.

   iOS/cross-OS notes for anything added here:
   - plain flexbox + transitions only, no :has(), no container queries;
   - touch-action + tap-highlight so the button responds instantly on iOS
     instead of waiting out the double-tap-zoom delay;
   - the height animation is driven by main.js through the Web Animations API
     with a capability check, so an engine without it simply snaps. */
.review__more {
  align-self: flex-start;
  margin-top: 0.35rem;
  margin-bottom: 1.3rem;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--color-primary);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.18s ease;
}
.review__more:hover,
.review__more:focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
}
/* When the button is present the text's own bottom margin would double up
   with the button's, so the text gives its margin to the button. */
.review__text.is-clampable {
  margin-bottom: 0;
}

/* ==========================================================================
   No page zoom on phones / touch devices (â‰¤1024px)
   --------------------------------------------------------------------------
   `pan-x pan-y` lets the page scroll normally in both directions but tells
   the browser NOT to treat a touch as pinch-zoom or double-tap-zoom.
   Deliberately NOT `manipulation`: that keeps pinch-zoom enabled (it only
   drops the double-tap delay), which is the opposite of what is wanted here.

   This is half the mechanism. iOS Safari has ignored `user-scalable=no` in
   the viewport meta since iOS 10 and still honours a two-finger pinch even
   with touch-action set, so main.js also blocks Safari's gesture* events â€”
   see "Disable page zoom" there. Both halves are gated to â‰¤1024px so desktop
   zoom is untouched.
   ========================================================================== */
@media (max-width: 1024px) {
  html.ntt-no-zoom,
  html.ntt-no-zoom body {
    touch-action: pan-x pan-y;
  }
}

/* ==========================================================================
   OUR JOURNEY  (pages/journey.blade.php)
   --------------------------------------------------------------------------
   Every colour is a theme token, never a literal, so the palette published
   from the admin panel (and the dark theme) applies here with no extra rules.
   Reveal animations are opt-in via [data-jrn-reveal] and are neutralised
   entirely under prefers-reduced-motion at the bottom of this block.
   ========================================================================== */

/* Scroll-in reveal. The .is-in class is added by the IntersectionObserver in
   main.js; with JS off the content must not stay invisible, hence the
   html:not(.jrn-js) guard below. */
[data-jrn-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.55s ease, transform 0.55s ease;
  will-change: opacity, transform;
}
[data-jrn-reveal].is-in {
  opacity: 1;
  transform: none;
}
html:not(.jrn-js) [data-jrn-reveal] {
  opacity: 1;
  transform: none;
}

.jrn-eyebrow {
  display: inline-block;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin-bottom: 0.6rem;
}
.jrn-title {
  font-size: 2rem;
  font-weight: 800;
  line-height: 1.15;
  color: var(--heading);
  margin-bottom: 1rem;
}
.jrn-title span,
.journey .section__title span {
  color: var(--color-primary);
}
.jrn-sub {
  color: var(--color-text-muted);
  max-width: 620px;
  margin: 0.6rem auto 0;
}

/* ---------- Intro ---------- */
.jrn-intro__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 3rem;
  align-items: center;
}
.jrn-intro__media {
  position: relative;
}
.jrn-intro__media img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.jrn-intro__badge {
  position: absolute;
  left: 1.2rem;
  bottom: 1.2rem;
  display: grid;
  padding: 0.7rem 1.1rem;
  border-radius: var(--radius-md);
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  color: #fff;
  box-shadow: var(--shadow-md);
}
.jrn-intro__badge small { font-size: 0.7rem; opacity: 0.85; letter-spacing: 0.08em; }
.jrn-intro__badge b { font-size: 1.5rem; line-height: 1; }
.jrn-intro__copy p {
  color: var(--color-text-muted);
  margin-bottom: 0.9rem;
}
.jrn-intro__points {
  list-style: none;
  margin: 1.2rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.6rem;
}
.jrn-intro__points li {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  color: var(--color-text);
  font-size: 0.95rem;
}
.jrn-intro__points i { color: var(--color-primary); flex: 0 0 auto; margin-top: 2px; }

/* ---------- Timeline ---------- */
.jrn-timeline {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 880px;
  position: relative;
}
/* The spine. Inset top and bottom so it does not dangle past the first and
   last milestone dots. */
.jrn-timeline::before {
  content: "";
  position: absolute;
  left: 27px;
  top: 12px;
  bottom: 12px;
  width: 2px;
  background: linear-gradient(180deg, var(--color-primary), var(--color-primary-2), transparent);
  opacity: 0.35;
}
.jrn-tl__item {
  position: relative;
  padding-left: 72px;
  padding-bottom: 1.6rem;
}
.jrn-tl__item:last-child { padding-bottom: 0; }
.jrn-tl__dot {
  position: absolute;
  left: 0;
  top: 4px;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: 1.35rem;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  box-shadow: 0 8px 20px rgba(var(--grad-btn-2-rgb), 0.28);
  transition: transform var(--transition);
}
.jrn-tl__item:hover .jrn-tl__dot { transform: scale(1.06); }
.jrn-tl__card {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 1.25rem 1.4rem;
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.jrn-tl__item:hover .jrn-tl__card {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
  border-color: var(--color-primary);
}
.jrn-tl__year {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--color-primary);
  background: var(--surface-3);
  border-radius: 999px;
  padding: 0.2rem 0.7rem;
  margin-bottom: 0.5rem;
}
.jrn-tl__title { font-size: 1.1rem; font-weight: 700; color: var(--heading); }
.jrn-tl__text { color: var(--color-text-muted); font-size: 0.94rem; margin-top: 0.35rem; }

/* ---------- Stats ---------- */
.jrn-stats__panel {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
  padding: 2rem 1.5rem;
  border-radius: var(--radius-lg);
  background: linear-gradient(135deg, var(--grad-footer-1), var(--grad-footer-2));
  box-shadow: var(--shadow-lg);
}
.jrn-stat { text-align: center; color: #fff; padding: 0.4rem; }
.jrn-stat__icon {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  margin: 0 auto 0.6rem;
  border-radius: 50%;
  font-size: 1.2rem;
  background: rgba(255, 255, 255, 0.16);
}
.jrn-stat__value {
  display: block;
  font-size: 2rem;
  font-weight: 800;
  line-height: 1.1;
  /* Tabular figures keep the row from jittering while the count-up runs. */
  font-variant-numeric: tabular-nums;
}
.jrn-stat__label { font-size: 0.85rem; opacity: 0.9; }

/* ---------- Network ---------- */
.jrn-net__grid,
.jrn-values__grid,
.jrn-team__grid {
  display: grid;
  gap: 1.25rem;
}
.jrn-net__grid { grid-template-columns: repeat(3, 1fr); }
.jrn-city {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition), box-shadow var(--transition);
}
.jrn-city:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.jrn-city__img { position: relative; aspect-ratio: 16 / 10; overflow: hidden; }
.jrn-city__img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.5s ease;
}
.jrn-city:hover .jrn-city__img img { transform: scale(1.06); }
.jrn-city__code {
  position: absolute;
  top: 0.8rem;
  left: 0.8rem;
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  color: #fff;
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
  background: rgba(var(--grad-btn-2-rgb), 0.85);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.jrn-city__body { padding: 1rem 1.1rem 1.2rem; }
.jrn-city__body h3 { font-size: 1.05rem; font-weight: 700; color: var(--heading); }
.jrn-city__body p { font-size: 0.9rem; color: var(--color-text-muted); margin-top: 0.25rem; }

/* ---------- Values ---------- */
.jrn-values__grid { grid-template-columns: repeat(4, 1fr); }
.jrn-value {
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 1.4rem 1.2rem;
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}
.jrn-value:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--color-primary); }
.jrn-value__icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 14px;
  font-size: 1.3rem;
  color: var(--color-primary);
  background: var(--surface-3);
  margin-bottom: 0.85rem;
}
.jrn-value h3 { font-size: 1.02rem; font-weight: 700; color: var(--heading); }
.jrn-value p { font-size: 0.9rem; color: var(--color-text-muted); margin-top: 0.35rem; }

/* ---------- Team ---------- */
.jrn-team__grid { grid-template-columns: repeat(3, 1fr); }
.jrn-member {
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 1.6rem 1.3rem;
  box-shadow: var(--shadow-sm);
  transition: transform var(--transition), box-shadow var(--transition);
}
.jrn-member:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
/* Icon medallion rather than a photo: these cards name teams, not people. */
.jrn-member__icon {
  display: grid;
  place-items: center;
  width: 84px;
  height: 84px;
  margin: 0 auto 0.9rem;
  border-radius: 50%;
  font-size: 2rem;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  box-shadow: 0 10px 24px rgba(var(--grad-btn-2-rgb), 0.26);
  transition: transform var(--transition);
}
.jrn-member:hover .jrn-member__icon { transform: scale(1.05); }
.jrn-member h3 { font-size: 1.05rem; font-weight: 700; color: var(--heading); }
.jrn-member__role {
  display: block;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--color-primary);
  margin-top: 0.15rem;
}
.jrn-member p { font-size: 0.9rem; color: var(--color-text-muted); margin-top: 0.5rem; }

/* ---------- CTA ---------- */
.jrn-cta__panel {
  text-align: center;
  padding: 2.6rem 1.5rem;
  border-radius: var(--radius-lg);
  background:
    linear-gradient(135deg, rgba(var(--grad-btn-2-rgb), 0.92), rgba(var(--color-primary-rgb), 0.92)),
    url("../assets/cta-bg-image.png") center/cover no-repeat;
  color: #fff;
  box-shadow: var(--shadow-lg);
}
.jrn-cta__panel h2 { font-size: 1.9rem; font-weight: 800; }
.jrn-cta__panel p { opacity: 0.92; margin-top: 0.5rem; }
.jrn-cta__actions {
  display: flex;
  gap: 0.8rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 1.4rem;
}
.jrn-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1.5rem;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.95rem;
  transition: transform var(--transition), box-shadow var(--transition), background var(--transition);
  /* No double-tap delay on touch, same as the site other pill buttons. */
  touch-action: manipulation;
}
.jrn-btn--solid { background: #fff; color: var(--grad-btn-2); }
.jrn-btn--solid:hover { transform: translateY(-2px); box-shadow: 0 12px 26px rgba(0, 0, 0, 0.2); }
.jrn-btn--ghost { border: 1.5px solid rgba(255, 255, 255, 0.7); color: #fff; }
.jrn-btn--ghost:hover { background: rgba(255, 255, 255, 0.14); transform: translateY(-2px); }

/* ---------- Responsive ---------- */
@media (max-width: 1024px) {
  .jrn-values__grid { grid-template-columns: repeat(2, 1fr); }
  .jrn-net__grid { grid-template-columns: repeat(2, 1fr); }
  .jrn-stats__panel { grid-template-columns: repeat(2, 1fr); row-gap: 1.4rem; }
}
@media (max-width: 860px) {
  .jrn-intro__grid { grid-template-columns: 1fr; gap: 2rem; }
  .jrn-team__grid { grid-template-columns: 1fr; }
  .jrn-title { font-size: 1.7rem; }
}
@media (max-width: 620px) {
  .jrn-net__grid { grid-template-columns: 1fr; }
  .jrn-values__grid { grid-template-columns: 1fr; }
  .jrn-timeline::before { left: 19px; }
  .jrn-tl__item { padding-left: 56px; }
  .jrn-tl__dot { width: 40px; height: 40px; font-size: 1rem; }
  .jrn-stat__value { font-size: 1.6rem; }
  .jrn-cta__panel h2 { font-size: 1.5rem; }
}

/* Motion-sensitive visitors get the content, not the movement. Overrides the
   reveal transform AND the hover lifts, so nothing slides under them. */
@media (prefers-reduced-motion: reduce) {
  [data-jrn-reveal],
  [data-jrn-reveal].is-in {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .jrn-city:hover,
  .jrn-value:hover,
  .jrn-member:hover,
  .jrn-btn:hover,
  .jrn-tl__item:hover .jrn-tl__card,
  .jrn-tl__item:hover .jrn-tl__dot { transform: none; }
  .jrn-city:hover .jrn-city__img img { transform: none; }
  .jrn-member:hover .jrn-member__icon { transform: none; }
}

/* ============================================================
   Flight results (/flights) â€” E2 slice
   Plain by intent. The Google-Flights presentation (filter chips,
   sort, expandable itinerary, mobile sheets) is W1-W6; see
   docs/flight-search-results.md before restyling this.
   ============================================================ */
.flightl__head { margin: 0 0 6px; }
/* The theme's .pill carries a 1rem bottom margin for centred section heads;
   in this left-aligned head it only needs to clear the title. */
.flightl__head .pill { margin-bottom: 0.5rem; }
.flightl__title {
  font-size: clamp(1.5rem, 4vw, 2rem);
  margin: 0 0 4px;
  color: var(--heading);
  font-variant-numeric: tabular-nums;
}
.flightl__sub { color: var(--color-text-muted); margin: 0; }
.flightl__status { color: var(--color-text-muted); margin: 18px 0 14px; min-height: 1.4em; }
.flightl__empty { color: var(--color-text-muted); }
.flightl__back { margin-top: 16px; }

.flightl__notice {
  border-radius: 18px;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.flightl__notice--error {
  background: var(--surface-3);
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 18%, #fff);
  border-radius: 18px;
  color: var(--color-text);
}

.flightl__list { display: flex; flex-direction: column; gap: 12px; }
/* Cards carry far more inside them on a phone â€” two legs, two pin controls, a
   price block and two actions â€” so 12px between them made the list read as one
   continuous slab. The tag that straddles each card's top edge needs the room
   too. */
@media (max-width: 894.98px) {
  .flightl__list { gap: 32px; }
}

.flightl__skeleton {
  height: 92px;
  border-radius: 18px;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
  background-size: 200% 100%;
  animation: flight-shimmer 1.4s linear infinite;
}
@keyframes flight-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* Placeholders sitting UNDER real results, while the search is still running.
   Shorter and progressively fainter than the full-height ones that stand in
   for the whole list: below real cards these are a "more on the way" cue, and
   at full strength they compete with the flights the visitor is reading. */
.flightl__skeleton--tail { height: 68px; opacity: 0.55; }
.flightl__skeleton--tail.is-faint { height: 56px; opacity: 0.3; }

@media (prefers-reduced-motion: reduce) {
  /* A frozen gradient reads as a broken image, so hold a flat tone instead. */
  .flightl__skeleton { animation: none; background: var(--surface-2); }
}

.flightc {
  position: relative;
  background: var(--surface);
  /* Matches .deal exactly: the brand-tinted border is what makes a card look
     like it belongs to this site. A neutral --border-soft card reads as
     foreign next to the deal and offer cards on the same page. */
  border: 1px solid #eee9f7;
  border: 2px solid color-mix(in srgb, var(--grad-btn-1) 50%, #fff);
  border-radius: 18px;
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
/* The theme's card hover is translateY(-4px). Deliberately NOT used here: that
   works for a grid of a few cards, but this is a dense list of sixty rows and
   lifting each one under the cursor makes scanning jumpy. Google Flights does
   not move its rows either. The brand border-colour shift is kept, so the
   hover still reads as the same design language. */
.flightc:hover,
.flightc.is-open {
  border-color: #c9b6f2;
  border-color: color-mix(in srgb, var(--grad-btn-1) 40%, #fff);
  box-shadow: 0 16px 36px -16px rgba(109, 40, 217, 0.45);
}

/* The whole summary row is the control â€” a chevron-sized hit target is a poor
   one, and this keeps it one tab stop instead of several. */
.flightc__summary {
  /* logo | legs | price | chevron. Fixed outer tracks so the price column
     starts at the same x on every card in the list. */
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr) 132px 14px;
  gap: 14px;
  align-items: center;
  width: 100%;
  background: none;
  border: 0;
  /* HEADROOM FOR THE BADGES THAT STRADDLE THE TOP EDGE.

     .flightc__badges is absolutely positioned at top:-12px and is about 19px
     tall, so it hangs ~7px INSIDE the card. With 12px of padding that left ~5px
     between "Economy class" and the price directly beneath it, and the two read
     as one block. 22px gives the price room to be its own thing.

     The mobile rule below already carried extra padding for the same reason
     (for .flightc__tag at top:-9px); desktop never did. */
  padding: 22px 16px 14px;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  /* Matches the card it fills, so the focus ring and any hover ground follow
     the card's corner instead of cutting across it. */
  border-radius: 18px;
}
.flightc__summary:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }

/* Sized whether or not an image loads, so a slow logo cannot shift the row. */
.flightc__logo {
  position: relative;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border-radius: 10px;
  background: var(--surface-3);
  overflow: hidden;
}
.flightc__logo img { width: 38px; height: 38px; object-fit: contain; border-radius: 10px; }

/* The per-leg carrier mark beside the AIRLINE NAME is phone-only — on desktop
   the same leg carries its mark beside the Outbound/Return pill instead (see
   .flightc__logo--tag below), and both at once would just repeat. Declared
   here, with the other logo rules, so they are read together. */
.flightc__logo--leg { display: none; }

/* THE LEG'S MARK, BEFORE ITS OUTBOUND/RETURN PILL — desktop only.
   Sized between the card header's 44px tile and the phone row's 20px one: big
   enough to identify an airline at a glance, small enough that the pill beside
   it still reads as the label for the row. */
.flightc__logo--tag {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  /* The header mark pulls itself 4px into the row below to tighten the card
     header (see .flightc__logo); inherited here that would lift this one off
     the pill it belongs to. */
  margin-bottom: 0;
}
.flightc__logo--tag img { width: 22px; height: 22px; border-radius: 6px; }
.flightc__logo--tag .flightc__mono { font-size: 0.55rem; }

/* The tag cell stacks by default (.flightc__col is a column). The mark has to
   sit BESIDE the pill, not above it, so this one cell becomes a row.
   `align-items: stretch` is dropped with it — that was what let the lone pill
   fill the cell's width, and in a row it would stretch the mark's height. */
/* BOTH PILLS THE SAME WIDTH, still. `.flightc__col--tag`'s `align-items:
   stretch` used to do this: in a COLUMN, stretch sizes the pill across the
   track. The cell is a row now, where align-items works on the vertical axis
   instead — so the pills fell back to their own text widths and "OUTBOUND" and
   "RETURN", stacked one above the other, came out visibly different. Growing
   the pill into whatever the mark leaves restores it. */
.flightc__leg .flightc__col--tag .flightc__legtag { flex: 1 1 auto; min-width: 0; }

/* `.flightc__leg` prefix is NOT decoration. `.flightc__col` declares
   `flex-direction: column` and is declared LATER in this file; at equal
   specificity (0-1-0) source order decides, so a bare `.flightc__col--tag`
   here loses and the mark stacks ON TOP of the pill instead of sitting beside
   it — measured as a 26px vertical offset, exactly the tile's height. The
   compound selector (0-2-0) wins regardless of order. */
.flightc__leg .flightc__col--tag {
  /* Unchanged: how the whole cell sits in the leg row. */
  align-self: center;
  flex-direction: row;
  align-items: center;
  gap: 8px;
}

/* DARK MODE: keep the logo tile WHITE. --surface-3 is #2a2438 here, and
   airline logos are overwhelmingly dark ink on a transparent background —
   supplied as artwork for a white page, not as a themeable asset. On the dark
   tile they disappeared. This is a case where the tile must NOT follow the
   theme: the carrier's own artwork decides the background it needs, exactly
   like a favicon strip. Placed directly after the light rule so source order
   can never work against it. */
[data-theme="dark"] .flightc__logo {
  background: #fff;
}

/* Fallback only â€” see logoHtml(). Shown when there is no logo URL, or when
   the image fails to load. Rendering it permanently behind the artwork made
   the carrier code print across the logo. */
.flightc__mono {
  display: none;
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--color-text-muted);
  letter-spacing: 0.02em;
}
.flightc__logo.is-fallback .flightc__mono { display: block; }

/* The one item allowed to shrink. Without min-width:0 a long carrier name
   pushes the price block off the card. */
/* Two directions stack; the logo, price and chevron span both. */
.flightc__legs { display: flex; flex-direction: column; gap: 8px; min-width: 0; }

/* THE line that creates the column rhythm. minmax/fixed tracks â€” never `auto` â€”
   because an auto track sizes to its own card's content and every card then
   lands its duration and stops in a different place. */
.flightc__leg {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 92px 116px;
  gap: 12px;
  align-items: baseline;
}

/* ---- Round-trip leg labels -------------------------------------------------
   A round-trip card shows two time rows and, unlabelled, the visitor has to
   infer which way round they are from the airport codes. The label is added as
   a LEADING FIXED track â€” fixed, never `auto`, for the same reason the three
   tracks above are fixed: an auto track sizes to its own card's content and
   every card then lands its times in a different place.

   The track is added by a modifier on the container, so a ONE-WAY card keeps
   the exact three-track grid it has always had. A search is either one-way or
   round-trip, so the two shapes never appear in the same list and cannot
   misalign against each other. */
/* NO LOGO TRACK ON A ROUND TRIP. The header mark is not rendered there (each
   leg carries its own beside its pill), so the 46px track would otherwise sit
   empty and push every following column out of line with the one-way cards. */
.flightc__summary--labelled { grid-template-columns: minmax(0, 1fr) 132px 14px; }

/* 140px, not 104px: the track now holds the leg's carrier mark (26px) and the
   8px gap before the pill as well as the pill itself, which keeps ~106px — the
   width it had when it was alone. Sized here rather than by letting the track
   grow, for the reason the comment above gives: an auto track sizes to its own
   card and every card then lands its times somewhere different. */
.flightc__legs--labelled .flightc__leg {
  grid-template-columns: 140px minmax(0, 1fr) 92px 116px;
}

/* The pill sits against the times, which are the tallest thing in the row, so
   it centres on the row rather than sitting on its baseline. */
/* align-self centres the COLUMN vertically in the row; align-items centres the
   badge HORIZONTALLY inside it. The labelled-leg grid gives this column a
   FIXED 104px track (see .flightc__legs--labelled), and the pill is only as
   wide as "OUTBOUND" or "RETURN" — so without this it sat hard against the
   left edge of the track while the row's other columns read as centred.
   `stretch` rather than `center`, so BOTH pills fill the 104px track and are
   therefore the same width — "OUTBOUND" and "RETURN" are different lengths and
   two differently-sized pills stacked directly above one another read as a
   mistake. Stretching to the track is used in preference to a hardcoded
   min-width: the width then follows the grid definition instead of being a
   second number that has to be kept in step with it. The label inside is
   centred by .flightc__legtag { justify-content: center }.
   The narrow-screen override further down deliberately keeps flex-start:
   there the layout is a 2-column grid with no fixed track, and left-aligned
   is correct — stretch would run the pill the full width of the row. */
.flightc__col--tag { align-self: center; align-items: stretch; }

.flightc__legtag {
  display: inline-flex;
  align-items: center;
  /* Keeps the icon + label centred now that the pill is wider than its text. */
  justify-content: center;
  gap: 5px;
  padding: 3px 9px 3px 7px;
  border-radius: 999px;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  /* Brand-tinted rather than a flat grey chip: it has to read as part of this
     site's card, next to the price and the provider tag. */
  color: var(--color-primary);
  /* Plain values first, color-mix second â€” the same fallback pattern .flightc
     and .deal use. color-mix needs iOS Safari 16.2; without these the pill
     would lose its ground and its border entirely on an older iPhone. */
  background: #f3eefb;
  border: 1px solid #e3d8f5;
  background: color-mix(in srgb, var(--grad-btn-1) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 22%, transparent);
}
/* bi-airplane points NOSE-UP at 0deg. Outbound flies AWAY (nose right),
   return flies BACK (nose left).

   45deg/135deg was tried first, to copy the booking panel's
   plane-departure / plane-arrival pair. It failed at real size: this glyph is
   a TOP-DOWN plane and therefore nearly symmetric, so a 90deg difference in
   tilt is unreadable at 11px â€” the two pills looked identical unless zoomed.
   Font Awesome's versions work because they are SIDE views with a runway
   line, which Bootstrap Icons has no equivalent of.
   Left versus right survives any size, and for a round trip "away and back"
   is the same thing takeoff/landing was saying.

   The <i> is a flex item of an inline-flex parent, so it is already
   blockified and transform applies; an untouched inline <i> would ignore it. */
.flightc__legtag i { font-size: 0.8rem; line-height: 1; transform: rotate(90deg); }
.flightc__legtag--return i { transform: rotate(-90deg); }

/* Return is the quieter of the two â€” the outbound is what a visitor scans for
   first, and two identically loud pills give the row no hierarchy. */
.flightc__legtag--return {
  color: var(--color-text-muted);
  background: var(--surface-3);
  border-color: var(--border-soft);
}

/* ---- Pin control (compose a round trip to request) -------------------------
   A trailing FIXED track, same rule as the label: never `auto`, or every card
   sizes it to its own text and the column rhythm goes. */
.flightc__legs--labelled .flightc__leg { grid-template-columns: 140px minmax(0, 1fr) 92px 116px 122px; }
/* stretch, not flex-end: the two controls hold different text ("Pin outbound"
   vs "Pin return" vs "Pinned"), so content-sized buttons came out three
   different widths and the column looked ragged. Stretching them to the fixed
   track makes every pin control on every card identical, and the label is
   centred inside it. */
.flightc__col--pin { align-self: center; align-items: stretch; }

.flightc__pin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  padding: 5px 11px;
  border-radius: 999px;
  font: inherit;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  color: var(--color-primary);
  background: var(--surface);
  border: 1px solid #ddd0f2;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 30%, transparent);
  transition: background-color 0.15s var(--ease), border-color 0.15s var(--ease),
              color 0.15s var(--ease), transform 0.12s var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .flightc__pin:hover { background: color-mix(in srgb, var(--grad-btn-1) 10%, transparent); }
}
.flightc__pin:active { transform: scale(0.95); }
.flightc__pin:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* Pinned: filled, so a chosen leg is unmistakable at a glance down the list. */
.flightc__pin.is-pinned {
  color: #fff;
  background: var(--grad-btn-1);
  border-color: var(--grad-btn-1);
}

/* Locked while the visitor is still choosing the OTHER direction. Dimmed AND
   non-interactive â€” a control that looks live but does nothing is worse than
   one that plainly cannot be used. */
.flightc__pin:disabled { cursor: not-allowed; }
.flightc__pin.is-dimmed {
  opacity: 0.4;
  color: var(--color-text-muted);
  background: var(--surface-3);
  border-color: var(--border-soft);
}
/* The pinned one stays fully legible even though it is disabled: it is the
   answer to "which one did I pick?". */
.flightc__pin.is-pinned:disabled { opacity: 1; }

.flightc__pin i { font-size: 0.8rem; line-height: 1; }

/* C12 â€” pin feedback. The page deliberately does not move, so this outline is
   what confirms the click landed. Box-shadow only: it cannot shift layout. */
@keyframes flightcFlash {
  0%   { box-shadow: 0 0 0 3px color-mix(in srgb, var(--grad-btn-1) 45%, transparent); }
  100% { box-shadow: var(--shadow-sm); }
}
.flightc--flash { animation: flightcFlash 0.65s var(--ease); }

@media (prefers-reduced-motion: reduce) {
  .flightc--flash { animation: none; }
  .flightc__pin { transition: none; }
  .flightc__pin:active { transform: none; }
}

/* ---- Price block extras: traveller line + fare details -------------------- */
.flightc__pax {
  font-size: 0.7rem;
  color: var(--color-text-muted);
  white-space: nowrap;
}

.flightc__info {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 3px;
  padding: 0;
  font: inherit;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--color-primary);
  background: none;
  border: 0;
  cursor: pointer;
  white-space: nowrap;
}
.flightc__info:hover { text-decoration: underline; }
.flightc__info:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; border-radius: 4px; }
.flightc__info i { font-size: 0.78rem; line-height: 1; }

/* Per-result booking request. Sits under "Fare details" so the price column
   reads top to bottom as amount -> what it covers -> explain it -> act on it. */
.flightc__book {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  margin-top: 7px;
  padding: 7px 10px;
  border: 0;
  border-radius: 999px;
  font: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  cursor: pointer;
  transition: filter 0.15s var(--ease), transform 0.12s var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .flightc__book:hover { filter: brightness(1.07); }
}
.flightc__book:active { transform: scale(0.97); }
.flightc__book:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.flightc__book i { font-size: 0.76rem; line-height: 1; }

/* NOTE: there is deliberately NO min-width for phones here. An earlier version
   set 148px, reasoning that a content-sized price column would shrink the
   button. It did the opposite: it forced that column wider than a 360px
   viewport could pay for, and the whole card came apart. The card is a single
   full-width column below 600px now, so the button is already full width. */
@media (prefers-reduced-motion: reduce) {
  .flightc__book { transition: none; }
  .flightc__book:active { transform: none; }
}

/* The panel is a SIBLING of the summary, absolutely positioned against the
   card (.flightc is position: relative). Inside the summary, every click in it
   would also fire the card's expand handler. */
.flightfare {
  position: absolute;
  /* top / bottom are set by placeFarePanel() â€” it anchors to the TRIGGER and
     flips up when there is no room below, the same rule the search widget's
     pickers use. `top: 100%` was the original and it is why the panel
     disappeared on an expanded card: 100% of a card several hundred pixels
     tall is nowhere near the button that opened it. */
  right: 12px;
  z-index: 5;
  width: min(280px, calc(100% - 24px));
  padding: 12px 14px 10px;
  border: 1px solid var(--border-soft);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.14);
  text-align: left;
}
.flightfare[hidden] { display: none; }
/* Lifts the whole card while its panel is open, so the panel is never painted
   under the card that follows it in the list. */
.flightc.has-fare-open { z-index: 6; }

.flightfare__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.flightfare__close {
  padding: 2px;
  font-size: 0.7rem;
  line-height: 1;
  color: var(--color-text-muted);
  background: none;
  border: 0;
  cursor: pointer;
}
.flightfare__close:hover { color: var(--color-text); }

.flightfare__rows { margin: 0; }
.flightfare__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 0;
  font-size: 0.82rem;
  border-bottom: 1px dashed var(--border-soft);
}
.flightfare__row:last-child { border-bottom: 0; }
.flightfare__row dt { margin: 0; color: var(--color-text-muted); }
.flightfare__row dd { margin: 0; text-align: right; color: var(--color-text); }
/* The total is the answer to the question the panel was opened to ask. */
.flightfare__row.is-strong dt { color: var(--color-text); font-weight: 600; }
.flightfare__row.is-strong dd { font-weight: 700; color: var(--color-primary); font-size: 0.95rem; }

.flightfare__note {
  margin: 8px 0 0;
  font-size: 0.72rem;
  line-height: 1.4;
  color: var(--color-text-muted);
}

@media (max-width: 600px) {
  /* Anchored to the card rather than the button down here: a 280px panel
     hanging off a right-aligned trigger runs off a 320px screen. */
  .flightfare { right: 10px; left: 10px; width: auto; }
}

/* ---- Floating search summary ----------------------------------------------
   What the visitor asked for, held in view once the search itself has scrolled
   away. Fixed under the sticky header, centred on the content column, and
   sliding in rather than appearing — an element that materialises mid-scroll
   reads as a layout bug.

   It never covers a control: it is a single row at the very top of the page,
   above the results and below the header, and it is out of the flow so nothing
   under it moves when it appears. */
.flightsum {
  position: fixed;
  z-index: 45;                      /* under the header (50), over the cards */
  /* A starting value only — flight-search.js overwrites it with the MEASURED
     height of whatever is sticky at the top, so this cannot drift out of step
     with the header the way a hardcoded offset does. */
  top: 78px;
  /* Square top corners: it meets the header edge to edge, and a pill radius
     against a straight edge leaves two slivers of background that read as a
     rendering fault. The bottom stays rounded — that edge is free. */
  border-radius: 0 0 18px 18px;
  left: 50%;
  /* Slides DOWN from behind the header (z-index 45 against its 50), so it
     appears to come out of it rather than fading in over the results. */
  transform: translate(-50%, -14px);
  display: flex;
  align-items: center;
  gap: 12px;
  /* A ceiling only. The real width is set by sizeSearchSummary(), which
     measures the results column so the bar lines up with the cards under it. */
  max-width: calc(100vw - 20px);
  padding: 9px 9px 9px 16px;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.14);
  /*border: 2px solid color-mix(in srgb, var(--grad-btn-1) 50%, #fff);*/
  /*box-shadow: 0 10px 30px rgba(15, 23, 42, 0.14);*/
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
}
.flightsum.is-in {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  pointer-events: auto;
}
/* Route and meta share a wrapping block: side by side while they fit, stacked
   when they do not. NOTHING here truncates — the whole point of the bar is that
   the visitor can read what they asked for without scrolling back. */
.flightsum__text {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 1px 10px;
  min-width: 0;
}
.flightsum__text *{
  color: #ffffff;
}
.flightsum__route {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--heading);
  letter-spacing: 0.02em;
}
/* WHITE, like every other mark on this bar. The bar's background IS the brand
   gradient (var(--grad-btn-1) -> var(--grad-btn-2)), so painting the arrow in
   var(--color-primary) put a brand colour on top of itself: 1.0:1 against the
   gradient's start on the blue theme — the same colour, so the icon simply was
   not there — and 1.16:1 on the purple one, so this was invisible in both.
   It needs stating explicitly rather than left to `.flightsum__text *` above,
   because this selector is more specific and would otherwise win. */
.flightsum__route i { font-size: 0.8rem; color: #ffffff; }
.flightsum__meta {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 6px;
  min-width: 0;
  font-size: 0.8rem;
  color: var(--color-text);
  font-weight: 600;
}
.flightsum__dot { opacity: 0.55; }
.flightsum__cabin { text-transform: capitalize; }
.flightsum__btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  padding: 4px 14px;
  border: 0;
  border-radius: 999px;
  /* THE SITE'S PRIMARY-BUTTON GRADIENT, the same one every other primary
     button uses.

     This read `--gradient-primary` — a variable defined NOWHERE in the app, so
     the fallback beside it was not a fallback at all, it was the colour. And it
     was the wrong purple: #8f5abe/#6d3ea3 is the ADMIN PANEL's pair, while the
     website's buttons run #7c3aed → #4c1d95. The button has therefore never
     matched the ones around it, and could not follow a theme change either. */
  background: linear-gradient(135deg, var(--grad-btn-1, #7c3aed), var(--grad-btn-2, #4c1d95));
  color: #fff;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: filter 0.18s ease, transform 0.18s ease;
}
.flightsum__btn:hover { filter: brightness(1.07); }
.flightsum__btn:active { transform: scale(0.97); }
.flightsum__btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.flightsum__btn i { font-size: 0.85rem; line-height: 1; }

/* Phones: the dates and traveller count are the first thing to go — the route
   and the way back to the form are what must survive. */
/* Phones: the text stacks onto two lines and the pill becomes a rounded card —
   a 999px radius on a two-line box bows the text away from its own edges. The
   meta line STAYS: hiding it was the old compromise, and it hid the dates,
   which are the thing most often being checked. */
@media (max-width: 640px) {
  /* TWO ROWS, and the meta gets the whole width of the second one.

     Left as a flex row the button sat beside the text and took ~100px out of
     every line of it, so "Sun, Nov 15 – Sun, Nov 22 · 3 travellers · Premium
     Economy" broke into three ragged lines. Route and button share row one;
     the dates own row two.

     display: contents on the wrapper lets its two children become grid items
     directly, so no extra element is needed just to be a grid parent. */
  .flightsum {
    display: grid;
    /* A DEFINITE WIDTH is required for the grid — a `1fr` column measured
       against a shrink-wrapped fixed element collapses, which once squeezed
       the route to 71px and put the button on top of it. sizeSearchSummary()
       sets the real value; this is the floor it falls back to. */
    width: calc(100vw - 20px);
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "route btn"
      "meta  meta";
    align-items: center;
    gap: 3px 10px;
    padding: 9px 9px 9px 14px;
    border-radius: 0 0 16px 16px;
  }
  .flightsum__text { display: contents; }
  .flightsum__route { grid-area: route; font-size: 0.85rem; }
  .flightsum__meta { grid-area: meta; font-size: 0.72rem; }
  .flightsum__btn { grid-area: btn; margin-left: 0; padding: 7px 12px; font-size: 0.75rem; }
}

/* THE FORM ANSWERING BACK. One bounce, then still — it exists to say "here",
   not to keep asking for attention. */
@keyframes flightsearch-called {
  0%   { transform: translateY(0);     box-shadow: 0 0 0 0 color-mix(in srgb, var(--grad-btn-1) 40%, transparent); }
  35%  { transform: translateY(-8px);  box-shadow: 0 0 0 10px color-mix(in srgb, var(--grad-btn-1) 12%, transparent); }
  60%  { transform: translateY(0);     box-shadow: 0 0 0 16px color-mix(in srgb, var(--grad-btn-1) 6%, transparent); }
  80%  { transform: translateY(-3px); }
  100% { transform: translateY(0);     box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
}
#flightsearch.is-called {
  animation: flightsearch-called 0.9s cubic-bezier(0.22, 1, 0.36, 1);
  border-radius: var(--radius-lg);
}
@media (prefers-reduced-motion: reduce) {
  .flightsum { transition: opacity 0.2s ease, visibility 0.2s; transform: translate(-50%, 0); }
  #flightsearch.is-called { animation: none; }
}

/* ---- Badge tooltip --------------------------------------------------------
   The full text of a truncated badge, on tap as well as on click — `title`
   never shows on a touch screen, which is exactly where the truncation bites.

   position: fixed against the viewport, not inside the card: the card clips its
   own overflow and a bubble anchored there would be cut off by the very edge
   the badges straddle. */
.flighttip {
  position: fixed;
  z-index: 1250;
  max-width: min(320px, calc(100vw - 24px));
  padding: 9px 12px;
  border-radius: 10px;
  background: #221836;
  color: #fff;
  font-size: 0.78rem;
  font-weight: 500;
  line-height: 1.4;
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.26);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.flighttip.is-in { opacity: 1; visibility: visible; }
/* pre-line so a note can break its own lines. The duration tip puts the fly
   time and the transit time on separate rows — they are two figures, and run
   together they read as one sentence that has to be picked apart. Spaces still
   collapse (this is pre-LINE, not pre), so every other tip is unaffected: they
   contain no newlines. */
.flighttip__txt { display: block; white-space: pre-line; }
/* The badge's own words, ahead of the colon. Brighter than the explanation
   after it so the two halves read as "what it says" then "what it means". */
.flighttip__title { font-weight: 700; color: #fff; }
.flighttip { color: rgba(255, 255, 255, 0.86); }

/* Dismiss — hidden where a pointer exists, because there the bubble is already
   dismissed by moving on: another click, a scroll, Escape. */
.flighttip__close { display: none; }

@media (hover: none), (pointer: coarse) {
  .flighttip {
    /* Room for the button in the corner, so the sentence never runs under it. */
    padding-right: 32px;
  }
  .flighttip__close {
    position: absolute;
    top: 5px;
    right: 5px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.16);
    color: #fff;
    /* The bubble itself ignores the pointer so it cannot swallow a tap meant
       for the card; the button opts back in, exactly as the toast does. */
    pointer-events: auto;
    cursor: pointer;
  }
  .flighttip__close i { font-size: 0.6rem; line-height: 1; }
}
/* The pointer. Below the bubble by default; flipped above when the bubble had
   to open under its badge for want of room. */
.flighttip::after {
  content: "";
  position: absolute;
  left: 50%;
  margin-left: -5px;
  border: 5px solid transparent;
  border-top-color: #221836;
  top: 100%;
}
.flighttip.is-below::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: #221836;
}

/* ---- Pin toast ------------------------------------------------------------
   Confirmation for a leg added to (or removed from) the custom trip.

   Bottom-centre on a phone — the top of the screen belongs to the sticky filter
   bar — and bottom-left on a desktop, where the centre of a wide viewport is a
   long way from anything the visitor is looking at.

   pointer-events: none, always: it sits over the results while it fades, and a
   tap must reach the card underneath rather than the message about it. */
.flighttoast {
  position: fixed;
  z-index: 1200;
  left: 50%;
  bottom: 22px;
  transform: translate(-50%, 16px);
  display: flex;
  align-items: center;
  gap: 10px;
  /* Wide enough for the sentence AND the button beside it. At 420px the
     message wrapped to two lines inside a pill built for one, which is what
     made it look cramped; this gives it the room to stay on one line on a
     phone and leaves the viewport guard for the narrowest screens. */
  width: max-content;
  max-width: calc(100vw - 24px);
  /* Right padding leaves the dismiss button its corner, so a long message can
     never run underneath it. */
  padding: 11px 42px 11px 16px;
  border-radius: 999px;
  background: #221836;
  color: #fff;
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.35;
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.28);
  opacity: 0;
  /* The BOX ignores the pointer so it never blocks a card underneath while it
     fades; the buttons inside opt back in. */
  pointer-events: none;
  transition: opacity 0.22s ease, transform 0.22s ease;
}

/* WHERE THERE IS A REAL POINTER, the whole toast accepts one — otherwise
   mouseenter never fires on it and "do not dismiss while hovered" cannot work
   at all.

   Deliberately NOT unconditional. On a touch screen there is no hover to pause,
   and a toast that swallowed taps would sit over a card's Request Booking
   button for five seconds. `(hover: hover) and (pointer: fine)` is exactly the
   set of devices that gain something and lose nothing. */
@media (hover: hover) and (pointer: fine) {
  .flighttoast { pointer-events: auto; }
}
.flighttoast > i { font-size: 0.95rem; line-height: 1; color: #c9aef4; flex: 0 0 auto; }
.flighttoast__msg { min-width: 0; }
.flighttoast.is-in { opacity: 1; transform: translate(-50%, 0); }
.flighttoast.is-out { opacity: 0; transform: translate(-50%, 10px); }

/* "View at top" — the only part of the toast that takes a click. */
.flighttoast__btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-left: 2px;
  padding: 6px 12px 6px 8px;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  font: inherit;
  font-size: 0.8rem;
  white-space: nowrap;
  cursor: pointer;
  pointer-events: auto;
  transition: background 0.18s ease;
}
.flighttoast__btn:hover { background: rgba(255, 255, 255, 0.24); }
.flighttoast__btn:focus-visible { outline: 2px solid #c9aef4; outline-offset: 2px; }
.flighttoast__btn i { font-size: 1.05rem; line-height: 1; color: #fff; }

/* DISMISS — pinned to the top-right corner rather than placed in the flex row.

   As a flex item it would sit at the end of whichever line had room, which on a
   phone means below the message and beside the action; absolute keeps it in the
   same corner in every layout, which is where a close control is looked for.
   The container is position:fixed, so it is the containing block already. */
.flighttoast__close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  cursor: pointer;
  pointer-events: auto;          /* the box itself takes no clicks — see above */
  transition: background 0.18s ease;
}
.flighttoast__close:hover { background: rgba(255, 255, 255, 0.26); }
.flighttoast__close:focus-visible { outline: 2px solid #c9aef4; outline-offset: 2px; }
.flighttoast__close i { font-size: 0.7rem; line-height: 1; color: #fff; }

@media (min-width: 768px) {
  .flighttoast { left: 24px; transform: translate(0, 16px); max-width: min(620px, calc(100vw - 48px)); }
  .flighttoast.is-in { transform: translate(0, 0); }
  .flighttoast.is-out { transform: translate(0, 10px); }
}

/* Narrow phones: the sentence plus the button will not fit on one line, so it
   becomes two rather than shrinking the text.

   The MESSAGE keeps the icon company on the first line — `flex: 1 1 100%` sent
   the sentence to its own row and left the pin stranded in the corner, attached
   to nothing. Only the button wraps, and it keeps to the right where a button
   is looked for. */
@media (max-width: 430px) {
  .flighttoast {
    flex-wrap: wrap;
    border-radius: 18px;
    padding: 12px 14px;
    row-gap: 8px;
  }
  .flighttoast__msg { flex: 1 1 auto; }
  /* LEFT-aligned on its own row: it reads as the next step after the sentence
     above it, and a lone button hugging the right edge of a two-line toast
     looked like it belonged to the dismiss control rather than the message. */
  .flighttoast__btn { margin-left: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .flighttoast,
  .flighttoast.is-in,
  .flighttoast.is-out { transition: opacity 0.22s ease; transform: translate(-50%, 0); }
}

/* ---- Composed round trip panel --------------------------------------------
   One pinned outbound + one pinned return, which the visitor then asks us to
   book. Sits above the list, because it is what the pinning is FOR: a panel
   below the results would be off-screen at the moment it is created. */
.flightpin {
  margin: 0 0 18px;
  border: 1px solid #e0d4f4;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 26%, transparent);
  border-radius: 18px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  animation: flightpinIn 0.24s var(--ease) both;
}
@keyframes flightpinIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

.flightpin__head {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 11px 16px;
  background: #f6f2fc;
  border-bottom: 1px solid #eae0f8;
  background: color-mix(in srgb, var(--grad-btn-1) 8%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--grad-btn-1) 16%, transparent);
}
.flightpin__title {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 0.82rem; font-weight: 700; color: var(--color-primary);
}
.flightpin__steps { display: inline-flex; gap: 6px; }
.flightpin__step {
  font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
  padding: 3px 9px; border-radius: 999px;
  color: var(--color-text-muted); background: var(--surface-3);
  border: 1px solid var(--border-soft);
}
/* A done step is filled, so "what is still missing" is answerable at a glance
   without reading the slots below. */
.flightpin__step.is-done {
  color: #fff; background: var(--grad-btn-1); border-color: var(--grad-btn-1);
}
.flightpin__step.is-done::before { content: "\2713\00a0"; }

/* Pushed to the far end so it can never be mistaken for the primary action. */
.flightpin__clear {
  margin-left: auto; padding: 3px 8px;
  font: inherit; font-size: 0.72rem; font-weight: 600;
  color: var(--color-text-muted); background: none; border: 0; border-radius: 6px;
  cursor: pointer;
}
.flightpin__clear:hover { color: var(--color-danger, #b91c1c); text-decoration: underline; }
.flightpin__clear:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.flightpin__slots { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
/* The divider is on the SECOND slot, so a single-slot state has no dangling
   rule hanging off its edge. */
.flightpin__slots > * + * { border-left: 1px solid var(--border-soft); }

.flightpin__slot { padding: 12px 16px; min-width: 0; }
.flightpin__slotlabel {
  display: block; margin-bottom: 6px;
  font-size: 0.64rem; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--color-text-muted);
}
.flightpin__slot--empty { background: var(--surface-2); }
.flightpin__hint { font-size: 0.82rem; color: var(--color-text-muted); }

.flightpin__leg { display: flex; align-items: center; gap: 10px; min-width: 0; }
.flightpin__leg .flightc__legs { flex: 1; min-width: 0; }
/* The panel is half the width of the list, so the row drops to two tracks â€”
   the label and pin cells do not exist here at all. */
/* 10px BETWEEN the columns, 2px between stacked lines. One `gap` value fed
   the row axis too, which reappeared as a 10px step between the route codes
   and the stops line under them — far looser than the 1px the lines inside
   each column sit at. */
.flightpin__leg .flightc__leg { grid-template-columns: minmax(0, 1fr) auto; gap: 2px 10px; }
/* THE STOPS LINE BELONGS UNDER THE ROUTE CODES.

   Sent to `1 / -1` it became a full-width row of its own, placed below the
   TALLEST cell of the row above — the left block, which carries the times,
   the airline and the baggage. That left 37px of dead air between DAC-NRT
   and "1 stop via CAN" on a phone, reading as a layout that had failed to
   close rather than as grouping.

   Same fix as the list card on mobile: the left block spans both rows, so
   column 2 stacks duration / codes / stops with nothing in between. The
   column is auto-width and the stops text is the widest of the three, so
   duration and codes are pinned to its right edge or they would drift left
   of the codes they belong with. */
.flightpin__leg .flightc__col--when { grid-row: span 2; }
.flightpin__leg .flightc__col--dur,
.flightpin__leg .flightc__col--stops {
  grid-column: 2;
  align-items: flex-end;
  text-align: right;
}
.flightpin__leg .flightc__col--stops {
  flex-direction: row; flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0 6px;
  align-self: start;
}

.flightpin__remove {
  flex: 0 0 auto; width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: 50%; border: 1px solid var(--border-soft);
  background: var(--surface); color: var(--color-text-muted);
  cursor: pointer; font-size: 0.7rem;
}
.flightpin__remove:hover { color: var(--color-danger, #b91c1c); border-color: currentColor; }
.flightpin__remove:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.flightpin__foot {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 12px 16px;
  border-top: 1px solid var(--border-soft);
  background: var(--surface-2);
}
.flightpin__note {
  flex: 1; min-width: 240px; margin: 0;
  font-size: 0.8rem; line-height: 1.45; color: var(--color-text-muted);
}
.flightpin__cta {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 11px 20px; border: 0; border-radius: 999px;
  font: inherit; font-size: 0.88rem; font-weight: 700; color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  cursor: pointer;
  transition: filter 0.15s var(--ease), transform 0.12s var(--ease);
}
.flightpin__cta:hover { filter: brightness(1.07); }
.flightpin__cta:active { transform: scale(0.98); }
.flightpin__cta:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }
/* Disabled until both legs are pinned, and it SAYS why rather than just going
   grey â€” a dead button with no explanation is the thing people click twice. */
.flightpin__cta:disabled {
  cursor: not-allowed; filter: none; transform: none;
  color: var(--color-text-muted); background: var(--surface-3);
}

@media (max-width: 600px) {
  .flightpin__slots { grid-template-columns: 1fr; }
  .flightpin__slots > * + * { border-left: 0; border-top: 1px solid var(--border-soft); }
  .flightpin__cta { width: 100%; justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .flightpin { animation: none; }
  .flightpin__cta { transition: none; }
  .flightpin__cta:active { transform: none; }
}
.flightc__col { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.flightc__col--dur, .flightc__col--stops { text-align: left; }

.flightc__times {
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--heading);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Secondary rank: present, never competing with the times or the price. */
/* Baggage beside the airline name in the collapsed row.

   Inline, and allowed to WRAP under the carrier name rather than squeezing it:
   the carrier column is minmax(0, 1fr) and an airline like "China Eastern"
   plus two allowances does not fit on one line on a phone. Wrapping keeps the
   name intact, which is the part being scanned. */
.flightc__bags {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-left: 10px;
  vertical-align: baseline;
}
.flightc__bag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
  color: var(--color-text-muted);
}
.flightc__bag > i { color: var(--color-primary); font-size: 0.9em; line-height: 1; }

/* "None" is a fact worth showing, not an absence worth hiding â€” but it must
   not read as loudly as an included allowance. */
.flightc__bag.is-none { opacity: 0.65; }
.flightc__bag.is-none > i { color: var(--color-text-muted); }

/* The carrier line owns the wrap, so it cannot stay on a single baseline. */
.flightc__col--when .flightc__carrier { display: block; }

.flightc__carrier,
.flightc__route,
.flightc__via {
  font-size: 0.8rem;
  line-height: 1.3;
  color: var(--color-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.flightc__dur, .flightc__stops {
  font-size: 0.88rem;
  line-height: 1.25;
  color: var(--color-text);
  white-space: nowrap;
}

/* THE DURATION CARRIES A TOOLTIP, so it says so the way the rest of this card
   does — with the ⓘ the Fare details control uses. That replaced a dotted
   underline: the underline was the generic "more behind this word" hint, but
   the icon is the hint THIS interface already uses for exactly this, and two
   different affordances for one idea is one too many.

   Deliberately quiet — the number is what the eye is scanning for, and the
   marker must not compete with it. */
/* LAYOVER AND OVERNIGHT FLAGS.

   Small chips rather than more prose: the rows they sit on are already a
   sentence ("6h 15m layover in Kuala Lumpur"), and a second clause buried in it
   would be read as part of the first. A chip is scannable down the timeline,
   which is how someone compares two itineraries.

   Two colours, because they are not the same kind of statement. "Long layover"
   is a caution — something to plan around — and takes the amber the cabin
   substitution badge already uses. "Overnight" is a neutral fact about the
   flight, so it stays in the panel's own ink and is marked only by its moon. */
.flightd__flag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  white-space: nowrap;
  vertical-align: baseline;
}
/* THE ICON IS RED IN BOTH CHIPS, whatever the chip's own ink.

   The glyph is the part that has to be spotted while scanning a timeline; the
   words explain it once you have. Red is what this panel already uses for
   "something here is not what you would assume" — the date-change marker and
   the early-departure flag — so all three read as one family, while each chip
   keeps its own ground and text colour. */
.flightd__flag .bi {
  font-size: 0.9em;
  line-height: 1;
  color: #d93025;
}

.flightd__flag--warn {
  color: #8a5a00;
  background: #fff7e6;
  border: 1px solid #f3d9a4;
}
.flightd__flag--night {
  color: var(--color-text-muted);
  background: color-mix(in srgb, var(--grad-btn-1) 8%, #fff);
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 16%, #fff);
}

/* THE EARLY-DEPARTURE FLAG — red, because it is a warning, not a detail.

   The only red on a card besides the date-change marker, and they say the same
   kind of thing: this is not what you would assume. It sits in the details
   timeline beside the FIRST stop's time — the moment the traveller has to be at
   the airport.

   OUT OF FLOW, anchored to the time cell. In flow it had nowhere to go: the
   cell is a fixed 3.4em (49px) and the digits already take 36px, so the icon
   wrapped onto a second line INSIDE the cell — which grew the row from 23px to
   46px and left the icon sitting 11px below the time it belongs to. As a
   sibling instead of a child it pushed the airport column from 159px to 191px
   on that row alone. Absolute does neither.

   Anchored to .flightd__time rather than to the row, so the vertical centring
   is against the TIME in both layouts — below the breakpoint the row also holds
   the airport on a second line, and centring on the row would drop the icon
   into the gap between them.

   ANCHORED TO THE DIGITS, not to the cell. The digits are NOT all one width —
   "06:00" measures 41px where "02:10" measures 36px, because this face has no
   tabular figures whatever `font-variant-numeric` asks for — so any fixed
   offset from the cell lands on the last digit of the wider times. Hanging it
   off the digits' own box puts it 5px past them whatever they say, and works
   unchanged in both layouts: the wide cell is a fixed track with slack to
   spare, the compact one hugs the digits. */
/* Only present on a flagged stop — see flight-search.js.

   inline-BLOCK, not inline. An absolutely positioned child resolves its
   percentages against its containing block, and an INLINE relative parent does
   not give it a single clean box to measure: `left: calc(100% + 5px)` computed
   to 23.9px against a 36px span instead of 41px, putting the icon back on top
   of the digits. inline-block gives it a real box without changing the line. */
.flightd__clock {
  position: relative;
  display: inline-block;
}

/* THE WHOLE CLOCK IS THE HOVER TARGET — and looks exactly as it did.

   A 14px icon is a poor thing to aim at, and the advice is about this
   departure, so the time answers for it too. Deliberately NO colour change and
   NO underline on the digits: the red belongs to the icon, which is the
   warning; a red or underlined time would read as the time itself being wrong.
   The cursor is the only signal, and it is enough beside a visible icon. */
.flightd__clock[data-badge-tip] { cursor: help; }

/* The advice, for screen readers. The icon is aria-hidden (decoration) and the
   bubble is built on hover, so without this the warning would exist only for
   people using a pointer. */
.flightd__early-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.flightd__early {
  position: absolute;
  left: calc(100% + 1px);
  top: 50%;
  transform: translateY(-50%);
  font-size: 1em;
  line-height: 1;
  color: #d93025;
  cursor: help;
}
.flightd__early:hover { color: #b3261e; }

.flightc__dur[data-badge-tip] {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  cursor: help;
}
.flightc__dur[data-badge-tip] > .bi {
  font-size: 0.78em;
  line-height: 1;
  /* Baseline alignment puts an icon's box on the text baseline, which sits it
     slightly low against digits; a hair of lift reads as level. */
  transform: translateY(0.5px);
  color: var(--color-text-muted);
  opacity: 0.75;
}
.flightc__dur[data-badge-tip]:hover > .bi {
  color: var(--color-primary);
  opacity: 1;
}
/* Nonstop is the thing people scan for, so it is the one secondary value
   allowed any emphasis. */
.flightc__stops.is-direct { color: var(--color-primary); font-weight: 600; }
.flightc__via:empty { display: none; }
.flightc__dash { color: var(--color-text-muted); font-weight: 400; margin: 0 5px; }
.flightc__offset { font-size: 0.62em; color: var(--color-primary); margin-left: 2px; }

.flightc__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  color: var(--color-text-muted);
  font-size: 0.9rem;
  min-width: 0;
}

.flightc__price { text-align: right; display: block; }
.flightc__amount {
  display: block;
  /* .deal__price â€” brand purple at 1.5rem/600. The first pass used --heading
     at 700, which is the single most visible reason the card looked alien.
     Largest thing on the row, as in every flight UI worth copying. */
  font-size: 1.35rem;
  line-height: 1.2;
  font-weight: 600;
  color: var(--color-primary);
  font-variant-numeric: tabular-nums;
}

/* DARK MODE: the price reads like the flight times — var(--heading), which is
   #f2effa here. It was var(--color-primary) (#9a6ac2), a mid purple that sat
   quieter than the times right next to it, even though the price is the thing
   the row is scanned for. Light mode keeps the purple: on a white card it is
   the accent, not a legibility problem. */
[data-theme="dark"] .flightc__amount {
  color: var(--heading);
}
.flightc__qualifier {
  display: block;
  color: var(--color-text-muted);
  font-size: 0.75rem;
  line-height: 1.3;
}

/* The actions wrapper exists for the phone layout (see the media block). Here
   it must change nothing at all: display:contents makes the booking button a
   direct child of the price block for layout, exactly as before it was wrapped. */
.flightc__actions { display: contents; }
.flightc__toggle { display: none; }

.flightc__chev {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--color-text-muted);
  border-bottom: 2px solid var(--color-text-muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--transition);
}
.flightc.is-open .flightc__chev { transform: rotate(-135deg) translate(-2px, -2px); }

/* The right-hand badge row. Both badges used to be positioned individually at
   right:14px, which worked only while at most one could show; the cabin badge
   is on EVERY card, so it would have sat exactly on top of "Different airport".
   A flex row lets any number of them coexist and wrap. */
/* THE ROW ACROSS THE CARD'S TOP EDGE.

   One positioned row holding both sides, rather than a tag pinned left and a
   badge group pinned right. The old arrangement reserved a guessed 130px for
   the tag (`max-width: calc(100% - 130px)`) and a longer one — "Cheapest with a
   convenient connection" — ran straight underneath the badges.

   pointer-events are off on the row so it cannot intercept a click meant for
   the card beneath it; each badge switches them back on for its own tooltip. */
.flightc__toprow {
  position: absolute;
  top: -12px;
  left: 14px;
  right: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  pointer-events: none;
}
.flightc__toprow > * { pointer-events: auto; }

.flightc__badges {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  /* The facts about the ticket. They take what they need and never shrink. */
  flex: 0 0 auto;
}
/* ONE BORDER FOR ALL THREE TOP BADGES. They sit on the same card edge, and
   before this they did not agree: the provider tag had no border at all, the
   warning had a 1px 18% hairline and the cabin badge a 1px 16% one — three
   near-identical chips, each outlined differently.

   Plain value first, color-mix second: that is this file's own fallback
   pattern (color-mix needs iOS Safari 16.2, and without the literal the badges
   would lose their outline entirely on an older iPhone). #be9df6 IS the mix —
   #7c3aed at 50% against #fff — so the two agree wherever both are understood.

   Only the WIDTH and the default colour live here. `.is-substitute` and the
   dark-mode rule override `border-color` alone, so they keep their own ink and
   pick the 2px up from this rule. */
.flightc__tag,
.flightc__warn,
.flightc__cabinbadge {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
  cursor: pointer;
  border: 2px solid #be9df6;
  border: 2px solid color-mix(in srgb, var(--grad-btn-1) 50%, #fff);
}
/* The provider's own marketing, and the one thing here that may be cut: it
   yields the row's leftover width and ellipses. The full text is one tap away
   — see the badge tooltip. */
.flightc__tag {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
/* .pill, the theme's badge: brand-tinted ground with primary text â€” not a
   solid fill, which nothing else on the site uses. */
.flightc__tag {
  color: var(--color-primary);
  background: #efe9fd;
  background: color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
  letter-spacing: 0.06em;
}
/* A caution, not a promotion â€” so it keeps a neutral ground rather than the
   brand tint .flightc__tag uses, but takes the same tinted hairline so the
   two badges sit on the same card without looking unrelated. */
.flightc__warn {
  background: var(--surface-3);
  color: var(--color-text);
  letter-spacing: 0.06em;
  cursor: pointer;
}

/* The cabin being SOLD, on every card.
   Two states carry the whole point of the badge:
     default      â€” the cabin that was searched for. Stated, not shouted.
     is-substituteâ€” a cabin the visitor did NOT ask for. The provider returns
                    these silently and in bulk (a measured premium_economy
                    search came back 71 Business to 11 premium economy), so
                    this one has to be legible at a glance against a wall of
                    otherwise identical cards. */
.flightc__cabinbadge {
  background: color-mix(in srgb, var(--grad-btn-1) 8%, #fff);
  color: var(--color-text);
}

/* DARK MODE: the right-hand cabin badge uses the SAME colour system as the
   left-hand provider tag (.flightc__tag) — brand-tinted chip, primary-coloured
   text. Its own `color: var(--color-text)` resolves to a LIGHT ink in dark
   mode, which it then painted onto a near-white chip: light-on-light, and the
   badge effectively vanished. The tag never had that problem because it names
   its colour explicitly rather than inheriting the body ink.
   Placed immediately after the rule it overrides, so source order can never
   work against it. Percentages are one step up from the light values to keep
   the chip readable against the darker card behind it. */
[data-theme="dark"] .flightc__cabinbadge {
  color: var(--color-primary);
  background: color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
  border-color: color-mix(in srgb, var(--grad-btn-1) 22%, #fff);
}
.flightc__cabinbadge.is-substitute {
  background: #fff7e6;
  color: #8a5a00;
  border-color: #f3d9a4;
}

/* The substitution notice above the list. Amber like the badge it explains â€”
   the two are one message, and giving them different colours would read as two
   unrelated warnings. */
.flightl__cabinnote {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  flex-wrap: wrap;
  margin: 0 0 14px;
  padding: 11px 14px;
  border-radius: var(--radius-md, 10px);
  background: #fff7e6;
  border: 1px solid #f3d9a4;
  color: #7a4f00;
  font-size: 0.85rem;
  line-height: 1.45;
}
.flightl__cabinnote i { flex: 0 0 auto; margin-top: 2px; font-size: 0.95rem; }
.flightl__cabinnote span { flex: 1 1 220px; min-width: 0; }
.flightl__cabinnote-btn {
  flex: 0 0 auto;
  padding: 5px 12px;
  border-radius: 999px;
  border: 1px solid #d9a441;
  background: #fff;
  color: #8a5a00;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.flightl__cabinnote-btn:hover { background: #8a5a00; color: #fff; border-color: #8a5a00; }

/* Narrow screens: the two floating pill rows have to share one line.
   Before the cabin badge existed the right-hand row was almost always empty,
   so the promo tag on the left could have the whole width; now that every card
   carries a cabin badge, a long tag ("Cheapest with a convenient layover")
   runs into it â€” measured at 390px, overlapping by 20px on that card.

   The TAG is the one that yields, and deliberately: it is a nicety, while the
   cabin badge is what stops a Business fare being read as the premium economy
   somebody searched for. The reserve is sized for the worst case, a cabin
   badge AND "Different airport" side by side.

   nowrap on the row matters as much as the reserve â€” the row hangs at
   top:-9px, so letting it wrap to a second line would push it down over the
   card's first content row instead of overlapping the tag. */
@media (max-width: 600px) {
  .flightc__badges {
    flex-wrap: nowrap;
    max-width: calc(100% - 40px);
  }
  .flightc__tag {
    max-width: calc(100% - 215px);
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* ---- Expanded itinerary -------------------------------------------- */
.flightd {
  border-top: 1px solid color-mix(in srgb, var(--grad-btn-1) 12%, #fff);
  padding: 2px 18px 10px;
}
/* THE DIVIDER SITS MIDWAY BETWEEN THE TWO LEGS.
   margin-top and padding-top are equal, which looks symmetric in the CSS and
   was not on the page: the heading that follows carries its own 6px top margin,
   so the real gap was 8px above the line and 15px below it and the rule read as
   a mis-drawn border. The heading's margin is zeroed HERE — only where it
   follows a divider — so the first leg's title keeps its spacing from the top
   of the panel.

   THE NUMBERS ARE NOT EQUAL BECAUSE THE GAPS ARE NOT SYMMETRIC. Two things sit
   below the line and nothing sits above it: the 1px border itself, and the
   heading's 1px half-leading — the empty strip a line box leaves above its
   glyphs. Equal 8px/8px therefore measured 8px of white above the rule and 10px
   below, which is what the eye reads as "not centred". margin-top carries those
   2px so the INK is evenly spaced. Verified by measuring the heading's glyph
   box (a Range rect), not its element box; box-to-box measurement says it is
   centred when it is not. */
.flightd__leg + .flightd__leg { margin-top: 10px; padding-top: 8px; border-top: 2px dashed color-mix(in srgb, var(--grad-btn-1) 40%, #fff); }
.flightd__leg + .flightd__leg > .flightd__title { margin-top: 0; }
/* NOT UPPERCASE any more. The heading used to be three-letter codes — "DAC TO
   HND" — where caps read as a label. It now carries city names, and
   "DHAKA (DAC) TO TOKYO (HND)" shouted a full line of proper nouns and made
   the codes in brackets hard to pick out. Sentence case, with the letter
   spacing dropped back to normal (0.06em exists tof open up capitals). */
/* A FILLED PILL, not a coloured caption. These two headings are the only
   labels in an open panel — everything else is flight data — and as plain text
   they read as another data row rather than as the thing that splits the
   itinerary in two. The gradient is the one the primary controls already use
   (.btn-login, the search tab, the Search button), so the panel does not
   introduce a fifth purple of its own.

   inline-block: the pill is as wide as its route, not the full panel. */
.flightd__title {
  display: block;
  font-size: 0.88rem;
  font-weight: 600;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  padding: 5px 10px;
  border-radius: 8px;
  margin: 6px 0 8px;
}
/* Baggage allowances. Pills on one wrapping row: the flat "Checked: 1 x 23 kg
   . Cabin: 1 x 5 kg" line read as a footnote and buried the only two numbers
   in the panel a traveller actually scans for.

   Text size is unchanged at 0.85rem â€” the density comes from the layout, not
   from shrinking anything. */
.flightd__bags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
}
.flightd__bag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 11px 3px 9px;
  border-radius: 999px;
  /* --surface-2, not a color-mix onto #fff: this panel has a dark theme, and
     the sibling .flightd__layover is already theme-aware the same way. */
  background: var(--surface-2);
  border: 1px solid color-mix(in srgb, var(--color-primary) 15%, var(--surface));
  font-size: 0.85rem;
  /* Tightened from 1.4 so the pills cost no more vertical room than the flat
     line they replaced. This is line-height, NOT font-size â€” the text is still
     0.85rem, same as before. Measured: flat 29.8px vs pills 31.7px. */
  line-height: 1.3;
  color: var(--color-text);
}
.flightd__bag > i {
  color: var(--color-primary);
  font-size: 0.95em;
  line-height: 1;
}
.flightd__bag-k { color: var(--color-text-muted); }
/* The number carries the weight â€” it is what the eye is hunting for. */
.flightd__bag-v { font-weight: 600; }

/* "Not included" means the opposite of the other pills, so it must not look
   like them: no fill, dashed edge, muted throughout. */
.flightd__bag.is-none {
  background: transparent;
  border-style: dashed;
  border-color: color-mix(in srgb, var(--color-text-muted) 35%, var(--surface));
  color: var(--color-text-muted);
}
.flightd__bag.is-none > i,
.flightd__bag.is-none .flightd__bag-v { color: var(--color-text-muted); }
.flightd__bag.is-none .flightd__bag-v { font-weight: 500; }

.flightd__tl { list-style: none; margin: 0; padding: 0 0 0 18px; position: relative; }
/* The spine. Drawn on the list, not per item, so it cannot break between
   segments. */
.flightd__tl::before {
  content: "";
  position: absolute;
  left: 4px; top: 8px; bottom: 8px;
  width: 2px;
  background: var(--border-soft);
}
.flightd__stop { position: relative; display: flex; gap: 12px; font-size: 0.9rem; padding: 0; }
/* `top` IS TIED TO THE ROW'S HEIGHT and has to move whenever the row's padding
   does. The dot is 14px tall (10px + 2px of border each side); the row is 23px
   with the padding at 0, so 5px centres it on the time beside it.

   History, because it has now happened twice: 9px was right for the original
   29px row, 6px for the 25px row, 5px for this one. Left alone it sits low
   against EVERY time in the panel — 3px the first time, 1px the second. If the
   stop padding or font size changes again, re-measure. */
.flightd__stop::before {
  content: "";
  position: absolute;
  left: -18px; top: 5px;
  width: 10px; height: 10px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--color-primary);
}
/* THE DATE-CHANGE MARKER — red, and red on purpose.

   It is the one thing in this panel that contradicts what the reader has
   assumed: every other line is a time, and times imply "same day" until told
   otherwise. Nothing else in the timeline is red, so it cannot be mistaken for
   part of the itinerary.

   It sits on the time rail, in the time column's own width, so the row below it
   keeps its alignment — the stop becomes two lines (date over time) rather than
   the date pushing the time sideways. Shown at EVERY width; a phone is where a
   missed date change costs the most. */
/* WIDE SCREENS PUT THE DATE ABOVE ITS TIME — AND TAKE NO SPACE FOR IT.

   Three arrangements were tried. Beside the time it sat between the time and
   the airport and pushed the airport rightwards on that row alone, breaking the
   column every other stop keeps. Stacked in a two-row grid it stopped pushing
   anything sideways but made the marked row taller than its neighbours, so the
   timeline gained a step. Taken out of flow it does neither: the row keeps the
   exact height and alignment it has with no marker at all, and the date hangs
   in the leading above the time.

   The stop is already `position: relative` for its own timeline dot, so this
   anchors to the row rather than the panel. `left: 0` puts it on the time
   column's own edge, so it lines up with the digits beneath it.

   ONLY THE MARKED ROW (.is-dated, set in flight-search.js). Below the
   breakpoint the airport already wraps to its own line, leaving room on the
   time's — there the date sits beside it and this does not apply. */
@media (min-width: 895px) {
  /* The fixed 3.4em track is 13px wider than the digits, so the icon sits in
     that slack rather than past the cell — past it would reach the airport. */

  .flightd__stop.is-dated .flightd__daychg {
    position: absolute;
    left: 0;
    bottom: 100%;
    font-size: 0.68rem;
  }

  /* A DEPARTURE'S DATE HANGS BELOW ITS TIME INSTEAD.

     Above a departure is the layover band — a filled strip the date would sit
     on top of. Below it is the segment row, whose 3.4em indent holds the time
     column open for exactly this. An arrival keeps the default: above it is
     that same empty column, and below it are the baggage pills, which start at
     the left edge. */
  .flightd__stop.is-dated .flightd__daychg--departure {
    bottom: auto;
    top: 100%;
  }
}

.flightd__daychg {
  /* Shared by both layouts. Where it SITS differs: above the time on a wide
     screen (see the media query above), beside it below the breakpoint, where
     the airport already occupies its own line and there is room on the time's.
     `flex: 0 0 auto` keeps it off the time column's fixed track. */
  flex: 0 0 auto;
  align-self: center;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.1;
  color: #d93025;
  white-space: nowrap;
}
/* The arrow that ties the date to its time. Sized from the date's own font so
   the two scale together, and nudged up a hair because the glyph's ink sits
   low in its box.

   MIRRORED, because it leads the date instead of trailing it. Both glyphs are
   drawn travelling left-to-right before they turn; ahead of the words that
   curve doubles back through them. Flipped, it starts at the words and turns
   away toward the time, which is the direction the reader's eye has to go. */
.flightd__daychg-arrow {
  display: inline-block;
  font-size: 0.95em;
  line-height: 1;
  /*margin-right: 3px;*/
  /*vertical-align: -0.05em;*/
  /*transform: scaleX(-1);*/
}

.flightd__time { flex: 0 0 3.4em; font-weight: 600; color: var(--heading); font-variant-numeric: tabular-nums; }
.flightd__place { min-width: 0; }
/* The segment row carries the operating carrier's mark before its name, at
   EVERY width — it used to be phone-only, so the widest screens were the only
   place it could not be seen. A flex row rather than inline: an aircraft name
   can run to several lines, and the mark has to stay level with the FIRST of
   them rather than float beside the middle one. */
.flightd__ride {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 0 0 2px 3.4em;
  margin-left: 12px;
  color: var(--color-text-muted);
  font-size: 0.85rem;
}
.flightd__logo {
  width: 24px;
  height: 24px;
  border-radius: 6px;
  margin-bottom: 0;   /* see .flightc__logo--tag */
}
.flightd__logo img { width: 20px; height: 20px; border-radius: 5px; }
.flightd__logo .flightc__mono { font-size: 0.55rem; }
.flightd__ride-txt { min-width: 0; }
.flightd__layover {
  margin: 5px 0 5px -15px;
  padding: 5px 12px;
  background: var(--surface-3);
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

/* 894.98px, and the number is MEASURED rather than chosen. RE-MEASURED on
   2026-09-12: it was 739.98px.

   The desktop card is five leg tracks (label | times | duration | stops | pin)
   inside a four-track summary (logo | legs | price | chevron). Every track is
   min-width:0, so when the fixed ones no longer leave room the flexible times
   column does not push the card wider — it silently collapses to 0 and its
   content spills across the duration beside it. That is the failure this
   breakpoint exists to prevent, and it is invisible in the CSS: the grid still
   "works", it just renders on top of itself.

   WHY THE NUMBER MOVED. Giving each leg its own carrier mark widened the label
   track from 104px to 140px, so the fixed tracks plus gaps now need 518px
   instead of 482px, and the collapse starts 155px further up. Measured by
   binary-searching the viewport width at which every .flightc__col--when still
   holds its own content: clean at 895px, broken at 894px.

   ANY CHANGE TO A FIXED TRACK INVALIDATES THIS NUMBER. Re-run
   tests/manual/flight-card-layout-check.cjs, which sweeps the widths and fails
   on exactly this collapse. */
/* VERY NARROW PHONES (iPhone SE 1st gen and the like).
   At 320px the leg's times line needs up to 128px and its column is 118px, so
   it clips — and what gets cut is the "+1" day-offset superscript, the one
   thing on that line a traveller cannot afford to miss ("arrives the NEXT
   day"). Pre-existing, not introduced with the per-leg marks: nothing here
   touches the compact card's time column.

   THE SIZE WAS MEASURED, by sweeping it against every leg on a real result
   page at 320px: 1.05rem clipped 31 legs of 72 (worst 10px), 1rem clipped 21,
   0.98rem still clipped 21, 0.95rem was the first to clip none. 0.93rem is
   taken rather than 0.95rem purely for headroom — every offset in that sample
   was "+1", and "+2" is wider.

   340px and up fit at the normal size, so this only trims the last 20px of the
   range, and only the times line. */
@media (max-width: 339.98px) {
  .flightc__times { font-size: 0.93rem; }
}

@media (max-width: 894.98px) {
  /* SINGLE COLUMN below the breakpoint, and it has to be single column.
     The previous version kept a three-track summary (logo | legs | price) with
     a multi-track leg grid nested inside the middle one. Once each leg also
     carried a pin control and the price block a Request Booking button, the
     minimum content width of those tracks exceeded the viewport â€” and because
     every track is min-width:0, grid shrinks them without complaint rather
     than reporting anything. The result was silent: times printed over
     durations, pin buttons sat OUTSIDE the card to the left, and the booking
     button ran past the right edge.
     Nothing here may reintroduce a fixed or minimum width that the narrowest
     supported viewport (320px) cannot pay for. */
  .flightc__summary {
    /* Two tracks, used only by row 1: the carrier mark on the left and the
       expand chevron on the right. Everything else spans both. */
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 12px;
    /* Extra headroom: .flightc__tag sits at top:-9px and .flightc__badges at
       top:-12px, both straddling the card's edge. Raised with the desktop rule
       so the price is clear of the cabin badge at every width. */
    padding: 24px 12px 14px;
  }

  /* The two legs are separate journeys and now carry a control each, so they
     get a rule between them instead of an 8px gap that read as one block. */
  .flightc__legs { grid-column: 1 / -1; gap: 0; }
  .flightc__leg + .flightc__leg {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border-soft);
  }
  /* justify-self, or it stretches across the whole column. Slightly smaller
     and pulled tight to the leg beneath it: on its own full-width row a 44px
     mark with a 12px gap under it read as an orphaned block rather than the
     card's header. */
  .flightc__logo { grid-column: 1; grid-row: 1; justify-self: start; width: 38px; height: 38px; margin-bottom: -4px; }
  .flightc__logo img { width: 32px; height: 32px; }
  /* THE EXPAND AFFORDANCE SITS BESIDE THE BOOKING BUTTON.

     It used to have the top-right corner, opposite the carrier mark. Then the
     mark moved into the legs, which left the chevron alone on a row of its own
     — about 23px of empty band above the first leg, paid for by nothing.

     Down here it shares the footer row with Request Booking: the price block
     takes column 1 and the chevron column 2, so the button ends where the
     price does and the chevron sits just past it. Nothing overlaps, and the
     card's last row now reads "act on it, or open it".

     Still decorative (aria-hidden): the whole summary is the button, so this
     never has to be a tap target in its own right. */
  .flightc__legs { grid-row: 1; }
  /* FULL WIDTH. While the chevron held column 2 of this row, the price block
     stopped ~40px short of the card's edge — so "Round trip" and the traveller
     count ranged right to a line 40px inside every other right-aligned thing on
     the card (the duration, the route, the stops). The toggle lives inside the
     block now, so the block gets the whole width back and all the right-hand
     text shares one edge. */
  .flightc__price { grid-column: 1 / -1; grid-row: 2; }

  /* THE EXPAND AFFORDANCE MOVES INTO THE FOOTER, AND SAYS WHAT IT DOES.

     It used to hold the top-right corner. Once the carrier mark moved into the
     legs that left it alone on a row of its own — about 23px of empty band
     above the first leg, paid for by nothing.

     The bare tick is dropped here for a labelled control: on a phone the
     chevron was the only thing saying the card opens at all, and a 10px mark is
     a poor way to say it. "Show Details" / "Hide Details" says it in words, and
     the label doubles as the state readout.

     It sits in a flex row WITH the booking button rather than in a grid cell
     beside it, because stretched flex siblings are the same height BY
     DEFINITION — the previous version took 28px from a measurement of the
     button and was wrong as soon as the button's font metrics differed. There
     is nothing left to keep in step.

     Still aria-hidden: the whole summary is the button. */
  .flightc__chev { display: none; }
  .flightc__actions {
    display: flex;
    align-items: stretch;
    gap: 8px;
    grid-column: 1 / -1;
    margin-top: 8px;
  }
  .flightc__toggle {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 7px 14px;
    border-radius: 999px;
    background: var(--surface-3);
    color: var(--color-text-muted);
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
  }
  .flightc__toggle-hide { display: none; }
  .flightc.is-open .flightc__toggle-show { display: none; }
  .flightc.is-open .flightc__toggle-hide { display: inline; }
  .flightc__toggle-arrow {
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform var(--transition);
  }
  .flightc.is-open .flightc__toggle-arrow { transform: rotate(-135deg) translate(-2px, -2px); }

  .flightc__legs { width: 100%; }
  /* ONE VERTICAL GAP, USED EVERYWHERE IN THE LEG.

     Six places set spacing inside this row — the grid's own row gap, the two
     .flightc__col stacks, and the carrier line that holds the baggage — and
     they disagreed (1px inside a column, 2px between rows, 10px beside the
     name). The result was a left column with its lines almost touching and a
     right column drifting out of step with it.

     They are all 6px now, so times → airline → baggage on the left lines up
     with duration → route → stops on the right, and every gap in the card is
     the same gap. Change it here and both columns move together. */
  .flightc__leg { grid-template-columns: minmax(0, 1fr) auto; row-gap: 6px; }
  .flightc__col { gap: 6px; }
  /* The labelled (round-trip) variant must be restated: its selector is
     (0,2,0) against this block's (0,1,0) and would otherwise keep the desktop
     five-track rule here on specificity alone. Its second track is set with
     the pin control further down — that is what makes the two pins match. */

  /* Row 1 is the direction label with its pin control opposite; row 2 the
     times and duration; row 3 the stops. Pairing the label with its own pin
     is what makes the control obviously belong to THAT leg â€” on its own
     full-width row it read as an action on the whole card.
     This pairing was tried once before and collapsed, but that was while the
     summary still had three columns and the legs were squeezed into the
     middle one. The summary is a single full-width column now, so the row has
     the whole card to spend. Measured at 320px: label 104 + pin 118 + gap 10
     = 232 of ~294 available.
     .flightc__col is a flex COLUMN, so each needs its own cross-axis
     alignment or its child stretches to the track. */
  .flightc__col--tag { grid-column: 1; grid-row: 1; align-self: center; align-items: flex-start; }
  /* TWO STACKS, NOT THREE RAGGED ROWS.

     What made this card look busy on a phone was that the leg's three blocks
     each claimed a different share of the width: times and carrier on the
     left, duration and route on the right, and then "1 stop via KUL" alone on
     a full-width row underneath, left-aligned under nothing in particular.

     Now every fact reads down one of two columns — when/who on the left,
     how-long/where/how-many-stops on the right — with one flush edge each:

         [OUTBOUND]                    [Pin outbound]
         22:50 – 08:25+2                     30h 35m
         (logo) Batik Air Malaysia           DAC–NRT
         35 kg  7 kg                  1 stop via KUL

     The left block SPANS both content rows so the stops line sits directly
     under the route codes rather than being pushed down to clear the baggage
     line — grid distributes the spanned height across the two rows instead of
     stacking them. */
  /* Columns are named; ROWS are not. Saying "row 2" here would be saying
     "below the label row" — true on a round trip, false on a one-way card,
     which has no label row and would get a 0px phantom row plus its 6px gap
     above the times. Only the SPAN is declared, and auto-placement puts the
     pair in whatever row is actually free. */
  .flightc__col--when { grid-column: 1; grid-row: span 2; }
  .flightc__col--dur { grid-column: 2; }
  .flightc__col--stops {
    grid-column: 2;
    flex-direction: row;
    /* Wraps rather than overflowing: column 2 is a fixed 122px track (that is
       what keeps the two pin buttons identical), and "2 stops via DOH, KUL"
       is wider than that. */
    flex-wrap: wrap;
    justify-content: flex-end;
    text-align: right;
    gap: 0 6px;
    align-self: start;
  }
  .flightc__via { white-space: normal; }

  /* EQUAL WIDTHS ON A PHONE TOO.

     Desktop gets this for free: both pairs are stretched into FIXED tracks
     (104px label, 122px pin), so "OUTBOUND"/"RETURN" and "Pin outbound"/"Pin
     return"/"Pinned" come out identical whatever their text measures. Here
     each leg is its OWN grid, so nothing could size against the leg above it
     and all four came out content-sized and visibly ragged.

     The PIN keeps the desktop mechanism rather than a guessed minimum: a fixed
     second track, stretched into. A min-width would have had to be at least
     the widest label, and this block widens the button's padding (13px vs
     11px) — so the number would have been a guess that fails the moment a
     label or a font changes. A track cannot fail that way.

     Affordable at the width this block was measured against: at 320px the card
     gives ~296px, and 122 + 12 gap = 134 leaves 162px for a times column whose
     widest content ("19:05 – 19:35+1") is ~110px. Column 1 is minmax(0, 1fr)
     and can still shrink, so nothing can overflow the card. */
  .flightc__legs--labelled .flightc__leg { grid-template-columns: minmax(0, 1fr) 122px; }
  .flightc__col--pin { grid-column: 2; grid-row: 1; align-self: center; align-items: stretch; }
  /* The LABEL shares column 1 with the times, so it has no track of its own to
     stretch into and takes the one tool left.

     112px, not the desktop track's 104px. Measured in Chrome at this font:
     MEASURED WHEN THE LABEL READ "OUTBOUND": that word was 105.19px and
     "RETURN" 85.13px — so a 104px MINIMUM left the
     outbound pill at its natural 105 and the return at 104, one pixel apart
     and still visibly unequal. (Desktop gets away with 104 because it
     STRETCHES both into the track, which squeezes the outbound instead.)

     A minimum with headroom is the right tool here rather than a fixed width:
     both pills land exactly on it, and if a device's font ever renders
     a label wider than 112px the pill grows instead of clipping its own
     label. */
  .flightc__legtag { min-width: 112px; }
  /* No min-width on the pin — the track above sizes it. A comfortable tap
     height, since the control is no longer full width. */
  .flightc__pin { min-width: 0; min-height: 34px; padding-left: 13px; padding-right: 13px; }

  /* TOTAL TIME AND AIRPORT CODES sit under the pin control, in the same auto
     track — which the pin makes wider than either of them. Left-aligned they
     floated in the middle of the card with a ragged right edge against the
     button above; ranged right they line up with the pin, the price and the
     card's own edge. */
  .flightc__col--dur { text-align: right; align-items: flex-end; }

  /* THE CARRIER MARK MOVES INTO THE LEG. The header's copy is hidden (it is
     the :not() below), and each leg carries its own beside the airline name —
     which is where a phone reader is already looking, and correct even when
     the two legs are flown by different airlines. */
  .flightc__logo:not(.flightc__logo--leg):not(.flightd__logo) { display: none; }
  /* The desktop mark beside the pill stands down here — the one beside the
     airline name below says the same thing, closer to where a phone reader is
     already looking. The cell goes back to stacking with it. */
  .flightc__logo--tag { display: none; }
  /* LEFT-ALIGNED HERE, and it needs this selector to say so.

     The desktop rule above is `.flightc__leg .flightc__col--tag` (0-2-0) and
     sets `align-items: center`, which outranks the 0-1-0 override a few lines
     up — so the pill came out centred over its leg instead of sitting at the
     left edge with the times and the airline name beneath it. Matching the
     desktop selector's specificity is what lets flex-start win.

     `flex: 0 0 auto` on the pill for the same reason: the desktop rule grows it
     to fill the track (so OUTBOUND and RETURN match), but this cell is a COLUMN
     here, where growing is vertical. */
  .flightc__leg .flightc__col--tag {
    flex-direction: column;
    gap: 1px;
    align-items: flex-start;
  }
  .flightc__leg .flightc__col--tag .flightc__legtag { flex: 0 0 auto; }
  .flightc__col--when .flightc__logo--leg {
    display: grid;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    border-radius: 5px;
    /* THE HEADER MARK PULLS ITSELF 4px INTO THE ROW BELOW (see .flightc__logo
       above — deliberate, to tighten the card header). Inherited here that
       negative margin took 4px off this line's height, so a 20px mark sat in
       a 16px row and hung into the baggage line beneath it. Measured: set the
       mark to 40px and the row came out 36px, every time, 4px short. */
    margin-bottom: 0;
  }
  .flightc__col--when .flightc__logo--leg img { width: 17px; height: 17px; border-radius: 4px; }
  .flightc__col--when .flightc__logo--leg .flightc__mono { font-size: 0.5rem; }
  /* The name line becomes a row so the mark sits ON the text baseline block
     rather than above it; it keeps wrapping for the baggage badges. */
  /* GRID, NOT A WRAPPING FLEX ROW.

     As flex this was [mark][name] with the baggage forced onto a second line
     by flex-basis:100%. It laid out correctly but sized wrongly: the first
     line's cross size came out 16.6px — the height of the NAME — while the
     mark is 20px, so the mark hung 3.7px into the gap below and sat almost
     against the baggage line. That is the same "baggage behind the logo"
     collision in a quieter form.

     A grid row cannot do that: its height is the tallest item in it, always.
     Two rows, [mark | name] over [baggage], and the 6px gap is then really
     6px. It also drops the flex-basis:100% trick and the flex:1 1 0 the name
     needed — the minmax(0, 1fr) column does that job by definition. */
  .flightc__col--when .flightc__carrier {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: 6px;
    /* The desktop rule truncates this line with an ellipsis; here the name
       carries its own truncation, so the box must not clip the mark. */
    overflow: visible;
    white-space: normal;
  }
  /* BAGGAGE GETS ITS OWN LINE, under the logo and the airline name.

     It used to sit inline after the name and wrap only when it ran out of
     room, so on a long name ("Batik Air Malaysia") it collided with the
     carrier mark. flex-basis:100% breaks the line deliberately instead of
     leaving it to whatever the name happens to measure — one predictable
     shape for every airline. The 10px indent goes with it: that was spacing
     from a name it no longer sits beside. */
  /* BAGGAGE GETS THE SECOND ROW, under the mark and the name — spanning both
     columns, so it starts at the card's edge and not under the name. */
  .flightc__col--when .flightc__bags { grid-column: 1 / -1; margin-left: 0; }
  /* A name too long for the space is ELLIPSED, never wrapped: one line for
     every airline, so the rhythm below it is the same on every card. */
  .flightc__col--when .flightc__carrier-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* The price block becomes the card's footer: amount on the left, its
     qualifiers right-aligned beside it, then the two actions full width. */
  .flightc__price {
    display: grid;
    /* THE PRICE TAKES WHAT IT NEEDS; the qualifiers take what is left.

       It was the other way round (1fr for the amount, auto for the
       qualifiers), which is fine until the qualifiers grow — and they just
       did, to 0.8rem. At 320px "total · 3 travellers" then claimed 104px of a
       226px row and "BDT 372,394" broke across two lines for want of 4px.
       The amount is the one thing on this card that must never wrap, so it
       sizes first and the traveller count wraps instead if it ever has to. */
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    gap: 2px 6px;
    text-align: left;
    padding-top: 10px;
    border-top: 1px solid var(--border-soft);
  }
  .flightc__amount { grid-column: 1; grid-row: 1; font-size: 1.15rem; }
  /* 0.8rem — the size every other secondary line on the card uses (carrier,
     route, via). At 0.75 and 0.7 these two were the smallest text in the card
     for no reason: "Round trip" and the traveller count are as much a part of
     what the price means as the route is. */
  .flightc__qualifier, .flightc__pax {
    grid-column: 2;
    text-align: right;
    font-size: 0.8rem;
    line-height: 1.3;
    /* The safety valve for the rule above: on a viewport too narrow for both,
       this wraps rather than pushing the price off its line. */
    white-space: normal;
    font-weight: 600;
  }
  /* Bottom of the PRICE stack, not the top of the booking button.
     It used to span the full width and align left while every other price line
     (Round trip, Economy class, total Â· N travellers) was right-aligned in
     column 2 â€” so it read as a label belonging to the button beneath it rather
     than as the last line of the price. Placing it in column 2 lets it
     auto-flow directly under the traveller count, which is where it sits on
     desktop too.
     A text link at 0.7rem is also a poor tap target, hence the height. */
  .flightc__info {
    /* Directly under the price AMOUNT, in its column â€” that is what "bottom of
       the price" means. In column 2 it closed the meta stack instead, which
       put it beside the price rather than beneath it. */
    grid-column: 1;
    grid-row: 2;
    justify-content: flex-start;
    /* 24px, down from 32. This button's height IS the second row's height, and
       the traveller count is baseline-aligned inside that row — so eight px of
       tap padding it did not need were eight px of daylight between the two
       lines of the price block. The row's two lines now sit 5px apart, the
       same as the amount and "Fare details" on the left. */
    min-height: 24px;
    font-size: 0.78rem;
  }
  /* flex:1 — it takes the row's width minus the toggle. The top margin moved to
     .flightc__actions, which is what the row is spaced from now. */
  .flightc__book { flex: 1 1 auto; width: auto; min-width: 0; margin-top: 0; }

  .flightd { padding: 2px 14px 10px; }

  /* THE AIRPORT GOES UNDER THE TIME.

     Side by side, the name is squeezed into what a 3.4em time column leaves —
     "Hazrat Shahjalal International Airport (DAC)" then wraps to three or four
     ragged lines beside a single 05:00. Stacked, it gets the card's full width
     and the pair reads as one stop: when, then where. */
  /* A SMALL GRID, not a column flex. Stacking is right — but the date-change
     marker belongs BESIDE its time, and in a column flex it could only become a
     third row, drifting away from the time it qualifies. Two columns give the
     time and the marker one row, and the airport the full width beneath them,
     which is the same stacked reading as before. */
  .flightd__stop {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 8px;
    row-gap: 0px;
    align-items: center;
  }
  /* The 3.4em basis sizes the TIME COLUMN in the wide layout; here the track
     does that job, so the basis would only fight it. */
  .flightd__time { flex: 0 0 auto; grid-column: 1; }
  .flightd__daychg { grid-column: 2; justify-self: start; }
  /* BESIDE THE TIME, NOT ABOVE OR BELOW IT, so both arrows turn to point back
     along the row at the time on their left, rather than up or down at another
     row.

     OPPOSITE ROTATIONS, because the two glyphs are mirror images: the up arrow
     ends its curve pointing up and the down arrow pointing down, so reaching
     the same leftward heading means turning one anticlockwise and the other
     clockwise. Rotating both the same way sent the arrivals' arrows off to the
     right, away from the very time they label. */
  .flightd__daychg-arrow {
    display: inline-block;
    margin-right: 2px;
    vertical-align: 0;
  }
  .flightd__daychg--departure .flightd__daychg-arrow { transform: rotate(-90deg); }
  .flightd__daychg--arrival .flightd__daychg-arrow { transform: rotate(90deg); }
  /* Spanning both columns is what pushes it onto its own row. */
  .flightd__place { grid-column: 1 / -1; }

  /* The carrier's mark beside the segment's airline name. */
  .flightd__ride {
    padding-left: 0;
    margin-left: 0;
    display: flex;
    /* Top, not centre: an aircraft name can run to three lines ("Boeing 737
       MAX 8 / BBJ MAX 8 / Boeing 737 MAX 200") and a centred mark then floats
       beside the middle one, attached to nothing. Level with the first line it
       reads as belonging to the airline name it precedes. */
    align-items: flex-start;
    gap: 8px;
  }
  .flightd__ride .flightd__logo {
    display: grid;
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    border-radius: 5px;
    /* The card-header mark pulls itself 4px into the row below; inherited here
       that would misalign it against the text. */
    margin-bottom: 0;
  }
  .flightd__ride .flightd__logo img { width: 18px; height: 18px; border-radius: 4px; }
  .flightd__ride .flightd__logo .flightc__mono { font-size: 0.5rem; }
  .flightd__ride-txt { min-width: 0; }
  /* Centred on phones. The timeline above them is a left-anchored rail, so
     left-aligned pills sat under its last stop and read as belonging to that
     airport rather than to the leg as a whole. Centred, they read as a summary
     of the direction â€” and on a narrow card the row is short enough that the
     shift is a clear visual break rather than a stray indent. */
  .flightd__bags { justify-content: center; margin-top: 10px; }

  /* Anchored to the card rather than the trigger down here: a 280px panel
     hanging off a right-aligned button runs off a 320px screen. */
  .flightfare { right: 10px; left: 10px; width: auto; }

  .flightpin__slots { grid-template-columns: 1fr; }
  .flightpin__slots > * + * { border-left: 0; border-top: 1px solid var(--border-soft); }
  .flightpin__cta { width: 100%; justify-content: center; }
}

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

/* ---- Filter + sort bar --------------------------------------------- */
.flightf {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* nowrap is the point: the chips scroll instead of pushing the count and the
     Sort control onto a second line. It becomes wrap only below 720px, where
     one line genuinely cannot hold both. */
  flex-wrap: nowrap;
  gap: 10px 16px;
  margin: 18px 0 4px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border-soft);
}

/* Nav buttons + viewport + the reset button, on one line. */
.flightf__filters {
  display: flex;
  align-items: center;
  gap: 8px;
  /* min-width:0 lets this shrink below its content width, which is what makes
     the track scroll instead of the row overflowing. Without it a flex item
     refuses to go under min-content and the whole bar widens the page. */
  min-width: 0;
  flex: 1 1 auto;
}

/* The arrows overlay the faded edges instead of sitting in the flex flow.
   In flow, showing one shrank the track -> overflow changed -> ResizeObserver
   re-fired -> Chrome cut the loop off and left them stuck. Out of flow they
   change no geometry, so the measurement is stable. */
.flightf__slider {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.flightf__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 20%, #fff);
  border-radius: 50%;
  background: var(--surface);
  color: var(--color-primary);
  font-size: 0.9rem;
  cursor: pointer;
  transition: var(--transition);
}
.flightf__nav--prev { left: 0; }
.flightf__nav--next { right: 0; }
.flightf__nav:hover {
  border-color: transparent;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  color: #fff;
}

/* Drag-to-scroll, mouse only â€” touch keeps native momentum scrolling. */
.flightf__chips { cursor: grab; }
.flightf__chips.is-dragging {
  cursor: grabbing;
  /* scroll-behavior:smooth fights a scrollLeft written on every pointermove:
     each assignment starts a new animation and the track lags the cursor. */
  scroll-behavior: auto;
  user-select: none;
}
.flightf__chips.is-dragging .flightf__chip { pointer-events: none; }

/* The fades live here, on the clipping box â€” not on the track, whose own
   coordinate space scrolls with it. Widths are custom properties so the JS can
   drop an edge to 0 when there is nothing more that way. */
.flightf__viewport {
  position: relative;
  min-width: 0;
  flex: 1 1 auto;
  --fade-l: 0px;
  --fade-r: 0px;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--fade-l),
                      #000 calc(100% - var(--fade-r)), transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 var(--fade-l),
              #000 calc(100% - var(--fade-r)), transparent 100%);
}

/* Scrolls horizontally rather than wrapping into a tall block. The fade at the
   right edge is what tells the visitor there is more â€” a clipped row with no
   affordance reads as the whole set. */
.flightf__chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth;
  padding: 2px 0;
}
.flightf__chips::-webkit-scrollbar { display: none; }

/* The fade widths are deliberately NOT animated.

   The first version registered them with @property so the mask length could
   transition. Measured result: with the inline style reading `--fade-r: 26px`,
   the COMPUTED value was 0px â€” a registered property whose transition has not
   advanced reports its initial-value, so the fade rendered as absent exactly
   where it was needed. Seen at 1024px while 1440px and 820px were correct,
   which is the worst kind of bug: intermittent and width-dependent.

   The easing was decoration; the fade meaning "there is more this way" is not.
   The motion the visitor actually reads is the scroll itself, and that is
   smooth via scroll-behavior below. */

@media (prefers-reduced-motion: reduce) {
  .flightf__chips { scroll-behavior: auto; }
}

/* ---- the filter bar on small screens ----

   Below 720px one line cannot hold six chips AND the count AND a sort select,
   so the bar wraps into two rows: the scrolling chips, then the count + sort.
   The chips still scroll rather than wrap, which is what keeps that second row
   from being pushed further down as badges appear. */
@media (max-width: 720px) {
  .flightf { flex-wrap: wrap; }
  .flightf__filters { flex: 1 1 100%; }
  .flightf__right { flex: 1 1 100%; justify-content: space-between; }
}

/* Touch devices scroll the track by dragging, so the arrows are dead weight â€”
   and at 30px each they cost real width on the row that has least of it. */
@media (hover: none) and (pointer: coarse) {
  .flightf__nav { display: none !important; }
}

.flightf__chip {
  flex: 0 0 auto;
  /* inline-flex so the label and its value badge share a baseline and a gap;
     the badge is a child element, not appended text. */
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 18%, #fff);
  background: var(--surface);
  color: var(--color-text);
  border-radius: 999px;
  padding: 7px 14px;
  font: inherit;
  font-size: 0.88rem;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: var(--transition);
}
.flightf__chip:hover { border-color: color-mix(in srgb, var(--grad-btn-1) 40%, #fff); }
/* An active chip carries the brand gradient â€” the same ramp the topbar and the
   primary buttons use. This is what stops the filter row reading as generic
   grey chrome bolted onto a purple site. */
.flightf__chip.is-on {
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  border-color: transparent;
  color: #fff;
}
/* The selected VALUE, beside the chip's name. Shown only when the filter is
   actually filtering something â€” an empty badge is hidden outright rather than
   rendered as a stray blank pill. */
/* ============================================================
   `hidden` must actually hide, everywhere in flight search
   ============================================================
   `[hidden] { display: none }` is a USER-AGENT rule at the lowest possible
   specificity, so any class rule setting `display` silently beats it. This
   component sets display on almost everything, and MEASURED on 26 Aug 2026 four
   elements ignored `hidden` outright:

     the bar      display:flex        1160px wide before any results existed
     Clear all    display:inline-flex   89px wide with nothing to clear
     nav prev     display:flex          30px wide with nothing to scroll
     nav next     display:flex          30px wide with nothing to scroll

   The JS was correct in every case â€” `el.hidden = true` was set â€” which is why
   asserting the `hidden` PROPERTY passed while the buttons sat there in plain
   sight. Assert computed display, not the property.

   Attribute selectors, so specificity wins without `!important`. Add to this
   list whenever a flight element gains a `display` rule; it is cheaper than
   rediscovering this a third time. */
.flightf[hidden],
.flightf__nav[hidden],
.flightf__chip[hidden],
.flightf__chip-badge[hidden],
.flightf__viewport[hidden],
.flightf__pop[hidden],
.flightf__right[hidden],
.flightl__region[hidden],
.flightl__skeleton[hidden],
.flightc[hidden],
.flightd[hidden],
.flightp[hidden],
.flightp__skip[hidden],
.flightpin[hidden],
.flightl__cabinnote[hidden] {
  display: none;
}

.flightf__chip-badge {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.45;
  background: color-mix(in srgb, var(--grad-btn-1) 14%, var(--surface));
  color: var(--color-primary);
}
/* On an active chip the ground is the brand gradient, so the badge lifts off it
   with translucent white rather than a second opaque colour that would fight it. */
.flightf__chip.is-on .flightf__chip-badge {
  background: rgba(255, 255, 255, 0.24);
  color: #fff;
}

.flightf__chip--reset { border-style: dashed; }
.flightf__chip:focus-visible,
.flightf__sort select:focus-visible,
.flightl__link:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

.flightf__right { display: flex; align-items: center; gap: 14px; flex: 0 0 auto; }
/* THE STATUS LINE'S SPINNER.

   Sits to the RIGHT of the sentence, on the same baseline, and only while the
   search is genuinely still running — setStatus() adds it for the two working
   states and never for a final one, because a spinner beside "No flights
   found" contradicts the words next to it.

   em-sized so it tracks the status text rather than needing its own number,
   and the ring uses the brand colour on a faint track so the rotation is
   visible at 1em. */
.flightl__status.is-busy { display: flex; align-items: center; gap: .5rem; }
.flightl__spin {
  display: inline-block;
  width: .95em;
  height: .95em;
  flex: 0 0 auto;
  border: 2px solid color-mix(in srgb, var(--color-primary) 22%, transparent);
  border-top-color: var(--color-primary);
  border-radius: 50%;
  animation: flightlSpin .7s linear infinite;
}
@keyframes flightlSpin { to { transform: rotate(360deg); } }

/* Reduced motion: keep the ring as a static marker rather than spinning it.
   The sentence already says the search is running, so nothing is lost. */
@media (prefers-reduced-motion: reduce) {
  .flightl__spin { animation: none; }
}


.flightf__sort { display: flex; align-items: center; gap: 6px; font-size: 0.88rem; }
.flightf__sort-label { color: var(--color-text-muted); }
.flightf__sort select {
  font: inherit;
  font-size: 0.88rem;
  padding: 6px 10px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 18%, #fff);
  background: var(--surface);
  color: var(--color-text);
}

/* ---- Filter popover ------------------------------------------------ */
.flightf__pop {
  position: absolute;
  top: calc(100% - 6px);
  z-index: 30;
  min-width: 240px;
  max-width: min(340px, calc(100vw - 32px));
  max-height: 320px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 18%, #fff);
  border-radius: 18px;
  box-shadow: var(--shadow-lg);
  padding: 10px;

  /* Closed state. `hidden` is still set by the markup for the very first
     paint, but after that the panel is driven by .is-open so it can
     transition â€” display:none cannot. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  pointer-events: none;
  transition:
    opacity 200ms cubic-bezier(.2,.9,.25,1),
    transform 200ms cubic-bezier(.2,.9,.25,1),
    visibility 0s linear 200ms;
}
.flightf__pop.is-open {
  opacity: 1;
  visibility: visible;
  transform: none;
  pointer-events: auto;
  transition:
    opacity 260ms cubic-bezier(.2,.9,.25,1),
    transform 260ms cubic-bezier(.2,.9,.25,1),
    visibility 0s;
}

/* The card's detail panel is animated in JS (height, padding and border-top
   together â€” see slide()), so it must not carry a competing CSS transition.
   Deliberately NO `will-change`: a results list holds up to 250 cards, and
   promoting every one of their panels to its own layer costs far more than the
   one panel actually being animated saves. */

@media (prefers-reduced-motion: reduce) {
  .flightf__pop,
  .flightf__pop.is-open { transition: none; }
}
.flightf__pop-empty { color: var(--color-text-muted); margin: 6px; font-size: 0.9rem; }

.flightf__opt {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 8px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.9rem;
}
.flightf__opt:hover { background: var(--surface-2); }
.flightf__opt > span:nth-of-type(1) { flex: 1 1 auto; min-width: 0; }

/* The tick itself. A browser-default checkbox is ~13px, which reads as an
   afterthought beside a 26px carrier logo and is a small target on a phone.
   Radios are sized with it so every filter panel keeps one control size. */
.flightf__opt input[type="checkbox"],
.flightf__opt input[type="radio"] {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin: 0;
  accent-color: var(--color-primary);
  cursor: pointer;
}

/* AIRLINE ROWS carry the carrier's mark, so the first span is the logo tile,
   not the label — it must NOT take the growing track above. Same specificity
   as that rule (0,2,0) and placed after it, so it wins on source order. */
.flightf__opt--air > span:nth-of-type(1) { flex: 0 0 auto; }
.flightf__opt-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The card's tile at filter scale. Reusing .flightc__logo keeps ONE logo
   implementation — including the dark-mode rule that holds the tile white,
   because airline artwork is dark ink drawn for a white page. */
.flightf__opt .flightc__logo { width: 26px; height: 26px; border-radius: 7px; }
.flightf__opt .flightc__logo img { width: 22px; height: 22px; border-radius: 6px; }
.flightf__opt .flightc__mono { font-size: 0.6rem; letter-spacing: 0; }
/* An option that would yield nothing still shows its 0 and stays clickable:
   landing on an empty list deliberately beats landing on one with no
   explanation. */
.flightf__n {
  flex: 0 0 auto;
  color: var(--color-text-muted);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.flightf__range { display: flex; flex-direction: column; gap: 8px; padding: 10px 8px; font-size: 0.9rem; }
.flightf__range input[type="range"] { width: 100%; accent-color: var(--color-primary); }

/* ---- Times panel: per-leg departure / arrival ranges ----------------
   The panel is wider than the others because it holds two dual-handle sliders
   whose handle labels must not collide at the ends. */
.flightf__pop[data-kind="times"] {
  min-width: min(330px, calc(100vw - 32px));
  max-width: min(380px, calc(100vw - 32px));
  max-height: min(420px, calc(100vh - 140px));
}

.flightf__tabs {
  display: flex;
  gap: 4px;
  margin: 2px 2px 10px;
  border-bottom: 1px solid var(--border-soft);
}
.flightf__tab {
  flex: 1 1 0;
  padding: 8px 6px 10px;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--color-text-muted);
  cursor: pointer;
  /* The active underline sits ON the container's border rather than under it,
     so switching tabs does not shift the panel by a pixel. */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--transition), border-color var(--transition);
}
.flightf__tab:hover { color: var(--color-text); }
.flightf__tab.is-active { color: var(--color-primary); border-bottom-color: var(--color-primary); }

.flightf__route {
  margin: 0 2px 4px;
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

.flightf__time { padding: 10px 4px 4px; }
.flightf__time-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  color: var(--color-text);
}
/* Departure CLIMBS, arrival DESCENDS â€” the takeoff/landing pair Google Flights
   uses, which Bootstrap Icons has no glyph for (it ships four plane icons, all
   the same top-down nose-up shape).

   So the same trick the leg pills use: one glyph, angled. The angle is 180deg
   apart, and that is not arbitrary â€” .flightc__legtag records that 45/135 was
   tried there and FAILED, because this glyph is a top-down plane and nearly
   symmetric, so a 90deg difference is unreadable at small sizes. Rendered side
   by side at 15px, 0/180 is legible and -45/135 is two identical X shapes.
   Do not "improve" this back to a 45deg tilt.

   Outline for departure, solid for arrival, carries the same distinction a
   second way for anyone who reads weight faster than angle.

   The <i> is a flex item, so it is blockified and the transform applies; an
   untouched inline <i> would ignore it. */
.flightf__time-head i {
  color: var(--color-text-muted);
  display: inline-block;
  font-size: 0.95rem;
  line-height: 1;
}
.flightf__time[data-time-kind="arr"] .flightf__time-head i { transform: rotate(180deg); }
.flightf__time-head strong { font-weight: 600; }

/* The dual-handle range.
   Two native inputs stacked over one drawn track. The inputs themselves take no
   pointer events â€” only their thumbs do â€” because the one painted on top would
   otherwise swallow every press meant for the other, leaving one handle dead. */
.flightf__dual {
  position: relative;
  height: 34px;
  margin-top: 6px;
}
.flightf__dual-track {
  position: absolute;
  left: 9px;
  right: 9px;
  top: 50%;
  height: 4px;
  transform: translateY(-50%);
  border-radius: 999px;
  background: color-mix(in srgb, var(--grad-btn-1) 16%, #fff);
}
.flightf__dual-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: 999px;
  background: var(--color-primary);
}
.flightf__dual input[type="range"] {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  width: 100%;
  height: 34px;
  margin: 0;
  padding: 0;
  background: none;
  appearance: none;
  -webkit-appearance: none;
  pointer-events: none;      /* the BODY is inert; see the thumbs below */
}
.flightf__dual input[type="range"]:focus { outline: none; }
.flightf__dual input[type="range"].is-front { z-index: 2; }

.flightf__dual input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  pointer-events: auto;      /* ...but the thumb is grabbable */
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--color-primary);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28);
  cursor: grab;
}
.flightf__dual input[type="range"]::-moz-range-thumb {
  pointer-events: auto;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--color-primary);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28);
  cursor: grab;
}
.flightf__dual input[type="range"]:focus-visible::-webkit-slider-thumb {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}
.flightf__dual input[type="range"]::-webkit-slider-runnable-track { background: none; }
.flightf__dual input[type="range"]::-moz-range-track { background: none; }

/* Bag stepper: a labelled -/N/+ row.
   Replaces a single "checked bag included" checkbox, which could not say how
   MANY bags, and could say nothing at all about carry-on. */
.flightf__step {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 8px;
  font-size: 0.9rem;
}
.flightf__step + .flightf__step { border-top: 1px solid var(--border-soft); }
.flightf__step-label { flex: 1 1 auto; min-width: 0; }
.flightf__step-ctl {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 4px;
}
.flightf__step-btn {
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 22%, transparent);
  background: color-mix(in srgb, var(--grad-btn-1) 8%, transparent);
  color: var(--color-primary);
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.flightf__step-btn:hover:not([disabled]) { background: var(--color-primary); color: #fff; }
/* Disabled means "the results cannot go further", so it has to look inert
   rather than merely quiet â€” this is the edge of what the fares offer. */
.flightf__step-btn[disabled] {
  opacity: 0.4;
  cursor: default;
  background: var(--surface-3);
  color: var(--color-text-muted);
  border-color: var(--border-soft);
}
.flightf__step-val {
  min-width: 22px;
  text-align: center;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Section heading inside a panel ("Layover duration", "Airports"). */
.flightf__sub {
  margin: 10px 2px 2px;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* The "All connecting airports" master switch.
   A real checkbox drives it â€” visually hidden rather than display:none, so it
   keeps its place in the tab order and its label association. */
.flightf__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 8px;
  margin-top: 6px;
  border-top: 1px solid var(--border-soft);
  font-size: 0.9rem;
  cursor: pointer;
}
.flightf__toggle input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.flightf__switch {
  flex: 0 0 auto;
  position: relative;
  width: 38px;
  height: 22px;
  border-radius: 999px;
  background: var(--surface-3);
  border: 1px solid var(--border-soft);
  transition: background var(--transition), border-color var(--transition);
}
.flightf__switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  transition: transform var(--transition);
}
.flightf__toggle input:checked ~ .flightf__switch {
  background: var(--color-primary);
  border-color: var(--color-primary);
}
.flightf__toggle input:checked ~ .flightf__switch::after { transform: translateX(16px); }
.flightf__toggle input:focus-visible ~ .flightf__switch {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* Connections lists a dozen-plus airports, so it gets the wider panel the
   Times filter uses and scrolls inside it. */
.flightf__pop[data-kind="connections"] {
  min-width: min(330px, calc(100vw - 32px));
  max-width: min(380px, calc(100vw - 32px));
  max-height: min(440px, calc(100vh - 140px));
}

.flightf__pop-foot {
  display: flex;
  justify-content: flex-end;
  padding: 6px 2px 2px;
  margin-top: 4px;
  border-top: 1px solid var(--border-soft);
  /* Pinned to the bottom of the scrolling panel. The Layover panel lists a
     dozen-plus airports, so an in-flow Clear sat below the fold â€” the one
     control that undoes what the panel just did should never need scrolling
     to. The panel itself is the scroll container, so sticky resolves against
     it. Opaque ground, or the list shows through as it passes underneath. */
  position: sticky;
  bottom: -10px;                /* cancels the panel's own 10px padding */
  z-index: 1;
  background: var(--surface);
}
.flightf__clear {
  border: 0;
  background: none;
  padding: 6px 4px;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-primary);
  cursor: pointer;
}
.flightf__clear[disabled] { color: var(--color-text-muted); cursor: default; }

.flightl__link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--color-primary);
  text-decoration: underline;
  cursor: pointer;
}

@media (max-width: 600px) {
  .flightf { gap: 10px; }
  .flightf__right { width: 100%; justify-content: space-between; }
  .flightf__pop { left: 0 !important; right: 0; max-width: none; }
}

/* This theme has no global .btn â€” buttons are block-scoped (.cta__btn,
   .story__btn, .policy__btn). The flights page emitted class="btn", which
   matched nothing and rendered as bare link text. */
.flightl__btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 1.4rem;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.95rem;
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  box-shadow: var(--shadow-sm);
  transition: var(--transition);
}
.flightl__btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.flightl__btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .flightl__btn:hover { transform: none; }
}

/* Best / Other split. Only rendered while sorting by Best â€” under an explicit
   sort the visitor asked for one ordering and splitting would override it. */
.flightl__group + .flightl__group { margin-top: 34px; }
.flightl__group-title {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 1rem;
  font-weight: 600;
  color: var(--heading);
  /* 18px, not 10px. .flightc__tag is absolutely positioned at top:-9px and
     hangs OVER the first card's edge, so a 10px margin left roughly 1px of
     visible air between "Best flights" and the "Cheapest" pill under it. Any
     spacing above a card has to pay for that 9px overhang first. */
  margin: 0 0 24px;
}
.flightl__group-sub {
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--color-text-muted);
}
/* THE spacing that actually applies to a sectioned list, and the one that was
   wrong. `.flightl__list` is the flex container with the `gap`, but
   `.flightl__group` is a plain <section> â€” so as soon as results are split into
   Best / Other (the default whenever the provider tags some of them, i.e. most
   real searches) the gap stops applying to the cards and THIS margin takes
   over. It was 12px and stayed 12px through several rounds of "increase the
   card spacing", because the flat-list gap is what was being changed.
   Keep this in step with .flightl__list's gap, both here and in the phone
   override below. */
.flightl__group .flightc + .flightc { margin-top: 20px; }

/* Phone overrides for the group. These MUST sit after the base rules above:
   the selectors are identical, media queries add no specificity, and source
   order decides. Placed earlier in the file they lost silently â€” the 32px
   never applied and the cards stayed 20px apart. */
@media (max-width: 894.98px) {
  .flightl__group .flightc + .flightc { margin-top: 32px; }
  .flightl__group + .flightl__group { margin-top: 40px; }
  .flightl__group-title { margin-bottom: 30px; }
}

/* Inline results on the homepage.
   The section wrapper carries NO padding of its own â€” the region does, and the
   region is [hidden] until a search runs. Otherwise an empty padded section
   sits between the hero and the offers, pushing the page down for a results
   list that does not exist yet. */
.flightl--inline { padding-block: 0; }
.flightl--inline .flightl__region { padding-block: 3.5rem; }

/* Reserve height the moment results are revealed so the offers below do not
   jump as cards stream in over the next two minutes. */
.flightl__region { scroll-margin-top: 90px; }

/* ============================================================
   Flight search progress overlay (.flightp)
   Shown while a search runs â€” on submit and on reload of a
   results URL. Theme-aligned: brand gradient, 18px+ radii,
   color-mix borders. See flight-search-results.md Â§1câ€“Â§1d3.
   ============================================================ */
.flightp {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: 20px;
}
.flightp[hidden] { display: none; }

.flightp__scrim {
  position: absolute;
  inset: 0;
  background: rgba(var(--grad-btn-2-rgb), 0.28);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
}
/* Engines without backdrop-filter get a heavier scrim instead of a
   see-through one that leaves the page behind unreadable. */
@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  .flightp__scrim { background: rgba(var(--grad-btn-2-rgb), 0.55); }
}

.flightp__card {
  position: relative;
  /* Clips the full-bleed band to the card's radius. Safe here — nothing in
     this card pops OUT of it, unlike the booking form's calendars. */
  overflow: hidden;
  width: min(430px, 100%);
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--grad-btn-1) 14%, #fff);
  border-radius: 26px;
  box-shadow: 0 40px 90px -30px rgba(var(--grad-btn-2-rgb), 0.55);
  padding: 26px 26px 22px;
  text-align: center;
  animation: flightp-in 320ms cubic-bezier(.2,.9,.25,1) both;
}
@keyframes flightp-in {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
  to   { opacity: 1; transform: none; }
}

.flightp__logo {
  /* Auto margins, not the card's text-align: the theme reset makes every img
     display:block, which text-align cannot centre. */
  display: block;
  height: 34px;
  width: auto;
  margin: 0 auto 16px;
  opacity: .92;
}

.flightp__route {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin: 0 0 4px;
  font-size: clamp(1.15rem, 4vw, 1.5rem);
  font-weight: 700;
  color: var(--heading);
  line-height: 1.2;
}
.flightp__plane-ico {
  color: var(--color-primary);
  flex: 0 0 auto;
  font-size: 1.05rem;
  /* bi-airplane-fill points UP; the route line reads left-to-right. */
  transform: rotate(90deg);
}
.flightp__meta { margin: 0 0 4px; font-size: .86rem; color: var(--color-text-muted); }

/* ---- the band ----

   A strip of night sky across the full width of the card. It is full-bleed by
   negative margins matching the card's padding, so it needs no change to the
   card's own box.

   *** IT DOES NOT FOLLOW THE THEME. ***
   Everything on it — the arc, the codes, the label — is drawn in white on a
   fixed purple gradient, in both light and dark mode. Theming it would mean
   re-tuning five foreground colours for a surface that is already dark. What
   DOES change in dark mode is the card around it. */
.flightp__band {
  position: relative;
  margin: 18px -26px 0;
  /* Bottom padding leaves the row the codes are pinned into. */
  padding: 16px 26px 34px;
  /* THE SAME THREE-STOP SHAPE, from colours the panel already publishes.

     Each literal here was already one of the brand's own gradient stops —
     #4c1d95 is --grad-btn-2, #6d28d9 is --grad-topbar-2, #7c3aed is
     --grad-btn-1 — so the band can follow the theme without a single new
     setting: same angle, same 0/55/100 stops, same dark-to-vivid travel, just
     read from the variables instead of spelled out. The literals stay as the
     fallback, so an unthemed deployment is unchanged. */
  background: linear-gradient(135deg,
    var(--grad-btn-2, #4c1d95) 0%,
    var(--grad-topbar-2, #6d28d9) 55%,
    var(--grad-btn-1, #7c3aed) 100%);
  overflow: hidden;
}
/* A soft glow behind the arc, so the band is not a flat rectangle. */
.flightp__band::after {
  content: "";
  position: absolute;
  left: 50%; top: -60%;
  width: 300px; height: 300px;
  transform: translateX(-50%);
  background: radial-gradient(circle, rgba(255, 255, 255, .16), transparent 62%);
  pointer-events: none;
}

/* ---- the arc ---- */
.flightp__arc { position: relative; margin: 0; }
.flightp__arc-line {
  stroke: rgba(255, 255, 255, .55);
  stroke-dasharray: 5 7;
  animation: flightp-dash 1.1s linear infinite;
}
@keyframes flightp-dash { to { stroke-dashoffset: -24; } }
.flightp__dot { fill: #fff; }
.flightp__dot--end { fill: rgba(255, 255, 255, .8); }

/* The plane is an <animateMotion> child of the svg (see the partial), so its
   position needs no CSS at all — only its colour. */
.flightp__plane { fill: #fff; }

/* Pinned to the BAND, not the arc: the two codes then sit on one line
   whatever height the arc renders at. */
.flightp__code {
  position: absolute;
  bottom: 12px;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .08em;
  color: rgba(255, 255, 255, .92);
}
.flightp__code--from { left: 26px; }
.flightp__code--to { right: 26px; }

/* The band's label, in FLOW beneath the arc — see the note in the partial
   about why it is not centred on top of it. */
.flightp__searching {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* PULLED UP INTO THE ARC'S FOOTPRINT.

     The svg is 64 units tall but the flight path ends at y=50, so its bottom
     14 units are empty — stacking the label after it left that dead space plus
     a margin, and the label drifted down towards the city names instead of
     sitting under the arc. Negative margin closes the empty band.

     Safe at the centre: at mid-width the path is at its APEX (y≈22), so the
     line and the plane are far above this text. It is only near the two ends
     that the path comes down to y=50, and nothing is drawn there. */
  margin-top: -14px;
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #fff;
  white-space: nowrap;
}
.flightp__pulse {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: #fff;
  animation: flightp-pulse 1.4s ease-in-out infinite;
}
/* box-shadow, not a scaled pseudo-element: the ring can then grow past the
   dot without affecting the label's layout beside it. */
@keyframes flightp-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, .55); }
  50%      { box-shadow: 0 0 0 7px rgba(255, 255, 255, 0); }
}

/* ---- live counts ---- */
.flightp__count {
  margin: 22px 0 0;
  font-size: clamp(2.6rem, 11vw, 3.4rem);
  font-weight: 800;
  line-height: 1;
  /* The heading colour, not the brand purple: the band above is already
     purple, and the number is the one thing that should read as a fact
     rather than as decoration. */
  color: var(--heading);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.02em;
}

/* THE SPINNER THAT STANDS IN FOR THE COUNT.

   Sized off the count's own font-size so the card does not change height when
   the number replaces it — a reflow at that moment would jump the status line
   and the progress bar, in a card whose whole job is to look steady while
   nothing is happening yet.

   Purple ring on a faint track, matching the band above, and only the one arc
   is coloured so the rotation is visible at all. */
.flightp__count .flightp__spin {
  display: inline-block;
  width: .82em;
  height: .82em;
  vertical-align: -.04em;
  border: .11em solid color-mix(in srgb, var(--color-primary) 22%, transparent);
  border-top-color: var(--color-primary);
  border-radius: 50%;
  animation: flightpSpin .7s linear infinite;
}
/* Only one of the two is ever shown. The number is the default so that a
   browser which never runs the script still reports the count it was given. */
.flightp__count.is-waiting [data-flight-progress-count] { display: none; }
.flightp__count:not(.is-waiting) .flightp__spin { display: none; }

@keyframes flightpSpin { to { transform: rotate(360deg); } }

/* Respect a reduced-motion preference: keep the ring as a static indicator
   rather than spinning it. The progress bar and the status line still report
   movement, so nothing is lost. */
@media (prefers-reduced-motion: reduce) {
  .flightp__count .flightp__spin { animation: none; }
}

.flightp__countlabel {
  margin: 6px 0 0;
  font-size: .84rem;
  color: var(--color-text-muted);
}
/* The JS writes "N flights found · N airlines" here; the numbers in it should
   carry the same weight as the label around them. */
.flightp__countlabel b { font-weight: 700; color: var(--color-text); }

/* The message is kept short enough to sit on ONE line at phone width — a
   two-line status put the tick beside the middle of a ragged block and read as
   a layout fault rather than as a sentence. Measured: the line has 277px at a
   390px viewport, and the wording was chosen to fit it.

   `text-align: center` is insurance, not the plan: on a very narrow phone
   (~320px) even this message wraps, and centred lines under a first-line tick
   look deliberate where ragged-left ones do not. `align-items: flex-start`
   with a top-padded tick keeps the tick on the FIRST line instead of floating
   against the middle of a two-line block. */
.flightp__status {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 8px;
  margin: 18px 0 0;
  font-size: .86rem;
  color: var(--color-text);
  min-height: 1.3em;
  text-align: center;
}
.flightp__tick {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: color-mix(in srgb, #16a34a 14%, transparent);
  color: #16a34a;
}

.flightp__bar {
  margin: 18px 0 0;
  height: 7px;
  border-radius: 99px;
  background: color-mix(in srgb, var(--heading) 10%, transparent);
  overflow: hidden;
}
.flightp__bar > span {
  display: block;
  height: 100%;
  width: 0%;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--grad-btn-1), var(--grad-btn-2));
  transition: width 420ms cubic-bezier(.2,.9,.25,1);
}

.flightp__skip {
  margin: 16px 0 0;
  background: none;
  border: 0;
  padding: 6px 10px;
  font: inherit;
  font-size: .85rem;
  font-weight: 600;
  color: var(--color-primary);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  border-radius: 8px;
}
.flightp__skip:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* The logo, now a link home. inline-block so the card's own text-align centres
   it and the hit area is the mark itself — a block anchor would make the whole
   width of the card navigate. The img inside keeps its 16px bottom margin, so
   nothing about the card's spacing moves. */
.flightp__homelink {
  display: inline-block;
  border-radius: 10px;
  text-decoration: none;
}
.flightp__homelink:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }

/* "Return to Home" — the way out of a search that runs over a minute.
   The site's primary CTA treatment, matching .btn-login: the same brand
   gradient, pill and shadow tinted with --color-primary, so it reads as the
   same kind of control wherever it appears rather than as a one-off.

   Brand tokens, never literal hex — this stylesheet is shared by every project
   key, and a hardcoded purple would follow NTT's palette onto a brand that does
   not use it. */
.flightp__home {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin: 16px 0 0;
  padding: .5rem 1.15rem;
  font-size: .82rem;
  font-weight: 600;
  line-height: 1.2;
  color: #fff;
  text-decoration: none;
  border: 0;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  box-shadow: 0 8px 18px -8px var(--color-primary);
  transition: var(--transition);
}
.flightp__home-ico {
  /* Optical size, and a gap rather than a margin so the flex centring above
     still centres the pair as a unit. */
  font-size: 1.02em;
  line-height: 1;
  margin-right: .42em;
}
.flightp__home:hover {
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 12px 22px -8px var(--color-primary);
}
.flightp__home:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

/* Dark mode needs no override: the gradient carries its own white text and sits
   on the card rather than borrowing its colour, so it reads the same on both
   surfaces — which is the point of using the shared CTA treatment. The offset
   on the focus ring is what keeps it clear of the shadow. */

/* ---- dark mode ----

   The BAND is untouched on purpose: it is a fixed piece of night sky with
   white content on it, and it reads correctly against either card. What
   changes is the card it sits in, the scrim behind it, and the two neutral
   surfaces (the progress track, the tick's halo) that would otherwise be
   mixed against white.

   The logo's dark treatment lives with the other logo rules near the top of
   this file, not here — see the note there. */
[data-theme="dark"] .flightp__scrim {
  background: rgba(8, 5, 20, .62);
}
[data-theme="dark"] .flightp__card {
  background: var(--surface);
  border-color: rgba(255, 255, 255, .1);
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, .75);
}
[data-theme="dark"] .flightp__band {
  /* Slightly deeper, so the card's own dark surface still reads as lighter
     than the band rather than merging with it.

     Same published stops as the light band, one step darker — so the dark band
     follows a re-brand along with everything else instead of staying on the
     shipped purple. Two of the three were already exactly a published colour
     (#5b21b6 is --grad-deep, #6d28d9 is --grad-topbar-2); the first had no
     match, so it is --grad-btn-2 darkened, which lands within 3/255 per channel
     of the #3b1580 it replaces — closer than the eye resolves, and one source
     of truth rather than a sixth literal to keep in step. */
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--grad-btn-2, #4c1d95) 82%, #000) 0%,
    var(--grad-deep, #5b21b6) 55%,
    var(--grad-topbar-2, #6d28d9) 100%);
}
[data-theme="dark"] .flightp__bar {
  background: rgba(255, 255, 255, .12);
}
[data-theme="dark"] .flightp__tick {
  background: rgba(34, 197, 94, .18);
  color: #4ade80;
}
[data-theme="dark"] .flightp__countlabel b { color: var(--color-text); }


@media (max-width: 420px) {
  .flightp__card { padding: 20px 18px 18px; border-radius: 22px; }
  .flightp__logo { height: 28px; }
}

/* Motion is decoration here; the numbers carry the information. */
@media (prefers-reduced-motion: reduce) {
  .flightp__card { animation: none; }
  .flightp__arc-line,
  .flightp__pulse { animation: none; }
  .flightp__bar > span { transition: none; }
  /* The plane is SMIL, which this query cannot touch â€” flight-search.js pauses
     it in progShow(). Do not add `.flightp__plane { animation: none }` back and
     assume it is handled. */
}

/* ==========================================================================
   REVIEW DETAIL POPUP  +  the small button variant it shares with the cards
   --------------------------------------------------------------------------
   Deliberately built on the EXISTING .modal / .modal__overlay / .modal__card /
   .modal__close from the auth modal, so the backdrop blur, the pop-in
   animation and the close button are literally the same code. Only the inner
   layout and the wider card are new — a second dialog system would drift from
   the first the moment either was touched.
   ========================================================================== */

/* --- small button ---------------------------------------------------------
   Same gradient, weight, radius and hover as .btn-outline; only the box is
   smaller. Used for "View on Facebook" on both the card and the popup, where a
   full-size button would shout louder than the review it belongs to. */
.btn-outline--xs {
  gap: 0.35rem;
  padding: 0.32rem 0.7rem;
  font-size: 0.72rem;
  line-height: 1.2;
  border-radius: 999px;
  /* OUTLINED BY DEFAULT — the inverse of the full-size .btn-outline, which
     starts filled. At this size, next to a review, a solid gradient pill reads
     as the primary action on the card and competes with "Show more". Outline
     first, fill on hover: same two states, swapped, so the hover still lands
     on the brand gradient the rest of the site uses. */
  color: var(--color-primary);
  background: var(--surface);
  border-color: #d9cdf3;
  border-color: color-mix(in srgb, var(--grad-btn-1) 30%, #fff);
  box-shadow: none;
}
.btn-outline--xs:hover,
.btn-outline--xs:focus-visible {
  color: #fff;
  background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
  border-color: transparent;
  transform: none;
  box-shadow: 0 6px 12px -8px var(--color-primary);
}
/* Bootstrap Icons render as an inline font glyph, so it needs its own size
   rather than inheriting the 0.72rem button text — and it must not shrink when
   the label wraps. */
.btn-outline--xs .bi {
  flex: 0 0 auto;
  font-size: 0.85rem;
  line-height: 1;
}
.review-modal__badge .bi {
  font-size: 0.85rem;
  line-height: 1;
}

/* Sits UNDER the location line inside the name block, not at the end of the
   row — so "Recommended on Facebook" and the link that proves it stay
   together. align-self keeps it to its own width instead of stretching. */
.review__fb {
  align-self: flex-start;
  margin-top: 0.45rem;
}
/* .review__user is a flex row; .review__who must be allowed to shrink or a
   long name pushes the button out of the card. */
.review__who {
  min-width: 0;
}
.review__who {
  display: flex;
  flex-direction: column;
}
.review__who b,
.review__who small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
  max-width: 100%;
}

/* --- the popup ----------------------------------------------------------- */
.review-modal__card {
  max-width: 560px;
  padding: 1.9rem 1.75rem 1.6rem;
}
.review-modal__head {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  /* Room for the close button in the corner. */
  padding-right: 2.5rem;
  margin-bottom: 1rem;
}
.review-modal__avatar {
  width: 54px;
  height: 54px;
  font-size: 1.05rem;
}
.review-modal__who {
  min-width: 0;
}
.review-modal__who b {
  display: block;
  font-size: 1.05rem;
  color: var(--heading);
  line-height: 1.3;
}
.review-modal__who small {
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

.review-modal__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.55rem 0.9rem;
  padding-bottom: 0.95rem;
  border-bottom: 1px solid var(--border-soft);
  margin-bottom: 1.1rem;
}
.review-modal__date {
  font-size: 0.8rem;
  color: var(--color-text-muted);
}
.review-modal__badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--color-primary);
  background: #f1ecfb;
  background: color-mix(in srgb, var(--grad-btn-1) 10%, #fff);
}

/* The review itself. Long reviews scroll INSIDE the popup rather than growing
   it past the viewport — .modal__card already caps at 92vh, and without an
   inner scroll region the actions row would be pushed out of reach. */
.review-modal__text {
  margin: 0;
  font-size: 1rem;
  line-height: 1.75;
  color: var(--color-text);
  white-space: pre-line;
  max-height: 46vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.review-modal__text::before {
  content: "\201C";
  color: var(--color-primary);
  font-size: 1.6rem;
  line-height: 0;
  vertical-align: -0.35rem;
  margin-right: 0.15rem;
}
.review-modal__text::after {
  content: "\201D";
  color: var(--color-primary);
  font-size: 1.6rem;
  line-height: 0;
  vertical-align: -0.55rem;
  margin-left: 0.1rem;
}

.review-modal__actions {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: 1.4rem;
}
.review-modal__dismiss {
  margin-left: auto;
  padding: 0.32rem 0.9rem;
  border-radius: 999px;
  border: 1.5px solid var(--border-soft);
  background: none;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: var(--transition);
}
.review-modal__dismiss:hover {
  color: var(--color-primary);
  border-color: #d9cdf3;
  border-color: color-mix(in srgb, var(--grad-btn-1) 27%, #fff);
}

/* Where a deep-linked ?review=<id> lands. .header is position:sticky at 78px
   tall on every breakpoint, so scrolling a card to y=0 would hide its top
   third behind the header. 78 + 26 of breathing room. Same approach the
   flight list already uses (.flightl__region). */
.review {
  scroll-margin-top: 104px;
}

/* The card the popup was opened from, briefly highlighted after a deep-linked
   page load so the visitor can see WHICH review the link pointed at once they
   close the dialog. */
.review.is-review-target {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

@media (max-width: 600px) {
  .review-modal__card {
    padding: 1.5rem 1.15rem 1.25rem;
  }
  .review-modal__avatar {
    width: 46px;
    height: 46px;
  }
  .review-modal__text {
    font-size: 0.95rem;
    max-height: 42vh;
  }
  .review__user {
    flex-wrap: wrap;
    row-gap: 0.55rem;
  }
}



/* ============================================================================
   BOOKING REQUEST (/booking)
   Summary on the left, form on the right; stacked on narrow screens with the
   SUMMARY FIRST, so a visitor confirms what they picked before typing.
   ========================================================================= */
.booking__title { font-size: clamp(24px, 3vw, 34px); margin: 14px 0 6px; }
.booking__lead { color: #5b5b6b; max-width: 62ch; margin: 0 0 22px; }

.booking__grid { display: grid; gap: 22px; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 980px) {
    .booking__grid { grid-template-columns: 380px 1fr; }
    /* Sticky on desktop only: on a phone it would eat the screen the form needs. */
    .booking__summary { position: sticky; top: 90px; }
}

.booking__card { background: #fff; border: 1px solid var(--color-primary); border-radius: 16px; padding: 18px; box-shadow: 0 10px 30px rgba(31, 18, 71, .05); }
.booking__card-head { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.booking__chip { display: inline-block; padding: 3px 10px; border-radius: 999px; background: color-mix(in srgb, var(--color-primary, #6f48a9) 10%, #fff); color: var(--color-primary-dark, #4c1fae); font-size: 12px; font-weight: 700; }
.booking__chip--soft { background: #f4f4f8; color: #5b5b6b; }

.booking__leg + .booking__leg { margin-top: 16px; padding-top: 16px; border-top: 1px dashed #ece9f5; }
.booking__leg-head { display: flex; justify-content: space-between; align-items: baseline; font-weight: 700; font-size: 13px; margin-bottom: 10px; }
.booking__leg-head span { font-weight: 500; color: #6b6b7b; font-size: 12px; }

.booking__route { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center; }
.booking__point strong { display: block; font-size: 20px; line-height: 1.1; }
.booking__point span { font-size: 13px; color: #2b2b3a; }
.booking__point small { display: block; font-size: 11px; color: #6b7280; }
.booking__point--end { text-align: right; }
.booking__path { text-align: center; }
.booking__line { display: block; height: 2px; background: linear-gradient(90deg, color-mix(in srgb, var(--color-primary, #6f48a9) 22%, #fff), #b9a9f0); margin: 5px 0; border-radius: 2px; }
.booking__duration, .booking__stops { font-size: 11px; color: #6b6b7b; }

.booking__segments { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; }
.booking__segments li { display: grid; gap: 2px; padding: 8px 10px; background: #fafaff; border-radius: 10px; font-size: 12px; }
.booking__seg-airline em { font-style: normal; color: #6b6b7b; margin-left: 6px; }
.booking__seg-route { color: #4a4a5a; }
.booking__seg-layover { color: #936110; }

/* One fact per LINE, label left and value right.

   Side by side in a narrow summary column, a value like "Economy class"
   wrapped onto a second line while its neighbours did not, so the row read as
   ragged and the labels stopped lining up with their values. Stacked rows keep
   every value on one line however long it is. */
.booking__facts { margin: 16px 0; padding: 4px 0; border-top: 1px solid #f0eef8; border-bottom: 1px solid #f0eef8; }
.booking__facts > div {
    display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
    padding: 9px 0;
}
.booking__facts > div + div { border-top: 1px solid #f6f4fb; }
.booking__facts span { font-size: 12px; color: #6b7280; flex: 0 0 auto; }
.booking__facts strong { font-size: 13.5px; color: #21212e; text-align: right; }

.booking__price { display: grid; gap: 2px; }
.booking__price span { font-size: 12px; color: #6b6b7b; }
.booking__price strong { font-size: 26px; color: var(--color-primary-dark, #3b1e8f); }
.booking__price small { font-size: 12px; color: #8a8a99; }
.booking__note { margin: 12px 0 0; font-size: 12px; color: #6b6b7b; }

.booking__form { display: grid; gap: 16px; }
.booking__block { border: 1px solid #ece9f5; border-radius: 16px; padding: 16px 18px 18px; background: #fff; }
.booking__block legend { font-weight: 800; font-size: 15px; padding: 0 6px; display: flex; align-items: center; gap: 8px; }
.booking__hint { color: #8a8a99; font-size: 12.5px; margin: 4px 0 12px; }
.booking__hint--foot { text-align: center; margin: 0; }

.booking__row { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
/* Rows are separate elements inside a plain block, so nothing separated them:
   fields sat 12px apart WITHIN a row and flush against the next row. The gap
   under the title/gender radios is where that first showed, but every row
   boundary had it. Same 12px, so vertical and horizontal rhythm match. */
.booking__row + .booking__row { margin-top: 12px; }

/* *** A TRAVELLER CARD'S ROWS SHARE ONE FIXED THREE-COLUMN GRID. ***

   `auto-fit` COLLAPSES EMPTY TRACKS. A row holding two fields therefore drops
   its third column and the two split the full width at 50% each, while a row
   of three puts its second column at 33%. That is why Title/Gender sat at the
   halfway point instead of above First/Last name — the rows were never on the
   same columns, they just looked close when the field counts matched.

   Fixed tracks, so column 2 is column 2 on every row regardless of how many
   fields that row happens to hold. */
.booking__pax .booking__row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 760px) {
    /* Narrow enough that three columns would squeeze every control; here the
       fields flow and alignment across rows stops being meaningful anyway. */
    .booking__pax .booking__row { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
}
/* align-content: start is LOAD-BEARING.

   A field is a grid, and the row it sits in is as tall as its tallest field —
   the last-name one, which carries the "No surname" switch under its input.
   Without this, the auto rows STRETCH to fill that extra height, and they
   divide it differently depending on how many children a field has (3 for
   first name, 4 for last name). The result was inputs sitting at three
   different heights across one row. */
.booking__field { display: grid; gap: 5px; font-size: 13px; align-content: start; }
.booking__field--wide { grid-column: 1 / -1; }
.booking__field > span { font-weight: 600; color: #3a3a4a; }
.booking__field i { color: #c22f6b; font-style: normal; }
.booking__field input, .booking__field select {
    height: 44px; padding: 0 12px; border: 1px solid #e2ddf0; border-radius: 10px;
    font: inherit; background: #fff; color: #21212e;
}
.booking__field input:focus, .booking__field select:focus { outline: 2px solid #7c3aed; outline-offset: 1px; border-color: transparent; }

.booking__submit {
    /* Matches the gap BETWEEN traveller cards, which is 28px: the form grid
       contributes 16px and `.booking__pax + .booking__pax` adds 12px more. The
       button is not a .booking__pax, so it received only the 16px and sat
       noticeably tighter to the last card than the cards sat to each other. */
    margin-top: 12px;
    height: 52px; border: 0; border-radius: 12px; cursor: pointer;
    background: linear-gradient(90deg, var(--grad-topbar-2, #6d28d9), var(--color-primary-2, #8b5cf6)); color: #fff;
    font-size: 15px; font-weight: 700;
}
.booking__submit:hover { filter: brightness(1.06); }
.booking__submit--link { display: inline-flex; align-items: center; justify-content: center; padding: 0 26px; text-decoration: none; }

.booking__alert { border-radius: 12px; padding: 12px 14px; margin-bottom: 16px; font-size: 13px; }
.booking__alert--error { background: #fdeef3; color: #8d1c45; border: 1px solid #f6cfdd; }
.booking__alert ul { margin: 6px 0 0; padding-left: 18px; }

.booking__done { max-width: 560px; margin: 40px auto; text-align: center; }
.booking__done-mark { width: 64px; height: 64px; margin: 0 auto 14px; border-radius: 50%; display: grid; place-items: center; background: #e9f9f0; color: #128a4d; font-size: 30px; }
.booking__ref { margin: 18px 0 4px; font-size: 13px; color: #6b6b7b; }
.booking__ref strong { display: block; font-size: 20px; color: #21212e; letter-spacing: .04em; margin-top: 4px; }

/* A custom (pinned) pair has no quoted fare — styled as a statement, not a
   number, so it never reads as a price of zero. */
.booking__price--quote strong { font-size: 20px; color: #6b6b7b; }


/* ---- Booking: traveller accordion -------------------------------------- */
/* NO overflow:hidden here. It was clipping the date picker's calendar at the
   card edge — the popover is a child of the card, so any overflow clip on an
   ancestor traps it however high its z-index. The rounded corners are kept by
   clipping only the header instead. */
.booking__pax { border: 1px solid var(--color-primary); border-radius: 16px; background: #fff; position: relative; }
.booking__pax-head { border-radius: 16px 16px 0 0; }
/* An open card sits above the ones after it — and above the submit button —
   so its calendar is never drawn underneath them. */
.booking__pax.is-open { z-index: 5; }
.booking__pax + .booking__pax { margin-top: 12px; }
.booking__pax-head {
    width: 100%; display: flex; align-items: center; gap: 10px;
    padding: 14px 16px; border: 0; background: none; cursor: pointer; text-align: left; font: inherit;
    border-radius: 16px 16px 0 0;
}
.booking__pax-title { display: flex; align-items: center; gap: 8px; font-weight: 800; font-size: 15px; }
.booking__pax-state { margin-left: auto; color: #8a8a99; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; }
.booking__pax-caret { width: 10px; height: 10px; border-right: 2px solid #8a8a99; border-bottom: 2px solid #8a8a99; transform: rotate(45deg); transition: transform .18s; flex: 0 0 auto; }
.booking__pax.is-open .booking__pax-caret { transform: rotate(-135deg); }
/* Animated with grid-template-rows 0fr -> 1fr rather than max-height: it
   transitions to the content's REAL height, so there is no guessed maximum to
   overshoot (the usual cause of a card that snaps at the end) and no reflow
   measuring in JS.
   NOT display:none — a display:none control is barred from being focused, and
   the browser then refuses to submit while pointing at a field it cannot show.
   Collapsed content stays in the document; the `invalid` handler opens its
   card so the message is visible. */
.booking__pax-body {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .28s cubic-bezier(.4, 0, .2, 1);
}
.booking__pax.is-open .booking__pax-body { grid-template-rows: 1fr; }
.booking__pax-inner { overflow: hidden; min-height: 0; padding: 0 16px; }
.booking__pax.is-open .booking__pax-inner { padding-bottom: 16px; }
/* Overflow must be hidden WHILE animating or the content spills out of the
   closing card — but once open it has to be visible, or the date picker's
   calendar is clipped by the card edge. booking.js flips this after the
   transition ends. */
.booking__pax-inner.is-overflowing { overflow: visible; }

/* While a calendar is open in THIS card, lift it clear of the cards below and
   stop anything clipping it. `.is-open` alone gives z-index 5, which every
   other open card also has — the next card still won a paint-order tie and
   covered the calendar. booking.js adds `is-picking` for exactly as long as a
   calendar is open.

   *** 20, AND IT MUST STAY UNDER THE STICKY HEADER'S 50. ***
   It only has to beat the sibling cards (5), and this element is a stacking
   context, so every popover inside it is capped at this value however high its
   own z-index goes. At 60 the whole card — and the open dropdown with it —
   painted straight over the header. */
.booking__pax.is-picking { z-index: 20; }
.booking__pax.is-picking .booking__pax-inner,
.booking__pax.is-picking .booking__pax-body { overflow: visible; }

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

/* ---- Date of birth ------------------------------------------------------ */
.ntt-dob, .ntt-pick { position: relative; }
.ntt-dob__display, .ntt-pick__display {
    width: 100%; height: 44px; padding: 0 12px; text-align: left;
    border: 1px solid #e2ddf0; border-radius: 10px; background: #fff; font: inherit; color: #21212e; cursor: pointer;
}
.ntt-dob__display.is-empty, .ntt-pick__display.is-empty { color: #9a9aab; }
/* The shared picker flags an empty value on the WRAPPER, not the display, so
   the "Select date" placeholder needs this second form to stay grey. */
.ntt-dp.muted-val .ntt-dob__display { color: #9a9aab; }
.ntt-dob__display:focus, .ntt-pick__display:focus { outline: 2px solid #7c3aed; outline-offset: 1px; }
.ntt-dob__pop, .ntt-pick__pop {
    display: none; position: absolute; z-index: 60; top: calc(100% + 6px); left: 0; min-width: 260px;
    background: #fff; border: 1px solid #ece9f5; border-radius: 12px; box-shadow: 0 18px 40px rgba(31,18,71,.18); padding: 10px;
}
.ntt-dob__pop.open, .ntt-pick__pop.open { display: block; }
/* Flipped ABOVE the field when there is not enough room below — booking.js
   measures and adds this. Near the foot of the form, which is exactly where the
   last traveller's card sits, dropping downwards would put the calendar behind
   the submit button or off-screen. */
.ntt-dob__pop.is-above, .ntt-pick__pop.is-above { top: auto; bottom: calc(100% + 6px); }
.ntt-dob__bar { display: flex; gap: 8px; margin-bottom: 8px; }
.ntt-dob__sel { flex: 1; height: 34px; border: 1px solid #e2ddf0; border-radius: 8px; font: inherit; background: #fff; }
.ntt-dob__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.ntt-dob__dow { text-align: center; font-size: 11px; color: #8a8a99; padding: 4px 0; }
.ntt-dob__pad { height: 32px; }
.ntt-dob__day { height: 32px; border: 0; border-radius: 8px; background: none; font: inherit; font-size: 13px; cursor: pointer; }
.ntt-dob__day:hover:not(:disabled) { background: #f2edff; }
.ntt-dob__day.is-on { background: #6d28d9; color: #fff; }
.ntt-dob__day:disabled { color: #cfcfda; cursor: default; }

/* ---- Custom dropdown ---------------------------------------------------- */
.ntt-pick__pop { min-width: 100%; padding: 6px; }
.ntt-pick__item { display: block; width: 100%; text-align: left; padding: 9px 10px; border: 0; border-radius: 8px; background: none; font: inherit; font-size: 13.5px; cursor: pointer; }
.ntt-pick__item:hover { background: #f2edff; }
.ntt-pick__item.is-on { background: #6d28d9; color: #fff; }


/* ---- Booking: final confirmation dialog --------------------------------
   Uses the site's shared .modal shell (backdrop, animation, Escape), so it
   looks and behaves like the auth and review dialogs rather than like a
   third-party alert box. */
.booking-confirm__card { max-width: 460px; padding: 26px; }
.booking-confirm__title { font-size: 20px; margin: 0 0 6px; }
.booking-confirm__lead { color: #6b6b7b; font-size: 13.5px; margin: 0 0 16px; }
.booking-confirm__facts { margin: 0 0 20px; display: grid; gap: 10px; }
.booking-confirm__facts > div { display: grid; grid-template-columns: 92px 1fr; gap: 10px; align-items: start; }
.booking-confirm__facts dt { color: #8a8a99; font-size: 12px; }
.booking-confirm__facts dd { margin: 0; font-size: 13.5px; font-weight: 600; word-break: break-word; }
.booking-confirm__actions { display: flex; gap: 10px; }
.booking-confirm__back, .booking-confirm__go {
    flex: 1; height: 46px; border-radius: 10px; border: 0; cursor: pointer; font: inherit; font-weight: 700;
}
.booking-confirm__back { background: #f2f2f7; color: #3a3a4a; }
.booking-confirm__go { background: linear-gradient(90deg, var(--grad-topbar-2, #6d28d9), var(--color-primary-2, #8b5cf6)); color: #fff; }
.booking-confirm__go:disabled { opacity: .7; cursor: default; }


/* ---- Booking: inline validation messages -------------------------------
   Animated open rather than toggled, so a message appearing does not jolt
   everything below it up the page. */
/* `.booking__field > .booking__err`, NOT `.booking__err`.
   The label rule `.booking__field > span` (0,1,1) outweighs a bare class
   (0,1,0), so the message inherited the label's dark grey and every error
   rendered near-black. Matching the parent here wins the cascade honestly,
   without reaching for !important. */
.booking__field > .booking__err,
.booking__err {
    display: block;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    color: #d92020;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 1.35;
    transition: max-height .22s ease, opacity .22s ease, margin-top .22s ease;
}
.booking__field > .booking__err.is-shown,
.booking__err.is-shown { max-height: 60px; opacity: 1; margin-top: 5px; }
.booking__err.is-shown::before { content: "\26A0"; margin-right: 5px; }

/* The field itself is marked too, so the eye finds it without reading. */
.booking__field.has-error input,
.booking__field.has-error .ntt-dob__display,
.booking__field.has-error .ntt-pick__display {
    border-color: #d92020;
    box-shadow: 0 0 0 3px rgba(217, 32, 32, .12);
}


/* ---- Calendar: month + year dropdowns -----------------------------------
   Sit between the two arrows, which keep working for single-month steps.

   These are NOT <select> elements. A native select's list is drawn by the
   operating system — a dark macOS menu in one browser, a white list in
   another — so the same page looked like two different products and nothing
   about the list could be themed. These are ordinary buttons, so they render
   identically everywhere.

   Widths are `auto`: a button sizes to its own content, so "September" cannot
   be clipped and no width needs maintaining. (The old <select> DID need one —
   `.flightsearch .seg input, .flightsearch .seg select { width: 100% }`
   stretched it, and a min-width cannot counter a width of 100%. That rule
   does not match a button, so the problem is gone rather than worked around.)

   Scoped with :is(...) so the SAME calendar serves the flight widget and any
   other host — the booking form's date of birth today. :is() keeps the
   specificity the old `.flightsearch` prefix had, so no rule changes rank. */
:is(.flightsearch, .ntt-dp) .dp-sels { display: flex; gap: 6px; align-items: center; }
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel { position: relative; flex: 0 0 auto; }

/* The closed control: text and a chevron, no box. */
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__btn {
    display: flex;
    align-items: center;
    gap: 6px;
    width: auto;
    height: 32px;
    /* Right padding is chevron clearance, nothing more. */
    padding: 0 18px 0 4px;
    border: 0;
    background-color: transparent;
    /* Individual background-* properties, never the `background` shorthand:
       `.flightsearch .seg select` sets `background: transparent`, and a
       shorthand there would wipe this image out. */
    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='%23736f8f' d='M2 4.5 6 8.5 10 4.5z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 2px center;
    background-size: 10px;
    font: inherit;
    font-size: 14px;
    font-weight: 700;
    color: var(--ink);
    white-space: nowrap;
    text-align: left;
    cursor: pointer;
    transition: color 0.15s var(--ease);
}
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__btn:hover { color: var(--v1); }
/* No outline: Chrome treats a click on a control as focus-visible, so the ring
   appeared on plain mouse use. The colour shift keeps a keyboard user
   something to see. */
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__btn:focus,
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__btn:focus-visible { outline: none; color: var(--v1); }

/* The open list. Drawn by the page, so it looks the same in every browser. */
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__pop {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 5;
    display: none;
    min-width: 100%;
    /* A hundred years of birth dates has to scroll rather than run off. */
    max-height: 210px;
    overflow-y: auto;
    padding: 4px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 10px;
    box-shadow: 0 18px 36px -14px rgba(34, 28, 70, 0.45);
}
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel.open .dp-sel__pop { display: block; }
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__item {
    display: block;
    width: 100%;
    padding: 7px 10px;
    border: 0;
    border-radius: 7px;
    background: transparent;
    font: inherit;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ink);
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}
/* :not(.is-on) matters. `:hover:not(:disabled)` is (0,3,0) and `.is-on` is
   (0,2,0), so hovering the SELECTED row swapped its solid fill for a faint
   tint while its white text stayed put — an apparently blank row. */
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__item:hover:not(:disabled):not(.is-on) {
    background: var(--accent-soft);
    color: var(--v1);
}
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__item.is-on,
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__item.is-on:hover {
    background: var(--v1);
    color: #fff;
}
/* A month outside the allowed range — before `min`, or after `max`. */
:is(.flightsearch, .ntt-dp) .dp-sels .dp-sel__item:disabled { color: var(--muted); cursor: default; }

/* ---- Calendar tokens for hosts OTHER than the flight widget -------------
   The calendar's colours read --v1, --ink, --line, --surface and friends, and
   those are declared on `.flightsearch` ITSELF, not on :root. Anywhere else
   every var() resolved to nothing: `background: var(--v1)` fell back to
   transparent, so the SELECTED month rendered white-on-white — a blank row.

   Same values as the .flightsearch block, so one calendar looks identical
   wherever it is mounted. */
.ntt-dp {
    --ink: #221c46;
    --muted: #9a93bc;
    --line: rgba(34, 28, 70, 0.12);
    --surface: #ffffff;
    --v1: var(--grad-btn-2);
    --v2: var(--color-primary);
    --accent-soft: rgba(109, 40, 217, 0.1);
    --ease: cubic-bezier(0.2, 0.9, 0.25, 1);
}
[data-theme="dark"] .ntt-dp {
    --ink: #ece9f6;
    --muted: #8e88a8;
    --line: rgba(255, 255, 255, 0.14);
    --surface: #211d2c;
    --v1: var(--color-primary);
    --v2: var(--color-primary-2);
}

/* The shared picker flags an empty value on the WRAPPER, not the display, so
   the "Select date" placeholder needs this second form to stay grey. */
.ntt-dp.muted-val .ntt-dob__display { color: #9a9aab; }


/* ---- Contact information (asked once, for the whole booking) ------------ */
.booking__contact {
    border: 1px solid var(--color-primary);
    border-radius: 16px;
    background: #fff;
    padding: 16px;
    margin-bottom: 12px;
    /* Above the traveller cards below it, so the dial-code list is never
       painted under the first card — the same reason those cards lift while a
       popover is open. */
    position: relative;
    z-index: 6;
}
/* Same ceiling as the traveller cards: above the page, under the header. */
.booking__contact.pick-open,
.booking__contact:has(.pick-open) { z-index: 20; }
.booking__contact-title { margin: 0 0 4px; font-size: 15px; font-weight: 800; color: #21212e; }
.booking__contact .booking__hint { margin: 0 0 12px; color: #6a6a7a; font-size: 13px; }

/* Dial code + number: ONE control to the eye, two inputs to the form.

   The border, radius and focus ring belong to the WRAPPER; the code button and
   the number field inside are stripped bare and separated by a hairline. Two
   separately-bordered boxes with a gap read as two questions — it is one phone
   number. */
.booking__phone {
    display: flex;
    align-items: stretch;
    height: 44px;
    border: 1px solid #e2ddf0;
    border-radius: 10px;
    background: #fff;
    /* *** NO `overflow: hidden` HERE. ***
       The dial-code panel is a popover INSIDE this wrapper, and an ancestor's
       overflow clips a popover no matter how high its z-index is. The button
       rounds its own left corners instead — same result, nothing clipped. */
}
/* :focus-within, so tabbing into EITHER half rings the whole control — the
   same ring the other fields get on focus. */
.booking__phone:focus-within { outline: 2px solid #7c3aed; outline-offset: 1px; border-color: transparent; }
.booking__phone:hover { border-color: #c9bff0; }
/* Out-specifies `.booking__field input` (0,1,1) so the number field gives up
   its own border, radius and ring to the wrapper. */
.booking__phone > input[type="tel"] {
    flex: 1 1 auto;
    min-width: 0;
    height: 100%;
    border: 0;
    border-radius: 0;
    background: transparent;
}
.booking__phone > input[type="tel"]:focus { outline: none; }
.ntt-dial { position: relative; flex: 0 0 auto; display: flex; }
.ntt-dial__display {
    height: 100%; padding: 0 10px 0 12px; min-width: 78px;
    border: 0;
    /* The hairline IS the join between the two halves. */
    border-right: 1px solid #e2ddf0;
    /* Matches the wrapper's radius so the button sits flush in the corner —
       see the note above about not clipping with overflow. */
    border-radius: 9px 0 0 9px;
    background: transparent;
    font: inherit; font-weight: 700; color: #21212e; cursor: pointer; text-align: left;
    display: flex; align-items: center; gap: 6px;
}
.ntt-dial__display::after {
    content: "";
    width: 7px; height: 7px;
    border-right: 2px solid #8a8a99; border-bottom: 2px solid #8a8a99;
    transform: translateY(-2px) rotate(45deg);
    flex: 0 0 auto;
}
.ntt-dial__display:hover { color: #6d28d9; }
.ntt-dial__display:focus,
.ntt-dial__display:focus-visible { outline: none; }
/* The panel hangs off the whole control, not just the button. */
.ntt-dial__pop { margin-top: 2px; }
.ntt-dial__pop {
    display: none; position: absolute; z-index: 60; top: calc(100% + 6px); left: 0;
    width: 300px; max-width: 78vw;
    background: #fff; border: 1px solid #ece9f5; border-radius: 12px;
    box-shadow: 0 18px 40px rgba(31, 18, 71, .18); padding: 8px;
}
.ntt-dial__pop.open { display: block; }
/* Flipped above when there is no room below — booking.js measures and adds
   this, exactly as it does for the other popovers. */
.ntt-dial__pop.is-above { top: auto; bottom: calc(100% + 6px); }
.ntt-dial__search { padding: 2px 2px 8px; }
.ntt-dial__search input {
    width: 100%; height: 36px; padding: 0 10px;
    border: 1px solid #e2ddf0; border-radius: 8px; font: inherit; color: #21212e;
}
.ntt-dial__search input:focus { outline: 2px solid #7c3aed; outline-offset: 1px; border-color: transparent; }
/* 195 countries have to scroll rather than run off the page. */
.ntt-dial__list { max-height: 240px; overflow-y: auto; }
.ntt-dial__item {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 8px 10px; border: 0; border-radius: 8px; background: transparent;
    font: inherit; font-size: 13.5px; color: #21212e; text-align: left; cursor: pointer;
}
.ntt-dial__item:hover:not(.is-on) { background: rgba(124, 58, 237, .1); color: #6d28d9; }
.ntt-dial__item.is-on, .ntt-dial__item.is-on:hover { background: #7c3aed; color: #fff; }
.ntt-dial__name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ntt-dial__code { flex: 0 0 auto; font-weight: 700; opacity: .85; }
.ntt-dial__empty { margin: 0; padding: 10px; color: #8a8a99; font-size: 13px; }

@media (max-width: 560px) {
    /* NOT flex-wrap: the two halves are one control now, and wrapping would
       split it across two lines with a stray border. */
    .ntt-dial__display { min-width: 70px; padding: 0 8px 0 10px; }
}


/* ---- Title radios ------------------------------------------------------- */
/* *** NAMED .booking__radio, NOT .booking__title. ***

   `.booking__title` is ALREADY the page heading's class
   (`font-size: clamp(24px,3vw,34px); margin: 14px 0 6px`). Reusing it for the
   radio labels gave every button that 14px top margin, which is what pushed
   the buttons away from their own label — and pushed this block's `position:
   relative` onto the <h1> in return. Two different things, two names.

   The row is a CONTROL, so it matches the other controls' geometry: the same
   5px under its label and the same 44px height as an input beside it. */
.booking__radios { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; }
.booking__radio { position: relative; margin: 0; }
/* The input stays in the DOM and focusable — hiding it with display:none
   would take it out of the tab order and off the accessibility tree. */
.booking__radio input {
    position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer;
}
.booking__radio span {
    display: flex; align-items: center; justify-content: center;
    min-width: 62px; height: 44px; padding: 0 16px;
    border: 1px solid #e2ddf0; border-radius: 10px; background: #fff;
    font-size: 13.5px; font-weight: 700; color: #4a4a5a; transition: all .15s ease;
}
.booking__radio input:hover + span { border-color: #c9bff0; }
.booking__radio input:checked + span { border-color: var(--grad-btn-1, #7c3aed); background: #7c3aed; color: #fff; }
.booking__radio input:focus-visible + span { outline: 2px solid #7c3aed; outline-offset: 2px; }

/* ---- "No surname" switch ------------------------------------------------ */
/* Kept for any label that still pairs a heading with a control; the surname
   switch no longer uses it (it sits under its field instead). */
.booking__label-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.booking__switch--under { margin-top: 8px; }
.booking__switch { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; font-weight: 600; }
.booking__switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.booking__switch-track {
    width: 34px; height: 20px; border-radius: 999px; background: #ddd8ec;
    position: relative; transition: background .18s ease; flex: 0 0 auto;
}
.booking__switch-dot {
    position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
    background: #fff; box-shadow: 0 1px 3px rgba(31, 18, 71, .3); transition: transform .18s ease;
}
.booking__switch input:checked + .booking__switch-track { background: var(--grad-btn-1, var(--grad-btn-1, #7c3aed)); }
.booking__switch input:checked + .booking__switch-track .booking__switch-dot { transform: translateX(14px); }
.booking__switch input:focus-visible + .booking__switch-track { outline: 2px solid #7c3aed; outline-offset: 2px; }
.booking__switch-text { font-size: 12.5px; color: #6a6a7a; }
/* Disabled by the switch — greyed, but still visible, so it is obvious the
   field was turned off rather than removed. */
.booking__field input.is-disabled { background: #f6f5fa; color: #9a9aab; cursor: not-allowed; }

/* ---- Passport photo ----------------------------------------------------- */
.booking__file { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.booking__file-btn {
    position: relative; display: inline-flex; align-items: center; gap: 8px;
    padding: 10px 16px; border: 1px dashed color-mix(in srgb, var(--color-primary, #6f48a9) 30%, #fff); border-radius: 10px;
    background: #faf8ff; font-size: 13.5px; font-weight: 700; color: var(--grad-topbar-2, #6d28d9); cursor: pointer;
}
.booking__file-btn:hover { border-color: var(--grad-btn-1, #7c3aed); background: #f3eeff; }
/* *** [hidden] MUST WIN. ***
   `display: flex` on the class beat the hidden attribute's UA `display: none`,
   so the empty preview rendered anyway — a broken-image icon and a "Remove"
   link for a file nobody had chosen. */
.booking__file-preview[hidden] { display: none; }
.booking__file-preview {
    display: flex; align-items: center; gap: 12px;
    padding: 8px 12px 8px 8px;
    border: 1px solid #ece9f5; border-radius: 12px; background: #fbfaff;
}
.booking__file-preview img {
    width: 56px; height: 56px; object-fit: cover;
    border-radius: 9px; border: 1px solid #e2ddf0; background: #fff;
    /* Nothing to show is better than a torn-page icon if a thumbnail ever
       fails to decode. */
    color: transparent;
}
.booking__file-meta { display: flex; flex-direction: column; gap: 3px; font-size: 12.5px; min-width: 0; }
.booking__file-name {
    max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    color: #21212e; font-weight: 700;
}
.booking__file-ok { color: #1f9254; font-size: 11.5px; font-weight: 700; }
.booking__file-remove {
    border: 0; background: transparent; padding: 0; text-align: left;
    font: inherit; font-size: 12px; color: #c22f6b; cursor: pointer;
}
.booking__file-remove:hover { text-decoration: underline; }
.booking__file-hint { margin: 6px 0 0; font-size: 12px; color: #8a8a99; }
/* While the browser is converting a HEIC the field says so, and the form's
   submit is disabled — see setBusy() in booking.js. */
.booking__field.is-converting .booking__file-btn { opacity: .6; pointer-events: none; }
.booking__field.is-converting .booking__file-hint::after {
    content: " Converting photo…"; color: #6d28d9; font-weight: 700;
}

/* The country picker reuses the dial-code widget but has no code column, so
   it needs room for a full country name. */
.ntt-country { width: 100%; }
.ntt-country .ntt-dial__display {
    width: 100%; min-width: 0; height: 44px;
    border: 1px solid #e2ddf0; border-radius: 10px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ntt-country .ntt-dial__display.is-empty { color: #9a9aab; font-weight: 600; }
.ntt-country .ntt-dial__pop { width: 320px; max-width: 90vw; }


/* ---- Airline mark on each segment --------------------------------------
   A rounded square, sized in CSS whether or not an image loads, so a slow or
   missing logo cannot shift the row it sits in. */
.booking__seg-logo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex: 0 0 auto;
    margin-right: 8px;
    border-radius: 7px;
    border: 1px solid #ece9f5;
    background: #fff;
    overflow: hidden;
    vertical-align: middle;
}
/* contain, never cover: a carrier's mark must not be cropped to fill a square. */
.booking__seg-logo img { width: 100%; height: 100%; object-fit: contain; padding: 3px; }
.booking__seg-mono {
    font-size: 9.5px; font-weight: 800; letter-spacing: .02em;
    color: var(--grad-topbar-2, #6d28d9); line-height: 1;
}
/* The name line becomes a row so the mark sits beside the text rather than
   pushing it onto its own line. */
.booking__seg-airline { display: inline-flex; align-items: center; }


/* ---- Consent row -------------------------------------------------------
   Sits directly above the submit button it gates. Small text, because it is a
   confirmation rather than a heading — but the LINKS stay full-contrast and
   underlined, since a legal link nobody can see is not much of a link. */
.booking__consent {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-top: 4px;
    cursor: pointer;
}
/* The real input stays in the DOM and focusable — display:none would take it
   out of the tab order and off the accessibility tree. */
.booking__consent input {
    position: absolute;
    opacity: 0;
    width: 18px;
    height: 18px;
    margin: 0;
    cursor: pointer;
}
.booking__consent-box {
  user-select: none;
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
    margin-top: 0px;
    border: 1.5px solid #d5cfe8;
    border-radius: 5px;
    background: #fff;
    position: relative;
    transition: background .15s ease, border-color .15s ease;
}
/* The tick is drawn, not an image: one less request and it inherits nothing. */
.booking__consent-box::after {
    content: "";
    position: absolute;
    left: 5px; top: 1px;
    width: 5px; height: 10px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: rotate(45deg) scale(0);
    transition: transform .15s ease;
}
.booking__consent input:checked + .booking__consent-box {
    background: var(--grad-btn-1, #7c3aed);
    border-color: #7c3aed;
}
.booking__consent input:checked + .booking__consent-box::after { transform: rotate(45deg) scale(1); }
.booking__consent input:focus-visible + .booking__consent-box { outline: 2px solid #7c3aed; outline-offset: 2px; }
.booking__consent:hover .booking__consent-box { border-color: #b7a8ef; }

.booking__consent-text {
    font-size: 12.5px;
    line-height: 1.5;
    color: #6a6a7a;
}
.booking__consent-text a {
    color: var(--grad-topbar-2, #6d28d9);
    font-weight: 700;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.booking__consent-text a:hover { color: var(--grad-deep, var(--grad-deep, #5b21b6)); }

/* Consent + message + button as ONE block, so the form's 16px grid gap applies
   once around them instead of between each. Everything inside is set here. */
.booking__submit-block {
    display: grid;
    gap: 10px;
    /* Keeps the 28px rhythm from the last traveller card (16px grid + 12px),
       which the button used to carry itself. */
    margin-top: 12px;
}
/* The button's own spacing now belongs to the block. */
.booking__submit-block .booking__submit { margin-top: 0; }

/* The message sits WITH its checkbox, not as a sibling of it: collapsed it adds
   nothing, and open it pushes only the button down. */
.booking__consent-wrap { display: block; }
.booking__err--consent { margin-top: 0; }
.booking__err--consent.is-shown { margin-top: 4px; }

/* The field itself is marked when it fails, like every other input. */
.booking__consent.has-error .booking__consent-box { border-color: #d92020; }

/* ============================================================
   BOOKING REQUEST — DARK MODE

   This module shipped light-only: 105 colour declarations, not one of them
   inside a [data-theme="dark"] block. On a dark page its three cards stayed
   full white, and every label written as a dark grey on white became grey on
   near-black.

   Nothing above is edited. These rules only apply when the visitor is in dark
   mode, so the light design is exactly as it was, and they are written in the
   theme's own tokens rather than in new greys — a fourth palette invented here
   would drift from the rest of the site the first time either changed.

   THE SURFACES ARE LAYERED, not flat: the page, then the card, then the fields
   inside it. A single dark grey for all three loses the edges that tell a form
   apart from what it sits on.
   ============================================================ */

/* ---- the three cards ---- */
[data-theme="dark"] .booking__card,
[data-theme="dark"] .booking__block,
[data-theme="dark"] .booking__contact,
[data-theme="dark"] .booking__pax {
  background: var(--surface);
  border-color: var(--color-border);
}
[data-theme="dark"] .booking__block { border: 1px solid var(--color-border); }

/* ---- recessed surfaces inside a card: segment rows, previews, thumbnails ---- */
[data-theme="dark"] .booking__segments li,
[data-theme="dark"] .booking__file-preview,
[data-theme="dark"] .booking__seg-logo,
[data-theme="dark"] .booking__file-preview img {
  background: var(--surface-2);
  border-color: var(--color-border);
}

/* ---- form controls ---- */
[data-theme="dark"] .booking__field input,
[data-theme="dark"] .booking__field select,
[data-theme="dark"] .booking__field textarea,
[data-theme="dark"] .booking__phone,
[data-theme="dark"] .booking__radio span,
[data-theme="dark"] .booking__consent-box {
  background: var(--surface-2);
  border-color: var(--color-border);
  color: var(--color-text);
}
/* The number field is a bare input inside .booking__phone, so it must not
   paint its own ground over the wrapper's. */
[data-theme="dark"] .booking__phone input { background: transparent; }
[data-theme="dark"] .booking__field input::placeholder,
[data-theme="dark"] .booking__field textarea::placeholder { color: var(--color-text-muted); }
[data-theme="dark"] .booking__field input.is-disabled {
  background: var(--surface-3);
  color: var(--color-text-muted);
}
/* The tick is drawn as a border on ::after — white on the brand fill, which is
   what it already is, so only the unchecked box needs restating. */
[data-theme="dark"] .booking__switch-track { background: var(--surface-3); }

/* ---- headings, body text, quiet text ---- */
[data-theme="dark"] .booking__point span,
[data-theme="dark"] .booking__facts strong,
[data-theme="dark"] .booking__contact-title,
[data-theme="dark"] .booking__file-name,
[data-theme="dark"] .booking__ref strong { color: var(--heading); }

[data-theme="dark"] .booking__field > span,
[data-theme="dark"] .booking__seg-route,
[data-theme="dark"] .booking__radio span { color: var(--color-text); }

[data-theme="dark"] .booking__lead,
[data-theme="dark"] .booking__leg-head span,
[data-theme="dark"] .booking__point small,
[data-theme="dark"] .booking__duration,
[data-theme="dark"] .booking__stops,
[data-theme="dark"] .booking__seg-airline em,
[data-theme="dark"] .booking__facts span,
[data-theme="dark"] .booking__price span,
[data-theme="dark"] .booking__price small,
[data-theme="dark"] .booking__price--quote strong,
[data-theme="dark"] .booking__note,
[data-theme="dark"] .booking__hint,
[data-theme="dark"] .booking__contact .booking__hint,
[data-theme="dark"] .booking__consent-text,
[data-theme="dark"] .booking__switch-text,
[data-theme="dark"] .booking__file-hint,
[data-theme="dark"] .booking__pax-state,
[data-theme="dark"] .booking__ref,
[data-theme="dark"] .booking-confirm__lead,
[data-theme="dark"] .booking-confirm__facts dt { color: var(--color-text-muted); }

[data-theme="dark"] .booking__chip--soft {
  background: var(--surface-3);
  color: var(--color-text-muted);
}
[data-theme="dark"] .booking__pax-caret { border-bottom-color: var(--color-text-muted); }

/* ---- status colours: lifted for a dark ground, not reused from light ----
   #a06a12 on near-black is 2.4:1 and #8d1c45 is worse. These are the same
   hues taken up to where they read. */
[data-theme="dark"] .booking__seg-layover { color: #e0a83c; }
[data-theme="dark"] .booking__alert--error {
  background: rgba(194, 47, 107, 0.16);
  border-color: rgba(194, 47, 107, 0.42);
  color: #ff92b6;
}
[data-theme="dark"] .booking__err,
[data-theme="dark"] .booking__field > .booking__err { color: #ff92b6; }
[data-theme="dark"] .booking__field.has-error input,
[data-theme="dark"] .booking__field.has-error select,
[data-theme="dark"] .booking__consent.has-error .booking__consent-box { border-color: #ff92b6; }
[data-theme="dark"] .booking__done-mark {
  background: rgba(18, 138, 77, 0.18);
  color: #56dfa2;
}
[data-theme="dark"] .booking__file-ok { color: #56dfa2; }
[data-theme="dark"] .booking__file-remove { color: #ff92b6; }

/* ---- the upload button, whose light ground has to become a dark one ---- */
[data-theme="dark"] .booking__file-btn {
  background: color-mix(in srgb, var(--color-primary, #6f48a9) 16%, var(--surface));
  border-color: color-mix(in srgb, var(--color-primary, #6f48a9) 45%, var(--surface));
  color: var(--color-primary-2, #9a6ac2);
}
[data-theme="dark"] .booking__file-btn:hover {
  background: color-mix(in srgb, var(--color-primary, #6f48a9) 26%, var(--surface));
}
[data-theme="dark"] .booking__chip {
  background: color-mix(in srgb, var(--color-primary, #6f48a9) 22%, var(--surface));
  color: var(--color-primary-2, #9a6ac2);
}
[data-theme="dark"] .booking__price strong,
[data-theme="dark"] .booking__seg-mono,
[data-theme="dark"] .booking__consent-text a { color: var(--color-primary-2, #9a6ac2); }

/* ---- the back button on the confirm screen ---- */
[data-theme="dark"] .booking-confirm__back {
  background: var(--surface-3);
  color: var(--color-text);
}

/* ---- the IATA code -------------------------------------------------------
   `.booking__point strong` declares no colour of its own, so it inherits — and
   what it inherited was a light-mode literal, which left it at 1.12:1 on the
   dark card. Named explicitly rather than relying on inheritance. */
[data-theme="dark"] .booking__point strong { color: var(--heading); }

/* ---- date / phone pickers -----------------------------------------------
   Custom components, not <input>s, so the field rules above do not reach them
   and they stayed full white in the middle of a dark form — the one thing on
   this card that still looked like a light page.

   Their popups are given the RAISED surface rather than the field surface: a
   panel that floats over the form should not be the same colour as the thing
   it covers. */
[data-theme="dark"] .ntt-dob__display,
[data-theme="dark"] .ntt-pick__display,
[data-theme="dark"] .ntt-dial__display,
[data-theme="dark"] .ntt-dob__sel,
[data-theme="dark"] .ntt-dial__search input {
  background: var(--surface-2);
  border-color: var(--color-border);
  color: var(--color-text);
}
[data-theme="dark"] .ntt-dial__display { background: transparent; }
[data-theme="dark"] .ntt-dob__display.is-empty,
[data-theme="dark"] .ntt-pick__display.is-empty,
[data-theme="dark"] .ntt-dial__display.is-empty,
[data-theme="dark"] .ntt-dob__display { color: var(--color-text-muted); }

[data-theme="dark"] .ntt-dob__pop,
[data-theme="dark"] .ntt-pick__pop,
[data-theme="dark"] .ntt-dial__pop {
  background: var(--surface-3);
  border-color: var(--color-border);
}
[data-theme="dark"] .ntt-dob__dow,
[data-theme="dark"] .ntt-dial__empty { color: var(--color-text-muted); }
[data-theme="dark"] .ntt-dob__day,
[data-theme="dark"] .ntt-pick__item,
[data-theme="dark"] .ntt-dial__item { color: var(--color-text); }
[data-theme="dark"] .ntt-dob__day:disabled { color: color-mix(in srgb, var(--color-text-muted) 45%, transparent); }
[data-theme="dark"] .ntt-dob__day:hover:not(:disabled),
[data-theme="dark"] .ntt-pick__item:hover,
[data-theme="dark"] .ntt-dial__item:hover:not(.is-on) {
  background: color-mix(in srgb, var(--color-primary, #6f48a9) 26%, var(--surface-3));
  color: var(--color-text);
}

/* The selected day/item keeps the brand fill it already has — white on it
   reads in either theme, so only the colour source is made dynamic. */
.ntt-dob__day.is-on,
.ntt-pick__item.is-on { background: var(--grad-topbar-2, #6d28d9); }
.ntt-dial__item.is-on,
.ntt-dial__item.is-on:hover { background: var(--grad-btn-1, #7c3aed); }
.ntt-dial__display:hover,
.ntt-dial__item:hover:not(.is-on) { color: var(--grad-topbar-2, #6d28d9); }

/* The required-field marker. #c22f6b is 3.06:1 on the dark card — the same
   pink lifted for a dark ground that the contact form and the booking errors
   use, so "required" looks like one thing across the site. */
[data-theme="dark"] .booking__field i { color: #ff92b6; }

/* ---------- Phone filter sheet ----------------------------------------------

   The chip row + Sort control are a desktop pattern: eight chips that scroll
   sideways, each opening a popover positioned beside it. On a phone the row is
   mostly off screen and a popover has nowhere to go, so below 880px both are
   replaced by one full-screen surface holding every filter, stacked.

   Colours come from the theme tokens the admin publishes (--color-primary,
   --surface, --heading …), so a site that rebrands gets this for free rather
   than keeping a second palette in step. */

.flightl__statusrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* Desktop keeps the chips; the button is phone-only. */
.flightl__filterbtn { display: none; }

.ffsheet { display: none; }

@media (max-width: 880px) {
  /* The chip row and the Sort control move INTO the sheet, so they are not
     rendered twice. display:none rather than removing them from the markup:
     the chips are still the elements refreshChipLabels() writes badges to, and
     the <select> is still the one source of truth for sorting. */
  .flightf { display: none; }

  /* Same trap as .ffsheet below: this display rule outranks the browser's
     [hidden]{display:none}, so without the guard the button shows before there
     are any results to filter. */
  .flightl__filterbtn[hidden] { display: none; }
  .flightl__filterbtn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex: 0 0 auto;
    padding: 8px 14px;
    border: 1px solid var(--color-primary);
    border-radius: 999px;
    background: var(--surface);
    color: var(--color-primary);
    font-size: .82rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
  }
  /* Third time for this trap: the display rule below outranks the browser's
     [hidden]{display:none}, so without this the badge renders as an empty
     purple dot whenever no filters are set — a mark with nothing to say. */
  .flightl__filterbtn-badge[hidden] { display: none; }
  .flightl__filterbtn-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--color-primary);
    color: #fff;
    font-size: .68rem;
    font-weight: 800;
  }

  .ffsheet {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 120;            /* above the sticky header (50) and chat (96) */
  }
  /* `display:block` above outranks the browser's own [hidden]{display:none} —
     same specificity, and this rule comes later — so without this the closed
     sheet stays in the layout and its full-screen scrim swallows every tap on
     the results underneath. Invisible, because the scrim is transparent until
     .is-open. */
  .ffsheet[hidden] { display: none; }
  .ffsheet__scrim {
    position: absolute;
    inset: 0;
    background: rgba(17, 12, 30, .55);
    opacity: 0;
    transition: opacity .22s ease;
  }
  .ffsheet.is-open .ffsheet__scrim { opacity: 1; }

  /* A near-full-height sheet rather than a true full screen: the strip of
     scrim left at the top is what says "this is a layer over the results" and
     gives a thumb somewhere to dismiss it. */
  .ffsheet__panel {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    max-height: 92vh;
    max-height: 92dvh;       /* dvh so the mobile URL bar cannot clip the footer */
    background: var(--surface);
    border-radius: 20px 20px 0 0;
    transform: translateY(100%);
    transition: transform .26s cubic-bezier(.22, .7, .28, 1);
  }
  .ffsheet.is-open .ffsheet__panel { transform: none; }

  .ffsheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 18px 12px;
    border-bottom: 1px solid var(--color-border, rgba(0,0,0,.08));
  }
  .ffsheet__title { margin: 0; font-size: 1.05rem; font-weight: 800; color: var(--heading); }
  .ffsheet__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: 0;
    border-radius: 50%;
    background: var(--color-bg-alt, rgba(0,0,0,.05));
    color: var(--heading);
    cursor: pointer;
  }

  /* The only scrolling part. -webkit-overflow-scrolling for momentum on iOS,
     overscroll-behavior so flicking past the end does not drag the page
     underneath the sheet. */
  .ffsheet__body {
    flex: 1 1 auto;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding: 4px 18px 18px;
  }

  .ffsheet__sec { padding: 16px 0; border-bottom: 1px solid var(--color-border, rgba(0,0,0,.07)); }
  .ffsheet__sec:last-child { border-bottom: 0; }
  .ffsheet__sec-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 10px;
    font-size: .95rem;
    font-weight: 800;
    color: var(--heading);
  }
  .ffsheet__sec-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--color-primary) 14%, transparent);
    color: var(--color-primary);
    font-size: .7rem;
    font-weight: 700;
  }

  /* The panel markup is shared with the desktop popover, which positions and
     sizes itself. Inside the sheet it must simply fill the width. */
  .ffsheet__sec-body .flightf__pop-inner,
  .ffsheet__sec-body > * { width: 100%; max-width: 100%; }

  /* FIVE ROWS, THEN SCROLL.

     A Dhaka route lists a dozen carriers and as many connecting airports;
     unbounded, those two sections pushed Price, Times, Duration and Bags so far
     down the sheet that nobody found them. Capping at five keeps every filter
     reachable and still shows enough of each list to make it obvious it
     scrolls.

     Measured in rows rather than a round pixel number: --ffrow is the real
     height of a row, so the cut always lands BETWEEN rows. A fixed max-height
     slices the sixth one in half at one font size and shows five and a bit at
     another.

     SHEET ONLY. The desktop popover already scrolls as a whole
     (.flightf__pop, max-height:320px) and a scroller inside a scroller traps
     the wheel over the inner one. */
  .ffsheet__sec-body .flightf__scroll {
    /* A STARTING VALUE ONLY — capScrollLists() measures the real rows and
       overwrites this inline. Row height is not knowable here: an airline row
       carries a logo that may or may not have loaded, and both lists reflow
       with the font size, so any constant shows four rows on one device and six
       on another. Five rows means five rows. */
    max-height: 220px;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    /* Room for the scrollbar so it never sits on the count. */
    padding-right: 2px;
  }

  .ffsheet__opts { display: grid; gap: 2px; }
  .ffsheet__opt {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 2px;
    font-size: .9rem;
    color: var(--color-text);
    cursor: pointer;
  }
  .ffsheet__opt input { accent-color: var(--color-primary); width: 18px; height: 18px; }

  .ffsheet__foot {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 18px calc(12px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--color-border, rgba(0,0,0,.08));
    background: var(--surface);
  }
  .ffsheet__clear {
    flex: 0 0 auto;
    padding: 12px 16px;
    border: 1px solid var(--color-border, rgba(0,0,0,.14));
    border-radius: 999px;
    background: transparent;
    color: var(--color-text-muted);
    font-size: .85rem;
    font-weight: 700;
    cursor: pointer;
  }
  .ffsheet__apply {
    flex: 1 1 auto;
    padding: 13px 18px;
    border: 0;
    border-radius: 999px;
    background: linear-gradient(135deg, var(--grad-btn-1), var(--grad-btn-2));
    color: #fff;
    font-size: .92rem;
    font-weight: 800;
    cursor: pointer;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ffsheet__scrim,
  .ffsheet__panel { transition: none; }
}
