/* ===========================================================================
   ddstats player site — the visual system.
   ===========================================================================

   Every colour here comes from DESIGN.md §2 and was contrast-measured there
   against both of its backgrounds in both themes. Do not "tidy" a hex value:
   the ratios are the reason the numbers on this site are readable, and a
   value changed here without re-measuring breaks that silently.

   ONE RULE GOVERNS THE WHOLE FILE:

       CHROMA IS DATA. If a coloured pixel is not carrying a number, it is a
       bug.

   Devil Daggers is a black void, grey stone, bone-white daggers, and enemies
   that are the only source of colour — a narrow band of red through orange.
   There is no green in that game and no blue. So navigation, buttons,
   headings, borders, hover states and active states here are all achromatic,
   and the only coloured things on the page are the five milestone tiers and
   the death rule. That single restraint is what makes this look like its
   subject instead of like a dashboard template, and it costs nothing.

   Two deliberate exceptions, both justified:
     --focus   the one cool colour. It is allowed to break the rule precisely
               BECAUSE it must never be confusable with data. Taken unchanged
               from console/style.css so focus feels the same across both.
     --ember   the death rule and the ingest pulse. Two marks, sitewide.

   The five tier colours are a single hue heated from cold iron to white heat,
   and they are monotonic in lightness in both themes — they darken as they
   heat on paper and brighten as they heat on the void. That means they still
   read as an ordered scale in greyscale, and to a reader who sees no colour
   at all. They were chosen over bronze/silver/gold metallics for exactly that
   reason: three long-wavelength metallics are indistinguishable to a
   deuteranope.

   The tier colours are used as FILLS ONLY, never as text. The darkest of them
   is 3.29:1 against its background — fine for a bar (WCAG 1.4.11 wants 3:1),
   not enough for words (1.4.3 wants 4.5:1). Tier names are set in ordinary
   text colour and rely on the bar next to them for their colour.
   =========================================================================== */


/* ---------------------------------------------------------------------------
   TOKENS

   The light set is the default. The dark set is written out twice on purpose:
   once for "the operating system says dark and the reader has not chosen",
   and once for "the reader pressed the theme button". There is no
   preprocessor here and there never will be, so that duplication is the price
   of the manual override working in both directions. KEEP THE TWO BLOCKS
   IDENTICAL.
   --------------------------------------------------------------------------- */

:root {
  color-scheme: light;

  /* DESIGN.md §2.4 — light theme. Ratios are against --ground / --raised. */
  --ground:   #FAF7F2;   /* paper                                        */
  --raised:   #F0EBE3;   /* surface: cards, table heads, insets   1.11:1 */
  --text:     #1A1614;   /* ink                                  16.81:1 */
  --muted:    #5A534C;   /* labels, units, secondary              7.08:1 */
  --edge:     #78716A;   /* control borders, axis lines           4.50:1 */
  --ember:    #B8340B;   /* death rule and ingest pulse. Nothing else.   */
  --hairline: #D8D0C4;   /* decorative row separators. Carries nothing.  */
  --focus:    #0B3FA8;   /* matches the operator console exactly  8.58:1 */

  /* The forge ramp, light. Darkens as it heats. */
  --tier-1: #C0552A;     /* bronze  60s     4.30:1 */
  --tier-2: #A8471C;     /* silver  120s    5.48:1 */
  --tier-3: #8A4210;     /* gold    250s    6.85:1 */
  --tier-4: #6A3F08;     /* devil   500s    8.44:1 */
  --tier-5: #402C0C;     /* golden  1000s  12.41:1 */

  --font-text: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-figure: ui-monospace, "Cascadia Mono", "SF Mono", "DejaVu Sans Mono",
    Menlo, Consolas, monospace;

  --radius: 4px;
  --radius-card: 8px;

  /* The fixed columns of a run row, in one place so that the milestone axis
     drawn above the list lines up with the bars inside it. If you change a
     column width, the axis follows automatically. Pixels, not ems, because
     these are also used from :root where the font size is different. */
  --col-time: 104px;
  --col-tier: 62px;
  --col-mark: 46px;
  --col-death: 88px;
  --col-when: 44px;
  --col-gap: 10px;
  --run-meta-w: calc(var(--col-time) + var(--col-tier) + var(--col-mark)
    + var(--col-death) + var(--col-when) + 6 * var(--col-gap));
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    /* DESIGN.md §2.3 — dark theme. The ground is #12100F, not #000: true
       black on an OLED screen makes thin rules vanish and bars appear to
       float. Nothing on this site should glow. */
    --ground:   #12100F;
    --raised:   #1C1917;
    --text:     #EDE7DF;   /* 15.45:1 */
    --muted:    #A29B92;   /*  6.90:1 */
    --edge:     #7E7770;   /*  4.30:1 */
    --ember:    #FF7A45;   /*  7.34:1 */
    --hairline: #35302C;
    --focus:    #9CC2FF;   /* 10.46:1 */

    --tier-1: #A85336;     /* 3.57:1 on ground, 3.29:1 on raised — the
                              tightest value in the system. If --raised is
                              ever lightened, re-measure this first. */
    --tier-2: #C96A33;
    --tier-3: #E28A3C;
    --tier-4: #F5B056;
    --tier-5: #FFDCA8;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --ground:   #12100F;
  --raised:   #1C1917;
  --text:     #EDE7DF;
  --muted:    #A29B92;
  --edge:     #7E7770;
  --ember:    #FF7A45;
  --hairline: #35302C;
  --focus:    #9CC2FF;
  --tier-1: #A85336;
  --tier-2: #C96A33;
  --tier-3: #E28A3C;
  --tier-4: #F5B056;
  --tier-5: #FFDCA8;
}


/* ---------------------------------------------------------------------------
   BASE
   --------------------------------------------------------------------------- */

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

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--font-text);
  font-size: 15px;
  line-height: 1.5;
}

/* Every number on this site. Tabular figures are not decoration: a column of
   times whose decimal points do not line up cannot be compared by eye, which
   is the only thing this list is for. Applied belt-and-braces even on the
   monospace stack, so that if every monospace candidate is missing and the
   fallback is proportional, the columns still line up. */
.figure,
.run__time, .run__when, .run__tier,
.stat__value, .bigtime, .bars__value, .bars__pct,
.session__besttime, .session__span, .session__count,
.plist__time, .plist__rank,
.datatable__table td, .pager__where, .copy__text, code {
  font-family: var(--font-figure);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
}

/* Player names are never monospace. A monospace stack falls back per glyph on
   Cyrillic, CJK and mathematical-bold names and produces a ransom note; the
   system stack is the only thing that renders them correctly, and no webfont
   we could ship would cover that range anyway. */
.run__player, .session__player, .player-head__name, .plist__name,
.run-head__name {
  font-family: var(--font-text);
  unicode-bidi: isolate;
  overflow-wrap: anywhere;
}

a { color: inherit; }
a:hover { text-decoration-thickness: 2px; }

h1, h2, h3 { font-weight: 600; margin: 0; }

p { margin: 0 0 0.6em; }
p:last-child { margin-bottom: 0; }

button { font: inherit; }

/* One focus style everywhere, identical to the operator console's. The
   transparent outline is not redundant: it is what keeps a visible ring in
   Windows High Contrast mode, where box-shadow is dropped. */
:focus-visible {
  outline: 2px solid transparent;
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--focus);
  border-radius: var(--radius);
}
@media (forced-colors: active) {
  :focus-visible { outline-color: Highlight; }
}

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  background: var(--raised);
  color: var(--text);
  padding: 10px 14px;
  border: 1px solid var(--edge);
}
.skip-link:focus { left: 8px; top: 8px; }

.column {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 16px;
}

@media (max-width: 400px) {
  .column { padding: 0 10px; }
  body { font-size: 14px; }
}


/* ---------------------------------------------------------------------------
   TOP BAR AND FOOTER
   Achromatic, on purpose. Nothing up here is data.
   --------------------------------------------------------------------------- */

.topbar {
  border-bottom: 1px solid var(--hairline);
  background: var(--ground);
}

.topbar__inner {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 52px;
  flex-wrap: wrap;
}

.brand {
  text-decoration: none;
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-right: auto;
}
.brand__name {
  font-family: var(--font-figure);
  font-size: 17px;
  letter-spacing: 0.04em;
  font-weight: 600;
}
.brand__tag {
  color: var(--muted);
  font-size: 12px;
}
@media (max-width: 620px) { .brand__tag { display: none; } }

.nav { display: flex; gap: 4px; }
.nav__link {
  text-decoration: none;
  color: var(--muted);
  padding: 5px 10px;
  border-radius: var(--radius);
  font-size: 14px;
}
.nav__link:hover { color: var(--text); background: var(--raised); }
.nav__link[aria-current="page"] {
  color: var(--text);
  background: var(--raised);
  box-shadow: inset 0 -2px 0 var(--edge);
}

.footer {
  margin-top: 48px;
  padding: 20px 0 40px;
  border-top: 1px solid var(--hairline);
  color: var(--muted);
  font-size: 13px;
}
.footer a { color: var(--text); }

.noscript {
  padding: 32px 16px;
  max-width: 620px;
}


/* ---------------------------------------------------------------------------
   CONTROLS
   --------------------------------------------------------------------------- */

.button {
  background: var(--raised);
  color: var(--text);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 5px 10px;
  font-size: 13px;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
}
.button:hover { border-color: var(--text); }

.theme { min-width: 74px; text-align: center; }

/* No `overflow: hidden` on this group, ever. The site's one focus style is a
   box-shadow ring (the outline is deliberately transparent so the ring
   survives Windows High Contrast), and a box-shadow paints OUTSIDE the border
   box, so a clipping ancestor erases it completely. This was the only
   clipping ancestor on the site and its only children are the two focusable
   links, which left the view toggle with no visible focus indicator at all —
   every outward edge of the ring clipped at exactly 0px.

   The clip was only ever there to round the ends of the group, so the options
   round their own outer corners instead. */
.segmented {
  display: inline-flex;
  border: 1px solid var(--edge);
  border-radius: var(--radius);
}
.segmented__option {
  padding: 4px 12px;
  font-size: 13px;
  color: var(--muted);
  text-decoration: none;
  background: var(--ground);
}
.segmented__option:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.segmented__option:last-child  { border-radius: 0 var(--radius) var(--radius) 0; }
.segmented__option + .segmented__option { border-left: 1px solid var(--edge); }
.segmented__option:hover { color: var(--text); }
.segmented__option.is-on { background: var(--raised); color: var(--text); font-weight: 600; }


/* ---------------------------------------------------------------------------
   MAIN, PAGE HEADS
   --------------------------------------------------------------------------- */

#main { padding-top: 22px; outline: none; }

.head {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.head__left { flex: 1 1 260px; }
.head__right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
}
@media (max-width: 620px) {
  .head__right { align-items: flex-start; width: 100%; }
}

.head__title {
  font-family: var(--font-figure);
  font-size: 15px;
  letter-spacing: 0.16em;
  margin-bottom: 2px;
}
.head__sub { color: var(--muted); font-size: 13px; }

.subhead {
  font-family: var(--font-figure);
  font-size: 12px;
  letter-spacing: 0.14em;
  color: var(--muted);
  margin: 0 0 6px;
}

.crumbs {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: 18px;
  font-size: 13px;
}
.crumbs__back { color: var(--muted); text-decoration: none; }
.crumbs__back:hover { color: var(--text); text-decoration: underline; }
.crumbs__id { color: var(--muted); font-family: var(--font-figure); }


/* ---------------------------------------------------------------------------
   THE INGEST PULSE

   The single most valuable mark on this site. The community's belief is that
   ddstats is dead; this is the pixel that disagrees. It is also THE ONLY
   ANIMATION ANYWHERE HERE, which is what earns it the exception — and it is
   removed entirely, not merely slowed, under prefers-reduced-motion.
   --------------------------------------------------------------------------- */

.pulse {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  color: var(--muted);
  margin: 0;
}
.pulse__dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--muted);
}
.pulse--live .pulse__dot {
  background: var(--ember);
  animation: breathe 2s ease-in-out infinite;
}
.pulse--quiet .pulse__dot {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--edge);
}

@keyframes breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.35; }
}

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


/* ---------------------------------------------------------------------------
   THE RUNG BAR

   Five tier bands, each exactly one fifth of the width whatever it spans, and
   a narrow overflow cell past 1000 seconds so a 1163-second run is visibly
   off the end of the ladder rather than pinned to it.

   The 86% / 14% split and the 17.2% cell width are computed in milestone.js.
   The repeating gradient below places a divider every 17.2%, which lands them
   at 0, 60, 120, 250, 500 and 1000 — the same six numbers as the axis drawn
   above the list. If you change one, change both.
   --------------------------------------------------------------------------- */

.rung {
  position: relative;
  display: block;
  width: 100%;
  height: 14px;
  min-width: 80px;
}
.rung--compact { height: 10px; }

.rung__track,
.rung__fill,
.rung__grid {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  pointer-events: none;
}

.rung__track {
  right: 0;
  border: 1px solid var(--hairline);
  border-radius: 1px;
  /* The empty ladder, as a faint rail rather than a solid block.
     Written in the long form (two stops per colour) rather than the newer
     two-position shorthand, because the long form has worked in every browser
     since 2012 and this file is meant to still work untouched in 2035. */
  background-image: repeating-linear-gradient(
    to right,
    var(--hairline) 0, var(--hairline) 1px,
    transparent 1px, transparent 4px);
  background-position: 0 50%;
  background-size: 100% 2px;
  background-repeat: repeat-x;
}

.rung__fill {
  background: var(--edge);
  border-radius: 1px;
}
.rung[data-tier="bronze"] .rung__fill { background: var(--tier-1); }
.rung[data-tier="silver"] .rung__fill { background: var(--tier-2); }
.rung[data-tier="gold"]   .rung__fill { background: var(--tier-3); }
.rung[data-tier="devil"]  .rung__fill { background: var(--tier-4); }
.rung[data-tier="golden"] .rung__fill { background: var(--tier-5); }
/* Below the first threshold there is no COLOUR at all — no sixth hue on a
   five-step scale, no chroma spent on warm-ups and restarts. But the mark
   itself is not decorative: for the fifth to a quarter of rows that are
   sub-60s it is the only fill drawn, and in the player page's RUN PROFILE the
   "NO DAGGER" bar is usually the longest one in the chart. It is therefore
   achromatic at FULL strength — --edge is 4.50:1 on paper and 4.30:1 on the
   void — and not the 0.55 alpha it used to carry, which blended down to
   2.08:1 / 2.12:1 and was the only mark on the site under the 3:1 floor that
   WCAG 1.4.11 sets for a graphic. Grey is the encoding of absence here; faint
   grey was just a contrast failure. */
.rung[data-tier="none"] .rung__fill { background: var(--edge); }

.rung__grid {
  right: 0;
  /* A divider every 17.2% of the width. Those land at 0, 60, 120, 250, 500
     and 1000 seconds — the same six numbers as the axis above the list.
     17.2 is 86 / 5 and comes from milestone.js. Change one, change both.

     THESE ARE MEANT TO BE COUNTED. milestone.js's header names them as one of
     the four channels that carry the tier, and the one that survives when the
     colour is deleted — but this layer sits OVER .rung__fill, and as --edge
     at 0.75 alpha it came out at 1.07:1 against light bronze and 1.13:1
     against light silver. Invisible over exactly the part of the bar the
     reader is asked to count across, leaving bar length and the adjacent word
     doing all the work.

     No single colour fixes that, because a divider crosses two very different
     backgrounds: the tier fill on its left and the page behind the empty
     track on its right. So each divider is TWO 1px stripes, and whichever one
     is legible where it lands is the one you see:

       --ground   invisible against the empty track (it IS the page), and
                  4.30:1 to 12.41:1 against every colour in the tier ramp.
       --edge     invisible against the tier ramp (they sit at nearly the same
                  luminance), and 4.50:1 light / 4.30:1 dark against the page.

     Every divider therefore reads as a 1px high-contrast mark on both sides
     of the fill, in both themes, with no alpha anywhere. */
  background-image: repeating-linear-gradient(
    to right,
    var(--ground) 0, var(--ground) 1px,
    var(--edge) 1px, var(--edge) 2px,
    transparent 2px, transparent 17.2%);
}

.rung__over {
  position: absolute;
  right: -2px; top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--text);
  line-height: 1;
}

/* The axis, drawn once above a list. Its right padding is exactly the width
   of the fixed columns in a run row, so every bar below it is on this scale. */
.rungaxis {
  position: relative;
  /* Exactly the inset of the bar inside a run row: 2px of transparent left
     border plus 8px of padding on the left, and the fixed columns plus the
     row's own padding on the right. Get this wrong and every bar in the list
     is read against an axis that is a few pixels out. */
  padding-left: 10px;
  padding-right: calc(var(--run-meta-w) + 6px);
  margin-bottom: 4px;
}
.rungaxis__rule {
  height: 1px;
  background: var(--edge);
  margin-top: 2px;
}
.rungaxis__labels { position: relative; height: 15px; }
.rungaxis__tick {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  font-family: var(--font-figure);
  font-size: 10.5px;
  color: var(--muted);
  white-space: nowrap;
}
.rungaxis__tick:first-child { transform: none; }
.rungaxis__tick--over { font-style: italic; }
.rungaxis__caption {
  font-size: 11px;
  color: var(--muted);
  margin-top: 2px;
}

@media (max-width: 999px) {
  /* Two lines per row: the bar shares line 1 with the time only. */
  .rungaxis {
    padding-left: 8px;
    padding-right: calc(var(--col-time) + var(--col-gap) + 6px);
  }
}
.run-head__ladder .rungaxis,
.player-head__pb .rungaxis { padding-left: 0; padding-right: 0; }


/* ---------------------------------------------------------------------------
   RUN ROWS AND SESSIONS

   Column order is deliberate: BAR, then time. The bar is the thing the eye
   catches without reading; the number is the confirmation. Reversed, the eye
   has to read digits, which is slower.
   --------------------------------------------------------------------------- */

.runs { list-style: none; margin: 0; padding: 0; }
.runs__item { border-top: 1px solid var(--hairline); }
.runs__item:first-child { border-top: none; }

.run {
  display: flex;
  align-items: center;
  gap: var(--col-gap);
  padding: 5px 6px 5px 8px;
  min-height: 34px;
  text-decoration: none;
  color: inherit;
  border-left: 2px solid transparent;
}
.run:hover { background: var(--raised); }
.run--best { border-left-color: var(--edge); }

.run__bar   { flex: 1 1 auto; min-width: 80px; display: block; }
.run__time  { flex: 0 0 var(--col-time); text-align: right; font-size: 13px; }
.run__tier  { flex: 0 0 var(--col-tier); font-size: 11px; letter-spacing: 0.08em; color: var(--muted); }
.run__mark  { flex: 0 0 var(--col-mark); }
.run__player{ flex: 0 1 130px; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.run__death { flex: 0 0 var(--col-death); font-size: 11px; letter-spacing: 0.06em; color: var(--muted); }
.run__when  { flex: 0 0 var(--col-when); text-align: right; font-size: 11.5px; color: var(--muted); }

/* Tier names are text, so they are never given a tier colour — see the note
   at the top of this file. The only distinction made is between "reached a
   rung" and "did not". */
/* No alpha here. --muted was chosen at 7.08:1 light / 6.90:1 dark and is
   already visibly recessive against the var(--text) used by the line below,
   which is the whole distinction this pair exists to draw. The 0.7 that used
   to be here blended it to 3.45:1 / 3.95:1 — under the 4.5:1 that SC 1.4.3
   requires of text, and the only text on the site that failed it. It is not
   incidental text either: "no dagger" is the tier headline on every run page
   and every player page whose best is under 60s, and the tier column on the
   fifth to a quarter of rows that are sub-60s. */
.run__tier[data-tier="none"] { color: var(--muted); }
.run__tier:not([data-tier="none"]) { color: var(--text); }
.run__tier--big {
  font-size: 13px;
  letter-spacing: 0.14em;
  font-weight: 600;
}

/* The personal-best mark. Loud, because it is rare: under one run in a
   hundred meets the player's own leaderboard best. Loudness here is weight
   and a glyph, not colour — colouring it would put it in competition with the
   tier ramp for the reader's one hue. */
.pbmark {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-family: var(--font-figure);
  font-size: 11px;
  padding: 1px 5px;
  border-radius: var(--radius);
  background: var(--raised);
  border: 1px solid var(--hairline);
  white-space: nowrap;
}
.pbmark--pb { font-weight: 700; color: var(--text); border-color: var(--edge); }
.pbmark--near { color: var(--muted); }
.pbmark__word { letter-spacing: 0.04em; }

.runs__note, .session__footnote {
  color: var(--muted);
  font-size: 12px;
  margin: 10px 0 0;
}

.sessions { margin-top: 4px; }

.session {
  border-top: 1px solid var(--hairline);
}
.session:first-child { border-top: none; }

.session__summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 6px 9px 4px;
  cursor: pointer;
  list-style: none;
  flex-wrap: wrap;
}
.session__summary::-webkit-details-marker { display: none; }
.session__summary:hover { background: var(--raised); }

.session__caret {
  flex: 0 0 12px;
  color: var(--muted);
}
.session__caret::before { content: "\25B8"; }           /* ▸ */
.session[open] .session__caret::before { content: "\25BE"; }  /* ▾ */

.session__player { font-weight: 600; font-size: 14px; }
.session__count  { font-size: 12px; color: var(--muted); }
.session__span   { font-size: 12px; color: var(--muted); }
.session__spark  { flex: 0 0 auto; line-height: 0; opacity: 0.9; }
.session__best {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}
.session__bestlabel { font-size: 11px; color: var(--muted); letter-spacing: 0.08em; }
.session__besttime { font-size: 13px; }

.session__body { padding: 2px 0 12px 14px; }


/* ---------------------------------------------------------------------------
   PAGINATION
   Numbered, never infinite scroll: these addresses get pasted into Discord
   and have to still mean the same thing tomorrow.
   --------------------------------------------------------------------------- */

.pager { margin-top: 18px; border-top: 1px solid var(--hairline); padding-top: 12px; }
.pager__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.pager__link {
  text-decoration: none;
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 5px 12px;
  font-size: 13px;
}
.pager__link:hover { background: var(--raised); }
.pager__link.is-off { color: var(--muted); border-color: var(--hairline); }
.pager__where { font-size: 12px; color: var(--muted); }


/* ---------------------------------------------------------------------------
   PANELS AND STATS
   --------------------------------------------------------------------------- */

.panel {
  margin-top: 26px;
  border-top: 1px solid var(--hairline);
  padding-top: 14px;
}
.panel__title {
  display: flex;
  gap: 12px;
  align-items: baseline;
  flex-wrap: wrap;
  font-family: var(--font-figure);
  font-size: 12px;
  letter-spacing: 0.16em;
  margin-bottom: 10px;
}
.panel__note { color: var(--muted); letter-spacing: 0; font-size: 11.5px; }
.panel__foot, .panel__more {
  color: var(--muted);
  font-size: 12px;
  margin-top: 10px;
}
.panel__more a { color: var(--text); }
.panel__more--muted { font-style: italic; }

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 26px;
}
.stats--grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 14px 20px;
}
.stat { min-width: 0; }
.stat__label {
  display: block;
  font-size: 10.5px;
  letter-spacing: 0.12em;
  color: var(--muted);
}
.stat__value {
  display: block;
  font-size: 17px;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.stat__sub {
  display: block;
  font-size: 11.5px;
  color: var(--muted);
  overflow-wrap: anywhere;
}


/* ---------------------------------------------------------------------------
   THE RUN PAGE

   One number is the whole point of this page. The integer seconds read from
   across a room; the hundredths are present, exact and selectable, because
   this community copies them into Discord. Rounding the headline and hiding
   the real value in a tooltip was rejected: a tooltip is not a place to keep
   the one number a page exists for.
   --------------------------------------------------------------------------- */

/* Several class names in the JavaScript exist only as containers to hang a
   page's parts on — .game, .player, .today, .runs__area, .panel__body. They
   have no rules here on purpose: they are structure, and inventing styling
   for them would be styling for its own sake. */

.run-head { margin-bottom: 4px; }

.run-head__who {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 6px;
}
.run-head__name { font-size: 17px; font-weight: 600; }
.run-head__pid  { font-family: var(--font-figure); font-size: 12px; color: var(--muted); }
.run-head__when { margin-left: auto; font-size: 12px; color: var(--muted); }

.run-head__score {
  display: flex;
  align-items: baseline;
  gap: 22px;
  flex-wrap: wrap;
  margin: 6px 0 14px;
}

.bigtime {
  margin: 0;
  font-family: var(--font-figure);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  /* The size lives HERE, on the parent, so that the two `em` sizes below are
     fractions of the headline and not of the body text. Moving the clamp down
     onto .bigtime__whole makes the hundredths render at nine pixels. */
  font-size: clamp(2.6rem, 9vw, 4.5rem);
}
.bigtime--small { font-size: clamp(1.8rem, 6vw, 2.6rem); }
.bigtime__whole { font-size: 1em; }
.bigtime__frac  { font-size: 0.58em; color: var(--muted); vertical-align: baseline; }
/* The unit is decoration; the digits either side of it are what people copy
   into Discord, so the `s` is excluded from a selection. */
.bigtime__unit  { font-size: 0.3em; color: var(--muted); margin-left: 0.4em; user-select: none; }

/* Death types are a nominal variable with sixteen values. Colouring them
   would need sixteen hues; they get typography instead. */
.deathword {
  margin: 0;
  font-family: var(--font-text);
  font-size: clamp(0.95rem, 3vw, 1.25rem);
  letter-spacing: 0.22em;
  font-weight: 600;
  color: var(--text);
}

.run-head__ladder { margin: 10px 0 12px; }
.run-head__tier {
  display: flex;
  gap: 14px;
  align-items: baseline;
  flex-wrap: wrap;
  margin-top: 10px;
}
.run-head__of  { font-family: var(--font-figure); font-size: 12px; color: var(--muted); }
.run-head__gap { font-size: 12px; color: var(--muted); }

.pb {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--muted);
  margin: 0 0 4px;
  padding: 8px 0;
  border-top: 1px solid var(--hairline);
}
.pb--pb { color: var(--text); }
.pb--unknown { font-style: italic; }

.provenance {
  font-size: 12px;
  color: var(--muted);
  border-left: 2px solid var(--hairline);
  padding-left: 10px;
  margin-top: 18px;
}

/* The death card. The only place on this site where a sentence is generated
   from data — and it earns it: "peaked at 900 and were gone 271 seconds
   later" is the run, described. */
.death {
  margin-top: 26px;
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--ember);
  border-radius: var(--radius-card);
  background: var(--raised);
  padding: 14px 16px;
}
.death__word {
  font-family: var(--font-text);
  font-size: 15px;
  letter-spacing: 0.24em;
  margin-bottom: 6px;
}
.death__facts { font-family: var(--font-figure); font-size: 13px; }
.death__arc { color: var(--muted); font-size: 13px; margin-bottom: 0; }

/* No telemetry. The two cases are NOT the same and do not look the same:
   "there was never a recording" is normal, "the recording produced nothing"
   is a fault and gets the ember rule. */
.notele {
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--edge);
  border-radius: var(--radius-card);
  background: var(--raised);
  padding: 14px 16px;
  max-width: 62ch;
}
.notele--fault { border-left-color: var(--ember); }
.notele__head { font-weight: 600; }
.notele p:last-child { color: var(--muted); font-size: 13px; }

.inset { margin-top: 22px; }
.inset__why { color: var(--muted); font-size: 12px; max-width: 60ch; }


/* ---------------------------------------------------------------------------
   CHARTS

   Not one colour is set in chart.js. Everything below is the styling for the
   classes it emits. Series are separated by POSITION, never by hue — each one
   owns a horizontal band and carries its own written label — so the chart
   survives being printed in greyscale or read by someone who sees no colour.
   --------------------------------------------------------------------------- */

.chart {
  margin: 4px 0 0;
  border-radius: var(--radius);
}
.chart:focus-visible { box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--focus); }
.chart__plot { width: 100%; overflow: hidden; }
.chart__plot svg { display: block; max-width: 100%; }

.readout {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  padding: 8px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--raised);
  margin-bottom: 10px;
  min-height: 44px;
}
.readout__cell { display: flex; flex-direction: column; min-width: 70px; }
.readout__label { font-size: 9.5px; letter-spacing: 0.12em; color: var(--muted); }
.readout__value {
  font-family: var(--font-figure);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
}

.band__title {
  font-family: var(--font-figure);
  font-size: 10px;
  letter-spacing: 0.14em;
  fill: var(--muted);
}
.band__note  { font-family: var(--font-figure); font-size: 10px; fill: var(--muted); }
.band__ytick { font-family: var(--font-figure); font-size: 10px; fill: var(--muted); }
.band__end   { font-family: var(--font-figure); font-size: 11px; fill: var(--text); }
.band__base  { stroke: var(--edge); stroke-width: 1; }
.band__top   { stroke: var(--hairline); stroke-width: 1; }

.series__line {
  fill: none;
  stroke: var(--text);
  stroke-width: 1.25;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.series__line--step { stroke-width: 2; }
.series__area { fill: var(--text); opacity: 0.1; stroke: none; }

/* Thin evidence. A dashed stretch says "the estimate under here is made of
   very few runs" — distinct from the crosshair's 2 3 dashes so the two can
   never be read as the same mark. The strip underneath carries the count. */
.series__line--thin   { stroke-width: 0.75; }
.series__line--sparse { stroke-dasharray: 4 3; }

/* The open endpoint of a partial bucket: a month that is seven days long,
   drawn as a hollow dot so it cannot be read as a crash. */
.series__open { fill: var(--ground); stroke: var(--text); stroke-width: 1.25; }

/* Quantile ribbons. Three nested marks of ONE achromatic colour at three
   opacities, with the median stroked — position, fill density and stroke
   weight, so the chart is identical in greyscale and in Windows High
   Contrast. The p25/p75 edges are stroked for exactly that reason: at 0.14
   opacity the inner band has no edge of its own when colour is removed. */
.ribbon       { fill: var(--text); opacity: 0.07; stroke: none; }
.ribbon--iqr  { opacity: 0.14; }
.ribbon__edge { fill: none; stroke: var(--text); stroke-width: 0.75; opacity: 0.5; }

/* How much evidence is left. Its own 8px lane under a band, never overlaid on
   it: a chart's right-hand end is noise drawn at the same confidence as its
   left-hand end unless something says how many runs are still in it. */
.atrisk       { fill: var(--edge); stroke: var(--edge); }
.atrisk--thin { fill: var(--edge); stroke: var(--edge); opacity: 0.5; }
.atrisk__label{ font-family: var(--font-figure); font-size: 9.5px; fill: var(--muted); }

.axis__rule  { stroke: var(--edge); stroke-width: 1; }
.axis__tick  { stroke: var(--edge); stroke-width: 1; }
.axis__label { font-family: var(--font-figure); font-size: 10px; fill: var(--muted); }
.axis__sub   { font-family: var(--font-figure); font-size: 9.5px; fill: var(--muted); }

/* The death rule: the only vertical line on the chart, the only 2px stroke,
   and one of only two coloured marks on the whole site. It sits at exactly
   game_info.game_time, which is a real coordinate — the final recorded frame
   is the instant of death, not an approximation of it. */
.deathrule { stroke: var(--ember); stroke-width: 2; }
.deathrule__label { font-family: var(--font-figure); font-size: 10px; fill: var(--ember); }

.cursor { stroke: var(--text); stroke-width: 1; stroke-dasharray: 2 3; }

.thresh { stroke: var(--edge); stroke-width: 1; stroke-dasharray: 3 4; opacity: 0.8; }
.thresh__label { font-family: var(--font-figure); font-size: 9.5px; fill: var(--muted); }

.spark__bar { fill: var(--edge); }
.spark__bar[data-tier="bronze"] { fill: var(--tier-1); }
.spark__bar[data-tier="silver"] { fill: var(--tier-2); }
.spark__bar[data-tier="gold"]   { fill: var(--tier-3); }
.spark__bar[data-tier="devil"]  { fill: var(--tier-4); }
.spark__bar[data-tier="golden"] { fill: var(--tier-5); }

/* The numbers behind a chart, always available and never hidden behind a
   hover. This is the version you can copy into a spreadsheet. */
.datatable { margin-top: 16px; }
.datatable > summary { font-size: 12.5px; color: var(--muted); cursor: pointer; }
.datatable > summary:hover { color: var(--text); }
.datatable__note { font-size: 11.5px; color: var(--muted); margin: 8px 0; }
.datatable__scroll { max-height: 340px; overflow: auto; border: 1px solid var(--hairline); }
.datatable__table { border-collapse: collapse; width: 100%; font-size: 12px; }
.datatable__table th {
  position: sticky; top: 0;
  background: var(--raised);
  text-align: right;
  padding: 5px 8px;
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--muted);
  border-bottom: 1px solid var(--hairline);
}
.datatable__table td {
  text-align: right;
  padding: 3px 8px;
  border-bottom: 1px solid var(--hairline);
}


/* ---------------------------------------------------------------------------
   HORIZONTAL BAR LISTS
   --------------------------------------------------------------------------- */

.bars { width: 100%; border-collapse: collapse; }
.bars__row { border-bottom: 1px solid var(--hairline); }
.bars__row:last-child { border-bottom: none; }
.bars__label {
  text-align: left;
  font-weight: 400;
  padding: 4px 10px 4px 0;
  white-space: nowrap;
}
.bars__name {
  font-family: var(--font-figure);
  font-size: 11px;
  letter-spacing: 0.1em;
}
.bars__sub {
  font-family: var(--font-figure);
  font-size: 10px;
  color: var(--muted);
  margin-left: 6px;
}
.bars__track { width: 100%; padding: 4px 8px 4px 0; }
.bars__fill {
  display: block;
  height: 10px;
  background: var(--edge);
  border-radius: 1px;
  min-width: 1px;
}
.bars__fill[data-tier="bronze"] { background: var(--tier-1); }
.bars__fill[data-tier="silver"] { background: var(--tier-2); }
.bars__fill[data-tier="gold"]   { background: var(--tier-3); }
.bars__fill[data-tier="devil"]  { background: var(--tier-4); }
.bars__fill[data-tier="golden"] { background: var(--tier-5); }
/* Full strength, for the same reason as .rung[data-tier="none"] .rung__fill. */
.bars__fill[data-tier="none"]   { background: var(--edge); }
.bars__value { text-align: right; padding: 4px 0 4px 8px; font-size: 12.5px; white-space: nowrap; }
.bars__pct   { text-align: right; padding: 4px 0 4px 10px; font-size: 11.5px; color: var(--muted); white-space: nowrap; }


/* ---------------------------------------------------------------------------
   PLAYER PAGE
   --------------------------------------------------------------------------- */

.player-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}
.player-head__name { font-size: clamp(1.3rem, 5vw, 1.9rem); font-weight: 600; }
.player-head__pid  { font-family: var(--font-figure); font-size: 13px; color: var(--muted); }
.player-head__rank {
  margin-left: auto;
  font-family: var(--font-figure);
  font-size: 13px;
  color: var(--muted);
}
.player-head__pb { margin-top: 16px; }
.player-head__pb .rung { margin-top: 8px; }

.plist { list-style: none; margin: 0; padding: 0; }
.plist__item {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 4px 0;
  border-bottom: 1px solid var(--hairline);
  flex-wrap: wrap;
}
.plist__item:last-child { border-bottom: none; }
.plist__name { flex: 1 1 140px; font-size: 13.5px; }
.plist__rank { font-size: 11.5px; color: var(--muted); }
.plist__time { font-size: 12.5px; }


/* ---------------------------------------------------------------------------
   STATES: loading, empty, failed

   Three outcomes, three different sentences, and never a shimmering grey
   rectangle. A skeleton loader is indistinguishable from a broken page, and
   on a service whose reputation problem is "it's dead" that is the worst
   possible failure mode.
   --------------------------------------------------------------------------- */

.state {
  color: var(--muted);
  font-size: 13.5px;
  padding: 18px 2px;
}
.state--loading { font-family: var(--font-figure); }
.state--odd { color: var(--text); }

.failure {
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--ember);
  border-radius: var(--radius-card);
  background: var(--raised);
  padding: 14px 16px;
  margin: 14px 0;
  max-width: 64ch;
}
.failure--inline { margin: 8px 0; padding: 10px 12px; }
.failure__head { font-weight: 600; margin-bottom: 6px; }
.failure__advice { color: var(--muted); font-size: 13px; }
.failure__detail { margin-top: 8px; font-size: 12px; color: var(--muted); }
.failure__detail summary { cursor: pointer; }
.failure__detail p { margin-top: 6px; font-family: var(--font-figure); overflow-wrap: anywhere; }
.failure .button { margin-top: 10px; }

.notfound { padding: 24px 0; max-width: 60ch; }
.notfound__head { font-size: 20px; margin-bottom: 10px; }
.notfound p { color: var(--muted); font-size: 14px; }
.notfound__links { padding-left: 18px; }
.notfound .button { margin-top: 12px; }


/* ---------------------------------------------------------------------------
   THE EMPTY STATE

   On day one this IS the site. It states the start date as a fact rather than
   an apology, and it gives the reader the single action that fixes the
   emptiness. An empty page with a call to action is a NEW page; an empty page
   without one is a BROKEN page. That is the whole difference.
   --------------------------------------------------------------------------- */

.dayone { padding: 22px 0 10px; max-width: 64ch; }
.dayone__head { font-size: 18px; font-weight: 600; }
.dayone__body { color: var(--muted); font-size: 14px; }
.dayone__where { color: var(--muted); font-size: 12px; margin-top: 8px; }
.dayone__daily { margin-top: 8px; }

.copy {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
}
.copy__text {
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 7px 12px;
  background: var(--raised);
  font-size: 13px;
  user-select: all;
}
.copy__status { font-size: 12px; color: var(--muted); }

.ladder { margin-top: 16px; }
.ladder__none { color: var(--muted); font-size: 13px; margin-top: 8px; }
.worldline { font-family: var(--font-figure); font-size: 13px; margin-top: 14px; }

/* ---------------------------------------------------------------------------
   THE COMPOSITION BAR — chart 5, and nothing else on the site.

   The only place the forge ramp appears outside a rung bar, because it is the
   only chart whose variable IS the tier. It keeps DESIGN.md §5.3's four
   channels intact: segment length, countable two-stripe dividers, the written
   tier word, and the printed count. Delete the colour and it is still a
   labelled, countable, ordered bar.
   --------------------------------------------------------------------------- */

.stackwrap { margin: 18px 0 4px; }
.stack__caption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 6px;
}
.stack__title {
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--muted);
  text-transform: uppercase;
}
.stack__total {
  font-family: var(--font-figure);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--muted);
}
.stack {
  display: flex;
  height: 28px;
  border: 1px solid var(--hairline);
  overflow: hidden;
  border-radius: 2px;
}
.stack__seg {
  position: relative;
  min-width: 1px;
  background: var(--edge);
  display: flex;
  align-items: center;
  overflow: hidden;
}
.stack__seg[data-tier="bronze"] { background: var(--tier-1); }
.stack__seg[data-tier="silver"] { background: var(--tier-2); }
.stack__seg[data-tier="gold"]   { background: var(--tier-3); }
.stack__seg[data-tier="devil"]  { background: var(--tier-4); }
.stack__seg[data-tier="golden"] { background: var(--tier-5); }
.stack__seg[data-tier="none"]   { background: var(--edge); }

/* THE TWO-STRIPE DIVIDER, exactly as .rung__grid argues it. A divider here
   crosses two very different backgrounds and no single colour is visible
   against both: --edge reads 4.50:1 over the page and 1.07:1 over light
   bronze, i.e. not at all, precisely where it matters. So each divider is 1px
   --ground beside 1px --edge. --ground is 4.30:1–12.41:1 against every colour
   in the ramp and invisible against the page; --edge is invisible against the
   ramp and 4.30:1–4.50:1 against the page. Whichever stripe lands somewhere it
   can be seen is the one the reader sees; the other disappears. No alpha, no
   new token. */
.stack__seg + .stack__seg { border-left: 1px solid var(--ground); }
.stack__seg + .stack__seg::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 1px;
  background: var(--edge);
}

/* THE WORDS GO HERE, NOT INSIDE THE BAR, AND THAT IS A MEASUREMENT.

   The tier fills are chosen to sit 3:1–12.41:1 from the page ground so they
   work as MARKS, which is SC 1.4.11's 3:1. Small text ON one of them is a
   different requirement — SC 1.4.3's 4.5:1 — and no token in this file clears
   it. Measured with the WCAG relative-luminance formula over the hex values
   above:

     word colour     light bronze   dark bronze   dark golden
     --ground            4.30:1        3.57:1       14.51:1
     --text              3.91:1        4.32:1        1.06:1

   --ground is the better of the two and still fails on bronze in both themes;
   --text collapses to 1.06:1 on the golden step. So the written-word channel
   lives here instead, at --text on the page ground: 16.81:1 light, 15.45:1
   dark. This list is in the same left-to-right order as the bar, and the bar's
   accessible name says so.

   If anybody ever puts a word back inside a segment, re-measure it first. A
   label that is documented as one of four redundant channels and is in fact
   unreadable is worse than three channels honestly. */
.stack__legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  margin-top: 8px;
  font-size: 12px;
  list-style: none;
  padding: 0;
}
.stack__legenditem { display: flex; gap: 6px; align-items: baseline; }
.stack__legendname { color: var(--muted); letter-spacing: 0.06em; font-size: 10.5px; }
.stack__legendnum {
  font-family: var(--font-figure);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
}

/* ---------------------------------------------------------------------------
   THE ARCHIVE PAGE

   Nothing here introduces a colour value. The page is panels, prose and the
   chart classes above.
   --------------------------------------------------------------------------- */

.archive__lede {
  max-width: 68ch;
  color: var(--muted);
  font-size: 14px;
  margin-top: 8px;
}
.archive__sentence {
  max-width: 68ch;
  font-size: 14.5px;
  margin: 14px 0 2px;
  color: var(--text);
}
.archive__caption {
  max-width: 72ch;
  color: var(--muted);
  font-size: 13px;
  margin-top: 12px;
}
.archive__scale {
  font-family: var(--font-figure);
  font-size: 11.5px;
  color: var(--muted);
  margin-top: 10px;
}
.archive__foot {
  border-top: 1px solid var(--hairline);
  margin-top: 26px;
  padding-top: 12px;
  color: var(--muted);
  font-size: 12px;
  font-family: var(--font-figure);
}


/* ---------------------------------------------------------------------------
   OFFLINE PREVIEW BAR
   Only ever visible when the site is not talking to a real server.
   --------------------------------------------------------------------------- */

.previewbar {
  background: var(--raised);
  border-bottom: 1px solid var(--edge);
  padding: 10px 0;
  font-size: 12.5px;
}
.previewbar__lead { color: var(--text); max-width: 90ch; }
.previewbar__label {
  font-family: var(--font-figure);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  color: var(--muted);
  display: block;
  margin-top: 6px;
}
.previewbar__loading { color: var(--muted); font-family: var(--font-figure); }
.previewbar__row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.previewbar__choice {
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  padding: 3px 9px;
  text-decoration: none;
  color: var(--muted);
  background: var(--ground);
}
.previewbar__choice:hover { color: var(--text); }
.previewbar__choice.is-on { color: var(--text); font-weight: 600; background: var(--raised); }
.previewbar__note { color: var(--muted); margin-top: 6px; max-width: 90ch; }
.previewbar__note a { color: var(--text); }


/* ---------------------------------------------------------------------------
   NARROW SCREENS

   Two breakpoints, not five. Discord links are opened on phones constantly,
   so the phone layout is not a degraded desktop: it is the same information
   at two lines per row.
   --------------------------------------------------------------------------- */

@media (max-width: 999px) {
  .run { flex-wrap: wrap; padding: 7px 6px; }
  /* Forces a line break after the time, so line 1 is bar + time and line 2 is
     everything else. */
  .run::after { content: ""; flex-basis: 100%; height: 0; order: 3; }
  .run__bar   { order: 1; flex: 1 1 auto; }
  .run__time  { order: 2; flex: 0 0 var(--col-time); }
  .run__tier  { order: 4; flex: 0 0 auto; }
  .run__mark  { order: 5; flex: 0 0 auto; }
  .run__player{ order: 6; flex: 0 1 auto; max-width: 40%; }
  .run__death { order: 7; flex: 0 0 auto; }
  .run__when  { order: 8; flex: 0 0 auto; margin-left: auto; }

  .session__summary { gap: 8px; }
  .session__span { flex-basis: 100%; }
  .session__best { margin-left: 0; flex-basis: 100%; }
  .session__body { padding-left: 4px; }
}

@media (max-width: 480px) {
  .session__spark { display: none; }
  .run__player { max-width: 100%; }
  .stats--grid { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }
  .bars__pct { display: none; }
}

/* Anything wide enough to matter is allowed to scroll inside its own box: the
   frame table has .datatable__scroll, charts are drawn to the measured width
   of their container, long player names wrap anywhere. This last line is the
   belt to that pair of braces — the page itself must never scroll sideways at
   320px, and a sideways-scrolling page on a phone is the single most
   noticeable way a layout can be broken. */
html, body { overflow-x: hidden; }
