/* ============================================================================
   نهنگ مهاجر — the landing page at migrantwhale.com
   ----------------------------------------------------------------------------
   ▶ THE TOKENS BELOW ARE COPIED FROM web/src/styles/tokens.css, AND THAT IS A
     DELIBERATE EXCEPTION TO THE "ONE FILE, BECAUSE TWO COPIES DRIFT" RULE.

     This page is a separate deployable on a separate hostname with no build
     step — it cannot import from the Vite app, and giving it a build step to
     share four dozen custom properties would cost more than it saves. What
     makes the copy safe is that the values carrying a MEASURED constraint are
     the ones annotated here: --muted and --grad-strong both exist at their
     current values because they failed WCAG AA at their previous ones. If this
     file and tokens.css ever disagree, tokens.css is right.

   ▶ NO JAVASCRIPT AT ALL. Not minimalism for its own sake: the reader arrives
     from an SMS, on a phone, on an Iranian mobile connection, and often with
     the page competing against a slow DNS lookup. Every byte here is HTML, CSS
     and four font files. There is nothing to hydrate, so there is no layout
     shift and nothing to wait for.

   ▶ Logical properties throughout EXCEPT transform, which is physical by
     definition — the same rule app.css follows. `inset-inline-start` resolves
     to the right edge under dir="rtl"; `translateX` never does.
   ========================================================================= */

/* ---------- fonts ---------------------------------------------------------- */
/* THREE files, subsetted by brand/subset_fonts.py to the glyphs this page
   actually sets. Dana is the VARIABLE file, so every weight from 10 to 990 is
   reachable out of ONE download rather than one download per weight — which is
   why 500 and 600 appear below with no face of their own.
   Morabba is here at 700 and 800 only; the other four weights it ships are not
   referenced by this stylesheet and so are not served. */
@font-face {
  font-family: 'Dana';
  src: url('/assets/fonts/DanaVF.5aabee1f.woff2') format('woff2-variations');
  font-weight: 10 990;
  font-style: normal;
  font-display: swap;
}
@font-face { font-family: 'Morabba'; src: url('/assets/fonts/Morabba-Bold.c0184d0c.woff2') format('woff2');       font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: 'Morabba'; src: url('/assets/fonts/Morabba-ExtraBold.25153b34.woff2') format('woff2');  font-weight: 800; font-style: normal; font-display: swap; }

:root {
  --font-display: 'Morabba', 'Dana', system-ui, sans-serif;
  --font-text: 'Dana', system-ui, sans-serif;

  --primary: #C5A1CB;
  --primary-light: #E8D5EC;
  --primary-lighter: #F3EAF5;
  --primary-dark: #9B6FA3;
  --primary-darker: #7A4F82;

  --bg: #FAFAFE;
  --surface: #FFFFFF;
  --surface-2: #F8F4FA;
  --text: #2D1F33;
  --text-2: #6B5A72;
  /* Not #9A8BA0. That measured 3.07–3.19:1 and failed AA everywhere it was used
     as text; this is the same hue, darker, at 4.55 on --surface-2. */
  --muted: #786C7D;
  --border: #E8DEF0;
  --border-light: #F0E8F4;

  /* White text on flat --primary measures 2.25:1 — the worst contrast in the
     product. Darkening the TEXT does not fix it (the ramp is too wide and dark
     text then fails at the far stop); shifting the ramp into the lower half of
     the same purple holds at both ends, 4.50 and 6.43. Only for white-on-
     gradient. --primary stays a surface tint. */
  --grad-strong: linear-gradient(135deg, #926899 0%, #7A4F82 100%);

  --warn-bg: #FDF8EC;
  --warn-border: #F0DFB4;
  --warn-text: #8A6A32;

  --shadow-sm: 0 2px 8px rgba(155, 111, 163, .08);
  --shadow-md: 0 4px 20px rgba(155, 111, 163, .12);
  --shadow-lg: 0 10px 40px rgba(155, 111, 163, .16);

  --r-sm: 10px;
  --r-md: 14px;
  --r-lg: 20px;
  --r-xl: 28px;
  --r-full: 999px;

  --ease: cubic-bezier(.22, .61, .36, 1);
  --wrap: 1120px;
  --safe-b: env(safe-area-inset-bottom, 0px);
}

/* ---------- reset ---------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-text);
  /* 17px, not 16. Persian's identity lives in small letter shapes — سـ against
     شـ, حـ against جـ against خـ — so it needs more size than an equivalent
     Latin setting to stay legible. */
  font-size: 17px;
  line-height: 2;
  /* Persian is a CONNECTED script. Letter-spacing breaks the joins, so it is
     pinned at zero rather than left to a framework default. */
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 { font-family: var(--font-display); font-weight: 800; letter-spacing: 0; margin: 0; }
p { margin: 0; }
img, svg { max-width: 100%; }
a { color: inherit; }

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: clamp(20px, 5vw, 40px); }
.wrap-narrow { max-width: 760px; }

/* Phosphor, at the pack's own 256 viewBox. `currentColor` throughout — the one
   thing an emoji cannot do, which is why there are none on this page. */
.i { width: 1em; height: 1em; fill: currentColor; flex-shrink: 0; display: block; }
[dir="rtl"] .i-dir { transform: scaleX(-1); }

/* ---------- buttons -------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 52px;
  padding-inline: 26px;
  border-radius: var(--r-full);
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), background-color .2s var(--ease);
}
.btn .i { font-size: 1.15em; }

.btn-primary {
  background: var(--grad-strong);
  color: #fff;
  box-shadow: var(--shadow-md);
}
.btn-primary:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
.btn-primary:active { transform: translateY(0); }

.btn-lg { min-height: 60px; padding-inline: 34px; font-size: 1.12rem; }

.btn-ghost {
  min-height: 42px;
  padding-inline: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--primary-darker);
  font-size: .94rem;
}
.btn-ghost:hover { background: var(--primary-lighter); border-color: var(--primary-light); }

:where(a, .btn):focus-visible {
  outline: 3px solid var(--primary-dark);
  outline-offset: 3px;
}

/* ---------- header --------------------------------------------------------- */
.topbar {
  position: sticky; inset-block-start: 0; z-index: 10;
  background: rgba(250, 250, 254, .82);
  backdrop-filter: blur(12px);
  border-block-end: 1px solid var(--border-light);
}
.topbar-in { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 68px; }

.brand { display: inline-flex; align-items: center; gap: 11px; font-family: var(--font-display); font-weight: 700; }
.brand-mark { border-radius: 22%; display: block; }
.brand-name { font-size: 1.06rem; }

/* ---------- hero ----------------------------------------------------------- */
.hero {
  position: relative;
  padding-block: clamp(56px, 11vw, 104px) clamp(48px, 9vw, 88px);
  overflow: hidden;
}
/* The wash, as a pseudo-element rather than a background image: it is one
   gradient, it costs no request, and it cannot fail to load. */
.hero::before {
  content: "";
  position: absolute; inset-block-start: -30%; inset-inline: -10%;
  block-size: 130%;
  background:
    radial-gradient(ellipse 60% 50% at 50% 22%, rgba(197, 161, 203, .30), transparent 70%),
    radial-gradient(ellipse 40% 40% at 82% 8%, rgba(155, 111, 163, .13), transparent 70%);
  pointer-events: none;
}
.hero-in { position: relative; text-align: center; }

.eyebrow {
  display: inline-block;
  margin-block-end: 22px;
  padding: 7px 18px;
  border-radius: var(--r-full);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  font-size: .86rem;
  font-weight: 500;
  color: var(--primary-darker);
}

.hero h1 {
  margin: 0 auto 22px;
  /* ▶ NO `max-width` HERE, DELIBERATELY, AND IT USED TO BE THE BUG.

     It was `20ch`. The h1 is two phrases and `em` is `display: block`, so each
     phrase is its own line box; the intent is one phrase per line. The longer
     phrase («جوابش در اعماق اقیانوسی از تجربه‌هاست.») needs about 17× the
     font-size in px — measured, not guessed — which at the 56px display size is
     ~955px. `20ch` is ~615px, so BOTH phrases wrapped and the hero rendered as
     four lines.

     Widening the cap looks like the fix and is not: `.wrap` is already 1120px
     with padding, so from 1024px down the CONTAINER is narrower than any cap
     worth setting, and a `max-width` there is simply inert. Two overlapping
     width constraints where one of them never binds is how the real cause
     (the font-size below) stays hidden. `.wrap` governs the width. */

  /* ▶ 5vw, NOT 6.2vw, AND THE COEFFICIENT IS DERIVED FROM THE COPY.

     Phrase 2 needs 17.055× the font-size in px to stay on one line — measured
     on the real element at `white-space: nowrap`, not estimated. At 6.2vw the
     headline outran its container at EVERY width from 1024px down, i.e. the
     whole tablet and small-laptop range. 1024px missed by less than a pixel,
     which is exactly why it looked right on a large monitor and wrong on a
     smaller one, and why widening a `max-width` appeared to fix it.

     ▶ THE SCROLLBAR IS IN THIS NUMBER, AND IT IS THE EASY 2px TO LOSE.
     `vw` resolves against the viewport INCLUDING the classic scrollbar; the
     layout container excludes it. A page with content taller than the window
     therefore lays out ~15-17px narrower than every `vw` in this file believes.
     The first attempt at this fix used 5.2vw — correct to the arithmetic,
     2px short in a real browser, still wrapping at 768px.

     So the constraint is `17.055 × font <= 0.9vw - 17`, whose tightest point is
     ~5.11vw at the small end of the fluid range. 5vw sits just under it.

     ▶ RE-DERIVE THIS IF THE HEADLINE CHANGES. Measure the longer phrase at
     `nowrap` and a known size, divide to get the ratio, then solve the line
     above. A longer headline needs a smaller coefficient, and nothing here will
     tell you it has gone wrong — the page simply renders three lines.

     Below ~610px it wraps regardless, and that is correct rather than a
     shortfall: two lines at 320px would need a 17px hero. There the floor
     (1.95rem) wins and `balance` decides how it breaks. The ceiling stays
     3.5rem — 61px would fit at 1120px+, but the design wants an unbroken
     headline, not a bigger one. */
  font-size: clamp(1.95rem, 5vw, 3.5rem);
  /* Persian ascenders and descenders overlap at display sizes; 1.45 is the
     tightest this can go before «مهاجرتی‌ات» collides with the line under it. */
  line-height: 1.45;
  color: var(--text);
  /* Narrow enough and a phrase still wraps — nothing can prevent that at 320px.
     `balance` decides HOW: two even lines rather than a full line and one
     orphaned word. Inherited, so it applies to the `em` and to the anonymous
     block around the first phrase separately, which is what we want: each
     phrase balances within itself. Unsupported browsers ignore it and get
     today's behaviour. */
  text-wrap: balance;
}
.hero h1 em {
  display: block;
  font-style: normal;
  background: linear-gradient(135deg, #9B6FA3 0%, #7A4F82 60%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.lead {
  margin: 0 auto;
  max-width: 58ch;
  font-size: clamp(1rem, 2.4vw, 1.12rem);
  line-height: 2.1;
  color: var(--text-2);
}

.cta-row {
  margin-block-start: 34px;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
}
.cta-note { font-size: .88rem; color: var(--muted); }

/* ▶ A NOWRAP BUTTON IS A BUTTON THAT WILL EVENTUALLY OVERFLOW THE PAGE.
   `.btn` sets `white-space: nowrap`, which is right when there is room and
   wrong when there is not. The hero CTA
   («از اعماق اقیانوس تجربه‌ها پرس» plus the arrow) is intrinsically 360px, so
   on a 320-390px phone it pushed the DOCUMENT wider than the viewport and the
   whole page scrolled sideways — in RTL, where the “home” edge is the right
   one, that is especially disorienting.

   Below this width the label is allowed to wrap instead. A two-line button is
   ordinary on a phone; a horizontally scrolling page is not. Desktop is
   untouched, and no copy changed — the words are the owner's.

   430px, not 390px: 390 is where it happened to start overflowing with TODAY'S
   label. One word longer and the same bug returns at a wider phone, so the
   breakpoint sits above the whole common phone range instead of at the edge of
   the current failure. */
@media (max-width: 430px) {
  .btn { white-space: normal; text-align: center; }
}

/* ---------- stats ---------------------------------------------------------- */
.stats {
  margin: clamp(44px, 8vw, 68px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1px;
  background: var(--border-light);
  border: 1px solid var(--border-light);
  border-radius: var(--r-lg);
  overflow: hidden;
}
.stats > div { background: var(--surface); padding: 22px 12px; }
.stats dt { font-size: .84rem; color: var(--muted); order: 2; }
.stats dd {
  margin: 0; order: 1;
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(1.35rem, 4.4vw, 1.85rem);
  color: var(--primary-darker);
  line-height: 1.4;
}
.stats > div { display: flex; flex-direction: column; }
.stats-note { margin-block-start: 14px; font-size: .84rem; color: var(--muted); }

@media (min-width: 720px) { .stats { grid-template-columns: repeat(4, 1fr); } }

/* ---------- bands ---------------------------------------------------------- */
.band { padding-block: clamp(58px, 10vw, 104px); }
.band-tint { background: var(--surface-2); }

.h2 {
  margin: 0 auto;
  max-width: 24ch;
  text-align: center;
  font-size: clamp(1.45rem, 4.4vw, 2.1rem);
  line-height: 1.6;
  color: var(--primary-darker);
}
.h2-sub {
  margin: 18px auto 0;
  max-width: 62ch;
  text-align: center;
  color: var(--text-2);
  font-size: 1rem;
}

/* ---------- cards ---------------------------------------------------------- */
.grid-3 {
  margin-block-start: clamp(34px, 6vw, 54px);
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
}
@media (min-width: 860px) { .grid-3 { grid-template-columns: repeat(3, 1fr); } }

/* Four peers read as 2x2, never as 3 + 1 with a hole beside the orphan. */
.grid-4 {
  margin-block-start: clamp(34px, 6vw, 54px);
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
}
@media (min-width: 860px) { .grid-4 { grid-template-columns: repeat(2, 1fr); } }

.card {
  padding: clamp(24px, 3.4vw, 32px);
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  transition: transform .25s var(--ease), box-shadow .25s var(--ease);
}
@media (hover: hover) {
  .card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
}
.card h3 { margin-block: 16px 10px; font-size: 1.12rem; font-weight: 700; color: var(--text); line-height: 1.7; }
.card p { color: var(--text-2); font-size: .97rem; }
.card em { font-style: normal; font-weight: 700; color: var(--primary-darker); }

.card-ic {
  display: grid; place-items: center;
  inline-size: 46px; block-size: 46px;
  border-radius: var(--r-md);
  background: var(--primary-lighter);
  color: var(--primary-darker);
  font-size: 24px;
}
/* On a tinted badge the icon has to step along the SAME ramp, not lift off it:
   a translucent white wash here computed to almost exactly the panel's own
   value and the badge disappeared. */
.ic-warn { background: var(--warn-bg); border: 1px solid var(--warn-border); color: var(--warn-text); }

.card-accent { border-color: var(--primary-light); background: linear-gradient(180deg, #fff 0%, var(--primary-lighter) 100%); }

/* ---------- the sample answer --------------------------------------------- */
.answer-demo { margin-block-start: clamp(34px, 6vw, 52px); display: flex; justify-content: center; }

.demo-card {
  position: relative;
  width: 100%; max-width: 760px;
  padding: clamp(24px, 4vw, 40px);
  border-radius: var(--r-xl);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
}
.demo-tag {
  position: absolute;
  inset-block-start: 18px; inset-inline-end: 18px;
  padding: 4px 12px;
  border-radius: var(--r-full);
  background: var(--primary-lighter);
  color: var(--primary-darker);
  font-size: .74rem; font-weight: 600;
}
/* The reader's OWN question, so it hugs the side the script starts from — the
   right, under dir="rtl" — exactly where the chat app puts it.

   The margin therefore goes on the inline-END side: `inline-start` resolves to
   the RIGHT edge here, so setting it there pushed the bubble left and made the
   reader's message look like the assistant's. Same for the notched corner:
   `start-start` is top-right under RTL, which is the corner a right-hugging
   bubble points from. */
.demo-q {
  margin-block-end: 26px;
  margin-inline-end: clamp(0px, 18vw, 180px);
  padding: 14px 20px;
  border-radius: var(--r-lg);
  border-start-start-radius: 6px;
  background: var(--grad-strong);
  color: #fff;
  font-size: .98rem;
}

/* ▶ FOUR PEERS, BUILT IDENTICALLY. No panel on any of them, and no per-section
   tint. Each one had a defensible local reason for its own container once, and
   the SET had none: three containers for three parts of one reply reads as
   three widgets that happened to land together. What distinguishes them is the
   icon, the title and the content — never the box. */
.part { padding-block: 20px; border-block-end: 1px solid var(--border-light); }
.part-last { border-block-end: 0; padding-block-end: 0; }

.part-head { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; }
.part-ic {
  display: grid; place-items: center;
  inline-size: 32px; block-size: 32px;
  border-radius: var(--r-sm);
  background: var(--primary-lighter);
  color: var(--primary-darker);
  font-size: 18px;
}
.part-head h3 { font-size: 1.02rem; font-weight: 700; color: var(--text); }
.chip {
  padding: 3px 11px;
  border-radius: var(--r-full);
  background: var(--surface-2);
  border: 1px solid var(--border-light);
  color: var(--muted);
  font-size: .76rem;
  line-height: 1.9;
}
.part-body { margin-block-start: 10px; color: var(--text-2); font-size: .95rem; }

/* ---------- chips list ----------------------------------------------------- */
.chips {
  margin: clamp(28px, 5vw, 40px) 0 0;
  padding: 0; list-style: none;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
}
.chips li {
  padding: 9px 20px;
  border-radius: var(--r-full);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  font-size: .94rem;
  font-weight: 500;
  color: var(--text);
}
.chips .chip-alt { background: var(--primary-lighter); border-color: var(--primary-light); color: var(--primary-darker); }

.note {
  margin-block-start: 26px;
  text-align: center;
  color: var(--text-2);
  font-size: .95rem;
}
.note em { font-style: normal; font-weight: 700; color: var(--primary-darker); }

/* ---------- ticks ---------------------------------------------------------- */
.ticks { margin: clamp(28px, 5vw, 40px) 0 0; padding: 0; list-style: none; display: grid; gap: 14px; }
/* Flex with a real icon, replacing an absolutely-positioned CSS checkmark.
   That shape mixed LOGICAL borders with a PHYSICAL transform, so it rendered
   mirrored under dir="rtl" — see the note in index.html. Laying the row out as
   flex also means the badge can never overlap the text at any width, which the
   fixed 60px inline-start padding could not promise. */
.ticks li {
  display: flex;
  align-items: flex-start;
  gap: 15px;
  padding: 22px 26px;
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  color: var(--text-2);
  font-size: .97rem;
}
.tick-ic {
  flex-shrink: 0;
  display: grid; place-items: center;
  inline-size: 30px; block-size: 30px;
  /* Optical, not arithmetic: the badge aligns to the first LINE's cap height,
     not to the top of a 2em-line-height text box. */
  margin-block-start: 3px;
  border-radius: var(--r-full);
  background: var(--primary-lighter);
  color: var(--primary-darker);
  font-size: 17px;
}
.ticks strong { display: block; color: var(--text); font-weight: 700; }

/* ---------- closer --------------------------------------------------------- */
.closer {
  padding-block: clamp(64px, 11vw, 112px);
  background:
    radial-gradient(ellipse 70% 90% at 50% 0%, rgba(197, 161, 203, .26), transparent 70%),
    var(--bg);
  border-block-start: 1px solid var(--border-light);
}
.closer-in { text-align: center; }
.closer-mark { border-radius: 22%; box-shadow: var(--shadow-md); }
.closer h2 {
  margin-block: 24px 14px;
  font-size: clamp(1.4rem, 4.6vw, 2.05rem);
  line-height: 1.6;
  color: var(--primary-darker);
}
.closer p { margin-block-end: 30px; color: var(--text-2); }

/* ---------- footer --------------------------------------------------------- */
.foot {
  padding-block: 34px calc(34px + var(--safe-b));
  background: var(--surface-2);
  border-block-start: 1px solid var(--border);
}
.foot-in { display: grid; gap: 16px; justify-items: center; text-align: center; }
.foot-note { max-width: 62ch; color: var(--muted); font-size: .87rem; line-height: 1.95; }
.foot-link {
  color: var(--primary-dark);
  font-size: .9rem;
  text-decoration: none;
  /* A Latin run inside an RTL block: correct bidi puts it LTR on its own, but
     it is isolated explicitly so a neighbouring dot or dash cannot be pulled
     into it and reorder the host name on screen. */
  unicode-bidi: isolate;
  direction: ltr;
}
.foot-link:hover { text-decoration: underline; }

@media (min-width: 720px) {
  .foot-in { grid-template-columns: auto 1fr auto; align-items: center; text-align: start; justify-items: start; }
  .foot-link { justify-self: end; }
}
