/* charts.css: v2 chart layer (owner: viz). Colours, type and durations come
   from tokens.css; chart geometry comes from the --viz-* properties that
   js/v2/viz/core.mjs (GEOM) and charts/common.mjs (GEO) publish on :root.

   Chart grammar (VISUAL-SPEC v3): no grid rows, no Y-axis column, straight
   2 px net lines coloured by the period's direction, a dotted baseline at
   the period start, one flat fill (8 %) under the net line only, the
   invested line in the added colour (--chart-added) with an inline end label, quiet captions under
   the plot (start left, change right), values read by scrubbing. Bars are
   flat with a 2 px corner radius at most; the only outline is the dashed
   provisional-month marker (bridge); unfinished years keep their fills at
   reduced opacity. Every fill is a flat colour. */

/* ------------------------------------------------------------ shared */
/* Categorical data palette: one hue per category, dark and low in chroma
   (OKLCH L 0.62, C 0.05; account steps vary L only), so they read as tinted
   greys on true black. Large shapes never carry them as solid fills: areas
   use them at --viz-cat-fill-alpha under a full-hue top edge, the treemap
   uses only an 8 px dot per tile; bar100 segments, chips and edges use the
   full hue. Charts use these, never the bright --c-* hues (palette.py in the
   V4 viz notes regenerates them). */
:root {
  --viz-cat-bourse-1: #7189A4; --viz-cat-bourse-2: #546B85; --viz-cat-bourse-3: #88A1BD; --viz-cat-bourse-4: #445A74;
  --viz-cat-epargne-1: #62908D; --viz-cat-epargne-2: #45726F; --viz-cat-epargne-3: #7AA9A5;
  --viz-cat-immo-1: #998265; --viz-cat-immo-2: #B29A7D;
  --viz-cat-autre-1: #907E9C; --viz-cat-autre-2: #73617E;
  --viz-cat-others: #68686C;
  --viz-cat-fill-alpha: 0.5;   /* stacked areas: fill under the full-hue edge */
  --viz-cat-edge-w: 1.5px;     /* stacked areas: top edge in the full hue */
  /* treemap tiles: the elevated surface over 2 px black gaps */
  --viz-tile: var(--cell-2);
  --viz-tile-gap: var(--bg);
  /* tax per account: neutral net part, one red for tax on gains, a darker
     red for acquisition tax (no category hue) */
  --viz-tax-net: var(--chart-neutral);
  --viz-tax-gains: var(--neg);
  --viz-tax-acq: #A3292B;
  /* bars of a period that is not complete: the same fills, less opaque */
  --viz-partial-alpha: 0.4;
}
:where(.ui-sheet .ui-group) { --viz-tile: var(--cell-3); }
/* Label halos and tile gaps match the surface under the chart:
   var(--bg) (true black) on pages by default, var(--cell) in grouped cells
   and sheets, var(--cell-2) for groups inside a sheet. :where() keeps these
   at zero specificity, so a screen's own --chart-halo wins. */
:where(.ui-group, .ui-sheet, .ui-sheet-page) { --chart-halo: var(--cell); }
:where(.ui-sheet .ui-group) { --chart-halo: var(--cell-2); }
.viz-host { position: relative; }
.viz { display: block; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent; }
svg.viz { overflow: visible; }
.viz text {
  font: var(--t-subhead);
  letter-spacing: var(--tr-subhead);
  font-variant-numeric: tabular-nums;
  fill: var(--label-2);
}
.viz:focus { outline: none; }
.viz:focus-visible { outline: calc(var(--hairline) * 4) solid var(--tint); outline-offset: calc(var(--hairline) * 4); border-radius: var(--radius-xs); }
.viz-gate {
  margin: 0;
  font: var(--t-footnote);
  letter-spacing: var(--tr-footnote);
  color: var(--label-2);
}
.viz-host-band.viz-host-M .viz-gate {
  height: var(--viz-band-h);
  display: flex; align-items: center; justify-content: center;
}
.viz .is-pos { --dir: var(--pos); --dir-line: var(--pos-line); }
.viz .is-neg { --dir: var(--neg); --dir-line: var(--neg-line); }
.viz .is-zero, .viz .is-flat { --dir: var(--label-3); }
.viz .is-inv { --dir: var(--chart-added); }
.viz .is-muted { --dir: var(--label-3); }
.viz .is-hl { --dir: var(--label); }

/* captions under a chart: start left, change right */
.viz-caps {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--margin);
  margin-top: var(--radius-xs);
  font: var(--t-subhead); letter-spacing: var(--tr-subhead);
  font-variant-numeric: tabular-nums;
  color: var(--label-2);
  white-space: nowrap;
}
.viz-cap-l { min-width: 0; overflow: hidden; }
.viz-cap-r { margin-left: auto; overflow: hidden; }
.viz-cap-v { font-weight: 400; color: var(--label); }
/* too narrow for both sides: drop the start caption, then wrap the change */
.viz-caps.is-noleft .viz-cap-l { display: none; }
.viz-caps.is-wrap { white-space: normal; }
.viz-caps.is-wrap .viz-cap-r { text-align: right; }

/* line pieces shared by the band and renderLines */
.viz .viz-baseline-start { stroke: var(--label-3); stroke-width: var(--viz-line-w); vector-effect: non-scaling-stroke; }
/* no fill under the net line: the line and the dotted baseline carry it */
.viz .viz-fill { display: none; }
.viz .viz-endlabel { fill: var(--label-2); } /* text: label-2 for contrast; the line stays label-3 */
.viz .viz-endlabel.is-pos, .viz .viz-endlabel.is-neg, .viz .viz-endlabel.is-inv { fill: var(--dir); }
.viz .viz-endlabel.is-hl { fill: var(--label); }
.viz .viz-rule { stroke: var(--label-3); opacity: 0; transition: opacity var(--dur-fade) var(--ease-linear); }
.viz.is-scrub .viz-rule { opacity: 1; }

/* ------------------------------------------------------------ band */
.viz-band-wrap { --dir: var(--pos); --dir-line: var(--pos-line); }
.viz-band-wrap.is-down { --dir: var(--neg); --dir-line: var(--neg-line); }
.viz-band { width: 100%; --dir: var(--pos); --dir-line: var(--pos-line); touch-action: pan-y; }
.viz-band.is-down { --dir: var(--neg); --dir-line: var(--neg-line); }
@media (hover: hover) and (pointer: fine) { .viz-band.viz-tier-M, .viz-band.viz-tier-L { cursor: crosshair; } }
.viz-band .viz-net {
  fill: none; stroke: var(--dir-line, var(--dir));
  stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  transition: stroke var(--dur-tween) var(--ease-linear);
}
.viz-band .viz-inv {
  fill: none; stroke: var(--chart-added);
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: var(--viz-inv-dash);
  vector-effect: non-scaling-stroke;
}
.viz-band .viz-proj .viz-net { stroke-linecap: butt; }
/* M and L tiers: a thicker value line with a soft glow of its own colour */
:is(.viz-band, .viz-lines):is(.viz-tier-M, .viz-tier-L) :is(.viz-net, .viz-ln.is-pos, .viz-ln.is-neg) {
  stroke-width: var(--viz-net-stroke-m);
  filter: drop-shadow(0 0 var(--viz-glow) color-mix(in srgb, var(--dir-line, var(--dir)) var(--viz-glow-mix), transparent));
}
@media (prefers-reduced-transparency: reduce), (forced-colors: active) {
  :is(.viz-band, .viz-lines) :is(.viz-net, .viz-ln) { filter: none; }
}
.viz-band .viz-future { opacity: var(--viz-future-dim); }
.viz-band .viz-dot {
  fill: var(--dir-line, var(--dir)); stroke: var(--chart-halo, var(--bg));
  opacity: 0; transition: opacity var(--dur-fade) var(--ease-linear);
}
.viz-band .viz-endlabel { fill: var(--chart-added); } /* "Invested", the band's only end label */
.viz-band .viz-dot-inv {
  fill: var(--chart-added); stroke: var(--chart-halo, var(--bg)); stroke-width: var(--viz-dot-ring);
  opacity: 0; transition: opacity var(--dur-fade) var(--ease-linear);
}
.viz-band.is-scrub .viz-dot, .viz-band.is-scrub .viz-dot-inv { opacity: 1; }
.viz-band.viz-tier-S .viz-rule, .viz-band.viz-tier-S .viz-dot, .viz-band.viz-tier-S .viz-dot-inv,
.viz-band.viz-tier-S .viz-fill, .viz-band.viz-tier-S .viz-baseline-start { display: none; }
/* L tier only (full screen): readout, y ticks with dotted gridlines, start
   and end dates. S and M stay axis-free. */
.viz-band .viz-grid { stroke: var(--sep); stroke-width: var(--viz-line-w); vector-effect: non-scaling-stroke; }
.viz-band text.viz-ytick {
  fill: var(--label-2);
  paint-order: stroke; stroke: var(--chart-halo, var(--bg)); stroke-linejoin: round;
}
.viz-band text.viz-readout-v { font: var(--t-title2); letter-spacing: var(--tr-title2); fill: var(--label); }
.viz-band text.viz-readout-d { fill: var(--label-2); }
.is-masked .viz-band text.viz-ytick { visibility: hidden; }

/* ------------------------------------------------------------ renderLines (cvm, yoy, proj) */
.viz-lines { width: 100%; touch-action: pan-y; }
@media (hover: hover) and (pointer: fine) { .viz-lines.viz-tier-M, .viz-lines.viz-tier-L { cursor: crosshair; } }
.viz .viz-ln {
  fill: none; stroke: var(--dir-line, var(--dir, var(--label-3)));
  stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  transition: stroke var(--dur-tween) var(--ease-linear);
}
/* what you added / invested: a dotted line in the added colour */
.viz .viz-ln.is-inv { stroke-dasharray: var(--viz-inv-dash); }

/* legend of the main line charts (band, renderLines): small, under the
   plot; a solid swatch for the value line, a dotted one for invested */
:is(.viz-band-wrap > .viz-legend, .viz-legend:has(.viz-legend-swatch--line, .viz-legend-swatch--invested)) {
  justify-content: flex-start; column-gap: 12px;
  min-height: 0; margin-top: var(--radius-xs);
  font: var(--t-caption1); letter-spacing: var(--tr-caption1); color: var(--label-2);
}
.viz-legend-swatch { flex: none; display: inline-block; width: 14px; height: 3px; border-radius: var(--radius-full); }
.viz-legend-swatch--line { background: var(--dir-line, var(--pos-line)); }
.viz-legend-swatch--invested {
  background: radial-gradient(circle, var(--chart-added) 0.9px, transparent 1.2px) 0 50% / 5px 3px repeat-x;
}
.viz .viz-pband { fill: var(--dir, var(--pos)); fill-opacity: var(--viz-fill-alpha); stroke: none; }
.viz-lines .viz-dot { fill: var(--dir, var(--label-2)); stroke: var(--chart-halo, var(--bg)); }

/* ------------------------------------------------------------ sparkline */
.viz-spark { flex: none; overflow: visible; --spark: var(--label-3); }
.viz-spark.is-pos { --spark: var(--pos); }
.viz-spark.is-neg { --spark: var(--neg); }
.viz-spark-line { fill: none; stroke: var(--spark); stroke-linecap: round; stroke-linejoin: round; transition: stroke var(--dur-tween) var(--ease-linear); }
.viz-spark-dot { fill: var(--spark); transition: fill var(--dur-tween) var(--ease-linear); }

/* ------------------------------------------------------------ 100 % bar */
.viz-bar100 { position: relative; }
.viz-bar100-track {
  display: flex; gap: var(--viz-bar100-gap);
  height: var(--viz-bar100-h);
  border-radius: var(--viz-bar-rx);
  overflow: hidden;
  background: var(--bg); /* the 2 px gaps between segments read black */
}
.viz-bar100-seg {
  flex: 1 1 0; min-width: 0; height: 100%;
  border-radius: var(--viz-bar-rx);
  transform-origin: 0 50%;
  transition: opacity var(--dur-axis) var(--ease-linear);
}
.viz-bar100.is-dim .viz-bar100-seg { opacity: 0.25; }
.viz-bar100.is-dim .viz-bar100-seg.is-hl { opacity: 1; }
.viz-bar100-ticks { position: absolute; left: 0; right: 0; top: calc(-1 * var(--viz-target-out)); bottom: calc(-1 * var(--viz-target-out)); pointer-events: none; }
.viz-bar100-tick {
  position: absolute; top: 0; bottom: 0;
  width: var(--viz-target-w);
  transform: translateX(-50%);
  border-radius: var(--viz-bar-rx);
  background: var(--label);
}

/* ------------------------------------------------------------ ceiling gauge */
.viz-gauge {
  display: inline-flex; gap: var(--viz-gauge-gap);
  width: var(--viz-gauge-w); height: var(--viz-gauge-h);
  flex: none;
  --viz-gauge-color: var(--label);
}
.viz-gauge-seg {
  flex: 1 1 0; min-width: 0;
  background: var(--fill);
  border-radius: var(--viz-bar-rx);
  overflow: hidden;
}
.viz-gauge-fill {
  display: block; height: 100%;
  background: var(--viz-gauge-color);
  transform-origin: 0 50%;
  transition: transform var(--dur-tween) var(--ease-out);
}
.viz-gauge.is-over .viz-gauge-fill { background: var(--neg); }

/* ------------------------------------------------------------ bullet
   Neutral by default: the fill in the label colour on a tertiary-fill track,
   red only when the value is over its limit (a screen's --viz-bullet-color
   never hides that). */
.viz-bullet {
  position: relative;
  display: flex; align-items: center;
  height: var(--viz-bullet-tick-h);
  --viz-bullet-color: var(--label);
}
.viz-bullet-track {
  flex: 1 1 auto;
  height: var(--viz-bullet-h);
  background: var(--fill);
  border-radius: var(--viz-bar-rx);
  overflow: hidden;
}
.viz-bullet-bar {
  display: block; height: 100%;
  background: var(--viz-bullet-color);
  border-radius: var(--viz-bar-rx);
  transform-origin: 0 50%;
  transition: transform var(--dur-tween) var(--ease-out);
}
.viz-bullet.is-over .viz-bullet-bar { background: var(--neg); }
.viz-bullet-target {
  position: absolute; top: 0; bottom: 0;
  width: var(--viz-target-w);
  background: var(--label-2);
  border-radius: var(--viz-bar-rx);
  transform: translateX(-50%);
}
.viz-bullet-target[hidden] { display: none; }

/* ------------------------------------------------------------ L tier
   js/charts/fullscreen.mjs overlay restyled for v2 (its classes are fixed). */
.body--modal-open { overflow: hidden; }
.modal-fs {
  position: absolute; inset: 0;
  z-index: var(--z-fullscreen);
  display: flex; flex-direction: column;
  padding: var(--st) var(--sr) var(--sb) var(--sl);
  background: var(--bg);
  color: var(--label);
  animation: viz-fs-in var(--dur-fade) var(--ease-out) both;
  --chart-halo: var(--bg);
}
.modal-fs__header {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--cell-pad-y) var(--margin);
  flex: none;
}
.modal-fs__title { margin: 0; font: var(--t-headline); letter-spacing: var(--tr-headline); }
.modal-fs__close {
  width: var(--tap-min); height: var(--tap-min);
  display: grid; place-items: center;
  border-radius: var(--radius-full);
  font: var(--t-title3);
  cursor: pointer;
}
/* plain fallback at specificity 0,0,1: it beats base.css `button` and loses
   to the glass owner's .glass material (components.css) once that exists */
button:where(.modal-fs__close) {
  border: 0;
  background: var(--fill);
  color: var(--label);
}
.modal-fs__close:focus-visible { outline: calc(var(--hairline) * 4) solid var(--tint); outline-offset: calc(var(--hairline) * 4); }
.modal-fs__chart {
  position: relative;
  flex: 1 1 auto; min-height: 0;
  display: flex; align-items: stretch;
  padding: 0 var(--margin) var(--margin);
}
.modal-fs__chart svg.chart { width: 100%; height: 100%; }
.modal-fs__chart > .viz:not(svg) { align-self: center; width: 100%; }
.modal-fs__chart > .viz-host { flex: 1 1 auto; align-self: center; min-width: 0; }
@keyframes viz-fs-in { from { opacity: 0; } to { opacity: 1; } }

/* ================================================================ row and bar charts
   bridgeBar, divergingBars, dotPlot (HTML rows: label | track | value) and
   monthlyBars, stackedYearly, drawdown, area100/categoryArea, periodWaterfall,
   returnHistogram, returnsHeatmap (SVG). */

.viz-mini { flex: none; overflow: visible; }
.viz-note {
  margin: var(--radius-xs) 0 0;
  font: var(--t-footnote); letter-spacing: var(--tr-footnote);
  color: var(--label-2);
}
.viz-chip {
  display: inline-block; flex: none;
  width: var(--viz-chip-d); height: var(--viz-chip-d);
  border-radius: var(--radius-full);
  margin-right: var(--radius-xs);
}
.viz .viz-zero { stroke: var(--label-3); stroke-width: var(--viz-line-w); vector-effect: non-scaling-stroke; }

/* horizontal rows (bridge, diverging, dot plot) */
.viz-bridge, .viz-diverging, .viz-dotplot, .viz-dot-rows { display: flex; flex-direction: column; }
.viz-hrow {
  display: grid;
  grid-template-columns: minmax(0, 7.5em) minmax(0, 1fr) minmax(4.5em, auto);
  align-items: center;
  column-gap: var(--radius-sm);
  min-height: var(--viz-row-h);
  margin: 0; padding: 0;
  border: 0; background: none; color: inherit; text-align: left;
  font: var(--t-subhead); letter-spacing: var(--tr-subhead);
  font-variant-numeric: tabular-nums;
}
button.viz-hrow { min-height: var(--tap-min); cursor: pointer; -webkit-tap-highlight-color: transparent; border-radius: var(--radius-xs); transition: background-color var(--dur-press-out) var(--ease-linear); }
button.viz-hrow:active { background: var(--cell-2); transition-duration: 0ms; }
button.viz-hrow:focus-visible { outline: calc(var(--hairline) * 4) solid var(--tint); outline-offset: 0; }
.viz-hrow-label { display: flex; align-items: center; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--label-2); }
.viz-hrow-value { text-align: right; white-space: nowrap; color: var(--label); }
.viz-hrow.is-total { border-top: var(--hairline) solid var(--sep); margin-top: var(--radius-xs); }
.viz-hrow.is-total .viz-hrow-label, .viz-hrow.is-total .viz-hrow-value { font: var(--t-headline); letter-spacing: var(--tr-headline); color: var(--label); }
.viz-hrow-track { position: relative; height: var(--viz-hbar-h); }
/* bridge: the label column sizes to its longest label ("Impôt sur les gains")
   and every row shares it through subgrid; without subgrid labels wrap. */
@supports (grid-template-columns: subgrid) {
  .viz-bridge { display: grid; grid-template-columns: max-content minmax(0, 1fr) minmax(4.5em, auto); column-gap: var(--radius-sm); }
  .viz-bridge > * { grid-column: 1 / -1; }
  .viz-bridge > .viz-hrow { grid-template-columns: subgrid; }
  .viz-bridge .viz-hrow-label { overflow: visible; }
}
@supports not (grid-template-columns: subgrid) {
  .viz-bridge .viz-hrow-label { white-space: normal; }
}
.viz-hbar {
  position: absolute; top: 0; bottom: 0;
  box-sizing: border-box;
  min-width: var(--viz-hbar-min);
  border-radius: var(--viz-bar-rx);
  background: var(--dir, var(--chart-invested));
}
.viz-hbar-zero {
  position: absolute; top: calc(-1 * var(--viz-hbar-h) / 3); bottom: calc(-1 * var(--viz-hbar-h) / 3);
  width: var(--viz-line-w); transform: translateX(-50%);
  background: var(--label-3);
}

/* bridge */
.viz-bridge-added { --dir: var(--chart-added); }
.viz-bridge-tax { --dir: var(--label-3); }
.viz-bridge-total { --dir: var(--label); }
.viz-bridge-total.is-neg { --dir: var(--neg); }
/* provisional month: the partial-period marker (dashed 1 px, no fill) */
.viz-bridge.is-provisional .viz-hbar {
  background: transparent;
  border: var(--viz-line-w) dashed var(--dir, var(--chart-invested));
}
svg.viz-bridge rect { fill: var(--dir, var(--chart-invested)); }
svg.viz-bridge .viz-bridge-markets.is-pos { --dir: var(--pos); }
svg.viz-bridge .viz-bridge-markets.is-neg { --dir: var(--neg); }

/* diverging */
.viz-div-bar.is-pos { --dir: var(--pos); }
.viz-div-bar.is-neg { --dir: var(--neg); }
svg.viz-diverging rect { fill: var(--dir); }

/* dot plot */
.viz-dot-stem {
  position: absolute; top: 50%;
  height: var(--viz-stem-w); margin-top: calc(-1 * var(--viz-stem-w) / 2);
  border-radius: var(--viz-bar-rx);
  background: var(--label-3);
}
.viz-dot-pt {
  position: absolute; top: 50%;
  width: var(--viz-dot-d); height: var(--viz-dot-d);
  margin: calc(-1 * var(--viz-dot-d) / 2) 0 0 calc(-1 * var(--viz-dot-d) / 2);
  border-radius: var(--radius-full);
  background: var(--label); /* white: the row's value carries the sign, no category hue */
  box-shadow: 0 0 0 var(--viz-ring-w) var(--chart-halo, var(--bg));
}
.viz-dot-ref {
  position: absolute; top: calc(-1 * var(--viz-hbar-h) / 2); bottom: calc(-1 * var(--viz-hbar-h) / 2);
  width: 0; border-left: var(--viz-line-w) dashed var(--label-2);
}
svg.viz-dot .viz-dot-track { stroke: var(--fill); stroke-width: var(--viz-ring-w); stroke-linecap: round; }
svg.viz-dot .viz-dot-pt { fill: var(--label); stroke: var(--chart-halo, var(--bg)); stroke-width: var(--viz-line-w); }

/* SVG bar and area charts */
:is(.viz-monthly, .viz-yearly, .viz-drawdown, .viz-area100, .viz-wf, .viz-hist, .viz-heat):not(.viz-mini) { width: 100%; touch-action: pan-y; }
@media (hover: hover) and (pointer: fine) { :is(.viz-monthly, .viz-drawdown, .viz-area100).viz-tier-M, :is(.viz-monthly, .viz-drawdown, .viz-area100).viz-tier-L { cursor: crosshair; } }
.viz .viz-xlabel { fill: var(--label-2); }
.viz-avg-label, .viz-ybar-value, .viz-dd-label, .viz-wf-value {
  paint-order: stroke;
  stroke: var(--chart-halo, var(--bg));
  stroke-linejoin: round;
}
/* plate behind the monthly average label: readable over hatched projected bars */
.viz-ylabel-plate { fill: var(--chart-halo, var(--bg)); }
svg.viz-monthly.is-selectable { cursor: pointer; }
.viz .viz-baseline { stroke: var(--label-3); stroke-width: var(--viz-line-w); vector-effect: non-scaling-stroke; }
:is(.viz-drawdown, .viz-area100) .viz-rule { stroke: var(--label-3); }
.viz-drawdown .viz-dot { fill: var(--neg); stroke: var(--chart-halo, var(--bg)); opacity: 0; transition: opacity var(--dur-fade) var(--ease-linear); }
.viz-drawdown.is-scrub .viz-dot { opacity: 1; }

/* monthly bars */
.viz-mbar { fill: var(--chart-added); transform-box: fill-box; transition: opacity var(--dur-fade) var(--ease-linear); }
.viz-mbar.is-neg { fill: var(--neg); }
.viz-mbar.is-hl { fill: var(--label); }
.viz-hatch { stroke: var(--chart-added); stroke-width: var(--viz-line-w); }
.viz-monthly.is-scrub .viz-mbar { opacity: 0.3; }
.viz-monthly.is-scrub .viz-mbar.is-on { opacity: 1; fill: var(--label); }
.viz .viz-avg { stroke: var(--label-2); stroke-width: var(--viz-line-w); stroke-dasharray: var(--viz-avg-dash); vector-effect: non-scaling-stroke; }
.viz .viz-avg-label { fill: var(--label-2); }

/* drawdown */
.viz-dd-area { fill: var(--neg); fill-opacity: var(--viz-fill-alpha); }
.viz-dd-line { fill: none; stroke: var(--neg); stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
svg.viz-mini .viz-dd-line { stroke-width: var(--viz-spark-stroke); }
.viz-dd-mark { stroke: var(--chart-halo, var(--bg)); stroke-width: var(--viz-ring-w); }
.viz-dd-mark.is-peak { fill: var(--label-2); }
.viz-dd-mark.is-trough { fill: var(--neg); }
.viz-dd-mark.is-recovery { fill: var(--pos); }
.viz .viz-dd-label { fill: var(--neg); }

/* stacked yearly */
.viz-ybar { transform-box: view-box; }
.viz-ybar-added { fill: var(--chart-added); }
.viz-ybar-markets.is-pos { fill: var(--pos); }
.viz-ybar-markets.is-neg { fill: var(--neg); }
/* years that are not complete: the same fills at reduced opacity, with a
   small "partial" caption under the year label (no outline) */
.viz-ybar.is-partial rect:not(.viz-hit), svg.viz-mini rect.is-partial { fill-opacity: var(--viz-partial-alpha); }
.viz text.viz-ybar-partial { font: var(--t-footnote); letter-spacing: var(--tr-footnote); fill: var(--label-2); }
.viz .viz-ybar-value { fill: var(--label-2); }
.viz-hit { fill: transparent; cursor: pointer; outline: none; }
.viz-hit:focus-visible { stroke: var(--tint); stroke-width: var(--viz-ring-w); }

/* stacked areas (area100, categoryArea): each band a tinted fill (its hue at
   --viz-cat-fill-alpha over black) under a 1.5 px top edge in the full hue */
.viz-area100-wrap { display: flex; flex-direction: column; }
.viz-area-band { fill-opacity: var(--viz-cat-fill-alpha); stroke: none; }
.viz-area-edge { fill: none; stroke-width: var(--viz-cat-edge-w); stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
svg.viz-mini .viz-area-edge { stroke-width: var(--viz-line-w); }
.viz-legend {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: var(--margin); row-gap: var(--radius-xs);
  min-height: var(--viz-legend-h);
  font: var(--t-subhead); letter-spacing: var(--tr-subhead);
  color: var(--label-2);
  font-variant-numeric: tabular-nums;
}
.viz-legend-item { display: inline-flex; align-items: center; gap: var(--radius-xs); }
.viz-legend-item b { font-weight: 400; color: var(--label); }
.viz-legend-item .viz-chip { margin-right: 0; }

/* period waterfall */
.viz-wf-wrap { display: flex; flex-direction: column; }
.viz-wf-bar { transform-box: view-box; fill: var(--chart-invested); }
.viz-wf-added { fill: var(--chart-added); }
.viz-wf-start, .viz-wf-end { fill: var(--label-3); }
.viz-wf-end { fill: var(--label-2); }
.viz-wf-markets.is-pos { fill: var(--pos); }
.viz-wf-markets.is-neg { fill: var(--neg); }
.viz-wf-tax { fill: var(--label-4); }
.viz .viz-wf-link { stroke: var(--label-3); stroke-width: var(--viz-line-w); vector-effect: non-scaling-stroke; }
.viz .viz-wf-value { fill: var(--label-2); }
.viz .viz-wf-value.is-level { fill: var(--label); }

/* return histogram */
.viz-hist-wrap { display: flex; flex-direction: column; }
.viz-hist-bar { transform-box: view-box; fill: var(--label-3); }
.viz-hist-bar.is-pos { fill: var(--pos); fill-opacity: var(--viz-heat-2); }
.viz-hist-bar.is-neg { fill: var(--neg); fill-opacity: var(--viz-heat-2); }

/* returns heatmap: flat colour steps (no blending) */
.viz-heat text.viz-heat-head, .viz-heat text.viz-heat-year { font: var(--t-footnote); letter-spacing: var(--tr-footnote); fill: var(--label-2); }
.viz-heat .viz-heat-total { fill: var(--label-2); }
.viz-heat-cell { fill: var(--dir, var(--fill)); outline: none; }
.viz-heat-cell.is-empty { fill: var(--fill-2); }
.viz-heat-cell.is-zero, .viz-heat-cell.is-s0 { fill: var(--fill); }
.viz-heat-cell.is-s1 { fill-opacity: var(--viz-heat-1); }
.viz-heat-cell.is-s2 { fill-opacity: var(--viz-heat-2); }
.viz-heat-cell.is-s3 { fill-opacity: var(--viz-heat-3); }
.viz-heat-cell.is-s4 { fill-opacity: var(--viz-heat-4); }
.viz-heat-hit { fill: transparent; cursor: pointer; outline: none; -webkit-tap-highlight-color: transparent; }
.viz-heat-hit:focus-visible { stroke: var(--label); stroke-width: var(--viz-ring-w); }
.viz-heat-cell.is-hl { stroke: var(--label); stroke-width: var(--viz-ring-w); }

/* account small multiples */
.viz-mult {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5em, 1fr));
  gap: var(--radius-sm);
}
.viz-mult-cell {
  display: grid; grid-template-rows: auto auto var(--viz-mult-h) auto; row-gap: var(--hairline);
  min-width: 0; margin: 0; padding: var(--radius-sm);
  border: 0; border-radius: var(--radius-sm);
  background: var(--cell);
  color: inherit; text-align: left;
  font: var(--t-subhead); letter-spacing: var(--tr-subhead);
  font-variant-numeric: tabular-nums;
}
:where(.ui-group, .ui-sheet, .ui-sheet-page, .gal-card) .viz-mult-cell { background: var(--cell-2); }
button.viz-mult-cell { cursor: pointer; -webkit-tap-highlight-color: transparent; transition: background-color var(--dur-press-out) var(--ease-linear); }
button.viz-mult-cell:active { background: var(--fill); transition-duration: 0ms; }
button.viz-mult-cell:focus-visible { outline: calc(var(--hairline) * 4) solid var(--tint); outline-offset: 0; }
/* names and changes wrap between words, never end in "…" */
.viz-mult-name { color: var(--label); overflow-wrap: break-word; }
.viz-mult-value { color: var(--label); }
.viz-mult-change { color: var(--label-2); overflow-wrap: break-word; }
.viz-mult-plot { display: block; width: 100%; height: var(--viz-mult-h); overflow: visible; }

/* allocation treemap: tiles on the elevated surface over 2 px black gaps,
   white name after an 8 px category dot, grey share and value */
.viz-tree { position: relative; overflow: hidden; border-radius: var(--radius-xs); background: var(--viz-tile-gap); }
.viz-tree-tile {
  position: absolute; box-sizing: border-box;
  display: flex; flex-direction: column; justify-content: flex-start; gap: 0;
  margin: 0; padding: var(--radius-xs) var(--radius-xs) var(--radius-xs) calc(var(--radius-xs) + var(--hairline) * 4);
  border: var(--viz-line-w) solid var(--viz-tile-gap);
  border-radius: calc(var(--viz-bar-rx) * 2);
  background: var(--viz-tile);
  overflow: hidden;
  color: var(--label-2); text-align: left;
  font: var(--t-subhead); letter-spacing: var(--tr-subhead);
  font-variant-numeric: tabular-nums;
}
button.viz-tree-tile { cursor: pointer; -webkit-tap-highlight-color: transparent; transition: background-color var(--dur-press-out) var(--ease-linear); }
button.viz-tree-tile:active { background: var(--cell-3); transition-duration: 0ms; }
button.viz-tree-tile:focus-visible { outline: calc(var(--hairline) * 4) solid var(--label); outline-offset: calc(-1 * var(--hairline) * 4); }
.viz-tree-name, .viz-tree-share, .viz-tree-value { display: block; flex: none; overflow: hidden; white-space: nowrap; }
.viz-tree-name { color: var(--label); }
/* a two-word name may wrap between words, never inside one */
.viz-tree-name.is-wrap { white-space: normal; overflow-wrap: normal; word-break: normal; }
.viz-tree-dot {
  display: inline-block; vertical-align: 0.05em;
  width: var(--viz-chip-d); height: var(--viz-chip-d);
  margin-right: var(--radius-xs);
  border-radius: var(--radius-full);
}
svg .viz-tree-mini { stroke: none; fill: var(--viz-tile); }
.viz-tree-legend { margin-top: var(--radius-sm); }
.viz-tree-legend[hidden] { display: none; }
.viz-tree-legend .viz-tree-dot { margin-right: 0; }

/* savings ceilings */
.viz-ceil { display: flex; flex-direction: column; }
.viz-ceil-row {
  display: grid; grid-template-rows: auto auto auto; row-gap: var(--radius-xs);
  min-height: var(--viz-ceil-row-h); margin: 0; padding: var(--radius-xs) 0;
  border: 0; background: none; color: inherit; text-align: left;
  font: var(--t-subhead); letter-spacing: var(--tr-subhead);
  font-variant-numeric: tabular-nums;
}
.viz-ceil-row + .viz-ceil-row { border-top: var(--hairline) solid var(--sep); }
button.viz-ceil-row { cursor: pointer; -webkit-tap-highlight-color: transparent; }
button.viz-ceil-row:focus-visible { outline: calc(var(--hairline) * 4) solid var(--tint); outline-offset: 0; }
.viz-ceil-head { display: flex; justify-content: space-between; gap: var(--margin); }
.viz-ceil-name { color: var(--label); }
.viz-ceil-value, .viz-ceil-room { color: var(--label-2); }
.viz-ceil-track { position: relative; display: block; height: var(--viz-bullet-h); border-radius: var(--viz-bar-rx); background: var(--fill); overflow: hidden; }
.viz-ceil-bar { position: absolute; left: 0; top: 0; bottom: 0; border-radius: var(--viz-bar-rx); background: var(--label); transition: width var(--dur-tween) var(--ease-out); }
.viz-ceil-bar.is-over { background: var(--neg); }
svg .viz-ceil-track { fill: var(--fill); }
svg .viz-ceil-fill { fill: var(--label); }
svg .viz-ceil-fill.is-over { fill: var(--neg); }

/* privacy: amounts drawn as text inside charts follow the mask (vmoney
   already prints •••• €; captions keep their layout) */
.is-masked .viz-tree-value, .is-masked .viz-mult-value { letter-spacing: 0; }
/* With amounts hidden no chart tells a gain from a loss by colour (green and
   red become the secondary label colour), and bullets, ceiling bars and
   gauges drop their fill, which shows an amount against its limit. */
.is-masked :is(.viz-host, .viz, .viz-band-wrap, .modal-fs) {
  --pos: var(--label-2);
  --neg: var(--label-2);
  --viz-tax-gains: var(--label-2);
  --viz-tax-acq: var(--label-3);
}
.is-masked :is(.viz-bullet-bar, .viz-ceil-bar, .viz-gauge-fill),
.is-masked svg .viz-ceil-fill { visibility: hidden; }

/* ------------------------------------------------------------ band: two-finger range
   A flat translucent highlight between the two fingers, a rule and a dot on
   the net line at each end. The end dots follow the sign of the range (the
   privacy mask turns both colours grey). The single-month rule and dot stay
   hidden while a range is shown. */
.viz-band .viz-range { fill: var(--fill); stroke: none; opacity: 0; pointer-events: none; }
.viz-band .viz-range-rule { stroke: var(--label-3); vector-effect: non-scaling-stroke; opacity: 0; pointer-events: none; }
.viz-band .viz-range-dot { fill: var(--pos); stroke: var(--chart-halo, var(--bg)); opacity: 0; pointer-events: none; }
.viz-band.is-range-down .viz-range-dot { fill: var(--neg); }
.viz-band .viz-range, .viz-band .viz-range-rule, .viz-band .viz-range-dot {
  transition: opacity var(--dur-fade) var(--ease-linear);
}
.viz-band.is-range :is(.viz-range, .viz-range-rule, .viz-range-dot) { opacity: 1; }
.viz-band.is-range :is(.viz-rule, .viz-dot, .viz-dot-inv) { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .viz-band .viz-range, .viz-band .viz-range-rule, .viz-band .viz-range-dot { transition: none; }
}

/* estimated history (Settings › Values): a dotted rule at the first real
   month and a caption-size label; the estimated part is faded by an alpha
   mask (charts/common.mjs realDataMarker) */
.viz .viz-real-mark { pointer-events: none; }
.viz .viz-real-rule {
  stroke: var(--label-3); stroke-width: var(--viz-line-w); stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.viz text.viz-real-label {
  font: var(--t-caption2); letter-spacing: var(--tr-caption2);
  fill: var(--label-3);
  paint-order: stroke; stroke: var(--chart-halo, var(--bg)); stroke-width: var(--viz-ring-w); stroke-linejoin: round;
}
/* estimated cells and bars drawn without the mask (heatmap, row charts) */
.viz .is-est { opacity: var(--viz-est-alpha); }
