/*!
 * WiFi Speed Test - custom theme on top of Bootstrap 5
 * Design tokens first, then components. No Bootstrap source overrides needed.
 */

/* ------------------------------------------------------------------ tokens */
html { scroll-padding-top: 5rem; }

/* Two roles are kept apart on purpose:
     --wst-blue*   filled surfaces that carry white text (buttons, GO)
     --wst-accent* accent TEXT on the page background (links, status, hovers)
   In dark mode a filled surface must get darker on hover while accent text must
   get lighter, so a single "blue-dark" token cannot serve both. */
:root {
  /* Filled surfaces that carry white text (buttons, GO, navbar pills). */
  --wst-blue: #13547a;
  --wst-blue-hover: #0e4260;
  --wst-blue-active: #0a3149;
  --wst-blue-disabled: #dbe9f0;
  --wst-blue-disabled-text: #4a6c7d;
  /* Accent TEXT on the page background: links, status lines, hovers. */
  --wst-accent: #13547a;
  --wst-accent-hover: #0a3149;
  --wst-blue-soft: #f0f8ff;

  /* The logo: the needle and hub of the mark, and the "WiFi" of the wordmark. */
  --wst-brand-ink: #13224a;
  --wst-brand-accent: #1f6feb;

  /* The secondary mint. Never carries white text - 1.9:1 would fail AA - so
     anything filled with it uses --wst-mint-ink instead. */
  --wst-mint: #80d0c7;
  --wst-mint-hover: #6cc4ba;
  --wst-mint-ink: #0b3a4f;
  --wst-mint-soft: #e8f7f5;
  /* The template's signature diagonal, used for the hero and page headers. */
  --wst-gradient: linear-gradient(15deg, #13547a 0%, #80d0c7 100%);

  --wst-ink: #0f1c24;
  --wst-body: #6b6d70;
  --wst-muted: #717275;
  --wst-line: #dde9f0;
  --wst-bg: #ffffff;
  --wst-bg-soft: #f0f8ff;
  --wst-surface: #ffffff;
  --wst-surface-2: #f8fcff;
  --wst-gauge-track: #e2eef4;
  --wst-gauge-tick: #c6dbe5;
  --wst-focus-ring: rgba(19, 84, 122, .45);
  --wst-live: #157a5b;

  --wst-hairline: rgba(255, 255, 255, .9);
  --wst-glow: rgba(128, 208, 199, .38);
  --wst-well-a: rgba(19, 84, 122, .07);
  --wst-well-b: rgba(128, 208, 199, 0);
  --wst-hero-a: rgba(19, 84, 122, .12);
  --wst-hero-b: rgba(128, 208, 199, .18);
  --wst-map-ink: rgba(19, 84, 122, .55);
  --wst-map-grid: rgba(19, 84, 122, .10);

  --wst-shadow-sm: 0 1px 2px rgba(13, 43, 62, .05), 0 6px 16px -6px rgba(13, 43, 62, .12);
  --wst-shadow-lg:
    0 1px 2px rgba(13, 43, 62, .05),
    0 12px 28px -10px rgba(13, 43, 62, .15),
    0 32px 64px -32px rgba(13, 43, 62, .20);
  --wst-radius: 20px;
  --wst-radius-sm: 10px;
  --wst-radius-pill: 100px;

  --wst-font-title: 'Montserrat', 'Segoe UI', system-ui, -apple-system, sans-serif;

  /* Bootstrap 5.3 theme hooks */
  --bs-primary: #13547a;
  --bs-primary-rgb: 19, 84, 122;
  --bs-body-color: #6b6d70;
  --bs-body-color-rgb: 107, 109, 112;
  --bs-body-bg: #ffffff;
  --bs-heading-color: #0f1c24;
  --bs-border-color: #dde9f0;
  --bs-link-color: var(--wst-accent);
  --bs-link-color-rgb: 19, 84, 122;
  --bs-link-hover-color: var(--wst-accent-hover);
  --bs-border-radius: 10px;
  --bs-border-radius-lg: 20px;
  --bs-focus-ring-color: rgba(19, 84, 122, .3);

  color-scheme: light;
}

/* Dark theme. assets/js/theme.js sets data-theme (our tokens) and data-bs-theme
   (Bootstrap's own dark styles) on <html> before the page paints. Every
   --bs-* value set on :root above has to be restated here, because :root and
   [data-bs-theme=dark] have equal specificity and this stylesheet loads last. */
:root[data-theme="dark"] {
  --wst-blue: #2b7fa6;
  --wst-blue-hover: #22688a;
  --wst-blue-active: #1a5270;
  --wst-blue-disabled: #143242;
  --wst-blue-disabled-text: #90aebd;
  /* Mint reads as the accent on a dark ground, where the deep teal cannot. */
  --wst-accent: #80d0c7;
  --wst-accent-hover: #a8e2dc;
  --wst-blue-soft: #0d2c3c;

  /* The navy of the logo disappears on this ground; the mark keeps its
     gradients and only the needle, hub and wordmark lighten. */
  --wst-brand-ink: #e9f6f8;
  --wst-brand-accent: #6bb4ff;

  --wst-mint: #80d0c7;
  --wst-mint-hover: #9adcd4;
  --wst-mint-ink: #06222f;
  --wst-mint-soft: #0f3341;
  --wst-gradient: linear-gradient(15deg, #0b3a56 0%, #3f8f88 100%);

  --wst-ink: #e9f6f8;
  --wst-body: #b0c5cf;
  --wst-muted: #8fa6b2;
  --wst-line: #1d3f52;
  --wst-bg: #07161f;
  --wst-bg-soft: #0a1f2b;
  --wst-surface: #0d2534;
  --wst-surface-2: #11303f;
  --wst-gauge-track: #16323f;
  --wst-gauge-tick: #2c5468;
  --wst-focus-ring: rgba(128, 208, 199, .55);
  --wst-live: #5fd6a4;

  --wst-hairline: rgba(255, 255, 255, .07);
  --wst-glow: rgba(128, 208, 199, .30);
  --wst-well-a: rgba(128, 208, 199, .10);
  --wst-well-b: rgba(128, 208, 199, 0);
  --wst-hero-a: rgba(19, 84, 122, .38);
  --wst-hero-b: rgba(128, 208, 199, .14);
  --wst-map-ink: rgba(128, 208, 199, .55);
  --wst-map-grid: rgba(255, 255, 255, .085);

  --wst-shadow-sm: 0 1px 2px rgba(0, 0, 0, .4), 0 6px 18px rgba(0, 0, 0, .3);
  --wst-shadow-lg:
    0 1px 2px rgba(0, 0, 0, .5),
    0 16px 40px -12px rgba(0, 0, 0, .55),
    0 40px 80px -40px rgba(0, 0, 0, .6);

  --bs-primary: #2b7fa6;
  --bs-primary-rgb: 43, 127, 166;
  --bs-body-color: #b0c5cf;
  --bs-body-color-rgb: 176, 197, 207;
  --bs-body-bg: #07161f;
  --bs-heading-color: #e9f6f8;
  --bs-border-color: #1d3f52;
  --bs-link-color: var(--wst-accent);
  --bs-link-color-rgb: 128, 208, 199;
  --bs-link-hover-color: var(--wst-accent-hover);
  --bs-focus-ring-color: rgba(128, 208, 199, .35);

  color-scheme: dark;
}

/* -------------------------------------------------------------------- font */
/* Montserrat is self-hosted rather than pulled from Google Fonts: same look,
   one fewer third party, and no request leaves this origin. One variable file
   covers every weight the page uses. The latin-ext file is only downloaded when
   a page actually contains a character in its range. */
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/montserrat-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/montserrat-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Bootstrap display utilities beat Reboot's [hidden] rule; restore it. */
[hidden] { display: none !important; }

/* -------------------------------------------------------------- base type */
body {
  background-color: var(--wst-bg);
  color: var(--wst-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
  color: var(--wst-ink);
  font-family: var(--wst-font-title);
  font-weight: 700;
  letter-spacing: -0.015em;
  text-wrap: balance; /* progressive enhancement: nicer line breaks */
}

h1 {
  font-size: clamp(2.05rem, 5.6vw, 3.4rem);
  line-height: 1.08;
  font-weight: 800;
  letter-spacing: -0.035em;
}
h2 { font-size: clamp(1.45rem, 3.4vw, 2.05rem); line-height: 1.18; margin-bottom: .75rem; }
h3 { font-size: clamp(1.1rem, 2.4vw, 1.3rem); line-height: 1.3; margin-bottom: .5rem; }

.lead { color: var(--wst-body); font-size: clamp(1.02rem, 2.2vw, 1.2rem); }
.text-muted-2 { color: var(--wst-muted); }

a { text-decoration-thickness: 1px; text-underline-offset: 2px; }

:focus-visible {
  outline: 3px solid var(--wst-focus-ring);
  outline-offset: 2px;
  border-radius: 6px;
}

/* Skip link */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 1080;
  background: var(--wst-blue);
  color: #fff;
  padding: .65rem 1rem;
  border-radius: 0 0 10px 0;
  font-weight: 600;
}
.skip-link:focus { left: 0; }

/* Readable line-length wrappers (kept here so no inline styles are needed and a
   strict Content-Security-Policy can forbid style attributes entirely). */
.measure-sm { max-width: 34rem; }
.measure-md { max-width: 44rem; }
.measure-lg { max-width: 48rem; }
.measure-xl { max-width: 52rem; }
/* Hidden by being zero-sized rather than display:none. The brand mark strokes
   itself with gradients declared in the sprite, and a paint server inside a
   display:none subtree is never built, so Chrome drops the reference and the
   stroked paths vanish. Zero size keeps the sprite out of the way and the
   gradients resolvable. */
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Sprite icons take their colour from whatever element hosts them. Without this
   the <path> elements inside each <symbol> fall back to the SVG default of
   black - fine on the light theme by accident, invisible on the dark one.
   Symbol content that sets its own fill (the hero pins) is unaffected, because
   a presentation attribute on the element beats an inherited value. */
svg use { fill: currentColor; }

/* ---------------------------------------------------------------- surface */
.surface {
  position: relative;
  background: var(--wst-surface);
  border: 1px solid var(--wst-line);
  border-radius: var(--wst-radius);
  box-shadow: var(--wst-shadow-sm);
}
/* Hairline highlight along the top edge - the detail that reads as depth. */
.surface::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--wst-hairline), transparent);
  pointer-events: none;
}
.surface--raised { box-shadow: var(--wst-shadow-lg); }
.surface-soft { background: var(--wst-bg-soft); }
.section-pad { padding-block: clamp(2.5rem, 6vw, 4.5rem); }

/* Ambient wash behind the hero. Purely decorative and non-interactive. */
.hero {
  position: relative;
  isolation: isolate;
  /* The decorative wash and the map wallpaper are allowed to bleed past this
     section; clip stops that bleed becoming 139px of horizontal page scroll.
     overflow-x: clip rather than hidden, because hidden would force the other
     axis to auto and make this a scroll container. */
  overflow-x: clip;
}
.hero::before {
  content: "";
  position: absolute;
  /* Was -10% on both sides. The right-hand bleed extended the page by a tenth
     of its own width, which is what let the whole site slide sideways. The
     gradients fade to transparent at 72%, so squaring this off is invisible. */
  inset: -6rem 0 auto;
  height: 40rem;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(58% 60% at 50% 0%, var(--wst-hero-a), transparent 72%),
    radial-gradient(42% 52% at 84% 12%, var(--wst-hero-b), transparent 72%);
}

/* Hero wallpaper: a map plane with Wi-Fi pins, drawn behind the hero content.
   Decorative only (aria-hidden in the markup) and masked so it fades out well
   before the speed-test card, which keeps the headline contrast untouched. */
.hero__map {
  position: absolute;
  inset: -3rem 0 auto;
  width: 100%;
  height: clamp(23rem, 52vw, 38rem);
  z-index: -1;
  pointer-events: none;
  color: var(--wst-map-ink);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 30%, rgba(0, 0, 0, .55) 58%, transparent 90%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 30%, rgba(0, 0, 0, .55) 58%, transparent 90%);
}
.hero__grid { color: var(--wst-map-grid); }
.hero__routes { opacity: .55; }
.hero__links { opacity: .6; }
.hero__rings { opacity: .5; }
.hero__pins { opacity: 1; }

/* ----------------------------------------------------------------- navbar */
.site-header {
  background: var(--wst-bg);
  border-bottom: 1px solid var(--wst-line);
  transition: background-color .2s ease, border-color .2s ease;
}
@supports (backdrop-filter: blur(1px)) {
  .site-header {
    background: color-mix(in srgb, var(--wst-bg) 78%, transparent);
    backdrop-filter: blur(14px) saturate(140%);
  }
}
.navbar-brand {
  color: var(--wst-ink);
  font-weight: 700;
  letter-spacing: -0.02em;
  display: inline-flex;
  align-items: center;
  gap: .55rem;
}
.navbar-brand:hover { color: var(--wst-accent); }
.brand-mark {
  width: 34px;
  height: 24px;
  flex: 0 0 34px;
  /* The needle and hub; the arcs and the dial carry their own gradients. */
  color: var(--wst-brand-ink);
}
/* The wordmark sets its own colour so the footer's link colour cannot mute it. */
.brand-name { color: var(--wst-brand-ink); white-space: nowrap; }
.brand-name__accent { color: var(--wst-brand-accent); }
.navbar-brand:hover .brand-name,
.navbar-brand:hover .brand-name__accent { color: var(--wst-accent); }
.navbar .nav-link {
  color: var(--wst-body);
  font-weight: 500;
  padding-inline: .85rem;
  border-radius: 8px;
}
.navbar .nav-link:hover,
.navbar .nav-link:focus { color: var(--wst-accent); }
.navbar-toggler { border-color: var(--wst-line); padding: .35rem .55rem; }

/* ------------------------------------------------------------------- btns */
.btn {
  --bs-btn-font-weight: 600;
  --bs-btn-padding-y: .625rem;
  --bs-btn-padding-x: 1.15rem;
  border-radius: 999px;
  transition: transform .16s ease, box-shadow .16s ease, background-color .16s ease;
}
.btn-primary {
  --bs-btn-bg: var(--wst-blue);
  --bs-btn-border-color: var(--wst-blue);
  --bs-btn-hover-bg: var(--wst-blue-hover);
  --bs-btn-hover-border-color: var(--wst-blue-hover);
  --bs-btn-active-bg: var(--wst-blue-active);
  --bs-btn-active-border-color: var(--wst-blue-active);
  --bs-btn-disabled-bg: var(--wst-blue-disabled);
  --bs-btn-disabled-border-color: var(--wst-blue-disabled);
  --bs-btn-disabled-color: var(--wst-blue-disabled-text);
  /* Bootstrap dims disabled buttons to 65%; the colours above already carry
     enough contrast, so keep the label readable instead. */
  --bs-btn-disabled-opacity: 1;
  box-shadow: 0 6px 18px -6px var(--wst-glow);
}
.btn-primary:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -8px var(--wst-glow);
}
.btn-primary:disabled { box-shadow: none; }

/* ------------------------------------------------------------------ hero */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  margin: 0 0 1.15rem;
  padding: .4rem .9rem .4rem .75rem;
  border: 1px solid var(--wst-line);
  border-radius: 999px;
  background: var(--wst-surface);
  box-shadow: var(--wst-shadow-sm);
  font-size: .82rem;
  font-weight: 600;
  color: var(--wst-body);
}
.eyebrow__dot {
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  background: var(--wst-live);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--wst-live) 22%, transparent);
  flex: 0 0 auto;
}

/* ------------------------------------------------------------------ gauge */
.speedtest-card { padding: clamp(1.35rem, 4vw, 2.75rem); }

.gauge {
  position: relative;
  width: min(340px, 100%);
  aspect-ratio: 1 / 1;
  margin-inline: auto;
}
/* Ambient glow that lifts while a transfer phase is running. Animating only
   opacity keeps this off the layout and paint path. */
.gauge::before {
  content: "";
  position: absolute;
  inset: 6%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, var(--wst-glow), transparent 64%);
  opacity: 0;
  transition: opacity .55s ease;
  pointer-events: none;
}
.gauge[data-phase="download"]::before,
.gauge[data-phase="upload"]::before,
.gauge[data-phase="complete"]::before { opacity: 1; }

.gauge__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.gauge__well { fill: url(#gaugeWell); }
.gauge__track {
  fill: none;
  stroke: var(--wst-gauge-track);
  stroke-width: 16;
  stroke-linecap: round;
}
.gauge__progress {
  fill: none;
  stroke: url(#gaugeGradient);
  stroke-width: 16;
  stroke-linecap: round;
  stroke-dasharray: 631.46 841.95;
  stroke-dashoffset: 631.46;
}
.gauge__ticks line {
  stroke: var(--wst-gauge-tick);
  stroke-width: 2;
  stroke-linecap: round;
}
.gauge__labels text {
  fill: var(--wst-muted);
  font-size: 12px;
  font-weight: 600;
  text-anchor: middle;
  font-variant-numeric: tabular-nums;
}
.gauge__dot {
  transform-box: view-box;
  transform-origin: 182px 182px;
  transform: rotate(0deg);
  will-change: transform;
}
.gauge__dot circle {
  fill: var(--wst-blue);
  stroke: var(--wst-surface);
  stroke-width: 3;
}

.gauge__center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 22%;
}
.gauge__phase {
  margin: 0 0 .25rem;
  font-size: .76rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--wst-muted);
  min-height: 1.2em;
}
.gauge__value {
  margin: 0;
  font-size: clamp(2.7rem, 11.5vw, 3.9rem);
  font-weight: 800;
  line-height: 1.02;
  color: var(--wst-ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.04em;
}
.gauge__unit {
  margin: .2rem 0 0;
  font-size: .95rem;
  font-weight: 600;
  color: var(--wst-muted);
}

/* GO button sits in the middle of the ring before the test starts */
.go-btn {
  position: relative;
  width: clamp(116px, 35%, 144px);
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  border: 0;
  background: linear-gradient(155deg, var(--wst-blue), var(--wst-blue-hover));
  color: #fff;
  font-size: 1.65rem;
  font-weight: 800;
  letter-spacing: .08em;
  box-shadow:
    0 14px 32px -8px var(--wst-glow),
    inset 0 1px 0 rgba(255, 255, 255, .22);
  transition: transform .18s ease, box-shadow .18s ease, filter .18s ease;
}
/* Slow halo so the primary action reads as live without being busy. */
.go-btn::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--wst-blue);
  opacity: 0;
  animation: wst-pulse 2.8s ease-out infinite;
  pointer-events: none;
}
@keyframes wst-pulse {
  0% { transform: scale(1); opacity: .5; }
  70% { transform: scale(1.28); opacity: 0; }
  100% { transform: scale(1.28); opacity: 0; }
}
.go-btn:hover {
  transform: scale(1.03);
  filter: brightness(1.06);
  box-shadow: 0 18px 40px -10px var(--wst-glow), inset 0 1px 0 rgba(255, 255, 255, .22);
}
.go-btn:active { transform: scale(.98); }

/* Theme switch: sits outside the collapsible menu so it stays reachable on
   mobile without opening the hamburger. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--wst-line);
  border-radius: 50%;
  background: transparent;
  color: var(--wst-body);
  transition: background-color .18s ease, color .18s ease, border-color .18s ease;
}
.theme-toggle:hover {
  background: var(--wst-bg-soft);
  color: var(--wst-accent);
  border-color: var(--wst-accent);
}

/* ---------------------------------------------------------- live readouts */
.metrics {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .75rem;
  margin-top: 1.85rem;
}
.metric {
  background: linear-gradient(180deg, var(--wst-surface-2), var(--wst-bg-soft));
  border: 1px solid var(--wst-line);
  border-radius: 16px;
  padding: .95rem .75rem;
  text-align: center;
}
.metric__label {
  margin: 0 0 .3rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .35rem;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wst-muted);
}
.metric__value {
  margin: 0;
  font-size: clamp(1.3rem, 4.6vw, 1.7rem);
  font-weight: 700;
  color: var(--wst-ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.metric__unit {
  font-size: .8rem;
  font-weight: 600;
  color: var(--wst-muted);
  margin-left: .15rem;
}
@media (min-width: 576px) {
  .metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.status-line {
  margin-top: 1.15rem;
  min-height: 1.5rem;
  font-weight: 600;
  color: var(--wst-accent);
}

/* ---------------------------------------------------------------- results */
.result-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .75rem;
}
@media (min-width: 768px) {
  .result-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
.result-tile {
  position: relative;
  border: 1px solid var(--wst-line);
  border-radius: 18px;
  padding: 1.1rem 1.15rem 1.2rem;
  background: linear-gradient(180deg, var(--wst-surface-2), var(--wst-bg-soft));
  overflow: hidden;
}
/* A hairline of the metric's own colour along the top edge, so the four tiles
   read as four different things at a glance. */
.result-tile::after {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 3px;
  background: var(--metric-ink);
  opacity: .85;
}
.result-tile__label {
  margin: 0;
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wst-muted);
  display: flex;
  align-items: center;
  gap: .55rem;
}
.result-tile__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: 50%;
  background: var(--metric-bg);
  color: var(--metric-ink);
}
.result-tile__value {
  margin: .55rem 0 0;
  font-size: clamp(1.75rem, 5.5vw, 2.3rem);
  font-weight: 800;
  color: var(--wst-ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  letter-spacing: -0.03em;
}
.result-tile__unit {
  font-size: .85rem;
  font-weight: 600;
  color: var(--wst-muted);
  margin-left: .25rem;
}

/* Per-metric accent. Colour is decorative reinforcement only - every tile keeps
   its text label and icon. */
.result-tile[data-metric="ping"]     { --metric-ink: #4338ca; --metric-bg: #eef2ff; }
.result-tile[data-metric="download"] { --metric-ink: #1d4ed8; --metric-bg: #eff5ff; }
.result-tile[data-metric="upload"]   { --metric-ink: #0f766e; --metric-bg: #e6fbf8; }
.result-tile[data-metric="jitter"]   { --metric-ink: #92650a; --metric-bg: #fff6e5; }

:root[data-theme="dark"] .result-tile[data-metric="ping"]     { --metric-ink: #a5b4fc; --metric-bg: #2a3260; }
:root[data-theme="dark"] .result-tile[data-metric="download"] { --metric-ink: #93b8ff; --metric-bg: #1e3564; }
:root[data-theme="dark"] .result-tile[data-metric="upload"]   { --metric-ink: #5eead4; --metric-bg: #134442; }
:root[data-theme="dark"] .result-tile[data-metric="jitter"]   { --metric-ink: #f5c86b; --metric-bg: #42351a; }

/* ------------------------------------------------------- results card shell */
.results-card { padding: clamp(1.15rem, 3vw, 1.75rem); }
.results-card__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin-bottom: 1.15rem;
}
.results-card__actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 1.15rem;
}
@media (max-width: 575.98px) {
  .results-card__actions { justify-content: stretch; }
  .results-card__actions .btn { width: 100%; }
}

/* The rating drives one token set, so the badge, the meter fill and the summary
   panel can never disagree about what colour "Average" is. */
#results[data-rating="excellent"]  { --rating-ink: #11633c; --rating-bg: #e7f8ee; --rating-line: #bfe8d2; }
#results[data-rating="good"]       { --rating-ink: #1a4bb8; --rating-bg: #eff5ff; --rating-line: #cadcfb; }
#results[data-rating="average"]    { --rating-ink: #8a5a06; --rating-bg: #fff5e2; --rating-line: #f6e0b5; }
#results[data-rating="slow"]       { --rating-ink: #9a4413; --rating-bg: #fff0e6; --rating-line: #f8d5bd; }
#results[data-rating="very-slow"]  { --rating-ink: #a32020; --rating-bg: #fdecec; --rating-line: #f6c9c9; }

:root[data-theme="dark"] #results[data-rating="excellent"] { --rating-ink: #69dfa4; --rating-bg: #0f2a1f; --rating-line: #1d4634; }
:root[data-theme="dark"] #results[data-rating="good"]      { --rating-ink: #9dc2ff; --rating-bg: #132340; --rating-line: #24406e; }
:root[data-theme="dark"] #results[data-rating="average"]   { --rating-ink: #f0c274; --rating-bg: #2c2412; --rating-line: #4d3d17; }
:root[data-theme="dark"] #results[data-rating="slow"]      { --rating-ink: #f2ad83; --rating-bg: #301f12; --rating-line: #55361a; }
:root[data-theme="dark"] #results[data-rating="very-slow"] { --rating-ink: #f4a1a1; --rating-bg: #301717; --rating-line: #552626; }

.rating {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  border-radius: 999px;
  padding: .45rem .95rem;
  font-weight: 700;
  font-size: .95rem;
  background: var(--rating-bg);
  color: var(--rating-ink);
  border: 1px solid var(--rating-line);
}
.rating__icon { flex: 0 0 auto; }

/* --------------------------------------------------------- quality summary */
.quality {
  display: flex;
  align-items: flex-start;
  gap: .95rem;
  margin-top: 1.15rem;
  padding: .95rem 1.1rem;
  border: 1px solid var(--rating-line);
  border-radius: 16px;
  background: var(--rating-bg);
}
.quality__meter {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 3px;
  width: 92px;
  flex: 0 0 92px;
  margin-top: .3rem;
}
.quality__step {
  height: 7px;
  border-radius: 3px;
  background: var(--rating-ink);
  opacity: .22;
}
.quality__step.is-on { opacity: 1; }
.quality__text { color: var(--wst-body); }

@media (max-width: 575.98px) {
  .quality { flex-direction: column; gap: .6rem; }
  .quality__meter { margin-top: 0; }
}

/* --------------------------------------------------------- free wifi promo */
.wifi-promo__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  flex: 0 0 46px;
  border-radius: 14px;
  background: var(--wst-blue-soft);
  color: var(--wst-accent);
}

/* ------------------------------------------------------------- info cards */
.info-card__label {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wst-muted);
}
.info-card__value {
  font-weight: 600;
  color: var(--wst-ink);
  word-break: break-word;
}

/* ------------------------------------------------------------ SEO content */
.prose p { max-width: 72ch; }
.prose ul { max-width: 72ch; }
.prose li { margin-bottom: .4rem; }
.prose h2 { margin-top: 2.75rem; }
.prose h3 { margin-top: 1.75rem; }
.prose h2:first-child { margin-top: 0; }

.table-speeds {
  --bs-table-bg: var(--wst-surface);
  --bs-table-color: var(--wst-body);
  --bs-table-border-color: var(--wst-line);
  border-radius: 16px;
  overflow: hidden;
  box-shadow: var(--wst-shadow-sm);
}
.table-speeds th { color: var(--wst-ink); font-weight: 700; }
.table-speeds td, .table-speeds th { padding: .9rem 1.1rem; vertical-align: middle; }
.table-speeds thead th {
  background: var(--wst-bg-soft);
  border-bottom-color: var(--wst-line);
}

/* --------------------------------------------------------------- accordion */
.accordion {
  --bs-accordion-btn-focus-box-shadow: 0 0 0 .25rem rgba(37, 99, 235, .2);
  --bs-accordion-active-bg: var(--wst-blue-soft);
  --bs-accordion-active-color: var(--wst-ink);
  --bs-accordion-border-color: var(--wst-line);
  --bs-accordion-border-radius: 16px;
  --bs-accordion-btn-padding-y: 1.05rem;
  --bs-accordion-bg: var(--wst-surface);
}
/* Separate cards rather than one seam - Bootstrap collapses the radii and drops
   the top border on stacked items, so both are restored here. */
.accordion-item {
  margin-bottom: .625rem;
  border-radius: var(--bs-accordion-border-radius) !important;
  border-top: 1px solid var(--bs-accordion-border-color) !important;
  overflow: hidden;
  box-shadow: var(--wst-shadow-sm);
}
.accordion-item:last-child { margin-bottom: 0; }
.accordion-button { font-weight: 600; color: var(--wst-ink); }
.accordion-button:not(.collapsed) { box-shadow: none; }

/* ------------------------------------------------------------------ footer */
.site-footer {
  background: var(--wst-bg-soft);
  border-top: 1px solid var(--wst-line);
}
.site-footer a { color: var(--wst-body); text-decoration: none; }
.site-footer a:hover { color: var(--wst-accent); text-decoration: underline; }
/* Comfortable tap targets for the footer link lists on touch screens. */
.site-footer nav a {
  display: inline-block;
  padding-block: .375rem;
}
.footer-link__icon {
  margin-right: .5rem;
  flex: 0 0 auto;
  vertical-align: -2px;
  color: var(--wst-muted);
  transition: color .18s ease;
}
.site-footer nav a:hover .footer-link__icon { color: var(--wst-accent); }

.footer-heading {
  margin-bottom: .6rem;
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--wst-muted);
}

/* A footer does not need the full section rhythm - that is what made this one
   598px tall. */
.site-footer { padding-block: clamp(2rem, 4vw, 3rem); }

.footer-tagline {
  margin: .6rem 0 0;
  max-width: 24rem;
  font-size: .875rem;
  color: var(--wst-muted);
}

.footer-links { list-style: none; margin: 0; padding: 0; font-size: .9rem; }

/* The bottom line replaces an <hr> plus its margins: same separation, less
   height, and room for the attribution beside the copyright. */
.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem 1.5rem;
  align-items: baseline;
  justify-content: space-between;
  margin-top: 2rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--wst-line);
  font-size: .82rem;
  color: var(--wst-muted);
}
.footer-sources { font-size: .78rem; }

/* ------------------------------------------------------- reduced motion */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .go-btn::after { animation: none; opacity: 0; }
  .go-btn:hover { transform: none; filter: none; }
  .btn-primary:hover:not(:disabled) { transform: none; }
}


/* ==========================================================================
   Theme layer - TemplateMo 590 "Topic Listing" visual language
   Deep teal #13547a and mint #80d0c7 on an aliceblue ground, Montserrat
   headings, pill buttons, generous section padding.

   One deliberate departure from the template: it fills buttons with the mint
   and writes on them in white, which is 1.9:1 and unreadable for many people.
   Mint surfaces here use --wst-mint-ink (a deep teal) instead, which keeps the
   colour and passes AA.
   ========================================================================== */

.navbar-brand,
.nav-link,
.btn,
.filter-chip,
.chip,
.status-badge,
.stat-grid dt,
.globe-panel__title,
.filter-group__title { font-family: var(--wst-font-title); }

/* Uppercase, letter-spaced navigation, straight from the template. */
.navbar-nav .nav-link {
  font-size: .875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .5px;
}
.navbar-brand { font-weight: 700; letter-spacing: -.01em; }

/* Pill buttons everywhere. */
.btn {
  --bs-btn-border-radius: var(--wst-radius-pill);
  border-radius: var(--wst-radius-pill);
  font-weight: 600;
}
.btn-lg { --bs-btn-border-radius: var(--wst-radius-pill); padding-inline: 1.6rem; }

/* The mint call-to-action. Dark ink on mint, not white. */
.btn-mint {
  --bs-btn-bg: var(--wst-mint);
  --bs-btn-border-color: var(--wst-mint);
  --bs-btn-color: var(--wst-mint-ink);
  --bs-btn-hover-bg: var(--wst-mint-hover);
  --bs-btn-hover-border-color: var(--wst-mint-hover);
  --bs-btn-hover-color: var(--wst-mint-ink);
  --bs-btn-active-bg: var(--wst-mint-hover);
  --bs-btn-active-color: var(--wst-mint-ink);
  --bs-btn-disabled-bg: var(--wst-mint);
  --bs-btn-disabled-border-color: var(--wst-mint);
  --bs-btn-disabled-color: var(--wst-mint-ink);
}

/* A section on the pale-blue ground, the template's alternating rhythm. */
.section-bg { background-color: var(--wst-bg-soft); }

/* The signature diagonal. Text on it is always white or --wst-mint-ink. */
.gradient-header {
  background-image: var(--wst-gradient);
  color: #fff;
}
.gradient-header h1,
.gradient-header h2,
.gradient-header p,
.gradient-header .breadcrumb-item,
.gradient-header .breadcrumb-item a,
.gradient-header .breadcrumb-item + .breadcrumb-item::before { color: #fff; }
.gradient-header .breadcrumb-item a:hover { color: var(--wst-mint); }
.gradient-header .lead { color: rgba(255, 255, 255, .92); }

/* Cards pick up the template's 20px corners. */
.card, .stat-card, .cable-card, .route-result { border-radius: var(--wst-radius); }

/* Dropdown navigation for the Internet Cables menu. */
.navbar .dropdown-menu {
  border: 1px solid var(--wst-line);
  border-radius: var(--wst-radius-sm);
  box-shadow: var(--wst-shadow-lg);
  padding: .4rem;
}
.navbar .dropdown-item {
  border-radius: 8px;
  font-family: var(--wst-font-title);
  font-size: .875rem;
  font-weight: 500;
  padding: .5rem .75rem;
}
.navbar .dropdown-item:hover,
.navbar .dropdown-item:focus { background: var(--wst-blue-soft); color: var(--wst-accent); }
.navbar .dropdown-item.active,
.navbar .dropdown-item:active { background: var(--wst-blue-soft); color: var(--wst-accent); }

/* A shorter section rhythm for the page headers on inner pages. */
.section-pad-sm { padding-block: clamp(1.75rem, 4vw, 3rem); }

/* Sortable table headers: a real button so the keyboard reaches them. */
th[data-sort] button {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-weight: 700;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: .3rem;
}
th[data-sort] button::after { content: "\2195"; opacity: .35; font-size: .85em; }
th[aria-sort="ascending"] button::after { content: "\2191"; opacity: 1; }
th[aria-sort="descending"] button::after { content: "\2193"; opacity: 1; }
th[data-sort] button:focus-visible { outline: 3px solid var(--wst-focus-ring); outline-offset: 2px; }

/* Navigation icons. Decorative - every link keeps its text label, which is what
   screen readers and search engines read, so the icons are aria-hidden. */
.nav-link__icon,
.dropdown-item__icon {
  vertical-align: -.15em;
  margin-right: .4rem;
  /* Only a slight step back from the label. Anything dimmer washes out against
     the dark theme's background, where the label colour is already muted. */
  opacity: .85;
  transition: opacity .2s;
}
.nav-link:hover .nav-link__icon,
.nav-link.active .nav-link__icon,
.dropdown-item:hover .dropdown-item__icon,
.dropdown-item.active .dropdown-item__icon { opacity: 1; }

/* On a phone the bar becomes a stacked list, where the icons line the labels up. */
@media (max-width: 991.98px) {
  .navbar-nav .nav-link { display: flex; align-items: center; }
  .nav-link__icon { margin-right: .55rem; }
}

@media (prefers-reduced-motion: reduce) {
  .nav-link__icon,
  .dropdown-item__icon { transition: none; }
}

/* ================================================================= comments
   Per-page visitor comments. Comment text is written with textContent by
   assets/js/comments.js and styled with white-space: pre-line, so line breaks
   survive but nothing a visitor types is ever parsed as markup.
   ========================================================================= */
.comments { background: var(--wst-bg-soft); border-top: 1px solid var(--wst-line); }
.comments__inner { max-width: 46rem; margin-inline: auto; }

.comments__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem 1rem;
  margin-bottom: 1.25rem;
}
.comments__head h2 { margin: 0; font-size: clamp(1.35rem, 3vw, 1.75rem); }
.comments__count { margin: 0; color: var(--wst-muted); font-size: .9rem; }

.comments__form {
  background: var(--wst-surface);
  border: 1px solid var(--wst-line);
  border-radius: var(--wst-radius);
  padding: 1.25rem;
  box-shadow: var(--wst-shadow-sm);
}
.comments__replying {
  margin: 0 0 .85rem;
  padding: .5rem .75rem;
  border-radius: var(--wst-radius-sm);
  background: var(--wst-blue-soft);
  color: var(--wst-accent);
  font-size: .875rem;
}
.comments__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem 1.25rem;
  margin-top: 1rem;
}
.comments__note {
  margin: 0;
  flex: 1 1 18rem;
  font-size: .78rem;
  color: var(--wst-muted);
}
.comments__status { margin: .85rem 0 0; font-size: .875rem; }

/* Off-screen rather than display:none, because a bot that skips hidden fields
   would skip a display:none trap too. */
.comments__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.comment-list { list-style: none; margin: 2rem 0 0; padding: 0; }
.comment {
  display: flex;
  gap: .85rem;
  padding: 1rem 0;
  border-top: 1px solid var(--wst-line);
}
.comment--reply { margin-left: 2.75rem; }
.comment--reply .comment__avatar { width: 30px; height: 30px; flex-basis: 30px; font-size: .7rem; }

.comment__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 38px;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  font-family: var(--wst-font-title);
  font-size: .8rem;
  font-weight: 700;
  color: #fff;
  background: var(--wst-blue);
}
/* A fixed palette, picked by a hash of the name, so the same person keeps the
   same colour down a thread. Classes rather than inline styles: the site's CSP
   blocks style attributes. */
.comment__avatar--0 { background: #13547a; }
.comment__avatar--1 { background: #1d7a6c; }
.comment__avatar--2 { background: #7a5213; }
.comment__avatar--3 { background: #5c3a7a; }
.comment__avatar--4 { background: #7a1d3f; }
.comment__avatar--5 { background: #2a6e9e; }
.comment__avatar--6 { background: #3f7a1d; }
.comment__avatar--7 { background: #7a3d13; }

.comment__main { min-width: 0; flex: 1 1 auto; }
.comment__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .25rem .6rem;
  margin: 0 0 .3rem;
}
.comment__name { font-weight: 700; color: var(--wst-ink); }
.comment__time { font-size: .78rem; color: var(--wst-muted); }
.comment__body {
  margin: 0;
  /* Keeps the writer's line breaks; still plain text, never markup. */
  white-space: pre-line;
  overflow-wrap: anywhere;
}
.comment__reply {
  margin-top: .5rem;
  padding: 0;
  border: 0;
  background: none;
  font-size: .82rem;
  font-weight: 600;
  color: var(--wst-accent);
  cursor: pointer;
}
.comment__reply:hover { text-decoration: underline; }
.comment__reply:focus-visible { outline: 3px solid var(--wst-focus-ring); outline-offset: 2px; }

.comment-empty {
  padding: 1.5rem 0;
  color: var(--wst-muted);
  border-top: 1px solid var(--wst-line);
}

/* ------------------------------------------------------------- site map
   The /sitemap/ page. The long lists flow into as many columns as the
   viewport has room for, so a 167-cable list stays one screen of reading
   rather than one screen of scrolling. */

.sitemap-heading {
  margin: 0 0 .6rem;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--wst-muted);
}

.sitemap-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: .92rem;
}
.sitemap-list li { margin-bottom: .4rem; break-inside: avoid; }
.sitemap-list a { text-decoration: none; }
.sitemap-list a:hover { text-decoration: underline; }
.sitemap-list .flag { margin-right: .4rem; vertical-align: -.2em; }
.sitemap-list__meta {
  margin-left: .4rem;
  font-size: .8rem;
  color: var(--wst-muted);
}

.sitemap-list--flow { columns: 15rem auto; column-gap: 2rem; }

.sitemap-top { padding-bottom: 1.75rem; border-bottom: 1px solid var(--wst-line); }

.sitemap-block { margin-top: 2.5rem; }
.sitemap-block__title { display: flex; align-items: baseline; gap: .6rem; margin-bottom: .35rem; }
.sitemap-block__count {
  font-size: .78rem;
  font-weight: 600;
  color: var(--wst-muted);
  letter-spacing: .04em;
}
.sitemap-block__note { margin-bottom: 1.1rem; font-size: .9rem; color: var(--wst-muted); }

/* --------------------------------------------------------- footer sharing
   Two share buttons under the footer tagline. They post the page you are on,
   not the home page: share.js rewrites the hrefs, which ship pointing at the
   site root so the buttons still work with JavaScript off. */

.footer-share__title { margin-top: 1.4rem; }

.footer-share {
  display: flex;
  gap: .5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.footer-share__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 1px solid var(--wst-line);
  border-radius: 50%;
  color: var(--wst-body);
  transition: color .15s ease, background-color .15s ease, border-color .15s ease;
}
/* The footer underlines its links on hover; a round icon button must not. */
.site-footer .footer-share__btn:hover,
.site-footer .footer-share__btn:focus-visible {
  color: #fff;
  text-decoration: none;
}
.site-footer .footer-share__btn--facebook:hover,
.site-footer .footer-share__btn--facebook:focus-visible {
  background: #1877f2;
  border-color: #1877f2;
}
.site-footer .footer-share__btn--reddit:hover,
.site-footer .footer-share__btn--reddit:focus-visible {
  background: #ff4500;
  border-color: #ff4500;
}
.footer-share__btn:focus-visible { outline: 3px solid var(--wst-focus-ring); outline-offset: 2px; }
