/* ============================================================
   BOOK MY TRIP — DESIGN SYSTEM
   ------------------------------------------------------------
   PALETTE: dusk sky.
   Built from a supplied six-swatch ramp — indigo, slate #26425A,
   soft blue #86A8CF, pale pink #E1CBD7, mauve #C38EB4 — the
   colours of the sky between sunset and night. Two of the six
   labels were obscured in the source image, so the deepest and
   darkest anchors are derived to sit at either end of the ramp.

   The one colour NOT from that ramp is red #D42021, sampled from
   logo.png. It survives as the single saturated colour on the
   site, and it appears in exactly two roles: primary calls to
   action, and the selected state of a form control. Nothing else
   is allowed to be red. In a palette this muted, one hot accent
   is what makes a button read as a button — spend it anywhere
   else and it stops meaning "press this".

   The two light surfaces are deliberately opposite temperatures:
   --canvas is a cool blue paper, --blush a warm mauve one. That
   is what makes alternating bands read as chosen rather than as
   two slightly different whites.
   ============================================================ */

/* ---------- 1. TOKENS ---------- */
:root {
  /* Dusk ramp — the site's structural colour. --navy-700 and
     --navy-400 are the supplied #26425A and #86A8CF verbatim; the
     rest interpolate outward from them. */
  --navy-900: #0E1D30;
  --navy-800: #17293D;
  --navy-700: #26425A;
  --navy-600: #3A5B78;
  --navy-400: #86A8CF;
  --navy-200: #BCCDE1;
  --navy-50:  #E7EDF6;

  /* Mauve — the warm half of the ramp. #C38EB4 and #E1CBD7 are
     supplied verbatim. Used for soft fills, warm surfaces and the
     ambient background fields, never for a control.

     --mauve-700 is derived rather than supplied. The ramp's mauve
     is a fill colour: at 2:1 against a pale sky it can't carry
     text or an icon. This is the same hue pushed down until it
     clears 4.5:1, so the warm half of the palette can appear in
     type without anything becoming unreadable. */
  --mauve-700: #8E5580;
  --mauve-500: #C38EB4;
  --mauve-400: #D3A9C5;
  --mauve-100: #E1CBD7;
  --mauve-50:  #F3E9EF;

  /* Action — from logo.png, not from the ramp. See header note.
     Deepened from #A81719: this is the small-text red (eyebrows,
     "read more", inline links), and on the densest surface on the
     site — the mauve blob of .page-hero over the mauve end of the
     sky — the old value measured 4.44:1. The brand red for fills
     is still --red-500; this is only ever used for type. */
  --red-600: #9C1518;
  --red-500: #D42021;
  --red-400: #E8494B;
  --red-100: #FBE4E4;

  /* Raw triplets, so rgba() can be written against the brand
     rather than against a hardcoded copy of it. */
  --rgb-red:   212, 32, 33;
  --rgb-navy:  23, 41, 61;
  --rgb-deep:  14, 29, 48;
  --rgb-slate: 38, 66, 90;
  --rgb-blue:  134, 168, 207;
  --rgb-mauve: 195, 142, 180;

  /* Gold used to run through the whole site — button rings, the
     scroll bar, the wizard rail, a hairline on every dark section.
     None of that is in the supplied ramp, and all of it now uses
     mauve instead. What's left is star ratings, where gold is a
     convention rather than a colour choice, so it stays — softened
     from #E8A317 to sit beside mauve without shouting. */
  /* Deepened from #E8A317. A five-star row is a rating graphic and
     has to clear 3:1; bright amber on a near-white card was 2.19:1.
     The deeper ochre also sits better against a dusk sky than a
     bright amber did. Stars are the only thing using it. */
  --gold-500: #B8801B;
  /* --gold-500 is a 3:1 colour. Star glyphs are 16px text, so they need
     4.5:1 — on the pale canvas the 500 lands at 2.98. This is the same
     ochre taken down until it clears (4.97 on canvas, 5.7 on white).
     Keep the 500 for fills and rules; text uses this. */
  --gold-700: #8A5E08;
  --green-500: #12A150;
  --green-100: #E4F6EC;
  --amber-500: #F5A623;
  --amber-100: #FEF3E0;

  /* ---------- logo kit ----------
     Sampled from the wordmark, not derived from the dusk ramp above,
     and deliberately not wired into any section class. The logo's
     own system is flat: one navy, one red, white, no gradient. The
     site's system is a dusk ramp with veils and glass. Both are
     deliberate, and the one surface where they meet is the hero
     lead form — a self-contained product sitting on the theme
     rather than a band of it.

     These are additive. Nothing that renders today changes because
     they exist. If they ever replace the ramp site-wide that's a
     re-skin, not a rename: grep --bmt- to find every use. */
  --bmt-navy:         #092043;
  --bmt-navy-hover:   #081D3C;
  --bmt-navy-pressed: #071A37;
  --bmt-navy-wash:    #EBEDF0;
  --bmt-navy-tint:    #E1E4E8;
  --bmt-red:          #D02222;
  --bmt-red-hover:    #BB1F1F;
  --bmt-red-pressed:  #AB1C1C;
  --bmt-red-tint:     #F9E4E4;
  --bmt-white:        #FFFFFF;
  --bmt-on-navy:      #FFFFFF;
  --bmt-on-red:       #FFFFFF;
  /* 5.4:1 on white, so helper text can be set in it at any size.
     A muted navy-grey, never a neutral grey. */
  --bmt-text-secondary: #5B6B7C;
  --rgb-bmt-navy: 9, 32, 67;
  --rgb-bmt-red:  208, 34, 34;

  /* One curve, three durations, for the form. Prefixed because
     --ease already exists below with a different value — redefining
     that would retime all thirteen pages. */
  --bmt-ease:     cubic-bezier(.4, 0, .2, 1);
  --bmt-dur-fast: 180ms;
  --bmt-dur-base: 240ms;
  --bmt-dur-slow: 1000ms;

  /* Neutrals — all tinted toward the dusk indigo so nothing reads
     as a cold grey next to it.

     The two line tokens are translucent slate rather than fixed
     greys. Every surface on this site is now semi-transparent over
     a moving sky, so a fixed grey hairline would read cool over the
     mauve stretches and warm over the blue ones. A slate at 13%
     picks up whatever it sits on and stays the same weight. */
  --ink:      #121C2E;
  --ink-soft: #44506A;
  /* Darkened from #6F7B95, which was set when light sections were
     opaque white. They are veils now, so the palest thing under a
     line of muted text is no longer #fff — it's the mauve end of
     the sky at #E3D3E2, where the old value measured 2.97:1. This
     clears 4.5:1 over every stop in both sky layers. */
  --ink-mute: #4F5A74;
  --line:      rgba(38, 66, 90, .14);
  --line-soft: rgba(38, 66, 90, .08);
  --surface:  #FFFFFF;
  /* Cards sit on a veil that sits on the sky, so they're near-white
     rather than white — enough transparency to pick up the colour
     underneath, not enough to lose their edge. --surface stays
     fully opaque for the things that must not show through: the
     mobile drawer, the chat panel, dropdowns, modals. */
  --card:     rgba(255, 255, 255, .88);
  --canvas:   #EAF0F8;
  --blush:    #F6EDF3;

  /* Sky — the fixed backdrop the entire site sits on, and the
     answer to "the white areas look empty". Nothing on the page is
     opaque white any more: the light sections are frosted veils and
     this is what shows through them, including in the wide margins
     on a large screen.

     Four stops taken across the supplied ramp — cool, warm, cool,
     warm — so scrolling moves through temperature rather than
     through shades of one colour. All four are held above 90%
     lightness on purpose: no text contrast anywhere on the site
     should depend on which part of the sky a paragraph lands over. */
  --sky-1: #E8EFF9;
  --sky-2: #F4E9F0;
  --sky-3: #E1E9F7;
  --sky-4: #F1E6EE;

  /* Veils — the translucent fills the section classes use. Kept as
     tokens because the page heroes need to match them exactly. */
  --veil-hi:   rgba(255, 255, 255, .66);
  --veil-cool: rgba(226, 235, 248, .62);
  --veil-warm: rgba(233, 216, 226, .62);

  /* Type */
  --font-display: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
  --font-body:    'Inter', system-ui, -apple-system, sans-serif;
  --font-script:  'Caveat', cursive;
  /* System mono, deliberately not a webfont. Used only for the
     instrument readout on the homepage — figures, country codes,
     bearings. Loading a fourth family for one section is not
     worth the request; ui-monospace resolves to SF Mono, Cascadia
     and Roboto Mono on the three platforms that matter. */
  --font-mono:    ui-monospace, SFMono-Regular, 'Cascadia Mono', Menlo, Consolas, monospace;

  /* Space */
  --sp-1: 4px;   --sp-2: 8px;   --sp-3: 12px;  --sp-4: 16px;
  --sp-5: 24px;  --sp-6: 32px;  --sp-7: 48px;  --sp-8: 64px;
  --sp-9: 96px;  --sp-10: 128px;

  /* Radius */
  --r-sm: 8px;  --r-md: 14px;  --r-lg: 22px;  --r-xl: 32px;  --r-full: 999px;

  /* Shadow */
  /* Shadows are cast in brand navy rather than neutral black —
     on a navy-tinted canvas a grey shadow reads as dirt. */
  --sh-xs: 0 1px 2px rgba(var(--rgb-navy), .06);
  --sh-sm: 0 2px 8px rgba(var(--rgb-navy), .07);
  --sh-md: 0 8px 24px rgba(var(--rgb-navy), .09);
  --sh-lg: 0 18px 48px rgba(var(--rgb-navy), .14);
  --sh-xl: 0 32px 80px rgba(var(--rgb-navy), .18);
  --sh-red: 0 10px 30px rgba(var(--rgb-red), .3);

  /* Motion */
  --ease: cubic-bezier(.22,1,.36,1);
  --ease-io: cubic-bezier(.65,0,.35,1);
  --t-fast: .18s;  --t-mid: .32s;  --t-slow: .6s;

  --nav-h: 76px;
  --maxw: 1240px;
}

/* ---------- 2. RESET ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 20px);
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font-body);
  /* Transparent, because the sky lives on ::before below. Painting
     it here instead would put it behind everything including the
     fixed layer, and a scrolling gradient this large is the one
     thing on the page worth pinning. */
  background: transparent;
  color: var(--ink);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* ---------- 2b. THE SKY ----------
   One fixed layer behind the entire site. Every light section above
   it is a translucent veil, so this is what's actually showing
   through them — which is why there is no longer any part of the
   site that is plain white.

   It has to be fixed rather than scrolling. The dusk ramp only
   reads as a sky if the viewport feels like a window onto something
   larger; a gradient that scrolls with the page reads as a very
   tall striped background instead. Fixed also means it paints once
   and never repaints on scroll.

   Negative z-index puts it below every in-flow background on the
   page while staying above html's own — the one position that
   works without touching a single section's stacking. */
html { background: var(--sky-1); }

body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background:
    radial-gradient(120vw 90vh at 12% -10%,  var(--sky-2), transparent 62%),
    radial-gradient(110vw 85vh at 92% 22%,   var(--sky-3), transparent 60%),
    radial-gradient(120vw 95vh at 8%  85%,   var(--sky-4), transparent 64%),
    radial-gradient(100vw 80vh at 98% 108%,  var(--sky-2), transparent 58%),
    linear-gradient(168deg, var(--sky-1) 0%, var(--sky-3) 48%, var(--sky-2) 100%);
  /* The whole sky breathes. 90s and ±3% of scale, which is slow
     enough that you never catch it moving and only notice that the
     page isn't static. Transform-only, so it composites. */
  animation: sky-drift 90s var(--ease-io) infinite alternate;
  will-change: transform;
}
@keyframes sky-drift {
  from { transform: scale(1)    translate3d(0, 0, 0); }
  to   { transform: scale(1.06) translate3d(-1.5%, 1%, 0); }
}
@media (prefers-reduced-motion: reduce) {
  body::before { animation: none; }
}
/* The sky is a fixed layer, and a fixed layer is re-painted on
   every sheet when a page is printed. The visa result page is
   meant to be printable, so the sky comes off entirely there. */
@media print {
  body::before { display: none; }
  html, body { background: #fff; }
}

img, svg, video { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }

:focus-visible {
  outline: 3px solid var(--red-400);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Off-screen until tabbed to, then the first stop on every page. Must clear
   the sticky header (z-index 900) or it slides up behind it. Uses :focus, not
   :focus-visible — a skip link has to appear for every route to focus. */
.skip-link {
  position: fixed;
  top: var(--sp-3);
  left: var(--sp-3);
  z-index: 1000;
  padding: .7rem 1.1rem;
  background: var(--navy-900);
  color: #fff;
  font-weight: 700;
  font-size: .95rem;
  border-radius: var(--r-sm);
  box-shadow: var(--sh-md);
  transform: translateY(calc(-100% - var(--sp-6)));
  transition: transform var(--t-fast) var(--ease);
}
.skip-link:focus { transform: translateY(0); }

/* ---------- 3. TYPOGRAPHY ---------- */
h1, h2, h3, h4, h5 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.025em;
  color: var(--navy-900);
}

h1 { font-size: clamp(2.4rem, 5.4vw, 4.4rem); }
h2 { font-size: clamp(1.9rem, 3.8vw, 3rem); }
h3 { font-size: clamp(1.3rem, 2.2vw, 1.75rem); }
h4 { font-size: 1.15rem; letter-spacing: -0.015em; }

/* Level-independent type scale. A card title belongs at h3 in the document
   outline — it sits under an h2 section head — but it should not be drawn at
   h3 size. These let the heading level follow the structure while the size
   follows the design. A class (0,1,0) always beats a bare element selector
   (0,0,1), so source order doesn't matter here. */
.h-md { font-size: clamp(1.3rem, 2.2vw, 1.75rem); }
.h-sm { font-size: 1.15rem; letter-spacing: -0.015em; }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  /* --red-600 for the text, --red-500 for the dash. The eyebrow is
     12.5px uppercase, the smallest type on the page, and it sits on
     the warm veil where --red-500 measures 3.4:1. The dash beside
     it is decoration and can stay the brighter brand red. */
  color: var(--red-600);
  margin-bottom: var(--sp-3);
}
.eyebrow::before {
  content: "";
  width: 26px; height: 2px;
  background: var(--red-500);
  border-radius: 2px;
}

.accent-script {
  font-family: var(--font-script);
  font-weight: 600;
  color: var(--red-500);
  font-size: 1.12em;
  letter-spacing: 0;
}

.lead {
  font-size: clamp(1.02rem, 1.5vw, 1.2rem);
  color: var(--ink-soft);
  line-height: 1.75;
  max-width: 62ch;
}

.muted { color: var(--ink-mute); }

/* ---------- 4. LAYOUT ---------- */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--sp-5);
}

section { position: relative; }
.sec      { padding-block: clamp(56px, 8vw, 108px); }
.sec-tight{ padding-block: clamp(40px, 5vw, 68px); }

.sec-head { max-width: 720px; margin-bottom: var(--sp-7); }
.sec-head.center { margin-inline: auto; text-align: center; }
.sec-head p { margin-top: var(--sp-4); }

/* Four surface levels. Three of them are now VEILS, not fills —
   translucent sheets laid over the fixed sky, so each one takes its
   colour partly from whatever the sky is doing behind it. That is
   the whole reason there is no longer a dead white band anywhere on
   the site: .bg-white is 66% white over a mauve-and-blue gradient,
   not 100% white over nothing.

   They still alternate in temperature as well as in value, which is
   what the dusk palette is for:
     .bg-white  the brightest veil — cards still lift off it
     .bg-canvas the cool one, blue-leaning
     .bg-tint   the warm one, mauve-leaning
     .bg-navy   the deep end of the ramp, indigo into slate. The one
                opaque surface, because the rhythm needs somewhere
                the sky stops.

   Each veil also carries a hairline of the next colour along its
   top edge, so two adjacent light bands separate without a border
   drawn across the page. */
.bg-white  {
  background:
    linear-gradient(180deg, rgba(255,255,255,.86) 0%, var(--veil-hi) 22%, var(--veil-hi) 100%);
}
.bg-canvas {
  background:
    linear-gradient(180deg, rgba(var(--rgb-blue), .16) 0%, transparent 30%),
    var(--veil-cool);
}
/* Not a flat fill. The supplied palette is a gradient sky, and its
   dark end has two distinct colours in it (indigo and slate) — a
   single flat --navy-900 throws away the half of the ramp that
   makes it look like dusk rather than like navy. */
.bg-navy   {
  background: linear-gradient(165deg, var(--navy-900) 0%, var(--navy-700) 62%, #1B3348 100%);
  color: #fff;
}
.bg-navy h1, .bg-navy h2, .bg-navy h3, .bg-navy h4 { color: #fff; }
.bg-navy .lead { color: rgba(255,255,255,.78); }
/* The brand red is a light-surface colour. On the navy gradient it
   drops to 2:1 — below the 3:1 a display-size accent needs — so the
   script accent hands over to mauve, which is the other end of the
   same ramp and reads as the same gesture. Several pages already do
   this inline; this makes it the rule rather than the exception. */
.bg-navy .accent-script { color: var(--mauve-400); }
/* The warm band — mauve over the sky rather than instead of it.
   The only large surface on the site that leans pink, so it reads
   as a deliberate change of light rather than another off-white. */
.bg-tint   {
  background:
    linear-gradient(180deg, rgba(var(--rgb-mauve), .22) 0%, transparent 34%),
    var(--veil-warm);
}

/* ---------- 4b. ATMOSPHERE ON LIGHT SECTIONS ----------
   Large, heavily blurred colour fields drifting slowly behind the
   content, so the light surfaces read as sky rather than as paper.

   With the dusk palette these do more work than they used to. The
   supplied ramp IS a gradient — that's the whole character of it —
   so a flat #FFFFFF section is the one place the palette isn't
   present. These put it back: mauve and blue, the two mid-ramp
   colours, at 13-19% opacity. Enough to tint the surface without
   becoming a visible shape or costing contrast.

   Red is deliberately absent here. It's the action colour, and a
   red wash behind the content would compete with the buttons for
   the only saturated note on the page.

   Cost control, since this paints on nearly every section:
     · transform-only animations, so they composite off the main
       thread and never trigger layout
     · 26–35s cycles, so each frame's delta is tiny
     · paused when the section is off-screen (polish.js), the same
       IntersectionObserver approach as the buttons
     · dropped entirely under reduced-motion and on small screens,
       where a 70px blur is the most expensive thing on the page  */
.bg-white,
.bg-canvas,
.bg-tint {
  position: relative;
  isolation: isolate;
  /* The blobs deliberately overhang the section edges — that's what
     stops them reading as circles sitting in a box. Overhang has to
     be clipped, though: a section paints as one unit, and sibling
     sections paint in DOM order, so a blob poking out of the top
     would land on top of the *previous* section's text. Same
     treatment .bg-navy already uses for its aurora. */
  overflow: hidden;
}
/* Content sits above the blobs. Most sections wrap their content
   in .wrap; the direct-child selector also catches the few that
   use a different container. */
.bg-white  > *,
.bg-canvas > *,
.bg-tint   > * { position: relative; z-index: 1; }

.bg-white::before,  .bg-white::after,
.bg-canvas::before, .bg-canvas::after,
.bg-tint::before {
  content: "";
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  z-index: 0;
}

/* On white — mauve top-right, blue bottom-left. The two ends of
   the ramp's midsection, so a white band still reads as dusk. */
.bg-white::before {
  width: min(54vw, 620px); aspect-ratio: 1;
  top: -14%; right: -10%;
  background: radial-gradient(circle, rgba(var(--rgb-mauve), .7), transparent 64%);
  filter: blur(70px);
  opacity: .24;
  animation: mesh-a 26s var(--ease-io) infinite alternate;
}
.bg-white::after {
  width: min(44vw, 520px); aspect-ratio: 1;
  bottom: -16%; left: -10%;
  background: radial-gradient(circle, rgba(var(--rgb-blue), .75), transparent 60%);
  filter: blur(64px);
  opacity: .22;
  animation: mesh-b 32s var(--ease-io) infinite alternate;
}

/* On canvas — the cool band, so it goes deeper: slate and blue.
   Same two-blob structure, opposite temperature to .bg-tint. */
.bg-canvas::before {
  width: min(50vw, 580px); aspect-ratio: 1;
  top: -12%; left: -12%;
  background: radial-gradient(circle, rgba(var(--rgb-slate), .5), transparent 66%);
  filter: blur(66px);
  opacity: .2;
  animation: mesh-c 29s var(--ease-io) infinite alternate;
}
.bg-canvas::after {
  width: min(40vw, 470px); aspect-ratio: 1;
  bottom: -14%; right: -8%;
  background: radial-gradient(circle, rgba(var(--rgb-blue), .8), transparent 62%);
  filter: blur(62px);
  opacity: .22;
  animation: mesh-d 35s var(--ease-io) infinite alternate;
}

/* .bg-tint already carries a mauve gradient, so one blob is enough
   — and it's blue, to keep a trace of the cool end in the warm
   band rather than doubling the pink already there. */
.bg-tint::before {
  width: min(38vw, 450px); aspect-ratio: 1;
  top: 14%; right: -10%;
  background: radial-gradient(circle, rgba(var(--rgb-blue), .6), transparent 58%);
  filter: blur(58px);
  opacity: .26;
  animation: mesh-e 24s var(--ease-io) infinite alternate;
}

/* Paused by polish.js when the section scrolls out of view. */
.atmos-idle::before,
.atmos-idle::after { animation-play-state: paused; }

@keyframes mesh-a {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-8vw, 6vh, 0) scale(1.16); }
}
@keyframes mesh-b {
  from { transform: translate3d(0, 0, 0) scale(1.08); }
  to   { transform: translate3d(7vw, -5vh, 0) scale(1); }
}
@keyframes mesh-c {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(6vw, -4vh, 0) scale(1.14); }
}
@keyframes mesh-d {
  from { transform: translate3d(0, 0, 0) scale(1.1); }
  to   { transform: translate3d(-5vw, 5vh, 0) scale(1); }
}
@keyframes mesh-e {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-6vw, -5vh, 0) scale(1.12); }
}

/* Small screens: a 60–70px blur over half the viewport is the
   single most expensive paint on the page and the effect is
   largely lost at that size anyway. The flat brand colour is the
   better trade. */
@media (max-width: 700px) {
  .bg-white::before,  .bg-white::after,
  .bg-canvas::before, .bg-canvas::after,
  .bg-tint::before { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .bg-white::before,  .bg-white::after,
  .bg-canvas::before, .bg-canvas::after,
  .bg-tint::before { animation: none; }
}

.grid { display: grid; gap: var(--sp-5); }
.g2 { grid-template-columns: repeat(2, 1fr); }
.g3 { grid-template-columns: repeat(3, 1fr); }
.g4 { grid-template-columns: repeat(4, 1fr); }

@media (max-width: 980px) {
  .g3, .g4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .g2, .g3, .g4 { grid-template-columns: 1fr; }
}

/* ---------- 5. BUTTONS ---------- */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 15px 30px;
  border-radius: var(--r-full);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: .96rem;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: transform var(--t-mid) var(--ease),
              box-shadow var(--t-mid) var(--ease),
              background var(--t-fast) ease,
              color var(--t-fast) ease;
  white-space: nowrap;
  overflow: hidden;
  isolation: isolate;
}
.btn:active { transform: translateY(1px) scale(.99); }

.btn-primary {
  background: var(--red-500);
  color: #fff;
  box-shadow: var(--sh-red);
}
.btn-primary:hover {
  background: var(--red-600);
  transform: translateY(-2px);
  box-shadow: 0 16px 40px rgba(var(--rgb-red), .38);
}

/* A gradient rather than a flat fill, so the secondary button
   carries the same dusk ramp the dark sections do. */
.btn-navy {
  background: linear-gradient(140deg, var(--navy-800), var(--navy-700) 70%, var(--navy-600));
  color: #fff;
  box-shadow: var(--sh-md);
}
.btn-navy:hover {
  background: linear-gradient(140deg, var(--navy-900), var(--navy-700));
  transform: translateY(-2px);
  box-shadow: var(--sh-lg);
}

.btn-ghost {
  background: rgba(255, 255, 255, .78);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--navy-800);
  border: 1.5px solid var(--line);
}
.btn-ghost:hover { border-color: var(--navy-700); transform: translateY(-2px); box-shadow: var(--sh-md); }

.btn-outline-light {
  background: rgba(255,255,255,.07);
  color: #fff;
  border: 1.5px solid rgba(255,255,255,.3);
  backdrop-filter: blur(8px);
}
.btn-outline-light:hover { background: rgba(255,255,255,.16); border-color: rgba(255,255,255,.6); transform: translateY(-2px); }

/* #128C7E, WhatsApp's own dark brand green, rather than the #25D366
   of the logo tile. White on #25D366 is 1.98:1 — the bright green
   is meant to carry a white glyph, not white text. #075E54 is the
   darker of the two brand greens and clears 4.5:1; the bright one
   stays on the logo tiles, where it belongs. */
.btn-wa { background: #075E54; color: #fff; box-shadow: 0 10px 28px rgba(7,94,84,.32); }
.btn-wa:hover { background: #0A7A6C; transform: translateY(-2px); box-shadow: 0 16px 38px rgba(7,94,84,.42); }

.btn-lg { padding: 18px 38px; font-size: 1.03rem; }
.btn-sm { padding: 10px 20px; font-size: .87rem; }
.btn-block { display: flex; width: 100%; }

/* Live pulse dot */
.pulse-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: #4ADE80;
  box-shadow: 0 0 0 0 rgba(74,222,128,.7);
  animation: pulse 2s infinite;
  flex-shrink: 0;
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(74,222,128,.7); }
  70%  { box-shadow: 0 0 0 9px rgba(74,222,128,0); }
  100% { box-shadow: 0 0 0 0 rgba(74,222,128,0); }
}

/* ---------- 6. CARDS ---------- */
/* Frosted, not solid. backdrop-filter blurs the sky and whatever
   atmosphere blob is drifting behind the card, so each card carries
   a soft trace of the colour it happens to be sitting over — and
   that trace changes as the blob moves. It's the cheapest way to
   make 40 identical cards stop looking identical.

   saturate() is doing as much work as the blur: white at 88% over a
   pale sky washes the colour out, and pushing saturation back up is
   what keeps the mauve reading as mauve rather than as grey. */
.card {
  background: var(--card);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
  transition: transform var(--t-mid) var(--ease),
              box-shadow var(--t-mid) var(--ease),
              border-color var(--t-mid) var(--ease),
              background var(--t-mid) var(--ease);
}
.card-hover:hover {
  transform: translateY(-6px);
  box-shadow: var(--sh-lg);
  border-color: rgba(var(--rgb-mauve), .5);
  background: rgba(255, 255, 255, .94);
}

.icon-badge {
  width: 54px; height: 54px;
  display: grid;
  place-items: center;
  border-radius: var(--r-md);
  background: linear-gradient(145deg, var(--navy-50), var(--mauve-50));
  color: var(--navy-700);
  margin-bottom: var(--sp-4);
  transition: background var(--t-mid) var(--ease), color var(--t-mid) var(--ease),
              transform var(--t-mid) var(--ease);
}
/* Was red. Red is the action colour and a card is not an action —
   the badge now lights up in the ramp's own two mid colours, which
   reads as the card waking up rather than as a button. */
.card-hover:hover .icon-badge {
  background: linear-gradient(145deg, var(--navy-700), var(--mauve-500));
  color: #fff;
  transform: scale(1.08) rotate(-4deg);
}
.icon-badge svg { width: 26px; height: 26px; }

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: var(--r-full);
  font-size: .78rem;
  font-weight: 700;
  background: linear-gradient(135deg, var(--navy-50), var(--mauve-50));
  color: var(--navy-700);
}
.pill-red   { background: var(--red-100);   color: var(--red-600); }
.pill-green { background: var(--green-100); color: #0B7A3B; }
/* #8A5800 rather than #A66A00: amber on its own tint is the
   tightest pair in the pill set, and it was sitting at 4.08:1. */
.pill-amber { background: var(--amber-100); color: #8A5800; }
.pill-light { background: rgba(255,255,255,.14); color: #fff; backdrop-filter: blur(6px); }

/* Callout box — used on services, packages, about, country and eligibility
   pages, so it lives here rather than in a single page's stylesheet. */
.notice {
  background: linear-gradient(135deg, rgba(var(--rgb-blue), .16), rgba(var(--rgb-mauve), .14));
  border-left: 4px solid var(--navy-600);
  border-radius: var(--r-sm);
  padding: var(--sp-4) var(--sp-5);
  font-size: .89rem;
  line-height: 1.7;
  color: var(--ink-soft);
}
.notice strong { color: var(--navy-900); }

/* ---------- 7. MARQUEE ---------- */
.marquee {
  display: flex;
  overflow: hidden;
  user-select: none;
  gap: var(--gap, 40px);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.marquee__track {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--gap, 40px);
  min-width: 100%;
  animation: scroll-x var(--dur, 34s) linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee--rev .marquee__track { animation-direction: reverse; }

@keyframes scroll-x {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-100% - var(--gap, 40px))); }
}

/* ---------- 8. SCROLL REVEAL ---------- */
/* The value of data-reveal picks the entrance. Empty (`data-reveal`
   with no value) keeps the original fade-up, so none of the ~40
   existing call sites had to change — the variants are opt-in and
   used where the content has a reason to move that way.

   Transform + opacity only, both compositor properties, so a whole
   grid revealing at once doesn't cost a layout pass. */
[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity .75s var(--ease), transform .75s var(--ease);
  will-change: opacity, transform;
}
[data-reveal].in { opacity: 1; transform: none; }

/* --- variants --- */
/* Rises further and slower. For section headings, where the extra
   travel gives the eye something to follow into the section. */
[data-reveal="rise"]  { transform: translateY(46px); transition-duration: .9s; }

/* Grows into place. For counters and media — a number that settles
   from slightly small reads as being placed, where a number sliding
   in reads as a slide deck. */
[data-reveal="scale"] { transform: scale(.94) translateY(18px); transition-duration: .85s; }

/* Enters from the side it sits on, so a two-column split converges
   on its own centre line instead of both halves drifting up. */
[data-reveal="left"]  { transform: translateX(-40px); }
[data-reveal="right"] { transform: translateX(40px); }

[data-reveal][data-delay="1"] { transition-delay: .09s; }
[data-reveal][data-delay="2"] { transition-delay: .18s; }
[data-reveal][data-delay="3"] { transition-delay: .27s; }
[data-reveal][data-delay="4"] { transition-delay: .36s; }
[data-reveal][data-delay="5"] { transition-delay: .45s; }
[data-reveal][data-delay="6"] { transition-delay: .54s; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  /* Every variant has to be neutralised explicitly — the global
     rule above only shortens durations, and a 0.01ms blur or clip
     still flashes once at full strength. */
  [data-reveal] {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

/* ---------- 9. FORMS ---------- */
.field { margin-bottom: var(--sp-4); }
.field label {
  display: block;
  font-size: .85rem;
  font-weight: 700;
  color: var(--navy-800);
  margin-bottom: 7px;
}
.field .hint { font-size: .78rem; color: var(--ink-mute); font-weight: 500; margin-top: 5px; }

.input, .select, .textarea {
  width: 100%;
  padding: 14px 16px;
  border: 1.5px solid var(--line);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, .74);
  font-size: .96rem;
  transition: border-color var(--t-fast) ease, box-shadow var(--t-fast) ease,
              background var(--t-fast) ease;
}
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--navy-600);
  background: #fff;
  box-shadow: 0 0 0 4px rgba(var(--rgb-slate), .14);
}
.input::placeholder, .textarea::placeholder { color: var(--ink-mute); }
.textarea { resize: vertical; min-height: 120px; }

.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23767F99' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  padding-right: 42px;
  cursor: pointer;
}

/* ---------- 10. UTILITIES ---------- */
.center { text-align: center; }
.flex { display: flex; }
.items-center { align-items: center; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.gap-2 { gap: var(--sp-2); } .gap-3 { gap: var(--sp-3); }
.gap-4 { gap: var(--sp-4); } .gap-5 { gap: var(--sp-5); }
.wrap-flex { flex-wrap: wrap; }
.mt-2 { margin-top: var(--sp-2); } .mt-3 { margin-top: var(--sp-3); }
.mt-4 { margin-top: var(--sp-4); } .mt-5 { margin-top: var(--sp-5); }
.mt-6 { margin-top: var(--sp-6); } .mt-7 { margin-top: var(--sp-7); }
.mb-2 { margin-bottom: var(--sp-2); } .mb-3 { margin-bottom: var(--sp-3); }
.mb-4 { margin-bottom: var(--sp-4); } .mb-5 { margin-bottom: var(--sp-5); }
.mb-6 { margin-bottom: var(--sp-6); } .mb-7 { margin-bottom: var(--sp-7); }
.mt-8 { margin-top: var(--sp-8); }    .mt-9 { margin-top: var(--sp-9); }
.mb-8 { margin-bottom: var(--sp-8); } .mb-9 { margin-bottom: var(--sp-9); }
.hide-mobile { display: initial; }
.show-mobile { display: none; }
@media (max-width: 820px) {
  .hide-mobile { display: none !important; }
  .show-mobile { display: initial; }
}
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
