/* ULTRABENCH — intelligence-vs-cost page (scatter, value table). The model-detail
   page's `.md-*` rules live in model-detail.css. Tokens come from base.css; nothing here
   loads a font, a library or a CDN asset. */

/* ---------- shared insight furniture ---------- */

.insight-panel { display: flex; flex-direction: column; }
.insight-note {
  margin: 10px 0 0; color: var(--text-faint); font-size: 12px; line-height: 1.5;
}
/* the cost chart's reading guide sits between the panel heading and the chart: a one-line
   lede, then "How to read", collapsed (VF-220) */
.scatter-guide { margin: 0 0 4px; }
.scatter-how { margin: 0 0 10px; font-size: 12px; color: var(--text-faint); }
.scatter-how > summary { display: inline-block; cursor: pointer; color: var(--text-dim); padding: 2px 0; }
.scatter-how > summary:hover { color: var(--text); }
.scatter-how-text { margin: 4px 0 0; line-height: 1.5; }
@media (pointer: coarse) {
  .scatter-how > summary { padding: 10px 0; }
}
/* A subhead inside a panel (UB-354): the level under the panel title, set smaller and
   fainter than it. The line-height is the block's own, as the h4 it replaces inherited. */
.insight-subhead {
  font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-faint); margin: 0 0 6px; line-height: inherit;
}

/* ---------- cost scatter ---------- */

.scatter-wrap { width: 100%; }
.scatter-svg {
  display: block; width: 100%; height: auto;
  background: var(--bg-inset);
  border: 1px solid var(--line); border-radius: var(--radius-s);
}
/* the chart is one tab stop; while the keyboard walks its dots, the frame says so */
.scatter-svg:has(.scatter-dot:focus-visible) { outline: 1px solid var(--accent); outline-offset: -1px; }

.scatter-grid   { stroke: var(--line); stroke-width: 1; }
.scatter-tick   { fill: var(--text-faint); font-family: var(--mono); font-size: 11px; }
.scatter-axis   { fill: var(--text-dim); font-family: var(--sans); font-size: 12px; letter-spacing: .04em; }
/* The halo (stroke painted UNDER the fill) keeps a label readable where the frontier
   line or a grid line runs through it — without it the dashes strike the text out. */
.scatter-label {
  fill: var(--text-dim); font-family: var(--sans); font-size: 11px;
  paint-order: stroke fill; stroke: var(--bg-inset); stroke-width: 3px;
  stroke-linejoin: round; stroke-linecap: round;
  /* labels are never interactive — and opacity:0 alone leaves the default
     visiblePainted hit target, dead-zoning the dots beneath a quiet label */
  pointer-events: none;
}
/* Hover-reveal: every label is visible by default (owner directive 2026-08-18);
   hovering a dot hides all labels except the hovered model's. Source order carries the
   cascade: `.scatter-label.is-active` ties `.is-hovering .scatter-label` at (0,2,0), so
   it must come LAST to beat the hider without !important (`.is-hovering` = SVG root). */
.is-hovering .scatter-label { opacity: 0; }
.scatter-label.is-active { opacity: 1; }
.scatter-dot    { stroke: var(--bg-inset); stroke-width: 1; cursor: pointer; }
.scatter-dot:hover, .scatter-dot:focus { stroke: var(--text); stroke-width: 1.5; r: 6.5px; }
/* Squares, diamonds and triangles are paths: `r` means nothing to them, so they grow by
   scaling about their own box — the same 1.3× a circle's r 5 → 6.5 gives. */
path.scatter-dot { transform-box: fill-box; transform-origin: center; }
path.scatter-dot:hover, path.scatter-dot:focus { transform: scale(1.3); }
/* Free listings sit ON the axis, not at a price the log scale can hold — the hollow ring
   says "no price position" rather than pretending to one. The ring is the vendor colour,
   carried on the mark's `color` attribute: the `.scatter-dot` stroke above would
   otherwise paint it in the chart background and the ring would vanish. */
.scatter-dot-free { fill: none; stroke: currentColor; stroke-width: 2; }
.scatter-dot-free:hover, .scatter-dot-free:focus { stroke-width: 3; }
/* A superseded model stays on the chart, faded (UB-253) — never hollow, which already
   means free. Named (hover, focus, a tap), it reads at full strength. */
.scatter-dot-sup { opacity: .45; }
.scatter-dot-sup:hover, .scatter-dot-sup:focus, .scatter-dot-sup.is-picked { opacity: 1; }
/* Better than (spec 10): dots outside the query sit back, the reference is ringed. */
.scatter-dot-out { opacity: .2; }
.scatter-dot-out:hover, .scatter-dot-out:focus, .scatter-dot-out.is-picked { opacity: 1; }
/* The yardstick reads at full strength even when superseded, ringed in the text colour: an
   accent ring vanished on the vendors whose own colour is the accent hue. Hover, focus and a
   tap (.scatter-dot:hover and .scatter-dot.is-picked are 0,2,0) never thin the ring. */
.scatter-dot-ref, .scatter-dot-sup.scatter-dot-ref { opacity: 1; stroke: var(--text); stroke-width: 2.5; }
.scatter-dot-ref:hover, .scatter-dot-ref:focus, .scatter-dot.scatter-dot-ref.is-picked { stroke-width: 3; }
/* A free reference keeps its hollow shape (hollow = free); only its ring turns text-coloured. */
.scatter-dot-free.scatter-dot-ref { stroke-width: 3; }
/* A dimmed dot's label, and a qualifying one with no free spot in a dense cluster, show only
   while their dot is the active one (.scatter-label.is-active, 0,2,0), so names never overlap. */
.scatter-label-out, .scatter-label-crowded { opacity: 0; }
.scatter-frontier {
  fill: none; stroke: var(--accent); stroke-width: 1.5;
  stroke-dasharray: 5 4; opacity: .55;
}

/* The touch pick: a first tap names a dot here, under the chart; its link is the real
   44px target (a dot in a 237-point cloud cannot be one), and nearby dots re-pick. */
.scatter-dot.is-picked { stroke: var(--text); stroke-width: 2; }
.scatter-pick {
  margin-top: 8px; padding: 8px 12px;
  background: var(--bg-inset); border: 1px solid var(--line); border-radius: var(--radius-s);
  font-size: 13px; color: var(--text-dim);
}
.scatter-pick[hidden] { display: none; }
.scatter-pick-name { color: var(--text); font-weight: 600; }
.scatter-pick-meta { font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums; }
.scatter-pick-open { display: inline-flex; align-items: center; min-height: 44px; }
.scatter-pick-near { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding-bottom: 4px; }
.scatter-pick-near .muted { font-size: 12px; margin-right: 2px; }
.scatter-pick-near button {
  min-height: 32px; padding: 4px 10px; font: inherit; font-size: 12px; color: var(--text-dim);
  background: var(--bg-raised); border: 1px solid var(--line-strong); border-radius: var(--radius-s);
  cursor: pointer;
}

.scatter-legend {
  list-style: none; margin: 10px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 6px 14px;
  font-size: 12px; color: var(--text-dim);
}
.scatter-legend li { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.legend-mark { display: inline-block; flex: none; overflow: visible; }

/* ranked models with no plottable price — named under the legend so every indexed
   model is represented on this page. Each name + index pair is one atomic list item, so a
   wrap can never strand an index beside the next model's name; a very long name wraps
   inside its own item, its index still with it. */
.scatter-unpriced {
  margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px;
  font-size: 13px; line-height: 1.8; color: var(--text-dim);
}
.scatter-unpriced-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0 14px;
}
.scatter-unpriced-list li { max-width: 100%; min-width: 0; }
.scatter-unpriced .muted {
  font-family: var(--mono); font-size: 11px; white-space: nowrap; font-variant-numeric: tabular-nums;
}

/* ---------- value table ---------- */

.value-ratio { color: var(--accent); }

.value-free { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; }
.value-free-list { list-style: none; margin: 0; padding: 0; font-size: 13px; }
.value-free-list li {
  display: flex; justify-content: space-between; gap: 12px; padding: 3px 0;
}
.value-free-n { font-family: var(--mono); font-variant-numeric: tabular-nums; color: var(--text-dim); }

/* ---------- intelligence-vs-cost page ---------- */

.cost-page svg { max-width: 100%; height: auto; }
/* The value table spans the page like the chart; its number columns shrink to their figures
   and the model column takes the rest, so the three figures of a row read as one group. */
.cost-value table.data .num { width: 1%; }

/* ---------- responsive ---------- */

@media (prefers-reduced-motion: no-preference) {
  .scatter-label { transition: opacity .12s ease; }
}

@media (max-width: 900px) {
  .scatter-legend { font-size: 11px; }
}

/* Phones draw the chart in its own 420x520 portrait frame (compact), which renders at
   ~0.81x of its user units, so the type is set a little UP in user units to land near its
   desktop size. Resting labels are dropped rather than shrunk — the dot cloud, the axes
   and the frontier still carry the shape — and the one .is-active label (hover, focus or
   a first tap) re-enters beside its dot. */
@media (max-width: 560px) {
  .scatter-label { display: none; }
  .scatter-label.is-active { display: block; font-size: 15px; }
  .scatter-tick { font-size: 13px; }
  .scatter-axis-x { font-size: 14px; }
  /* the rotated y title would sit on top of the y ticks in the narrow left band */
  .scatter-axis-y { display: none; }
  .scatter-dot { r: 5.5px; }
  /* out-cascades the global hover/focus bump (same specificity, later in file) */
  .scatter-dot:hover, .scatter-dot:focus { r: 7px; }
  path.scatter-dot { transform: scale(1.1); }
  path.scatter-dot:hover, path.scatter-dot:focus { transform: scale(1.4); }
  .scatter-frontier { stroke-width: 2; stroke-dasharray: 6 5; }
  .scatter-grid { stroke-width: 1; }
}
