/* Cirrina site — brand tokens, layout, and RTL.
   Colours read from the desktop application's own theme files
   (src/Cirrina.UI/Themes/{Light,Dark}Theme.xaml), not invented here:
   accent #5B8DEF, dark neutrals #0A0F1E/#111A2E/#E8ECF4.

   ONE theme for the site (dark-forward), not a light/dark toggle - a
   judgment call for this phase, flagged in the M4 report rather than made
   silently.

   RTL is a layout, not an attribute (see PRODUCT_ROADMAP.md's M4 notes):
   this file uses CSS LOGICAL properties (margin-inline, padding-inline,
   text-align: start/end, inset-inline) everywhere spacing and alignment are
   set, so [dir="rtl"] mirrors the page automatically. Anything that cannot
   mirror itself this way (a directional arrow glyph) is called out and
   handled explicitly near its own rule. */

/* ---- fonts, self-hosted -------------------------------------------------
   Owner-approved, 2026-09-13. These used to be two <link> tags pointing at
   fonts.googleapis.com and fonts.gstatic.com, which handed Google the IP
   address of every visitor to a site whose central claim is that Cirrina
   keeps your keys on your own machine. The files now live in ../fonts/ and
   are described in ../fonts/README.md - where they came from, their
   licences, and why the binaries come from Google Fonts' CDN while the
   licences come from Google Fonts' repository.

   The unicode-range on each rule is copied verbatim from Google's own
   stylesheet and is load-bearing rather than decoration: it is what stops an
   English reader downloading the Arabic subset, and the reverse. Inter and
   Cairo are VARIABLE fonts - one file covers 400-700 - which is why their
   weight descriptor is a range and four weights cost one download. */

@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/poppins-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/poppins-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/inter-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/inter-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Cairo';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/cairo-arabic.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}

@font-face {
  font-family: 'Cairo';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/cairo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Cairo';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/cairo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg: #0A0F1E;
  --bg-elevated: #111A2E;
  --bg-elevated-2: #16213A;
  --border: #22304C;
  --text: #E8ECF4;
  --text-secondary: #8A96B2;
  --accent: #5B8DEF;
  --accent-strong: #6E9CF5;
  --on-accent: #FFFFFF;
  --success: #4ADE80;
  --danger: #F87171;
  --pending: #FBBF24;
  --radius-sm: 8px;
  --radius: 14px;
  --radius-lg: 20px;
  --max-width: 1120px;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow: 0 16px 32px -16px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 24px 56px -20px rgba(0, 0, 0, 0.6);
  --font-heading: "Poppins", "Segoe UI", system-ui, sans-serif;
  --font-body: "Inter", "Segoe UI", system-ui, sans-serif;
  --font-arabic: "Cairo", "Segoe UI", system-ui, sans-serif;
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  --space-5: 4rem;
}

* {
  box-sizing: border-box;
}

html {
  color-scheme: dark;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* Arabic needs a taller line and its own face - set once, on the language,
   not per component, so nothing is missed as pages are added. */
:lang(ar) {
  font-family: var(--font-arabic);
  line-height: 1.85;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  font-weight: 600;
  line-height: 1.25;
  margin: 0 0 var(--space-2);
}

:lang(ar) h1, :lang(ar) h2, :lang(ar) h3, :lang(ar) h4 {
  font-family: var(--font-arabic);
  font-weight: 700;
}

p {
  margin: 0 0 var(--space-2);
  color: var(--text-secondary);
}

a {
  color: var(--accent-strong);
}

img, svg {
  max-width: 100%;
}

.container {
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: var(--space-3);
}

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

.site-header {
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  /* A solid ground, not backdrop-filter: blur() over rgba. Sticky
     positioning combined with a backdrop filter has real, documented
     compositing quirks in some browser/GPU combinations - not worth it for
     a header bar, and a fully opaque background reads just as clean. */
  background: var(--bg);
  z-index: 10;
}

/* A thin accent line under the header instead of a flat border - decorative
   only, drawn as a pseudo-element so it costs nothing structurally. Centred
   with the same inset-inline/margin-inline idiom the candle marks use, so it
   never needs a direction-specific rule. */
.site-header::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: -1px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent) 50%, transparent);
  opacity: 0.6;
  pointer-events: none;
}

.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--space-2);
  gap: var(--space-3);
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--font-heading);
  font-weight: 600;
  font-size: 1.25rem;
  letter-spacing: 0.06em;
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}

.brand-icon {
  width: 1.6rem;
  height: 1.6rem;
  flex-shrink: 0;
}

.brand-lockup {
  width: 72px;
  height: auto;
}

.wordmark .accent {
  color: var(--accent-strong);
}

.wordmark {
  transition: opacity 0.15s ease;
}

.wordmark:hover {
  opacity: 0.85;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav a {
  position: relative;
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 0.95rem;
  padding-block: 0.25rem;
}

.site-nav a:hover {
  color: var(--text);
}

/* An animated underline on plain nav links only - a CTA styled with `.btn`
   grows its own hover state and does not need this one too. `scaleX` grows
   from the element's own centre outward, which is symmetric in either
   writing direction and needs no RTL-specific rule. */
.site-nav a:not(.btn)::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: -2px;
  height: 2px;
  border-radius: 1px;
  background: var(--accent-strong);
  transform: scaleX(0);
  transition: transform 0.2s ease;
}

.site-nav a:not(.btn):hover::after {
  transform: scaleX(1);
}

/* `.site-nav a` (a class plus an element) outranks `.btn-primary` (one
   class) in specificity, so without this the CTA pill rendered its label in
   the nav's own muted secondary colour instead of on-accent white. */
.site-nav a.btn {
  color: var(--on-accent);
}

.lang-switch {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.lang-switch a {
  padding-inline: 0.6rem;
  padding-block: 0.3rem;
  font-size: 0.85rem;
  text-decoration: none;
  color: var(--text-secondary);
}

.lang-switch a[aria-current="true"] {
  background: var(--accent);
  color: var(--on-accent);
}

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding-inline: 1.25rem;
  padding-block: 0.7rem;
  border-radius: var(--radius-sm);
  font-weight: 600;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
  font-size: 0.95rem;
  font-family: inherit;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.btn-sm {
  padding-inline: 1rem;
  padding-block: 0.4rem;
  font-size: 0.85rem;
}

.btn-primary {
  background: linear-gradient(135deg, var(--accent), var(--accent-strong));
  color: var(--on-accent);
  box-shadow: 0 8px 20px -8px rgba(91, 141, 239, 0.6);
}

.btn-primary:hover {
  box-shadow: 0 10px 26px -6px rgba(91, 141, 239, 0.75);
  transform: translateY(-1px);
}

.btn-secondary {
  background: transparent;
  color: var(--text);
  border-color: var(--border);
}

.btn-secondary:hover {
  border-color: var(--accent-strong);
  color: var(--accent-strong);
  transform: translateY(-1px);
}

.btn[aria-disabled="true"] {
  opacity: 0.5;
  pointer-events: none;
}

/* A directional cue that must MIRROR in RTL rather than stay put - the one
   kind of element CSS logical properties cannot handle on their own. */
.btn .arrow {
  display: inline-block;
}

[dir="rtl"] .btn .arrow {
  transform: scaleX(-1);
}

/* A platform glyph (Windows/Mac) beside a download button's own label - not
   directional, so unlike .arrow it never mirrors in RTL. */
.btn .platform-icon {
  width: 1.1em;
  height: 1.1em;
  flex-shrink: 0;
  fill: currentColor;
}

/* The Cirrina Web glyph is a window outline, so it is drawn as strokes where
   the two platform logos above are solid fills. Still not directional. */
.btn .platform-icon.platform-icon-stroke {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.btn .platform-icon.platform-icon-stroke circle {
  fill: currentColor;
  stroke: none;
}

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

.card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow);
}

.section {
  padding-block: var(--space-5);
}

.section-tight {
  padding-block: var(--space-4);
}

.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-3);
}

/* Two cards side by side. Capped so a third column cannot fit: auto-fit leaves
   an empty third track (and its gap) beside two items, which pushes them off
   centre. Stacks to one column on a narrow screen by the same minmax. */
.feature-grid.feature-grid-two {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  max-width: 56rem;
  margin-inline: auto;
}

/* ---- hero ------------------------------------------------------------------
   Two blurred colour blobs behind the copy, decorative only. Placed with
   inset-inline (not left/right), so they mirror for free rather than needing
   a [dir="rtl"] rule - the same idiom the illustrations already use. */
.hero {
  position: relative;
  overflow: hidden;
}

.hero::before,
.hero::after {
  content: "";
  position: absolute;
  width: 34rem;
  height: 34rem;
  border-radius: 50%;
  filter: blur(64px);
  z-index: -1;
  pointer-events: none;
}

.hero::before {
  top: -14rem;
  inset-inline-start: -12rem;
  background: radial-gradient(circle, rgba(91, 141, 239, 0.28), transparent 70%);
}

.hero::after {
  top: -10rem;
  inset-inline-end: -14rem;
  background: radial-gradient(circle, rgba(110, 156, 245, 0.2), transparent 70%);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-inline: auto;
  padding-inline: 0.9rem;
  padding-block: 0.35rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.hero h1 {
  font-size: clamp(2.75rem, 6vw, 4.5rem);
  letter-spacing: -0.02em;
  background: linear-gradient(135deg, var(--text) 45%, var(--accent-strong));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ---- forms --------------------------------------------------------------- */

label {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.9rem;
  color: var(--text-secondary);
}

input[type="text"],
input[type="email"],
input[type="password"] {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  padding: 0.65rem 0.8rem;
  font-family: inherit;
  font-size: 1rem;
}

fieldset {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-2);
  margin: 0 0 var(--space-2);
}

legend {
  padding-inline: 0.4rem;
  color: var(--text-secondary);
  font-size: 0.9rem;
}

[role="alert"] {
  background: rgba(248, 113, 113, 0.12);
  border: 1px solid var(--danger);
  color: var(--danger);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
}

/* ---- buy page ------------------------------------------------------------- */

.buy-amount {
  font-family: var(--font-heading);
  font-size: 2rem;
  font-weight: 600;
  color: var(--text);
}

/* A wallet address, transaction hash, or licence key inside RTL text is the
   single most dangerous rendering case on this site: the Unicode Bidi
   Algorithm can visually re-order segments of a long mixed alphanumeric run
   embedded in RTL flow, and a buyer who copies a reordered address sends
   real money to the wrong place. `direction: ltr` alone fixes the reading
   order WITHIN the element; `unicode-bidi: isolate` is what stops the
   surrounding RTL paragraph's context from influencing it in the first
   place - both together, always, on anything a buyer might copy verbatim.
   See ltr-isolate below for the same treatment on inline cases (an <input>
   value, a licence key). */
.buy-address {
  font-family: ui-monospace, "Cascadia Code", monospace;
  font-size: 1.05rem;
  word-break: break-all;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-2);
  direction: ltr;
  unicode-bidi: isolate;
  text-align: start;
}

/* Same isolation, for an inline value rather than its own block - the
   transaction-hash <input> and the displayed licence key. */
.ltr-isolate {
  direction: ltr;
  unicode-bidi: isolate;
  text-align: start;
}

.network-badge {
  display: inline-block;
  background: rgba(91, 141, 239, 0.15);
  color: var(--accent-strong);
  border: 1px solid var(--accent);
  border-radius: 999px;
  padding-inline: 0.75rem;
  padding-block: 0.2rem;
  font-size: 0.85rem;
  font-weight: 600;
}

.countdown {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--pending);
}

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

.site-footer {
  border-top: 1px solid var(--border);
  padding-block: var(--space-4);
  color: var(--text-secondary);
  font-size: 0.85rem;
}

.site-footer .container {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  justify-content: space-between;
}

.site-footer a {
  color: var(--text-secondary);
}

.footer-company {
  white-space: pre-line;
  max-width: 32em;
}

.footer-links {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.footer-links strong {
  display: block;
  color: var(--text);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 0.2rem;
}

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

.pricing-card {
  position: relative;
  padding-top: var(--space-4);
}

.pricing-card.is-popular {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), var(--shadow);
  transform: translateY(-4px);
}

/* Centred with inset-inline + margin-inline, the same idiom the candle marks
   use - correct in either writing direction with no rule of its own. */
.pricing-badge {
  position: absolute;
  top: -0.7rem;
  inset-inline: 0;
  margin-inline: auto;
  width: fit-content;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.75rem;
  font-weight: 700;
  padding-inline: 0.75rem;
  padding-block: 0.25rem;
  border-radius: 999px;
  white-space: nowrap;
}

/* ---- pricing tabs (Desktop / Web) ------------------------------------------
   NO JavaScript, because the site promises none. Two visually hidden radio
   inputs hold the state; the panels and the label highlighting are selected
   with the general-sibling combinator on :checked. The radios are
   .visually-hidden rather than display:none so they stay in the keyboard order:
   Tab lands on the group and the arrow keys switch panels, which is the
   browser's own radio behaviour and needs no script.

   Both panels are always in the document. Only one is displayed, and the
   content rules (ContentRulesTests) scan the text of both. */

.pricing-tabs > input[type="radio"] {
  /* .visually-hidden's own declarations, repeated here so the tab control
     does not depend on a utility class staying as it is. */
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.tab-list {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin-bottom: var(--space-3);
}

.tab-list label {
  cursor: pointer;
  padding-inline: 1.25rem;
  padding-block: 0.55rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-weight: 600;
  font-size: 0.95rem;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.tab-list label:hover {
  border-color: var(--accent);
  color: var(--text);
}

#tab-desktop:checked ~ .tab-list label[for="tab-desktop"],
#tab-web:checked ~ .tab-list label[for="tab-web"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

/* The visible focus style the tab labels did not have: the radio is the thing
   that holds focus, and its label is the thing the eye is on. */
#tab-desktop:focus-visible ~ .tab-list label[for="tab-desktop"],
#tab-web:focus-visible ~ .tab-list label[for="tab-web"] {
  outline: 3px solid var(--accent-strong);
  outline-offset: 3px;
}

.tab-panel {
  display: none;
}

#tab-desktop:checked ~ .panel-desktop,
#tab-web:checked ~ .panel-web {
  display: block;
}

.footer-disclaimer {
  flex-direction: column;
  gap: 0.25rem;
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: 0.75rem;
}

.footer-disclaimer p {
  margin: 0;
}

/* ---- admin ------------------------------------------------------------------ */

.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.admin-table th, .admin-table td {
  text-align: start;
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--border);
}

.admin-totals {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

.admin-total {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-2) var(--space-3);
  min-width: 140px;
}

.admin-total .value {
  font-size: 1.8rem;
  font-family: var(--font-heading);
  font-weight: 600;
}

.admin-total .label {
  color: var(--text-secondary);
  font-size: 0.85rem;
}

/* ============================================================================
   FEATURE ILLUSTRATIONS
   ============================================================================
   Rules these all follow, and the reason for each, because every one of them
   is a real bug or a real line in PRODUCT_ROADMAP.md:

   1. NO TEXT INSIDE ANY SVG, EVER. The previous set drew its labels as SVG
      <text>, which inherits the page's `direction` but is anchored at a fixed
      x in a fixed viewBox - so on the Arabic page the balance label ran off
      the edge and rendered as "الرص", its value showed as "00", and another
      label collided with the marker beside it. Every label here is an
      ordinary HTML element laid out with logical properties. It mirrors for
      free, wraps for free, localizes for free, and nothing can be clipped by
      a viewBox that never knew how wide Arabic would be.

   2. SHAPES ARE HTML WHERE THEY CAN BE. A circle is a div with a
      border-radius, not a <circle>. That keeps the whole set inside one
      layout system, which is what makes mobile width and RTL work without a
      second implementation. SVG is used for exactly two things - the eye and
      the padlock - because those are real drawings.

   3. NO NUMBERS. No prices, no percentages, no balances. The old set showed
      a "$1,000.00" balance, which on the Arabic page rendered as "00" -
      worse than no balance at all, and against the roadmap's
      no-performance-data rule besides. Capital is shown as a bar that stays
      whole.

   4. OUTCOME, NEVER DECISION. A dot lighting up shows THAT a bot acted.
      Nothing here shows a price being followed, a level being waited for, a
      low being tracked, or a pause before a buy. No chart appears anywhere
      in the set at all - a deliberate over-correction, since a price line
      with anything moving along it invites exactly the reading the roadmap
      forbids.

   5. THE BASE STYLE IS THE STILL. Every component looks composed with all
      motion removed, because that is precisely what a reduced-motion visitor
      gets. Animations move AWAY from a good state rather than towards one.
      Where the base alone would read as unfinished, the reduced-motion block
      at the end composes it explicitly. */

.viz {
  position: relative;
  border-radius: var(--radius);
  background: linear-gradient(160deg, var(--bg-elevated-2), var(--bg-elevated));
  border: 1px solid var(--border);
  padding: var(--space-3);
  overflow: hidden;
  /* No `direction` here, deliberately - every illustration follows the page.
     An earlier version pinned this box to `direction: ltr` on the theory that
     the artwork is non-directional, and it caused a real bug rather than
     preventing one: the standing-orders watcher is anchored with
     `inset-inline-start` (which then resolved to LEFT, from this rule) and
     driven by keyframes chosen with `[dir="rtl"]` (which still matched,
     because the attribute is on <html>), so the two disagreed and on the
     Arabic page the eye walked out of the picture and sat in the text column.

     Pinning was never load-bearing anyway: the dot fields, the single coin
     and the padlock are symmetric, so direction changes nothing about them.
     The one genuinely non-directional piece of TEXT - Paper/Testnet/Live -
     pins itself at `.rung-label`, where the reason is visible next to the
     thing it applies to. */
}

.viz-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  margin-top: var(--space-2);
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.viz-key {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.viz-key::before {
  content: "";
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--border);
  flex-shrink: 0;
}

.viz-key.is-buy::before { background: var(--accent); }
.viz-key.is-sell::before { background: var(--success); }

/* ---- 1. two trades that happened -----------------------------------------
   Five candles and four marks. See _VizCandles.cshtml for the boundaries the
   owner set on this one; the two that constrain the CSS specifically:

   THE CANDLES HAVE NO ANIMATION AT ALL. Not a slow one, not a subtle one.
   A chart that moves before a mark appears is a picture of the program
   watching a price.

   THE MARKS ANIMATE OPACITY AND NOTHING ELSE. No transform, no scale, no
   travel - a flash, not an arrival. Anything that reads as approaching or
   committing is the mechanism wearing a different coat.

   Everything is positioned with LOGICAL insets and `margin-inline: auto`
   rather than `translateX(-50%)`. That is not style: an element anchored
   with `inset-inline-start` and centred with a negative translateX centres
   correctly in LTR and lands half its own width off in RTL, which is the
   same class of bug as the travelling watcher that walked out of the Arabic
   page last round. Auto margins between two logical insets mirror for free
   and there is nothing to get the sign of. */

/* Capped and centred rather than stretched across the whole box. Five
   candles spread over 600px sit so far apart that the bodies read as bars in
   a chart nobody drew; grouped, they read as candles. The cap is what keeps
   the body-to-gap ratio looking like a chart at every width. */
.viz-candles {
  display: flex;
  align-items: stretch;
  gap: 2.5%;
  max-width: 24rem;
  margin-inline: auto;
  min-height: 14rem;
  /* THE ONE ELEMENT ON THIS SITE THAT DOES NOT MIRROR, and it is a convention
     exception rather than a layout one. Every trading chart a buyer has seen
     runs left to right - including on Arabic exchanges and Arabic finance
     sites - so a mirrored one reads as subtly wrong without the viewer being
     able to say why. The owner's call, taken after seeing it mirrored.

     Safe here in a way it was NOT safe on the standing-orders block, and the
     difference is worth stating because that bug cost real time: nothing in
     this component branches on `[dir="rtl"]`, and the marks are centred with
     `inset-inline: 0` + auto margins, which resolves identically whichever
     direction is in force. Pinning only reorders the candles. The watcher
     went wrong precisely because its anchor was pinned while its keyframes
     were still chosen by the page's direction, and the two disagreed. */
  direction: ltr;
}

.candle {
  flex: 1;
  position: relative;
}

.wick,
.body {
  position: absolute;
  /* Two logical insets plus auto margins: centred in both directions, with
     no transform and no sign to flip. */
  inset-inline: 0;
  margin-inline: auto;
}

.wick {
  width: 2px;
  top: var(--wick-top);
  height: var(--wick-h);
  border-radius: 1px;
}

.body {
  width: 78%;
  top: var(--body-top);
  height: var(--body-h);
  border-radius: 2px;
}

/* Falling and rising carry the palette's own buy and sell colours, so this
   illustration and the slow field opposite it speak the same language. The
   bodies are filled at low opacity rather than solid, so a mark flashing
   over one stays the brightest thing in the picture. */
/* The bodies are OPAQUE, and that is not a colour preference: a wick runs the
   full height of its candle, so a translucent body lets the line show
   straight through the middle of it and the result reads as a bar with a
   stripe rather than as a candle. Real charts hide the wick behind the body.
   The tint is stacked over an opaque token rather than hand-mixed into a hex,
   so it still follows the palette if the palette moves. */
.candle.is-down .wick { background: var(--accent); }
.candle.is-down .body {
  background:
    linear-gradient(rgba(91, 141, 239, 0.3), rgba(91, 141, 239, 0.3)),
    var(--bg-elevated);
  border: 1px solid var(--accent);
}

.candle.is-up .wick { background: var(--success); }
.candle.is-up .body {
  background:
    linear-gradient(rgba(74, 222, 128, 0.26), rgba(74, 222, 128, 0.26)),
    var(--bg-elevated);
  border: 1px solid var(--success);
}

/* A mark is positioned VERTICALLY and in no other way.

   The first version nudged each one sideways as well, on the theory that four
   different offsets make the placement harder to read as a rule. On screen it
   did the opposite of belonging: a mark shifted toward the gap between two
   candles floats, and reads as a stray dot rather than as a trade on that
   candle. Centred, each one is unmistakably attached to its own candle - and
   the variation that actually matters is kept, in the vertical gap, where a
   gap is not a price level and so teaches nothing. */
.mark {
  position: absolute;
  top: var(--mark-top);
  inset-inline: 0;
  margin-inline: auto;
  width: 0.9rem;
  height: 0.9rem;
  border-radius: 50%;
  opacity: 0;
  animation: mark-flash 9s ease-in-out infinite;
}

.mark.is-buy {
  background: var(--accent);
  box-shadow: 0 0 0.9rem rgba(91, 141, 239, 0.9);
}

.mark.is-sell {
  background: var(--success);
  box-shadow: 0 0 0.9rem rgba(74, 222, 128, 0.8);
}

/* The buy of a pair, then its sell about half a second later; the second
   pair well after the first has gone. Delays rather than four keyframe sets,
   because all four marks do the same thing at different moments. */
.mark-1 { animation-delay: 0s; }
.mark-2 { animation-delay: 0.55s; }
.mark-3 { animation-delay: 3.9s; }
.mark-4 { animation-delay: 4.45s; }

/* Opacity only. On fast, off gently, then a long gap before the loop so the
   chart is a still picture most of the time - which is what it is. */
@keyframes mark-flash {
  0% { opacity: 0; }
  2%, 14% { opacity: 1; }
  20%, 100% { opacity: 0; }
}

/* ---- 2. the slow field: a bot left running through a long session --------
   A roomy field of tokens warming one at a time on a long unhurried cycle.

   This was one of TWO dot fields until the owner's review: a faster, denser
   one sat in the first block and the two read as the same picture twice. The
   fast one is gone, replaced by the candles above, and this is now the only
   field on the page - which is why it can be the calm one without needing to
   be told apart from anything.

   Pure CSS, and no script anywhere on this site as a result. The stagger is
   an index written onto each element by the view, which SCATTERS it rather
   than passing 0,1,2,3... - a linear stagger across a row-major grid sweeps
   the field in visible diagonal bands, which is the one thing a calm field
   must not do. */

.viz-dots {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 1rem;
  align-content: center;
  min-height: 11rem;
}

.dot {
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--bg);
  border: 1px solid var(--border);
}

.viz-slow .dot {
  animation: dot-breathe 14s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 583ms);
}

/* Lit for roughly a fifth of the cycle, so a handful are warm at any moment
   and the rest are waiting. */
@keyframes dot-breathe {
  0%, 4%, 26%, 100% { background-color: var(--bg); border-color: var(--border); }
  10% { background-color: var(--accent); border-color: var(--accent-strong); }
  19% { background-color: var(--success); border-color: var(--success); }
}

/* ---- 3. standing orders: watchers accumulate, capital stays whole --------
   The biggest block on the page, and the product's strongest claim. One
   watcher travels the row and settles; a second appears; a third; then all
   three fade and it begins again. Underneath, the capital bar never changes -
   which is the entire point, and is shown by NOT animating it. */

.viz-orders {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-height: 14rem;
  justify-content: center;
  /* Headroom for the watchers, which sit ABOVE their coins and would
     otherwise be clipped by .viz's overflow:hidden - the first version of
     this cut the top off every eye. */
  padding-top: 2.5rem;
}

/* A six-column GRID rather than a flex row with space-between, and the
   reason is the travelling watcher below: in a grid, a child sized to one
   column can be moved by exact multiples of its own width, so translateX
   steps land on coin centres at every viewport width. The flex version
   travelled a distance measured in multiples of the EYE's width, which
   drifted away from the coins the moment the container changed size. */
.coin-row {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  justify-items: center;
  align-items: end;
  position: relative;
}

.coin {
  position: relative;
  width: 3.2rem;
  height: 3.2rem;
  border-radius: 50%;
  background: var(--bg);
  border: 1px solid var(--border);
  flex: 0 0 auto;
}

/* A watcher sits above its coin. Two of the three simply arrive; the first
   travels the length of the row first, which is what makes the set read as
   one thing looking around rather than three things blinking. */
.watcher {
  position: absolute;
  bottom: calc(100% + 0.6rem);
  left: 50%;
  margin-left: -1.1rem;
  width: 2.2rem;
  color: var(--accent-strong);
  opacity: 0;
  filter: drop-shadow(0 0 0.5rem rgba(91, 141, 239, 0.5));
}

.watcher svg { display: block; width: 100%; height: auto; }

.coin.is-watched {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(91, 141, 239, 0.16);
}

/* Three separate arrival times on ONE undelayed cycle, rather than one
   keyframe set with animation-delay. A delay shifts the whole cycle, so the
   three watchers would fade out at three different moments and the set would
   never be empty - and "then all of them fade and it begins again" is half
   the idea. These share a fade at 76-88%, so they leave together. */
.watcher-2 { animation: watch-appear-2 11s ease-in-out infinite; }
.watcher-3 { animation: watch-appear-3 11s ease-in-out infinite; }

@keyframes watch-appear-2 {
  0%, 38% { opacity: 0; transform: translateY(0.4rem) scale(0.8); }
  46%, 76% { opacity: 1; transform: translateY(0) scale(1); }
  88%, 100% { opacity: 0; transform: translateY(-0.3rem) scale(0.9); }
}

@keyframes watch-appear-3 {
  0%, 54% { opacity: 0; transform: translateY(0.4rem) scale(0.8); }
  62%, 76% { opacity: 1; transform: translateY(0) scale(1); }
  88%, 100% { opacity: 0; transform: translateY(-0.3rem) scale(0.9); }
}

/* The travelling one. It is a child of the ROW, not of a coin, and is exactly
   one column wide - so translateX(200%) is exactly two coins, at any width.
   That is the whole reason .coin-row became a grid.

   `transform: translateX` is not a logical property, so this is the ONE place
   in the whole set where mirroring is written by hand: two keyframe sets
   picked by dir, rather than one that silently travels the wrong way across
   the Arabic page. */
.watcher-travel {
  position: absolute;
  bottom: calc(100% + 0.6rem);
  /* Clear the base rule's physical left/margin-left FIRST, then place with
     the logical property - otherwise on an RTL page `left: 50%` from
     `.watcher` and `inset-inline-start` (which is `right` there) are both
     set alongside a width, and the browser has to discard one of them. */
  left: auto;
  right: auto;
  margin-inline: 0;
  inset-inline-start: 0;
  width: calc(100% / 6);
  display: flex;
  justify-content: center;
  animation: watch-travel-ltr 11s ease-in-out infinite;
}

.watcher-travel svg { width: 2.2rem; }

[dir="rtl"] .watcher-travel { animation-name: watch-travel-rtl; }

@keyframes watch-travel-ltr {
  0% { opacity: 0; transform: translateX(0); }
  9% { opacity: 1; transform: translateX(0); }
  30%, 76% { opacity: 1; transform: translateX(200%); }
  88%, 100% { opacity: 0; transform: translateX(200%); }
}

@keyframes watch-travel-rtl {
  0% { opacity: 0; transform: translateX(0); }
  9% { opacity: 1; transform: translateX(0); }
  30%, 76% { opacity: 1; transform: translateX(-200%); }
  88%, 100% { opacity: 0; transform: translateX(-200%); }
}

/* Capital. A bar, whole, with no number on it and no segment in it. It is
   the only element in the set whose job is to be visibly INERT, so it gets
   the slowest sheen available and nothing else: enough to show the page is
   alive, not enough to suggest the balance is doing anything. */
.capital {
  height: 1.5rem;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-strong));
  position: relative;
  overflow: hidden;
}

.capital::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent 40%, rgba(255, 255, 255, 0.22) 50%, transparent 60%);
  transform: translateX(-100%);
  animation: capital-sheen 7s ease-in-out infinite;
}

@keyframes capital-sheen {
  0%, 55% { transform: translateX(-100%); }
  90%, 100% { transform: translateX(100%); }
}

/* ---- 4. one coin, and you stop watching ---------------------------------
   A single coin swinging, an eye above it that closes and fades while a
   steady marker stays lit. The coin keeps moving after the eye has gone,
   which is the whole sentence: it is still being handled, you are not the
   one handling it. */

.viz-single {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 12rem;
}

.single-eye {
  width: 2.6rem;
  color: var(--accent-strong);
  animation: eye-close 8s ease-in-out infinite;
  transform-origin: center;
}

.single-eye svg { display: block; width: 100%; height: auto; }

@keyframes eye-close {
  0%, 40% { opacity: 1; transform: scaleY(1); }
  55%, 70% { opacity: 0.85; transform: scaleY(0.08); }
  80%, 100% { opacity: 0; transform: scaleY(0.08); }
}

.single-coin {
  width: 4.2rem;
  height: 4.2rem;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, var(--bg-elevated-2), var(--bg));
  border: 2px solid var(--accent);
  box-shadow: 0 0 1.4rem rgba(91, 141, 239, 0.3);
  animation: coin-swing 3.1s ease-in-out infinite;
}

@keyframes coin-swing {
  0%, 100% { transform: translateY(-0.7rem); }
  50% { transform: translateY(0.7rem); }
}

/* The marker that stays lit once the eye has gone: the order is still there.
   Deliberately a plain steady pulse with no direction and no target - it
   says "armed", it does not say what it is waiting for. */
.armed {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0.7rem rgba(91, 141, 239, 0.8);
  animation: armed-pulse 2s ease-in-out infinite;
}

@keyframes armed-pulse {
  0%, 100% { opacity: 0.45; transform: scale(0.85); }
  50% { opacity: 1; transform: scale(1.15); }
}

/* ---- 5. Paper, then Testnet, then real money -----------------------------
   Built from HTML boxes rather than SVG precisely BECAUSE it is directional:
   a flex row follows the page's own dir, so the Arabic page reads the
   sequence right to left on its own, labels and all, with nothing mirrored
   by hand. The old SVG version could not do that without a second drawing. */

.viz-ladder {
  display: flex;
  align-items: flex-end;
  gap: 0.75rem;
  min-height: 12rem;
}

.rung {
  flex: 1;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  padding-block: 0.6rem;
  gap: 0.5rem;
  position: relative;
  overflow: hidden;
}

.rung-1 { height: 5rem; }
.rung-2 { height: 7.5rem; }
.rung-3 { height: 10rem; border-color: var(--accent); }

.rung-label {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-secondary);
  position: relative;
  z-index: 1;
  /* Paper / Testnet / Live are the literal names the desktop application
     shows, and it has no Arabic UI - so they stay English on both pages and
     stay LTR inside a mirrored row. The same rule as USDT and TRC-20. */
  direction: ltr;
}

.rung-3 .rung-label { color: var(--text); }

.rung-fill {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 0;
  background: linear-gradient(180deg, rgba(91, 141, 239, 0.35), rgba(91, 141, 239, 0.08));
  animation: rung-fill 9s ease-in-out infinite;
}

.rung-2 .rung-fill { animation-delay: 1.4s; }
.rung-3 .rung-fill {
  animation-delay: 2.8s;
  background: linear-gradient(180deg, rgba(91, 141, 239, 0.85), rgba(91, 141, 239, 0.25));
}

@keyframes rung-fill {
  0% { height: 0; }
  18%, 78% { height: 100%; }
  94%, 100% { height: 0; }
}

/* ---- 6. the keys stay here -----------------------------------------------
   The security block's mark. One shape, no motion beyond a slow breath: the
   claim is that nothing moves, so an illustration that bustles would argue
   against its own caption. */

.viz-lock {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 9rem;
}

.lock-ring {
  width: 6.5rem;
  height: 6.5rem;
  border-radius: 50%;
  border: 2px solid var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--accent-strong);
  box-shadow: 0 0 0 0 rgba(91, 141, 239, 0.35);
  animation: lock-breathe 5s ease-in-out infinite;
}

.lock-ring svg { width: 2.6rem; height: auto; display: block; }

@keyframes lock-breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(91, 141, 239, 0.35); }
  50% { box-shadow: 0 0 0 0.9rem rgba(91, 141, 239, 0); }
}

/* ---- feature rows --------------------------------------------------------
   Artwork beside copy, alternating down the page. A two-column grid places
   its first child in the INLINE-start column, so RTL mirrors the whole
   layout with no rule of its own; `.flip` swaps the pair by explicit column
   assignment, which mirrors in RTL for exactly the same reason. */

/* Each feature is its own elevated panel rather than a row separated by a
   flat border - `.feature-stack` (the container the six of them sit in)
   supplies the gap between panels, so no `+` sibling rule is needed here. */
.feature-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.feature-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  align-items: center;
  padding: var(--space-4);
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition: border-color 0.25s ease, transform 0.25s ease;
}

.feature-row:hover {
  border-color: var(--accent);
  transform: translateY(-2px);
}

.feature-row .feature-art { grid-column: 1; grid-row: 1; }
.feature-row .feature-copy { grid-column: 2; grid-row: 1; }
.feature-row.flip .feature-art { grid-column: 2; }
.feature-row.flip .feature-copy { grid-column: 1; }

/* The standing-orders block: the product's strongest claim, so it gets the
   page's widest artwork and its own heading size rather than sitting in the
   same rhythm as everything else. */
.feature-row.feature-lead {
  grid-template-columns: 1.35fr 1fr;
}

.feature-lead h2 { font-size: 1.9rem; }

.feature-copy h2 {
  font-size: 1.5rem;
  margin-bottom: var(--space-1);
}

.feature-copy p { margin-bottom: var(--space-1); }

@media (max-width: 860px) {
  .feature-stack {
    gap: var(--space-3);
  }

  .feature-row,
  .feature-row.feature-lead {
    grid-template-columns: 1fr;
    gap: var(--space-3);
    padding: var(--space-3);
  }

  /* On one column the artwork always leads, whichever side it took on a wide
     screen - an explicit row assignment, because `.flip`'s column rules
     would otherwise leave the pair in source order on some rows and not on
     others. */
  .feature-row .feature-art,
  .feature-row.flip .feature-art { grid-column: 1; grid-row: 1; }
  .feature-row .feature-copy,
  .feature-row.flip .feature-copy { grid-column: 1; grid-row: 2; }

  .feature-lead h2 { font-size: 1.5rem; }

  /* 24 tokens over 4 columns is six complete rows on a phone. */
  .viz-dots { grid-template-columns: repeat(4, 1fr); gap: 0.9rem; }

  .viz-candles { min-height: 10rem; }
  .coin { width: 2.2rem; height: 2.2rem; }
  .watcher { width: 1.6rem; margin-left: -0.8rem; }

  /* The travelling watcher must stay ONE COLUMN wide at every width - that
     is what makes its translateX steps land on coin centres. The `.watcher`
     rule above has the same specificity and comes first, so without this it
     silently wins here and the eye drifts off its coin: measured at 18px out
     on a phone, and the RTL page only passed the same check by landing
     inside the tolerance. Shrink the GLYPH on a phone, never the step. */
  .watcher-travel { width: calc(100% / 6); margin-inline: 0; }
  .watcher-travel svg { width: 1.6rem; }

  .viz-orders { padding-top: 2rem; min-height: 11rem; gap: var(--space-3); }
  .viz-ladder { min-height: 9rem; }
  .rung-1 { height: 4rem; }
  .rung-2 { height: 5.75rem; }
  .rung-3 { height: 7.5rem; }
}

/* ---- reduced motion ------------------------------------------------------
   Every animation off, then a COMPOSED STILL rather than an empty frame. The
   global rule is the net for anything added later without thinking about it;
   the rules under it compose the pieces whose base state alone would read as
   unfinished. */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition-duration: 0.001ms !important;
  }

  /* The chart, finished, with all four marks resting visible - the owner's
     own description of what this one's still should be. The marks are the
     only part of it that ever moves, so with them held on, the reduced-motion
     view is the complete picture rather than a lesser one. */
  .mark { opacity: 1; }

  /* The field, mid-activity: some warm, some cooling, the rest waiting.
     Written out index by index rather than as `an + b`, and that is
     deliberate. ANY arithmetic progression across a grid draws a line: a step
     that is coprime with the column count walks a diagonal, a step that
     shares a factor stacks into columns. The first version used `5n+4` on a
     ten-column grid and banded into two stripes; on this six-column grid
     every small step draws a diagonal instead. A hand-picked scatter is the
     only version that reads as a market, and with 24 tokens it is short
     enough to simply write down. */
  .viz-dots .dot:nth-child(2),
  .viz-dots .dot:nth-child(11),
  .viz-dots .dot:nth-child(16),
  .viz-dots .dot:nth-child(21) {
    background: var(--accent);
    border-color: var(--accent-strong);
  }

  .viz-dots .dot:nth-child(5),
  .viz-dots .dot:nth-child(9),
  .viz-dots .dot:nth-child(18),
  .viz-dots .dot:nth-child(23) {
    background: var(--success);
    border-color: var(--success);
  }

  /* Three watchers present at once - the state the animation spends most of
     its cycle in. The travelling one also needs its ARRIVED position: with
     the animation off it falls back to translateX(0), which parks it on top
     of the watcher already sitting on the first coin and leaves the middle
     one bare. Two eyes on one coin and a ringed coin with none is not a
     still, it is a glitch. */
  .watcher { opacity: 1; }
  .watcher-travel { transform: translateX(200%); }
  [dir="rtl"] .watcher-travel { transform: translateX(-200%); }
  .capital::after { display: none; }

  /* The eye has closed and the marker is lit: the end of the sentence, which
     is the half worth showing. */
  .single-eye { opacity: 0.25; }

  .rung-fill { height: 100%; }
}

/* ---- docs ------------------------------------------------------------------ */

.card img {
  display: block;
  width: 100%;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  margin-block: var(--space-2);
}

dl { margin: 0; }
dl > div { margin-bottom: var(--space-2); }
dl dt { margin-bottom: 0.2rem; }
dl dd { margin: 0; margin-inline-start: 0; }

/* ---- utility --------------------------------------------------------------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.text-center {
  text-align: center;
}

.muted {
  color: var(--text-secondary);
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.inline {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
