
/* Sonsoo theme laye — SINGLE SOURCE. tools/uild-theme.py writes this file into every shipped
   page inside the "GENERATED theme" region; the copy in a page is never edited by hand.

   The dark palette is the base and lives in each page's own :root. This file adds three things:
     1. the light palette, keyed on data-theme="light" on <html> (set by _shared/theme.js);
     2. overrides for the handful of surfaces whose dark colour is a literal, not a token —
        nav, mobile bar, chat fab, hero-art veil, primary button, ghost tints;
     3. logo tiles and service icon tiles — plated marks and tokenised icon containers.
   Selectors are prefixed with :root so they outrank same-specificity rules in the generated
   <style> blocks that sit later in the body (banks-context, credibility-band, …). */

:root { color-scheme: dark; }

/* ---- surface system (2026-09-04) ---------------------------------------------------------
   Every page's own :root declares the dark palette as --bg / --bg-2 / --surface / --surface-2;
   this region sits at the end of <head>, so the values below win on source order and are the
   ones the site actually paints. The dark base is re-declared here as a WARM charcoal ramp
   (the page literals are a blue-black) at the same luminance steps, so nothing changes
   hierarchy, only cast. The semantic roles map onto those four tokens plus the additions
   below; components consume the roles, never a raw white or black:
     surface-base     the page          --bg
     surface-section  a flat band       --bg-2
     surface-paper    document panel    --surface      (light: warm paper, never browser white)
     surface-tinted   stone card on a pale or image-backed section   --surface-tinted
     surface-raised   paper with lift   --surface-raised (+ --shadow)
     surface-dark     focused tool      --surface-dark, paired with the on-dark text tokens
   A surface variant always sets its own foreground pairing; text is never inherited across a
   surface boundary. */
:root {
  --bg:#0C0D0C; --bg-2:#121412; --surface:#1A1C1A; --surface-2:#212421;
  --surface-base:var(--bg); --surface-section:var(--bg-2); --surface-paper:var(--surface);
  --surface-tinted:#232624; --surface-raised:#20231F;
  --surface-specimen-light:#ECE7DB; --surface-specimen-dark:#0D1114; --surface-card-footer:#1A1C1A; --border-specimen:rgba(255,255,255,.18); --construction-line:rgba(255,255,255,.55);   /* brand specimen cards (2026-09-08): the light field stays light in both themes */
  --surface-dark:#1F2321; --surface-dark-2:#262B28;
  --text-on-dark:#F4F5F2; --muted-on-dark:#B3ADA3; --faint-on-dark:#8A857B;
  --line-on-dark:rgba(255,255,255,.12); --line-soft-on-dark:rgba(255,255,255,.07); --line-strong-on-dark:rgba(255,255,255,.26);
  --text-on-light:#14181B; --muted-on-light:#5E5952; --faint-on-light:#8A857D;
  --line-on-light:rgba(20,24,27,.10); --line-soft-on-light:rgba(20,24,27,.06); --line-strong-on-light:rgba(20,24,27,.18);
  --control-surface:rgba(255,255,255,.03); --control-surface-dark:rgba(255,255,255,.04);
  --focus-ring:var(--mineral);
  --timeline-connector:rgba(255,255,255,.34);   /* connector lines only (2026-09-07) — light value in the light block */
  --marker-win-bg:rgba(138,163,148,.22); --marker-win-ink:#DCE7DF; --marker-miss-bg:rgba(184,148,116,.22); --marker-miss-ink:#EADFCF;   /* comparison-panel markers (2026-09-07) */
  /* interactive action cards (Connect cards, contact-method cards): base → subtle hover → clear focus → restrained press */
  --surface-action:var(--surface); --surface-action-hover:var(--surface-raised); --surface-action-active:var(--surface);
  --border-action:var(--line); --border-action-hover:var(--line-strong);
  --shadow-action-hover:0 6px 18px rgba(0,0,0,.22);
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg:#F5F2EA; --bg-2:#EEEAE0; --surface:#FCFAF5; --surface-2:#F8F5EE;
  --surface-tinted:#EEE9DE; --surface-raised:#FDFCF8;
  --text:#14181B; --muted:#5E5952; --faint:#8A857D;
  --line:rgba(20,24,27,.10); --line-soft:rgba(20,24,27,.06); --line-strong:rgba(20,24,27,.18);
  --surface-specimen-light:#ECE7DB; --surface-specimen-dark:#0D1114; --surface-card-footer:transparent; --border-specimen:rgba(20,24,27,.22); --construction-line:rgba(20,24,27,.55);   /* brand specimen cards (2026-09-08): page · card edge · preview field · caption strip */
  --timeline-connector:rgba(20,24,27,.36);
  --marker-win-bg:rgba(78,97,101,.16); --marker-win-ink:#1F2C2E; --marker-miss-bg:rgba(122,106,72,.16); --marker-miss-ink:#14181B;
  --mineral:#7A6A48; --mineral-hi:#8E7A54; --mineral-30:rgba(122,106,72,.3); --sand:#7A6A48;
  --sage:#4E6165; --brass:#7A5F2E; --text-mute:#5E5952;
  --glass:rgba(252,250,245,.78);
  --control-surface:rgba(20,24,27,.03);
  --shadow-action-hover:0 6px 18px rgba(20,24,27,.07);
  --shadow:0 20px 60px rgba(20,24,27,.10), inset 0 1px 0 rgba(255,255,255,.9);
  /* action cards paint on the tinted cream (2026-09-10, owner brief) — the white --surface
     read as pure white on the ivory ground; tinted sits in the same warm family. Hover lifts to
     --surface-2, the footer referral card's hover tone (owner 2026-09-22: "the referral one on mouse
     over is the right tone, not white") — --surface-raised (#FDFCF8) read as a white card. */
  --surface-action:var(--surface-tinted); --surface-action-hover:var(--surface-2); --surface-action-active:var(--surface-tinted);
}

/* ---- literal surfaces ------------------------------------------------------------------- */
:root[data-theme="light"] .nav { background: rgba(245,242,234,.86); }
:root[data-theme="light"] .mbar { background: rgba(245,242,234,.94); }
:root[data-theme="light"] .chat-fab { background: #25D366; border-color: transparent; }   /* 2026-09-08: the launcher is the solid WhatsApp-green conversion control in BOTH themes — the light paper repaint made it a washed-out white circle with an invisible white glyph */
:root[data-theme="light"] .hero-art::after,
:root[data-theme="light"] .banks-hero .hero-art::after {
  background: linear-gradient(90deg, var(--bg) 4%, rgba(245,242,234,.88) 30%, rgba(245,242,234,.20) 58%, rgba(245,242,234,.30) 100%),
              linear-gradient(0deg, var(--bg) 2%, transparent 40%);
}
:root[data-theme="light"] .frame {
  background: linear-gradient(135deg, rgba(20,24,27,.03), rgba(20,24,27,0) 60%), var(--bg-2);
}

/* ---- buttons and tints ------------------------------------------------------------------ */
:root[data-theme="light"] .btn-primary { background:#14181B; color:#F5F2EA; }
:root[data-theme="light"] .btn-primary:hover { background:#2A2F34; }
:root[data-theme="light"] .vy-ask button { color:#F5F2EA; }
:root[data-theme="light"] .newsletter-email { color: var(--text); }
:root[data-theme="light"] .newsletter-submit:hover { color:#F5F2EA; }
:root[data-theme="light"] .btn-ghost { background: rgba(20,24,27,.03); }
:root[data-theme="light"] .btn-ghost:hover { background: rgba(20,24,27,.06); }
:root[data-theme="light"] .cov-mark { border-color: rgba(20,24,27,.12); background: rgba(20,24,27,.03); }
:root[data-theme="light"] .fzshelf-logo { background: rgba(20,24,27,.04); }

/* ---- no-media heroes (hero-media.json mode wizardNoMedia | noMedia) ------------------------
   The generator removes the placeholder art layer; the section paints the base surface with a
   quiet top-to-bottom tonal gradient so the hero reads as intentional, in both themes. */
:root [data-hero-mode="wizardNoMedia"], :root [data-hero-mode="noMedia"] { background: linear-gradient(180deg, var(--bg-2) 0%, var(--bg) 68%); }

/* ---- theme switch (footer .themebox: system · light · dark) ------------------------------ */
:root .themebox button.is-current { color: var(--text); background: rgba(255,255,255,.10); }
:root[data-theme="light"] .themebox { background: rgba(20,24,27,.03); }
:root[data-theme="light"] .themebox button:hover { background: rgba(20,24,27,.06); }
:root[data-theme="light"] .themebox button.is-current { background: rgba(20,24,27,.08); }

/* ---- free-zone shelf marks: the silhouette filters that lived here were retired 2026-09-07 (owner brief) — third-party marks
   render as supplied or fall back to a monogram; the slot rules are tools/_zone_logo.py, written by the shelf generators. */

/* ---- timeline connector (2026-09-07, owner brief) ----------------------------------------------
   The emirate week-by-week ribbon drew its connector with a literal dark-theme alpha that vanished on the light base.
   One semantic token, both themes (light rgba(20,24,27,.22), dark rgba(255,255,255,.20)); one solid 1px axis through
   the node centres. Only connector lines may use it. */
   /* through the node centres: ribbon padding 44 + node top 44 + node radius 10; first to last column centre (2026-09-07) */

/* comparison-panel marker tokens (--marker-*) are consumed by _shared/emirate-fit-comparison.css (2026-09-07) */

/* ---- logo tiles (2026-09-05) ---------------------------------------------------------------
   Bank marks render as supplied: no filter, invert, recolour or crop. tools/build-bank-logos.py
   and tools/build-banks-context.py write data-logo-tone="light|dark|colour" on each tile — the
   asset's measured luminance class from banks.json — and the tile hosts the mark on a contained
   plate only where the theme would otherwise swallow it: light marks on an ink plate in the
   light theme, dark and colour marks on a paper plate in the dark theme. Wordmark fallbacks
   carry no tone and take the tile's own foreground. */
:root {
  --logo-plate-paper:#F5F2EA; --logo-plate-paper-line:rgba(20,24,27,.14);
  --logo-plate-ink:var(--surface-dark); --logo-plate-ink-line:var(--line-on-dark);
}
:root .cov-mark .cov-logo, :root .bg2-mark-logo, :root .bc-logo-img { filter: none; opacity: 1; }
:root .bg2-mark[data-logo-tone], :root .bc-logo[data-logo-tone] { padding: 4px 8px; border: 1px solid transparent; border-radius: 6px; }
:root .bc-logo[data-logo-tone] { height: auto; }
:root .bc-logo[data-logo-tone] .bc-logo-img { height: 26px; }
/* 2026-09-21 (owner: white plates behind bank logos look wrong on dark cards): no paper plate by tone. A mark swaps to its
   official reversed file on the dark theme (banks.json logo.onDark → data-face) or ships as supplied when it reads there
   (logo.darkLegible); only a record BankIdentity names as having neither keeps the plate (data-logo-dark="plate"). */
:root[data-theme="light"] [data-face="dark"], :root:not([data-theme="light"]) [data-face="light"] { display: none; }
:root:not([data-theme="light"]) :is(.cov-mark, .bg2-mark, .bc-logo)[data-logo-dark="plate"] { background: var(--logo-plate-paper); border-color: var(--logo-plate-paper-line); }
:root:not([data-theme="light"]) .cov-mark[data-logo-dark="plate"] span { color: var(--text-on-light); }
:root[data-theme="light"] :is(.cov-mark, .bg2-mark, .bc-logo)[data-logo-tone="light"] { background: var(--logo-plate-ink); border-color: var(--logo-plate-ink-line); }
:root[data-theme="light"] .cov-mark[data-logo-tone="light"] span { color: var(--text-on-dark); }

/* ---- service icon tiles (.icon-tile, 2026-09-05) --------------------------------------------
   The leading icon of a service-option row sits on an elevated tile from the same surface family
   as its card: warm stone on the light theme, lifted charcoal on dark — never a black square.
   Icons are one family: 24-unit viewBox, 1.6 stroke, round caps and joins, currentColor, and
   decorative (aria-hidden) because the row title carries the meaning. Hover lifts the tile one
   tonal step; focus belongs to the row, not the tile. */
:root { --surface-icon: var(--surface-dark-2); --surface-icon-hover: #2C312E; --border-icon: var(--line-on-dark); --icon-service: #E6E0D2; }
:root[data-theme="light"] { --surface-icon: #EEE9DE; --surface-icon-hover: #E4DED0; --border-icon: rgba(20,24,27,.10); --icon-service: #3A3631; }
:root .icon-tile { display: grid; place-items: center; background: var(--surface-icon); border: 1px solid var(--border-icon); color: var(--icon-service); transition: background var(--ease, .18s ease), border-color var(--ease, .18s ease); }
:root .icon-tile > svg { width: 40px; height: 40px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
:root a:hover .icon-tile, :root a:focus-visible .icon-tile { background: var(--surface-icon-hover); border-color: var(--line-strong); }
@media (hover: none) { :root a:hover .icon-tile { background: var(--surface-icon); border-color: var(--border-icon); } }
@media (prefers-reduced-motion: reduce) { :root .icon-tile { transition: none; } }

/* ---- portrait avatars in light ------------------------------------------------------------
   The team medallions (.ncta-medallion) and founder avatars (.founders-avatar) carry studio
   portraits shot on black. On light they read as hard black discs, so the wrapper becomes a warm
   matte plate and the portrait is lifted slightly. Image-only treatment; dark is untouched. */
:root .ncta-medallion-photo { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%; }   /* the portrait fills its disc — was an inline style on every copy */
:root[data-theme="light"] .ncta-medallion-disc,
:root[data-theme="light"] .founders-avatar { background: #e9e4d9; border: 1px solid rgba(37,34,29,.10); box-shadow: 0 3px 10px rgba(37,34,29,.08); }
:root[data-theme="light"] .ncta-medallion-photo,
:root[data-theme="light"] .founders-avatar img { filter: saturate(0.78) contrast(0.88) brightness(1.12); }   /* data-driven variant: a mark whose glyphs are fills on a filled shape cannot be silhouetted */
:root[data-theme="light"] .cov-mark--composite .cov-logo + span { color: var(--text); }

/* ---- page-specific literals found by the light-mode audit (2026-09-03) ------------------- */
:root[data-theme="light"] .dub-gate-card,
:root[data-theme="light"] .dub-refer-row,
:root[data-theme="light"] .dub-qual-form fieldset { background: rgba(20,24,27,.04); }
:root[data-theme="light"] .newsletter-email::placeholder { color: rgba(20,24,27,.45); }
:root[data-theme="light"] .res-cover-title { color: var(--text); }
:root[data-theme="light"] .bkh-prep-body p { color: var(--muted); }
:root[data-theme="light"] .bk-fit-strong h3,
:root[data-theme="light"] .bk-choose-cta-self { color:#3F6B55; }
:root[data-theme="light"] .cov-wordmark,
:root[data-theme="light"] .cov-mark b { color: var(--text); }
:root[data-theme="light"] .bk-fit-strong li svg { color:#3F6B55; }
:root[data-theme="light"] .bk-fit-weak h3 { color:#8C5F63; }          /* literal #c3a6a8 reads at 1.6:1 on paper */
:root[data-theme="light"] .bk-fit-weak li svg { color:#9E7276; }
:root[data-theme="light"] .cwp-fact-k { color: var(--muted); }         /* literal mineral tint on ivory */
:root[data-theme="light"] .asset-stage-dark,
:root[data-theme="light"] .brand-asset-stage-dark { --text: var(--text-on-dark); --muted: var(--muted-on-dark); --faint: var(--faint-on-dark); color: var(--text); }
:root .svc-fields :is(input, textarea, select),
:root .intake-form :is(input, textarea, select) { border-color: var(--line); }                       /* form controls: a visible edge on dark (was .06 alpha) */
:root[data-theme="light"] .svc-fields :is(input, textarea, select),
:root[data-theme="light"] .intake-form :is(input, textarea, select) { border-color: rgba(20,24,27,.16); background: var(--control-surface); }
:root[data-theme="light"] .dub-fzgrid-tier.t1 { color:#3F6B55; }                          /* tier chips: pastel literals on ivory */
:root[data-theme="light"] .dub-fzgrid-tier.t3 { color:#8A5F33; }
:root[data-theme="light"] .cwp-steps span { color: var(--muted); }
/* ---- how-sonsoto-works: the tabbed "Two ways to begin" panel was replaced by a static two-block card on 2026-09-08 (owner); its remaps are gone */
/* the timeline's numerals and inactive titles were literal white tints: 26% on near-black, and
   still 26% (of charcoal) on ivory after the first light pass. Tokens instead: numerals at the
   faint role (visible, quiet), inactive titles at the muted role, the current step at text. */
:root .hsw-tl-n { color: var(--faint); }
:root .hsw-tl-last .hsw-tl-n { color: var(--muted); }
:root .hsw-tl-body h3 { color: var(--muted); }
:root .hsw-tl-last .hsw-tl-body h3 { color: var(--text); }
:root .hsw-tl-item::before { background: var(--line-strong); }
:root .hsw-tl-owner { color: var(--muted); border-color: var(--line-strong); }
/* ---- vertical stepper (.vstep — the free-zone "What the timeline looks like" sections, referral "How it works") ----
   The page copies draw the circle ring, its fill and the connector in literal warm-white tints (rgba(230,224,210,…)):
   a dark-base value that vanishes on the ivory ground, and one page hand-patched its own light values. Token-owned
   here for both themes (2026-09-09, owner brief): raised-surface fill, the strong hairline for the ring, the text
   role for the numeral; one 1px connector in the connector token, drawn behind the circles from the first circle's
   centre to the last's. Each step draws the run from its own circle centre (padding 18 + radius 28 = 46px from its
   top) to the next step's circle centre (46px into the next item) — anchored on the circles, so a step whose text
   wraps taller than its circle still meets the line at the centre. The dashed border segments are retired. */
:root .vstep .vstep-item:not(:last-child)::before { content: ""; position: absolute; left: 28px; top: 46px; bottom: -46px; width: 1px; border: 0; background: var(--timeline-connector); }
:root .vstep .vstep-n { position: relative; z-index: 1; background: var(--surface-raised); border-color: var(--faint); color: var(--text); }   /* ring on the faint text token (owner, 2026-09-09: the strong hairline read as pale on the ivory ground) */
/* active step on a fine pointer (2026-09-09, owner brief): the same treatment the horizontal process timeline gives a hovered
   marker (_shared/process-timeline.css — marker fills --text, numeral --bg), carried to the vertical stepper: the hovered item's
   circle fills, its numeral goes light, its timing label and copy step up to --text; the other steps keep their default state
   (no dimming, no motion). Static list items stay static — nothing is made focusable for a decorative state. */
@media (hover: hover) and (pointer: fine) {
  :root .vstep .vstep-item:hover .vstep-n { background: var(--text); border-color: var(--text); color: var(--bg); }
  :root .vstep .vstep-item:hover .vstep-when { color: var(--text); }
  :root .vstep .vstep-item:hover .vstep-body p { color: var(--text); }
}
/* the two explanatory diagrams declare their own palette inside the inline SVG as literal
   ivory tints — invisible on the ivory panel in light. The document stylesheet outranks the
   SVG's own <style> on specificity, so the diagram takes the theme's linework roles here. */
/* ---- action cards (.contact-card: Connect cards in the Countries dropdown, the contact rail) ----
   The page rule painted hover as a literal rgba(255,255,255,.04) — a dark-base tint that, as a
   replacement background on the light theme, dropped the paper fill and read as a white flash.
   The component states are token-owned here for both themes: a one-step warm lift on hover with
   a stronger hairline and a soft shadow, the theme's focus ring for keyboard focus, and a press
   that returns to the base surface. Touch devices get no sticky hover. */
:root .contact-card { background: var(--surface-action); border-color: var(--border-action); transition: background var(--ease, .18s ease), border-color var(--ease, .18s ease), box-shadow var(--ease, .18s ease); }
/* Ask Vy is the one .contact-card rendered as a <button> (it opens a panel rather than navigating);
   every other one is an <a>. A button's UA style centres its text, so that single card sat centred in a
   left-aligned stack (owner, 2026-09-11). Set the alignment on the class so it cannot depend on which
   element a future card happens to use. */
:root .contact-card { text-align: left; }
:root .contact-card:hover { background: var(--surface-action-hover); border-color: var(--border-action-hover); box-shadow: var(--shadow-action-hover); }
:root .contact-card:hover .cc-ic { border-color: var(--line-strong); }
:root .contact-card:active { background: var(--surface-action-active); box-shadow: none; }
:root .contact-card:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; border-radius: 12px; }
@media (hover: none) { :root .contact-card:hover { background: var(--surface-action); border-color: var(--border-action); box-shadow: none; } }
@media (prefers-reduced-motion: reduce) { :root .contact-card { transition: none; } }
:root .calc-section { --calc-bg: var(--surface-dark); --calc-panel: #262A28; --calc-panel-2: #2C302E; --calc-panel-3: #333836; --calc-ink-4: #8A857B; }   /* deliberate dark tool: warm charcoal, not #0d0d0d; small labels lifted above 3:1 */
/* In light the calculator is a paper tool like every other panel: its --calc-* palette resolves
   to the theme's surface/text/line tokens; the sand accent becomes the charcoal action colour. */
:root[data-theme="light"] .calc-section {
  --calc-bg: var(--bg); --calc-panel: var(--surface-raised); --calc-panel-2: var(--surface); --calc-panel-3: var(--surface-tinted);
  --calc-line: var(--line); --calc-line-2: var(--line-strong); --calc-line-strong: rgba(20,24,27,.28);
  --calc-ink: var(--text); --calc-ink-2: var(--muted); --calc-ink-3: var(--faint); --calc-ink-4: var(--faint);
  --calc-accent: #14181B; --calc-accent-ink: #F4F5F2; --calc-ok: #3F6B55; --calc-warn: #8A5F33; --calc-gov: #4A6A8C; --calc-third: #8A5F33; --calc-sonsoto: #14181B;
}
/* the sticky specialist bar inside the calculator painted a literal near-black glass in both themes; it is a tonal panel of the tool's own palette */
:root .calc-section .calc-sticky { background: var(--calc-panel-3); backdrop-filter: none; -webkit-backdrop-filter: none; }

/* ---- wizard engine in light ------------------------------------------------------------
   wizards/engine.css declares its palette as --wiz-* literals for the dark base (#F4F5F2 text on
   a near-black card). On a light page the card surface flips with the theme but those literals
   did not, leaving off-white text on ivory. Remap the engine's tokens to the theme's — the
   component's own surface now determines its foreground. */
:root[data-theme="light"] .wiz {
  --wiz-bg: rgba(20,24,27,.02); --wiz-line: var(--line); --wiz-line-soft: var(--line-soft);
  --wiz-mineral: var(--mineral); --wiz-text: var(--text); --wiz-muted: var(--muted); --wiz-faint: var(--faint);
  --wiz-accent: var(--mineral); --wiz-sage: var(--sage); --wiz-brass: var(--brass);
}
:root[data-theme="light"] .wiz-progress-bar { background: rgba(20,24,27,.10); }
:root[data-theme="light"] .wiz-opt { border-color: rgba(20,24,27,.22); }
:root[data-theme="light"] .wiz-opt-dot { border-color: rgba(20,24,27,.38); }
:root[data-theme="light"] .wiz-opt:hover { border-color: rgba(20,24,27,.28); background: rgba(20,24,27,.03); }
:root[data-theme="light"] .wiz-opt.is-selected { background: rgba(122,106,72,.08); }
:root[data-theme="light"] .wiz-opt.is-selected .wiz-opt-dot { color: #F5F2EA; }

/* ---- literal off-white pill CTAs in light -------------------------------------------------
   .rt-secondary (route page hero) and .consult-cta-row .btn-outline paint their label as a literal
   rgba(244,245,242,…) — invisible on ivory. Same treatment as .btn-ghost: the theme's text and
   line tokens in light. */
:root[data-theme="light"] .rt-secondary,
:root[data-theme="light"] .consult-cta-row .btn-outline { color: var(--text); border-color: var(--line-strong); background: rgba(20,24,27,.03); }
:root[data-theme="light"] .rt-secondary:hover,
:root[data-theme="light"] .consult-cta-row .btn-outline:hover { color: var(--text); border-color: var(--mineral); background: rgba(20,24,27,.06); }

/* ---- select affordance — ONE shared chevron for every dropdown (owner brief, 2026-09-08) --------------
   Every native <select> that opens a list (single-choice, not the 36px icon-only header language control)
   draws the same 16px outline chevron-down at a 14px right inset in the theme's muted ink, with enough end
   padding that the value never runs under it. The native indicator is suppressed here so no page has to;
   a page's own select rules keep their height, border, background and radius — this layer owns the icon.
   Specificity is deliberately above the page rules (and above the light-theme control-surface shorthand). */
:root select:not([multiple]):not([size]):not(.uic) { appearance: none; -webkit-appearance: none; background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23B3ADA3' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 14px center; background-size: 16px 16px; padding-inline-end: 40px; cursor: pointer; }
:root[data-theme="light"] select:not([multiple]):not([size]):not(.uic) { background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235E5952' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); }
:root select:not([multiple]):not([size]):not(.uic):disabled { opacity: .55; cursor: default; }

/* ---- hero descriptor — the short muted inventory line under a hero heading (owner brief, 2026-09-08) ----
   "Logos, lockups, marks." on brand.html: lower emphasis than the heading, a shade lighter than body copy,
   never uppercase, mono, bold or a label. One class, no page override. */
.hero-descriptor { margin: 0 0 30px; font-family: inherit; font-size: clamp(17px, 1.5vw, 19px); line-height: 1.45; font-weight: 400; color: var(--muted); letter-spacing: 0; text-transform: none; max-width: 32ch; }

/* ---- form validation feedback (owner brief, 2026-09-08; _shared/form-validation.js) ----------------------
   Error under the field: icon + text, never colour alone; the field itself carries aria-invalid. Tokens only. */
:root { --error-ink:#8F3B2B; --error-line:rgba(143,59,43,.55); }
:root:not([data-theme="light"]) { --error-ink:#E39A88; --error-line:rgba(227,154,136,.55); }
:root[data-theme="light"] { --error-ink:#8F3B2B; --error-line:rgba(143,59,43,.55); }
/* row-marker glyphs (owner brief, 2026-09-09; SuitabilitySplit inline rows): a visibly green check for the positive panel,
   a visibly red minus for the "may need more context" panel, a muted burnt-orange up-right arrow for an alternative route. Muted, not saturated; the panel heading carries the
   meaning so the colour is never the only signal. The miss glyph reuses the form-error ink. */
:root { --marker-win-glyph:#2E8A47; --marker-miss-glyph:var(--error-ink); --marker-alt-glyph:#B8672A; }
:root:not([data-theme="light"]) { --marker-win-glyph:#6FD48A; --marker-alt-glyph:#E0955A; }
:root[data-theme="light"] { --marker-win-glyph:#2E8A47; --marker-alt-glyph:#B8672A; }
.fv-error { display:flex; align-items:flex-start; gap:6px; margin:6px 0 0; font-size:12.5px; line-height:1.45; color:var(--error-ink); }
.fv-error svg { flex:none; margin-top:2px; }
.fv-error.fv-hint { color:var(--muted); }
.fv-summary { margin:10px 0 0; font-size:13px; color:var(--error-ink); }
.fv-error[hidden], .fv-summary[hidden] { display:none; }   /* the flex/margin rules above must not override the hidden attribute */
:root [aria-invalid="true"] { border-color:var(--error-line) !important; }

/* Desktop header utility cluster — owner brief 2026-09-08, IA superseded 2026-09-17.
   tools/build-nav-actions.py owns the contact pair's markup; tools/build-header-cta.py owns the CTA
   and now renders Sign in after it; these rules own the geometry. Shipped IA:
       [ WhatsApp ] [ Call us ]  |  [ Start your setup ]  |  [ Sign in ]
   The 09-08 brief read [ Sign in ] | [ WhatsApp ] [ Call ] [ CTA ] — that is HISTORY, not the target.
   Sign in is an account utility and must not precede the contact controls or the CTA.
   The pages carry their own inline .nav-util/.uic rules; this region is inlined at the END of
   <head> by build-theme.py, so it wins on source order — no page CSS, no nth-child, no !important.
   The ONE divider moves from the end of .nav-util (where it separated the cluster from the CTA)
   to the boundary between Sign in and the contact controls. */
:root .nav-util { gap: 18px; padding-inline-end: 0; margin-inline-end: 0; border-inline-end: 0; }
:root .nav-contact-actions { display: inline-flex; align-items: center; gap: 8px; padding-inline-start: 18px; border-inline-start: 1px solid var(--line-soft); }
:root .nav-actions { gap: 16px; }
/* ONE action group, not four drifting objects (owner review 2026-09-17). Spacing carries the hierarchy:
   the contact pair is tightest, a moderate step opens before the primary CTA, and Sign in closes up behind
   it instead of being exiled at the edge. This also repairs a real overflow — with Sign in moved next to the
   CTA the row was 87px wider than .nav-inner at 1440 and clipped Sign in past the viewport at 1120, which is
   why the cluster looked stranded against the right edge: it was outside its own wrap. The nav-link ladder
   (36 → 20 → 16) simply starts one step earlier, so the links keep the spacing they already have below
   1400px rather than being relocated. */
:root .nav-links { gap: 22px; }
:root .nav-inner { gap: 24px; }
:root .nav-actions { gap: 12px; flex: 0 0 auto; }
:root .nav-util { margin-inline-start: 0; margin-inline-end: 4px; }        /* the moderate step before the CTA */
:root .nav-contact-actions { gap: 6px; padding-inline-start: 14px; }       /* the contact pair: tightest gap in the row */
:root .nav-actions .signin { padding: 6px 8px; }                           /* closes the optical gap after the CTA */
/* Sign in is an account utility, not a destination (owner review 2026-09-15): one step quieter and smaller than the
   nav labels (14.5px, --muted), set off from "Contact" by the cluster's own margin, never heavier than the contact
   tiles or the CTA. Hover/focus restore full ink; the icon stays. */
:root .nav-util { margin-inline-start: 10px; }
:root .nav-util .signin { font-size: 13px; color: var(--faint); padding: 6px 10px; gap: 6px; letter-spacing: .01em; }
:root .nav-util .signin svg { width: 14px; height: 14px; }
:root .nav-util .signin:hover, :root .nav-util .signin:focus-visible { color: var(--text); }
:root .nav-util .signin:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
/* WhatsApp: green on the GLYPH only — the tile keeps the neutral .uic surface and hairline, exactly like
   "Call us" beside it. Owner, 2026-09-10 (afternoon): "the subtle one we had before, only the logo green,
   not the background", applied to every contact control in the top menu. This supersedes the solid-green
   tile brief from earlier the same day; the floating .chat-fab launcher is NOT in scope and stays solid.
   Only `color` is set here, so the surface, border, hover surface and focus ring all stay the shared .uic
   behaviour — no second definition of the tile to drift. */
:root .uic--whatsapp { color: #25D366; }
:root .uic--whatsapp:hover { color: #1FBA5B; }
:root .uic--whatsapp:focus-visible { outline: 2px solid var(--focus-ring, var(--mineral)); outline-offset: 2px; }
/* Contact-card WA icon (the "Get in touch" panel): same rule — the .cc-ic tile stays neutral, the glyph is
   brand green. data-contact-whatsapp is set by build-nav-actions.py. */
:root .contact-card[data-contact-whatsapp] .cc-ic { color: #25D366; }
:root .contact-card[data-contact-whatsapp]:hover .cc-ic { color: #1FBA5B; }
/* Contact Center "Prefer WhatsApp? — How can we help?": the third copy of the same mark, and the one that
   was left behind. Its page-carried rule paints the glyph var(--muted), so the panel showed a grey
   WhatsApp logo directly above a green one in the footer's Get-in-touch card (owner, 2026-09-11:
   "fix that WA logo on continue here — the bottom of the page should look like that"). Colour only,
   here rather than in the 106 page copies, so there is one place the brand green lives. */
:root .vy-wa .wa { color: #25D366; }
:root .vy-wa:hover .wa { color: #1FBA5B; }
/* Call: a LABELLED neutral control (owner brief 2026-09-09) — the handset glyph alone was easy to miss beside
   WhatsApp. Same .uic tile, dark text and hairline, widened to fit "Call us"; never red (error/danger), no new
   colour token. The number is not shown while nav-actions.json › call.href is the placeholder. */
:root .uic--labelled { width: auto; display: inline-flex; align-items: center; gap: 7px; padding: 0 12px 0 10px; color: var(--text); font-size: 13.5px; font-weight: 500; letter-spacing: -.005em; text-decoration: none; }
:root .uic--labelled .uic-label { white-space: nowrap; line-height: 1; }
:root .uic--labelled:hover { color: var(--text); border-color: var(--line-strong); }
:root .uic--labelled:focus-visible { outline: 2px solid var(--focus-ring, var(--mineral)); outline-offset: 2px; }
@media (max-width: 1400px) { :root .nav-links { gap: 20px; } }
@media (max-width: 1160px) { :root .nav-links { gap: 16px; } :root .nav-inner { gap: 16px; }
  :root .nav-actions { gap: 10px; } :root .nav-util { margin-inline-end: 2px; }
  :root .nav-contact-actions { gap: 5px; padding-inline-start: 10px; } }
/* the contact pair is the first thing the page already drops on a narrow desktop (page CSS, max-width:1080).
   It stopped fitting 30px earlier than that once Sign in joined the row, so the same fallback starts at 1120
   rather than leaving a ~20px band where the cluster spills past the wrap. */
@media (max-width: 1120px) { :root .nav-util { display: none; } }
/* Header fit is decided by the CONTENT, not by a guessed width (owner, 2026-09-17: "fix it"). The row is a
   different width in every locale — the Spanish header is ~180px wider than the English one and overflowed its
   wrap even at 1440 — so no media query can name the width at which the links stop fitting. theme.js measures
   .nav-inner (always in its uncompacted state, so the decision is stable) and sets data-nav-compact when the row
   does not fit; the drawer is the same one the small-screen nav already uses. The media queries above stay as the
   no-JS fallback. */
:root[data-nav-compact] .nav-links, :root[data-nav-compact] .nav-util { display: none; }
:root[data-nav-compact] .nav-toggle { display: block; margin-inline-start: auto; }
:root[data-nav-compact] .nav-inner { gap: 16px; }
/* TIGHT tier (2026-09-21): tried by theme.js before compacting, only when the natural row overflows. It changes
   horizontal spacing and hides the call pill's visible text (the link keeps its aria-label and title); it changes
   NO heights, so every item keeps its vertical position. Gate: tools/audit-nav-geometry.mjs (G1–G4). */
/* Load stability (2026-09-21): the header row is parsed piece by piece — every menu item carries a large mega-panel,
   so the parser yields between items and the browser can paint a half-built row. Each new item then re-centres the
   links and the late action cluster pushes everything again (measured: 2–5 header layout shifts per load on every page
   family under a throttled network). Until the row's LAST element (.nav-toggle, present on all 170 pages) exists, keep
   the links and actions laid out but invisible; they appear once, already in their final place. The row keeps its fixed
   height, so nothing below moves. Pure CSS; a browser without :has() drops the rule and behaves as before. */
.nav-inner:not(:has(.nav-toggle)) > :not(.brand):not(.brand-menu) { visibility: hidden; }
/* SON-3245 (2026-09-27): the paid landing page's header (_shared/chrome-header-landing.html, .nav--landing) is brand + the
   contact pair + one CTA, with no drawer and therefore no .nav-toggle — the guard above would blank its actions. It opts out
   here, by its own class, and its actions sit at the inline end. */
.nav--landing .nav-inner:not(:has(.nav-toggle)) > :not(.brand):not(.brand-menu) { visibility: visible; }   /* same specificity + one class: wins over the guard */
:root .nav--landing .nav-actions { margin-inline-start: auto; }   /* :root-prefixed like the row rules above, so it outranks their margin reset */
:root[data-nav-tight] .nav-inner, :root[data-nav-snug] .nav-inner { gap: 24px; }
:root[data-nav-tight] .nav-links, :root[data-nav-snug] .nav-links { gap: 20px; }
:root[data-nav-tight] .nav-actions, :root[data-nav-snug] .nav-actions { gap: 10px; }
:root[data-nav-tight] .uic--call.uic--labelled { padding-inline: 0; width: 36px; justify-content: center; }
:root[data-nav-tight] .uic--call.uic--labelled .uic-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
/* Measuring (2026-09-22): theme.js sets data-nav-measure only for its synchronous measuring pass (never painted), so an
   animated width — the call pill's 0.22s transition — is read at its final value, not mid-animation. The drawer toggle's
   box is fixed for the same reason: the row is measured the moment the toggle is parsed, before its icon may be. */
:root[data-nav-measure] .nav-inner, :root[data-nav-measure] .nav-inner * { transition: none !important; }
:root .nav-toggle { min-width: 36px; min-height: 36px; }

/* ---- section boundary (2026-09-08, owner brief) ------------------------------------------
   ONE quiet line at the top of every top-level section after the first. Before this, each
   section class opted in on its own (svc-core, faq, consult …) and the ones that never did —
   .ncta "Talk to a UAE specialist", .founders-grid, .referral-section, the bank-detail body
   sections, fz-included, the Vietnam mid-page trio, the index closing CTA — floated between
   bounded neighbours. A second convention owned the line from BELOW (trust, stats, every
   dub-* emirate section, the bank/legal/careers heroes), which stacked two 1px lines on ~60
   boundaries. Ownership is now the FOLLOWING section, always; the bottom-owned declarations
   were removed at their sources and tools/audit-section-boundaries.py fails on a new one.
   `~`, not `+`: generators leave <style> elements between sections, and `+` would skip those.
   Deliberately NOT :root-prefixed — (0,0,3) sits under any class rule, so a component that is
   genuinely attached to the block above opts out with `border-top: 0` on its own class
   (brand-section under its merged hero, g-body under the guide hero, the fzp-section strip
   that data-follows another section). No nth-child, no !important, no per-page markup. */
body > section ~ section { border-top: 1px solid var(--line-soft); }
    

/* Ask Vy is the one .contact-card rendered as a <button> — it opens a panel rather than navigating.
   A button shrink-wraps to its content and centres its own text, so in a block-level stack of <a>
   cards it came out both narrower and centre-aligned (owner, 2026-09-11). Set both on the class so
   the card family does not depend on which element a given card happens to use. */
:root .contact-card { width: 100%; box-sizing: border-box; }

/* "Need to meet outside these hours? Just ask." — the link is a two-word phrase and was breaking
   between "Just" and "ask" at 1024px and 390px, which left a stray full stop on its own line
   (owner, 2026-09-11). An underlined link split across lines also reads as two links. */
:root .rail-meta-link { white-space: nowrap; }

/* Market navigation rows — the country switcher in the Markets panel (2026-09-11).
   Two problems, one shared rule. The rows had no hover at all, so UAE and Vietnam read as static
   information rather than as the choices they are. And only the CURRENT market's row carried a
   class — every other row was a bare <a> — so the one row that is not a choice was the only one a
   hover could ever have targeted; tools/build-market-nav.py now gives them all `.mkt-row` and
   `aria-current` separates the states.
   Lives here rather than on the page: 20 pages carry market rows and no .mkt-row rule of their own,
   and a navigation-row treatment should not be re-declared per page. */
:root /* Market-switcher flag (2026-09-18). Declared in market-taxonomy.json › markets.<k>.flag and
   registered in data/image-roles.json; build-market-nav.py omits the slot until the file exists, so
   these rules simply do not apply yet. 3:2 at 20x14, a hairline to keep a white-heavy flag from
   dissolving into a light ground, no shadow. */
.mkt-flag { flex: 0 0 auto; display: inline-flex; width: 20px; height: 14px; border-radius: 2px; overflow: hidden; box-shadow: 0 0 0 1px var(--line-soft, rgba(20,24,27,.10)); }
.mkt-flag img { display: block; width: 100%; height: 100%; object-fit: cover; }
.mkt-row .rw { min-width: 0; }
/* Inline variant: the footer switch strip and the mobile drawer list markets as inline text
   links, not as the flex row .mkt-row gives, so the flag carries its own spacing there. */
.mkt-flag--inline { margin-right: 7px; vertical-align: -2px; }
.mkt-row { transition: background-color 140ms ease, color 140ms ease; }
:root .mkt-row .go { transition: transform 140ms ease, color 140ms ease; }
/* :not([aria-current]) — the row for the market you are already on keeps its rest state and its
   Current badge; lifting it would offer a choice that is not one. */
:root .mkt-row:not([aria-current]):hover,
:root .mkt-row:not([aria-current]):focus-visible { background: rgba(230, 224, 210, .10); color: var(--text); }
:root .mkt-row:not([aria-current]):hover .mk,
:root .mkt-row:not([aria-current]):focus-visible .mk { color: var(--text); }
:root .mkt-row:not([aria-current]):hover .go,
:root .mkt-row:not([aria-current]):focus-visible .go { color: var(--text); transform: translateX(2px); }
@media (prefers-reduced-motion: reduce) {
  :root .mkt-row, :root .mkt-row .go { transition: none; }
  :root .mkt-row:not([aria-current]):hover .go,
  :root .mkt-row:not([aria-current]):focus-visible .go { transform: none; }
}

/* 1-HOUR SUBMISSION SLA — form-adjacent line under the consult card heading (data/sla.json, 2026-09-16) */
:root .consult-card .sla-form { margin: -4px 0 14px; font-size: 13.5px; line-height: 1.5; color: var(--muted); }
:root .consult-card .sla-form b { font-weight: 600; color: var(--text); text-transform: uppercase; letter-spacing: .02em; }

/* Market switcher (owner 2026-09-16: the current market reads as a quiet label, not a bordered chip; the AE/VN
   abbreviations are gone from the rows and the desktop menu — the country name is the label). */
:root .mkt-badge { border: 0; border-radius: 0; padding: 0; font-size: 10px; letter-spacing: .14em; color: var(--faint); }
:root .mkt-current .mkt-row, :root .dmkt-current { opacity: .62; }
:root .mkt-current .mkt-row:hover, :root .mkt-current .mkt-row:focus-visible,
:root .dmkt-current:hover, :root .dmkt-current:focus-visible { opacity: 1; }

/* The consult heading was capped narrower than its own column, so "30 minutes with a specialist." broke across two
   lines with the column half empty (owner, 2026-09-17: "on the same line"). The column already bounds it. */
:root .consult-copy h2 { max-width: none; text-wrap: balance; }

/* Mark = market switcher (SON-4173, ported from the repo copy 2026-09-17 by tools/migrate-brand-menu.py): the Countries
   panel hangs off the brand link; the Countries item is gone from the main nav. Loaded last, so it beats the page's nav rules. */
:root .nav-inner .brand-menu { position: relative; display: flex; align-items: center; }
:root .nav-inner .brand-menu > .brand { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; }
:root .nav-inner .brand-menu > .panel { left: 0; right: auto; min-width: 324px; }
/* the current market recedes instead of wearing a chip; aria-current is the hook */
:root .mkt-row[aria-current="page"] { opacity: .45; }
:root .mkt-row[aria-current="page"]:hover, :root .mkt-row[aria-current="page"]:focus-visible { opacity: 1; }
/* Market flags: authentic geometry, palette pulled toward the site (reds knocked back, Vietnam's star = brass). */
:root { --flag-red: #9C4038; --flag-ae-green: #4A6B52; --flag-ae-white: #E8E4DA; --flag-ae-black: #23262A; --flag-vn-star: var(--brass, #C2A26A); --flag-edge: rgba(255,255,255,.16); }
:root[data-theme="light"] { --flag-red: #96453C; --flag-ae-green: #4C6B53; --flag-ae-white: #EFEBE1; --flag-ae-black: #2A2D30; --flag-vn-star: #B08F52; --flag-edge: rgba(20,24,27,.18); }
:root .mkt-flag { display: inline-flex; flex: none; line-height: 0; margin-right: 8px; }
:root .mkt-flag svg { display: block; border-radius: 2px; }
:root .mkt-row .mkt-flag { align-self: flex-start; margin-top: 3px; }
:root .mkt-row .rw { margin-right: auto; }
/* Country marker (2026-09-22, owner-approved; FLAG ONLY since the owner's follow-up the same day: "remove the country
   name, just leave the flag"). A 16x11 flag BESIDE the Sonsoto lock-up on UAE and Vietnam pages (market-taxonomy.json ›
   country_marker, written by build-market-nav.py): the page's country context, and it opens the same market switcher
   as the mark. The country name stays in the button as visually hidden text — its accessible name. Hidden by default — without JS, during load and wherever it does not
   fit the wrap's content box: theme.js sets data-nav-marker after choosing the tier without it; if the full-spacing
   row has no room it may take the tight tier's gaps (data-nav-snug, the call label stays), never a tier. Atlas:
   chrome_header_country_marker. */
:root .nav-inner .mkt-marker { display: none; align-items: center; gap: 6px; margin-inline-start: 12px; padding: 4px 0; border: 0; border-radius: 3px; background: none; color: var(--muted); font: inherit; font-size: 13px; font-weight: 500; line-height: 1; letter-spacing: normal; white-space: nowrap; cursor: pointer; }
:root[data-nav-marker] .nav-inner .mkt-marker { display: inline-flex; }
:root .nav-inner .mkt-marker .mkt-flag { width: 16px; height: 11px; margin: 0; }
:root .nav-inner .mkt-marker:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; color: var(--text); }
:root .nav-inner .mkt-marker-vh, :root .nav-inner .mkt-marker-name { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }

/* Header rhythm (owner review, 2026-09-18): three deliberate groups — brand · navigation · actions. Before this the nav
   floated mid-bar with an equal 69px on either side (both .nav-links and .nav-actions carried margin-inline-start:auto),
   which read as loose beside the dense CTA. The nav now sits with the brand, the action cluster keeps the only auto
   margin. BUDGET: _shared/theme.js compacts the row to the drawer toggle the moment .nav-inner overflows, and the
   widest row (market/about pages: brand 104 + links 497 + actions 407) must clear the 1180px wrap at 1440 — the first
   cut (48 + 64px) measured 1197 and hid the menu on every page but index. */
:root .nav-inner { gap: 40px; }
:root .nav-links { gap: 28px; margin-inline: auto; }   /* owner, 2026-09-18: the three items sit centred between brand and actions */
:root .nav-actions { margin-inline-start: 0; gap: 14px; }   /* only .nav-links carries auto margins → the trio is centred */
@media (max-width: 1400px) { :root .nav-inner { gap: 28px; } :root .nav-links { gap: 22px; } }
@media (max-width: 1160px) { :root .nav-inner { gap: 20px; } :root .nav-links { gap: 16px; } :root .nav-actions { gap: 10px; } }
/* Phone and compact header (owner, 2026-09-25: "the hamburger menu and the start your set up cta, should be aligned
   right side"). The rhythm above leaves the only auto margins on .nav-links; once the links are hidden — every page at
   <=860px, and theme.js's compact tier (data-nav-compact) above that — nothing pushed the actions, so the CTA and the
   menu toggle sat against the brand with the row's slack on the right (measured 46-312px at 375-768). The action
   cluster takes the auto margin whenever the links are gone; the desktop rhythm is untouched. */
@media (max-width: 860px) { :root .nav-actions { margin-inline-start: auto; } }
:root[data-nav-compact] .nav-actions { margin-inline-start: auto; }
/* Korean (2026-09-25): lines break between words, as Korean typesetting expects; Hangul otherwise wraps at any
   syllable. A word longer than its box still wraps rather than overflowing. */
:root:lang(ko) body { word-break: keep-all; overflow-wrap: break-word; }
/* Japanese breaks between any two characters by default; keep the browser's CJK line breaking but let long Latin
   names (free zones, banks) wrap instead of overflowing (SON-4818). */
:root:lang(ja) body { line-break: strict; overflow-wrap: anywhere; }

/* Main-site footer (owner, 2026-09-18): four columns after the brand — Services · Company · Support · Trust — spread across
   the width. The page rule still declares the old six-track grid with !important; this outranks it on specificity. */
:root body[data-desk-market='global'] .foot-grid { grid-template-columns: 2.25fr 1fr 1.05fr 0.95fr 0.85fr 1.15fr !important; }   /* 2026-09-18 evening: + Jurisdictions column · 2026-09-19 (owner): brand track 1.5fr → 1.85fr so the whole menu group starts further right; the five menu tracks keep their ratios, so their own spacing stays even. Tighter 1.7fr · looser 2fr. Desktop only — ≤900px keeps the two-column rule below. */
@media (max-width: 900px) { :root body[data-desk-market='global'] .foot-grid { grid-template-columns: 1fr 1fr !important; } }

/* Announcement-bar CTA as a chip (owner, 2026-09-18): the same pill as the desk-band "Chat with us" (.offhours-cta),
   in the same place on both rotator slides (referral · hiring). Page-inline .referral-bar-cta is the underline link;
   this wins on specificity. */
:root .referral-bar-cta { padding: 6px 14px; border: 1px solid rgba(230,224,210,.25); border-radius: 999px; background: transparent; color: var(--text); font-size: 13px; font-weight: 500; line-height: 1.3; transition: border-color var(--ease,.18s ease), color var(--ease,.18s ease); }
:root .referral-bar-cta:hover, :root .referral-bar-cta:focus-visible { border-color: var(--mineral); color: var(--mineral); }
@media (max-width: 720px) { :root .referral-bar-cta { padding: 5px 10px; } }

/* Desk status strip moved to the page foot (owner, 2026-09-18): it now sits between the close-band CTAs and the footer.
   Its own bottom hairline is the divider above the footer, so the footer's top border would double it. */
:root .offhours-strip + .foot { border-top: 0; }

/* Announcement bar: message + chip sit together around the centre (owner, 2026-09-18); the dismiss × stays at the
   right edge. Page-inline rules give the rotator flex:1 and the message flex:1, which pushed the chip to the far right.
   18:30 (owner): the dismiss × is REMOVED from the markup on every page — the bar cannot be closed, period; the script's
   `if (bar && closeBtn)` guard skips the whole dismissal block, so a stale localStorage flag can no longer hide it. */
:root .referral-bar-inner { justify-content: center; }
:root .rbar-rotator { flex: 0 1 auto; }
:root .rbar-slide { justify-content: center; gap: 28px; }
:root .referral-bar-msg { flex: 0 1 auto; }
@media (max-width: 720px) { :root .rbar-slide { gap: 12px; } }

/* Phone header (2026-09-18, found while verifying the category pages at 390px): brand + "Start your setup" + "Sign in" +
   the drawer toggle need ~349px of a 350px row, so the CTA sat 7px over the wordmark on EVERY page. Below 480px the
   header Sign in yields (the drawer carries the same Sign in link); the row gap tightens. Measured: name.right 124 vs
   CTA.x 117 before; no overlap after. */
@media (max-width: 480px) { :root .nav-actions .signin { display: none; } :root .nav-inner { gap: 12px; } :root .nav-actions { gap: 8px; } }

/* Jurisdictions dropdown (main site, owner 2026-09-18): two flag rows + the Connect column; anchored to its trigger
   like the Services panel. `.mkt-flag` inside a plain panel row sits inline before the label. */
:root .navitem.has-panel:has(.panel-inner-jx) { position: relative; }
:root .navitem.has-panel:has(.panel-inner-jx) > .panel { left: 0; right: auto; --panel-w: 560px; }
:root .panel-inner.panel-inner-jx { grid-template-columns: 1fr 1fr; }
:root .panel-inner-jx .pcol > ul > li > a { display: inline-flex; align-items: center; gap: 10px; }
:root .foot-grid .fcol li a { display: inline-flex; align-items: center; gap: 8px; }

/* Main-site Services panel (2026-09-18 evening): one category column + Connect, so it no longer needs the country
   pages' 820px three-column width. Hidden panels still count toward .nav-inner's scrollWidth (that is what the
   header-fit script reads), and with Jurisdictions to its left the 820px panel ran 16px past the row at 1440 and
   compacted the menu. 600px keeps it inside the row from 1280px up. Country pages keep the three-column panel. */
:root body[data-desk-market='global'] .navitem.has-panel:has(.panel-inner-bs) > .panel { --panel-w: 600px; }
:root body[data-desk-market='global'] .panel-inner.panel-inner-bs { grid-template-columns: 1fr 1fr; }

/* Main-site footer (owner, 2026-09-19): the wider brand track alone gave no air — the referral card stretches to the
   track, so the gap to "Jurisdictions" stayed at the 20px grid gap. The brand block keeps its previous content width
   (track 277px − 40px) and the 40px becomes the separation before the menu group. Desktop only. */
@media (min-width: 901px) { :root body[data-desk-market='global'] .foot-grid > .foot-brand { padding-inline-end: 96px; } }   /* 2026-09-19 night (owner): more room before Jurisdictions — the menu columns move right (brand track 1.85→2.25fr, padding 40→96px) */

/* Footer social row (2026-09-19): six networks now (TikTok added, Facebook back on the main site). At the page's 16px gap
   six 36px buttons need 296px and wrapped 4 + 2 in the ~235px brand column. One row everywhere: 34px buttons, 6px gap
   (6×34 + 5×6 = 234px). Phones keep the same row. */
:root .foot-social { flex-wrap: nowrap; gap: 6px; }
:root .foot-social a { width: 34px; height: 34px; flex: 0 0 34px; }
/* Intake outcome — SHARED (2026-09-21). A lead form answered with nothing a visitor could see: the
   status line was appended as the form's last grid cell, so it inherited a column (147px wide on
   contact.html) and sat below the Send button, off-screen on a long form, while every field stayed
   filled. The confirmation PANEL existed but only referral.html carried its rules, so no other page
   could use it. Both live here now, on every page the theme reaches. Markup: tools/build-intake-wire.py. */
:root .form-done { grid-column: 1 / -1; padding: 30px 26px; text-align: center; border: 1px solid var(--line); border-radius: 13px; background: var(--surface-tinted); }
:root .form-done:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 3px; }
:root .form-done .tick { width: 42px; height: 42px; border-radius: 50%; margin: 0 auto 15px; display: grid; place-items: center; border: 1px solid var(--line-strong); background: var(--surface-2); }
:root .form-done .tick svg { width: 19px; height: 19px; color: var(--sage); }
:root .form-done h3 { margin: 0 0 8px; font-size: 18px; line-height: 1.3; color: var(--text); }
:root .form-done p, :root .form-done [data-intake-status] { margin: 0 auto; max-width: 34em; font-size: 14px; line-height: 1.6; color: var(--muted); }
/* the in-form line (sending · failure · success on a form with no panel): full width, never a squeezed column */
:root [data-intake-status] { grid-column: 1 / -1; margin-top: 14px; font-size: 14px; line-height: 1.6; color: var(--muted); }
:root [data-intake-status]:empty { display: none; }
:root [data-intake-status][data-intake-error] { color: var(--semantic-danger-text, #E4B9A6); }

/* Footer social row, quieter boxes (owner brief, 2026-09-19): monochrome icons, same size/order/spacing/destinations. The box
   drops its fill and keeps only the soft hairline; hover adds a faint surface and a firmer border; keyboard focus gets the
   site's focus ring. A network with no configured URL renders as .foot-social-off (tools/build-social-links.py): same slot,
   no border, muted, not a link. Tokens only. */
:root .foot-social a { display: grid; place-items: center; border-radius: 9px; border: 1px solid var(--line-soft); background: transparent; transition: background-color .18s ease, border-color .18s ease; }
:root .foot-social a:hover { background: var(--surface-2); border-color: var(--line); }
:root .foot-social a:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
:root .foot-social .foot-social-off { display: grid; place-items: center; width: 34px; height: 34px; flex: 0 0 34px; border-radius: 9px; border: 1px solid transparent; color: var(--faint); opacity: .5; }
:root .foot-social .foot-social-off svg { width: 17px; height: 17px; }
@media (prefers-reduced-motion: reduce) { :root .foot-social a { transition: none; } }

/* ncta — near-footer contact module, font size only (owner 2026-09-19: "the font there is too small … fix at the right place";
   then "i only asked to increase the font, nothing else"). The module's CSS is hand-carried in each page's own <style>; this
   block, inlined everywhere by build-theme.py, raises the three font sizes on every page that carries it. Was 15 / 13 / 9.5px
   (9px ≤620px). Nothing else about the module is changed here. */
:root .ncta-cta-list .ncta-cta-label b { font-size: 17px; }
:root .ncta-cta-list .ncta-cta-label span { font-size: 15px; }
:root .ncta-medallion-role { font-size: 11px; }
@media (max-width: 620px) { :root .ncta-medallion-role { font-size: 10px; } }
/* ncta icons, component-wide (owner 2026-09-19: "make the logos larger please for the component not just the page"): the
   icon tile 34→42px, the glyph 16→21px, on every page that carries the module. */
:root .ncta-cta-list .ncta-cta-icon { width: 42px; height: 42px; flex: 0 0 42px; border-radius: 11px; }
:root .ncta-cta-list .ncta-cta-icon svg { width: 21px; height: 21px; }

/* Top-navigation geometry — ONE contract for the announcement bar + header on every page (2026-09-21, nav layout-shift
   fix; evidence docs/audits/nav-geometry-2026-09-21/, gate tools/audit-nav-geometry.mjs). Measured before: the header row
   itself was identical everywhere (70px, labels 23.77px below its top), but the whole header sat 4.64px higher on the
   market landings and planners (ae|vn/index, ae|vn/cost-estimator, ae/setup-estimate and their locale twins) because a
   page-inline "Announcement bar dimmed" block set the bar to 12.5px type and 8px padding (47.25px bar vs 51.89px). The
   global pages also carried their own label ladder (14px at <=1160px vs 13.5px on market pages), the header CTA was
   13.5px on some pages and 13px on others at phone width, and at phone width the bar grew with its translated message
   (42 / 52 / 69.5px). Geometry is owned HERE; pages keep only their colours (the dimmed market bar stays dimmed).
   `html:root body …` (0,2,2+) outranks the page-inline `body.uae-page .referral-bar…` (0,2,1) and plain page rules. */
html:root body .referral-bar { font-size: 13.5px; line-height: 1.4; }
html:root body .referral-bar-inner { box-sizing: border-box; height: 51px; padding-block: 0; padding-inline: 24px; }
html:root body .referral-bar-msg { font-size: 13.5px; line-height: 1.4; }
html:root body .referral-bar-cta { font-size: 13px; line-height: 1.3; }
@media (max-width: 720px) {
  html:root body .referral-bar-inner { padding-inline: 16px; }
  /* two lines at most, whatever the locale: the dot rides inline so the text can be clamped */
  html:root body .referral-bar-msg { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; font-size: 12.5px; }
  html:root body .referral-bar-msg .referral-bar-dot { display: inline-block; vertical-align: middle; margin-inline-end: 10px; position: relative; top: -1px; }
}
html:root body header.nav .navlabel, html:root body header.nav .navlink { font-size: 14.5px; line-height: 1.55; }
@media (max-width: 1400px) { html:root body header.nav .navlabel, html:root body header.nav .navlink { font-size: 13.5px; } }
@media (max-width: 560px) { html:root body header.nav .nav-actions .btn-sm { padding: 9px 13px; font-size: 13px; } }
    