/* ==========================================================================
   eSulfa — site styles
   --------------------------------------------------------------------------
   Brand source of truth: brand/tokens.json. Colour values here are copied from
   it deliberately rather than imported, because this file has to work with no
   build step on Apache shared hosting. If you change a colour, change it there
   first and mirror it here.

   Written with CSS logical properties throughout (inline-start, padding-inline)
   so the Arabic RTL layout is the same stylesheet mirrored, not a second one.

   Section index
     1.  Fonts
     2.  Tokens
     3.  Base
     4.  Motion primitives
     5.  Layout
     6.  Type
     7.  Buttons
     8.  Header
     9.  Hero + pay-cycle figure
     10. Arc motif
     11. Steps
     12. App stage
     13. Price
     14. Forms
     15. Footer
     16. Page hero / prose / cards (about + 404)
     17. Reduced motion
   ========================================================================== */

/* 1. Fonts ================================================================ */

/* Inter — body. Variable, two subsets, unicode-range split so Arabic pages
   never download the Latin-ext file they cannot use. */
@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/inter-var-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter";
  src: url("/assets/fonts/inter-var-latin-ext.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Nunito — display. The brand sheet names this "Nunito Rounded"; the shipping
   Google Fonts family with those rounded terminals is Nunito. Nunito Sans is a
   different, non-rounded face and is not a substitute. No Arabic coverage. */
@font-face {
  font-family: "Nunito";
  src: url("/assets/fonts/nunito-var-latin.woff2") format("woff2");
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Nunito";
  src: url("/assets/fonts/nunito-var-latin-ext.woff2") format("woff2");
  font-weight: 400 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* IBM Plex Sans Arabic — carries both display and body duty in Arabic, since
   neither Nunito nor Inter has Arabic glyphs. Arabic subsets only: Latin inside
   Arabic copy (eSulfa, CliQ, digits) falls through to Inter. */
@font-face {
  font-family: "IBM Plex Sans Arabic";
  src: url("/assets/fonts/plexarabic-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0898-08E1,
    U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74,
    U+FE76-FEFC;
}
@font-face {
  font-family: "IBM Plex Sans Arabic";
  src: url("/assets/fonts/plexarabic-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0898-08E1,
    U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74,
    U+FE76-FEFC;
}
@font-face {
  font-family: "IBM Plex Sans Arabic";
  src: url("/assets/fonts/plexarabic-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0898-08E1,
    U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74,
    U+FE76-FEFC;
}
@font-face {
  font-family: "IBM Plex Sans Arabic";
  src: url("/assets/fonts/plexarabic-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0898-08E1,
    U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74,
    U+FE76-FEFC;
}

/* 2. Tokens =============================================================== */

:root {
  /* --- brand, from brand/tokens.json ---------------------------------- */
  --deep-teal:   #0d2f3e;
  --aqua:        #21c1b8;
  --mint:        #7fe6d0;
  --orange:      #ffb020;
  --off-white:   #f7fafc;

  /* --- surfaces. Ratios are measured against --text. ------------------- */
  --bg:          #0d2f3e;   /* 13.42:1 */
  --bg-sunken:   #0a2532;   /* 15.14:1 */
  --surface:     #123b4c;   /* 11.41:1 */
  --surface-2:   #17475a;   /*  9.61:1 */

  --border:        rgb(247 250 252 / 10%);
  --border-strong: rgb(247 250 252 / 18%);
  --border-accent: rgb(33 193 184 / 38%);

  /* --- text ------------------------------------------------------------ */
  --text:        #f7fafc;   /* 13.42:1 AAA */
  --text-muted:  #a8c4ce;   /*  7.67:1 AAA */
  --text-faint:  #8aaab6;   /*  5.70:1 AA  */

  /* --- accents --------------------------------------------------------- */
  --accent:        var(--aqua);
  --accent-soft:   rgb(33 193 184 / 12%);
  --accent-line:   rgb(33 193 184 / 30%);
  --cta:           var(--orange);
  /* Derived from the brand orange rather than being a fourth orange nobody
     approved. colour-mix keeps it provably a lightening of --cta. */
  --cta-hover:     color-mix(in srgb, var(--orange) 82%, var(--off-white));
  --success:       var(--mint);
  --danger:        #ff9a8b;  /* 6.98:1 */

  /* Deep teal is the ONLY correct text colour on aqua, mint or orange.
     White measures 1.49-2.24:1 on those and fails WCAG at every size. */
  --on-accent:   var(--deep-teal);

  /* --- type ------------------------------------------------------------ */
  --font-display: "Nunito", "Inter", ui-rounded, system-ui, sans-serif;
  --font-en: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-ar: "IBM Plex Sans Arabic", "Inter", ui-sans-serif, system-ui, sans-serif;

  --fs-display: clamp(2.5rem, 1.4rem + 4.6vw, 4.75rem);
  --fs-h1: clamp(2.25rem, 1.55rem + 3vw, 3.75rem);
  --fs-h2: clamp(1.875rem, 1.4rem + 1.9vw, 2.75rem);
  --fs-h3: clamp(1.1875rem, 1.1rem + 0.4vw, 1.375rem);
  /* Two steps that were missing. The scale ran 2.75rem straight down to
     0.875rem with nothing between, so every section had exactly two voices —
     shout and whisper — and body copy sat at the size of a caption. */
  --fs-h3-lg: clamp(1.375rem, 1.15rem + 1vw, 1.875rem);
  --fs-lead-sm: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  --fs-lead: clamp(1.0625rem, 1rem + 0.45vw, 1.3125rem);
  --fs-body: 1rem;
  --fs-sm: 0.875rem;
  --fs-xs: 0.8125rem;

  /* --- space: 4px rhythm ----------------------------------------------- */
  --s1: 4px;  --s2: 8px;   --s3: 12px;  --s4: 16px;  --s5: 24px;
  --s6: 32px; --s7: 48px;  --s8: 64px;  --s9: 96px;

  /* --- radius ----------------------------------------------------------- */
  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 22px;
  --r-xl: 32px;
  --r-pill: 999px;

  /* --- measure ---------------------------------------------------------- */
  --container: 1180px;
  --gutter: clamp(20px, 5vw, 40px);
  /* Tightened from 7vw/104px. Two sections of air between every block read
     as an unfinished page rather than a generous one. */
  --section-y: clamp(48px, 5vw, 76px);
  --header-h: 68px;

  /* --- motion. One rhythm for the whole site. --------------------------- */
  --dur-fast: 160ms;
  --dur: 240ms;
  --dur-slow: 420ms;
  --ease-out: cubic-bezier(0.16, 0.84, 0.44, 1);
  --ease-spring: cubic-bezier(0.34, 1.4, 0.64, 1);
  /* Entrances overshoot slightly and settle, which is what makes motion read as
     physical rather than as a value being interpolated. Kept mild — this is a
     financial product, and a bounce that draws attention to itself is the wrong
     register for it. */
  --ease-enter: cubic-bezier(0.22, 1.12, 0.36, 1);

  /* --- elevation -------------------------------------------------------- */
  --shadow-sm: 0 1px 2px rgb(4 18 25 / 40%);
  --shadow-md: 0 8px 24px -8px rgb(4 18 25 / 55%);
  --shadow-lg: 0 28px 64px -20px rgb(4 18 25 / 70%);
  --glow-accent: 0 0 0 1px rgb(33 193 184 / 30%), 0 12px 40px -12px rgb(33 193 184 / 45%);
}

/* 3. Base ================================================================= */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

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

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-en);
  font-size: var(--fs-body);
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* The aurora. Two soft blooms in the brand accents over the deep teal ground,
   fixed so they read as light in the room rather than as page decoration.
   Pointer-events none and behind everything. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(60rem 40rem at 12% -8%,  rgb(33 193 184 / 16%), transparent 62%),
    radial-gradient(48rem 34rem at 92% 8%,   rgb(127 230 208 / 10%), transparent 60%),
    radial-gradient(70rem 50rem at 50% 108%, rgb(255 176 32 / 7%),  transparent 64%);
}

html[lang="ar"] body { font-family: var(--font-ar); line-height: 1.85; }

img, svg { display: block; max-width: 100%; }
svg { flex: none; }

a { color: inherit; }

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

::selection { background: var(--accent); color: var(--on-accent); }

.skip-link {
  position: absolute;
  inset-inline-start: -9999px;
  top: var(--s3);
  z-index: 100;
  padding: var(--s3) var(--s5);
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--r-sm);
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { inset-inline-start: var(--s3); }

/* 4. Motion primitives ==================================================== */

/* Scroll reveal. Content is visible by default and only hidden once JS has
   confirmed it can run — html.js is set by the inline script in <head>. A
   renderer that never executes JS therefore photographs a complete page rather
   than an empty one. site.js has three independent paths to is-visible. */
.js .reveal {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition:
    opacity var(--dur-slow) var(--ease-out),
    transform var(--dur-slow) var(--ease-enter);
  /* A failsafe that needs no JavaScript at all.
     Every other fallback lives inside initReveal(), which is inside site.js —
     so the one failure that hides the page for good is site.js not arriving or
     arriving truncated, while the inline head script and this stylesheet both
     succeed. Four seconds later the browser un-hides everything on its own.
     .is-visible still wins whenever the reveal is working normally. */
  animation: reveal-failsafe 0s linear 4s forwards;
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}
@keyframes reveal-failsafe { to { opacity: 1; transform: none; } }

/* Focus must never land somewhere invisible. opacity: 0 keeps an element in the
   tab order, so a keyboard user tabbing straight off a fresh load could reach
   the hero CTA while it was still fading in. Higher specificity than the rule
   above, so no !important is needed. */
.js .reveal:focus-within { opacity: 1; transform: none; transition: none; animation: none; }

/* 5. Layout =============================================================== */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section-y); position: relative; }

.section-head { max-width: 46rem; margin-block-end: var(--s7); }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-family: var(--font-en);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  margin-block-end: var(--s4);
}
.eyebrow::before {
  content: "";
  inline-size: 22px;
  block-size: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--mint), var(--aqua));
}
html[lang="ar"] .eyebrow { letter-spacing: normal; }

/* 6. Type ================================================================= */

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
html[lang="ar"] h1,
html[lang="ar"] h2,
html[lang="ar"] h3 {
  font-family: var(--font-ar);
  letter-spacing: normal;
  line-height: 1.35;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); font-weight: 600; line-height: 1.3; }

.lead {
  font-size: var(--fs-lead);
  color: var(--text-muted);
  max-width: 42rem;
  text-wrap: pretty;
}

/* The launch mark. Replaces the border-and-pulsing-dot badge every template
   ships with. The announcement instead borrows the brand's own grammar: the
   pay-cycle arc in miniature, drawing itself in, with the logo's two payday
   dots landing at its end. Launch = payday for Jordan. */
.launch-mark {
  display: inline-flex;
  align-items: center;
  gap: var(--s4);
  color: var(--mint);
  font-size: var(--fs-lead-sm);
  font-weight: 600;
}
/* English gets the editorial spaced-caps eyebrow; Arabic script has no
   uppercase and must never be letter-spaced (it breaks the joins), so the
   treatment is scoped to the language, not shared. */
html[lang="en"] .launch-mark span {
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-size: var(--fs-sm);
  font-weight: 700;
}
.launch-mark__glyph { flex: none; inline-size: 96px; block-size: auto; overflow: visible; }
/* The arc travels in the reading direction, so it mirrors in Arabic. */
html[dir="rtl"] .launch-mark__glyph { transform: scaleX(-1); }
@media (max-width: 420px) {
  .launch-mark__glyph { inline-size: 72px; }
  html[lang="en"] .launch-mark span { letter-spacing: 0.06em; }
}

.launch-mark__dot {
  fill: var(--cta);
  transform-box: fill-box;
  transform-origin: center;
}
/* Comet and ripple exist only while their animations run. Base state is
   invisible, so no-JS, print and never-revealed pages simply never see them. */
.launch-mark__comet, .launch-mark__ring { opacity: 0; }
.launch-mark__ring { transform-box: fill-box; transform-origin: center; }

/* Entrance, once the hero reveal fires — one story told in order:
     1. the comet sweeps the curve, laying the arc down as its trail;
     2. the text wipes on in the reading direction, following it;
     3. the two payday dots spring on where the comet arrives, and the
        landing rings out once.
   Afterwards the comet re-runs as a faint glint every 8s, and the dots give
   a small nod as it reaches them — alive, but never a pulsing badge. */
.js .reveal .launch-mark__trail {
  stroke-dasharray: 100 100;
  stroke-dashoffset: 100;
  transition: stroke-dashoffset 700ms var(--ease-out) 150ms;
}
.js .reveal.is-visible .launch-mark__trail { stroke-dashoffset: 0; }

.js .reveal.is-visible .launch-mark__comet {
  stroke-dasharray: 10 90;
  animation: lm-comet 8s linear 150ms infinite;
}
@keyframes lm-comet {
  0%    { stroke-dashoffset: 10;  opacity: 0; }
  1%    { opacity: 0.95; }
  10.5% { stroke-dashoffset: -95; opacity: 0.95; }
  11.5% { stroke-dashoffset: -95; opacity: 0; }
  100%  { stroke-dashoffset: -95; opacity: 0; }
}

.js .reveal .launch-mark span {
  clip-path: inset(-15% 100% -15% 0);
  transition: clip-path 700ms var(--ease-out) 300ms;
}
/* .js sits on <html> itself, so the RTL variant compounds onto it. The wipe
   opens from the inline-start edge in both directions. */
html[dir="rtl"].js .reveal .launch-mark span { clip-path: inset(-15% 0 -15% 100%); }
.js .reveal.is-visible .launch-mark span,
html[dir="rtl"].js .reveal.is-visible .launch-mark span { clip-path: inset(-15% 0 -15% 0); }

.js .reveal .launch-mark__dot {
  opacity: 0;
  transform: scale(0.4);
  transition:
    transform var(--dur) var(--ease-spring) 820ms,
    opacity var(--dur-fast) var(--ease-out) 820ms;
}
.js .reveal .launch-mark__dot--2 { transition-delay: 960ms, 960ms; }
.js .reveal.is-visible .launch-mark__dot { opacity: 1; transform: scale(1); }

.js .reveal.is-visible .launch-mark__ring {
  animation: lm-ripple 700ms var(--ease-out) 1000ms 1 forwards;
}
@keyframes lm-ripple {
  0%   { transform: scale(0.6); opacity: 0.7; }
  100% { transform: scale(2.3); opacity: 0; }
}

/* The idle nod: phase-locked to the 8s glint, so the dots acknowledge the
   comet exactly as it arrives. Starts one full cycle in, after the entrance
   transition has long settled and handed the transform over. */
.js .reveal.is-visible .launch-mark__dot { animation: lm-nod 8s ease 8.95s infinite; }
.js .reveal.is-visible .launch-mark__dot--2 { animation-delay: 9.1s; }
@keyframes lm-nod {
  0%   { transform: scale(1); }
  1.5% { transform: scale(1.12); }
  5%   { transform: scale(1); }
  100% { transform: scale(1); }
}

/* 7. Buttons ============================================================== */

.btn {
  --btn-py: 12px;
  --btn-px: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-block-size: 44px;            /* touch target floor */
  padding: var(--btn-py) var(--btn-px);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font: inherit;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-body);
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
  position: relative;
  isolation: isolate;
  transition:
    transform var(--dur-fast) var(--ease-spring),
    box-shadow var(--dur) var(--ease-out),
    background-color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out);
}
html[lang="ar"] .btn { font-family: var(--font-ar); font-weight: 600; }

.btn--sm { --btn-py: 9px;  --btn-px: 18px; font-size: var(--fs-sm); min-block-size: 44px; }
.btn--lg { --btn-py: 15px; --btn-px: 30px; font-size: 1.0625rem; min-block-size: 52px; }

/* The one primary CTA per view. Warm orange against the teal ground is the
   single highest-contrast element on the page, which is the point. */
.btn--primary {
  background: var(--cta);
  color: var(--on-accent);
  box-shadow: 0 1px 0 rgb(255 255 255 / 25%) inset, var(--shadow-md);
}
.btn--primary:hover { background: var(--cta-hover); }
.btn--primary:hover,
.btn--primary:focus-visible { transform: translateY(-2px); box-shadow: 0 1px 0 rgb(255 255 255 / 25%) inset, 0 16px 36px -12px rgb(255 176 32 / 45%); }
.btn--primary:active { transform: translateY(0) scale(0.98); }

.btn--ghost {
  background: rgb(247 250 252 / 4%);
  border-color: var(--border-strong);
  color: var(--text);
}
.btn--ghost:hover { background: rgb(247 250 252 / 9%); border-color: var(--accent); }
.btn--ghost:active { transform: scale(0.98); }

.btn[disabled] { opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: none; }

.link-quiet {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-block-size: 44px;
  color: var(--text-muted);
  font-weight: 500;
  text-decoration: none;
  transition: color var(--dur) var(--ease-out);
}
.link-quiet:hover { color: var(--mint); }
.link-quiet .icon-flip { transition: transform var(--dur) var(--ease-out); }
.link-quiet:hover .icon-flip { transform: translateX(4px); }
/* The arrow points at the reading direction, so it mirrors in Arabic. */
html[dir="rtl"] .icon-flip { transform: scaleX(-1); }
html[dir="rtl"] .link-quiet:hover .icon-flip { transform: scaleX(-1) translateX(4px); }

/* 8. Header =============================================================== */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgb(13 47 62 / 72%);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-block-end: 1px solid transparent;
  transition: border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.site-header.is-scrolled {
  border-block-end-color: var(--border);
  background: rgb(10 37 50 / 88%);
}
.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--s5);
  min-block-size: var(--header-h);
}

/* The logo is the brand pack's artwork, not a reconstruction.
   It used to be typeset here — the word in the display face, the arc as a
   clipped ring on ::after, the two points as radial gradients on ::before.
   Three separate approximations that only ever coincidentally lined up: the
   arc sat over the "e" alone while the points sat past the "a", so the mark
   rendered as a stray semicircle and two loose dots either side of a word.
   A logo is identity, not decoration; it gets shipped as the file the brand
   ships, and the only thing left to do here is size and place it. */
.wordmark {
  margin-inline-end: auto;
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;              /* touch target floor */
  text-decoration: none;
}
.wordmark img {
  display: block;
  block-size: 44px;
  inline-size: auto;
  /* The lockup is Latin artwork in both languages, so it must never mirror
     with the page the way a directional icon should. */
  transform: none;
  transition: transform var(--dur-fast) var(--ease-out);
}
.wordmark:hover img { transform: translateY(-1px); }

.header-nav { display: none; gap: var(--s6); }
@media (min-width: 620px) { .header-nav { display: flex; } }
.header-nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  color: var(--text-muted);
  font-weight: 500;
  text-decoration: none;
  transition: color var(--dur) var(--ease-out);
}
.header-nav a:hover { color: var(--text); }
.header-nav a::after {
  content: "";
  position: absolute;
  inset-block-end: 16px;
  inset-inline: 0;
  block-size: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--mint), var(--aqua));
  transform: scaleX(0);
  transform-origin: var(--tx-origin, left);
  transition: transform var(--dur) var(--ease-out);
}
html[dir="rtl"] .header-nav a::after { --tx-origin: right; }
.header-nav a:hover::after,
.header-nav a[aria-current="page"]::after { transform: scaleX(1); }
.header-nav a[aria-current="page"] { color: var(--text); }

.header-actions { display: flex; align-items: center; gap: var(--s3); }

.lang-toggle {
  display: flex;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: rgb(10 37 50 / 60%);
}
.lang-toggle button {
  /* A real 44px control rather than a small one with an invisible hit area
     stretched around it: the visible target and the touchable target should be
     the same thing. */
  min-block-size: 44px;
  padding-inline: 14px;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-faint);
  font: inherit;
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  transition: color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.lang-toggle button:hover { color: var(--text); }
.lang-toggle button[aria-pressed="true"] {
  background: var(--accent);
  color: var(--on-accent);
}
.header-cta { display: none; }
@media (min-width: 480px) { .header-cta { display: inline-flex; } }

/* 9. Hero + 3D canvas ===================================================== */

.hero {
  position: relative;
  padding-block: clamp(48px, 7vw, 96px) clamp(56px, 8vw, 104px);
  isolation: isolate;
}
.hero .container { position: relative; z-index: 1; }

/* The hero is a claim beside the evidence for it, so it is two columns rather
   than a paragraph floating over a decorative background. */
.hero-grid {
  display: grid;
  gap: var(--s8) var(--s7);
  align-items: center;
}
@media (min-width: 940px) {
  .hero-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); }
}

/* The pay cycle. */
.cycle { margin: 0; color: var(--text-faint); }
.cycle__svg { inline-size: 100%; block-size: auto; overflow: visible; }

/* Drawn on load, left to right, so the eye follows the cycle in the direction
   it runs. The dash offset animates rather than the dasharray, because
   animating the array itself re-lays out the pattern every frame. */
.cycle__earned {
  stroke-dashoffset: 64;
  animation: cycle-draw 1.5s var(--ease-out) 0.25s forwards;
}
@keyframes cycle-draw { to { stroke-dashoffset: 0; } }

.cycle__now { opacity: 0; animation: cycle-mark 0.5s var(--ease-spring) 1.5s forwards; }
@keyframes cycle-mark { from { opacity: 0; } to { opacity: 1; } }

.cycle__dot { opacity: 0; animation: cycle-mark 0.4s var(--ease-spring) 1.75s forwards; }
.cycle__dot--2 { animation-delay: 1.85s; }

/* The scale beneath the arc. A flex row, so RTL reverses it with no extra
   rule — the cycle then reads right to left, as Arabic does. */
.cycle__scale {
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  margin-block-start: var(--s4);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.cycle__now-label { color: var(--text); }
.cycle__note {
  margin-block-start: var(--s4);
  font-size: var(--fs-sm);
  line-height: 1.7;
  color: var(--text-muted);
  max-inline-size: 40ch;
}
/* The arc sweeps from the start of the cycle to payday. In Arabic that runs
   the other way, so the whole figure mirrors — the labels are HTML and flip on
   their own. */
html[dir="rtl"] .cycle__svg { transform: scaleX(-1); }

.hero h1 { margin-block: var(--s5) 0; max-width: 18ch; }
.hero .lead { margin-block-start: var(--s5); }

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s4) var(--s6);
  margin-block-start: var(--s7);
}

/* One per line. Wrapping three of these across a half-width column left the
   third on its own underneath, which reads as an orphan rather than a list. */
.facts {
  display: grid;
  gap: var(--s3);
  margin-block-start: var(--s7);
  padding: 0;
  list-style: none;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.facts li { display: flex; align-items: center; gap: var(--s2); }
.facts svg { color: var(--mint); }

@media (min-width: 900px) {
  /* The hero is a real two-column grid now, so the copy no longer needs to be
     held back to 60% to keep clear of a background artifact. */
  .hero h1 { max-width: 15ch; }
  .hero .lead { max-width: 34ch; }
}

/* 10. Arc motif =========================================================== */

/* A hairline arc used to separate major sections. It is the logo's sweep
   abstracted into a rule, which is what keeps the page feeling like one brand
   rather than a stack of unrelated blocks. */
.arc-rule {
  position: relative;
  block-size: 1px;
  border: 0;
  margin: 0;
  background: linear-gradient(90deg, transparent, var(--border-strong) 18%, var(--border-strong) 82%, transparent);
}
.arc-rule::after {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  /* Centred with auto margins, not with inset-inline-start + translateX.
     That pair mixed a LOGICAL inset with a PHYSICAL transform: under RTL the
     inset resolves to right: 50%, putting the accent's right edge on the centre
     line, while translateX(-50%) still moves physically left — so on the Arabic
     pages the accent sat a full 340px off centre with half of it hanging past
     the end of the rule. Auto margins are direction-agnostic, and they free
     `transform` for the draw-in below to use on its own. */
  inset-inline: 0;
  margin-inline: auto;
  inline-size: min(340px, 48vw);
  block-size: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--aqua), var(--mint), transparent);
  opacity: 0.55;
}

/* 11. Steps =============================================================== */

.steps {
  --step-coin: 40px;
  position: relative;
  display: grid;
  gap: var(--s7);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* The line the markers are strung along. Inset by half a marker at each end so
   it starts and finishes inside the first and last coin rather than dangling. */
.steps::before {
  content: "";
  position: absolute;
  inset-block-start: calc(var(--step-coin) / 2);
  inset-inline-start: calc(var(--step-coin) / 2);
  inline-size: 1px;
  block-size: calc(100% - var(--step-coin));
  background: linear-gradient(180deg, var(--accent-line), rgb(33 193 184 / 8%));
}
@media (min-width: 880px) {
  .steps { grid-template-columns: repeat(3, 1fr); gap: var(--s6); }
  .steps::before {
    inset-inline: calc(var(--step-coin) / 2) calc(100% / 3 - var(--step-coin));
    inline-size: auto;
    block-size: 1px;
    background: linear-gradient(90deg, var(--accent-line), rgb(33 193 184 / 8%));
  }
}
@media (max-width: 879px) {
  .step { padding-inline-start: calc(var(--step-coin) + var(--s5)); padding-block-start: 0; }
  .step__num { inset-block-start: -4px; }
}

/* A step is a position in a sequence, so it is drawn as one: numbered markers
   strung along a single line, in order. It used to be a bordered card
   identical to the ones in "what it's for" below, which made a process and a
   set of qualities look like the same kind of thing and threw away the only
   structural difference between the two sections. The numerals are legitimate
   here for the same reason — this content really is ordered. */
.step {
  position: relative;
  padding-block-start: calc(var(--step-coin) + var(--s5));
}
.step__num {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  display: grid;
  place-items: center;
  inline-size: var(--step-coin);
  block-size: var(--step-coin);
  border-radius: var(--r-pill);
  background: var(--bg);
  border: 1px solid var(--border-accent);
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--mint);
  /* Sits on the connecting line and masks it, so the line reads as passing
     between the markers rather than through them. */
  z-index: 1;
}
.step__icon {
  display: block;
  margin-block-end: var(--s4);
  color: var(--mint);
}
.step h3 { margin-block-end: var(--s3); max-inline-size: 20ch; }
.step p {
  color: var(--text-muted);
  font-size: var(--fs-body);
  line-height: 1.75;
  max-inline-size: 34ch;
}

/* 12. App stage =========================================================== */

/* Three real screenshots on CSS device frames, fanned in perspective.
   The fan angles are NOT set here. They are set by script from each phone's
   measured position relative to the stage centre, for two reasons: the grid
   reverses under RTL, so any angle keyed to :first-child would put the fan's
   left-hand tilt on the right-hand phone in Arabic; and with no script at all
   the phones stay flat and square, which is a perfectly good static layout
   rather than a broken one. */

.stage-wrap {
  /* 3D transforms push geometry outside the grid's box. Without this the page
     gains a horizontal scrollbar at some widths, which the layout tests catch
     and which is miserable on a phone. `clip` rather than `hidden` so it
     cannot become a scroll container and swallow sticky positioning. */
  overflow-x: clip;
  /* The fan leans phones toward the viewer, so it needs vertical air that the
     unrotated boxes do not account for. */
  padding-block: var(--s4);
  margin-block: calc(var(--s4) * -1);
}

.stage {
  display: grid;
  gap: var(--s7);
  margin: 0;
  padding: 0;
  list-style: none;
  justify-items: center;
  perspective: 1600px;
  perspective-origin: 50% 42%;
}

.phone {
  --ry: 0deg;
  --tz: 0px;
  inline-size: 100%;
  max-inline-size: 240px;
  transform: rotateY(var(--ry)) translateZ(var(--tz));
  transform-style: preserve-3d;
  transition: transform var(--dur-slow) var(--ease-out);
  will-change: transform;
}

/* The device. Built rather than photographed: a photo of a handset dates the
   page to a hardware generation and weighs more than the screenshot inside it.
   It draws no Dynamic Island either — the captures are full-screen grabs off a
   real device, so the island is already in the pixels, and adding a CSS one on
   top gave every phone two of them. */
.phone__frame {
  position: relative;
  border-radius: 30px;
  padding: 6px;
  background: linear-gradient(155deg, #35576a, #0a2532 46%, #223f4e);
  box-shadow:
    0 1px 0 rgb(247 250 252 / 14%) inset,
    0 24px 48px -18px rgb(0 0 0 / 62%),
    0 2px 8px rgb(0 0 0 / 32%);
}

.phone__screen {
  display: block;
  inline-size: 100%;
  block-size: auto;
  border-radius: 24px;
  background: var(--sunken);
}

.phone__step {
  margin-block-start: var(--s5);
  text-align: center;
  color: var(--muted);
  font-size: 0.9375rem;
  line-height: 1.5;
}
.phone__step b {
  display: block;
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  margin-block-end: 2px;
}

@media (min-width: 640px) {
  .stage { grid-template-columns: repeat(3, 1fr); gap: var(--s6); align-items: start; }
  .phone { max-inline-size: none; }
}

/* 13. Price =============================================================== */

.price-card {
  display: grid;
  gap: var(--s6);
  padding: clamp(var(--s6), 4vw, var(--s8));
  border: 1px solid var(--border-accent);
  border-radius: var(--r-xl);
  background:
    radial-gradient(80% 120% at 0% 0%, rgb(33 193 184 / 12%), transparent 60%),
    linear-gradient(180deg, rgb(247 250 252 / 6%), rgb(247 250 252 / 2%));
  box-shadow: var(--shadow-lg);
  position: relative;
  overflow: hidden;
}
@media (min-width: 820px) {
  .price-card { grid-template-columns: auto 1fr; align-items: center; gap: var(--s9); }
}

.price {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  font-family: var(--font-display);
  /* Tabular figures so the number never reflows between languages. */
  font-variant-numeric: tabular-nums;
}
.price__num {
  font-size: clamp(4rem, 2.2rem + 7vw, 7rem);
  font-weight: 800;
  line-height: 0.9;
  letter-spacing: -0.04em;
  background: linear-gradient(140deg, var(--off-white) 10%, var(--mint) 55%, var(--aqua) 95%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}
.price__cur {
  font-size: clamp(1.25rem, 1rem + 1vw, 1.75rem);
  font-weight: 700;
  color: var(--mint);
}
.price-caption { color: var(--text-muted); margin-block-start: var(--s3); }

.ticks { display: grid; gap: var(--s4); margin: 0; padding: 0; list-style: none; }
.ticks li { display: flex; align-items: center; gap: var(--s3); font-size: var(--fs-lead); }
.ticks svg {
  padding: 3px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--mint);
}
.price-note { margin-block-start: var(--s5); color: var(--text-faint); font-size: var(--fs-sm); }

/* 14. Forms =============================================================== */

.book-head { max-width: 46rem; margin-block-end: var(--s6); }
.book-head .lead { margin-block-start: var(--s4); }

.segmented {
  display: inline-flex;
  padding: 4px;
  margin-block-end: var(--s6);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: rgb(10 37 50 / 70%);
}
.segmented button {
  min-block-size: 44px;
  padding-inline: clamp(14px, 3.4vw, 26px);
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-weight: 600;
  font-size: var(--fs-sm);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.segmented button:hover { color: var(--text); }
.segmented button[aria-selected="true"] {
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: var(--shadow-sm);
}

.panel > h3 { margin-block-end: var(--s3); }
.panel > .lead { margin-block-end: var(--s6); }

.form-card {
  padding: clamp(var(--s5), 3.5vw, var(--s7));
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, rgb(247 250 252 / 5%), rgb(247 250 252 / 2%));
}

.grid-fields { display: grid; gap: var(--s5); }
@media (min-width: 720px) {
  .grid-fields { grid-template-columns: 1fr 1fr; }
  .field--full { grid-column: 1 / -1; }
}

.field { display: grid; gap: var(--s2); }
.field > label {
  display: flex;
  align-items: center;
  gap: 3px;
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
}
.req { color: var(--orange); }

.field input,
.field select,
.field textarea {
  inline-size: 100%;
  min-block-size: 46px;            /* touch floor; also stops iOS zoom at 16px */
  padding: 11px var(--s4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: rgb(10 37 50 / 60%);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-body);
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.field textarea { min-block-size: 112px; resize: vertical; line-height: 1.6; }
.field input::placeholder,
.field textarea::placeholder { color: var(--text-faint); opacity: 1; }

.field input:hover,
.field select:hover,
.field textarea:hover { border-color: rgb(247 250 252 / 28%); }

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-color: var(--accent);
  background: rgb(10 37 50 / 85%);
  box-shadow: 0 0 0 3px rgb(33 193 184 / 22%);
}

/* Native select arrow, drawn as an inline SVG data URI so it needs no network
   request and no img-src beyond the data: already allowed by the CSP. */
.field select {
  appearance: none;
  padding-inline-end: 44px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23a8c4ce' 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 var(--s4) center;
}
html[dir="rtl"] .field select { background-position: left var(--s4) center; }
.field select option { background: var(--bg-sunken); color: var(--text); }

.field-hint { font-size: var(--fs-xs); color: var(--text-faint); }
.field-error { font-size: var(--fs-xs); color: var(--danger); min-block-size: 0; }
/* Colour is never the only signal — an invalid field also gets the arrow glyph. */
.field.is-invalid input,
.field.is-invalid select,
.field.is-invalid textarea { border-color: var(--danger); }
.field.is-invalid .field-error::before { content: "! "; font-weight: 700; }

.check {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
  font-size: var(--fs-sm);
  color: var(--text-muted);
  cursor: pointer;
  padding-block: var(--s2);
}
.check input {
  flex: none;
  inline-size: 20px;
  block-size: 20px;
  margin-block-start: 2px;
  accent-color: var(--aqua);
  cursor: pointer;
}

/* Honeypot. Off-screen rather than display:none, because some bots skip
   hidden inputs. Hidden from assistive tech via aria-hidden in the markup. */
.hp {
  position: absolute !important;
  inline-size: 1px;
  block-size: 1px;
  /* Zeroed explicitly. box-sizing is border-box globally, so an input keeps its
     UA border and padding as a floor and refuses to shrink below 8x6 without
     this — measurably not the 1px the rule intends. */
  border: 0;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.form-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s4);
  margin-block-start: var(--s6);
}
.form-note { font-size: var(--fs-xs); color: var(--text-faint); }
.form-status { flex-basis: 100%; font-size: var(--fs-sm); color: var(--danger); }
.form-status:empty { display: none; }

.success {
  padding: clamp(var(--s5), 3.5vw, var(--s7));
  border: 1px solid var(--border-accent);
  border-radius: var(--r-lg);
  background:
    radial-gradient(70% 120% at 0% 0%, rgb(127 230 208 / 12%), transparent 62%),
    linear-gradient(180deg, rgb(247 250 252 / 5%), rgb(247 250 252 / 2%));
}
.success:focus-visible { outline-offset: 6px; }
.success__icon {
  display: inline-flex;
  padding: var(--s3);
  margin-block-end: var(--s4);
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--mint);
}
.success h3 { margin-block-end: var(--s3); }
.success p { color: var(--text-muted); max-width: 44rem; }
.success__share {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s4);
  margin-block-start: var(--s6);
  padding-block-start: var(--s5);
  border-block-start: 1px solid var(--border);
}
.success__share p { font-size: var(--fs-sm); }
.copy-btn.is-copied { border-color: var(--mint); color: var(--mint); }

/* 15. Footer ============================================================== */

.site-footer {
  margin-block-start: var(--section-y);
  padding-block: var(--s8) var(--s6);
  border-block-start: 1px solid var(--border);
  background: linear-gradient(180deg, transparent, rgb(10 37 50 / 70%));
}
.footer-top { display: grid; gap: var(--s7); }
@media (min-width: 720px) {
  .footer-top { grid-template-columns: 1fr auto; gap: var(--s9); }
}
.footer-brand p { margin-block-start: var(--s3); color: var(--text-muted); font-size: var(--fs-sm); }
/* Single column until there is genuinely room for two. The contact column holds
   an email address, which has a wide minimum and pushed the page 2px past the
   viewport at 320px when forced into a half-width column. */
.footer-links { display: grid; gap: var(--s6); }
@media (min-width: 400px) {
  .footer-links { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s7); }
}
.footer-col h2 {
  font-family: var(--font-en);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-block-end: var(--s4);
}
html[lang="ar"] .footer-col h2 { font-family: var(--font-ar); letter-spacing: normal; }
.footer-col ul { display: grid; gap: var(--s3); margin: 0; padding: 0; list-style: none; }
.footer-col a {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;            /* touch target floor */
  /* An inline-flex box sizes to its content and will not shrink, so a long
     email address would otherwise push past the column it sits in. */
  max-inline-size: 100%;
  overflow-wrap: anywhere;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  text-decoration: none;
  transition: color var(--dur) var(--ease-out);
}
.footer-col a:hover { color: var(--mint); }

.footer-legal {
  margin-block-start: var(--s8);
  padding-block-start: var(--s5);
  border-block-start: 1px solid var(--border);
  display: grid;
  gap: var(--s4);
}
.legal-line { font-size: var(--fs-xs); color: var(--text-faint); max-width: 62rem; line-height: 1.7; }
.footer-copy { font-size: var(--fs-xs); color: var(--text-faint); }

/* 16. Page hero / prose / cards (about + 404) ============================= */

.page-hero {
  padding-block: clamp(56px, 8vw, 104px) clamp(32px, 5vw, 56px);
  position: relative;
}
.page-hero h1 { margin-block: var(--s4) 0; max-width: 20ch; }
.page-hero .lead { margin-block-start: var(--s5); }
.page-hero .hero-actions { margin-block-start: var(--s6); }

/* margin-inline-end pins the measure to the start of the container. Without it
   a .prose used on its own — rather than as the right column of a .split, which
   is the only place it existed before — sits centred, leaving an empty third
   beside every paragraph. */
.prose { max-width: 42rem; margin-inline-end: auto; color: var(--text-muted); display: grid; gap: var(--s4); }
.prose p { line-height: 1.75; }
.prose--intro { color: var(--text); font-size: var(--fs-lead); }

.split { display: grid; gap: var(--s6); }
@media (min-width: 900px) {
  .split { grid-template-columns: minmax(0, 22rem) minmax(0, 1fr); gap: var(--s9); align-items: start; }
  .split > .section-head { margin-block-end: 0; position: sticky; top: calc(var(--header-h) + var(--s6)); }
}

.cards { display: grid; gap: var(--s5); }
@media (min-width: 760px) {
  .cards { grid-template-columns: 1fr 1fr; }
  /* An odd number of cards leaves the last one alone in its row with a hole
     beside it, which reads as a layout that ran out rather than a list that
     finished. Spanning it is not a patch: in every one of these lists the last
     card is the conclusion the ones above build to, so full width is what it
     should have had anyway. */
  .cards .card:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
.card {
  padding: var(--s6) var(--s5);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: linear-gradient(180deg, rgb(247 250 252 / 5%), rgb(247 250 252 / 2%));
  transition: transform var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.card:hover { transform: translateY(-3px); border-color: var(--border-accent); }
.card h3 { margin-block-end: var(--s3); }
.card p { color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.7; }
.card__mark {
  display: inline-flex;
  margin-block-end: var(--s4);
  padding: var(--s3);
  border-radius: var(--r-md);
  background: var(--accent-soft);
  color: var(--mint);
}

.stack { display: grid; gap: var(--s5); }
.stack-item {
  padding-inline-start: var(--s5);
  border-inline-start: 2px solid var(--accent-line);
}
.stack-item h3 { margin-block-end: var(--s2); }
.stack-item p { color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.7; }

.numbered { display: grid; gap: var(--s5); margin: 0; padding: 0; list-style: none; counter-reset: n; }
.numbered li {
  counter-increment: n;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s4);
  align-items: start;
}
.numbered li::before {
  content: counter(n);
  display: grid;
  place-items: center;
  inline-size: 30px;
  block-size: 30px;
  border-radius: 50%;
  border: 1px solid var(--border-accent);
  background: var(--accent-soft);
  color: var(--mint);
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}
.numbered p { color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.7; }

.callout {
  padding: var(--s5);
  border: 1px solid var(--border-accent);
  border-radius: var(--r-lg);
  background: var(--accent-soft);
  color: var(--text);
}
.callout p { line-height: 1.75; }


/* 18. Brand essence ======================================================= */
/* The three propositions from the brand profile. Iconography follows the
   asset pack's designs, drawn as inline SVG rather than using its PNGs — those
   are upscales from a raster brand board and blur at any usable size. */

/* Not a third row of identical boxes.
   "How it works" is a sequence and is drawn as one. These three are a SET of
   qualities with no order, so they take a different form entirely: an
   editorial list, hairline-separated, the phrase carrying the weight and the
   explanation set beside it. Rendering both sections as three bordered cards
   made the page read as one repeated component and gave the reader nothing to
   tell them apart. */
.essence {
  display: grid;
  gap: 0;
  border-block-start: 1px solid var(--border);
}

.essence__item {
  display: grid;
  gap: var(--s3) var(--s7);
  align-items: start;
  padding-block: var(--s6);
  border-block-end: 1px solid var(--border);
}
@media (min-width: 880px) {
  .essence__item {
    grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
    padding-block: var(--s7);
  }
  /* Placed explicitly. Left to auto-flow, the three children fill the two
     columns in DOM order — icon, then heading beside it, then the paragraph
     wrapping onto the next row under the icon — which puts the phrase on the
     right and its explanation on the left, the opposite of the reading order
     the layout is for. */
  .essence__icon        { grid-column: 1; grid-row: 1; }
  .essence__item h3     { grid-column: 1; grid-row: 2; }
  .essence__item p      { grid-column: 2; grid-row: 1 / span 2; }
}

/* Small, unframed and inline with the phrase. A 58px box with a border was
   most of what made these read as cards. */
.essence__icon {
  display: inline-flex;
  color: var(--mint);
}
.essence__icon svg { inline-size: 26px; block-size: 26px; }

.essence__item h3 {
  font-size: var(--fs-h3-lg);
  line-height: 1.25;
  margin-block: var(--s2) 0;
  text-wrap: balance;
}
.essence__item p {
  color: var(--text-muted);
  font-size: var(--fs-lead-sm);
  line-height: 1.75;
  max-inline-size: 52ch;
  margin: 0;
}


/* The privacy link under each form.
   A bare inline link measured 16px tall, which the touch-floor test caught —
   and it is a link people tap on a phone before handing over their details, so
   it earns a real target rather than an exemption. */
.form-note a {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  color: var(--text-muted);
  text-decoration-color: var(--accent-line);
  text-underline-offset: 3px;
}
.form-note a:hover { color: var(--text); }
.form-note a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* The privacy page's dateline belongs with the lead, not crushed against it. */
.page-hero .sub { margin-block-start: var(--s4); color: var(--text-faint); }

/* ==========================================================================
   18. Motion
   --------------------------------------------------------------------------
   Everything here is transform and opacity only, so nothing triggers layout,
   and every rule in this section is switched off by the reduced-motion block
   above. Motion is used to say something — an element arriving, a control
   acknowledging a press, the brand arc drawing itself — and nowhere purely
   for decoration.
   ========================================================================== */

/* Press feedback. A control that does not acknowledge a tap feels broken on a
   phone long before anything has had time to load. 0.98, not 0.95: these are
   wide targets, and a deeper scale reads as a lurch rather than a press. */
/* `transition` is a shorthand, so re-declaring it replaces the whole list. An
   earlier version of this rule listed .btn as well and silently dropped the
   --ease-spring the button was authored with 900 lines above — the press
   feedback that shipped was not the one anyone had tuned. .btn keeps its own. */
.card, .step, .lang-toggle button {
  transition:
    transform var(--dur-fast) var(--ease-out),
    background-color var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out);
}
.btn:active, .lang-toggle button:active { transform: scale(0.98); }

/* The section divider draws itself when it comes into view.
   The arc is the brand's load-bearing shape; having it simply appear wastes the
   one moment it is being introduced. Scale on the X axis only, from the centre
   out, so it reads as being drawn rather than growing. */
.js .arc-rule::after {
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--dur-slow) var(--ease-out);
}
.js .arc-rule.is-visible::after { transform: scaleX(1); }

/* The step markers land after the line they sit on, so the sequence reads as
   the line being strung and then pinned rather than everything at once. */
.js .reveal .step__num {
  transform: scale(0.82);
  opacity: 0;
  transition:
    transform var(--dur) var(--ease-spring) 120ms,
    opacity var(--dur) var(--ease-out) 120ms;
}
.js .reveal.is-visible .step__num { transform: scale(1); opacity: 1; }

/* Quiet links settle rather than snap.
   The arrow is deliberately NOT re-declared here: section 7 already owns it,
   including the RTL mirror, and a second declaration of the same selector wins
   on source order and silently drops the scaleX(-1) — which flipped the arrow
   180 degrees on hover in Arabic, animating through zero width to do it. One
   owner per property. */
.footer-col a, .prose a, .form-note a {
  transition: color var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out);
}

/* Print ====================================================================
   The reveal is written to fail open, and it does for the cases it was written
   for: no JS, no IntersectionObserver, never scrolled. Print is the one path
   that slips through. A visitor who loads the page and scrolls once disarms the
   six-second failsafe, and printing after that does not re-run JS or fire
   another scroll — so every .reveal still below the viewport prints at opacity
   0, on a PDF the visitor keeps.

   End states only. Nothing here animates; it simply asserts the finished frame.
   ========================================================================== */
@media print {
  .js .reveal { opacity: 1 !important; transform: none !important; }
  .js .arc-rule::after { transform: scaleX(1) !important; }
  .js .reveal .step__num { opacity: 1 !important; transform: none !important; }
  .js .reveal .launch-mark__trail { stroke-dashoffset: 0 !important; }
  .js .reveal .launch-mark__dot { opacity: 1 !important; transform: none !important; animation: none !important; }
  .js .reveal .launch-mark span { clip-path: none !important; }
  /* The hero figure draws itself on load; on paper it is simply drawn. */
  .cycle__earned { stroke-dashoffset: 0 !important; animation: none !important; }
  .cycle__now, .cycle__dot { opacity: 1 !important; animation: none !important; }
}

/* 17. Reduced motion ====================================================== */

/* Everything decorative stops. Content still arrives, instantly, because the
   reveal transition is what is removed — not the end state. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .js .reveal { opacity: 1; transform: none; }
  /* The end states, kept; only the travel to them is dropped. */
  .js .arc-rule::after { transform: scaleX(1); }
  .js .reveal .step__num { transform: none; opacity: 1; }
  .btn:active, .lang-toggle button:active { transform: none; }
  .link-quiet:hover .icon-flip { transform: none; }
  .js .reveal .launch-mark__trail { transition: none; stroke-dashoffset: 0; }
  .js .reveal .launch-mark__dot { transform: scale(1); opacity: 1; animation: none; }
  .js .reveal .launch-mark span,
  html[dir="rtl"].js .reveal .launch-mark span { clip-path: none; transition: none; }
  .launch-mark__comet, .launch-mark__ring { animation: none; }
  .cycle__earned, .cycle__now { transition: none; stroke-dasharray: 64 100; }
  .cycle__now { stroke-dasharray: 0.01 64 0.01 100; }

  /* The app stage. initStage() bails out under reduced motion before it writes
     a single transform, so the phones are already flat and the fan never runs.
     This is the belt to that braces: it also covers a stale inline transform
     left behind if the user turns the preference on mid-visit. */
  .stage, .phone { transform: none !important; }
  .phone { transition: none; }
}
