/* ============================================================================
   Complevia — complevia.fyi
   Plain CSS. No build step, no preprocessor, no third-party host.
   Deployed by copying files.

   The page is dark in every context, on purpose: all five screenshots are the
   app's own #0B0F14, so on a dark page they read as the product rather than as
   pictures of a product. Every colour below is sampled from those screenshots,
   which is what keeps the page and the app from drifting apart.
   ========================================================================= */

:root {
  color-scheme: dark;

  /* ── Surfaces, darkest to lightest ─────────────────────────────────── */
  --ink-900:     #080B10;   /* page */
  --ink-800:     #0B0F14;   /* the app's own background — wells, code, image beds */
  --ink-700:     #111720;   /* alternating section band */
  --surface:     #171D25;   /* cards — the app's card colour */
  --surface-2:   #1E2632;   /* raised / hover */

  /* ── Lines ─────────────────────────────────────────────────────────── */
  --line:        #263040;   /* hairlines, dividers, image frames (decorative) */
  --line-strong: #56647E;   /* borders that identify a control — 3.22:1 on --ink-900 */

  /* ── Text (contrast measured against --ink-900) ────────────────────── */
  --text:        #E8EDF4;   /* 16.3:1 */
  --text-mid:    #A7B2C2;   /*  8.96:1 */
  --text-dim:    #7E8A9B;   /*  5.49:1 on page, 4.84:1 on --surface */

  /* ── Accent — the app's blue ───────────────────────────────────────── */
  --accent:      #4DA3FF;   /* 7.32:1 on --ink-900 */
  --accent-soft: #71B5FE;
  --accent-tint: rgba(77, 163, 255, .12);
  --on-accent:   #080B10;   /* 7.50:1 on --accent */
  --focus:       #9EC9FF;

  /* ── Spacing scale, 4px base ───────────────────────────────────────── */
  --s2: 8px;   --s3: 12px;  --s4: 16px;  --s5: 24px;
  --s6: 32px;  --s7: 48px;  --s8: 64px;  --s9: 96px;  --s10: 128px;

  /* ── Shape ─────────────────────────────────────────────────────────── */
  --r-sm: 10px;  --r-md: 14px;  --r-lg: 20px;  --r-xl: 28px;

  /* ── Layout ────────────────────────────────────────────────────────── */
  --max-w: 1200px;
  --measure: 36em;          /* ~612px — nothing on this page is a 1120px paragraph */
  --header-h: 64px;

  --lift: 0 24px 60px -32px rgba(0, 0, 0, .85);
}

/* ── Reset ──────────────────────────────────────────────────────────── */

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

html {
  /* 1rem = 17px, and still scales with the reader's own browser setting. */
  font-size: 106.25%;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Without this every anchor lands underneath the sticky header. */
  scroll-padding-top: calc(var(--header-h) + 16px);
}

body {
  margin: 0;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
               Arial, "Noto Sans", sans-serif;
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text-mid);
  background:
    radial-gradient(1200px 620px at 50% -220px, rgba(77, 163, 255, .10), transparent 70%),
    var(--ink-900);
  background-attachment: fixed, scroll;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* A belt only. The layout must not need it — the nav is what used to push
     the document sideways between 641 and 785px, and it now collapses at 900. */
  overflow-x: clip;
}

img, svg, picture { display: block; max-width: 100%; }
img { height: auto; }

h1, h2, h3, h4 {
  color: var(--text);
  margin: 0;
  font-weight: 600;
  text-wrap: balance;
}

p { margin: 0; text-wrap: pretty; }

ul, ol { margin: 0; padding: 0; }

a { color: var(--accent); }
a:hover { color: var(--accent-soft); }

code, kbd, samp, pre {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", monospace;
}

/* One focus rule for everything. The dark inner ring guarantees the light
   outer ring stays visible even on the accent-filled download button, so
   there is no per-element exception anyone can forget to add. */
:where(a, button, summary, input, .btn):focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--ink-900), 0 0 0 5px var(--focus);
  border-radius: var(--r-sm);
}

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

/* ── Skip link ──────────────────────────────────────────────────────── */

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

/* ── Layout primitives ──────────────────────────────────────────────── */

.container {
  width: 100%;
  max-width: var(--max-w);
  margin-inline: auto;
  padding-inline: clamp(20px, 5vw, 56px);
}

.section { padding-block: clamp(64px, 9vw, 128px); }

.section--band {
  background: var(--ink-700);
  border-block: 1px solid var(--line);
}

.measure { max-width: var(--measure); }

.stack > * + * { margin-top: var(--s5); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ── Type scale ─────────────────────────────────────────────────────── */

h1 {
  font-size: clamp(2.1rem, 6vw, 3.8rem);
  line-height: 1.02;
  letter-spacing: -0.022em;
  font-weight: 600;
}

.h1-quiet {
  display: block;
  color: var(--text-mid);
  font-weight: 500;
}

h2 {
  font-size: clamp(1.6rem, 3.4vw, 2.35rem);
  line-height: 1.1;
  letter-spacing: -0.018em;
}

h3 {
  font-size: 1.25rem;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.lead {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  line-height: 1.5;
  letter-spacing: -0.005em;
  color: var(--text-mid);
}

.eyebrow {
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0;
}

.meta {
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--text-dim);
}

.meta strong { color: var(--text); font-weight: 600; }

/* ── Buttons ────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  padding: 15px 26px;
  min-height: 52px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  text-decoration: none;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease;
}

.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-soft); color: var(--on-accent); }

.btn-ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--line-strong);
}
.btn-ghost:hover { background: var(--surface); color: var(--text); }

.btn-sm {
  font-size: 0.92rem;
  padding: 11px 18px;
  min-height: 44px;          /* WCAG 2.2 target size, with room */
  border-radius: 999px;
}

/* ── Header ─────────────────────────────────────────────────────────── */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(8, 11, 16, .84);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--line);
}

/* THREE COLUMNS, so the sections actually sit in the middle.
   Under plain flex the nav could only centre inside whatever space the logo
   and the buttons happened to leave, which put it 78px left of the header's
   real centre. Outer columns sized 1fr make the two sides equal by
   construction, so the middle column is the middle of the header however long
   the logo or the button labels get. */
.header-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s4);
  min-height: var(--header-h);
  padding-block: var(--s2);
}

.header-inner > .logo { justify-self: start; }
.header-inner > .site-nav { justify-self: center; }

/* The call to action and the language menu are one element now. As separate
   grid items they could not be pushed to the corner together, which is what
   left a gap between them and the edge. */
.header-actions {
  display: flex;
  align-items: center;
  gap: var(--s3);
  justify-self: end;
}

.logo {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--text);
  font-weight: 600;
  letter-spacing: -0.01em;
  padding-block: 6px;
}
.logo:hover { color: var(--text); }

.logo-icon { width: 28px; height: 28px; border-radius: 8px; flex: 0 0 auto; }
.logo-text { font-size: 1.05rem; }

.site-nav ul {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2.4vw, 28px);
  list-style: none;
}

.site-nav a {
  display: inline-block;
  padding-block: 12px;        /* 18px-tall inline anchors were below the floor */
  font-size: 0.94rem;
  color: var(--text-mid);
  text-decoration: none;
}
.site-nav a:hover { color: var(--text); }

/* The nav collapses well before it can overflow. The previous stylesheet hid
   it at 640px while seven links plus a nowrap pill still needed ~790px, which
   is what pushed the whole document sideways between 641 and 785px. The four
   links have a replacement below the hero, so nothing is lost by hiding them. */
@media (max-width: 900px) {
  .site-nav { display: none; }

  /* AND THE GRID BECOMES TWO COLUMNS, because it still had three.
     With the nav hidden the row kept a `1fr auto 1fr` template for two items,
     so the actions sat in the middle `auto` column and the trailing `1fr` held
     empty space the header could not spare.
     `minmax(0, 1fr)` rather than `1fr` is the load-bearing half: a grid item's
     `min-width` is `auto`, not zero, so a plain `1fr` column REFUSES to shrink
     below its content. On a 390px phone the logo and the call to action then
     overflowed the row and the button was drawn on top of the wordmark. */
  .header-inner { grid-template-columns: minmax(0, 1fr) auto; }

  /* `max-width: 100%` IS THE ONE THAT ACTUALLY BINDS. `justify-self: start`
     sizes the item to its CONTENT, not to its column, so with `min-width: 0`
     alone the logo was still 124px inside a 103px column and overhung it by
     21px — measured, on the Polish page, after two earlier fixes that each
     looked right in English. Bounding it to the column is what finally lets
     the ellipsis below have a narrower box to work with. */
  .header-inner > .logo {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
  }

  /* The name gives way before the button does. It is the one thing here that
     can be cut without costing the visitor anything — the mark stays, and it
     is a link either way. */
  .logo-text {
    /* `min-width: 0` AGAIN, one level down, and without it the two lines below
       do nothing. The logo is a flex container, and a flex ITEM also defaults to
       `min-width: auto` — so the span refused to shrink, the ellipsis never had
       a narrower box to apply to, and a long label ("Wypróbuj Complevia") drew
       straight over the wordmark. Fixing the grid alone left Polish broken. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* A phone-sized call to action. Not a different button — the same one, with
   the padding a 390px row can actually afford, and no wrapping: "Získat
   Complevia" and "Complevia herunterladen" are the same control as the English
   and must not become two lines of it. */
@media (max-width: 560px) {
  /* THE WORDMARK GIVES WAY TO THE MARK, in every language at once.
     Truncation was the first answer and it was worse than the problem: Polish
     rendered "Comp…", which reads as a broken page rather than as a decision,
     and it read differently in each of the 21 locales depending on how long
     that language's call to action happens to be. An icon-only logo is the
     same in all of them, and it is still a link home with its own aria-label.
     Between 560 and 900 there IS room for the name, and the ellipsis above
     stays as the safety net for the languages that only just fit. */
  .logo-text { display: none; }

  .header-inner { gap: var(--s3); }
  .header-actions { gap: var(--s2); }
  .header-actions .btn {
    padding-inline: 12px;
    font-size: 14px;
    white-space: nowrap;
  }
}

/* ── Hero ───────────────────────────────────────────────────────────── */

/* The top pad is deliberately small at the narrow end: on a 375x812 phone
   every pixel above the h1 is a pixel the download button is pushed down,
   and the acceptance gate for this page is that the button's top edge stays
   at or above 480. */
.hero { padding-block: clamp(20px, 7vw, 96px) clamp(48px, 7vw, 88px); }

.hero-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(40px, 5vw, 72px);
  align-items: center;
}

/* The copy is FIRST in source order, so the single-column case stacks it on
   top for free. There is no `order` property anywhere in this file — that is
   what keeps the headline and the download button above the fold on a phone. */
@media (min-width: 900px) {
  .hero-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); }
}

.hero-copy > * + * { margin-top: var(--s5); }
.hero-copy .eyebrow + h1 { margin-top: var(--s4); }
.hero-copy h1 { max-width: 16ch; }
.hero-copy .lead { max-width: 32em; }
.hero-meta { margin-top: var(--s4); max-width: 40em; }

/* Two ways in, two buttons, one style. `flex: 1 1 220px` makes the pair share
   the row at equal width instead of one leading and one trailing — the layout
   is what says neither surface is the real one. */
.hero-copy .hero-actions { margin-top: var(--s6); }

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  max-width: 34em;
}

.hero-actions .btn { flex: 1 1 220px; }

@media (max-width: 560px) {
  .hero-copy .hero-actions { margin-top: var(--s5); }
  .hero-actions .btn { flex: 1 1 100%; }
}

/* ── Hero demo: the escalation ladder, drawn ────────────────────────── */
/* No screenshot in /screens shows a notification, and the mechanic this page
   sells IS the notification. So this one illustration is drawn — and labelled
   as an illustration directly beneath, so it can never be mistaken for a
   capture. Frozen, no animation: a diagram that needs motion to be read is a
   diagram that fails for prefers-reduced-motion and in a screenshot. */

.hero-demo {
  margin: 0;
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--r-xl);
  padding: clamp(18px, 3vw, 26px);
  box-shadow: var(--lift);
}

.demo-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-bottom: var(--s6);
}

.demo-title { font-size: 1.05rem; font-weight: 600; color: var(--text); }

.demo-pill {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--accent);
  background: var(--accent-tint);
  border: 1px solid rgba(77, 163, 255, .28);
  border-radius: 999px;
  padding: 5px 12px;
  white-space: nowrap;
}

.ruler { position: relative; margin: 0 6px var(--s7); }

.ruler-track {
  height: 6px;
  border-radius: 3px;
  background: var(--line);
  position: relative;
}

.ruler-dot {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  border-radius: 50%;
}
.ruler-dot--soft {
  inset-inline-start: 1.5%; width: 8px;  height: 8px;  margin-inline-start: -4px;
  background: rgba(77, 163, 255, .5);
  box-shadow: 0 0 0 3px rgba(77, 163, 255, .08);
}
.ruler-dot--focused {
  inset-inline-start: 42%;  width: 11px; height: 11px; margin-inline-start: -5.5px;
  background: rgba(77, 163, 255, .78);
  box-shadow: 0 0 0 5px rgba(77, 163, 255, .10);
}
.ruler-dot--strict {
  inset-inline-start: 78%;  width: 14px; height: 14px; margin-inline-start: -7px;
  background: var(--accent);
  box-shadow: 0 0 0 8px rgba(77, 163, 255, .14);
}

.ruler-ticks { position: relative; height: 1.5em; margin-top: 12px; }

.ruler-tick {
  position: absolute;
  top: 0;
  font-size: 0.8rem;
  color: var(--text-dim);
  white-space: nowrap;
  /* The tick's width is its text, so it cannot be centred with a margin the
     way the dots above are. The transform stays, and RTL gets the mirrored
     one — the single place on this page where a direction override is the
     only honest answer. */
  transform: translateX(-50%);
}
.ruler-tick--start { inset-inline-start: 0; transform: none; }
.ruler-tick--end   { inset-inline-end: 0; inset-inline-start: auto; transform: none; }
.ruler-tick--b { inset-inline-start: 42%; }
.ruler-tick--c { inset-inline-start: 78%; }
[dir="rtl"] .ruler-tick--b,
[dir="rtl"] .ruler-tick--c { transform: translateX(50%); }

.notif-stack { display: grid; gap: 10px; }

.notif {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 12px 14px;
  background: #12171F;
}
.notif--2 { background: #151C26; border-color: rgba(77, 163, 255, .22); }
.notif--3 {
  background: #17202C;
  border-color: rgba(77, 163, 255, .45);
  border-inline-start: 3px solid var(--accent);
}

.notif-when {
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 4px;
}
.notif--3 .notif-when { color: var(--accent); }

.notif-body { font-size: 0.94rem; color: var(--text-mid); }
.notif--2 .notif-body { font-size: 1rem; color: var(--text); }
.notif--3 .notif-body { font-size: 1.02rem; font-weight: 600; color: var(--text); }

.notif-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }

.notif-btn {
  font-size: 0.8rem;
  font-weight: 600;
  padding: 7px 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  color: var(--text-mid);
}
.notif--3 .notif-btn:not(.notif-btn--fill) { border-color: rgba(77, 163, 255, .4); color: var(--text); }
.notif--3 .notif-btn--fill { border-color: var(--accent); }
.notif-btn--fill {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.demo-note {
  margin-top: var(--s5);
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--text-dim);
}

@media (max-width: 480px) {
  /* The intermediate times live in the card headers anyway. */
  .ruler-tick--a, .ruler-tick--b, .ruler-tick--c { display: none; }
}

/* ── Fact strip ─────────────────────────────────────────────────────── */

.facts {
  border-block: 1px solid var(--line);
  background: var(--ink-800);
  padding-block: var(--s5);
}

.facts ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s6);
  list-style: none;
  font-size: 0.88rem;
  color: var(--text-dim);
}

.facts strong { color: var(--text); font-weight: 600; }

/* ── Contents: the four failures, and where each is answered ────────── */

.toc { padding-block: clamp(48px, 6vw, 80px); }

.toc-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--s3);
  list-style: none;
  margin-top: var(--s6);
}

.toc-list a {
  display: block;
  height: 100%;
  padding: 18px 20px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  text-decoration: none;
  color: var(--text);
  transition: background-color .15s ease, border-color .15s ease;
}
.toc-list a:hover { background: var(--surface-2); border-color: var(--line-strong); }

.toc-list b { display: block; font-size: 1rem; font-weight: 600; }
.toc-list span {
  display: block;
  margin-top: 6px;
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--text-mid);
}

/* ── A section with one screenshot beside it ────────────────────────── */
/* Deliberately NOT a gallery grid. One screenshot per argument, next to the
   argument it supports. A grid of five phones proves five screens exist; a
   captioned screenshot beside a claim proves the claim — and a two-item flow
   layout has no column count that can collapse to one. */

.split {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(32px, 5vw, 64px);
  align-items: center;
}

@media (min-width: 900px) {
  .split { grid-template-columns: minmax(0, 1fr) minmax(0, 340px); }
  /* Image column moves left on wide screens only; the text stays first in
     source order, so the stacked mobile case is unaffected. */
  .split--rev { grid-template-columns: minmax(0, 340px) minmax(0, 1fr); }
  .split--rev > .shot { grid-column: 1; grid-row: 1; }
}

.split-copy > * + * { margin-top: var(--s5); }
.split-copy p { max-width: var(--measure); }

.shot { margin: 0; }

.shot img {
  width: 100%;
  max-width: 300px;
  height: auto;
  margin-inline: auto;
  border-radius: var(--r-lg);
  border: 1px solid var(--line);
  background: var(--ink-800);
}

/* No drop shadow and no fake bezel: on a page built from the app's own
   near-black, a 1px line is the whole separation the screenshot needs. */

.shot figcaption {
  margin-top: var(--s3);
  max-width: 34em;
  margin-inline: auto;
  font-size: 0.94rem;
  line-height: 1.45;
  color: var(--text-mid);
}

@media (max-width: 899px) {
  .shot img { max-width: 260px; }
}

/* ── Cards ──────────────────────────────────────────────────────────── */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s3);
  list-style: none;
  margin-top: var(--s7);
}

.card {
  padding: 20px 22px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.card h3 { font-size: 1.05rem; }
.card p { margin-top: var(--s2); font-size: 0.94rem; color: var(--text-mid); }

/* ── The honest-limits list ─────────────────────────────────────────── */

.limits {
  list-style: none;
  margin-top: var(--s7);
  display: grid;
  gap: var(--s5);
  max-width: var(--measure);
}

.limits li {
  padding-inline-start: var(--s5);
  border-inline-start: 2px solid var(--line-strong);
}

.limits b { color: var(--text); font-weight: 600; }

/* ── Steps ──────────────────────────────────────────────────────────── */

.steps {
  list-style: none;
  counter-reset: step;
  display: grid;
  gap: var(--s5);
  margin-top: var(--s7);
  max-width: var(--measure);
}

.steps li {
  counter-increment: step;
  display: grid;
  /* minmax(0, 1fr), not 1fr: a grid track's default floor is min-content, so
     the unwrappable `white-space: pre` command block below would widen this
     track — and with it the whole document — instead of scrolling inside its
     own box. Removing the floor is what lets .cmd's overflow-x do its job. */
  grid-template-columns: 34px minmax(0, 1fr);
  gap: var(--s4);
  align-items: start;
}

.steps li::before {
  content: counter(step);
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--accent-tint);
  border: 1px solid rgba(77, 163, 255, .3);
  color: var(--accent);
  font-size: 0.9rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.steps b { color: var(--text); font-weight: 600; }

/* ── The two ways in ────────────────────────────────────────────────── */
/* Telegram and the Android app are the same product on two surfaces, so they
   get one grid, equal columns and identical cards. Stretch is deliberate: the
   Android card is taller because it carries three install steps, and letting
   the cards match height stops that extra height reading as importance. */

.routes {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s5);
  margin-top: var(--s7);
}

@media (min-width: 760px) {
  .routes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.route {
  padding: clamp(20px, 3vw, 28px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.route > * + * { margin-top: var(--s4); }

/* Full-width buttons so the two cards' calls to action are the same size at
   every viewport, whatever their labels weigh. */
.route .btn { width: 100%; }

.route .steps { margin-top: var(--s5); gap: var(--s4); }

/* ── Digest block ───────────────────────────────────────────────────── */

.digest-label {
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: var(--s2);
}

.digest {
  /* user-select:all means one click selects the whole hash, so nobody
     compares a half-copied digest and concludes the file is bad. */
  user-select: all;
  -webkit-user-select: all;
  display: block;
  font-size: 0.82rem;
  line-height: 1.7;
  overflow-wrap: anywhere;
  word-break: break-all;
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: 16px;
  color: var(--text);
  max-width: var(--measure);
}

.cmd {
  display: block;
  font-size: 0.85rem;
  background: var(--ink-800);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 10px 12px;
  margin-top: var(--s2);
  color: var(--text);
  overflow-x: auto;
  white-space: pre;
}

/* ── FAQ — native <details>, no JavaScript ──────────────────────────── */

.faq { margin-top: var(--s7); max-width: 46em; }

.faq details { border-bottom: 1px solid var(--line); }

.faq summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s4);
  padding: 20px 0;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
}
.faq summary::-webkit-details-marker { display: none; }

.faq summary::after {
  content: "";
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  margin-top: 9px;
  border-right: 2px solid var(--text-dim);
  border-bottom: 2px solid var(--text-dim);
  transform: rotate(45deg);
  transition: transform .18s ease;
}
.faq details[open] summary::after { transform: rotate(-135deg); }

.faq details > div { padding-bottom: 22px; max-width: var(--measure); }
.faq details > div > * + * { margin-top: var(--s3); }

/* ── Section head ───────────────────────────────────────────────────── */

.section-header { max-width: 40em; }
.section-header > * + * { margin-top: var(--s4); }

/* ── CTA block ──────────────────────────────────────────────────────── */

.cta-block {
  margin-top: var(--s7);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s4);
}

/* ── Prose pages (privacy, supporting articles) ─────────────────────── */

.prose { max-width: 42em; }
.prose > * + * { margin-top: var(--s5); }
.prose h2 { margin-top: var(--s8); font-size: clamp(1.35rem, 2.6vw, 1.75rem); }
.prose h3 { margin-top: var(--s6); font-size: 1.15rem; }
.prose ul, .prose ol { padding-inline-start: 1.35em; display: grid; gap: var(--s3); }
.prose li { padding-inline-start: 4px; }
.prose li::marker { color: var(--text-dim); }
.prose .updated { font-size: 0.88rem; color: var(--text-dim); }
.prose blockquote {
  margin: 0;
  padding-inline-start: var(--s5);
  border-inline-start: 2px solid var(--line-strong);
  color: var(--text-mid);
}

.crumb {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding-block: 10px;
  font-size: 0.94rem;
  font-weight: 600;
  text-decoration: none;
}
.crumb:hover { text-decoration: underline; }

.related {
  margin-top: var(--s8);
  padding-top: var(--s6);
  border-top: 1px solid var(--line);
}
.related ul { list-style: none; display: grid; gap: var(--s3); margin-top: var(--s4); }
.related a { font-weight: 600; }

/* ── Footer ─────────────────────────────────────────────────────────── */

.site-footer {
  border-top: 1px solid var(--line);
  background: var(--ink-800);
  padding-block: var(--s8);
  margin-top: var(--s9);
}

.footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
}

.footer-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  font-weight: 600;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s5);
  list-style: none;
}

.footer-links a {
  display: inline-block;
  padding-block: 10px;         /* 17px-tall inline anchors were below the floor */
  font-size: 0.94rem;
  color: var(--text-mid);
  text-decoration: none;
}
.footer-links a:hover { color: var(--text); }

.footer-copy { font-size: 0.88rem; color: var(--text-dim); }

/* ── Standalone links ───────────────────────────────────────────────── */
/* WCAG 2.2 §2.5.8 exempts a link sitting inside a sentence, but a link that
   IS the paragraph reads as a control and has to clear 24px. A bare inline
   anchor is ~20px tall, so these get their own padding. */
p > a:only-child,
.prose p > a:first-child:last-of-type { display: inline-block; padding-block: 6px; }

/* ── Large screens ──────────────────────────────────────────────────── */
/* Type stops growing well before here — every clamp() upper bound is a fixed
   px value. At 2560px the page is not a ribbon on a black field because the
   wash and the band sections are full-bleed; only the content is columnar. */

@media (min-width: 1800px) {
  html { font-size: 112.5%; }         /* 1rem = 18px */
  :root { --max-w: 1280px; }
}

/* ── Reduced motion ─────────────────────────────────────────────────── */

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

/* ── Print ──────────────────────────────────────────────────────────── */

@media print {
  .site-header, .toc, .cta-block, .skip-link { display: none; }
  body { background: #fff; color: #000; }
  .faq details > div { display: block; }
}

/* A paragraph that is nothing but links: same target-size reasoning as the
   :only-child rule above, but there are two of them side by side. */
.link-row { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); }
.link-row a { display: inline-block; padding-block: 6px; }

/* ── The checksum, folded away ─────────────────────────────────────────────
   It was step 2 of 4, which put two terminal commands between an ordinary
   reader and a habit tracker. Kept, because it genuinely matters for an APK
   that arrived from somewhere other than this page — but not in the path of
   someone who just wants the app. */

.verify {
  margin-top: var(--s7);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

.verify > summary {
  cursor: pointer;
  padding: 14px 18px;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text-mid);
  /* A 44px target: the row is the control, not the little triangle. */
  min-height: 44px;
  display: flex;
  align-items: center;
  border-radius: var(--r-md);
}

/* THE NON-OBVIOUS PART. Giving <summary> `display: flex` above buys a 44px tap target and costs
   the disclosure behaviour: overriding summary's display strips the engine's cue for hiding a
   closed details' remaining children, so the body rendered at full height with the arrow still
   pointing closed. The FAQ blocks on this page work only because nobody changed their summary's
   display. Hiding the body explicitly makes it independent of that quirk in either direction. */
.verify:not([open]) > .verify-body { display: none; }

.verify > summary:hover { color: var(--text); }
.verify > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.verify[open] > summary { border-bottom: 1px solid var(--line); }

.verify-body { padding: 18px; }
.verify-body > .meta { max-width: var(--measure); }
.verify-body .cmd { margin-block: 6px var(--s3); }

/* ── The studio link has to look clickable ─────────────────────────────────
   It sat inside the copyright line in the footer's muted grey with no
   underline, which is a link only to someone who thinks to hover it. The
   owner asked twice for a way to actually get to the studio, so it gets the
   affordance rather than the styling of surrounding text. */

.footer-copy a {
  color: var(--text-mid);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  /* The copyright is one short line; the padding gives the link a real target
     without disturbing the line's rhythm. */
  padding-block: 4px;
}

.footer-copy a:hover,
.footer-copy a:focus-visible { color: var(--text); }

/* And in the footer nav, where it now sits beside Privacy. */
.footer-links a[href*="ssvstudio"] {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

/* ── Two routes, actually equal ────────────────────────────────────────────
   The cards were pixel-identical and the Telegram one was 54% empty, which
   reads as one finished option beside an afterthought — the impression the
   whole equal-billing change exists to remove. Both now carry three steps. */

.steps--compact { margin-top: var(--s4); }
.steps--compact li { font-size: 0.94rem; }
.steps--compact li + li { margin-top: var(--s3); }

/* A paired CTA must not have one button wider than the other. Sizing them by
   the grid rather than by their labels means copy can change — and the owner's
   "· 4.8 MB" can stay — without the pair going ragged. */
.cta-pair {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s3);
  align-items: stretch;
  max-width: 520px;
}

@media (min-width: 560px) {
  .cta-pair { grid-template-columns: 1fr 1fr; }
}

.cta-pair .btn {
  width: 100%;
  /* Two lines of label must not make one button taller than its partner. */
  min-height: 52px;
  text-align: center;
}

/* Same rule for the hero pair, which had the same wrap at 320px. */
.hero-actions .btn { min-height: 52px; text-align: center; }

/* And in the route cards, where the APK label wraps at 320px. Every paired
   button on the page now sizes the same way, so copy can change without one
   of a pair going taller than the other. */
.route .btn { min-height: 52px; text-align: center; }


/* ── Right-to-left ─────────────────────────────────────────────────────────
   Everything above is written with logical properties, so dir="rtl" mirrors
   the layout on its own. What logical properties cannot fix is text that is
   NOT in the page's language, and typography tuned for Latin.

   1. Latin runs inside Arabic. A string that is only digits and punctuation —
      "09:00 – 20:00" — is direction-neutral, so the bidi algorithm lays it out
      in the paragraph's direction and the window reads 20:00 – 09:00. An
      isolate with an explicit ltr direction pins it.
   2. A shell command and a hex digest are code, not prose. They are LTR in
      every language, always, not only under dir=rtl.
   3. Negative letter-spacing is a Latin display-type trick. Applied to Arabic
      it pulls joined letterforms into each other, and it does nothing good to
      Devanagari, Thai or CJK either.
   4. Arabic and Devanagari have taller ascenders and lower descenders than
      Latin at the same size; 1.65 leading that is comfortable in English is
      cramped in both. */

.digest, .cmd {
  direction: ltr;
  text-align: start;
  unicode-bidi: isolate;
}

/* ONLY the pill. It is the one string on the page that is a RANGE — two
   numbers with a neutral character between them — and a neutral between two
   numbers takes the paragraph's direction, so under dir=rtl it renders as
   "20:00 – 09:00". A lone "09:10" needs no help: a digit run is strongly
   left-to-right on its own and the bidi algorithm already gets it right.

   Do NOT extend this to .ruler-tick. `direction` on an absolutely positioned
   box also decides how ITS OWN inset-inline-start resolves, so setting ltr
   here un-mirrors the element's position — the ticks stayed in their English
   places while the dots underneath them moved, and the last two labels
   collided. Found by rendering the page under dir=rtl, not by reading it. */
[dir="rtl"] .demo-pill {
  direction: ltr;
  unicode-bidi: isolate;
}

/* The system stack's first entry already resolves to a usable Arabic face on
   iOS, macOS, Android and Windows; the named fallbacks are for the Linux
   desktops where it does not. */
:root[lang="ar"] body {
  font-family: system-ui, -apple-system, "Segoe UI", "Noto Sans Arabic",
               "Noto Naskh Arabic", Tahoma, Arial, sans-serif;
  line-height: 1.85;
}

:root[lang="ar"] h1, :root[lang="ar"] h2, :root[lang="ar"] h3,
:root[lang="ar"] .lead, :root[lang="ar"] .eyebrow,
:root[lang="hi"] h1, :root[lang="hi"] h2, :root[lang="hi"] h3,
:root[lang="th"] h1, :root[lang="th"] h2, :root[lang="th"] h3,
:root[lang="ja"] h1, :root[lang="ja"] h2, :root[lang="ja"] h3,
:root[lang="ko"] h1, :root[lang="ko"] h2, :root[lang="ko"] h3,
:root[lang="zh-CN"] h1, :root[lang="zh-CN"] h2, :root[lang="zh-CN"] h3 {
  letter-spacing: normal;
}

/* h1 is clamped to 16ch, which is a sentence in English and a paragraph in
   CJK, where a character carries far more of a word. */
:root[lang="ja"] .hero-copy h1,
:root[lang="ko"] .hero-copy h1,
:root[lang="zh-CN"] .hero-copy h1,
:root[lang="th"] .hero-copy h1 { max-width: 22ch; }

:root[lang="hi"] body, :root[lang="th"] body { line-height: 1.8; }

/* The skip link animates in from off-screen ABOVE the viewport, which is
   direction-independent — but its horizontal anchor is not, and it is the
   first thing a keyboard user meets. */
[dir="rtl"] .skip-link { inset-inline-start: 12px; }

/* Turkish uppercasing is language-sensitive: with lang="tr" the browser maps
   i → İ, which is right for "ücretsiz" and wrong for the brand, rendering
   "ANDROİD". The eyebrow is the only uppercased element that carries a Latin
   brand name, so Turkish reads it in its own case rather than mis-spelling
   Android. (.notif-when, .digest-label and .lang-switch-h are unaffected:
   they hold no dotted i, or the Turkish mapping is the correct one.) */
:root[lang="tr"] .eyebrow { text-transform: none; }

/* ── Language menu, in the header ──────────────────────────────────────────
   Was a flat list of 21 links in the footer: crawlable, but a reader who
   wants their own language had to scroll the entire page to find it. A
   native <details> needs no JavaScript — a <select> would need an onchange
   handler, and a language switcher that stops working when the script does
   is worse than none. A closed <details> is still rendered and indexed, so
   folding it away costs nothing with crawlers. */

.lang-switch { position: relative; }

.lang-switch > summary {
  display: flex;
  align-items: center;
  gap: 6px;
  /* 44px of target, in a header that is only 65px tall. */
  min-height: 44px;
  padding: 0 10px;
  border-radius: var(--r-sm);
  font-size: 0.9rem;
  color: var(--text-mid);
  cursor: pointer;
  white-space: nowrap;
  list-style: none;
}
.lang-switch > summary::-webkit-details-marker { display: none; }
.lang-switch > summary::after {
  content: "";
  width: 7px; height: 7px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: rotate(45deg) translateY(-2px);
  transition: transform .15s ease;
}
.lang-switch[open] > summary::after { transform: rotate(-135deg) translateY(-2px); }

.lang-switch > summary:hover { color: var(--text); }
.lang-switch > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.lang-globe { font-size: 1rem; line-height: 1; }

/* The panel. Anchored to the end of the trigger so it never leaves the
   viewport on the right, and capped in height so 21 entries scroll instead
   of running off a short laptop screen. */
.lang-switch > ul {
  position: absolute;
  z-index: 60;
  inset-block-start: calc(100% + 6px);
  inset-inline-end: 0;
  margin: 0;
  padding: 8px;
  list-style: none;
  min-width: 220px;
  max-height: min(70vh, 520px);
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: 0 18px 48px rgba(0, 0, 0, .5);
  /* Two columns once there is room; 21 entries is a long thin list otherwise. */
  columns: 1;
}
@media (min-width: 480px) { .lang-switch > ul { columns: 2; min-width: 340px; } }

.lang-switch > ul a {
  display: block;
  padding: 9px 12px;
  border-radius: var(--r-sm);
  font-size: 0.92rem;
  color: var(--text-mid);
  text-decoration: none;
  break-inside: avoid;
}
.lang-switch > ul a:hover { background: rgba(255, 255, 255, .05); color: var(--text); }
.lang-switch > ul a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* The current language is marked by a tick as well as weight — colour alone
   is not a distinction for everyone. */
.lang-switch > ul a[aria-current="page"] {
  color: var(--text);
  font-weight: 600;
}
.lang-switch > ul a[aria-current="page"]::before { content: "✓ "; color: var(--accent); }

/* On a narrow header the label is a globe alone; the name would push the CTA
   off the row. */
@media (max-width: 720px) {
  .lang-current { display: none; }
  .lang-switch > summary { padding: 0 8px; }
}

/* The menu sits in the header's right corner, after the call to action. It
   gets a little air so the two do not read as one control, and the panel
   still anchors to the corner rather than hanging off the viewport. */
