/**
 * SkyKaro — refinements.css
 * Loaded LAST. The craft layer: typography, depth, motion, water.
 *
 * Everything here is deliberate polish on top of the working components —
 * type scale and tracking, layered shadows instead of single blurs, hairline
 * borders, expo easing, retina-crisp rendering, and the animated lagoon.
 */

/* ══════════════════════════════════════════════════════════
   1. TOKENS — depth, easing, rhythm
═══════════════════════════════════════════════════════════ */
:root {
  /* Layered shadows. One big blur reads as fog; stacked shadows with
     tightening opacity read as an object sitting above a surface. */
  --sk-e1: 0 1px 2px rgba(16,20,42,.04), 0 2px 4px rgba(16,20,42,.04);
  --sk-e2: 0 1px 2px rgba(16,20,42,.05), 0 4px 8px rgba(16,20,42,.05),
           0 8px 16px rgba(16,20,42,.04);
  --sk-e3: 0 2px 4px rgba(16,20,42,.05), 0 8px 16px rgba(16,20,42,.06),
           0 16px 32px rgba(16,20,42,.06);
  --sk-e4: 0 4px 8px rgba(16,20,42,.06), 0 12px 24px rgba(16,20,42,.08),
           0 24px 48px rgba(16,20,42,.10);
  --sk-e5: 0 8px 16px rgba(16,20,42,.08), 0 24px 48px rgba(16,20,42,.12),
           0 48px 88px rgba(16,20,42,.14);

  /* A hairline that stays 1 physical pixel on retina */
  --sk-hairline: rgba(16,20,42,.08);
  --sk-hairline-strong: rgba(16,20,42,.13);

  /* Expo-out: fast start, long soft settle. Reads as expensive. */
  --ease-out: cubic-bezier(.16,1,.3,1);
  --ease-in-out: cubic-bezier(.65,0,.35,1);
  --ease-spring: cubic-bezier(.34,1.56,.64,1);

  --t-fast: 160ms var(--ease-out);
  --t-base: 280ms var(--ease-out);
  --t-slow: 520ms var(--ease-out);

  /* Type scale — 1.25 minor third, fluid */
  --fs-display: clamp(2.75rem, 1.6rem + 4.4vw, 4.75rem);
  --fs-h1:      clamp(2.15rem, 1.5rem + 2.6vw, 3.35rem);
  --fs-h2:      clamp(1.75rem, 1.3rem + 1.9vw, 2.6rem);
  --fs-h3:      clamp(1.2rem, 1.05rem + .6vw, 1.5rem);
  --fs-lead:    clamp(1.02rem, .96rem + .35vw, 1.2rem);
  --fs-body:    .975rem;
  --fs-sm:      .875rem;
  --fs-xs:      .78rem;
}

[data-theme="dark"] {
  --sk-e1: 0 1px 2px rgba(0,0,0,.30), 0 2px 4px rgba(0,0,0,.24);
  --sk-e2: 0 1px 2px rgba(0,0,0,.32), 0 4px 8px rgba(0,0,0,.30), 0 8px 16px rgba(0,0,0,.24);
  --sk-e3: 0 2px 4px rgba(0,0,0,.34), 0 8px 16px rgba(0,0,0,.32), 0 16px 32px rgba(0,0,0,.28);
  --sk-e4: 0 4px 8px rgba(0,0,0,.36), 0 12px 24px rgba(0,0,0,.36), 0 24px 48px rgba(0,0,0,.34);
  --sk-e5: 0 8px 16px rgba(0,0,0,.40), 0 24px 48px rgba(0,0,0,.44), 0 48px 88px rgba(0,0,0,.46);
  --sk-hairline: rgba(255,255,255,.09);
  --sk-hairline-strong: rgba(255,255,255,.15);
}

/* Map the existing shadow tokens onto the layered set so every component
   that already uses them upgrades without being touched individually. */
:root {
  --sk-shadow-xs: var(--sk-e1);
  --sk-shadow-sm: var(--sk-e1);
  --sk-shadow-md: var(--sk-e2);
  --sk-shadow-lg: var(--sk-e3);
  --sk-shadow-xl: var(--sk-e4);
  --sk-shadow-card: var(--sk-e2);
  --sk-shadow-card-hover: var(--sk-e4);
  --sk-border: var(--sk-hairline);
  --sk-border-light: var(--sk-hairline);
}

/* ══════════════════════════════════════════════════════════
   2. RENDERING — the "4k" crispness
═══════════════════════════════════════════════════════════ */
html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

body {
  font-size: var(--fs-body);
  letter-spacing: -0.006em;      /* Inter is slightly loose by default */
  font-feature-settings: 'kern' 1, 'liga' 1, 'calt' 1;
  font-optical-sizing: auto;
}

/* Headings: real optical tracking. Large type needs NEGATIVE tracking or it
   looks airy and amateur; small caps-y labels need positive tracking. */
h1, .hero-title, .section-title {
  letter-spacing: -0.028em;
  font-feature-settings: 'kern' 1, 'liga' 1, 'ss01' 1;
}
h2 { letter-spacing: -0.022em; }
h3, h4 { letter-spacing: -0.014em; }

.section-title { font-size: var(--fs-h2); line-height: 1.08; }
.section-subtitle { font-size: var(--fs-lead); line-height: 1.65; letter-spacing: -0.004em; }

.section-eyebrow {
  font-size: .72rem;
  letter-spacing: .16em;
  font-weight: 700;
}

/* Numerals: tabular + lining everywhere a figure is compared or aligned.
   Proportional digits make price columns look ragged. */
.flight-price, .stat-number, .total-price, .dest-price, .ret-price,
.itin-time, .route-time, .summary-row .value, .ret-time, .seg-times,
.itin-seg-time, td, .stat-box .val {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* Long-form reading measure */
.legal-card p, .post-body p, .faq-answer p {
  max-width: 68ch;
  line-height: 1.75;
}

/* Balance short headings across lines instead of leaving orphans */
h1, h2, h3, .section-title, .hero-title { text-wrap: balance; }
.section-subtitle, .hero-sub, .why-text { text-wrap: pretty; }

/* ══════════════════════════════════════════════════════════
   3. HERO — the lagoon
   Layered caustics drifting over the water, masked away from the sand
   and sky so only the sea moves. All animation is transform/opacity so it
   composites on the GPU and never repaints.
═══════════════════════════════════════════════════════════ */
/* Stacking, bottom to top:
   -4 photo · -3 water FX · -2 readability overlay · -1 bottom fade
   The overlay sits ABOVE the water so the shimmer is dimmed behind the
   headline but stays bright out over the open lagoon. */
.hero-bg      { z-index: -4; }
.hero-overlay { z-index: -2; }
.hero-fade    { z-index: -1; }

.hero-water {
  position: absolute;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  overflow: hidden;
  /* The shoreline runs bottom-left to top-right. Fade the effect out before
     it reaches the beach, and again before the sky. */
  -webkit-mask-image:
    linear-gradient(107deg, #000 0%, #000 46%, rgba(0,0,0,.45) 62%, transparent 74%),
    linear-gradient(to top, #000 0%, #000 55%, transparent 88%);
  mask-image:
    linear-gradient(107deg, #000 0%, #000 46%, rgba(0,0,0,.45) 62%, transparent 74%),
    linear-gradient(to top, #000 0%, #000 55%, transparent 88%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* Caustic light — big soft cells of refracted sunlight */
.hero-water::before,
.hero-water::after {
  content: '';
  position: absolute;
  inset: -30%;
  background-repeat: repeat;
  mix-blend-mode: soft-light;
  will-change: transform;
}

.hero-water::before {
  background-image:
    radial-gradient(58% 42% at 22% 30%, rgba(255,255,255,.42), transparent 62%),
    radial-gradient(48% 36% at 68% 62%, rgba(190,255,246,.36), transparent 66%),
    radial-gradient(40% 52% at 44% 82%, rgba(255,255,255,.30), transparent 64%),
    radial-gradient(36% 30% at 82% 22%, rgba(160,246,255,.28), transparent 60%);
  background-size: 62% 58%, 54% 50%, 46% 62%, 40% 44%;
  animation: causticDrift 26s var(--ease-in-out) infinite alternate;
  opacity: .85;
}

.hero-water::after {
  background-image:
    radial-gradient(44% 30% at 34% 54%, rgba(255,255,255,.34), transparent 64%),
    radial-gradient(52% 38% at 74% 34%, rgba(214,255,250,.28), transparent 68%),
    radial-gradient(30% 40% at 12% 70%, rgba(255,255,255,.26), transparent 62%);
  background-size: 48% 44%, 58% 52%, 36% 48%;
  animation: causticDrift2 34s var(--ease-in-out) infinite alternate;
  opacity: .7;
}

@keyframes causticDrift {
  0%   { transform: translate3d(0,0,0) scale(1); }
  50%  { transform: translate3d(1.6%, -1.1%, 0) scale(1.06); }
  100% { transform: translate3d(-1.3%, 1.5%, 0) scale(1.03); }
}
@keyframes causticDrift2 {
  0%   { transform: translate3d(0,0,0) scale(1.04) rotate(0deg); }
  50%  { transform: translate3d(-2%, 1.2%, 0) scale(1); rotate: .4deg; }
  100% { transform: translate3d(1.8%, -1.6%, 0) scale(1.07); }
}

/* Swell — slow wide bands rolling toward the beach */
.hero-swell {
  position: absolute;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(107deg, #000 0%, #000 44%, transparent 70%);
  mask-image: linear-gradient(107deg, #000 0%, #000 44%, transparent 70%);
}
.hero-swell::before {
  content: '';
  position: absolute;
  inset: -20% -40%;
  background: repeating-linear-gradient(
    100deg,
    transparent 0 44px,
    rgba(255,255,255,.055) 44px 52px,
    transparent 52px 96px
  );
  animation: swell 19s linear infinite;
  will-change: transform;
}
@keyframes swell {
  from { transform: translate3d(0,0,0); }
  to   { transform: translate3d(96px, -18px, 0); }
}

/* Sun glitter — tiny high-frequency sparkle, very low opacity */
.hero-glitter {
  position: absolute;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  opacity: .5;
  -webkit-mask-image: linear-gradient(107deg, #000 0%, #000 40%, transparent 64%);
  mask-image: linear-gradient(107deg, #000 0%, #000 40%, transparent 64%);
  background-image:
    radial-gradient(circle at 18% 62%, rgba(255,255,255,.9) 0, transparent 1.2px),
    radial-gradient(circle at 42% 78%, rgba(255,255,255,.8) 0, transparent 1px),
    radial-gradient(circle at 61% 55%, rgba(255,255,255,.85) 0, transparent 1.1px),
    radial-gradient(circle at 29% 88%, rgba(255,255,255,.75) 0, transparent 1px),
    radial-gradient(circle at 54% 40%, rgba(255,255,255,.8) 0, transparent 1px);
  background-size: 180px 140px, 220px 190px, 160px 170px, 240px 160px, 200px 210px;
  animation: glitter 5.5s steps(2, jump-none) infinite alternate;
}
@keyframes glitter {
  from { opacity: .28; transform: translate3d(0,0,0); }
  to   { opacity: .6;  transform: translate3d(-3px, 2px, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-water::before, .hero-water::after,
  .hero-swell::before, .hero-glitter { animation: none !important; }
  .hero-glitter { opacity: .3; }
}

/* Hero type gets a touch more presence */
.hero-title {
  font-size: var(--fs-display);
  text-shadow: 0 1px 2px rgba(6,10,26,.28), 0 8px 40px rgba(6,10,26,.34);
}
.hero-sub {
  font-size: var(--fs-lead);
  color: rgba(255,255,255,.88);
  text-shadow: 0 1px 16px rgba(6,10,26,.42);
}
.hero-eyebrow {
  background: rgba(255,255,255,.10);
  border-color: rgba(255,255,255,.22);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.16);
  letter-spacing: .01em;
}

/* ══════════════════════════════════════════════════════════
   4. SEARCH CARD — button gets its own column
═══════════════════════════════════════════════════════════ */
.sf-grid {
  /* from · swap · to · depart · return · travelers · SEARCH */
  grid-template-columns: minmax(0,1.05fr) 38px minmax(0,1.05fr) 152px 152px minmax(0,.95fr) auto;
  gap: 10px;
}
/* One-way drops the return date, so the template drops its track with it.
   Without this the six surviving fields auto-place into the seven-track
   grid one column to the left of where they belong. */
.sf-grid--oneway {
  grid-template-columns: minmax(0,1.1fr) 38px minmax(0,1.1fr) 152px minmax(0,1fr) auto;
}
@media (max-width: 1180px) {
  .sf-grid { grid-template-columns: minmax(0,.9fr) 34px minmax(0,.9fr) 140px 140px minmax(0,.9fr) auto; gap: 8px; }
  .sf-grid--oneway { grid-template-columns: minmax(0,1fr) 34px minmax(0,1fr) 140px minmax(0,.95fr) auto; }
}

.sf-submit-field { justify-content: flex-end; }
.sf-search-btn {
  width: auto;
  min-width: 132px;
  padding: 0 26px;
  flex-shrink: 0;
  letter-spacing: -0.005em;
  box-shadow: 0 1px 2px rgba(0,120,96,.20), 0 6px 16px rgba(17,156,135,.30);
  transition: transform var(--t-fast), box-shadow var(--t-base), background var(--t-fast);
}
.sf-search-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0,120,96,.24), 0 10px 26px rgba(17,156,135,.42);
}
.sf-search-btn:active { transform: translateY(0); }

/* Travelers no longer shares its cell */
.sf-pax-row { display: block; }
.sf-pax-wrap { width: 100%; }

/* Glass card: crisper edge, inner top highlight */
.sf-wrap {
  background: rgba(255,255,255,.13);
  border: 1px solid rgba(255,255,255,.24);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.28),
    0 2px 8px rgba(6,10,26,.16),
    0 18px 48px rgba(6,10,26,.30);
  border-radius: 22px;
  padding: 22px;
}

@media (max-width: 980px) {
  .sf-grid {
    grid-template-columns: 1fr 1fr;
    gap: 12px;
  }
  .sf-swap-wrap { display: none; }
  .sf-submit-field { grid-column: 1 / -1; }
  .sf-search-btn { width: 100%; min-width: 0; }
}
@media (max-width: 620px) {
  .sf-grid { grid-template-columns: 1fr; }
  .sf-wrap { padding: 18px; border-radius: 18px; }
}

/* ══════════════════════════════════════════════════════════
   5. SURFACES — hairlines, inner highlight, calmer hovers
═══════════════════════════════════════════════════════════ */
.card, .why-card, .step-card, .review-card, .trust-bar,
.form-card, .summary-card, .filter-card, .flight-card, .blog-card,
.newsletter-section, .faq-item {
  border: 1px solid var(--sk-hairline);
  box-shadow: var(--sk-e2);
  transition: transform var(--t-base), box-shadow var(--t-base), border-color var(--t-base);
}

/* A 1px inner top highlight is what makes a surface read as lit from above */
.why-card, .step-card, .review-card, .trust-bar, .summary-card, .form-card, .flight-card {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.7), var(--sk-e2);
}
[data-theme="dark"] .why-card,
[data-theme="dark"] .step-card,
[data-theme="dark"] .review-card,
[data-theme="dark"] .trust-bar,
[data-theme="dark"] .summary-card,
[data-theme="dark"] .form-card,
[data-theme="dark"] .flight-card {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06), var(--sk-e2);
}

/* Restrained lift — 6px hovers feel jumpy at this card size */
.why-card:hover, .step-card:hover, .review-card:hover {
  transform: translateY(-3px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.7), var(--sk-e4);
  border-color: var(--sk-hairline-strong);
}
[data-theme="dark"] .why-card:hover,
[data-theme="dark"] .step-card:hover,
[data-theme="dark"] .review-card:hover {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06), var(--sk-e4);
}
.dest-card:hover { transform: translateY(-4px); }
.flight-card:hover { transform: none; box-shadow: inset 0 1px 0 rgba(255,255,255,.7), var(--sk-e3); }

/* ══════════════════════════════════════════════════════════
   6. COMPONENT TYPE
═══════════════════════════════════════════════════════════ */
.why-title { font-size: var(--fs-h3); letter-spacing: -0.016em; margin-bottom: 10px; }
.why-text  { font-size: var(--fs-body); line-height: 1.68; color: var(--sk-text-secondary); }

.dest-name  { font-size: 1.35rem; letter-spacing: -0.022em; }
.dest-country { font-size: .8rem; letter-spacing: .01em; opacity: .8; }
.dest-price {
  font-size: .8rem; letter-spacing: -0.01em;
  box-shadow: 0 1px 2px rgba(0,120,96,.24), 0 6px 18px rgba(17,156,135,.34);
}

.stat-number { letter-spacing: -0.035em; }
.stat-label  { font-size: .8rem; letter-spacing: .02em; }

.trust-item { font-size: .85rem; letter-spacing: -0.004em; }

.review-text   { font-size: var(--fs-body); line-height: 1.72; }
.review-author { font-size: .92rem; letter-spacing: -0.01em; }

.flight-price { letter-spacing: -0.03em; }
.airline-name { letter-spacing: -0.012em; }

.btn { letter-spacing: -0.008em; transition: transform var(--t-fast), box-shadow var(--t-base), background var(--t-fast); }
.btn:hover { transform: translateY(-1.5px); }
.btn-primary { box-shadow: 0 1px 2px rgba(0,120,96,.20), 0 6px 16px rgba(17,156,135,.30); }
.btn-primary:hover { box-shadow: 0 2px 4px rgba(0,120,96,.24), 0 10px 26px rgba(17,156,135,.42); }

/* Section rhythm — a little more air between bands */
.section { padding: clamp(64px, 7vw, 104px) 0; }
.section-sm { padding: clamp(44px, 4.5vw, 68px) 0; }
.section-head { margin-bottom: clamp(28px, 3.4vw, 44px); }

/* ══════════════════════════════════════════════════════════
   7. FOOTER
═══════════════════════════════════════════════════════════ */
.sk-footer {
  background:
    linear-gradient(180deg, var(--sk-primary) 0%, #151936 100%);
  border-top: 1px solid rgba(255,255,255,.06);
}
.footer-col h2 { font-size: .74rem; letter-spacing: .15em; }
.footer-col ul li a { font-size: .875rem; transition: color var(--t-fast), transform var(--t-fast); }
.footer-col ul li a:hover { transform: translateX(2px); }
.footer-brand p { font-size: .875rem; line-height: 1.75; }

/* ══════════════════════════════════════════════════════════
   8. HEADER
═══════════════════════════════════════════════════════════ */
.sk-header--solid { box-shadow: var(--sk-e1); }
.sk-header.scrolled { box-shadow: var(--sk-e2); }
.sk-logo { letter-spacing: -0.035em; }
.sk-nav-link { font-size: .875rem; letter-spacing: -0.006em; }
.sk-call-btn {
  letter-spacing: -0.008em;
  box-shadow: 0 1px 2px rgba(0,120,96,.18), 0 4px 12px rgba(17,156,135,.26);
}

/* ══════════════════════════════════════════════════════════
   9. FORM CONTROLS
═══════════════════════════════════════════════════════════ */
.form-input, .form-select, .newsletter-input, .sort-select {
  transition: border-color var(--t-fast), box-shadow var(--t-fast), background var(--t-fast);
  letter-spacing: -0.004em;
}
.form-input:focus, .form-select:focus, .newsletter-input:focus {
  box-shadow: 0 0 0 3.5px rgba(17,156,135,.14);
}
.form-label { font-size: .7rem; letter-spacing: .1em; }

/* Selection colour on brand */
::selection { background: rgba(17,156,135,.26); color: var(--sk-text); }

/* ══════════════════════════════════════════════════════════
   10. HERO OCEAN — WebGL water surface
   js/hero-ocean.js paints the hero photo into a canvas and refracts the
   lagoon through a travelling wave field. Drifting CSS gradients change the
   LIGHT on a surface but never move the surface, so the eye still reads a
   photograph — the refraction is what makes it read as water.

   The canvas draws the photo itself, so when it is live we hide .hero-bg and
   dial the CSS caustics right down (they now only add a little sparkle on
   top). If WebGL is missing, the tab is hidden, or reduced-motion is set,
   .has-ocean is never added and the CSS-only version stays exactly as-is.
═══════════════════════════════════════════════════════════ */
/* The canvas sits in the SAME z-index slot as the photo and immediately after
   it in the DOM, so it paints on top — but the photo underneath is never
   hidden. The context is created with alpha:true and stays fully transparent
   until a frame actually lands, so every failure path (no WebGL, compile
   error, lost context, background-tab load, reduced motion) falls back to the
   untouched photograph with nothing to clean up. */
.hero-ocean {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: -4;
  display: block;
  pointer-events: none;
}

/* The shader derives its caustics and glint from the real wave normals, so the
   CSS gradient layers are not just redundant — they beat against the shader
   with a second, unrelated rhythm and muddy the water. Retire the two big ones
   once the canvas is live and keep only a trace of sparkle. They stay at full
   strength as the fallback whenever .has-ocean is never added. */
.hero.has-ocean .hero-water   { opacity: 0; }
.hero.has-ocean .hero-swell   { opacity: 0; }
.hero.has-ocean .hero-glitter { opacity: .18; }

/* The canvas renders one fixed crop. Leaving heroZoom running would slide the
   photo underneath out of register with it. */
.hero.has-ocean .hero-bg { animation: none; transform: none; }

/* ══════════════════════════════════════════════════════════
   11. ICON SYSTEM
   Inline SVG replacing emoji. Icons inherit currentColor and sit on the
   text baseline, so they always match the weight and colour beside them —
   which emoji, being OS-supplied colour bitmaps, can never do.
═══════════════════════════════════════════════════════════ */
.sk-icon {
  display: inline-block;
  vertical-align: -0.135em;   /* optical baseline, not box-bottom */
  flex-shrink: 0;
  color: inherit;
}
.sk-icon--teal   { color: var(--sk-teal-dark); }
.sk-icon--muted  { color: var(--sk-text-muted); }
.sk-icon--danger { color: #DC2626; }
.sk-icon--warn   { color: #D97706; }
.sk-icon--ok     { color: #059669; }

/* Icons inside a coloured chip/tile centre themselves */
.trust-icon .sk-icon,
.why-icon .sk-icon,
.step-icon .sk-icon,
.footer-contact-icon .sk-icon,
.form-section-title .icon .sk-icon { vertical-align: middle; }

/* Buttons: give the icon a little breathing room from the label */
.btn .sk-icon, .sf-search-btn .sk-icon, .book-btn .sk-icon { margin-right: -2px; }

/* ══════════════════════════════════════════════════════════
   12. DARK MODE — text that used the brand navy as a text colour
   --sk-primary is #1B1F3B (Midnight Indigo). design-system.css never
   overrides it for [data-theme="dark"], which is correct: it is the BRAND
   colour and must stay navy on teal buttons and badges.

   But a dozen pages also used it as a *text* colour on light card
   backgrounds. In dark mode those cards become #1A2040 — so the text was
   navy on navy, roughly 1.01:1, i.e. completely invisible. Legal headings,
   blog article H2s, the about-page stats and the route stat chips were all
   affected.

   Fixed here rather than per page because the offending declarations live
   in per-page inline <style> blocks that load after every stylesheet.
═══════════════════════════════════════════════════════════ */
[data-theme="dark"] .legal-card h2,
[data-theme="dark"] .legal-card h3,
[data-theme="dark"] .post-body h2,
[data-theme="dark"] .post-body h3,
[data-theme="dark"] .stat-chip .s-val,
[data-theme="dark"] .flight-price,
[data-theme="dark"] .summary-total .total-price,
[data-theme="dark"] .confirmation-card h1,
[data-theme="dark"] .auth-card h1,
[data-theme="dark"] .auth-logo,
[data-theme="dark"] .channel-value,
[data-theme="dark"] .price-from,
[data-theme="dark"] .about-stat-num {
  color: var(--sk-text) !important;
}

/* Teal-backed elements keep the navy — navy on teal is correct and high
   contrast in BOTH themes, so those pairings are deliberately not reset. */

/* ══════════════════════════════════════════════════════════
   13. FIXED-HEADER CLEARANCE
   Section 3 sets padding-top on the interior hero blocks so their headings
   clear the 70px fixed header. Several pages then declare
   `padding: 64px 24px` — a SHORTHAND — inside their own inline <style>,
   which loads after this file and resets padding-top back to 64px, putting
   the H1 six pixels underneath the header.

   !important is the right tool here: a shared layer legitimately needs to
   beat per-page shorthand it does not control.
═══════════════════════════════════════════════════════════ */
.about-hero, .blog-hero, .post-hero, .contact-hero, .faq-hero,
.dest-hero, .route-hero, .results-header, .booking-hero, .legal-hero {
  padding-top: calc(var(--sk-header-h) + 40px) !important;
}
@media (max-width: 600px) {
  .about-hero, .blog-hero, .post-hero, .contact-hero, .faq-hero,
  .dest-hero, .route-hero, .results-header, .booking-hero, .legal-hero {
    padding-top: calc(var(--sk-header-h) + 24px) !important;
  }
}

/* ══════════════════════════════════════════════════════════
   14. COOKIE BANNER
   Three stylesheets define #cookie-banner and they disagree:

   - design-system.css  full-width dark bar, hidden with translateY(100%)
   - ui-enhancements.css floating centred white card, `left:50%` +
                         `transform:translateX(-50%)`, hidden with bottom:-120px
   - here               the layout corrections

   The bug that survived: design-system's `#cookie-banner.visible` sets
   `transform: translateY(0)`, and (1,1,0) beats ui-enhancements' plain
   `#cookie-banner` (1,0,0) no matter which file loads later. So the instant
   the banner appeared it dropped translateX(-50%) and jumped half its own
   width to the right — ~110px off-screen at 360px wide, putting "Accept All"
   out of reach. A cookie dialog nobody can accept.

   Fixed by owning BOTH states here (last file wins at equal specificity) and
   driving the reveal off transform alone, so `bottom` is no longer load-
   bearing. bottom:-120px was also too small to hide the stacked mobile
   layout, which is taller than 120px.
═══════════════════════════════════════════════════════════ */
#cookie-banner {
  /* Centred WITHOUT transform. ui-enhancements.css centred it with
     left:50% + translateX(-50%), and that translate loses to design-system's
     `#cookie-banner.visible{transform:translateY(0)}` — same specificity is
     not even required, (1,1,0) beats (1,0,0) regardless of file order. So the
     instant the banner appeared it snapped half its own width to the right.

     left/right + margin-inline:auto is the abspos centring algorithm: width
     resolves to max-width and the leftover is split evenly between the auto
     margins. Nothing in the other two files touches those properties, so this
     cannot be clobbered — and `transform:none` on both states neutralises the
     competing declarations instead of trying to out-rank them. */
  left: 12px;
  right: 12px;
  margin-inline: auto;
  width: auto;
  max-width: 560px;
  transform: none;
  bottom: 8px;
  opacity: 0;
  /* Off-screen but still in the DOM: without this the dialog's two buttons
     stay in the tab order for the 2.2s before it appears, and permanently
     after Decline is clicked. The 0s/.32s delay keeps it focusable for the
     whole fade-out rather than vanishing from the a11y tree mid-transition. */
  visibility: hidden;
  transition: bottom .45s cubic-bezier(.34, 1.28, .64, 1), opacity .32s ease,
              visibility 0s linear .32s;
  padding: 16px clamp(16px, 4vw, 24px);
  gap: 14px;
  /* Hardcoded #fff in ui-enhancements.css made the text — which correctly
     uses var(--sk-text) — white-on-white in dark mode. */
  background: var(--sk-card);
  color: var(--sk-text);
  border-color: var(--sk-border);
}
#cookie-banner.visible {
  transform: none;
  bottom: 24px;
  opacity: 1;
  visibility: visible;
  transition-delay: 0s;
}
#cookie-banner p { min-width: 0; color: var(--sk-text-secondary); }

@media (prefers-reduced-motion: reduce) {
  #cookie-banner { transition: opacity .2s ease; }
}

@media (max-width: 640px) {
  #cookie-banner { flex-direction: column; align-items: stretch; text-align: left; }
  #cookie-banner .cookie-btns { display: flex; gap: 10px; }
  #cookie-banner .cookie-btns .btn { flex: 1; }
}
@media (max-width: 360px) {
  #cookie-banner { left: 8px; right: 8px; padding: 14px 16px; }
}

/* ══════════════════════════════════════════════════════════
   15. AUTOCOMPLETE STACKING
   The suggestions dropdown has to clear the sticky results sidebar and the
   glass search card's own backdrop-filter context, or the options cannot
   be clicked on the results page.
═══════════════════════════════════════════════════════════ */
.sk-autocomplete-wrap { position: relative; z-index: 60; }
.sk-autocomplete-wrap:focus-within { z-index: 80; }
.sk-dropdown { z-index: 400; }
.results-layout .sidebar { z-index: 1; }

/* ══════════════════════════════════════════════════════════
   16. ICON STATE TOGGLES
   Both glyphs are rendered server-side; CSS picks the visible one. Swapping
   them in JS would mean rebuilding SVG from strings, and the previous
   textContent assignment erased the icon entirely.
═══════════════════════════════════════════════════════════ */
.sk-theme-btn .icon-when-dark,
[data-theme="dark"] .sk-theme-btn .icon-when-light { display: none; }
[data-theme="dark"] .sk-theme-btn .icon-when-dark  { display: inline-flex; }
.sk-theme-btn .icon-when-light { display: inline-flex; }

.sk-hamburger .icon-when-open { display: none; }
.sk-hamburger.is-open .icon-when-closed { display: none; }
.sk-hamburger.is-open .icon-when-open   { display: inline-flex; }

.sk-theme-btn > span, .sk-hamburger > span { display: inline-flex; align-items: center; }

/* ══════════════════════════════════════════════════════════
   17. ICON CONTAINERS introduced with the SVG set
═══════════════════════════════════════════════════════════ */
.cta-popup-icon {
  width: 52px; height: 52px;
  margin: 0 auto 14px;
  border-radius: var(--sk-r-lg);
  background: var(--sk-teal-muted);
  border: 1px solid rgba(17,156,135,.22);
  display: flex; align-items: center; justify-content: center;
  color: var(--sk-teal-dark);
}

.pillar-icon {
  width: 46px; height: 46px;
  border-radius: var(--sk-r-md);
  background: var(--sk-teal-muted);
  border: 1px solid rgba(17,156,135,.2);
  display: flex; align-items: center; justify-content: center;
  color: var(--sk-teal-dark);
  margin-bottom: 4px;
}

/* Footer route links: align the little plane with the label */
.footer-col ul li a .sk-icon { opacity: .75; }
.footer-col ul li a:hover .sk-icon { opacity: 1; }

/* Scroll-to-top arrow was a bare "↑" text glyph */
#scroll-top { font-family: var(--font-heading); font-weight: 700; }

/* ══════════════════════════════════════════════════════════
   18. DESTINATION GRID — 4-tile editorial layout
   One tall hero tile, two square tiles, one wide tile. Removing the
   Pakistan destination left four, and a 5-up grid with a gap looks broken.
═══════════════════════════════════════════════════════════ */
/* `dense` is what stops this grid needing to be counted by hand.
 *
 * It has been hand-tuned twice — at four cards, then at seven — each time by
 * working out which span to drop so the spanning tiles did not leave a hole.
 * That is a rule that breaks every time a destination is added, and adding
 * Greece, Mexico and the Dominican Republic broke it again (11 cells at two
 * columns). Dense auto-placement backfills the gaps itself, at every breakpoint,
 * for any number of cards. The tall tile can now keep its span on mobile too.
 *
 * The trade is that dense can place a later card into an earlier gap, so visual
 * order may differ from DOM order. On a decorative destination grid that is
 * fine — and every card is a self-contained link, so nothing depends on
 * reading them in sequence. */
.dest-grid { grid-auto-rows: 220px; grid-auto-flow: dense; }
.dest-card--tall { grid-row: span 2; }
.dest-card--wide { grid-column: span 2; }

@media (max-width: 900px) {
  .dest-grid { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 200px; }
  /* Every card becomes 1x1 here, and that is the whole trick.
     `dense` can only backfill a gap with a card that FITS it — a span-2 tile
     can never drop into a single empty cell, which is exactly the hole this
     grid kept growing (measured: row 5, column 2, with ten cards). With no
     spans there is nothing that cannot fit, so mid-grid holes become
     impossible at any card count; the worst case is a short final row. */
  .dest-card--tall, .dest-card--wide { grid-row: auto; grid-column: auto; }
}
@media (max-width: 560px) {
  .dest-grid { grid-template-columns: 1fr; grid-auto-rows: 190px; }
  .dest-card--tall, .dest-card--wide { grid-row: auto; grid-column: auto; }
}

/* Card brand badge inside the card-number field */
.card-type-icon {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: .06em;
  color: var(--sk-text-muted);
}
.card-type-icon.has-brand {
  color: var(--sk-primary);
  background: var(--sk-bg-subtle);
  border: 1px solid var(--sk-border);
  border-radius: 5px;
  padding: 3px 7px;
}
[data-theme="dark"] .card-type-icon.has-brand { color: var(--sk-text); }

/* Status icon on the unsubscribe / confirmation cards */
.unsub-icon {
  width: 60px; height: 60px; margin: 0 auto 18px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
}
.unsub-icon.is-ok   { background: rgba(5,150,105,.10);  color: #059669; border: 1px solid rgba(5,150,105,.24); }
.unsub-icon.is-warn { background: rgba(217,119,6,.10);  color: #D97706; border: 1px solid rgba(217,119,6,.24); }

/* Blog thumbnail / post-hero glyphs are SVG now, not 56px emoji */
.blog-card-thumb .sk-icon { color: rgba(255,255,255,.92); }
.post-emoji { display: inline-flex; color: var(--sk-teal); margin-bottom: 14px; }

/* ══════════════════════════════════════════════════════════
   19. SEARCH CARD — restore the solid variant
   Section 4 restyles the plain `.sf-wrap`, and because this file loads after
   ui-enhancements.css it was also beating `.sf-wrap--compact`. Every page
   that asks for the opaque card (results, SEO landers) was still getting the
   frosted-glass hero treatment. Re-assert the solid variant here, after it.
═══════════════════════════════════════════════════════════ */
.sf-wrap--compact,
.search-page .sf-wrap {
  background: var(--sk-card);
  border: 1px solid var(--sk-hairline);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.6), var(--sk-e3);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
[data-theme="dark"] .sf-wrap--compact,
[data-theme="dark"] .search-page .sf-wrap {
  background: var(--sk-card);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), var(--sk-e3);
}
/* Labels and inputs inside the solid card need dark-on-light, not the
   white-on-photo treatment the hero card uses. */
.sf-wrap--compact .sf-label { color: var(--sk-text-muted); }
.sf-wrap--compact .sf-input {
  background: var(--sk-bg-white);
  border-color: var(--sk-border);
  color: var(--sk-text);
}
[data-theme="dark"] .sf-wrap--compact .sf-input { background: rgba(255,255,255,.05); }
.sf-wrap--compact .sf-input::placeholder { color: var(--sk-text-muted); }
.sf-wrap--compact .sf-trip-tabs { background: var(--sk-bg-subtle); }
.sf-wrap--compact .sf-trip-tab { color: var(--sk-text-muted); }
.sf-wrap--compact .sf-trip-tab.active { background: var(--sk-primary); color: #fff; }
.sf-wrap--compact .sf-swap-btn {
  background: var(--sk-bg-subtle);
  border: 1px solid var(--sk-border);
  color: var(--sk-text-secondary);
}

/* ══════════════════════════════════════════════════════════
   20. CHECKOUT ITINERARY
   flight-details.php renders the selected flight through 17 classes that had
   no rule anywhere — on the one page where the customer is about to pay, the
   itinerary was raw unstyled divs.
═══════════════════════════════════════════════════════════ */
.itin-leg {
  padding: 16px 0;
  border-bottom: 1px solid var(--sk-border-light);
}
.itin-leg:last-of-type { border-bottom: none; }

.itin-leg-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 12px;
}
.itin-leg-label {
  font-family: var(--font-heading);
  font-weight: 800; font-size: .7rem;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--sk-teal-dark);
}
.itin-leg-date { font-size: .78rem; color: var(--sk-text-muted); font-weight: 600; }

.itin-leg-main {
  display: grid;
  grid-template-columns: auto 1fr minmax(64px, auto) 1fr;
  align-items: center;
  gap: 10px;
}
.itin-logo {
  width: 26px; height: 26px; object-fit: contain;
  border-radius: 6px; background: #fff; flex-shrink: 0;
}
.itin-endpoint { min-width: 0; }
.itin-time {
  font-family: var(--font-heading);
  font-weight: 800; font-size: 1.05rem; line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.itin-code { font-size: .7rem; font-weight: 700; color: var(--sk-text-muted); letter-spacing: .04em; }
.itin-middle { text-align: center; min-width: 0; }
.itin-dur { font-size: .68rem; font-weight: 700; color: var(--sk-text-secondary); white-space: nowrap; }
.itin-middle .route-line { margin: 5px 0; }
.itin-middle .stops-label { font-size: .64rem; }

.itin-carrier {
  margin-top: 8px;
  font-size: .74rem;
  color: var(--sk-text-muted);
}

.itin-segments {
  margin-top: 12px;
  padding: 12px 14px;
  background: var(--sk-bg-subtle);
  border-radius: var(--sk-r-md);
  display: flex; flex-direction: column; gap: 10px;
}
.itin-seg { display: flex; gap: 10px; align-items: flex-start; }
.itin-seg-dot {
  width: 7px; height: 7px; margin-top: 6px; flex-shrink: 0;
  border-radius: 50%;
  background: var(--sk-teal);
  box-shadow: 0 0 0 3px var(--sk-teal-muted);
}
.itin-seg-time {
  font-size: .8rem; line-height: 1.4;
  font-variant-numeric: tabular-nums;
}
.itin-seg-time strong { font-weight: 800; }
.itin-seg-time .seg-arrow { color: var(--sk-text-muted); margin: 0 3px; }
.itin-seg-meta { font-size: .72rem; color: var(--sk-text-muted); margin-top: 2px; line-height: 1.45; }

.itin-layover {
  margin-left: 17px;
  align-self: flex-start;
  font-size: .7rem; color: var(--sk-text-muted);
  background: var(--sk-card);
  border: 1px dashed var(--sk-border);
  border-radius: var(--sk-r-full);
  padding: 4px 11px;
}
.itin-layover .overnight { color: #D97706; font-weight: 700; margin-left: 5px; }

.summary-divider {
  height: 1px; margin: 16px 0 4px;
  background: var(--sk-border);
}

@media (max-width: 420px) {
  .itin-leg-main { grid-template-columns: 1fr auto 1fr; }
  .itin-logo { display: none; }
}

/* ══════════════════════════════════════════════════════════
   21. THIRD-PARTY FAILURE SAFETY
   Three separate single points of failure, all on unpkg/jsDelivr:

   a) aos.css sets `[data-aos^="fade"]{opacity:0}` and only `.aos-animate`
      restores it. There are 68 data-aos attributes across the site, so a
      blocked CDN (corporate proxy, ad blocker, offline, regional block) or
      JS disabled left entire pages blank — including the legal pages.
   b) #loading-screen is an opaque full-screen overlay removed only by JS.
   c) The date inputs are readonly and depend on flatpickr; without it the
      user cannot enter a date at all, so search becomes impossible.

   Fix: nothing is hidden until the code that animates has actually run and
   said so. (a) is now moot — AOS is gone, replaced by js/reveal.js, which is
   same-origin and sets .sk-reveal itself; see §30. This rule stays as a belt
   for the data-aos attributes still in the markup.
═══════════════════════════════════════════════════════════ */
html:not(.sk-reveal) [data-aos] {
  opacity: 1 !important;
  transform: none !important;
  visibility: visible !important;
}

/* Failsafe: the overlay removes itself after 4s with no JS at all. The
   normal path still hides it via .hidden as soon as main.js runs. */
#loading-screen {
  animation: skLoaderFailsafe 1ms linear 4s forwards;
}
@keyframes skLoaderFailsafe {
  to { opacity: 0; visibility: hidden; pointer-events: none; }
}

/* ══════════════════════════════════════════════════════════
   22. DARK MODE — remaining hardcoded light-theme colours
   Several pages paint fixed hex or brand-navy text onto cards that go dark.
═══════════════════════════════════════════════════════════ */
[data-theme="dark"] .about-stat .num,
[data-theme="dark"] .fl-card .num,
[data-theme="dark"] .error-code,
[data-theme="dark"] .blog-card-body h2,
[data-theme="dark"] .sidebar-card h3,
[data-theme="dark"] .post-body h4,
[data-theme="dark"] .value-card h3,
[data-theme="dark"] .step-text strong,
[data-theme="dark"] .next-step-item strong { color: var(--sk-text); }

/* Amber / red / green notice boxes were hardcoded for a white page */
[data-theme="dark"] .alert-warn,
[data-theme="dark"] [style*="#FEF3C7"],
[data-theme="dark"] [style*="#FEF2F2"] {
  background: rgba(217,119,6,.12) !important;
  border-color: rgba(217,119,6,.32) !important;
  color: #FCD34D !important;
}
[data-theme="dark"] [style*="#FEF2F2"] { background: rgba(220,38,38,.12) !important; border-color: rgba(220,38,38,.30) !important; color: #FCA5A5 !important; }
[data-theme="dark"] [style*="#D1FAE5"] { background: rgba(5,150,105,.14) !important; border-color: rgba(5,150,105,.32) !important; color: #6EE7B7 !important; }
[data-theme="dark"] .booking-alert { background: rgba(220,38,38,.10); border-color: rgba(220,38,38,.30); }
[data-theme="dark"] .booking-alert strong,
[data-theme="dark"] .booking-alert p { color: #FCA5A5; }

/* A ghost button on a card must not be the same colour as the card */
[data-theme="dark"] .btn-ghost { color: var(--sk-text); border-color: var(--sk-hairline-strong); }

/* ══════════════════════════════════════════════════════════
   23. DISPLAY-SIZED ICON CONTAINERS
   These boxes were built around 48-64px emoji. An SVG inherits its box from
   the width/height attribute, not font-size, so the old font-size rules no
   longer do anything — the containers need to centre the glyph themselves.
═══════════════════════════════════════════════════════════ */
/* .contact-success-icon dropped with contact.php's inline success card —
   that form now redirects to /thank-you/contact, which styles its own icon. */
.confirmation-icon, .channel-icon,
.value-icon, .fl-icon, .no-results .icon, .error-plane, .blog-card-thumb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}
.confirmation-icon   { color: var(--sk-teal-dark); }
.channel-icon        { color: var(--sk-teal-dark); }
.value-icon, .fl-icon{ color: var(--sk-teal-dark); }
.no-results .icon    { color: var(--sk-text-muted); margin-bottom: 18px; }
.error-plane         { color: var(--sk-teal); }

/* ══════════════════════════════════════════════════════════
   24. GRID TRACKS THAT REFUSE TO SHRINK
   Grid and flex items default to min-width:auto — their content-based
   minimum — so a `1fr` track can resolve WIDER than the grid that contains
   it, and nothing in the box model stops it. Measured at a 320px viewport
   (305px client, 257px content box after the page gutters):

     .contact-layout    track 325px  — .channel-card holds an unbroken
                                       email address
     .about-intro-card  track 306px  — .family-link-card
     .results-layout    track 283px  — .no-results

   body { overflow-x: hidden } meant the page never scrolled, it silently
   clipped — which is why this presented as "the layout is wrong below
   ~372px" rather than as an overflow.

   min-width:0 on the items is the whole fix: it drops the track's automatic
   minimum to zero so `1fr` can shrink, and it leaves every desktop
   grid-template-columns untouched. overflow-wrap then lets the long token
   break, instead of overflowing the item once the track stops growing
   to accommodate it.
═══════════════════════════════════════════════════════════ */
.contact-layout > *,
.about-intro-card > *,
.results-layout > * { min-width: 0; }

/* .channel-card is a flex row (icon + copy) inside a column flex parent, so
   its own children need the same release or the copy overflows the card. */
.channel-card,
.channel-card > * { min-width: 0; }

.channel-card,
.family-link-card,
.no-results { overflow-wrap: break-word; }

/* Releasing the tracks above stops the PAGE overflowing, but it just moves
   the problem inward: both of these cards are flex ROWS whose last child is
   deliberately unbreakable — .channel-value is white-space:nowrap around a
   phone number and an email address, and the about card ends in a 189px
   "Visit SkyTeam Canada" button. Below ~430px there is no width at which a
   52px icon, the copy and that element coexist on one line, so the row has
   to become a stack. This is the fix for both "contact overflows below
   372px" and "the SkyTeam button is clipped off-screen". */
@media (max-width: 430px) {
  .channel-card { flex-wrap: wrap; row-gap: 12px; padding: 18px 18px; }
  .channel-card > div:not(.channel-icon) { flex: 1 1 auto; }
  .channel-value {
    margin-left: 0;
    flex: 1 0 100%;
    white-space: normal;
    overflow-wrap: anywhere;   /* info@skykaro.com has no break opportunity */
  }

  .family-link-card { flex-wrap: wrap; row-gap: 14px; padding: 22px; }
  .family-link-card > div:last-child { flex: 1 1 100%; }
  .family-link-card .btn { white-space: normal; }
}

/* ══════════════════════════════════════════════════════════
   25. HERO HEADINGS ON DARK GRADIENTS
   Every gradient hero sets `color:#fff` on its container. The subtitle <p>
   inherits that and renders white — but design-system.css has
   `h1,h2,h3,h4,h5 { color: var(--sk-text) }`, and an explicit declaration
   always beats inheritance no matter how weak its specificity. So every hero
   H1 rendered #1B1F3B on a #1B1F3B->#2A3060 gradient: **1.0:1, invisible**,
   in LIGHT mode, on /about /contact /faq /blog, every /cheap-flights-to-*
   destination page, every route page and every blog post.

   It hid from the automated pass because the hero's background is a
   `background-image` gradient — `backgroundColor` reports transparent, so a
   walk-up looking for an opaque background sails past the hero entirely and
   compares the text against some pale ancestor. It is obvious the moment you
   actually look at the page.

   `inherit` rather than `#fff`: it takes whatever the container defines, so
   it fixes the dark-gradient heroes and is a no-op anywhere the container
   sets no colour of its own.
═══════════════════════════════════════════════════════════ */
.about-hero   :is(h1, h2, h3, h4),
.blog-hero    :is(h1, h2, h3, h4),
.post-hero    :is(h1, h2, h3, h4),
.contact-hero :is(h1, h2, h3, h4),
.faq-hero     :is(h1, h2, h3, h4),
.dest-hero    :is(h1, h2, h3, h4),
.route-hero   :is(h1, h2, h3, h4),
.results-header :is(h1, h2, h3, h4),
.booking-hero :is(h1, h2, h3, h4),
.legal-hero   :is(h1, h2, h3, h4) { color: inherit; }

/* The same trap, on mid-page bands rather than heroes. Each of these sets
   `color:#fff` over a dark gradient and styles its heading's font but not its
   colour, so the global rule wins and the heading goes navy-on-navy:
     .mission-strip       /about        "Our Mission"
     .price-banner        destinations  "Flights to India"
     .call-strip          route pages   "Want an Exclusive ... Fare?"
     .coming-soon-banner  /login        "Customer Accounts — Coming Soon"

   To find more of these, do NOT compare text against a computed background —
   gradients make that useless. Look for a heading whose colour is dark inside
   an ancestor whose declared colour is light; that needs no knowledge of the
   background at all and returns no false positives. */
.mission-strip      :is(h1, h2, h3, h4),
.price-banner       :is(h1, h2, h3, h4),
.call-strip         :is(h1, h2, h3, h4),
.coming-soon-banner :is(h1, h2, h3, h4) { color: inherit; }

/* ══════════════════════════════════════════════════════════
   26. DARK MODE — muted text was below AA
   --sk-text-muted #5C6480 on --sk-card #1A2040 is 2.71:1, under the 4.5:1
   floor for body text. It carries the legal pages' "Last updated", blog
   dates, form subtitles, .auth-divider and .alt-actions. Lightening the
   token fixes every instance at once; light mode is untouched.
═══════════════════════════════════════════════════════════ */
[data-theme="dark"] { --sk-text-muted: #838FAD; }   /* 4.9:1 on --sk-card */

/* ══════════════════════════════════════════════════════════
   27. LOADING SCREEN — was hardcoded white
   #loading-screen is `background:#fff` with a `--sk-primary` wordmark and an
   #e2e8f0 track, so a dark-mode visitor got a full-screen white flash with a
   near-invisible logo before the page faded in. data-theme is set server-side
   from the sk_theme cookie (v2-head.php), so tokens are correct on the very
   first paint — there is no flash-of-wrong-theme to work around.
═══════════════════════════════════════════════════════════ */
#loading-screen { background: var(--sk-bg); }
.loading-logo   { color: var(--sk-text); }
.loading-bar    { background: var(--sk-hairline-strong); }

/* A checkmark usable from a ::before, where an <svg> cannot go. Same stroke
   weight and cap style as include/icons.php so it sits alongside the real
   icon set. Applied as a mask, so `background-color` tints it. */
:root {
  --sk-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12.5l5 5L20 6.5'/%3E%3C/svg%3E");
}

/* ══════════════════════════════════════════════════════════
   28. HERO — stop burying the photograph
   components.css lays a 105deg scrim over the whole image starting at
   rgba(13,16,34,0.88) — 88% opaque black across the left ~40%, still 72% at
   the 42% stop. The beach, the lagoon and the bungalows are all behind that.
   You cannot buy your way out of it with a better photo; nothing survives an
   0.88 scrim.

   skyteamtravel.ca (the flagship) does the opposite and is right:
   `bg-gradient-to-t from-slate-900 via-slate-900/40 to-transparent` — fully
   TRANSPARENT at the top, darkening only toward the bottom where the copy and
   the floating search card sit. The photo is the hero; the scrim only shows up
   where it has to.

   Same idea here, adapted to a headline that sits upper-LEFT rather than
   bottom-centre: a soft left scrim that is gone by 62%, plus a bottom scrim
   for the search card. Peak opacity 0.62 instead of 0.88, and it clears the
   lagoon entirely. Headline contrast is held by the scrim plus a text shadow
   rather than by flooding the whole frame.
═══════════════════════════════════════════════════════════ */
.hero-overlay {
  background:
    /* bottom scrim — protects the search card and meets .hero-fade */
    linear-gradient(to top,
      rgba(11,14,30,0.72) 0%,
      rgba(11,14,30,0.34) 18%,
      rgba(11,14,30,0.06) 38%,
      transparent 58%),
    /* left scrim — protects the headline, gone before the lagoon */
    linear-gradient(100deg,
      rgba(11,14,30,0.62) 0%,
      rgba(13,17,36,0.40) 26%,
      rgba(16,20,42,0.16) 46%,
      transparent 62%);
}

/* With a lighter scrim the headline needs its own contrast rather than
   relying on the whole frame being dark. */
.hero-title, .hero h1, .hero-sub, .hero-eyebrow {
  text-shadow: 0 1px 2px rgba(8,11,24,.34), 0 2px 12px rgba(8,11,24,.28);
}

/* The 1.07 zoom magnifies an already-upscaled 1376x768 source. 1.035 keeps
   the drift without doubling down on the softness. */
@keyframes heroZoom {
  from { transform: scale(1.005); }
  to   { transform: scale(1.035); }
}

/* The accent line is `background-clip:text` with a transparent fill, so
   text-shadow paints BEHIND the transparent glyphs and does nothing for it.
   drop-shadow filters the rasterised result instead, so it actually lands.
   Without this "Pay Less." is teal-on-turquoise over the brightest part of
   the lagoon — the one spot in the frame it cannot afford to sit on. */
.hero-title .teal-line {
  filter: drop-shadow(0 1px 2px rgba(8,11,24,.42))
          drop-shadow(0 3px 10px rgba(8,11,24,.30));
}

/* A soft pool of shade behind the headline block only, so the copy holds up
   without dimming the lagoon. This is what carries readability now that the
   flat 0.88 scrim is gone. */
.hero-overlay::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(78% 62% at 26% 40%,
    rgba(9,12,26,0.46) 0%,
    rgba(9,12,26,0.26) 42%,
    rgba(9,12,26,0.08) 66%,
    transparent 82%);
}

/* ══════════════════════════════════════════════════════════
   29. HERO WATER — the "smoke"
   §3 paints .hero-water::before/::after as four big radial gradients under
   `mix-blend-mode: soft-light`, drifting on 26s/34s loops. At that scale and
   blend mode they do not read as caustics — they read as smoke or haze rolling
   across the frame, which is exactly what they looked like.

   They also predate js/hero-ocean.js, which now does caustics properly in the
   shader (causticGain/specGain) with a real dispersion relation and a mask
   locked to the photograph. Two systems were drawing the same thing, and the
   crude one was on top.

   The CSS blobs are retired. The container stays — the WebGL canvas mounts
   into it — and it becomes the visible fallback only where WebGL is
   unavailable, as a still tint rather than something that moves.
═══════════════════════════════════════════════════════════ */
.hero-water::before,
.hero-water::after {
  content: none;
}

/* The canvas was sitting at z-index -4, the same layer as the photo, so the
   real simulation was fighting .hero-bg for stacking order instead of
   compositing over it. -3 puts it between the photo and the readability
   overlay, which is what §3's comment always said the stack should be. */
.hero canvas.hero-ocean {
  z-index: -3;
}

@media (prefers-reduced-motion: reduce) {
  .hero canvas.hero-ocean { display: none; }
}

/* ══════════════════════════════════════════════════════════
   30. SCROLL REVEALS (js/reveal.js — replaces AOS)
   Nothing is hidden until reveal.js sets .sk-reveal on <html>, so no JS, a
   thrown error or a blocked file all leave the page fully visible. Same
   contract as the AOS guard in §21, minus the remote dependency.

   Transform distances are small on purpose. Big travel reads as a template;
   16px with a long expo settle reads as considered.
═══════════════════════════════════════════════════════════ */
html.sk-reveal [data-reveal] {
  opacity: 0;
  transition:
    opacity   .52s var(--ease-out) var(--rv-delay, 0ms),
    transform .52s var(--ease-out) var(--rv-delay, 0ms);
  will-change: opacity, transform;
}
html.sk-reveal [data-reveal="u"] { transform: translate3d(0, 16px, 0); }
html.sk-reveal [data-reveal="d"] { transform: translate3d(0, -16px, 0); }
html.sk-reveal [data-reveal="l"] { transform: translate3d(18px, 0, 0); }
html.sk-reveal [data-reveal="r"] { transform: translate3d(-18px, 0, 0); }
html.sk-reveal [data-reveal="z"] { transform: scale(.965); }
html.sk-reveal [data-reveal="f"] { transform: none; }

html.sk-reveal [data-reveal].is-in {
  opacity: 1;
  transform: none;
  /* Released after the animation so the compositor is not holding layers for
     every revealed block on a long page. */
  will-change: auto;
}

@media (prefers-reduced-motion: reduce) {
  html.sk-reveal [data-reveal] { opacity: 1 !important; transform: none !important; }
}

/* ══════════════════════════════════════════════════════════
   31. TEXT CONTRAST — the two tokens that fail AA as text
   Measured across 14 pages: 192 elements below the WCAG AA floor, in three
   colour pairs, all traceable to two tokens used as text on light surfaces.

     --sk-text-muted  #8A96A8  ->  3.00:1 on #fff, 2.67:1 on #EFF2F8   (60 el)
     --sk-teal        #00D4AA  ->  1.91:1 on #fff, 1.81:1 on #F7F9FC   (70 el)
     --sk-teal-dark   #00B899  ->  2.53:1 on #fff                      (34 el)

   Normal text needs 4.5:1. Nothing here was close.

   The teal tokens are NOT darkened, because they are also fills, borders and
   glows — the brand would go muddy and every teal button with it. And teal on
   the dark navy surfaces is already high-contrast and correct. So a separate
   text-only token is introduced, dark in light mode and the untouched brand
   teal in dark mode, and applied only where teal is genuinely TYPE.

   HISTORICAL: the three hex values quoted above are the pre-§33 palette. The
   ramp was retuned there (78-80% saturation instead of 100%), which lifted
   --sk-teal-dark to 4.9:1 on white on its own. This section still stands —
   --sk-teal at #19C8AB is 2.12:1 and is still a fill colour, not a type
   colour — but the numbers above are no longer current measurements.
═══════════════════════════════════════════════════════════ */
:root {
  /* 5.32:1 on #fff, 4.70:1 on #EFF2F8 — still clearly secondary to the
     #1B1F3B body colour, but legible rather than decorative. */
  --sk-text-muted: #5F6B80;
  /* 5.66:1 on #fff, 5.37:1 on #F7F9FC, 5.12:1 on the #EFF2F8 subtle surface.
     #00806B was tried first and cleared white at 4.88:1 but only reached
     4.36:1 on #EFF2F8, where .section-eyebrow and .post-tag actually sit. */
  --sk-teal-text: #00755F;
}
[data-theme="dark"] {
  /* On the dark surfaces the brand teal is already ~8:1. Leave it alone. */
  --sk-teal-text: var(--sk-teal);
  /* MUST be re-asserted here. :root and [data-theme="dark"] have the SAME
     specificity (a pseudo-class and an attribute selector both count as one
     class), so the later block wins outright — and the :root above sits later
     in the file than §26. Without this line the light-mode muted colour
     (#5F6B80) leaks into dark mode and lands at 2.95:1 on --sk-card. */
  --sk-text-muted: #838FAD;
}

/* Teal used as type. Fills, borders, backgrounds and glows keep --sk-teal. */
.legal-card a,
.post-body a,
.faq-answer a,
p > a,
li > a,
.read-more,
.post-tag,
.section-eyebrow,
.value-icon,
.channel-value,
.blog-category,
.airline-chip,
.airline-tag,
.tip-num,
.stat-chip .s-val   { color: var(--sk-teal-text); }

/* The header is transparent over the dark hero, where teal reads fine — it is
   only once .scrolled paints it white that the active link fails. Scope the
   correction to that state so the hero treatment is untouched. */
.sk-header.scrolled .sk-nav-link.active { color: var(--sk-teal-text); }

/* Colour alone is not a distinguishing cue for a link inside body copy
   (WCAG 1.4.1). These sit in prose, so they get an underline as well. */
.legal-card p a,
.post-body p a,
.faq-answer a { text-decoration: underline; text-underline-offset: 2px; }

/* Page-level <style> blocks load AFTER every stylesheet (see §6 of HANDOFF),
   and most of these colours are declared there — so the rules above lose on
   source order alone. !important is the documented tool for that in this file.
   Verified first that NO teal text sits on a dark solid surface, so darkening
   the type cannot cost contrast anywhere. */
.channel-value,
.blog-category,
.read-more,
.post-tag,
.airline-chip,
.airline-tag,
.tip-num,
.legal-card a,
.post-body a,
.faq-answer a,
#cookie-banner a,
.legal-card p a          { color: var(--sk-teal-text) !important; }

/* The header only fails once .scrolled paints it white — over the hero it is
   teal on a dark photo and correct. Both the active nav link and the teal half
   of the wordmark are affected. */
.sk-header.scrolled .sk-nav-link.active,
.sk-header.scrolled .sk-logo span { color: var(--sk-teal-text) !important; }

/* Stragglers the sweep turned up after the token landed. */

/* The loading overlay is the FIRST thing a visitor sees. §27 made its
   background and wordmark theme-aware, but the teal half of the logo kept
   var(--sk-teal) — 1.81:1 on the light background. */
.loading-logo span { color: var(--sk-teal-text); }

/* disclaimer.php styles .notice copy with --sk-teal-dark (2.53:1 on white)
   from its inline block, so this needs the same !important as the rest. */
.notice, .notice p, .notice strong,
.alt-actions a { color: var(--sk-teal-text) !important; }

/* Dark mode: --sk-primary used as TEXT, again — the same trap as §25/§9, this
   time from the contact and FAQ inline <style> blocks. #1B1F3B on the #1A2040
   card is 1.01:1. --sk-text is identical to --sk-primary in light mode and
   flips in dark, so this costs nothing in light. */
[data-theme="dark"] .channel-title,
[data-theme="dark"] .faq-question,
[data-theme="dark"] .faq-icon,
[data-theme="dark"] .form-label,
[data-theme="dark"] .hours-card strong,
[data-theme="dark"] .contact-form-card label { color: var(--sk-text) !important; }

/* The FAQ icon has two backgrounds and therefore needs two colours. Collapsed
   it sits on --sk-teal-muted (translucent teal over the dark card) and needs
   light type; open, it sits on SOLID --sk-teal and needs the dark brand navy.
   The dark-mode rule above set it light in both states, which made the open
   "+" 1.69:1 on teal. */
[data-theme="dark"] .faq-item.open .faq-icon,
[data-theme="dark"] .faq-item.open .faq-question .faq-icon { color: var(--sk-primary) !important; }

/* login.php's wordmark: teal on the light auth background, 1.81:1. */
.auth-logo .logo-text span { color: var(--sk-teal-text); }

/* ══════════════════════════════════════════════════════════
   32. FAQ ACCORDION — accessible pattern
   The trigger is now a real <button> inside an <h3> (see js/accordion.js for
   why). That brings default button styling with it, so it has to be reset
   back to what the three pages already looked like — and the <h3> must not
   inherit the global heading scale, because visually it is a row, not a
   heading.
═══════════════════════════════════════════════════════════ */
.faq-h {
  margin: 0;
  font: inherit;
  letter-spacing: inherit;
}
.faq-question,
.faq-q {
  /* Reset the UA button, keep the layout the pages already declare. */
  width: 100%;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.faq-q-text { flex: 1; min-width: 0; }

/* A visible, high-contrast focus ring — this is a primary keyboard control. */
.faq-question:focus-visible,
.faq-q:focus-visible {
  outline: 3px solid var(--sk-teal);
  outline-offset: -3px;
  border-radius: inherit;
}

/* Collapsed answers were hidden with max-height:0 + overflow:hidden, which
   hides them visually but leaves every word in the accessibility tree — a
   screen reader read all twelve answers straight through. visibility DOES
   prune the subtree, and delaying it by the length of the transition keeps
   the open/close animation intact.

   Guarded on html.sk-acc (set by accordion.js) so that with no JS every panel
   stays open and readable rather than permanently collapsed. */
html.sk-acc .faq-answer,
html.sk-acc .faq-a {
  visibility: hidden;
  transition: max-height .4s ease, padding .3s ease, visibility 0s linear .4s;
}
html.sk-acc .faq-item.open .faq-answer,
html.sk-acc .faq-item.open .faq-a {
  visibility: visible;
  transition-delay: 0s;
}

/* ══════════════════════════════════════════════════════════
   33. THE EXPENSIVE PASS — numerals, micro-labels, edges
   The search-results and checkout pages read as generic while the homepage
   did not. The homepage was the only place carrying the craft details: an
   inset top highlight, layered shadows, translucency. Everywhere else got
   flat white boxes, 1.5px borders, saturated fills and a coloured glow.

   Both reference sites (skyteamtravel.ca and letsfly) independently follow
   the same four rules, so they are the ones adopted here:

     1. Colour is RATIONED. The accent is type, hairlines and one small
        button — never a large fill. Under ~5% of any viewport.
     2. Shadows are wide, soft, tinted with the ink colour, and capped at
        ~10% opacity. Blur is 2-3x the y-offset. Never a coloured halo.
     3. Letter-spacing has POLARITY: big numbers get negative tracking,
        tiny uppercase labels get positive tracking, nothing in between.
     4. Figures are tabular. A column of prices that shifts by a pixel per
        digit is the single most obvious "nobody looked at this" tell.
═══════════════════════════════════════════════════════════ */

/* ── 33.1 Figures ─────────────────────────────────────────
   Proportional digits make a right-aligned price column ragged and make a
   live-updating total jitter as it changes. Every number the eye compares
   vertically or watches change is locked to tabular. */
.flight-price, .ret-price, .total-price, .summary-row .value,
.itin-time, .route-time, .ret-time, .xt-opt-price,
#grandTotal, #submitTotal, .stat-number, .price-col .flight-price {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* Display figures: tight tracking, optical line-height. A price set at the
   body's default tracking looks like body copy that happens to be big. */
.flight-price, .total-price, .ret-price, .route-time, .itin-time,
.co-route-codes, .stat-number {
  letter-spacing: -0.02em;
  line-height: 1;
}

/* ── 33.2 Micro-labels ───────────────────────────────────
   The counterpart to the rule above: small, heavy, uppercase, POSITIVELY
   tracked, muted. Both references use this identically for every eyebrow. */
.form-section-note, .filter-label, .itin-leg-label, .bag-extra + p,
.summary-row .label small, .flight-price-sub, .ret-price-sub {
  letter-spacing: 0.08em;
}

/* ── 33.3 Edges ──────────────────────────────────────────
   The inset top highlight is the detail that separates the homepage's search
   card from every other card on the site. It is one line: a 1px inner
   highlight along the top edge, so the card catches light like a physical
   surface instead of being a filled rectangle. */
.form-card, .flight-card, .filter-card {
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.9),
    var(--sk-e2);
}
/* The sticky summary sits one step above the content cards — it is the
   object the customer is deciding against. */
.summary-card {
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.9),
    var(--sk-e3);
}
/* Tiles get the highlight but NO drop shadow. They live inside a card that
   is already elevated; shadowing them too makes the surface look quilted. */
.summary-total, .xt-opt, .bag-tile, .trust-tile, .xt-benefit {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.75);
}
[data-theme="dark"] .form-card, [data-theme="dark"] .summary-card,
[data-theme="dark"] .flight-card, [data-theme="dark"] .filter-card {
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.06),
    0 2px 4px rgba(0,0,0,.24), 0 12px 32px rgba(0,0,0,.34);
}

/* ── 33.4 Buttons ────────────────────────────────────────
   Solid fill with a 10% gradient step, not a two-stop rainbow, and a neutral
   shadow rather than a coloured halo. Ink type on teal reads 7.6:1. */
.book-btn, .submit-booking, .btn-primary, .sf-search-btn {
  background: linear-gradient(180deg, var(--sk-teal), var(--sk-teal-deep));
  box-shadow: var(--sk-teal-glow);
  letter-spacing: 0.01em;
}
.book-btn:hover, .submit-booking:hover, .btn-primary:hover, .sf-search-btn:hover {
  background: linear-gradient(180deg, var(--sk-teal-deep), var(--sk-teal-dark));
  box-shadow: var(--sk-teal-glow-hover);
}
/* Transform is faster than colour, so the press reads as snappy. */
.book-btn, .submit-booking, .btn-primary {
  transition: background .22s var(--ease-out), box-shadow .22s var(--ease-out),
              transform .14s var(--ease-out);
}
.book-btn:active, .submit-booking:active, .btn-primary:active { transform: translateY(0) scale(.99); }

/* ── 33.5 Focus ──────────────────────────────────────────
   A soft accent halo instead of a hard ring — visible, but it does not look
   like a validation error. */
.form-input:focus, .form-select:focus, .sf-input:focus, .sort-select:focus {
  border-color: var(--sk-teal-deep);
  box-shadow: 0 0 0 3px rgba(17,156,135,.13);
  outline: none;
}

/* ══════════════════════════════════════════════════════════
   34. iOS INPUT ZOOM — the "search box bursts out of the card"
   Reported on iPhone (Chrome, which is WebKit like Safari): tapping a field
   in the search card made the whole layout overflow its container.

   It is not a layout bug and it does not reproduce on desktop at any width.
   Mobile WebKit AUTO-ZOOMS the page whenever a focused text field computes
   below 16px, to bring the text up to a readable size. The zoom scales the
   viewport, so everything appears to burst its box. The affected fields:

     .sf-input      14px   From / To / Depart / Return
     .sort-select   13px   results sort
     .form-input    (checkout — same class family)

   16px is the exact threshold; 15.9px still zooms. The right fix is to meet
   it rather than to fight it with maximum-scale=1 on the viewport tag, which
   would also block deliberate pinch-zoom and is a WCAG 1.4.4 failure.

   Scoped to coarse pointers as well as width, so a narrow desktop window
   keeps the tighter 14px type it was designed with.
═══════════════════════════════════════════════════════════ */
@media (max-width: 820px), (pointer: coarse) {
  input[type="text"], input[type="search"], input[type="email"],
  input[type="tel"],  input[type="number"], input[type="password"],
  input[type="date"], input[type="month"],  input[type="datetime-local"],
  select, textarea,
  .sf-input, .form-input, .form-select, .form-textarea, .sort-select,
  /* components.css declares `textarea.form-input` (0,1,1). A bare
     `.form-input` (0,1,0) loses to it no matter how late this file loads,
     so the contact-form message box kept its 14.5px and kept zooming. */
  textarea.form-input {
    font-size: 16px;
  }
  /* The field boxes were sized around 14px type. Trim the vertical padding
     so the taller text does not grow the search card by ~2px per row. */
  .sf-input { padding-top: 6px; padding-bottom: 6px; }
}

/* ══════════════════════════════════════════════════════════
   35. DATE PICKER — it was wearing flatpickr's factory theme
   The calendar was the stock CDN stylesheet with not one override, so it
   carried none of the site's type, colour, radius or shadow language. The
   "old-fashioned up/down arrows" are flatpickr's year stepper: a native
   <input type=number> whose browser spinner it re-skins as two tiny
   triangles. Nothing about it belongs on a page like this.

   Restyled here rather than by swapping libraries — flatpickr's behaviour
   (range linking, minDate, altInput) is already wired into main.js and works.
═══════════════════════════════════════════════════════════ */
.flatpickr-calendar {
  font-family: var(--font-body);
  border-radius: var(--sk-r-lg);
  border: 1px solid var(--sk-border);
  background: var(--sk-card);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), var(--sk-e4);
  padding: 6px;
  width: auto; min-width: 19rem;
}
.flatpickr-calendar.arrowTop::before, .flatpickr-calendar.arrowTop::after,
.flatpickr-calendar.arrowBottom::before, .flatpickr-calendar.arrowBottom::after { display: none; }

/* ── Header ── */
.flatpickr-months { padding: 6px 4px 2px; align-items: center; }
.flatpickr-months .flatpickr-month { background: transparent; color: var(--sk-text); height: 40px; }
.flatpickr-current-month {
  font-family: var(--font-heading);
  font-size: 15px; font-weight: 800; padding: 0;
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
.flatpickr-current-month .flatpickr-monthDropdown-months {
  font-family: var(--font-heading); font-weight: 800; font-size: 15px;
  color: var(--sk-text); background: transparent;
  border-radius: var(--sk-r-sm); padding: 2px 6px;
}
.flatpickr-current-month .flatpickr-monthDropdown-months:hover { background: var(--sk-bg-subtle); }
.flatpickr-monthDropdown-month { color: var(--sk-text); background: var(--sk-card); }

/* The year stepper. The arrows are the thing the client flagged: hide them
   and let the year read as plain type. The month chevrons already move the
   calendar, and flatpickr keeps the field keyboard-steppable regardless. */
.numInputWrapper span.arrowUp, .numInputWrapper span.arrowDown { display: none; }
.numInputWrapper:hover { background: transparent; }
.flatpickr-current-month input.cur-year {
  font-family: var(--font-heading); font-weight: 800; font-size: 15px;
  color: var(--sk-text); padding: 0 2px 0 4px;
}
.flatpickr-current-month input.cur-year::-webkit-outer-spin-button,
.flatpickr-current-month input.cur-year::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}
.flatpickr-current-month input.cur-year { -moz-appearance: textfield; appearance: textfield; }

/* Month chevrons — circular hit targets, not bare glyphs floating in a corner */
.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
  width: 32px; height: 32px; padding: 7px;
  top: 8px; border-radius: 50%;
  color: var(--sk-text-secondary);
  transition: background var(--t-fast), color var(--t-fast);
}
.flatpickr-months .flatpickr-prev-month { left: 8px; }
.flatpickr-months .flatpickr-next-month { right: 8px; }
.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover {
  background: var(--sk-bg-subtle); color: var(--sk-teal-text);
}
.flatpickr-months .flatpickr-prev-month svg,
.flatpickr-months .flatpickr-next-month svg { fill: currentColor; width: 13px; height: 13px; }
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg { fill: currentColor; }

/* ── Weekday row ── */
.flatpickr-weekdays { background: transparent; height: 30px; }
span.flatpickr-weekday {
  background: transparent;
  font-family: var(--font-heading);
  font-size: 10px; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--sk-text-muted);
}

/* ── Days ── */
.flatpickr-days { border: none; width: 100%; }
/* flatpickr's dayContainer is a fixed 307.875px and its days are sized to
   suit that. Restyling the day to a flat 38px left slack in the row, so any
   week that is not full — the last one, always — spread its cells across the
   gap instead of aligning to the column above. Seen only by looking at it.
   Sizing by fraction keeps all seven columns locked whatever the width. */
.flatpickr-rContainer, .flatpickr-days .dayContainer {
  width: 100%; min-width: 0; max-width: none;
}
.dayContainer { padding: 2px 0; justify-content: flex-start; }
.flatpickr-day {
  font-family: var(--font-body);
  font-size: 13.5px; font-weight: 600;
  color: var(--sk-text);
  border: none; border-radius: var(--sk-r-sm);
  height: 38px; line-height: 38px;
  flex-basis: 14.2857%; max-width: 14.2857%;
  font-variant-numeric: tabular-nums;
  transition: background var(--t-fast), color var(--t-fast);
}
/* The weekday header must use the same seven-column split or the labels
   drift out of register with the numbers beneath them. */
.flatpickr-weekdaycontainer { width: 100%; }
span.flatpickr-weekday { flex-basis: 14.2857%; max-width: 14.2857%; }
.flatpickr-day:hover { background: var(--sk-bg-subtle); }
.flatpickr-day.today {
  box-shadow: inset 0 0 0 1.5px var(--sk-teal-dark);
  color: var(--sk-teal-text); font-weight: 800;
}
.flatpickr-day.selected,
.flatpickr-day.startRange, .flatpickr-day.endRange,
.flatpickr-day.selected:hover {
  background: var(--sk-teal-deep);
  border-color: var(--sk-teal-deep);
  color: #fff; font-weight: 800;
  box-shadow: none;
}
.flatpickr-day.inRange {
  background: var(--sk-teal-muted);
  color: var(--sk-teal-text);
  box-shadow: none; border-radius: 0;
}
.flatpickr-day.prevMonthDay, .flatpickr-day.nextMonthDay { color: var(--sk-text-muted); opacity: .55; }
.flatpickr-day.flatpickr-disabled, .flatpickr-day.flatpickr-disabled:hover {
  color: var(--sk-text-muted); opacity: .32; background: transparent;
}

/* Dark mode: the stock theme assumes a white calendar. */
[data-theme="dark"] .flatpickr-calendar {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), 0 18px 44px rgba(0,0,0,.5);
}
[data-theme="dark"] .flatpickr-day:hover { background: var(--sk-bg-subtle); }
[data-theme="dark"] .flatpickr-monthDropdown-month { background: var(--sk-card); }

/* ══════════════════════════════════════════════════════════
   36. SECTION GROUND — the page was a white void between cards
   Every section outside the hero sat on flat --sk-bg with nothing behind it,
   so the homepage read as a stack of cards floating in an empty room. The
   fix is NOT a coloured band per section: it is a barely-there ground plane,
   the same trick the hero photo does for the top of the page.

   Three layers, all extremely low contrast, none of them decorative shapes:
     1. a hairline grid, 3% ink, so large empty areas have texture at all
     2. two soft radial washes tinted with the brand, under 4%
     3. a top hairline so consecutive sections read as distinct planes

   Everything is on ::before at z-index 0 with the section's own content
   lifted above it, so no existing background declaration is overridden —
   sections that already set --sk-bg-white or --sk-bg-subtle keep theirs and
   simply sit on top.
═══════════════════════════════════════════════════════════ */
.section, .section-sm { position: relative; }
.section > *, .section-sm > * { position: relative; z-index: 1; }

.section::before, .section-sm::before {
  content: '';
  position: absolute; inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    radial-gradient(60% 70% at 12% 0%,  rgba(17,156,135,.045), transparent 70%),
    radial-gradient(55% 65% at 92% 100%, rgba(27,31,59,.038),  transparent 72%),
    linear-gradient(to right,  rgba(16,20,42,.028) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(16,20,42,.028) 1px, transparent 1px);
  background-size: 100% 100%, 100% 100%, 46px 46px, 46px 46px;
  /* Fade the grid out at the edges so it never collides with the footer or
     the hero — it should register as texture, not as a visible table. */
  -webkit-mask-image: radial-gradient(120% 90% at 50% 50%, #000 45%, transparent 100%);
          mask-image: radial-gradient(120% 90% at 50% 50%, #000 45%, transparent 100%);
}

/* Sections that declare their own fill sit ON the ground plane rather than
   fighting it: keep the wash, drop the grid so the surface stays clean. */
.section[style*="--sk-bg-white"]::before,
.section[style*="--sk-bg-subtle"]::before {
  background-image:
    radial-gradient(60% 70% at 12% 0%,  rgba(17,156,135,.05), transparent 70%),
    radial-gradient(55% 65% at 92% 100%, rgba(27,31,59,.04),  transparent 72%);
  background-size: 100% 100%, 100% 100%;
}

/* A hairline between consecutive sections. Cheap, and it does most of the
   work of making the page feel constructed rather than poured. */
.section + .section::after,
.section + .section-sm::after,
.section-sm + .section::after {
  content: '';
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: min(1180px, 88%); height: 1px; z-index: 0;
  background: linear-gradient(90deg, transparent, var(--sk-border), transparent);
}

[data-theme="dark"] .section::before,
[data-theme="dark"] .section-sm::before {
  background-image:
    radial-gradient(60% 70% at 12% 0%,  rgba(25,200,171,.05), transparent 70%),
    radial-gradient(55% 65% at 92% 100%, rgba(124,92,252,.045), transparent 72%),
    linear-gradient(to right,  rgba(255,255,255,.028) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,.028) 1px, transparent 1px);
  background-size: 100% 100%, 100% 100%, 46px 46px, 46px 46px;
}

/* ══════════════════════════════════════════════════════════
   37. AUDIT FIXES — contrast, tokens, and last-writer-wins accidents
   Found by a full design audit of the funnel. Each of these is a specific
   defect with a specific cause, not a preference.
═══════════════════════════════════════════════════════════ */

/* 37.1 .btn-primary was WHITE TEXT ON TEAL — about 2.1:1.
   design-system.css:166 had it right (ink on teal, 7.6:1). ui-enhancements
   then re-declared `background: var(--sk-primary); color: #fff`. §33 later
   repainted the BACKGROUND back to the teal gradient but never re-declared
   the colour, so the white was left stranded on teal. Every "Call Us Now",
   "Search More Flights" and no-results CTA in the funnel was affected, and
   sat inches from a .book-btn using ink on the identical gradient. */
.btn-primary, .book-btn, .submit-booking, .sf-search-btn {
  color: var(--sk-primary);
}
.btn-primary:hover { color: var(--sk-primary); }

/* 37.2 The two-tier hairline was collapsed into one.
   §1 mapped BOTH --sk-border and --sk-border-light to the same value, so a
   card's outline and its internal row dividers rendered identically and a
   stack of rows read as a table instead of as a card. The inner tier goes
   back to being genuinely quieter. */
:root { --sk-border-light: rgba(16,20,42,.045); }
[data-theme="dark"] { --sk-border-light: rgba(255,255,255,.045); }

/* 37.3 Amber semantics now use the token (see design-system.css).
   .stops-label.has-stops is text at 12px/700 — it needs to clear AA, and at
   #F59E0B it was 2.15:1. */
.stops-label.has-stops,
.day-offset,
.layover-pill .overnight,
.itin-layover .overnight { color: var(--sk-amber); }

/* 37.4 Selected flight card wore the coloured halo §33 forbids, and because
   its selector was stronger it also stripped that card's inset highlight —
   so selecting a row made it the only one in the list without the top-edge
   light. Same left rail as .is-best, in a stronger tone. */
.flight-card.is-selected {
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.9),
    0 2px 6px rgba(16,20,42,.05), 0 18px 40px rgba(16,20,42,.09);
  border-color: rgba(17,156,135,.45);
  position: relative; overflow: hidden;
}
.flight-card.is-selected::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: linear-gradient(180deg, var(--sk-teal), var(--sk-teal-dark));
}

/* 37.5 Return-flight panel drifted from the outbound row it sits inside. */
.ret-time { letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
/* Positive tracking is for small UPPERCASE labels only (§33 rule 3).
   .ret-price-sub and .summary-row small are sentence-case body copy, so the
   0.08em was spacing out ordinary prose. */
.ret-price-sub, .summary-row .label small { letter-spacing: 0; }
.ret-head { letter-spacing: .1em; }
/* A grey card nested inside a white card inverts the system's page-tinted /
   card-white relationship, and the hover lift contradicts the deliberate
   no-lift decision on the outbound rows directly above it. */
.ret-row { background: var(--sk-card); border: 1px solid var(--sk-border); }
.ret-row:hover { transform: none; border-color: rgba(17,156,135,.28); }
/* The error state was bare red text with no panel — the weakest surface in
   the funnel, shown exactly when something has gone wrong. */
.ret-error {
  background: #FEF2F2; border: 1px solid #FCA5A5;
  border-radius: var(--sk-r-lg); padding: 13px 16px;
  color: #B91C1C; font-size: 13px; line-height: 1.55;
}
[data-theme="dark"] .ret-error {
  background: rgba(190,18,18,.14); border-color: rgba(252,165,165,.32); color: #FCA5A5;
}

/* 37.6 Missing tabular figures on numbers that repeat down a column or
   change live under the cursor. */
.flight-number, .sf-qty-val, .order-id-badge, .ret-price {
  font-variant-numeric: tabular-nums;
}
/* An order ID is a big bold figure the customer is asked to copy — it had
   POSITIVE tracking, the wrong polarity for its size. */
.order-id-badge { letter-spacing: -0.01em; }

/* 37.7 Dark mode: the first interactive surface in the funnel was white.
   airport-autocomplete.css tokenises .sk-dropdown correctly, but
   ui-enhancements.css loads AFTER it and re-declares `background:#fff`,
   while the text inside stays var(--sk-text) — near-white on white. */
[data-theme="dark"] .sk-dropdown,
[data-theme="dark"] .sf-pax-pop,
[data-theme="dark"] #cta-popup {
  background: var(--sk-card);
  border: 1px solid var(--sk-border);
}
[data-theme="dark"] .sk-dd-item:hover,
[data-theme="dark"] .sk-dd-item.active { background: var(--sk-bg-subtle); }

/* 37.8 One sticky offset for one 70px header (was 80px / 90px). */
.sidebar, .summary-card { top: 88px; }

/* 37.9 .btn-secondary carried a 25% shadow rising to 35% — 2.5x the ~10%
   cap the rest of the system observes. */
.btn-secondary { box-shadow: var(--sk-e2); }
.btn-secondary:hover { box-shadow: var(--sk-e3); }

/* 37.10 .btn-ghost used --sk-teal as TYPE on hover (1.9:1). §31 exists
   precisely for this: there is a text-safe teal. */
.btn-ghost:hover { color: var(--sk-teal-text); border-color: var(--sk-teal-dark); }

/* ══════════════════════════════════════════════════════════
   38. JOIN THE SECONDARY PAGES TO THE SYSTEM
   Both design audits landed on the same root cause: §5 and §33 apply the
   craft details through explicit class ALLOW-LISTS, and every class on those
   lists came from the homepage, the results page or checkout. No secondary
   page class was ever added — so /about, /faq, /contact, /blog, /login, the
   five legal pages and the three new ones were never fighting the design
   system, they were never joined to it. Fourteen pages of flat white boxes.

   This is the join. Where a page's own <style> would win on source order,
   the fix went into that page instead — see about.php, login.php, contact.php
   and the legal pages in this commit.
═══════════════════════════════════════════════════════════ */

/* 38.1 The inset top highlight — §33.3 calls it "the detail that separates
   the homepage's search card from every other card on the site", and it was
   on none of these. */
.value-card, .about-intro-card, .team-note, .family-link-card,
.faq-item, .faq-cta, .channel-card, .contact-form-card, .hours-card,
.blog-card, .sidebar-card, .legal-card, .role-card,
.sup-card, .sup-channel, .auth-box, .info-card, .info-block,
.wholesale-band, .wholesale-carriers {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), var(--sk-e2);
}
[data-theme="dark"] .value-card, [data-theme="dark"] .about-intro-card,
[data-theme="dark"] .faq-item, [data-theme="dark"] .channel-card,
[data-theme="dark"] .contact-form-card, [data-theme="dark"] .blog-card,
[data-theme="dark"] .sidebar-card, [data-theme="dark"] .legal-card,
[data-theme="dark"] .role-card, [data-theme="dark"] .sup-card,
[data-theme="dark"] .sup-channel, [data-theme="dark"] .auth-box {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06),
              0 2px 4px rgba(0,0,0,.24), 0 12px 32px rgba(0,0,0,.34);
}
/* Tiles inside an already-elevated card get the highlight only, or the
   surface looks quilted. */
.stat-chip, .sup-channel .ch-ico, .role-card .role-ico {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.75);
}

/* 38.2 Tabular figures. §33 rule 4: ragged digits in a compared column are
   the most obvious "nobody looked at this" tell. These are all compared
   columns or live-updating values, and none of them were covered. */
.s-val, .tip-num, .channel-value, .price-from,
.stat-chip .s-val, .about-stat-num, .sup-badge {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}
/* The largest figure on every destination page, set at body tracking. */
.price-from, .about-stat-num, .s-val { letter-spacing: -0.02em; }

/* 38.3 Letter-spacing polarity on small pills — §33 rule 3 wants tiny
   uppercase labels POSITIVELY tracked; these all inherited the body's
   negative default. */
.blog-category, .tag-pill, .airline-chip, .airport-chip,
.airline-tag, .related-link, .s-lbl, .sup-badge, .carrier-head {
  letter-spacing: .09em;
}

/* 38.4 Legal pages were a single unbroken slab — /terms measures 1467px of
   continuous white with eight h2s and nothing to break them. A hairline above
   each section gives the eye somewhere to rest without adding chrome. */
.legal-card h2 {
  padding-top: 26px;
  border-top: 1px solid var(--sk-border-light);
}
.legal-card h2:first-of-type { padding-top: 0; border-top: none; }
/* Links were underlined inside <p> but not inside <li> — same card, two
   treatments. */
.legal-card li a { text-decoration: underline; text-underline-offset: 2px; }

/* 38.5 /login and /support and /careers had no hero and opened straight onto
   body copy. They cannot all gain a photo, but they can gain the ground
   plane the rest of the site now has (§36) plus breathing room at the top. */
.auth-wrap, .sup-wrap, .careers-wrap { position: relative; }
.auth-wrap::before, .sup-wrap::before, .careers-wrap::before {
  content: ''; position: absolute; inset: -40px 0 0; z-index: 0;
  pointer-events: none;
  background-image:
    radial-gradient(50% 45% at 50% 0%, rgba(17,156,135,.055), transparent 70%),
    radial-gradient(60% 50% at 85% 100%, rgba(27,31,59,.035), transparent 72%);
}
.auth-wrap > *, .sup-wrap > *, .careers-wrap > * { position: relative; z-index: 1; }

/* 38.6 /blog thumbnails were 180x240 slabs of flat indigo holding one 34px
   icon — about 0.9% of the area doing any work. The dead `font-size:56px`
   was sizing an emoji that is now an SVG. Texture plus a smaller footprint. */
.blog-card-thumb {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cpath d='M20 16v8M16 20h8' stroke='%23fff' stroke-opacity='.07' stroke-width='1.2' fill='none'/%3E%3C/svg%3E"),
    linear-gradient(135deg, var(--sk-primary), var(--sk-primary-light));
}
.blog-card-thumb svg { opacity: .85; }

/* 38.7 §35 styles the calendar's month dropdown and year field at 15px, and
   those selectors are more specific than §34's blanket rule — so the two
   controls inside the date picker were still under the 16px iOS threshold
   and would zoom the page when tapped. Same fix, matching specificity. */
@media (max-width: 820px), (pointer: coarse) {
  .flatpickr-current-month .flatpickr-monthDropdown-months,
  .flatpickr-current-month input.cur-year {
    font-size: 16px;
  }
}

/* 38.8 The trust bar wrapped its fifth item onto a second row, and because
   the row is `justify-content: space-between` the orphan sat hard left with a
   dangling divider beside it — it read as broken rather than as a wrap. The
   five US labels the client wrote are long, so rather than shorten them:
   tighten the row so all five fit, and if they ever cannot, wrap CENTRED and
   drop the dividers so the second row does not start with one. */
/* The five labels the client wrote are long. Dropping the pipe dividers
   frees roughly 100px, which is what lets all five sit on one line at the
   container's 1232px — and the row reads cleaner without them anyway. Gap
   alone separates the items. */
.trust-row { justify-content: center; gap: 12px 24px; }
.trust-item { font-size: 12.5px; white-space: nowrap; }
.trust-divider { display: none; }
@media (max-width: 1100px) {
  .trust-row { gap: 12px 22px; }
  .trust-item { font-size: 12px; }
}

/* ══════════════════════════════════════════════════════════
   39. COSMETIC CONSISTENCY
   The leftovers from the design audit: values that were not wrong so much as
   uncoordinated. Each group below is one decision applied everywhere.
═══════════════════════════════════════════════════════════ */

/* 39.1 Every button is a pill.
   They came in four radii — .btn 10px, .btn-sm 8px, .sf-search-btn 12px, and
   the funnel CTAs at 9999px — often two of them in the same viewport. The
   funnel's primary actions were already pills, so that is the shape that
   wins. */
.btn, .btn-sm, .btn-lg, .btn-ghost, .btn-primary, .btn-secondary, .btn-teal,
.sf-search-btn, .sf-trip-tab, .sf-cabin-btn, .sf-done-btn,
.book-btn, .call-btn, .submit-booking, .sort-select, .auth-submit,
.sk-call-btn, .sk-mob-call, .sk-mob-wa, .cta-phone, .social-btn-auth {
  border-radius: var(--sk-r-full);
}
/* Square-ish controls stay square-ish — a 40px icon button as a circle reads
   as an avatar, not a control. */
.sk-theme-btn, .sk-hamburger, .sf-swap-btn, .sf-qty-btn {
  border-radius: var(--sk-r-sm);
}

/* 39.2 Panels and overlays on one rung. .sk-dropdown was 14px while its
   sibling .sk-cur-menu was 18px, and the pax popover 16px. */
.sk-dropdown, .sf-pax-pop, #cta-popup, .sk-cur-menu, .flatpickr-calendar {
  border-radius: var(--sk-r-lg);
}
/* The hero search card was 22px against 24px flight cards stacked directly
   beneath it — a 2px delta at the same scale reads as a mistake. */
.sf-wrap { border-radius: var(--sk-r-xl); }

/* 39.3 Empty states were 80px of padding around a 34px icon in an ~880px
   box — the largest dead area in the funnel. Tighter, with the icon actually
   sized rather than relying on a font-size that an SVG ignores. */
.no-results { padding: 52px 24px; }
.no-results .icon {
  width: 60px; height: 60px; margin: 0 auto 18px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--sk-bg-subtle);
  color: var(--sk-text-muted);
}
.no-results .icon svg { width: 26px; height: 26px; }
.no-results h2 { font-size: 21px; margin-bottom: 8px; }
.no-results p  { font-size: 15px; margin-bottom: 22px; }

/* Dead `font-size` on wrappers whose only child is a fixed-size SVG. They
   were sizing emoji before the icon sweep; now they do nothing but mislead. */
.secure-notice > span[style], .no-results .icon[style] { font-size: 0 !important; }

/* 39.4 Semantic colour tokens. The error and warning panels were raw Tailwind
   hex inlined per page (#FEF2F2 / #FCA5A5 / #991B1B / #FEF3C7 / #F59E0B),
   with dark-mode patches that reached the container but not its children. */
:root {
  --sk-danger:       #B4232A;
  --sk-danger-tint:  #FDF2F2;
  --sk-danger-line:  rgba(180,35,42,.28);
  --sk-success:      #0F7666;
  --sk-success-tint: rgba(17,156,135,.10);
}
[data-theme="dark"] {
  --sk-danger:       #FCA5A5;
  --sk-danger-tint:  rgba(180,35,42,.16);
  --sk-danger-line:  rgba(252,165,165,.30);
  --sk-success:      #5FE0C9;
  --sk-success-tint: rgba(25,200,171,.14);
}
.booking-alert, .ret-error, .form-error, .alert-error {
  background: var(--sk-danger-tint) !important;
  border-color: var(--sk-danger-line) !important;
}
.booking-alert strong, .booking-alert p, .booking-alert .booking-alert-help,
.booking-alert a, .ret-error { color: var(--sk-danger) !important; }
.co-notice--amber {
  background: var(--sk-amber-tint); border-color: rgba(147,92,16,.25); color: var(--sk-amber);
}
.bag-tile.is-yes .bag-ico { background: var(--sk-success-tint); color: var(--sk-success); }
.bag-tile.is-no  .bag-ico { background: var(--sk-amber-tint);   color: var(--sk-amber); }

/* 39.5 Arrow glyphs that survived the icon sweep. They render at a different
   weight and baseline to the SVG set beside them, and differently again per
   OS. Where the arrow is decoration next to a word, size and align it so at
   least it stops looking like stray punctuation. */
.seg-toggle .chev, .sf-trip-tab, .btn .arrow-glyph {
  font-variant-emoji: text;
}
.seg-toggle .chev {
  display: inline-block; font-size: 11px; line-height: 1;
  transform: translateY(-1px); transition: transform var(--t-fast);
}
.seg-toggle.open .chev { transform: translateY(-1px) rotate(180deg); }

/* 39.6 The two arrows that must stay as characters — one lives inside a JS
   string, one repeats per flight segment. Align and weight them so they read
   as part of the type rather than as stray punctuation. */
.seg-arrow, .btn-arrow, .h1-arrow {
  display: inline-flex; align-items: center;
  vertical-align: middle; line-height: 1;
}
.h1-arrow { color: var(--sk-teal); margin: 0 4px; }
.seg-arrow { color: var(--sk-text-muted); margin: 0 4px; font-weight: 400; }
.btn-arrow { font-weight: 700; }

/* ═══ 40 · FOOTER — PAYMENT MARKS & TRUSTPILOT ═══════════════
   Official acceptance marks on white chips: Visa's navy and Amex's blue need
   a light ground in the dark footer, and a uniform chip row reads as one
   system rather than four stickers. Chips are auto-width with equal padding
   so each mark keeps its own proportions at a shared optical height.
   Trustpilot uses its dark-background variant (white wordmark, green star)
   straight on the footer ink — no chip, per their brand rules. */
.footer-badges {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px 28px; flex-wrap: wrap;
  /* 1280 + zero horizontal padding: the row must share .footer-bottom's own
     24px gutter, or "We accept" sits indented against the copyright line. */
  max-width: 1280px; margin: 0 auto 14px;
  padding: 2px 0 16px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.footer-pay { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.footer-badges-label {
  font-size: 11px; font-weight: 600; letter-spacing: .08em;
  text-transform: uppercase; color: rgba(255,255,255,.38);
}
.pay-list {
  display: flex; align-items: center; gap: 8px;
  list-style: none; margin: 0; padding: 0;
}
.pay-chip {
  display: flex; align-items: center; justify-content: center;
  height: 34px; padding: 0 10px;
  background: #fff; border-radius: var(--sk-r-sm);
  box-shadow: inset 0 0 0 1px rgba(27,31,59,.06);
}
.pay-chip img { display: block; }
.tp-badge {
  display: inline-flex; align-items: center; gap: 10px;
  text-decoration: none; border-radius: var(--sk-r-sm);
  transition: opacity var(--t-fast);
}
.tp-badge img { display: block; }
.tp-badge:hover .footer-badges-label,
.tp-badge:focus-visible .footer-badges-label { color: rgba(255,255,255,.62); }
@media (max-width: 560px) {
  .footer-badges { justify-content: center; padding: 0 0 14px; }
  .footer-pay { justify-content: center; }
}

/* 40.1 Two-line office address in the footer contact column. */
.footer-contact-addr { line-height: 1.5; display: inline-block; }

/* 40.2 Fixed-button clearance. The floating call button (§47.1, in the corner
   the WhatsApp bubble used to hold) and the scroll-top button are fixed to the
   bottom-right, and at full scroll they sat directly on the operated-by line
   and the disclaimer's phone number. Keep the footer's last rows out of that
   zone: right clearance where the rows are single lines, bottom clearance
   where they wrap. On mobile §47.2 overrides the bottom value again, for the
   taller sticky call bar. */
@media (min-width: 900px) {
  .footer-badges { padding-right: 96px; }
  .footer-bottom-inner, .footer-disclaimer { padding-right: 96px; }
}
@media (max-width: 899.98px) {
  .footer-bottom { padding-bottom: 104px; }
}

/* ═══ 41 · THE WORDMARK ══════════════════════════════════════
   The logo is now an inline SVG partial (include/logo-wordmark.php) — real
   Outfit 650 outlines, hand-kerned, generated by the logo build. "Sky" fills
   with currentColor so every existing .sk-logo / .loading-logo colour rule
   keeps working; "Karo" fills with a gradient whose stops read
   --logo-t1/--logo-t2, defined per ground below.

   Two pairs, one rule of thumb:
     dark ground  -> bright pair (teal -> mint) — glows against ink
     light ground -> deep pair (teal-text -> teal-deep) — stays readable;
   the bright #19C8AB is a fill colour, never small type on white (§ palette).

   :root and [data-theme] carry equal specificity — the dark-theme
   re-assertions must stay BELOW the light-ground rules. */
.sk-logo, .loading-logo {
  --logo-t1: var(--sk-teal);
  --logo-t2: #4ADCC2;
}
.sk-logo { display: inline-flex; align-items: center; }
.sk-wordmark { height: .98em; width: auto; display: block; }
.loading-logo { display: flex; justify-content: center; }

/* Light grounds: solid header, scrolled header, loading screen in light. */
.sk-header--solid .sk-logo,
.sk-header.scrolled .sk-logo {
  --logo-t1: var(--sk-teal-text);
  --logo-t2: var(--sk-teal-deep);
}
:root:not([data-theme="dark"]) .loading-logo {
  --logo-t1: var(--sk-teal-text);
  --logo-t2: var(--sk-teal-deep);
}

/* Dark theme: those same contexts sit on ink again -> bright pair. */
[data-theme="dark"] .sk-header--solid .sk-logo,
[data-theme="dark"] .sk-header.scrolled .sk-logo {
  --logo-t1: var(--sk-teal);
  --logo-t2: #4ADCC2;
}

/* ═══ 42 · PRICE SAVER — DOMESTIC BAND ═══════════════════════
   Featured route panel + six route chips. The panel borrows the wholesale
   band's ink gradient so the homepage keeps one "dark feature" vocabulary;
   chips are ordinary cards: xl radius, top-edge highlight, small lift on
   hover. Figures are tabular and negative-tracked like every other price. */
.dom-band {
  display: grid; grid-template-columns: 340px 1fr; gap: 20px;
  align-items: stretch;
}
.dom-feature {
  display: flex; flex-direction: column; justify-content: center; gap: 12px;
  padding: 30px 28px;
  background: linear-gradient(140deg, var(--sk-primary) 0%, var(--sk-primary-light) 100%);
  border-radius: var(--sk-r-xl);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), var(--sk-e3);
  color: #fff; text-decoration: none; position: relative; overflow: hidden;
  transition: transform var(--t-fast), box-shadow var(--t-base);
}
.dom-feature::after {
  content: ''; position: absolute; inset: auto -40px -60px auto;
  width: 180px; height: 180px; border-radius: 50%;
  background: radial-gradient(circle, rgba(25,200,171,.28), transparent 70%);
  pointer-events: none;
}
.dom-feature:hover { transform: translateY(-3px); box-shadow: inset 0 1px 0 rgba(255,255,255,.14), var(--sk-e4); }
.dom-feature-badge {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--sk-teal); background: rgba(25,200,171,.12);
  border: 1px solid rgba(25,200,171,.3);
  padding: 5px 12px; border-radius: var(--sk-r-full);
}
.dom-feature-route { display: flex; align-items: center; gap: 14px; }
.dom-code {
  font-family: var(--font-heading); font-weight: 800; font-size: 34px;
  letter-spacing: -0.02em;
}
.dom-plane { color: var(--sk-teal); display: inline-flex; }
.dom-feature-cities { font-size: 14px; color: rgba(255,255,255,.72); }
.dom-feature-price { font-size: 14px; color: rgba(255,255,255,.72); }
.dom-feature-price strong {
  font-family: var(--font-heading); font-weight: 800; font-size: 30px;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
  color: #fff; margin: 0 2px;
}
.dom-cur { font-size: 11px; font-weight: 700; letter-spacing: .06em; color: var(--sk-teal); }
.dom-feature-cta {
  display: inline-flex; align-items: center; gap: 7px; margin-top: 4px;
  font-size: 14px; font-weight: 700; color: var(--sk-teal);
}
.dom-grid {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px;
}
.dom-chip {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 18px 22px;
  background: var(--sk-card); border: 1px solid var(--sk-border);
  border-radius: var(--sk-r-xl);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), var(--sk-e1);
  text-decoration: none; color: inherit;
  transition: transform var(--t-fast), box-shadow var(--t-base), border-color var(--t-base);
}
.dom-chip:hover {
  transform: translateY(-2px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), var(--sk-e3);
  border-color: rgba(17,156,135,.3);
}
.dom-chip-codes {
  display: block; font-family: var(--font-heading); font-weight: 800;
  font-size: 17px; letter-spacing: -0.01em; color: var(--sk-text);
}
.dom-chip-arrow { color: var(--sk-teal-dark); font-weight: 700; margin: 0 2px; }
.dom-chip-cities { display: block; font-size: 13px; color: var(--sk-text-muted); margin-top: 3px; }
.dom-chip-price { font-size: 13px; color: var(--sk-text-muted); white-space: nowrap; }
.dom-chip-price strong {
  font-family: var(--font-heading); font-weight: 800; font-size: 19px;
  letter-spacing: -0.015em; font-variant-numeric: tabular-nums;
  color: var(--sk-text); margin-left: 2px;
}
@media (max-width: 900px) {
  .dom-band { grid-template-columns: 1fr; }
  .dom-feature { padding: 26px 24px; }
}
@media (max-width: 560px) {
  .dom-grid { grid-template-columns: 1fr; }
  .dom-code { font-size: 28px; }
}
[data-theme="dark"] .dom-chip { box-shadow: inset 0 1px 0 rgba(255,255,255,.06), var(--sk-e1); }
[data-theme="dark"] .dom-chip:hover { box-shadow: inset 0 1px 0 rgba(255,255,255,.06), var(--sk-e3); }

/* ═══ 43 · HERO VERTICAL RHYTHM ══════════════════════════════
   The search card is what the visitor came for, and it was landing 70px
   BELOW the fold on a 776px viewport — hero content measured 934px tall, so
   the card was clipped and its centre sat 370px below the middle of the
   screen. Reference (skyteamtravel.ca) runs eyebrow -> title -> two-line sub
   -> card, and lands the card mid-screen.

   Three changes get it there without touching the approved headline copy:
   the duplicated trust chips are gone (see index.php), the city line moved
   below the card, and the block padding is tightened. Header clearance is
   still comfortable — the fixed header is 80px and top padding is 112px.

   ── SECOND PASS (client: "still towards the bottom") ──────
   That first pass got the card whole and on-screen, but measured on
   production its TOP was at 62% and its CENTRE at 73% — the middle of the
   thing people came to use was sitting in the bottom quarter. Trimmed a
   further ~110px out of the stack above it: smaller headline cap, a sub that
   sets in two lines instead of three, and tighter gaps throughout.

   The counter-intuitive part: `.hero` centres its block
   (`align-items:center`), so shrinking the content alone does NOT raise the
   card — the block is re-centred in the same viewport and everything drifts
   back down by half of whatever was saved. Trimming 110px only bought 8%.

   So the hero now aligns its block to the TOP and the card's position is
   simply `padding-top + the stack above it` — a fixed number, identical on
   every screen height, instead of something that drifts with the viewport.
   The leftover space falls below the card, where the photograph absorbs it,
   and `min-height` still caps the hero at 900px so a tall monitor does not
   get an endless beach. */
.hero        { align-items: flex-start; }
.hero-inner  { padding-top: 92px; padding-bottom: 72px; }
.hero-copy   { margin-bottom: 22px; }
.hero-eyebrow{ margin-bottom: 16px; }
.hero-title  { font-size: clamp(2.2rem, 4.4vw, 3.6rem); margin-bottom: 14px; }
/* Two lines, not three: the wider measure is what buys the card its height,
   and a punchier sub is the right answer anyway. */
.hero-sub    { margin-bottom: 0; max-width: 660px; font-size: clamp(.98rem, 1.25vw, 1.06rem); }

/* Below the card now, so it reads as a footnote to the search rather than a
   second subtitle. Wider and centred under the full card width. */
.hero-cities {
  margin: 18px auto 0;
  max-width: 74ch;
  text-align: center;
  font-size: 13px;
  color: rgba(255,255,255,0.68);
  text-shadow: 0 1px 10px rgba(0,0,0,.34);
}

@media (max-width: 820px) {
  /* Was 96px, i.e. lower than the desktop 92 — pointless, since the mobile
     header is the shorter of the two. */
  .hero-inner  { padding-top: 80px; padding-bottom: 44px; }
  .hero-copy   { margin-bottom: 20px; }
  .hero-cities { font-size: 12.5px; text-align: left; margin-top: 14px; }
}

/* 43.1 Short viewports — the real failure case.
   A 1366×768 laptop gives roughly 640px of viewport once browser chrome is
   taken out, and that is the machine the complaint came from. Width-based
   queries never fire there: it is a WIDE screen that is SHORT. Scale the hero
   down against HEIGHT so the search card stays whole on a laptop lid. */
@media (max-height: 760px) and (min-width: 821px) {
  .hero-inner  { padding-top: 92px; padding-bottom: 40px; }
  .hero-title  { font-size: clamp(2.2rem, 4.2vw, 3.4rem); margin-bottom: 14px; }
  .hero-eyebrow{ margin-bottom: 14px; }
  .hero-sub    { font-size: 1rem; line-height: 1.55; }
  .hero-copy   { margin-bottom: 22px; }
  .hero-cities { margin-top: 12px; font-size: 12.5px; }
}
@media (max-height: 640px) and (min-width: 821px) {
  .hero-inner  { padding-top: 76px; padding-bottom: 20px; }
  .hero-title  { font-size: clamp(1.8rem, 3vw, 2.2rem); margin-bottom: 10px; }
  .hero-eyebrow{ margin-bottom: 10px; padding: 6px 14px; font-size: 12px; }
  .hero-sub    { margin-bottom: 0; font-size: .95rem; line-height: 1.5; }
  .hero-copy   { margin-bottom: 18px; }
  /* The city line is the first thing to go: it is SEO evidence, and on a
     laptop lid the search card earns that space instead. Still in the DOM
     for crawlers — visually hidden, not removed. */
  .hero-cities { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
}

/* 43.2 Mobile viewport units. 100vh on iOS includes the URL bar, so the hero
   is taller than the screen and the card is pushed under it on first paint.
   svh is the small-viewport height — the honest number. */
@supports (height: 100svh) {
  .hero { min-height: min(100svh, 900px); }
}

/* ═══ 44 · FOOTER TYPE — LIFT OFF THE FLOOR ══════════════════
   Client: "the footer fonts are too light." Measured against the darkest end
   of the footer gradient (#151936), five of the eight footer text styles were
   failing WCAG AA (4.5:1) outright:

     disclaimer .35   -> 3.21    copyright .35    -> 3.21
     family badge .35 -> 3.21    contact label .40-> 3.77
     badges label .38 -> 3.51

   So this is not only taste — most of the footer was legally thin as well as
   visually thin. Values below are chosen from that measurement, not by eye,
   and every one now clears AAA (7:1) while keeping the hierarchy intact:
   headings white, values brightest, body next, small labels quietest.

   Alpha rather than flat hex so the gradient still shows through, and so the
   same rules keep working if the footer ink is ever retuned. */
.footer-contact-val,
.footer-col ul a            { color: rgba(255,255,255,.90); }   /* 14.03:1 */
.footer-brand p             { color: rgba(255,255,255,.78); }   /* 10.74:1 */
.footer-bottom p,
.footer-family-badge        { color: rgba(255,255,255,.74); }   /*  9.77:1 */
.footer-disclaimer          { color: rgba(255,255,255,.72); }   /*  9.35:1 */
.footer-contact-label,
.footer-badges-label        { color: rgba(255,255,255,.66); }   /*  8.03:1 */

/* The two longest runs of small text carry a little more weight as well —
   at 12–13px on a dark ground, 400 reads thinner than the same weight does
   on white, which is the other half of what "too light" meant. */
.footer-disclaimer,
.footer-bottom p            { font-weight: 450; }
.footer-contact-label       { letter-spacing: .06em; }

/* Links need somewhere brighter to go now that rest state is .90. */
.footer-col ul a:hover,
.footer-contact-val:hover   { color: var(--sk-teal); }

/* Inline links inside the disclaimer were inheriting the old washed tone. */
.footer-disclaimer a        { color: rgba(255,255,255,.92); text-decoration: underline; text-underline-offset: 2px; }
.footer-disclaimer a:hover  { color: var(--sk-teal); }
.footer-disclaimer strong,
.footer-bottom p strong     { color: rgba(255,255,255,.96); font-weight: 700; }

/* ═══ 45 · MOTION LAYER ══════════════════════════════════════
   Styles for js/motion.js. Everything here is inert until that file adds its
   classes, so a blocked or failed script costs polish and nothing else. */

/* 45.1 Momentum scrolling owns the scroll position while it is active, so the
   CSS smooth-scroll must stand down or the two interpolate against each other
   and the page feels rubbery. Anchor jumps still animate: scrollIntoView
   ({behavior:'smooth'}) asks explicitly and overrides this. */
html.sk-momentum { scroll-behavior: auto; }

/* 45.2 Card parallax. <picture> becomes the moving frame — the photo keeps its
   own transform for the hover zoom, so the two never fight over one property.
   112% height gives the drift somewhere to go without ever showing an edge;
   motion.js caps travel at 5% each way. */
.dest-card picture {
  position: absolute;
  inset: -6% 0;
  display: block;
  will-change: transform;
}
.dest-card picture img { position: absolute; inset: 0; width: 100%; height: 100%; }

/* 45.3 Magnetic buttons. The pull is composed WITH the hover lift rather than
   replacing it — an inline transform would otherwise cancel `.btn:hover`
   (§refinements 405) and the button would stop lifting the moment it gained a
   magnet. Specificity is raised past the bare `.btn:hover` on purpose. */
.btn.sk-magnetic, a.sk-magnetic, .sk-call-btn.sk-magnetic {
  transform: translate3d(var(--mx, 0px), calc(var(--my, 0px) + var(--lift, 0px)), 0);
  transition: transform 260ms cubic-bezier(.22, 1, .36, 1), box-shadow var(--t-base);
}
.btn.sk-magnetic:hover, a.sk-magnetic:hover, .sk-call-btn.sk-magnetic:hover { --lift: -1.5px; }
.btn.sk-magnetic:active, a.sk-magnetic:active, .sk-call-btn.sk-magnetic:active { --lift: 0px; }
/* While the cursor is inside, follow it immediately; the transition above is
   for the settle back to centre on leave. */
.sk-magnetic:hover { transition: transform 90ms linear, box-shadow var(--t-base); }

/* 45.4 Line-by-line title reveal. Each line sits in a clipping box and rises
   into it, so the letters appear to be uncovered rather than to fly in. */
.section-title.sk-split .ln {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  /* Descenders (g, y, p) are clipped by an exact-height box — pad the box and
     pull it back so they survive. */
  padding-bottom: .12em;
  margin-bottom: -.12em;
}
.section-title.sk-split .ln-i {
  display: inline-block;
  transform: translate3d(0, 105%, 0);
  opacity: 0;
  transition:
    transform 760ms cubic-bezier(.16, 1, .3, 1) var(--ln-d, 0ms),
    opacity   520ms ease-out var(--ln-d, 0ms);
  will-change: transform;
}
.section-title.sk-split.is-lit .ln-i {
  transform: translate3d(0, 0, 0);
  opacity: 1;
  will-change: auto;
}

@media (prefers-reduced-motion: reduce) {
  .dest-card picture { position: absolute; inset: 0; transform: none !important; }
  .sk-magnetic { transform: none !important; }
  .section-title.sk-split .ln-i { transform: none !important; opacity: 1 !important; }
}

/* 45.5 Reveal tempo. reveal.js tags each auto-revealed block with a role;
   these give the three roles distinct gestures so the page composes itself
   instead of repeating one move down the whole scroll.

     lead — section furniture: further, slower, softest curve
     card — grid items: shorter and quicker, so a row feels crisp
     band — full-width blocks: scale, never translate. A 1200px band sliding
            vertically is indistinguishable from a layout shift. */
html.sk-reveal [data-reveal][data-tempo="lead"] {
  transform: translate3d(0, 26px, 0);
  transition:
    opacity   .78s cubic-bezier(.16, 1, .3, 1) var(--rv-delay, 0ms),
    transform .78s cubic-bezier(.16, 1, .3, 1) var(--rv-delay, 0ms);
}
html.sk-reveal [data-reveal][data-tempo="card"] {
  transform: translate3d(0, 14px, 0);
  transition:
    opacity   .46s cubic-bezier(.22, 1, .36, 1) var(--rv-delay, 0ms),
    transform .46s cubic-bezier(.22, 1, .36, 1) var(--rv-delay, 0ms);
}
html.sk-reveal [data-reveal][data-tempo="band"] {
  transform: scale(.985);
  transition:
    opacity   .66s cubic-bezier(.16, 1, .3, 1) var(--rv-delay, 0ms),
    transform .66s cubic-bezier(.16, 1, .3, 1) var(--rv-delay, 0ms);
}
html.sk-reveal [data-reveal][data-tempo].is-in { transform: none; }

/* ═══ 46 · ICON CONTRAST ON DARK GROUNDS ═════════════════════
   Client: "the icons in the footer are greyed or almost invisible."
   Measured, and they were not exaggerating — two separate faults, both of
   them a light-surface value used on a dark surface.

   FAULT A — the footer's icons inherited the page text colour.
   The footer is dark in BOTH themes, but `--sk-text` is not: it is navy
   #1B1F3B in light mode and near-white in dark. So the five contact icons
   and the social icon rendered navy-on-navy at 1.06:1 and 1.14:1 in LIGHT
   mode — which is the default, and the only mode the client was looking at.
   Dark mode hid the bug completely, which is why the earlier dark-theme
   pass came back clean. An always-dark region has to state its own colours
   rather than inherit theme-dependent ones.

   FAULT B — `--sk-teal-dark` (#0F7666) used as an icon colour on dark.
   That token is the type-on-LIGHT teal (§31). On the dark card surfaces it
   measures 2.54:1. The bright `--sk-teal` is the fill token, and a filled
   icon is exactly what it is for: 6.6:1 on those cards, 7.17:1 in the
   footer. This does not contradict "teal is never a text colour" — these
   are icons, and the handoff already notes teal on dark navy is correct.

     footer contact icons   1.06 -> 7.17
     footer social icon     1.14 -> 12.51
     footer route icons     3.11 -> 7.17
     step/why/pillar (dark) 2.54 -> 6.60
     channel icons  (dark)  2.54 -> 6.60                                  */

/* A · the footer states its own icon colours, in both themes. */
.footer-contact-icon {
  background: rgba(25, 200, 171, .13);
  box-shadow: inset 0 0 0 1px rgba(25, 200, 171, .18);
}
.footer-contact-icon .sk-icon,
.footer-contact-icon svg { color: var(--sk-teal); }

.footer-social a svg,
.footer-social a .sk-icon { color: #EEF0FF; }
/* The tile fills solid teal on hover, so the glyph has to invert with it. */
.footer-social a:hover svg,
.footer-social a:hover .sk-icon { color: var(--sk-primary); }

.footer-col ul li a .sk-icon,
.footer-col ul li a svg { color: var(--sk-teal); }
.footer-col ul li a:hover .sk-icon { color: var(--sk-teal-light, #4ADCC2); }

/* B · on dark grounds the icon teal is the bright fill, not the light-mode
   type value. Scoped to the tiles so ordinary teal type on light surfaces
   keeps the readable dark token it needs. */
[data-theme="dark"] .why-icon .sk-icon,
[data-theme="dark"] .step-icon .sk-icon,
[data-theme="dark"] .pillar-icon .sk-icon,
[data-theme="dark"] .channel-icon .sk-icon,
[data-theme="dark"] .why-icon svg,
[data-theme="dark"] .step-icon svg,
[data-theme="dark"] .pillar-icon svg,
[data-theme="dark"] .channel-icon svg { color: var(--sk-teal); }

/* ── 46.1 Headings on always-dark surfaces ──────────────────
   /careers shipped with an INVISIBLE heading: "Introduce yourself" rendered
   navy-on-navy at a contrast ratio of exactly 1.00.

   The mechanism is worth stating, because it will happen again otherwise.
   `.apply-card` sets `color:#fff`, and the heading inside it was expected to
   pick that up. But that white is INHERITED, and inheritance loses to any
   rule that matches the element directly — even a bare tag selector. So
   design-system's `h1,h2,h3,h4,h5 { color: var(--sk-text) }` won, and
   --sk-text is navy in light mode. In dark mode --sk-text flips to near-white
   and the bug vanishes, which is why it survived every dark-theme pass.

   `color: inherit` puts these headings back on their surface's own colour,
   whatever the theme does. Same fix shape as the footer icons above: a
   region that is dark in BOTH themes must not read theme-dependent tokens.

   Keep this list in step with the dark surfaces in the page <style> blocks. */
.apply-card    :is(h1,h2,h3,h4,h5,h6),
.mission-strip :is(h1,h2,h3,h4,h5,h6),
.post-hero     :is(h1,h2,h3,h4,h5,h6),
.blog-hero     :is(h1,h2,h3,h4,h5,h6),
.faq-hero      :is(h1,h2,h3,h4,h5,h6),
.dest-hero     :is(h1,h2,h3,h4,h5,h6),
.contact-hero  :is(h1,h2,h3,h4,h5,h6),
.wholesale-band:is(h1,h2,h3,h4,h5,h6),
.dom-feature   :is(h1,h2,h3,h4,h5,h6),
.stats-strip   :is(h1,h2,h3,h4,h5,h6) { color: inherit; }
/* NOT the footer. `.footer-col h2` already states #fff, and `.sk-footer
   :is(h1..h6)` carries the same (0,1,1) specificity — landing later in this
   file, it silently won and pushed the column headings to 1.07:1. Caught on
   the re-audit; the lesson is that "make it inherit" is only safe on a
   surface that has not already solved the problem its own way. */

/* ══════════════════════════════════════════════════════════
   47. AEO SURFACES — the blocks answer engines actually read

   Three new components shared by every SEO landing page. They exist for
   extraction as much as for the reader, which shapes the styling:

     .sk-crumbs  the visible breadcrumb trail. Rendered from the SAME array
                 that produces the BreadcrumbList JSON-LD, so markup and page
                 cannot disagree about the path.
     .sk-answer  the quick-answer paragraph. First prose on the page, one
                 self-contained paragraph, and the target of the WebPage's
                 speakable selector. An AI Overview lifting one passage lifts
                 this one, so it must read correctly with zero surrounding
                 context — which is a writing constraint, not a CSS one, but
                 the styling has to keep it visually first too.
     .sk-facts   the key-facts table. Tabular data extracts far more reliably
                 than the same facts buried in a paragraph.

   All three sit on the ordinary page surface, which is light in light mode and
   dark in dark mode, so they read theme tokens directly. That is correct HERE
   and would be the §46 bug on an always-dark surface — the distinction is
   whether the surface itself flips with the theme. These do.
   ══════════════════════════════════════════════════════════ */

/* ── 47.1 Breadcrumb trail ────────────────────────────────── */
.sk-crumbs { margin: 0 0 18px; font-size: 13px; }
.sk-crumbs ol {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
}
.sk-crumbs li { display: flex; align-items: center; gap: 6px; min-width: 0; }
/* The separator is generated, never a character in the markup — a "/" in the
   DOM ends up inside the crawler's text as part of the link name. */
.sk-crumbs li + li::before {
  content: '/'; color: var(--sk-text-muted); opacity: .55; font-weight: 600;
}
.sk-crumbs a {
  color: var(--sk-text-secondary); text-decoration: none; font-weight: 600;
  border-radius: var(--sk-r-sm); transition: color var(--sk-t-fast);
}
.sk-crumbs a:hover { color: var(--sk-teal-text); text-decoration: underline; }
.sk-crumbs [aria-current="page"] span {
  color: var(--sk-text-muted); font-weight: 600;
  /* The trail can be long on a route page and it is the least important thing
     on it — truncate rather than wrap to a second line on a phone. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 60vw;
}

/* Trail sitting on a dark hero rather than the page ground. Same reasoning as
   §46: this surface does NOT flip with the theme, so it cannot read the
   theme-dependent tokens above. */
.route-hero .sk-crumbs a,
.dest-hero  .sk-crumbs a { color: rgba(255,255,255,.82); }
.route-hero .sk-crumbs a:hover,
.dest-hero  .sk-crumbs a:hover { color: #fff; }
.route-hero .sk-crumbs [aria-current="page"] span,
.dest-hero  .sk-crumbs [aria-current="page"] span,
.route-hero .sk-crumbs li + li::before,
.dest-hero  .sk-crumbs li + li::before { color: rgba(255,255,255,.62); }

/* ── 47.2 Quick answer ────────────────────────────────────── */
.sk-answer {
  background: var(--sk-card);
  border: 1px solid var(--sk-border);
  /* The accent is a left rule, not a fill: §33 rations accent to under ~5% of
     the viewport, and this block is large. --sk-teal-dark rather than
     --sk-teal because it sits against a light card in light mode. */
  border-left: 4px solid var(--sk-teal-dark);
  border-radius: var(--sk-r-xl);
  padding: 24px 28px;
  margin: 0 0 28px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), var(--sk-e2);
}
.sk-answer-label {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-heading); font-weight: 800;
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--sk-teal-text);
  margin-bottom: 10px;
}
.sk-answer p {
  margin: 0; font-size: 16px; line-height: 1.7; color: var(--sk-text);
}
.sk-answer p + p { margin-top: 12px; }
.sk-answer strong { color: var(--sk-text); font-weight: 800; }
/* The provenance line under a tracked fare — smaller, quieter, and never
   omitted, because the figure above it is only defensible with it. */
.sk-answer-src {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--sk-border);
  font-size: 12.5px; line-height: 1.6; color: var(--sk-text-muted);
}
[data-theme="dark"] .sk-answer { box-shadow: inset 0 1px 0 rgba(255,255,255,.05), var(--sk-e2); }

/* ── 47.3 Key facts ───────────────────────────────────────── */
.sk-facts {
  width: 100%; border-collapse: collapse; margin: 0 0 28px;
  background: var(--sk-card); border: 1px solid var(--sk-border);
  border-radius: var(--sk-r-xl); overflow: hidden;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), var(--sk-e2);
}
[data-theme="dark"] .sk-facts { box-shadow: inset 0 1px 0 rgba(255,255,255,.05), var(--sk-e2); }
.sk-facts caption {
  caption-side: top; text-align: left; padding: 18px 22px 0;
  font-family: var(--font-heading); font-weight: 800; font-size: 15px;
  color: var(--sk-text);
}
.sk-facts th, .sk-facts td {
  padding: 13px 22px; text-align: left; font-size: 14px; line-height: 1.6;
  border-top: 1px solid var(--sk-border); vertical-align: top;
}
.sk-facts tr:first-child th, .sk-facts tr:first-child td { border-top: 0; }
.sk-facts th {
  width: 34%; font-weight: 700; color: var(--sk-text-secondary);
  white-space: nowrap;
}
.sk-facts td { color: var(--sk-text); font-weight: 600; }
/* Figures line up in a compared column — §38.2. */
.sk-facts td.num { font-variant-numeric: tabular-nums; }

@media (max-width: 560px) {
  /* Stack rather than squeeze: a 34% label column on a phone leaves the value
     three words wide and the table taller than the paragraph it replaced. */
  .sk-facts th, .sk-facts td { display: block; width: auto; white-space: normal; }
  .sk-facts th { padding-bottom: 2px; font-size: 12px; text-transform: uppercase;
                 letter-spacing: .04em; color: var(--sk-text-muted); }
  .sk-facts td { padding-top: 0; border-top: 0; }
  .sk-answer { padding: 20px; }
  .sk-answer p { font-size: 15px; }
}

/* ── 47.4 Month strip — tracked fares by departure month ──── */
.sk-months { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 28px; }
.sk-month {
  flex: 1 1 92px; min-width: 92px;
  background: var(--sk-card); border: 1px solid var(--sk-border);
  border-radius: var(--sk-r-md); padding: 12px 10px; text-align: center;
}
.sk-month .m-name {
  font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--sk-text-muted); margin-bottom: 5px;
}
.sk-month .m-val {
  font-family: var(--font-heading); font-weight: 800; font-size: 16px;
  color: var(--sk-text); font-variant-numeric: tabular-nums;
}
/* The cheapest month is the point of the strip, so it gets the accent — as a
   border and a type colour, not a fill, so the row stays calm. */
.sk-month.is-low { border-color: var(--sk-teal-dark); }
.sk-month.is-low .m-val { color: var(--sk-teal-text); }
.sk-month.is-low .m-name { color: var(--sk-teal-text); }

/* ══════════════════════════════════════════════════════════
   48. THE MYTHOS LAYER — themed section naming

   SkyKaro is named for Icarus, and the client wants that theme through the
   site's section naming. The kicker is how it is done without cost: a small
   themed label ABOVE a heading that stays literal and keyword-bearing.

     <p class="sk-kicker">The Daedalus Method</p>
     <h2>Getting the best price on New York to Delhi flights</h2>

   Headings are the primary surface for both ranking and answer-engine
   extraction, so the theme must never occupy one on a page that is trying to
   rank. See include/mythos.php for the rule and for which headings the theme
   IS allowed to own outright.

   Styled as a small-caps eyebrow: §33 rule 3 wants positive tracking on tiny
   uppercase labels, and this is the canonical case for it.
   ══════════════════════════════════════════════════════════ */

.sk-kicker {
  font-family: var(--font-heading);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--sk-teal-text);
  margin: 0 0 5px;
  display: flex;
  align-items: center;
  gap: 9px;
}
/* A short rule after the label, so it reads as a section marker rather than as
   a heading that lost its size. Grows to fill, capped so it never becomes a
   full-width divider. */
.sk-kicker::after {
  content: '';
  height: 1px;
  flex: 1 1 auto;
  max-width: 56px;
  background: linear-gradient(to right, var(--sk-teal-dark), transparent);
  opacity: .55;
}
/* Centred sections (FAQ, CTA) need the rule on both sides or the label sits
   visually off-axis. */
.sk-kicker--center { justify-content: center; }
.sk-kicker--center::before {
  content: '';
  height: 1px; flex: 1 1 auto; max-width: 56px;
  background: linear-gradient(to left, var(--sk-teal-dark), transparent);
  opacity: .55;
}

.sk-themed-h { margin-top: 0; }

/* On the dark hero and the gradient CTA strips, --sk-teal-text is the
   type-on-LIGHT teal and drops to ~2:1. Same §46 trap as everywhere else: a
   surface that does not flip with the theme must not read a theme-dependent
   token. --sk-teal is the fill-weight teal and reads correctly on ink. */
.route-hero .sk-kicker,
.dest-hero  .sk-kicker,
.call-strip .sk-kicker,
.price-banner .sk-kicker { color: var(--sk-teal); }
.route-hero .sk-kicker::after,
.dest-hero  .sk-kicker::after,
.call-strip .sk-kicker::after,
.price-banner .sk-kicker::after,
.call-strip .sk-kicker--center::before,
.price-banner .sk-kicker--center::before {
  background: linear-gradient(to right, var(--sk-teal), transparent);
}

@media (max-width: 560px) {
  .sk-kicker { font-size: 10px; letter-spacing: .12em; }
  .sk-kicker::after, .sk-kicker--center::before { max-width: 28px; }
}

/* ═══ 47 · CALL CTAs ═════════════════════════════════════════
   WhatsApp went dark (SK_WHATSAPP_ON, off 2026-08-25) and its bubble held the
   most valuable pixel on the page: bottom-right, above the fold on every
   scroll position. Rather than leave that corner empty, calling takes it —
   which is the conversion this business actually runs on.

   The split is deliberate:
     desktop  -> #call-fab, a corner button, because a full-width bar on a
                 1440px viewport looks like an ad
     mobile   -> #sk-callbar, full width in the thumb zone, because a 54px
                 corner circle is the worst place to put the primary CTA on a
                 phone

   Never both at once: the media queries below are mutually exclusive.
   ══════════════════════════════════════════════════════════ */

/* ── 47.1 Desktop floating call button ─────────────────────── */
#call-fab {
  position: fixed; bottom: 24px; right: 24px; z-index: 700;
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--sk-teal); color: var(--sk-primary);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 22px rgba(17,156,135,0.42);
  transition: transform .2s ease, box-shadow .2s ease;
}
#call-fab:hover { transform: scale(1.08); box-shadow: 0 10px 32px rgba(17,156,135,0.55); }
#call-fab:focus-visible { outline: 3px solid var(--sk-primary); outline-offset: 3px; }

/* An expanding ring rather than a bouncing button: it reads as "line is open"
   and, unlike a transform on the button itself, it cannot shift the tap
   target out from under a cursor mid-click. */
.call-fab-ring {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid var(--sk-teal);
  animation: callRing 2.4s ease-out infinite;
}
@keyframes callRing {
  0%   { transform: scale(1);   opacity: .75; }
  70%  { transform: scale(1.5); opacity: 0; }
  100% { transform: scale(1.5); opacity: 0; }
}

/* When WhatsApp is switched back on it takes the corner and the call button
   stacks above it, so neither is buried. */
#wa-bubble ~ #call-fab { bottom: 88px; }
#wa-bubble ~ #call-fab ~ #scroll-top { bottom: 152px; }

/* ── 47.2 Mobile sticky call bar ───────────────────────────── */
#sk-callbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 690;
  display: none; gap: 9px; padding: 9px 12px;
  padding-bottom: calc(9px + env(safe-area-inset-bottom, 0px));
  background: color-mix(in srgb, var(--sk-primary) 96%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid rgba(255,255,255,.10);
  box-shadow: 0 -6px 22px rgba(27,31,59,.28);
  transform: translateY(110%);
  transition: transform .32s cubic-bezier(.34,1.4,.64,1);
}
#sk-callbar.visible { transform: translateY(0); }

#sk-callbar .cb-call {
  flex: 1.7 1 0; display: flex; align-items: center; justify-content: center; gap: 9px;
  background: var(--sk-teal); color: var(--sk-primary);
  border-radius: var(--sk-r-full); padding: 11px 14px;
  font-family: var(--font-heading); font-weight: 800;
  box-shadow: 0 3px 12px rgba(17,156,135,.4);
}
.cb-call-txt { display: flex; flex-direction: column; line-height: 1.15; text-align: left; }
.cb-call-txt strong { font-size: 14.5px; font-weight: 800; }
/* The wait-time line is the reason this converts — it answers "will I be on
   hold?" before the tap, not after. First thing to drop when space is tight. */
.cb-call-txt small { font-size: 10.5px; opacity: .78; font-weight: 600; }

#sk-callbar .cb-quote {
  flex: 1 1 0; display: flex; align-items: center; justify-content: center; gap: 7px;
  border: 1.5px solid rgba(255,255,255,.34); color: #fff;
  border-radius: var(--sk-r-full); padding: 11px 12px;
  font-family: var(--font-heading); font-weight: 700; font-size: 13.5px;
}
#sk-callbar .cb-quote:active { background: rgba(255,255,255,.12); }

@media (max-width: 767.98px) {
  #call-fab { display: none; }
  #sk-callbar { display: flex; }
  /* Clear the bar so it never sits on top of the last rows of the footer. */
  .footer-bottom { padding-bottom: 132px; }
}
@media (max-width: 359px) {
  .cb-call-txt small { display: none; }
  #sk-callbar .cb-quote { font-size: 12.5px; }
}

/* The cookie banner owns the bottom of the screen until it is dismissed —
   z-index alone would leave the bar peeking out beneath it. */
body.sk-cookie-open #sk-callbar { transform: translateY(110%); }

/* ── 47.3 Mobile nav call row, WhatsApp off ──────────────────
   No width rule needed: .sk-mob-call is already flex:1 and simply fills the
   row once it is the only child. It just needs to cope with a longer label,
   since solo it carries the number and the 24/7 promise too. */
.sk-mob-cta--solo .sk-mob-call { font-size: 13.5px; line-height: 1.35; }

@media (prefers-reduced-motion: reduce) {
  .call-fab-ring { animation: none; opacity: .35; }
  #sk-callbar { transition: none; }
}

/* ═══ 48 · CALLBACK ("we'll call you") ═══════════════════════
   Every other CTA on the site asks the visitor to place the call. This one
   asks for a name and a number and puts the effort on us, which is the only
   path left for the visitor who will not fill in the contact form and will
   not phone from a desk.

   Two hosts, one form partial: the /callback page and the site-wide modal.
   The styles below are shared; only the wrappers differ.
   ══════════════════════════════════════════════════════════ */

.cb-form .cb-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 540px) { .cb-form .cb-row { grid-template-columns: 1fr; } }
.cb-form .form-group { margin-bottom: 14px; }
.cb-opt { font-weight: 500; color: var(--sk-text-muted); }
.cb-submit { width: 100%; padding: 14px; font-size: 15px; margin-top: 4px; }
.cb-fineprint {
  font-size: 12.5px; color: var(--sk-text-muted);
  text-align: center; margin-top: 12px; line-height: 1.55;
}
.cb-errors {
  background: #FEF2F2; border: 1px solid #FECACA; border-radius: var(--sk-r-lg);
  padding: 12px 16px; margin-bottom: 16px; font-size: 14px; color: #991B1B;
}

/* ── 48.1 The /callback page ───────────────────────────────── */
.cb-hero {
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='60' viewBox='0 0 60 60'%3E%3Cpath d='M30 24v12M24 30h12' stroke='%23fff' stroke-opacity='.045' stroke-width='1.5' fill='none'/%3E%3C/svg%3E"),
    linear-gradient(135deg, var(--sk-primary) 0%, var(--sk-primary-light) 62%, #33407A 100%);
  padding: 54px 24px 92px; text-align: center; color: #fff;
}
.cb-hero .sk-kicker { color: var(--sk-teal); }
.cb-hero .sk-kicker::before { background: linear-gradient(to left,  var(--sk-teal), transparent); opacity: .6; }
.cb-hero .sk-kicker::after  { background: linear-gradient(to right, var(--sk-teal), transparent); opacity: .6; }
.cb-hero h1 {
  font-family: var(--font-heading); font-weight: 900;
  font-size: clamp(26px, 3.6vw, 36px); letter-spacing: -.02em; margin-bottom: 12px;
}
.cb-hero p { color: rgba(255,255,255,.74); font-size: 15.5px; line-height: 1.7; max-width: 54ch; margin: 0 auto; }

.cb-wrap {
  max-width: 1000px; margin: -56px auto 0; padding: 0 24px 88px;
  display: grid; grid-template-columns: 1fr 330px; gap: 26px; align-items: start;
}
@media (max-width: 880px) { .cb-wrap { grid-template-columns: 1fr; margin-top: -48px; } }
.cb-card {
  background: var(--sk-card); border: 1px solid var(--sk-border);
  border-radius: var(--sk-r-2xl); padding: 34px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9), var(--sk-e4);
}
@media (max-width: 540px) { .cb-card { padding: 24px 20px; } }

.cb-aside {
  background: var(--sk-bg-subtle); border: 1px solid var(--sk-border);
  border-radius: var(--sk-r-2xl); padding: 28px;
}
.cb-aside h2 { font-family: var(--font-heading); font-weight: 800; font-size: 17px; margin-bottom: 8px; }
.cb-aside p  { font-size: 14px; color: var(--sk-text-secondary); line-height: 1.65; margin-bottom: 16px; }
.cb-aside-call { width: 100%; justify-content: center; }
.cb-points { list-style: none; padding: 0; margin: 20px 0 0; display: grid; gap: 11px; }
.cb-points li {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: 13.5px; color: var(--sk-text-secondary); line-height: 1.5;
}
.cb-points li svg { color: var(--sk-teal-dark); flex-shrink: 0; margin-top: 1px; }

/* ── 48.2 The modal ────────────────────────────────────────── */
.cb-modal { position: fixed; inset: 0; z-index: 950; display: flex;
            align-items: center; justify-content: center; padding: 20px; }
.cb-modal[hidden] { display: none; }
.cb-modal-backdrop {
  position: absolute; inset: 0; background: rgba(15,18,36,.62);
  backdrop-filter: blur(3px); opacity: 0; transition: opacity .22s ease;
}
.cb-modal.visible .cb-modal-backdrop { opacity: 1; }
.cb-modal-panel {
  position: relative; width: 100%; max-width: 500px;
  max-height: calc(100vh - 40px); overflow-y: auto;
  background: var(--sk-card); border: 1px solid var(--sk-border);
  border-radius: var(--sk-r-2xl); padding: 32px;
  box-shadow: 0 24px 70px rgba(15,18,36,.42);
  transform: translateY(14px) scale(.985); opacity: 0;
  transition: transform .24s cubic-bezier(.34,1.4,.64,1), opacity .22s ease;
}
.cb-modal.visible .cb-modal-panel { transform: none; opacity: 1; }
@media (max-width: 540px) { .cb-modal-panel { padding: 24px 20px; } }
.cb-modal-close {
  position: absolute; top: 14px; right: 14px;
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--sk-bg-subtle); border: 1px solid var(--sk-border);
  color: var(--sk-text-secondary); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.cb-modal-close:hover { background: var(--sk-border); color: var(--sk-primary); }
.cb-modal-panel h2 {
  font-family: var(--font-heading); font-weight: 900;
  font-size: 22px; letter-spacing: -.015em; color: var(--sk-primary);
  margin-bottom: 8px; padding-right: 36px;
}
.cb-modal-sub { font-size: 14px; color: var(--sk-text-secondary); line-height: 1.6; margin-bottom: 20px; }
.cb-modal-alt { text-align: center; font-size: 13px; color: var(--sk-text-muted); margin-top: 14px; }
.cb-modal-alt a { color: var(--sk-teal-text); font-weight: 800; }

/* Stop the page behind the dialog scrolling under it. */
body.cb-modal-open { overflow: hidden; }

/* The exit-intent popup's secondary link. */
.cta-alt {
  display: block; text-align: center; margin-top: 10px;
  font-size: 13px; color: rgba(255,255,255,.72);
  text-decoration: underline; text-underline-offset: 3px;
}
.cta-alt:hover { color: #fff; }

@media (prefers-reduced-motion: reduce) {
  .cb-modal-backdrop, .cb-modal-panel { transition: none; }
}
