/* Use common system fonts instead of Roboto for a more natural look */

/* OVH: the frame must never scroll. The document is exactly one viewport tall,
   laid out as a column whose flexible child is the gauge app and whose fixed
   footer is the credits, so there is nothing to overflow. Upstream left the
   credits *after* a 100vh app, making the page 100vh + credits tall; because
   the stylesheet also hides scrollbars, that overflow was invisible but a
   wheel over the gauge (or an elastic scroll on macOS) shifted the whole UI
   and revealed blank space above the dial. */
html {
  height: 100%;
  overflow: hidden;
}

body {
  margin: 0px;
  padding: 0px;
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

::-webkit-scrollbar {
  display: none;
}
html {
  -ms-overflow-style: none; 
  scrollbar-width: none;
}
 
.Credits {
/* OVH: was rgb(125 119 119) -> 4.40:1 on white, under the 4.5:1 AA floor. */
color: #787272;
text-align: center;
/* OVH: 14px -> 11px, so the attribution line reads a touch smaller without
   losing legibility. Re-apply on OpenSpeedTest upgrade. */
font-size:11px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-weight: 500;
/* OVH: flex child so it keeps its own height and the app fills the rest. */
flex: 0 0 auto;
padding: 4px 12px 6px;
} 
.Credits a {
  text-decoration: none;
  color: rgb(113, 113, 113);
}
.Credits a:hover {
color: #0050d7;
}

.ConnectError {
  display: none;

}

.openSpeedtestApp {
 
  /* OVH: was 100vh — the full iframe — with the .Credits block laid out *below*
     it, so the document was always ~35px taller than the frame and scrolled
     internally. Worse, this stylesheet hides scrollbars, so the overflow was
     invisible. Now the flex column above reserves the credits' own height and
     this element takes the rest (height: auto + flex-grow), so the SVG fills
     the available box exactly and the whole frame stays 100vh. */
  flex: 1 1 auto;
  min-height: 0;
  height: auto;
  width: 100%;
  display: none;
  overflow: hidden;
}
.main-Gaugebg {
  fill: none;
  stroke: rgb(231, 231, 232);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 22px;
  stroke-dasharray: 681;
}
.main-GaugeBlue {
  fill: none;
  stroke: url(#gradient);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 22px;
  stroke-dasharray: 681;
  stroke-opacity: 0;
}
.main-GaugeWhite {
  fill: none;
  stroke: rgb(255, 255, 255);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 15px;
  stroke-dasharray: 0, 681;
  stroke-dashoffset: 1;
  stroke-opacity: 0;
}
.oDo-Meter {
  font-size: 16.633283615112305px;
  fill: gray;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
}
.oDoLive-Speed {
  font-size: 28px;
  fill: #201e1e;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: middle;
}

.oDoLive-Status {
  font-size: 10px;
  fill: #d2d1d2;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: middle;
}
.uiBg {
  fill: #d2d1d2;
}
.progressbg {
  stroke: rgb(231, 231, 232);
  stroke-width: 8px;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 400;
  stroke-dashoffset: 0;
}
.Cards {
  fill: var(--rcCard, #f2f2f2);
}
.Symbol {
  fill: var(--rcArrow, url(#gradient));
}
.rtext {
  font-size: 12px;
  fill: var(--rcCaption, #333);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
}
/* OVH: the value and the unit live in the SAME symbol (downResultC1 and
   friends hold both, and jitterMob holds the whole PING block), so the only
   thing that can size them apart is the <use> they are cloned through: both
   inherit its font-size, and these two rules then split it. The value takes
   the whole inherited size (1em) and the unit a fraction of it, which keeps
   upstream's 23:12 ratio at whatever size the layout asks for.
   That indirection is what the mobile layout needs. Its cards are 88.1 units
   wide against the desktop card's 278.1, but both drew the value at 23px: a
   six-digit reading measured 85.9 units — 97.5% of the mobile card, touching
   both edges ("some text size for value or unit are a bit too big", 2026-09-11).
   The per-layout sizes are set on the uses just below. Re-apply on upgrade. */
.rtextnum {
  font-size: 1em;
  fill: var(--rcValue, #201e1e);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: middle;
}
.rtextmbms {
  font-size: 0.522em;   /* 12/23 of the value, as upstream drew it */
  fill: var(--rcUnit, #5f5f5f);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: middle;
}
.jitter-Mob {
  font-size: 0.391em;   /* 9/23 */
  fill: #5f5f5f;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: middle;
}

/* The reading size per layout. Desktop is upstream's 23px; the mobile layout
   drops to 17px, which puts a six-digit value at ~72% of its card (63 units
   of 88.1) instead of 97.5%, with the unit following at 0.522em = 8.9px.
   Attribute selectors are used because the result <use>s carry no class of
   their own; `[*|href]` matches xlink:href in every namespace. */
#UI-Desk use[*|href="#downResultC1"],
#UI-Desk use[*|href="#upResultC2"],
#UI-Desk use[*|href="#pingResultC3"],
#UI-Desk use[*|href="#jitterResultC3"],
#UI-Mob use[*|href="#downResultC1"],
#UI-Mob use[*|href="#upResultC2"],
#UI-Mob use[*|href="#jitterMob"] {
  font-size: 17px;
}
#UI-Desk use[*|href="#downResultC1"],
#UI-Desk use[*|href="#upResultC2"],
#UI-Desk use[*|href="#pingResultC3"],
#UI-Desk use[*|href="#jitterResultC3"] {
  font-size: 23px;
}
.startButton {
  fill: url(#RadialGradient1);
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
  pointer-events: visible;
}
.buttonTxt {
  font-size: 40px;
  fill: #ffffff;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: middle;
}

.intro-Progress {
  stroke: #0050d7;
  stroke-width: 8px;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 400;
  stroke-dashoffset: 0;
}
.progressElmstart {
  stroke: #0050d7;
  stroke-width: 8px;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 400;
  stroke-dashoffset: 0;
  display: block;
}
.Startsettings {
  fill: url(#RadialGradient1);
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  -webkit-tap-highlight-color: transparent;
  cursor: pointer;
  pointer-events: visible;
  opacity: 0.1;
  transition: opacity 1s ease-in-out;
}
.Startsettings:hover {
  opacity: 1;
}

.progressbg {
  stroke: rgb(231, 231, 232);
  stroke-width: 8px;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 400;
  stroke-dashoffset: 0;
}
.deskStart {
  fill: none;
  stroke: rgb(231, 231, 232);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 22px;
  stroke-dasharray: 681;
  stroke: url(#gradient);
}
#UI-Desk {
  display: none;
}
#UI-Mob {
  display: none;
}
.oDoTop-Speed {
  font-size: 16.96px;
  fill: gray;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: end;
}
#upSymbolDesk {
  fill: #000e9c;
  display: none;
}
#downSymbolDesk {
  fill: #000e9c;
  display: none;
}
#upSymbolMob {
  fill: #000e9c;
  display: none;
}
#downSymbolMob {
  fill: #000e9c;
  display: none;
}

#ipMob {
  font-size: 15px;
  fill: #201e1e;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: middle;
  display: none;
}
#ipDesk {
  font-size: 15px;
  fill: #201e1e;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-anchor: middle;
  display: none;
}

.spinner {
  position: absolute;
  z-index: 999;
  top: 50vh;
  left: 50vw;
  text-align: center;
}

.spinner > div {
  width: 20px;
  height: 20px;
  background-color: #000e9c;

  border-radius: 100%;
  display: inline-block;
  -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
  animation: sk-bouncedelay 1.4s infinite ease-in-out both;
}

.spinner .bounce1 {
  -webkit-animation-delay: -0.32s;
  animation-delay: -0.32s;
}

.spinner .bounce2 {
  -webkit-animation-delay: -0.16s;
  animation-delay: -0.16s;
}

@-webkit-keyframes sk-bouncedelay {
  0%,
  80%,
  100% {
    -webkit-transform: scale(0);
  }
  40% {
    -webkit-transform: scale(1);
  }
}

@keyframes sk-bouncedelay {
  0%,
  80%,
  100% {
    -webkit-transform: scale(0);
    transform: scale(0);
  }
  40% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }
}

.darkmode {
  margin-bottom: -15px;
  fill: #75757a99;
  padding-top: 16px;
  display: none;
  cursor: pointer;
  margin-right: 30px;
}
#daymode {
  margin-right: 40px;
}
.darkmode:hover {
  fill: #000000;
}

/* The nested SVG viewports fill their parent (the flexed .openSpeedtestApp)
   rather than the viewport. With 100vh they were sized to the *frame*, one
   viewport taller than the box the credits had left, and the SVG then scaled
   to that bigger box — the dial was pushed down and the extra height became
   the top blank the wheel could reveal. Height 100% ties the scale to the box
   it actually occupies. */
.Mobile,
.Desktop {
  visibility: hidden;
  width: 100%;
  height: 100%;
}

@media only screen and (orientation: landscape) {
  .Mobile {
    visibility: hidden;
  }
  .Desktop {
    visibility: visible;
  }
}

@media only screen and (orientation: portrait) {
  .spinner {
    top: 42vh;
    left: 42vw;
  }
  .Mobile {
    visibility: visible;
  }
  .Desktop {
    visibility: hidden;
  }
}
@media only screen and (max-width: 300px) {
  .Credits{
    display: none;
  }
}

/* OVH: the text that replaces the logo, and the gauge gradients, carry the
   brand colours in the light skin. darkmode.css lifts them for the dark
   ground — same split as .Credits. */
.ostStartHint {
  fill: #000e9c;
}

/* OVH: the ping icon (ODS 'refresh') used to carry an inline fill, so the light
   and dark skins could not differ. Brand value here; darkmode.css lifts it. */
.pingIc {
  fill: var(--rcIcon, #000e9c);
}

/* OVH: the OVHcloud wordmark shown under the "Ready" hint. Brand navy on the
   light ground, the lifted blue in darkmode.css. */
.ovhLogo {
  fill: #000e9c;
}

/* ============================================================================
   OVH: motion. Upstream only cross-faded whole groups, so a run felt static —
   the gauge moved, but the numbers and cards appeared with no life of their
   own. These are presentation-only (no measurement code is touched) and are
   all dropped under prefers-reduced-motion.
   ========================================================================== */

/* The three result cards rise in one after another when the results are shown.
   #UI-Desk / #UI-Mob go from display:none to block, which restarts CSS
   animations, so every run gets the reveal. The x/y are the upstream
   coordinates in app.svg, used to stagger desktop (by y) and mobile (by x). */
@keyframes cardIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}
use.Cards { animation: cardIn 520ms cubic-bezier(.22,.61,.36,1) both; }
use.Cards[y="31"],    use.Cards[x="1"]     { animation-delay: 0ms; }
use.Cards[y="129.3"], use.Cards[x="104.1"] { animation-delay: 90ms; }
use.Cards[y="228.5"], use.Cards[x="207.1"] { animation-delay: 180ms; }

/* A soft glow on the live gauge arc and the run's progress line, so the eye has
   something to follow while the needle sweeps. The arc has stroke-opacity 0 at
   rest, so there is no glow until a run actually starts. */
.main-GaugeBlue {
  filter: drop-shadow(0 0 6px rgba(0, 80, 215, 0.45));
}
.intro-Progress,
.progressElmstart {
  filter: drop-shadow(0 0 4px rgba(0, 80, 215, 0.4));
}

/* Start button hover feedback. A scale transform is NOT used here: on an SVG
   <use> that references a symbol whose content is larger than the use box,
   transform-box: fill-box + transform-origin: center resolves to the wrong
   point, so the button visibly shifts (~25px down/right) on hover and becomes
   hard to click. The drop-shadow glow keeps the affordance without moving it. */
#startButtonDesk, #startButtonMob {
  transition: filter 160ms ease-out;
}
#startButtonDesk:hover, #startButtonMob:hover {
  filter: drop-shadow(0 4px 12px rgba(0, 80, 215, 0.45));
}

/* OVH: the waiting state of the two desktop result cards. The vendor fills
   #graphc1 / #graphc2 during a run; before that the box is empty and the card
   reads as a hole with a number at one end. The placeholder lives in the intro
   group in app.svg, so the vendor hiding the intro at the start of a run is
   what removes it — nothing here has to watch for that. The box keeps the
   reserved chart area and breathes softly; the "Waiting for test" message that
   sits inside it (and the greying of the whole results strip) comes from the
   .idleResults block just below. Colours follow the card it sits on: a step
   down from .Cards in the light skin, and the page ground in darkmode.css. */
.graphIdle rect {
  fill: #e6e6e9;
}
@keyframes graphIdlePulse {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 1; }
}
.graphIdle { animation: graphIdlePulse 2.4s ease-in-out infinite; }

/* The results strip while nothing has been measured yet: one disabled control,
   in grey, with an explicit message in each speed card's reserved chart area
   (at the right of the DOWNLOAD / UPLOAD captions). The --rc* custom
   properties are read by the var() forms above (.Cards/.rtext/.rtextnum/
   .rtextmbms/.Symbol/.pingIc): CSS cannot reach the cloned content inside a
   <use> shadow tree with a descendant selector, but inherited custom
   properties can, which is what makes per-skin greying possible at all. The
   tokens sit on the wrapper's light-skin values here and on the dark ones in
   darkmode.css; the wrapper itself is gone from the DOM the moment a run
   starts, so normal colours return with the UI group. */
#intro-Desk .idleResults {
  --rcCard: #efefef;
  --rcCaption: #a3a0a0;
  --rcValue: #b3b0b0;
  --rcUnit: #bfbcbc;
  --rcArrow: #c6c7cd;
  --rcIcon: #c6c7cd;
}
.idleMsg {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2px;
  fill: #6e6969;   /* ≥4.5:1 on the #e6e6e9 box it sits in */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

@media (prefers-reduced-motion: reduce) {
  use.Cards { animation: none; }
  .graphIdle { animation: none; }
  .main-GaugeBlue, .intro-Progress, .progressElmstart { filter: none; }
  #startButtonDesk, #startButtonMob { transition: none; }
  /* The finished-state blur is a look, not motion, so it stays — only its
     fade-in goes. */
  .openSpeedtestApp.ost-finished { transition: none; }
}

/* OVH: the finished state — the WHOLE widget blurs behind one sharp control.
   The 2026-09-11 version blurred nine named dial parts and kept the
   "Finished" label sharp, so a geometry-pinned button could sit under it;
   that pairing died in Firefox (the pinning math — see speedtest.html — was
   Chromium-exact only). The full-frame blur needs no names and no
   measurements: one rule on the SVG root, which gains its own stacking
   context, so the sibling button stays sharp above it. The results are still
   legible through 6px of blur; the point of the state is "done — one click
   from a new run", not the numbers (the parent page carries those).
   Re-apply on OpenSpeedTest upgrade. */
.openSpeedtestApp.ost-finished {
  filter: blur(6px);
  transition: filter 400ms ease-out;
}

/* OVH: the finished-state overlay. Shown a few seconds after a run ends
   (see OST_OVERLAY_DELAY_MS in speedtest.html): the widget blurs out behind it
   and a centred card carries the result summary, a Copy-results button and
   Start new test. The card is plain HTML centred by flexbox — the same pixels
   in every engine, which is why it replaced the 2026-09-10/13 label-geometry
   pinning (Chromium-exact, wrong in Firefox). Re-apply on OpenSpeedTest
   upgrade. */
.ost-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.35);
}
.ost-overlay[hidden] {
  display: none;
}
.ost-card {
  box-sizing: border-box;   /* min/max-width must include the padding — at a
                               320px frame the card is otherwise 336px wide */
  background: rgba(255, 255, 255, 0.96);
  color: #14161f;
  border-radius: 12px;
  padding: 22px 28px 24px;
  min-width: 280px;
  max-width: min(92vw, 420px);
  box-shadow: 0 10px 34px rgba(0, 14, 156, 0.22);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  text-align: center;
}
.ost-card-title {
  margin: 0 0 16px;
  font-size: 16px;
  font-weight: 700;
  color: #000e9c;
}
.ost-results {
  margin: 0 0 20px;
  display: grid;
  grid-template-columns: auto auto;
  justify-content: center;
  column-gap: 26px;
  row-gap: 8px;
  text-align: left;
}
.ost-row {
  display: contents;   /* let dt/dd sit in the two grid columns */
}
.ost-row dt {
  font-size: 13px;
  font-weight: 600;
  color: #5b6072;
}
.ost-row dd {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #14161f;
}
.ost-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}
/* Secondary (copy) button: ODS outlined look. */
.ost-copy {
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  color: #0050d7;
  background: transparent;
  border: 2px solid #0050d7;
  border-radius: 8px;
  padding: 8px 18px;
  cursor: pointer;
  transition: background 160ms ease-out, color 160ms ease-out;
}
.ost-copy:hover { background: #0050d7; color: #fff; }
.ost-copy:active { background: #00185e; border-color: #00185e; color: #fff; }
/* Copy feedback line under the buttons. */
.ost-copied {
  margin: 14px 0 0;
  font-size: 13px;
  font-weight: 600;
  color: #2b8000;   /* ODS success-500 */
}
.ost-copied[hidden] { display: none; }

/* OVH: "Start new test" primary button, now a normal flow element inside the
   overlay card's actions row (it used to be position:fixed centred by itself).
   Colours follow the ODS primary button ramp (bg primary-500, hover 700,
   active 800; radius = theme 8px). Re-apply on OpenSpeedTest upgrade. */
.start-new-test {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 15px;
  font-weight: 600;
  color: #fff;
  background: #0050d7;
  border: none;
  border-radius: 8px;
  padding: 10px 22px;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 80, 215, 0.35);
  transition: background 160ms ease-out, transform 160ms ease-out;
}
.start-new-test:hover {
  background: #000e9c;
  transform: scale(1.04);
}
.start-new-test:active {
  background: #00185e;
  transform: scale(0.97);
}
