/* Fonts are loaded via <link> in app.py's index_string — Inter, Source Serif 4
   and Space Grotesk together, in one request, before this file is parsed. The
   @import that used to sit here duplicated the Inter fetch and serialised it
   behind this stylesheet. See docs/design_system.md §3.1.

   COLOURS IN THIS FILE COME FROM var(--vm-*) ONLY.
   Those custom properties are generated from utils_design_tokens.py and
   injected into :root by app.py (see _TOKENS_CSS there). Do not write a hex
   literal into this file — design_system.md §2.0 rule 2. If a colour is needed
   and has no token, it is not an approved colour.

   This file was a dark theme (`#0d1117` GitHub-dimmed) until the Verdemar
   inversion; see design_system.md §2.5. A large block of Bootstrap-class rules
   (.card, .btn, .table, .form-control, .badge, .nav-link, .text-muted …) was
   deleted rather than converted: dash-bootstrap-components is not a dependency,
   no Bootstrap stylesheet is loaded, and nothing in this codebase applies those
   class names — so the rules could not match anything. */

/* ── Global reset & typography (design_system.md §3) ─────────────────────── */
body {
    font-family: var(--vm-font-ui);
    background-color: var(--vm-canvas);
    color: var(--vm-text-primary);
    font-size: 16px;
    line-height: 1.6;
    /* Proportional figures by default; tabular is opt-in per §3.4 below, so
       prose isn't forced into monospaced-looking digits. */
    font-feature-settings: "tnum" off;
}

/* §3.2 — h1/h2 are editorial (Source Serif 4); h3/h4 are Inter Semibold.
   Sizes follow the §3.3 scale. Mantine's own Title component reads the theme
   for this; these rules cover plain html.H* elements. */
h1, .h1 { font-family: var(--vm-font-editorial); font-weight: 400; font-size: 32px; line-height: 1.2; }
h2, .h2 { font-family: var(--vm-font-editorial); font-weight: 400; font-size: 24px; line-height: 1.3; }
h3, .h3 { font-family: var(--vm-font-ui); font-weight: 600; font-size: 20px; line-height: 1.4; }
h4, .h4 { font-family: var(--vm-font-ui); font-weight: 600; font-size: 17px; line-height: 1.4; }
h5, .h5,
h6, .h6 { font-family: var(--vm-font-ui); font-weight: 600; }

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    color: var(--vm-text-primary);
}

/* §3.4 — tabular figures are MANDATORY wherever numbers stack or align.
   Misaligned digits across rows destroy the reliability signal instantly. */
.number,
td.numeric,
.currency,
.percentage,
.ag-cell-num-aughtcy {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
}

/* ── Scrollbars ──────────────────────────────────────────────────────────── */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: var(--vm-canvas);
}

::-webkit-scrollbar-thumb {
    background: var(--vm-border-strong);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--vm-text-tertiary);
}

/* ── react-select dropdown (components/modals/account_create.py,
      pages/settings/instruments.py) ──────────────────────────────────────────
   Two live usages. Kept because those two controls are a plain react-select,
   not a DMC Select, so the Mantine theme does not reach them. */
.mantine-like-dropdown .Select-control {
    border: 1px solid var(--vm-border);
    border-radius: var(--vm-radius-sm);
    height: 36px;
    background-color: var(--vm-surface);
    color: var(--vm-text-primary);
    font-family: var(--vm-font-ui);
    font-size: 14px;
}

.mantine-like-dropdown .Select-control:hover {
    border-color: var(--vm-border-strong);
}

.mantine-like-dropdown.is-focused .Select-control {
    border-color: var(--vm-primary);
    box-shadow: var(--vm-focus-ring);
}

.mantine-like-dropdown .Select-placeholder {
    line-height: 36px;
    padding-left: 10px;
    color: var(--vm-text-tertiary);
}

.mantine-like-dropdown .Select-value-label,
.mantine-like-dropdown .Select-value,
.mantine-like-dropdown.has-value .Select-control .Select-value-label,
.mantine-like-dropdown .Select-input input {
    color: var(--vm-text-primary);
}

.mantine-like-dropdown .Select-value-label,
.mantine-like-dropdown .Select-single-value {
    line-height: 36px;
    padding-left: 10px;
}

.mantine-like-dropdown .Select-input {
    height: 36px;
    padding-left: 10px;
    color: var(--vm-text-primary);
}

.mantine-like-dropdown .Select-menu-outer {
    background-color: var(--vm-surface);
    border: 1px solid var(--vm-border);
    border-radius: var(--vm-radius-sm);
    box-shadow: var(--vm-shadow-elevated);
    font-family: var(--vm-font-ui);
    font-size: 14px;
    z-index: 9999;
    color: var(--vm-text-primary);
}

.mantine-like-dropdown .Select-option {
    background-color: var(--vm-surface);
    color: var(--vm-text-primary);
}

.mantine-like-dropdown .Select-option.is-focused {
    background-color: var(--vm-primary-subtle);
    color: var(--vm-text-primary);
}

/* ──────────────────────────────────────────────────────────────────────────
   Data Grid — AG Grid theme (design_system.md §5.4 + §5.4a)
   --------------------------------------------------------
   ONE source of truth for every data table. Pages import
   components/data_grid.make_data_grid() and these tokens drive the look.

   §5.4a is the binding of §5.4's visual rules onto AG Grid's CSS custom
   properties — the grid cannot read the Mantine theme, so this is how the
   design tokens reach it. Every --ag-grid-* value below resolves to a
   var(--vm-*); none is a literal.

   DENSITY IS DELIBERATELY UNCHANGED HERE. §5.4 specifies a 52px desktop row;
   these grids use 34px. Going to 52px would show roughly half as many rows per
   screen on holdings and transactions — a real UX decision, not a colour one,
   so it is left for the §11 conformance pass rather than slipped into the
   theme inversion. Flagged, not forgotten.
   ────────────────────────────────────────────────────────────────────── */
:root {
    /* transparent: the grid sits on a `surface` card and inherits it, so the
       card's border is the grid's edge and there is no double background. */
    --ag-grid-surface-0: transparent;
    --ag-grid-surface-1: var(--vm-surface-sunken);
    --ag-grid-surface-2: var(--vm-primary-subtle);
    --ag-grid-surface-menu: var(--vm-surface);
    --ag-grid-input-bg: var(--vm-surface);

    --ag-grid-text-strong: var(--vm-text-primary);
    --ag-grid-text-muted: var(--vm-text-secondary);
    --ag-grid-text-faint: var(--vm-text-tertiary);

    --ag-grid-line: var(--vm-border);
    --ag-grid-border: var(--vm-border);

    --ag-grid-accent: var(--vm-primary);
    --ag-grid-accent-soft: var(--vm-primary-subtle);
    --ag-grid-edit-soft: var(--vm-surface-sunken);

    --ag-grid-row-h: 34px;
    --ag-grid-header-h: 36px;
    --ag-grid-cell-pad-x: 10px;
}

.ag-theme-alpine.ag-theme-aughtcy,
.ag-theme-alpine {
    --ag-background-color: var(--ag-grid-surface-0) !important;
    /* §5.4: NO zebra striping — odd rows match the body background. Alpine
       sets a striped default, so this override is load-bearing. */
    --ag-odd-row-background-color: var(--ag-grid-surface-0) !important;
    --ag-header-background-color: var(--ag-grid-surface-1) !important;
    --ag-header-foreground-color: var(--ag-grid-text-muted) !important;
    --ag-foreground-color: var(--ag-grid-text-strong) !important;
    --ag-font-size: 13px !important;
    --ag-font-family: var(--vm-font-ui) !important;
    --ag-borders: none !important;
    --ag-row-border-color: var(--ag-grid-line) !important;
    --ag-border-color: transparent !important;
    /* §5.4: horizontal rules between rows, no vertical cell borders. */
    --ag-cell-horizontal-border: solid transparent !important;
    --ag-header-column-separator-display: none !important;
    --ag-row-hover-color: var(--ag-grid-surface-2) !important;
    --ag-selected-row-background-color: var(--ag-grid-accent-soft) !important;
    --ag-range-selection-border-color: var(--ag-grid-accent) !important;
    --ag-input-focus-border-color: var(--ag-grid-accent) !important;
    --ag-row-height: var(--ag-grid-row-h) !important;
    --ag-header-height: var(--ag-grid-header-h) !important;
    --ag-cell-horizontal-padding: var(--ag-grid-cell-pad-x) !important;
}

/* Header — muted, uppercase, letter-spaced (§5.4). Vertical padding is left to
   AG Grid so the header heights in the pinned-left and scrolling
   containers stay in lockstep (overriding padding desynchronised them and
   shifted body rows). */
.ag-theme-alpine .ag-header-cell {
    font-size: 10.5px !important;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600 !important;
    border-bottom: 1px solid var(--ag-grid-border) !important;
    color: var(--ag-grid-text-muted) !important;
}

.ag-theme-alpine .ag-header-cell-text,
.ag-theme-alpine .ag-icon {
    color: var(--ag-grid-text-muted) !important;
}

/* Body cell — compact, single-line, ellipsis on overflow. Vertical
   centering and height are left to AG Grid (it sets row height inline);
   overriding line-height here previously offset cells between containers. */
.ag-theme-alpine .ag-cell {
    color: var(--ag-grid-text-strong) !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Numeric column — right-aligned, tabular figures so digits line up (§3.4). */
.ag-theme-alpine .ag-cell.ag-cell-num-aughtcy {
    justify-content: flex-end;
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum';
}

/* Editable cell — dotted underline on the value (always visible so the
   user can tell editable cells apart even without hovering), faint
   background tint on row hover, and a pencil icon on row hover.

   The underline is applied to the cell itself, NOT to a descendant — earlier
   iterations put it on an inner span and that interacted with AG Grid's
   virtualised re-render, leaving ghost text visible in adjacent columns. */
.ag-theme-alpine .ag-cell.ag-cell-editable-aughtcy {
    cursor: text;
    background-color: transparent !important;
}

/* In modern AG Grid the .ag-cell IS the value container (no inner
   .ag-cell-value child), so descendant selectors don't match. The
   `.ag-cell.ag-cell-editable-aughtcy` selector wins on specificity over AG
   Grid's own `.ag-cell` defaults — no !important needed. */
.ag-theme-alpine .ag-cell.ag-cell-editable-aughtcy {
    text-decoration: underline dotted var(--ag-grid-text-faint);
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
}

/* The pencil is a data: URI, so its colour cannot be a var(). It is the
   %23-escaped form of the text-tertiary token (#8A8A8A); if that token moves,
   move this with it. Called out because it is the one place in this file a
   colour is not resolved through a variable, and it is a CSS limitation
   rather than an exception to §2.0. */
.ag-theme-alpine .ag-row-hover .ag-cell.ag-cell-editable-aughtcy {
    background-color: var(--ag-grid-edit-soft) !important;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%238A8A8A' opacity='0.7'><path d='M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z'/></svg>");
    background-repeat: no-repeat;
    background-position: right 6px center;
    background-size: 11px 11px;
}

/* Platform-costs matrix — cell highlighting.
   Cheapest cell in each account-type column (where the user has a balance):
   subtle `positive` fill so the diagonal of best fits stands out.
   Unsupported cell (platform doesn't offer that account type): rendered as
   "✗" with a muted fill so the gap is visible without being noisy.
   Note these are the ONE sanctioned cell-level colour cue: §2.4 forbids
   colour-coding whole ROWS by gain/loss, which this is not — it marks a
   category, not a value's sign. */
.ag-theme-alpine .ag-cell.platform-matrix-cell-cheapest {
    background-color: var(--vm-positive-subtle) !important;
    font-weight: 600;
}
.ag-theme-alpine .ag-cell.platform-matrix-cell-unsupported {
    color: var(--ag-grid-text-faint) !important;
    background-color: var(--vm-surface-sunken);
}
/* All-in-one column · best cell: the same positive tint plus the positive
   foreground, so the row-winner reads stronger than the per-column cheapest
   highlight without introducing a second green. */
.ag-theme-alpine .ag-cell.platform-matrix-cell-allinone-best {
    background-color: var(--vm-positive-subtle) !important;
    color: var(--vm-positive) !important;
    font-weight: 700;
}

/* Selected row — subtle accent: thin left bar + tinted fill (§5.4). */
.ag-theme-alpine .ag-row-selected {
    background-color: var(--ag-grid-accent-soft) !important;
    box-shadow: inset 2px 0 0 0 var(--ag-grid-accent) !important;
}

/* Pinned first column gets a hair of separation from the scrolling area. */
.ag-theme-alpine .ag-pinned-left-cols-container .ag-cell,
.ag-theme-alpine .ag-pinned-left-header {
    border-right: 1px solid var(--ag-grid-border) !important;
}

/* Cell tooltips — the PDF-reader grid sets a default `tooltipValueGetter`
   returning the cell's own value, so a column narrower than its content stays
   readable on hover. AG Grid's stock tooltip is a dark chip that collapses
   whitespace and sits on no design token; give it the app's surface, room for a
   fund name, and `pre-line` so the Issues cell's bullet list keeps its lines. */
.ag-theme-alpine .ag-tooltip,
.ag-tooltip {
    white-space: pre-line;
    max-width: 380px;
    background-color: var(--vm-surface) !important;
    color: var(--vm-text-primary) !important;
    border: 1px solid var(--vm-border) !important;
    border-radius: var(--vm-radius-sm) !important;
    box-shadow: var(--vm-shadow-elevated);
    padding: 6px 10px;
    font-size: 12px;
    line-height: 1.4;
}

/* Filter popup / column menu — floats above the page, so unlike the grid it
   gets a real surface and the §4.5 elevated shadow. */
.ag-theme-alpine .ag-menu,
.ag-theme-alpine .ag-popup-child,
.ag-theme-alpine .ag-filter-toolpanel-search {
    background-color: var(--ag-grid-surface-menu) !important;
    border: 1px solid var(--ag-grid-border) !important;
    border-radius: var(--vm-radius-sm) !important;
    box-shadow: var(--vm-shadow-elevated);
    color: var(--ag-grid-text-strong) !important;
}

.ag-theme-alpine .ag-menu-option,
.ag-theme-alpine .ag-filter-condition,
.ag-theme-alpine .ag-filter {
    background-color: var(--ag-grid-surface-menu) !important;
    color: var(--ag-grid-text-strong) !important;
}

.ag-theme-alpine .ag-menu-option:hover,
.ag-theme-alpine .ag-menu-option-active {
    background-color: var(--ag-grid-surface-2) !important;
}

.ag-theme-alpine .ag-filter input[class^="ag-"],
.ag-theme-alpine .ag-filter select[class^="ag-"],
.ag-theme-alpine .ag-input-field-input,
.ag-theme-alpine .ag-picker-field-display {
    background-color: var(--ag-grid-input-bg) !important;
    color: var(--ag-grid-text-strong) !important;
    border: 1px solid var(--ag-grid-border) !important;
    border-radius: var(--vm-radius-xs) !important;
}

/* ── Popup select cell editors ────────────────────────────────────────────
   An inline agSelectCellEditor is capped at its cell's width, so a long option
   truncates mid-word ("Other / niche broker (United Ki…") — fatal for the
   accounts grid's Platform picker, whose whole point is telling apart two rows
   that share a name. Columns using `cellEditorPopup` get a list sized to its
   own content instead, with a floor so a narrow column still reads. */
.ag-theme-alpine .ag-popup-editor {
    width: max-content !important;
    min-width: 18rem !important;
    max-width: 28rem;
}

.ag-theme-alpine .ag-popup-editor .ag-list-item {
    overflow: visible;
}

.ag-theme-alpine .ag-filter-apply-panel {
    background-color: var(--ag-grid-surface-menu) !important;
    border-top: 1px solid var(--ag-grid-border) !important;
}

.ag-theme-alpine .ag-filter-apply-panel button {
    color: var(--ag-grid-accent) !important;
}

.ag-theme-alpine .ag-label {
    color: var(--ag-grid-text-muted) !important;
}

/* ── Dash DataTable row hover ─────────────────────────────────────────────
   A few surfaces still use dash_table.DataTable rather than AG Grid. Its
   default hover paint is near-white, which on a white `surface` is invisible;
   §5.4 wants `primary-subtle` with the text unchanged. (The old dark theme
   used a solid blue fill with white text — far too loud for this system.) */
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner table tr:hover td,
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner table tr:hover td.dash-cell {
    background-color: var(--vm-primary-subtle) !important;
    color: var(--vm-text-primary) !important;
}

.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner td.dash-cell:hover {
    background-color: var(--vm-primary-subtle) !important;
    color: var(--vm-text-primary) !important;
}

.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner td.focused,
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner td.cell--selected {
    background-color: var(--vm-primary-subtle) !important;
    color: var(--vm-text-primary) !important;
}

/* Headers are non-interactive — suppress every hover/focus paint the
   DataTable would otherwise apply to <th> cells. */
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner tr:hover th,
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner tr:hover th.dash-header,
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner th.dash-header:hover,
.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner th.dash-header.focused {
    background-color: var(--vm-surface-sunken) !important;
    color: var(--vm-text-secondary) !important;
}

/* ── Plotly hover tooltip overflow ─────────────────────────────────────── */
/*
 * Plotly pie hover tooltips live inside the chart SVG and are otherwise
 * clipped by the various wrapper divs plotly inserts.  The holdings page
 * needs a multi-line tooltip (the "Other" slice breakdown) to extend past
 * the chart card edges, so force every plotly wrapper — and its SVG
 * containers — to allow overflow.
 */
.js-plotly-plot,
.js-plotly-plot .plot-container,
.js-plotly-plot .plot-container .svg-container,
.js-plotly-plot .main-svg {
    overflow: visible !important;
}

/* Empty-state welcome hero action links (pages/overview/holdings.py) */
.hold-action-link:hover {
    text-decoration: underline;
}

.hold-inline-menu,
.hold-inline-menu > div {
    display: inline !important;
}

/* ──────────────────────────────────────────────────────────────────────────
   Loading indicator (unified)
   ---------------------------
   One visual language for "the app is working" across the entire UI:
   the centred Verdemar six-glyph spinner. Two variants share it:

     .aughtcy-loading        →  wraps a card region; the spinner is
                                overlaid on the wrapped content while
                                .is-loading is set. Content fades to 0.4
                                opacity behind the spinner.

     .aughtcy-page-loader    →  full-page centred variant for the OAuth
                                callback flash and admin-dashboard first
                                paint.

   Pages flip the `is-loading` class via clientside callbacks. The spinner
   element itself comes from components/loading.py, which routes to
   components.brand.spinner — see design_system.md §10.1 for where a spinner
   is allowed at all (short version: here and buttons; elsewhere, skeletons).
   ────────────────────────────────────────────────────────────────────── */
.aughtcy-loading {
    position: relative;
    width: 100%;
}

.aughtcy-loading__spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    display: none;
    z-index: 5;
    pointer-events: none;
}

.aughtcy-loading__content {
    transition: opacity 0.2s ease;
}

.aughtcy-loading.is-loading .aughtcy-loading__spinner { display: block; }
.aughtcy-loading.is-loading .aughtcy-loading__content { opacity: 0.4; }

/* Full-page variant — centred on the viewport. */
.aughtcy-page-loader {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    width: 100%;
}

/* Auth-resolution overlay — full-viewport fixed spinner shown while the
   router resolves the post-auth path (magic-link landing, persisted-session
   cold start). A 200 ms delay before becoming visible suppresses the flash
   on fast resolutions; see the clientside callback in app.py. */
.auth-resolution-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: var(--vm-canvas);
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    display: none;
}
.auth-resolution-overlay.is-visible { display: flex; }

/* Reassurance + last-resort reload, revealed on a delay so a normal fast
   resolution never shows them. Times are measured from when `.is-visible` is
   applied (animation-delay); when the overlay hides, the classes drop and the
   animations reset for next time. Pure CSS — no JS timers to reset the countdown
   as the router retries in the background. */
.auth-resolution-message {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    opacity: 0;
}
.auth-resolution-overlay.is-visible .auth-resolution-message {
    animation: acy-auth-msg-in 0.4s ease-out 9s forwards;
}
@keyframes acy-auth-msg-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ── First-paint page gate (components/loading.py: page_gate/page_overlay) ──
   For a page that arrives in two waves — config rail instantly, data-driven
   body seconds later. The body is hidden and an opaque overlay covers the
   content area until the first build lands, so the page presents as one
   finished screen instead of a shell with a spinner in its lower half.

   Distinct from `.auth-resolution-overlay` above, which is app-level and fixed
   to the viewport: this one is absolute inside the page's own content area, so
   the header and sidebar stay live and the user can navigate away from a slow
   page rather than being pinned to it.

   FIRST PAINT ONLY. Re-gating on every rebuild would blank the controls out
   from under the user mid-interaction; later loads belong to `.aughtcy-loading`
   over the region that is actually changing.
   ─────────────────────────────────────────────────────────────────── */
.aughtcy-page-gate-root {
    position: relative;
}

/* `visibility`, not `display`: hidden content keeps its layout box, so the
   overlay has the page's real height to centre in and the reveal introduces no
   layout jump. */
.aughtcy-page-gate {
    visibility: hidden;
}

.aughtcy-page-gate.is-ready {
    visibility: visible;
}

.aughtcy-page-overlay {
    position: absolute;
    inset: 0;
    z-index: 10;
    background: var(--vm-canvas);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    /* The CENTRING BOX is capped at one viewport, the covering box is not.
       `inset: 0` spans the gate's full height, and the gate keeps its layout box
       while hidden — so on a tall page `justify-content: center` would centre the
       mark halfway down the *document* and leave the spinner below the fold. It
       does not bite today (a gated page is the settings tile plus an empty
       preview, ~760 px, mark measured at y=436 of a 744 px viewport) but it is
       one declaration to make it structural rather than a coincidence of how
       tall this particular page happens to be. Nothing shows below the cap: the
       body underneath is `visibility: hidden`, not merely covered. */
    max-height: 100vh;
}

.aughtcy-page-overlay.is-done {
    display: none;
}

/* The SHOW path is pure CSS and the hide path is the only thing JS touches.
   A 200 ms delay means a warm cache-hit load reveals the finished page having
   never flashed a spinner, and — unlike a JS timer — nothing has to be armed
   for the mark to appear, so a broken clientside callback degrades to a spinner
   rather than to a blank page. */
.aughtcy-page-overlay__mark {
    /* RESTING STATE IS VISIBLE, and the ~200 ms delay lives inside the keyframes
       rather than in `animation-delay` + `forwards`. An element whose resting
       opacity is 0, revealed only by an animation *completing*, is a blank page
       anywhere the animation is never applied or is cancelled — a
       prefers-reduced-motion rule, a stylesheet that loaded partially, a future
       `animation: none`. That is the exact failure this overlay exists to
       prevent, so it must not be reachable through the reveal itself.

       KNOWN AND DELIBERATELY NOT FIXED: in a BACKGROUNDED tab Chrome freezes the
       document timeline (`document.timeline.currentTime` stays 0), which pins a
       running animation at its 0 % keyframe — so the mark is invisible there no
       matter what the resting value says. That is not a defect and no resting
       opacity can override it: nobody is looking at a background tab, and the
       animation resumes the instant it is foregrounded. Verified directly while
       driving this page over CDP, where every animation on the page — the brand
       spinner included — was frozen the same way. Do not "fix" it by dropping
       the animation; you would trade a flash nobody sees for one everybody
       does. */
    opacity: 1;
    animation: acy-page-overlay-in 0.4s ease-out;
}

/* Revealed ~8 s in, for the case where the build is genuinely slow and a bare
   spinner starts to read as a hung page. Same technique as the auth overlay's
   reassurance block: animation-delay, no JS timer to drift. */
/* The message is the one thing that MAY hide behind an animation: if it never
   runs the user simply doesn't get the extra reassurance line, which degrades to
   the spinner alone rather than to a blank screen. Same trade the auth overlay's
   reassurance block already makes. */
.aughtcy-page-overlay__message {
    opacity: 0;
    animation: acy-page-overlay-msg-in 0.4s ease-out 8s forwards;
    max-width: 320px;
    text-align: center;
}

/* The 0→50% hold IS the 200 ms delay. Expressing it here rather than as
   `animation-delay` keeps the resting opacity at 1 (see the mark rule above). */
@keyframes acy-page-overlay-in {
    0%, 50% { opacity: 0; transform: translateY(4px); }
    100%    { opacity: 1; transform: translateY(0); }
}

@keyframes acy-page-overlay-msg-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Printing mid-build would otherwise paper the sheet with an opaque panel. */
@media print {
    .aughtcy-page-overlay { display: none !important; }
    .aughtcy-page-gate { visibility: visible !important; }
}

/* ── Skeleton mode (design_system.md §10.1) ───────────────────────────────
   A region built with `loading.wrap(..., skeleton="table")` shows a shaped
   placeholder instead of a spinner over dimmed content. "A skeleton tells the
   user what is arriving; a spinner only tells them to wait."

   The skeleton sits ALONGSIDE the content in the DOM and CSS chooses between
   them, so the clientside callback that toggles `is-loading` stays identical
   for both modes and knows nothing about which one a region uses. */
.aughtcy-loading__skeleton {
    display: none;
}

.aughtcy-loading.is-loading .aughtcy-loading__skeleton {
    display: block;
}

/* In skeleton mode the spinner and the dimmed content both stand down — two
   simultaneous loading signals is one more than the user needs.
   
   Selected via `:has()` on the SKELETON CHILD rather than a marker class on the
   wrapper, and that detail is load-bearing. Roughly a dozen clientside callbacks
   set this element's className wholesale (`return "aughtcy-loading is-loading"`),
   so any class added server-side is wiped the first time a store resolves — a
   marker class here silently stopped working the moment the page updated.
   Keying off structure the callbacks don't touch means they need to know nothing
   about which mode a region uses, which was the point. */
.aughtcy-loading.is-loading:has(.aughtcy-loading__skeleton) .aughtcy-loading__spinner,
.aughtcy-loading.is-loading:has(.aughtcy-loading__skeleton) .aughtcy-loading__content {
    display: none;
}

/* §10.1 specifies skeletons as `surface-sunken` blocks. Mantine's Skeleton
   builds itself from two pseudo-elements — `::before` paints the body colour
   and `::after` paints the block — and defaults the block to a mid grey
   (`rgb(167,167,167)`), which on a white card reads as a filled bar rather than
   a recess. Retint both to the tokens so a loading card looks like the card it
   is about to become. */
.aughtcy-loading__skeleton .mantine-Skeleton-root::before {
    background-color: var(--vm-surface);
}

.aughtcy-loading__skeleton .mantine-Skeleton-root::after {
    background-color: var(--vm-surface-sunken);
}

/* ── Wide comparison tables on a phone (design_system.md §9.2 case 2) ──────
   A returns matrix is a comparison grid: turning its rows into cards destroys
   the comparison that is its entire purpose, so §9.2 permits an opt-in
   horizontal scroller instead. The table carries a `min-width` so it keeps its
   columns rather than collapsing them into an unreadable smear, and its
   container's `overflow-x: auto` then scrolls.

   This is NOT the §11 horizontal-scroll violation — that rule is about the
   PAGE scrolling sideways. A scroller the user opted into, inside a card, is
   the sanctioned escape hatch.

   The first column sticks so the row label stays visible while the numbers
   scroll under it; without that you are scrolling a grid of figures with
   nothing to say which row you are on. */
.vm-scroll-table th:first-child,
.vm-scroll-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    /* Opaque, or the scrolling columns show through the label. */
    background-color: var(--vm-surface);
}

.vm-scroll-table thead th:first-child {
    background-color: var(--vm-surface-sunken);
}

/* ── Mobile-only refinements (design_system.md §9) ─────────────────────────
   EVERYTHING in this block is inside a max-width query. The app is
   desktop-first by design (§1.6) and a mobile pass must not alter the desktop
   site — so these rules simply do not exist above the breakpoint.

   768px is Mantine's `sm` and the AppShell's own navbar breakpoint; keep them
   in step. */
@media (max-width: 767px) {

    /* §5.2/§5.3: "Height: 40px desktop, 44px mobile (touch target)." The 44px
       floor is the Apple/W3C minimum finger target — below it people mis-tap,
       and on this app a mis-tap can edit money. Desktop keeps its 40px: a mouse
       pointer hits a 36px control perfectly well, and growing every desktop
       control would be a change nobody asked for. */
    .mantine-Button-root,
    .mantine-ActionIcon-root,
    .mantine-Input-input,
    .mantine-Tabs-tab,
    .mantine-SegmentedControl-label {
        min-height: 44px;
    }

    /* NavLink's 44px lives in sidebar_collapse.css, beside the 40px rule it
       overrides — same specificity, and that file loads after this one, so a
       copy here would silently lose. Put an override next to what it overrides. */

    /* The PDF importer's "View original document" is an action TARGET sitting
       opposite the Discard button, not inline prose, so §5.2's 44px floor
       applies to it — but it is a dmc.Anchor and matches none of the selectors
       above, so it rendered 16px tall. Scoped to the one id deliberately:
       adding .mantine-Anchor-root to that list would floor every inline text
       link in the app (privacy policy, terms, footers) to a 44px block. */
    #pdf-original-link {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
    }

    /* Every modal's close button is Mantine's 28x28 CloseButton, under §5.2's
       44px floor on a surface §9.1 marks FULL support. It is the same defect as
       the link above and it sat in all sixteen modals, unseen, because no audit
       could open a modal until dev/mobile_audit.py grew a modal pass — closing
       that blind spot is what surfaced this.

       Scoped to Modal, not to the shared .mantine-CloseButton-root: the Drawer
       close buttons live on Tools surfaces that §9.1 gates as whole pages, and
       flooring a control nobody can reach from a phone is churn, not a fix. */
    .mantine-Modal-close {
        min-height: 44px;
        min-width: 44px;
    }

    /* NumberInput's ▲▼ spinners are a DELIBERATE EXCEPTION and stay ~17px.
       Two stacked 44px halves would make an 88px control, and the chevrons are
       not the way anyone sets a value on a phone — you tap the field and type.
       They are a mouse affordance that costs nothing to leave in place, and
       enlarging them would trade a real input for a rare one. Documented here
       so the audit's tap-target count is not read as an oversight. */
    .mantine-NumberInput-control {
        min-height: 0;
    }

    /* Market-pulse strip: wrap to two tiles per row instead of a one-line
       scroller. Five 180px tiles are 900px; in a ~293px phone column that
       showed 1.6 tiles with nothing to indicate the rest were a swipe away.
       They were reachable, but not discoverable — and a market strip you have
       to guess at is not doing its job. */
    .mp-strip {
        flex-wrap: wrap;
        overflow-x: visible;
    }

    /* `!important` is required, not decorative: the tile carries an INLINE
       `min-width: 180px` (it is what keeps the tiles readable in the desktop
       row), and inline styles beat any selector without it. Without this the
       tiles wrapped one-per-row — five rows of market data where two were
       wanted. */
    .mp-strip .mp-tile {
        flex: 1 1 46% !important;
        min-width: 0 !important;
    }

    /* The sticky first column has to be narrow enough to leave room for what
       you are scrolling TO. A row label like "MSCI ACWI Tracker (iShares)
       (GBP)" is 230px of a 261px container, so the sticky column pinned almost
       the whole width and the data columns arrived one sliver at a time — the
       table scrolled correctly and still could not be read.

       Capped at 42% with an ellipsis. §9.2 case 3 forbids shrinking TYPE to fit
       columns; it does not forbid truncating a label, and an ellipsis says
       plainly that there is more. The label only has to identify the row — the
       numbers beside it are the point. */
    .vm-scroll-table th:first-child,
    .vm-scroll-table td:first-child {
        max-width: 42vw;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Two-up tiles are ~130px, and at the corrected 13px type the sub-label
       ('YTD · GBP · TODAY +0.00%') no longer fits on one line — it was being
       cut off mid-word with no ellipsis, i.e. simply lost. Let it wrap onto a
       second line here; the desktop row keeps `nowrap`, where it fits.
       A worked example of two good changes colliding: the type fix and the
       tile-wrap fix were each right and together they truncated. */
    .mp-strip .mp-tile-chips {
        white-space: normal !important;
        flex-wrap: wrap;
        overflow: visible !important;
    }

    /* Overview corporate actions: drop the declared per-share rate, keep the
       payout. Five columns do not fit a phone, and of the two money columns
       the payout is the one you check on the move — the per-share rate is a
       comparison figure, and comparing is a desk task (§1.6). It stays one tap
       away on Asset Detail rather than being squeezed into 40px here.
       Hiding beats scrolling for a teaser: this table is a glance, and a
       glance you have to swipe is not one. */
    .ov-corp-table .ov-corp-per-share {
        display: none;
    }

    /* Re-cut the remaining four columns for 375px. The desktop percentages
       assume five columns and leave the date at ~59px, which wraps
       '2026-08-14' onto two lines — a date is a single token and reads as
       damage when broken. Trimming the cell padding to 6px buys ~32px across
       the row, and the widths below spend it where content is fixed-size
       (date, payout, action), letting the asset name — the one column that
       degrades gracefully, via its ellipsis — absorb what is left.

       The date also drops to 13px — §3.3's floor, and it is already the row's
       most de-emphasised element. Width alone could not hold '2026-08-14' on
       one line without starving the asset name down to about six characters,
       which trades a broken date for an unreadable name. `nowrap` then makes
       the fit a hard guarantee rather than a measurement that holds until the
       first wider font.

       `!important` is required, not decorative: the widths and padding are
       inline styles on the th/td, which a stylesheet rule cannot otherwise
       beat. nth-child(4) is the hidden per-share column, hence the gap. */
    .ov-corp-table th,
    .ov-corp-table td {
        padding-left: 6px !important;
        padding-right: 6px !important;
    }

    .ov-corp-table th:nth-child(1),
    .ov-corp-table td:nth-child(1) {
        width: 29% !important;
        font-size: 13px !important;
        white-space: nowrap;
    }

    .ov-corp-table th:nth-child(2),
    .ov-corp-table td:nth-child(2) { width: 26% !important; }

    .ov-corp-table th:nth-child(3),
    .ov-corp-table td:nth-child(3) { width: 21% !important; }

    .ov-corp-table th:nth-child(5),
    .ov-corp-table td:nth-child(5) { width: 24% !important; }
}

/* ── Scroll affordance for wide tables (design_system.md §9.2 case 2) ──────
   A table that scrolls sideways is only useful if the reader KNOWS it does.
   Mobile scrollbars are overlay-style and invisible until you already scroll,
   so a sticky-column table looks like a table that simply stops — which is
   how "reachable" and "discoverable" came apart on the market-pulse strip too.

   The classic pure-CSS scroll shadow: two `local` gradients painted in the
   surface colour sit over each edge and travel WITH the content, and two
   `scroll` shadows sit under them fixed to the container. At the left end the
   cover hides the left shadow; scroll right and it slides away to reveal it.
   Self-managing, no JS, and it disappears entirely when there is nothing to
   scroll to.

   `:has()` targets the container from the table, because these containers are
   inline-styled at several call sites and have no shared class of their own. */
div:has(> .vm-scroll-table) {
    background:
        linear-gradient(to right, var(--vm-surface) 40%, rgba(255, 255, 255, 0))
            left center / 28px 100% no-repeat local,
        linear-gradient(to left, var(--vm-surface) 40%, rgba(255, 255, 255, 0))
            right center / 28px 100% no-repeat local,
        radial-gradient(farthest-side at 0 50%, rgba(26, 26, 26, 0.13), rgba(255, 255, 255, 0))
            left center / 12px 100% no-repeat scroll,
        radial-gradient(farthest-side at 100% 50%, rgba(26, 26, 26, 0.13), rgba(255, 255, 255, 0))
            right center / 12px 100% no-repeat scroll;
}
