/* SAVA — the landing page.
 *
 * Everything on this page is a token. The previous version chose every number
 * locally and it showed: ten distinct border-radii, five distinct section gaps
 * with no common divisor, and exactly one elevation, so nothing read as an
 * object sitting on a surface. Nobody consciously sees that; everybody feels it.
 *
 * The scales below are the whole design. If a value is not in this block it
 * should be, and the exceptions are commented where they occur.
 */

:root {
  /* ------------------------------------------------------------- colour
   *
   * CHANNELS FIRST, COLOURS DERIVED. Every brand colour is declared as three
   * numbers — `20 29 38` — and the usable colour is built from them. It reads
   * like an indirection for its own sake and it is the only thing that makes
   * the palette switchable at all: this file is full of tints and glows at 6%,
   * 18%, 55% of the accent, and `rgba(197, 31, 93, .18)` is a pink that
   * survives a switch to the orange palette. There is no way to take an alpha
   * of `var(--accent)` in CSS that older Android WebViews understand, so the
   * channels are published and `rgb(var(--accent-rgb) / .18)` does it.
   *
   * `rgb(R G B / A)` — space-separated, slash for alpha — has been in every
   * engine since 2020. `color-mix()` and relative colour syntax would be
   * tidier and are years newer; this site's readers are on Iranian mobile
   * connections and whatever browser came with the handset.
   *
   * THE VALUES BELOW ARE ONLY THE FALLBACK. They are `sava_dark` from
   * backend/app/theme.py, to the digit, and they are what paints when nobody
   * has published a palette yet — or when the published one cannot be served.
   *
   * A published palette arrives as /theme.css, a second `:root` block linked
   * after this file and therefore winning on cascade order. It is a static file
   * written by the panel and served off disk, so it is render-blocking like any
   * stylesheet and the document's FIRST paint is already in the right colours.
   * No JavaScript is involved and nothing here ever flashes from one palette to
   * another. */
  --bg-rgb:          20 29 38;
  --surface-rgb:     27 38 52;
  --surface-high-rgb: 36 52 71;
  --accent-rgb:      197 31 93;    /* brand pink — actions only */
  --accent-2-rgb:    232 68 111;   /* lighter, the far end of every gradient */
  --accent-deep-rgb: 122 16 56;    /* the accent at depth. NOT a second hue. */
  --on-accent-rgb:   255 255 255;  /* what is legible ON the accent */
  --cream-rgb:       226 226 210;  /* also the source of every hairline */
  --text-rgb:        226 226 210;
  --muted-rgb:       139 154 171;
  --like-rgb:        76 195 138;
  --like-soft-rgb:   139 217 143;
  --nope-rgb:        226 68 92;

  --bg:            rgb(var(--bg-rgb));
  --surface:       rgb(var(--surface-rgb));
  --surface-high:  rgb(var(--surface-high-rgb));
  --accent:        rgb(var(--accent-rgb));
  --accent-2:      rgb(var(--accent-2-rgb));
  --accent-deep:   rgb(var(--accent-deep-rgb));
  --on-accent:     rgb(var(--on-accent-rgb));
  --cream:         rgb(var(--cream-rgb));
  --text:          rgb(var(--text-rgb));
  --muted:         rgb(var(--muted-rgb));
  --like:          rgb(var(--like-rgb));
  --like-soft:     rgb(var(--like-soft-rgb));
  --nope:          rgb(var(--nope-rgb));
  --hairline:      rgb(var(--cream-rgb) / .10);
  --hairline-top:  rgb(var(--cream-rgb) / .18);

  /* --------------------------------------------------------- elevation */
  /* One light source, above and slightly forward. A card is lit along its top
     edge (the inset highlight) and occludes what is behind it (the outer
     shadow). A single flat hairline on all four sides describes a world with no
     light in it, which is what this page had — every "card" was --surface on
     --bg at 1.10:1 with a 1.32:1 border, which is not a card, it is text
     floating on the page. */
  --e1: 0 1px 2px rgba(0, 0, 0, .35);
  --e2: 0 10px 28px -12px rgba(0, 0, 0, .60), 0 2px 6px rgba(0, 0, 0, .28);
  --e3: 0 36px 72px -28px rgba(0, 0, 0, .75), 0 10px 24px -10px rgba(0, 0, 0, .45);
  /* WHITE, not `--cream`. This is a specular highlight — the top edge of a
     surface catching the light — and light is light whichever palette is
     published. Derived from cream it inverted on the light palette into a grey
     smudge across the top of every card, because there `--cream` is the ink
     colour. Hairlines below DO follow cream, and correctly: a border is the
     edge of an object, so it belongs to the ink, and it wants to be pale on a
     dark ground and dark on a pale one. */
  --lit: inset 0 1px 0 rgb(255 255 255 / .07);

  /* ----------------------------------------------------------- spacing */
  --s1: 4px;  --s2: 8px;   --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px;  --s8: 64px; --s9: 96px;
  --section-y: clamp(72px, 9vw, 128px);
  --gutter:    clamp(20px, 4vw, 32px);

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

  /* -------------------------------------------------------------- type */
  /* The whole stack, in one place, because `:lang(en) body` below prepends a
     digits-only family to it and two hand-copied stacks would drift. */
  --face: "IRANSansX", "Vazirmatn", "Segoe UI", Tahoma, -apple-system, sans-serif;

  /* Desktop ramp: 68 / 44 / 24 / 17 / 15 / 13. The jump from h2 to h3 is
     deliberate and large — the old page had section headings at 16-17px sitting
     over 15-17px body text, separated only by a synthesised bold, which is why
     the cards read as undifferentiated grey text. */
  --fs-xs:   13px;
  --fs-sm:   15px;
  --fs-body: 17px;
  --fs-lead: clamp(17px, .8vw + 14px, 21px);
  --fs-h3:   clamp(19px, .6vw + 16px, 24px);
  --fs-h2:   clamp(27px, 2.4vw + 15px, 44px);
  --fs-h1:   clamp(34px, 4.4vw + 14px, 68px);
}

/* --------------------------------------------------------------- the face
 *
 * IRANSansXFaNum, the face both apps ship, subset to three weights totalling
 * 77 KB — less than one of the screenshots. This file used to argue for the
 * system stack on the grounds that a 400 KB Persian webfont is too much to ask
 * of an Iranian mobile connection. The premise was right and the conclusion was
 * wrong: the fix is to make the font small, not to do without it.
 *
 * Persian rendered in the OS fallback is a *naskh* face, so the headline sat
 * under a geometric wordmark in a completely different typographic voice, and
 * every bold was synthesised — which on connected Arabic script smears the
 * joins. `font-synthesis: none` guarantees we never fake a cut we did not ship.
 */
@font-face {
  font-family: "IRANSansX";
  src: url("/assets/fonts/iransansx-regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IRANSansX";
  src: url("/assets/fonts/iransansx-bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "IRANSansX";
  src: url("/assets/fonts/iransansx-black.woff2") format("woff2");
  font-weight: 900; font-style: normal; font-display: swap;
}

/* ----------------------------------------------------- digits, in English
 *
 * The face above is the FaNum cut, and FaNum means its character map points
 * U+0030–U+0039 — the ASCII digits — at the PERSIAN glyphs. On the Persian
 * page that is exactly what you want and is half of why this font was chosen.
 * On the English page it is a bug, and a loud one: «7 days free, no card» came
 * out as «۷ days free», `SHA-256` as `SHA-۲۵۶`, the APK's size as «۴۶٫۸ MB»
 * and the footer's year as «۲۰۲۶» — Persian numerals inside English sentences,
 * which reads as mojibake rather than as a translation.
 *
 * There is no OpenType feature to switch off, because the substitution is in
 * the cmap and not in a lookup. The fix is to put ONE MORE family in front of
 * it that owns nothing but the ten digits, so English digits resolve there and
 * every other character still falls through to IRANSansX.
 *
 * `local()` only, no download: this costs nothing on any connection, and the
 * digits of a system UI face next to IRANSansX's Latin are close enough that
 * nobody will see the seam. If none of the names below exist the @font-face
 * simply fails to match and the stack falls through to IRANSansX — i.e. back
 * to today's behaviour, never to a blank.
 *
 * Scoped by USE rather than by declaration, since @font-face has no scope: it
 * enters the stack only under `:lang(en)` below. The Persian page keeps its
 * Persian numerals, including the ones inside «اثرانگشت SHA-256». */
@font-face {
  font-family: "LatinDigits";
  src: local("Helvetica Neue"), local("Helvetica"), local("Arial"),
       local("Segoe UI"), local("Roboto"), local("DejaVu Sans");
  unicode-range: U+0030-0039;
}

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

html {
  /* `clip`, not `hidden`: `overflow-x: hidden` on body makes body a scroll
     container, which breaks `position: sticky` on descendants and smooth
     scrolling in Safari. Nothing here needs a scroll container. */
  overflow-x: clip;
  /* The header is sticky, so an anchor target would otherwise land under it. */
  scroll-padding-block-start: 96px;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--face);
  font-synthesis: none;
  font-size: var(--fs-body);
  -webkit-font-smoothing: antialiased;
}
/* English gets one extra family in front, and it owns only the digits. See the
   note beside `@font-face LatinDigits`. Written as a prefix of `--face` rather
   than as a second stack, so the two can never drift apart. */
:lang(en) body { font-family: "LatinDigits", var(--face); }

/* Persian and Latin do not want the same metrics out of the same box. Persian
   carries ascenders and descenders the Latin ramp never budgets for, so a
   leading that is comfortable in one is cramped or gappy in the other. */
:lang(fa) body { line-height: 1.85; }
:lang(en) body { line-height: 1.6; }

h1, h2, h3 { margin: 0; text-wrap: balance; }
p { margin: 0; text-wrap: pretty; }

h1 { font-size: var(--fs-h1); font-weight: 900; }
h2 { font-size: var(--fs-h2); font-weight: 900; }
h3 { font-size: var(--fs-h3); font-weight: 700; }

:lang(fa) h1 { line-height: 1.34; } :lang(en) h1 { line-height: 1.04; }
:lang(fa) h2 { line-height: 1.38; } :lang(en) h2 { line-height: 1.15; }
:lang(fa) h3 { line-height: 1.55; } :lang(en) h3 { line-height: 1.30; }

/* Negative tracking on Arabic script is not "tight" — it pulls the connected
   forms into each other and in several renderers breaks the joins outright.
   Persian has no case either, so any uppercase treatment is Latin-only. */
:lang(en) h1 { letter-spacing: -.02em; }
:lang(en) h2 { letter-spacing: -.015em; }
:lang(fa) h1, :lang(fa) h2, :lang(fa) h3 { letter-spacing: 0; }

a { color: inherit; }
img { max-width: 100%; height: auto; display: block; }

/* `hidden` is only `display: none` at the UA level, so any later `display` rule
   beats it — and the install buttons and pending rows are all flex. */
[hidden] { display: none !important; }

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

.skip {
  position: absolute; inset-block-start: -120px; inset-inline-start: var(--s4);
  z-index: 60; background: var(--accent); color: var(--on-accent);
  padding: var(--s3) var(--s5); border-radius: var(--r-md);
  font-size: var(--fs-sm); font-weight: 700; text-decoration: none;
}
.skip:focus { inset-block-start: var(--s3); }

.wrap {
  max-width: 1140px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ------------------------------------------------------------------ light
 *
 * ONE source, centred. `left: 50%` is deliberate and is the one place in this
 * file that must NOT be a logical property: the previous version anchored the
 * glow with `inset-inline-end`, so pressing EN moved the room's lighting to the
 * other side of the screen. A room does not relight itself when you change
 * language. Two blobs in two corners was two light sources, which is why the
 * page read as gradient wallpaper rather than as something lit.
 */
.aurora {
  position: fixed; inset: 0; z-index: -2;
  overflow: hidden; pointer-events: none;
}
.aurora::before {
  content: ""; position: absolute;
  top: -46vh; left: 50%; translate: -50% 0;
  width: min(1500px, 160vw); aspect-ratio: 1;
  background: radial-gradient(closest-side,
    rgb(var(--accent-rgb) / .30), rgb(var(--accent-rgb) / .10) 46%, rgb(var(--accent-rgb) / 0) 72%);
  filter: blur(50px);
}
.aurora::after {                       /* the floor, darkening toward the footer */
  content: ""; position: absolute; inset-block-end: 0; inset-inline: 0; height: 60vh;
  background: linear-gradient(to top, rgba(0, 0, 0, .40), transparent);
}

/* A blurred wash across a near-black field bands into visible rings on any 8-bit
   panel, and banding is one of the loudest "cheap" signals on a dark page. One
   inline SVG turbulence, no request. */
body::after {
  content: ""; position: fixed; inset: 0; z-index: -1;
  pointer-events: none; opacity: .035; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>");
}

/* ----------------------------------------------------------------- header */

.bar {
  position: sticky; inset-block-start: 0; z-index: 50;
  border-block-end: 1px solid transparent;
  background: transparent;
  transition: background .3s, border-color .3s, backdrop-filter .3s;
}
.bar.stuck {
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(16px) saturate(1.25);
  border-block-end-color: var(--hairline);
}
@supports not (backdrop-filter: blur(1px)) {
  .bar.stuck { background: var(--bg); }
}

.bar-inner { display: flex; align-items: center; gap: var(--s5); min-height: 68px; }
.brand { display: flex; align-items: center; text-decoration: none; flex: none; }
/* The lockup is a transparent PNG now, so it sits ON the aurora rather than on a
   flat tile of its own. The old JPEGs were drawn on a solid #151D27, and once
   the wash is composited the page behind them is not that colour — they read as
   dark rectangles pasted onto a mauve page, which was the single most visible
   defect on the site. */
.brand img { width: auto; height: 28px; }

/* ------------------------------------------------------------- the wordmark
 *
 * TINTED, NOT PAINTED, so the logo follows the palette. A pink wordmark left
 * sitting on an orange page is the one thing that would make a published
 * palette look broken rather than merely different.
 *
 * The PNG's ink is a single colour with the counters and the two eyes as
 * transparent cut-outs — checked, not assumed — which makes it a usable alpha
 * mask. Three things have to happen together:
 *
 *  1. `mask-image` clips the element to the letterforms.
 *  2. `background-color` paints `--accent` through that clip.
 *  3. `content` swaps the bitmap for an image that draws nothing. Without this
 *     the element still paints its own pink pixels ON TOP of the background —
 *     a replaced element draws its content over its background box — and the
 *     result is the original logo masked by its own silhouette, i.e. no visible
 *     change at all. Easy to leave out, and invisible on the one palette the
 *     logo was drawn for.
 *
 * An EMPTY SVG, not the usual 1x1 transparent GIF. The GIF stores transparency
 * as a palette index over a white pixel, and stretched across the box it
 * rendered as opaque white — so the wordmark came out white on every palette,
 * which looks like a deliberate choice rather than a bug. An SVG with no
 * children has nothing to paint and no palette to misread.
 *
 * The replacement has no intrinsic ratio, so `width: auto` would collapse;
 * `aspect-ratio` restores the wordmark's own 680:297.
 *
 * Scoped to @supports so that a browser without masks keeps the untouched pink
 * PNG — wrong colour on a non-pink palette, but a legible logo rather than an
 * empty box. Both prefixes: Safari only stopped needing -webkit- at 15.4.
 */
@supports ((-webkit-mask-image: url("/assets/wordmark-flat.png")) or
           (mask-image: url("/assets/wordmark-flat.png"))) {
  .brand img,
  .foot-brand img {
    content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E");
    aspect-ratio: 680 / 297;
    background-color: var(--accent);
    -webkit-mask: url("/assets/wordmark-flat.png") center / contain no-repeat;
            mask: url("/assets/wordmark-flat.png") center / contain no-repeat;
  }
}

.bar-nav { display: none; gap: var(--s5); font-size: var(--fs-sm); }
.bar-nav a { color: var(--muted); text-decoration: none; transition: color .2s; }
.bar-nav a:hover { color: var(--text); }
@media (min-width: 880px) { .bar-nav { display: flex; } }

.bar-right { display: flex; align-items: center; gap: var(--s2); margin-inline-start: auto; }
/* Shown at every width, including the phone. It used to be `display: none`
   below 620px, which left the sticky bar on the one surface most of the traffic
   arrives at holding nothing but a language toggle. */
.bar-cta {
  display: inline-flex; align-items: center;
  min-height: 40px;
  padding-inline: 14px; border-radius: var(--r-sm);
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: var(--on-accent); text-decoration: none; white-space: nowrap;
  font-size: var(--fs-xs); font-weight: 700;
  box-shadow: var(--e1);
  transition: background .2s;
}
@media (min-width: 560px) {
  .bar-cta { padding-inline: 18px; font-size: var(--fs-sm); }
}
/* Brightened rather than swapped for a second, lighter gradient. The hand-picked
   hover pair only existed because the accent was a constant; now that it comes
   from the panel, a hardcoded lighter pink is a hover state that stops matching
   its own button the moment somebody publishes a different palette. */
@media (hover: hover) and (pointer: fine) {
  .bar-cta:hover { filter: brightness(1.1); }
}

.lang {
  min-height: 40px; display: inline-flex; align-items: center; padding-inline: 16px;
  background: var(--surface-high); color: var(--text);
  border: 1px solid var(--hairline); border-radius: var(--r-sm);
  font: inherit; font-size: var(--fs-xs); font-weight: 700;
  cursor: pointer; transition: border-color .2s;
}
.lang:hover { border-color: var(--accent-2); }

/* ------------------------------------------------------------------- hero */

main > section { padding-block: var(--section-y); }
main > section + section { padding-block-start: 0; }   /* one gap, not two */

.hero { padding-block-start: clamp(36px, 5vw, 64px); }
.hero-inner {
  display: grid; gap: clamp(48px, 6vw, 72px);
  grid-template-columns: 1fr; align-items: center;
}
@media (min-width: 920px) {
  .hero-inner { grid-template-columns: minmax(0, 1.06fr) minmax(0, .8fr); }
}

.eyebrow {
  display: inline-flex; align-items: center; gap: var(--s2);
  margin-block-end: var(--s4);
  font-size: var(--fs-xs); font-weight: 700; color: var(--accent-2);
}
.eyebrow::before { content: ""; width: 22px; height: 1px; background: currentColor; }
:lang(en) .eyebrow { letter-spacing: .09em; text-transform: uppercase; }
:lang(fa) .eyebrow { letter-spacing: 0; }

/* `em`, not `ch`. `ch` is the advance of "0", and Persian text at 18ch runs about
   a third longer than Latin at 18ch — so the measure was silently different in
   each language. */
.hero h1 { max-width: 13em; }

.lede {
  margin-block-start: var(--s5);
  max-width: 34em; font-size: var(--fs-lead); color: var(--muted);
}

/* ------------------------------------------------------------- installs */

.installs {
  display: flex; flex-direction: column; align-items: stretch;
  gap: var(--s3); margin-block-start: var(--s7); max-width: 420px;
}

/* CENTRED, not start-aligned. These are stretched to a common 420px so they
   stack as one block, and the labels are nowhere near that wide — start-aligned
   left the Android button with 193px of empty gradient at one end, which reads
   as a button that failed to load its second half. Centring also hides the fact
   that only the bundled Android row carries an icon: the store buttons the
   panel adds are label-only, and start-aligned they lined up with nothing. */
.install {
  display: flex; align-items: center; justify-content: center; gap: var(--s3);
  padding: 15px 22px; border-radius: var(--r-md);
  background: var(--surface-high);
  border: 1px solid var(--hairline);
  border-block-start-color: var(--hairline-top);
  color: var(--text); text-decoration: none;
  box-shadow: var(--e1), var(--lit);
  transition: transform .2s cubic-bezier(.16, 1, .3, 1), box-shadow .2s, background .2s;
}
.install.primary {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  border-color: transparent; color: var(--on-accent);
  /* Contact shadow plus a tint, not the 30px pink halo this had before. */
  box-shadow: inset 0 1px 0 rgb(var(--on-accent-rgb) / .18),
              0 8px 20px -8px rgb(var(--accent-rgb) / .55),
              0 2px 4px rgba(0, 0, 0, .3);
}
.install-icon { display: flex; flex: none; opacity: .95; }
/* `text-align` as well as the flex centring above: the two lines are different
   lengths, and centring only the pair would leave the shorter one hanging off
   one edge of the longer. */
.install-lines { display: flex; flex-direction: column; gap: 2px; min-width: 0; text-align: center; }
.install-lines > span:first-child { font-weight: 700; font-size: var(--fs-sm); }
.install-sub { font-size: var(--fs-xs); opacity: .8; }

/* Guarded: on iOS an unguarded hover transform sticks after a tap until the
   reader touches something else. */
@media (hover: hover) and (pointer: fine) {
  .install:hover { transform: translateY(-2px); box-shadow: var(--e2), var(--lit); }
  .install.primary:hover {
    filter: brightness(1.1);
    box-shadow: inset 0 1px 0 rgb(var(--on-accent-rgb) / .22),
                0 14px 28px -10px rgb(var(--accent-rgb) / .6);
  }
}
.install:active { transform: translateY(0) scale(.99); }

/* Not a button — a status line. A greyed-out button is a promise with a date on
   it; and an absent one reads as "does not exist", which is how every iPhone
   visitor concluded the app was Android-only. */
/* No inline padding. It used to carry the button's 22px so its text lined up
   with a button label, but the label is centred now — and the 22px was putting
   these dots on a different start edge from the `.trust` ticks directly below
   them, which is two columns of status text in one stack, indented differently
   for no reason a reader can see. */
.pending {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: 11px 0; font-size: var(--fs-sm); color: var(--muted);
}
/* An 8px mark on a 13px grid. The dot is deliberately smaller than the `.trust`
   ticks below — "still coming" should be quieter than "you get this" — but it
   has to sit on the same axis as them, so it takes the same 13px slot and is
   painted into the middle of it rather than filling it. */
.pending .dot {
  flex: none; width: 13px; height: 13px;
  margin-block-start: .42em;                 /* same first-line rule as `.trust` */
  background: radial-gradient(circle at 50% 50%, var(--accent-2) 0 4px, transparent 4.5px);
  animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

.install-note {
  margin-block-start: var(--s4);
  font-size: var(--fs-sm); color: var(--muted); max-width: 34em;
}

.trust {
  list-style: none; margin: var(--s6) 0 0; padding: 0;
  display: flex; flex-direction: column; gap: var(--s3);
  font-size: var(--fs-sm); color: var(--muted);
}
/* Top-aligned, not centred. On a phone these lines wrap, and a centred mark on
   a two-line item drifts into the gap between the lines — pointing at nothing,
   and out of step with the single-line items above and below it. Nudged down by
   roughly half a line so it sits on the first line rather than on its cap. */
.trust li { display: flex; align-items: flex-start; gap: var(--s3); }
/* The eye out of the wordmark, carried as a system rather than left trapped
   inside three raster images. */
.trust li::before {
  content: ""; flex: none; width: 13px; height: 13px; border-radius: 50%;
  margin-block-start: .42em;
  background: var(--accent-2);
  box-shadow: inset 0 0 0 3.5px var(--bg), inset -1px -1px 0 5.5px var(--accent-2);
}

/* --------------------------------------------------------------- devices */

.phone {
  position: relative;
  width: min(300px, 72vw);
  padding: 10px; border-radius: 44px;
  background: linear-gradient(160deg, var(--surface-high), var(--surface));
  border: 1px solid rgb(var(--cream-rgb) / .16);
  box-shadow: var(--e3), inset 0 1px 0 rgb(255 255 255 / .10);
}
.phone-lg { width: min(340px, 78vw); }
/* Concentric: outer radius = inner radius + padding. */
/* The last three literals in this file, and they are deliberate: the island,
   the screen behind a screenshot, and the glass reflection are a piece of
   HARDWARE, not brand colour. A phone's cutout is black on a pink palette and
   black on an orange one, and tying them to `--bg` would tint the handset
   itself every time somebody published a palette. */
.phone::before {                      /* the island */
  content: ""; position: absolute; inset-block-start: 22px; left: 50%;
  translate: -50% 0; width: 88px; height: 25px; border-radius: var(--r-full);
  background: #05080B; z-index: 2;
}
.phone::after {                       /* glass specular */
  content: ""; position: absolute; inset: 10px; border-radius: 34px;
  pointer-events: none; z-index: 1;
  background: linear-gradient(118deg, rgba(255, 255, 255, .10), rgba(255, 255, 255, 0) 38%);
}
/* Bottom-anchored crop. Every screenshot carries the iOS status bar — 15:26 and
   a green 77% battery — and #34C759 was the only green pixel on the whole site,
   sitting inside a hand-drawn bezel. This eats the top few percent and keeps the
   tab bar, and it survives panel-uploaded shots of any aspect. */
.phone-screen {
  position: relative; overflow: hidden; border-radius: 34px;
  aspect-ratio: 507 / 1035; background: #0C1219;
}
.phone-screen img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 100%; }

.hero-device { display: flex; justify-content: center; }
@media (min-width: 920px) {
  .hero-device { justify-content: flex-end; }
  /* Rotation is NOT a logical property — it has to be mirrored by hand, or the
     device leans the wrong way in one of the two languages. */
  .hero-device .phone { rotate: -3.5deg; }
  [dir="rtl"] .hero-device .phone { rotate: 3.5deg; }
}

/* ---------------------------------------------------------- section heads */

.sec-head { max-width: 40em; margin-block-end: clamp(32px, 4vw, var(--s7)); }
.sub { margin-block-start: var(--s3); color: var(--muted); font-size: var(--fs-lead); }

/* ----------------------------------------------------------------- cards */

.card {
  background:
    linear-gradient(180deg, rgb(255 255 255 / .05), rgb(255 255 255 / 0) 42%),
    var(--surface-high);
  border: 1px solid var(--hairline);
  border-block-start-color: var(--hairline-top);
  border-radius: var(--r-lg);
  box-shadow: var(--e2), var(--lit);
  padding: clamp(20px, 2.2vw, 28px);
  transition: transform .25s cubic-bezier(.16, 1, .3, 1), box-shadow .25s, border-color .25s;
}
@media (hover: hover) and (pointer: fine) {
  .card:hover {
    transform: translateY(-3px);
    box-shadow: var(--e3), inset 0 1px 0 rgb(255 255 255 / .10);
    border-color: rgb(var(--accent-2-rgb) / .28);
  }
}
.card h3 { margin-block-end: var(--s3); }
.card p { color: var(--muted); font-size: var(--fs-sm); }

/* ----------------------------------------------------------------- proof */

.taste-card {
  margin: 0; border-radius: var(--r-xl);
  padding: clamp(24px, 3vw, 40px);
  background:
    linear-gradient(180deg, rgb(var(--accent-rgb) / .14), rgb(var(--accent-rgb) / .03) 55%),
    var(--surface);
  border: 1px solid rgb(var(--accent-2-rgb) / .26);
  box-shadow: var(--e3), inset 0 1px 0 rgb(var(--accent-2-rgb) / .18);
}
.taste-head {
  display: flex; align-items: center; gap: var(--s2);
  font-size: var(--fs-h3); font-weight: 700; color: var(--cream);
  margin-block-end: var(--s5);
}
.spark { color: var(--accent-2); }
.taste-card blockquote { margin: 0; font-size: var(--fs-lead); color: var(--text); }
:lang(fa) .taste-card blockquote { line-height: 2.05; }
.taste-note {
  margin-block-start: var(--s5); padding-block-start: var(--s5);
  border-block-start: 1px solid var(--hairline);
  font-size: var(--fs-xs); color: var(--muted);
}

/* ----------------------------------------------------------------- steps */

.steps ol {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--s5); grid-template-columns: 1fr;
  counter-reset: step;
}
@media (min-width: 780px) { .steps ol { grid-template-columns: repeat(3, 1fr); } }
.steps li { counter-increment: step; }
.steps .n {
  display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: 50%;
  margin-block-end: var(--s4);
  background: var(--accent); color: var(--on-accent);
  font-weight: 900; font-size: var(--fs-sm);
  box-shadow: inset 0 0 0 4px var(--bg), 0 0 0 1px rgb(var(--accent-2-rgb) / .45);
}
/* Was hard-coded as ۱ ۲ ۳ in the markup, so an English reader saw Persian
   numerals inside Latin cards. `persian` is a real CSS counter style. */
.steps .n::before { content: counter(step); }
:lang(fa) .steps .n::before { content: counter(step, persian); }

/* -------------------------------------------------------------- features */

/* THE PROPORTIONS HERE ARE THE POINT, and they were wrong: this section ran to
   2618px — a third of the entire page — to make three claims of one sentence
   each, because every row paired two lines of text with a 630px-tall handset.
   Scrolling it felt like three empty screens in a row.
   Fixed in three places: a smaller device in these rows (the hero keeps the big
   one, which is where a hero device belongs), less padding between rows, and no
   padding above the first one — its own section heading is already there and
   the two gaps stacked into 120px of nothing. */
.feature {
  display: grid; gap: clamp(32px, 5vw, 64px); align-items: center;
  grid-template-columns: 1fr;
  padding-block: clamp(28px, 3vw, 44px);
}
.feature:first-of-type { padding-block-start: 0; }
.feature figure { margin: 0; display: flex; justify-content: center; }
.feature .phone { width: min(248px, 66vw); }
@media (min-width: 920px) {
  .feature { grid-template-columns: minmax(0, 1fr) minmax(0, .62fr); }
  /* `order` resolves against the writing direction, so this alternates correctly
     in both languages without a second rule. */
  .feature:nth-of-type(even) .feature-text { order: 2; }
  .feature:nth-of-type(even) .feature-shot { order: 1; justify-content: flex-start; }
}
.feature-text p {
  margin-block-start: var(--s4);
  color: var(--muted); font-size: var(--fs-lead); max-width: 30em;
}

.minor, .cmp-grid, .faq-list {
  display: grid; gap: var(--s5); grid-template-columns: 1fr;
}
@media (min-width: 720px) {
  .minor { grid-template-columns: repeat(2, 1fr); }
  .cmp-grid { grid-template-columns: repeat(3, 1fr); }
}
.minor { margin-block-start: var(--s6); }

/* ------------------------------------------------------------ subscription */

/* What an account includes. It replaced three price cards — prices live on the
   signed-in account page now, beside the button that charges them — so this is
   deliberately a list and not a grid of cards: a card implies a thing you pick
   between, and there is nothing to pick between here.

   `.plan`, `.price` and `.badge` below are still built by the ACCOUNT page's
   script, which is why they stay; only the three-column grid that laid them out
   on THIS page is gone. */
/* `list-style` and `padding` are reset here and not by accident: `display: grid`
   does NOT stop an <li> generating the UA's own marker, so without this every
   item carried TWO bullets — the browser's disc and the accent dot drawn below —
   and the whole list sat 40px inside its own column on the UA's list padding.
   `.trust` above resets both for the same reason; this list did not. */
.incl {
  list-style: none;
  margin: var(--s6) 0 0;
  padding: 0;
  display: grid; gap: var(--s4); grid-template-columns: 1fr;
  max-width: 46em;
}
@media (min-width: 720px) { .incl { grid-template-columns: repeat(2, 1fr); } }
.incl li {
  position: relative;
  padding-inline-start: 1.6em;
  color: var(--muted); font-size: var(--fs-lead); line-height: 1.7;
}
/* Drawn rather than a list-marker, so it can take the accent and sit on the
   first line's baseline in both writing directions. */
.incl li::before {
  content: "";
  position: absolute; inset-block-start: .62em; inset-inline-start: 0;
  width: 7px; height: 7px; border-radius: var(--r-full);
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
}

.plan { position: relative; text-align: center; }
.plan.best { border-color: rgb(var(--accent-2-rgb) / .38); }
.badge {
  position: absolute; inset-block-start: -11px; inset-inline-start: 50%;
  translate: 50% 0;
  padding: 4px 14px; border-radius: var(--r-full);
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: var(--on-accent); font-size: 12px; font-weight: 700; white-space: nowrap;
}
[dir="ltr"] .badge { translate: -50% 0; }
.plan h3 { font-size: var(--fs-sm); font-weight: 700; color: var(--muted); }
.price { margin-block-start: var(--s3); }
.price b { font-size: clamp(28px, 3vw, 38px); font-weight: 900; color: var(--cream); }
.price span { font-size: var(--fs-sm); color: var(--muted); }
.was { margin-inline-start: var(--s2); font-size: var(--fs-sm); color: var(--muted);
       text-decoration: line-through; opacity: .75; }
.per { margin-block-start: var(--s2); font-size: var(--fs-xs); color: var(--muted); }
.price-note {
  margin-block-start: var(--s5); font-size: var(--fs-sm); color: var(--muted);
  max-width: 46em;
}

/* Buying, directly, without installing anything first. Set apart from the plan
   cards rather than repeated inside each of them: three identical buttons in a
   row asks for three decisions at once, and the plan is chosen on the next page
   anyway. */
.price-cta {
  margin-block-start: var(--s6);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4);
}
.btn-buy {
  display: inline-flex; align-items: center; min-height: 52px;
  padding-inline: var(--s6); border-radius: var(--r-sm);
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: var(--on-accent); text-decoration: none;
  font-size: var(--fs-sm); font-weight: 700;
  box-shadow: var(--e2);
  transition: filter .2s;
}
@media (hover: hover) and (pointer: fine) {
  .btn-buy:hover { filter: brightness(1.08); }
}
.price-cta-note { font-size: var(--fs-sm); color: var(--muted); max-width: 30em; }

/* The quiet door for somebody who already has an account. Hidden on the
   narrowest phones, where the bar has to hold the install button and the
   language toggle and nothing else will fit. */
.bar-signin {
  display: none; color: var(--muted); text-decoration: none;
  font-size: var(--fs-sm); padding-inline: var(--s2);
  transition: color .2s;
}
.bar-signin:hover { color: var(--text); }
@media (min-width: 560px) { .bar-signin { display: inline-flex; align-items: center; } }

/* ------------------------------------------------------------------- FAQ */

.faq-list details { padding: 0; }
.faq-list summary {
  cursor: pointer; list-style: none;
  padding: clamp(18px, 2vw, 24px);
  font-size: var(--fs-h3); font-weight: 700;
  display: flex; align-items: center; gap: var(--s3);
}
.faq-list summary::-webkit-details-marker { display: none; }
/* PHYSICAL borders, deliberately, and one of the few places in this file where
   a logical property is the wrong tool. Two adjacent borders rotated 45° make a
   chevron pointing at the corner they share; `border-inline-end` in RTL is the
   LEFT edge, so on the Persian page — which is the default page — the caret
   pointed sideways and read as a back arrow rather than as "opens downward".
   A chevron that means "down" means down in both writing directions, so it is
   pinned to right+bottom and left alone. */
.faq-list summary::after {
  content: ""; flex: none; margin-inline-start: auto;
  width: 10px; height: 10px;
  border-right: 2px solid var(--accent-2);
  border-bottom: 2px solid var(--accent-2);
  rotate: 45deg; transition: rotate .2s;
}
.faq-list details[open] summary::after { rotate: -135deg; }
.faq-list details p {
  padding: 0 clamp(18px, 2vw, 24px) clamp(18px, 2vw, 24px);
  color: var(--muted); font-size: var(--fs-sm); max-width: 52em;
}

/* ------------------------------------------------------------------- CTA */

.cta-inner {
  border-radius: var(--r-xl);
  padding: clamp(36px, 5vw, 64px);
  text-align: center;
  background:
    linear-gradient(180deg, rgb(var(--accent-rgb) / .18), rgb(var(--accent-deep-rgb) / .06) 60%),
    var(--surface);
  border: 1px solid rgb(var(--accent-2-rgb) / .22);
  box-shadow: var(--e3), inset 0 1px 0 rgb(var(--accent-2-rgb) / .16);
}
.cta-inner p {
  margin-block-start: var(--s4); color: var(--muted);
  font-size: var(--fs-lead); max-width: 38em; margin-inline: auto;
}
.cta-inner .installs { margin-inline: auto; }

/* "Download an APK from a website" is, correctly, a red flag. This is what a
   careful Iranian visitor checks before trusting it, and publishing it costs us
   nothing. */
.verify {
  margin-block-start: var(--s7);
  padding: clamp(20px, 2.4vw, 28px);
  border: 1px dashed var(--hairline);
  border-radius: var(--r-lg);
}
.verify h3 { font-size: var(--fs-sm); color: var(--muted); font-weight: 700; }
.verify dl { margin: var(--s4) 0 0; display: grid; gap: var(--s3); font-size: var(--fs-xs); }
.verify dl > div { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); }
.verify dt { color: var(--muted); min-width: 8em; }
.verify dd { margin: 0; color: var(--text); }
.verify code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; word-break: break-all;
  background: var(--surface); padding: 2px 7px; border-radius: 6px;
}

/* ---------------------------------------------------------------- footer */

.foot {
  border-block-start: 1px solid var(--hairline);
  padding-block: var(--s8);
  background: rgba(0, 0, 0, .18);
}
.foot-inner {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s5); justify-content: space-between;
  font-size: var(--fs-sm); color: var(--muted);
}
.foot-brand { display: flex; align-items: center; gap: var(--s3); }
.foot-brand img { width: auto; height: 22px; }
.foot nav { display: flex; flex-wrap: wrap; gap: var(--s5); }
.foot a { text-decoration: none; transition: color .2s; }
.foot a:hover { color: var(--text); }
.copy { font-size: var(--fs-xs); }

/* ---------------------------------------------------------------- motion */

.reveal { opacity: 0; transform: translateY(22px); }
.reveal.in { opacity: 1; transform: none; }
.reveal {
  transition: opacity .5s cubic-bezier(.16, 1, .3, 1),
              transform .65s cubic-bezier(.16, 1, .3, 1);
  /* Staggered per item, set by the script. `ease` on everything simultaneously
     read as sluggish-then-abrupt, and every card in a grid fired at once because
     they all entered the viewport together. */
  transition-delay: calc(var(--i, 0) * 70ms);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
}
