/*
 * Car Inflation Calculator — the artwork layer.
 *
 * The car stack, the road, and everything that moves in the hero. Kept apart
 * from cic.css so the layout and the motion can be reasoned about separately;
 * the blanket prefers-reduced-motion rule in cic.css covers .cic-art too.
 */

/* ---------- the car stack ---------- */

.cic-stage {
  position: relative;
  width: 100%;
  max-width: calc(760px * var(--cic-ui-scale));
  margin: 0 auto calc(8px * var(--cic-ui-scale));
  /* CLS reservation. The SVG is width:100% with a 640x200 viewBox, so its
     height is 31.25% of its width; the wrapper holds that ratio before the
     first frame paints. */
  aspect-ratio: 640 / 200;
}
@supports not (aspect-ratio: 1) {
  .cic-stage { height: 0; padding-bottom: 31.25%; }
  .cic-stage > * { position: absolute; inset: 0; }
}

.cic-car-layer {
  position: absolute; inset: 0;
  opacity: 0;
  transform: translateX(calc(var(--cic-slide, 0) * 34px)) scale(.985);
  transition: opacity .55s var(--cic-ease), transform .55s var(--cic-ease);
  will-change: opacity, transform;
  pointer-events: none;
}
.cic-car-layer.is-current {
  opacity: 1;
  transform: translateX(0) scale(1);
}

.cic-car { display: block; width: 100%; height: 100%; overflow: visible; color: var(--cic-text); }

/*
 * Bodywork. Stroked rather than filled: a filled silhouette at this size reads
 * as a black blob against a near-black page, while a 2px stroke keeps the
 * proportions that distinguish one era from the next.
 *
 * The class is cic-shell, not cic-body. The page element carries
 * class="cic-body", and stroke is an inherited SVG property, so an unscoped
 * rule of that name painted a white stroke onto every SVG element on the page.
 * It surfaced as the chart's axis and series labels rendering white and bold
 * whatever fill they were given.
 */
.cic-shell {
  fill: rgba(255, 255, 255, .045);
  stroke: var(--cic-text);
  stroke-width: 2.2;
  stroke-linejoin: round;
}
.cic-shell--ghost { fill: none; stroke: var(--cic-faint); stroke-dasharray: 7 7; }
.cic-glass { fill: rgba(56, 189, 248, .17); stroke: rgba(56, 189, 248, .5); stroke-width: 1.4; }
.cic-fender { fill: none; stroke: var(--cic-text); stroke-width: 2; }
.cic-trim { fill: none; stroke: var(--cic-muted); stroke-width: 1.2; }
.cic-fin { fill: var(--cic-red); stroke: none; }
.cic-scoop { fill: rgba(255, 255, 255, .13); stroke: var(--cic-muted); stroke-width: 1.2; }
.cic-bumper { fill: var(--cic-surface-3); stroke: var(--cic-muted); stroke-width: 1.4; }
.cic-rail { fill: none; stroke: var(--cic-muted); stroke-width: 3; stroke-linecap: round; }
.cic-lamp, .cic-lamp-wedge { fill: var(--cic-now); stroke: none; }
.cic-art-note {
  fill: var(--cic-faint); font-family: var(--cic-font-ui);
  font-size: 15px; letter-spacing: .18em;
}

.cic-tyre { fill: #121216; stroke: var(--cic-text); stroke-width: 2.2; }
.cic-rim { fill: none; stroke: var(--cic-muted); stroke-width: 1.6; }
.cic-spoke { stroke: var(--cic-faint); stroke-width: 1; }
.cic-spoke--alloy { stroke: var(--cic-muted); stroke-width: 2.4; stroke-linecap: round; }

.cic-road { stroke: var(--cic-line-strong); stroke-width: 2; }

/*
 * The headlight sweep. A soft band that crosses the body once when the year
 * changes, then stops -- a permanent loop would be noise behind a number
 * people are trying to read.
 */
.cic-shine { opacity: 0; pointer-events: none; }
.cic-car-layer.is-current .cic-shine { animation: cic-sweep 1.1s var(--cic-ease) .18s 1; }
@keyframes cic-sweep {
  0%   { opacity: 0; transform: translateX(60px); }
  22%  { opacity: .85; }
  100% { opacity: 0; transform: translateX(520px); }
}

/*
 * Wheel spin while the scrubber is being dragged. Tied to the drag rather
 * than running forever, so the page is still when the visitor is.
 */
.cic-app[data-cic-dragging="1"] .cic-car-layer.is-current .cic-spoke,
.cic-app[data-cic-dragging="1"] .cic-car-layer.is-current .cic-rim {
  animation: cic-spin .55s linear infinite;
  transform-box: fill-box;
  transform-origin: center;
}
@keyframes cic-spin { to { transform: rotate(360deg); } }

/* ---------- the road under the rail ---------- */

.cic-roadstrip {
  position: relative; height: calc(16px * var(--cic-ui-scale));
  margin: calc(10px * var(--cic-ui-scale)) auto 0;
  max-width: calc(980px * var(--cic-ui-scale));
  overflow: hidden; opacity: .55;
}
.cic-roadstrip::before {
  content: ""; position: absolute; left: 0; right: -220px; top: 50%;
  height: 2px; transform: translateY(-50%);
  background: repeating-linear-gradient(
    90deg,
    var(--cic-line-strong) 0 26px,
    transparent 26px 52px
  );
  animation: cic-road-run 1.6s linear infinite;
  animation-play-state: paused;
}
.cic-app[data-cic-dragging="1"] .cic-roadstrip::before { animation-play-state: running; }
@keyframes cic-road-run { to { transform: translate(-52px, -50%); } }

/* ---------- panel entrances ---------- */

/*
 * Panels lift in once, when they first come near the viewport. The class is
 * added by cic-app.js from an IntersectionObserver rather than on load, so
 * nothing below the fold animates while the hero is still settling.
 */
.cic-reveal-on-scroll {
  opacity: 0;
  transform: translateY(calc(18px * var(--cic-ui-scale)));
  transition: opacity .6s var(--cic-ease-out), transform .6s var(--cic-ease-out);
}
.cic-reveal-on-scroll.is-visible { opacity: 1; transform: none; }
.cic-reveal-on-scroll[data-cic-delay="1"] { transition-delay: .09s; }
.cic-reveal-on-scroll[data-cic-delay="2"] { transition-delay: .18s; }
.cic-reveal-on-scroll[data-cic-delay="3"] { transition-delay: .27s; }

/* ---------- the century chart ---------- */

.cic-chart-frame {
  position: relative; width: 100%;
  background: var(--cic-surface);
  border: 1px solid var(--cic-line);
  border-radius: var(--cic-radius);
  padding: calc(20px * var(--cic-ui-scale)) calc(16px * var(--cic-ui-scale)) calc(10px * var(--cic-ui-scale));
  /* Reservation: the SVG holds a 1000x420 viewBox at width:100%. */
  aspect-ratio: 1000 / 420;
  min-height: calc(280px * var(--cic-ui-scale));
}
.cic-chart { display: block; width: 100%; height: 100%; overflow: visible; }

.cic-axis-line { stroke: var(--cic-line); stroke-width: 1; }
.cic-grid-line { stroke: var(--cic-line); stroke-width: 1; stroke-dasharray: 2 5; }
.cic-axis-text {
  fill: var(--cic-muted); font-family: var(--cic-font-ui);
  font-size: 13px; font-weight: 400; letter-spacing: .04em;
  paint-order: stroke; stroke: none;
}
.cic-axis-text--y { text-anchor: end; }

.cic-series {
  fill: none; stroke-width: 2.4; stroke-linejoin: round; stroke-linecap: round;
  stroke-dasharray: var(--cic-len, 4000);
  stroke-dashoffset: var(--cic-len, 4000);
}
.cic-chart-frame.is-drawn .cic-series {
  transition: stroke-dashoffset 1.6s var(--cic-ease-out);
  stroke-dashoffset: 0;
}
.cic-chart-frame.is-drawn .cic-series--cpi { transition-delay: .12s; }
.cic-chart-frame.is-drawn .cic-series--income { transition-delay: .24s; }

/*
 * Chart series palette.
 *
 * These are NOT the UI accent colours. The interface accent is the brand red
 * #e11d2a, but a categorical chart palette has to clear five checks at once
 * (OKLCH lightness band, chroma floor, colour-blind separation, normal-vision
 * separation, contrast against the surface) and the brand red sits outside
 * the dark-mode lightness band of L 0.48-0.67. These three were found by
 * searching OKLCH space against the validator rather than picked by eye:
 *
 *   node scripts/validate_palette.js "#cc3430,#4790d8,#b37903" \
 *        --mode dark --surface "#141417"
 *   -> all five checks PASS, worst adjacent CVD deltaE 22.6 (tritan).
 *
 * #cc3430 is a darker step of the brand red, so the chart still reads as part
 * of the same page. Re-run the validator before changing any of the three.
 *
 * Each line also carries a dash pattern, so identity never rests on colour
 * alone -- that plus the direct end-labels is what makes the chart legible in
 * greyscale, in forced-colours mode and to anyone who cannot separate the hues.
 */
.cic-series--car { stroke: #cc3430; }
.cic-series--cpi { stroke: #4790d8; stroke-dasharray: 9 5; }
.cic-series--income { stroke: #b37903; stroke-dasharray: 2 5; }
.cic-chart-frame.is-drawn .cic-series--cpi,
.cic-chart-frame.is-drawn .cic-series--income { stroke-dasharray: none; }

.cic-marker { stroke: var(--cic-text); stroke-width: 1; stroke-dasharray: 3 4; opacity: .65; }
.cic-marker-dot { stroke: var(--cic-bg); stroke-width: 2; }
.cic-marker-dot--car { fill: #cc3430; }
.cic-marker-dot--cpi { fill: #4790d8; }
.cic-marker-dot--income { fill: #b37903; }

/* Direct labels at the right-hand end of each line. */
.cic-series-label {
  font-family: var(--cic-font-ui); font-size: 13px; font-weight: 600;
  dominant-baseline: middle; stroke: none;
}
.cic-series-label--car { fill: #cc3430; }
.cic-series-label--cpi { fill: #4790d8; }
.cic-series-label--income { fill: #b37903; }

/* Crosshair readout. */
/* stroke:none is not the SVG default here -- without it the transparent hit
   rect picks up a visible outline and draws a white box around the plot. */
.cic-hit { fill: transparent; stroke: none; cursor: crosshair; }
.cic-tip {
  position: absolute; z-index: 5; pointer-events: none;
  background: var(--cic-bg-2); border: 1px solid var(--cic-line-strong);
  border-radius: var(--cic-radius); padding: calc(10px * var(--cic-ui-scale));
  font-family: var(--cic-font-ui); font-size: calc(13px * var(--cic-ui-scale));
  color: var(--cic-text); white-space: nowrap;
  opacity: 0; transition: opacity .15s;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .55);
}
.cic-tip.is-on { opacity: 1; }
.cic-tip-year { font-family: var(--cic-font-display); letter-spacing: .06em; margin-bottom: .4em; }
.cic-tip-row { display: flex; align-items: center; gap: .5em; }
.cic-tip-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.cic-tip-val { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--cic-text); }
.cic-tip-name { color: var(--cic-muted); }

/* The table behind the chart, for screen readers and anyone who wants the numbers. */
.cic-table-toggle {
  margin-top: calc(14px * var(--cic-ui-scale));
  font-family: var(--cic-font-ui); font-size: calc(13px * var(--cic-ui-scale));
  color: var(--cic-muted);
}
.cic-table-toggle summary { cursor: pointer; }
.cic-table-toggle summary:hover { color: var(--cic-text); }
.cic-table-scroll { max-height: calc(340px * var(--cic-ui-scale)); overflow: auto; margin-top: calc(12px * var(--cic-ui-scale)); }
.cic-table-scroll table { width: 100%; border-collapse: collapse; font-size: calc(13px * var(--cic-ui-scale)); }
.cic-table-scroll th, .cic-table-scroll td {
  padding: calc(6px * var(--cic-ui-scale)) calc(10px * var(--cic-ui-scale));
  border-bottom: 1px solid var(--cic-line); text-align: right;
  font-variant-numeric: tabular-nums;
}
.cic-table-scroll th:first-child, .cic-table-scroll td:first-child { text-align: left; }
.cic-table-scroll thead th {
  position: sticky; top: 0; background: var(--cic-surface);
  color: var(--cic-muted); text-transform: uppercase; letter-spacing: .08em;
  font-size: calc(11px * var(--cic-ui-scale));
}

.cic-legend {
  display: flex; flex-wrap: wrap; gap: calc(18px * var(--cic-ui-scale));
  margin-top: calc(14px * var(--cic-ui-scale));
  font-family: var(--cic-font-ui); font-size: calc(13px * var(--cic-ui-scale));
  color: var(--cic-dim);
}
.cic-legend-key { display: inline-flex; align-items: center; gap: .55em; }
.cic-legend-swatch {
  width: calc(22px * var(--cic-ui-scale)); height: calc(3px * var(--cic-ui-scale));
  border-radius: 2px; background: var(--cic-muted); flex: none;
}
.cic-legend-swatch--car { background: #cc3430; }
.cic-legend-swatch--cpi { background: #4790d8; }
.cic-legend-swatch--income { background: #b37903; }
.cic-legend-val { color: var(--cic-text); font-variant-numeric: tabular-nums; }
