/* ==========================================================================
   coinsider — editorial crypto-exchange review design system
   Original CSS authored from the extracted invezz.com DNA (see DNA.md).
   Tokens first; no hardcoded colors inside components.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Type */
  --font-sans: "Onest", "Segoe UI", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --weight-normal: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  /* the smallest step on the scale: avatar initials, chip counts, caption furniture.
     It existed as a bare 11px literal in three places before it had a name (audit 34) */
  --text-2xs: 11px;
  --text-xs: 12px;
  --text-sm: 14px;
  --text-base: 16px;
  --text-lg: 18px;
  --text-xl: 24px;
  --text-2xl: 30px;
  --text-3xl: 36px;
  --text-4xl: 42px;
  /* ONE page-title size, as a token rather than a selector. It used to be four
     competing rules — `h1` (42), `.page-hero h1` (36), `.review-title` (36),
     `.post-title` (36) — so a page's title size depended on which wrapper it
     happened to sit in: reviews were 36 while the restricted page next to them was
     42. Worse, the responsive step was written as a bare `h1` (0,0,1) and so could
     never beat `.page-hero h1` (0,1,1): hero pages stayed 36px down to 320,
     ending up LARGER on a phone than the pages that correctly shrank to 30.
     A token cannot lose a specificity fight, so the step below just changes it. */
  --text-page-title: var(--text-3xl);
  /* the home marketing hero is deliberately one step up; nothing else uses 4xl */
  --text-hero-title: var(--text-4xl);
  /* The rest of the document scale, same contract: ONE definition each, so the
     responsive step below is the only place a heading size ever changes. h2 had the
     identical defect h1 did — four rules (`h2`, `.section-header h2`, `.prose h2`,
     `.faq-section .section-header h2`) and a mobile step naming only two of them, so
     prose and FAQ headings never shrank and at 375 an h2 rendered the SAME 30px as
     the h1 above it. Component headings that are a different role (a card title, the
     verdict box, the newsletter panel) keep their own size and are not this concept. */
  --text-section-title: var(--text-2xl);
  --text-sub-title: var(--text-xl);
  --text-minor-title: var(--text-lg);
  --leading-tight: 1.2;
  --leading-snug: 1.375;
  --leading-body: 1.5;
  --leading-relaxed: 1.625;
  --tracking-tight: -0.025em;
  --tracking-wide: 0.025em;

  /* Color — core */
  --background-default: #ffffff;
  --surface-well: #fcf7f1;
  --surface-raised: #ffffff;
  --surface-dark: #202020;
  --surface-dark-raised: #252525;
  --foreground-default: #3d3d3d;
  --foreground-light: #68707f;
  /* #6b7280 — darkened from mined #9aa1ad (2.6:1) to reach WCAG AA 4.5:1+ on white/tints */
  --foreground-muted: #6b7280;
  --foreground-on-dark: #f4f2ec;
  --foreground-on-dark-muted: #b9b5aa;
  /* text/icons sitting on saturated fills (cta green, score pills, callout icons) */
  --on-accent: #ffffff;
  /* exchange logos are drawn for white — the plate stays white in BOTH themes so
     white-square logos (OKX, Bybit) read as intentional tiles on dark cards */
  --logo-plate: #ffffff;
  --border-default: #e0dbcc;
  --border-strong: #c9c2ad;

  /* Color — brand (CryptoWinRate orange, from the live site's Elementor kit;
     tints/strong are the mined gold ladder re-derived at the orange hue) */
  --brand: #f7931a;
  --brand-strong: #e07e08;
  --brand-tint: #fff4e6;
  --brand-tint-faint: #fcf7f1;
  --brand-border: rgba(247, 147, 26, 0.15);
  --brand-soft: rgba(247, 147, 26, 0.2);
  --on-brand: #3d3d3d;

  /* Podium accents for ranks 2 and 3 (rank 1 uses the solid --brand fill via
     .top-pick). Border + numeral only, so each has a separate ink value: a
     silver light enough to read as metal on a border is too pale to read as
     text on white. */
  --medal-silver: #9aa4b2;
  --medal-silver-ink: #5f6875;
  --medal-bronze: #b07a4b;
  --medal-bronze-ink: #93602f;

  /* Color — status */
  --positive: #74cdb1;
  /* #1e7a5f — darkened from mined #3fae8d (2.7:1) to reach WCAG AA 4.5:1+ as text on white/tints */
  --positive-strong: #1e7a5f;
  /* Second green, for the 7.x score band. It separates from --positive-strong by HUE
     (leaf, not emerald) rather than by lightness: .score-num paints the numeral itself
     via background-clip:text, so anything pastel enough to read as "a tint" — --positive
     #74cdb1 is 2.0:1 — becomes unreadable as ink. #3a8542 is 4.55:1 on white, so it still
     clears AA at the 16px bold .score-num-sm size while reading a clear step down. */
  --positive-soft: #3a8542;
  --positive-tint: #e3f5ef;
  --positive-tint-faint: #f2faf8;
  --positive-border: rgba(116, 205, 177, 0.3);
  --negative: #f3876d;
  --negative-strong: #e05f3e;
  /* Second red, for scores under 3.5. On light it goes DARKER — deeper ink reads as
     more severe, and the extra contrast is free (7.47:1 on white). The dark theme
     cannot copy that move; see its own note below. */
  --negative-deep: #a3231a;
  --negative-tint: #ffd8bd;
  --negative-tint-faint: #fef4f1;
  --negative-border: rgba(243, 135, 109, 0.3);
  --neutral-score: #d97706;
  --neutral-tint: #fff4e6;

  /* Color — secondary accents */
  --accent: #654afd;
  --accent-tint: #f7f6ff;
  /* Reverted to the authored #528654 in 0.29.2. It was darkened to #4d7d4f in 0.29.0
     because WCAG 2.1 scored it 4.29:1 under the white label — but that formula is known
     to misjudge light text on saturated mid-tones, which is why APCA (the WCAG 3
     candidate) exists. APCA rates the original -75.0 and the darkened -78.7: both
     comfortably fine for a bold 14px label. The change bought a number, not legibility. */
  --cta-green: #528654;
  --cta-green-strong: #467448;

  /* Elevation */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-menu: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);

  /* Shape — 5px steps (signature) */
  --radius-sm: 5px;
  --radius: 10px;
  --radius-md: 15px;
  --radius-lg: 20px;
  --radius-xl: 25px;
  --radius-2xl: 30px;
  --radius-pill: 9999px;

  /* Space & layout */
  --space: 0.25rem;
  --container-max: 1100px;
  --container-pad: 20px;
  --header-h: 54px;
  --header-h-desktop: 68px;
  /* 300 + the 36px gap leaves the article column exactly 724px inside the 1100
     container — which is what `.prose`'s own `max-width: 68ch` resolves to in Onest
     at 16px. At the old 380 the column was 640px, so the declared measure never
     bound and body copy ran at 69 characters instead of the ~78 it was set for:
     the sidebar was silently overriding the type spec. */
  --sidebar-w: 300px;
  --section-gap: 72px;

  /* Motion */
  --duration-fast: 0.1s;
  --duration: 0.15s;
  --duration-slow: 0.3s;
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);

  color-scheme: light;
}

[data-theme="dark"] {
  --background-default: #191919;
  --surface-well: #202020;
  --surface-raised: #232323;
  --surface-dark: #141414;
  --surface-dark-raised: #1d1d1d;
  --foreground-default: #e8e6e1;
  --foreground-light: #a8a49a;
  /* #8f8b81 — lightened from #7d796f (4.05:1) to reach WCAG AA 4.5:1+ on the dark ladder */
  --foreground-muted: #8f8b81;
  /* bright variant kept for dark surfaces — the light-theme value above would fail on dark */
  --positive-strong: #3fae8d;
  /* Same hue separation as light, re-lit for the dark ladder. Matched in LIGHTNESS to
     --positive-strong above (L .319 vs .333) on purpose: the two greens must differ by
     hue only, or the softer band looks like the brighter one turned down. */
  --positive-soft: #5aab4e;
  /* "More severe" cannot mean "darker" here — the light theme's #a3231a drops to
     2.35:1 on #191919 and the worst venues would be the hardest to read. On dark the
     severity cue inverts: this is a truer, more saturated crimson against the salmon
     --negative-strong, brighter rather than deeper. 4.67:1 as ink. */
  --negative-deep: #ef4444;
  /* hover for the inverted .btn-dark in dark theme */
  --foreground-dim: #cfccc4;
  --foreground-on-dark: #f4f2ec;
  --foreground-on-dark-muted: #b9b5aa;
  --border-default: #38352c;
  --border-strong: #4c483b;

  --brand-tint: rgba(247, 147, 26, 0.12);
  --brand-tint-faint: rgba(247, 147, 26, 0.06);
  --brand-border: rgba(247, 147, 26, 0.28);
  --on-brand: #241605;

  /* On the dark surface the ink and the border swap roles: the numeral has to
     lighten to stay legible while the border stays muted. */
  --medal-silver: #8e949e;
  --medal-silver-ink: #c6ccd6;
  --medal-bronze: #a9754a;
  --medal-bronze-ink: #d6a271;

  --positive-tint: rgba(116, 205, 177, 0.14);
  --positive-tint-faint: rgba(116, 205, 177, 0.07);
  --negative-tint: rgba(243, 135, 109, 0.16);
  --negative-tint-faint: rgba(243, 135, 109, 0.08);
  --neutral-tint: rgba(217, 119, 6, 0.16);
  /* #654afd is ~3:1 on dark surfaces — lightened for AA, same hue */
  --accent: #a495ff;
  --accent-tint: rgba(101, 74, 253, 0.12);

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-menu: 0 10px 15px -3px rgba(0, 0, 0, 0.5), 0 4px 6px -4px rgba(0, 0, 0, 0.5);

  color-scheme: dark;
}

/* --------------------------------------------------------------------------
   2. RESET + BASE
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* The UA sheet's [hidden] { display: none } loses to ANY author display rule on the
   same element, at any specificity — it silently broke a search filter and needed six
   per-component patches before becoming this one line. Carry into the theme reset. */
[hidden] { display: none !important; }

html {
  overflow-x: clip;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h-desktop) + 16px);
}

body {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-normal);
  line-height: var(--leading-body);
  color: var(--foreground-default);
  background-color: var(--background-default);
  transition: background-color var(--duration-slow) var(--ease), color var(--duration-slow) var(--ease);
  -webkit-font-smoothing: antialiased;
}

img,
svg {
  display: block;
  max-width: 100%;
}
/* Pictures keep their own proportions everywhere. Imported articles carry the old
   CMS's width/height attributes: once max-width narrows a wide screenshot, the
   height attribute alone would hold and squash it (a 900x342 screenshot showed at
   350x342 on a phone). Fixed boxes such as 16px coin icons fit the picture inside
   instead of distorting it; rules that want cropping set object-fit: cover. */
img {
  height: auto;
  object-fit: contain;
}

h1, h2, h3, h4 {
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  /* no text-wrap: balance — owner ruling: headings wrap naturally, balance breaks
     lines early with space remaining */
}

h1 { font-size: var(--text-page-title); }
h2 { font-size: var(--text-section-title); }
h3 { font-size: var(--text-sub-title); }
h4 { font-size: var(--text-minor-title); }

p { line-height: var(--leading-relaxed); }

/* Long-token safety: real scraped content flows through prose, cards and cells,
   and nothing here may push the layout sideways. Table cells use break-word,
   which (unlike anywhere) leaves min-content column sizing alone. */
.prose,
.card h3,
.card h4,
.article-card .excerpt,
.review-card .blurb,
dd,
.field-hint {
  overflow-wrap: anywhere;
}
td,
th {
  overflow-wrap: break-word;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

input,
select,
textarea {
  font: inherit;
  color: inherit;
}

ul[role="list"],
ol[role="list"] {
  list-style: none;
}

::selection {
  background: var(--brand);
  color: var(--on-brand);
}

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

* {
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: var(--radius-pill);
  border: 2px solid var(--background-default);
}
::-webkit-scrollbar-track { background: transparent; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  padding: 10px 18px;
  background: var(--surface-dark);
  color: var(--foreground-on-dark);
  border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus-visible { left: 0; }

/* --------------------------------------------------------------------------
   3. LAYOUT PRIMITIVES
   -------------------------------------------------------------------------- */
.container {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.section {
  padding-block: calc(var(--section-gap) / 2);
}

.section:first-child { padding-top: 40px; }

.section-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 28px;
}

.section-header:has(> .section-header-actions) { flex-wrap: wrap; }
.section-header-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-inline-start: auto;
  max-width: 100%;
  flex-wrap: wrap;
  gap: 12px 24px;
}
.section-header-actions > * { margin-bottom: 0; }
.section-header-actions > .link-more, .link-more-accent { border-bottom: 2px solid var(--brand); }

/* An eyebrow+heading block starts a new run of the page, so it carries its own air
   ABOVE it — the component owns the spacing, not each page that happens to use it.
   This used to be `.prose > .faq-section { margin-top: 2em }`, which meant the review
   page (FAQ inside the article body) looked right while the referral and tool pages —
   same component, no .prose ancestor — sat flush under the table above them.
   `:not(:first-child)` is what keeps it honest: a header that opens its container is
   already spaced by the container's own padding, so it must not stack a second gap.
   The wrapper carries it for faqSection(), whose header IS its first child. */
.faq-section:not(:first-child) {
  margin-top: calc(var(--section-gap) / 2);
}
/* the sectionHeader() `sub` slot: styling belongs to the component, not to whichever
   renderer fills it (it was `class="text-sm text-muted" style="margin-top:4px"` inline).

   These two were sharing one rule block — a selector-list merge that gave every FAQ
   section on the site the sub-slot's `margin-top: 4px` instead of its own gap, so the
   "Questions / Frequently asked" header sat flush against the paragraph above it on
   every page that renders one. It also leaked the 14px muted text styling into the FAQ
   wrapper, where the accordion had to out-specify it to look normal. */
.section-header-sub {
  font-size: var(--text-sm);
  color: var(--foreground-muted);
  margin-top: 4px;
}
.section-header:not(:first-child) {
  margin-top: calc(var(--section-gap) / 2);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--foreground-light);
  margin-bottom: 10px;
}

.eyebrow::before {
  content: "";
  width: 18px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--brand);
}

/* Accent variant of the section eyebrow. Must follow .eyebrow — same
   specificity, so source order decides. Every FAQ header carries it, plus the
   related-articles and author back-catalogue headers, so it is global. */
.eyebrow-accent { color: var(--accent); }
.eyebrow-accent::before { background: var(--accent); }

.grid {
  display: grid;
  gap: 20px;
}

.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* equal-height rows for card grids; harmful once single-column (every card
   stretches to the tallest sibling), so the 640 single-column step releases it */
.grid-equal-rows { grid-auto-rows: 1fr; }

/* Article cards keep one column at every count: widening their 16:9 thumbnail
   would set the height of every equal-height row. Other card types may span a
   lone final item to fill the row. A single-card grid has no earlier row, so
   its only item stays at the ordinary column width. */
.grid-3 > :last-child:nth-child(3n + 1):not(:first-child):not(.article-card) { grid-column: 1 / -1; }

/* Card grids inside prose span the full container now that the measure caps
   only text children — three-up renders exactly like the same grid outside
   prose, and the shared small-screen orphan rules below take over. */

.article-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--sidebar-w);
  gap: 36px;
  align-items: start;
}

.sidebar-sticky {
  position: sticky;
  top: calc(var(--header-h-desktop) + 20px);
  display: grid;
  gap: 20px;
}

.divider {
  border: 0;
  border-top: 1px solid var(--border-default);
  margin-block: 8px;
}

/* --------------------------------------------------------------------------
   4. BUTTONS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 22px;
  border-radius: var(--radius-pill);
  border: 1.5px solid transparent;
  font-size: 15px;
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  white-space: nowrap;
  transition: background-color var(--duration) var(--ease),
    border-color var(--duration) var(--ease),
    color var(--duration) var(--ease),
    box-shadow var(--duration) var(--ease);
}

.btn-primary {
  background: var(--brand);
  color: var(--on-brand);
}
.btn-primary:hover { background: var(--brand-strong); }

.btn-dark {
  background: var(--surface-dark);
  color: var(--foreground-on-dark);
}
.btn-dark:hover { background: var(--surface-dark-raised); }
[data-theme="dark"] .btn-dark {
  background: var(--foreground-default);
  color: var(--background-default);
}
[data-theme="dark"] .btn-dark:hover { background: var(--foreground-dim); }

.btn-outline {
  border-color: var(--border-strong);
  background: transparent;
  color: var(--foreground-default);
}
.btn-outline:hover {
  border-color: var(--foreground-default);
  background: var(--surface-well);
}

.btn-ghost {
  color: var(--foreground-light);
}
.btn-ghost:hover {
  color: var(--foreground-default);
  background: var(--surface-well);
}

.btn-cta {
  background: var(--cta-green);
  color: var(--on-accent);
}
.btn-cta:hover { background: var(--cta-green-strong); }

.btn-sm { padding: 6px 14px; font-size: var(--text-sm); }
.btn-lg { padding: 14px 30px; font-size: var(--text-base); }
.btn-block { width: 100%; }

/* The operational-status disabled CTA (visitCta()): a venue that is inactive, winding
   down or closed keeps its button SHAPE — the layout must not shift under a data change —
   but loses the accent, the link and the arrow. It ships as a <span aria-disabled>, never
   a dead <a>, so nothing is focusable that cannot be activated. Written last among the
   .btn variants so it wins over .btn-cta without a specificity fight. */
.btn-disabled {
  background: var(--surface-well);
  border-color: var(--border-default);
  color: var(--foreground-muted);
  cursor: not-allowed;
}
.btn-disabled:hover { background: var(--surface-well); }

.btn .arrow {
  transition: transform var(--duration) var(--ease);
}
.btn:hover .arrow { transform: translateX(3px); }

.link-more {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 15px;
  font-weight: var(--weight-semibold);
  color: var(--foreground-default);
  transition: color var(--duration) var(--ease);
}
.link-more:hover { color: var(--brand-strong); }

/* --------------------------------------------------------------------------
   5. BADGES, PILLS, CHIPS, TAGS
   -------------------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  background: var(--surface-well);
  border: 1px solid var(--border-default);
  color: var(--foreground-light);
}

.badge-brand {
  background: var(--brand-tint);
  border-color: var(--brand-border);
  color: var(--brand-strong);
}
.badge-positive {
  background: var(--positive-tint);
  border-color: var(--positive-border);
  color: var(--positive-strong);
}
.badge-negative {
  background: var(--negative-tint-faint);
  border-color: var(--negative-border);
  color: var(--negative-strong);
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 16px;
  border-radius: var(--radius-pill);
  border: 1.5px solid var(--border-default);
  background: var(--surface-raised);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--foreground-light);
  transition: border-color var(--duration) var(--ease),
    background-color var(--duration) var(--ease),
    color var(--duration) var(--ease);
}
.chip:hover {
  border-color: var(--border-strong);
  color: var(--foreground-default);
}
.chip[aria-pressed="true"] {
  background: var(--surface-dark);
  border-color: var(--surface-dark);
  color: var(--foreground-on-dark);
}
[data-theme="dark"] .chip[aria-pressed="true"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
}
.chip .count {
  font-size: var(--text-xs);
  font-family: var(--font-mono);
  /* No opacity. At 0.7 this 12px count blended to #959ba5 — APCA 53.9 where 12px normal
     weight wants ~75. Inheriting the chip's own colour at full strength reaches 74.5 and
     stays correct in the pressed states, where the chip's text colour changes entirely
     (a hardcoded grey here would be wrong on the dark and brand fills). */
}
/* A disclosure trigger that hides active filters. Same fill as a pressed chip, because
   it means the same thing to the eye — but it is NOT aria-pressed (it owns aria-expanded;
   one control cannot honestly claim both), so the state needs its own hook. */
.chip[data-active="1"] {
  background: var(--surface-dark);
  border-color: var(--surface-dark);
  color: var(--foreground-on-dark);
}
[data-theme="dark"] .chip[data-active="1"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* --------------------------------------------------------------------------
   6. SCORE SYSTEM
   score >= 8.5 → positive · >= 7 → positive-soft · >= 6 → neutral · >= 3.5 → negative
   · below → negative-deep
   The bands are cwr_band()'s to decide — see the rationale there. Every tier it can
   return needs a rule in BOTH blocks below, or the missing one silently inherits the
   amber --score-base default and a good venue renders as a warning.
   -------------------------------------------------------------------------- */
.score-num {
  --score-base: var(--neutral-score);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
  color: transparent;
  background-color: var(--score-base);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='16' viewBox='0 0 32 16'%3E%3Cpath d='M0 8 L8 0 L16 8 L24 0 L32 8 L24 16 L16 8 L8 16 Z' fill='%23ffffff' fill-opacity='0.14'/%3E%3C/svg%3E");
  background-size: 32px 16px;
  -webkit-background-clip: text;
  background-clip: text;
}
.score-num[data-band="positive"] { --score-base: var(--positive-strong); }
.score-num[data-band="positive-soft"] { --score-base: var(--positive-soft); }
.score-num[data-band="neutral"] { --score-base: var(--neutral-score); }
.score-num[data-band="negative"] { --score-base: var(--negative-strong); }
.score-num[data-band="negative-deep"] { --score-base: var(--negative-deep); }

.score-hero-value {
  font-size: 64px;
  line-height: 1;
  letter-spacing: var(--tracking-tight);
}

.score-pill {
  --score-base: var(--neutral-score);
  /* Optical centring (owner report, 2026-08-12). Digits have almost no ink below the
     baseline — measured 1px against Onest's 5px reserved descent — so with symmetric
     padding the numerals land 1px ABOVE centre and the pill reads bottom-heavy. Measured
     on the live page with the real font: 9px above the ink, 10px below. Shifting the box
     half a pixel each way makes it 9.5/9.5 and leaves the 32px height untouched.
     Half a pixel is the whole correction, so it cannot be done with integer padding —
     4px/3px overshoots to 1px low. Every variant that re-declares padding re-applies it. */
  --pill-nudge: 0.5px;
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  padding: calc(4px + var(--pill-nudge)) 12px calc(4px - var(--pill-nudge));
  border-radius: var(--radius);
  background: var(--score-base);
  color: var(--on-accent);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-base);
}
.score-pill[data-band="positive"] { --score-base: var(--positive-strong); }
.score-pill[data-band="positive-soft"] { --score-base: var(--positive-soft); }
.score-pill[data-band="neutral"] { --score-base: var(--neutral-score); }
.score-pill[data-band="negative"] { --score-base: var(--negative-strong); }
.score-pill[data-band="negative-deep"] { --score-base: var(--negative-deep); }
.score-pill small {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  /* No opacity here. Fading white against a saturated fill cost ~0.8 of contrast ratio
     and made the "/10" suffix HARDER to read than the number it qualifies (2.39–3.77:1
     across the bands). Size and weight already subordinate it; the alpha only hurt. */
}

/* .score-row / .score-track / .score-fill removed with the sub-score block (2026-07-28).
   Worth recording why the bars looked empty: .score-fill was a <span> inside a <span>
   track, and only the track was blockified (it is a grid item). The fill stayed
   display:inline, where width/height simply do not apply — so width:90% was ignored and
   it rendered 0x0 in every browser. The rAF animation and band colours never ran. */

/* --------------------------------------------------------------------------
   7. MONOGRAM LOGO TILES (placeholder brand marks)
   -------------------------------------------------------------------------- */
.monogram {
  --tile-hue: 210;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: linear-gradient(
    135deg,
    hsl(var(--tile-hue) 55% 92%),
    hsl(var(--tile-hue) 45% 82%)
  );
  border: 1px solid hsl(var(--tile-hue) 35% 74%);
  color: hsl(var(--tile-hue) 45% 30%);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  letter-spacing: -0.02em;
}
[data-theme="dark"] .monogram {
  background: linear-gradient(
    135deg,
    hsl(var(--tile-hue) 30% 24%),
    hsl(var(--tile-hue) 34% 17%)
  );
  border-color: hsl(var(--tile-hue) 25% 32%);
  color: hsl(var(--tile-hue) 55% 78%);
}
.monogram-sm { width: 38px; height: 38px; font-size: var(--text-sm); border-radius: var(--radius); }
.monogram-lg { width: 68px; height: 68px; font-size: var(--text-xl); border-radius: var(--radius-lg); }

/* --------------------------------------------------------------------------
   8. CARDS
   -------------------------------------------------------------------------- */
.card {
  background: var(--surface-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: 24px;
}

.card-hover {
  transition: border-color var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
}
.card-hover:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-sm);
}

.card-well {
  background: var(--surface-well);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: 24px;
}

/* Review card (exchange tile in grids) */
.review-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  position: relative;
}
.review-card .head {
  display: flex;
  align-items: center;
  gap: 14px;
}
.review-card .head .titles { flex: 1; min-width: 0; }
/* Home grid only (cwr_review_card score arg): the pill sits hard right on the logo line. */
.review-card .head .score-pill { flex: none; }
.review-card h3 {
  font-size: var(--text-lg);
}
.review-card h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
/* A regional value in the card ("10×", dotted underline) opens its own card of
   per-country values (regional.js). The title link above covers the whole
   card, so the value sits above it; under it, hovering or pressing the value
   only ever opened the review. */
.review-card .rv-on {
  position: relative;
  z-index: 1;
}
.review-card .sub {
  font-size: var(--text-sm);
  color: var(--foreground-light);
  margin-top: 2px;
}
.review-card .blurb {
  font-size: var(--text-sm);
  color: var(--foreground-light);
  line-height: var(--leading-relaxed);
}
/* Two lines of blurb, so every card in a row is the same height whatever the copy. */
.review-card .blurb {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Fixed 2x2. The old flex row put four items on one line and orphaned "Assets" onto a
   second row on its own — the same shape of bug as the auto-fit stat tiles. */
.review-card .facts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px 16px;
  border-top: 1px solid var(--border-default);
  padding-top: 14px;
  margin-top: auto;
}
.review-card .facts dt {
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.review-card .facts dd {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--foreground-default);
  font-weight: var(--weight-semibold);
  margin-top: 2px;
}
.review-card .card-cta {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--brand-strong);
}
.review-card:hover .card-cta span { transform: translateX(3px); }
.review-card .card-cta span {
  display: inline-block;
  transition: transform var(--duration) var(--ease);
}

/* Article card (news/guides) */
.article-card {
  display: flex;
  flex-direction: column;
  gap: 0;
  overflow: hidden;
  padding: 0;
  position: relative;
}
.article-card .thumb {
  --tile-hue: 210;
  /* 16:9 everywhere — one thumbnail format across home, archive and the "more on"
     rows, so a real image drop-in later never needs a second crop (owner 2026-08-04) */
  aspect-ratio: 16 / 9;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    radial-gradient(circle at 80% 10%, hsl(var(--tile-hue) 60% 88% / 0.9), transparent 55%),
    linear-gradient(135deg, hsl(var(--tile-hue) 45% 94%), hsl(var(--tile-hue) 40% 84%));
  border-bottom: 1px solid var(--border-default);
}
[data-theme="dark"] .article-card .thumb {
  background:
    radial-gradient(circle at 80% 10%, hsl(var(--tile-hue) 35% 30% / 0.9), transparent 55%),
    linear-gradient(135deg, hsl(var(--tile-hue) 28% 22%), hsl(var(--tile-hue) 32% 15%));
}
.article-card .thumb .glyph {
  font-family: var(--font-mono);
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  color: hsl(var(--tile-hue) 45% 34%);
  opacity: 0.9;
}
[data-theme="dark"] .article-card .thumb .glyph { color: hsl(var(--tile-hue) 50% 75%); }
/* A cover image (cwr_article_cover_id) fills the same 16:9 box. */
.article-card .thumb-cover { display: block; }
.article-card .thumb-cover img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* A light/dark pair shows the cover for the site's mode; the hidden one is lazy, so it is never fetched. */
.article-card .thumb-cover .cover-dark { display: none; }
[data-theme="dark"] .article-card .thumb-cover .cover-light { display: none; }
[data-theme="dark"] .article-card .thumb-cover .cover-dark { display: block; }
.article-card .body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: 18px 20px 20px;
  flex: 1;
}
.article-card .meta { align-self: stretch; }
.article-card h3 {
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  transition: color var(--duration) var(--ease);
}
.article-card:hover h3 { color: var(--brand-strong); }
/* off-site marker on titles that leave the prototype for cryptowinrate.com */
.article-card h3 .ext { opacity: 0.7; font-size: var(--text-sm); }
.article-card h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
}
.article-card .excerpt {
  font-size: var(--text-sm);
  color: var(--foreground-light);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.article-card .meta {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  font-size: var(--text-xs);
  color: var(--foreground-muted);
}
/* dates and read-times never break internally ("22 Jan / 2026") —
   the row wraps between items instead */
.article-card .meta > * { white-space: nowrap; }
.article-card .meta .dot { opacity: 0.5; }

/* .article-card-featured deleted 2026-08-04 — no renderer emits it any more. Its
   side-by-side layout was the only article card that could not hold 16:9. */

/* Ranking card */
.rank-card {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto;
  gap: 18px;
  align-items: start;
  padding: 24px;
  position: relative;
}
.rank-card.top-pick {
  border-color: var(--brand);
  border-width: 1.5px;
  background:
    linear-gradient(var(--surface-raised), var(--surface-raised)) padding-box;
}
.rank-card .rank-no {
  font-family: var(--font-mono);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  color: var(--foreground-muted);
  border: 1.5px solid var(--border-default);
  border-radius: var(--radius);
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.rank-card.top-pick .rank-no {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
}

/* Podium: 2nd silver, 3rd bronze — border and numeral only, no fill, so 1st
   keeps the solid brand block as the single loudest mark on the list and 2/3
   read as a quieter tier rather than three competing blocks.
   Gated on [data-editorial] because a podium
   place is an EDITORIAL assertion, exactly like the top-pick treatment beside
   it (d2) — once the reader re-sorts, the card in second position is second by
   THEIR chosen measure, not the site's, so the medal must not follow it.
   ranking.js clears the flag on the list, and data-rank stays the editorial
   index so nothing has to be recomputed. */
.rank-list[data-editorial="1"] .rank-card[data-rank="1"] .rank-no {
  border-color: var(--medal-silver);
  color: var(--medal-silver-ink);
}
.rank-list[data-editorial="1"] .rank-card[data-rank="2"] .rank-no {
  border-color: var(--medal-bronze);
  color: var(--medal-bronze-ink);
}
.rank-card .rank-main {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}
.rank-card .rank-head {
  display: flex;
  align-items: center;
  gap: 14px;
}
/* the name block shrinks beside the monogram instead of dropping below it — long
   badge pills used to push it down on some cards only; the pill wraps its own text */
.rank-card .rank-head > div { flex: 1; min-width: 0; }
.rank-card .rank-head .badge { white-space: normal; }
.rank-card h3 { font-size: var(--text-xl); }
.rank-card .specs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
  padding: 12px 16px;
  background: var(--surface-well);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
}
.rank-card .specs div { font-size: var(--text-xs); color: var(--foreground-muted); }
.rank-card .specs strong {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--foreground-default);
}
.rank-card .quick-pros {
  display: grid;
  gap: 5px;
  font-size: var(--text-sm);
  color: var(--foreground-light);
}
.rank-card .quick-pros li {
  list-style: none;
  padding-left: 24px;
  position: relative;
}
.rank-card .quick-pros li::before {
  content: "✓";
  position: absolute;
  left: 2px;
  color: var(--positive-strong);
  font-weight: var(--weight-bold);
}
.rank-card .rank-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  min-width: 150px;
}
.rank-card .rank-side .of-ten {
  display: block;
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  margin-top: -8px;
}
.pick-flag {
  position: absolute;
  top: -12px;
  left: 20px;
  padding: 3px 12px;
  background: var(--brand);
  color: var(--on-brand);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  border-radius: var(--radius-pill);
}

/* --------------------------------------------------------------------------
   9. PROS / CONS
   -------------------------------------------------------------------------- */
.pros-cons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}
.pc-card {
  border-radius: var(--radius-lg);
  padding: 20px 22px;
  border: 1px solid;
}
/* h3, not h4: these sit under the "<Venue> Pros & Cons" h2, so h4 skipped a level.
   font-size is explicit here, as it always was, so the tag change moves nothing. */
.pc-card h3 {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  margin-bottom: 14px;
}
.pc-card ul {
  display: grid;
  gap: 9px;
  list-style: none;
}
.pc-card li {
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  padding-left: 26px;
  position: relative;
}
.pc-card li::before {
  position: absolute;
  left: 0;
  font-weight: var(--weight-bold);
}
.pc-pros {
  background: var(--positive-tint-faint);
  border-color: var(--positive-border);
}
.pc-pros h3 { color: var(--positive-strong); }
.pc-pros li::before { content: "+"; color: var(--positive-strong); }
.pc-cons {
  background: var(--negative-tint-faint);
  border-color: var(--negative-border);
}
.pc-cons h3 { color: var(--negative-strong); }
.pc-cons li::before { content: "–"; color: var(--negative-strong); }

/* --------------------------------------------------------------------------
   10. TABLES
   -------------------------------------------------------------------------- */
.table-wrap {
  /* containing block for absolute descendants (e.g. .visually-hidden header labels)
     — without this they escape the scroll clip and widen the page */
  position: relative;
  overflow-x: auto;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}

/* Only the directory table expands beyond the normal heading/filter measure. */
.archive-table-region {
  width: calc(100% - 2 * var(--container-pad));
  max-width: 1600px;
  margin-inline: auto;
}

/* Clipped-edge cue (markScrollClips() sets data-clip): the cut edge fades and the
   fade retracts once scrolled to, so a clipped column reads as scrollable — not
   broken. Browsers without mask support just show the plain cut. */
.table-wrap[data-clip],
.formula[data-clip] { scrollbar-width: thin; }
.table-wrap[data-clip="end"],
.formula[data-clip="end"] {
  mask-image: linear-gradient(90deg, #000 calc(100% - 36px), rgba(0, 0, 0, 0.25));
}
.table-wrap[data-clip="start"],
.formula[data-clip="start"] {
  mask-image: linear-gradient(270deg, #000 calc(100% - 36px), rgba(0, 0, 0, 0.25));
}
.table-wrap[data-clip="both"],
.formula[data-clip="both"] {
  mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0.25), #000 36px, #000 calc(100% - 36px), rgba(0, 0, 0, 0.25));
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}
/* Imported-content figures (review-corpus tables/images): the figure is the
   scroll container for a wide table — wide content scrolls inside its own
   box, the page never does — and browser-default 40px figure side margins
   are dropped (they pushed content past small viewports). */
figure.wp-block-table,
figure.wp-block-image {
  margin: 1.5em 0;
  max-width: 100%;
}
figure.wp-block-table {
  overflow-x: auto;
}
/* Legacy prose also carries RAW hand-authored <table>s (inline-styled
   comparison tables from the old site) with no wrapper element — sometimes
   nested in bare legacy <div>s, so no child combinator. On small viewports
   the table box itself becomes the scroll container; media-queried so
   desktop table layout is untouched. Component tables re-assert their
   normal layout: they live inside .table-wrap, which already scrolls, or
   they are the review's two-column .fact-table, which never needs to and
   shrank to its content as a block (owner 2026-09-27, narrow screens). */
@media (max-width: 640px) {
  :is(.prose, .ranking-content) table {
    display: block;
    overflow-x: auto;
  }
  :is(.prose, .ranking-content) :is(.table-wrap table, table.fact-table) {
    display: table;
    overflow-x: visible;
  }
}
thead th {
  text-align: left;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--foreground-muted);
  font-weight: var(--weight-semibold);
  padding: 12px 16px;
  background: var(--surface-well);
  border-bottom: 1px solid var(--border-default);
  white-space: nowrap;
}
/* Imported article tables use the legacy align attribute to describe column
   alignment. An author stylesheet declaration outranks that browser hint, so
   repeat the supported values here: numeric headings then sit on the same
   edge as the values beneath them. */
thead th[align="center" i] { text-align: center; }
thead th[align="right" i] { text-align: right; }
/* second header line for parenthesised fact labels (maker/taker) */
th .th-sub {
  display: block;
  font-weight: var(--weight-normal);
  color: var(--foreground-muted);
  /* No opacity: at 0.8 this blended to APCA 56.4. Full strength is 69.2. The 10px size
     is what still holds it back — APCA wants ~90 there — so if this ever needs to go
     further, the lever is the font-size, not the colour. */
}
tbody td {
  padding: 13px 16px;
  border-bottom: 1px solid var(--border-default);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr {
  transition: background-color var(--duration-fast) var(--ease);
}
tbody tr:hover { background: var(--surface-well); }
td:first-child { font-weight: var(--weight-semibold); }
td[data-num], th[data-num] {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
/* Explicit editorial tables share the theme table system. Row headers need
   the same cell geometry as values; wrapping headings keeps wide comparisons
   inside the desktop article measure. */
.table-wrap.table-editorial { isolation: isolate; }
.table-editorial table { font-size: var(--text-sm); line-height: 1.45; }
.table-editorial caption {
  padding: 14px 16px;
  color: var(--foreground-default);
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  line-height: 1.4;
  text-align: left;
  caption-side: top;
}
.table-editorial thead th {
  padding: 10px 12px;
  white-space: normal;
  text-transform: none;
  letter-spacing: normal;
  line-height: 1.4;
}
.table-editorial tbody :is(th, td) {
  padding: 11px 12px;
  border-bottom: 1px solid var(--border-default);
  vertical-align: middle;
  text-align: left;
}
.table-editorial tbody th { font-weight: var(--weight-semibold); }
.table-editorial tbody td { font-weight: var(--weight-normal); }
.table-editorial tbody tr:last-child > :is(th, td) { border-bottom: 0; }
.table-editorial :is(th, td) > :first-child { margin-top: 0; }
.table-editorial :is(th, td) > :last-child { margin-bottom: 0; }
.table-editorial--wide table { min-width: 680px; font-size: 13px; }
.table-editorial--wide :is(th, td) { padding: 10px 8px; }
.table-editorial--wide tbody th { white-space: nowrap; }
.table-editorial--wide thead th { font-size: var(--text-xs); }
@media (max-width: 640px) {
  .table-editorial tbody th {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--surface-raised);
    border-right: 1px solid var(--border-default);
  }
  .table-editorial thead th:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    border-right: 1px solid var(--border-default);
  }
}

/* Review overview: CEM-backed facts are grouped by meaning. Group headings
   use local currentColor SVGs and disappear together with empty groups. */
.ov-stats thead th {
  padding: 8px 16px;
}
.ov-stats tbody td {
  padding: 8.25px 16px;
}
.ov-stats .stats-group-row th {
  padding: 5px 16px;
  border-bottom: 1px solid var(--border-default);
  text-align: left;
  background: var(--brand-tint);
  color: var(--foreground-light);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.ov-stats tbody + tbody .stats-group-row th {
  border-top: 1px solid var(--border-default);
}
.ov-stats .stats-group-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* Collapsed table borders place this box 2.17px above the visual row centre. */
  transform: translateY(2px);
}
.ov-stats .stats-group-icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  color: var(--brand-strong);
}
.ov-stats .stats-solo td {
  border-top: 6px solid var(--brand-tint);
}
.ov-stats .score-pill {
  padding: calc(2px + var(--pill-nudge)) 9px calc(2px - var(--pill-nudge));
  font-size: var(--text-base);
}
.ov-stats .score-pill small {
  font-size: 10px;
}
/* KYC limits compare one metric across up to four tiers. Keep the transposed
   row on one line and let .table-wrap expose horizontal scroll on small screens. */
.kyc-limit-table { min-width: max-content; }
.kyc-limit-table tbody th {
  padding: 13px 16px;
  border-bottom: 0;
  text-align: left;
  vertical-align: middle;
  font-weight: var(--weight-semibold);
  white-space: nowrap;
}
.kyc-limit-table tbody td {
  border-bottom: 0;
  white-space: nowrap;
}
[data-change-up="true"] { color: var(--positive-strong); }
[data-change-up="false"] { color: var(--negative-strong); }

/* Fact sheet (sidebar) — the review page renders it twice: .factsheet-aside in
   the sidebar (desktop) and .factsheet-inline under the verdict, which takes
   over on ≤1024px where the whole aside stacks below the article */
.factsheet-inline { display: none; }
.fact-table {
  width: 100%;
  font-size: var(--text-sm);
}
.fact-table th {
  text-align: left;
  font-weight: var(--weight-medium);
  color: var(--foreground-light);
  padding: 9px 0;
  border-bottom: 1px solid var(--border-default);
  width: 46%;
}
.fact-table td {
  text-align: right;
  font-weight: var(--weight-semibold);
  padding: 9px 0;
  border-bottom: 1px solid var(--border-default);
}
.fact-table tr:last-child th,
.fact-table tr:last-child td { border-bottom: 0; }

/* --------------------------------------------------------------------------
   11. PROSE (article typography)
   -------------------------------------------------------------------------- */
/* The measure lives on the TEXT, not the wrapper: migrated editor documents
   run whole component sections (bonus/article card grids, FAQ accordions)
   through the one prose wrap, and a 68ch cap on the wrapper squeezed those
   components into the text column — the exact sections a non-migrated page
   renders full-width outside the prose. Capping only the text children keeps
   reading measure identical while components span the container, matching
   the non-migrated layout. */
.prose {
  max-width: none;
}
.prose > :is(p, h2, h3, h4, h5, h6, ul, ol, blockquote, figure, table) {
  max-width: 68ch;
}
/* Ranking articles get the prose typography WITHOUT the measure. Their prose
   and their components (rank list, glance table) share one AWP content stream
   and cannot be split apart without re-ordering blocks — which blueprint §9
   forbids — so a 68ch cap would crush the tables rather than set the text. */
.ranking-content .awp-content-block-body { max-width: none; }
.ranking-content .awp-content-block-body > * + * { margin-top: 1.15em; }
.ranking-content .awp-content-block-body h2 {
  margin-top: 2em;
  scroll-margin-top: calc(var(--header-h-desktop) + 20px);
}
.ranking-content .awp-content-block-body h3 { margin-top: 1.6em; }
.ranking-content .awp-content-block-body p { color: var(--foreground-default); }
.ranking-content .awp-content-block-body :is(ul, ol) { padding-left: 24px; display: grid; gap: 8px; }
.ranking-content .awp-content-block-body li { line-height: var(--leading-relaxed); }
/* Editor-document rankings (0.10.0+): the prose stream lives DIRECTLY in the
   article — no .awp-content-block-body wrapper survives the migration — so the
   same typography binds to the stream's own children. Child-scoped on purpose:
   descendant rules would reach inside components (.pc-card's list grid must
   not inherit prose list padding). scroll-margin stays a descendant rule for
   both columns — TOC anchors also live in the spine, and it has no layout
   cost. */
.ranking-content > * + * { margin-top: 1.15em; }
.ranking-content > h2 { margin-top: 2em; }
.ranking-content > h3 { margin-top: 1.6em; }
.ranking-content > :is(h2, h3):first-child { margin-top: 0; }
.ranking-content h2, .ranking-spine h2 { scroll-margin-top: calc(var(--header-h-desktop) + 20px); }
.ranking-content > p { color: var(--foreground-default); }
.ranking-content > :is(ul, ol) { padding-left: 24px; display: grid; gap: 8px; }
.ranking-content > :is(ul, ol) li { line-height: var(--leading-relaxed); }
/* 0.11.4 (owner ruling 2026-08-08): THE site-wide article list design —
   one global rule for every content stream (reviews, rankings, guides,
   referrals, availability), introduced rankings-only in 0.11.3. Bare
   (classless) lists that sit directly in the stream get brand square
   markers; CLASSED component lists (ol.steps, .incident-list, pc-card
   internals…) opt out by carrying a class — never enumerated here. */
:is(.prose, .ranking-content) > ul:not([class]) { list-style: none; padding-left: 4px; }
:is(.prose, .ranking-content) > ul:not([class]) > li { position: relative; padding-left: 22px; }
:is(.prose, .ranking-content) > ul:not([class]) > li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 0.5em;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--brand);
}
:is(.prose, .ranking-content) > ol:not([class]) > li::marker { color: var(--brand-strong); font-weight: 700; }
.prose > * + * { margin-top: 1.15em; }
.prose h2 {
  margin-top: 2em;
  scroll-margin-top: calc(var(--header-h-desktop) + 20px);
}
.prose h3 {
  margin-top: 1.6em;
}
/* Same contract as `.faq-section:not(:first-child)` above: a heading that OPENS the
   article body is already spaced by the byline's 24px bottom margin, so it must not
   stack a second gap on top. Articles whose prose starts with a paragraph (most of
   them) sat 24px under the rule; the ones that open on a heading — the restricted
   pages, which lead with "Country Checker" — collapsed to the h2's 2em and started
   60px down instead, which is why they read as a hole no other article had. */
/* ...but the condition is the PROSE BLOCK opening its container, not the heading opening
   the prose block. Written as `.prose > :is(h2,h3):first-child` it also fired on a SECOND
   prose block mid-article — the alternatives page puts its card list between two of them,
   so "X vs the alternatives" sat flush against the last card with no gap at all. Both
   openers still qualify: a prose block that starts its container (the bonus explainer)
   and one that follows the byline (whose 24px already supplies the air). */
.prose:first-child > :is(h2, h3):first-child,
.byline + .prose > :is(h2, h3):first-child { margin-top: 0; }
/* A card's title is not an article heading. `.prose h3` above ties `.review-card h3` and
   `.article-card h3` on specificity and comes later, so inside an article the title took the
   1.6em heading gap: the name sat 14px below the logo's centre and the card grew 11px of air
   on top (restricted pages' alternatives grid, the referral pages' "more about" row). */
.prose .card h3 { margin-top: 0; }
.prose p { color: var(--foreground-default); }
.prose ul, .prose ol { padding-left: 24px; display: grid; gap: 8px; }
.prose li { line-height: var(--leading-relaxed); }
/* One editorial link style for every article template and standalone FAQ.
   Layout classes never select link styling. Components retain their own controls. */
.article-links a[href]:not(.btn):not(.link-more):not(.chip):not(.card a):not(.ip-banner a):not(.cmp-name a):not(.table-visit):not(.arrow-card-link):not(.choice-pick):not(.video-embed-play) {
  font-weight: var(--weight-semibold);
  border-bottom: 2px solid var(--brand);
  transition: color var(--duration) var(--ease);
}
.article-links a[href]:not(.btn):not(.link-more):not(.chip):not(.card a):not(.ip-banner a):not(.cmp-name a):not(.table-visit):not(.arrow-card-link):not(.choice-pick):not(.video-embed-play):hover { color: var(--brand-strong); }
.prose blockquote {
  border-left: 3px solid var(--brand);
  background: var(--surface-well);
  padding: 16px 22px;
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-size: var(--text-lg);
  line-height: var(--leading-relaxed);
}
.prose blockquote cite {
  display: block;
  margin-top: 8px;
  font-size: var(--text-sm);
  font-style: normal;
  color: var(--foreground-light);
}
.prose .table-wrap { margin-block: 1.4em; }

/* --------------------------------------------------------------------------
   12. CALLOUTS
   -------------------------------------------------------------------------- */
.callout {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 14px;
  padding: 16px 20px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-default);
  background: var(--surface-well);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}
.callout .icon {
  width: 22px;
  height: 22px;
  display: block;
  flex-shrink: 0;
  margin-top: 2px;
  /* disc colour; the mark colour rides on --chip-ink */
  color: var(--border-default);
  --chip-ink: var(--foreground-default);
}
.callout strong:first-child { display: block; margin-bottom: 3px; }
.callout-info {
  background: var(--accent-tint);
  border-color: transparent;
}
.callout-info .icon { color: var(--accent); --chip-ink: var(--on-accent); }
.callout-tip {
  background: var(--positive-tint-faint);
  border-color: var(--positive-border);
}
.callout-tip .icon { color: var(--positive-strong); --chip-ink: var(--on-accent); }
.callout-warn {
  background: var(--negative-tint-faint);
  border-color: var(--negative-border);
}
.callout-warn .icon { color: var(--negative-strong); --chip-ink: var(--on-accent); }
.callout-brand {
  background: var(--brand-tint);
  border-color: var(--brand-border);
}
.callout-brand .icon { color: var(--brand); --chip-ink: var(--on-brand); }
/* Review notices (closure notice, security warning, the CEM Warning text) and
   the security section's incident box. A bright red frame so they read before
   anything else on the page; the salmon tint was too soft to notice (owner
   2026-09-25, the incident box the same day: "same red border"). The bright
   red is the frame and the icon; the title takes a deeper red on light and a
   lighter one on dark, because bright red as small bold text falls just
   under 4.5:1 on the tint (title ~6:1 either way).
   The banner margin gives it its own air: it sat flush against the card
   below it on closing venues (owner report 2026-08-09, BitMart/BitMEX).
   Scoped to these classes, not .callout: inline prose callouts, the risk
   warning included, keep their own look and flow spacing. */
.status-banner,
.callout-incident {
  --status-red: #dc2626;
  --status-red-ink: #b91c1c;
  border: 2px solid var(--status-red);
  background: #fef2f2;
}
.status-banner {
  margin: 14px 0 18px;
  padding: 14px 18px;
  border-radius: var(--radius-lg);
}
.status-banner .icon,
.callout-incident .icon { color: var(--status-red); --chip-ink: #fff; }
.status-banner-title,
.callout-incident > div > strong:first-child,
.callout-incidents > strong { color: var(--status-red-ink); }
[data-theme="dark"] .status-banner,
[data-theme="dark"] .callout-incident {
  --status-red: #ef4444;
  --status-red-ink: #f87171;
  background: rgba(239, 68, 68, 0.1);
}

.risk-note {
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  line-height: 1.6;
  padding: 12px 16px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
}

/* --------------------------------------------------------------------------
   13. FORMS
   -------------------------------------------------------------------------- */
.input,
.select {
  width: 100%;
  padding: 10px 16px;
  border: 1.5px solid var(--border-default);
  border-radius: var(--radius);
  background: var(--surface-raised);
  font-size: 15px;
  color: var(--foreground-default);
  transition: border-color var(--duration) var(--ease);
}
.input::placeholder { color: var(--foreground-muted); }
.input:hover, .select:hover { border-color: var(--border-strong); }
.input:focus, .select:focus {
  outline: none;
  border-color: var(--brand);
}
.select {
  appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 L6 6.5 L11 1.5' fill='none' stroke='%2368707f' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  cursor: pointer;
}
.field-label {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  margin-bottom: 6px;
}
.field-hint {
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  margin-top: 5px;
}
/* Two placements the hint needs, previously nudged inline at the call site: -lead opens
   a block straight under a divider, -tight pulls up under a chart. They sit AFTER the
   base rule on purpose — same single-class specificity, so source order is the only
   thing deciding, and declared first they silently lost to the 5px above. */
.field-hint-lead { margin-top: 0; }
.field-hint-tight { margin-top: -8px; }

.search-bar {
  position: relative;
  display: flex;
  align-items: center;
}
.search-bar .input { padding-left: 40px; }
.search-bar svg {
  position: absolute;
  left: 14px;
  width: 16px;
  height: 16px;
  color: var(--foreground-muted);
  pointer-events: none;
}

/* Newsletter */
.newsletter {
  display: flex;
  gap: 10px;
}
.newsletter .input { flex: 1; }
/* key takeaways box (ported article openers) */
.takeaways {
  padding: 18px 22px;
  margin-bottom: 20px;
  background: var(--surface-well);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
}
.takeaways .tk-title {
  display: block;
  margin-bottom: 10px;
}
.takeaways ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 7px;
}
.takeaways li {
  display: flex;
  gap: 9px;
  font-size: var(--text-sm);
  color: var(--foreground-light);
  line-height: var(--leading-relaxed);
}
.takeaways .tick {
  color: var(--positive-strong);
  font-weight: var(--weight-bold);
  flex: none;
}

/* referral page — the bonus card standing alone, plus the steps.
   `.referral-hero` and `.code-chip-lg` lived here and are gone: that hero was a second
   bonus card with its own copy of the rows, and it kept rendering the old collapsing
   layout after the grid cards moved to fixed rows. The page now renders bonusCard()
   unchanged; this only caps how wide the slot is, so the card is never stretched across
   a prose column. A SLOT may vary per surface — the card may not. */
.bonus-solo {
  max-width: 380px;
  margin-bottom: 4px;
}
@media (max-width: 480px) {
  .bonus-solo { max-width: none; }
}
/* Step chips (owner 2026-08-04, ruled from the list-styles demo) — the ONE how-to
   component, replacing .referral-steps. Rank-no idiom at 32px, hairline connector,
   first step ringed in brand. Dedicated sections only, by explicit template call. */
.steps {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 0;
  counter-reset: step;
}
/* inside article prose the generic .prose ol spacing must not win */
.prose ol.steps { padding-left: 0; gap: 0; }
/* same guard for the ranking stream (0.11.6: buying_guide serializes as steps) */
.ranking-content > ol.steps { padding-left: 0; gap: 0; }
.steps li {
  counter-increment: step;
  position: relative;
  padding: 0 0 22px 52px;
}
.steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: -2px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-sm);
  /* every chip carries the brand (owner 2026-08-04) — not just step 1 */
  color: var(--brand-strong);
  background: var(--surface-raised);
  border: 1.5px solid var(--brand);
  border-radius: var(--radius);
}
.steps li:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 15.5px;
  top: 34px;
  bottom: 4px;
  border-left: 1px solid var(--border-default);
}
/* The CONTENT is a plain authored list (owner 2026-08-04): the author writes a normal
   list and bolds the lead phrase; a dedicated section's template applies .steps and
   this styles the raw markup — li text is the muted description, the author's own
   <strong> becomes the block title. No shortcode syntax, no blocks, no wrapper spans. */
.steps li {
  font-size: var(--text-sm);
  color: var(--foreground-light);
  line-height: var(--leading-relaxed);
}
.steps li > strong {
  display: block;
  margin-bottom: 2px;
  font-size: var(--text-base);
  color: var(--foreground-default);
}
/* heading-row eligibility picker: label + the compact country pill. The 2px gap was
   sized for the bare-flag variant; beside a bordered pill the label needs air and
   weight or it reads as loose text floating next to a control (owner 2026-08-04). */
.bonus-elig {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--foreground-light);
  white-space: nowrap;
}
/* bonus chamber cards (home) — compact label/value rows with hairline separators;
   the CTA pins to the card bottom so rows align across the grid */
.bonus-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px;
}
.bonus-rows .brow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border-default);
  font-size: var(--text-sm);
}
.bonus-rows .brow:first-child { border-top: 1px solid var(--border-default); }
/* The label never breaks; the VALUE drops to its own line when the pair won't fit. A
   long code (CRYPTOTRADING, STARTTRADING) in a quarter-width card was splitting "Bonus
   code" across two lines, which reads as a layout bug rather than a tight fit. */
.bonus-rows .brow { flex-wrap: wrap; }
.bonus-rows .brow > span {
  color: var(--foreground-light);
  white-space: nowrap;
}
/* Set by fitBonusRows() on a grid where at least one code actually wrapped: from there
   every code row in that grid takes the two-line layout, so no card is a row taller than
   the one beside it. Nothing is stacked when nothing needed to be. Scoped to the CARD
   (fitBonusRows stacks a visual row of neighbours together, not the whole grid). */
.bonus-card[data-code-stacked="1"] .brow-code > :first-child { flex: 1 0 100%; }
.bonus-card[data-code-stacked="1"] .brow-code { row-gap: 5px; }
/* The card foot carries the auto margin, not the button: on the bonus hub a card can
   hold a CTA, a terms link, or both (five venues have no CWR signup URL), and the pin
   to the card bottom has to survive every combination so rows align across the grid. */
.bonus-foot {
  margin-top: auto;
  display: grid;
  gap: 8px;
  justify-items: stretch;
}
.bonus-cta { margin-top: auto; }
.bonus-terms {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--foreground-light);
  text-align: center;
}
.bonus-terms:hover { color: var(--brand); }
.bonus-more {
  display: flex;
  justify-content: center;
  margin-top: 22px;
}
.bonus-more .count {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--foreground-muted);
}
#bonus-count:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; }
/* bonus hub: the eligibility picker sits in the hero, where the country is the first
   thing the page is filtered by — label, combo and the count it produces on one line */
.bonus-geo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  margin-top: 18px;
  padding: 12px 16px;
  /* the same positive tint the geo banner uses for "this venue accepts you" — the bar
     answers the same question for the whole page, so it reads as the same signal */
  border: 1px solid var(--positive-border);
  border-radius: var(--radius-lg);
  background: var(--positive-tint-faint);
}
.bonus-geo .bg-label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}
.bonus-geo .bg-note {
  flex: 1 1 260px;
  min-width: 0;
  font-size: var(--text-xs);
  color: var(--foreground-muted);
}
/* Exclude-restricted toggled OFF: the geo line downgrades from a claim about the list
   to an availability check (owner 2026-08-04) — text greys and the positive tint goes
   neutral, but the PICKER keeps full contrast: it still drives the restricted marks. */
.bonus-geo[data-inactive] {
  border-color: var(--border-default);
  background: var(--surface-well);
}
.bonus-geo[data-inactive] .bg-label,
.bonus-geo[data-inactive] .bg-note {
  color: var(--foreground-muted);
  opacity: 0.75;
}
.bonus-card .head {
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Name takes the slack so the score pill sits hard right, on the logo's own line.
   No overflow-wrap here: with `min-width: 0` the flex item resolves narrow enough that
   `anywhere` broke every long name mid-word ("Hyperliqu / id") even with room to spare.
   Names are single words, so they either fit or ellipsise — neither is a hyphen. */
.bonus-card .head strong {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Compact pill + tighter gap: at the four-up width the head has ~212px, and a full-size
   pill left the longest name on the roster ("Hyperliquid", 88px) a few pixels short.
   Ellipsising the exchange name on a card whose job is naming the exchange is the wrong
   trade, so the pill and the gaps give up the room instead. qa.mjs measures the real
   text width against the box at four widths, so a longer name fails the run. */
.bonus-card .head { gap: 8px; }
.bonus-card .head .score-pill {
  flex: none;
  /* Re-declaring padding drops the base rule's optical nudge; re-apply it. */
  padding: calc(3px + var(--pill-nudge)) 8px calc(3px - var(--pill-nudge));
  font-size: var(--text-xs);
}
.bonus-card .head .score-pill small { font-size: 10px; }
.bonus-amt {
  font-size: var(--text-sm);
  color: var(--foreground-light);
}
.bonus-amt strong {
  display: block;
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-tight);
  color: var(--foreground-default);
}
.code-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 3px 9px;
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--foreground-default);
  background: var(--surface-well);
  cursor: pointer;
  transition: border-color var(--duration) var(--ease), background-color var(--duration) var(--ease);
}
.code-chip:hover {
  border-color: var(--brand);
  background: var(--brand-tint-faint);
}
/* "no code" occupies the chip's exact box — same padding, border width and font metrics,
   just invisible. Derived rather than a guessed min-height, so the two states cannot
   drift apart when the chip is restyled: a plain em-dash sat 5px shorter than a chip,
   which is all it takes for one card's rows to fall out of line with its neighbour's. */
.code-none {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border: 1.5px solid transparent;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--foreground-muted);
}
.code-chip svg {
  width: 15px;
  height: 15px;
  flex: none;
  color: var(--foreground-light);
}

/* homepage dedicated signup panel (below hero) */
.newsletter-panel {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 28px;
  align-items: center;
  padding: 26px 28px;
  background: var(--brand-tint-faint);
  border: 1px solid var(--brand-border);
  border-radius: var(--radius-lg);
}
.newsletter-panel h2 { font-size: var(--text-xl); }
.nl-points {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 16px;
  color: var(--foreground-light);
}
.nl-points .tick {
  color: var(--positive-strong);
  font-weight: var(--weight-bold);
  margin-right: 4px;
}
@media (max-width: 900px) {
  .newsletter-panel { grid-template-columns: minmax(0, 1fr); }
}
.newsletter-done {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-radius: var(--radius);
  background: var(--positive-tint);
  border: 1px solid var(--positive-border);
  color: var(--positive-strong);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

/* --------------------------------------------------------------------------
   14. ACCORDION (FAQ)
   -------------------------------------------------------------------------- */
.accordion {
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface-raised);
}
.accordion-item + .accordion-item {
  border-top: 1px solid var(--border-default);
}
/* The row heading is a semantic wrapper, not typography — the button sets its own type.
   Without this, .prose h3 reaches in and makes the rows 93px instead of 54px. Must stay
   after the .prose block to win the specificity tie. */
.accordion h3 { font: inherit; margin: 0; }

/* ---- the FAQ block (faqSection() — every FAQ on the site) ---- */
.faq-section { padding-bottom: calc(var(--section-gap) / 2); }
.faq-section .section-header h2 {
  margin-top: 0;
  scroll-margin-top: calc(var(--header-h-desktop) + 20px);
}
.accordion-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  padding: 16px 20px;
  text-align: left;
  font-size: 15px;
  font-weight: var(--weight-semibold);
  transition: background-color var(--duration) var(--ease);
}
.accordion-btn:hover { background: var(--surface-well); }
.accordion-btn .chev {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  display: flex;
  /* THE circled chevron is one SVG (cwr_chev_circle()): ring + glyph share a
     22x22 viewBox, concentric by construction, so the 180-degree flip below is an
     exact mirror. Ring lives on the circle element, ink on currentColor. */
  transition: transform var(--duration-slow) var(--ease);
}
.accordion-btn .chev .chev-circle { width: 22px; height: 22px; }
.accordion-btn .chev circle {
  stroke: var(--border-strong);
  fill: transparent;
  transition: fill var(--duration) var(--ease), stroke var(--duration) var(--ease);
}
.accordion-btn[aria-expanded="true"] .chev {
  transform: rotate(180deg);
  color: var(--on-brand);
}
.accordion-btn[aria-expanded="true"] .chev circle {
  fill: var(--brand);
  stroke: var(--brand);
}
/* An answer is OPEN as served. Opening one takes a script (review.js), so the
   stylesheet may only close them where scripts run: html[data-js], stamped in
   <head> before the first paint. Until 0.82.18 the answers were closed for
   everyone, and a reader without JavaScript could not read a single one. */
.accordion-panel {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--duration-slow) var(--ease);
}
.accordion-panel > div { overflow: hidden; }
html[data-js] .accordion-item:not([data-open="true"]) .accordion-panel { grid-template-rows: 0fr; }
/* A closed answer is gone, not only folded to no height: its links took keyboard
   focus while nothing of them could be seen. The delay lets the fold finish
   before the text disappears; an opening answer shows at once. */
html[data-js] .accordion-item:not([data-open="true"]) .accordion-panel > div {
  visibility: hidden;
  transition: visibility 0s linear var(--duration-slow);
}
.accordion-panel p {
  padding: 0 20px 18px;
  font-size: var(--text-sm);
  color: var(--foreground-light);
  line-height: var(--leading-relaxed);
}
.accordion-item[data-open="true"] .accordion-panel {
  grid-template-rows: 1fr;
}

/* --------------------------------------------------------------------------
   15. BREADCRUMBS, PAGINATION, TOC
   -------------------------------------------------------------------------- */
.breadcrumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  margin-bottom: 18px;
}
.breadcrumbs a {
  color: var(--foreground-light);
  font-weight: var(--weight-medium);
  transition: color var(--duration) var(--ease);
}
.breadcrumbs a:hover { color: var(--brand-strong); }
.breadcrumbs .sep { opacity: 0.5; }

.pagination {
  display: flex;
  /* the archive grew past a handful of pages with the review-corpus
     import — buttons wrap instead of widening the page */
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 36px;
}
.page-btn {
  min-width: 38px;
  height: 38px;
  padding-inline: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius);
  border: 1.5px solid var(--border-default);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--foreground-light);
  transition: border-color var(--duration) var(--ease), color var(--duration) var(--ease),
    background-color var(--duration) var(--ease);
}
.page-btn:hover { border-color: var(--border-strong); color: var(--foreground-default); }
.page-btn[aria-current="page"] {
  background: var(--surface-dark);
  border-color: var(--surface-dark);
  color: var(--foreground-on-dark);
}
.pagination-gap {
  min-width: 20px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--foreground-muted);
  font-variant-numeric: tabular-nums;
}
[data-theme="dark"] .page-btn[aria-current="page"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
}

.toc {
  font-size: var(--text-sm);
}
/* h2: the TOC is a sibling of the article body, not a child of anything in it, so
   after the page h1 only h2 avoids a jump. Size is set here, so it still reads as the
   small uppercase label it always was. */
.toc h2 {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--foreground-muted);
  margin-bottom: 10px;
}
.toc ol {
  list-style: none;
  display: grid;
}
.toc a {
  display: block;
  padding: 6px 0 6px 14px;
  border-left: 2px solid var(--border-default);
  color: var(--foreground-light);
  transition: color var(--duration) var(--ease), border-color var(--duration) var(--ease);
}
.toc a:hover { color: var(--foreground-default); }
.toc a.active {
  border-left-color: var(--brand);
  color: var(--foreground-default);
  font-weight: var(--weight-semibold);
}
/* The contents list where the layout stacks (cwr_toc_mobile(), owner 2026-10-07).
   At 1024px and below the article is one column and the sidebar's card above is
   hidden, so the same card rides in a <details> at the top of the article. It is
   closed as served and the reader opens it; no script is involved. The referral
   page had this first, as its own component; these are its rules. */
.mobile-toc {
  display: none;
  margin: 24px 0;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
}
.mobile-toc > summary {
  padding: 16px 20px;
  font-weight: 650;
  cursor: pointer;
}
.mobile-toc .toc {
  display: block;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: none;
  padding-top: 0;
}
.mobile-toc .toc h2 { display: none; }
@media (max-width: 1024px) {
  .mobile-toc { display: block; }
}

/* --------------------------------------------------------------------------
   16. AVATARS & BYLINE
   -------------------------------------------------------------------------- */
.avatar {
  --tile-hue: 210;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  background: linear-gradient(135deg, hsl(var(--tile-hue) 50% 88%), hsl(var(--tile-hue) 42% 76%));
  color: hsl(var(--tile-hue) 45% 28%);
  border: 1px solid hsl(var(--tile-hue) 35% 70%);
}
[data-theme="dark"] .avatar {
  background: linear-gradient(135deg, hsl(var(--tile-hue) 30% 26%), hsl(var(--tile-hue) 34% 18%));
  color: hsl(var(--tile-hue) 55% 78%);
  border-color: hsl(var(--tile-hue) 25% 34%);
}
.avatar-sm { width: 28px; height: 28px; font-size: var(--text-2xs); }
.avatar-lg { width: 72px; height: 72px; font-size: var(--text-xl); }

/* Author photo over the initials tile, same load contract as .monogram: the initials
   ARE the fallback and hold the box, the photo only covers them once it has decoded.
   Hiding the initials up front would leave an empty circle whenever the CDN is slow. */
.avatar { position: relative; overflow: hidden; }
.avatar .av-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--duration) var(--ease);
}
.avatar[data-photo] .av-img { opacity: 1; }
.avatar[data-photo] .av-initials { visibility: hidden; }

.byline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 20px;
  /* The component owns its rhythm. This was `mt-2 mb-3` passed in by each caller, and
     four of the seven had drifted — the single-post page passed no top margin at all,
     so its rule sat flush against the h1 while every other article-shaped page had a
     16px gap. Spacing decided at the call site is the same root cause the fact registry
     fixed for formatting; a shared component that only looks shared is worse than none. */
  margin-block: 16px 24px;
  /* Soft brand panel (owner 2026-10-01, replaced the two hairline rules): the faint
     orange well with an even orange outline, so the byline reads as CryptoWinRate's
     own furniture rather than a generic meta row. */
  padding: 14px 18px;
  border: 1px solid var(--brand-soft);
  border-radius: var(--radius-md);
  background: var(--brand-tint-faint);
  font-size: var(--text-sm);
}
[data-theme="dark"] .byline { border-color: var(--brand-border); }
.byline .avatar { width: 42px; height: 42px; }
.byline .who {
  display: flex;
  align-items: center;
  gap: 12px;
}
/* the whole name block is the link to the author page — a hover that only lit the
   name left the avatar beside it looking un-clickable */
.byline .who-link {
  display: flex;
  align-items: center;
  gap: 12px;
  transition: color var(--duration) var(--ease);
}
.byline .who-link:hover .name { color: var(--brand-strong); }
.byline .who .name {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 15px;
  font-weight: var(--weight-semibold);
  line-height: 1.3;
}
.byline .byline-verified { width: 17px; height: 17px; flex-shrink: 0; color: var(--brand); }
.byline .who .role {
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  display: block;
}
/* Publish date over update date. The update line is the one readers check, so it
   carries the weight, the green dot and the underline (owner 2026-10-01). */
.byline .when {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  margin-left: auto;
  color: var(--foreground-muted);
  font-size: var(--text-xs);
  line-height: 1.6;
}
.byline .when-published { display: inline-flex; align-items: center; gap: 5px; }
.byline .byline-cal { width: 14px; height: 14px; flex-shrink: 0; }
.byline .when-updated {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--foreground-default);
  font-size: 13px;
  font-weight: var(--weight-bold);
  text-decoration: underline;
  text-decoration-color: var(--positive-soft);
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
.byline .when-updated::before {
  content: "";
  width: 8px;
  height: 8px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  background: var(--positive-soft);
  box-shadow: 0 0 0 3px var(--positive-tint);
}
/* The desk wears the logo mark: no initials tile behind it in either theme, and
   no round crop, which would cut the tips of the mark's C. */
.avatar.avatar-brand,
[data-theme="dark"] .avatar.avatar-brand { background: none; border: 0; border-radius: 0; overflow: visible; }
.avatar-brand .mark { display: block; width: 100%; height: 100%; }

/* --------------------------------------------------------------------------
   17. HEADER & NAV
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  height: var(--header-h-desktop);
  background: var(--background-default);
  border-bottom: 1px solid var(--border-default);
  transition: background-color var(--duration-slow) var(--ease);
}
.site-header .container {
  height: 100%;
  display: flex;
  align-items: center;
  gap: 26px;
  position: relative; /* containing block for .mega-wide (see §21b) */
}

.logo {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 22px;
  font-weight: var(--weight-bold);
  letter-spacing: -0.03em;
  flex-shrink: 0;
}
.logo .mark { width: 28px; height: 28px; }
.logo .accent { color: var(--brand-strong); }
/* The logo mark's ring is black in the original. It takes the text colour, so it
   reads on the light header, the dark header and the always-dark footer alike. */
.mark { color: var(--foreground-default); }
.footer-brand .mark { color: var(--foreground-on-dark); }
/* The desktop bar is 68px high and has room for a larger mark (owner, 2026-10-07).
   Up to 1000px the bar is tight (the English menu's last band, then the phone bar
   with its four buttons), so the mark keeps its 28px there. */
.site-header .logo .mark { width: 40px; height: 40px; }
@media (max-width: 1000px) {
  .site-header .logo .mark { width: 28px; height: 28px; }
}

.main-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  height: 100%;
}
.nav-item { height: 100%; display: flex; position: relative; }
.nav-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0 13px;
  font-size: 15px;
  font-weight: var(--weight-semibold);
  color: var(--foreground-light);
  position: relative;
  white-space: nowrap;
  transition: color var(--duration) var(--ease);
}
.nav-link::after {
  content: "";
  position: absolute;
  left: 13px;
  right: 13px;
  bottom: 14px;
  height: 2.5px;
  border-radius: var(--radius-pill);
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--duration) var(--ease);
}
.nav-link:hover,
.nav-panel-toggle:hover { color: var(--foreground-default); }
.nav-link:hover::after { transform: scaleX(1); }
.nav-link[aria-current="page"] { color: var(--foreground-default); }
.nav-link[aria-current="page"]::after { transform: scaleX(1); }
.nav-item:has(> .nav-panel-toggle:not([hidden])) > .nav-link { padding-right: 0; }
.nav-item:has(> .nav-panel-toggle:not([hidden])) > .nav-link::after { right: 0; }
.nav-panel-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 28px;
  width: 28px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--foreground-light);
  cursor: pointer;
}
/* ONE chevron artwork (chevDown() / the static nav copies); size lives here, never
   in per-site width/height attributes. flex:none because a squeezed flex parent
   otherwise crushes the svg (the table-header picker bug). */
.chev-down {
  width: 10px;
  height: 7px;
  flex: none;
}
.nav-panel-toggle .chev-down {
  width: 9px;
  height: 6px;
  transition: transform var(--duration) var(--ease);
}
.nav-panel-toggle[aria-expanded="true"] .chev-down { transform: rotate(180deg); }
/* nav labels never wrap, so the 901–1080 band must fit them by tightening gaps and
   padding instead (the desktop nav hides ≤900, so this rule only touches that band) */
@media (max-width: 1080px) {
  .site-header .container { gap: 16px; }
  .nav-link { padding: 0 8px; }
  .nav-link::after { left: 8px; right: 8px; }
}
/* right-edge nav items anchor their panel to the right so its (hidden) box can
   never extend the page past the viewport */
.mega.mega-right { left: auto; right: -20px; }
/* seven items now — the bottom of the band needs one squeeze more */
@media (max-width: 960px) {
  .site-header .container { gap: 10px; }
  .nav-link { padding: 0 5px; }
  .nav-link::after { left: 5px; right: 5px; }
}

/* Mega menu */
.mega {
  position: absolute;
  top: calc(100% - 6px);
  left: -20px;
  min-width: 480px;
  background: var(--surface-raised);
  color: var(--foreground-default);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-menu);
  padding: 22px 24px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 28px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease),
    visibility var(--duration);
}
.nav-item:not([data-nav-enhanced]):hover > .mega,
.nav-item:not([data-nav-enhanced]):focus-within > .mega,
.nav-item[data-nav-enhanced][data-mega-open="true"] > .mega {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.mega-title {
  grid-column: 1 / -1;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--brand);
  font-weight: var(--weight-semibold);
  margin-bottom: 6px;
}
.mega a {
  display: block;
  padding: 8px 10px;
  margin: 0 -10px;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--foreground-light);
  transition: color var(--duration-fast) var(--ease), background-color var(--duration-fast) var(--ease);
}
.mega a:hover {
  color: var(--foreground-default);
  background: var(--surface-well);
}
.mega a.mega-all {
  color: var(--foreground-default);
  background: color-mix(in srgb, var(--brand) 7%, var(--surface-well));
}
.mega a.mega-all:hover {
  background: color-mix(in srgb, var(--brand) 12%, var(--surface-well));
}
.mega a small {
  display: block;
  font-size: var(--text-xs);
  color: var(--foreground-light);
  font-weight: var(--weight-normal);
}

.header-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Theme toggle glyph follows the theme. Was an inline display:none in index.html plus
   two .style.display writes per toggle in applyTheme — three places to keep in sync
   for one boolean. Light is the default, so the sun is the hidden one at rest. */
.ico-sun { display: none; }
[data-theme="dark"] .ico-moon { display: none; }
[data-theme="dark"] .ico-sun { display: block; }

.icon-btn {
  width: 38px;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  color: var(--foreground-light);
  border: 1.5px solid transparent;
  transition: color var(--duration) var(--ease), border-color var(--duration) var(--ease),
    background-color var(--duration) var(--ease);
}
.icon-btn:hover {
  color: var(--foreground-default);
  border-color: var(--border-default);
  background: var(--surface-well);
}
.icon-btn svg { width: 18px; height: 18px; }

.hamburger { display: none; }
#menu-btn .ico-close { display: none; }
#menu-btn[aria-expanded="true"] .ico-close { display: block; }
#menu-btn[aria-expanded="true"] .ico-open { display: none; }

/* Mobile nav panel */
.mobile-nav {
  display: none;
  position: fixed;
  inset: var(--header-h) 0 auto 0;
  z-index: 99;
  background: var(--background-default);
  border-bottom: 1px solid var(--border-default);
  box-shadow: var(--shadow-menu);
  padding: 16px var(--container-pad) 26px;
  max-height: calc(100dvh - var(--header-h));
  overflow-y: auto;
}
.mobile-nav[data-open="true"] { display: block; }
.mobile-nav a {
  display: block;
  padding: 13px 4px;
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  border-bottom: 1px solid var(--border-default);
}
.mobile-nav a small {
  display: block;
  font-size: var(--text-xs);
  font-weight: var(--weight-normal);
  color: var(--foreground-muted);
}
.mobile-nav a[aria-current="page"] { color: var(--brand-strong); }
.mobile-nav .btn { margin-top: 18px; }

/* --------------------------------------------------------------------------
   18. FOOTER
   -------------------------------------------------------------------------- */
.site-footer {
  margin-top: var(--section-gap);
  background: var(--surface-dark);
  color: var(--foreground-on-dark-muted);
  font-size: var(--text-sm);
}
.footer-main {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr 1fr;
  gap: 40px;
  padding-block: 56px 40px;
}
.footer-brand .logo { color: var(--foreground-on-dark); margin-bottom: 14px; }
.footer-brand p { line-height: var(--leading-relaxed); max-width: 34ch; }
/* h2, not h4 — the visual size is set here, so the tag is free to be the one the
   document outline needs. As h4 these were a three-level jump on every page whose
   only heading is the h1 (the ranking hub, the review, the referral hub…), which is
   most of the site. h2 can never jump, whatever precedes it. */
.footer-col h2 {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--brand);
  margin-bottom: 16px;
}
.footer-col ul {
  list-style: none;
  display: grid;
  gap: 10px;
}
.footer-col a {
  transition: color var(--duration) var(--ease);
}
.footer-col a:hover { color: var(--foreground-on-dark); }
/* global signup band — every page, directly above the footer */
.newsletter-band {
  margin-top: var(--section-gap);
  background: var(--surface-well);
  border-top: 1px solid var(--border-default);
  padding-block: 28px;
}
.newsletter-band + .site-footer { margin-top: 0; }
.newsletter-band .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}
.newsletter-band .pitch strong {
  display: block;
  color: var(--foreground-default);
  font-size: var(--text-base);
}
.newsletter-band .pitch {
  color: var(--foreground-light);
  font-size: var(--text-sm);
}
.newsletter-band .newsletter { min-width: 340px; }
/* The four promises take their own full-width line under the pitch/form row. Four
   abreast needs more room than the pitch column has left once the 340px form has taken
   its share of --container-max, so sitting them beside the form would have silently
   wrapped them into two ragged rows. `flex-basis: 100%` is what breaks the line — the
   band's .container is the flex-wrap row that already holds the pitch and the form.

   Centred at their natural widths, NOT a 4×1fr grid: equal columns stretched the four
   to the full 1060px and the reader got four captions marooned at the far corners of a
   band instead of one legible list. Flex + wrap also retires the 900/640 column
   breakpoints this rule used to carry — the row now reflows on its own measure. */
.newsletter-band .nl-points {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 26px;
}
/* One per line by then, and a centred single column reads as ragged — line the ticks
   up on the left edge so it is a list again. */
@media (max-width: 640px) {
  .newsletter-band .nl-points { justify-content: flex-start; }
}
.footer-legal {
  border-top: 1px solid var(--surface-dark-raised);
  padding-block: 22px;
  font-size: var(--text-xs);
  line-height: 1.7;
  display: grid;
  gap: 12px;
}
.footer-legal .bar {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}
.footer-legal a { text-decoration: underline; text-underline-offset: 2px; }
.footer-legal a:hover { color: var(--foreground-on-dark); }

/* --------------------------------------------------------------------------
   19. HERO (home)
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  border-bottom: 1px solid var(--border-default);
  background:
    radial-gradient(680px 320px at 85% -40%, var(--brand-tint), transparent 70%),
    var(--surface-well);
  overflow: hidden;
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(var(--border-default) 1px, transparent 1px),
    linear-gradient(90deg, var(--border-default) 1px, transparent 1px);
  background-size: 44px 44px;
  opacity: 0.25;
  mask-image: radial-gradient(720px 380px at 80% 0%, black, transparent 75%);
  -webkit-mask-image: radial-gradient(720px 380px at 80% 0%, black, transparent 75%);
  pointer-events: none;
}
.hero .container {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: 48px;
  align-items: center;
  padding-block: 64px;
}
.hero h1 {
  font-size: var(--text-hero-title);
  margin-bottom: 18px;
}
.hero h1 .u {
  background-image: linear-gradient(var(--brand), var(--brand));
  background-repeat: no-repeat;
  background-size: 100% 30%;
  background-position: 0 88%;
}
.hero .lede {
  font-size: var(--text-lg);
  color: var(--foreground-light);
  max-width: 46ch;
  margin-bottom: 26px;
}
.hero .cta-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 22px;
}
.hero .trust {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  font-size: var(--text-xs);
  color: var(--foreground-muted);
}
.hero .trust span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.hero .trust .tick { color: var(--positive-strong); font-weight: var(--weight-bold); }

/* top pick mini stack */
.hero-picks {
  display: grid;
  gap: 12px;
}
.hero-picks .mini {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 18px;
  background: var(--surface-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  transition: border-color var(--duration) var(--ease), box-shadow var(--duration) var(--ease);
}
.hero-picks .mini:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-sm);
}
.hero-picks .mini .titles { flex: 1; min-width: 0; }
.hero-picks .mini .titles strong { display: block; font-size: 15px; }
.hero-picks .mini .titles small {
  color: var(--foreground-muted);
  font-size: var(--text-xs);
}
.hero-picks .head-note {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-semibold);
  color: var(--foreground-muted);
}
/* The ranking link only exists for markets that HAVE a ranking article, so it cannot
   be part of the stack's own rhythm — it sits on 4px of its own and the box simply
   ends after the third pick everywhere else.

   Deliberately NOT a .btn. It keeps the picks' own geometry — same radius, same 18px
   inset, label left and glyph right on the monogram/score columns — so it reads as the
   place the list continues rather than a control parachuted under it. It also has to
   wrap: .btn is `white-space: nowrap` and "…in United States of America" is wider than
   this column, so the pill would have burst its own container the day a second country
   ranking shipped.

   The resting state is the card surface under a 1.5px BRAND outline, not a tint fill:
   --brand-tint (#fff4e6) against the hero's own #fcf7f1 is a 3-value difference, which
   is no state at all — the control read as a smudge until you hovered it. Outline-tier
   is also the honest rank. A solid fill here matches "Browse all reviews" pound for
   pound and the hero ends up with two primaries arguing across the fold; this way the
   page CTA stays the loudest thing on it and the ranking link is plainly second. */
.arrow-card-link,
.hero-picks .picks-cta {
  margin-top: 4px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 18px;
  border: 1.5px solid var(--brand);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  color: var(--foreground-default);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  line-height: 1.35;
  transition: background-color var(--duration) var(--ease),
    border-color var(--duration) var(--ease),
    color var(--duration) var(--ease),
    box-shadow var(--duration) var(--ease);
}
/* outline fills in — the state actually changes rank on hover instead of nudging a
   shade, and every property that moves is paint (the border holds 1.5px), so nothing
   below it shifts */
.arrow-card-link:hover,
.hero-picks .picks-cta:hover {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--shadow-sm);
}
.arrow-card-link .arrow,
.hero-picks .picks-cta .arrow {
  color: var(--brand);
  transition: transform var(--duration) var(--ease), color var(--duration) var(--ease);
}
.arrow-card-link:hover .arrow,
.hero-picks .picks-cta:hover .arrow { color: inherit; transform: translateX(3px); }

/* staggered load-in — hero children only */
@media (prefers-reduced-motion: no-preference) {
  .hero [data-reveal] {
    opacity: 0;
    transform: translateY(14px);
    animation: rise 0.55s var(--ease-out) forwards;
  }
  .hero [data-reveal="2"] { animation-delay: 0.08s; }
  .hero [data-reveal="3"] { animation-delay: 0.16s; }
  .hero [data-reveal="4"] { animation-delay: 0.24s; }
  .hero [data-reveal="5"] { animation-delay: 0.32s; }
}
@keyframes rise {
  to { opacity: 1; transform: none; }
}

/* Category hero (listing pages) */
.page-hero {
  border-bottom: 1px solid var(--border-default);
  background: var(--surface-well);
  padding-block: 44px;
}
/* The hero already closes itself with 44px of padding and a rule, so the section
   under it does NOT need a second full `--section-gap / 2` on top — that stacked
   36px onto the 44px already there. The ranking pages made it worst: hero padding
   + section padding + the byline's own 16px lead put 97px of nothing between the
   lede and the separator. 24px here mirrors the byline's 24px bottom margin, so
   the rule sits with equal air on both sides. */
.page-hero + .section { padding-top: 24px; }
.page-hero + .section > .container > .byline:first-child { margin-top: 0; }

.hub-hero-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.65fr); gap: 40px; align-items: start; }
@media (max-width: 900px) {
  .hub-hero-grid { grid-template-columns: minmax(0, 1fr); gap: 24px; }
}
.page-hero h1 { margin-bottom: 10px; }
.page-hero .lede {
  color: var(--foreground-light);
  max-width: 62ch;
  font-size: var(--text-base);
}

/* --------------------------------------------------------------------------
   20. SCORE HERO (review page opener)
   -------------------------------------------------------------------------- */
/* score tile floats top-right (same 14/18 inset as the geo banner's country
   dropdown above it) and the verdict text wraps around it */
.score-hero {
  padding: 14px 18px 18px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  background: var(--surface-well);
}
.score-hero::after {
  content: "";
  display: table;
  clear: both;
}
.score-hero .score-box {
  float: right;
  margin: 0 0 12px 20px;
  text-align: center;
  padding: 8px 26px 10px;
  background: var(--surface-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
}
.score-hero .score-box .of {
  display: block;
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  margin-top: 4px;
}
.score-hero .verdict h2 {
  font-size: var(--text-lg);
  margin-bottom: 8px;
}
.score-hero .verdict p {
  font-size: var(--text-sm);
  color: var(--foreground-light);
}

/* Empty state */
.empty-state {
  text-align: center;
  padding: 56px 20px;
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  color: var(--foreground-light);
}
.empty-state .glyph { font-size: var(--text-3xl); margin-bottom: 10px; }
/* Level-agnostic, and the size is pinned rather than inherited: cwr_empty_state()
   emits h2 normally and h1 on 404.php, and all three levels must render identically —
   the tag answers to the document outline, not to the design. Without the explicit
   font-size the global h1/h2/h3 ladder would resize this block per caller. */
.empty-state :is(h1, h2, h3) {
  font-size: var(--text-sub-title);
  margin-bottom: 6px;
  color: var(--foreground-default);
}
.empty-state p { font-size: var(--text-sm); margin-bottom: 18px; }

/* --------------------------------------------------------------------------
   21. STYLE-GUIDE HELPERS — moved to style-guide.css (docket d7, ruled 2026-08-04).
   The UI kit does not ship; the theme conversion ports this file and must carry
   no style-guide selectors.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   22. UTILITIES
   -------------------------------------------------------------------------- */
.text-light { color: var(--foreground-light); }
.text-muted { color: var(--foreground-muted); }
.text-sm { font-size: var(--text-sm); }
.text-xs { font-size: var(--text-xs); }
.mono { font-family: var(--font-mono); }
.mt-1 { margin-top: 8px; }
.mt-2 { margin-top: 16px; }
.mt-3 { margin-top: 24px; }
.mt-4 { margin-top: 36px; }
.mb-2 { margin-bottom: 16px; }
.mb-3 { margin-bottom: 24px; }
.text-center { text-align: center; }
.m-0 { margin: 0; }
.flex { display: flex; align-items: center; gap: 10px; }
.between { justify-content: space-between; }
.wrap { flex-wrap: wrap; }
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   22b. COMPONENT MODIFIERS

   Introduced 2026-07-29 by the inline-style sweep: every rule here replaces a
   `style="..."` attribute that used to sit in app.js. Values are carried over
   verbatim - this was a refactor, not a redesign - so anything that looks like
   a magic number was already one. Tokenise them here, where they are visible,
   rather than back in the markup.

   Two of the old inline styles were pure no-ops (`gap:10px` on .flex, which
   already sets exactly that) and were simply deleted.
   -------------------------------------------------------------------------- */

/* Score numerals: one component, three sizes. */
.score-num-sm { font-size: 16px; }
.score-num-lg { font-size: 40px; }
/* Only the rank card shrinks the hero numeral. Scoped, because the old inline
   style beat BOTH the 64px base rule and the 48px rule in the 640 breakpoint -
   a bare class here would have shrunk the review hero and the UI-kit samples. */
.rank-side .score-hero-value { font-size: 44px; }

/* "n/a" stands in for a missing score and must hold the same optical weight. */
.score-na { font-size: 22px; font-weight: var(--weight-bold); }
.score-na-lg { font-size: 28px; font-weight: var(--weight-bold); }

/* Rank card side column. */
/* Home stats strip: auto-fit so 3 figures never orphan onto their own row. */
.stats-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 20px;
}

.rank-actions {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  width: 100%;
  max-width: 190px;
}
/* The tall Visit button (0.72.2) keeps its height but not btn-lg's 30px sides: "Visit
   ChangeNOW ↗" needs 157px, so the column grows to 190px and the sides shrink to 12px.
   A still longer name wraps inside the button instead of spilling past the column. */
.rank-actions .btn-lg { padding-inline: 12px; white-space: normal; text-align: center; }

/* Rank-card pops row: the method/feature detail-pop openers AND the availability
   chip, one horizontal row in the MAIN column (owner 2026-08-04 — the chip does not
   need its own row). */
.rank-pops {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 10px;
  align-items: center;
}
/* the chip's own top margin belongs to its standalone placements, not this row */
.rank-pops .avail-chip { margin-top: 0; }

/* Rank-card extras (owner 2026-08-04): the venue's bonus terms + the same
   "Terms & how to claim" link the bonus card carries, in the side column's free
   space under the CTAs. Rows are fail-closed spec rows, NOT the bonus card (that
   component's contract is fixed four rows; this one renders only authored values,
   and no values renders no block). */
.rank-extras {
  width: 100%;
  max-width: 170px;
  border-top: 1px solid var(--border-default);
  padding-top: 10px;
  display: grid;
  gap: 8px;
  font-size: var(--text-xs);
}
.rx-rows { display: grid; gap: 4px; }
.rx-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  align-items: baseline;
}
.rx-row > span { color: var(--foreground-light); }
.rx-row > b { text-align: right; }
/* Glance-table visit affordances (owner 2026-08-04): a "Visit ↗" text link after the
   name — the review page's "Source ↗" idiom pointed outward — and click-anywhere rows. */
.cmp-name { justify-content: space-between; gap: 12px; }
.table-visit {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--accent);
  white-space: nowrap;
}
.table-visit:hover { color: var(--foreground-default); }
tr.row-visit { cursor: pointer; }
tr.row-visit:hover td { background: var(--surface-well); }
/* the heading lives outside .prose, so it needs its own header-offset anchor room */
#at-a-glance { scroll-margin-top: calc(var(--header-h-desktop) + 20px); }

/* Pill openers in the method-chip visual vocabulary — same radius/border/surface,
   plus the interactive tells a static chip lacks: pointer, hover lift, chevron. */
.rx-pop {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--foreground-default);
  cursor: pointer;
  justify-self: start;
}
.rx-pop:hover { border-color: var(--accent); color: var(--accent); }
.rx-pop b { font-weight: var(--weight-bold); }
.rx-ico { display: inline-flex; width: 15px; height: 15px; flex: none; color: var(--accent); }
.rx-ico svg { width: 100%; height: 100%; }
.rx-pop[data-detail-pop="methods"] .rx-ico { color: var(--brand-strong); }
.rx-chev { width: 9px; height: 6px; flex: none; color: var(--foreground-light); }
.rx-pop:hover .rx-chev { color: var(--accent); }

/* Detail pop — the one dialog shell (methods + features share it). */
.detail-pop-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  background: color-mix(in srgb, var(--surface-dark) 45%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.detail-pop {
  width: min(560px, 100%);
  max-height: min(70vh, 640px);
  display: flex;
  flex-direction: column;
  padding: 18px 20px;
  gap: 14px;
}
.dp-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.dp-head h3 { flex: 1; min-width: 0; }
.dp-body {
  overflow-y: auto;
  display: grid;
  gap: 12px;
}

/* Filter/sort toolbars above listings. */
.toolbar-row { gap: 14px; }
.list-toolbar { gap: 12px; margin-bottom: 12px; }
.sort-label { gap: 8px; }
.select-auto { width: auto; }
.rank-list {
  display: grid;
  gap: 24px;
  padding-top: 12px;
}

/* Search field widths by context. */
.search-bar-inline { max-width: 260px; margin: 12px 0 2px; }
.search-bar-md { min-width: 220px; }
.search-bar-lg { min-width: 240px; }

/* Review header. */
.review-head { gap: 16px; margin-bottom: 14px; }
.review-head-main { flex: 1; min-width: 240px; }

/* The verdict heading opens its own block, so it sits flush. Must be scoped
   rather than a .mt-0 utility: this h2 lives inside .prose, and .prose h2
   (0,1,1) beats any single class. The old inline style won by being inline. */
.verdict h2 { margin-top: 0; }

/* Section + sidebar modifiers. */
.section-flush { padding-bottom: 0; }
.sidebar-sticky-offset { margin-top: 20px; }
.chip-row-tight { margin-bottom: 8px; }

/* The mark inside a chip inherits the chip's ink colour. */
.chip-mark { color: var(--chip-ink, var(--on-accent)); }

/* Measures narrower than --container-max. */
.container-md { max-width: 860px; }
.container-narrow { max-width: 820px; }
.empty-state-narrow { max-width: 560px; margin-inline: auto; }
/* Sidebar-less pages (buy-crypto-with): prose runs the full container, matching the
   ranked block beside it — the 68ch measure is for pages where a sidebar justifies
   a narrower column (owner 2026-08-04). */
.prose-full { max-width: none; }

/* Guide article title sits under a breadcrumb trail. */
.guide-title { margin-top: 10px; }

/* --------------------------------------------------------------------------
   23. RESPONSIVE
   -------------------------------------------------------------------------- */
@media (max-width: 1200px) {
  /* below the full container the column can no longer hold 68ch anyway, so the
     sidebar gives up proportionally rather than eating the whole shortfall */
  :root { --sidebar-w: 280px; }
}

@media (max-width: 1024px) {
  /* single column: the whole article shares the 68ch reading measure, centered —
     otherwise the header and tables run ~250px past the prose's right edge */
  .article-layout {
    grid-template-columns: minmax(0, 1fr);
    max-width: 68ch;
    margin-inline: auto;
  }
  .article-layout .prose { max-width: none; }
  .sidebar-sticky { position: static; }
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* two columns: any odd-count grid ends in an orphan — span it (a single-card grid
     is not an orphan, same guard as the three-column rule) */
  .grid-3 > :last-child:nth-child(odd):not(:first-child):not(.article-card) { grid-column: 1 / -1; }
  .hero .container { grid-template-columns: 1fr; padding-block: 48px; gap: 32px; }
  .footer-main { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  /* five children in two columns: the odd last column spans instead of orphaning */
  .footer-main > :last-child:nth-child(odd) { grid-column: 1 / -1; }
  .toc { display: none; }
  /* fact sheet moves up into the article; the aside copy would render at the
     very bottom of the stacked layout */
  .factsheet-inline { display: block; }
  .factsheet-aside { display: none; }
}

@media (max-width: 900px) {
  .main-nav { display: none; }
  .hamburger { display: inline-flex; }
  .site-header { height: var(--header-h); }
  html { scroll-padding-top: calc(var(--header-h) + 16px); }
  .sidebar-sticky { top: calc(var(--header-h) + 20px); }
  .prose h2 { scroll-margin-top: calc(var(--header-h) + 20px); }
  .ranking-content h2, .ranking-spine h2 { scroll-margin-top: calc(var(--header-h) + 20px); }
  :root { --text-hero-title: var(--text-3xl); }
  .rank-card { grid-template-columns: minmax(0, 1fr); }
  .rank-card .rank-no { position: absolute; top: 24px; left: 24px; z-index: 1; }
  .rank-card .rank-head { padding-left: 58px; min-height: 44px; }
  .rank-card .rank-side {
    flex-direction: row;
    justify-content: space-between;
    width: 100%;
    border-top: 1px solid var(--border-default);
    padding-top: 16px;
  }
  /* the row already carries the full-width separator above — the extras block's own
     border was a second line inside it (owner 2026-08-04) */
  .rank-card .rank-extras { border-top: 0; padding-top: 0; }
}

@media (max-width: 640px) {
  :root { --section-gap: 52px; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: minmax(0, 1fr); }
  .prose .grid-3 { grid-template-columns: minmax(0, 1fr); }
  .grid-equal-rows { grid-auto-rows: auto; }
  :root { --text-page-title: var(--text-2xl); --text-hero-title: var(--text-2xl); }
  :root {
    --text-section-title: var(--text-xl);
    --text-sub-title: var(--text-lg);
    --text-minor-title: var(--text-base);
  }
  .score-hero-value { font-size: 48px; }
  /* tile stays top-right on phones too (owner ruling) — tighter inset so the
     verdict text keeps a usable wrap width beside it */
  .score-hero .score-box {
    padding: 6px 14px 8px;
    margin: 0 0 10px 14px;
  }
  .pros-cons { grid-template-columns: 1fr; }
  .footer-main { grid-template-columns: 1fr; gap: 28px; padding-block: 40px 28px; }
  .newsletter-band .newsletter { min-width: 0; width: 100%; }
  .newsletter { flex-direction: column; }
  .nl-points { grid-template-columns: minmax(0, 1fr); }
  .section-header { flex-direction: column; align-items: flex-start; gap: 8px; }
  .byline .when { margin-left: 0; align-items: flex-start; width: 100%; }
  .hero .cta-row .btn { flex: 1; }
  /* Rank card on phones (owner 2026-08-04): score + buttons + bonus side by side was
     cluttered. The bonus block moves above the score + buttons row as a WELL BOX —
     the same surface treatment as the specs box, so it reads as its own section
     without a second rule floating in whitespace. Cards without a bonus collapse
     cleanly: no block, no box. */
  .rank-card .rank-side { flex-wrap: wrap; row-gap: 14px; }
  .rank-card .rank-extras {
    order: -1;
    width: 100%;
    max-width: none;
    background: var(--surface-well);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    padding: 12px 16px;
    gap: 6px;
  }
  .rank-card .rx-row { font-size: var(--text-sm); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  html { scroll-behavior: auto; }
}

/* ==========================================================================
   CWR build additions — real-data components (tokens only, no raw colors)
   ========================================================================== */

/* real exchange logos inside monogram tiles (initials remain the fallback) */
.monogram { position: relative; overflow: hidden; }
.monogram .logo-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; border-radius: inherit;
  padding: 5px;
  background: var(--logo-plate);
}
/* initials are the fallback, not an underlay — transparent-cornered logos
   (Coinbase) used to show them peeking through */
.monogram[data-logo] .mg-initials { visibility: hidden; }
/* the hue tile is the initials-fallback identity only — once a real logo sits on
   the plate the ring goes neutral, so colors can't read as meaning (owner 2026-07-30)
   …and so does the GROUND under it. The plate above is inset by the border but takes
   `border-radius: inherit`, i.e. the OUTER radius on a box that needs the inner one,
   and on a fractional-DPR display the inset itself lands mid-device-pixel. Both leak
   a hairline of this element's own background around the plate. In light mode that
   hairline is pale hue on white and invisible; in dark mode it is near-black on white
   and reads as four lines boxing the icon (owner report, 2026-08-13). Painting the
   plate colour here leaves nothing behind the seam to show, at any zoom or scaling —
   the geometry stays as it is, because chasing inner-radius arithmetic would have to
   be redone for every tile size. */
.monogram[data-logo],
[data-theme="dark"] .monogram[data-logo] {
  border-color: var(--border-default);
  background: var(--logo-plate);
}
/* …except the ring IS meaning now (owner, same day): availability in the visitor's
   country — green supported, red restricted. Re-renders with the residence picker. */
.monogram[data-avail="true"],
[data-theme="dark"] .monogram[data-avail="true"] {
  border-color: var(--positive);
}
.monogram[data-avail="false"],
[data-theme="dark"] .monogram[data-avail="false"] {
  border-color: var(--negative);
}

/* geo availability banner (mock IP detect in the prototype) */
.ip-banner {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 18px; margin-bottom: 20px;
  border: 1px solid var(--positive-border);
  background: var(--positive-tint-faint);
  border-radius: var(--radius-lg);
}
.ip-banner[data-ok="false"] {
  border-color: var(--negative-border);
  background: var(--negative-tint-faint);
}
/* The restricted verdict is the loudest thing this page says, and on dark the
   faint tokens said it in a whisper — the same 8% fill and 30% border that all
   but vanished on the country pills (owner report 2026-08-16). It takes the
   pills' values exactly: the banner and the list are one verdict stated twice,
   and a banner softer than the chips below it reads as the weaker claim.

   Only the frame moves. The title keeps --foreground-default and the
   description --foreground-light, both still AA over an 18% salmon fill on
   #191919 (11.9:1 and 6.2:1), so nothing that carries words gets quieter. */
[data-theme="dark"] .ip-banner[data-ok="false"] {
  border-color: rgba(243, 135, 109, 0.6);
  background: rgba(243, 135, 109, 0.18);
}
.ip-banner .flag { font-size: 30px; line-height: 1; }
.ip-banner .txt { flex: 1; min-width: 200px; display: grid; gap: 2px; }
.ip-banner .txt strong { font-size: var(--text-base); }
.ip-banner .txt span { font-size: var(--text-sm); color: var(--foreground-light); }
.ip-banner .select { width: auto; max-width: 220px; flex: none; }
@media (max-width: 600px) {
  .ip-banner { flex-wrap: wrap; }
  .ip-banner .select { width: 100%; max-width: none; }
}

/* payment / withdrawal method chips */
/* deposit/withdrawal sections live in a contained card: table-style darker
   heading row (the section h2, so TOC anchors keep working), then search + pills */
.method-card {
  margin: 20px 0;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  overflow: hidden;
}
.prose .method-card-head,
.method-card-head {
  margin: 0;
  padding: 12px 18px;
  font-size: var(--text-base);
  background: var(--surface-well);
  border-bottom: 1px solid var(--border-default);
}
.method-card-body { padding: 2px 18px 16px; }
.method-card-body .method-chips { margin-bottom: 2px; }

.method-chips { display: flex; flex-wrap: wrap; gap: 10px; margin: 14px 0; }
.method-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px; border: 1px solid var(--border-default);
  border-radius: var(--radius-pill); background: var(--surface-raised);
  font-size: var(--text-sm); font-weight: 500; color: var(--foreground-default);
}

/* Shared searchable restriction list used by the hub and exchange guides. */
.availability-restriction-list-component > h2 { margin: 0 0 8px; }
.availability-restriction-summary { margin: 0 0 12px; }
.availability-restriction-search { width: 100%; margin-bottom: 12px; }
.availability-restriction-search .input { width: 100%; }
[data-availability-restriction-list] .chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 18px;
}
[data-availability-restriction-list] .method-chip {
  width: auto;
  max-width: 100%;
  min-width: 0;
  border-radius: var(--radius-md);
  overflow-wrap: anywhere;
}
[data-availability-restriction-list] .method-chip[hidden] { display: none; }
.availability-search-empty { color: var(--foreground-muted); }
.mc-ico { display: inline-flex; width: 16px; height: 16px; flex: none; color: var(--foreground-light); }
.mc-ico svg { width: 100%; height: 100%; }
[data-kind="bank"] .mc-ico { color: var(--accent); }
[data-kind="card"] .mc-ico { color: var(--brand-strong); }
[data-kind="crypto"] .mc-ico { color: var(--positive-strong); }
[data-kind="wallet"] .mc-ico { color: var(--foreground-light); }
[data-kind="p2p"] .mc-ico { color: var(--neutral-score); }
.method-chip[data-kind="restricted"] {
  border-color: var(--negative-border); background: var(--negative-tint-faint);
}
/* On dark, the shared faint tint (8% salmon) and 30% border all but vanish: the
   chip reads 1.13:1 against #191919 and the border 1.75:1, so a wall of 35
   country pills looks like plain chips carrying no negative signal at all.
   Scoped rather than a token bump — --negative-tint-faint and --negative-border
   are shared with nine other components on other surfaces, and this list is the
   one place the tint alone has to carry the meaning.

   Border 0.6 clears the 3:1 non-text threshold on both dark surfaces it can land
   on (3.36:1 on the page, 3.18:1 on a card). Salmon ink rather than the default
   foreground, matching .avail-chip[data-ok="false"]: 5.23:1 and 4.65:1, AA on
   both. The tint stays low enough that the ink keeps that headroom. */
[data-theme="dark"] .method-chip[data-kind="restricted"] {
  border-color: rgba(243, 135, 109, 0.6);
  background: rgba(243, 135, 109, 0.18);
  color: var(--negative);
}

/* rank-card availability chip */
.avail-chip {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 12px; padding: 6px 12px;
  border-radius: var(--radius-pill); font-size: var(--text-sm); font-weight: 500;
  border: 1px solid var(--positive-border); background: var(--positive-tint-faint);
  color: var(--positive-strong);
}
.avail-chip[data-ok="false"] {
  border-color: var(--negative-border); background: var(--negative-tint-faint);
  color: var(--negative-strong);
}
/* Neutral until the reader's country is known. The base rule is positive-tinted,
   so without this the server-rendered chip reads as a green "supported" verdict
   before geo.js has resolved anything — a claim the cached HTML cannot make. */
.avail-chip[data-ok=""] {
  border-color: var(--border-default); background: var(--surface-well);
  color: var(--foreground-muted);
}

/* n/a score pill */
.score-pill[data-band="none"] {
  background: var(--surface-well); color: var(--foreground-muted);
  border: 1px solid var(--border-default);
}

/* world availability map */
.world-map { padding: 14px; }
.world-map .map-slot svg { width: 100%; height: auto; display: block; }
.world-map .geo-country { stroke: var(--background-default); stroke-width: 0.5; }
.world-map .geo-supported { fill: var(--positive); opacity: 0.75; }
.world-map .geo-supported:hover { opacity: 1; }
.world-map .geo-restricted { fill: var(--negative); }
.world-map .geo-restricted:hover { opacity: 0.85; }
.world-map .geo-unconfirmed { fill: var(--foreground-light); opacity: .35; }
.map-legend .dot-unconfirmed { background: var(--foreground-light); opacity: .5; }
.map-legend {
  display: flex; flex-wrap: wrap; gap: 12px 22px; padding: 12px 8px 2px;
  font-size: var(--text-sm); color: var(--foreground-light);
}
.map-legend .dot {
  display: inline-block; width: 12px; height: 12px;
  border-radius: 50%; margin-right: 7px; vertical-align: -1px;
}
.map-legend .dot-ok { background: var(--positive); }
.map-legend .dot-no { background: var(--negative); }

/* vs hero */
.vs-hero {
  display: flex; align-items: center; justify-content: space-around; gap: 18px;
  padding: 26px 18px; margin-bottom: 24px; flex-wrap: wrap;
}
.vs-hero .side {
  display: grid; justify-items: center; gap: 10px;
  font-size: var(--text-lg); text-decoration: none; color: var(--foreground-default);
}
.vs-cta { display: grid; gap: 10px; align-content: start; }
/* exchange switcher = the same searchable combo as the country picker */
.exchange-combo .combo-btn { font-weight: var(--weight-semibold); }
.exchange-combo .combo-list .monogram {
  width: 24px;
  height: 24px;
  font-size: var(--text-xs);
  border-radius: var(--radius-sm);
}
/* related-articles heading carries the exchange tile */
.vs-more-head { display: flex; align-items: center; gap: 10px; }
/* winning value: green underline, not a pill (owner 2026-07-30) */
.vs-best {
  border-bottom: 2px solid var(--positive);
  padding-bottom: 1px;
}
.vs-hero .vs-badge {
  font-weight: 800; font-size: var(--text-xl); color: var(--brand-strong);
  border: 2px solid var(--brand-border); border-radius: 50%;
  width: 56px; height: 56px; display: grid; place-items: center;
  background: var(--brand-tint);
}

/* review feature grid */
.feature-grid { display: flex; flex-wrap: wrap; gap: 10px; margin: 14px 0; }
.feature-grid .feat {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px; border-radius: var(--radius-pill);
  font-size: var(--text-sm); font-weight: 500;
  border: 1px solid var(--positive-border); background: var(--positive-tint-faint);
  color: var(--positive-strong);
}
/* flex-none so the tick keeps its size when the label wraps */
.feature-grid .feat svg { width: 14px; height: 14px; flex: none; }
.feature-grid .feat[data-on="false"] {
  border-color: var(--border-default); background: var(--surface-well);
  color: var(--foreground-muted); text-decoration: line-through;
}


/* live data strip under "N. Exchange" article headings */
.ex-strip {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 16px; margin: 14px 0 18px;
  border: 1px solid var(--border-default); border-radius: var(--radius-lg);
  background: var(--surface-well);
}
.ex-strip .titles { display: grid; line-height: 1.25; }
.ex-strip .titles small { color: var(--foreground-light); font-size: var(--text-xs); }
.ex-strip .spacer { flex: 1; }

/* sticky-TOC visit button — appears once the fact sheet has scrolled away */
.toc-visit {
  display: none;
}
.toc-visit[data-show="true"] {
  display: inline-flex;
  animation: tocVisitIn 200ms cubic-bezier(0.4, 0, 0.2, 1);
}
/* ≤1024px the aside stacks below the article and .mobile-cta owns the signup
   CTA; the display:none aside fact sheet also reads as "scrolled away" to the
   observer, which would show this button permanently */
@media (max-width: 1024px) {
  .toc-visit[data-show="true"] { display: none; }
}
@keyframes tocVisitIn {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* sticky fix: the sidebar column must span the full article height, otherwise
   position:sticky has no room to travel (align-items:start kept it content-sized) */
.article-layout > aside { align-self: stretch; }

/* mobile sticky signup bar — shows only when the sidebar TOC is gone (≤1024px) */
.mobile-cta {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  align-items: center;
  gap: 12px;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
  background: var(--surface-raised);
  border-top: 1px solid var(--border-default);
  box-shadow: var(--shadow-menu);
}
.mobile-cta .mc-titles { flex: 1; min-width: 0; display: grid; line-height: 1.25; }
.mobile-cta .mc-titles strong { font-size: var(--text-base); }
.mobile-cta .mc-titles small {
  color: var(--foreground-light); font-size: var(--text-xs);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mobile-cta .btn { flex: none; }
@media (max-width: 1024px) {
  .mobile-cta { display: flex; }
  body.has-mobile-cta .site-footer { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }
}

/* ip-banner v2: flag + title + compact picker on one row, description full width.
   Own long-token safety here so placement inside .prose cannot change wrapping. */
.ip-banner { display: block; overflow-wrap: anywhere; }
.ip-banner .ip-head { display: flex; align-items: center; gap: 12px; }
.ip-banner .ip-head strong { flex: 1; min-width: 0; font-size: var(--text-base); }
/* The mockup writes the combo straight into .ip-head; this theme writes a
   declarative [data-country-picker] slot instead, because geo.js re-renders the
   picker on every country change and needs an anchor that survives it. The
   wrapper must not become the flex/grid item, or the combo stops being one:
   the [data-stack] rules below address .country-combo directly, and in the flex
   state the extra box pushed the title off the icon. `contents` keeps the slot
   in the DOM and its child in the layout. */
.ip-banner .ip-head [data-country-picker] { display: contents; }
.ip-banner .ip-head .select {
  flex: none; width: auto; max-width: 170px;
  padding: 7px 30px 7px 12px; font-size: var(--text-sm);
}
.ip-banner .ip-desc {
  margin: 8px 0 0; font-size: var(--text-sm); color: var(--foreground-light);
}
/* Banner links are component chrome, never prose links. The explanatory source
   link stays at 1px; the next-step alternatives CTA is deliberately stronger at
   2px. This hierarchy must be identical wherever the banner is mounted. */
.ip-banner :is(.ip-desc, .ip-alt) a {
  font-weight: var(--weight-semibold);
  color: var(--foreground);
  border-bottom: 1px solid var(--brand);
  transition: color var(--duration) var(--ease);
}
.ip-banner .ip-alt a { border-bottom-width: 2px; }
.ip-banner :is(.ip-desc, .ip-alt) a:hover { color: var(--brand-strong); }
/* Its own line under the sentence, so it reads as an action rather than as
   another clause. Layout only — the link treatment is the rule above. */
.ip-banner .ip-alt {
  margin: 10px 0 0;
  font-size: var(--text-sm);
}
/* EU platform card (inc/components/eu-platform-card.php): its own card right
   after the geo banner, framed like the incident box but in blue (owner
   2026-09-27). Heading, sentence and button stack in one column beside the
   flag (owner 2026-10-03): with the button beside the sentence, a long label
   ("Sieh dir die Bonusangebote von Bybit EU an") squeezed the text into a
   narrow column. The geo gate hides it with [hidden], which the grid display
   would otherwise override. */
.eu-platform-card {
  --eu-blue: #1d4ed8;
  --eu-blue-ink: #1e3a8a;
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas: "flag title" "flag body" "flag cta";
  align-items: center;
  gap: 10px 18px;
  margin: 0 0 20px;
  padding: 18px 20px;
  border: 2px solid var(--eu-blue);
  border-radius: var(--radius-lg);
  background: #eff6ff;
}
[data-theme="dark"] .eu-platform-card {
  --eu-blue: #3b82f6;
  --eu-blue-ink: #93c5fd;
  background: rgba(59, 130, 246, 0.1);
}
.eu-platform-card[hidden] { display: none; }
.eu-platform-card--text { grid-template-areas: "flag title" "flag body"; }
.eu-platform-card .eu-platform-flag { grid-area: flag; align-self: start; line-height: 0; }
.eu-platform-card .eu-platform-flag .flag-img { border-radius: 4px; }
.eu-platform-card .eu-platform-title { grid-area: title; font-size: var(--text-base); color: var(--eu-blue-ink); }
.eu-platform-card .eu-platform-body { grid-area: body; margin: 0; font-size: var(--text-sm); color: var(--foreground-light); }
.eu-platform-card > .eu-platform-link,
.eu-platform-card > .eu-platform-signup { grid-area: cta; justify-self: start; margin-top: 4px; }
.eu-platform-card .eu-platform-link { white-space: nowrap; }
@media (max-width: 700px) {
  .eu-platform-card { grid-template-columns: auto 1fr; grid-template-areas: "flag title" "body body" "cta cta"; }
  .eu-platform-card--text { grid-template-areas: "flag title" "body body"; }
  .eu-platform-card .eu-platform-flag { align-self: center; }
  .eu-platform-card > .eu-platform-link,
  .eu-platform-card > .eu-platform-signup { justify-self: stretch; }
  /* A long translated label ("Sieh dir die Bonusangebote von Bybit EU an") is wider
     than a phone: let it wrap. The arrow stays glued to the last word. */
  .eu-platform-card .eu-platform-link { width: 100%; white-space: normal; text-align: center; }
}
/* EU choice pop (inc/components/eu-choice.php, owner 2026-09-28): an EU
   reader's "Visit" on a global venue with a separate EU version. The site's
   detail-pop shell; two option columns, the EU one tinted and outlined in the
   EU card's blue, the blocked one greyed. Stacks on phones. */
.eu-choice {
  --eu-blue: #1d4ed8;
  width: min(640px, 100%);
  max-height: none;
}
[data-theme="dark"] .eu-choice { --eu-blue: #3b82f6; }
.eu-choice .dp-head h3 { font-size: var(--text-lg); line-height: 1.3; }
.euc-lead { margin: 0; color: var(--foreground-muted); font-size: var(--text-sm); line-height: 1.55; }
.euc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.euc-opt {
  display: grid;
  justify-items: center;
  align-content: start;
  gap: 6px;
  padding: 16px 14px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  text-align: center;
}
.euc-opt strong { font-size: var(--text-base); }
.euc-sub { font-size: var(--text-xs); color: var(--foreground-muted); }
.euc-status { display: inline-flex; align-items: center; gap: 6px; margin: 2px 0 6px; font-size: var(--text-sm); font-weight: 600; }
.euc-status svg { width: 14px; height: 14px; }
.euc-status.no { color: var(--negative-strong); }
.euc-status.yes { color: var(--positive-strong); }
.euc-opt.is-blocked .monogram { filter: grayscale(1); opacity: .6; }
.euc-opt.is-blocked strong { color: var(--foreground-muted); }
.euc-opt.is-ok { border: 2px solid var(--eu-blue); background: #eff6ff; }
[data-theme="dark"] .euc-opt.is-ok { background: rgba(59, 130, 246, 0.1); }
.euc-opt .btn { width: 100%; justify-content: center; }
.euc-foot { margin: 0; font-size: var(--text-xs); color: var(--foreground-muted); text-align: center; }
.euc-foot button { padding: 0; border: 0; background: none; color: var(--accent); font: inherit; text-decoration: underline; cursor: pointer; }
@media (max-width: 560px) {
  .euc-grid { grid-template-columns: 1fr; }
}
.flag-img {
  display: inline-block; vertical-align: -3px;
  border-radius: 3px;
}
.ip-banner .flag .flag-img { vertical-align: middle; }
.th-flag { display: inline-flex; align-items: center; gap: 6px; }
.avail-chip .flag-img { vertical-align: -4px; }
/* Picker above the verdict line, title full width beneath it. Set by fitGeoBanners()
   in app.js when the title would otherwise wrap to 3+ lines beside the combo — a
   measured condition, not a width: the same banner is one line for "Kraken accepts
   clients from Malta" and four for "Binance is not available in United States of
   America" at the identical viewport, so any breakpoint both over- and under-fires.
   Grid, not flex-wrap: wrapping puts the combo BELOW the title, which is the layout
   we are trying to get away from. */
.ip-banner .ip-head[data-stack] {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 8px 10px;
}
/* On its own row the combo sizes to the country name rather than stretching: a
   full-width pill for "Malta" is a lot of chrome for one word. It may still grow to
   the full row when the name is long, never past it. */
.ip-banner .ip-head[data-stack] .country-combo {
  grid-column: 1 / -1;
  grid-row: 1;
  justify-self: start;
  max-width: 100%;
}
.ip-banner .ip-head[data-stack] .country-combo .combo-btn { width: auto; max-width: 100%; }
.ip-banner .ip-head[data-stack] .cv-ico { grid-row: 2; }
.ip-banner .ip-head[data-stack] strong { grid-row: 2; }

/* searchable country combo */
.country-combo { position: relative; flex: none; }
/* header variant: flag + ISO code, pill-shaped to sit in the .icon-btn row */
.combo-compact .combo-btn {
  padding: 7px 11px;
  border-radius: var(--radius-pill);
  font-weight: var(--weight-semibold);
}
/* phone header: the picker joins search/theme/hamburger in a 375px row — tighten
   the pill and the action gap so the row keeps the zero-overflow baseline */
@media (max-width: 640px) {
  .header-actions { gap: 6px; }
  .combo-compact .combo-btn { padding: 7px 9px; gap: 6px; }
}
/* smallest phones: flag-only pill (the aria-label and the open list carry the name);
   the chevron goes too — before flex:none it was silently crushed to 0 anyway */
@media (max-width: 480px) {
  .combo-compact .combo-btn span,
  .combo-compact .combo-btn .chev-down { display: none; }
  /* flag at natural size (no flex-crush) needs the pill trimmed to keep the row */
  .combo-compact .combo-btn { padding: 6px 5px; }
}
.combo-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 12px; font-size: var(--text-sm); font-weight: 500;
  font-family: var(--font-sans); color: var(--foreground-default);
  background: var(--surface-raised); border: 1px solid var(--border-default);
  border-radius: var(--radius-md); cursor: pointer;
  transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.combo-btn:hover { border-color: var(--border-strong); }
.combo-btn .chev-down { color: var(--foreground-light); }
/* right:0 is the anchoring; geo.js placePop() slides the box back inside the
   viewport when that anchoring would hang it off an edge (a picker at the LEFT
   of a phone row otherwise threw the pop off screen). max-width is the floor
   under that: below ~266px no placement can help a 250px box, so it narrows
   instead. The 16px is placePop's POP_GUTTER on both sides — keep them equal. */
.combo-pop {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 70;
  width: 250px; max-width: calc(100vw - 16px); padding: 10px;
  background: var(--surface-raised); border: 2px solid var(--brand);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-menu);
}
.combo-title {
  padding: 2px 2px 8px;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}
.combo-title small {
  display: block;
  margin-top: 1px;
  font-size: var(--text-xs);
  font-weight: var(--weight-normal);
  color: var(--foreground-muted);
}
.combo-search { width: 100%; margin-bottom: 8px; }
/* `ul.combo-list` deliberately ties and follows `.prose ul`, while the option
   rule follows `.prose li`: the combo owns its layout even when a component
   containing it is mounted inside an article stream. */
ul.combo-list {
  display: block; gap: 0;
  list-style: none; margin: 0; padding: 0;
  max-height: 250px; overflow-y: auto;
}
.combo-list li {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: var(--radius-sm);
  font-size: var(--text-sm); line-height: var(--leading-body); cursor: pointer;
}
.combo-list .combo-group {
  padding: 9px 10px 3px;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--foreground-muted);
  cursor: default;
}
.combo-list .combo-group:hover { background: transparent; }
.combo-list .combo-group + .combo-group,
.combo-list li[data-combo-pop] + .combo-group,
/* any group header that follows option rows separates — the language pane's
   "All languages" after the Recommended rows earns the same rule the country
   list's "All countries" gets after Popular (0.27.1) */
.combo-list li[data-combo-opt] + .combo-group {
  margin-top: 6px;
  border-top: 1px solid var(--border-default);
  border-radius: 0;
  padding-top: 10px;
}
.combo-list li:hover, .combo-list li:focus-visible { background: var(--surface-well); }
.combo-list li[aria-selected="true"] { background: var(--brand-tint-faint); font-weight: 600; }
/* ONE header pill for region AND language (0.23.0): the pop grows into two
   side-by-side panes — country left, language right — each with its OWN
   search box (the input handler scopes filtering to its pane). Only the
   header pill ever gets .combo-pop-wide; banner pickers stay single-pane. */
.combo-pop-wide { width: 520px; }
.combo-panes { display: flex; gap: 12px; align-items: flex-start; }
.combo-panes .combo-pane { flex: 1 1 0; min-width: 0; }
.combo-panes .combo-pane + .combo-pane {
  border-left: 1px solid var(--border-default);
  padding-left: 12px;
}
@media (max-width: 560px) {
  /* Keep the header picker split 50/50 on phones: residence stays left and
     language stays right. .combo-pop's viewport max-width narrows the 520px
     desktop width while the two grid tracks remain exactly equal. */
  .combo-panes {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 8px;
    align-items: start;
  }
  .combo-panes .combo-pane + .combo-pane {
    padding-left: 8px;
  }
  /* A long country or language label wraps inside its half instead of making
     either pane wider than its 50% share. */
  .combo-list li { align-items: flex-start; }
  .combo-list li > span {
    min-width: 0;
    white-space: normal;
    overflow-wrap: anywhere;
  }
}
/* reviews index — controls row, compact roster table, per-row actions */
.reviews-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.reviews-controls .input {
  flex: 1 1 220px;
  max-width: 340px;
}
.chip-sep {
  width: 1px;
  align-self: stretch;
  margin: 2px 4px;
  background: var(--border-default);
}
/* labeled toggle switch (reviews "Exclude restricted") — knob slides, layout never shifts */
/* The availability switch and the clear-filters control share a row. Clear is
   hidden until something is on, so this row is usually just the switch — the
   gap only exists when the second control does. Wraps rather than squeezing:
   on a phone "Clear 4 filters" beside a switch is two lines by design. */
.filter-switch-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 14px;
}
.switch-ctl {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 4px;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--foreground-light);
  cursor: pointer;
  transition: color var(--duration) var(--ease);
}
.switch-ctl:hover,
.switch-ctl[aria-checked="true"] { color: var(--foreground-default); }
.switch-track {
  position: relative;
  width: 34px;
  height: 20px;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  transition: background-color var(--duration) var(--ease);
}
.switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: var(--radius-pill);
  background: var(--logo-plate);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration) var(--ease);
}
.switch-ctl[aria-checked="true"] .switch-track { background: var(--positive-strong); }
.switch-ctl[aria-checked="true"] .switch-knob { transform: translateX(14px); }
/* collapsible facet panel under the controls row */
[data-reviews-panel] .chev-down { transition: transform var(--duration) var(--ease); }
[data-reviews-panel][aria-expanded="true"] .chev-down { transform: rotate(180deg); }
.filter-panel {
  display: grid;
  gap: 20px;
  padding: 20px 22px;
  margin-bottom: 14px;
  background: var(--surface-well);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
}
/* compact facet columns; methods get their own two-up band below */
.filter-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 20px 28px;
}
.filter-methods {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px 28px;
  border-top: 1px solid var(--border-default);
  padding-top: 18px;
}
/* same band, one group across the full width (bonus hub: platform & products) */
.filter-wide {
  border-top: 1px solid var(--border-default);
  padding-top: 18px;
}
@media (max-width: 760px) {
  .filter-methods { grid-template-columns: minmax(0, 1fr); }
}
.filter-group { display: grid; gap: 8px; align-content: start; }
.filter-group-head {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  font-weight: var(--weight-semibold);
  color: var(--foreground-muted);
}
.filter-group .input { max-width: 280px; }
.filter-group .chip-row { gap: 6px; }
.filter-panel .chip { padding: 5px 11px; font-size: var(--text-xs); gap: 6px; }
.filter-panel .chip .mc-ico { width: 14px; height: 14px; }
.filter-panel-foot {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  border-top: 1px solid var(--border-default);
  padding-top: 12px;
}
/* Positional, not per-page: this was `[data-reviews-reset]`, so the bonus hub's own
   reset button fell back to centred-in-its-track and the two panels' feet disagreed.
   The foot is always [reset] [collapse] [spacer] — pin each cell, whoever it is.

   Pinned by COLUMN, not by justification alone: the reset ships `hidden` while
   nothing is filtering, which takes it out of the layout entirely, and auto
   placement then slid the collapse control up into the FIRST track — so the
   control that is meant to sit centred sat hard left on exactly the state a
   reader arrives in. nth-child counts DOM position, so it names the same three
   controls whether or not the reset is showing. minmax(0,1fr) rather than 1fr
   on the flanks so a long reset label ("Clear 4 filters") cannot push the
   middle track off centre by growing its own past its share. */
.filter-panel-foot > :nth-child(1) { grid-column: 1; justify-self: start; }
.filter-panel-foot > :nth-child(2) { grid-column: 2; }
.filter-panel-foot > :nth-child(3) { grid-column: 3; justify-self: end; }
/* Below ~560px the reset and the collapse control no longer fit one row beside
   each other, and a three-track foot can only hold them there by overlapping.
   Stack instead — centred, which is what the row is for. */
@media (max-width: 560px) {
  .filter-panel-foot {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }
  .filter-panel-foot > :nth-child(1),
  .filter-panel-foot > :nth-child(2),
  .filter-panel-foot > :nth-child(3) { grid-column: 1; justify-self: center; }
}
.reset-x {
  display: inline-flex;
  width: 12px;
  height: 12px;
  color: var(--negative-strong);
}
.reset-x svg { width: 100%; height: 100%; }
/* brand logo overlays the drawn kind icon once it loads (monogram pattern) */
.mc-ico { position: relative; }
.mc-ico .mc-logo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.mc-ico[data-logo="1"] svg { display: none; }
/* loaded logos size by HEIGHT: every mark renders exactly as tall as the slot
   (uniform height across chips), width flows per logo. The negative margin
   absorbs the extra height so pills never grow. */
.mc-ico[data-logo="1"],
.filter-panel .chip .mc-ico[data-logo="1"] {
  width: auto;
  height: 24px;
  margin: -5px 0;
}
.mc-ico[data-logo="1"] .mc-logo {
  position: static;
  width: auto;
  height: 100%;
  max-width: 64px;
  object-fit: contain;
}
/* In a chip the square brand tile gets the same look as the index tile: rounded
   corners and a thin line that keeps a white logo visible (owner, 2026-10-03).
   The files stay plain squares; the rounding is this theme's choice. */
.mc-ico[data-logo="1"] { border-radius: 7px; overflow: hidden; }
.mc-ico[data-logo="1"]::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--foreground-default) 12%, transparent);
  pointer-events: none;
}
.table-compact thead th {
  padding: 10px 10px;
  /* long registry labels ("Spot fees (maker/taker)") may wrap — cells stay tight */
  white-space: normal;
}
/* availability header: the flag IS the picker (big flag + chevron, no name).
   width:max-content — the table cell otherwise squeezes the button below its
   content and the chevron pokes out of the pill */
.th-country .combo-btn {
  width: max-content;
  padding: 3px 7px;
  gap: 6px;
  background: transparent;
  border-color: transparent;
}
.combo-btn .flag-img { flex: none; }
.th-country .chev-down { width: 12px; height: 8px; }
.th-country .chev-down path { stroke-width: 2.2; }
.th-country .combo-btn:hover { border-color: var(--border-strong); }
.combo-fixed .combo-pop {
  position: fixed;
  right: auto;
  text-transform: none;
  letter-spacing: normal;
}
/* a value that must never break mid-pair ("0.10% / 0.20%") */
.cell-nowrap { white-space: nowrap; }
.table-compact tbody td { padding: 9px 10px; white-space: nowrap; }
.table-compact td:first-child a { white-space: nowrap; }
/* Reviews hub: the column titles stay in view down the whole list (owner 2026-09-30).
   A wrap that scrolls sideways is the scroll container for sticky cells, so:
   - a table that fits (archive.js stamps [data-fits]) clips instead of scrolling,
     and the header row sticks under the site header;
   - one that must scroll sideways (tablets, phones, no JS) becomes a box one screen
     high, and the header row sticks to the top of the box.
   A collapsed border stays behind when its cell sticks, so the rule under the
   header row is an inset shadow. */
#reviews-tablewrap {
  max-height: calc(100vh - var(--header-h-desktop) - 24px);
  max-height: calc(100dvh - var(--header-h-desktop) - 24px);
}
#reviews-tablewrap thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  border-bottom: 0;
  box-shadow: inset 0 -1px 0 var(--border-default);
}
#reviews-tablewrap[data-fits] { max-height: none; overflow: clip; }
#reviews-tablewrap[data-fits] thead th { top: var(--header-h-desktop); }
@media (max-width: 900px) {
  #reviews-tablewrap {
    max-height: calc(100vh - var(--header-h) - 24px);
    max-height: calc(100dvh - var(--header-h) - 24px);
  }
  #reviews-tablewrap[data-fits] thead th { top: var(--header-h); }
}
.row-actions {
  display: inline-flex;
  gap: 8px;
  justify-content: flex-end;
}
.row-actions .btn { white-space: nowrap; }

/* instant map tooltip */
.world-map { position: relative; }
.map-tip {
  position: absolute; z-index: 20; pointer-events: none;
  display: flex; align-items: center; gap: 8px;
  padding: 7px 11px; border-radius: var(--radius-md);
  background: var(--surface-dark); color: var(--foreground-on-dark);
  font-size: var(--text-sm); box-shadow: var(--shadow-menu); white-space: nowrap;
}
.map-tip span { font-weight: 600; }
.map-tip span[data-ok="true"] { color: var(--positive); }
.map-tip span[data-ok="false"] { color: var(--negative); }

.toc-visit[data-show="false"] { display: none !important; }
/* Same switch, one level out. On a page whose follow CTA names a venue chosen
   per visitor (the alternatives family), the button cannot carry the id the
   observer looks up — three candidates would be three of one id — so the SLOT
   does, and the switch travels with it. Block, not inline-flex: the slot holds
   a full-width button, not the button itself. */
.toc-visit-slot { display: none; }
.toc-visit-slot[data-show="true"] { display: block; }
@media (max-width: 1024px) {
  .toc-visit-slot[data-show="true"] { display: none; }
}

/* review-card v2: logo + name in one row, labeled stats row incl. score */
.review-card .head h3 { flex: 1; min-width: 0; }
.review-card .meta { flex-wrap: wrap; row-gap: 10px; }
.review-card .meta > div { min-width: 0; }
.review-card .meta .score-pill { display: inline-flex; margin-top: 3px; font-size: 13px; padding: calc(3px + var(--pill-nudge)) 9px calc(3px - var(--pill-nudge)); }

/* verdict icon — replaces the duplicate flag next to the country dropdown */
/* The disc is drawn inside the svg, not painted as a CSS background — that is what
   guarantees the mark is centred on it. `color` is the disc, `--chip-ink` is the mark. */
.cv-ico {
  flex: none;
  width: 26px; height: 26px;
  display: block;
  color: var(--positive-strong);
  --chip-ink: var(--on-accent);
}
.cv-ico[data-ok="false"] { color: var(--negative-strong); }
.chip-ico { width: 100%; height: 100%; display: block; }
/* per-exchange verdict lines in a banner description (vs page) */
.ip-desc .ip-line {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 3px;
}
.ip-line .cv-ico { width: 15px; height: 15px; }



/* region pills — visually distinct from country pills */
.method-chip[data-kind="region"] {
  border-color: var(--brand-strong); background: var(--brand-soft);
}

/* searchable coin list */
.coins-box { margin: 16px 0; }
/* market labels shown while a search spans both lists */
.coin-list-head {
  margin: 12px 0 2px;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--foreground-muted);
}
.coin-list {
  display: flex; flex-wrap: wrap; gap: 8px;
  max-height: 320px; overflow-y: auto;
  padding: 14px; border: 1px solid var(--border-default);
  border-radius: var(--radius-lg); background: var(--surface-well);
}
.coin-chip {
  font-family: var(--font-mono); font-size: var(--text-xs); font-weight: 600;
  padding: 5px 10px; border-radius: var(--radius-pill);
  background: var(--surface-raised); border: 1px solid var(--border-default);
  color: var(--foreground-default); cursor: default;
}

/* coins box logo pills */
.coin-chip { display: inline-flex; align-items: center; gap: 6px; }
.coin-chip img { width: 16px; height: 16px; border-radius: 50%; flex: none; display: block; }
.coin-chip--named { max-width: 100%; font-family: var(--font-sans); }
.coin-chip-name { min-width: 0; overflow-wrap: anywhere; }
.coin-chip-symbol { flex: none; font-family: var(--font-mono); }
.coin-chip--named .coin-chip-symbol { color: var(--foreground-muted); font-size: 0.9em; }
.coin-glyph {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 8px; font-weight: 700; letter-spacing: 0;
  color: #fff; background: hsl(var(--g, 200) 55% 42%);
}

/* search empty-state: collapse the list, message takes the card's place */
.coin-list.is-empty { display: none; }
[data-coins-empty]:not([hidden]) {
  display: block; margin: 0; padding: 14px;
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-lg); background: var(--surface-well);
}

/* alternatives action buttons */
.alt-actions { display: grid; gap: 6px; justify-items: start; }
.alt-actions .btn { white-space: nowrap; }
/* current exchange's column has no Review link — invisible stand-in keeps its
   Visit button on the same row as the other columns' */
.alt-actions .alt-ghost { visibility: hidden; }

/* ported from the coinsider template: wide tools mega menu */
/* A 640px panel anchored to its own nav item (left:-20px) ran past the viewport
   right edge across the whole 901–1126px band (~104px clipped at 1000px). Anchor
   it to the header container instead — right-aligned to the content edge — and
   cap it to the viewport so it can never clip. The nav item goes static so the
   container becomes the panel's containing block; top: calc(100% - 6px) resolves
   to the same header-bottom position either way. */
.nav-item:has(> .mega-wide) { position: static; }
.mega-wide {
  width: min(640px, calc(100vw - 40px));
  left: auto;
  right: 20px;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0 16px;
  align-items: start;
}
.mega-wide .mega-title { margin-bottom: 4px; }
.mega-wide a { padding: 7px 10px; }
.mega-wide .mega-col { padding: 0 8px 10px 0; }
.mega-wide > .mega-all {
  grid-column: 1 / -1;
  margin-top: 6px;
  padding-top: 10px;
}


/* --------------------------------------------------------------------------
   24. SINGLE POST, AUTHOR & SEARCH
   -------------------------------------------------------------------------- */

/* --- single post ------------------------------------------------------- */
.post-title { margin-top: 10px; }

/* Screenshots come from the live CMS at whatever size they were uploaded. The well
   holds the row height before the image decodes, so a 20-figure step-by-step guide
   does not reflow twenty times on the way down. */
.article-figure {
  margin-block: 1.6em;
}
.article-figure img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--surface-well);
}
.article-figure figcaption {
  margin-top: 8px;
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  line-height: var(--leading-body);
}

/* Editorial step-by-step tutorials. The explicit shortcode owns placement;
   native details/summary keeps the complete guide server-rendered and usable
   without JavaScript. */
.cwr-guide-accordion {
  overflow: hidden;
  max-width: 760px;
  margin: 12px 0;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}
/* wpautop can leave a standalone break between explicit tutorial shortcodes.
   Hide only that separator so adjacent guides keep their shared margin rhythm. */
.cwr-guide-accordion + br:has(+ .cwr-guide-accordion) { display: none; }
.prose p:empty:has(+ .cwr-guide-accordion),
.prose p:has(> br:only-child):has(+ .cwr-guide-accordion) { display: none; }
.cwr-guide-summary {
  display: flex;
  min-height: 58px;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 12px 18px;
  background: var(--brand-tint-faint);
  color: var(--foreground-default);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-bold);
  line-height: var(--text-xl);
  list-style: none;
  user-select: none;
}
.cwr-guide-summary-method {
  text-decoration-line: underline;
  text-decoration-color: var(--brand);
  text-decoration-thickness: 3px;
  text-underline-offset: 0.2em;
}
.cwr-guide-summary::-webkit-details-marker { display: none; }
.cwr-guide-summary:hover,
.cwr-guide-summary:focus-visible { background: var(--brand-tint); }
.cwr-guide-summary:focus-visible {
  outline: 3px solid var(--brand-soft);
  outline-offset: -3px;
}
.cwr-guide-summary-toggle {
  display: block;
  flex: 0 0 30px;
  width: 30px;
  height: 30px;
}
.cwr-guide-summary-ring {
  fill: var(--surface-raised);
  stroke: var(--border-default);
  stroke-width: 1;
}
.cwr-guide-summary-plus,
.cwr-guide-summary-minus {
  fill: var(--brand);
}
.cwr-guide-summary-minus { display: none; }
.cwr-guide-accordion[open] .cwr-guide-summary { border-bottom: 1px solid var(--border-default); }
.cwr-guide-accordion[open] .cwr-guide-summary-plus { display: none; }
.cwr-guide-accordion[open] .cwr-guide-summary-minus { display: block; }
.cwr-guide-body { padding: 20px 18px 22px; }
.prose .cwr-guide-body > h3 {
  max-width: 724px;
  margin: 0 0 16px;
  color: var(--foreground-default);
  font-size: 23px;
  line-height: 30px;
}
.prose .cwr-guide-body > h3:not(:first-child) { margin-top: 32px; }
.prose .cwr-guide-body > h4 {
  max-width: 724px;
  margin: 28px 0 0;
  color: var(--foreground-default);
  font-size: 19px;
  line-height: 26px;
}
.prose .cwr-guide-body > p { max-width: 724px; }
.cwr-guide-body figure {
  max-width: 724px;
  margin-right: 0;
  margin-left: 0;
}
.cwr-guide-body img {
  width: auto;
  max-width: 100%;
  height: auto;
  border-radius: var(--radius);
}

@media (max-width: 720px) {
  .cwr-guide-summary {
    min-height: 54px;
    padding: 11px 14px;
  }
  .cwr-guide-body { padding: 17px 14px 20px; }
}

.tag-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 2em;
  padding-top: 1.4em;
  border-top: 1px solid var(--border-default);
}
/* a chip that is a link, not a filter: no aria-pressed state to represent */
.chip-static { cursor: pointer; }

/* --- author box (end of post) + author page ---------------------------- */
.author-box {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 20px;
  align-items: start;
  margin-top: 40px;
}
.author-box .ab-name {
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  margin-top: 4px;
}
.author-box .ab-name a { transition: color var(--duration) var(--ease); }
.author-box .ab-name a:hover { color: var(--brand-strong); }
.author-box .ab-role {
  font-size: var(--text-sm);
  color: var(--foreground-muted);
  margin-top: 2px;
}
.author-box .ab-bio {
  margin-top: 10px;
  max-width: 68ch;
  line-height: var(--leading-relaxed);
  color: var(--foreground-light);
}
.author-box .ab-socials {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}

/* the sidebar exchange card on a post — .card already owns the shell */
.author-card .btn-block + .btn-block { margin-top: 8px; }

/* --- header search panel ------------------------------------------------ */
.search-pop {
  position: sticky;
  /* rides directly under the sticky header, and below it in the stack (header 100,
     mobile-nav 99) so scrolling never slides the panel over the chrome that owns it */
  top: var(--header-h-desktop);
  z-index: 98;
  background: var(--background-default);
  border-bottom: 1px solid var(--border-default);
  box-shadow: var(--shadow-menu);
  padding-block: 14px;
}
/* Search field + its buttons. The field keeps the plain .search-bar shell (icon absolutely
   positioned inside it); the buttons are siblings of that shell, never children of it. */
.search-form {
  display: flex;
  align-items: center;
  gap: 10px;
}
.search-form .search-bar {
  flex: 1;
  min-width: 0;
}
.search-form .search-bar .input { width: 100%; }
.search-form > .btn { flex: none; }
/* the close button is a real control, so its glyph must not inherit the absolute,
   pointer-events:none treatment the .search-bar magnifier gets */
.search-form > .icon-btn { flex: none; }

.search-suggest {
  display: grid;
  gap: 2px;
  margin-top: 10px;
}
.search-suggest:empty { display: none; }
.search-suggest a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 14px;
  border-radius: var(--radius);
  transition: background var(--duration) var(--ease);
}
.search-suggest a:hover { background: var(--surface-well); }
.search-suggest .sg-title { font-weight: var(--weight-medium); }
.search-suggest .sg-kind {
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  white-space: nowrap;
  flex: none;
}
.search-suggest .sg-empty {
  padding: 10px 14px;
  font-size: var(--text-sm);
  color: var(--foreground-muted);
}

/* --- search results page ------------------------------------------------ */
.search-page-form {
  margin-top: 20px;
  max-width: 640px;
}

.search-group + .search-group { margin-top: 36px; }
.search-group-head {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--text-lg);
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border-default);
}
.search-group-head .count {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--foreground-muted);
  background: var(--surface-well);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  padding: 2px 9px;
}
/* `sr-list`, never `search-results`: WordPress core stamps `search-results` on
   the BODY of every search that found something, so a component class of that
   name matches the entire document — `.search-results a { display: grid }` put
   the logo mark above the wordmark and every nav chevron under its label, on
   search pages with results only. No component class may take a core body-class
   name (`search`, `search-results`, `search-no-results`, `error404`, `home`,
   `single`, `page`, `archive`, …). */
.sr-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  list-style: none;
  padding: 0;
}
.sr-list li + li { border-top: 1px solid var(--border-default); }
.sr-list a {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 3px;
  padding: 14px 12px;
  border-radius: var(--radius);
  transition: background var(--duration) var(--ease);
}
.sr-list a:hover { background: var(--surface-well); }
.sr-list .sr-title {
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
}
.sr-list .sr-sub {
  font-size: var(--text-sm);
  color: var(--foreground-muted);
  line-height: var(--leading-body);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
mark {
  background: var(--brand-tint);
  color: inherit;
  border-radius: 3px;
  padding: 0 2px;
}

/* the article card's byline is a link now; it must not inherit the card's h3 hover.
   It sits ABOVE the title link, which is stretched over the whole card
   (.article-card h3 a::after): without that, a click on the author's name
   opened the article and the author page could not be reached from a card. */
.article-card .meta-author {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  position: relative;
  z-index: 1;
  transition: color var(--duration) var(--ease);
}
.article-card .meta-author:hover { color: var(--brand-strong); }

@media (max-width: 640px) {
  .author-box {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
  }
  /* field on its own row, buttons under it — safe now that the magnifier is positioned
     against .search-bar rather than against the wrapping form */
  .search-form { flex-wrap: wrap; }
  .search-form .search-bar { flex: 1 1 100%; }
  .search-suggest a { flex-direction: column; gap: 2px; }
  .search-suggest a .sg-kind { align-self: flex-start; }
}

/* Structured security incidents (CEM_Hacks entries). One entry keeps the single
   paragraph it always had; several render as a dated list, newest first. */
/* `.prose ul` is (0,1,1) and sets padding-left: 24px, so a bare `.incident-list`
   (0,1,0) lost and the dates sat indented under the heading. Scoped so it wins
   outright rather than relying on being later in the file. */
/* multi-entry callout: the list is its own grid row spanning both callout columns,
   so the dates begin at the callout's padding edge rather than after the icon */
.callout-incidents .incident-list,
.prose .callout-incidents .incident-list {
  grid-column: 1 / -1;
  margin-top: 0;
}
.incident-list,
.prose .incident-list {
  display: grid;
  /* the date column lives on the LIST so every row shares one width — with the grid on
     each <li> the columns were sized per row and "Oct 2022" vs "May 2019" staggered the
     text by 2px */
  grid-template-columns: auto minmax(0, 1fr);
  gap: 10px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
}
.incident-list > li {
  display: grid;
  /* the fallback first: without subgrid each row sizes its own columns, which is exactly
     the old behaviour rather than a broken one */
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  gap: 4px 10px;
  align-items: baseline;
}
.incident-date {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--negative-tint-faint);
  border: 1px solid var(--negative-border);
  color: var(--negative-strong);
}
/* a lone entry keeps its date inline with the sentence rather than as a grid cell */
.callout-warn > div > .incident-date { margin-right: 6px; }
@media (max-width: 520px) {
  /* one column: the date stacks over its text, so the list drops the shared track too */
  .incident-list,
  .prose .incident-list { grid-template-columns: minmax(0, 1fr); }
  .incident-list > li { grid-template-columns: minmax(0, 1fr); }
  .incident-date { justify-self: start; }
}

/* --------------------------------------------------------------------------
   URL MAP: section indexes, planned pages, sitemap

   These render the address space itself. The production path is shown on every
   row on purpose — the point of these pages is to see where a link goes, so the
   path is content, not debug output.
   -------------------------------------------------------------------------- */

.index-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
}
.index-toolbar .search-bar { flex: 1 1 240px; }

.index-list {
  /* Explicit single column: an implicit track is max-content, which lets a long
     path push the row wider than the viewport on a phone. */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  margin-top: 20px;
  list-style: none;
  padding: 0;
}
@media (min-width: 720px) {
  .index-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.index-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 4px 12px;
  min-width: 0;
  padding: 11px 14px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  background: var(--surface-raised);
  transition: border-color var(--duration) var(--ease), color var(--duration) var(--ease);
}
.index-item:hover { border-color: var(--brand); }
.index-item:hover .index-name { color: var(--brand-strong); }

.index-name {
  grid-column: 1;
  font-weight: var(--weight-semibold);
  color: var(--foreground-default);
  transition: color var(--duration) var(--ease);
  overflow-wrap: anywhere;
}
.index-path {
  grid-column: 1;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  overflow-wrap: anywhere;
}
.index-item .badge { grid-column: 2; grid-row: 1 / span 2; }

/* A row can LEAD with a mark where its namespace has an iconography (payment
   methods do; a list of comparison addresses does not). The grid grows its
   column only when one actually arrived, so every other index is untouched. */
.index-item.has-ico { grid-template-columns: auto minmax(0, 1fr) auto; }
.index-item.has-ico .index-ico { grid-column: 1; grid-row: 1 / span 2; }
.index-item.has-ico .index-name,
.index-item.has-ico .index-path { grid-column: 2; }
.index-item.has-ico .badge { grid-column: 3; }
/* The tile only sizes and frames THE method icon — the glyph's colour still
   comes from the [data-kind] chip rules, which key off an ancestor attribute. */
.index-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex: none;
  border: 1px solid var(--border-default);
  border-radius: var(--radius);
  background: var(--surface-well);
}
.index-ico .mc-ico { width: 22px; height: 22px; }
/* A loaded brand mark fills the whole tile, edge to edge: the square file is
   clipped by the tile's own radius, so no tile colour shows around it and the
   mark itself looks rounded (owner, 2026-10-03). The files stay plain squares,
   so another theme can frame them its own way. A glyph fallback keeps the
   padded tile. */
.index-ico { position: relative; overflow: hidden; }
.index-ico:has(.mc-ico[data-logo="1"]) { border: 0; background: none; }
.index-ico .mc-ico[data-logo="1"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  border-radius: 0;
}
.index-ico .mc-ico[data-logo="1"]::after { content: none; }
.index-ico .mc-ico[data-logo="1"] .mc-logo {
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}
/* A hairline keeps a white mark's tile visible on a white card. */
.index-ico:has(.mc-ico[data-logo="1"])::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--foreground-default) 12%, transparent);
  pointer-events: none;
}
/* Buy-with hub cards: THE method mark beside the method's name and how many
   exchanges accept it. The mark is the index tile at a larger size. */
.pay-card { display: flex; align-items: center; gap: 12px; padding: 14px 16px; }
.pay-card .index-ico { width: 40px; height: 40px; }
.pay-card-text { min-width: 0; }
.pay-card-name { display: block; color: var(--foreground-default); overflow-wrap: anywhere; }
.pay-card-lg { gap: 16px; padding: 24px; }
.pay-card-lg .index-ico { width: 52px; height: 52px; }
.pay-card-lg .pay-card-name { font-size: var(--text-lg); }
/* A hub's searchable card list: clear of the search box above it and of the risk note below. */
.hub-list { margin: 16px 0 24px; }
/* An article hub prints every card and hides all but the first twelve; the search
   box, the tags and "Show more" bring the rest out, and all three are scripts.
   Where no script runs (no html[data-js]) every card shows, and the three
   controls, which could do nothing there, are not offered. Only a list with a
   batch limit is opened: the section index hides cards on the server's own
   answer to ?q= and ?sub=, and that must stand. */
html:not([data-js]) [data-index-limit] > [data-index-item][hidden] { display: flex !important; }
html:not([data-js]) [data-index-limit] ~ [data-index-more],
html:not([data-js]) .chip-row:has(> button[data-index-chip]),
html:not([data-js]) .search-bar:has(#index-q):not(form .search-bar) { display: none; }
.pay-grid { gap: 12px; }
.index-item[data-state="planned"] .index-ico { background: var(--background-default); }

/* A planned address is real but has no page in this build yet. Muted, never broken. */
.index-item[data-state="planned"] { background: var(--surface-well); }
.index-item[data-state="planned"] .index-name { color: var(--foreground-light); }

/* ---- planned / removed page ---- */

.planned-note {
  margin-top: 20px;
  padding: 18px 20px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  background: var(--surface-well);
}
.planned-note dl {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  margin: 14px 0 0;
}
@media (min-width: 620px) {
  .planned-note dl { grid-template-columns: max-content minmax(0, 1fr); gap: 8px 18px; }
}
.planned-note dt {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--foreground-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.planned-note dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

/* ---- sitemap ---- */

.sitemap-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  margin-top: 24px;
}
@media (min-width: 720px) {
  .sitemap-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1000px) {
  .sitemap-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.sitemap-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding: 18px 20px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  transition: border-color var(--duration) var(--ease);
}
.sitemap-card:hover { border-color: var(--brand); }
.sitemap-card h3 { margin: 0; font-size: var(--text-lg); }
.sitemap-card h3 a { color: var(--foreground-default); }
.sitemap-card:hover h3 a { color: var(--brand-strong); }
.sitemap-card .path {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--foreground-muted);
  overflow-wrap: anywhere;
}
.sitemap-card p { margin: 4px 0 0; font-size: var(--text-sm); color: var(--foreground-light); }
.sitemap-card .counts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 10px;
}

/* The ranking data spine runs the container's full width — the glance table is
   878px of columns and the two-column article layout gives the prose 724px, so
   leaving the table in that column forced horizontal scrolling the design does
   not have. Prose keeps the layout; the spine escapes it. */
.ranking-spine { margin-bottom: 24px; }

/* Alternatives spine toolbar (0.14.0): the hub's chips and facet panel mounted
   over the full-width card list. A flex row that wraps — chips lead, the sort
   control rides the right edge — with the count line and the panel each taking
   a full row beneath. */
/* The alternatives spine opens with a separator (0.14.1, owner report: the
   pills sat too close to the intro's last sentence): a rule above the toolbar
   and clear air on both sides of it. On the spine, not the toolbar — the
   toolbar ships [hidden] without JS and the breathing room must not leave
   with it. */
.alts-spine { margin-top: 14px; border-top: 1px solid var(--border-default); padding-top: 26px; }
.picks-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-bottom: 14px; }
.picks-toolbar .chip-row { margin: 0; }
.picks-toolbar .sort-label { margin-left: auto; gap: 8px; }
.picks-toolbar .picks-count { flex-basis: 100%; margin: 0; }
.picks-toolbar .filter-panel { flex-basis: 100%; }
.picks-more-row { display: flex; justify-content: center; margin: 18px 0 4px; }


/* --------------------------------------------------------------------------
   EXCHANGE SCREENSHOT GALLERY (0.18.0, ratified spec 2026-08-09)
   Strip + device tabs (server-rendered, inc/components/gallery.php) and the
   fullscreen lightbox (assets/js/gallery.js). The strip scrolls inside its
   own container -- the page body never scrolls horizontally (qa rule).
   -------------------------------------------------------------------------- */
.ex-gallery { margin: 22px 0; }
.ex-gallery-tabs { display: flex; gap: 8px; margin-bottom: 10px; }
.ex-gallery-tab {
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--foreground-light);
  background: none;
  border: 1px solid var(--border-default);
  border-radius: 999px;
  padding: 5px 14px;
  cursor: pointer;
}
.ex-gallery-tab[aria-selected="true"] {
  color: var(--on-accent);
  background: var(--brand);
  border-color: var(--brand);
}
.ex-gallery-strip {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding-bottom: 6px;
  scrollbar-width: thin;
}
.gal-thumb {
  flex: 0 0 auto;
  width: 148px;
  padding: 0;
  background: var(--surface-raised);
  border: 1px solid var(--border-default);
  border-radius: 8px;
  overflow: hidden;
  cursor: zoom-in;
  text-align: center;
}
.gal-thumb:hover { border-color: var(--brand); }
.gal-thumb img {
  width: 100%;
  height: 92px;
  object-fit: cover;
  object-position: top;
  display: block;
}
.gal-thumb-cap {
  display: block;
  font-size: var(--text-2xs);
  color: var(--foreground-muted);
  padding: 4px 6px 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gal-inline { cursor: zoom-in; }

/* Lightbox: full-viewport dialog above the detail pop (z 90). */
.ex-lightbox {
  position: fixed;
  inset: 0;
  z-index: 95;
  background: color-mix(in srgb, var(--surface-dark) 94%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.exl-fig {
  margin: 0;
  max-width: min(1200px, 92vw);
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.exl-img {
  max-width: 100%;
  max-height: calc(92vh - 44px);
  object-fit: contain;
  border-radius: 6px;
}
.exl-cap {
  color: var(--foreground-on-dark);
  font-size: var(--text-sm);
  text-align: center;
}
.exl-close, .exl-prev, .exl-next {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--foreground-on-dark) 30%, transparent);
  background: color-mix(in srgb, var(--surface-dark-raised) 72%, transparent);
  color: var(--foreground-on-dark);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}
.exl-close:hover, .exl-prev:hover, .exl-next:hover { border-color: var(--brand); color: var(--brand); }
.exl-close { top: 14px; right: 14px; }
.exl-prev { left: 10px; top: 50%; transform: translateY(-50%); }
.exl-next { right: 10px; top: 50%; transform: translateY(-50%); }
@media (max-width: 480px) {
  .exl-prev { left: 4px; }
  .exl-next { right: 4px; }
  .gal-thumb { width: 124px; }
}

/* --------------------------------------------------------------------------
   INTERNATIONAL TEXT HARDENING (0.24.0)
   Every width in this file was tuned against ENGLISH strings. Translated
   pages stamp html[lang] per language (the translation plugin's renderer),
   so these rules key off that attribute — an English page can not change by
   construction, and a language needs no rules of its own here.
   -------------------------------------------------------------------------- */

/* German-class expansion (+20-35%) meets narrow columns: hyphenate flowing
   text wherever the browser has dictionaries for the page language.
   `hyphens` inherits, so headings inside prose are covered. UI labels are
   deliberately NOT hyphenated — a hyphenated button reads broken; labels
   rely on the long-token safety rules above instead. */
html:not([lang^="en"]) .prose,
html:not([lang^="en"]) .ranking-content,
html:not([lang^="en"]) .article-card .excerpt,
html:not([lang^="en"]) .review-card .blurb {
  hyphens: auto;
}

/* Tables are the exception to the rule above: a table cell is as narrow as
   its longest piece, so with hyphenation on, the browser cut the label
   column of a five-column comparison down to a few letters per line
   ("Gegrün-det", "Maker-Gebühr im Spot-Handel" on four lines; owner
   2026-10-08). Cells break between words only. */
html:not([lang^="en"]) :is(.prose, .ranking-content) :is(th, td) {
  hyphens: manual;
}

/* The alternatives matrix (one label column, one column per exchange): the
   value columns are as wide as their nowrap "Visit" and "Review" buttons, and
   when those take the whole measure the label column was left with its
   minimum. It keeps 12em (two lines for the longest Russian label) and the
   value cells give up 4px of side padding each. The table scrolls inside
   .table-wrap when the buttons still do not fit (German "Erfahrungsbericht"). */
html:not([lang^="en"]) .alt-table td:first-child { min-width: 12em; }
html:not([lang^="en"]) .alt-table :is(th, td):not(:first-child) { padding-inline: 12px; }

/* The main nav hands over to the hamburger at 900px — a width chosen for
   the English labels. Translated labels run up to a third wider. Measured on the
   site's own menu in ten languages (2026-10-07): with the hand-over at 1020px the
   Russian, Spanish and German bars were 23 to 52px wider than the page column, and
   below about 1140px the language button on the right was cut off. So non-English
   pages hand over at 1100px, 200px earlier than English. */
@media (max-width: 1100px) {
  html:not([lang^="en"]) .main-nav { display: none; }
  html:not([lang^="en"]) .hamburger { display: inline-flex; }
}

/* Above that the translated menu sits a little closer together, so the longest
   one (German) fits the column with room to spare: the two gaps beside the menu
   are 20px instead of 26px (that also pays for the larger logo mark), and each
   link has 8px of side padding instead of 13px. English is untouched. */
@media (min-width: 1101px) {
  html:not([lang^="en"]) .site-header .container { gap: 20px; }
  html:not([lang^="en"]) .nav-link { padding: 0 8px; }
  html:not([lang^="en"]) .nav-link::after { left: 8px; right: 8px; }
}

/* First live case of the predicted button overflow (owner 2026-08-12):
   "Mercado Bitcoin besuchen" is wider than the rank card's CTA column and
   .btn is white-space: nowrap, so the pill burst the card border. Full-width
   CTAs (.btn-block) sit in a defined column, so letting them WRAP is
   well-behaved: a two-line button beats one outgrowing its card, for every
   long brand × language combination. English keeps nowrap geometry. */
html:not([lang^="en"]) .btn-block {
  white-space: normal;
  overflow-wrap: break-word;
  /* flex centers the text BLOCK, not the lines inside it — a wrapped label
     reads left-ragged without this */
  text-align: center;
}

/* Rankings navigation: one panel body in the desktop disclosure and mobile drawer. */
.nav-item:has(> .mega-rankings) { position: static; }
.mega-rankings {
  width: min(660px, calc(100vw - 40px));
  min-width: 0;
  left: max(20px, calc(50% - 330px));
  right: auto;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: stretch;
  gap: 28px;
  max-height: calc(100dvh - var(--header-h) - 20px);
  overflow-y: auto;
}
.mega-rankings .mega-title, .rankings-panel .mega-title { margin-bottom: 14px; }
.mega-rankings > .mega-col, .rankings-panel > .mega-col { display: flex; flex-direction: column; }
.mega-rankings .mega-all, .rankings-panel .mega-all { margin-top: auto; }
.mega-rankings .mega-col > a:has(+ .mega-all), .rankings-panel .mega-col > a:has(+ .mega-all), [data-ranking-popular] { margin-bottom: 12px; }
.mega-rankings .ranking-country, .rankings-panel .ranking-country {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 42px;
}
.ranking-country .flag-img { flex: 0 0 24px; }
.ranking-country-copy { min-width: 0; }
.ranking-personal[hidden], [data-ranking-popular] [hidden] { display: none !important; }
.mobile-rankings { border-bottom: 1px solid var(--border-default); }
.mobile-rankings > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 13px 4px;
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  cursor: pointer;
  list-style: none;
}
.mobile-rankings > summary::-webkit-details-marker { display: none; }
.mobile-rankings[open] > summary .chev-down { transform: rotate(180deg); }
.rankings-panel { display: grid; gap: 24px; padding: 12px 4px 20px; }
.rankings-panel a { font-size: var(--text-sm); font-weight: var(--weight-medium); padding: 10px 0; border: 0; }
.rankings-panel .mega-all { color: var(--brand-strong); }
@media (min-width: 600px) { .rankings-panel { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* The shared author avatar uses a local editorial photo when one is published. */
.avatar .author-photo{display:block;width:100%;height:100%;object-fit:cover;border-radius:inherit}.author-box .avatar-lg{width:112px;height:112px;flex:0 0 112px}.ab-bio{white-space:pre-line}.ab-socials{flex-wrap:wrap}@media(max-width:600px){.author-box .avatar-lg{width:80px;height:80px;flex-basis:80px}}

/* At narrow phone widths keep every header control reachable. The home link
   retains its explicit accessible name when the wordmark becomes mark-only. */
@media (max-width: 360px) {
  .site-header .logo > span { display: none; }
}

/* Ranking feature comparison: horizontal scrolling stays inside the table. */
.table-features thead th:not(:first-child),
.table-features .feature-cell { text-align: center; }
.feature-status { display: inline-flex; align-items: center; justify-content: center; }
.feature-status[data-state="yes"] { color: var(--positive-strong); }
.feature-status[data-state="no"] { color: var(--negative-strong); }
.feature-status[data-state="unknown"] { color: var(--foreground-muted); }
.feature-status svg { width: 18px; height: 18px; }
.table-features tr > :first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface-raised);
  border-right: 1px solid var(--border-default);
}
.table-features thead th:first-child { z-index: 2; }

/* Explicit editorial components share the site card and table styles. */
.guide-links { margin-block: 28px; }
.guide-links-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 12px; margin-block: 16px; }
.guide-links-grid .arrow-card-link { margin: 0; text-decoration: none; }
.guide-links-title, .network-lookup-title { margin-block: 0 16px; }
.network-lookup-control { max-width: 420px; margin-block: 16px; }
.network-lookup-control .input { width: 100%; }
.network-lookup [hidden] { display: none !important; }
/* An address in the table is there to be pasted into a wallet; network-lookup.js copies it on a click. */
.network-lookup[data-copied-label] tbody code { cursor: copy; }

/* Setup guides (theme 0.82.6): a hero line, a row of choices with one panel each, a
   two-option comparison, notes and a source list. They were a stylesheet inside one
   article, and an article that carries its own <style> cannot be translated. */
.guide-hero { display: flex; align-items: center; gap: 20px; margin: 4px 0 8px; }
.guide-hero p { margin: 0; }
.guide-lede { font-size: var(--text-lg); color: var(--foreground-light); max-width: 60ch; }
.choice-picker { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-top: 18px; }
.choice-pick {
  display: block;
  border: 1.5px solid var(--border-default); background: var(--surface-well);
  border-radius: 12px; padding: 14px 10px; text-align: center; cursor: pointer; text-decoration: none;
  font-family: var(--font-sans); font-size: var(--text-sm); font-weight: var(--weight-semibold);
  color: var(--foreground-default); transition: border-color .15s, background .15s;
}
.choice-pick small { display: block; font-weight: var(--weight-normal); color: var(--foreground-muted); font-size: var(--text-2xs); margin-top: 3px; }
.choice-pick:hover { border-color: var(--brand); }
.choice-pick.active { border-color: var(--brand); background: var(--brand-tint); box-shadow: 0 0 0 1px var(--brand) inset; }
.choice-pick .monogram { display: flex; margin: 0 auto 10px; }
.choice-panel { border: 1px solid var(--border-default); border-radius: 14px; padding: 24px; margin-top: 14px; background: var(--surface-raised); }
/* Every panel but the first is authored [hidden]. Without review.js a choice still opens its panel. */
.choice-panel[hidden]:target { display: block !important; }
.choice-panel-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.prose .choice-panel-head h3 { margin: 0; }
.prose .choice-panel h4 { margin: 20px 0 6px; font-size: var(--text-base); }
.prose .choice-panel > :last-child { margin-bottom: 0; }
.prose .choice-store { margin: 4px 0 20px; }
.choice-store .btn { display: inline-flex; align-items: center; gap: 8px; max-width: 100%; white-space: normal; }
.choice-store .btn svg { flex: none; }
.guide-note {
  border-left: 3px solid var(--brand); background: var(--surface-well);
  padding: 10px 14px; border-radius: 0 8px 8px 0; font-size: var(--text-sm); color: var(--foreground-light);
  margin: 18px 0 0;
}
.guide-note.warn { border-color: var(--negative-strong); }
.option-pair { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: repeat(7, auto); column-gap: 16px; row-gap: 0; margin: 20px 0 8px; }
.option-card { display: grid; grid-row: span 7; grid-template-rows: subgrid; background: var(--surface-raised); border: 1px solid var(--border-default); border-radius: var(--radius-lg); padding: 22px 24px 8px; }
.option-card.is-pick { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-border); }
.option-card-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.option-card-kicker { font-size: var(--text-xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--foreground-light); }
.option-card-pick { display: inline-flex; align-items: center; gap: 5px; font-size: var(--text-2xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--brand-strong); }
.option-card-pick::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--brand); }
.prose .option-card h3 { margin: 0 0 6px; font-size: var(--text-lg); line-height: 1.3; color: var(--foreground-default); }
.prose .option-card-lede { margin: 0 0 14px; font-size: var(--text-sm); line-height: 1.55; color: var(--foreground-light); }
.option-facts { display: grid; grid-row: span 4; grid-template-rows: subgrid; margin: 0; }
.option-facts div { padding: 12px 0; border-top: 1px solid var(--border-default); }
.option-facts dt { margin-bottom: 3px; font-size: var(--text-2xs); font-weight: var(--weight-semibold); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--foreground-muted); }
.option-facts dd { margin: 0; font-size: var(--text-sm); line-height: 1.5; color: var(--foreground-default); }
@media (max-width: 720px) {
  .option-pair { grid-template-columns: 1fr; grid-template-rows: none; row-gap: 16px; }
  .option-card, .option-facts { grid-row: auto; grid-template-rows: none; }
}
.guide-refs { border-top: 1px solid var(--border-default); margin-top: 40px; padding-top: 18px; font-size: var(--text-sm); color: var(--foreground-muted); }
.guide-refs ul { padding-left: 20px; }

/* --------------------------------------------------------------------------
   Region-dependent values (theme 0.78.0): the dotted underline, the hover/
   tap card, the review overview's split regional rows and the EU-version
   pointer. assets/js/regional.js drives every rule below. The card and the
   pointer reuse --eu-blue / --eu-blue-ink (.eu-platform-card, above) rather
   than a second blue, so the underline, the card, the EU pointer and the EU
   flag badge all read as one system.
   -------------------------------------------------------------------------- */
.rv-on {
  cursor: help;
  text-decoration-line: underline;
  text-decoration-style: dotted;
  text-decoration-color: var(--eu-blue, #1d4ed8);
  text-decoration-thickness: 1.5px;
  text-underline-offset: 3px;
  border-radius: 2px;
}
.rv-on:hover { color: var(--eu-blue-ink, #1e3a8a); }
.rv-on:focus-visible { outline: 2px solid var(--eu-blue, #1d4ed8); outline-offset: 2px; }
[data-theme="dark"] .rv-on { text-decoration-color: #3b82f6; }
[data-theme="dark"] .rv-on:hover { color: #93c5fd; }
[data-theme="dark"] .rv-on:focus-visible { outline-color: #3b82f6; }

.rv-card {
  --eu-blue: #1d4ed8;
  --eu-blue-ink: #1e3a8a;
  --eu-tint: #eff6ff;
  --eu-border: rgba(29, 78, 216, 0.22);
  position: fixed;
  z-index: 95;
  width: min(320px, calc(100vw - 24px));
  padding: 14px 14px 12px;
  background: var(--surface-raised);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-menu);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  color: var(--foreground-default);
  text-align: left;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity var(--duration) var(--ease), transform var(--duration) var(--ease);
}
[data-theme="dark"] .rv-card {
  --eu-blue: #3b82f6;
  --eu-blue-ink: #93c5fd;
  --eu-tint: rgba(59, 130, 246, 0.12);
  --eu-border: rgba(96, 165, 250, 0.32);
}
.rv-card[data-side="top"] { transform: translateY(-4px); }
.rv-card.is-open { opacity: 1; transform: none; }
.rv-card[hidden] { display: none; }
.rv-title {
  margin: 0 0 8px;
  padding: 0 2px;
  font-weight: var(--weight-semibold);
  color: var(--foreground-default);
}
.rv-rows { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.rv-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
}
.rv-row.is-current { background: var(--eu-tint); box-shadow: inset 0 0 0 1px var(--eu-border); }
.rv-where { color: var(--foreground-light); }
.rv-row.is-current .rv-where { color: var(--foreground-default); font-weight: var(--weight-medium); }
.rv-here {
  display: block;
  margin-top: 1px;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  color: var(--eu-blue-ink);
  letter-spacing: 0.01em;
}
.rv-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-semibold);
  white-space: nowrap;
  color: var(--foreground-default);
}
.rv-foot {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
  padding: 10px 2px 0;
  border-top: 1px solid var(--border-default);
  font-size: var(--text-xs);
}
.rv-change { color: var(--eu-blue); font-weight: var(--weight-semibold); white-space: nowrap; text-decoration: none; }
.rv-change:hover { text-decoration: underline; text-underline-offset: 3px; }
.rv-change::after { content: " \2192"; }
[data-theme="dark"] .rv-change { color: var(--eu-blue-ink); }

/* Review overview: the visitor's (or the page country's) regional-fee row. */
tr.ov-regional.is-current td { background: var(--eu-tint, #eff6ff); }
tr.ov-regional.is-current td:first-child { box-shadow: inset 3px 0 0 var(--eu-blue, #1d4ed8); }
[data-theme="dark"] tr.ov-regional.is-current td { background: rgba(59, 130, 246, 0.12); }
[data-theme="dark"] tr.ov-regional.is-current td:first-child { box-shadow: inset 3px 0 0 #3b82f6; }
.rv-chip {
  display: inline-flex;
  align-items: center;
  margin-left: 6px;
  padding: 1px 8px;
  border: 1px solid var(--eu-border, rgba(29, 78, 216, 0.22));
  border-radius: var(--radius-pill);
  background: var(--surface-raised);
  color: var(--eu-blue-ink, #1e3a8a);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  line-height: 16px;
  vertical-align: 1px;
  white-space: nowrap;
}
[data-theme="dark"] .rv-chip { color: #93c5fd; }

/* Ranking card / rank-card row: EU-version pointer (OKX -> OKX EU). */
.eu-pointer {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--eu-border, rgba(29, 78, 216, 0.22));
  border-radius: var(--radius-pill);
  background: var(--eu-tint, #eff6ff);
  color: var(--eu-blue-ink, #1e3a8a);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  text-decoration: none;
  transition: border-color var(--duration) var(--ease);
}
.eu-pointer strong { font-weight: var(--weight-bold); }
.eu-pointer:hover { border-color: var(--eu-blue, #1d4ed8); }
.eu-pointer:focus-visible { outline: 2px solid var(--eu-blue, #1d4ed8); outline-offset: 2px; }
[data-theme="dark"] .eu-pointer {
  border-color: rgba(96, 165, 250, 0.32);
  background: rgba(59, 130, 246, 0.12);
  color: #93c5fd;
}
[data-theme="dark"] .eu-pointer:hover,
[data-theme="dark"] .eu-pointer:focus-visible { border-color: #3b82f6; outline-color: #3b82f6; }

/* EU flag disc on an EU platform's monogram tile (option D): a small round
   flag badge on the bottom-right corner, over the initials/logo the tile
   already carries — .monogram sets overflow:hidden for the logo plate, so
   .mg-eu opts back into overflow:visible to let the badge sit half outside
   the tile, the way the approved preview showed it. */
.monogram.mg-eu { overflow: visible; }
.monogram.mg-eu::after {
  content: "";
  position: absolute;
  z-index: 2;
  width: 20px;
  height: 20px;
  right: 1px;
  bottom: 1px;
  border-radius: 50%;
  background: url(../flags/eu.svg) center/cover no-repeat;
  box-shadow: 0 0 0 1.5px var(--surface-raised, #fff);
}
.monogram-sm.mg-eu::after { width: 16px; height: 16px; right: 0; bottom: 0; }
.monogram-lg.mg-eu::after { width: 24px; height: 24px; right: 2px; bottom: 2px; }

/* --------------------------------------------------------------------------
   HEADER: every width, every state (the 2026-10-07 responsiveness audit)
   The rules above set the bar for the widths they were written at. This
   block closes what the audit measured between and beyond them. It comes
   last on purpose: each rule here wins by order over the one it corrects.
   -------------------------------------------------------------------------- */

/* The height of the bar that is on screen. Panels that hang under the bar
   read this one value: the phone menu assumed the 54px phone bar and the
   search panel the 68px desktop bar, so each was 14px off wherever the other
   bar showed. --admin-h is the WordPress toolbar above the bar for staff. */
:root { --bar-h: var(--header-h-desktop); --admin-h: 0px; }
@media (max-width: 900px) { :root { --bar-h: var(--header-h); } }
/* (Written as an attribute match on the body: core's class names are not used
   as class selectors in this file, see theme-contract.php.) */
body[class~="admin-bar"] { --admin-h: 32px; }
@media (max-width: 782px) { body[class~="admin-bar"] { --admin-h: 46px; } }
/* At 600px and below the toolbar scrolls away with the page. */
@media (max-width: 600px) { body[class~="admin-bar"] { --admin-h: 0px; } }

.site-header { height: var(--bar-h); top: var(--admin-h); }
.mobile-nav {
  inset: calc(var(--bar-h) + var(--admin-h)) 0 auto 0;
  max-height: calc(100vh - var(--bar-h) - var(--admin-h));
  max-height: calc(100dvh - var(--bar-h) - var(--admin-h));
  overscroll-behavior: contain;
}
.search-pop { top: calc(var(--bar-h) + var(--admin-h)); }

/* The English menu is wider than the page column from 901 to 926px (a Surface
   Pro in portrait is 912px): it hands over to the menu button at 930px. */
@media (max-width: 930px) {
  .main-nav { display: none; }
  .hamburger { display: inline-flex; }
}
/* The menus are edited in WP admin, and a reader may enlarge text or spacing.
   Where a menu still does not fit, the script measures it and hands over to
   the menu button (enhance.js, fitNav). */
html[data-nav-collapsed] .main-nav { display: none; }
html[data-nav-collapsed] .hamburger { display: inline-flex; }
/* ...and when the phone bar itself is still too wide, the name gives way. */
html[data-bar-tight] .site-header .logo > span { display: none; }

/* A flyout taller than a short window scrolls inside itself (the Guides
   flyout is 604px in German; a 1366x768 laptop shows about 600px of page). */
.mega {
  max-height: calc(100vh - var(--bar-h) - var(--admin-h) - 12px);
  max-height: calc(100dvh - var(--bar-h) - var(--admin-h) - 12px);
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Phones from 361 to 388px: the name and the four buttons were up to 28px
   wider than the bar. The name is shown from 375px (the iPhone width) and a
   little smaller up to 400px; the buttons sit closer together. */
@media (max-width: 400px) {
  .site-header .logo { font-size: 20px; gap: 6px; }
  .header-actions { gap: 2px; }
}
@media (max-width: 374px) {
  .site-header .logo > span { display: none; }
}

/* The header's country and language box on a phone held sideways: the lists
   give way first, and the box itself scrolls if that is still not enough. */
#header-country .combo-pop {
  max-height: calc(100vh - var(--bar-h) - var(--admin-h) - 14px);
  max-height: calc(100dvh - var(--bar-h) - var(--admin-h) - 14px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
#header-country ul.combo-list {
  max-height: max(96px, min(250px, calc(100vh - var(--bar-h) - var(--admin-h) - 150px)));
  max-height: max(96px, min(250px, calc(100dvh - var(--bar-h) - var(--admin-h) - 150px)));
}

/* Touch: the buttons keep their size and gain a larger touch area around
   them (38px buttons and the 30x28px country button reach about 44px; the
   flyout arrows grow from 28 to 40px). Nothing moves. The offsets count from
   inside the button's border, hence 5px for 3px beyond the edge. */
.site-header .icon-btn,
.site-header .combo-compact .combo-btn,
.nav-panel-toggle { position: relative; }
.nav-panel-toggle { z-index: 1; }
@media (max-width: 930px), (pointer: coarse) {
  .site-header .icon-btn::before { content: ""; position: absolute; inset: -5px; }
  .site-header .combo-compact .combo-btn::before { content: ""; position: absolute; inset: -9px -7px; }
}
@media (pointer: coarse) {
  .nav-panel-toggle::before { content: ""; position: absolute; inset: 0 -8px 0 -4px; }
  /* iOS zooms the page when a field under 16px takes focus. */
  .input, .select { font-size: 16px; }
}

/* The page behind the open phone menu stays put. The gutter keeps the page
   from shifting sideways where the scrollbar takes room. */
html[data-menu-open="true"] { overflow: hidden; scrollbar-gutter: stable; }

/* Without JavaScript (no html[data-js]): the search and menu buttons are
   plain links (header.php prints them in <noscript>), the menu opens as the
   link's target, and the dark-mode button, which only a script can serve,
   is not offered. Where scripts run, <noscript> holds its markup as text
   and must stay hidden, so it is opened up only without them. */
html:not([data-js]) .header-actions noscript { display: contents; }
html:not([data-js]) #search-btn,
html:not([data-js]) #theme-btn,
html:not([data-js]) #menu-btn { display: none; }
.mobile-nav:target { display: block; }
.mobile-nav-close { color: var(--foreground-light); }
.nojs-languages { margin-top: 18px; font-size: var(--text-sm); }
.nojs-languages a { display: inline-block; margin: 0 14px 6px 0; text-decoration: underline; }

/* --------------------------------------------------------------------------
   COOKIE NOTICE (inc/analytics.php, assets/js/consent.js)
   One bar at the bottom of the window, served hidden and shown by the script
   to a reader who has not chosen yet. It sits above the page and below the
   header's panels; the two buttons weigh the same.
   -------------------------------------------------------------------------- */
.consent-bar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: 90;
  background: var(--surface-raised);
  color: var(--foreground-default);
  border-top: 1px solid var(--border-default);
  box-shadow: var(--shadow-menu);
  padding-block: 14px;
  padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
}
.consent-bar[hidden] { display: none; }
.consent-bar .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px 24px;
}
.consent-text {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-body);
  color: var(--foreground-light);
}
.consent-text a { color: var(--foreground-default); text-decoration: underline; white-space: nowrap; }
.consent-actions { display: flex; gap: 10px; flex: none; }
.consent-actions .btn { min-width: 96px; }
@media (max-width: 640px) {
  .consent-bar .container { flex-direction: column; align-items: stretch; }
  .consent-actions .btn { flex: 1 1 0; min-height: 44px; }
}
/* The last lines of the page stay readable while the notice is up. */
html[data-consent-open="true"] .site-footer { padding-bottom: 96px; }
@media (max-width: 640px) {
  html[data-consent-open="true"] .site-footer { padding-bottom: 176px; }
}
/* "Cookie settings" in the footer's legal row reads as one of its links. */
.consent-open {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: underline;
}
[data-consent-open-wrap][hidden] { display: none; }

/* ---------------------------------------------------------------------------
   YouTube videos play only on a press (inc/components/video-embed.php). The
   page carries a poster drawn by the theme, no player and no thumbnail, so
   nothing is requested from Google or YouTube before the press. The box has
   the player's proportions from the start (the aspect ratio is set on the
   element, from the player's own width and height), and the player is put
   inside it on the press: the page does not move.
   --------------------------------------------------------------------------- */
.video-embed {
  position: relative;
  width: 100%;
  max-width: 100%;
  aspect-ratio: 16 / 9;
  margin: 24px 0;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--surface-dark);
  background-image: radial-gradient(ellipse at 50% 40%, rgba(247, 147, 26, 0.22), transparent 62%);
  color: var(--foreground-on-dark);
}
.video-embed .video-embed-play {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 24px;
  text-align: center;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}
.video-embed-title {
  max-width: 36em;
  font-size: var(--text-base);
  font-weight: var(--weight-bold);
  line-height: 1.35;
  color: inherit;
}
.video-embed-button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 68px;
  height: 68px;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  transition: transform var(--duration) var(--ease), background var(--duration) var(--ease);
}
.video-embed-button svg {
  width: 32px;
  height: 32px;
  margin-left: 4px;
  fill: currentColor;
}
.video-embed .video-embed-play:hover .video-embed-button,
.video-embed .video-embed-play:focus-visible .video-embed-button {
  transform: scale(1.08);
  background: var(--brand-strong);
}
.video-embed .video-embed-play:focus-visible { outline: 3px solid var(--brand); outline-offset: -3px; }
.video-embed-note {
  font-size: var(--text-xs);
  color: rgba(244, 242, 236, 0.72);
}
.video-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
/* A browser without aspect-ratio keeps the 16:9 box by padding. */
@supports not (aspect-ratio: 16 / 9) {
  .video-embed { height: 0; padding-bottom: 56.25%; }
}
@media (max-width: 480px) {
  .video-embed .video-embed-play { gap: 10px; padding: 16px; }
  .video-embed-title { font-size: var(--text-sm); }
  .video-embed-button { width: 56px; height: 56px; }
  .video-embed-button svg { width: 26px; height: 26px; }
}
