@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

:root {
  --terminal-fg: #c3cbd3;
  --bg: #0a0d12;
  --panel: #12171f;
  --panel-line: #232b36;
  --signal-cyan: #4ce0d2;
  --pulse-amber: #ffb454;
  --alert-coral: #ff6b5e;
  --text-primary: #edf2f5;
  --text-muted: #8a97a6;
  /* Body copy: dimmer than headings, but still comfortably readable. Kept
     separate from --text-muted, which is for labels and captions where lower
     contrast is acceptable. */
  --text-body: #c3cbd3;
  --radius: 10px;
  --font-display: 'Space Grotesk', sans-serif;
  --font-body: 'Inter', sans-serif;
  --font-mono: 'IBM Plex Mono', monospace;
}

/* Light theme: same accent hues, swapped surface/text so contrast stays right */
[data-theme="light"] {
  --bg: #f4f6f8;
  --panel: #ffffff;
  --panel-line: #e1e5ea;
  --signal-cyan: #0f9c8e;
  --pulse-amber: #c97a12;
  --alert-coral: #d94f42;
  --text-primary: #161b22;
  --text-muted: #5c6673;
  /* Near-black rather than a mid grey: on a white panel this is ~11:1, so
     long prose stays readable rather than washing out. */
  --text-body: #2b333d;
}
[data-theme="light"] body {
  background-image:
    linear-gradient(180deg, rgba(15,156,142,0.06), transparent 40%),
    repeating-linear-gradient(0deg, rgba(0,0,0,0.012) 0px, rgba(0,0,0,0.012) 1px, transparent 1px, transparent 3px);
}
[data-theme="light"] .btn-start,
[data-theme="light"] .btn-solid { color: #ffffff; }
[data-theme="light"] .trace-wrap { background: #eef1f4; }
[data-theme="light"] .consent-card,
[data-theme="light"] .panel-card,
[data-theme="light"] .instrument { box-shadow: 0 8px 24px rgba(20,30,40,0.06); }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text-primary);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  /* Safety net: in RTL mode any element that still uses a physical
     left/right offset instead of a logical one (a leftover from a future
     edit, a third-party embed, etc.) can no longer drag the whole
     document's scrollable area sideways and strand the visible content
     off-screen. The real offenders should still be fixed individually
     (see the [dir="rtl"] .skip-link rule below for the one that caused
     the reported "page shifts right in Arabic" bug), but this keeps a
     single bad offset from breaking the entire page again. */
  overflow-x: hidden;
}

body {
  background-image:
    linear-gradient(180deg, rgba(76,224,210,0.05), transparent 40%),
    repeating-linear-gradient(0deg, rgba(255,255,255,0.015) 0px, rgba(255,255,255,0.015) 1px, transparent 1px, transparent 3px);
}

a { color: var(--signal-cyan); text-decoration: none; }

/* Content column widened from a fixed 960px.
   960px was leaving a lot of unused space on a normal desktop display. It is
   now fluid: it grows with the viewport but stops at 1280px, because a
   measurement readout and its metric grid stop being easier to read past
   roughly that point — line lengths get long and the eye has to travel.
   `min()` keeps a sensible margin on mid-size windows without a breakpoint. */
/* Content column.
   1280px on its own, but the side rails are fixed-position elements sitting
   in the margins, so when they are actually serving an ad the column has to
   leave room for them or they overlap the content. `--rail-reserve` is 0 by
   default and set only when an ad network has filled a rail (body.ads-active,
   applied by ads.js), so a site running no ads keeps the full width. */
/* Ad layout reservation.
   `ads-reserved` is set by ads.js as soon as it confirms the operator has an
   ad network configured — NOT when an ad renders. Reserving up front is the
   point: it stops the column jumping narrower the moment an ad arrives, and
   it means the space is visibly there and ready even while AdSense is still
   in its 24-48h approval window.
   `ads-active` is set separately, only when a slot really was filled, and is
   what makes a slot visible. Space reserved, box hidden until filled. */
:root { --rail-reserve: 0px; }
/* Content narrows when ads are configured so the rails have somewhere to go.
   Sized against a 1536px CSS viewport — what a 1920px screen reports at
   Windows' default 125% scaling, which is the most common desktop setup and
   the one this was being tested on. 1040 content + 2x160 rails + gutters
   fits inside 1440px. */
body.ads-reserved { --rail-reserve: 700px; --content-max: 1040px; }
:root { --content-max: 1280px; }

.wrap {
  max-width: min(var(--content-max), calc(100% - 32px));
  margin: 0 auto;
  padding: 0 24px;
  box-sizing: border-box;
}
@media (max-width: 640px) {
  .wrap { max-width: 100%; padding: 0 16px; }
}

/* Header */
.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24px;
  border-bottom: 1px solid var(--panel-line);
}

.logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: -0.01em;
}

/* The wordmark now carries the actual app icon rather than an abstract dot,
   so the browser tab, the installed home-screen icon and the header all show
   the same mark. Sized in em so it tracks the wordmark if the font size
   changes, and given an explicit width/height so it reserves its space before
   the image loads instead of shifting the header. */
/* Inline SVG rather than the PNG app icon. The PNG carries its own dark tile
   baked into the image, so in light mode it showed as a dark square stuck to
   the wordmark. Inline SVG has a transparent background and draws itself from
   CSS variables, so the same mark works in both themes and stays crisp at any
   density. The PNG is still used for the favicon and the installed app icon,
   where a tile is exactly what the platform wants. */
.logo-mark {
  width: 1.5em;
  height: 1.5em;
  flex-shrink: 0;
  display: block;
  overflow: visible;
}
.logo-mark .logo-trace {
  stroke: var(--signal-cyan);
  fill: none;
  stroke-width: 5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.logo-mark .logo-node { fill: var(--pulse-amber); }

.logo a,
a.logo {
  color: inherit;
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 10px;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}

.nav-divider {
  width: 1px;
  height: 18px;
  background: var(--panel-line);
  flex-shrink: 0;
}

.nav-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.site-nav a {
  color: var(--text-muted);
  font-size: 0.9rem;
}
.site-nav a:hover { color: var(--text-primary); }

.theme-btn {
  background: none;
  border: 1px solid var(--panel-line);
  color: var(--text-muted);
  border-radius: 100px;
  width: 32px;
  height: 32px;
  font-size: 0.95rem;
  cursor: pointer;
  line-height: 1;
}
.theme-btn:hover { color: var(--text-primary); }

/* Language switcher */
.lang-switch {
  position: relative;
  display: inline-block;
}
.lang-switch button#langBtn {
  background: none;
  border: 1px solid var(--panel-line);
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 0.82rem;
  border-radius: 100px;
  padding: 6px 14px;
  cursor: pointer;
}
.lang-switch button#langBtn:hover { color: var(--text-primary); }
.lang-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: 8px;
  padding: 6px;
  min-width: 140px;
  z-index: 60;
  box-shadow: 0 12px 32px rgba(0,0,0,0.4);
}
[dir="rtl"] .lang-menu { right: auto; left: 0; }
.lang-menu button {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 0.85rem;
  padding: 8px 10px;
  border-radius: 6px;
  cursor: pointer;
}
[dir="rtl"] .lang-menu button { text-align: right; }
.lang-menu button:hover { background: rgba(255,255,255,0.05); color: var(--text-primary); }
.lang-menu button.active { color: var(--signal-cyan); }
.account-menu-email {
  padding: 8px 10px 10px;
  font-size: 0.78rem;
  color: var(--text-muted);
  border-bottom: 1px solid var(--panel-line);
  margin-bottom: 4px;
  word-break: break-all;
}

/* Hero / instrument panel */
.instrument {
  margin: 48px auto 24px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  padding: 40px clamp(20px, 5vw, 56px);
}

.instrument-top {
  display: flex;
  justify-content: flex-end;
  align-items: baseline;
  margin-bottom: 8px;
}

.connection-info {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  margin-bottom: 24px;
}
.connection-row { margin: 3px 0; color: var(--text-muted); }
.connection-label { color: var(--text-muted); }
.connection-sep { color: var(--text-muted); margin-right: 4px; }
.connection-value { color: var(--text-primary); font-weight: 600; }

.status-pill {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--text-muted);
  border: 1px solid var(--panel-line);
  border-radius: 100px;
  padding: 4px 12px;
}
.status-pill.live { color: var(--signal-cyan); border-color: rgba(76,224,210,0.4); }

/* Big readout */
.readout {
  text-align: center;
  margin: 12px 0 8px;
}

.readout-value {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: clamp(3.5rem, 12vw, 6rem);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--signal-cyan);
  transition: color 0.3s;
}
.readout-value.upload-phase { color: var(--pulse-amber); }

.readout-unit {
  font-family: var(--font-mono);
  color: var(--text-muted);
  font-size: 1rem;
  margin-top: 4px;
}
.readout-unit-sub { font-size: 0.85rem; opacity: 0.8; }

.readout-phase {
  font-family: var(--font-display);
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 6px;
}

/* Pulse trace (signature element) */
.trace-wrap {
  margin: 28px 0 32px;
  border: 1px solid var(--panel-line);
  border-radius: 8px;
  background: #0d1218;
  overflow: hidden;
}
#trace {
  display: block;
  width: 100%;
  height: 120px;
}

/* Start button */
.start-row { display: flex; justify-content: center; }

.btn-start {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: 0.02em;
  color: #06110f;
  background: var(--signal-cyan);
  border: none;
  border-radius: 100px;
  padding: 14px 42px;
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.15s;
  box-shadow: 0 0 0 rgba(76,224,210,0.5);
}
.btn-start:hover { transform: translateY(-1px); box-shadow: 0 6px 24px rgba(76,224,210,0.25); }
.btn-start:disabled { opacity: 0.5; cursor: default; transform: none; box-shadow: none; }

/* Metric cards */
.metrics {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: var(--panel-line);
  border: 1px solid var(--panel-line);
  border-radius: 8px;
  overflow: hidden;
  margin-top: 32px;
}
.metric {
  background: var(--panel);
  padding: 18px 16px;
  text-align: center;
}
.metric-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  text-transform: uppercase;
}
.metric-value {
  font-family: var(--font-mono);
  font-size: 1.4rem;
  font-weight: 600;
  margin-top: 6px;
}
.metric-value.download { color: var(--signal-cyan); }
.metric-value.upload { color: var(--pulse-amber); }
.metric-value.ping,
.metric-value.jitter { color: var(--text-primary); }

.metric-sub {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  margin-top: 2px;
}

@media (max-width: 640px) {
  .metrics { grid-template-columns: repeat(2, 1fr); }
}

/* Insights & history panel cards */
.panel-card {
  margin-top: 24px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: var(--radius);
  padding: 24px clamp(20px, 5vw, 32px);
}
.panel-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--font-display);
  font-size: 0.95rem;
  margin-bottom: 16px;
}

.score-badge {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  padding: 5px 12px;
  border-radius: 100px;
  border: 1px solid var(--panel-line);
  color: var(--text-muted);
}
.score-badge.score-excellent { color: var(--signal-cyan); border-color: rgba(76,224,210,0.4); }
.score-badge.score-good { color: #8ee6a0; border-color: rgba(142,230,160,0.35); }
.score-badge.score-fair { color: var(--pulse-amber); border-color: rgba(255,180,84,0.4); }
.score-badge.score-poor { color: var(--alert-coral); border-color: rgba(255,107,94,0.4); }

.insights-list { display: flex; flex-direction: column; gap: 14px; }
.insight-row { display: flex; gap: 12px; align-items: flex-start; }
.insight-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; flex-shrink: 0; }
.insight-good .insight-dot { background: var(--signal-cyan); box-shadow: 0 0 8px var(--signal-cyan); }
.insight-warn .insight-dot { background: var(--pulse-amber); box-shadow: 0 0 8px var(--pulse-amber); }
.insight-bad .insight-dot { background: var(--alert-coral); box-shadow: 0 0 8px var(--alert-coral); }
.insight-title { font-weight: 600; font-size: 0.9rem; }
.insight-detail { color: var(--text-muted); font-size: 0.85rem; margin-top: 2px; line-height: 1.5; }

.history-table { width: 100%; border-collapse: collapse; }
.history-table td {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--text-muted);
  padding: 8px 4px;
  border-bottom: 1px solid var(--panel-line);
}
.history-table tr:last-child td { border-bottom: none; }

/* Consent card: small, corner-anchored, non-blocking */
.consent-card {
  position: fixed;
  right: 20px;
  bottom: 20px;
  max-width: 340px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: 12px;
  padding: 18px 20px;
  box-shadow: 0 16px 40px rgba(0,0,0,0.45);
  z-index: 50;
  transition: opacity 0.3s, transform 0.3s;
}
[dir="rtl"] .consent-card { right: auto; left: 20px; }
.consent-card.consent-dismiss { opacity: 0; transform: translateY(8px); }
.consent-card p {
  margin: 0 0 14px;
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.5;
}
.consent-card .consent-confirm { margin: 0; color: var(--signal-cyan); font-size: 0.85rem; }
.consent-actions { display: flex; gap: 10px; justify-content: flex-end; }
[dir="rtl"] .consent-actions { justify-content: flex-start; }
.btn-ghost, .btn-solid {
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 600;
  border-radius: 6px;
  padding: 8px 14px;
  cursor: pointer;
  border: 1px solid var(--panel-line);
}
.btn-ghost { background: transparent; color: var(--text-muted); }
.btn-ghost:hover { color: var(--text-primary); }
.btn-solid { background: var(--signal-cyan); color: #06110f; border-color: var(--signal-cyan); }

@media (max-width: 480px) {
  .consent-card { right: 12px; left: 12px; bottom: 12px; max-width: none; }
}

/* Methodology modal: shown once per browser, explains the server-location
   tradeoff honestly rather than a one-sided "we're better" claim. */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Above the mobile nav drawer (120) and its scrim (110). At the old value
     of 100 the login dialog opened UNDERNEATH the open drawer, which is
     exactly what it looked like on a phone: the menu stayed in front and the
     dialog was stuck behind it. */
  z-index: 200;
  padding: 20px;
  overflow-y: auto;
}
.modal-overlay[hidden] {
  display: none;
}
.modal-card {
  background: var(--panel);
  border: 1px solid var(--panel-line);
  border-radius: 12px;
  padding: 28px 28px 24px;
  max-width: 480px;
  box-shadow: 0 24px 60px rgba(0,0,0,0.5);
}
.modal-card h2 {
  font-family: var(--font-display);
  font-size: 1.1rem;
  margin: 0 0 14px;
}
.modal-card p {
  color: var(--text-muted);
  font-size: 0.88rem;
  line-height: 1.65;
  margin: 0 0 20px;
}
.modal-card .btn-solid {
  width: 100%;
  padding: 11px;
  font-size: 0.9rem;
}

/* Account modal: close button, tabs, and form fields. Also reused as the
   general "form inside a card" pattern (e.g. the WiFi QR generator page). */
/* The close control used to be a bare muted glyph with no background and no
   border — invisible against the panel until you knew to look for it. It now
   uses the same treatment as the primary button so it reads as a control, and
   it is 36px square so it is a comfortable tap target rather than the ~14px
   the glyph alone occupied. */
.modal-close-x {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--signal-cyan);
  border: none;
  border-radius: 9px;
  color: #06110f;
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
  line-height: 1;
  transition: filter 0.15s, transform 0.15s;
}
[dir="rtl"] .modal-close-x { right: auto; left: 14px; }
.modal-close-x:hover { filter: brightness(1.1); transform: scale(1.05); }
.modal-close-x:focus-visible { outline: 2px solid var(--text-primary); outline-offset: 2px; }

/* Space for the close button so it never sits on top of the tabs. */
.modal-card { position: relative; }
.modal-card .account-tabs { padding-right: 44px; }
[dir="rtl"] .modal-card .account-tabs { padding-right: 0; padding-left: 44px; }

.account-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--panel-line);
}
.account-tab {
  background: none;
  border: none;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  padding: 8px 4px 10px;
  margin-right: 20px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
}
.account-tab.active { color: var(--signal-cyan); border-bottom-color: var(--signal-cyan); }

.account-form { display: flex; flex-direction: column; gap: 6px; }
.account-form[hidden] { display: none; }
.account-form label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-top: 10px;
}
.account-form input,
.account-form select {
  background: #0d1218;
  border: 1px solid var(--panel-line);
  color: var(--text-primary);
  padding: 10px 12px;
  border-radius: 6px;
  font-family: var(--font-body);
  font-size: 0.92rem;
  width: 100%;
}
[data-theme="light"] .account-form input,
[data-theme="light"] .account-form select {
  background: #f4f6f8;
}
.account-form input:focus,
.account-form select:focus { outline: 2px solid var(--signal-cyan); }
.account-hint { font-size: 0.76rem; color: var(--text-muted); margin: 4px 0 0; }
.account-error { color: var(--alert-coral); font-size: 0.82rem; min-height: 1.2em; margin-top: 8px; }

.account-btn {
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: auto !important;
  padding: 0 12px !important;
  font-size: 0.82rem !important;
}

/* Footer */
.site-footer {
  margin-top: 64px;
  padding: 32px 24px 48px;
  text-align: center;
  color: var(--text-muted);
  font-size: 0.8rem;
  border-top: 1px solid var(--panel-line);
}
.site-footer a { color: var(--text-muted); margin: 0 8px; }

/* Ad placeholder panel. Named "promo-panel" rather than "ad-slot" —
   generic ad blockers commonly hide any element whose class name contains
   "ad-", which would otherwise hide this even before any real ad script
   goes in. */
/* THE BUG: these placeholders defaulted to `display: flex`, so the dashed
   "Ad space" box was shown to every visitor and only hidden if ads.js got far
   enough to hide it. It doesn't get that far in the most common case — a
   visitor who hasn't ticked the Advertising consent box makes ads.js return
   early, before any hiding happens — so real users just saw empty "Ad space"
   boxes on the live site.
   They now default to display:none and are only revealed when an ad network
   has actually put something in them (.ad-live, set by ads.js). Failing
   closed is the right default here: an unfilled ad slot should be invisible,
   not a dashed box announcing that an advert was meant to be there. */
.promo-panel {
  margin: 32px auto;
  /* A responsive AdSense display unit needs real height to render into;
     at 100px Google frequently declines to fill and the slot stays blank. */
  min-height: 280px;
  border: 1px dashed var(--panel-line);
  border-radius: 8px;
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 0.75rem;
  font-family: var(--font-mono);
}
.promo-panel.ad-live { display: block; border: none; min-height: 280px; }

/* Side ad rails: sit in the page margins outside the centered content column.
   Only shown when the viewport is wide enough that they won't crowd the test
   itself — below that width they're simply not rendered. Same naming note
   as above applies here too. */
.promo-rail {
  position: fixed;
  top: 120px;
  /* Widened from 160px.
     A responsive AdSense unit at 160px has almost no inventory to draw on —
     Google's responsive formats are built around 300px and up, so a 160px
     container reliably comes back blank even when everything else is right.
     300x600 (half page) is a standard, well-stocked size. */
  width: 300px;
  min-height: 600px;
  border: 1px dashed var(--panel-line);
  border-radius: 8px;
  /* Same fail-closed default as .promo-panel above. */
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 0.75rem;
  font-family: var(--font-mono);
  z-index: 5;
}
.promo-rail.ad-live { display: block; }
.promo-rail.ad-live .adsbygoogle { width: 300px; height: 600px; display: block; }

/* Reserved but not yet filled: the slot occupies its space silently. No
   border and no text, so a visitor sees clean margin rather than an empty
   dashed box announcing a missing advert. */
body.ads-reserved .promo-rail,
body.ads-reserved .promo-panel {
  display: block;
  /* A barely-there outline: enough that you can confirm the space is
     reserved, faint enough that a visitor reads it as margin rather than as
     a broken advert. Once a real ad fills it (.ad-live) the outline goes. */
  border: 1px dashed var(--panel-line);
  background: none;
  opacity: 0.5;
}
body.ads-reserved .promo-rail.ad-live,
body.ads-reserved .promo-panel.ad-live { border: none; opacity: 1; }
/* Centred in whatever margin is left beside the content column, so the rails
   sit in dead space rather than crowding the test. */
.promo-rail-left  { left:  max(12px, calc((100vw - var(--content-max)) / 4 - 150px)); }
.promo-rail-right { right: max(12px, calc((100vw - var(--content-max)) / 4 - 150px)); }
/* Below this the rails cannot coexist with the content column without
   crowding it: 1280 content + 2x160 rails + gutters needs ~1660px. Beats
   .ad-live deliberately — an ad that overlaps the speed test is worse than no
   ad. Google is fine with a unit not being rendered; it just isn't requested. */
/* Two 300px rails plus a 1040px column needs ~1700px. Between 1400 and 1700
   there is room for one, so the left rail is dropped and the right one kept —
   one served ad beats two that never fill. Below 1400 neither fits, both go,
   and the column reclaims the full width rather than leaving a gap for rails
   that aren't there. */
@media (max-width: 1699px) {
  .promo-rail-left,
  .promo-rail-left.ad-live,
  body.ads-reserved .promo-rail-left { display: none; }
  body.ads-reserved { --rail-reserve: 360px; --content-max: 1000px; }
}
@media (max-width: 1399px) {
  .promo-rail,
  .promo-rail.ad-live,
  body.ads-reserved .promo-rail { display: none; }
  body.ads-reserved { --rail-reserve: 0px; --content-max: 1280px; }
}

/* Static content pages (privacy) */
/* Deliberately NOT widened with .wrap. Legal and glossary text is
   long-form prose, and a 1280px measure is genuinely harder to read than a
   ~75-character line. Widening everything uniformly would have made the
   Privacy Policy worse, not better. */
.prose { padding: 48px 0 80px; max-width: 760px; }
.prose h1 { font-family: var(--font-display); }
.prose h2 { font-family: var(--font-display); font-size: 1.1rem; margin-top: 2em; }
/* Was hardcoded #c3cbd3 — a pale grey for dark backgrounds. On the light
   theme that gave roughly 1.7:1 contrast against a near-white panel, well
   under the 4.5:1 minimum for body text, which made the Glossary, Terms,
   Privacy and VPS pages genuinely hard to read. `--text-body` follows the
   active theme. */
.prose p, .prose li { color: var(--text-body); line-height: 1.7; }

:focus-visible { outline: 2px solid var(--signal-cyan); outline-offset: 2px; }

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

/* RTL support (Arabic) */
/* RTL support (Arabic) — gap-based flex layout handles spacing automatically
   when the whole row's direction flips, so no manual margin overrides needed
   here anymore (unlike the old margin-left hack this replaced). */
[dir="rtl"] .insight-row { flex-direction: row-reverse; }
[dir="rtl"] .metrics, [dir="rtl"] .instrument-top { direction: rtl; }

/* ═══════════════════════════════════════════════════════════════════════
   v17 — mobile header rebuild, install UI, granular consent, new metrics
   ═══════════════════════════════════════════════════════════════════════ */

/* --- Header layout -------------------------------------------------------
   The old header let one flex row wrap. Wrapping only spills items onto new
   lines in source order — it can't reorder — so on a phone the nav links
   landed ABOVE the logo and the controls were stranded on a third row.
   The header is now an explicit grid: logo, then nav, then a menu button
   that only exists on mobile. */

.site-header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 16px;
  padding: 16px 24px;
  position: relative;
}

.nav-toggle { display: none; }

.site-nav {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  /* Separates the link group from the controls group. The links have their
     own tighter internal gap below, so the two groups read as distinct
     rather than one long undifferentiated row. */
  gap: 28px;
  flex-wrap: nowrap;
}

/* BUG FIX: this file's original .nav-links rule (much earlier, shared with
   older markup) sets flex-wrap: wrap, and nothing above overrode it for
   the current desktop layout. .site-nav going nowrap only stops ITS direct
   children from wrapping — .nav-links is one of those children, and is
   itself a flex container whose own child links kept wrapping onto a
   second row once a fifth link (My Account) pushed the row past whatever
   width was left after .nav-controls. Nowrap here stops that; the
   980px breakpoint below (raised from 860px for the same reason) is what
   actually gives the row enough room before the drawer takes over. */
.nav-links {
  flex-wrap: nowrap;
  /* Was 20px, which read as cramped once five links sat side by side —
     particularly "VPS Speed Test" and "My Account", whose two-word labels
     ran together visually. Padding on the anchors rather than gap alone also
     enlarges the click target. */
  gap: 4px;
}
.site-nav .nav-links a {
  padding: 8px 12px;
  border-radius: 7px;
  /* Multi-word labels were wrapping onto two lines inside their own link,
     which is what made the row look crowded and uneven. */
  white-space: nowrap;
  transition: background 0.15s, color 0.15s;
}
.site-nav .nav-links a:hover { background: rgba(255, 255, 255, 0.05); }
[data-theme="light"] .site-nav .nav-links a:hover { background: rgba(0, 0, 0, 0.05); }

.nav-controls { gap: 12px; }

/* Skip link — keyboard users shouldn't have to tab the whole nav to reach
   the one control that matters on this page. */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  background: var(--signal-cyan);
  color: #06110f;
  padding: 10px 16px;
  border-radius: 6px;
  font-weight: 600;
  z-index: 200;
}
.skip-link:focus { left: 16px; }

/* THE ARABIC LAYOUT BUG: this rule was missing. "left: -9999px" is a
   physical offset, not a logical one. In an LTR document that just parks
   the link off the left edge, harmless. But once <html dir="rtl"> is set,
   the document's writing direction flips and the browser's horizontal
   scroll anchor moves to the right edge — so an element sitting 9999px to
   the *physical* left is no longer "off to the side", it's off the start
   of a now-enormous scrollable area. That's what stretched the whole
   page and pushed all real content out to the right, forcing you to keep
   scrolling to reach it. It also explains why the menu "stopped working"
   in Arabic: the menu wasn't broken, it was rendered off-screen along
   with everything else, so taps were landing on empty space.
   Mirroring the offset for RTL (and adding the overflow-x:hidden safety
   net above) fixes both symptoms from this one root cause. */
[dir="rtl"] .skip-link { left: auto; right: -9999px; }
[dir="rtl"] .skip-link:focus { right: 16px; left: auto; }

@media (max-width: 980px) {
  .site-header {
    grid-template-columns: 1fr auto;
    padding: 14px 18px;
  }

  /* Hamburger: 44px so it meets the minimum comfortable touch target. The
     old controls were ~32px and hard to hit accurately. */
  .nav-toggle {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 5px;
    width: 44px;
    height: 44px;
    background: none;
    border: 1px solid var(--panel-line);
    border-radius: 10px;
    cursor: pointer;
    padding: 0;
  }
  .nav-toggle span {
    display: block;
    width: 18px;
    height: 2px;
    background: var(--text-primary);
    border-radius: 2px;
    transition: transform 0.25s, opacity 0.2s;
  }
  .nav-toggle-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  .nav-toggle-open span:nth-child(2) { opacity: 0; }
  .nav-toggle-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  /* Drawer slides in from the trailing edge, full height, above everything. */
  .site-nav {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(320px, 86vw);
    background: var(--panel);
    border-left: 1px solid var(--panel-line);
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0;
    padding: 84px 20px 32px;
    transform: translateX(105%);
    transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
    z-index: 120;
    overflow-y: auto;
    box-shadow: -20px 0 50px rgba(0, 0, 0, 0.45);
  }
  [dir="rtl"] .site-nav {
    right: auto;
    left: 0;
    border-left: none;
    border-right: 1px solid var(--panel-line);
    transform: translateX(-105%);
  }
  .site-nav.nav-drawer-open { transform: translateX(0); }

  .nav-links {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    width: 100%;
  }
  .site-nav .nav-links a {
    display: block;
    padding: 15px 4px;
    font-size: 1rem;
    color: var(--text-primary);
    border-bottom: 1px solid var(--panel-line);
  }
  /* THE BUG: the rule above (2 classes + element) has higher specificity
     than the browser's own "[hidden] { display: none }" default (1
     attribute selector), so it was winning and showing WiFi QR / My
     Account in the drawer even while logged out — the `hidden` attribute
     account.js sets was being visually ignored. This adds the attribute
     into the selector so it out-specifies the rule above instead of
     fighting it, and actually hides the link again. */
  .site-nav .nav-links a[hidden] { display: none; }

  .nav-divider { display: none; }

  .nav-controls {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    width: 100%;
    margin-top: 24px;
  }
  .nav-controls .lang-switch { width: 100%; }
  .nav-controls .lang-switch button#langBtn,
  .nav-controls .theme-btn,
  .nav-controls .account-btn {
    width: 100% !important;
    max-width: none !important;
    height: 46px;
    border-radius: 10px;
    font-size: 0.92rem !important;
    padding: 0 16px !important;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  /* In the drawer the menu has room to open downward in flow rather than
     floating over content it can't fit next to. */
  .nav-controls .lang-menu {
    position: static;
    width: 100%;
    margin-top: 8px;
    box-shadow: none;
  }
  .nav-menu-title {
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-muted);
    padding: 0 4px 8px;
  }
}

.nav-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 110;
  backdrop-filter: blur(2px);
}
.nav-scrim[hidden] { display: none; }
@media (min-width: 861px) { .nav-scrim { display: none; } }

body.nav-locked { overflow: hidden; }

/* --- Service strip: the VPS CLI pitch, between the header and the test --- */
.service-strip {
  max-width: min(var(--content-max), calc(100% - 32px));
  margin: 20px auto 0;
  padding: 0 24px;
}
.service-strip-inner {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 20px;
  border-radius: 12px;
  border: 1px solid rgba(76, 224, 210, 0.28);
  background:
    linear-gradient(115deg, rgba(76, 224, 210, 0.10), rgba(255, 180, 84, 0.06) 60%, transparent);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.2s, transform 0.2s;
}
.service-strip-inner:hover {
  border-color: rgba(76, 224, 210, 0.55);
  transform: translateY(-1px);
}
.service-strip-badge {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--signal-cyan);
  border: 1px solid rgba(76, 224, 210, 0.4);
  border-radius: 100px;
  padding: 4px 10px;
}
.service-strip-text { flex: 1; min-width: 0; }
.service-strip-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text-primary);
}
.service-strip-sub {
  font-size: 0.82rem;
  color: var(--text-muted);
  margin-top: 2px;
}
.service-strip-arrow { color: var(--signal-cyan); font-size: 1.1rem; flex-shrink: 0; }
[dir="rtl"] .service-strip-arrow { transform: scaleX(-1); }

@media (max-width: 640px) {
  .service-strip { padding: 0 18px; }
  .service-strip-inner { flex-wrap: wrap; gap: 10px; padding: 14px 16px; }
  .service-strip-arrow { display: none; }
  .service-strip-title { font-size: 0.9rem; }
}

/* --- Progress bar across the instrument panel --- */
.test-progress {
  height: 3px;
  background: var(--panel-line);
  border-radius: 100px;
  overflow: hidden;
  margin: 0 0 22px;
}
.test-progress-bar {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--signal-cyan), var(--pulse-amber));
  border-radius: 100px;
  transition: width 0.3s ease;
}

/* --- Phase steps --- */
.phase-steps {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-bottom: 18px;
  flex-wrap: wrap;
}
.phase-step {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  border: 1px solid var(--panel-line);
  border-radius: 100px;
  padding: 5px 12px;
  transition: color 0.25s, border-color 0.25s;
}
.phase-step.phase-active { color: var(--signal-cyan); border-color: rgba(76, 224, 210, 0.45); }
.phase-step.phase-complete { color: var(--text-primary); border-color: var(--panel-line); }

/* --- Metrics: six cells now (bufferbloat + consistency) --- */
.metrics { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 640px) { .metrics { grid-template-columns: repeat(2, 1fr); } }

.metric-value.bloat-good { color: var(--signal-cyan); }
.metric-value.bloat-warn { color: var(--pulse-amber); }
.metric-value.bloat-bad { color: var(--alert-coral); }

.trend-note { color: var(--text-muted); font-size: 0.8rem; margin: 12px 0 0; }

/* --- Install prompt --- */
.install-banner {
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: min(var(--content-max), calc(100% - 32px));
  margin: 20px auto 0;
  padding: 14px 20px;
  border: 1px solid var(--panel-line);
  border-radius: 12px;
  background: var(--panel);
}
.install-banner[hidden] { display: none; }
.install-banner-icon { width: 38px; height: 38px; border-radius: 9px; flex-shrink: 0; }
.install-banner-text { flex: 1; min-width: 0; }
.install-banner-title { font-weight: 600; font-size: 0.92rem; }
.install-banner-sub { color: var(--text-muted); font-size: 0.8rem; margin-top: 2px; }
.install-banner-actions { display: flex; gap: 8px; flex-shrink: 0; }

@media (max-width: 640px) {
  .install-banner { margin: 16px 18px 0; flex-wrap: wrap; }
  .install-banner-actions { width: 100%; }
  .install-banner-actions .btn-solid { flex: 1; }
}

.install-steps { counter-reset: step; margin: 0; padding: 0; list-style: none; }
.install-steps li {
  position: relative;
  padding: 0 0 14px 34px;
  color: var(--text-muted);
  font-size: 0.88rem;
  line-height: 1.6;
}
.install-steps li::before {
  counter-increment: step;
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(76, 224, 210, 0.14);
  color: var(--signal-cyan);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
[dir="rtl"] .install-steps li { padding: 0 34px 14px 0; }
[dir="rtl"] .install-steps li::before { left: auto; right: 0; }

/* --- Granular consent --- */
.consent-card { max-width: 400px; }
.consent-actions { flex-wrap: wrap; }
.consent-actions .btn-ghost,
.consent-actions .btn-solid { flex: 1; min-width: 96px; }

.consent-option {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px 0;
  border-bottom: 1px solid var(--panel-line);
}
.consent-option:last-of-type { border-bottom: none; }
.consent-option input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  flex-shrink: 0;
  accent-color: var(--signal-cyan);
  cursor: pointer;
}
.consent-option-disabled { opacity: 0.45; }
.consent-option-disabled input { cursor: not-allowed; }
.consent-option-title { font-weight: 600; font-size: 0.88rem; }
.consent-option-desc { color: var(--text-muted); font-size: 0.8rem; line-height: 1.55; margin-top: 3px; }
.consent-option-locked {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--signal-cyan);
  margin-left: 6px;
}

.modal-card-wide { max-width: 560px; max-height: 86vh; overflow-y: auto; }

/* --- Copy-to-clipboard command block (CLI page) --- */
.command-block {
  position: relative;
  background: #0d1218;
  border: 1px solid var(--panel-line);
  border-radius: 10px;
  padding: 16px 56px 16px 18px;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  color: var(--text-primary);
  overflow-x: auto;
  white-space: pre;
  margin: 12px 0 20px;
  line-height: 1.6;
}
[data-theme="light"] .command-block { background: #eef1f4; }
.command-block .cmd-prompt { color: var(--signal-cyan); user-select: none; }
.copy-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  background: var(--panel);
  border: 1px solid var(--panel-line);
  color: var(--text-muted);
  border-radius: 7px;
  padding: 7px 11px;
  font-family: var(--font-body);
  font-size: 0.74rem;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
[dir="rtl"] .copy-btn { right: auto; left: 10px; }
.copy-btn:hover { color: var(--text-primary); border-color: var(--text-muted); }
.copy-btn.copied { color: var(--signal-cyan); border-color: rgba(76, 224, 210, 0.5); }

.terminal {
  background: #0d1218;
  border: 1px solid var(--panel-line);
  border-radius: 10px;
  overflow: hidden;
  margin: 20px 0 28px;
}
[data-theme="light"] .terminal { background: #10161d; }
.terminal-bar {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--panel-line);
  background: rgba(255, 255, 255, 0.02);
}
.terminal-dot { width: 10px; height: 10px; border-radius: 50%; }
.terminal-title {
  margin-left: 8px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-muted);
}
.terminal-body {
  padding: 16px 18px;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.75;
  color: var(--terminal-fg);
  overflow-x: auto;
  white-space: pre;
}
.terminal-body .t-cyan { color: var(--signal-cyan); }
.terminal-body .t-amber { color: var(--pulse-amber); }
.terminal-body .t-green { color: #8ee6a0; }
.terminal-body .t-dim { color: var(--text-muted); }

/* --- Data-collection disclosure table (privacy page) --- */
.data-table { width: 100%; border-collapse: collapse; margin: 18px 0 28px; font-size: 0.85rem; }
.data-table th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 10px 12px 10px 0;
  border-bottom: 1px solid var(--panel-line);
  font-weight: 500;
}
[dir="rtl"] .data-table th { text-align: right; padding: 10px 0 10px 12px; }
.data-table td {
  padding: 11px 12px 11px 0;
  border-bottom: 1px solid var(--panel-line);
  color: var(--text-body);
  vertical-align: top;
  line-height: 1.55;
}
[dir="rtl"] .data-table td { padding: 11px 0 11px 12px; }
.data-table tr:last-child td { border-bottom: none; }
.tag {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  padding: 3px 8px;
  border-radius: 100px;
  border: 1px solid var(--panel-line);
  color: var(--text-muted);
  white-space: nowrap;
}
.tag-consent { color: var(--signal-cyan); border-color: rgba(76, 224, 210, 0.4); }
.tag-essential { color: var(--text-primary); }
.tag-never { color: var(--alert-coral); border-color: rgba(255, 107, 94, 0.4); }

.callout {
  border-left: 3px solid var(--signal-cyan);
  background: rgba(76, 224, 210, 0.05);
  padding: 16px 20px;
  border-radius: 0 8px 8px 0;
  margin: 22px 0;
}
[dir="rtl"] .callout {
  border-left: none;
  border-right: 3px solid var(--signal-cyan);
  border-radius: 8px 0 0 8px;
}
.callout p { margin: 0; font-size: 0.88rem; }

/* --- Feature grid (account benefits) --- */
.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1px;
  background: var(--panel-line);
  border: 1px solid var(--panel-line);
  border-radius: 10px;
  overflow: hidden;
  margin: 20px 0 28px;
}
.feature-cell { background: var(--panel); padding: 20px; }
.feature-cell h3 {
  font-family: var(--font-display);
  font-size: 0.92rem;
  margin: 0 0 6px;
  color: var(--text-primary);
}
.feature-cell p { color: var(--text-muted); font-size: 0.84rem; line-height: 1.6; margin: 0; }

/* --- Mobile density pass on the instrument itself --- */
@media (max-width: 640px) {
  .instrument { margin: 20px auto 20px; padding: 24px 18px; }
  .connection-info { font-size: 0.76rem; }
  .connection-row { display: flex; justify-content: space-between; gap: 10px; }
  .connection-value { text-align: right; word-break: break-all; }
  [dir="rtl"] .connection-value { text-align: left; }
  .readout-value { font-size: clamp(3rem, 17vw, 4.5rem); }
  .trace-wrap { margin: 20px 0 24px; }
  #trace { height: 96px; }
  .btn-start { width: 100%; padding: 16px 24px; font-size: 1.05rem; }
  .panel-card { padding: 20px 18px; }
  .prose { padding: 28px 0 60px; }
  .site-footer { margin-top: 40px; }
  .history-table td { font-size: 0.74rem; padding: 8px 3px; }
}

/* --- Footer: one row of links, wrapping cleanly on narrow screens.
   Separators are drawn with CSS rather than typed "·" characters between
   anchors, so a wrapped line never starts with an orphaned dot. --- */
.footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 0;
}
.footer-links a { padding: 4px 14px; position: relative; }
.footer-links a:not(:last-child)::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 1px;
  height: 11px;
  background: var(--panel-line);
}
[dir="rtl"] .footer-links a:not(:last-child)::after { right: auto; left: 0; }
.footer-note { margin-top: 10px; }

@media (max-width: 560px) {
  .footer-links a { padding: 6px 10px; font-size: 0.85rem; }
  /* Dividers look broken once links wrap onto several short rows. */
  .footer-links a::after { display: none; }
}

/* --- Privacy controls panel (privacy policy page) --- */
.controls-panel {
  border: 1px solid var(--panel-line);
  border-radius: 10px;
  overflow: hidden;
  margin: 18px 0 30px;
}
.control-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 18px 20px;
  background: var(--panel);
}
.control-row + .control-row { border-top: 1px solid var(--panel-line); }
.control-title { font-weight: 600; font-size: 0.92rem; }
.control-desc {
  color: var(--text-muted);
  font-size: 0.83rem;
  line-height: 1.55;
  margin-top: 4px;
  max-width: 52ch;
}
.control-row button { flex-shrink: 0; }

@media (max-width: 600px) {
  .control-row { flex-direction: column; align-items: stretch; gap: 14px; }
  .control-row button { width: 100%; }
}

/* --- Numbered installation steps (VPS page) --- */
.lede {
  font-size: 1.02rem;
  line-height: 1.75;
  color: var(--text-primary);
}

.step {
  display: flex;
  gap: 18px;
  padding: 22px 0;
  border-top: 1px solid var(--panel-line);
}
.step:first-of-type { border-top: none; padding-top: 8px; }

.step-num {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 0.92rem;
  color: var(--signal-cyan);
  background: rgba(76, 224, 210, 0.10);
  border: 1px solid rgba(76, 224, 210, 0.35);
}
.step-body { flex: 1; min-width: 0; }
.step-body h3 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  margin: 4px 0 8px;
}
.step-body p { margin: 0 0 12px; }
.step-body .command-block { margin: 12px 0; }
.step-note {
  color: var(--text-muted);
  font-size: 0.84rem;
  line-height: 1.6;
  margin: 10px 0 0 !important;
}

@media (max-width: 560px) {
  .step { gap: 12px; }
  .step-num { width: 28px; height: 28px; font-size: 0.82rem; }
  .step-body h3 { font-size: 0.98rem; }
  /* Long one-line install commands are the whole point of this page, so they
     scroll horizontally rather than wrapping into something un-copyable. */
  /* On a phone the copy button was absolutely positioned over the command
     text, covering the end of the very line the button exists to copy. Below
     this width the button drops onto its own full-width row underneath the
     command instead of floating on top of it. */
  .command-block {
    font-size: 0.78rem;
    padding: 14px 14px 12px;
    white-space: pre-wrap;
    word-break: break-all;
  }
  .command-block .copy-btn {
    position: static;
    display: block;
    width: 100%;
    margin-top: 12px;
    padding: 10px;
    font-size: 0.8rem;
  }
  [dir="rtl"] .command-block .copy-btn { left: auto; right: auto; }
}

/* --- CAPTCHA --- */
.captcha-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 0 10px;
}
.captcha-image {
  flex: 1;
  min-height: 64px;
  border: 1px solid var(--panel-line);
  border-radius: 8px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  background: #0d1218;
  /* The SVG is authored at 200x64 but the modal is narrower on a phone, so it
     scales to fit rather than overflowing or being cropped. */
  color: var(--text-muted);
  font-size: 0.78rem;
  text-align: center;
  padding: 4px;
}
.captcha-image svg { width: 100%; height: auto; max-width: 200px; display: block; }
.captcha-image.captcha-loading { opacity: 0.4; }

.captcha-refresh {
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: 8px;
  border: 1px solid var(--panel-line);
  background: none;
  color: var(--text-muted);
  font-size: 1.15rem;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, transform 0.3s;
}
.captcha-refresh:hover {
  color: var(--signal-cyan);
  border-color: rgba(76, 224, 210, 0.45);
  transform: rotate(180deg);
}

.captcha-input {
  font-family: var(--font-mono);
  letter-spacing: 0.32em;
  text-transform: uppercase;
  text-align: center;
  font-size: 1.05rem;
}

/* --- Legal pages --- */
.legal-meta { color: var(--text-muted); font-size: 0.84rem; margin-top: -6px; }
.callout-warn {
  border-left-color: var(--pulse-amber);
  background: rgba(255, 180, 84, 0.06);
}
[dir="rtl"] .callout-warn { border-right-color: var(--pulse-amber); }
#legalContent h2 { margin-top: 34px; }
#legalContent .table-wrap { overflow-x: auto; }


/* Build version in the footer — so "which version am I looking at?" is
   answerable from any page without opening DevTools or curling an endpoint. */
.footer-version {
  margin-top: 10px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  opacity: 0.75;
}

/* ═══════════════════════════════════════════════════════════════════════
   Header controls — language, theme, account
   ═══════════════════════════════════════════════════════════════════════
   These were three unrelated shapes sitting in a row: a pill-shaped account
   button, a small circular icon button, and a wider dropdown. Different
   heights, different corner radii, different weights, so the group read as
   leftovers rather than a control cluster.
   They now share one height, one radius and one border treatment, and sit in
   a bordered group that separates them from the navigation links. */
@media (min-width: 861px) {
  .nav-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    padding-left: 20px;
    margin-left: 4px;
    border-left: 1px solid var(--panel-line);
  }
  [dir="rtl"] .nav-controls {
    padding-left: 0;
    padding-right: 20px;
    border-left: none;
    border-right: 1px solid var(--panel-line);
  }

  /* One shared shape for every control in the group. */
  .nav-controls .theme-btn,
  .nav-controls .account-btn,
  .nav-controls .lang-switch > button {
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 0 14px;
    border: 1px solid var(--panel-line);
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted);
    font-family: var(--font-body);
    font-size: 0.85rem;
    line-height: 1;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
    white-space: nowrap;
  }
  .nav-controls .theme-btn:hover,
  .nav-controls .account-btn:hover,
  .nav-controls .lang-switch > button:hover {
    color: var(--text-primary);
    border-color: var(--text-muted);
    background: rgba(255, 255, 255, 0.04);
  }
  [data-theme="light"] .nav-controls .theme-btn:hover,
  [data-theme="light"] .nav-controls .account-btn:hover,
  [data-theme="light"] .nav-controls .lang-switch > button:hover {
    background: rgba(0, 0, 0, 0.04);
  }

  /* The theme toggle is icon-only, so it gets a square footprint instead of
     a wide button with a lone glyph rattling around inside it. */
  .nav-controls .theme-btn[aria-label] {
    width: 36px;
    padding: 0;
    font-size: 0.95rem;
  }

  /* Signed in: the account button carries the email, so it needs to be the
     emphasised item in the group — but capped, because a long address would
     otherwise push the whole header out of shape. */
  .nav-controls .account-btn.is-signed-in {
    color: var(--signal-cyan);
    border-color: rgba(76, 224, 210, 0.4);
    max-width: 190px;
    overflow: hidden;
    text-overflow: ellipsis;
    display: inline-block;
    line-height: 34px;
  }
}

/* The language dropdown sat flush against the button edge. */
.lang-switch { position: relative; }
.lang-menu {
  min-width: 100%;
  margin-top: 6px;
  border-radius: 8px;
  overflow: hidden;
}
.lang-menu button {
  width: 100%;
  text-align: left;
  padding: 9px 14px;
  font-size: 0.85rem;
}
[dir="rtl"] .lang-menu button { text-align: right; }


/* ═══════════════════════════════════════════════════════════════════════
   Header width is independent of the content column
   ═══════════════════════════════════════════════════════════════════════
   `.site-header` carries `.wrap`, so it inherited `--content-max` — which
   narrows to 1000-1040px once ad space is reserved. Two consequences, both
   visible: the header sat at a different width on pages depending on whether
   the ad config had loaded, and on any one page it visibly JUMPED narrower a
   moment after load, because the reservation is applied after an async fetch.

   Site chrome should not move when an advert is reserved. The header now has
   its own fixed measure on every page, in every ad configuration. */
.site-header.wrap {
  max-width: min(1280px, calc(100% - 32px));
}
@media (max-width: 640px) {
  .site-header.wrap { max-width: 100%; }
}

/* The footer had the same inheritance, and the same jump. */
.site-footer {
  max-width: min(1280px, calc(100% - 32px));
  margin-left: auto;
  margin-right: auto;
}
