/* SyxVPN — the public page.
 *
 * One stylesheet for both languages. No CDN and no third-party script: this
 * page is read from networks where fonts.googleapis.com is a gamble, and a
 * page that waits on a blocked host is a page nobody sees.
 *
 * The one font it does load is served from this same origin, so it is exactly
 * as reachable as the page asking for it — see the @font-face below.
 *
 * Near-black ground and one lime accent, used as light rather
 * than as paint. Four treatments carry the whole page, and every one of them is
 * a gradient — nothing here downloads an image:
 *
 *   .arc     a wide flat band with a bright thin edge, as if a lit sphere were
 *            rising past the bottom of the section above it.
 *   .lit     a card with that light spilling over its own top corner rather
 *            than clipped by it. Clipped, it is a gradient painted on a panel;
 *            spilling, the panel is an object with something bright behind it.
 *   .label   a section's name in wide-tracked capitals, a rule running out from
 *            either side.
 *   .chip    a round icon well with a lime rim.
 *
 * The light has to fall away fast. Spread over a card's full width and half its
 * height it stops reading as light and becomes a muddy olive wash, which is the
 * one failure mode this look has.
 */
:root {
  --bg: #0a0a0a;
  --surface: #141414;
  --surface-high: #1b1b1b;
  --border: #262626;
  --text: #f4f4f4;
  --dim: #909090;
  --faint: #6a6a6a;
  --accent: #d8f83a;
  --accent-ink: #0e1400;
  /* The pale end of the accent, for light rather than for paint. There was
     a warmer gold here doing that job; two accents on one page meant the
     price, the ticks and the glow on every card read as a second brand. */
  --accent-soft: #eafc9a;
  --radius: 26px;
  --max: 1120px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

/* Persian, in a face drawn for Persian.
 *
 * The stack used to start with system-ui, which meant the Persian text was
 * always drawn by whatever the device happened to default to — Tahoma on
 * Windows, something else on Android — and Vazirmatn, sitting fourth, was never
 * reached even on a machine that had it. A stack is not a preference list if
 * the first entry always matches.
 *
 * So the face is served rather than hoped for. Three things keep that honest:
 *
 *   it is on this origin, not a CDN, so it is reachable wherever the page is;
 *   `unicode-range` is Persian only, so the English page never downloads it and
 *     Latin keeps the system's own grotesque, which is what the design wants;
 *   `swap` means the text is readable from the first paint in the fallback and
 *     changes face when the file lands — never invisible while waiting.
 *
 * One variable file, 48KB, every weight from 100 to 900. Vazirmatn 33.0.3,
 * SIL Open Font License 1.1 — the licence ships beside it in public/fonts/.
 */
@font-face {
  font-family: "Vazirmatn Web";
  src: url("/fonts/vazirmatn-nl-var.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  /* Arabic script, Arabic-Indic and Persian digits, presentation forms — plus
     U+200C, the zero-width non-joiner, which Persian cannot be set without. */
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+200C-200F,
                 U+FB50-FDFF, U+FE70-FEFF;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* The face is asked for on the Persian page only. `unicode-range` alone was not
   enough: the English page carries the word فارسی in its language switcher, and
   a browser downloads a 48KB file for one nav link as readily as for a whole
   page of text. There, that word can have the system's own Arabic fallback.

   Persian also sits lower and looser than Latin at the same size: set to a
   Latin line height its descenders and its dots collide. Corrected here too,
   so both belong to the same rule and cannot drift apart. */
[dir="rtl"] body {
  font-family: "Vazirmatn Web", system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.85;
}
[dir="rtl"] h1, [dir="rtl"] h2 { line-height: 1.45; letter-spacing: 0; }
[dir="rtl"] .card h3, [dir="rtl"] .plan-head .term { letter-spacing: 0; }
/* The head of a price card sits on the leading edge, so the light belongs on
   the other one — which swaps sides with the direction. */
[dir="rtl"] .plan::before { --gx: 12%; }
[dir="rtl"] .plans .plan:nth-child(2)::before { --gx: 18%; }
[dir="rtl"] .plans .plan:nth-child(3)::before { --gx: 6%; }

a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }

.wrap { width: 100%; max-width: var(--max); margin: 0 auto; padding: 0 20px; }

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

.top {
  position: sticky; top: 0; z-index: 40;
  background: rgba(10, 10, 10, .82);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}
.top .wrap { position: relative; display: flex; align-items: center; gap: 16px; height: 68px; }
/* The mark alone: the name is no longer set as type beside it. The glow is the
   one from the reference icon, in the site's accent rather than its blue. */
.brand { display: flex; align-items: center; gap: 11px; }
/* The name in capitals beside the mark, in white rather than the accent — the
   mark is already the accent, and two of them side by side would leave nothing
   for the eye to land on first. */
.brand-name {
  font-size: 13.6px; font-weight: 700; color: var(--text);
  letter-spacing: .07em; white-space: nowrap;
  /* Latin either way, so it keeps its own direction on the RTL page. */
  direction: ltr;
  line-height: 1;
  /* Centring a flex item centres its line box, and capitals never reach the
     bottom of theirs — the space below the baseline is for descenders they do
     not have — so the name rides high on the tile. This trims the box to the
     capitals themselves, top and bottom, and then `align-items: center` is
     centring the ink. It reads the figure out of the font, which is the only
     way this can be right in both places: the fallback below was measured
     against this machine's font, and a phone renders SYX VPN in a different
     one with a different cap height. */
  text-box: trim-both cap alphabetic;
}
@supports not (text-box: trim-both cap alphabetic) {
  .brand-name { transform: translateY(1.92px); }
  @media (max-width: 359px) { .brand-name { transform: translateY(.33px); } }
}
@media (max-width: 359px) { .brand-name { font-size: 12px; } .brand { gap: 8px; } }
}
.brand svg { display: block; }
/* Light around the tile rather than a line drawn on it: two shadows, a tight
   one for the edge and a wide one for the bloom. */
.brand .mark {
  filter: drop-shadow(0 0 4px rgba(216, 248, 58, .55)) drop-shadow(0 0 13px rgba(216, 248, 58, .3));
}
.brand:hover .mark {
  filter: drop-shadow(0 0 5px rgba(216, 248, 58, .7)) drop-shadow(0 0 18px rgba(216, 248, 58, .42));
}
.nav { display: flex; gap: 24px; margin-inline-start: auto; align-items: center; }
.nav a { color: var(--dim); font-size: 14px; }
.nav a:hover { color: var(--text); }
.nav .btn { padding: 10px 20px; font-size: 14px; }

/* Language switcher: a <details>, so it opens with JavaScript off. */
.langsel { position: relative; margin-inline-start: 4px; }
/* Where the menu button is showing, the switcher sits closer to it — the nav's
   24px gap leaves the two floating apart at the end of a phone's header. */
@media (max-width: 860px) { .langsel { margin-inline-end: -12px; } }
.langsel > summary {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  padding: 3px 2px; font-size: 12px; color: var(--dim);
  list-style: none; user-select: none;
}
.langsel > summary svg { width: 14px; height: 14px; }
.langsel > summary::-webkit-details-marker { display: none; }
.langsel[open] > summary, .langsel > summary:hover { color: var(--text); }
.langsel > summary:focus { outline: none; }
.langsel > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 8px; }

.langsel .menu, .menubtn .menu {
  position: absolute; inset-inline-end: 0; top: calc(100% + 10px);
  min-width: 168px; padding: 6px;
  background: var(--surface-high);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
  z-index: 30;
  transform-origin: top var(--menu-origin, right);
}
/* The header bar is not mirrored on the Persian page. Everything else in the
   page flips, but the mark and the menu are the two things a reader reaches for
   without looking, and moving them to the opposite corner buys nothing: both
   are symbols, not text. So the bar itself stays LTR — mark on the left, menu
   on the right — and only the words inside it come back to RTL. */
[dir="rtl"] .top .wrap { direction: ltr; }
[dir="rtl"] .top .langsel > summary,
[dir="rtl"] .top .langsel .menu a,
[dir="rtl"] .top .menubtn .menu a { direction: rtl; }
/* With the bar upright again, its menus open from the right like the LTR page's. */
[dir="rtl"] .top .langsel .menu, [dir="rtl"] .top .menubtn .menu { --menu-origin: right; }

[dir="rtl"] .langsel .menu, [dir="rtl"] .menubtn .menu { --menu-origin: left; }
.langsel[open] .menu, .menubtn[open] .menu { animation: menu-in .16s cubic-bezier(.2, .7, .3, 1); }
@keyframes menu-in {
  from { opacity: 0; transform: translateY(-6px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
.langsel .menu a, .menubtn .menu a {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 10px; border-radius: 9px;
  font-size: 14px; line-height: 1.3; color: var(--dim); white-space: nowrap;
}
.langsel .menu a:hover, .menubtn .menu a:hover { background: var(--surface); color: var(--text); }
.langsel .menu a[aria-current="true"] { color: var(--accent); }
.langsel .menu .tick { width: 14px; text-align: center; opacity: 0; }
.langsel .menu a[aria-current="true"] .tick { opacity: 1; }

.menubtn { position: static; border: 0; background: none; padding: 0; display: none; }
.menubtn > summary {
  display: flex; align-items: center; justify-content: center;
  /* 20% up from 36. line-height: 0 so no stray text baseline can push the
     glyph off centre inside the square. */
  /* The same tile as the mark beside it: 42px, and a radius of 11.34 because
     the mark's rounded rect is rx 27 in a 100 viewBox drawn at 42. */
  width: 42px; height: 42px; border-radius: 11.34px; line-height: 0;
  /* No outline on the tile — the same light the mark throws, in the same two
     shadows: a tight one for the edge, a wide one for the bloom. The border
     stays as a transparent hairline so the box keeps its measured size. */
  border: 1px solid transparent; background: #0d0d0d;
  box-shadow: 0 0 4px rgba(216, 248, 58, .55), 0 0 13px rgba(216, 248, 58, .3);
  /* White, like the mark on the tile beside it. The lime is the light both
     tiles throw, not the thing drawn on them; this used to be lime on lime and
     the two tiles no longer matched once the mark turned white. Both glyphs
     inherit it, so the close cross changes with the bars. */
  color: #ffffff; cursor: pointer; list-style: none; user-select: none;
}
.menubtn > summary svg { display: block; width: 26.5px; height: 26.5px; }
.menubtn > summary::-webkit-details-marker { display: none; }
.menubtn > summary:focus { outline: none; }
.menubtn > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.menubtn[open] > summary, .menubtn > summary:hover {
  box-shadow: 0 0 5px rgba(216, 248, 58, .7), 0 0 18px rgba(216, 248, 58, .42);
}
.menubtn > summary .ico-close { display: none; }
.menubtn[open] > summary .ico-open { display: none; }
.menubtn[open] > summary .ico-close { display: block; }
.menubtn[open] .menu {
  position: fixed; inset: 68px 12px auto; min-width: 0;
  border: 1px solid var(--border); border-radius: 22px;
  background: var(--surface-high); padding: 8px;
  box-shadow: 0 26px 60px rgba(0, 0, 0, .6);
  animation: sheet-in .18s cubic-bezier(.2, .7, .3, 1);
  z-index: 60;
}
.menubtn[open] .menu a { padding: 15px 14px; border-radius: 14px; font-size: 17px; font-weight: 600; color: var(--text); }
.menubtn[open] .menu a:active { background: var(--surface); }
.menubtn[open] .menu a:last-child {
  margin-top: 6px; padding-top: 16px;
  border-top: 1px solid var(--border); border-radius: 0 0 14px 14px;
  color: var(--accent);
}
@keyframes sheet-in {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}
.top:has(.menubtn[open])::after {
  content: ""; position: fixed; inset: 68px 0 0;
  background: rgba(0, 0, 0, .6); z-index: 50;
}
.top:has(.menubtn[open]) .brand { position: relative; z-index: 70; }
html:has(.menubtn[open]) { overflow: hidden; }
@media (max-width: 860px) { .menubtn { display: block; } .nav .hide-sm { display: none; } }

/* ------------------------------------------------------------------- light */

/* The banner between the hero and the features.
 *
 * This was a gradient standing in for a lit horizon; it is now the artwork
 * itself. `width`/`height` are on the img so the row is reserved before the
 * bytes land and nothing below it jumps, and the negative margin still tucks
 * the next section under the glow the way the drawn one did. */
.arc { margin: 0; }
/* Edge to edge on a phone: no gutter and no corner radius, so the horizon runs
 * off both sides of the screen instead of sitting on a card in the middle of
 * a 412px one.
 *
 * On a wide screen it goes back in its frame. Full bleed there is 614px of one
 * band on a 1280 viewport, and cropping it to fit is not an option: the mark
 * runs from a seventh of the way down to nine tenths, so there is no middle
 * third to keep — a 340px cap took "BEYOND BORDERS" off the bottom and cut
 * through the wordmark above it.
 */
.arc img { display: block; width: 100%; height: auto; }
@media (min-width: 861px) {
  .arc { max-width: 820px; margin: 0 auto; padding: 0 20px; }
  .arc img { border-radius: 18px; }
}

.label {
  display: flex; align-items: center; gap: 18px; margin: 0 0 22px;
  color: var(--accent); font-size: 12px; font-weight: 600;
  letter-spacing: .34em; text-transform: uppercase; direction: ltr;
}
.label::before, .label::after { content: ""; flex: 1; height: 1px; background: linear-gradient(90deg, transparent, rgba(216, 248, 58, .35)); }
.label::after { background: linear-gradient(90deg, rgba(216, 248, 58, .35), transparent); }
.label.center { justify-content: center; }
.label.start::before { display: none; }
[dir="rtl"] .label.start::before { display: block; }
[dir="rtl"] .label.start::after { display: none; }

/* ------------------------------------------------------------------ typography */

section { padding: 92px 0; scroll-margin-top: 76px; }
h1 {
  font-size: clamp(40px, 8.6vw, 84px); font-weight: 300;
  line-height: 1.06; margin: 24px 0 0; letter-spacing: -.03em; text-wrap: balance;
}
/* Kept whole. Left to itself the accented phrase breaks across the line and
   leaves its last word stranded, which reads as a mistake rather than a break. */
h1 .on { color: var(--accent); white-space: nowrap; }
h2 {
  font-size: clamp(28px, 5vw, 46px); font-weight: 300;
  line-height: 1.16; margin: 0 0 16px; letter-spacing: -.025em; text-wrap: balance;
}
h3 { font-weight: 400; }
.lede { color: var(--dim); font-size: clamp(15px, 2.4vw, 18px); line-height: 1.7; max-width: 560px; margin: 26px auto 0; text-wrap: pretty; }
.sub { color: var(--dim); margin: 0 0 44px; max-width: 620px; line-height: 1.7; }
.note { color: var(--faint); font-size: 13px; margin-top: 18px; }

/* ---------------------------------------------------------------- buttons */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  border-radius: 999px; padding: 15px 32px; font-weight: 600; font-size: 15px;
  border: 1px solid transparent; cursor: pointer;
}
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { filter: brightness(1.07); }
/* The accent outline, filled with a trace of its own colour — the reference's
   second button, and the reason the pair does not read as one button and one
   link. */
.btn-accent {
  border-color: rgba(216, 248, 58, .55); color: var(--text);
  background: rgba(216, 248, 58, .07);
}
.btn-accent:hover { border-color: var(--accent); background: rgba(216, 248, 58, .13); }
.btn-ghost { background: transparent; border-color: var(--border); color: var(--text); }
.btn-ghost:hover { border-color: var(--faint); }
.btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }
/* The pair is one decision offered twice, so the two buttons are the same size.
   A flex row sizes each to its own words — "Get started" next to "See all
   plans" comes out lopsided, which reads as a main button and an afterthought.
   Two equal grid tracks hold them level at every width; the max-width keeps
   them from stretching across a desktop column. */
.cta {
  display: grid; gap: 14px; margin: 36px auto 0; max-width: 430px;
  /* Side by side, in two equal tracks. The 150px floor is the narrowest a
     button gets before a pair stops being readable — at 360px the tracks come
     out at 157 — and only below that does the row fall to one column. A long
     label ("Download for Android") takes two lines inside its track rather than
     breaking the row, which is how this pair already read and is fine. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
}
/* Narrower than a free-standing button: at 360px each track is about 160px, and
   the label needs the room more than the padding does. */
.cta > .btn { padding-inline: 18px; }
/* The one-button row in the about section, which should hug its words and sit
   against the start edge rather than stretch to the full track. */
.cta.start {
  display: flex; justify-content: flex-start; flex-wrap: wrap;
  max-width: none; margin-inline: 0;
}

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

.hero { position: relative; overflow: hidden; padding: 64px 0 72px; text-align: center; }
.hero > .wrap { position: relative; z-index: 1; }
/* The ground: a faint dot field standing in for the reference's dotted world
   map, faded out in every direction so it never becomes a texture. */
.hero::before {
  content: ""; position: absolute; inset: 0;
  background-image: radial-gradient(rgba(255, 255, 255, .16) 1px, transparent 1px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(120% 85% at 50% 12%, #000 12%, transparent 68%);
  mask-image: radial-gradient(120% 85% at 50% 12%, #000 12%, transparent 68%);
  opacity: .5; pointer-events: none;
}
.hero::after {
  content: ""; position: absolute; inset: auto 0 -46% 0; height: 540px;
  background: radial-gradient(50% 60% at 50% 100%, rgba(216, 248, 58, .22), rgba(216, 248, 58, .05) 45%, transparent 72%);
  pointer-events: none; animation: glow 9s ease-in-out infinite;
}
@keyframes glow {
  0%, 100% { opacity: .7; transform: translateY(0) scale(1); }
  50%      { opacity: 1;  transform: translateY(-14px) scale(1.04); }
}
.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--border); background: var(--surface);
  border-radius: 999px; padding: 7px 16px; font-size: 13px; color: var(--dim);
}
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }

/* ------------------------------------------------------- the proof band */

/* Big number, quiet label. Every row here has to be something a reader could
   check: this is the exact shape a page uses to say "seventy-two million
   users", and wearing that shape without the truth behind it is how a product
   starts lying before it has shipped. */
/* Tiles, in the page's own material. This row used to be a number beside a
   caption set in wide-tracked capitals — which reads as a caption only while it
   stays on one line. At 430px it wrapped to four ragged lines beside a number
   with nothing to line up against, so the row meant to look checkable looked
   like a mistake instead. Stacked in a card, in ordinary sentence case, it
   reads. */
.proof {
  display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  margin-top: 52px; text-align: start;
}
.proof-item {
  display: flex; flex-direction: column; gap: 7px;
  padding: 22px 24px;
  border: 1px solid var(--border); border-radius: 20px;
  background: var(--surface);
}
.proof-item b {
  font-size: clamp(26px, 4.2vw, 38px); font-weight: 300; letter-spacing: -.03em;
  line-height: 1.15; text-wrap: balance;
}
.proof-item span { color: var(--dim); font-size: 13px; line-height: 1.6; }

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

/* The track minimum is what decides how many cards a row holds, so it is set
 * against the real number: the section is 1080px wide inside its padding, and
 * four tracks with three 20px gaps leave (1080 - 60) / 4 = 255px each. 250px
 * clears that with room to spare, so a full row is four across on a desktop
 * and folds to two and then one on the way down. auto-fit collapses the empty
 * tracks, so the three-card row still spreads across the section instead of
 * leaving a gap where a fourth card would have been. */
.grid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr)); }

.card {
  position: relative; overflow: hidden;
  padding: 30px; min-height: 236px;
  display: flex; flex-direction: column;
  /* Not a flat fill: the surface lifts slightly at the top, which is what makes
     the glow read as light landing on glass rather than paint applied to it. */
  background: linear-gradient(180deg, #1b1b1b, #121212 62%);
  border: 1px solid var(--border); border-radius: var(--radius);
  transition: border-color .2s ease, box-shadow .2s ease;
}

/* The light inside the card.
 *
 * Measured off the reference rather than guessed, because the two things that
 * make it work are both easy to get wrong: it is saturated — an accent near
 * #d5c00d, not a pale cream, which is what a blurred white turns into and what
 * makes a knock-off of this look washed out — and it is *large*, its core
 * sitting anywhere from a fifth to half way down the card.
 *
 * It is clipped by the card. That matters: cut by the rounded corner it reads
 * as light inside a glass box, and the corner itself becomes the brightest
 * edge. (The featured price card is the one exception, and spills deliberately.)
 *
 * The core moves from card to card — right, centre, centre-left, left. Four
 * identical cards lit identically read as one card stamped four times; this is
 * the cheapest possible fix for that and the reference does exactly it.
 */
.card::before {
  content: ""; position: absolute; z-index: 0;
  /* Its own rounded top corners, matching the card's inside the 1px border.
     The card clips with overflow:hidden, which is enough in Chrome — but WebKit
     does not apply the rounded part of that clip to a composited child, and this
     one is composited because it is masked. So the light kept a square corner
     and spilled past the card's curve on iOS. Rounding the light itself is
     correct without needing anything to clip it.
     -1px because the pseudo-element is laid out against the padding box, which
     sits inside the card's border. */
  border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
  /* A corner light, and it must be spent before it reaches a word.
     The reference's cards are tall and mostly empty, so a core at 43% of their
     height still sits far above their title. These cards are shorter and
     text-dense — at 430px the headings start between 40 and 95px down — and the
     same proportions put the light squarely behind the heading, which is the one
     place it may never be. So the band is short, measured in pixels rather than
     as a fraction of a height that varies, and masked to nothing at its foot so
     there is no edge where it stops. */
  inset: 0 0 auto 0; height: 108px;
  -webkit-mask-image: linear-gradient(180deg, #000 0, #000 34%, transparent 92%);
  mask-image: linear-gradient(180deg, #000 0, #000 34%, transparent 92%);
  --gx: 92%; --gy: 30px;
  background:
    radial-gradient(24% 62% at var(--gx) var(--gy), rgba(216, 248, 58, .96), transparent 72%),
    radial-gradient(48% 116% at var(--gx) var(--gy), rgba(150, 180, 40, .34), transparent 76%),
    radial-gradient(92% 190% at var(--gx) var(--gy), rgba(85, 105, 25, .17), transparent 80%);
  /* Enough blur to lose the gradient's banding, not enough to eat the peak: at
     22px the core measured barely half the reference's brightness. */
  filter: blur(13px);
  pointer-events: none; opacity: var(--lit);
  transition: opacity .25s ease;
}
.grid .card:nth-child(4n + 2)::before { --gx: 56%; --gy: 34px; }
.grid .card:nth-child(4n + 3)::before { --gx: 72%; --gy: 24px; }
.grid .card:nth-child(4n + 4)::before { --gx: 18%; --gy: 36px; }
/* A card that is only a statement carries no icon, so its heading begins at the
   padding and there is no clear zone above it to light. It goes unlit — not
   every card has to be, and a row of plain claims reads better without. */
.grid-2 .card::before { display: none; }

.card > * { position: relative; z-index: 1; }
.card:hover { border-color: var(--faint); box-shadow: 0 14px 34px rgba(0, 0, 0, .45); }
.card h3 { margin: auto 0 10px; font-size: 20px; letter-spacing: -.01em; }
.card p { margin: 0; color: var(--dim); font-size: 14px; }

/* The icon well. */
.chip {
  width: 56px; height: 56px; border-radius: 50%;
  /* Translucent, so the glow behind it tints the well instead of being hidden
     by it — in the reference the disc is olive wherever the light passes under
     it, and neutral grey everywhere else. */
  background: rgba(255, 255, 255, .05);
  border: 1px solid rgba(216, 248, 58, .55);
  display: flex; align-items: center; justify-content: center; color: var(--accent);
  transition: transform .2s ease;
}
.card:hover .chip { transform: translateY(-2px); }
.chip-sq { border-radius: 15px; border-color: rgba(216, 248, 58, .5); color: var(--accent); }

/* --------------------------------------------------------------- split row */

.split { display: grid; gap: 48px; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 861px) { .split { grid-template-columns: 1fr 1fr; gap: 64px; } }
/* Beside a paragraph in a half-width column, one tile per row — two of them
   side by side there would be about a hundred pixels each. */
.split .proof { margin-top: 0; grid-template-columns: 1fr; }

/* ------------------------------------------------------------ trust marks */

/* The reference puts a wall of client logos here. This product has no clients
   to name, so the row names what it is built out of instead — which is both
   true and, for anybody who recognises the words, more convincing. */
.marks {
  display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; align-items: center;
  padding: 10px 0 0;
}
/* Chips rather than bare words. Six wide-tracked capitals floating in a row wrap
   into something that looks spilled rather than listed; an outline around each
   one gives the wrap somewhere to happen and matches the chip already used under
   the big call to action. */
.marks span {
  font-size: 12px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase;
  color: var(--dim); direction: ltr;
  padding: 9px 15px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface);
}

/* ------------------------------------------------------------------- bars */

.bars { display: grid; gap: 28px; }
.bar-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 12px; }
.bar-head h4 { margin: 0; font-size: 17px; font-weight: 400; }
.bar-head b { color: var(--accent); font-size: 14px; font-weight: 500; letter-spacing: .18em; direction: ltr; }
.bar-track { height: 9px; border-radius: 999px; background: var(--surface-high); overflow: hidden; }
.bar-fill {
  /* An <i> is inline, and width does nothing to an inline box: the JS was
     setting the width all along and the track stayed empty. */
  display: block;
  height: 100%; border-radius: 999px; width: 0;
  background: linear-gradient(90deg, var(--accent), var(--accent-soft));
  transition: width 1.4s cubic-bezier(.2, .7, .3, 1);
}

/* ---------------------------------------------------------------- pricing */

/* The product switch above the plan grid.

   Two products are sold here and they are never listed together: the servers
   behind the switch, and the configs to carry elsewhere. This is the app's
   Premium switcher in CSS — same 46px pill, same 4px inset, same 14% accent
   tint under the active half — so somebody who has used one recognises the
   other. It is hidden until the plans arrive, because a switch over an empty
   grid is a control that does nothing. */
.switcher {
  display: flex; gap: 4px;
  width: min(340px, 100%); margin: 0 auto 22px; padding: 4px;
  height: 46px; border-radius: 23px;
  background: var(--surface-high); border: 1px solid var(--border);
}
.switcher[hidden] { display: none; }
.switcher .seg {
  flex: 1; border: 0; border-radius: 19px; background: none;
  font: inherit; font-size: 13px; font-weight: 500;
  color: var(--dim); cursor: pointer;
  transition: background .16s ease, color .16s ease;
}
.switcher .seg.on {
  background: rgba(216, 248, 58, .14);
  color: var(--accent); font-weight: 600;
}
.switcher .seg:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.plans { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.plan {
  position: relative; overflow: hidden;
  padding: 30px 28px; display: flex; flex-direction: column; gap: 4px;
  background: linear-gradient(180deg, #1b1b1b, #121212 62%);
  border: 1px solid var(--border); border-radius: var(--radius);
  transition: border-color .2s ease, box-shadow .2s ease;
}
.plan::before {
  content: ""; position: absolute; z-index: 0;
  /* Its own rounded top corners, matching the card's inside the 1px border.
     The card clips with overflow:hidden, which is enough in Chrome — but WebKit
     does not apply the rounded part of that clip to a composited child, and this
     one is composited because it is masked. So the light kept a square corner
     and spilled past the card's curve on iOS. Rounding the light itself is
     correct without needing anything to clip it.
     -1px because the pseudo-element is laid out against the padding box, which
     sits inside the card's border. */
  border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
  inset: 0 0 auto 0; height: 86px;
  -webkit-mask-image: linear-gradient(180deg, #000 0, #000 30%, transparent 94%);
  mask-image: linear-gradient(180deg, #000 0, #000 30%, transparent 94%);
  --gx: 88%; --gy: 16px;
  background:
    radial-gradient(17% 68% at var(--gx) var(--gy), rgba(216, 248, 58, .80), transparent 70%),
    radial-gradient(34% 128% at var(--gx) var(--gy), rgba(150, 180, 40, .40), transparent 74%),
    radial-gradient(62% 210% at var(--gx) var(--gy), rgba(85, 105, 25, .22), transparent 78%);
  filter: blur(13px); pointer-events: none; opacity: var(--lit);
}
.plans .plan:nth-child(2)::before { --gx: 82%; --gy: 24px; }
.plans .plan:nth-child(3)::before { --gx: 94%; --gy: 14px; }
.plan > * { position: relative; z-index: 1; }
.plan:hover { border-color: var(--faint); box-shadow: 0 14px 34px rgba(0, 0, 0, .45); }
.plan.best { border-color: rgba(216, 248, 58, .42); }
/* The featured card is lit from underneath as well — the reference's one piece
   of asymmetry, and what makes it read as raised rather than merely outlined. */
.plan.best { overflow: visible; }
.plan.best::after {
  content: ""; position: absolute; z-index: 0;
  inset-inline: 6%; bottom: -34px; height: 90px;
  background: radial-gradient(50% 60% at 50% 30%, rgba(234, 252, 154, .5), rgba(216, 248, 58, .22) 45%, transparent 75%);
  filter: blur(14px); pointer-events: none;
}
.plan .tag {
  align-self: flex-start; margin-bottom: 12px;
  background: var(--accent); color: var(--accent-ink);
  font-size: 11px; font-weight: 700; padding: 4px 12px; border-radius: 999px;
}
.plan-head { display: flex; align-items: center; gap: 14px; margin-bottom: 20px; }
.plan-head .chip-sq { width: 50px; height: 50px; flex: none; }
/* The plan's own metal, in the corner the shield used to hold. Taller than the
   chip it replaces, because the mark is a tall shape and a square crop of it
   would cut the crown off. */
.plan-head .tier { width: 40px; height: auto; flex: none; display: block; margin: -4px 0; }
.plan-head .term { font-size: 19px; font-weight: 500; line-height: 1.3; }
.plan-head .for { color: var(--dim); font-size: 13px; }
.plan .price { font-size: 42px; font-weight: 300; letter-spacing: -.035em; color: var(--accent); line-height: 1.2; }
/* The unit is a word, not part of the figure: set tight against the digits it
   reads as "2USDT". A margin on both sides survives either direction. */
.plan .price span { font-size: 15px; font-weight: 500; color: var(--dim); margin: 0 8px; letter-spacing: 0; }
.plan .per { color: var(--faint); font-size: 12px; min-height: 18px; }

/* What the plan actually buys.
 *
 * A tick is a claim, so the two marks have to mean different things: `+` for
 * what this plan includes, a filled `×` for what it does not. The only row that
 * is ever crossed out is the product tab a plan does not unlock — which is real,
 * comes from the plan's own record, and is exactly the thing somebody asks for
 * a refund over when a card failed to say it.
 */
.plan .feat { list-style: none; margin: 22px 0 0; padding: 0; display: grid; gap: 13px; }
.plan .feat li { display: flex; align-items: flex-start; gap: 12px; font-size: 14px; line-height: 1.5; }
.plan .feat li::before {
  content: "+"; flex: none;
  width: 20px; height: 20px; margin-top: 1px;
  display: flex; align-items: center; justify-content: center;
  color: var(--accent); font-size: 17px; line-height: 1;
}
.plan .feat li.no { color: var(--faint); }
.plan .feat li.no::before {
  content: "\00d7";
  background: var(--accent); color: var(--accent-ink);
  border-radius: 5px; font-size: 13px; font-weight: 700;
  width: 18px; height: 18px; margin-top: 2px;
}

.plan .buy { margin-top: auto; padding-top: 26px; }
.plan .buy a { display: block; text-align: center; border-radius: 999px; padding: 13px; border: 1px solid var(--border); font-weight: 600; font-size: 14px; }
.plan.best .buy a { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.plan .buy a:hover { border-color: var(--faint); }
.plan.best .buy a:hover { border-color: var(--accent); filter: brightness(1.07); }
.plans-note { color: var(--faint); font-size: 13px; margin-top: 22px; }
.plans-empty { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 30px; color: var(--dim); font-size: 14px; }
.plans-empty a { color: var(--accent); }


/* --------------------------------------------------------------- big call */

/* A card the width of the page with the light behind its top edge, the one
   place on the page where the type goes really large. */
.callout {
  position: relative; overflow: hidden; text-align: center;
  background: var(--surface); border: 1px solid var(--border); border-radius: 34px;
  padding: 72px 30px;
}
.callout::before {
  content: ""; position: absolute; inset-inline: 18%; top: -90px; height: 240px;
  background: radial-gradient(50% 60% at 50% 70%, rgba(234, 252, 154, .34), rgba(216, 248, 58, .18) 45%, transparent 74%);
  filter: blur(26px); pointer-events: none;
}
.callout > * { position: relative; z-index: 1; }
.callout h2 { font-size: clamp(32px, 6.4vw, 60px); max-width: 15ch; margin-inline: auto; }
.callout .sub { margin-inline: auto; margin-bottom: 0; }
.guarantee {
  display: inline-flex; align-items: center; gap: 10px; margin-top: 34px;
  color: var(--dim); font-size: 14px;
}
.guarantee .chip { width: 34px; height: 34px; }
.guarantee .chip svg { width: 16px; height: 16px; }

/* ----------------------------------------------------------- testimonials */

.quote {
  position: relative; padding: 30px; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
}
.quote::before {
  content: "\201D"; display: block;
  font-size: 86px; line-height: .7; color: var(--faint); opacity: .45;
  margin-bottom: 18px; font-family: Georgia, "Times New Roman", serif;
}
.quote p { margin: 0 0 26px; color: var(--dim); font-size: 15px; line-height: 1.75; }
.quote cite { margin-top: auto; color: var(--accent); font-size: 14px; font-style: normal; }

/* ---------------------------------------------------------------- media */

/* A 16:9 well for a screenshot or a clip. It carries its own gradient, so the
   section is not broken by a missing file — this page ships with no photography
   and should not look like it is waiting for some. */
.media {
  position: relative; border-radius: var(--radius); overflow: hidden;
  aspect-ratio: 16 / 10; max-width: 100%;
  background:
    radial-gradient(70% 90% at 70% 8%, rgba(216, 248, 58, .22), transparent 62%),
    linear-gradient(160deg, var(--surface-high), #0d0d0d 70%);
  border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
}
.media img { width: 100%; height: 100%; object-fit: cover; }
.media .play {
  position: absolute; width: 66px; height: 66px; border-radius: 50%;
  background: var(--accent); color: var(--accent-ink);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 0 12px rgba(216, 248, 58, .12);
}

/* ------------------------------------------------------------------ faq */

#faq details { background: var(--surface); border: 1px solid var(--border); border-radius: 20px; padding: 20px 24px; margin-bottom: 12px; }
#faq details[open] { border-color: rgba(216, 248, 58, .32); }
/* A tenth off the whole accordion: question, answer and the marker beside
   them, so the three stay in the proportion they were drawn in. */
#faq summary { cursor: pointer; font-weight: 500; font-size: 14.4px; list-style: none; }
#faq summary::-webkit-details-marker { display: none; }
#faq summary::after { content: "+"; float: inline-end; color: var(--accent); font-weight: 400; font-size: 19.8px; line-height: 1.1; }
#faq details[open] summary::after { content: "–"; }
#faq details p { color: var(--dim); font-size: 12.6px; margin: 14px 0 0; }

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

/* Centred, because three left-aligned columns in a phone's width is a third of
   a screen of text and two thirds of nothing. */
footer { border-top: 1px solid var(--border); padding: 64px 0 52px; color: var(--faint); font-size: 13px; }
/* minmax(0, 1fr) rather than the default auto column, here and in every
   centred grid below it. An auto column is sized to its widest item, so a
   single item that will not shrink — an <input>, which keeps an intrinsic
   width of its own — widens the column past the box, and then everything
   centred in that column moves right by half the overflow. That is one
   element misbehaving and the whole panel looking crooked. A 0-floored track
   cannot outgrow its container, so nothing else can be dragged along. */
.foot-lead {
  text-align: center; display: grid; grid-template-columns: minmax(0, 1fr);
  justify-items: center; gap: 22px;
}
.foot-lead > svg { display: block; filter: drop-shadow(0 0 4px rgba(216, 248, 58, .5)) drop-shadow(0 0 13px rgba(216, 248, 58, .26)); }
.foot-tag { color: var(--dim); max-width: 46ch; margin: 0; line-height: 1.8; }

/* One pill holding both halves, rather than a field with a button under it. */
.signup { width: 100%; min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 10px; }
.signup h4 { margin: 0; font-size: 15px; font-weight: 500; color: var(--text); }
.signup form {
  display: flex; flex-direction: row; align-items: center; gap: 8px;
  width: 100%; min-width: 0; max-width: 410px;
  padding: 5px; padding-inline-start: 20px;
  border: 1px solid rgba(216, 248, 58, .45); border-radius: 999px;
  background: rgba(216, 248, 58, .06);
}
.signup input {
  flex: 1 1 auto; width: auto; min-width: 0;
  background: none; border: 0; color: var(--text);
  font: inherit; font-size: 14px; padding: 11px 0;
}
.signup input::placeholder { color: var(--accent); opacity: .75; }
.signup input:focus { outline: none; }
.signup .btn { flex: none; padding: 12px 20px; font-size: 14px; }
.signup .said { color: var(--accent); font-size: 13px; min-height: 18px; margin: 0; }

/* The four accounts. Any without an address yet is a span, not a link: a guessed
   instagram.com/<name> is as likely to be a stranger's as ours. */
.social { display: flex; gap: 10px; justify-content: center; }
.soc {
  display: grid; place-items: center; width: 42px; height: 42px;
  border-radius: 12px; border: 1px solid var(--border); background: var(--surface);
  color: var(--accent);
  transition: border-color .18s ease, background-color .18s ease, transform .18s ease;
}
.soc svg { width: 18px; height: 18px; }
a.soc:hover {
  border-color: var(--accent); background: rgba(216, 248, 58, .08); transform: translateY(-2px);
}
/* The ones with no account behind them yet. They keep the colour — it is the
   row's colour, not a promise — and say so on a long press rather than looking
   broken. */
.soc[aria-disabled="true"] { cursor: default; }

/* The lead sits in the page's own material — the card surface and the light
   across its top — so the footer reads as part of the page rather than a
   different document stapled to the end of it. */
.foot-panel {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; gap: 20px;
  width: 100%; padding: 34px 22px;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: linear-gradient(180deg, #191919, #111 70%);
}
.foot-panel::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 96px;
  /* Rounded for the same reason the cards' light is: WebKit will not apply the
     rounded part of the parent's clip to a masked child. */
  border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
  -webkit-mask-image: linear-gradient(180deg, #000 0, #000 32%, transparent 94%);
  mask-image: linear-gradient(180deg, #000 0, #000 32%, transparent 94%);
  background: radial-gradient(38% 120% at 50% 6px, rgba(216, 248, 58, .5), transparent 72%);
  filter: blur(14px); pointer-events: none;
}
.foot-panel > * { position: relative; z-index: 1; }

/* One centred row rather than three columns of headings. Ten links do not need
   to be filed under three titles, and at a phone's width the columns were about
   a hundred pixels each — three narrow lists where one wrapping row reads. */
.foot-row {
  display: flex; flex-wrap: wrap; justify-content: center;
  /* The rows sat about 18px apart — 4px of gap plus 7px of padding on each
     link — which read as four separate lists rather than one wrapped one.
     Halved, and taken out of the gap first so the links keep most of the
     height that makes them tappable. */
  /* 22px across. 26 was here to stop the language link being stranded on a
     row of its own; the footnote group below does that properly now, and at
     26 a 390px screen — which is most iPhones — spilled one link onto a fifth
     row. Measured at 360/390/412/430 in both languages: 22 is four rows
     everywhere except 360, where it is five. */
  gap: 2px 22px; margin: 42px 0 0; padding: 0 6px;
}
.foot-row a { color: var(--dim); padding: 4px 2px; font-size: 13.5px; }
.foot-row a:hover { color: var(--text); }
/* The download is the one thing here anybody came for, and it was sharing a
   row with two account links. Last and full width, so it wraps onto a line of
   its own and the two move up into the space it left. */
.foot-row .dl { flex-basis: 100%; text-align: center; }
/* The globe sits with the label rather than beside it, so the pair wraps
   as one word and the icon cannot end a line on its own. */
.foot-row .lang { display: inline-flex; align-items: center; gap: 6px; }
/* The footnote row: the disclaimer and the language switch, kept together
   so they cannot wrap apart at one width and back together at another. */
/* `foot-note`, not `note`: that name is already a paragraph style with an
   18px top margin on it, which landed as an extra gap above this row and
   nowhere else. */
.foot-row .foot-note { flex-basis: 100%; display: flex; justify-content: center; gap: 22px; }
@media (min-width: 620px) { .foot-row { gap: 4px 30px; margin-top: 50px; } }

.foot {
  display: flex; gap: 10px 20px; flex-wrap: wrap; justify-content: center; text-align: center;
  margin-top: 44px; padding-top: 26px; border-top: 1px solid var(--border);
}
.foot a { color: var(--dim); }
.foot a:hover { color: var(--text); }

/* ------------------------------------------------------------- articles
 *
 * The listing reuses the card, so an article looks like everything else on the
 * site rather than like a blog bolted to the side of it. The reading column is
 * the one thing here that is not the page's own width: prose is read, not
 * scanned, and a 1120px line is a line nobody finishes.
 */
.grid.articles { grid-template-columns: 1fr; gap: 14px; max-width: 760px; margin-inline: auto; }
@media (min-width: 780px) { .grid.articles { grid-template-columns: 1fr 1fr; max-width: none; } }
.article-card h2 { font-size: 21px; font-weight: 500; letter-spacing: -.01em; margin: 0 0 8px; }
.article-card p { color: var(--dim); font-size: 14.5px; margin: 0; }
.article-card time { display: block; margin-top: 14px; color: var(--faint); font-size: 12.5px; direction: ltr; }
[dir="rtl"] .article-card time { text-align: right; }

.prose { max-width: 720px; }
/* An article's title is a headline, not the hero's statement, so it does not
   take the hero's size. */
.prose h1 { font-size: clamp(28px, 5.4vw, 44px); margin: 0 0 6px; }
.prose h2 { font-size: clamp(20px, 3.4vw, 26px); font-weight: 500; margin: 44px 0 0; letter-spacing: -.01em; }
.prose p { color: var(--dim); font-size: 16px; margin: 16px 0 0; }
.prose .sub { margin-top: 10px; }
.prose ul, .prose ol { color: var(--dim); font-size: 16px; margin: 16px 0 0; padding-inline-start: 22px; }
.prose li { margin-top: 8px; }
.prose li::marker { color: var(--accent); }
.prose strong { color: var(--text); font-weight: 600; }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.prose code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .9em;
  background: var(--surface-high); border: 1px solid var(--border);
  border-radius: 6px; padding: 1px 5px; direction: ltr; display: inline-block;
}
.prose pre {
  margin: 20px 0 0; padding: 16px 18px; overflow-x: auto;
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  direction: ltr; text-align: left;
}
.prose pre code { background: none; border: 0; padding: 0; font-size: 13.5px; line-height: 1.7; }

.crumbs { margin-bottom: 22px; font-size: 13.5px; }
.crumbs a { color: var(--dim); }
.crumbs a:hover { color: var(--text); }
.byline { color: var(--faint); font-size: 13px; margin-top: 14px; direction: ltr; }
[dir="rtl"] .byline { direction: rtl; }

/* ------------------------------------------------------- coming soon
 *
 * A native <dialog>, so the modal behaviour, the focus trap, Escape and the
 * inert background are the browser's rather than three more listeners here.
 * ::backdrop only exists on a dialog opened with showModal(), which is what
 * the handler uses.
 */
.soon {
  border: 1px solid var(--border); border-radius: 22px;
  background: var(--surface-high); color: var(--text);
  padding: 30px 26px; max-width: 380px; width: calc(100% - 32px);
  text-align: center; box-shadow: 0 30px 80px rgba(0, 0, 0, .6);
}
.soon::backdrop { background: rgba(0, 0, 0, .68); backdrop-filter: blur(3px); }
.soon h2 { font-size: 26px; font-weight: 500; margin: 0; letter-spacing: -.01em; }
.soon p { color: var(--dim); font-size: 14.5px; margin: 12px 0 22px; }
.soon .btn { width: 100%; }
/* The badges are buttons now, and a button brings its own font and background. */
button.badge { font: inherit; cursor: pointer; }

/* --------------------------------------------------------------- badges */

.store { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-top: 34px; }
.badge {
  display: inline-flex; align-items: center; gap: 11px;
  min-height: 56px; padding: 9px 18px; border-radius: 13px; border: 1px solid transparent;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.badge .b-ico { flex: none; display: flex; }
.badge .b-txt { text-align: start; }
.badge .b-txt small { display: block; font-size: 11px; font-weight: 600; line-height: 1.45; opacity: .68; }
.badge .b-txt strong { display: block; font-size: 18px; font-weight: 700; line-height: 1.2; letter-spacing: -.3px; }
.badge-store {
  width: 150px; height: 52px; min-height: 0; padding: 0 12px; gap: 9px;
  justify-content: flex-start; border-radius: 12px;
  /* Store badges keep their own direction on the RTL page: mirrored, the icon
     lands on the wrong side of a name that is Latin text either way. */
  direction: ltr;
  background: #ffffff; color: #0a0a0a; border-color: #0a0a0a;
}
.badge-store .b-ico svg { width: 24px; height: 24px; }
.badge-store .b-txt small { font-size: 9px; line-height: 1.35; opacity: .8; }
.badge-store .b-txt strong { font-size: 15px; letter-spacing: -.2px; }
.badge-store .b-txt small, .badge-store .b-txt strong { white-space: nowrap; }
.badge-store:hover { transform: translateY(-2px); box-shadow: 0 12px 26px rgba(255, 255, 255, .13); }
.badge[aria-disabled="true"] { cursor: not-allowed; }
.badge[aria-disabled="true"]:hover { transform: none; }
@media (max-width: 359px) {
  .store { gap: 8px; }
  .badge-store { width: 132px; padding: 0 8px; gap: 6px; }
  .badge-store .b-ico svg { width: 20px; height: 20px; }
  .badge-store .b-txt small { font-size: 8px; }
  .badge-store .b-txt strong { font-size: 13px; }
}

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

@media (max-width: 860px) {
  section { padding: 60px 0; }
  .hero { padding: 32px 0 44px; }
  .callout { padding: 52px 22px; border-radius: 28px; }
}

/* ------------------------------------------------------------ the lamp
 *
 * In the reference the cards are not lit equally: a card low in the viewport is
 * nearly dark and brightens as it rises, continuously, so the whole page feels
 * like it is moving under a light rather than playing an animation.
 *
 * Measured off the recording, tracking one card's brightest pixel while the
 * page scrolled: at 63% down the viewport its peak read #667327, at 51% #9a8e14,
 * at 16% #c2b517. A smooth ramp, not a trigger — which is why a one-shot fade
 * cannot reproduce it and why this uses a scroll timeline.
 *
 * `view()` ties the animation to the element's own travel through the viewport,
 * runs off the main thread, and needs no scroll listener. Where it is not
 * supported the glow simply sits at full strength, which is the state every
 * frame of this animation is heading towards — so nothing is lost, and nothing
 * is hidden waiting for a feature to exist.
 */
/* The value the light is turned up by. Registered, because an unregistered
   custom property is a string and CSS cannot interpolate a string. */
@property --lit {
  syntax: "<number>";
  inherits: true;
  /* Full, so anything that never runs the animation shows a fully lit card. */
  initial-value: 1;
}
@keyframes lamp {
  /* Nearly out at the bottom of its travel: the further the light has to come
     up, the more of the movement there is to see. */
  from { --lit: .06; }
  to   { --lit: 1; }
}

/* The sections themselves, likewise: a rise that tracks the scroll instead of
   firing once. */
@keyframes rise {
  from { opacity: 0; transform: translateY(34px); }
  to   { opacity: 1; transform: none; }
}

/* Cards travel further than a paragraph does, and come in fractionally small,
   so a card reads as an object arriving rather than text fading up. Separate
   from `rise` because the same distance on a heading would read as the page
   assembling itself. */
@keyframes lift {
  from { opacity: 0; transform: translateY(52px) scale(.94); }
  to   { opacity: 1; transform: none; }
}

/* The card's arrival: the light coming up, and the card itself.

   This used to be a pair of scroll-linked animations on `animation-timeline:
   view()`, which ties progress to scroll position — so scrolling back up ran
   them backwards and the page took itself apart again. That was deliberate and
   it was wrong: a card arriving is an event, not a state of the scrollbar, and
   watching it un-arrive is the kind of motion that makes a page feel unstable.

   So both now run once, off the `in` class the observer adds and never
   removes, and the observer stops watching the element the moment it does.
   One motion per card per visit. The keyframes are unchanged; only what drives
   them is. */
@media (prefers-reduced-motion: no-preference) {
  /* The observer's own fade would be a second motion on the same object, on
     its own clock. The animations below are the only thing moving. */
  .js .card.reveal { transition: none; }
  .js .card.reveal.in,
  .js .plans.reveal.in > .plan {
    animation: lamp .75s linear both, lift .9s cubic-bezier(.5, 0, .35, 1) both;
  }

  /* A row should not light as one slab, so each column comes up a little after
     the one before it. On a phone they are stacked and already apart, and the
     delay costs nothing there. */
  .js .grid > .card.reveal.in:nth-child(2),
  .js .plans.reveal.in > .plan:nth-child(2) { animation-delay: .08s, .08s; }
  .js .grid > .card.reveal.in:nth-child(3),
  .js .plans.reveal.in > .plan:nth-child(3) { animation-delay: .16s, .16s; }
  .js .grid > .card.reveal.in:nth-child(4) { animation-delay: .24s, .24s; }
}

/* Guarded by the `js` class the page sets on <html> before anything renders:
   with scripting off nothing is ever hidden, so the page cannot arrive blank. */
.js .reveal { opacity: 0; transform: translateY(28px); }
.js .reveal.in {
  opacity: 1; transform: none;
  transition: opacity .6s cubic-bezier(.2, .7, .3, 1), transform .6s cubic-bezier(.2, .7, .3, 1);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal { opacity: 1; transform: none; }
  .hero::after { animation: none; }
  .bar-fill { transition: none; }
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}
