/* ============================================================
   Board — a session status board for the ASX day.
   Dark is the design's home; light is a designed counterpart,
   not an inversion.
   ============================================================ */

:root {
  color-scheme: dark;
  --bg: #0a0b0d;
  --panel: #131519;
  --panel-2: #191c22;
  --ink: #f2f0eb;
  --ink-2: #a6a89f;
  --ink-3: #75776f;
  --rule: #232730;
  --rail: #1e222a;
  --acc: #d9a441;          /* sodium amber — the session clock, and nothing else */
  --focus: #9c93e4;

  /* score magnitude, sequential indigo. Never the only cue: bar length and the
     number itself always carry the same information. */
  --s1: #2b2752; --s2: #3e3878; --s3: #5a51a8;
  --s4: #7c72ce; --s5: #9c93e4; --s6: #bdb6f2;

  /* Rarity, for the score tiles only. A ladder borrowed from games, where it
     does one thing a plain ramp cannot: it makes a single tile legible without
     comparison. A ramp tells you a score is higher than the tile beside it; a
     grade tells you what this one is, on its own, across a grid of forty-five.
     Ordinal, so it is still a ramp: teal, blue, indigo, plum, then the whole
     sweep at once. The cuts are the owner's, from watching the live board.

     One near-collision, kept apart by context: `--r-uncommon` sits near `--up`,
     the green a rising price uses. Pushed toward teal, and the two never share
     a shape — `--up` is bare inline digits, this is only ever a pill. */
  --r-uncommon: #2c8f86;
  --r-rare:     #3f8fd0;
  --r-epic:     #6b6be0;
  --r-legend:   #7d4fae;
  /* The top of the ladder is not a hue at all. Gold was, and it read as weaker
     than the band below it — a light colour on a dark ground recedes however
     saturated it is, so luminance was arguing against the ordering. A prism
     cannot lose that argument: it is categorically unlike every other grade
     rather than one more step along the same scale.

     And every rung above is a slice of this sweep, walked from its cool end to
     its warm end, so the ladder and its summit are the same object: each lower
     grade is a colour the top tile already contains. An earlier version put
     orange at 80-85, which broke that — warm hues advance, it was the only warm
     thing on the board, and it pulled more attention than its rank deserved.

     First stop equals last so the image is periodic; see the sweep below. */
  --prism: linear-gradient(90deg, #7b5cff, #4aa3f0, #35e0d0, #4aa3f0, #7b5cff);
  --prism-ink: #0b1020;

  /* The mark, as a mask rather than an inline SVG with a gradient of its own.
     Masking a `--prism` background means the icon is painted by the same
     moving image as the tile's border, pill and meter — so on an Exceptional
     tile all four drift together instead of four gradients each running on
     their own clock. It also picks up the light theme's prism for free.
     One path, evenodd, so the tag's hole is a hole and not a filled dot. */
  --tag-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill-rule='evenodd' d='M17.6 2.4 H27 A2.6 2.6 0 0 1 29.6 5 V14.4 A3.2 3.2 0 0 1 28.7 16.6 L16.6 28.7 A3.2 3.2 0 0 1 12.1 28.7 L3.3 19.9 A3.2 3.2 0 0 1 3.3 15.4 L15.4 3.3 A3.2 3.2 0 0 1 17.6 2.4 Z M23.2 5.7 a3.1 3.1 0 1 0 0 6.2 a3.1 3.1 0 1 0 0 -6.2 Z'/%3E%3C/svg%3E");

  --price: #7fb0c4;        /* price is its own panel and its own hue */

  /* Three ways to invest the same money, on the same schedule. Categorical,
     not a ramp — none of them ranks above another a priori. Validated for
     colour-vision separation against this surface in both modes. */
  --plan-a: #7c72ce;       /* follow the score */
  --plan-b: #199e70;       /* same amount, your buy plan */
  --plan-c: #3987e5;       /* same amount, 100% benchmark */
  --up: #0ca30c;
  --dn: #e66767;

  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, Consolas, monospace;

  --r: 12px;
  --gap: 10px;
}

:root[data-theme="light"] {
  color-scheme: light;
  --bg: #f4f3ef;
  --panel: #ffffff;
  --panel-2: #faf9f6;
  --ink: #14151a;
  --ink-2: #5a5c58;
  --ink-3: #83857f;
  --rule: #e3e2dc;
  --rail: #eceae4;
  --acc: #8a5d0b;
  --focus: #4a3fa8;
  --s1: #dad7f0; --s2: #b4aee2; --s3: #8b82d2;
  --s4: #6559be; --s5: #453ba0; --s6: #2e267a;
  /* Same ladder, re-mixed for ink on paper: each one dark enough to carry
     white text at the size these pills are set. */
  --r-uncommon: #167a72;
  --r-rare:     #2a6fb8;
  --r-epic:     #5145c0;
  --r-legend:   #6b3f99;
  /* Deeper on paper, so white ink carries and the sweep does not wash out. */
  --prism: linear-gradient(90deg, #5b3fd6, #2f7fd4, #17a8b4, #2f7fd4, #5b3fd6);
  --prism-ink: #fffdf7;
  --price: #2f6f8f;
  --plan-a: #6559be;
  --plan-b: #0d8f61;
  --plan-c: #2a78d6;
  --up: #0ca30c;
  --dn: #d03b3b;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 50;
  background: var(--panel); color: var(--ink); padding: 10px 14px; border-radius: 0 0 8px 0;
}
.skip:focus { left: 0; }

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 6px;
}

/* --- shared type roles ------------------------------------------------- */
.lbl {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.lbl.accent { color: var(--acc); }

/* MONEY is monospace and always carries a $.
   SCORE is sans and always carries /100.
   The two must never be mistakable for each other at a glance. */
.money { font-family: var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.score-val { font-family: var(--sans); font-weight: 700; font-variant-numeric: tabular-nums; letter-spacing: -.03em; }
.score-suffix { font-family: var(--sans); font-weight: 500; color: var(--ink-3); }

/* --- top bar ----------------------------------------------------------- */
.bar {
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--rule);
}
/* Three columns with equal side rails, so the title is optically centred
   regardless of how wide the two buttons are. */
.bar-in {
  max-width: 1180px; margin: 0 auto;
  padding: 10px 16px; padding-left: max(16px, env(safe-area-inset-left));
  padding-right: max(16px, env(safe-area-inset-right));
  display: grid; grid-template-columns: 34px 1fr 34px; align-items: center; gap: 10px;
}
.bar-side { position: relative; display: flex; }
.bar-side.end { justify-content: flex-end; }

/* Mark and words are one object, and the way to get that is to put the mark
   *in* the text rather than beside it. Two earlier attempts failed differently
   and both are worth remembering: `inline-flex` made the anchor an inline-level
   box, so the grid area's inherited `text-align` positioned it and the group
   drifted left of centre; `flex` centred the group correctly but, once the
   title wrapped to two lines on a phone, left the mark stranded out beside the
   menu button because it was a sibling of the whole text block rather than part
   of it. As an inline-block inside the sentence it behaves like any other
   glyph: it stays attached to the first word, and one `text-align: center`
   centres every line including the one carrying the mark. */
.wordmark {
  grid-column: 2;
  display: block;
  text-align: center;
  font-size: clamp(15px, 4.1vw, 21px);
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.15;
  color: var(--ink);
  text-decoration: none;
  text-wrap: balance;
}
.wordmark:hover { color: var(--ink); }

/* `flex: 0 0 auto` for the tile, where the mark is a flex item and must not be
   squeezed by a long fund name. The negative `vertical-align` centres the box
   on the cap height rather than sitting it on the baseline, which is where an
   inline-block lands by default and reads as a tag falling off the line. */
.mark {
  display: inline-block; flex: 0 0 auto;
  width: 1.05em; height: 1.05em;
  vertical-align: -.16em;
  background: var(--prism); background-size: 200% 100%;
  -webkit-mask: var(--tag-mask) center / contain no-repeat;
  mask: var(--tag-mask) center / contain no-repeat;
}
.wordmark .mark { margin-right: .34em; }

/* menu ----------------------------------------------------------------- */
.menu {
  position: absolute; top: calc(100% + 9px); left: 0; z-index: 40;
  min-width: 190px; padding: 6px;
  background: var(--panel); border: 1px solid var(--rule); border-radius: 11px;
  box-shadow: 0 12px 32px rgb(0 0 0 / .28);
  display: flex; flex-direction: column; gap: 2px;
}
.menu[hidden] { display: none; }
.menu a {
  display: block; padding: 9px 11px; border-radius: 7px;
  color: var(--ink-2); text-decoration: none; font-size: 14px; font-weight: 550;
}
.menu a:hover { background: var(--rail); color: var(--ink); }
.menu a[aria-current="page"] {
  color: var(--ink); background: var(--rail);
  box-shadow: inset 2px 0 0 var(--acc);
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.icon-btn {
  appearance: none; border: 1px solid var(--rule); background: var(--panel);
  color: var(--ink-2); width: 34px; height: 34px; border-radius: 9px;
  display: grid; place-items: center; cursor: pointer; flex: 0 0 auto;
}
.icon-btn:hover { color: var(--ink); border-color: var(--ink-3); }
.icon-btn .i-sun { display: none; }
:root[data-theme="light"] .icon-btn .i-sun { display: block; }
:root[data-theme="light"] .icon-btn .i-moon { display: none; }

/* --- layout ------------------------------------------------------------ */
.pad {
  max-width: 1180px; margin: 0 auto;
  padding: 16px 16px 40px;
  padding-left: max(16px, env(safe-area-inset-left));
  padding-right: max(16px, env(safe-area-inset-right));
  display: flex; flex-direction: column; gap: 26px;
}
.split { display: grid; grid-template-columns: 1fr; gap: 16px; }
.sec-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 9px; }
.sec-head.wrap-head { flex-wrap: wrap; row-gap: 10px; }

/* --- verdict band ------------------------------------------------------ */
.band { background: var(--panel); border-radius: var(--r); padding: 18px 16px; }
.band-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; }
.score-hero { display: flex; align-items: baseline; gap: 4px; margin-top: 8px; }
.score-num {
  font-weight: 700; font-size: 60px; line-height: .84;
  letter-spacing: -.045em; font-variant-numeric: tabular-nums;
}
.score-den { font-size: 19px; font-weight: 500; color: var(--ink-3); letter-spacing: -.01em; }

.pill {
  display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
  padding: 5px 11px; border-radius: 999px;
  background: var(--rail); color: var(--ink);
  font-size: 12px; font-weight: 650; letter-spacing: .01em;
}
.pill i { width: 7px; height: 7px; border-radius: 50%; background: var(--s4); flex: 0 0 auto; }
.blurb { margin: 12px 0 0; font-size: 14px; line-height: 1.55; color: var(--ink-2); max-width: 48ch; }

.clock { margin-top: 18px; }
.clock-track { position: relative; height: 8px; background: var(--rail); border-radius: 999px; overflow: hidden; }
.clock-fill { position: absolute; inset: 0 auto 0 0; width: 0; background: var(--acc); border-radius: 999px; }
.clock-row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-top: 7px; }

/* --- buy plan ----------------------------------------------------------- */
/* A table, not tiles: it sits beside a board of tiles and must not read as a
   second copy of it. Scores keep the board's tier colours, so 75 here and 75 on
   a tile are visibly the same grade. */
.plan-card { background: var(--panel); border-radius: var(--r); padding: 6px 14px 10px; }
.plan-card [data-tier]         { --tc: var(--ink-3); }
.plan-card [data-tier="base"]   { --tc: var(--r-uncommon); }
.plan-card [data-tier="good"]   { --tc: var(--r-rare); }
.plan-card [data-tier="strong"] { --tc: var(--r-epic); }
.plan-card [data-tier="high"]   { --tc: var(--r-legend); }
.plan-card [data-tier="peak"]   { --tc: var(--s5); }
.plan-empty { margin: 8px 0; font-size: 12.5px; color: var(--ink-2); }

/* The ticker column takes the slack, so the trend and the score sit together
   on the right, the way a watchlist reads: what it is, which way it is going,
   where it is now. The sparkline is a fixed 88px whatever the panel's width;
   stretched across the column it was a chart, and this only needs to be a
   direction. */
.plan { width: 100%; border-collapse: collapse; table-layout: fixed; }
.plan .c-sp { width: 128px; }
.plan .c-sc { width: 122px; }
.plan th, .plan td { padding: 0 0 0 10px; height: 38px; text-align: left; vertical-align: middle; }
.plan th:first-child, .plan td:first-child { padding-left: 0; }
.plan thead th {
  height: 32px; font-size: 10.5px; font-weight: 650; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-3); white-space: nowrap;
}
.plan tbody tr { border-top: 1px solid var(--rule); }
/* The two sortable headers: the label is the button, as in the holdings
   table, and the arrow marks the column the rows are sorted by. */
.plan thead button {
  appearance: none; border: 0; background: none; padding: 4px 0; margin: 0; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit; white-space: nowrap;
}
.plan thead button:hover,
.plan thead th[aria-sort="ascending"] button,
.plan thead th[aria-sort="descending"] button { color: var(--ink); }
.plan .sort-i { display: inline-block; min-width: 1em; margin-left: 3px; font-size: 8px; vertical-align: 1px; color: var(--s5); }
/* The score sits at the left of its cell, under its header, in a fixed-width
   slot so the day's change beside it lines up down the column. */
.plan .pl-scv { display: inline-block; min-width: 54px; }

/* The fund name lives behind the ticker. The dotted underline is the usual
   promise that there is more to an abbreviation; focus shows it too, so a tap
   on a phone gets it where there is no hover. */
.plan .pl-tk {
  position: relative; display: inline-block; cursor: help;
  font-family: var(--mono); font-size: 13px; font-weight: 650; letter-spacing: .04em; color: var(--ink);
  text-decoration: underline dotted color-mix(in srgb, var(--ink-3) 70%, transparent);
  text-underline-offset: 4px; text-decoration-thickness: 1px;
}
.plan .pl-tk:focus { outline: none; }
.plan .pl-tk:focus-visible { outline: 2px solid var(--s4); outline-offset: 3px; border-radius: 3px; }
.plan .pl-tk::after {
  content: attr(data-name); position: absolute; left: -8px; bottom: calc(100% + 7px); z-index: 5;
  white-space: nowrap; pointer-events: none; opacity: 0; transform: translateY(2px);
  transition: opacity .12s, transform .12s;
  font-family: var(--sans); font-size: 12px; font-weight: 550; letter-spacing: 0;
  padding: 5px 9px; border-radius: 7px; background: var(--ink); color: var(--bg);
  box-shadow: 0 4px 14px rgb(0 0 0 / .25);
}
.plan .pl-tk:hover::after, .plan .pl-tk:focus::after { opacity: 1; transform: none; }
.plan tr[data-tier="quiet"] .pl-tk { color: var(--ink-2); }
/* The theme sits after the ticker, which is a fixed width so every theme
   starts on the same line down the column. On a phone the ticker column is
   too narrow for both side by side, so the theme drops under the ticker. */
.plan tbody th { white-space: nowrap; overflow: visible; }
.plan .pl-tk { min-width: 46px; }
.plan .pl-th {
  margin-left: 10px; font-size: 12px; font-weight: 400; color: var(--ink-3);
  white-space: nowrap; vertical-align: middle;
}
/* On its own line under the fund: beside the theme it ran into the sparkline
   column at every desktop width. */
.plan .pl-cap { display: block; line-height: 1; margin: 4px 0 1px; }
.plan .cap-chip {
  display: inline-block; cursor: help; white-space: nowrap;
  font-family: var(--mono); font-size: 8.5px; font-weight: 650;
  letter-spacing: .09em; text-transform: uppercase;
  padding: 2.5px 7px; border-radius: 999px; color: var(--ink);
  background: color-mix(in srgb, var(--dn) 9%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dn) 55%, transparent);
}
/* Keyed to the card's own width, not the window's. From 720px the plan sits in
   the narrower of two columns, and between there and about 1000px the window
   was "desktop" while the card was phone-sized: the theme ran across the
   sparkline on every row. */
.plan-card { container-type: inline-size; }
@container (max-width: 425px) {
  .plan tbody th, .plan tbody td { height: 46px; }
  .plan tbody th { white-space: normal; line-height: 1.25; }
  .plan .pl-th { display: block; margin: 2px 0 0; font-size: 11px; white-space: normal; }
  .plan .c-sp { width: 82px; }
  .plan .pl-spark { width: 72px; }
  /* "Score Trend (6M)" is wider than the sparkline it heads; on a phone it
     takes two lines rather than squeezing the ticker column. */
  .plan thead th.trend { white-space: normal; line-height: 1.25; }
}
/* The narrowest the two-column layout gets (a 720px window): give the ticker
   column the width of a "Bubble −N" chip, from the sparkline. */
@container (max-width: 300px) {
  .plan .c-sp { width: 64px; }
  .plan .pl-spark { width: 56px; }
}

.pl-spark { position: relative; display: block; width: 88px; height: 22px; }
.pl-spark svg { display: block; width: 100%; height: 22px; overflow: visible; }
/* Line only. An area fill under nineteen of these turned each row into a
   shaded box. */
.pl-spark path { fill: none; stroke: var(--tc); stroke-width: 1.5; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.pl-spark i {
  position: absolute; right: -2.5px; width: 5px; height: 5px; margin-top: -2.5px;
  border-radius: 50%; background: var(--tc);
}

.plan .score-val { font-size: 16px; color: var(--tc); }
.plan .score-suffix { font-size: 10.5px; }
.plan tr[data-tier="quiet"] .score-val { color: var(--ink-2); font-weight: 600; }
/* The rarest grade wears the board's prism here too. */
.plan tr[data-tier="peak"] .score-val {
  background: var(--prism); background-size: 200% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
/* Beside the score, in a fixed-width slot so the scores still line up down
   the column whether a row moved 0.3 or 12.4. Sans, like the score it
   qualifies: it is points out of 100, not money. */
.pl-d {
  display: inline-block; width: 46px; margin-left: 6px; text-align: right;
  font-family: var(--sans); font-size: 11.5px; font-weight: 600;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.pl-d.flat { color: var(--ink-3); }

.plan-key {
  display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 2px; padding-top: 9px;
  border-top: 1px solid var(--rule); font-size: 11px; color: var(--ink-3);
}
.plan-key span { white-space: nowrap; }
.plan-key i {
  display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 5px;
  vertical-align: -.5px; background: var(--tc);
}
.plan-key [data-tier="peak"] i { background: var(--prism); }

.fineprint { font-size: 11.5px; color: var(--ink-3); line-height: 1.55; margin: 10px 0 0; }

/* --- holdings today ----------------------------------------------------- */
/* Money throughout, so monospace with a $; unit prices keep the tiles' bare
   two decimals. Red and green are price direction, as everywhere else. */
.kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap); margin-bottom: var(--gap); }
.kpi { position: relative; background: var(--panel); border-radius: var(--r); padding: 14px 16px; min-width: 0; }
/* The "?" beside a card label opens the same note as the one beside the
   market time; the note is placed against the card, not the label. */
.kpi .hint { margin-left: 2px; }
.kpi .hint-pop { top: 38px; left: 12px; }
.hint-pop b { color: var(--ink); font-weight: 600; }
.kpi .lbl { display: block; margin-bottom: 8px; }
.kpi .v {
  font-family: var(--mono); font-size: 22px; font-weight: 650; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.kpi .s { font-family: var(--mono); font-size: 12px; margin-top: 4px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.kpi .q { color: var(--ink-3); }
/* The day's move is the reason to open the section, so its card is the one
   outlined, in the colour of the move. */
.kpi.lead { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink-3) 45%, transparent); }
.kpi.lead.gain { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--up) 45%, transparent); }
.kpi.lead.loss { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dn) 45%, transparent); }
.kpi.lead .v { font-size: 26px; }
@media (max-width: 719px) {
  .kpis { grid-template-columns: 1fr 1fr; }
  .kpi .v { font-size: 17px; }
  .kpi.lead .v { font-size: 20px; }
}

/* The list, folded under the cards. The bar is the same width and weight as
   the cards so it reads as part of the section, not as a stray link. */
.hold-list > summary {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  list-style: none; cursor: pointer; user-select: none;
  background: var(--panel); border-radius: var(--r); padding: 10px 14px;
  font-size: 12.5px; font-weight: 600; color: var(--ink-2);
}
.hold-list > summary::-webkit-details-marker { display: none; }
.hold-list > summary:hover { color: var(--ink); }
.hold-list > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.hold-list > summary::after {
  content: ""; width: 6px; height: 6px; margin-top: -3px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); transition: transform .15s ease;
}
.hold-list[open] > summary::after { transform: rotate(-135deg); margin-top: 3px; }
.hold-list .when-open, .hold-list[open] .when-shut { display: none; }
.hold-list[open] .when-open { display: inline; }
.hold-list[open] > summary { border-radius: var(--r) var(--r) 0 0; border-bottom: 1px solid var(--rule); }
.hold-list[open] .ledger-wrap { border-radius: 0 0 var(--r) var(--r); }
@media (prefers-reduced-motion: reduce) { .hold-list > summary::after { transition: none; } }

/* Nine columns do not fit a phone, so the table scrolls sideways inside its
   card with the ticker pinned, rather than dropping columns someone asked for. */
.ledger-wrap { background: var(--panel); border-radius: var(--r); padding: 4px 14px 8px; overflow-x: auto; }
.ledger { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.ledger th, .ledger td { padding: 0 0 0 14px; height: 36px; text-align: right; white-space: nowrap; vertical-align: middle; }
.ledger tr > :first-child { padding-left: 0; text-align: left; }
.ledger td { font-family: var(--mono); font-size: 12.5px; color: var(--ink-2); }
.ledger td.val { color: var(--ink); font-weight: 600; }
.ledger .up { color: var(--up); }
.ledger .dn { color: var(--dn); }
.ledger .nil { color: var(--ink-3); }
.ledger tbody tr { border-top: 1px solid var(--rule); }
.ledger tbody th { font-weight: 400; }
.ledger .tk { font-family: var(--mono); font-size: 13px; font-weight: 650; color: var(--ink); letter-spacing: .04em; }
.ledger .th { font-size: 11.5px; color: var(--ink-3); margin-left: 9px; }
.ledger tfoot td, .ledger tfoot th { border-top: 1.5px solid var(--ink-3); height: 42px; color: var(--ink); font-weight: 650; }
.ledger tfoot th { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2); }

/* Sortable headers: the whole label is the button. The arrow marks the one
   column the rows are sorted by, and which way. */
.ledger thead th { height: 34px; }
.ledger thead button {
  appearance: none; border: 0; background: none; padding: 4px 0; margin: 0; cursor: pointer;
  font: inherit; font-size: 10.5px; font-weight: 650; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-3); white-space: nowrap;
}
.ledger thead button:hover { color: var(--ink); }
.ledger thead th[aria-sort="ascending"] button,
.ledger thead th[aria-sort="descending"] button { color: var(--ink); }
.ledger .sort-i { display: inline-block; min-width: 1em; margin-left: 3px; font-size: 8px; vertical-align: 1px; color: var(--s5); }

@media (max-width: 719px) {
  .ledger tr > :first-child { position: sticky; left: 0; z-index: 1; background: var(--panel); padding-right: 10px; }
  .ledger .th { display: none; }
}

/* --- chart ------------------------------------------------------------- */
.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  appearance: none; cursor: pointer;
  border: 1px solid var(--rule); background: var(--panel);
  color: var(--ink-2); font: inherit; font-size: 12px; font-weight: 600;
  padding: 5px 11px; border-radius: 999px; white-space: nowrap;
}
.chip:hover { color: var(--ink); border-color: var(--ink-3); }
.chip[aria-pressed="true"] { background: var(--s4); border-color: var(--s4); color: #fff; }
:root[data-theme="light"] .chip[aria-pressed="true"] { background: var(--s5); border-color: var(--s5); }

.chart-card { margin: 0; background: var(--panel); border-radius: var(--r); padding: 14px 12px 12px; }
.chart-cap {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 0 4px 8px; font-size: 12.5px; color: var(--ink-2); min-height: 22px;
}
.readout { font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); font-variant-numeric: tabular-nums; text-align: right; }
.readout b { color: var(--ink); font-weight: 600; }
.chart-host { width: 100%; touch-action: pan-y; }
#chart { display: block; width: 100%; height: auto; overflow: visible; }

/* --- holdings tiles ---------------------------------------------------- */
.grid { display: grid; grid-template-columns: 1fr; gap: 8px; }
.tile {
  background: var(--panel); border-radius: 10px; padding: 11px 13px;
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 3px 12px;
  border: 1px solid transparent; text-align: left; width: 100%;
  font: inherit; color: inherit; cursor: default;
}
button.tile { cursor: pointer; }
button.tile:hover { border-color: var(--ink-3); }
button.tile[aria-pressed="true"] { border-color: var(--s4); }
/* Which tile is driving the chart, visible while scanning the grid. Its own
   ring rather than a tier colour, so it does not read as a stronger score. */
button.tile[aria-pressed="true"] { box-shadow: inset 0 0 0 1px var(--s4); }

/* Confirmation next to the tiles, and a way up to the chart for whoever wants
   one — instead of taking them there uninvited. */
.charted {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 10px; border-radius: 999px; text-decoration: none;
  background: var(--panel); border: 1px solid var(--rule); color: var(--ink-2);
}
.charted b { font-family: var(--mono); font-size: 12px; font-weight: 650; color: var(--ink); }
.charted .go { color: var(--s5); font-size: 13px; line-height: 1; }
.charted:hover { border-color: var(--s4); color: var(--ink); }
.charted[hidden] { display: none; }
.tile .tk { font-family: var(--mono); font-size: 14px; font-weight: 650; letter-spacing: .04em; }
.tile .px { font-family: var(--mono); font-size: 11.5px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.tile .sc { text-align: right; white-space: nowrap; }
.tile .sc .score-val { font-size: 20px; }
.tile .sc .score-suffix { font-size: 11px; }
.tile .meter { grid-column: 1 / -1; height: 5px; background: var(--rail); border-radius: 999px; overflow: hidden; margin-top: 6px; }
.tile .meter i { display: block; height: 5px; border-radius: 999px; background: var(--s3); }

/* --- how loudly a tile speaks -------------------------------------------
   Bar length alone was carrying the whole signal, and a 78 next to a 61 is
   nine pixels in a grid of forty-five — invisible while scanning.

   So each tile is graded, and the grade is a rarity ladder: teal, blue,
   indigo, orange, gold. Borrowed from games because it solves the problem a
   sequential ramp does not. A ramp only means something by comparison — this
   indigo is darker than that one, so it must be lower — which requires reading
   two tiles. A rarity ladder is learned once and then read absolutely: gold
   means rare wherever it appears, with nothing beside it.

   One custom property carries the grade, and the ground, edge, bar and pill
   all mix from it, so a tier is one line and can never half-apply.

   Emphasis is spent, not sprayed. Nothing under 60 is graded at all, and the
   glow is reserved for the top two bands, which on an ordinary day are empty.
   If everything glowed, nothing would.

   Redundant on purpose: grade, bar length, number size and a word in plain
   English all say the same thing, so none of it is hue alone.

   This is presentation. The money is set by the anchor curve in config.yml,
   and none of this touches it. */
/* "EXCEPTIONAL VALUE BUY" does not fit beside a 24px numeral — squeezed into
   the score's column it wrapped to three clipped lines. Its own full-width row
   under the price, where the tile is at its widest, holds every label on one
   line at every tier. */
/* The tier pill, or the kind chip on a muted tile, on a full-width row of its
   own under the price. */
.tile .tile-tags {
  grid-column: 1 / -1; display: flex; flex-wrap: wrap;
  align-items: center; gap: 6px; margin: 7px 0 0;
}
.tile .tile-tags .tier { margin: 0; }

/* --- the second shelf -----------------------------------------------------
   Sectors, themes, commodities and bonds. Muted rather than hidden: the score
   and meter are still there and still ranked, but nothing on the tile spends
   the grade ladder's colour or says "buy". The dashed edge marks the tile as a
   different kind of thing at a glance, and the striped chip names which kind. */
.shelf + .shelf { margin-top: 28px; }
.shelf-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px; margin: 0 0 10px; }
.shelf-head .lbl { color: var(--ink-2); }
.shelf-note { font-size: 12.5px; line-height: 1.5; color: var(--ink-3); max-width: 72ch; }
.tile[data-tier="muted"] {
  border: 1px dashed color-mix(in srgb, var(--ink-3) 55%, transparent);
}
.tile .kind {
  font-family: var(--mono); font-size: 8.5px; font-weight: 650;
  letter-spacing: .09em; text-transform: uppercase; white-space: nowrap;
  padding: 2.5px 7px; border-radius: 999px; color: var(--ink);
  background: repeating-linear-gradient(-45deg,
    color-mix(in srgb, var(--ink-3) 30%, transparent) 0 3px, transparent 3px 6px);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink-3) 70%, transparent);
}
/* The bubble cap: the one chip that says the number was held down. Outlined
   in the loss colour, faintly, so it reads as a caution rather than a grade. */
.tile .kind.cap {
  background: color-mix(in srgb, var(--dn) 9%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dn) 55%, transparent);
  cursor: help;
}
.tile .tier {
  grid-column: 1 / -1; justify-self: start; margin: 7px 0 0;
  font-family: var(--mono); font-size: 8.5px; font-weight: 650;
  letter-spacing: .09em; text-transform: uppercase; white-space: nowrap;
  padding: 2.5px 7px; border-radius: 999px;
  background: var(--rar); color: var(--rar-ink, #fff);
}
.tile[data-tier] .meter i { background: var(--rar, var(--s3)); }
.tile[data-tier="quiet"] .meter i { background: var(--ink-3); opacity: .55; }
/* After the rule above it, which would otherwise paint a muted meter in the
   default indigo: same specificity, so order decides. */
.tile[data-tier="muted"] .meter i { background: var(--ink-3); opacity: .8; }
.tile[data-tier="quiet"] .sc .score-val { color: var(--ink-2); font-weight: 600; }
.tile[data-tier="quiet"] .tk { color: var(--ink-2); }

/* Graded tiles take their ground and edge from the same one colour. */
.tile[data-tier="base"],
.tile[data-tier="good"],
.tile[data-tier="strong"],
.tile[data-tier="high"],
.tile[data-tier="peak"] {
  background: color-mix(in srgb, var(--rar) var(--rar-fill), var(--panel));
  border-color: color-mix(in srgb, var(--rar) var(--rar-edge), transparent);
}

/* The commonest graded band, so it is the one that must not shout: an outlined
   pill and a quieter bar. A grid where a third of the tiles wear a filled
   badge is a grid with no hierarchy at all. */
.tile[data-tier="base"]   { --rar: var(--r-uncommon); --rar-fill: 4%;  --rar-edge: 18%; }
.tile[data-tier="base"] .tier {
  background: transparent; color: var(--rar);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--rar) 55%, transparent);
}
.tile[data-tier="base"] .meter i { opacity: .8; }
.tile[data-tier="good"]   { --rar: var(--r-rare);     --rar-fill: 8%;  --rar-edge: 32%; }
.tile[data-tier="strong"] { --rar: var(--r-epic);     --rar-fill: 12%; --rar-edge: 44%; }

/* The warm end. Orange and gold appear nowhere else in the grid, so a tile
   that reaches them is picked out of forty-five without being looked for. */
.tile[data-tier="high"] {
  --rar: var(--r-legend); --rar-fill: 15%; --rar-edge: 62%;
  box-shadow: 0 0 16px -2px color-mix(in srgb, var(--r-legend) 30%, transparent);
}
.tile[data-tier="high"] .meter { height: 7px; }
.tile[data-tier="high"] .meter i { height: 7px; }
.tile[data-tier="high"] .sc .score-val { font-size: 23px; }

/* The prism tier. Its border is the gradient itself — two backgrounds, one
   clipped to the padding box and one to the border box — because a border
   cannot take a gradient any other way. */
/* The sweep travels exactly one image width per cycle: the image is 200% of the
   box and the position runs 0% to 200%, which resolves to a shift of exactly
   2x the box — one whole period — so the wrap is invisible. It was 220% and
   0-220% before, a shift of 1.2 periods, and it snapped back mid-pattern every
   twelve seconds. Measured across the wrap: 108 out of 765 before, 3 after,
   which is the gradient's own per-pixel slope. Any change to one number here
   must change the other. */
.tile[data-tier="peak"] {
  --tint: color-mix(in srgb, #6a5cf0 13%, var(--panel));
  border: 2px solid transparent;
  background: linear-gradient(var(--tint), var(--tint)) padding-box, var(--prism) border-box;
  background-size: auto, 200% 100%;
  box-shadow: 0 0 26px -4px rgb(120 140 255 / .34);
  position: relative; overflow: hidden;
}
.tile[data-tier="peak"] .tier {
  background: var(--prism); background-size: 200% 100%;
  color: var(--prism-ink);
}
.tile[data-tier="peak"] .meter { height: 8px; }
.tile[data-tier="peak"] .meter i {
  height: 8px; background: var(--prism); background-size: 200% 100%;
  box-shadow: 0 0 10px rgb(150 190 255 / .5);
}

/* Motes. Each drifts up on its own clock with a negative delay, so they never
   pulse together, and each begins and ends at zero opacity — the restart is
   behind a fade, not a cut. Sized and placed in JS so no two tiles share a
   pattern. */
.spark {
  position: absolute; width: var(--sz); height: var(--sz); border-radius: 50%;
  pointer-events: none; opacity: 0;
  background: radial-gradient(circle, var(--c) 0%,
              color-mix(in srgb, var(--c) 55%, transparent) 45%, transparent 72%);
}
/* The number itself stays plain ink: it is the most important thing on the
   tile, and nothing beats maximum contrast for that job. */
.tile[data-tier="peak"] .sc .score-val { font-size: 24px; color: var(--ink); }

/* The mark, in the score line and only on this tier — it is the badge for a
   reading this rare, so putting it on every tile would spend it. Sized in `em`
   off the score so it tracks the 24px number here and the 22px one on a phone
   without a second breakpoint. Sits on the baseline of the digits rather than
   the line box, which is what "same height as the text" actually means. */
.tile[data-tier="peak"] .sc {
  display: inline-flex; align-items: baseline; gap: .3em;
}
.tile[data-tier="peak"] .sc .mark {
  width: .82em; height: .82em; font-size: 24px;
  align-self: center;
  filter: drop-shadow(0 0 6px rgb(150 190 255 / .55));
}

/* Ink on paper takes tint far less kindly than ink on black — the same fills
   read as a stain. Tint less, let the edge and the pill do the work. */
:root[data-theme="light"] .tile[data-tier="base"]   { --rar-fill: 4%; }
:root[data-theme="light"] .tile[data-tier="good"]   { --rar-fill: 5%; }
:root[data-theme="light"] .tile[data-tier="strong"] { --rar-fill: 6%; }
:root[data-theme="light"] .tile[data-tier="high"]   { --rar-fill: 8%; }
:root[data-theme="light"] .tile[data-tier="peak"] { --tint: color-mix(in srgb, #6a5cf0 7%, var(--panel)); }

/* A glow is a light-source effect and does not survive a white page, so the
   warm tiers get a heavier edge there instead. */
:root[data-theme="light"] .tile[data-tier="high"] { box-shadow: none; border-width: 1.5px; }
:root[data-theme="light"] .tile[data-tier="peak"] { box-shadow: 0 0 0 1px rgb(0 0 0 / .05); }

@media (prefers-reduced-motion: no-preference) {
  /* The rarest reading gets the only movement on the page: the prism drifting
     across itself over fourteen seconds, and a handful of motes. Slow enough to
     register out of the corner of an eye, never fast enough to ask to be
     watched. */
  .tile[data-tier="peak"],
  .tile[data-tier="peak"] .tier,
  .tile[data-tier="peak"] .mark,
  .tile[data-tier="peak"] .meter i { animation: prism 14s linear infinite; }
  @keyframes prism {
    from { background-position: 0% 50%; }
    to   { background-position: 200% 50%; }
  }
  .spark { animation: mote var(--d) ease-in-out var(--t) infinite; }
  @keyframes mote {
    0%   { transform: translate3d(0, 10px, 0) scale(.5); opacity: 0; }
    18%  { opacity: .9; }
    65%  { opacity: .45; }
    100% { transform: translate3d(6px, -46px, 0) scale(1.1); opacity: 0; }
  }
}
.up { color: var(--up); }
.dn { color: var(--dn); }

.foot { font-size: 12px; color: var(--ink-3); line-height: 1.6; max-width: 68ch; margin: 6px 0 0; }
.err { background: var(--panel); border: 1px solid var(--dn); border-radius: var(--r); padding: 14px 16px; color: var(--ink-2); font-size: 13.5px; }

@media (min-width: 720px) {
  .pad { padding: 24px 24px 56px; gap: 32px; }
  .split { grid-template-columns: 1.3fr 1fr; gap: 22px; align-items: start; }
  .band { padding: 24px 26px; }
  .score-num { font-size: 88px; }
  .score-den { font-size: 24px; }
  .grid { grid-template-columns: repeat(3, 1fr); }
  .chart-card { padding: 16px 16px 14px; }
  .tile .sc .score-val { font-size: 22px; }
}
@media (min-width: 1040px) {
  .grid { grid-template-columns: repeat(4, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}


/* ============================================================
   Prose pages (How it works, Contact) — same board, read rather
   than scanned, so the type gets room and the measure is capped.
   ============================================================ */
.prose { max-width: 720px; gap: 18px; }
.prose h1 {
  font-size: clamp(26px, 6vw, 38px); font-weight: 700; letter-spacing: -.03em;
  line-height: 1.1; margin: 6px 0 0; text-wrap: balance;
}
.prose h2 {
  font-size: 17px; font-weight: 650; letter-spacing: -.01em;
  margin: 18px 0 0; padding-bottom: 8px; border-bottom: 1px solid var(--rule);
}
.prose p { margin: 0; color: var(--ink-2); line-height: 1.68; max-width: 66ch; }
.prose .standfirst { font-size: 17px; line-height: 1.6; color: var(--ink); margin-top: 4px; }
.prose strong { color: var(--ink); font-weight: 650; }
.prose a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--s4); }
.prose a:hover { text-decoration-color: var(--ink); }
.prose .fineprint { max-width: 66ch; }

.prose .btn {
  display: inline-block; padding: 9px 16px; border-radius: 9px;
  background: var(--s4); color: #fff; text-decoration: none;
  font-size: 14px; font-weight: 600;
}
:root[data-theme="light"] .prose .btn { background: var(--s5); }
.prose .btn:hover { filter: brightness(1.08); }

.prose .callout {
  margin-top: 14px; padding: 13px 15px; border-radius: 10px;
  background: var(--panel); border-left: 3px solid var(--acc);
  font-size: 13px; color: var(--ink-2);
}

.tbl-scroll { overflow-x: auto; }
.prose table { border-collapse: collapse; width: 100%; min-width: 460px; font-size: 14px; }
.prose thead th {
  text-align: left; font-family: var(--mono); font-size: 10px; font-weight: 400;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3);
  padding: 0 12px 8px 0; border-bottom: 1px solid var(--rule);
}
.prose tbody td { padding: 9px 12px 9px 0; border-bottom: 1px solid var(--rule); color: var(--ink-2); vertical-align: top; }
.prose tbody td:first-child { color: var(--ink); font-weight: 550; white-space: nowrap; }
.prose tbody td:nth-child(2) { font-family: var(--mono); font-variant-numeric: tabular-nums; color: var(--ink-3); }

/* --- strategy-vs-benchmark chart --------------------------------------- */
.legend { display: flex; flex-wrap: wrap; gap: 14px; padding: 8px 4px 0; }
.legend span { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--ink-2); }
.legend i { width: 14px; height: 3px; border-radius: 2px; flex: 0 0 auto; }
/* The comparison index is drawn dashed and set back, so its swatch says so. */
.legend i.dash {
  height: 0; border-radius: 0; background: none !important;
  border-top: 2px dashed currentColor; color: var(--ink-3); opacity: .85;
}

.controls { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 14px;
  padding-top: 14px; border-top: 1px solid var(--rule); }
.ctl { display: flex; flex-direction: column; gap: 7px; }
.ctl .lbl { display: flex; align-items: baseline; gap: 6px; }
.ctl .lbl b { color: var(--ink); font-size: 13px; letter-spacing: 0; text-transform: none; font-weight: 650; }
.ctl input[type="range"] { width: 100%; accent-color: var(--s4); height: 22px; cursor: pointer; }
:root[data-theme="light"] .ctl input[type="range"] { accent-color: var(--s5); }

/* The sliders are 22px tall; match it so the three controls sit on one line. */
.ctl-select select {
  width: 100%; height: 32px; padding: 0 30px 0 10px;
  font: inherit; font-size: 13px; color: var(--ink);
  background: var(--panel-2, var(--panel)); border: 1px solid var(--rule);
  border-radius: 8px; cursor: pointer; appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 13px top 14px, right 8px top 14px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.ctl-select select:hover { border-color: var(--ink-3); }
.ctl-select select:focus-visible { outline: 2px solid var(--s4); outline-offset: 2px; }

@media (min-width: 720px) {
  .controls { grid-template-columns: 1fr 1fr; gap: 22px; }
}
@media (min-width: 900px) {
  .controls { grid-template-columns: 1fr 1fr 1fr; }
}

/* The amount/cadence/benchmark row drives both charts in its section. */
.ctl-hint { margin-top: 10px; opacity: .8; }

/* One line leads the balance chart; the other two are the floor it is measured
   against. The legend has to say so before the reader looks at the plot. */
.legend span.muted { color: var(--ink-3); }
.legend span.lead { color: var(--ink); font-weight: 600; }
.legend i.dot {
  height: 0; border-radius: 0; background: none !important;
  border-top: 2px dotted currentColor; color: var(--ink-3);
}

/* Long explanations, folded away. Present for whoever wants them, out of the
   way of the chart they describe. */
.note { margin-top: 14px; border-top: 1px solid var(--rule); padding-top: 10px; }
.note > summary {
  display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
  font-family: var(--mono); font-size: 10px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3); user-select: none;
}
.note > summary::-webkit-details-marker { display: none; }
.note > summary:hover { color: var(--ink-2); }
.note > summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: 6px; }
.note-i {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 17px; height: 17px; border-radius: 50%;
  border: 1px solid var(--rule); background: var(--panel-2);
  font-size: 10px; letter-spacing: 0; color: var(--ink-2);
}
.note > summary::after {
  content: ""; width: 5px; height: 5px; margin-left: 2px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .15s ease;
}
.note[open] > summary::after { transform: rotate(-135deg) translate(-1px, -1px); }
@media (prefers-reduced-motion: reduce) { .note > summary::after { transition: none; } }
.note .fineprint { margin-top: 9px; }

/* --- when the data was written ----------------------------------------- */
/* --- alerts: the bubble cap on a plan fund ------------------------------ */
.alerts { margin: 12px 0 0; display: grid; gap: 6px; }
.alerts .alert {
  margin: 0; padding: 9px 12px; border-radius: 10px; font-size: 13px; line-height: 1.45;
  color: var(--ink);
  background: color-mix(in srgb, var(--dn) 9%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dn) 50%, transparent);
}

.refreshed {
  margin: 14px 0 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  font-size: 12.5px; color: var(--ink-2);
}
.refreshed b { font-family: var(--mono); font-weight: 600; color: var(--ink); font-size: 12px; }
.refreshed .ago {
  padding: 2px 8px; border-radius: 999px; background: var(--rail);
  color: var(--ink-2); font-size: 11px; white-space: nowrap;
}

/* --- one bar per exchange ---------------------------------------------- */
.clocks { margin-top: 16px; display: flex; flex-direction: column; gap: 12px; }
.clock { margin-top: 0; display: flex; align-items: center; gap: 11px; }
.clock-body { flex: 1 1 auto; min-width: 0; }
/* Drawn flags, not emoji. A hairline ring in the page's own ink keeps a
   white-heavy flag (Japan, the US stripes) from dissolving into a light
   background, and stops a dark one floating on the dark theme. */
.flag {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 22%, transparent);
}
.flag-i { display: block; width: 100%; height: 100%; border-radius: 50%; }
.clock:not(.live) .flag { opacity: .55; }
.clock:not(.live) .clock-fill { background: var(--ink-3); }

/* Circle-icon variant of the chip, for the market tabs. */
.chip-ico { display: inline-flex; align-items: center; gap: 7px; }
.chip-ico .ico {
  width: 17px; height: 17px; border-radius: 50%; display: grid; place-items: center;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 28%, transparent);
}
.chip-ico .flag-i { display: block; width: 100%; height: 100%; border-radius: 50%; }
/* The globe is a line drawing in the chip's own colour, so it needs the room
   its stroke occupies rather than a ring around it. */
.chip-ico .ico:has(use[href="#flag-world"]) { box-shadow: none; }

/* Beside a ticker: sized to the cap height of the symbol next to it. */
.tk-flag {
  display: inline-block; width: 14px; height: 14px; border-radius: 50%;
  margin-right: 6px; vertical-align: -2px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 22%, transparent);
}
.tk-flag .flag-i { display: block; width: 100%; height: 100%; border-radius: 50%; }

/* The hover readout is exactly two rows, always, whatever the numbers say.
   Reserving a min-height did not work: two wrapped lines are taller than the
   reservation, so the chart below still moved as the cursor crossed dates with
   longer figures. Two explicit rows that never wrap cannot vary — each scrolls
   sideways in its own box on a narrow screen rather than pushing the page. */
.chart-cap .readout {
  display: grid; grid-template-rows: repeat(2, 1.5em); align-content: start;
  min-width: 0; justify-items: end;
}
.chart-cap .readout .ro-line {
  white-space: nowrap; overflow-x: auto; overflow-y: hidden;
  max-width: 100%; scrollbar-width: none; line-height: 1.5em;
}
.chart-cap .readout .ro-line::-webkit-scrollbar { display: none; }

/* Below 900px the inline form does not fit — 464px of figures in a 226px box,
   cropped mid-number — so the caption stacks and the readout becomes a grid:
   what it is, what it is worth, how far ahead. Still a fixed row count, so the
   chart does not move when the cursor does. */
.chart-cap .readout.stacked {
  display: grid; grid-template-columns: auto 1fr auto;
  grid-template-rows: repeat(var(--ro-rows, 3), 1.5em);
  align-content: start; justify-items: start; column-gap: 10px;
  width: 100%; text-align: left;
}
.readout .ro-when { grid-column: 1 / -1; color: var(--ink-3); }
.readout .ro-span { grid-column: 1 / -1; color: var(--ink-3); opacity: .85; }
.readout .ro-k { white-space: nowrap; }
.readout .ro-v { justify-self: start; white-space: nowrap; }
.readout .ro-d { justify-self: end; white-space: nowrap; }

@media (max-width: 899px) {
  .chart-cap { display: grid; grid-template-columns: 1fr; gap: 4px; }
}

/* --- the score's own history, under today's reading --------------------- */
.mini { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--rule); }
.mini-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
/* Eight ranges do not fit beside a label on a phone: the label was folding to
   two lines to make room. Give the chips the full width and put the label
   above them. */
@media (max-width: 560px) {
  .mini-head { flex-direction: column; align-items: flex-start; gap: 8px; }
}
.chips.tight { gap: 4px; }
.chips.tight .chip { padding: 3px 8px; font-size: 10.5px; }

/* The small charts' hover value. It lives in the panel head rather than over
   the plot, which is 96px tall at most — a floating label there would cover the
   line it is describing. Always populated, so nothing shifts when the pointer
   arrives or leaves. */
.mini-ro {
  font-family: var(--mono); font-size: 10.5px; color: var(--ink-2);
  font-variant-numeric: tabular-nums; white-space: nowrap; margin-right: auto;
  padding-left: 10px;
  /* Pinned to the label's line box. At 10.5px against the label's 10px it was
     three quarters of a pixel taller, which is invisible on its own and very
     visible as a chart that nudges when the pointer crosses it. */
  line-height: 15px;
}
.mini-ro b { color: var(--ink); font-weight: 650; }
.mini-ro .den { color: var(--ink-3); }
@media (max-width: 560px) { .mini-ro { padding-left: 0; } }

/* One fund out of a list is a dropdown, not nineteen buttons. */
.pick {
  font: inherit; font-size: 12px; font-family: var(--mono);
  color: var(--ink); background: var(--panel-2);
  border: 1px solid var(--rule); border-radius: 999px;
  padding: 5px 10px; max-width: 100%; cursor: pointer;
}
.pick:hover { border-color: var(--ink-3); }
.pick:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* A tile names its ticker first and says what it is underneath. */
.tile-id { min-width: 0; }
.tile .nm {
  font-size: 11px; line-height: 1.3; color: var(--ink-3);
  margin: 1px 0 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* --- today's card: let it breathe ---------------------------------------- */
.band { display: flex; flex-direction: column; gap: 20px; padding: 20px 18px; }
.band > * { margin: 0; }
.refreshed { color: var(--ink-3); }
.refreshed b { color: var(--ink-2); font-weight: 500; }
.refreshed .ago { background: transparent; border: 1px solid var(--rule); color: var(--ink-3); }
/* The caveat about feed lag is a footnote, not a headline: a superscript mark
   that answers when asked and takes up no room the rest of the time. */
.refreshed { position: relative; }
.hint {
  appearance: none; border: 0; background: none;
  /* Drawn small, tapped large: negative margins buy a finger-sized target
     without moving anything around it. */
  padding: 6px 8px 6px 3px; margin: -6px -8px -6px 0;
  font: inherit; font-size: 10px; line-height: 1; vertical-align: super;
  color: var(--ink-3); cursor: help;
}
.hint:hover, .hint[aria-expanded="true"] { color: var(--ink); }
.hint-pop {
  position: absolute; z-index: 20; top: calc(100% + 6px); left: 0;
  max-width: min(320px, calc(100vw - 32px));
  padding: 9px 11px; border-radius: 9px;
  background: var(--panel-2); border: 1px solid var(--rule);
  box-shadow: 0 10px 26px rgb(0 0 0 / .3);
  font-family: var(--sans); font-size: 12px; line-height: 1.5;
  color: var(--ink-2); text-transform: none; letter-spacing: 0;
}
.hint-pop a { color: var(--focus); white-space: nowrap; }
.clocks { margin-top: 0; }
/* The 20px card gap is right between a label and its number; it is not enough
   between the session bars and a chart, which are separate readings. */
.mini { margin-top: 8px; padding-top: 22px; }

/* What the score is made of. Folded away by default: the headline is the
   number, and this is the working underneath it. Open, it is one row per
   indicator — name and weight, a bar, and what that indicator reads today —
   using the same bar and the same indigo ramp as the holdings tiles, because
   it is the same 0-100 quantity. */
.recipe .ings {
  display: grid; grid-template-columns: 1fr minmax(48px, 88px) auto;
  align-items: center; gap: 7px 10px; margin-top: 11px;
}
@media (max-width: 420px) {
  /* The bar is the least of the three: it repeats what the number says. */
  .recipe .ings { grid-template-columns: 1fr 44px auto; column-gap: 8px; }
}
.recipe .ing { display: contents; }
.recipe .ing-k {
  font-size: 11.5px; color: var(--ink-2); letter-spacing: .01em; line-height: 1.35;
}
.recipe .ing-w {
  font-family: var(--mono); font-size: 10px; color: var(--ink-3);
  font-variant-numeric: tabular-nums; margin-left: 2px;
}
.recipe .ing-bar { height: 5px; border-radius: 999px; background: var(--rail); overflow: hidden; }
.recipe .ing-bar i { display: block; height: 5px; border-radius: 999px; }
.recipe .ing-v {
  font-family: var(--mono); font-size: 12.5px; color: var(--ink); font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap;
}
.recipe .ing-den { font-size: 9.5px; color: var(--ink-3); font-weight: 400; }
.recipe .fineprint { margin-top: 12px; }
.recipe .fineprint a { color: var(--focus); }

/* The fear gauge sits below the score's own history and shares its range.
   Grey on purpose: purple is the score, and nothing else in this card gets to
   look like it. */
#vix-panel { margin-top: 0; padding-top: 14px; }
#vix-panel #vix-state { text-transform: none; letter-spacing: .02em; }
#vix { display: block; width: 100%; height: auto; }

/* --- today's reading, beside its own history ---------------------------- */
/* Shared by the VIX thermometer and the Fear & Greed dial. The instrument
   answers "where are we now" and the line answers "how did we get here", and
   side by side the second is read as context for the first. The instrument is
   frozen on today: hovering the line fills the callout in the panel head and
   moves nothing else, so a glance at the dial is always a glance at today.
   Below 560px they stack, because a 168px instrument beside a chart leaves the
   chart unreadable. */
.gauge-row { display: flex; align-items: center; gap: 14px; }
/* Today's reading: the instrument, the number, and how it moved since the
   previous close. One block, so the three always read together and the chart
   beside it is plainly the history. */
/* Width, not flex-basis: below 560px the row stacks, the main axis turns
   vertical, and a 168px basis became a forced 168px height with a gap under it. */
.today { flex: 0 0 auto; width: 168px; text-align: center; }
.today svg { display: block; margin: 0 auto; }
.today-v {
  font-size: 26px; font-weight: 700; letter-spacing: -.02em; line-height: 1;
  margin-top: 2px; font-variant-numeric: tabular-nums;
}
.today-chg {
  margin-top: 5px; font-family: var(--mono); font-size: 11px; line-height: 1.35;
  font-variant-numeric: tabular-nums;
}
.today-chg span { display: block; }
.today-chg .w { color: var(--ink-3); font-size: 10px; letter-spacing: .01em; }
.gauge-row .chart-host { flex: 1 1 auto; min-width: 0; }
#fg, #vix { display: block; width: 100%; height: auto; }
.panel-note {
  margin: 8px 0 0; font-size: 11px; line-height: 1.45; color: var(--ink-3);
  /* Two lines' worth, always reserved. The text changes on hover, and without
     a floor the panel — and the card around it — resized under the pointer. */
  min-height: 32px;
}
@media (max-width: 560px) {
  .gauge-row { flex-direction: column; align-items: stretch; gap: 6px; }
  .today { align-self: center; }
  .panel-note { min-height: 48px; }
}

/* A formula set apart in the prose, in the face numbers use. */
.prose .formula {
  font-family: var(--mono); font-size: 14px; color: var(--ink);
  padding: 10px 14px; border-radius: 10px; background: var(--panel);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink-3) 35%, transparent);
  overflow-x: auto; white-space: nowrap;
}
