/* dopeIPTV — dopeiptv.org
   Quiet on purpose: near-black or white, greys, and one blue - the blue of the
   app's icon. The system's own typeface (San Francisco on Apple devices),
   Inter everywhere else (SIL Open Font License, fonts/OFL-Inter.txt),
   served from this site so no visitor's address goes to a font service.
   No glows, no gradients: the content is the design. CSP-safe. */
@font-face { font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/fonts/inter-latin-wght-normal.woff2) format("woff2"); unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/fonts/inter-latin-ext-wght-normal.woff2) format("woff2"); unicode-range: U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/fonts/inter-cyrillic-wght-normal.woff2) format("woff2"); unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/fonts/inter-cyrillic-ext-wght-normal.woff2) format("woff2"); unicode-range: U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F; }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/fonts/inter-greek-wght-normal.woff2) format("woff2"); unicode-range: U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF; }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/fonts/inter-vietnamese-wght-normal.woff2) format("woff2"); unicode-range: U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB; }
:root {
  --ink: #0e0e10; --bg: #151517; --surface: #202023; --surface-2: #28282c;
  --line: #333338; --line-2: #45454b; --text: #f5f5f7; --muted: #aeaeb4;
  --faint: #919197; --accent: #2997ff; --btn: #0071e3; --accent-ink: #fff;
  --live: #ff453a; --brand: #4c8dff; --wallet: #fff; --wallet-ink: #000; --heart: #ff375f;
  --radius: 22px; --maxw: 1080px;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Inter", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  color-scheme: dark;
}
@media (prefers-color-scheme: light) {
  :root {
    --ink: #f5f5f7; --bg: #fbfbfd; --surface: #f2f2f5; --surface-2: #fff;
  --line: #d2d2d7; --line-2: #c7c7cc; --text: #1d1d1f; --muted: #6a6a70;
  --faint: #737379; --accent: #0066cc; --btn: #0071e3; --accent-ink: #fff;
  --live: #ff3b30; --wallet: #000; --wallet-ink: #fff; --heart: #ff2d55; color-scheme: light;
  --bar: #f2f2f5; --bar-chip: #fff;
  }
}
:root[data-theme="dark"] {
  --ink: #0e0e10; --bg: #151517; --surface: #202023; --surface-2: #28282c;
  --line: #333338; --line-2: #45454b; --text: #f5f5f7; --muted: #aeaeb4;
  --faint: #919197; --accent: #2997ff; --btn: #0071e3; --accent-ink: #fff;
  --live: #ff453a; --wallet: #fff; --wallet-ink: #000; --heart: #ff375f; color-scheme: dark;
}
:root[data-theme="light"] {
  --ink: #f5f5f7; --bg: #fbfbfd; --surface: #f2f2f5; --surface-2: #fff;
  --line: #d2d2d7; --line-2: #c7c7cc; --text: #1d1d1f; --muted: #6a6a70;
  --faint: #737379; --accent: #0066cc; --btn: #0071e3; --accent-ink: #fff;
  --live: #ff3b30; --wallet: #000; --wallet-ink: #fff; --heart: #ff2d55; color-scheme: light;
  --bar: #f2f2f5; --bar-chip: #fff;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0; background: var(--bg); color: var(--text); font-family: var(--sans);
  font-size: 17px; line-height: 1.5; letter-spacing: -.01em; -webkit-font-smoothing: antialiased;
}
h1, h2 { font-family: var(--display); }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
.eyebrow { font-size: 15px; font-weight: 600; letter-spacing: -.01em;
  color: var(--muted); display: inline-flex; align-items: center; gap: 8px; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--live); }
@keyframes pulse { 0%{box-shadow:0 0 0 0 color-mix(in srgb,var(--live) 60%,transparent);}
  70%{box-shadow:0 0 0 7px transparent;} 100%{box-shadow:0 0 0 0 transparent;} }
@media (prefers-reduced-motion: reduce){ .dot{ animation: none; } }
/* The light theme: an off-white page (Apple's #fbfbfd) with grey bands a
   shade firmer than before - all white, the page was a glare and the bands
   hardly showed - and the bar in the bands' grey, not the page's white, so
   it stands apart from what scrolls under it. Its language button is white
   on it (--bar, --bar-chip: light themes only; dark keeps its colours). */
header.bar { position: sticky; top: 0; z-index: 20; backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  background: color-mix(in srgb, var(--bar, var(--bg)) 78%, transparent); border-bottom: 1px solid color-mix(in srgb, var(--line) 70%, transparent); }
.cr header.bar { -webkit-backdrop-filter: none; backdrop-filter: none;
  background: color-mix(in srgb, var(--bar, var(--bg)) 96%, transparent); }   /* nav.js: Chromium */
/* Chromium: the donate band without its rounded clip - moving layers
   clipped to rounded corners cost it an offscreen pass every frame. What
   moves stays inside by itself: the big heart fits the band, the drifting
   hearts are faded out before they reach its top. */
.cr .donate { overflow: visible; }
.cr .donate-big { top: 9%; height: 82%; width: auto; aspect-ratio: 1 / 1; margin-top: 0; }
@media (max-width: 760px) {
  .cr .donate-big { top: auto; right: 14px; bottom: 14px; width: 150px; height: 150px; }
}
.bar .wrap { display: flex; align-items: center; justify-content: space-between; height: 56px; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand .logo { width: 26px; height: 26px; flex: none; }
.brand .logo rect { fill: var(--brand); }
.brand .logo path { fill: #fff; }
.brand b { font-family: var(--display); font-weight: 600; font-size: 18px; letter-spacing: -.02em; }
nav.links { display: flex; align-items: center; gap: 26px; }
nav.links a { color: var(--muted); font-size: 13.5px; }
nav.links a:hover { color: var(--text); }
nav.links a[aria-current="page"] { color: var(--text); font-weight: 500; }
.btn { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font: inherit;
  font-weight: 500; font-size: 15px; padding: 10px 22px; border-radius: 999px;
  border: 1px solid transparent; transition: background .2s ease, border-color .2s ease, color .2s ease; }
.btn.primary { background: var(--btn); color: var(--accent-ink); }
.btn.primary:hover { background: color-mix(in srgb, var(--btn) 88%, #fff); }
.btn.ghost { background: transparent; border-color: var(--accent); color: var(--accent); }
.btn.ghost:hover { background: var(--accent); color: var(--accent-ink); }
.bar .btn { padding: 5px 14px; font-size: 13px; }
/* Below 1000px the inline links give way to the disclosure in the header
   (.navmenu). They used to just vanish, which left the help page with a
   header of nothing but the logo. The gaps close up a little before that. */
@media (max-width: 1100px) { nav.links { gap: 20px; } }
@media (max-width: 1000px) { nav.links a.navlink { display: none; } }

.hero { padding: 96px 0 40px; text-align: center; }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 64px; justify-items: center; }
/* Grid items default to their min-content width; the mock-up's rows do not
   wrap, so without this a long LIVE tag widened the whole column. */
.hero-grid > * { min-width: 0; }
@media (max-width: 900px) { .hero-grid { gap: 44px; } }
h1 { font-size: clamp(2.1rem, 5vw, 3.6rem); line-height: 1.07; letter-spacing: -.03em;
  margin: 16px auto 0; max-width: 19ch; text-wrap: balance; font-weight: 700;
  /* "enregistrements." is wider than a 320px phone at this size. */
  overflow-wrap: break-word; hyphens: manual; }
h1 .hl { color: inherit; }
/* The product name, as the first words of the page's one heading: the
   wordmark style of the header, a line above the headline. */
/* The name stays in the heading - what a search engine and a screen reader
   read - but is not shown: in blue above the heading it was one more small
   heading over a heading, and the logo in the bar already says it. */
h1 .h1-brand { position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
.lede { color: var(--muted); font-size: clamp(1.06rem, 2.2vw, 1.3rem); margin: 22px auto 0; max-width: 40em; line-height: 1.5; text-wrap: balance; }
.cta-row { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 32px; align-items: center; justify-content: center; }
.platnote { font-size: 13px; color: var(--faint); margin-top: 18px; }
.platnote b { color: var(--muted); font-weight: 600; }
.platnote .oses { white-space: nowrap; display: none; }
.os-known .platnote .oses { display: inline; }
.platnote::first-letter { text-transform: uppercase; }
.platnote b.is-mine { color: var(--text); }
.mock { width: min(860px, 100%); text-align: start; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 40px 80px -40px rgba(0,0,0,.45); overflow: hidden; }
.mock .titlebar { display: flex; align-items: center; gap: 7px; padding: 11px 14px;
  border-bottom: 1px solid var(--line); background: var(--surface-2); }
.tl-dot { width: 11px; height: 11px; border-radius: 50%; background: var(--line-2); }
.mock .body { display: grid; grid-template-columns: 40% 60%; min-height: 300px; }
.mock .chans { border-right: 1px solid var(--line); padding: 8px; }
.chan { display: flex; align-items: center; gap: 9px; padding: 8px 9px; border-radius: 8px; font-size: 13px; color: var(--muted);
  white-space: nowrap; min-width: 0; }
/* The name gives way to the LIVE tag, which is a word or two longer in
   most languages than in English: "News…" beats a tag cut in half. */
.chan .cn { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 480px) {
  .mock .body { grid-template-columns: 50% 50%; }
  .chan { gap: 6px; }
  .chan .live-tag { letter-spacing: 0; padding: 1px 3px; }
}
@media (max-width: 340px) { .chan .live-tag { display: none; } }
.chan .n { width: 22px; height: 22px; border-radius: 6px; background: var(--surface-2); border: 1px solid var(--line); flex: none; }
.chan.active { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--text); }
.chan.active .n { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 22%, transparent); }
.chan .live-tag { margin-left: auto; flex: none; font-size: 9px; font-weight: 700; letter-spacing: .06em;
  color: #fff; background: var(--live); padding: 2px 6px; border-radius: 999px; }
.mock .stage { display: flex; flex-direction: column; }
.screen { flex: 1; margin: 12px; border-radius: 12px; border: 1px solid var(--line);
  background: linear-gradient(160deg, #2c2c2e, #0b0b0c);
  position: relative; overflow: hidden; min-height: 150px; }
.screen::after { content: "\25B6"; position: absolute; inset: 0; display: grid; place-items: center;
  color: color-mix(in srgb, #fff 82%, transparent); font-size: 30px; }
.scan { position: absolute; inset: -25%; pointer-events: none;
  background: radial-gradient(28% 36% at 32% 42%, rgba(255, 255, 255, .2), transparent 70%),
    radial-gradient(24% 30% at 70% 62%, rgba(255, 255, 255, .13), transparent 70%); }
.mock.alive .scan { animation: pic 14s ease-in-out infinite alternate; }
.mock.alive.still .scan { animation-play-state: paused; }
@keyframes pic { 50% { transform: translate(7%, -5%) scale(1.08); } to { transform: translate(-6%, 4%) scale(1.02); } }
.mock.alive .screen::after { content: none; }
.timeline { margin: 0 12px 12px; }
.tl-track { height: 8px; border-radius: 6px; background: var(--surface-2); border: 1px solid var(--line); position: relative; }
.tl-fill { position: absolute; left: 0; top: 0; bottom: 0; width: 62%; background: var(--accent); border-radius: 6px 0 0 6px; }
.tl-head { position: absolute; left: 62%; top: -4px; width: 3px; height: 16px; background: var(--text); border-radius: 2px; }
.tl-meta { display: flex; justify-content: space-between; font-size: 11px; font-weight: 600; color: var(--faint); margin-top: 8px; }
.tl-meta .behind { color: var(--accent); }
.strip { padding: 8px 0 16px; }
.hero .strip { margin-top: 52px; text-align: start; }
.strip .wrap { display: flex; flex-wrap: wrap; gap: 8px 26px; align-items: center; justify-content: center; }
.chip { font-size: 14px; color: var(--faint); display: inline-flex; gap: 26px; align-items: center; }
section { padding: 96px 0; }
.sec-head { max-width: 60ch; margin-bottom: 40px; }
h2 { font-size: clamp(1.8rem, 4vw, 2.8rem); letter-spacing: -.03em; line-height: 1.08; margin: 0; text-wrap: balance; font-weight: 700; }
.sec-head p { color: var(--muted); margin: 14px 0 0; font-size: 1.1rem; }
/* Section openings centred, as a page of few words reads best. The FAQ keeps
   its heading in the side column. */
#features .sec-head, #shots .sec-head { margin-inline: auto; text-align: center; }
.dl-head { align-items: center !important; text-align: center; }
.dl-head .sec-head { margin-inline: auto; }
/* These openings are a heading alone, and the 60ch measure is for running
   text: it broke a one-line sentence in two on a wide screen. A heading
   longer than the page still wraps, balanced. */
#features .sec-head, #shots .sec-head, .dl-head .sec-head { max-width: none; }
/* Features, the way Apple lists them: a line icon, a title and a line or
   two of text - three columns on a computer, a list on a phone (app.js
   folds all but six there behind "Show all features"). No boxes. */
.feats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 48px 44px; max-width: 1000px; margin: 8px auto 0; }
.feats[hidden] { display: none; }
@media (max-width: 860px) { .feats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px 32px; } }
.feat .ic { display: block; width: 34px; height: 34px; margin-bottom: 16px; color: var(--accent);
  fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.ic .fill { fill: currentColor; stroke: none; }
.feat h3 { margin: 0; font-family: var(--display); font-size: 1.1rem; font-weight: 600; letter-spacing: -.02em; line-height: 1.3; }
.feat p { margin: 6px 0 0; color: var(--muted); font-size: 15.5px; line-height: 1.5; }
.shots { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
@media (max-width: 720px) { .shots { grid-template-columns: 1fr; } }
.shot { border-radius: 6px; overflow: hidden; background: var(--surface); }
.shot img { width: 100%; height: auto; display: block; cursor: zoom-in; }
.shot .ph { aspect-ratio: 16/10; width: 100%; display: grid; place-items: center; color: var(--faint); font-size: 12px; font-weight: 700; letter-spacing: .08em;
  background: linear-gradient(135deg, var(--surface-2), var(--ink)); }
.shot .cap { padding: 12px 16px; font-size: 13px; color: var(--muted); border-top: 1px solid var(--line); }
.shot .cap b { color: var(--text); font-weight: 600; }
.dl-head { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; margin-bottom: 26px; }
.release-tag { display: inline-block; font-size: 14px; color: var(--faint); }
.release-tag b { color: var(--text); font-weight: 600; }
.release-tag .dl-count { color: var(--faint); font-variant-numeric: tabular-nums; }
/* Downloads, grouped by OS so people pick their system, then their CPU. */
.dl-group { margin-bottom: 26px; }
.dl-group:last-of-type { margin-bottom: 0; }
.dl-group-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.dl-os-ico { font-size: 22px; line-height: 1; }
.dl-group-head h3 { font-size: 17px; font-weight: 700; margin: 0; }
.dl-guide { font-size: 13px; color: var(--muted); line-height: 1.7; margin: 0 0 14px; }
.dl-guide b { color: var(--text); font-weight: 600; }
.dls { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 720px) { .dls { grid-template-columns: minmax(0, 1fr); } }
.dl { display: flex; align-items: center; gap: 16px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; padding: 15px 18px; transition: border-color .2s ease, background .2s ease; }
.dl:hover { border-color: var(--accent); }
.dl.rec { border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); background: color-mix(in srgb, var(--accent) 7%, var(--surface)); }
.dl .meta { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.dl .meta .name { font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dl .meta .sub { font-size: 12px; color: var(--faint); overflow-wrap: anywhere; }
.dl .meta .sub .arch { font-weight: 600; color: var(--muted); }
.badge { font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, transparent); border-radius: 999px; padding: 1px 7px; }
.dl .go { margin-left: auto; color: var(--accent); font-weight: 600; font-size: 14px; white-space: nowrap; }
.dl .meta .sub .dl-count { color: var(--faint); font-variant-numeric: tabular-nums; }
/* Downloads: a card per system side by side, each with one thing to press
   and its other formats as small links under it. app.js marks the visitor's own card
   (.is-mine) and quiets the others' buttons. */
.dlx-cards { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 16px; }
.dlx-card { position: relative; display: flex; flex-direction: column; align-items: center; text-align: center;
  min-width: 0; padding: 34px 24px 22px; background: var(--surface); border-radius: var(--radius); }
.dlx-card.is-mine { box-shadow: inset 0 0 0 2px var(--accent); }
.dlx-yours { display: none; position: absolute; top: 14px; inset-inline-end: 14px; }
.dlx-card.is-mine .dlx-yours { display: inline-block; }
.dlx-ico { display: none; }
.dlx-card h3 { margin: 6px 0 0; font-family: var(--display); font-size: 26px; font-weight: 600; letter-spacing: -.025em; }
.dlx-sub { margin: 4px 0 18px; font-size: 13.5px; color: var(--muted); line-height: 1.5; }
.dlx-svg { width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.dlx-btns { align-self: stretch; display: flex; flex-direction: column; gap: 8px; }
.dlx-big { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 12px 18px; border-radius: 999px;
  background: var(--btn); color: var(--accent-ink); font-weight: 500; font-size: 15px;
  transition: background .15s ease, box-shadow .15s ease, color .15s ease; }
.dlx-big span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dlx-big small { direction: ltr; unicode-bidi: isolate; font-weight: 600; opacity: .7; font-size: 12px; white-space: nowrap; }
.dlx-big:hover { background: color-mix(in srgb, var(--btn) 88%, #fff); }
.dlx-big.alt, .dlx.has-mine .dlx-card:not(.is-mine) .dlx-big {
  background: var(--surface-2); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-2); }
.dlx-big.alt:hover, .dlx.has-mine .dlx-card:not(.is-mine) .dlx-big:hover {
  background: var(--surface-2); color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.dlx-meta { margin: 10px 0 18px; font-size: 12.5px; color: var(--faint); line-height: 1.5; }
.dlx-alt { margin: auto 0 0; padding-top: 14px; border-top: 1px solid var(--line); align-self: stretch;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 2px 14px; font-size: 13px; line-height: 1.7; }
.dlx-alt > span { flex-basis: 100%; margin-bottom: 4px; font-size: 13px; font-weight: 500; color: var(--faint); }
.dlx-alt a { direction: ltr; unicode-bidi: isolate; color: var(--accent); font-weight: 500; white-space: nowrap; }
.dlx-alt a span { font-size: 12px; color: var(--faint); }
.dlx-alt a:hover { text-decoration: underline; text-underline-offset: 3px; }
.dlx-how { margin-top: auto; padding-top: 16px; font-size: 13px; font-weight: 600; color: var(--muted); }
.dlx-alt + .dlx-how { margin-top: 0; }
.dlx-how::after { content: " \2192"; }
[dir="rtl"] .dlx-how::after { content: " \2190"; }
.dlx-how:hover { color: var(--accent); }
.dlx-gh { margin: 18px 0 0; font-size: 13px; text-align: center; }
#download .autonote { text-align: center; }
.dlx-gh a { color: var(--muted); text-decoration: underline; text-underline-offset: 3px; }
.dlx-gh a:hover { color: var(--accent); }
@media (max-width: 860px) {
  .dlx-cards { grid-auto-flow: row; grid-template-columns: minmax(0, 1fr); }
  .dlx-card.is-mine { order: -1; }
}
/* Donate: one quiet card after the downloads, shown only when render.jq has
   an address. The link is Stripe's own page; nav.js opens the dialog below
   over it instead, and goes to that page if the dialog cannot open. */
.donate { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 32px; align-items: center;
  padding: 36px 40px; border-radius: var(--radius); position: relative; overflow: hidden; isolation: isolate;
  background: radial-gradient(55% 130% at 86% 50%, color-mix(in srgb, var(--heart) 24%, transparent), transparent 72%),
    var(--surface); }
/* Behind the text: a big heart that beats, small ones drifting up past the
   button (.is-live, set by nav.js while the band is on screen). */
.donate-fx { position: absolute; inset: 0; z-index: -1; pointer-events: none; }
.donate-big { position: absolute; width: 300px; height: 300px; right: 5%; top: 50%; margin-top: -150px;
  fill: var(--heart); opacity: .1; }
.donate-fx i { position: absolute; bottom: -26px; width: 16px; height: 16px; background: var(--heart); opacity: 0;
  -webkit-mask: url(/heart.svg) center / contain no-repeat; mask: url(/heart.svg) center / contain no-repeat; }
.donate-fx i:nth-of-type(1) { left: 66%; width: 12px; height: 12px; animation-delay: 0s; }
.donate-fx i:nth-of-type(2) { left: 74%; width: 18px; height: 18px; animation-delay: 1.3s; }
.donate-fx i:nth-of-type(3) { left: 82%; width: 10px; height: 10px; animation-delay: 2.7s; }
.donate-fx i:nth-of-type(4) { left: 90%; width: 15px; height: 15px; animation-delay: 4.1s; }
.donate-fx i:nth-of-type(5) { left: 70%; width: 9px; height: 9px; animation-delay: 5.2s; }
.donate-fx i:nth-of-type(6) { left: 86%; width: 20px; height: 20px; animation-delay: 6.4s; }
.donate .donate-btn { position: relative; color: #fff; background: linear-gradient(135deg, #ff6b8b, #ff2d55 60%, #e8174a);
  box-shadow: 0 12px 30px -14px rgba(255, 45, 85, .8); }
.donate .donate-btn::after { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: rgba(255, 55, 95, .5); opacity: 0; pointer-events: none; }
.donate .donate-btn:hover { background: linear-gradient(135deg, #ff7d99, #ff3b62 60%, #f01e52); }
.donate-text h2 { font-size: clamp(1.35rem, 2.6vw, 1.7rem); margin: 10px 0 8px; }
.donate-text p { margin: 0; color: var(--muted); line-height: 1.65; max-width: 62ch; }
/* On a phone the long reason gives way to one short sentence (below). */
.donate-text .donate-short { display: none; }
.donate-act { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
.donate-btn { padding: 13px 26px; font-size: 15px; }
.donate-heart { width: 17px; height: 17px; fill: currentColor; }
.donate-btn .donate-heart { color: #fff; }
.donate-act small { color: var(--faint); font-size: 12px; max-width: 30ch; line-height: 1.5; }
@media (max-width: 760px) {
  .donate { grid-template-columns: minmax(0, 1fr); padding: 22px; gap: 18px;
    background: radial-gradient(90% 60% at 50% 100%, color-mix(in srgb, var(--heart) 22%, transparent), transparent 75%),
      var(--surface); }
  .donate-big { width: 200px; height: 200px; right: -30px; top: auto; bottom: -40px; margin-top: 0; opacity: .08; }
  .donate-act { align-items: stretch; }
  .donate-btn { justify-content: center; }
}
.autonote { margin-top: 18px; font-size: 13px; color: var(--faint); line-height: 1.75; }
.autonote code { font-family: var(--mono); background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 1px 6px; color: var(--muted); overflow-wrap: anywhere; }
/* What the app is not, before anyone downloads it: a quiet line. */
.dl-nochan { margin: 22px auto 0; text-align: center; font-size: 14px; color: var(--muted); max-width: 60ch; }
.autonote a.verify-link { color: var(--accent); font-weight: 600; }
.autonote a.verify-link:hover { text-decoration: underline; }

footer { background: var(--surface); padding: 28px 0 44px; margin-top: 24px; }
footer .wrap { display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
footer .v { font-size: 12.5px; color: var(--faint); }
/* wrap: four links do not fit on one line at 320px, and a footer is no
   reason for the whole page to scroll sideways. */
footer nav { display: flex; gap: 22px; flex-wrap: wrap; }
footer nav a { color: var(--muted); font-size: 12.5px; }
footer nav a:hover { color: var(--accent); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }

/* Screenshot lightbox (opened from app.js) */
.lb-overlay { position: fixed; inset: 0; z-index: 100; display: flex; align-items: center;
  justify-content: center; padding: 24px; background: rgba(0,0,0,.86); cursor: zoom-out;
  opacity: 0; pointer-events: none; transition: opacity .18s ease; }
.lb-overlay.open { opacity: 1; pointer-events: auto; }
.lb-overlay img { max-width: 96vw; max-height: 92vh; width: auto; height: auto;
  border-radius: 10px; box-shadow: 0 24px 70px rgba(0,0,0,.6); }
.lb-close { position: fixed; top: 18px; right: 22px; color: #fff; font-size: 30px;
  line-height: 1; background: none; border: 0; cursor: pointer; opacity: .8; }
.lb-close:hover { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .lb-overlay { transition: none; } }

/* ---- language switcher (custom roll-down, theme-aware) ------------------- */
/* A styled <details> instead of a native <select>: the OS pop-up button looks
   clunky and its popup highlight was unreadable in light mode. This one is
   fully themed, so the colours always read and it matches the site. Works with
   no JS (native disclosure); app.js only adds close-on-outside-click. */
.langpick { position: relative; }
.langpick summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 12px; border-radius: 12px;
  border: 1px solid transparent; background: var(--bar-chip, var(--surface));
  color: var(--text); font-size: 14px; line-height: 1;
  transition: border-color .2s ease, background .2s ease;
}
.langpick summary::-webkit-details-marker { display: none; }
.langpick summary::marker { content: ""; }
.langpick summary:hover { border-color: var(--accent); }
.langpick[open] summary { border-color: var(--accent); }
.langpick-caret { color: var(--muted); font-size: 11px; transition: transform .2s ease; }
.langpick[open] .langpick-caret { transform: rotate(180deg); }
.langpick-menu {
  position: absolute; top: calc(100% + 8px); inset-inline-end: 0; z-index: 40;
  min-width: 190px; max-height: 62vh; overflow-y: auto;
  padding: 6px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line-2);
  box-shadow: 0 18px 44px -18px rgba(0,0,0,.6);
  display: flex; flex-direction: column; gap: 2px;
}
.langpick-item {
  display: block; padding: 8px 12px; border-radius: 8px;
  color: var(--text); font-size: 14px; white-space: nowrap;
}
.langpick-item:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.langpick-item.is-current { color: var(--accent); font-weight: 600; }

/* ---- the narrow-window menu -------------------------------------------- */
/* Same disclosure as the language picker, so the header has one idea of what
   a menu looks like. It exists only where the inline links do not. */
.navmenu { position: relative; display: none; }
@media (max-width: 1000px) { .navmenu { display: block; } }
.navmenu summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; justify-content: center;
  /* 44px: a finger, not a pointer. */
  width: 44px; height: 40px; border-radius: 12px;
  border: 1px solid transparent; background: var(--bar-chip, var(--surface));
  transition: border-color .2s ease;
}
.navmenu summary::-webkit-details-marker { display: none; }
.navmenu summary::marker { content: ""; }
.navmenu[open] summary { border-color: var(--accent); }
.navmenu-bars { display: flex; flex-direction: column; gap: 4px; width: 18px; }
.navmenu-bars i {
  display: block; height: 2px; border-radius: 2px; background: var(--text);
  transition: transform .2s ease, opacity .2s ease;
}
/* Open: the bars become a cross, so the button says how to close it. */
.navmenu[open] .navmenu-bars i:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.navmenu[open] .navmenu-bars i:nth-child(2) { opacity: 0; }
.navmenu[open] .navmenu-bars i:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) { .navmenu-bars i { transition: none; } }
.navmenu-panel {
  position: absolute; top: calc(100% + 8px); inset-inline-end: 0; z-index: 40;
  min-width: 200px; padding: 6px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--line-2);
  box-shadow: 0 18px 44px -18px rgba(0,0,0,.6);
  display: flex; flex-direction: column; gap: 2px;
}
.navmenu-item {
  display: block; padding: 11px 14px; border-radius: 8px;
  color: var(--text); font-size: 15px; white-space: nowrap;
}
.navmenu-item:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.navmenu-item.is-current { color: var(--accent); font-weight: 600; }

/* The phone header's heart (render.jq, nav): Donate where the links are
   folded away. Same size and frame as the menu button beside it. */
.navheart { display: none; }
@media (max-width: 1000px) {
  nav.links .navheart { display: flex; align-items: center; justify-content: center; flex: none;
    width: 44px; height: 40px; border-radius: 12px; color: var(--live);
    border: 1px solid transparent; background: var(--bar-chip, var(--surface)); transition: border-color .2s ease; }
  nav.links .navheart:hover { border-color: var(--heart); color: var(--heart); }
  .langpick summary { height: 40px; }
  .navheart svg { width: 18px; height: 18px; fill: currentColor; }
}

/* Below 400px the header's Download button goes as well: brand, picker,
   menu and a "Télécharger" or "Завантажити" do not fit in 320-360px, and
   the button's target is one tap away in the menu and in the hero. */
@media (max-width: 400px) { .bar .btn { display: none; } }

/* A phone header has to hold the brand, the language picker, the menu and
   the download button in 360px. The language NAME is what gives - the globe
   still says what the button is, and the panel marks the current language.
   Without this the row overflowed and the whole page scrolled sideways. */
.langpick-cur { display: inline-flex; align-items: center; gap: 8px; }
@media (max-width: 720px) {
  nav.links { gap: 10px; }
  /* The globe, the heart and the menu: three buttons of one size. */
  .langpick summary { padding: 0; width: 44px; height: 40px; justify-content: center; }
  .langpick-name, .langpick-caret { display: none; }

  /* Both panels hang off a button barely wider than a finger, so anchoring
     them to it put them half off the screen - the language list at -25px on
     a 320px phone, and mirrored back out the other side in RTL. Dropping
     position:relative lets them anchor to the sticky header instead, and
     pinning both inline edges makes the width the screen's, not the
     content's. One rule, and neither can escape in either direction. */
  .langpick, .navmenu { position: static; }
  .langpick-menu, .navmenu-panel {
    inset-inline: 16px; min-width: 0; max-height: 70vh; overflow-y: auto;
  }
}

/* ---- utilities (replace former inline styles) --------------------------- */
/* .pt0 used to close the gap between two sections of one colour; the
   sections now alternate in bands, and each needs its own room. */
.mb0 { margin-bottom: 0; }


/* ---- Release notes ("What's new") -------------------------------------- */
/* Rendered from releases.json by render_notes() into a <template>; the
   "What's new" link beside the version in the download section opens them in
   the dialog the install steps use (app.js). No section of their own: most
   visitors do not read them, and whoever does finds them a click away. */
.release-tag .wn-link { color: var(--accent); font-weight: 600; }
.release-tag .wn-link::after { content: " \203A"; }
[dir="rtl"] .release-tag .wn-link::after { content: " \2039"; }
.release-tag .wn-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.notes h3 {
  margin: 26px 0 10px;
  font-size: 17px; font-weight: 600;
  color: var(--text);
}
.notes h3:first-child { margin-top: 0; }
.notes p { margin: 0 0 14px; color: var(--text); line-height: 1.65; }
.notes ul { margin: 0 0 14px; padding-left: 20px; }
.notes li {
  margin: 0 0 10px;
  color: var(--muted);
  line-height: 1.6;
}
.notes li strong { color: var(--text); font-weight: 600; }
.notes code {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1px 5px;
  font-size: .92em;
}
.notes blockquote {
  margin: 18px 0 0;
  padding: 10px 14px;
  border-left: 3px solid var(--line-2);
  color: var(--faint);
  font-size: 14px;
}
.notes a { color: var(--accent); }

/* ---- Help page --------------------------------------------------------- */
/* Questions folded, as the FAQ on the front page: with every answer open the
   page was seventeen thousand pixels of text. The search opens what it finds
   (help.js), so a match still reads without a second click. */
.help-top { padding: 46px 0 26px; border-bottom: 1px solid var(--line); }
.help-top h1 { font-size: 34px; margin: 0 0 22px; }
/* The site gives every section its padding; under the search bar that was a
   dead band above the questions. */
section.help-main { padding-top: 0; }

/* The search box is the point of the page, so it is sized like it. */
.help-searchbar {
  position: relative;
  display: flex; align-items: center;
  max-width: 680px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: 14px;
  transition: border-color .15s, box-shadow .15s;
}
.help-searchbar:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
}
.help-mag {
  position: absolute; left: 18px;
  display: flex; color: var(--faint); pointer-events: none;
}
.help-mag svg { width: 20px; height: 20px; display: block; }
.help-searchbar:focus-within .help-mag { color: var(--accent); }
.help-searchbar input {
  flex: 1; min-width: 0;
  background: none; border: 0; outline: none;
  padding: 17px 16px 17px 50px;
  color: var(--text); font-size: 17px; font-family: inherit;
}
.help-searchbar input::placeholder { color: var(--faint); }
.help-searchbar input::-webkit-search-cancel-button { display: none; }
.help-searchbar button {
  background: none; border: 0; cursor: pointer;
  color: var(--faint); font-size: 17px; padding: 0 18px; line-height: 1;
}
.help-searchbar button:hover { color: var(--accent); }
/* "/" jumps to the box (help.js) - shown where there is a keyboard to press
   it on, and gone once the box is in use. */
.help-key {
  flex: none; margin-inline-end: 16px; padding: 1px 8px; border-radius: 6px;
  border: 1px solid var(--line-2); color: var(--faint);
  font: 500 13px/1.4 var(--mono);
}
.help-searchbar:focus-within .help-key, body.searching .help-key { display: none; }
@media (hover: none), (pointer: coarse) { .help-key { display: none; } }
.help-count { margin: 12px 4px 0; color: var(--accent); font-size: 14px; font-weight: 600; }

.help-layout {
  display: grid; grid-template-columns: 230px 1fr; gap: 48px;
  align-items: start; padding-top: 34px;
}
/* A grid item's automatic minimum size is its CONTENT's minimum, so the long
   command lines in the <pre> blocks made the column as wide as the longest
   one and the whole page scrolled sideways on a phone. min-width:0 lets the
   column be the screen and the <pre> scroll inside itself, which is what its
   own overflow-x was already there for. */
.help-toc, .help-body { min-width: 0; }
/* Hiding the contents would otherwise leave the answers in the 230px column
   the contents used to sit in. While filtering the results take the width. */
body.searching .help-layout { grid-template-columns: 1fr; }
.help-toc {
  position: sticky; top: 80px;
  display: flex; flex-direction: column; gap: 1px;
}
/* An author `display` beats the UA's [hidden] rule, so the contents column
   would stay on screen while filtering unless this says otherwise. */
.help-toc[hidden] { display: none; }
.help-toc a {
  color: var(--muted); text-decoration: none; font-size: 14px;
  padding: 7px 12px; border-radius: 8px; line-height: 1.35;
}
.help-toc a:hover { color: var(--text); background: var(--surface-2); }
.help-toc a.is-current {
  color: var(--accent); background: var(--surface-2); font-weight: 600;
}

/* These are <section> elements inside a <section>, and the site gives every
   section 72px of padding - which showed up as a dead band above and below
   each heading. They set their own spacing with margins instead. */
.help-sec { padding: 0; margin: 0 0 44px; scroll-margin-top: 80px; }
.help-sec h2 {
  font-size: 26px; letter-spacing: -.025em;
  color: var(--text); margin: 0 0 4px;
  padding-bottom: 12px; border-bottom: 1px solid var(--line);
}
.help-item { border-bottom: 1px solid var(--line); scroll-margin-top: 80px; }
.help-sec .help-item:last-child { border-bottom: 0; }
.help-item > summary {
  list-style: none; cursor: pointer; color: var(--text);
  padding: 15px 0; display: flex; align-items: center; justify-content: space-between; gap: 18px;
}
.help-item > summary::-webkit-details-marker { display: none; }
.help-item > summary h3 { margin: 0; font-size: 17px; font-weight: 500; line-height: 1.35; color: inherit; }
.help-item > summary::after {
  content: "+"; flex: none; width: 28px; height: 28px; display: grid; place-items: center;
  color: var(--muted); font-size: 22px; font-weight: 300; line-height: 1;
  transition: transform .2s ease;
}
.help-item[open] > summary::after { transform: rotate(45deg); }
.help-item > summary:hover, .help-item > summary:hover::after { color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .help-item > summary::after { transition: none; } }
.help-a { color: var(--muted); line-height: 1.7; max-width: 74ch; padding: 0 0 22px; }
.help-a p { margin: 0 0 11px; }
.help-a p:last-child { margin-bottom: 0; }
.help-a b { color: var(--text); font-weight: 600; }
.help-a a { color: var(--accent); }
.help-a ol, .help-a ul { margin: 0 0 11px; padding-left: 22px; }
.help-a li { margin: 0 0 6px; }
.help-a code {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: 5px; padding: 1px 5px; font-size: .92em;
  word-break: break-word;
}
/* A command you are meant to run, not a word in a sentence. */
.help-a pre {
  margin: 0 0 11px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; padding: 13px 15px;
  overflow-x: auto;
}
.help-a pre code {
  background: none; border: 0; padding: 0;
  font-size: 13px; line-height: 1.6; color: var(--text); white-space: pre;
}
/* A command with its copy button (nav.js) - the button beside the text,
   not over it: the <pre> keeps room on its right. A command reads left to
   right in every language, so the button stays on the right too. */
.codebox { position: relative; margin: 0 0 11px; }
.codebox pre { margin: 0; padding-right: 52px; }
.copy {
  position: absolute; top: 7px; right: 7px; width: 32px; height: 32px; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface-2); color: var(--muted);
  transition: color .15s ease, border-color .15s ease;
}
.copy:hover { color: var(--text); border-color: var(--line-2); }
.copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.copy svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round; grid-area: 1 / 1; }
.copy svg + svg, .copy.is-done svg:first-child { display: none; }
.copy.is-done svg + svg { display: block; }
.copy.is-done { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
@media (prefers-reduced-motion: reduce) { .copy { transition: none; } }
.help-a .fine { font-size: 13px; line-height: 1.6; color: var(--faint); }
.help-a .msg { color: var(--text); border-inline-start: 2px solid var(--line-2); padding-inline-start: 12px; }
/* Every language, each in its own name (render.jq langlist). */
.help-a .langlist { list-style: none; padding: 0; margin: 4px 0 11px; columns: 3; column-gap: 24px; }
.help-a .langlist li { margin: 0 0 6px; color: var(--text); break-inside: avoid; }
@media (max-width: 560px) { .help-a .langlist { columns: 2; } }

.help-a .os { color: var(--faint); font-size: 12px; text-transform: uppercase;
  letter-spacing: .07em; display: block; margin: 0 0 5px; }
/* A shell command is left-to-right in every language. Done here rather than
   with dir="ltr" in the markup so the <pre> blocks stay byte-identical to
   the English ones - which is what the translation check enforces. */
[dir="rtl"] .help-a pre, [dir="rtl"] .help-a code { direction: ltr; }
[dir="rtl"] .help-a pre { text-align: left; }
/* A picture of the settings page an answer is about (render.jq). */
.help-shot { display: block; width: 100%; max-width: 640px; height: auto; margin: 14px 0 0;
  border-radius: 6px; border: 1px solid var(--line); background: var(--surface); }
.help-none { color: var(--muted); padding: 26px 2px; font-size: 16px; }
.help-foot { margin-top: 34px; }
/* While filtering, sections lose their headings' distance - the list should
   read as results, not as a table of contents with gaps in it. */
body.searching .help-sec { margin-bottom: 26px; }

@media (max-width: 860px) {
  .help-layout { grid-template-columns: 1fr; gap: 22px; }
  .help-toc {
    position: static; flex-direction: row; flex-wrap: wrap; gap: 6px;
    padding-bottom: 8px;
  }
  .help-toc a { background: var(--surface-2); }
  .help-top h1 { font-size: 27px; }
  .help-searchbar input { font-size: 16px; padding: 15px 14px 15px 46px; }
}

/* -- the donate dialog (nav.js) ---------------------------------------------
   A <dialog> over the page, as on solequiz.com: an amount, then Apple Pay /
   Google Pay or the card form (Stripe's, in Stripe's frames). On a phone it
   is a sheet from the bottom. */
nav.links .navdonate { color: var(--muted); }
.navdonate .heart { width: 15px; height: 15px; flex: none; fill: var(--heart); }
@media (min-width: 1001px) { nav.links a.navdonate { display: inline-flex; align-items: center; gap: 6px; } }
.navmenu-item.navdonate { display: flex; align-items: center; gap: 9px; }
dialog.dn {
  width: min(440px, calc(100vw - 32px)); max-height: calc(100dvh - 32px);
  padding: 0; border: 1px solid var(--line-2); border-radius: 18px;
  background: var(--surface); color: var(--text); overflow: auto;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .6);
}
dialog.dn::backdrop { background: rgba(5, 7, 10, .66); }
.dn [hidden] { display: none !important; }
/* Title, currency and close on one line; the line under the title has the
   whole width, also beside a currency menu on a small phone. */
.dn-head { display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas: "title cur x" "sub sub sub"; align-items: center; column-gap: 10px; padding: 22px 22px 4px; }
.dn-head > div { display: contents; }
.dn-head h2 { grid-area: title; min-width: 0; }
.dn-head p { grid-area: sub; }
.dn-head .dn-curwrap { grid-area: cur; margin-top: 0; }
.dn-head .dn-x { grid-area: x; margin: 0 -6px 0 0; }
.dn-head h2 { margin: 0; font-size: 1.2rem; font-weight: 700; letter-spacing: -.03em; }
.dn-head p { margin: 4px 0 0; color: var(--muted); font-size: 14px; line-height: 1.5; }
/* Who is paid: one quiet line at the foot, and the company in the footer's
   small print - there for whoever wonders, never in the way. */
.dn-more { margin: 2px 0 0; color: var(--faint); font-size: 12.5px; line-height: 1.45; text-align: center; }
.dn-more a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); }
.dn-more a:hover { color: var(--text); text-decoration-color: currentColor; }
.dn-payee { margin: 14px 0 0; color: var(--faint); font-size: 12px; line-height: 1.4; text-align: center;
  display: flex; align-items: center; justify-content: center; gap: 8px; }
/* Big enough and bright enough to be seen: at 15 px in the faint grey the
   company mark went unnoticed. */
.dn-payee .dn-ico { width: 18px; height: 18px; color: var(--muted); }
/* The payee's logotype in place of its name: a text colour through the
   logotype's shape (as the footer's), so it follows both themes. */
.dn-payee a.dn-sole { color: var(--muted); text-decoration: none; }
.dn-payee a.dn-sole:hover { color: var(--text); }
.dn-sole-logo { display: inline-block; height: 12px; aspect-ratio: 666 / 111; vertical-align: -1px;
  overflow: hidden; white-space: nowrap; text-indent: 110%; background: currentColor;
  -webkit-mask: var(--logo) center / contain no-repeat; mask: var(--logo) center / contain no-repeat; }
.dn-payee a { color: inherit; text-decoration: underline; text-underline-offset: 2px;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent); }
.dn-payee a:hover { color: var(--text); text-decoration-color: currentColor; }
.dn-payee a.dn-payee-link:has(img) { display: inline-flex; flex: none; }
.dn-payee a:has(img):hover { opacity: .85; }
footer .v a { color: inherit; }
/* Solecraft's logotype (its S is the company's mark) as the footer's own
   sign, the copyright line under it; drawn in a text colour, so it follows
   both themes. */
footer .maker { display: flex; flex-direction: column; align-items: flex-start; gap: 7px; }
footer .sole { display: block; color: var(--muted); line-height: 0; position: relative; }
footer .sole:hover { color: var(--text); }
/* Who made it, on hover: a label of the site's own rather than the browser's
   title tooltip, which comes late and looks different everywhere. */
footer .sole[data-tip]::after {
  content: attr(data-tip); position: absolute; inset-inline-start: 0; bottom: calc(100% + 9px);
  white-space: nowrap; font: 500 13px/1.3 var(--sans); letter-spacing: 0;
  color: var(--text); background: var(--surface-2); border: 1px solid var(--line);
  padding: 6px 10px; border-radius: 8px; box-shadow: 0 6px 18px rgba(0,0,0,.18);
  opacity: 0; transform: translateY(3px); pointer-events: none;
  transition: opacity .15s ease, transform .15s ease;
}
footer .sole[data-tip]:hover::after, footer .sole[data-tip]:focus-visible::after { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { footer .sole[data-tip]::after { transition: opacity .15s ease; transform: none; } }
footer .sole-logo { display: block; height: 17px; aspect-ratio: 666 / 111; overflow: hidden; white-space: nowrap;
  text-indent: 110%; background: currentColor;
  -webkit-mask: url(/solecraft-logo.svg) center / contain no-repeat; mask: url(/solecraft-logo.svg) center / contain no-repeat; }
footer .v a:hover { color: var(--accent); }
.dn-x { flex: none; width: 34px; height: 34px; margin: -4px -6px 0 0; border-radius: 50%;
  border: 0; background: transparent; color: var(--muted); font: inherit; font-size: 24px;
  line-height: 1; cursor: pointer; }
[dir="rtl"] .dn-x, [dir="rtl"] .dn-head .dn-x { margin: 0 0 0 -6px; }
.dn-x:hover { background: var(--surface-2); color: var(--text); }
.dn-body { padding: 16px 22px 22px; }
.dn-step { display: flex; flex-direction: column; gap: 14px; }
.dn-chips { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
.dn-chip { font: inherit; font-weight: 700; font-size: 15px; padding: 12px 4px; cursor: pointer;
  border-radius: 12px; border: 1px solid var(--line-2); background: var(--ink); color: var(--text);
  transition: border-color .15s ease, background .15s ease; }
@media (hover: hover) { .dn-chip:hover { border-color: color-mix(in srgb, var(--accent) 60%, var(--line-2)); } }
.dn-chip.is-on { border-color: var(--accent); color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--ink)); box-shadow: inset 0 0 0 1px var(--accent); }
.dn-other, .dn-email { display: flex; flex-direction: column; gap: 6px; font-size: 13px;
  font-weight: 600; color: var(--muted); }
/* The currency: a quiet pill in the dialog's head, on both steps, its arrow
   drawn here (appearance: none drops the browser's own). 16px: Safari on
   iPhone zooms the page in on a smaller field. */
.dn-curwrap { position: relative; flex: none; display: block; margin-top: -2px; }
.dn-curwrap::after { content: ""; position: absolute; inset-inline-end: 12px; top: 50%; width: 6px; height: 6px;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: translateY(-70%) rotate(45deg); pointer-events: none; }
.dn-cur { font: inherit; font-size: 16px; font-weight: 600; color: var(--text); cursor: pointer;
  padding: 5px 30px 5px 12px; border-radius: 999px; border: 1px solid var(--line-2);
  background: var(--ink); -webkit-appearance: none; appearance: none; }
[dir="rtl"] .dn-cur { padding: 5px 12px 5px 30px; }
.dn-cur:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.dn-cur:disabled { opacity: .55; cursor: default; }
.dn-input { font: inherit; font-size: 16px; font-weight: 500; color: var(--text); width: 100%;
  padding: 11px 13px; border-radius: 10px; border: 1px solid var(--line-2); background: var(--ink); }
.dn-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.dn-go, .dn-paybtn { justify-content: center; width: 100%; padding: 14px 18px; font-size: 16px; }
.dn-go:disabled, .dn-paybtn:disabled { opacity: .55; cursor: default; transform: none; }
/* The wallets' own colours, over .btn.primary's accent - and their own
   rule for them: the white button on a dark page, the black one on a light
   page (--wallet). A black button on the dark dialog all but vanished. */
.dn .btn.dn-wallet { background: var(--wallet); color: var(--wallet-ink); box-shadow: none;
  transition: opacity .15s ease; }
@media (hover: hover) { .dn .btn.dn-wallet:hover { background: var(--wallet); opacity: .88; } }
/* Shown at once where Safari says there is Apple Pay, live a moment later
   when Stripe is ready: the button itself, only a little quieter. */
.dn .btn.dn-wallet--wait:disabled { opacity: .82; cursor: progress; }
/* Paying by card, under a wallet button: the same button, a card on it in
   place of the wallet's mark - two equal ways to pay, each plain to see. As
   a small link it was easy to miss, and outlined it faded on the dark page
   much as a black wallet button did. */
.dn .btn.dn-card { justify-content: center; width: 100%; padding: 14px 18px; font-size: 16px;
  background: var(--wallet); color: var(--wallet-ink); border-color: transparent;
  transition: opacity .15s ease; }
@media (hover: hover) { .dn .btn.dn-card:hover { background: var(--wallet); opacity: .88; } }
.dn-ico { width: 20px; height: 20px; flex: none; }
.dn-paytop { display: flex; align-items: stretch; gap: 6px; }
.dn-chips--sm { flex: 1; min-width: 0; gap: 6px; }
.dn-chips--sm .dn-chip { min-width: 0; padding: 9px 2px; font-size: 14px; border-radius: 10px; }
/* The own amount: a fifth small box in the same row, as wide as an amount.
   16px: Safari on iPhone zooms the whole page in on a smaller input. */
.dn-input--sm { flex: none; width: 22%; max-width: 104px; min-width: 62px; padding: 8px 6px; font-size: 16px;
  border-radius: 10px; text-align: center; }
/* ... and it reads as a box to write in: a dashed edge and a pencil while
   empty, the input's own look once it has focus or an amount. */
.dn-own { flex: none; width: 25%; max-width: 124px; min-width: 76px; display: flex; align-items: center;
  justify-content: center; gap: 5px; padding: 0 8px; border-radius: 10px; cursor: text;
  border: 1.5px dashed var(--line-2); background: transparent; color: var(--muted); }
.dn-own:focus-within { border: 1.5px solid var(--accent); background: var(--ink); }
/* Holding the amount, it is the chosen one - and looks it, like a chip. */
.dn-own:has(input:not(:placeholder-shown)) { border: 1.5px solid var(--accent); color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--ink)); }
.dn-own:has(input:not(:placeholder-shown)) .dn-input--sm { color: var(--accent); font-weight: 700; }
.dn-own:has(input:not(:placeholder-shown)):focus-within { border-color: var(--accent); }
.dn-own .dn-input--sm { width: 100%; min-width: 0; max-width: none; flex: 1; padding: 8px 0;
  border: 0; background: transparent; box-shadow: none; }
.dn-own .dn-input--sm::placeholder { color: var(--muted); font-size: 14px; font-weight: 600; opacity: 1; }
.dn-own .dn-ico { order: -1; width: 15px; height: 15px; }
.dn-own .dn-input--sm:not(:placeholder-shown) + .dn-ico,
.dn-own .dn-input--sm:focus + .dn-ico { display: none; }
dialog.dn { overflow-x: hidden; }
@media (max-width: 560px) {
  .dn-chips.dn-chips--sm { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.dn-back { font: inherit; font-size: 14px; cursor: pointer; border: 0; background: none;
  color: var(--muted); padding: 4px 0; }
.dn-back:hover { color: var(--text); }
.dn-sum { font-size: 1.2rem; font-family: var(--display); font-weight: 700; letter-spacing: -.02em; color: var(--text); }
.dn-fields { min-height: 120px; }
.dn-error { margin: 0; font-size: 14px; color: #ff6b6b; line-height: 1.5; }
.dn-lock { margin: -2px 0 0; font-size: 12px; color: var(--faint); line-height: 1.5; text-align: center; }
.dn-loading { margin: 26px 0; text-align: center; color: var(--muted); }
.dn-done { align-items: center; text-align: center; padding: 6px 0 4px; gap: 6px; }
.dn-done h3 { margin: 0; font-size: 1.2rem; }
.dn-done p { margin: 0; color: var(--muted); }
.dn-heart { line-height: 1; color: var(--heart); margin-bottom: 6px !important; }
.dn-bigheart { width: 56px; height: 56px; fill: currentColor; display: block; margin: 0 auto;
  filter: drop-shadow(0 8px 18px rgba(255, 45, 85, .45)); }
.dn-titleheart { width: 22px; height: 22px; fill: var(--heart); vertical-align: -3px; margin-inline-end: 9px; }
/* The show (nav.js): a full-screen layer, a popover in the top layer, that
   nothing can click. */
.dn-burst { position: fixed; inset: 0; width: 100vw; height: 100vh; max-width: none; max-height: none;
  margin: 0; padding: 0; border: 0; background: transparent; overflow: visible; pointer-events: none;
  color: var(--heart); }
.dn-burst::backdrop { background: transparent; }
.dn-burst > * { position: absolute; left: 0; top: 0; }
.dn-ring { width: 46px; height: 46px; border-radius: 50%; border: 2px solid var(--heart);
  box-shadow: 0 0 18px rgba(255, 55, 95, .45); opacity: 0; }
.dn-ring--hero { border-width: 3px; }
.dn-spark { width: 16px; height: 16px; fill: currentColor; opacity: 0; }
.dn-hero { overflow: visible; opacity: 0; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) dialog.dn::backdrop { background: rgba(20, 26, 38, .45); }
  :root:not([data-theme="dark"]) .dn-error { color: #c62828; }
}
:root[data-theme="light"] dialog.dn::backdrop { background: rgba(20, 26, 38, .45); }
:root[data-theme="light"] .dn-error { color: #c62828; }
@media (max-width: 560px) {
  dialog.dn { width: 100%; max-width: 100%; margin: auto 0 0; border-radius: 18px 18px 0 0;
    border-bottom: 0; max-height: calc(100dvh - 24px); }
  .dn-chips { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .dn-body { padding-bottom: max(22px, env(safe-area-inset-bottom)); }
}

/* "How to install" (app.js): the help page's answer for one system in a
   dialog over the downloads - head and foot stay, the steps scroll. */
dialog.howdlg {
  width: min(620px, calc(100vw - 32px)); max-height: min(760px, calc(100dvh - 32px));
  padding: 0; border: 1px solid var(--line-2); border-radius: 18px;
  background: var(--surface); color: var(--text); overflow: hidden;
  box-shadow: 0 30px 80px -20px rgba(0, 0, 0, .6);
}
dialog.howdlg[open] { display: flex; flex-direction: column; animation: how-in .2s ease-out; }
dialog.howdlg::backdrop { background: rgba(5, 7, 10, .66); }
@keyframes how-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  @keyframes how-in { from { opacity: 0; } to { opacity: 1; } }
}
.howdlg-head { display: flex; align-items: center; gap: 12px; padding: 20px 22px 14px;
  border-bottom: 1px solid var(--line); }
.howdlg-head h2 { flex: 1; min-width: 0; margin: 0; font-size: 1.2rem; letter-spacing: -.03em; line-height: 1.3; }
.howdlg-head .dn-x { margin: -4px 0; margin-inline-end: -6px; }
.howdlg-body.help-a, .howdlg-body.notes { flex: 1 1 auto; min-height: 0; overflow: auto;
  overscroll-behavior: contain; max-width: none; padding: 18px 22px 8px; font-size: 16px; }
.howdlg-body pre { background: var(--bg); }
.howdlg-foot { margin: 0; padding: 14px 22px 18px; border-top: 1px solid var(--line); }
.howdlg-foot a { color: var(--accent); font-weight: 600; font-size: 15px; }
.howdlg-foot a::after { content: " \203A"; }
[dir="rtl"] .howdlg-foot a::after { content: " \2039"; }
.howdlg-foot a:hover { text-decoration: underline; text-underline-offset: 3px; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) dialog.howdlg::backdrop { background: rgba(20, 26, 38, .45); }
}
:root[data-theme="light"] dialog.howdlg::backdrop { background: rgba(20, 26, 38, .45); }
@media (max-width: 560px) {
  dialog.howdlg { width: 100%; max-width: 100%; margin: auto 0 0; border-radius: 18px 18px 0 0;
    border-bottom: 0; max-height: calc(100dvh - 24px); }
  .howdlg-foot { padding-bottom: max(18px, env(safe-area-inset-bottom)); }
}

/* ---- phones ------------------------------------------------------------- */
/* dopeIPTV is a computer's app, so on a phone the page is a short tour, not
   a download page: rows that swipe instead of long stacks, the downloads
   folded behind a button (app.js, .is-phone) with "send the link" first. */
.dl-phone { display: none; margin: 0 0 18px; padding: 22px; border-radius: var(--radius);
  background: var(--surface); text-align: center; }
.dl-phone p { margin: 0; color: var(--muted); font-size: 15px; line-height: 1.6; }
.dl-phone-act { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 14px; }
.dl-phone-act .btn { flex: 1 1 auto; justify-content: center; }
.dl-phone-act .btn[hidden] { display: none; }
@media (max-width: 720px) {
  .wrap { padding: 0 18px; }
  section { padding: 56px 0; }
  .sec-head { margin-bottom: 22px; }
  .hero { padding: 44px 0 24px; }
  /* The lede is one sentence, so a phone keeps it - smaller, under the
     heading - and the app window below cut down to its screen and timeline. */
  .hero .lede { font-size: 1.12rem; line-height: 1.45; margin: 14px auto 0;
    max-width: 30ch; text-wrap: balance; }
  .hero .mock { width: 100%; border-radius: 18px; }
  .hero .mock .titlebar, .hero .mock .chans { display: none; }
  .hero .mock .body { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .hero .mock .screen { min-height: 0; aspect-ratio: 16 / 9; margin: 10px; }
  .hero .mock .timeline { margin: 0 10px 10px; }
  .hero-grid { gap: 28px; }
  h1 { font-size: 2rem; }
  .cta-row { margin-top: 22px; flex-wrap: nowrap; }
  .cta-row .btn { flex: 1 1 auto; justify-content: center; padding: 12px 14px; }

  /* the formats as one line that swipes */
  .strip .wrap { flex-wrap: nowrap; justify-content: flex-start; overflow-x: auto;
    scrollbar-width: none; padding-bottom: 4px; }
  .strip .wrap::-webkit-scrollbar { display: none; }
  .chip { flex: none; }

  /* features: a list, the icon beside the text */
  .feats { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .feat { display: flex; gap: 16px; align-items: flex-start; }
  .feat .ic { flex: none; width: 28px; height: 28px; margin: 1px 0 0; }
  /* screenshots: a row to swipe through, the next one peeking in at the
     edge so it is clear there is more */
  .shots { display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory;
    margin: 0 -18px; padding: 2px 18px 12px; scroll-padding-inline: 18px; scrollbar-width: none; }
  .shots::-webkit-scrollbar { display: none; }
  .shots > .shot { flex: 0 0 88%; scroll-snap-align: start; }

  .dl-phone { display: block; }

  /* Fewer boxes: on a narrow screen the page reads as one column of text,
     and only the two swipe rows keep their cards. */
  .dl-phone { background: none; padding: 0; border-radius: 0; margin-bottom: 8px; }
  /* ...but the donate band keeps its card, its glow and its heart: on a
     phone it is the one thing to do here, the app being for computers. */
  .donate { text-align: center; gap: 20px; padding: 26px 20px 24px; }
  .donate-text p { margin-inline: auto; }
  .donate-act { align-items: center; }
  .donate-btn { min-width: 220px; }
}
.is-phone #download:not(.dl-open) .dlx,
.is-phone #download:not(.dl-open) .dls,
.is-phone #download:not(.dl-open) .autonote { display: none; }
.is-phone .dl-phone { display: block; }
/* On a phone the download section is the note and its two buttons: the big
   heading and the version line are for whoever is about to download. */
.is-phone #download .dl-head { display: none; }
/* No button at the top of a phone: the app cannot be installed there, and
   "Download" promised what the phone could not do. The line under the
   heading says where it runs; "Send the link" waits in the download
   section, and Download is in the menu. An app-store button comes back
   here once there is a phone app. */
.is-phone .hero .cta-row { display: none; }
/* The downloads go last on a phone: nothing there installs on it. What to
   do here - read, see, support - comes first. That swaps the last two bands
   (a computer has downloads, then donate), so their colours swap with them
   and the bands still take turns. */
.is-phone main { display: flex; flex-direction: column; }
.is-phone main > #download { order: 1; }
.is-phone #donate { background: var(--surface); }
.is-phone #download { background: var(--bg); }
.is-phone #download .dlx-card { background: var(--surface); }
.is-phone #donate .donate {
  background: radial-gradient(90% 60% at 50% 100%, color-mix(in srgb, var(--heart) 22%, transparent), transparent 75%),
    var(--bg); }
.is-phone .bar .btn { display: none; }

/* ---- motion ------------------------------------------------------------- */
/* Apple-style, and quiet: what scrolls into view rises into place (app.js
   gives it .rv, then .in; only what starts below the fold, so nothing on the
   first screen ever blinks), and in the hero's app window the channels take
   turns and the timeline plays.
   For anyone who asked their system for less motion it is calm, not still:
   what fades stays, what moves, grows or turns is left out. */
.rv { opacity: 0; transform: translateY(10px); }
.rv.in { opacity: 1; transform: none; transition: opacity .6s ease-out var(--d, 0s),
  transform .6s ease-out var(--d, 0s); }
.chan { transition: background .5s ease, color .5s ease; }
.chan .n { transition: border-color .5s ease, background .5s ease; }
.screen::before { content: ""; position: absolute; inset: 0; background: var(--tint, #4c8dff); opacity: .22;
  transition: background-color 1s ease; }

@media (prefers-reduced-motion: reduce) {
  .rv { transform: none; }
  .rv.in { transform: none; transition: opacity .7s ease-out var(--d, 0s); }
  .mock.alive .scan { animation: none; }
}

/* ---- the feature explorer (app.js) --------------------------------------- */
/* The titles as tabs, a small screen that shows each feature at work, one
   line of text at a time. It steps on by itself every few seconds (a thin
   bar fills under the current tab) until the visitor picks one. The screen
   is always dark - it is a TV - and its scenes are drawn with CSS, words
   kept out of them so no language is needed. */
.fxp { --dur: 6s; display: grid; grid-template-columns: minmax(220px, 290px) minmax(0, 1fr); gap: 44px;
  align-items: start; max-width: 1060px; margin: 8px auto 0; }
.fxp[hidden] { display: none; }
.fxp-tabs { display: flex; flex-direction: column; gap: 2px; }
.fxp-tab { position: relative; display: flex; align-items: center; gap: 12px; width: 100%; padding: 11px 14px;
  border: 0; border-radius: 14px; background: none; color: var(--muted); font: inherit; font-size: 15px; font-weight: 500;
  text-align: start; cursor: pointer; transition: background .2s ease, color .2s ease; }
.fxp-tab .ic { flex: none; width: 22px; height: 22px; margin: 0; color: currentColor; fill: none; stroke: currentColor;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; transition: color .2s ease; }
.fxp-tab:hover { color: var(--text); background: color-mix(in srgb, var(--text) 5%, transparent); }
.fxp-tab.is-on { color: var(--text); background: var(--surface); }
.fxp-tab.is-on .ic { color: var(--accent); }
.fxp-tab .bar { position: absolute; left: 14px; right: 14px; bottom: 4px; height: 2px; border-radius: 2px; overflow: hidden; }
.fxp-tab.playing .bar::after { content: ""; display: block; height: 100%; background: var(--accent);
  transform: scaleX(0); transform-origin: left; animation: fx-bar var(--dur) linear forwards; }
[dir="rtl"] .fxp-tab.playing .bar::after { transform-origin: right; }
@keyframes fx-bar { to { transform: none; } }
.fxp-stage { position: relative; aspect-ratio: 16 / 10; border-radius: 26px; overflow: hidden; color: #f5f5f7;
  background: radial-gradient(120% 90% at 50% 0%, #1f2638, #0b0b0e 70%); box-shadow: 0 30px 70px -40px rgba(0, 0, 0, .6); }
.fxp-cap h3 { margin: 24px 0 0; font-family: var(--display); font-size: clamp(1.3rem, 2.4vw, 1.7rem); font-weight: 700;
  letter-spacing: -.025em; line-height: 1.2; }
.fxp-cap p { margin: 8px 0 0; max-width: 56ch; color: var(--muted); font-size: 17px; line-height: 1.5; }
.fxp-cap.swap { animation: fx-cap .45s ease; }
@keyframes fx-cap { from { opacity: 0; transform: translateY(6px); } }

/* scenes */
.sc { position: absolute; inset: 0; opacity: 0; visibility: hidden; transition: opacity .55s ease, visibility 0s .55s; }
.sc.on { opacity: 1; visibility: visible; transition: opacity .55s ease; }
.sc .h1 { background: linear-gradient(135deg, #3b6fd8, #17244a); }
.sc .h2 { background: linear-gradient(135deg, #d0573f, #43191a); }
.sc .h3 { background: linear-gradient(135deg, #2fa66a, #11331f); }
.sc .h4 { background: linear-gradient(135deg, #d39a2c, #45300f); }
.sc .h5 { background: linear-gradient(135deg, #2aa7c9, #0e3340); }
.sc .h6 { background: linear-gradient(135deg, #8a5cf0, #281a52); }
.sc .v { position: absolute; inset: 7% 7% 24% 7%; border-radius: 16px; }
.sc .track { position: absolute; left: 7%; right: 7%; bottom: 11%; height: 6px; border-radius: 3px; background: rgba(255, 255, 255, .14); }

.sc-ts .arch { position: absolute; inset: 0; border-radius: 3px; background: linear-gradient(90deg, rgba(255,255,255,.34), rgba(255,255,255,.14)); }
.sc-ts .head { position: absolute; top: -7px; left: 68%; width: 4px; height: 20px; margin-left: -2px; border-radius: 2px; background: #fff; }
.sc-ts .head b { position: absolute; bottom: 28px; left: 50%; transform: translateX(-50%); padding: 3px 9px; border-radius: 999px;
  background: rgba(0, 0, 0, .6); font-size: 12px; font-weight: 600; white-space: nowrap; }
.sc-ts .head b { animation: ts-pulse 2.4s ease-in-out infinite; }
@keyframes ts-pulse { 50% { transform: translateX(-50%) scale(1.08); } }
/* Going back in time, drawn without sideways motion: a rewind arrow turning
   in the picture while the playhead stands behind live. */
.sc-ts .v { display: grid; place-items: center; }
.sc-ts .rw { width: 22%; height: auto; fill: none; stroke: rgba(255, 255, 255, .9); stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round; animation: ts-rewind 2.6s cubic-bezier(.5, 0, .3, 1) infinite; }
@keyframes ts-rewind { 0% { transform: rotate(0); } 60%, 100% { transform: rotate(-360deg); } }
.sc-ts .live { position: absolute; right: 0; top: 14px; padding: 2px 8px; border-radius: 999px; background: #ff453a;
  font-size: 10px; font-weight: 700; letter-spacing: .06em; }

.sc-mv .mvg { position: absolute; inset: 8%; display: grid; grid-template-columns: repeat(3, 1fr); gap: 3.5%; }
.sc-mv .mvg i { position: relative; border-radius: 10px; }
.sc-mv .mvg i::after { content: ""; position: absolute; inset: -5px; border: 3px solid #4c8dff; border-radius: 15px;
  opacity: 0; animation: mv-sel 6.3s infinite; }
.sc-mv .mvg i:nth-child(2)::after { animation-delay: .7s; } .sc-mv .mvg i:nth-child(3)::after { animation-delay: 1.4s; }
.sc-mv .mvg i:nth-child(4)::after { animation-delay: 2.1s; } .sc-mv .mvg i:nth-child(5)::after { animation-delay: 2.8s; }
.sc-mv .mvg i:nth-child(6)::after { animation-delay: 3.5s; } .sc-mv .mvg i:nth-child(7)::after { animation-delay: 4.2s; }
.sc-mv .mvg i:nth-child(8)::after { animation-delay: 4.9s; } .sc-mv .mvg i:nth-child(9)::after { animation-delay: 5.6s; }
@keyframes mv-sel { 0%, 10% { opacity: 1; } 11%, 100% { opacity: 0; } }

.sc-pz .pz { position: absolute; left: 50%; top: 38%; width: 76px; height: 76px; margin: -38px 0 0 -38px; border-radius: 50%;
  background: rgba(0, 0, 0, .5); animation: pz-beat 2.4s ease-in-out infinite; }
.sc-pz .pz::before, .sc-pz .pz::after { content: ""; position: absolute; top: 26px; width: 8px; height: 24px; border-radius: 2px; background: #fff; }
.sc-pz .pz::before { left: 27px; } .sc-pz .pz::after { right: 27px; }
@keyframes pz-beat { 50% { transform: scale(1.06); } }
.sc-pz .seen { position: absolute; left: 0; top: 0; bottom: 0; width: 40%; border-radius: 3px 0 0 3px; background: #fff; }
.sc-pz .buf { position: absolute; left: 40%; top: 0; bottom: 0; width: 52%; background: rgba(76, 141, 255, .75);
  transform: scaleX(.077); transform-origin: left; animation: pz-buf 6s ease-out infinite; }
@keyframes pz-buf { to { transform: none; } }

.sc-epg .epg { position: absolute; inset: 9% 7%; display: flex; flex-direction: column; gap: 5%; }
.sc-epg .r { flex: 1; display: flex; gap: 2%; }
.sc-epg .r b { width: 9%; border-radius: 8px; background: rgba(255, 255, 255, .16); }
.sc-epg .r i { border-radius: 8px; background: rgba(255, 255, 255, .07); border: 1px solid rgba(255, 255, 255, .08); }
.sc-epg .w1 { flex: 1; } .sc-epg .w2 { flex: 2; } .sc-epg .w3 { flex: 3; } .sc-epg .w4 { flex: 4; }
.sc-epg .r i.on { background: rgba(76, 141, 255, .38); border-color: #4c8dff; }
.sc-epg .now { position: absolute; top: 5%; bottom: 5%; left: 47%; width: 2px; background: #ff453a; }
.sc-epg .r i.on { position: relative; }
.sc-epg .r i.on::after { content: ""; position: absolute; inset: -1px; border-radius: 8px; background: rgba(76, 141, 255, .27);
  box-shadow: 0 0 0 3px rgba(76, 141, 255, .25); opacity: 0; animation: epg-on 2.4s ease-in-out infinite; }
@keyframes epg-on { 50% { opacity: 1; } }

.sc-rec .rec { position: absolute; left: 11%; top: 12%; display: flex; align-items: center; gap: 8px; padding: 6px 12px;
  border-radius: 999px; background: rgba(0, 0, 0, .55); font-size: 13px; font-weight: 700; letter-spacing: .02em; }
.sc-rec .rec i { width: 10px; height: 10px; border-radius: 50%; background: #ff453a; animation: rec-blink 1.2s infinite; }
@keyframes rec-blink { 50% { opacity: .2; } }
.sc-rec .disk { position: absolute; left: 7%; right: 7%; bottom: 11%; height: 6px; border-radius: 3px; background: rgba(255, 255, 255, .14);
  overflow: hidden; }
.sc-rec .disk i { display: block; height: 100%; border-radius: 3px; background: #ff453a; transform: translateX(-82%);
  animation: rec-fill 9s linear infinite; }
@keyframes rec-fill { to { transform: translateX(-34%); } }

.sc-prov .pl { position: absolute; inset: 14% 16%; display: flex; flex-direction: column; justify-content: center; gap: 6%; }
.sc-prov .pl div { display: flex; align-items: center; gap: 14px; padding: 4% 5%; border-radius: 16px; font-size: 15px; font-weight: 600;
  background: rgba(255, 255, 255, .06); border: 1px solid rgba(255, 255, 255, .09); }
.sc-prov .pl b { flex: none; width: 30px; height: 30px; border-radius: 9px; }
.sc-prov .pl span { flex: 1; }
.sc-prov .pl i { position: relative; flex: none; width: 22px; height: 22px; border-radius: 50%; background: #34c759; opacity: 0;
  transform: scale(.5); animation: prov-ok 6s infinite; }
.sc-prov .pl i::after { content: ""; position: absolute; left: 7px; top: 4px; width: 5px; height: 10px; border: solid #fff;
  border-width: 0 2.5px 2.5px 0; transform: rotate(45deg); }
.sc-prov .pl div:nth-child(2) i { animation-delay: .6s; } .sc-prov .pl div:nth-child(3) i { animation-delay: 1.2s; }
@keyframes prov-ok { 8%, 82% { opacity: 1; transform: none; } 92%, 100% { opacity: 0; transform: scale(.5); } }

.sc-play .btn { position: absolute; left: 50%; top: 38%; width: 80px; height: 80px; margin: -40px 0 0 -40px; border-radius: 50%;
  background: rgba(255, 255, 255, .92); }
.sc-play .btn::after { content: ""; position: absolute; left: 32px; top: 24px; border-style: solid; border-width: 16px 0 16px 26px;
  border-color: transparent transparent transparent #111; }
.sc-play .ring { position: absolute; left: 50%; top: 38%; width: 80px; height: 80px; margin: -40px 0 0 -40px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .6); animation: play-ring 2.4s ease-out infinite; }
.sc-play .r2 { animation-delay: 1.2s; }
@keyframes play-ring { from { transform: scale(1); opacity: .8; } to { transform: scale(2.4); opacity: 0; } }
.sc-play .tag { position: absolute; right: 10%; bottom: 29%; padding: 2px 9px; border-radius: 999px; background: rgba(0, 0, 0, .5);
  font-size: 12px; font-weight: 600; font-family: var(--mono); }

.sc-files .fold { position: absolute; left: 8%; top: 9%; display: flex; align-items: center; gap: 8px; padding: 6px 12px 6px 9px;
  border-radius: 999px; background: rgba(255, 255, 255, .08); font-size: 13px; font-weight: 600; font-family: var(--mono); }
.sc-files .fold svg { width: 18px; height: 18px; fill: none; stroke: #5b96ff; stroke-width: 1.8; stroke-linejoin: round; }
.sc-files .posters { position: absolute; inset: 24% 8% 9%; display: grid; grid-template-columns: repeat(4, 1fr); gap: 5% 3%; }
.sc-files .posters i { position: relative; border-radius: 10px; animation: files-in 6s ease-out infinite; }
.sc-files .posters i:nth-child(2) { animation-delay: .12s; } .sc-files .posters i:nth-child(3) { animation-delay: .24s; }
.sc-files .posters i:nth-child(4) { animation-delay: .36s; } .sc-files .posters i:nth-child(5) { animation-delay: .48s; }
.sc-files .posters i:nth-child(6) { animation-delay: .6s; } .sc-files .posters i:nth-child(7) { animation-delay: .72s; }
.sc-files .posters i:nth-child(8) { animation-delay: .84s; }
@keyframes files-in { 0% { opacity: 0; transform: translateY(10px); } 10%, 100% { opacity: 1; transform: none; } }
.sc-files .posters i.prog::after { content: ""; position: absolute; left: 9%; right: 9%; bottom: 8%; height: 4px; border-radius: 2px;
  background: linear-gradient(90deg, #4c8dff 62%, rgba(255, 255, 255, .35) 62%); }

.sc-cast .tv { position: absolute; left: 22%; right: 22%; top: 14%; bottom: 22%; }
.sc-cast .scr { position: absolute; inset: 0; border-radius: 14px; border: 6px solid #2a2a2e; display: grid; place-items: center; }
.sc-cast .scr svg { width: 34%; height: 34%; fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; }
.sc-cast .scr path { opacity: .25; animation: cast-wave 1.8s infinite; }
.sc-cast .scr .a2 { animation-delay: .3s; } .sc-cast .scr .a3 { animation-delay: .6s; }
@keyframes cast-wave { 20%, 50% { opacity: 1; } }
.sc-cast .stand { position: absolute; left: 38%; right: 38%; bottom: -12%; height: 6%; border-radius: 4px; background: #2a2a2e; }

.sc-subs .cc { position: absolute; right: 10%; top: 11%; padding: 3px 8px; border-radius: 6px; border: 2px solid rgba(255,255,255,.85);
  font-size: 12px; font-weight: 800; }
.sc-subs .subl { position: absolute; left: 50%; bottom: 30%; width: 52%; transform: translateX(-50%); display: flex; flex-direction: column;
  align-items: center; gap: 9px; animation: subs-swap 4s infinite; }
.sc-subs .subl i { width: 100%; height: 13px; border-radius: 3px; background: rgba(255, 255, 255, .92); box-shadow: 0 0 0 5px rgba(0, 0, 0, .55); }
.sc-subs .subl .s2 { width: 62%; }
@keyframes subs-swap { 0%, 8% { opacity: 0; } 15%, 85% { opacity: 1; } 92%, 100% { opacity: 0; } }

.sc-keys .keys { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 4.5%; }
.sc-keys .keys i { position: relative; isolation: isolate; display: grid; place-items: center; width: 18%; aspect-ratio: 1;
  color: #e5e5ea; animation: key-press 3.6s infinite; }
.sc-keys .keys i::before, .sc-keys .keys i::after { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: 20%; }
.sc-keys .keys i::before { background: #09090a; transform: translateY(7px); animation: key-side 3.6s infinite; }
.sc-keys .keys i::after { background: linear-gradient(#2e2e33, #1d1d20); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); }
.sc-keys .keys svg { width: 42%; height: 42%; fill: currentColor; animation: key-lit 3.6s infinite; }
.sc-keys .keys i:nth-child(2), .sc-keys .keys i:nth-child(2)::before, .sc-keys .keys i:nth-child(2) svg { animation-delay: 1.2s; }
.sc-keys .keys i:nth-child(3), .sc-keys .keys i:nth-child(3)::before, .sc-keys .keys i:nth-child(3) svg { animation-delay: 2.4s; }
@keyframes key-press { 0%, 14% { transform: translateY(6px); } 22%, 100% { transform: none; } }
@keyframes key-side { 0%, 14% { transform: translateY(1px); } 22%, 100% { transform: translateY(7px); } }
@keyframes key-lit { 0%, 14% { opacity: 1; } 22%, 100% { opacity: .62; } }

.sc-home .hero { position: absolute; left: 6%; right: 6%; top: 7%; height: 36%; border-radius: 14px; }
.sc-home .shelf { position: absolute; left: 6%; right: 6%; display: grid; grid-template-columns: repeat(4, 1fr); gap: 3%; height: 19%; }
.sc-home .s1 { top: 49%; } .sc-home .s2 { top: 73%; }
.sc-home .shelf i { position: relative; border-radius: 10px; }
.sc-home .shelf i.new::before, .sc-home .shelf i.new::after { content: ""; position: absolute; right: 8px; top: 8px;
  width: 10px; height: 10px; border-radius: 50%; background: #4c8dff; }
.sc-home .shelf i.new::before { opacity: 0; animation: home-new 1.8s infinite; }
@keyframes home-new { 0% { transform: scale(1); opacity: .7; } 70%, 100% { transform: scale(3); opacity: 0; } }

/* Only the scene on show moves, and only while the explorer is on screen
   (app.js sets .in-view). !important because each scene's "animation"
   shorthand above resets the play state: without it all twelve scenes ran
   all the time, wherever the page was scrolled - in Chrome a style, layout
   and paint every frame, 120 of them a second on a 120 Hz screen. And each
   moves only transform and opacity, which the GPU animates on its own. */
.sc:not(.on) *, .sc:not(.on) *::before, .sc:not(.on) *::after,
.fxp:not(.in-view) .sc *, .fxp:not(.in-view) .sc *::before, .fxp:not(.in-view) .sc *::after {
  animation-play-state: paused !important; }


@media (max-width: 860px) {
  .fxp { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .fxp-tabs { flex-direction: row; overflow-x: auto; margin: 0 -24px; padding: 2px 24px 6px; scrollbar-width: none; }
  .fxp-tabs::-webkit-scrollbar { display: none; }
  .fxp-tab { flex: none; width: auto; padding: 9px 15px; border-radius: 999px; background: var(--surface); font-size: 14px; }
  .fxp-tab .ic { width: 18px; height: 18px; }
  .fxp-tab.is-on { background: var(--text); color: var(--bg); }
  .fxp-tab.is-on .ic { color: var(--bg); }
  .fxp-tab .bar { left: 16px; right: 16px; bottom: 3px; }
  .fxp-tab.is-on .bar::after { background: var(--bg); }
  .fxp-stage { border-radius: 20px; }
}
@media (max-width: 720px) {
  .fxp-tabs { margin: 0 -18px; padding-inline: 18px; }
  .sc-prov .pl { inset: 10% 8%; } .sc-prov .pl div { font-size: 13px; gap: 10px; }
  .sc-prov .pl b { width: 24px; height: 24px; }
  .sc-pz .pz, .sc-play .btn, .sc-play .ring { transform-origin: center; scale: .75; }
  .fxp-cap h3 { margin-top: 18px; }
  .fxp-cap p { font-size: 16px; }
}
/* Less motion asked for: the scenes keep what fades (a frame lighting up,
   a blinking dot, a line of subtitles) and lose what moves or grows. */
@media (prefers-reduced-motion: reduce) {
  .sc-ts .rw, .sc-ts .head b, .sc-pz .pz, .sc-rec .disk i, .sc-keys .keys i,
  .sc-keys .keys i::before { animation: none !important; }
  .sc-pz .buf { transform: none; animation-name: calm-pulse !important; }
  .sc-prov .pl i { transform: none; animation-name: prov-ok-calm !important; }
  .sc-files .posters i { animation-name: files-in-calm !important; }
  .sc-play .ring { transform: scale(1.3); animation-name: calm-ring !important; }
  .sc-play .r2 { display: none; }
  .sc-home .shelf i.new::before { transform: scale(2.4); animation-name: calm-ring !important; }
  .fxp-cap.swap { animation-name: calm-fade; }
  .fxp { --dur: 8s; }                       /* app.js steps on more slowly too */
  .fxp-tab.playing .bar::after { transform: none; animation-name: calm-bar; }
  @keyframes calm-bar { from { opacity: 0; } }
  @keyframes calm-pulse { 50% { opacity: .35; } }
  @keyframes calm-ring { 0%, 100% { opacity: 0; } 40% { opacity: .55; } }
  @keyframes calm-fade { from { opacity: 0; } }
  @keyframes prov-ok-calm { 0% { opacity: 0; } 8%, 82% { opacity: 1; } 92%, 100% { opacity: 0; } }
  @keyframes files-in-calm { 0% { opacity: 0; } 10%, 100% { opacity: 1; } }
}

/* ---- living details ---------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) and (min-width: 721px) {
  /* the hero arrives in steps (computers only: on a phone it is simply there) */
  .hero h1, .hero .lede, .hero .cta-row, .hero .platnote, .hero .mock {
    animation: hero-in .6s ease-out both; }
  .hero h1 { animation-delay: .08s; } .hero .lede { animation-delay: .16s; }
  .hero .cta-row { animation-delay: .24s; } .hero .platnote { animation-delay: .3s; }
  .hero .mock { animation-delay: .3s; }
  @keyframes hero-in { from { opacity: 0; transform: translateY(8px); } }
  /* the download cards lift a little under the pointer */
  .dlx-card { transition: transform .35s cubic-bezier(.2, .7, .2, 1), box-shadow .35s ease; }
  .dlx-card:hover { transform: translateY(-4px); box-shadow: 0 26px 50px -30px rgba(0, 0, 0, .5); }
  .dlx-card.is-mine:hover { box-shadow: inset 0 0 0 2px var(--accent), 0 26px 50px -30px rgba(0, 0, 0, .5); }
  /* the donate band, while it is on screen */
  /* On a computer, calm: two soft beats and one drift of hearts each
     time it comes into view, then still. Pointed at, it beats on for as
     long as the pointer stays. */
  .donate.is-live .donate-big { animation: heart-beat 2.8s ease-in-out 2; }
  .donate.is-live .donate-fx i { animation: heart-drift 8s linear 1; }
  .donate.is-live .donate-btn::after { animation: heart-glow 2.8s ease-out 2; }
  .donate.is-live .donate-btn .donate-heart { animation: heart-beat 2.8s ease-in-out 2; }
  @media (hover: hover) {
    .donate.is-live:hover .donate-big, .donate.is-live:hover .donate-btn::after,
    .donate.is-live:hover .donate-btn .donate-heart,
    .donate.is-live:hover .donate-fx i { animation-iteration-count: infinite; }
  }
  /* the dialog's own heart beats twice as it opens */
  @keyframes heart-beat { 0%, 36%, 100% { transform: scale(1); } 7% { transform: scale(1.08); }
    14% { transform: scale(.98); } 22% { transform: scale(1.04); } }
  @keyframes heart-drift { 0% { transform: translate(0, 0) scale(.6) rotate(-10deg); opacity: 0; }
    12% { opacity: .5; } 100% { transform: translate(22px, -250px) scale(1.15) rotate(12deg); opacity: 0; } }
  @keyframes heart-glow { 0% { transform: none; opacity: .9; } 32%, 100% { transform: scale(1.12, 1.6); opacity: 0; } }
  dialog.dn[open] .dn-titleheart { animation: heart-twice 1.1s .9s ease-in-out both; }
  @keyframes heart-twice { 0%, 100% { transform: scale(1); } 15% { transform: scale(1.3); } 30% { transform: scale(.95); }
    45% { transform: scale(1.18); } 60% { transform: scale(1); } }
  /* the heart beats when you mean it */
  .donate-btn:hover .donate-heart, .navdonate:hover .heart, nav.links .navheart:hover svg { animation: beat .9s ease-in-out infinite; }
  @keyframes beat { 0%, 100% { transform: scale(1); } 20% { transform: scale(1.22); } 40% { transform: scale(.96); } 60% { transform: scale(1.12); } }
}

/* Less motion asked for: the hero fades in, the donate band's heart and
   button glow and a few hearts light up in place - nothing moves. */
@media (prefers-reduced-motion: reduce) {
  @media (min-width: 721px) {
    .hero h1, .hero .lede, .hero .cta-row, .hero .platnote, .hero .mock {
      animation: calm-fade .7s ease-out both; }
    .hero h1 { animation-delay: .08s; } .hero .lede { animation-delay: .16s; }
    .hero .cta-row { animation-delay: .24s; } .hero .platnote, .hero .mock { animation-delay: .3s; }
  }
  .donate .donate-btn::after { transform: scale(1.08, 1.45); }
  .donate.is-live .donate-big { animation: calm-heart 3s ease-in-out 3; }
  .donate.is-live .donate-btn::after { animation: calm-glow 3s ease-in-out 3; }
  .donate-fx i { bottom: auto; }
  .donate-fx i:nth-of-type(1) { top: 26%; } .donate-fx i:nth-of-type(2) { top: 58%; }
  .donate-fx i:nth-of-type(3) { top: 16%; } .donate-fx i:nth-of-type(4) { top: 72%; }
  .donate-fx i:nth-of-type(5) { top: 44%; } .donate-fx i:nth-of-type(6) { top: 30%; }
  .donate.is-live .donate-fx i { animation: calm-twinkle 7.8s ease-in-out 1; }
  @media (hover: hover) {
    .donate.is-live:hover .donate-big, .donate.is-live:hover .donate-btn::after,
    .donate.is-live:hover .donate-fx i { animation-iteration-count: infinite; }
  }
  dialog.dn[open] .dn-titleheart { animation: calm-twice 1.3s .35s ease-in-out both; }
  .owner-note { animation: calm-fade .35s ease-out both; }
  .owner-note.is-leaving { animation: calm-out .26s ease-in both; }
  @keyframes calm-fade { from { opacity: 0; } }
  @keyframes calm-out { to { opacity: 0; } }
  @keyframes calm-heart { 50% { opacity: .22; } }
  @keyframes calm-glow { 0%, 100% { opacity: 0; } 50% { opacity: .5; } }
  @keyframes calm-twinkle { 0%, 100% { opacity: 0; } 25% { opacity: .45; } 50% { opacity: 0; } }
  @keyframes calm-twice { 0%, 50%, 100% { opacity: 1; } 25%, 75% { opacity: .35; } }
}

/* On a phone, where nothing else moves, the donate band is alive while it
   is on screen: the button's heart beats twice every few seconds and a few
   hearts drift up behind it, slowly. Only transform and opacity. */
@media (prefers-reduced-motion: no-preference) and (max-width: 720px) {
  .donate.is-live .donate-btn .donate-heart { animation: heart-beat-ph 4.2s ease-in-out 1s infinite; }
  .donate.is-live .donate-big { animation: heart-beat-ph 4.2s ease-in-out 1s infinite; }
  .donate.is-live .donate-fx i { animation: heart-drift-ph 9s linear infinite; }
  .donate.is-live .donate-fx i:nth-of-type(n+4) { display: none; }
  .donate-fx i:nth-of-type(1) { left: 62%; } .donate-fx i:nth-of-type(2) { left: 78%; animation-delay: 3s; }
  .donate-fx i:nth-of-type(3) { left: 90%; animation-delay: 6s; }
  @keyframes heart-beat-ph { 0%, 30%, 100% { transform: scale(1); } 6% { transform: scale(1.14); }
    12% { transform: scale(.97); } 18% { transform: scale(1.08); } }
  @keyframes heart-drift-ph { 0% { transform: translate(0, 0) scale(.6) rotate(-10deg); opacity: 0; }
    14% { opacity: .5; } 100% { transform: translate(-16px, -210px) scale(1.1) rotate(10deg); opacity: 0; } }
}

/* The header's heart beats softly now and then: two small beats, then
   five seconds still - there, never busy. Only a transform (the GPU's),
   and pointed at it beats faster (above). Less motion asked for: it glows. */
@media (prefers-reduced-motion: no-preference) {
  nav.links a.navdonate .heart, nav.links .navheart svg { animation: nav-heart 6s ease-in-out 2s infinite; }
  nav.links a.navdonate:hover .heart, nav.links .navheart:hover svg { animation: beat .9s ease-in-out infinite; }
  @keyframes nav-heart { 0%, 16%, 100% { transform: scale(1); } 4% { transform: scale(1.18); }
    8% { transform: scale(.96); } 12% { transform: scale(1.1); } }
}
@media (prefers-reduced-motion: reduce) {
  nav.links a.navdonate .heart, nav.links .navheart svg { animation: nav-glow 6s ease-in-out 2s infinite; }
  @keyframes nav-glow { 0%, 20%, 100% { opacity: 1; } 8% { opacity: .45; } }
}

/* ---- sections in bands -------------------------------------------------- */
/* As Apple's pages are: the sections take turns, white and light grey (near
   black and graphite in dark mode), so on a phone above all it is clear
   where one ends and the next begins. What sat on the grey before sits on
   the page colour inside a grey band. */
/* hero, features, shots, downloads, donate - the donate card on the page
   colour's opposite, so it stays a card in its band. */
#features, #download { background: var(--surface); }
#features .fxp-tab.is-on { background: var(--bg); }
#download .dlx-card { background: var(--bg); }
#donate .donate {
  background: radial-gradient(55% 130% at 86% 50%, color-mix(in srgb, var(--heart) 24%, transparent), transparent 72%),
    var(--surface); }
#download .dlx-big.alt, #download .dlx.has-mine .dlx-card:not(.is-mine) .dlx-big { background: var(--surface); }
footer { background: var(--bg); border-top: 1px solid var(--line); margin-top: 0; }
@media (max-width: 720px) {
  section { padding: 52px 0; }
  .sec-head h2 { font-size: 1.75rem; }
}
@media (max-width: 860px) {
  #features .fxp-tab { background: var(--bg); }
  #features .fxp-tab.is-on { background: var(--text); }
}

/* ---- phones: fewer words --------------------------------------------------- */
/* Each section keeps its heading and the thing to touch; the paragraph under
   the heading stays for computers (and in the page, for search engines). */
@media (max-width: 720px) {
  /* A link to a section (the hero's Download) lands below the sticky bar,
     not under it: sections are shorter on top here than on a computer. */
  html { scroll-padding-top: 64px; }
  .hero .cta-row .btn.primary { min-width: 62%; justify-content: center; }
  #features .sec-head p, .donate-text p, #shots .sec-head p { display: none; }
  .donate-text .donate-short { display: block; margin: 0 auto 4px; max-width: 34ch;
    font-size: 15px; line-height: 1.5; }
  .sec-head { margin-bottom: 18px; }
  .donate-act small { display: none; }
  .dl-phone p { font-size: 15px; }
}

/* ---- phones: still, not sliding ------------------------------------------ */
/* Nothing moves sideways by itself on a phone: the drifting format line is
   left out, the explorer does not step on by itself (app.js), and its tabs
   are a fixed grid of twelve icons - tap one, the screen changes - instead
   of a row of pills that scrolled along. A swipe on the screen still works. */
@media (max-width: 860px) {
  .fxp-tabs { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 8px; overflow: visible;
    margin: 0; padding: 0; }
  .fxp-tab { justify-content: center; width: auto; height: 46px; padding: 0; border-radius: 14px; }
  .fxp-tab .ic { width: 22px; height: 22px; }
  .fxp-tab span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .fxp-tab .bar { display: none; }
}
@media (max-width: 720px) {
  .strip { display: none; }
}


/* ---- the owner's note (nav.js): new donations, for the signed-in admin ---- */
.owner-note { position: fixed; z-index: 60; right: 20px; bottom: 20px; width: min(360px, calc(100vw - 32px));
  display: flex; align-items: flex-start; gap: 12px; padding: 14px 12px 14px 16px; border-radius: 18px;
  background: var(--surface-2); color: var(--text); border: 1px solid color-mix(in srgb, var(--heart) 35%, var(--line));
  box-shadow: 0 22px 50px -24px rgba(0, 0, 0, .55); }
@media (prefers-reduced-motion: no-preference) {
  .owner-note { animation: owner-in .5s cubic-bezier(.18, .9, .22, 1.08) both; }
  .owner-note.is-leaving { animation: owner-out .26s ease-in both; }
  @keyframes owner-in { from { opacity: 0; transform: translateY(18px) scale(.97); } }
  @keyframes owner-out { to { opacity: 0; transform: translateY(12px); } }
}
.owner-heart { flex: none; width: 26px; height: 26px; margin-top: 1px; fill: var(--heart); }
.owner-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; font-size: 14px; }
.owner-body strong { font-size: 15px; }
.owner-list { margin: 0; padding: 0; list-style: none; color: var(--muted); line-height: 1.5; }
.owner-list li { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.owner-list b { color: var(--text); font-weight: 650; }
.owner-more { font-size: 13px; }
.owner-link { align-self: flex-start; margin-top: 2px; color: var(--accent); font-weight: 600; text-decoration: none; }
.owner-link:hover { text-decoration: underline; }
.owner-close { flex: none; width: 30px; height: 30px; margin: -4px -2px 0 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--muted); font: inherit; font-size: 20px; line-height: 1; cursor: pointer; }
.owner-close:hover { background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text); }
@media (max-width: 560px) {
  .owner-note { right: 16px; left: 16px; width: auto; bottom: max(16px, env(safe-area-inset-bottom)); }
}
