/* charts.css — dot-plot profile charts, dumbbell change view, band tiles (Task 7).
   Uses the token set already defined in styles.css: --l1/--l2/--l3, --tec/--eva/--pra/--eth/
   --dat, --mid, --border, --card-bg, --on-accent, --dark, --danger. All class names here are
   new (dp-*, db-*, bt-*) so nothing in styles.css needs to change or is overridden. */

:root {
  --dp-label-w: 12rem;
  --dp-score-w: 4.4rem;
  --dp-pill-w: 6.6rem;
  --dp-gap: .6rem;
  --dp-mark-size: 12px;
}

/* ---------------------------------------------------------------------------------------- */
/* Shared row grid — dotplot rows, dumbbell rows and their header strips all line up on the  */
/* same label / track / meta columns via matching flex-basis, the same trick styles.css uses */
/* for .band-legend against .bar-row.                                                        */
/* ---------------------------------------------------------------------------------------- */

.dotplot, .dumbbell { margin-block: 1rem; }

.dp-axis, .dp-bands, .dp-row,
.db-row {
  display: flex;
  align-items: center;
  gap: var(--dp-gap);
  padding-block: .35rem;
}

.dp-row + .dp-row, .db-row + .db-row { border-block-start: 1px solid var(--border); }

.dp-rows, .db-rows { list-style: none; margin: 0; padding: 0; }

.dp-label, .db-label {
  /* Shrinkable (flex-shrink:1, min-width:0), not the fixed flex-shrink:0 basis this used to
     carry: at ~601-767px the fixed rem widths of .dp-label/.dp-score/.dp-pill (and .db-change/
     .db-level below) together exceeded the available row width and forced page-level horizontal
     scroll — plain flex-basis rem values with no ability to shrink. Letting every column shrink
     proportionally (rather than adding another breakpoint) keeps one row layout all the way from
     this width up to desktop; the <600px breakpoint below still wraps to two lines regardless. */
  flex: 0 1 var(--dp-label-w);
  min-width: 0;
  font-size: .85rem;
  border-inline-start: 4px solid var(--mid);
  padding-inline-start: .5rem;
  line-height: 1.25;
}
.dp-label.TEC, .db-label.TEC { border-inline-start-color: var(--tec); }
.dp-label.EVA, .db-label.EVA { border-inline-start-color: var(--eva); }
.dp-label.PRA, .db-label.PRA { border-inline-start-color: var(--pra); }
.dp-label.ETH, .db-label.ETH { border-inline-start-color: var(--eth); }
.dp-label.DAT, .db-label.DAT { border-inline-start-color: var(--dat); }
.dp-label.ROLE, .db-label.ROLE { border-inline-start-color: var(--navy, var(--mid)); }

.dp-track, .db-track { flex: 1 1 auto; position: relative; height: 1.6rem; }

.dp-score { flex: 0 1 var(--dp-score-w); min-width: 2.6rem; font-variant-numeric: tabular-nums; font-size: .85rem; white-space: nowrap; }
.dp-pill { flex: 0 1 var(--dp-pill-w); min-width: 0; }

/* ---------------------------------------------------------------------------------------- */
/* Axis + band header strips                                                                 */
/* ---------------------------------------------------------------------------------------- */

.dp-axis { font-size: .75rem; color: var(--mid); }
.dp-axis-ticks, .dp-bands-track { position: relative; }
/* inset-inline-start here is set per-tick as an inline style (its value is data, not a fixed
   ratio), so no custom property is needed on this rule. The base centring transform is a small,
   direction-independent cosmetic nudge (see the mark-centring note below for why a fixed-size,
   known-position element like a mark needs the margin-inline-start technique instead — these
   ticks use the simpler transform because their overflow is what's corrected explicitly per
   edge below, not their centring). */
.dp-tick { position: absolute; transform: translateX(-50%); }
/* Edge ticks (value 1 and value 4) are snapped flush so their label doesn't overflow the track,
   rather than centred on the exact edge point (which would hang half the label off the end).
   :first-child (value 1, inset-inline-start:0%) resolves physically to `left:0` in LTR and
   `right:0` in RTL; either way the box's un-transformed edge already lands ON the container edge
   and grows inward (toward the far/other end) from there — translateX(0) is a no-op that keeps
   that placement, correct in both directions without an override.
   :last-child (value 4, inset-inline-start:100%) is the one that needs a direction-aware fix:
   at `left:100%` (LTR) the box grows outward past the *right* edge, so translateX(-100%) pulls
   it back left, flush inside; at `right:100%` (RTL) the box instead grows outward past the
   *left* edge, so it needs the opposite pull — translateX(+100%), back right, flush inside. */
.dp-axis .dp-tick:first-child { transform: translateX(0); }
.dp-axis .dp-tick:last-child { transform: translateX(-100%); }
[dir="rtl"] .dp-axis .dp-tick:last-child { transform: translateX(100%); }

/* Grouped band legend (spec §9, band-labelling owner decision): a top row of the three Standard
   levels — L1B and L1A share one "L1 Foundation" span — and a second row, under that L1 span
   only, of the plain-language "beginning" | "advanced" qualifiers. Un-labelled space to the
   right of the subs (under L2/L3, which have no sub-band) is a blank spacer of matching width. */
.dp-bands { font-size: .68rem; color: var(--mid); border-block-end: 1px solid var(--border); padding-block-end: .4rem; margin-block-end: .25rem; }
.dp-bands-track { display: flex; flex-direction: column; gap: .2rem; }
.dp-band-groups, .dp-band-subs { display: flex; }
.dp-band-group, .dp-band-sub, .dp-band-sub-spacer {
  box-sizing: border-box;
  border-inline-end: 1px dashed var(--border);
  padding-inline-start: .25rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dp-band-groups > :last-child, .dp-band-subs > :last-child { border-inline-end: none; }
/* Each group box is a percentage of the axis (60% / 23.33% / 16.67%) but the label inside it is
   a fixed-width string, so on a narrow track a box can be far too small for its own label — this
   track tops out around 390px, and a 23.33% span does not hold "L2 Intermediate" until the track
   passes ~394px. Clipping to the text-overflow:ellipsis above would drop the level name, so the
   label is allowed to overflow its box instead (white-space:nowrap already applies from the
   shared rule) — but overflowing inline text always spills toward the inline END of its box
   whatever text-align says, since alignment only distributes *positive* free space. An
   overflowing group therefore prints straight over the label of the group after it, which is
   harmless only for the LAST group: it has no next label, and spills past the end of the track
   into the empty .dp-score/.dp-pill placeholder cells (>=145px of blank space at every width
   above the 600px breakpoint below).
   So give every group but the last a floor of max-content: a box too small for its label widens
   to fit and pushes the groups after it along, instead of printing on top of them. flex-shrink:0
   keeps that widening from being handed straight back — with the row now wider than the track,
   flex would otherwise shrink the one group that has slack, and the first group's span is the
   one the sub-band row underneath lines up against. Widths only ever grow, so that span and the
   sub-row under it stay exact; the cost is that the boundary after a widened group, and the end
   of the strip, sit further along than the band boundary they mark — ~1px at the widest track,
   ~34px at the narrowest, into blank space either way.
   Verified with no clipping and no overlap at 601/660/700/750/820/900/1280/1600px, LTR and RTL.
   The previous rule kept the boxes at exact percentages and end-aligned the last group, on the
   theory that alignment would steer the overflow toward the track's own end; it does not, and
   from ~600px to ~870px — where the track is too narrow for the middle group's box — "L2
   Intermediate" ran up to ~28px over "L3 Advanced". */
.dp-band-group { overflow: visible; padding-inline-end: .25rem; flex-shrink: 0; }
.dp-band-groups > :not(:last-child) { min-width: max-content; }
/* Inert for the default legend, whose last label never fits its own 16.67% box — but if a caller
   passes a shorter one via opts.legend, sit it at the end of the track rather than leaving it
   stranded mid-band. */
.dp-band-groups > :last-child { text-align: end; }
.dp-band-group:nth-child(1) { background: color-mix(in srgb, var(--l1) 16%, transparent); font-weight: 600; }
.dp-band-group:nth-child(2) { background: color-mix(in srgb, var(--l2) 16%, transparent); font-weight: 600; }
.dp-band-group:nth-child(3) { background: color-mix(in srgb, var(--l3) 16%, transparent); font-weight: 600; }
.dp-band-sub:nth-child(1) { background: color-mix(in srgb, var(--l1) 8%, transparent); }
.dp-band-sub:nth-child(2) { background: color-mix(in srgb, var(--l1) 16%, transparent); }
.dp-band-sub-spacer { background: transparent; border-inline-end: none; }

/* ---------------------------------------------------------------------------------------- */
/* Track background stripes — one gradient layer per row, matching the .dp-band colours.    */
/* Drawn as a physical left-to-right gradient (CSS has no logical "to inline-end" keyword),  */
/* so it is flipped in RTL via a transform on this layer only — see the RTL block below.     */
/* ---------------------------------------------------------------------------------------- */

.dp-track-bg {
  position: absolute;
  inset: 0;
  border-radius: 4px;
  background: linear-gradient(
    to right,
    color-mix(in srgb, var(--l1) 8%, transparent) 0 33.33%,
    color-mix(in srgb, var(--l1) 16%, transparent) 33.33% 60%,
    color-mix(in srgb, var(--l2) 13%, transparent) 60% 83.33%,
    color-mix(in srgb, var(--l3) 13%, transparent) 83.33% 100%
  );
}

.dp-rule {
  position: absolute;
  inset-block: 0;
  border-inline-start: 1px dashed var(--border);
}

/* ---------------------------------------------------------------------------------------- */
/* Marks — dots, rings, whiskers                                                             */
/* ---------------------------------------------------------------------------------------- */

/* Centring a fixed-size mark on its inset-inline-start point: horizontal centring is done with
   margin-inline-start (a logical property, so it reads correctly from whichever physical edge
   "start" is), not transform. transform's X axis is always physical — translateX(-50%) shifts
   left regardless of direction, so at an RTL-start point (which is the container's right edge)
   it would shift the mark further right, off-centre by a full mark-width, rather than centring
   it. Vertical centring has no direction dependency, so translateY(-50%) is fine as-is. */
.dp-mark, .db-mark, .bt-mark {
  position: absolute;
  top: 50%;
  inset-inline-start: var(--pct, 0%);
  margin-inline-start: calc(var(--dp-mark-size) / -2);
  transform: translateY(-50%);
  width: var(--dp-mark-size);
  height: var(--dp-mark-size);
  border-radius: 50%;
  background: var(--dark);
  border: 2px solid var(--card-bg);
  box-shadow: 0 0 0 1px var(--dark);
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

.dp-mark.hollow { background: var(--card-bg); }
.dp-mark.ring { background: transparent; border: 2px solid var(--dark); box-shadow: none; }
.dp-mark.hidden { display: none; }
.bt-mark.hidden { display: none; }

/* --start/--width are pre-clamped to the [0,100] axis in JS (js/ui/charts.js's dotPlotRow), not
   a symmetric ±half-width calc() here — a calc() would still overrun the track at either end of
   the scale (e.g. score 1.00 with a wide mdc), running past the axis and into the score cell. */
.dp-whisker {
  position: absolute;
  top: 50%;
  inset-inline-start: var(--start, 0%);
  width: var(--width, 0%);
  height: 2px;
  transform: translateY(-50%);
  background: var(--mid);
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

.dp-chip {
  display: inline-block;
  font-size: .7rem;
  color: var(--mid);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 .4rem;
  margin-inline-start: .25rem;
}

/* ---------------------------------------------------------------------------------------- */
/* Level pills — distinct L1B/L1A shades so the two Foundation sub-bands read differently.    */
/* ---------------------------------------------------------------------------------------- */

.dp-pill, .bt-pill {
  display: inline-block;
  font-size: .75rem;
  font-weight: 600;
  padding: .1rem .5rem;
  border-radius: 999px;
  color: var(--on-accent);
  text-align: center;
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}
.dp-pill.L1B, .bt-pill.L1B { background: color-mix(in srgb, var(--l1) 88%, black); }
.dp-pill.L1A, .bt-pill.L1A { background: var(--l1); }
.dp-pill.L2, .bt-pill.L2 { background: var(--l2); }
.dp-pill.L3, .bt-pill.L3 { background: var(--l3); }

/* ---------------------------------------------------------------------------------------- */
/* Dumbbell — connector, prev/now marks, change + level-change columns                       */
/* ---------------------------------------------------------------------------------------- */

.db-change { flex: 0 1 9rem; min-width: 0; font-variant-numeric: tabular-nums; font-size: .85rem; }
.db-level { flex: 0 1 12rem; min-width: 0; font-size: .8rem; color: var(--mid); }
.db-change.db-meaningful { font-weight: 700; color: var(--dark); }
.db-flat-note { font-weight: 400; color: var(--mid); }

/* MDC surfaced visibly per row (not only in an sr-only string) — a small chip naming the
   threshold that made this row's change count as meaningful. */
.db-meaningful-tag {
  display: inline-block;
  font-size: .68rem;
  font-weight: 600;
  color: var(--dark);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 .4rem;
  margin-inline-start: .3rem;
  white-space: normal;
}

.db-connector {
  position: absolute;
  top: 50%;
  inset-inline-start: var(--from, 0%);
  width: var(--width, 0%);
  height: 2px;
  transform: translateY(-50%);
  background: var(--mid);
}
.db-connector.up { background: var(--ok, var(--l3)); }
.db-connector.down { background: var(--danger); }
.db-connector.flat { background: var(--mid); }

.db-mark.db-prev { background: var(--card-bg); border-color: var(--mid); box-shadow: 0 0 0 1px var(--mid); }
.db-mark.db-now { background: var(--dark); }

/* ---------------------------------------------------------------------------------------- */
/* Band tiles — compact summary cards                                                        */
/* ---------------------------------------------------------------------------------------- */

.bandtiles { margin-block: 1rem; }
.bt-tiles {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: .75rem;
}
.bt-tile {
  border: 1px solid var(--border);
  border-inline-start-width: 4px;
  border-radius: 8px;
  background: var(--card-bg);
  padding: .6rem .7rem;
  display: flex;
  flex-direction: column;
  gap: .4rem;
}
.bt-tile.TEC { border-inline-start-color: var(--tec); }
.bt-tile.EVA { border-inline-start-color: var(--eva); }
.bt-tile.PRA { border-inline-start-color: var(--pra); }
.bt-tile.ETH { border-inline-start-color: var(--eth); }
.bt-tile.DAT { border-inline-start-color: var(--dat); }

.bt-name { font-size: .8rem; font-weight: 600; }
.bt-track { position: relative; height: 1.1rem; background: var(--light, var(--body-bg)); border-radius: 4px; }

/* ---------------------------------------------------------------------------------------- */
/* Responsive — label drops to its own line under 600px                                      */
/* ---------------------------------------------------------------------------------------- */

@media (max-width: 600px) {
  .dp-row, .db-row {
    flex-wrap: wrap;
  }
  .dp-row .dp-label, .db-row .db-label {
    flex: 1 1 100%;
  }
  .dp-row .dp-track { order: 2; flex: 1 1 auto; }
  .dp-row .dp-score { order: 3; }
  .dp-row .dp-pill { order: 4; }
  .db-row .db-track { order: 2; flex: 1 1 auto; }
  .db-row .db-change { order: 3; }
  .db-row .db-level { order: 4; flex: 1 1 100%; }

  /* Spec §9 requires the numeral axis (1·2·3·4) and the grouped band legend visible at every
     width — never display:none, even here. The .dp-label/.dp-score/.dp-pill cells in these two
     header strips are empty placeholders that exist only to line the strip up with the data rows
     above/below at wide widths (see renderAxisHeader/renderLegend in charts.js); with nothing to
     line up with them under the data rows' own wrap, they'd just be blank space, so they're
     dropped and the ticks/legend track is allowed to take the row's full width instead. */
  .dp-axis .dp-label, .dp-axis .dp-score, .dp-axis .dp-pill,
  .dp-bands .dp-label, .dp-bands .dp-score, .dp-bands .dp-pill {
    display: none;
  }
  .dp-axis .dp-track, .dp-bands .dp-track { flex: 1 1 100%; }

  /* The band legend's group/sub widths are percentages of the (now full-width) track and stay
     accurate, but the fixed dashed-column layout reads cramped at this width — reflow it into a
     wrapping caption instead: each group/sub becomes an auto-width, wrapping chip rather than a
     fixed-percentage column, so the two rows read as a wrapping two-line caption under the chart. */
  .dp-band-groups, .dp-band-subs { flex-wrap: wrap; }
  .dp-band-group, .dp-band-sub {
    width: auto !important;
    flex: 0 0 auto;
    border-inline-end: none;
    padding-inline-end: .4rem;
    margin-block-end: .15rem;
  }
  .dp-band-sub-spacer { display: none; }
}

/* ---------------------------------------------------------------------------------------- */
/* RTL — every position on the chart (ticks, rules, marks, whiskers, the dumbbell connector,  */
/* legend/band widths) is expressed with logical properties (inset-inline-start, margin-      */
/* inline-start, flex row order) or percentages of a track that itself has no fixed direction, */
/* so all of it re-reads correctly, unmirrored, once `dir="rtl"` flips what "start" means —    */
/* see the mark-centring note above and the direction-aware tick-edge rules above that for the */
/* two places this needed an explicit (not automatic) fix. The one exception is .dp-track-bg:  */
/* it is a `linear-gradient(to right, …)`, and CSS gradients have no logical "to inline-end"   */
/* keyword, so that one physical background layer is mirrored by hand with scaleX(-1) — this   */
/* is safe because the layer carries no text and its own centre is its flip axis.              */
/* ---------------------------------------------------------------------------------------- */

[dir="rtl"] .dp-track-bg {
  transform: scaleX(-1);
}

/* ---------------------------------------------------------------------------------------- */
/* Print — force fills/pills/marks to render on paper (no print-color-adjust means most      */
/* browsers drop background colours when printing).                                          */
/* ---------------------------------------------------------------------------------------- */

@media print {
  .dp-track-bg, .dp-band-group, .dp-band-sub, .dp-pill, .bt-pill, .dp-mark, .db-mark, .dp-whisker, .db-connector {
    print-color-adjust: exact;
    -webkit-print-color-adjust: exact;
  }
}
