/* ============================================================
   Gradus — design system

   THIS IS THE ONLY STYLESHEET. There are no per-component
   .razor.css files; the build fails if one is added (see
   Client.csproj). A new page composes the classes below —
   if something is genuinely missing, it is added here as a
   primitive so the next page gets it too.

   Dark mode uses light-dark(): every token is declared once
   with both values. The root's color-scheme decides which one
   applies, so "system" follows the OS live with no JS.

   Section order is fixed: tokens, reset, layout, typography,
   primitives, shell, page-specific, motion, media queries.
   ============================================================ */

/* ---------- Tokens ---------- */

:root {
    color-scheme: light dark;

    /* The palette: the only place these colours are written out. */
    --gl-slate-deep: #475266;
    --gl-slate-pale: #c3cede;
    --gl-ochre-deep: #b58a3a;
    --gl-ochre-pale: #dcbc82;
    --gl-navy: #2f3847;
    --gl-graphite: #23262c;

    /* The ground and what lies on it. A flat graphite ground, pale silver by day, no gradients.
       Panels float on it: one flat translucent surface, a crisp hairline, a soft shadow. */
    --gl-base: light-dark(#e6e9ef, #1a1d23);
    --gl-glass: light-dark(rgb(255 255 255 / .76), rgb(255 255 255 / .05));
    --gl-solid: light-dark(#f9fafb, #25282e);
    --gl-bar: light-dark(rgb(230 233 239 / .74), rgb(26 29 35 / .74));

    /* The panel the page lies on, beside the sidebar, and the cards that lie on that panel. */
    --gl-shell: light-dark(#fff, var(--gl-base));
    --gl-panel: light-dark(#f4f5f8, #202329);
    --gl-panel-bar: light-dark(rgb(244 245 248 / .8), rgb(32 35 41 / .8));
    --gl-on-panel: light-dark(#fff, rgb(255 255 255 / .03));
    --gl-card: var(--gl-glass);
    --gl-card-shadow: inset 0 0 0 1px var(--gl-edge), var(--gl-shadow);
    --gl-raised: light-dark(rgb(250 251 253 / .88), rgb(40 43 51 / .86));
    --gl-well: light-dark(rgb(255 255 255 / .55), rgb(0 0 0 / .12));
    --gl-active: light-dark(rgb(255 255 255 / .82), rgb(255 255 255 / .13));
    --gl-hover: light-dark(rgb(255 255 255 / .5), rgb(255 255 255 / .06));
    --gl-press: light-dark(#fff, rgb(255 255 255 / .2));
    --gl-edge: light-dark(rgb(20 26 38 / .08), rgb(255 255 255 / .09));
    --gl-highlight: inset 0 1px 0 light-dark(rgb(255 255 255 / .9), rgb(255 255 255 / .16));
    --gl-shadow:
        0 1px 2px light-dark(rgb(30 36 48 / .04), rgb(0 0 0 / .12)),
        0 14px 32px -24px light-dark(rgb(30 36 48 / .22), rgb(0 0 0 / .35));
    --gl-frost: blur(44px) saturate(180%);

    /* The two accents of the whole site, slate and ochre, and the brand they come from. */
    --gl-slate: light-dark(var(--gl-slate-deep), var(--gl-slate-pale));
    --gl-on-slate: light-dark(#fff, var(--gl-graphite));
    --gl-ochre: light-dark(var(--gl-ochre-deep), var(--gl-ochre-pale));
    --gl-brand: light-dark(var(--gl-slate-deep), #3a4558);
    --gl-on-brand: #fff;
    --gl-on-brand-soft: rgb(255 255 255 / .75);
    --gl-sheen: rgb(255 255 255 / .25);
    --gl-backdrop: light-dark(rgb(30 36 48 / .24), rgb(18 20 25 / .42));
    --gl-google: #1a73e8;
    --gl-facebook: #1877f2;

    /* The page's lines. The gap runs between panels and round the screen. */
    --gl-gap: 16px;
    --gl-control: 38px;
    --gl-head-h: 104px;
    --gl-title-line: 32px;
    --gl-tools-w: 174px;
    --gl-inset: 24px;
    --gl-fade: 180ms ease;
    --gl-morph: 460ms cubic-bezier(.25, 1, .5, 1);

    /* A spring that settles with about 3% overshoot, sampled, for what travels across the screen. */
    --gl-spring: linear(0, .0334, .116, .2261, .3475, .4689, .5827, .6843, .7715, .8436, .9013, .9458, .9786, 1.0018, 1.0171, 1.0261, 1.0305, 1.0315, 1.0302, 1.0275, 1.0239, 1.0201, 1.0163, 1.0127, 1.0096, 1.0069, 1);

    /* The type sizes of the site. The one large figure on a screen is the only size outside them. */
    --gl-size-caption: 12px;
    --gl-size-note: 13px;
    --gl-size-body: 14px;
    --gl-size-heading: 16px;
    --gl-size-title: 20px;
    --gl-size-figure: 24px;

    --bg: var(--gl-base);
    --surface: var(--gl-glass);
    --surface-2: light-dark(rgb(20 26 38 / .055), rgb(255 255 255 / .07));
    --border: light-dark(rgb(20 26 38 / .09), rgb(255 255 255 / .09));
    --border-strong: light-dark(rgb(20 26 38 / .16), rgb(255 255 255 / .16));

    --text: light-dark(#2b313c, #f5f6f8);
    --text-2: light-dark(rgb(43 49 60 / .84), rgb(238 240 244 / .7));
    --text-3: light-dark(rgb(43 49 60 / .7), rgb(238 240 244 / .6));

    --accent: light-dark(#94702a, var(--gl-ochre-pale));
    --accent-soft: light-dark(rgb(181 138 58 / .15), rgb(220 188 130 / .15));

    --ink: light-dark(var(--gl-slate-deep), var(--gl-ochre-pale));
    --ink-fg: var(--gl-on-slate);

    --pos: light-dark(#17664c, #8bd8b3);
    --neg: light-dark(#b8433f, #f2a79f);
    --warn: light-dark(#7c5414, #e4c48a);
    --on-danger: #ffffff;

    /* One colour per asset type. A donut slice, a row badge and a bar segment all read the same
       token, so colour indexes meaning instead of decorating. Stays clear of the green and red of
       gains and losses; neighbours keep ΔE 15 and more, 7 under colour-blindness, in both themes. */
    --at-etf: var(--gl-slate);
    --at-realestate: light-dark(#3a7eb4, #2f7fb4);
    --at-stock: var(--gl-ochre);
    --at-cash: light-dark(#37a277, #55a780);
    --at-crypto: light-dark(#7f346e, #cf82c8);
    --at-bond: light-dark(#aa7db8, #8562ae);
    --at-commodity: light-dark(#858a3f, #b7bb7c);
    --at-vehicle: light-dark(#6b7686, #8e98a6);
    --at-other: light-dark(#a8734c, #c89d7d);

    /* Neutral series for anything without an asset type, such as portfolios. */
    --cat-1: var(--at-etf);
    --cat-2: var(--at-realestate);
    --cat-3: var(--at-stock);
    --cat-4: var(--at-cash);
    --cat-5: var(--at-crypto);
    --cat-6: var(--at-bond);

    --shadow-card: var(--gl-shadow);
    --shadow-pop: 0 30px 70px -24px light-dark(rgb(30 36 48 / .35), rgb(0 0 0 / .5));
    --scrim: rgb(0 0 0 / 0.55);
    --on-scrim: #ffffff;

    --font: "Geist", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    --font-brand: "Outfit", "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

    --s1: 4px;
    --s2: 8px;
    --s3: 12px;
    --s4: 16px;
    --s5: 24px;
    --s6: 32px;
    --s7: 48px;
    --s8: 64px;

    --radius-sm: 8px;
    --radius: 12px;
    --radius-lg: 24px;
    --radius-full: 999px;

    --sidebar-w: 248px;
    --topbar-h: 64px;
    --page-w: 1520px;
}

:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* ---------- Reset ---------- */

*, *::before, *::after {
    box-sizing: border-box;
}

html, body {
    margin: 0;
    padding: 0;
}

body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.5;
    letter-spacing: -0.005em;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, p, figure {
    margin: 0;
}

button {
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
}

a {
    color: inherit;
    text-decoration: none;
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 6px;
}

/* The ground lies under the whole page; a sheet on top may be translucent. */
html { background: var(--gl-base); }

body {
    background: transparent;
    font-feature-settings: "ss01", "cv11";
}

::selection { background: var(--accent-soft); }

/* ---------- Layout ---------- */

.page {
    width: 100%;
    max-width: var(--page-w);
    margin: 0 auto;
}

.stack {
    display: flex;
    flex-direction: column;
    gap: var(--s4);
}

.stack--tight { gap: var(--s2); }
.stack--loose { gap: var(--s5); }

.row {
    display: flex;
    align-items: center;
    gap: var(--s3);
}

.row--between { justify-content: space-between; }
.row--wrap { flex-wrap: wrap; }
.row--top { align-items: flex-start; }

.spacer { flex: 1; }

.grid {
    display: grid;
    gap: var(--s5);
}

.grid--stats { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.grid--half { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid--thirds { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* ---------- Typography ---------- */

.title {
    font-size: 26px;
    font-weight: 600;
    letter-spacing: -0.025em;
    line-height: 1.2;
}

.subtitle {
    font-size: 15px;
    color: var(--text-2);
}

.section-title {
    font-size: 16px;
    font-weight: 600;
    letter-spacing: -0.015em;
}

.label {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-2);
}

.muted { color: var(--text-2); }
.faint { color: var(--text-3); }
.center { text-align: center; }

/* House rule: every figure is mono and tabular so columns align. */
.num {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
}

.num--lg {
    font-size: 24px;
    font-weight: 600;
    letter-spacing: -0.03em;
}

.num--hero {
    font-size: 34px;
    font-weight: 600;
    line-height: 1.1;
}

.pos { color: var(--pos); }
.neg { color: var(--neg); }

.title {
    font-size: var(--gl-size-figure);
    font-weight: 600;
    letter-spacing: -.022em;
    line-height: 1.25;
}

.subtitle { font-size: var(--gl-size-body); }

.section-title {
    font-size: var(--gl-size-heading);
    font-weight: 600;
    letter-spacing: -.012em;
}

/* Figures are the sans in tabular numerals, not a mono. */
.num {
    font-family: var(--font);
    font-feature-settings: "tnum";
    letter-spacing: -.015em;
}

.num--lg { font-weight: 600; letter-spacing: -.03em; }
.num--hero { font-weight: 600; letter-spacing: -.04em; }

/* ---------- Card ---------- */

.card {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    min-width: 0;
    padding: var(--s5);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow-card);
}

.card--flush {
    padding: 0;
    gap: 0;
}

.card__head {
    display: flex;
    align-items: center;
    gap: var(--s3);
}

/* Tinted tile — the colour is what makes a figure read at a glance. */
.stat {
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    padding: 18px;
    border: 1px solid transparent;
    border-radius: var(--radius);
}

.stat__label {
    font-size: 13px;
    font-weight: 500;
    opacity: 0.8;
}

.stat__meta {
    font-size: 12px;
    opacity: 0.7;
}

/* One flat surface for every card: a hairline, no gradients. On the page's panel it is a shade off
   the panel's white, with no shadow. */
.card,
.stat {
    position: relative;
    border: 0;
    border-radius: var(--radius-lg);
    background: var(--gl-card);
    box-shadow: var(--gl-card-shadow);
}

.card {
    gap: var(--s4);
    padding: var(--s5);
}

.stat {
    gap: var(--s2);
    min-width: 0;
    padding: var(--s5);
    color: var(--text);
}

/* A table wider than its panel scrolls inside it, so the page keeps its right edge. */
.card--flush { gap: 0; padding: 0; overflow-x: auto; }
.card:has(> .table) { overflow-x: auto; }
.card--flush > .card__head { padding: var(--s5) var(--s5) var(--s2); }

/* What is in a card may shrink to the card: a long name stops the line, not the page. */
.card > * { min-width: 0; }

/* A card's own menu belongs at its right edge, not against its name. */
.card__head > [data-stop],
.card__head > .menu { margin-left: auto; }

.card__foot { padding: 0 var(--s5) var(--s5); }

.stat__label {
    font-size: var(--gl-size-note);
    color: var(--text-2);
    opacity: 1;
}

.stat__meta {
    font-size: var(--gl-size-note);
    color: var(--text-3);
    opacity: 1;
}

.stat .num--lg {
    margin-top: var(--s2);
    font-size: var(--gl-size-figure);
    font-weight: 600;
    letter-spacing: -.035em;
    white-space: nowrap;
}

.stat .num--lg small {
    font-size: .6em;
    font-weight: 500;
    letter-spacing: -.01em;
    color: var(--text-2);
}

.stat .bar { margin-top: var(--s2); }

/* The one stat tile: label with an optional icon chip, figure, meta, and an optional foot. */
.stat__top {
    display: flex;
    align-items: center;
    gap: var(--s2);
}

.stat__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    background: var(--accent-soft);
    color: var(--accent);
}

.stat__icon .icon { width: 14px; height: 14px; }

.stat__foot { margin-top: auto; padding-top: var(--s2); }

/* ---------- Button ---------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    height: 38px;
    padding: 0 var(--s4);
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text);
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: background 120ms ease, border-color 120ms ease, opacity 120ms ease;
}

.btn:hover:not(:disabled) { background: var(--surface-2); }

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn--primary {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--ink-fg);
}

/* The background must be restated: .btn:hover:not(:disabled) is more specific than
   .btn--primary, so without this the primary button turns pale and loses its label. */
.btn--primary:hover:not(:disabled) {
    background: var(--ink);
    opacity: 0.88;
}

.btn--ghost {
    background: transparent;
    border-color: transparent;
    color: var(--text-2);
}

.btn--ghost:hover:not(:disabled) {
    background: var(--surface-2);
    color: var(--text);
}

.btn--danger {
    border-color: var(--neg);
    color: var(--neg);
}

.btn--danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--neg) 10%, transparent);
}

.btn--sm {
    height: 32px;
    padding: 0 var(--s3);
    font-size: 13px;
}

.btn--icon {
    width: 38px;
    padding: 0;
}

.btn--icon.btn--sm { width: 32px; }

.btn--lg {
    height: 46px;
    padding: 0 22px;
    border-radius: 12px;
    font-size: 15px;
}

/* A button looks like one: a filled pill with a visible edge and full-strength words. */
.btn {
    border-color: var(--border-strong);
    border-radius: var(--radius-full);
    background: var(--gl-active);
    box-shadow: var(--gl-highlight);
    color: var(--text);
    font-weight: 500;
    transition: background var(--gl-fade), border-color var(--gl-fade), box-shadow var(--gl-fade), transform var(--gl-fade), opacity var(--gl-fade);
}

.btn:hover:not(:disabled) { background: var(--gl-press); }
.btn:active:not(:disabled) { transform: scale(.975); }

.btn--primary,
.btn--primary:hover:not(:disabled) {
    border-color: transparent;
    background: var(--ink);
    color: var(--ink-fg);
    box-shadow: none;
}

.btn--primary:hover:not(:disabled) { opacity: .9; }

.btn--ghost,
.btn--ghost:hover:not(:disabled) {
    border-color: transparent;
    background: transparent;
    box-shadow: none;
    color: var(--text-2);
}

.btn--ghost:hover:not(:disabled) {
    background: var(--gl-active);
    color: var(--text);
}

.btn--danger,
.btn--danger:hover:not(:disabled) {
    border-color: var(--neg);
    background: var(--neg);
    color: var(--on-danger);
}

/* ---------- Form ---------- */

.field {
    display: flex;
    flex-direction: column;
    gap: 7px;
    min-width: 0;
}

.input,
.select,
.textarea {
    width: 100%;
    height: 40px;
    padding: 0 var(--s3);
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
}

.textarea {
    height: auto;
    min-height: 88px;
    padding: var(--s2) var(--s3);
    resize: vertical;
}

.input:focus-visible,
.select:focus-visible,
.textarea:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
    border-color: var(--accent);
}

.input::placeholder { color: var(--text-3); }

.field__error {
    font-size: 13px;
    color: var(--neg);
}

.input,
.select,
.textarea {
    border-color: var(--border);
    border-radius: var(--radius);
    background: var(--gl-well);
    transition: border-color var(--gl-fade), box-shadow var(--gl-fade);
}

.input:hover,
.select:hover,
.textarea:hover { border-color: var(--border-strong); }

.input:focus-visible,
.select:focus-visible,
.textarea:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* ---------- Table ---------- */

.table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

.table th {
    padding: var(--s3) var(--s5);
    border-bottom: 1px solid var(--border);
    font-size: 12px;
    font-weight: 500;
    color: var(--text-3);
    text-align: left;
    white-space: nowrap;
}

.table td {
    height: 56px;
    padding: 0 var(--s5);
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}

.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr:hover { background: var(--surface-2); }

/* Every table in the app marks its numeric columns with .r. */
.table .r,
.table th.r { text-align: right; }

.asset {
    display: flex;
    align-items: center;
    gap: var(--s3);
    min-width: 0;
}

.asset__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: none;
    border-radius: 10px;
    background: color-mix(in srgb, var(--at, var(--text-3)) 14%, transparent);
    color: var(--at, var(--text-3));
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: -0.04em;
}

.asset__badge--me {
    background: var(--accent-soft);
    color: var(--accent);
    overflow: hidden;
}

.asset__badge img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.asset__name {
    display: block;
    font-weight: 500;
}

/* The name and its meta line are one text block: no gap between them, so the pair
   sits centred against the badge instead of drifting above it. */
.asset__text {
    min-width: 0;
}

.asset__meta {
    color: var(--text-3);
    font-size: 12px;
}

/* A table's head is a line of small words over a hairline, not a band. */
.table th {
    padding-top: var(--s4);
    padding-bottom: var(--s3);
    color: var(--text-3);
    font-size: var(--gl-size-caption);
    font-weight: 500;
    letter-spacing: .01em;
}

.card--flush > .card__head ~ * .table th { padding-top: var(--s2); }

.table tbody tr { transition: background var(--gl-fade); }
.table tbody tr:hover { background: var(--gl-hover); }

.asset__badge {
    border-radius: var(--radius);
    background: var(--surface-2);
    box-shadow: var(--gl-highlight);
    color: var(--text-2);
    font-family: var(--font);
    letter-spacing: -.01em;
}

.asset__badge--me {
    border-radius: 50%;
    background: var(--surface-2);
    box-shadow: var(--gl-highlight);
    color: var(--text);
    font-family: var(--font);
}

/* ---------- Choice chips ---------- */

.opts {
    display: flex;
    gap: var(--s2);
}

.opts--wrap { flex-wrap: wrap; }

.opt {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--s3);
    min-width: 0;
    padding: 11px 14px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface);
    cursor: pointer;
    transition: border-color 120ms ease, background 120ms ease;
}

.opt:hover { border-color: var(--text-3); }

.opt--chip {
    gap: var(--s2);
    padding: var(--s2) 13px;
}

.opt--on {
    border-color: var(--accent);
    background: var(--accent-soft);
}

/* The radio itself is off-screen: the chip is the control, and its border carries the state. */
.opt input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.opt:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.opt__t {
    font-size: 14px;
    font-weight: 500;
    color: var(--text);
}

.opt {
    border-color: var(--border);
    background: var(--gl-well);
}

.opt:hover { border-color: var(--border-strong); }

/* Choosers speak one language: the chosen one is filled slate. A chip is a pill; cards and rows
   keep their shape and take the slate edge on the active tint. */
.opt--on,
.choice:has(input:checked) {
    border-color: var(--gl-slate);
    background: var(--gl-active);
}

.choice input { accent-color: var(--gl-slate); }

.opt--chip { border-radius: var(--radius-full); }

.opt--chip.opt--on {
    background: var(--gl-slate);
    color: var(--gl-on-slate);
}

/* The label has its own text colour, so it has to be told to follow the chip. */
.opt--chip.opt--on .opt__t { color: inherit; }

.page .opt:not(.opt--tall) { padding: var(--s3) var(--s4); }

.page .opt--tall {
    gap: var(--s2);
    padding: var(--s3) var(--s2);
}

/* ---------- Pill ---------- */

.pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s1);
    height: 24px;
    padding: 0 10px;
    line-height: 1;
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    background: var(--surface-2);
    font-size: 12px;
    font-weight: 500;
    color: var(--text-2);
    white-space: nowrap;
}

.pill--pos {
    color: var(--pos);
    border-color: color-mix(in srgb, var(--pos) 32%, transparent);
    background: color-mix(in srgb, var(--pos) 10%, transparent);
}

.filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
}

.filters .pill {
    cursor: pointer;
}

.pill--on {
    border-color: color-mix(in srgb, var(--at, var(--border-strong)) 60%, transparent);
    background: color-mix(in srgb, var(--at, var(--surface)) 12%, transparent);
    color: var(--at, var(--text));
}

.pill .dot {
    background: var(--at);
}

.table__row--link {
    cursor: pointer;
}

.pill--sm {
    height: 18px;
    padding: 0 7px;
    margin-left: var(--s2);
    font-size: 11px;
}

/* A pill that carries a transaction type reads its colour from the --at the type set. */
.pill--type {
    color: var(--at);
    border-color: color-mix(in srgb, var(--at) 34%, transparent);
    background: color-mix(in srgb, var(--at) 10%, transparent);
}

.bar {
    height: 8px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--surface-2);
}

.bar__fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: var(--at);
}

.strong { font-weight: 600; }

.tiny {
    font-size: 12px;
    color: var(--text-3);
}

.pill--warn {
    color: var(--warn);
    border-color: color-mix(in srgb, var(--warn) 32%, transparent);
    background: color-mix(in srgb, var(--warn) 10%, transparent);
}

.dot {
    width: 9px;
    height: 9px;
    flex: none;
    border-radius: 3px;
    background: var(--at);
}

.pill { border-color: transparent; }

/* A label reads in the text colour; the dot beside it carries the type. */
.pill--type,
.opt__badge {
    background: var(--surface-2);
    color: var(--text);
}

/* A status pill is one size on a page: the small one the dashboard shows. */
.page .pill:not(.filters .pill) {
    height: 18px;
    padding: 0 var(--s2);
    font-size: var(--gl-size-caption);
}

.filters .pill {
    height: var(--gl-control);
    padding: 0 var(--s4);
    background: var(--surface-2);
    color: var(--text-2);
    font-size: var(--gl-size-note);
    transition: background var(--gl-fade), color var(--gl-fade);
}

.filters .pill:hover { color: var(--text); }

.filters .pill--on {
    background: var(--gl-slate);
    color: var(--gl-on-slate);
}

/* The one gauge: a single value on a track. */
.bar {
    height: 6px;
    border-radius: var(--radius-full);
    background: var(--surface-2);
}

.bar__fill {
    border-radius: var(--radius-full);
    background: var(--gl-slate);
}

.dot { border-radius: 50%; }

/* ---------- Icon ---------- */

.icon {
    width: 18px;
    height: 18px;
    flex: none;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* ---------- App shell ---------- */

.shell {
    display: flex;
    height: 100vh;
    overflow: hidden;
    background: var(--bg);
}

/* The app shell owns scrolling: the sidebar and .scroll scroll, the document never does. Public pages keep theirs. */
body:has(.shell:not(.shell--plain)) { overflow: hidden; }

.sidebar {
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: var(--sidebar-w);
    flex: none;
    padding: 20px var(--s4);
    border-right: 1px solid var(--border);
    background: var(--surface);
    overflow-y: auto;
}

/* The mark is a stroked staircase that inherits the text colour; only its last step is coloured. */
.brand-mark {
    display: block;
    width: 28px;
    height: 28px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.6;
    stroke-linecap: square;
}

.brand-mark__step { stroke: var(--accent); }

/* Lockup: mark 28px, gap 10px, wordmark in Outfit 400 tracked out. The wordmark stays lowercase. */
.brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 10px;
    color: var(--ink);
}

.brand__word {
    font-family: var(--font-brand);
    font-size: 20px;
    font-weight: 400;
    letter-spacing: 0.055em;
}

/* Only the sidebar stacks the brand above the nav; a horizontal bar centres it. */
.brand--stacked { padding-bottom: var(--s5); }

/* The landing footer repeats the lockup small and quiet, in the footer's own grey. */
.brand--footer {
    padding: 0;
    color: inherit;
}

.brand--footer .brand-mark {
    width: 20px;
    height: 20px;
}

.brand--footer .brand__word { font-size: 15px; }

.nav-group {
    padding: 20px 10px 6px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-3);
}

.nav-item {
    position: relative;
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    height: 40px;
    padding: 0 10px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--text-2);
    font-size: 14px;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease;
}

.nav-item:hover {
    background: var(--surface-2);
    color: var(--text);
}

.nav-item.is-active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 600;
}

.nav-item.is-active::before {
    content: '';
    position: absolute;
    left: -16px;
    top: 10px;
    bottom: 10px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--accent);
}

.nav-item__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 20px;
    margin-left: auto;
    padding: 0 7px;
    line-height: 1;
    border-radius: var(--radius-full);
    background: var(--surface-2);
    color: var(--text-3);
    font-size: 12px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.sidebar__foot {
    margin-top: auto;
    padding-top: var(--s4);
    border-top: 1px solid var(--border);
}

.sidebar__me {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    padding: var(--s2) 10px;
    border-radius: var(--radius-sm);
    color: inherit;
}

.sidebar__me:hover { background: var(--surface-2); }
.sidebar__me.is-active { background: var(--accent-soft); }

.sidebar__me-text {
    min-width: 0;
    line-height: 1.3;
}

.sidebar__me-name {
    font-size: 14px;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar__me-meta {
    font-size: 12px;
    color: var(--text-3);
}

.main {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}

.topbar {
    display: flex;
    align-items: center;
    gap: var(--s3);
    height: var(--topbar-h);
    flex: none;
    padding: 0 28px;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
}

.scroll {
    flex: 1;
    overflow-y: auto;
    padding: 28px;
}

/* Two parts on the ground: the sidebar lies straight on it, and everything else lies on one panel
   beside it. */
.shell:not(.shell--plain) {
    gap: var(--gl-gap);
    padding: var(--gl-gap);
    background: var(--gl-shell);
}

/* In the light theme the sidebar lies on white, where the white hover would not show. */
.sidebar {
    --gl-hover: var(--surface-2);

    padding: var(--s3);
    border: 0;
    background: none;
    box-shadow: none;
}

.shell:not(.shell--plain) > .main {
    --gl-bar: var(--gl-panel-bar);
    --gl-card: var(--gl-on-panel);
    --gl-card-shadow: inset 0 0 0 1px var(--gl-edge);

    overflow: hidden;
    border: 1px solid var(--gl-edge);
    border-radius: var(--radius-lg);
    background: var(--gl-panel);
    box-shadow: var(--gl-shadow);
}

.brand { gap: var(--s3); padding: 2px var(--s2); }
.brand--stacked { padding-bottom: var(--s3); }

.brand-mark {
    width: 34px;
    height: 34px;
    padding: var(--s2);
    border-radius: var(--radius);
    background: var(--gl-ochre-pale);
    color: var(--gl-navy);
    stroke-width: 3;
    box-shadow: inset 0 1px 0 var(--gl-sheen);
}

.brand-mark__step { stroke: var(--gl-navy); }

.brand__word {
    font-size: var(--gl-size-title);
    letter-spacing: .02em;
    color: var(--text);
}

.nav-group {
    padding: var(--s4) var(--s3) var(--s2);
    font-size: var(--gl-size-caption);
    color: var(--text-3);
}

.nav-item {
    gap: var(--s3);
    padding: 0 var(--s3);
    border-radius: var(--radius);
    transition: background var(--gl-fade), color var(--gl-fade), box-shadow var(--gl-fade);
}

.nav-item:hover {
    background: var(--gl-hover);
    color: var(--text);
}

.nav-item.is-active,
.nav-item.is-active:hover {
    background: var(--gl-brand);
    color: var(--gl-on-brand);
    font-weight: 600;
}

.nav-item.is-active .icon { color: var(--gl-ochre-pale); }
.nav-item.is-active .nav-item__count { color: var(--gl-on-brand-soft); }

.nav-item.is-active::before { content: none; }

.nav-item__count {
    min-width: 0;
    padding: 0;
    background: none;
    font-weight: 500;
}

.sidebar__foot {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    padding-top: var(--s3);
}

.sidebar__me {
    gap: var(--s3);
    padding: var(--s2) var(--s3);
    border-radius: var(--radius);
}

.sidebar__me:hover { background: var(--gl-hover); }
.sidebar__me.is-active { background: var(--gl-active); }

.scroll {
    overflow-x: hidden;
    scrollbar-gutter: stable;
}

.scroll::-webkit-scrollbar { width: 6px; }

.scroll::-webkit-scrollbar-thumb {
    border-radius: var(--radius-sm);
    background: transparent;
}

.scroll:hover::-webkit-scrollbar-thumb { background: var(--border-strong); }

@supports not selector(::-webkit-scrollbar) {
    .scroll {
        scrollbar-color: var(--border-strong) transparent;
        scrollbar-width: thin;
    }
}

.page {
    margin: 0;
    font-size: var(--gl-size-body);
}

.page.stack--loose,
.page .grid { gap: var(--gl-gap); }

/* The column is capped and centred; the page and its bar both live inside it. */
.gl-stage {
    max-width: var(--page-w);
    margin-inline: auto;
}

/* The page's inset on the panel. The scrollbar's 6px gutter makes up the difference on the right.
   The page bar's background reaches into that inset, so the panel never scrolls sideways. */
.scroll { padding: 0 var(--s5) var(--s6) var(--s6); }

/* The tools belong to the shell, so a redraw of the page leaves them alone. Their rail takes no
   height: they lie on the bar, at the right end of the column, and stay with it when it sticks. */
.gl-rail {
    position: sticky;
    z-index: 30;
    top: 0;
    display: flex;
    justify-content: flex-end;
    height: 0;
}

.gl-headtools {
    flex: none;
    display: flex;
    align-items: center;
    gap: 2px;
    width: var(--gl-tools-w);
    height: var(--gl-control);
    margin-top: calc(var(--s6) + (var(--gl-title-line) - var(--gl-control)) / 2);
}

.gl-headtools .btn--icon {
    width: var(--gl-control);
    height: var(--gl-control);
    color: var(--text);
}

.gl-headtools .bell { position: relative; }

/* The bar every screen opens on: one line straight on the ground, not a panel, on the same edges
   as the panels below. The ground colour is translucent, so at the top it cannot be told from the
   page, and what scrolls under it shows through, blurred, behind a straight line. */
.gl-headzone {
    position: sticky;
    z-index: 20;
    top: 0;
    margin-bottom: calc(var(--s2) * -1);
    padding-bottom: var(--s2);
}

/* On a layer of its own: frost on the bar itself would flatten the frost of the menus it opens.
   The line under it appears once the page has moved under it, driven by the scroll itself. */
.gl-headzone::before {
    content: "";
    position: absolute;
    z-index: -1;
    inset: 0 calc(var(--s5) * -1) 0 calc(var(--s6) * -1);
    background: var(--gl-bar);
    backdrop-filter: blur(16px) saturate(140%);
    animation: gl-scrolled linear both;
    animation-timeline: scroll(nearest block);
    animation-range: 0 4px;
}

@keyframes gl-scrolled {
    from { box-shadow: none; }
    to { box-shadow: 0 1px 0 var(--border); }
}

.gl-headnote { margin-top: calc(var(--s2) * -1); }

/* The name on one line, what the page is for on the next, as large and as far from the panel's top
   edge as from its side. A mark and the actions line up with the name, not with the block. */
.gl-pagehead {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    padding: var(--s6) calc(var(--gl-tools-w) + var(--s4)) var(--s4) 0;
}

.gl-pagehead__mark {
    flex: none;
    display: flex;
    align-items: center;
    min-height: var(--gl-title-line);
}

.gl-pagehead__mark .gl-logo {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
}

.gl-pagehead__mark .idcard__avatar {
    width: 36px;
    height: 36px;
    font-size: var(--gl-size-note);
}

.gl-pagehead__text {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--s1);
    min-width: 0;
}

.gl-pagehead__name {
    display: flex;
    align-items: baseline;
    gap: var(--s3);
    min-width: 0;
}

.gl-pagehead .title,
.gl-pagehead .crumb {
    font-size: var(--gl-size-figure);
    line-height: var(--gl-title-line);
    letter-spacing: -.02em;
}

.gl-pagehead .title {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.gl-pagehead .subtitle {
    max-width: 72ch;
    margin: 0;
    color: var(--text-2);
    font-size: var(--gl-size-body);
    line-height: 20px;
}

/* The trail reads as the start of the name: the parent, a slash, then the title itself. */
.gl-pagehead .crumb {
    flex: none;
    gap: var(--s3);
    color: var(--text-3);
}

.gl-pagehead .crumb a {
    margin: 0;
    padding: 0;
    color: var(--text-3);
    font-weight: 500;
}

.gl-pagehead .crumb a:hover {
    background: none;
    color: var(--text);
}

.gl-pagehead .crumb [aria-current] { display: none; }

.gl-pagehead__actions {
    flex: none;
    margin-top: calc((var(--gl-title-line) - var(--gl-control)) / 2);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--s2);
}

.gl-skel-title,
.gl-skel-lead { display: block; border-radius: var(--radius-sm); }
.gl-skel-title { width: 180px; height: 24px; }
.gl-skel-lead { width: 260px; max-width: 100%; height: 14px; }

/* The mark of what a screen is about. */
.gl-logo {
    position: relative;
    color: var(--gl-slate);
}

.gl-logo .icon { width: 17px; height: 17px; }

/* What sits under the bar is measured from it, not from the top of the screen. */
.set__nav { top: calc(var(--gl-head-h) + var(--gl-gap)); }
.set__sec { scroll-margin-top: calc(var(--gl-head-h) + var(--gl-gap)); }

/* ---------- Auth pages ---------- */

.auth {
    flex: 1;
    display: grid;
    place-items: center;
    padding: var(--s7) var(--s5);
}

.auth__card {
    width: 100%;
    max-width: 380px;
}

/* The sign-in page is its own surface: one column, nothing to read past, and
   only the language and theme controls kept in the corner. */

.authshell {
    position: relative;
    display: grid;
    place-items: center;
    min-height: 100vh;
    padding: 40px var(--s5);
}

.authshell__tools {
    position: absolute;
    top: var(--s4);
    right: var(--s4);
    display: flex;
    align-items: center;
    gap: var(--s2);
}

.auth__col {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 352px;
}

/* The mark carries the identity on its own, so the page needs no wordmark. */
.auth__mark {
    display: grid;
    place-items: center;
    align-self: center;
    width: 46px;
    height: 46px;
    border-radius: 13px;
    background: var(--ink);
    color: var(--ink-fg);
}

.auth__mark .brand-mark__step { stroke: currentColor; }

.auth__title {
    margin: 22px 0 0;
    font-size: 21px;
    font-weight: 600;
    letter-spacing: -0.02em;
    text-align: center;
}

.auth__sub {
    margin: 7px 0 0;
    color: var(--text-2);
    text-align: center;
}

.auth__sso {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    margin-top: 28px;
}

.auth__provider {
    display: flex;
    align-items: center;
    gap: 11px;
    height: 46px;
    padding: 0 14px;
    border: 1px solid var(--border-strong);
    border-radius: 11px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-weight: 500;
    text-align: left;
}

.auth__provider:disabled {
    color: var(--text-3);
    cursor: not-allowed;
    opacity: 0.7;
}

.auth__provider em {
    margin-left: auto;
    color: var(--text-3);
    font-size: 12px;
    font-style: normal;
    font-weight: 400;
}

.auth__glyph {
    display: inline-flex;
    flex: none;
    justify-content: center;
    width: 20px;
    font-size: 16px;
    font-weight: 700;
}

.auth__or {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin: 20px 0 var(--s4);
    color: var(--text-3);
    font-size: 13px;
}

.auth__or::before,
.auth__or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--border);
}

.auth__form {
    display: flex;
    flex-direction: column;
    gap: 11px;
}

.auth__form .input {
    height: 46px;
    border-radius: 11px;
}

.auth__pw {
    position: relative;
    display: flex;
}

.auth__pw .input {
    padding-right: 68px;
}

.auth__eye {
    position: absolute;
    top: 5px;
    right: 5px;
    bottom: 5px;
    display: inline-flex;
    align-items: center;
    padding: 0 10px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-2);
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

.auth__eye:hover {
    background: var(--surface-2);
    color: var(--text);
}

.auth__submit {
    height: 46px;
    border-radius: 11px;
}

.auth__alt {
    margin: 26px 0 0;
    color: var(--text-2);
    font-size: 13px;
    text-align: center;
}

.auth__link {
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--accent);
    font: inherit;
    font-weight: 500;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

.auth__link:disabled {
    color: var(--text-3);
    cursor: not-allowed;
    text-decoration-style: dashed;
}

.auth__legal {
    margin: 30px 0 0;
    color: var(--text-2);
    font-size: 12px;
    line-height: 1.55;
    text-align: center;
}

/* The app shell is a row (sidebar beside content); every other surface stacks. */
.shell--plain {
    flex-direction: column;
    height: auto;
    min-height: 100vh;
    overflow: visible;
}

.shell__main {
    flex: 1;
    display: flex;
    flex-direction: column;
}

.site-footer {
    display: flex;
    align-items: center;
    gap: var(--s3);
    width: 100%;
    max-width: var(--page-w);
    margin: 0 auto;
    padding: var(--s5);
    border-top: 1px solid var(--border);
    font-size: 13px;
}

.auth__glyph--google { color: var(--gl-google); }
.auth__glyph--facebook { color: var(--gl-facebook); }

/* ---------- Empty / loading ---------- */

.empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s3);
    padding: 56px var(--s5);
    text-align: center;
    color: var(--text-2);
}

.empty__body { max-width: 46ch; }

.onboard__hide { margin-left: auto; }

.skeleton {
    background: var(--surface-2);
    border-radius: var(--radius-sm);
    animation: pulse 1.4s ease-in-out infinite;
}

.skeleton--row { height: 20px; }
.skeleton--hero { height: 48px; }

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.45; }
}

.spinner {
    width: 16px;
    height: 16px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--text);
    border-radius: 50%;
    animation: spin 700ms linear infinite;
}

@keyframes spin {
    to { transform: rotate(360deg); }
}

/* ---------- Allocation ---------- */

.alloc {
    display: flex;
    height: 8px;
    border-radius: var(--radius-full);
    background: var(--surface-2);
    overflow: hidden;
}

.at--stock { --at: var(--at-stock); }
.at--etf { --at: var(--at-etf); }
.at--bond { --at: var(--at-bond); }
.at--crypto { --at: var(--at-crypto); }
.at--commodity { --at: var(--at-commodity); }
.at--cash { --at: var(--at-cash); }
.at--realestate { --at: var(--at-realestate); }
.at--vehicle { --at: var(--at-vehicle); }
.at--other { --at: var(--at-other); }
.at--warn { --at: var(--warn); }
.at--pos { --at: var(--pos); }
.at--neg { --at: var(--neg); }
.at--c1 { --at: var(--cat-1); }
.at--c2 { --at: var(--cat-2); }
.at--c3 { --at: var(--cat-3); }
.at--c4 { --at: var(--cat-4); }
.at--c5 { --at: var(--cat-5); }
.at--c6 { --at: var(--cat-6); }

.alloc__seg { background: var(--at); }

.donut {
    display: block;
    width: 100%;
    max-width: 190px;
    height: auto;
    margin: 0 auto;
}

/* Two rings side by side: title and note, the donut centred, the legend under it. */
.ring {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    min-width: 0;
}

.ring__donut {
    display: flex;
    justify-content: center;
    padding: 10px 0 6px;
}

.ring .donut { max-width: 210px; }

.alloc-legend__amount {
    width: 64px;
    flex: none;
    text-align: right;
}

.donut__arc { transition: stroke-opacity 120ms ease; }

.donut__label {
    fill: var(--text-3);
    font-size: 12px;
}

.donut__value {
    fill: var(--text);
    font-size: 18px;
    font-weight: 600;
    letter-spacing: -0.04em;
}

.alloc-legend {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 14px;
}

.alloc-legend__item {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* The row of the slice under the cursor steps forward; the rest stays as it is. */
.alloc-legend__item.is-hovered .alloc-legend__label { font-weight: 600; }

.alloc-legend__dot {
    width: 9px;
    height: 9px;
    flex: none;
    border-radius: 3px;
    background: var(--at);
}

.alloc-legend__label {
    color: var(--text);
    margin-right: auto;
    white-space: nowrap;
}

.mix-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.mix-legend__item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-3);
}

/* The one inline legend: wrapping, 12px, words in --text-2 and the figures in --text. */
.mix-legend {
    gap: var(--s2) var(--s4);
    color: var(--text-2);
    font-size: var(--gl-size-caption);
}

.mix-legend__item {
    gap: var(--s2);
    color: inherit;
    font-size: inherit;
}

.mix-legend .num { color: var(--text); }

/* The one split bar: 6px high, 2px between parts, 2px corners. */
.alloc {
    height: 6px;
    gap: 2px;
    border-radius: 2px;
    background: none;
}

.alloc__seg { border-radius: 2px; }

/* Donut: one thin, crisp ring. */
.donut { shape-rendering: geometricPrecision; }

.donut__label { fill: var(--text-2); font-size: var(--gl-size-caption); }

.donut__value {
    font-family: var(--font);
    font-size: var(--gl-size-title);
    font-weight: 600;
    letter-spacing: -.03em;
}

.ring__donut { padding: var(--s3) 0 var(--s2); }

.alloc-legend {
    gap: 0;
    max-height: 260px;
    margin: 0 calc(var(--s2) * -1);
    overflow-y: auto;
    font-size: var(--gl-size-note);
    scrollbar-color: var(--border-strong) transparent;
    scrollbar-width: thin;
}

.alloc-legend__item {
    gap: var(--s3);
    min-height: 32px;
    padding: 0 var(--s2);
    border-radius: var(--radius-sm);
    transition: background var(--gl-fade);
}

.alloc-legend__item:hover,
.alloc-legend__item.is-hovered { background: var(--gl-hover); }

.alloc-legend__dot { border-radius: 50%; }
.alloc-legend__amount { width: 48px; }

.donut__track { stroke: var(--surface-2); }

/* Parts of the same type step down in strength, in the ring and beside it alike. */
.alloc-legend__dot { opacity: var(--gl-shade, 1); }

/* Beside a ring the logo also has to say which colour is whose, so there it carries a dot. The
   logo is tinted by its type, so the dot reads the colour of the slice from the line it sits on. */
.alloc-legend__item { --gl-slice: var(--at); }
.alloc-legend__item .gl-logo { flex: none; }

.alloc-legend__item .gl-logo--typed::after {
    content: "";
    position: absolute;
    right: -3px;
    bottom: -3px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--gl-slice) calc(var(--gl-shade, 1) * 100%), var(--gl-solid));
    box-shadow: 0 0 0 2px var(--gl-solid);
}

.gl-logo--sm {
    width: 26px;
    height: 26px;
    border-radius: var(--radius-sm);
}

.gl-logo--sm .icon { width: 14px; height: 14px; }

.alloc-legend__item .gl-logo--sm.gl-logo--typed::after {
    right: -2px;
    bottom: -2px;
    width: 8px;
    height: 8px;
    box-shadow: 0 0 0 1.5px var(--gl-solid);
}

/* ---------- Dialog (native <dialog>) ---------- */

.dialog {
    width: min(560px, calc(100vw - 32px));
    max-height: min(86vh, 760px);
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 24px 64px -16px rgb(16 24 40 / .3);
    font-family: inherit;
}

/* A closed <dialog> is display:none by the browser's own rules. Setting a display
   on .dialog itself would override that and leave every modal on screen. */
.dialog[open] {
    display: flex;
    flex-direction: column;
}

.dialog:focus {
    outline: none;
}

.dialog::backdrop {
    background: rgb(0 0 0 / 0.45);
    backdrop-filter: blur(2px);
}

.dialog form {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.dialog__head {
    display: flex;
    align-items: flex-start;
    flex: none;
    gap: 14px;
    padding: 22px var(--s5) var(--s4);
}

.dialog__title {
    font-size: 16px;
    font-weight: 600;
    letter-spacing: -0.015em;
}

.dialog__body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    gap: 18px;
    min-height: 0;
    padding: var(--s1) var(--s5) 22px;
    overflow-y: auto;
}

.dialog__foot {
    display: flex;
    align-items: center;
    flex: none;
    gap: 10px;
    flex-wrap: wrap;
    padding: var(--s4) var(--s5);
    border-top: 1px solid var(--border);
    background: var(--surface-2);
}

/* What lies over something else is real frosted glass: dialogs, menus, panels, tooltips. */
.dialog,
.menu__list,
.panel,
.chart__tip,
.picker__list {
    border-color: var(--border);
    background: var(--gl-raised);
    backdrop-filter: var(--gl-frost);
    box-shadow: var(--gl-highlight), var(--shadow-pop);
}

.dialog::backdrop {
    background: var(--gl-backdrop);
    backdrop-filter: blur(8px);
}

.dialog__foot { background: transparent; }

/* A form of a few fields opens below the button that asked for it and leaves the screen undimmed.
   On a phone there is no room beside anything, and without anchor positioning there is nothing to
   hang from, so there it stays a normal dialog. */
@supports (position-area: block-end) {
    @media (min-width: 641px) {
        .gl-pop {
            width: 380px;
            max-width: none;
            inset: auto;
            margin: var(--s2) 0;
            position-area: block-end span-inline-end;
            position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
            transform-origin: top left;
        }

        .gl-pop::backdrop {
            background: transparent;
            backdrop-filter: none;
        }

        /* No room for the remark beside the buttons; the save button lighting up says the same. */
        .gl-pop .dialog__spacer {
            height: 0;
            overflow: hidden;
        }
    }
}

/* ---------- Toast ---------- */

.toasts {
    position: fixed;
    left: 50%;
    bottom: 28px;
    transform: translateX(-50%);
    align-items: center;
    z-index: 100;
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    max-width: min(460px, calc(100vw - 32px));
}

.alert {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 13px 15px;
    border: 1px solid color-mix(in srgb, var(--neg) 34%, transparent);
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--neg) 8%, transparent);
    font-size: 14px;
    text-wrap: pretty;
}

.alert__mark {
    display: flex;
    flex: none;
    margin-top: 1px;
    color: var(--neg);
}

.alert__body {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.alert__title {
    font-weight: 500;
}

.alert__detail {
    color: var(--text-2);
    font-size: 13px;
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    max-width: min(460px, calc(100vw - 32px));
    padding: 14px 18px 14px 15px;
    border-radius: var(--radius);
    background: var(--ink);
    color: var(--ink-fg);
    box-shadow: var(--shadow-pop);
    font-size: 14px;
}

.toast__mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 20px;
    height: 20px;
    margin-top: 1px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--ink-fg) 16%, transparent);
}

.toast__mark .icon {
    width: 13px;
    height: 13px;
}

.toast__body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.toast__title {
    font-weight: 500;
}

.toast__detail {
    font-size: 13px;
    opacity: .68;
    text-wrap: pretty;
}

.toast__acts {
    display: flex;
    gap: 6px;
    margin-top: 9px;
}

.toast__btn {
    padding: 5px 11px;
    border: 0;
    border-radius: 8px;
    background: color-mix(in srgb, var(--ink-fg) 16%, transparent);
    color: inherit;
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

.toast__btn:hover {
    background: color-mix(in srgb, var(--ink-fg) 26%, transparent);
}

.toast--error {
    background: var(--neg);
    color: var(--ink-fg);
}

.toast--error .toast__mark {
    background: color-mix(in srgb, var(--ink-fg) 18%, transparent);
}

.toast--error .toast__detail {
    opacity: .82;
}

/* ---------- Onboarding ---------- */

.onboard__steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--s5);
}

.onboard__step {
    display: flex;
    flex-direction: column;
    gap: var(--s1);
}

.onboard__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    margin-bottom: var(--s1);
    border-radius: var(--radius-full);
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 12px;
    font-weight: 600;
}

.onboard__title {
    font-size: 15px;
    font-weight: 600;
}

/* ---------- Boot screen ---------- */

.boot {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--s4);
    background: var(--bg);
}

/* The mark draws itself: the plan first (32 units of path), then the accent step lands (14 units),
   a short hold, a fade, and again. The wordmark settles in once, after the first climb. */
.boot__mark {
    width: 48px;
    height: 48px;
    color: var(--text);
}

.boot__plan {
    stroke-dasharray: 32;
    animation: boot-plan 2.4s ease-in-out infinite;
}

.boot__step {
    stroke-dasharray: 14;
    animation: boot-step 2.4s ease-in-out infinite;
}

@keyframes boot-plan {
    0% { stroke-dashoffset: 32; opacity: 1; }
    40% { stroke-dashoffset: 0; }
    85% { stroke-dashoffset: 0; opacity: 1; }
    100% { stroke-dashoffset: 0; opacity: 0; }
}

@keyframes boot-step {
    0%, 40% { stroke-dashoffset: 14; opacity: 1; }
    56% { stroke-dashoffset: 0; }
    85% { stroke-dashoffset: 0; opacity: 1; }
    100% { stroke-dashoffset: 0; opacity: 0; }
}

.boot__word {
    font-family: var(--font-brand);
    font-size: 22px;
    font-weight: 400;
    letter-spacing: 0.055em;
    color: var(--text);
    animation: boot-word 600ms ease-out 700ms both;
}

@keyframes boot-word {
    from { opacity: 0; transform: translateY(4px); }
    to { opacity: 1; transform: none; }
}

/* Blazor forces display:block inline when it shows this, so lay it out as a block. */
#blazor-error-ui {
    display: none;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 200;
    padding: var(--s3) var(--s5);
    background: var(--neg);
    color: var(--on-danger);
    font-size: 14px;
    text-align: center;
    box-shadow: var(--shadow-pop);
}

#blazor-error-ui .reload {
    margin-left: var(--s3);
    text-decoration: underline;
    font-weight: 600;
}

/* ---------- Landing page (server-rendered) ---------- */

.lp {
    --lp-w: 1180px;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.lp-nav {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: 28px;
    height: 68px;
    padding: 0 var(--s6);
    border-bottom: 1px solid var(--border);
    background: color-mix(in srgb, var(--bg) 86%, transparent);
    backdrop-filter: blur(10px);
}

.lp-nav__links {
    display: flex;
    gap: var(--s1);
}

.lp-nav__links a {
    padding: var(--s2) var(--s3);
    border-radius: 8px;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-2);
}

.lp-nav__links a:hover {
    background: var(--surface-2);
    color: var(--text);
}

.lp-wrap {
    width: 100%;
    max-width: var(--lp-w);
    margin: 0 auto;
    padding: 0 var(--s6);
}

.lp-hero {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    gap: 56px;
    align-items: center;
    padding-block: 88px 72px;
}

.lp-hero__copy {
    display: flex;
    flex-direction: column;
    gap: 26px;
}

.lp-display {
    font-size: clamp(38px, 5vw, 60px);
    font-weight: 600;
    letter-spacing: -0.04em;
    line-height: 1.04;
}

.lp-display em {
    font-style: normal;
    color: var(--accent);
}

.lp-display--sm { font-size: clamp(28px, 3.2vw, 38px); }
.lp-display--md { font-size: clamp(30px, 3.6vw, 44px); }

.lp-sub {
    font-size: 18px;
    line-height: 1.6;
    color: var(--text-2);
    max-width: 46ch;
}

.lp-sub--sm { font-size: 16px; }

.lp-cta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3);
}

.lp-preview {
    display: flex;
    flex-direction: column;
    gap: var(--s5);
    padding: var(--s5);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-pop);
}

.lp-preview__head {
    display: flex;
    align-items: flex-start;
    gap: var(--s4);
}

.lp-preview__total {
    font-size: 38px;
    font-weight: 600;
    letter-spacing: -0.04em;
    line-height: 1.1;
}

.lp-preview__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 170px;
    gap: var(--s5);
    align-items: center;
    padding-top: var(--s5);
    border-top: 1px solid var(--border);
}

.lp-strip {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    padding-block: 28px 72px;
    border-bottom: 1px solid var(--border);
}

.lp-strip__lead {
    width: 100%;
    margin-bottom: var(--s1);
    font-size: 12px;
    color: var(--text-3);
}

/* A tinted pill or icon reads its colour from the --at set by an .at--* class. */
.pill--at {
    border-color: color-mix(in srgb, var(--at) 34%, transparent);
    background: color-mix(in srgb, var(--at) 9%, transparent);
    color: var(--at);
}

.lp-section {
    padding-block: 72px;
    border-bottom: 1px solid var(--border);
}

.lp-section__head {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    max-width: 60ch;
    margin-bottom: 44px;
}

.lp-eyebrow {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-3);
}

.lp-features {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
    gap: 36px 40px;
}

.lp-feature {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.lp-feature__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin-bottom: 6px;
    border-radius: 11px;
    background: color-mix(in srgb, var(--at) 12%, transparent);
    color: var(--at);
}

.lp-feature__title {
    font-size: 17px;
    font-weight: 600;
    letter-spacing: -0.015em;
}

.lp-steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--s6);
}

.lp-step {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    padding-top: 20px;
    border-top: 2px solid var(--accent);
}

.lp-step__n {
    font-size: 13px;
    font-weight: 600;
    color: var(--accent);
}

.lp-privacy {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 40px;
    align-items: start;
}

.lp-final {
    padding-block: 88px;
    text-align: center;
}

.lp-final .lp-cta { justify-content: center; }

.lp-final .lp-sub {
    max-width: 46ch;
    margin: var(--s4) auto 28px;
}

/* The landing page is rendered before the browser reports its palette, so which icon
   belongs on the theme button is a question only CSS can answer. Sun when dark is in
   effect, moon otherwise, whether that came from the cookie or from the OS. */
.icon--sun { display: none; }

:root[data-theme="dark"] .icon--sun { display: block; }
:root[data-theme="dark"] .icon--moon { display: none; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .icon--sun { display: block; }
    :root:not([data-theme="light"]) .icon--moon { display: none; }
}

.lp-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s4);
    width: 100%;
    max-width: var(--lp-w);
    margin: 0 auto;
    padding: 28px var(--s6);
    border-top: 1px solid var(--border);
    font-size: 13px;
    color: var(--text-3);
}

/* ---------- Detail page ---------- */

.crumb {
    display: flex;
    align-items: center;
    gap: var(--s2);
    color: var(--text-3);
    font-size: 13px;
}

.crumb a {
    padding: var(--s1) 6px;
    margin-left: -6px;
    border-radius: 6px;
    color: var(--text-2);
}

.crumb a:hover {
    background: var(--surface-2);
    color: var(--text);
}

.crumb [aria-current] {
    color: var(--text);
    font-weight: 500;
}

.scrollx {
    overflow-x: auto;
}

.seg {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-2);
}

.seg button {
    height: 28px;
    padding: 0 13px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--text-2);
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

.seg button[aria-pressed="true"] {
    background: var(--surface);
    color: var(--text);
    font-weight: 600;
    box-shadow: var(--shadow-card);
}

.legend {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    margin: 0;
    padding: 0;
    list-style: none;
}

.legend__row {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 14px;
}

.legend__row .spacer { text-align: right; }

.flow {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    min-width: 0;
}

.flow__col > .tiny { white-space: nowrap; }

.flow__col {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    gap: 7px;
    min-width: 0;
}

.flow__bar {
    width: 100%;
    border-radius: 6px;
}

/* Money in and out: one baseline across the whole card, bars up for money in and down for money out,
   the figure hugging its bar, months in a row under the axis. Columns share one grid so both rows align. */
.money {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    min-width: 0;
}

.money__plot,
.money__months {
    display: grid;
    grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
    gap: 6px;
}

.money__plot {
    position: relative;
}

.money__plot::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 50%;
    border-top: 1px solid var(--border-strong);
}

.money__col {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 0;
}

.money__half {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s1);
    width: 100%;
    height: 82px;
}

.money__half .tiny { white-space: nowrap; }

.money__half--in { justify-content: flex-end; }

.money__bar {
    width: min(60%, 28px);
    flex: none;
    border-radius: 4px;
    background: var(--at);
}

.money__half--in .money__bar { border-radius: 4px 4px 0 0; }

.money__half--out .money__bar { border-radius: 0 0 4px 4px; }

.money__months { text-align: center; }

.money__months .tiny { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.crumb a {
    padding: var(--s1) var(--s2);
    border-radius: var(--radius-sm);
}

.crumb a:hover { background: var(--gl-hover); }

.seg {
    border: 0;
    background: var(--surface-2);
}

.page .seg { padding: var(--s1); }

/* One height, padding and type for every segmented control; the buttons keep their natural width. */
.seg button {
    height: 30px;
    padding: 0 var(--s3);
    color: var(--text-2);
    font-size: var(--gl-size-note);
    font-weight: 500;
    white-space: nowrap;
    transition: background var(--gl-fade), color var(--gl-fade);
}

.seg button:hover { color: var(--text); }

.seg button[aria-pressed="true"] {
    background: var(--gl-slate);
    box-shadow: none;
    color: var(--gl-on-slate);
}

/* Money in and out: slim columns either side of a hairline, the side says which is which. */
.money__plot::after { border-top-color: var(--border); }

.money__plot,
.money__months { gap: var(--s2); }

.money__half { gap: 0; }
.money__bar { min-height: 4px; }
.money__half .money__bar { border-radius: var(--radius-full); }

/* Money in is what was paid in, so it wears that colour; the side already says in or out. */
.money__half .at--pos { --at: var(--gl-ochre); }
.money__half .at--neg { --at: var(--gl-slate); }

/* A column in a chart, the same in every column chart: a slim pill, a tint over its whole slot on
   hover, and on keyboard focus a ring round the bar. */
.col {
    --gl-col-w: min(50%, 9px);
    position: relative;
    border-radius: var(--radius-sm);
    outline: none;
    transition: background var(--gl-fade);
}

.col:hover { background: var(--gl-hover); }

.col:focus-visible .money__bar {
    border-radius: var(--radius-full);
    box-shadow: 0 0 0 2px var(--accent);
}

.money__bar {
    --series: var(--at);
    position: relative;
    width: var(--gl-col-w);
}

/* A name under a column is shown whole, centred on the column even where it is wider. */
.money__months .tiny {
    display: flex;
    justify-content: center;
    overflow: visible;
}

/* ---------- Multi-step dialog ---------- */

.dialog--wide {
    width: min(680px, calc(100vw - 32px));
}

.steps {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin: 0;
    padding: 0 var(--s5) var(--s4);
    list-style: none;
}

.step {
    display: flex;
    align-items: center;
    gap: var(--s2);
    color: var(--text-3);
    font-size: 13px;
}

.step__num {
    display: grid;
    place-items: center;
    flex: none;
    width: 21px;
    height: 21px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text-3);
    font-size: 11px;
    font-weight: 600;
}

.step--now {
    color: var(--text);
    font-weight: 500;
}

.step--now .step__num {
    border-color: var(--ink);
    background: var(--ink);
    color: var(--ink-fg);
}

.step--done .step__num {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
}

.step__bar {
    flex: 1;
    min-width: 12px;
    height: 1px;
    background: var(--border);
}

@media (max-width: 620px) {
    .step:not(.step--now) span:last-child {
        display: none;
    }
}

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

/* ---------- Instrument search ---------- */

.search {
    position: relative;
    display: flex;
    align-items: center;
}

.search .input {
    padding-left: 38px;
}

.search__icon {
    position: absolute;
    left: 13px;
    display: flex;
    color: var(--text-3);
    pointer-events: none;
}

.results {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.result {
    display: flex;
    align-items: center;
    gap: 13px;
    width: 100%;
    padding: 11px 13px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 120ms ease, background 120ms ease;
}

.result:hover:not(:disabled) {
    border-color: var(--border-strong);
    background: var(--surface-2);
}

.result:disabled {
    cursor: default;
    background: var(--surface-2);
}

.result__mark {
    display: grid;
    place-items: center;
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    font-size: 11px;
    font-weight: 600;
}

.result__body {
    flex: 1;
    min-width: 0;
}

.result__sym {
    display: flex;
    align-items: center;
    gap: var(--s2);
    flex-wrap: wrap;
    font-size: 14px;
    font-weight: 600;
}

.result__meta {
    display: block;
    overflow: hidden;
    color: var(--text-3);
    font-size: 12px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* One control height on a page: the search box, the filters and the buttons line up. */
.page .search .input {
    height: var(--gl-control);
    border-radius: var(--radius-full);
}

/* ---------- Read-only rows and field pairs ---------- */

.kv {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--s2) 18px;
    margin: 0;
    font-size: 14px;
}

.kv dt {
    color: var(--text-2);
}

.kv dd {
    margin: 0;
    text-align: right;
}

.fldrow {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
}

@media (max-width: 560px) {
    .fldrow {
        grid-template-columns: 1fr;
    }
}

.suffix {
    position: relative;
    display: flex;
    align-items: center;
}

.suffix .input {
    padding-right: 58px;
}

.suffix__unit {
    position: absolute;
    right: 12px;
    color: var(--text-3);
    font-size: 13px;
    pointer-events: none;
}

/* Where the unit is a choice it sits in the same place the fixed one would, so the field reads the
   same either way: a number, then what it is counted in. */
.suffix__pick {
    position: absolute;
    right: 8px;
    padding: 2px var(--s1);
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface-2);
    color: var(--text-2);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

/* Next to a figure there is no field to sit in, so the same picker takes its place in the line. */
.suffix__pick--inline {
    position: static;
    flex: none;
}

.hairline {
    height: 1px;
    background: var(--border);
}

.back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--text-2);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.skiplink {
    padding: 0;
    border: 0;
    background: none;
    color: var(--text-2);
    font: inherit;
    font-size: 13px;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

.back:hover,
.skiplink:hover {
    color: var(--text);
}

/* ---------- Overflow menu ---------- */

.menu {
    position: relative;
    display: inline-flex;
}

/* The list is in the top layer, so it is placed against its own button rather than against a
   parent that may be clipping it. That needs CSS anchor positioning; the fallbacks flip it above
   the button when there is no room below. */
.menu__list {
    position: fixed;
    inset: auto;
    top: anchor(bottom);
    right: anchor(right);
    position-try-fallbacks: flip-block;
    margin: 6px 0 0;
    min-width: 208px;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    color: var(--text);
}

.menu__list:popover-open {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.menu__item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 10px;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--text);
    font: inherit;
    font-size: 14px;
    text-align: left;
    cursor: pointer;
}

.menu__item:hover {
    background: var(--surface-2);
}

.menu__item--danger {
    color: var(--neg);
}

.menu__item--danger:hover {
    background: color-mix(in srgb, var(--neg) 9%, transparent);
}

.menu__sep {
    height: 1px;
    margin: 5px 2px;
    background: var(--border);
}

.menu__item { border-radius: var(--radius-sm); }
.menu__item:hover { background: var(--gl-hover); }

/* ---------- Choice cards ---------- */

.choices {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.choice {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    padding: 13px 14px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface);
    cursor: pointer;
    transition: border-color 120ms ease, background 120ms ease;
}

.choice:hover {
    border-color: var(--text-3);
}

.choice input {
    flex: none;
    width: 16px;
    height: 16px;
    margin: 2px 0 0;
}

.choice__title {
    font-size: 14px;
    font-weight: 500;
}

.choice__body {
    display: block;
    margin-top: 2px;
    color: var(--text-2);
    font-size: 13px;
}

/* ---------- Delete inventory ---------- */

.note {
    display: flex;
    flex-direction: column;
    gap: 9px;
    padding: 14px var(--s4);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-subtle, var(--surface-2));
}

.note__row {
    display: flex;
    align-items: baseline;
    gap: var(--s3);
    font-size: 14px;
}

.note__row span:first-child {
    color: var(--text-2);
}

.note__row span:last-child {
    margin-left: auto;
    color: var(--text);
}

/* ---------- Field counter ---------- */

.field--plain {
    margin: 0;
    padding: 0;
    border: 0;
}

.field__foot {
    display: flex;
    gap: var(--s3);
    align-items: baseline;
}

.label__opt {
    margin-left: var(--s2);
    color: var(--text-3);
    font-weight: 400;
}

.count {
    margin-left: auto;
    color: var(--text-3);
    font-size: 12px;
}

.count--over {
    color: var(--neg);
}

.input--danger:focus-visible {
    border-color: var(--neg);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--neg) 18%, transparent);
}

/* ---------- Dialog head ---------- */

.dialog__sub {
    margin: 0;
    color: var(--text-2);
    font-size: 14px;
}

.dialog__x {
    margin-left: auto;
    margin-top: -4px;
}

.dialog__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 38px;
    height: 38px;
    border-radius: 11px;
}

.dialog__mark--danger {
    background: color-mix(in srgb, var(--neg) 12%, transparent);
    color: var(--neg);
}

.dialog__spacer {
    margin-left: auto;
}

.btn--danger {
    border-color: var(--neg);
    background: var(--neg);
}

/* The remark gives way, so the buttons stay together on one line. */
.dialog__spacer {
    flex: 1 1 0;
    min-width: 0;
}

/* ---------- Profile ---------- */

.idcard__avatar {
    position: relative;
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 68px;
    height: 68px;
    padding: 0;
    overflow: hidden;
    border: 0;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font: inherit;
    font-size: 24px;
    font-weight: 600;
    cursor: pointer;
}

.idcard__avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.idcard__cam {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--scrim);
    color: var(--on-scrim);
    opacity: 0;
    transition: opacity 120ms ease;
}

.idcard__avatar:hover .idcard__cam,
.idcard__avatar:focus-visible .idcard__cam { opacity: 1; }

/* A preference row: what it is on the left, the control on the right. The rules between rows
   do the separating, so the card needs no inner boxes. */
.prow {
    display: grid;
    grid-template-columns: minmax(180px, 250px) minmax(0, 1fr);
    gap: var(--s3) 28px;
    align-items: start;
    margin: 0;
    padding: 22px 0 0;
    border: 0;
    border-top: 1px solid var(--border);
}

.prow__legend {
    padding: 0;
    margin-bottom: var(--s1);
}

.prow__note {
    max-width: 34ch;
    text-wrap: pretty;
}

.prow:first-of-type { padding-top: var(--s1); border-top: 0; }
.prow:last-of-type { padding-bottom: var(--s1); }

@media (max-width: 780px) {
    .prow { grid-template-columns: minmax(0, 1fr); }
}

.opts--tiles {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    max-width: 340px;
}

.opts--single {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--s2);
    max-width: 340px;
}

.opt--tall {
    flex-direction: column;
    gap: 9px;
    padding: 10px var(--s2);
}

.opt__badge {
    --at: var(--accent);
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
}

.opt__text { min-width: 0; }

.opt__b {
    display: block;
    margin-top: 1px;
    font-size: 12px;
    color: var(--text-2);
}

/* The swatch is painted with literal colours: it has to show the theme it selects, not the
   theme currently applied. */
.sw {
    display: flex;
    width: 100%;
    max-width: 72px;
    height: 48px;
    overflow: hidden;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
}

.sw__half { width: 50%; overflow: hidden; }
.sw__half .sw__pane { width: 200%; }
.sw__half--r .sw__pane { margin-left: -100%; }

/* Each pane shows its own mode whatever the app's theme: color-scheme makes the tokens inside resolve to it. */
.sw__pane {
    display: flex;
    width: 100%;
    height: 100%;
    background: var(--bg);
}

.sw__pane[data-mode="light"] { color-scheme: light; }
.sw__pane[data-mode="dark"] { color-scheme: dark; }

.sw__side {
    width: 21px;
    flex: none;
    border-right: 1px solid var(--border);
    background: var(--surface);
}

.sw__body {
    display: flex;
    flex: 1;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    padding: 0 var(--s2);
}

.sw__bar {
    display: block;
    height: 4px;
    border-radius: 2px;
    background: var(--border-strong);
}

.sw__pane .sw__bar--accent { width: 58%; background: var(--accent); }
.sw__bar--wide { width: 78%; }
.sw__bar--narrow { width: 44%; }

.drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s2);
    padding: 26px 20px;
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface-2);
    color: var(--text-3);
    text-align: center;
    cursor: pointer;
}

.drop:hover,
.drop:focus-within { border-color: var(--accent); }

.idcard__avatar {
    background: var(--gl-brand);
    color: var(--gl-on-brand);
}

.prow {
    gap: var(--s3) var(--s6);
    padding-top: var(--s5);
}

/* The appearance tiles wrap to a new line before a name runs past its tile. */
.opts--tiles { grid-template-columns: repeat(auto-fill, minmax(min(104px, 100%), 1fr)); }

.sw__body { gap: var(--s1); }

/* ---------- Typeable picker ---------- */

.picker {
    position: relative;
    min-width: 0;
}

.picker__list {
    position: fixed;
    top: anchor(bottom);
    right: anchor(right);
    left: anchor(left);
    z-index: 30;
    margin-top: 6px;
    position-try-fallbacks: flip-block;
    display: flex;
    max-height: 264px;
    flex-direction: column;
    gap: 2px;
    overflow-y: auto;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    list-style: none;
}

.picker__item {
    display: flex;
    width: 100%;
    align-items: center;
    gap: var(--s3);
    padding: 7px var(--s2);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.picker__item:hover { background: var(--surface-2); }
.picker__item--on { background: var(--accent-soft); }

.picker__item:hover { background: var(--gl-hover); }

/* ---------- Value chart ---------- */

.chart-row {
    display: flex;
    gap: var(--s3);
    min-width: 0;
}

/* The plot is the frame the marker dot is placed against: the chart is 240px tall whatever the
   card's width, so a top in chart units is already a top in pixels. */
.chart-plot {
    position: relative;
    flex: 1;
    min-width: 0;
}

.chart {
    display: block;
    width: 100%;
    height: 240px;
    overflow: visible;
}

.chart__hit {
    fill: transparent;
}

.chart__cursor {
    stroke: var(--text-3);
    stroke-width: 1;
    stroke-dasharray: 3 4;
}

/* Money in is a solid green dot, money out a hollow red ring. Two signals rather than one, so
   the difference survives a small screen and a reader who does not see the colour. */
.chart__mark {
    --mark: var(--pos);

    position: absolute;
    box-sizing: border-box;
    width: 10px;
    height: 10px;
    margin: -5px 0 0 -5px;
    border: 2px solid var(--mark);
    border-radius: 50%;
    background: var(--mark);
    box-shadow: 0 0 0 2px var(--surface);
    pointer-events: none;
}

.chart__mark--out {
    --mark: var(--neg);

    background: var(--surface);
}

/* Sits on the ring and opens upwards, folding inwards at either edge of the plot so it never
   hangs off the card. */
.chart__tip {
    position: absolute;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--s2) 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-size: 13px;
    white-space: nowrap;
    pointer-events: none;
}

.chart__tip--middle { transform: translate(-50%, calc(-100% - 12px)); }
.chart__tip--start { transform: translate(0, calc(-100% - 12px)); }
.chart__tip--end { transform: translate(-100%, calc(-100% - 12px)); }

.chart__tip-kind { font-weight: 600; }

.chart__tip-row {
    display: flex;
    gap: var(--s3);
    justify-content: space-between;
}

.chart__dot {
    position: absolute;
    width: 10px;
    height: 10px;
    margin: -5px 0 0 -5px;
    border: 2px solid var(--surface);
    border-radius: 50%;
    background: var(--at);
    pointer-events: none;
}

.chart__grid {
    stroke: var(--border);
    stroke-width: 1;
    stroke-dasharray: 2 6;
}

/* The labels sit beside the plot, never inside it: a viewBox stretched to the card would
   stretch the text with it. */
.chart__scale {
    position: relative;
    flex: none;
    width: 56px;
    height: 240px;
}

.chart__scale span {
    position: absolute;
    right: 0;
    transform: translateY(-50%);
}

.chart__line {
    fill: none;
    stroke: var(--at);
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.chart__line--deposits {
    stroke: var(--text-3);
    stroke-width: 1.5;
    stroke-dasharray: 6 4;
}

.chart__legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s4);
    align-items: center;
    margin-top: var(--s2);
    font-size: 12px;
    color: var(--text-3);
}

.chart__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.chart__swatch {
    width: 14px;
    height: 0;
    border-top: 2.5px solid var(--at);
}

.chart__swatch--deposits {
    border-top: 1.5px dashed var(--text-3);
}

/* Each month sits at its own share of the plot, so the marks line up with the line above them
   instead of being spread evenly. The right padding keeps them clear of the value scale. */
.chart__axis {
    position: relative;
    height: 18px;
    margin-top: var(--s1);
    padding-right: 68px;
}

.chart__axis span {
    position: absolute;
    transform: translateX(-50%);
    white-space: nowrap;
}

.chart__axis span:first-child { transform: none; }

.skeleton--chart { height: 240px; }

/* notifications: the bell, its panel, and one row per notification */
.bell { position: relative; }

.bell__dot {
    position: absolute;
    top: 1px;
    right: 1px;
    min-width: 15px;
    height: 15px;
    padding: 0 3px;
    border-radius: var(--radius-full);
    background: var(--ink);
    color: var(--bg);
    font-size: 10px;
    line-height: 15px;
    text-align: center;
}

.panel {
    display: none;
    position: fixed;
    inset: auto;
    top: anchor(bottom);
    right: anchor(right);
    position-try-fallbacks: flip-block;
    margin: 6px 0 0;
    width: 380px;
    max-width: calc(100vw - var(--s4));
    padding: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.panel:popover-open {
    display: flex;
    flex-direction: column;
}

.panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--border);
}

.panel__scroll {
    max-height: 60vh;
    overflow-y: auto;
}

.panel__pad { padding: var(--s4); }

.panel__foot {
    padding: var(--s2) var(--s4);
    border-top: 1px solid var(--border);
}

.notif {
    display: flex;
    gap: var(--s3);
    width: 100%;
    padding: var(--s3) var(--s4);
    border: 0;
    border-bottom: 1px solid var(--border);
    background: none;
    text-align: left;
    cursor: pointer;
    transition: background 120ms ease;
}

.notif:hover { background: var(--surface-2); }

.notif--read { opacity: 0.62; }

.notif__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 28px;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-full);
    background: var(--surface-2);
    color: var(--text-2);
}

.notif__mark--quiet { color: var(--text-3); }

.notif__body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.notif__top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s2);
}

.notif__title { font-weight: 600; }

.notif__at {
    color: var(--text-3);
    white-space: nowrap;
}

.notif__text { color: var(--text-2); }

.notif__go {
    color: var(--ink);
    font-weight: 500;
}

.notif__unread {
    flex: 0 0 7px;
    width: 7px;
    height: 7px;
    margin-top: 10px;
    border-radius: var(--radius-full);
    background: var(--pos);
}

/* Settings page: a section rail beside one card per section, one setting per row. */
.setplain__facts {
    display: flex;
    gap: 0;
    margin: 0;
    flex-wrap: wrap;
}

.setplain__facts > div {
    padding: 2px 20px;
    border-left: 1px solid var(--border);
}

.setplain__facts dt {
    margin: 0 0 3px;
    font-size: 12px;
    color: var(--text-2);
}

.setplain__facts dd {
    margin: 0;
    font-size: 14px;
    font-weight: 500;
    color: var(--text);
}

.set {
    display: grid;
    grid-template-columns: 212px minmax(0, 1fr);
    gap: var(--s5);
    align-items: start;
}

.set__nav {
    position: sticky;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.set__link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px var(--s3);
    border-radius: 9px;
    font-size: 14px;
    font-weight: 500;
    color: var(--text-2);
    text-decoration: none;
    cursor: pointer;
}

.set__link:hover {
    background: var(--surface-2);
    color: var(--text);
}

/* Without JavaScript the rail follows the anchor you clicked: :target says which
   section that is, and the first link carries the state while none is chosen. */
.set:not(:has(:target)) .set__link[href$="#set-profile"],
.set:has(#set-profile:target) .set__link[href$="#set-profile"],
.set:has(#set-prefs:target) .set__link[href$="#set-prefs"],
.set:has(#set-notif:target) .set__link[href$="#set-notif"] {
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow-card);
}

.set:not(:has(:target)) .set__link[href$="#set-profile"] .icon,
.set:has(#set-profile:target) .set__link[href$="#set-profile"] .icon,
.set:has(#set-prefs:target) .set__link[href$="#set-prefs"] .icon,
.set:has(#set-notif:target) .set__link[href$="#set-notif"] .icon {
    color: var(--accent);
}

.set__main {
    display: flex;
    flex-direction: column;
    gap: var(--s4);
    min-width: 0;
}


.set__head {
    display: flex;
    gap: var(--s3);
    align-items: flex-start;
}

.set__ico {
    display: grid;
    place-items: center;
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    background: var(--accent-soft);
    color: var(--accent);
}

.set__rows {
    display: flex;
    flex-direction: column;
}

.set__row {
    display: flex;
    align-items: center;
    gap: var(--s3) var(--s5);
    padding: var(--s4) 0;
    border-top: 1px solid var(--border);
    flex-wrap: wrap;
}

.set__rows > .set__row:first-child {
    border-top: 0;
    padding-top: var(--s2);
}

.set__text {
    display: flex;
    flex: 1 1 240px;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.set__t {
    font-size: 14px;
    font-weight: 500;
    color: var(--text);
}

.set__d {
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-2);
}

.set__ctl {
    display: flex;
    justify-content: flex-end;
    margin-left: auto;
    min-width: 0;
    max-width: 100%;
}

.set__inline {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--s2);
    flex-wrap: wrap;
}

.set__val { color: var(--text); }

.set__thumb {
    display: grid;
    place-items: center;
    flex: none;
    width: 36px;
    height: 36px;
    overflow: hidden;
    border-radius: 50%;
    background: var(--accent);
    color: var(--surface);
    font-size: 13px;
    font-weight: 600;
}

.set__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

@media (max-width: 900px) {
    .set { grid-template-columns: minmax(0, 1fr); }

    .set__nav {
        position: static;
        flex-direction: row;
        overflow-x: auto;
    }

    .set__link { flex: none; }
}

.chart__grid {
    stroke: var(--border);
    stroke-dasharray: none;
}

/* A colour follows what it stands for: slate is the value, ochre is what was paid in. */
.chart__line--deposits {
    stroke: var(--gl-ochre);
    stroke-width: 1.5;
    stroke-dasharray: 1 4;
    stroke-linecap: round;
}

.chart__cursor {
    stroke: var(--border-strong);
    stroke-dasharray: none;
}

.chart__dot {
    border-color: var(--gl-raised);
    background: var(--gl-slate);
}

.chart__tip {
    padding: var(--s3) var(--s3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

/* .chart__tip sets a display of its own, which would otherwise beat the hidden attribute. */
.chart__tip[hidden] { display: none; }

.chart__axis span,
.chart__scale span {
    color: var(--text-3);
    font-size: var(--gl-size-caption);
}

.chart__swatch {
    width: 16px;
    border-top: 2px solid var(--gl-slate);
    border-radius: 2px;
}

.chart__swatch--deposits { border-top: 2px dotted var(--gl-ochre); }

.bell__dot { color: var(--ink-fg); }

.notif:hover { background: var(--gl-hover); }

.set__ico,
.set__link { border-radius: var(--radius-sm); }

.set__link {
    gap: var(--s3);
    padding: var(--s2) var(--s3);
}

.set__thumb {
    background: var(--gl-brand);
    color: var(--gl-on-brand);
}

/* A colour follows what it stands for: slate is the value, ochre is what was paid in. */
.gl-line {
    fill: none;
    stroke: var(--gl-slate);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* The latest value: a bright point with a ring around it. */
.gl-end {
    position: absolute;
    width: 9px;
    height: 9px;
    margin: calc(var(--s1) * -1) 0 0 calc(var(--s1) * -1);
    border-radius: 50%;
    background: var(--gl-slate);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--gl-slate) 24%, transparent);
    pointer-events: none;
}

/* The tooltip of a column is the .chart__tip, opened by hover or focus of its column instead of by
   script. It is anchored on the top edge of the column's bar, so it opens upwards from the bar like
   the value chart's; --start, --middle and --end fold it inwards near the card's edges. */
.col-tip {
    display: none;
    top: 0;
    width: max-content;
    max-width: 300px;
}

.col-tip.chart__tip--start { left: 0; }
.col-tip.chart__tip--middle { left: 50%; }
.col-tip.chart__tip--end { left: 100%; }

.col:hover,
.col:focus { z-index: 2; }

.col:hover .col-tip,
.col:focus .col-tip { display: flex; }

/* ---------- Transfers ---------- */

/* A message in the list the footer opens: the alert's mark, title and detail (app.css), without
   the box, since the popover is the box. An error is red; an ask or a hint is amber, something to
   know rather than something to fix, which is the second tone Alert.razor does not have yet. The
   words carry the meaning either way. */
.form-msg {
    display: flex;
    align-items: flex-start;
    gap: 9px;
}

.form-msg--error .alert__mark,
.form-msg--error .alert__title {
    color: var(--neg);
}

.form-msg--warn .alert__mark {
    color: var(--warn);
}

/* The two sides of a transfer are two places, not two dropdowns. The badge is the one a holding
   wears everywhere else in the app, so its colour says which one you are looking at before you
   read the name. */
.tf-side {
    display: flex;
    align-items: center;
    gap: var(--s3);
}

.tf-side__body {
    flex: 1;
    min-width: 0;
}

.tf-side__mark--empty {
    border: 1px dashed var(--border-strong);
    background: none;
}

/* The balance under a picker is a reminder, not a sentence: one line, cut off rather than
   wrapped, so the two columns keep the same height. */
.tf-side .field__foot {
    display: block;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The chevron that opens a picker without typing. It sits where a select's arrow would. */
.tf-side__open {
    position: absolute;
    right: 6px;
    top: 50%;
    display: flex;
    padding: var(--s1);
    border: 0;
    border-radius: 7px;
    background: none;
    color: var(--text-3);
    cursor: pointer;
    transform: translateY(-50%);
}

.tf-side__open:hover {
    color: var(--text);
}

.tf-side .picker .input {
    padding-right: 36px;
}

/* One line per portfolio in the list, so reaching across them stays readable. */
.tf-picker__group {
    padding: var(--s2) var(--s2) var(--s1);
    color: var(--text-3);
    font-size: 12px;
    font-weight: 500;
}

.tf-picker__text {
    display: flex;
    min-width: 0;
    flex-direction: column;
}

.tf-picker__name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tf-picker__meta {
    color: var(--text-3);
    font-size: 12px;
}

.tf-picker__none {
    margin: 0;
    padding: 10px var(--s2);
}

/* ---------- The dialog ----------

   Two cards side by side, one per holding, each in the grey .note box (padding, border,
   background), with the route between them. The cards share one grid of rows (subgrid), so a
   figure on the left and its counterpart on the right are on one line. Units and a price per unit
   are filled in only on a card whose holding counts in units; when only the other side does, this
   card keeps those rows as blank space, so the two still line up. Wider than the app's wide
   dialog, which app.css has no size for. */
.dialog--wide:has(.tf-sides) {
    width: min(1000px, calc(100vw - 32px));
}

.tf-sides {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 96px minmax(0, 1fr);
    grid-template-rows: repeat(9, auto);
    column-gap: var(--s3);
    row-gap: 10px;
}

.tf-sides--no-units {
    grid-template-rows: repeat(7, auto);
}

.tf-card {
    display: grid;
    grid-row: 1 / -1;
    grid-template-rows: subgrid;
    grid-template-columns: auto minmax(0, 1fr) 32px;
    align-items: center;
    column-gap: var(--s3);
    padding: 14px var(--s4);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--bg-subtle, var(--surface-2));
}

.tf-card--source {
    grid-column: 1;
}

.tf-card--target {
    grid-column: 3;
}

.tf-card__picker,
.tf-card__rule {
    grid-column: 1 / -1;
}

.tf-card__label {
    white-space: nowrap;
}

.tf-card__action {
    display: flex;
    justify-content: center;
}

/* Figures right-aligned, as every figure in a .note is, so they read as a column. */
.tf-card .tf-cell__text {
    text-align: right;
}

/* The target card before a holding is picked: the app's empty state over every row under the
   picker, in a dashed box like the empty holding badge. */
.tf-card__empty {
    grid-column: 1 / -1;
    grid-row: 2 / -1;
    align-self: stretch;
    justify-content: center;
    padding: var(--s4);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius);
}

/* Between the cards only the route, level with the two pickers' fields (the card's top padding
   comes first, as it does for the picker). The column is no wider than the route needs, so the
   two cards sit close. */
.tf-sides__rail {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    margin-top: 14px;
}

/* What arrives with the rate under it, set tight enough to fit the height of one field, so a
   transfer between two currencies is no taller than one within one. */
.tf-twoline {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    line-height: 1.25;
}

/* As tall as a field (an .input is 40px), so the line runs through the middle of the two
   pickers' fields. */
.tf-rail {
    position: relative;
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: 40px;
}

/* The swap button sits centred just above the line, so the line runs unbroken from dot to arrow.
   It reaches into the picker's label line above, which is empty in this column. */
.tf-rail__swap {
    position: absolute;
    bottom: calc(50% + 4px);
    left: 50%;
    transform: translateX(-50%);
}

/* The dot takes as much room as the arrow at the other end (18px), so the line between them,
   and the swap button above it, sit in the middle of the gap. */
.tf-rail__dot {
    width: 9px;
    height: 9px;
    flex: none;
    margin: 0 4.5px;
    border: 2px solid var(--text-3);
    border-radius: 999px;
}

.tf-rail__line {
    flex: 1;
    height: 2px;
    background: var(--border-strong);
}

.tf-rail__arrow {
    display: flex;
    color: var(--accent);
}

/* The chevron that points down in the app, turned to point at the target. */
.tf-rail__arrow .icon {
    width: 18px;
    height: 18px;
    transform: rotate(-90deg);
}

/* A cell: a field, or a figure written out. Either way as tall as a field (an .input is 40px),
   so opening a figure, or picking a fund instead of an account, does not move a thing. */
.tf-cell {
    display: flex;
    align-items: center;
    gap: var(--s3);
    min-width: 0;
    min-height: 40px;
}

.tf-cell__field {
    flex: 1;
    min-width: 0;
}

.tf-cell__text {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Under the cards: the transfer's date under Van and the date at the target under Naar, on the
   cards' own columns and inset as far as their content is (padding and border), so the labels
   line up with the labels in the cards. */
.tf-sides__foot {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 96px minmax(0, 1fr);
    column-gap: var(--s3);
}

.tf-sides__foot > * {
    padding: 0 17px;
}

.tf-sides__foot > :last-child {
    grid-column: 3;
}

/* The popover with every message, over the form, opening from its button's left edge
   and flipping above it when there is no room below, as the footer always has. */
.form-status__pop {
    right: auto;
    left: anchor(left);
}

/* The footer's free space holds the most important message on one line, left of the buttons,
   and the way to read them all. */
.dialog__spacer:has(.form-status) {
    display: flex;
    align-items: center;
    gap: var(--s3);
    min-width: 0;
    margin-right: auto;
    margin-left: 0;
}

.form-status {
    display: flex;
    align-items: center;
    gap: var(--s2);
    min-width: 0;
}

.form-status__text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.form-status--error {
    color: var(--neg);
}

.form-status--warn .icon {
    color: var(--warn);
}

.form-status--warn .form-status__text {
    color: var(--text);
}

/* The transfer badge in a grid: the swap arrow alone, sized to sit inside a small pill. A holding's history says it in words
   instead, so this is the only place the glyph carries it. */
.tf-mark {
    gap: 0;
    padding: 0 5px;
    color: var(--text-2);
}

.tf-mark .icon {
    width: 13px;
    height: 13px;
}

/* ---------- Hero and dashboard ---------- */

/* What you own and how it moved in one large panel, what it is made of beside it, the figures and
   the portfolios under it. */
.gl-dash {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(372px, 27%);
    grid-template-areas:
        "hero split"
        "kpis split"
        "list flow";
    gap: var(--gl-gap);
    margin-top: var(--gl-gap);
}

.gl-dash--empty {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: none;
}

.gl-dash--empty .gl-hero { grid-area: auto; padding-bottom: var(--s5); }
.gl-hero--empty { padding-bottom: var(--gl-inset); }

.gl-hero {
    grid-area: hero;
    gap: 0;
    padding: var(--gl-inset) var(--gl-inset) 0;
    overflow: clip;
}

.gl-hero__top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s4);
}

/* The one large figure on the screen. */
.gl-figure {
    display: flex;
    align-items: baseline;
    margin-top: var(--s2);
    font-size: clamp(46px, 4.2vw, 58px);
    font-weight: 600;
    letter-spacing: -.045em;
    line-height: 1.08;
    white-space: nowrap;
}

.gl-figure small {
    font-size: .44em;
    font-weight: 500;
    letter-spacing: -.02em;
}

.gl-figure small + small { margin-left: var(--s1); }

.gl-change {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s2) var(--s3);
    margin-top: var(--s3);
    font-size: var(--gl-size-note);
    color: var(--text-2);
}

/* The drawing runs in from the left edge of the panel and stops short of its scale. */
.gl-hero .chart-row {
    gap: var(--s4);
    margin: var(--s4) 0 0 calc(var(--gl-inset) * -1);
}

.gl-hero .chart__scale { width: 44px; }

.gl-hero .chart__axis {
    margin: var(--s2) 0 0 calc(var(--gl-inset) * -1);
    padding-right: var(--s8);
}

.gl-hero .chart__axis span:first-child { margin-left: var(--gl-inset); }
.gl-hero .mix-legend { margin: var(--s3) 0 var(--s5); }
.gl-hero .skeleton--chart { margin: var(--s5) 0 var(--s5); }

.gl-skel-label { width: 120px; }
.gl-skel-figure { width: 320px; max-width: 100%; height: 58px; }

.gl-split {
    grid-area: split;
    gap: var(--s4);
}

/* Full width is placement: the control spans the card and its buttons share the width. */
.gl-split .seg { display: flex; }
.gl-split .seg button { flex: 1 1 auto; }

.gl-split .ring > .section-title { display: none; }

.gl-kpis {
    grid-area: kpis;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--gl-gap);
}

.gl-list { grid-area: list; }

.gl-flow { grid-area: flow; }
.gl-flow > .card { height: 100%; }

.gl-rows {
    display: flex;
    flex-direction: column;
    margin: 0 calc(var(--s3) * -1) calc(var(--s2) * -1);
}

.gl-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    column-gap: var(--s4);
    padding: var(--s3);
    border-radius: var(--radius);
    color: inherit;
    transition: background var(--gl-fade);
}

.gl-row:hover { background: var(--gl-hover); }

.gl-row__text,
.gl-row__value {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.gl-row__name { font-size: var(--gl-size-body); font-weight: 500; }

.gl-row__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px var(--s2);
    font-size: var(--gl-size-note);
    color: var(--text-3);
}

.gl-row__value { align-items: flex-end; }

.gl-row__amount {
    font-size: var(--gl-size-body);
    font-weight: 600;
    letter-spacing: -.02em;
}

.gl-row__change { font-size: var(--gl-size-note); font-weight: 500; }

/* Portfolios: one list, the mix given the room the words beside it do not need. A long description
   takes a second line before the list has to scroll sideways. */
.gl-portfolios td { height: 76px; }
.gl-portfolios td:first-child { white-space: normal; }

.gl-portfolios td:nth-child(2) {
    min-width: 240px;
    white-space: normal;
}

.gl-portfolios .mix-legend { margin-top: var(--s2); }
.gl-portfolios .asset__name { color: inherit; }

/* ---------- Motion ---------- */

/* A new range: the line flows into its new shape and the end point travels with it. The line always
   has the same number of points, so the browser can tween one into the other. */
.gl-line,
.chart__line--deposits { transition: d var(--gl-morph); }
.gl-end { transition: top var(--gl-morph); }

/* A dialog fades in and settles into place while its veil fades in behind it; closing runs back. */
.dialog,
.dialog::backdrop {
    transition: opacity 200ms ease,
                transform 200ms ease,
                overlay 200ms ease allow-discrete,
                display 200ms ease allow-discrete;
}

.dialog:not([open]),
.dialog:not([open])::backdrop { opacity: 0; }

.dialog:not([open]) { transform: translateY(8px) scale(.98); }

@starting-style {
    .dialog[open],
    .dialog[open]::backdrop { opacity: 0; }

    .dialog[open] { transform: translateY(8px) scale(.98); }
}

/* The scale and the months of a range you chose fade in. */
.gl-hero.is-ranged .chart__scale span,
.gl-hero.is-ranged .chart__axis span { animation: gl-swap 320ms ease both; }

/* A large figure counts over to its new value; the final figure is what a screen reader hears. */
.gl-figure__rolling { display: contents; }

.gl-figure__final {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* The first screen after the app opens rises into place block by block, and its line draws itself
   from the left. Once you go elsewhere, screens simply appear. */
.gl-enter .gl-dash > *,
.gl-enter .page > :not(.gl-headzone) { animation: gl-rise 420ms cubic-bezier(.2, .7, .2, 1) both; }

.gl-enter .gl-dash > :nth-child(2) { animation-delay: 40ms; }
.gl-enter .gl-dash > :nth-child(3) { animation-delay: 70ms; }
.gl-enter .gl-dash > :nth-child(4) { animation-delay: 100ms; }
.gl-enter .gl-dash > :nth-child(5) { animation-delay: 130ms; }

.gl-enter .chart { animation: gl-draw 700ms cubic-bezier(.3, .6, .2, 1) 60ms both; }

/* One thumb per segmented control that slides to the chosen part. Without anchor positioning the
   chosen part keeps its own fill. */
@supports (anchor-scope: all) {
    .seg {
        position: relative;
        isolation: isolate;
        anchor-scope: all;
    }

    .seg button[aria-pressed="true"] {
        anchor-name: --gl-thumb;
        background: none;
    }

    .seg::before {
        content: "";
        position: absolute;
        z-index: -1;
        position-anchor: --gl-thumb;
        top: anchor(top);
        bottom: anchor(bottom);
        left: anchor(left);
        right: anchor(right);
        border-radius: var(--radius-full);
        background: var(--gl-slate);
        transition: left 420ms var(--gl-spring), right 420ms var(--gl-spring);
    }
}

@keyframes gl-swap {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes gl-rise {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: none; }
}

@keyframes gl-draw {
    from { clip-path: inset(-30% 100% -30% 0); }
    to { clip-path: inset(-30% -4% -30% 0); }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }

    .gl-figure__rolling { display: none; }
    .gl-figure__final { display: contents; }
}

/* Whoever asks the system for less transparency gets solid panels. */
@media (prefers-reduced-transparency: reduce) {
    :root {
        --gl-glass: light-dark(#fff, #24272e);
        --gl-raised: light-dark(#fff, #24272e);
        --gl-bar: var(--gl-base);
        --gl-panel-bar: var(--gl-panel);
        --gl-frost: none;
    }
}

/* ---------- Small screens ---------- */

@media (max-width: 980px) {
    .lp-hero { grid-template-columns: minmax(0, 1fr); gap: 40px; padding-block: 56px var(--s7); }
}

@media (max-width: 560px) {
    .lp-preview__grid { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 900px) {
    .shell { height: auto; overflow: visible; flex-direction: column; }
    body:has(.shell:not(.shell--plain)) { overflow: auto; }
    .sidebar { width: 100%; flex-direction: row; flex-wrap: wrap; overflow-x: auto; border-right: none; border-bottom: 1px solid var(--border); }
    .brand { padding: 0 10px; width: 100%; }
    .nav-group { display: none; }
    .nav-item.is-active::before { display: none; }
    .sidebar__foot { display: none; }
    .topbar { padding: 0 var(--s4); }
    .page { padding: var(--s5) var(--s4) var(--s7); }
    .lp-nav { gap: var(--s3); padding: 0 var(--s4); }
    .lp-nav__links { display: none; }
    .lp-wrap, .lp-foot { padding-inline: var(--s4); }
    .site-footer { flex-direction: column; align-items: flex-start; }
}

/* Three figures side by side need every pixel here. */
@media (max-width: 1400px) {
    .stat__icon { display: none; }
    .stat .num--lg { font-size: var(--gl-size-title); }
}

@media (max-width: 1240px) {
    /* Once name and actions no longer fit on one line the bar takes two, too tall to stay fixed: it
       scrolls away with the page. */
    .gl-rail,
    .gl-headzone { position: relative; }

    .gl-pagehead {
        flex-wrap: wrap;
        row-gap: var(--s3);
    }

    .gl-pagehead__text { flex: 1 1 60%; }

    .gl-pagehead__actions {
        flex: 1 1 100%;
        justify-content: flex-start;
    }

    .set__nav { top: var(--gl-gap); }
    .set__sec { scroll-margin-top: var(--gl-gap); }

    .grid--stats:has(> :nth-child(4)) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 1180px) {
    .gl-dash {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas: "hero" "kpis" "split" "list" "flow";
    }
}

@media (max-width: 900px) {
    .shell:not(.shell--plain) { padding: var(--s3); }
    .nav-item { width: auto; }
    .scroll { padding: var(--s1) var(--s4) var(--s5) var(--s5); }

    .gl-headzone::before { inset: 0 calc(var(--s4) * -1) 0 calc(var(--s5) * -1); }

    .gl-rail {
        position: static;
        height: auto;
        margin-bottom: var(--s2);
        padding-right: var(--s4);
    }

    .gl-headtools {
        width: auto;
        margin-top: 0;
    }

    .gl-pagehead { padding: var(--s2) 0 var(--s4); }
}

@media (max-width: 640px) {
    .gl-pagehead__name { flex-wrap: wrap; row-gap: 0; }
    .gl-pagehead__text { flex-basis: 100%; }
    .gl-pagehead .title { white-space: normal; }

    .scroll { padding: 0 var(--s3) var(--s5) var(--s4); }

    .gl-headzone::before { inset: 0 calc(var(--s3) * -1) 0 calc(var(--s4) * -1); }

    .card__head {
        flex-wrap: wrap;
        min-width: 0;
    }

    .grid--half { grid-template-columns: minmax(0, 1fr); }

    .alloc-legend__label {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .gl-split .seg {
        overflow-x: auto;
        scrollbar-width: none;
    }

    .gl-split .seg button { padding: 0 var(--s1); }

    .gl-hero__top { flex-wrap: wrap; }
    .gl-hero .chart__axis span:first-child { display: none; }
    .gl-kpis { grid-template-columns: minmax(0, 1fr); }
    .gl-row { grid-template-columns: auto minmax(0, 1fr); row-gap: var(--s1); }
    .gl-row__value { grid-column: 2; align-items: flex-start; }

    .gl-row .pill {
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .gl-figure { font-size: 40px; }
}
