/* =========================================================================
   ViewerVitals.tv - vv.css

   ONE RULE RUNS THROUGH THIS WHOLE FILE:
   Violet is the brand. Teal is the instrument. Anything violet is chrome -
   nav, buttons, links, headings. Anything teal, amber or rose is a reading
   taken from the stream. The visitor should never have to wonder whether a
   colour on screen means something or is just decoration.
   ========================================================================= */

/* ---------- tokens ---------- */
:root {
  /* surfaces */
  --bg:        #0A0812;
  --bg-soft:   #0E0B18;
  --panel:     #141020;
  --panel-2:   #1A1528;
  --line:      #2A2340;
  --line-soft: #201A32;

  /* text */
  --text:  #EAE6F5;
  --dim:   #9C95B8;
  --faint: #6C6589;

  /* brand */
  --violet:      #8B5CF6;
  --violet-lift: #A78BFA;
  --violet-deep: #6D3EE8;
  --violet-wash: rgba(139, 92, 246, 0.12);

  /* instrument tones - readings only, never chrome */
  --t-great: #34D8B4;
  --t-good:  #22D3EE;
  --t-fair:  #F2C14E;
  --t-warn:  #F58A3C;
  --t-bad:   #F4557A;
  --t-off:   #4A4362;

  /* type */
  --display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --body:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* metrics */
  --shell: 1240px;
  --gap: 14px;
  --radius: 12px;
  --radius-sm: 8px;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* A very slow violet wash behind everything, so the page reads as an
     instrument screen rather than a flat dark theme. */
  background-image:
    radial-gradient(900px 500px at 15% -8%, rgba(139, 92, 246, 0.14), transparent 70%),
    radial-gradient(700px 420px at 88% 4%, rgba(34, 211, 238, 0.07), transparent 70%);
  background-repeat: no-repeat;
  background-attachment: fixed;
}

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

a { color: var(--violet-lift); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--violet-lift);
  outline-offset: 2px;
  border-radius: 4px;
}

h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin: 0 0 0.5em;
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 24px;
}

/* Small uppercase label used on every panel. Encodes a real distinction:
   .eyebrow marks a section of the interface, not decoration. */
.eyebrow {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0;
}

.mono { font-family: var(--mono); }

/* =========================================================================
   HEADER
   ========================================================================= */

.vv-header {
  border-bottom: 1px solid var(--line-soft);
  background: rgba(10, 8, 18, 0.82);
  backdrop-filter: blur(10px);
  position: sticky;
  top: 0;
  z-index: 50;
}

.vv-header-inner {
  display: flex;
  align-items: center;
  gap: 24px;
  min-height: 0;
  padding-top: 7px;
  padding-bottom: 7px;
}

.vv-brand { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.vv-brand:hover { text-decoration: none; }

/* The artwork carries its own dark ground and glow, so it needs no frame
   drawn around it - a border here would fight the logo's own edge. */
.vv-mark {
  width: 56px; height: 56px;
  flex: 0 0 56px;
  border-radius: 12px;
  display: block;
  object-fit: cover;
}

.vv-wordmark {
  font-family: var(--display);
  font-weight: 800;
  font-size: 32px;
  letter-spacing: -0.025em;
  color: var(--text);
  line-height: 1.0;
  display: block;
}
.vv-wordmark .tld { color: var(--violet-lift); }

/* Held at its old size on purpose. Scaling it with the wordmark would keep
   the same proportions and gain nothing; leaving it puts the name clearly
   first and the description clearly second. */
.vv-tagline {
  font-size: 11.5px;
  color: var(--faint);
  line-height: 1.2;
  margin-top: 1px;
  display: block;
}

.vv-nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 30px;
}

.vv-nav a {
  color: var(--dim);
  font-size: 16.5px;
  font-weight: 500;
  text-decoration: none;
}
.vv-nav a:hover, .vv-nav a[aria-current="page"] { color: var(--text); text-decoration: none; }

.vv-nav-toggle { display: none; }

/* =========================================================================
   BUTTONS
   ========================================================================= */

.btn {
  font-family: var(--body);
  font-size: 15px;
  font-weight: 600;
  padding: 13px 22px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.08s ease;
  text-decoration: none;
  line-height: 1.2;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.btn[disabled]:active { transform: none; }

.btn-primary {
  background: linear-gradient(135deg, var(--violet), var(--violet-deep));
  color: #fff;
  border-color: rgba(167, 139, 250, 0.5);
  box-shadow: 0 6px 22px -10px rgba(139, 92, 246, 0.9);
}
.btn-primary:hover:not([disabled]) { background: linear-gradient(135deg, var(--violet-lift), var(--violet)); }

.btn-ghost {
  background: var(--panel);
  color: var(--text);
  border-color: var(--line);
}
.btn-ghost:hover:not([disabled]) { border-color: var(--violet); background: var(--panel-2); }

.btn-quiet {
  background: transparent;
  color: var(--dim);
  border-color: var(--line);
  font-size: 14px;
  padding: 10px 16px;
}
.btn-quiet:hover:not([disabled]) { color: var(--text); border-color: var(--violet); }

/* =========================================================================
   HERO + TEST ENTRY
   ========================================================================= */

.vv-hero { padding: 24px 0 22px; text-align: center; }

.vv-credit {
  font-size: 12px;
  color: var(--faint);
  text-align: right;
  margin: 0 0 6px;
}
.vv-credit a { color: var(--dim); }

.vv-hero h1 {
  font-size: clamp(32px, 5.4vw, 60px);
  margin: 0 auto 16px;
  max-width: 16ch;
}
.vv-hero h1 em { font-style: normal; color: var(--violet-lift); }

.vv-hero-sub {
  color: var(--dim);
  font-size: clamp(15px, 1.6vw, 18px);
  max-width: 60ch;
  margin: 0 auto 30px;
}

/* The one control the whole site exists for. It gets a violet ring and a
   generous target, and nothing else in the hero competes with it. */
.vv-entry {
  display: flex;
  gap: 10px;
  justify-content: center;
  align-items: stretch;
  flex-wrap: wrap;
  max-width: 860px;
  margin: 0 auto;
}

.vv-field {
  flex: 1 1 380px;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 8px 16px;
  text-align: left;
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}
.vv-field:focus-within {
  border-color: var(--violet);
  box-shadow: 0 0 0 4px var(--violet-wash);
}
.vv-field svg { flex: 0 0 auto; color: var(--violet-lift); }

.vv-field-body { flex: 1 1 auto; min-width: 0; }

.vv-field label {
  display: block;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
}

.vv-field input {
  width: 100%;
  background: none;
  border: none;
  color: var(--text);
  font-family: var(--body);
  font-size: 16px;
  padding: 1px 0 3px;
}
.vv-field input:focus { outline: none; }
.vv-field input::placeholder { color: var(--faint); }

.vv-entry .btn { flex: 0 0 auto; }

.vv-entry-note {
  margin: 16px auto 0;
  font-size: 13px;
  color: var(--faint);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.vv-entry-msg {
  margin: 14px auto 0;
  font-size: 14px;
  min-height: 1.4em;
  font-weight: 500;
}
.vv-entry-msg[data-tone="bad"]  { color: var(--t-bad); }
.vv-entry-msg[data-tone="ok"]   { color: var(--t-great); }
.vv-entry-msg[data-tone="info"] { color: var(--dim); }

/* =========================================================================
   PANELS
   ========================================================================= */

.panel {
  background: linear-gradient(180deg, var(--panel), var(--bg-soft));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
}

.panel-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.panel-head .eyebrow { margin-right: auto; }

.dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--t-off);
  flex: 0 0 8px;
}
.dot[data-tone="great"] { background: var(--t-great); box-shadow: 0 0 10px var(--t-great); }
.dot[data-tone="good"]  { background: var(--t-good);  box-shadow: 0 0 10px var(--t-good); }
.dot[data-tone="fair"]  { background: var(--t-fair);  box-shadow: 0 0 10px var(--t-fair); }
.dot[data-tone="warn"]  { background: var(--t-warn);  box-shadow: 0 0 10px var(--t-warn); }
.dot[data-tone="bad"]   { background: var(--t-bad);   box-shadow: 0 0 10px var(--t-bad); }

/* =========================================================================
   THE STAGE - player on the left, live instruments on the right
   ========================================================================= */

.vv-stage { padding: 6px 0 26px; }
.vv-stage[hidden] { display: none; }

.vv-stage-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.32fr) minmax(0, 1fr);
  gap: var(--gap);
  align-items: start;
}

.vv-player-wrap { position: relative; }

.vv-player-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 1px solid var(--line);
}
.vv-player-frame > div,
.vv-player-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* Fallback start control. Mobile browsers only grant playback permission to
   code running in the same task as the tap that triggered it; if anything
   defers (a fetch, a promise) the permission is gone by the time the player
   exists. This gives the visitor a fresh tap to spend. */
.vv-player-start {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  gap: 10px;
  background: rgba(10, 8, 18, 0.86);
  border: 0;
  cursor: pointer;
  color: var(--text);
  font-family: var(--body);
  font-size: 15px;
  font-weight: 600;
  z-index: 3;
  padding: 20px;
  text-align: center;
}
.vv-player-start span { display: block; font-size: 12.5px; font-weight: 400; color: var(--dim); max-width: 30ch; }
.vv-player-start .play-ring {
  width: 58px; height: 58px; border-radius: 50%;
  border: 2px solid var(--violet-lift);
  display: grid; place-items: center;
  background: var(--violet-wash);
}

.vv-player-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  margin-top: 12px;
  font-size: 12.5px;
  color: var(--faint);
}
.vv-player-meta strong { color: var(--dim); font-weight: 500; }

/* Progress of the test itself. Deliberately a thin line rather than a big
   spinner: the instruments are the thing to watch, not the countdown. */
.vv-progress {
  margin-top: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  background: var(--panel);
}
.vv-progress-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dim);
}
.vv-progress-row .count { margin-left: auto; color: var(--violet-lift); }
.vv-progress-track {
  margin-top: 10px;
  height: 4px;
  border-radius: 2px;
  background: var(--line-soft);
  overflow: hidden;
}
.vv-progress-fill {
  height: 100%;
  width: 0;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--violet-deep), var(--violet-lift));
  transition: width 0.9s linear;
}

/* =========================================================================
   INSTRUMENTS
   ========================================================================= */

.vv-instruments { display: block; }
.vv-instruments .vv-dial {
  max-width: 178px;
  margin: 0 auto 10px;
}

/* --- the dial ---
   EVERYTHING inside this gauge lives in the SVG's own coordinate space -
   label, number, percent sign, mini waveform. Layering CSS-pixel-sized HTML
   on top of a container-scaled SVG makes the two drift apart at different
   viewport widths and collide at some of them. Learned the hard way on the
   OPD build; not repeating it. */
.vv-dial { text-align: center; }
.vv-dial svg { width: 100%; height: auto; display: block; }

.vv-dial-label { font-family: var(--mono); font-size: 8.4px; letter-spacing: 0.12em; fill: var(--faint); }
.vv-dial-num   { font-family: var(--display); font-weight: 800; font-size: 36px; fill: var(--text); }
.vv-dial-pct   { font-family: var(--display); font-weight: 700; font-size: 15px; fill: var(--dim); }
.vv-dial-word  { font-family: var(--body); font-weight: 600; font-size: 13px; fill: var(--dim); }

.vv-tick { fill: var(--t-off); transition: fill 0.3s ease; }
.vv-tick.on[data-tone="great"] { fill: var(--t-great); }
.vv-tick.on[data-tone="good"]  { fill: var(--t-good); }
.vv-tick.on[data-tone="fair"]  { fill: var(--t-fair); }
.vv-tick.on[data-tone="warn"]  { fill: var(--t-warn); }
.vv-tick.on[data-tone="bad"]   { fill: var(--t-bad); }

/* --- metric rows --- */
.vv-rows { display: grid; gap: 2px; }

.vv-row {
  display: grid;
  grid-template-columns: 1fr auto minmax(96px, 128px);
  align-items: center;
  gap: 12px;
  padding: 9px 10px;
  border-radius: 6px;
  border: 1px solid transparent;
}
.vv-row:hover { background: rgba(139, 92, 246, 0.05); }

.vv-row-name {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--dim);
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
}
.vv-row-name .bullet {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--t-off);
  flex: 0 0 6px;
}

.vv-row-value {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  text-align: right;
  color: var(--text);
}

.vv-meter { height: 12px; display: block; width: 100%; }
.vv-meter rect { fill: var(--t-off); }

.vv-row[data-tone="great"] .vv-meter rect.on, .vv-row[data-tone="great"] .bullet { fill: var(--t-great); background: var(--t-great); }
.vv-row[data-tone="good"]  .vv-meter rect.on, .vv-row[data-tone="good"]  .bullet { fill: var(--t-good);  background: var(--t-good); }
.vv-row[data-tone="fair"]  .vv-meter rect.on, .vv-row[data-tone="fair"]  .bullet { fill: var(--t-fair);  background: var(--t-fair); }
.vv-row[data-tone="warn"]  .vv-meter rect.on, .vv-row[data-tone="warn"]  .bullet { fill: var(--t-warn);  background: var(--t-warn); }
.vv-row[data-tone="bad"]   .vv-meter rect.on, .vv-row[data-tone="bad"]   .bullet { fill: var(--t-bad);   background: var(--t-bad); }

.vv-row[data-tone="great"] .vv-row-value { color: var(--t-great); }
.vv-row[data-tone="good"]  .vv-row-value { color: var(--t-good); }
.vv-row[data-tone="fair"]  .vv-row-value { color: var(--t-fair); }
.vv-row[data-tone="warn"]  .vv-row-value { color: var(--t-warn); }
.vv-row[data-tone="bad"]   .vv-row-value { color: var(--t-bad); }

/* One pulse per sample, staggered down the panel, so the meters visibly
   ripple once a second and read as live instrumentation. */
@keyframes vv-tick-pulse {
  0%   { filter: brightness(1); }
  22%  { filter: brightness(1.85); }
  100% { filter: brightness(1); }
}
.vv-row.tick .vv-meter,
.vv-row.tick .bullet { animation: vv-tick-pulse 0.85s ease-out; }
.vv-row:nth-child(1).tick .vv-meter, .vv-row:nth-child(1).tick .bullet { animation-delay: 0ms; }
.vv-row:nth-child(2).tick .vv-meter, .vv-row:nth-child(2).tick .bullet { animation-delay: 55ms; }
.vv-row:nth-child(3).tick .vv-meter, .vv-row:nth-child(3).tick .bullet { animation-delay: 110ms; }
.vv-row:nth-child(4).tick .vv-meter, .vv-row:nth-child(4).tick .bullet { animation-delay: 165ms; }
.vv-row:nth-child(5).tick .vv-meter, .vv-row:nth-child(5).tick .bullet { animation-delay: 220ms; }
.vv-row:nth-child(6).tick .vv-meter, .vv-row:nth-child(6).tick .bullet { animation-delay: 275ms; }

/* =========================================================================
   THE QUALITY LADDER - the signature element.

   No other stream tester shows this. It is the one readout that separates
   "the broadcaster is sending low quality" from "your browser chose low
   quality", which is the single most misleading confusion in this whole
   category of tool.
   ========================================================================= */

.vv-ladder { margin-top: 14px; border-top: 1px solid var(--line-soft); padding-top: 12px; }

.vv-ladder summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.vv-ladder summary::-webkit-details-marker { display: none; }
.vv-ladder summary::after {
  content: "show all";
  margin-left: auto;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--violet-lift);
}
.vv-ladder[open] summary::after { content: "hide"; }

.vv-ladder-summary { font-size: 13px; color: var(--dim); }
.vv-ladder-summary strong { color: var(--t-good); font-weight: 600; }
.vv-ladder-summary[data-alert="1"] strong { color: var(--t-warn); }

.vv-ladder-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin: 10px 0 8px;
}

.vv-rung {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.03em;
  padding: 4px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--faint);
  background: var(--bg-soft);
  white-space: nowrap;
}
.vv-rung[data-state="source"] {
  border-color: rgba(139, 92, 246, 0.45);
  color: var(--violet-lift);
}
.vv-rung[data-state="received"] {
  border-color: var(--t-good);
  color: #05131A;
  background: var(--t-good);
  font-weight: 600;
}
.vv-ladder-note { font-size: 12px; color: var(--faint); margin: 0; }
.vv-ladder-note strong { color: var(--dim); font-weight: 500; }

/* =========================================================================
   REPORT
   ========================================================================= */

.vv-report { padding: 6px 0 16px; }
.vv-report[hidden] { display: none; }

.vv-verdict {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: 22px;
  align-items: center;
  padding: 18px 22px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background:
    radial-gradient(600px 260px at 0% 0%, rgba(139, 92, 246, 0.14), transparent 70%),
    linear-gradient(180deg, var(--panel), var(--bg-soft));
}

.vv-verdict-score {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(46px, 7vw, 74px);
  line-height: 0.9;
  letter-spacing: -0.03em;
}
.vv-verdict-score .pct { font-size: 0.42em; margin-left: 3px; color: var(--dim); }

.vv-verdict[data-tone="great"] .vv-verdict-score { color: var(--t-great); }
.vv-verdict[data-tone="good"]  .vv-verdict-score { color: var(--t-good); }
.vv-verdict[data-tone="fair"]  .vv-verdict-score { color: var(--t-fair); }
.vv-verdict[data-tone="warn"]  .vv-verdict-score { color: var(--t-warn); }
.vv-verdict[data-tone="bad"]   .vv-verdict-score { color: var(--t-bad); }

.vv-verdict-word {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(21px, 3vw, 30px);
  margin: 0 0 4px;
}
.vv-verdict-blurb { color: var(--dim); margin: 0; max-width: 56ch; }

/* --- the numbers --- */
.vv-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(152px, 1fr));
  gap: 1px;
  margin-top: var(--gap);
  background: var(--line-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.vv-figure { background: var(--panel); padding: 11px 14px; }
.vv-figure h4 {
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 5px;
}
.vv-figure-main {
  font-family: var(--display);
  font-weight: 700;
  font-size: 20px;
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.vv-figure-sub { font-size: 11.5px; color: var(--faint); margin-top: 3px; line-height: 1.4; }

.vv-figure[data-tone="great"] .vv-figure-main { color: var(--t-great); }
.vv-figure[data-tone="good"]  .vv-figure-main { color: var(--t-good); }
.vv-figure[data-tone="fair"]  .vv-figure-main { color: var(--t-fair); }
.vv-figure[data-tone="warn"]  .vv-figure-main { color: var(--t-warn); }
.vv-figure[data-tone="bad"]   .vv-figure-main { color: var(--t-bad); }

/* Marks a figure as something ViewerVitals worked out rather than something
   Twitch reported. The brief asks for this line to be visible, so it is a
   real badge, not a footnote. */
.vv-calc {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--violet-lift);
  border: 1px solid rgba(139, 92, 246, 0.35);
  border-radius: 3px;
  padding: 1px 5px;
  margin-left: 6px;
  vertical-align: 2px;
}

/* --- limitation --- */
.vv-limitation {
  margin-top: var(--gap);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.vv-limitation-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  background: var(--panel-2);
}
.vv-limitation-head h3 { margin: 0; font-size: 17px; }
.vv-limitation[data-tone="great"] .vv-limitation-head h3 { color: var(--t-great); }
.vv-limitation[data-tone="good"]  .vv-limitation-head h3 { color: var(--t-good); }
.vv-limitation[data-tone="fair"]  .vv-limitation-head h3 { color: var(--t-fair); }
.vv-limitation[data-tone="warn"]  .vv-limitation-head h3 { color: var(--t-warn); }
.vv-limitation[data-tone="bad"]   .vv-limitation-head h3 { color: var(--t-bad); }

.vv-limitation-body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
  gap: 1px;
  background: var(--line-soft);
}
.vv-lim-block { background: var(--panel); padding: 13px 16px; }
.vv-lim-block h4 {
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 9px;
}
.vv-lim-block p { margin: 0; font-size: 13.5px; color: var(--dim); }
.vv-lim-block ul { margin: 0; padding-left: 17px; font-size: 13.5px; color: var(--dim); }
.vv-lim-block li { margin-bottom: 5px; }
.vv-lim-block li::marker { color: var(--violet); }

.vv-secondary {
  margin-top: var(--gap);
  display: grid;
  gap: 10px;
}
.vv-secondary-item {
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel);
}
.vv-secondary-item summary {
  padding: 13px 18px;
  cursor: pointer;
  font-weight: 600;
  font-size: 14.5px;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 10px;
}
.vv-secondary-item summary::-webkit-details-marker { display: none; }
.vv-secondary-item summary::after {
  content: "+";
  margin-left: auto;
  color: var(--violet-lift);
  font-family: var(--mono);
}
.vv-secondary-item[open] summary::after { content: "\2212"; }
.vv-secondary-body { padding: 0 18px 16px; font-size: 14.5px; color: var(--dim); }
.vv-secondary-body ul { padding-left: 18px; margin: 8px 0 0; }

.vv-notice {
  margin-top: var(--gap);
  padding: 11px 15px;
  border-left: 2px solid var(--violet);
  background: var(--violet-wash);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--dim);
}
.vv-notice strong { color: var(--text); font-weight: 600; }

.vv-report-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: var(--gap);
}

/* =========================================================================
   AD SLOTS

   Built in from day one so wiring AdSense later is a paste, not a rebuild.

   Two rules encoded here rather than left to discipline:
   1. No slot sits beside the Begin test button or over the player. Twitch's
      Developer Services Agreement forbids obscuring the player, and their
      permission for advertising alongside it depends on the page carrying
      real content of its own - which is what the report is.
   2. An unfilled unit collapses to nothing. Google sets
      data-ad-status="unfilled" itself when it serves nothing, which is
      common for weeks on a new unit; without this the page reserves a large
      empty rectangle.
   ========================================================================= */

.vv-ad {
  margin: 32px auto;
  text-align: center;
  max-width: 970px;
}
.vv-ad-label {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 8px;
}
.vv-ad-inner { min-height: 0; }
.vv-ad ins.adsbygoogle[data-ad-status="unfilled"] { display: none !important; }
/* When no ad fills, the label should go too rather than float alone. */
.vv-ad:has(ins.adsbygoogle[data-ad-status="unfilled"]) .vv-ad-label { display: none; }
.vv-ad[hidden] { display: none; }

/* Placeholder shown only while AdSense is not yet live, so the layout can be
   checked. Delete .vv-ad-placeholder once the units are approved. */
.vv-ad-placeholder {
  border: 1px dashed var(--line);
  border-radius: var(--radius-sm);
  padding: 26px 16px;
  color: var(--faint);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* =========================================================================
   TOOLS / FEATURE CARDS
   ========================================================================= */

.vv-section { padding: 26px 0; }
.vv-section > h2 { font-size: clamp(23px, 3vw, 31px); }
.vv-section-sub { color: var(--dim); max-width: 62ch; margin: 0 0 22px; }

.vv-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: var(--gap);
}

.vv-card {
  background: linear-gradient(180deg, var(--panel), var(--bg-soft));
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 22px;
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color 0.16s ease, transform 0.16s ease;
}
a.vv-card:hover { border-color: var(--violet); transform: translateY(-2px); text-decoration: none; }

.vv-card-icon {
  width: 40px; height: 40px;
  border-radius: 10px;
  background: var(--violet-wash);
  border: 1px solid rgba(139, 92, 246, 0.3);
  display: grid; place-items: center;
  color: var(--violet-lift);
  margin-bottom: 14px;
}
.vv-card h3 { font-size: 17px; margin: 0 0 6px; }
.vv-card p { margin: 0; font-size: 14px; color: var(--dim); }

.vv-card-soon {
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 2px 6px;
  margin-left: 8px;
  vertical-align: 2px;
}

/* =========================================================================
   HISTORY (structure only until Twitch login lands)
   ========================================================================= */

.vv-history-empty {
  text-align: center;
  padding: 34px 20px;
  color: var(--dim);
}
.vv-history-empty p { max-width: 46ch; margin: 0 auto 16px; }

.vv-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.vv-table th {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: left;
  font-weight: 500;
  padding: 0 12px 10px;
  border-bottom: 1px solid var(--line);
}
.vv-table td { padding: 12px; border-bottom: 1px solid var(--line-soft); color: var(--dim); }

/* =========================================================================
   CONTENT PAGES
   ========================================================================= */

.vv-doc { padding: 26px 0 20px; }
.vv-doc-inner { max-width: 74ch; }
.vv-doc h1 { font-size: clamp(30px, 4.4vw, 44px); margin-bottom: 14px; }
.vv-doc h2 { font-size: 22px; margin-top: 34px; }
.vv-doc h3 { font-size: 17px; margin-top: 24px; }
.vv-doc p, .vv-doc li { color: var(--dim); }
.vv-doc li { margin-bottom: 7px; }
.vv-doc li::marker { color: var(--violet); }
.vv-doc-lede { font-size: 18px; color: var(--text); }
.vv-doc hr { border: 0; border-top: 1px solid var(--line-soft); margin: 34px 0; }

.vv-metric-defs { display: grid; gap: 12px; margin-top: 18px; }
.vv-metric-def {
  border: 1px solid var(--line);
  border-left: 2px solid var(--violet);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--panel);
  padding: 16px 18px;
}
.vv-metric-def h3 { margin: 0 0 6px; font-size: 16px; }
.vv-metric-def p { margin: 0; font-size: 14.5px; }
.vv-metric-def .src {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  display: block;
  margin-top: 8px;
}

/* =========================================================================
   ACCOUNT CHECKER

   Layout note: the result section sits ABOVE the entry form in the document.
   On a phone, a title plus a paragraph plus a form is already most of the
   screen, so a result rendered underneath all that is guaranteed to be below
   the fold. Putting it first - and hiding the intro once it has been read -
   means the answer lands directly under the header.
   ========================================================================= */

.vv-acct-intro { padding: 34px 0 22px; text-align: center; }
.vv-acct-intro h1 { font-size: clamp(30px, 5vw, 52px); margin: 0 auto 14px; }
.vv-acct-intro .vv-hero-sub { margin-bottom: 0; }

.vv-acct-entry { padding: 22px 0 10px; }
.vv-acct-again { text-align: center; margin-bottom: 10px; }

.vv-account-result { padding: 14px 0 0; }
.vv-account-result[hidden] { display: none; }

/* Three columns so the status badges hold the top right corner instead of
   dropping under the name once the display name gets long or the screen
   narrow. */
.vv-account-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: 14px;
  margin-bottom: 16px;
}

.vv-avatar {
  width: 62px; height: 62px;
  border-radius: 14px;
  border: 1px solid var(--line);
  object-fit: cover;
  background: var(--panel-2);
}

.vv-account-ident { min-width: 0; padding-top: 2px; }
.vv-account-name {
  font-family: var(--display);
  font-weight: 800;
  font-size: clamp(19px, 3vw, 25px);
  line-height: 1.15;
  /* break-word, not anywhere: only split a name when it genuinely cannot
     fit, rather than wherever the line happens to run out. */
  overflow-wrap: break-word;
}
.vv-account-login {
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--violet-lift);
  display: inline-block;
  margin-top: 2px;
  overflow-wrap: break-word;
}
/* The <wbr> the script inserts after the slash is the only wrap opportunity
   in the URL, so a long handle drops whole onto the second line instead of
   being cut through the middle. */
.vv-account-login .host { color: var(--faint); }
/* Deliberately NOT nowrap: preferring the break after the slash is right, but
   a handle wider than the card still has to break somewhere rather than
   overflow it. */
.vv-account-login .handle { overflow-wrap: break-word; }

.vv-account-badges {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
  max-width: 34%;
}

.vv-badge {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 4px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--dim);
  white-space: nowrap;
}
.vv-badge[data-kind="live"]     { border-color: var(--t-bad); color: var(--t-bad); }
.vv-badge[data-kind="partner"]  { border-color: var(--violet); color: var(--violet-lift); }
.vv-badge[data-kind="affiliate"]{ border-color: var(--t-good); color: var(--t-good); }

.vv-account-result .vv-report-actions { justify-content: center; }

.vv-account-title {
  margin: var(--gap) 0 0;
  font-size: 14.5px;
  color: var(--dim);
  font-style: italic;
  overflow-wrap: anywhere;
}

/* Public, but low value next to the account age, and easy to misread as
   something private. Small, last, and explained. */
.vv-account-id {
  margin: 16px 0 0;
  text-align: right;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--faint);
}

/* ---------- past broadcasts and clips ---------- */

.vv-media-block {
  margin-top: var(--gap);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 8px;
}
.vv-media-head  { grid-column: 1; grid-row: 1; }
.vv-media-list  { grid-column: 1; grid-row: 2; }
.vv-media-all   { grid-column: 2; grid-row: 2; }
.vv-media-player { grid-column: 1 / -1; grid-row: 3; }

.vv-media-head {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 12px;
  margin-bottom: 9px;
}
.vv-media-head .eyebrow { margin: 0; }

/* Sits in the row WITH the videos rather than floating at the right edge of
   the heading, because it is the same kind of thing: another way into the
   channel's material. It is also now the only control here that leaves the
   site - the rows themselves play in place. */
.vv-media-all {
  flex: 0 0 auto;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  line-height: 1.25;
  padding: 0 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-soft);
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--violet-lift);
  white-space: nowrap;
  text-decoration: none;
  transition: border-color 0.16s ease;
}
.vv-media-all:hover { border-color: var(--violet); text-decoration: none; }

/* Videos and clips open here rather than sending the visitor to Twitch. */
.vv-media-player {
  margin-top: 8px;
  border: 1px solid var(--violet);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #000;
}
.vv-media-frame { position: relative; aspect-ratio: 16 / 9; }
.vv-media-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
.vv-media-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  background: var(--panel);
  border-top: 1px solid var(--line);
}
.vv-media-playing {
  font-size: 13px;
  color: var(--dim);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-right: auto;
}

/* The single line shown in place of three empty panels. */
.vv-media-none {
  margin: var(--gap) 0 0;
  padding: 11px 15px;
  border-left: 2px solid var(--line);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--bg-soft);
  font-size: 12.5px;
  color: var(--faint);
  max-width: 78ch;
  line-height: 1.55;
}

/* Two rows plus a See-all button. auto-fill grid left a third of the card
   empty on a wide screen, because it reserved columns for items that were
   never coming. Flex sizes to what is actually there. */
.vv-media-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}

.vv-media-item {
  flex: 1 1 250px;
  max-width: 420px;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: inherit;
  text-decoration: none;
  min-width: 0;
  transition: border-color 0.16s ease;
}
.vv-media-item:hover { border-color: var(--violet); text-decoration: none; }

.vv-media-thumb {
  width: 84px;
  height: 47px;
  flex: 0 0 84px;
  object-fit: cover;
  border-radius: 5px;
  background: var(--bg-soft);
}

.vv-media-body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }

.vv-media-title {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  /* Two lines then ellipsis - stream titles run long and a wrapping title
     would make every row a different height. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.vv-media-meta {
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* =========================================================================
   FOOTER
   ========================================================================= */

.vv-footer {
  border-top: 1px solid var(--line-soft);
  margin-top: 50px;
  padding: 30px 0 44px;
  font-size: 13.5px;
  color: var(--faint);
}
.vv-footer-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 26px;
  align-items: center;
}
.vv-footer nav { display: flex; flex-wrap: wrap; gap: 20px; margin-left: auto; }
.vv-footer a { color: var(--dim); }
.vv-footer-legal {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
  font-size: 12px;
  line-height: 1.7;
  max-width: 88ch;
}

/* =========================================================================
   UTILITIES
   ========================================================================= */

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The `hidden` attribute is only enforced by the UA stylesheet, so ANY author
   display rule beats it and el.hidden = true silently does nothing. Every
   class the JS toggles .hidden on needs its own rule. Do not remove these. */
[hidden] { display: none !important; }

/* =========================================================================
   RESPONSIVE
   ========================================================================= */

@media (max-width: 1080px) {
  .vv-stage-grid { grid-template-columns: 1fr; }
}

@media (max-width: 1000px) {
  .vv-mark { width: 46px; height: 46px; flex-basis: 46px; }
  .vv-wordmark { font-size: 26px; }
  .vv-nav { gap: 20px; }
  .vv-nav a { font-size: 14.5px; }
}

@media (max-width: 860px) {
  .vv-nav { gap: 16px; }
  .vv-nav a { font-size: 13.5px; }
  .vv-verdict { grid-template-columns: 1fr; gap: 14px; text-align: center; }
  .vv-verdict-blurb { margin-inline: auto; }
}

@media (max-width: 700px) {
  .shell { padding: 0 16px; }
  .vv-hero { padding: 16px 0 16px; }
  .vv-header-inner { padding-top: 6px; padding-bottom: 6px; }
  .vv-mark { width: 42px; height: 42px; flex-basis: 42px; }
  /* No tagline underneath on a phone, so the wordmark can sit alone and
     centred against the logo rather than at the top of a two-line block. */
  .vv-wordmark { font-size: 23px; }
  .vv-tagline { display: none; }
  .vv-nav { display: none; }
  .vv-nav.open {
    display: flex;
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    background: var(--bg-soft);
    border-bottom: 1px solid var(--line);
    padding: 8px 16px 16px;
  }
  .vv-nav.open a { padding: 11px 0; width: 100%; font-size: 15px; }
  .vv-nav-toggle {
    display: inline-flex;
    margin-left: auto;
    background: none;
    border: 1px solid var(--line);
    border-radius: 8px;
    color: var(--dim);
    padding: 8px 10px;
    cursor: pointer;
  }
  .vv-entry { flex-direction: column; }
  .vv-entry .btn { width: 100%; }
  .vv-field { flex: 0 0 auto; }
  .vv-acct-intro { padding: 14px 0 12px; }
  /* On a phone there is not room for a display name AND a pair of status
     pills on one line. The name wins; the pills move to their own row
     underneath rather than crushing it into a narrow column. */
  .vv-account-head { grid-template-columns: auto minmax(0, 1fr); }
  .vv-account-badges {
    grid-column: 1 / -1;
    justify-content: flex-start;
    max-width: none;
  }
  .vv-media-block { grid-template-columns: 1fr; }
  .vv-media-all { grid-column: 1; grid-row: 4; }
  .vv-media-player { grid-column: 1; grid-row: 3; }
  .vv-media-item, .vv-media-all { flex: 1 1 100%; max-width: none; }
  /* Full width already, so the two-line stack is wasted height here. */
  .vv-media-all { flex-direction: row; gap: 5px; padding: 12px 16px; }
  /* Ragged-left wrapped mono type reads badly on a narrow screen. */
  .vv-account-id { text-align: left; }
  /* Three separate full-width buttons was three rows of mostly empty space.
     They share now, and the primary one still gets its own line. */
  .vv-report-actions { gap: 8px; }
  .vv-report-actions .btn { flex: 1 1 140px; padding: 11px 14px; font-size: 14px; }
  .vv-report-actions .btn-primary { flex-basis: 100%; }
  .vv-media-bar { flex-wrap: wrap; }
  .vv-media-playing { flex: 1 1 100%; margin-right: 0; }
  .vv-figures { grid-template-columns: repeat(auto-fit, minmax(138px, 1fr)); }
  .vv-verdict { padding: 16px; }
  .vv-row { grid-template-columns: 1fr minmax(88px, auto); grid-template-areas: "name value" "meter meter"; gap: 6px 10px; }
  .vv-row-name { grid-area: name; }
  .vv-row-value { grid-area: value; }
  .vv-row .vv-meter { grid-area: meter; }
  .vv-footer nav { margin-left: 0; }
}

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