/* ==========================================================================
   Dashboard, profiles and per-profile settings
   --------------------------------------------------------------------------
   The front door has to answer three questions without feeling like a
   spreadsheet: *where am I*, *what should I do right now*, *am I getting
   better*. So exactly one element on the page is loud — the recommendation —
   and everything below it is quiet, evenly weighted and generously spaced.

   Every colour, radius and step comes from base.css. The only things defined
   here are layout and the two hand-built charts (the trend and the spider),
   which have no equivalent component in the design system.
   ========================================================================== */

/* --------------------------------------------------------------------- */
/* Small shared pieces                                                    */
/* --------------------------------------------------------------------- */

/* A series/phase colour swatch. `--dot` is set inline from PHASE_COLORS so
   the SVG marks and the HTML legends can never disagree. */
.dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  min-width: 9px;
  border-radius: 50%;
  background: var(--dot, var(--ink-300));
}

.section-title {
  margin: 0;
  font-size: 1.12rem;
  color: var(--ink-900);
}

.muted {
  color: var(--ink-500);
  font-size: 0.92rem;
}

/* base.css colours `.empty h3`, but an empty state nested inside a card inside
   a section is at h4, and inherits body colour instead. The general fix is the
   selector list in base.css; this covers the dashboard until that lands. */
.empty h4 { color: var(--ink-700); }

.notice {
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--ink-200);
  border-left: 3px solid var(--ink-300);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink-700);
  font-size: 0.92rem;
}
.notice--info { border-left-color: var(--accent); background: var(--accent-soft); }
.notice--warn { border-left-color: var(--warn); background: var(--warn-soft); }
.notice--error { border-left-color: var(--danger); background: var(--danger-soft); }
.notice ul { margin: var(--space-2) 0 0; padding-left: 1.1rem; }

/* --------------------------------------------------------------------- */
/* Do this now                                                            */
/* --------------------------------------------------------------------- */

.next-up {
  position: relative;
  overflow: hidden;
  padding: var(--space-6) var(--space-6) var(--space-5);
  border: 1px solid var(--ink-200);
  border-radius: var(--radius-lg);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.9), var(--paper) 55%),
    radial-gradient(720px 260px at 8% -30%, var(--next-tint, var(--accent-soft)), transparent 70%);
  box-shadow: var(--shadow-sm);
}

/* The tint is the only per-state difference: the layout of the panel must not
   move when the recommendation changes, or the page flickers between visits. */
.next-up--library,
.next-up--assess-start { --next-tint: rgba(61, 90, 254, 0.14); }
.next-up--assess-phase { --next-tint: rgba(240, 165, 0, 0.16); }
.next-up--starred { --next-tint: rgba(255, 201, 60, 0.22); }
.next-up--timer { --next-tint: rgba(0, 155, 72, 0.14); }
.next-up--weakest { --next-tint: rgba(183, 18, 52, 0.12); }
.next-up--explore { --next-tint: rgba(0, 155, 72, 0.12); }

.next-up__eyebrow {
  margin: 0 0 var(--space-2);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-400);
}

.next-up__title {
  margin: 0 0 var(--space-3);
  font-size: clamp(1.35rem, 1.05rem + 1.2vw, 1.85rem);
  max-width: 26ch;
}

.next-up__body {
  max-width: 62ch;
  color: var(--ink-500);
  margin-bottom: var(--space-4);
}

.next-up__cases {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
}
.next-up__cases a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 6px 12px 6px 6px;
  min-height: 40px;
  border: 1px solid var(--ink-200);
  border-radius: var(--radius-pill);
  background: var(--paper);
  color: var(--ink-700);
  font-size: 0.88rem;
  font-weight: 560;
}
.next-up__cases a:hover { border-color: var(--ink-300); text-decoration: none; box-shadow: var(--shadow-xs); }

.next-up__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

/* --------------------------------------------------------------------- */
/* Projection and the split bars                                          */
/* --------------------------------------------------------------------- */

.projection {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-5);
}

.projection__figure { display: flex; flex-direction: column; }
.projection__value {
  font-size: clamp(2.4rem, 1.6rem + 3vw, 3.4rem);
  font-weight: 680;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}
.projection__value small { font-size: 0.42em; font-weight: 600; color: var(--ink-400); margin-left: 2px; }
.projection__label {
  font-size: 0.76rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-400);
  font-weight: 620;
}

.projection__delta { flex: 1 1 200px; }
.projection__note { margin: var(--space-2) 0 0; font-size: 0.88rem; color: var(--ink-500); }

.split-bars {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.split-bars__row {
  display: grid;
  grid-template-columns: 7.5rem minmax(0, 1fr) 3.5rem;
  align-items: center;
  gap: var(--space-3);
}
.split-bars__label { font-size: 0.84rem; color: var(--ink-500); font-weight: 560; }
.split-bars__track {
  display: flex;
  height: 26px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--ink-100);
}
.split-bars__seg {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  background: var(--seg-color, var(--ink-300));
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  overflow: hidden;
}
.split-bars__seg b { padding-inline: 4px; }
/* The goal row is the reference, not a second measurement: same hues, drawn
   as a hatched ghost so the eye reads the solid bar as "you". */
.split-bars__track.is-target .split-bars__seg {
  background:
    repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.55) 0 4px, transparent 4px 8px),
    var(--seg-color, var(--ink-300));
  opacity: 0.55;
  color: var(--ink-900);
}
.split-bars__total {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 0.86rem;
  font-weight: 620;
  color: var(--ink-700);
}

.phase-table th[scope="row"] {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 580;
  color: var(--ink-900);
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.92rem;
}

/* --------------------------------------------------------------------- */
/* Spider                                                                 */
/* --------------------------------------------------------------------- */

.spider {
  margin: 0;
  display: grid;
  place-items: center;
}
.spider svg { width: min(100%, 260px); height: auto; overflow: visible; }
.spider figcaption {
  margin-top: var(--space-2);
  font-size: 0.78rem;
  color: var(--ink-400);
}

.spider__ring { fill: none; stroke: var(--ink-100); stroke-width: 1; }
.spider__target { fill: none; stroke: var(--success); stroke-width: 1.2; stroke-dasharray: 3 4; opacity: 0.8; }
.spider__axis { stroke: var(--ink-200); stroke-width: 1; }
.spider__shape {
  fill: rgba(61, 90, 254, 0.14);
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
}
.spider__node { fill: var(--node, var(--accent)); stroke: var(--paper); stroke-width: 1.5; }
.spider__node.is-empty { fill: var(--paper); stroke: var(--ink-300); }

/* --------------------------------------------------------------------- */
/* Play-count radars                                                      */
/* --------------------------------------------------------------------- */
/*
   Same `.spider*` contract as the phase radar, one dot per case instead of one
   per phase — so 41, 57 and 21 axes rather than four. Everything that differs
   is a consequence of that count: the drawing needs more room, the rings have
   to recede so a hundred dots read as the subject, and the hit target has to
   shrink to the actual dot spacing or each disc swallows its neighbours.
*/

.spider--plays svg { width: 100%; max-width: 380px; }

/* Dotted and paler than the four-axis rings: at this density a solid ring
   competes with the data drawn on top of it. */
.spider--plays .spider__ring { stroke: var(--ink-200); stroke-dasharray: 1 4; }
.spider--plays .spider__ring.is-outer { stroke: var(--ink-300); }
.spider--plays .spider__ring--zero { stroke: var(--ink-300); stroke-dasharray: 1 3; }

/* The "0" and the outer-ring maximum, and the direct label on the busiest
   case. Both carry a paper halo (paint-order + stroke) in the markup so they
   stay legible where they cross a dot; that part is structural, not styling. */
.spider__scale { fill: var(--ink-400); font-size: 11px; font-weight: 640; }
.spider__peak { fill: var(--ink-700); font-size: 11px; font-weight: 700; }

.plays-intro { max-width: 82ch; }

.axis-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.axis-list li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.9rem;
  padding-block: 2px;
}
.axis-list b { font-variant-numeric: tabular-nums; color: var(--ink-900); }
.axis-list__empty { font-size: 0.8rem; color: var(--ink-400); }

/* --------------------------------------------------------------------- */
/* Trend chart                                                            */
/* --------------------------------------------------------------------- */

.trend { display: flex; flex-direction: column; gap: var(--space-3); }

.trend__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.trend__readout {
  margin: 0;
  font-size: 0.88rem;
  color: var(--ink-500);
  font-variant-numeric: tabular-nums;
}

.trend__legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.trend__legend button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: 6px 12px;
  border: 1px solid var(--ink-200);
  border-radius: var(--radius-pill);
  background: var(--paper);
  color: var(--ink-500);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 580;
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    background var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.trend__legend button b { font-variant-numeric: tabular-nums; color: var(--ink-700); }
.trend__legend button[aria-pressed="true"] {
  border-color: var(--series, var(--ink-300));
  color: var(--ink-900);
  background: var(--paper);
}
.trend__legend button[aria-pressed="false"] { opacity: 0.5; }
.trend__legend button[aria-pressed="false"] .dot { background: var(--ink-300); }

.trend__plot {
  position: relative;
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr);
  gap: var(--space-2);
}

.trend__yaxis {
  position: relative;
  font-size: 0.72rem;
  color: var(--ink-400);
  font-variant-numeric: tabular-nums;
}
.trend__yaxis span {
  position: absolute;
  right: 0;
  transform: translateY(-50%);
  white-space: nowrap;
}

/* The plot is stretched to its container rather than kept at a fixed aspect
   ratio: a trend needs to be ~200px tall on a phone and ~290px on a desktop,
   and letterboxing it would pull the HTML label overlay out of alignment with
   the drawing underneath. Everything inside the SVG is therefore either a
   stroke (immune, via non-scaling-stroke) or lives in the overlay. */
.trend__canvas {
  position: relative;
  height: clamp(190px, 19vw, 300px);
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, var(--ink-050), var(--paper));
  padding: 2px;
  touch-action: pan-y;
}
.trend__canvas:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.trend__canvas svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* A grid is a reference, not a projection: solid hairline, never dashed. The
   dashed ring on the radar is a different thing — that one is a threshold. */
.trend__grid line { stroke: var(--ink-200); stroke-width: 1; }

.trend__series { transition: opacity var(--dur) var(--ease); }
.trend__series.is-off { opacity: 0; pointer-events: none; }

.trend__line {
  fill: none;
  stroke: var(--series, var(--ink-700));
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
/* A single-point run draws nothing as a polyline, so the server emits a marker
   instead — without it the chart looks broken on day one. It is an HTML <i> in
   the overlay rather than an SVG <circle> because the plot box is stretched,
   and a circle in a stretched box comes out an ellipse. Geometry lives here so
   the template only has to place it: `left`, `top` and `--series`. */
.trend__marker {
  position: absolute;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: var(--series, var(--ink-700));
  box-shadow: 0 0 0 2px var(--paper);
  pointer-events: none;
}

/* The per-day dots are <line> strokes for the same reason, so their colour is
   `stroke`, set inline per series. All this rule owns is how far they recede
   behind the rolling mean. */
.trend__dot { opacity: 0.28; }

.trend__crosshair {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--ink-400);
  pointer-events: none;
}
.trend__crosshair::after {
  content: attr(data-label);
  position: absolute;
  top: 4px;
  left: 6px;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  background: var(--ink-900);
  color: #fff;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  white-space: pre;
  line-height: 1.35;
  box-shadow: var(--shadow-md);
}
/* Flip the tooltip to the left half once the cursor passes the middle. */
.trend__crosshair.is-flipped::after { left: auto; right: 6px; }

.trend__endpoint {
  position: absolute;
  transform: translate(6px, -50%);
  padding: 1px 7px;
  border-radius: var(--radius-pill);
  background: var(--paper);
  border: 1px solid var(--series, var(--ink-200));
  color: var(--ink-900);
  font-size: 0.72rem;
  font-weight: 640;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease);
}
.trend__endpoint.is-off { opacity: 0; }
/* The last day sits at ~98%, so its label would hang off the right edge of a
   phone. The server knows which endpoints are past 80% — it can set this class
   instead of writing the transform inline. */
.trend__endpoint--flip { transform: translate(calc(-100% - 6px), -50%); }

.trend__xaxis {
  position: relative;
  height: 1.1rem;
  margin-left: calc(2.6rem + var(--space-2));
  font-size: 0.72rem;
  color: var(--ink-400);
}
.trend__xaxis span { position: absolute; transform: translateX(-50%); white-space: nowrap; }
.trend__xaxis span:first-child { transform: none; }
.trend__xaxis span:last-child { transform: translateX(-100%); }

.table-view > summary {
  cursor: pointer;
  font-size: 0.86rem;
  color: var(--ink-500);
  padding: var(--space-2) 0;
  min-height: 40px;
  display: flex;
  align-items: center;
}
.table-view > summary:hover { color: var(--ink-900); }
.table-view[open] > summary { margin-bottom: var(--space-2); }

/*
   A table view is not a footnote — for the play radars it is the *equivalent*
   path to every case a dot links to, which is the only reason a 17px dot on a
   57-axis radar is defensible. So its links have to clear the tap-target floor
   too, or the accessible alternative is less reachable than the thing it is an
   alternative to.

   The anchor eats the cell's own padding back rather than adding to it: the
   link box grows from 14px to the full 48px cell, and the row height does not
   change. That matters at 57 rows.
*/
.table-view tbody a {
  display: flex;
  align-items: center;
  min-height: 40px;
  margin-block: calc(var(--space-3) * -1);
  padding-block: var(--space-3);
}

/* --------------------------------------------------------------------- */
/* Lists: splits, coverage, activity                                      */
/* --------------------------------------------------------------------- */

.split-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.split-list li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  font-size: 0.92rem;
  border-bottom: 1px solid var(--ink-100);
  min-height: 40px;
}
.split-list li:last-child { border-bottom: 0; }
.split-list b { font-variant-numeric: tabular-nums; color: var(--ink-900); }
.split-list b.is-empty { color: var(--ink-300); font-weight: 500; }
.split-list .ratio { font-size: 0.76rem; color: var(--ink-400); white-space: nowrap; }

.coverage { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-4); }
.coverage__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: 0.92rem;
  margin-bottom: var(--space-2);
}
.coverage__count { font-size: 0.8rem; color: var(--ink-400); font-variant-numeric: tabular-nums; }
.coverage__track {
  display: flex;
  height: 10px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--ink-100);
}
.coverage__seg { display: block; height: 100%; background: var(--seg-color, var(--ink-300)); }
/* Three disjoint buckets on one bar: solid = measured, tinted = claimed,
   hatched = unknown. Same hue throughout, so the phase stays identifiable. */
.coverage__seg--assessed { opacity: 0.42; }
.coverage__seg--untouched {
  background:
    repeating-linear-gradient(45deg, var(--ink-200) 0 3px, var(--ink-100) 3px 6px);
}
.coverage__note { margin: var(--space-2) 0 0; font-size: 0.82rem; color: var(--ink-400); }

.activity { list-style: none; margin: 0; padding: 0; }
.activity li {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--ink-100);
  font-size: 0.9rem;
  min-height: 40px;
}
.activity li:last-child { border-bottom: 0; }
.activity b {
  font-variant-numeric: tabular-nums;
  color: var(--ink-900);
  min-width: 4.2rem;
}
.activity b.is-dnf { color: var(--danger); }
.activity__when { color: var(--ink-400); font-size: 0.8rem; margin-left: auto; }
.activity__splits {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  font-size: 0.76rem;
  color: var(--ink-500);
  font-variant-numeric: tabular-nums;
}
.activity__splits > span { display: inline-flex; align-items: center; gap: 4px; }
.activity__splits .dot { width: 7px; height: 7px; min-width: 7px; }

.local-note {
  color: var(--ink-400);
  font-size: 0.82rem;
  text-align: center;
  margin: var(--space-4) 0 0;
}

/* --------------------------------------------------------------------- */
/* Profiles                                                               */
/* --------------------------------------------------------------------- */

.profile-card { display: flex; flex-direction: column; gap: var(--space-4); }
.profile-card.is-current { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

.profile-card__head { display: flex; align-items: center; gap: var(--space-3); }
.profile-card__avatar {
  width: 42px;
  height: 42px;
  min-width: 42px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent-soft);
  color: #2a41d8;
  font-weight: 700;
  font-size: 1.05rem;
}

.profile-card__stats,
.profile-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(74px, 1fr));
  gap: var(--space-3);
  margin: 0;
}
.profile-card__stats dt,
.profile-facts dt {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-400);
  font-weight: 640;
}
.profile-card__stats dd,
.profile-facts dd {
  margin: 0;
  font-size: 1.2rem;
  font-weight: 640;
  color: var(--ink-900);
  font-variant-numeric: tabular-nums;
}

.profile-card--new { border-style: dashed; background: var(--ink-050); box-shadow: none; }

.profile-export { margin-top: var(--space-4); }

/* --- per-profile settings ------------------------------------------- */

.setting-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 260px);
  gap: var(--space-3) var(--space-5);
  align-items: start;
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--ink-100);
}
.setting-row:last-of-type { border-bottom: 0; }
.setting-row__text { max-width: 56ch; }
.setting-row__label { font-size: 0.98rem; font-weight: 600; color: var(--ink-900); }
.setting-row__hint { margin: 2px 0 0; font-size: 0.85rem; color: var(--ink-500); }
.setting-row__default { margin: var(--space-2) 0 0; font-size: 0.78rem; color: var(--ink-400); }

.danger-zone { border-color: var(--danger-soft); background: #fffafb; }
.danger-zone h2 { color: var(--danger); }
.danger-zone__row { display: flex; flex-direction: column; gap: var(--space-3); padding-block: var(--space-3); }
.danger-zone__row + .danger-zone__row { border-top: 1px solid var(--ink-100); }
.danger-zone form { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: flex-end; }
.danger-zone .field { flex: 1 1 220px; }

/* --------------------------------------------------------------------- */
/* Phone                                                                  */
/* --------------------------------------------------------------------- */

@media (max-width: 720px) {
  .next-up { padding: var(--space-5) var(--space-4); }
  .next-up__actions .btn { flex: 1 1 100%; }

  .split-bars__row {
    grid-template-columns: minmax(0, 1fr) 3.4rem;
    grid-template-areas: "label label" "track total";
    row-gap: var(--space-1);
  }
  .split-bars__label { grid-area: label; }
  .split-bars__track { grid-area: track; }
  .split-bars__total { grid-area: total; }

  .trend__plot { grid-template-columns: 2.1rem minmax(0, 1fr); }
  .trend__xaxis { margin-left: calc(2.1rem + var(--space-2)); }
  .trend__legend { width: 100%; }

  .setting-row { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------------------- */
/* Radar hover label                                                      */
/* --------------------------------------------------------------------- */

.spider { position: relative; }

/* The real hover target. A 4px dot is not something anyone can point at, so
   an invisible 14px disc carries the interaction and the dot stays small. */
.spider__hit {
  fill: transparent;
  cursor: pointer;
}

.spider__point { outline: none; }
/* The play radars make these real <a href> elements, so the shared link colour
   and the underline-on-hover from base.css come along and mean nothing inside
   an SVG. Neither is wanted here — the dot is the affordance. */
.spider--plays .spider__point { text-decoration: none; }

.spider__point:hover .spider__node,
.spider__point:focus-visible .spider__node {
  r: 6;
  stroke-width: 3;
}
/* On a 57-axis radar a 6-unit dot on hover nearly touches its neighbours, so
   the emphasis is scaled to the spacing the server computed rather than fixed. */
.spider--plays .spider__point:hover .spider__node,
.spider--plays .spider__point:focus-visible .spider__node {
  r: calc(var(--spider-hit, 8px) * 0.7);
  stroke-width: 2;
}

.spider__point:focus-visible .spider__hit {
  fill: rgba(61, 90, 254, 0.1);
  stroke: var(--accent);
  stroke-width: 1.5;
}

.spider__tip {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 5;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  background: var(--ink-900);
  color: #fff;
  font-size: 0.8rem;
  line-height: 1.3;
  white-space: nowrap;
  box-shadow: var(--shadow-md);
  will-change: transform;
}

/* `display: flex` above outranks the UA's `[hidden] { display: none }`, so
   without this the `hidden` attribute is inert and the tooltip never goes
   away when the pointer leaves the dot. Any class that sets `display` has to
   restate this. */
.spider__tip[hidden] { display: none; }

.spider__tip b { font-weight: 640; }
.spider__tip span {
  color: var(--ink-300);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------- */
/* Tap targets                                                            */
/* --------------------------------------------------------------------- */
/*
   base.css sizes `.btn` from its padding, which lands at 37px, and `.btn--sm`
   at 30px — both under the 40px floor a thumb needs. base.css is the lead's
   file, so the floor is raised here for the pages this stylesheet serves.
   The proper fix is the same two lines in base.css; until then these three
   module stylesheets carry it.

   Inline links inside a list row are stretched to the row instead of being
   given padding of their own: the row is already 40px, so the target only
   looks small because the <a> box hugs its text.
*/

.btn { min-height: 44px; }
.btn--sm { min-height: 40px; }
.btn--icon { width: 44px; height: 44px; }

input[type="text"],
input[type="number"],
input[type="search"],
input[type="url"],
select {
  min-height: 44px;
}

[data-trend-range] a { min-height: 40px; }

/* "Full spider →", "Timer →" and friends: a hint styled as a link is still a
   link, and a 21px one is not something a thumb can hit. */
a.card__hint,
.card__hint a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
}

.split-list li a,
.axis-list li a,
.activity li a {
  display: flex;
  align-items: center;
  align-self: stretch;
  min-height: 40px;
}

.coverage__note a,
.profile-export a,
.local-note a {
  display: inline-block;
  padding-block: 10px;
}

/* The radar's visible dot stays 4px; its invisible hit disc is what the
   pointer and the thumb actually aim at. */
/* Four axes, four dots, acres of space between them — so the hit disc can be a
   full thumb wide. Scoped away from the play radars, where 21px would swallow
   about eight neighbours: those set `--spider-hit` per radar from the real dot
   spacing, which the server already computes. */
.spider:not(.spider--plays) .spider__hit { r: 21px; }
.spider--plays .spider__hit { r: var(--spider-hit, 8px); }

/* --------------------------------------------------------------------- */
/* Radar drill-down                                                       */
/* --------------------------------------------------------------------- */

/* The dots are buttons now, so say so on hover. */
.spider__point[data-phase] .spider__hit { cursor: pointer; }

.spider__point[aria-expanded="true"] .spider__node {
  r: 7;
  stroke: var(--accent);
  stroke-width: 3;
}

.spider-expand {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--ink-200);
}

.spider-expand__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

.spider-expand__head h3 {
  margin: 0;
  font-size: 0.95rem;
  flex: 1 1 auto;
}

.spider-expand__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  /* 57 OLL cases would run the card off the page; scroll the list, not the
     dashboard. */
  max-height: 340px;
  overflow-y: auto;
}

.spider-expand__list li {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--ink-100);
  font-size: 0.9rem;
}
.spider-expand__list li:last-child { border-bottom: 0; }

.spider-expand__name {
  flex: 0 0 auto;
  min-width: 9rem;
  font-weight: 560;
}

.spider-expand__list .alg {
  font-size: 0.82rem;
  overflow-wrap: anywhere;
}

@media (max-width: 620px) {
  .spider-expand__list li { flex-direction: column; gap: 4px; }
  .spider-expand__name { min-width: 0; }
}

/* --------------------------------------------------------------------- */
/* The full radar, in a modal                                             */
/* --------------------------------------------------------------------- */

/* The chart background is a target now, so hint it — but only there; the
   points keep the pointer cursor they already had. */
.spider:not(.spider--modal) svg { cursor: zoom-in; }
.spider:not(.spider--modal) .spider__point .spider__hit { cursor: pointer; }

.spider__zoom { margin-top: var(--space-3); align-self: center; }

.spider-modal {
  width: min(92vw, 900px);
  max-width: 92vw;
  max-height: 92vh;
  padding: var(--space-5);
  border: 1px solid var(--ink-200);
  border-radius: var(--radius-lg);
  background: var(--paper);
  box-shadow: var(--shadow-lg);
  color: var(--ink-700);
  overflow: auto;
}

.spider-modal::backdrop {
  background: rgba(20, 22, 26, 0.45);
  backdrop-filter: blur(2px);
}

.spider-modal__head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-2) var(--space-4);
  align-items: start;
  margin-bottom: var(--space-4);
}

.spider-modal__title { margin: 0; }

.spider-modal__hint {
  grid-column: 1;
  margin: 0;
  font-size: 0.88rem;
  color: var(--ink-500);
  max-width: 60ch;
}

.spider-modal__head .btn { grid-column: 2; grid-row: 1; }

/* Give the exploded 57-case view room to breathe — this is the whole reason
   the modal exists rather than growing the card.
   The card's own rules cap the chart at 560px and pad it heavily, which left
   a 260px radar floating in a 900px dialog. Undo both here and centre it. */
.spider--modal .spider__chart {
  max-width: none;
  padding: 0;
  min-height: 0;
  display: flex;
  justify-content: center;
}

.spider--modal .spider__svg {
  /* Sized against the *remaining* height: the modal header, breadcrumb,
     readout and the numbers toggle take roughly 300px, so a chart keyed to
     the full viewport height pushed its own bottom below the fold. */
  width: min(calc(92vh - 300px), 560px);
  min-width: 260px;
  max-width: 100%;
  margin-inline: auto;
}

@media (max-width: 620px) {
  .spider-modal { width: 96vw; padding: var(--space-4); }
  .spider--modal .spider__chart { min-height: 56vh; }
}

/* Without the dialog element, the polyfill path shows it as a plain panel
   rather than leaving it invisible. */
.spider-modal:not([open]) { display: none; }

/* Case rows in the radar's "Show the numbers" table carry the starting
   position and link to the case, the same affordance as the library's todo
   list — so the table is a way in, not just a readout. */
.spider__rowcase {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 560;
  color: var(--ink-900);
  min-height: 40px;
}
.spider__rowcase:hover { color: var(--accent); text-decoration: none; }
.spider__rowcase:hover .spider__rowthumb { border-color: var(--accent); }

.spider__rowthumb {
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  border: 1px solid var(--ink-200);
  border-radius: 5px;
  background: var(--paper);
  transition: border-color var(--dur-fast) var(--ease);
}
