/* ═══════════════════════════════════════════════════════════════════════════
 * DIURNAL DESIGN TOKENS — single source of truth (mirrors Theme/DiurnalTheme.cs).
 * A task app set as a printed daily broadsheet: warm newsprint, near-black ink,
 * one acid-chartreuse highlighter standing in for attention. "Nocturne" is the
 * night edition. ThemeTests asserts this table stays in sync with the C#.
 *
 *   Token                Light       Dark
 *   bg (paper)           #f2eee3     #131311
 *   surface (leaf)       #fbf9f3     #1c1c18
 *   surface-raised       #ffffff     #232320
 *   line (rule)          #d9d2c0     #333229
 *   ink                  #16140f     #ede9dc
 *   ink-muted            #56513f     #b3ad9b
 *   ink-faint            #6f6a5b     #948e7c
 *   accent (MARKER)      #d4f03c     #d4f03c
 *   accent-hover         #c3e02b     #e2fc55
 *   accent-ink           #16140f     #14140f
 *   accent-text (olive)  #5a6b12     #c9e84a
 *   pri-high (vermilion) #c9341a     #ff6b4a
 *   pri-medium (ochre)   #8a6205     #e0a93b
 *   pri-low (olive)      #5a6b12     #c9e84a
 *   pri-none (stone)     #7d7666     #6e6858
 *
 * ── TWO RULES ──────────────────────────────────────────────────────────────
 *  1. --q-accent is a FILL, never text. Chartreuse on paper is 1.11:1 as type.
 *     Lay --q-accent-ink on it (14.31:1); for a foreground in the marker family
 *     use --q-accent-text (the deep olive sibling, 5.11:1).
 *  2. Vermilion means late, and nothing else — it is the sheet's only alarm.
 *
 * Every foreground token clears WCAG AA against its own paper. Priority colors
 * are dots and edge bars, not type; pri-none is quietest at 3.89:1 / 3.35:1,
 * over the 3:1 that WCAG 1.4.11 sets for non-text UI components.
 *
 * The --q-* prefix is kept as an opaque token namespace: renaming it would touch
 * ~700 references for no functional gain. It does not imply the old Quarry palette.
 * ═══════════════════════════════════════════════════════════════════════════ */
:root {
    /* Light — newsprint */
    --q-bg: #f2eee3;
    --q-surface: #fbf9f3;
    --q-surface-raised: #ffffff;
    --q-line: #d9d2c0;
    --q-ink: #16140f;
    --q-ink-muted: #56513f;
    --q-ink-faint: #6f6a5b;
    --q-accent: #d4f03c;
    --q-accent-hover: #c3e02b;
    --q-accent-ink: #16140f;
    --q-accent-text: #5a6b12;
    --pri-high: #c9341a;
    --pri-medium: #8a6205;
    --pri-low: #5a6b12;
    --pri-none: #7d7666;

    /* Derived washes (auto-flip via var(--q-accent)) */
    --q-accent-soft: color-mix(in srgb, var(--q-accent) 12%, transparent);
    --q-accent-softer: color-mix(in srgb, var(--q-accent) 6%, transparent);

    /* A hairline one step softer than --q-line, for rules inside a block. */
    --q-line-soft: #e7e1d2;

    /* Radii — print has corners; the broadsheet register wants near-square edges. */
    --radius-xs: 1px;
    --radius-sm: 2px;
    --radius-md: 2px;
    --radius-lg: 3px;
    --radius-full: 999px;

    /* Shadows */
    --q-shadow-1: 0 1px 2px rgba(22, 20, 15, 0.07);
    --q-shadow-2: 0 8px 28px rgba(22, 20, 15, 0.13);

    /* Atmosphere */
    --q-grain-opacity: 0.055;
    --q-grain-blend: multiply;
    --q-edge-light: rgba(255, 255, 255, 0);
    /* Letterpress: type bitten into the sheet rather than floating over it. */
    --q-press: 0 1px 0 rgba(255, 255, 255, 0.55);
    /* The marker swipe: multiply on paper so ink reads through the highlighter. */
    --q-mark-blend: multiply;
    --q-mark-ink: inherit;

    /* Type */
    --q-font-display: "Fraunces", "Public Sans", Georgia, serif;
    --q-font-body: "Public Sans", system-ui, -apple-system, sans-serif;
    --q-font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Newsprint tooth — one tileable fractal-noise texture, laid over backgrounds at low opacity */
    --q-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='q'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23q)'/%3E%3C/svg%3E");
}

/* ── MudBlazor palette compatibility layer ──────────────────────────────────
 *
 * ~1,100 declarations across app.css and the scoped stylesheets read
 * --mud-palette-* names. Those used to be emitted at runtime by
 * MudThemeProvider; they are defined here instead, so the provider is no longer
 * what holds the app's colour together.
 *
 * Every one of these is expressed in terms of a --q-* token rather than a fixed
 * colour, which is what lets a single block serve both editions: the --q-* set
 * flips in the nocturne block below and these follow it down. The values match
 * what MudThemeProvider derived from DiurnalTheme, so nothing on screen moved.
 *
 * These names are kept only so the existing declarations keep working. Write
 * new CSS against the --q-* tokens; this layer is a bridge, not a palette.
 *
 * Only the names something actually reads are listed. --mud-palette-background-grey
 * is not among them: the two rules that asked for it were in view components that
 * had been unreachable since the view switcher was cut back, and both are now
 * deleted. (It would never have resolved anyway — MudBlazor spells it "gray".) */
:root {
    --mud-palette-primary: var(--q-accent);
    --mud-palette-primary-text: var(--q-accent-ink);
    --mud-palette-primary-hover: color-mix(in srgb, var(--q-accent) 6%, transparent);
    --mud-palette-primary-darken: #ceee1b;
    --mud-palette-secondary: var(--q-ink);
    --mud-palette-error: var(--pri-high);
    --mud-palette-info: #3c5a6e;

    --mud-palette-text-primary: var(--q-ink);
    --mud-palette-text-secondary: var(--q-ink-muted);
    --mud-palette-action-default: var(--q-ink-muted);
    --mud-palette-action-default-hover: color-mix(in srgb, var(--q-ink-muted) 6%, transparent);

    --mud-palette-surface: var(--q-surface);
    --mud-palette-background: var(--q-bg);
    --mud-palette-drawer-text: var(--q-ink);

    --mud-palette-divider: var(--q-line);
    --mud-palette-divider-light: var(--q-line);
    --mud-palette-lines-default: var(--q-line);
    --mud-palette-lines-inputs: var(--q-line);
    --mud-palette-table-lines: var(--q-line);

    /* The two that are not a --q-* token in disguise: MudBlazor mixed these
       against black and white rather than against the sheet. */
    --mud-palette-text-disabled: rgba(0, 0, 0, 0.376);
    --mud-palette-dark: #424242;
}

/* KEEP-IN-SYNC: diurnal-nocturne — app dark mode (DB-persisted toggle sets
 * .mud-theme-dark on the MudLayout). Tokens live on :root so portalized
 * popovers/dialogs inherit them.
 *
 * [data-theme="dark"] on <html> is the same switch thrown one element earlier:
 * App.razor stamps it from the persisted preference so the night tokens are in
 * force while the <head> is still parsing, before any shell markup has arrived
 * to be matched by :has(). window.slate.setTheme keeps it true after a toggle.
 *
 * The marker cannot simply invert: multiply against a dark sheet swallows the
 * chartreuse entirely, so at night the swipe becomes a solid fill with dark type
 * laid on it (--q-mark-blend / --q-mark-ink). */
:root:has(.mud-theme-dark),
:root[data-theme="dark"] {
    --q-bg: #131311;
    --q-surface: #1c1c18;
    --q-surface-raised: #232320;
    --q-line: #333229;
    --q-line-soft: #26251f;
    --q-ink: #ede9dc;
    --q-ink-muted: #b3ad9b;
    --q-ink-faint: #948e7c;
    --q-accent: #d4f03c;
    --q-accent-hover: #e2fc55;
    --q-accent-ink: #14140f;
    --q-accent-text: #c9e84a;
    --pri-high: #ff6b4a;
    --pri-medium: #e0a93b;
    --pri-low: #c9e84a;
    --pri-none: #6e6858;
    --q-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
    --q-shadow-2: 0 8px 28px rgba(0, 0, 0, 0.5);
    --q-grain-opacity: 0.16;
    --q-grain-blend: overlay;
    --q-edge-light: rgba(255, 255, 255, 0.05);
    --q-press: 0 1px 0 rgba(0, 0, 0, 0.45);
    --q-mark-blend: normal;
    --q-mark-ink: #14140f;

    /* The two compatibility values that do not ride a --q-* token down. */
    --mud-palette-text-disabled: rgba(255, 255, 255, 0.2);
    --mud-palette-dark: #27272f;
}

/* KEEP-IN-SYNC: diurnal-nocturne — static SSR auth pages render before the
 * MudThemeProvider exists, so they follow the OS scheme instead of the
 * .mud-theme-dark class. Must stay byte-for-byte equivalent to the block above;
 * ThemeTests compares the two token sets and fails on any drift. */
@media (prefers-color-scheme: dark) {
    :root:has(.auth-page) {
        --q-bg: #131311;
        --q-surface: #1c1c18;
        --q-surface-raised: #232320;
        --q-line: #333229;
        --q-line-soft: #26251f;
        --q-ink: #ede9dc;
        --q-ink-muted: #b3ad9b;
        --q-ink-faint: #948e7c;
        --q-accent: #d4f03c;
        --q-accent-hover: #e2fc55;
        --q-accent-ink: #14140f;
        --q-accent-text: #c9e84a;
        --pri-high: #ff6b4a;
        --pri-medium: #e0a93b;
        --pri-low: #c9e84a;
        --pri-none: #6e6858;
        --q-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
        --q-shadow-2: 0 8px 28px rgba(0, 0, 0, 0.5);
        --q-grain-opacity: 0.16;
        --q-grain-blend: overlay;
        --q-edge-light: rgba(255, 255, 255, 0.05);
        --q-press: 0 1px 0 rgba(0, 0, 0, 0.45);
        --q-mark-blend: normal;
        --q-mark-ink: #14140f;

        /* The two compatibility values that do not ride a --q-* token down. */
        --mud-palette-text-disabled: rgba(255, 255, 255, 0.2);
        --mud-palette-dark: #27272f;
    }
}

/* The paper is painted on the root element, not only on the sheet. A page load
 * streams the <head> well before the shell's markup arrives, and whatever the
 * browser paints in that window comes from here — with no background declared
 * it painted its own white canvas, which read as a flash on every refresh of
 * the night edition. color-scheme carries the same news to the scrollbars and
 * to the browser's own pre-paint canvas. */
html {
    -webkit-text-size-adjust: 100%;
    background: var(--q-bg);
    color-scheme: light;
}

:root:has(.mud-theme-dark),
:root[data-theme="dark"] {
    color-scheme: dark;
}

/* Static SSR auth pages have no theme attribute to read; they follow the OS. */
@media (prefers-color-scheme: dark) {
    :root:has(.auth-page) {
        color-scheme: dark;
    }
}

html, body {
    font-family: var(--q-font-body);
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}

body {
    overflow: hidden;
    touch-action: manipulation;
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--q-font-display);
    letter-spacing: -0.01em;
}

/* Links take the olive sibling, not the marker: chartreuse type on paper is 1.11:1. */
a, .btn-link {
    color: var(--q-accent-text);
}

/* ── Type scale ─────────────────────────────────────────────────────────────
 *
 * The app's own typography, replacing MudText. Sizes, weights, leading and
 * tracking are the values MudBlazor derived from the Typography block in
 * DiurnalTheme.cs, read back off the rendered page — so the swap moved nothing.
 *
 * Fraunces carries the headings and the two subtitles; Public Sans carries body
 * and caption. That split is the design's, not MudBlazor's, and it is why the
 * subtitles sit with the display face rather than the body face.
 *
 * Every one of these sets margin: 0 — MudText did too, and the surrounding
 * layouts were all built on the assumption that type brings no space with it. */
.q-h4, .q-h5, .q-h6, .q-subtitle1, .q-subtitle2 {
    font-family: var(--q-font-display);
    margin: 0;
}

.q-body1, .q-body2, .q-caption {
    font-family: var(--q-font-body);
    margin: 0;
}

.q-h4 {
    font-size: 2.125rem;
    font-weight: 600;
    line-height: 1.235;
    letter-spacing: -0.01em;
}

.q-h5 {
    font-size: 1.5rem;
    font-weight: 500;
    line-height: 1.334;
    letter-spacing: normal;
}

.q-h6 {
    font-size: 1.25rem;
    font-weight: 500;
    line-height: 1.6;
    letter-spacing: 0.0075em;
}

.q-subtitle1 {
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.75;
    letter-spacing: 0.00938em;
}

.q-subtitle2 {
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.57;
    letter-spacing: 0.00714em;
}

.q-body1 {
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    letter-spacing: 0.00938em;
}

.q-body2 {
    font-size: 0.875rem;
    font-weight: 400;
    line-height: 1.43;
    letter-spacing: 0.01071em;
}

.q-caption {
    font-size: 0.75rem;
    font-weight: 400;
    line-height: 1.66;
    letter-spacing: 0.03333em;
}

/* ── Glyphs ─────────────────────────────────────────────────────────────────
 *
 * The app's own icon and icon-button, replacing MudIcon and MudIconButton. The
 * numbers here are the ones MudBlazor's stylesheet resolved to, so the swap moved
 * nothing: a 24-unit viewBox sized in em, and a round 48px press target.
 *
 * Sizing is font-size on the glyph and 1em for the box, which is what lets a call
 * site override one icon with a single font-size rather than a width/height pair.
 *
 * Tones follow rule 1 above: --primary is the olive sibling, never the marker. */
.q-icon {
    display: inline-block;
    width: 1em;
    height: 1em;
    flex-shrink: 0;
    fill: currentColor;
    user-select: none;
}

.q-icon--sm { font-size: 1.25rem; }
.q-icon--md { font-size: 1.5rem; }
.q-icon--lg { font-size: 2.25rem; }

.q-icon--default   { color: var(--q-ink-muted); }
.q-icon--primary   { color: var(--q-accent-text); }
.q-icon--secondary { color: var(--q-ink); }
.q-icon--error     { color: var(--pri-high); }
.q-icon--warning   { color: var(--pri-medium); }
.q-icon--success   { color: var(--pri-low); }
.q-icon--info      { color: var(--mud-palette-info); }

.q-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    padding: 12px;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: var(--q-ink-muted);
    font-size: 1.5rem;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.q-icon-btn__label {
    display: flex;
}

.q-icon-btn:hover:not(:disabled) {
    background: var(--mud-palette-action-default-hover);
}

.q-icon-btn:disabled {
    color: var(--mud-palette-text-disabled);
    cursor: default;
}

.q-icon-btn--default   { color: var(--q-ink-muted); }
.q-icon-btn--primary   { color: var(--q-accent-text); }
.q-icon-btn--secondary { color: var(--q-ink); }
.q-icon-btn--error     { color: var(--pri-high); }
.q-icon-btn--warning   { color: var(--pri-medium); }
.q-icon-btn--success   { color: var(--pri-low); }
.q-icon-btn--info      { color: var(--mud-palette-info); }

/* ── Rule 1, enforced inside MudBlazor ──────────────────────────────────────
   MudBlazor spends --mud-palette-primary — the marker — directly on foregrounds:
   MudLink, text and outlined buttons, chips, tabs, nav items, icons. The rule
   above never reaches them, because .mud-primary-text carries !important. Every
   one of those is type or a meaningful glyph, so each is redirected to the olive
   sibling. Fills are deliberately absent from this list: .mud-theme-primary,
   filled buttons and filled chips keep the marker with accent-ink laid on it. */
.mud-primary-text,
.mud-button-text.mud-button-text-primary,
.mud-button-outlined.mud-button-outlined-primary,
.mud-button-group-text.mud-button-group-override-styles.mud-button-group-text-primary .mud-button-root,
.mud-button-group-outlined.mud-button-group-override-styles.mud-button-group-outlined-primary .mud-button-root,
.mud-chip-text.mud-chip-color-primary,
.mud-chip-outlined.mud-chip-color-primary,
.mud-avatar-outlined.mud-avatar-outlined-primary,
.mud-alert-text-primary .mud-alert-icon,
.mud-alert-outlined-primary .mud-alert-icon,
.mud-tab.mud-tab-active,
.mud-navmenu.mud-navmenu-default .mud-nav-link.active:not(.mud-nav-link-disabled),
.mud-navmenu.mud-navmenu-primary .mud-nav-link.active:not(.mud-nav-link-disabled),
.mud-navmenu.mud-navmenu-primary .mud-nav-link.active:not(.mud-nav-link-disabled) .mud-nav-link-icon,
.mud-treeview.mud-treeview-selected-primary .mud-treeview-item-content.mud-treeview-item-selected,
.mud-treeview.mud-treeview-checked-primary .mud-treeview-item-checkbox .mud-button-root.mud-icon-button,
.mud-input:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol {
    color: var(--q-accent-text) !important;
}

/* The one place the marker is correct as-is: a fill with ink laid on it. The
   border matches the fill so the button reads as a solid printed block. */
.btn-primary {
    color: var(--q-accent-ink);
    background-color: var(--q-accent);
    border-color: var(--q-accent);
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem var(--q-surface-raised), 0 0 0 0.25rem var(--q-accent-text);
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--pri-low);
}

.invalid {
    outline: 1px solid var(--pri-high);
}

.validation-message {
    color: var(--pri-high);
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: var(--q-ink-muted);
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ── Daybook brand mark (inline SVG, theme-aware) ─────────────
 * DaybookIcon.razor draws the mark from the global --q-* tokens above, so it
 * follows the edition on its own and needs no palette here. That is why the
 * three blocks of --slate-icon-* overrides this rule used to carry are gone:
 * they described the superseded Quarry stone (cool blue-gray body, tan letter,
 * 22% radius), none of which survives in Diurnal.
 *
 * No drop shadow either. The old mark was a stone and needed lifting off the
 * page; this one is a printed sheet carrying its own box rule, and a shadow
 * would break the flat print register. Corners come from the SVG's own rx,
 * which keeps them at the 2px register rather than a 22% pill.
 * ─────────────────────────────────────────────────────────── */
.slate-icon {
    display: block;
    flex-shrink: 0;
}


/*
 * Daybook task shell — global, loaded AFTER MudBlazor + scoped bundle (see App.razor).
 * Ensures TickTick-style flex layout and dark-theme fields even if scoped CSS order/loading fails.
 */

/* KEEP-IN-SYNC: TaskRow.razor.css — this global twin loads after the scoped
 * bundle and ties on specificity, so it WINS. Any row change must be made in
 * both files or the scoped one silently has no effect. */
.tasks-view .task-row {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: nowrap !important;
    /* Centred, not top-aligned — see the note on .task-row in TaskRow.razor.css.
       The row is a single line, so top alignment only left each part hanging by
       its own height, and the compensating margins stopped adding up once the
       actions box grew to 40px on touch. */
    align-items: center !important;
    gap: 14px;
    padding: 11px 16px;
    border-bottom: 1px solid var(--q-line);
    border-radius: 0;
    margin: 0;
    cursor: pointer;
    min-height: 0;
    box-sizing: border-box;
}

/* No row tint on hover — the marker sweeping across the title is the signal. */
.tasks-view .task-row:hover {
    background: transparent;
}

.tasks-view .task-row--selected {
    background: var(--q-accent-soft) !important;
}

.tasks-view .task-row--completed {
    opacity: 0.55;
}

/* Strike is drawn as an animated gradient line in TaskRow.razor.css (the sweep);
 * this global twin only needs to not re-impose a static text-decoration. */
.tasks-view .task-row--completed .task-row__title {
    text-decoration: none;
}

.tasks-view .task-row__check {
    flex-shrink: 0 !important;
    display: flex !important;
    align-items: center !important;
    cursor: pointer;
}

.tasks-view .task-row__body {
    flex: 1 1 auto !important;
    min-width: 0 !important;
    display: flex !important;
    flex-direction: row !important;
    /* Puts the priority dot on the title's centre line rather than hanging it
       from the cap height and pushing it back down with a margin. */
    align-items: center !important;
    gap: 10px;
}

.tasks-view .task-row__title {
    font-size: 0.98rem;
    line-height: 1.45;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tasks-view .task-row__right {
    display: flex !important;
    flex-direction: row !important;
    align-items: center !important;
    gap: 10px;
    flex-shrink: 0 !important;
}

.tasks-view .task-row__actions {
    flex-shrink: 0 !important;
    opacity: 0;
    transition: opacity 0.12s;
}

.tasks-view .task-row:hover .task-row__actions {
    opacity: 1;
}

/* Touch devices: no hover — keep delete affordance visible */
@media (hover: none) and (pointer: coarse) {
    .tasks-view .task-row__actions {
        opacity: 1;
    }
}

/* Detail panel: full flex column + no white native form controls */
.tasks-view__detail-col .detail-panel {
    display: flex !important;
    flex-direction: column !important;
    flex: 1 1 auto !important;
    min-height: 0 !important;
    height: 100% !important;
    max-height: 100% !important;
    background: transparent !important;
}

.tasks-view__detail-col .detail-panel__main {
    display: flex !important;
    flex-direction: column !important;
    flex: 1 1 auto !important;
    min-height: 0 !important;
    height: 100% !important;
}

.tasks-view__detail-col .detail-panel__meta {
    display: flex !important;
    flex-direction: row !important;
    align-items: center !important;
    justify-content: space-between !important;
    flex-shrink: 0 !important;
    min-height: 38px !important;
    padding: 6px 10px 6px 16px !important;
    border-bottom: 1px solid var(--mud-palette-divider) !important;
}

.tasks-view__detail-col .detail-panel__meta-due {
    flex: 0 1 auto !important;
    width: auto !important;
    padding: 3px 5px !important;
    border: none !important;
    background: transparent !important;
    border-radius: 6px !important;
}

.tasks-view__detail-col .detail-panel__meta-due:hover {
    background: var(--mud-palette-action-default-hover) !important;
}

.tasks-view__detail-col .detail-panel__meta-due-text {
    font-size: 0.78rem !important;
    color: var(--mud-palette-text-secondary) !important;
}

.tasks-view__detail-col .detail-panel__meta-due-icon {
    color: var(--mud-palette-text-secondary) !important;
    opacity: 0.7 !important;
}

.tasks-view__detail-col .detail-panel__title-row {
    padding: 18px 10px 18px 16px !important;
}

.tasks-view__detail-col .detail-panel__title-input {
    font-size: 1.02rem !important;
    font-weight: 700 !important;
    line-height: 1.35 !important;
}

.tasks-view__detail-col .detail-panel__scroll {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    overflow-y: auto !important;
}

.tasks-view__detail-col .detail-panel__toolbar {
    display: flex !important;
    flex-direction: row !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    gap: 4px;
    flex-shrink: 0 !important;
    min-height: 38px;
    padding: 5px 10px;
    border-top: 1px solid var(--mud-palette-divider);
    background: var(--mud-palette-surface);
}

.tasks-view__detail-col .detail-panel__add-subtask-row {
    display: flex !important;
    flex-direction: row !important;
    align-items: center !important;
    gap: 7px;
    min-height: 36px;
    min-width: 0;
    border-bottom: 1px solid var(--mud-palette-divider);
    opacity: 0.62;
}

.tasks-view__detail-col .detail-panel__subtask-input {
    flex: 1 1 auto !important;
    min-width: 0 !important;
}

.tasks-view__detail-col textarea,
.tasks-view__detail-col input.detail-panel__title-input,
.tasks-view__detail-col input.detail-panel__subtask-input,
.tasks-view__detail-col input.detail-panel__comment-input {
    box-sizing: border-box !important;
    width: 100% !important;
    max-width: 100% !important;
    appearance: none !important;
    -webkit-appearance: none !important;
    border: none !important;
    outline: none !important;
    box-shadow: none !important;
    background: transparent !important;
    background-color: transparent !important;
    color: var(--mud-palette-text-primary, #333) !important;
}

.tasks-view__detail-col textarea.detail-panel__notes {
    min-height: 42px;
    resize: vertical;
    color: var(--mud-palette-text-secondary, rgba(0, 0, 0, 0.65)) !important;
}

/* Newsprint tooth on the auth pages. The app's own sheet draws its grain from
 * BroadsheetShell.razor.css, which owns the layout root. */
.auth-page::before {
    content: "";
    position: fixed;
    inset: 0;
    background-image: var(--q-grain);
    background-size: 140px 140px;
    opacity: var(--q-grain-opacity);
    pointer-events: none;
    z-index: 1;
}

@media (max-width: 767.98px) {
    .tasks-view__list-scroll {
        padding-bottom: max(28px, env(safe-area-inset-bottom, 0px));
    }

    .tasks-view__add-row {
        margin-left: 12px;
        margin-right: 12px;
        padding: 12px 16px;
        min-height: 48px;
    }

    .tasks-view__view-switcher .mud-icon-button {
        width: 42px !important;
        height: 42px !important;
        min-width: 42px !important;
    }

    .tasks-view__group-header {
        padding: 12px 16px 8px;
    }

    .tasks-view .task-row {
        min-height: 0;
        padding: 13px 14px;
        margin-left: 0;
        margin-right: 0;
    }

    .tasks-view .task-row__title {
        font-size: 0.97rem;
    }

    .tasks-view__detail-col .detail-panel__title-input {
        font-size: 16px !important;
    }

    .tasks-view__detail-col textarea,
    .tasks-view__detail-col input.detail-panel__subtask-input,
    .tasks-view__detail-col input.detail-panel__comment-input {
        font-size: 16px !important;
    }

    .tasks-view__detail-col .detail-panel__toolbar .mud-button-root {
        min-height: 40px;
    }
}

.tasks-view {
    background: var(--mud-palette-background);
}

.tasks-view__add-row {
    border-bottom: none;
    background: transparent;
    border: 1.5px dashed var(--q-line);
    border-radius: 10px;
    margin: 4px 16px 12px;
    padding: 10px 14px;
}

.tasks-view__add-row:hover {
    border-color: color-mix(in srgb, var(--q-accent) 45%, var(--q-line));
    background: var(--q-accent-softer);
}

.tasks-view__add-row:focus-within {
    background: var(--mud-palette-surface);
    border-color: transparent;
    border-style: solid;
    box-shadow: 0 0 0 1.5px var(--q-accent-text) inset;
}

.tasks-view__view-switcher .mud-icon-button {
    width: 28px;
    height: 28px;
    padding: 4px;
}

.tasks-view__group-header {
    padding: 14px 22px 6px;
    background: transparent;
    border-bottom: none;
    position: static;
}

.tasks-view__group-count {
    background: transparent !important;
    padding: 0 4px !important;
    color: var(--q-ink-faint);
    font-weight: 500;
}

.tasks-view .task-row__title {
    color: var(--mud-palette-text-primary);
}

/* Marginalia sits quieter than body copy — ink-faint, not text-secondary. */
.tasks-view .task-row__date,
.tasks-view .task-row__list-name,
.tasks-view .task-row__subtask-badge,
.tasks-view .task-row__reminder {
    color: var(--q-ink-faint);
}

/* ── Account Management Settings (SSR — global) ──
 * Uses --mud-palette-* CSS variables emitted by MudThemeProvider's <style> tag.
 * These are present in the server-rendered HTML for both SSR and interactive pages,
 * so they correctly track the user's chosen Daybook theme (not the OS preference).
 * No prefers-color-scheme media queries needed.
 * ─────────────────────────────────────────────── */
.mgmt-shell {
    display: flex;
    flex-direction: column;
    padding-bottom: 48px;
    font-family: var(--q-font-body);
}

.mgmt-header {
    padding: 32px 36px 0;
}

.mgmt-header__eyebrow {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--q-accent-text);
    margin: 0 0 8px;
}

.mgmt-header__title {
    font-size: 1.6rem;
    font-weight: 700;
    letter-spacing: -0.025em;
    color: var(--mud-palette-text-primary, var(--q-ink));
    margin: 0 0 4px;
    line-height: 1.2;
}

.mgmt-header__subtitle {
    font-size: 0.85rem;
    color: var(--mud-palette-text-secondary, #666);
    margin: 0;
}

.mgmt-divider {
    border: none;
    border-top: 1px solid var(--mud-palette-divider, rgba(0,0,0,0.08));
    margin: 20px 36px 0;
}

.mgmt-body {
    display: flex;
    gap: 24px;
    padding: 28px 36px;
    align-items: flex-start;
}

/* Sidebar nav */
.mgmt-nav {
    width: 192px;
    flex-shrink: 0;
    position: sticky;
    top: 24px;
}

.mgmt-nav__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.mgmt-nav__link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-radius: 9px;
    font-size: 0.855rem;
    font-weight: 500;
    color: var(--mud-palette-text-secondary, #555);
    text-decoration: none;
    transition: background 0.12s, color 0.12s;
    line-height: 1;
}

.mgmt-nav__link:hover {
    background: var(--mud-palette-action-default-hover, rgba(92,107,192,0.08));
    color: var(--q-accent-text);
}

.mgmt-nav__link.active {
    background: color-mix(in srgb, var(--q-accent) 12%, transparent);
    color: var(--q-accent-text);
    font-weight: 600;
}

.mgmt-nav__icon {
    font-size: 17px;
    opacity: 0.55;
    flex-shrink: 0;
    transition: opacity 0.12s;
}

.mgmt-nav__link:hover .mgmt-nav__icon,
.mgmt-nav__link.active .mgmt-nav__icon {
    opacity: 1;
}

/* Content card */
.mgmt-content {
    flex: 1;
    min-width: 0;
    max-width: 600px;
    background: var(--mud-palette-surface, #fff);
    border-radius: 14px;
    border: 1px solid var(--mud-palette-divider, rgba(0,0,0,0.07));
    box-shadow: 0 1px 8px rgba(0,0,0,0.06);
    padding: 28px 32px;
}

.mgmt-content__section-title {
    font-size: 1rem;
    font-weight: 700;
    color: var(--mud-palette-text-primary, var(--q-ink));
    margin: 0 0 22px;
    letter-spacing: -0.01em;
    display: flex;
    align-items: center;
    gap: 8px;
}

.mgmt-content__section-title .material-icons {
    font-size: 20px;
    color: var(--q-accent-text);
    opacity: 0.85;
}

/* Form fields */
.mgmt-field {
    margin-bottom: 20px;
}

.mgmt-field:last-of-type {
    margin-bottom: 0;
}

.mgmt-label {
    display: block;
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--mud-palette-text-secondary, #888);
    margin-bottom: 7px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.mgmt-input {
    width: 100%;
    padding: 10px 14px;
    font-size: 0.9rem;
    font-family: inherit;
    border: 1.5px solid var(--mud-palette-divider, rgba(0,0,0,0.12));
    border-radius: 9px;
    outline: none;
    background: var(--mud-palette-background, var(--q-bg));
    color: var(--mud-palette-text-primary, var(--q-ink));
    transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
    box-sizing: border-box;
    line-height: 1.4;
}

.mgmt-input:focus {
    border-color: var(--q-accent-text);
    background: var(--mud-palette-surface, #fff);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--q-accent) 14%, transparent);
}

.mgmt-input:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.mgmt-input::placeholder {
    color: var(--mud-palette-text-disabled, #bbb);
}

.mgmt-validation {
    display: block;
    font-size: 0.78rem;
    color: var(--mud-palette-error, var(--pri-high));
    margin-top: 5px;
}

.mgmt-hint {
    font-size: 0.78rem;
    color: var(--mud-palette-text-secondary, #888);
    margin: 6px 0 0;
    line-height: 1.35;
}

/* Alerts — mgmt classes + Bootstrap .alert overrides within the card */
.mgmt-alert,
.mgmt-content .alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 16px;
    border-radius: 9px;
    font-size: 0.85rem;
    margin-bottom: 22px;
    line-height: 1.5;
    border: 1px solid transparent;
}

.mgmt-alert--error,
.mgmt-content .alert-danger {
    background: color-mix(in srgb, var(--pri-high) 10%, transparent);
    color: var(--mud-palette-error, var(--pri-high));
    border-color: color-mix(in srgb, var(--pri-high) 28%, transparent);
}

.mgmt-alert--success,
.mgmt-content .alert-success {
    background: color-mix(in srgb, var(--pri-low) 12%, transparent);
    color: var(--pri-low);
    border-color: color-mix(in srgb, var(--pri-low) 28%, transparent);
}

.mgmt-alert--progress {
    background: var(--q-accent-soft);
    color: var(--q-accent-text);
    border-color: color-mix(in srgb, var(--q-accent) 28%, transparent);
}

/* Buttons */
.mgmt-actions {
    display: flex;
    gap: 10px;
    margin-top: 26px;
    padding-top: 22px;
    border-top: 1px solid var(--mud-palette-divider, rgba(0,0,0,0.06));
}

.mgmt-btn {
    padding: 10px 22px;
    font-size: 0.875rem;
    font-weight: 600;
    font-family: inherit;
    border-radius: 9px;
    border: none;
    cursor: pointer;
    transition: opacity 0.15s, transform 0.1s, box-shadow 0.15s;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    text-decoration: none;
}

.mgmt-btn:active { transform: scale(0.985); }

.mgmt-btn--primary {
    background: var(--q-accent-text);
    color: #fff;
    box-shadow: 0 2px 8px color-mix(in srgb, var(--q-accent) 28%, transparent);
}

.mgmt-btn--primary:hover {
    opacity: 0.9;
    box-shadow: 0 4px 14px color-mix(in srgb, var(--q-accent) 38%, transparent);
}

.mgmt-btn--ghost {
    background: transparent;
    color: var(--q-accent-text);
    border: 1.5px solid var(--mud-palette-divider, rgba(92,107,192,0.25));
}

.mgmt-btn--ghost:hover {
    background: var(--mud-palette-action-default-hover, rgba(92,107,192,0.06));
    border-color: var(--q-accent-text);
}

.mgmt-btn--danger {
    background: transparent;
    color: var(--mud-palette-error, var(--pri-high));
    border: 1.5px solid rgba(198, 40, 40, 0.3);
}

.mgmt-btn--danger:hover {
    background: rgba(198, 40, 40, 0.06);
    border-color: var(--mud-palette-error, var(--pri-high));
}

.notification-settings {
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.notification-settings__hero {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 16px;
    align-items: flex-start;
    padding: 18px;
    border-radius: 16px;
    background: linear-gradient(135deg, color-mix(in srgb, var(--q-accent) 12%, transparent), color-mix(in srgb, var(--q-accent) 5%, transparent));
    border: 1px solid color-mix(in srgb, var(--q-accent) 18%, transparent);
}

.notification-settings__status {
    width: 48px;
    height: 48px;
    border-radius: 16px;
    display: grid;
    place-items: center;
    color: #fff;
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
}

.notification-settings__status .material-icons {
    font-size: 25px;
}

.notification-settings__status--success {
    background: linear-gradient(135deg, var(--pri-low), color-mix(in srgb, var(--pri-low) 60%, white));
}

.notification-settings__status--warning {
    background: linear-gradient(135deg, var(--pri-medium), color-mix(in srgb, var(--pri-medium) 60%, white));
}

.notification-settings__status--neutral {
    background: linear-gradient(135deg, var(--q-accent), color-mix(in srgb, var(--q-accent) 60%, white));
}

.notification-settings__eyebrow {
    margin: 0 0 4px;
    color: var(--q-accent-text);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.notification-settings__hero h3,
.notification-settings__guide h3 {
    margin: 0 0 6px;
    color: var(--mud-palette-text-primary, var(--q-ink));
    font-size: 1.05rem;
    letter-spacing: -0.01em;
}

.notification-settings__hero p,
.notification-settings__guide p {
    margin: 0;
    color: var(--mud-palette-text-secondary, #666);
    font-size: 0.88rem;
    line-height: 1.5;
}

.notification-settings__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.notification-settings__actions .mgmt-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    box-shadow: none;
}

.notification-settings__facts {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    margin: 0;
}

.notification-settings__facts div {
    padding: 14px;
    border-radius: 13px;
    background: var(--mud-palette-background, var(--q-bg));
    border: 1px solid var(--mud-palette-divider, rgba(0,0,0,0.07));
}

.notification-settings__facts dt {
    margin-bottom: 4px;
    color: var(--mud-palette-text-secondary, #777);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.notification-settings__facts dd {
    margin: 0;
    color: var(--mud-palette-text-primary, var(--q-ink));
    font-size: 0.9rem;
    font-weight: 600;
}

.notification-settings__guide {
    padding: 18px;
    border-radius: 14px;
    background: var(--mud-palette-surface, #fff);
    border: 1px dashed color-mix(in srgb, var(--q-accent) 36%, transparent);
}

.notification-settings__guide ol {
    margin: 12px 0 0;
    padding-left: 20px;
    color: var(--mud-palette-text-primary, var(--q-ink));
    font-size: 0.88rem;
    line-height: 1.6;
}

@media (max-width: 720px) {
    .mgmt-input {
        font-size: 16px;
        padding: 12px 14px;
    }
    .mgmt-btn {
        min-height: 44px;
        padding: 12px 20px;
    }
    .mgmt-nav__link {
        min-height: 44px;
        padding: 12px 14px;
    }
    .mgmt-body {
        flex-direction: column;
        padding: 16px 16px max(32px, env(safe-area-inset-bottom, 0px));
        gap: 16px;
    }
    .mgmt-nav {
        width: 100%;
        position: static;
    }
    .mgmt-nav__list {
        flex-direction: row;
        flex-wrap: wrap;
    }
    .mgmt-header {
        padding: 20px 16px 0;
    }
    .mgmt-divider {
        margin: 16px 16px 0;
    }
    .mgmt-content {
        padding: 20px;
    }
    .notification-settings__hero,
    .notification-settings__facts {
        grid-template-columns: 1fr;
    }
}

/*
 * iOS Safari auto-zooms when a focused input/textarea has computed font-size below 16px.
 * Phone landscape often exceeds 768px width, so max-width-only rules miss those viewports.
 * Touch-primary devices get 16px on form fields regardless of width (desktop mouse UIs unchanged).
 */
@media (hover: none) and (pointer: coarse) {
    .tasks-view__add-input,
    .cmd-input,
    .time-slot-picker__field-input,
    .reminder-panel__custom-input,
    .tasks-view__detail-col .detail-panel__title-input,
    .tasks-view__detail-col textarea,
    .tasks-view__detail-col input.detail-panel__subtask-input,
    .tasks-view__detail-col input.detail-panel__comment-input {
        font-size: 16px !important;
    }

    .mud-input-control .mud-input-slot input,
    .mud-input-control .mud-input-slot textarea {
        font-size: 16px !important;
    }
}

/* Week calendar resize preview (appended by week-cal-resize.js — outside Blazor scoped CSS) */
.week-cal__block-resize-ghost {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    overflow: hidden;
    border-radius: 6px;
    border: 1px solid color-mix(in srgb, var(--q-accent) 40%, transparent);
    border-left: 3px solid var(--q-accent-text);
    background: color-mix(in srgb, var(--q-accent) 16%, var(--q-surface));
    color: var(--q-ink);
    box-shadow: var(--q-shadow-2);
}

.week-cal__block-resize-ghost.week-cal__p-high {
    border-left-color: var(--pri-high);
    background: color-mix(in srgb, var(--pri-high) 18%, var(--q-surface));
}

.week-cal__block-resize-ghost.week-cal__p-medium {
    border-left-color: var(--pri-medium);
    background: color-mix(in srgb, var(--pri-medium) 16%, var(--q-surface));
}

.week-cal__block-resize-ghost.week-cal__p-low {
    border-left-color: var(--pri-low);
    background: color-mix(in srgb, var(--pri-low) 14%, var(--q-surface));
}

.week-cal__block-resize-ghost .week-cal__block-row {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: row;
    align-items: stretch;
    padding: 4px 0;
}

.week-cal__block-resize-ghost .week-cal__block-grip {
    flex: 0 0 18px;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 2px;
    color: rgba(0, 0, 0, 0.38);
    opacity: 0.55;
}

.week-cal__block-resize-ghost .mud-icon-root {
    font-size: 14px !important;
    width: 14px !important;
    height: 14px !important;
}

.mud-theme-dark .week-cal__block-resize-ghost .week-cal__block-grip {
    color: rgba(255, 255, 255, 0.45);
}

.week-cal__block-resize-ghost .week-cal__block {
    flex: 1 1 0;
    min-width: 0;
    margin: 0;
    padding: 2px 6px 2px 0;
    border: 0;
    background: transparent;
    text-align: left;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 2px;
    font: inherit;
    color: inherit;
    cursor: default;
    overflow: hidden;
}

.week-cal__block-resize-ghost .week-cal__block--dense-move {
    flex-direction: row !important;
    align-items: center !important;
    gap: 8px !important;
}

.week-cal__block-resize-ghost .week-cal__block-title {
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    width: 100%;
}

.week-cal__block-resize-ghost .week-cal__block-time {
    font-size: 0.66rem;
    font-weight: 500;
    opacity: 0.82;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════════════════
 * THE NEWSSTAND COPY — the sign-in sheet (Components/Account/Pages/Login.razor)
 *
 * The app's only shell is a broadsheet, so its front door is the same sheet with
 * nothing on it yet: folio line, masthead, double rule, front page in two
 * columns. The right column is a coupon, printed on its own scrap and clipped
 * out along a dashed cut line — filling it in is how you sign in.
 *
 * GLOBAL AND UNLAYERED ON PURPOSE (two reasons, both load-bearing):
 *  1. The fields are rendered by <InputText>/<InputCheckbox>. CSS isolation
 *     stamps its scope attribute only on markup the component itself writes, so
 *     a Login.razor.css would never reach those inputs.
 *  2. MudBlazor.min.css is unlayered and normalizes raw input/button. Layered
 *     rules lose to it regardless of specificity; these class selectors, being
 *     unlayered, out-specify its element selectors and win.
 *
 * The page carries `auth-page` alongside `newsstand`: that class is what flips
 * the token table to Nocturne off the OS scheme on static SSR pages, and what
 * lays the newsprint grain over the sheet. Do not drop it.
 *
 * Every load animation's BASE rule is its finished state and the keyframes run
 * from the hidden one, so `animation: none` under reduced motion leaves the page
 * fully composed rather than blank.
 * ═══════════════════════════════════════════════════════════════════════════ */

/* body sets overflow:hidden for the app shell, so this element owns the scroll
   the same way .bs does. */
.newsstand {
    height: 100dvh;
    overflow-y: auto;
    box-sizing: border-box;
    padding: 16px 28px 40px;
    padding-left: max(28px, env(safe-area-inset-left, 0px));
    padding-right: max(28px, env(safe-area-inset-right, 0px));
    background: var(--q-bg);
    color: var(--q-ink);
    font-family: var(--q-font-body);
    display: flex;
    flex-direction: column;
}

/* `margin-block: auto` centers the sheet on a tall screen and still lets it
   scroll from the top when it overflows a short one — `justify-content: center`
   would clip the masthead off the top edge instead. */
.newsstand__sheet {
    width: 100%;
    max-width: 1180px;
    margin: auto;
}

@keyframes ns-rise {
    from { opacity: 0; transform: translateY(10px); }
}

@keyframes ns-wipe {
    from { transform: scaleX(0); }
}

/* ── Folio line ─────────────────────────────────────────────────────────── */
.newsstand__folio {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--q-line);
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
    animation: ns-rise 0.5s ease both;
}

.newsstand__folio-issue {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* ── Masthead ───────────────────────────────────────────────────────────── */
.newsstand__mast {
    padding: clamp(18px, 3.2vw, 34px) 0 clamp(14px, 2vw, 20px);
    text-align: center;
}

/* Fraunces is loaded with its SOFT and WONK axes (see App.razor). The masthead
   is the one place that spends them: WONK swaps in the flared alternates and
   opsz 144 is the display cut, giving a wordmark no UI face can imitate. */
.newsstand__wordmark {
    margin: 0;
    font-family: var(--q-font-display);
    font-weight: 600;
    font-size: clamp(4.2rem, 15.5vw, 11rem);
    font-variation-settings: "opsz" 144, "SOFT" 0, "WONK" 1;
    letter-spacing: -0.045em;
    line-height: 0.82;
    color: var(--q-ink);
    text-shadow: var(--q-press);
    animation: ns-rise 0.62s cubic-bezier(0.22, 0.9, 0.3, 1) 0.06s both;
}

.newsstand__creed {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
    margin: clamp(10px, 1.6vw, 16px) 0 0;
    font-family: var(--q-font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
    animation: ns-rise 0.5s ease 0.16s both;
}

/* Hairlines running out to the trim, the way a strapline is set under a
   masthead. They are the width the flex row leaves them. */
.newsstand__creed::before,
.newsstand__creed::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--q-line);
}

.newsstand__creed-date {
    color: var(--q-ink-muted);
}

.newsstand__creed-date::before {
    content: "\00b7";
    margin-right: 14px;
    color: var(--q-line);
}

/* The masthead close: heavy over hair, with a paper gap between (as .bs). */
.newsstand__rule-double {
    height: 4px;
    border-top: 3px solid var(--q-ink);
    border-bottom: 1px solid var(--q-ink);
    transform-origin: left center;
    animation: ns-wipe 0.6s cubic-bezier(0.22, 0.9, 0.3, 1) 0.22s both;
}

/* ── The front page ─────────────────────────────────────────────────────── */
.newsstand__cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 384px;
    margin-top: clamp(24px, 3vw, 38px);
}

.newsstand__leader {
    min-width: 0;
    padding-right: clamp(28px, 4vw, 48px);
    border-right: 1px solid var(--q-line);
    animation: ns-rise 0.6s ease 0.32s both;
}

.newsstand__kicker {
    margin: 0 0 10px;
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--q-accent-text);
}

.newsstand__head {
    position: relative;
    /* Contains the marker's negative z-index so the swipe stays above the paper
       and below the type instead of dropping behind the sheet. */
    isolation: isolate;
    margin: 0 0 18px;
    font-family: var(--q-font-display);
    font-weight: 600;
    font-size: clamp(1.9rem, 3.4vw, 2.9rem);
    line-height: 1.05;
    letter-spacing: -0.025em;
    color: var(--q-ink);
    text-shadow: var(--q-press);
}

/* The marker — the app's one attention device, borrowed from the task row:
   skewed, ragged at both ends, multiplied on paper so the ink reads through it.
   Here it sweeps once on load rather than on hover, and lands last. Nocturne
   flips the blend to a solid fill with dark type (--q-mark-blend/--q-mark-ink). */
.newsstand__mark {
    position: relative;
    color: var(--q-mark-ink);
    animation: ns-mark-ink 0.45s ease 0.78s both;
}

.newsstand__mark::before {
    content: "";
    position: absolute;
    z-index: -1;
    left: -0.28em;
    right: -0.4em;
    top: 0.04em;
    bottom: 0.02em;
    background: var(--q-accent);
    mix-blend-mode: var(--q-mark-blend);
    clip-path: polygon(0 6%, 100% 0, 99.4% 92%, 0.6% 100%);
    transform: skewX(-3.5deg);
    transform-origin: left center;
    animation: ns-mark 0.52s cubic-bezier(0.22, 0.9, 0.3, 1) 0.7s both;
}

@keyframes ns-mark {
    from { transform: skewX(-3.5deg) scaleX(0); }
}

@keyframes ns-mark-ink {
    from { color: var(--q-ink); }
}

/* Two columns of body copy at broadsheet width — the reason the leader exists. */
.newsstand__lead {
    font-size: 0.95rem;
    line-height: 1.62;
    color: var(--q-ink-muted);
}

.newsstand__lead p {
    margin: 0 0 0.9em;
}

.newsstand__lead p:last-child {
    margin-bottom: 0;
}

/* A printed drop cap: three lines deep, in the display face, set tight to the
   text block on both sides. */
.newsstand__lead > p:first-child::first-letter {
    float: left;
    margin: 0.06em 0.08em 0 0;
    font-family: var(--q-font-display);
    font-weight: 600;
    font-size: 3.1em;
    line-height: 0.78;
    color: var(--q-ink);
    text-shadow: var(--q-press);
}

.newsstand__notices {
    margin-top: clamp(22px, 3vw, 32px);
    padding-top: 16px;
    border-top: 1px solid var(--q-line-soft);
}

.newsstand__notice-list {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 9px;
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--q-ink-muted);
}

.newsstand__notice-list li {
    display: flex;
    gap: 12px;
    align-items: baseline;
}

/* Hanging roman numerals, as a leader note is set. */
.newsstand__notice-num {
    flex: 0 0 20px;
    font-family: var(--q-font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
}

/* ── The coupon ─────────────────────────────────────────────────────────── */
.newsstand__coupon-well {
    padding-left: clamp(28px, 4vw, 48px);
    animation: ns-clip 0.62s cubic-bezier(0.22, 0.9, 0.3, 1) 0.4s both;
}

@keyframes ns-clip {
    from { opacity: 0; transform: translateY(-14px) rotate(-2.2deg); }
}

/* A scrap of the sheet, cut out and laid back down: dashed trim for the cut
   line, a hair off square, and a shadow so it sits on the paper rather than in
   it. Square corners — this is print, not a dialog. */
.newsstand__coupon {
    position: relative;
    padding: 30px 28px 26px;
    border: 2px dashed var(--q-line);
    background: var(--q-surface-raised);
    box-shadow: var(--q-shadow-2);
    transform: rotate(-0.45deg);
}

/* Rides the corner inside the cut line. An earlier pass sat it ON the line,
   which needs a swatch of card behind the glyph to break the dashes — and that
   swatch read as a white tab stuck to the top edge, worst of all on a phone
   where the coupon runs to the trim. The dashed trim already says "coupon"; the
   scissors only has to confirm it. */
.newsstand__snip {
    position: absolute;
    top: 11px;
    right: 13px;
    display: inline-flex;
    color: var(--q-ink-faint);
    opacity: 0.7;
}

.newsstand__coupon-kicker {
    margin: 0 0 4px;
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
}

.newsstand__coupon-title {
    margin: 0 0 22px;
    font-family: var(--q-font-display);
    font-weight: 600;
    font-size: 1.75rem;
    letter-spacing: -0.025em;
    line-height: 1;
    color: var(--q-ink);
}

/* Vermilion means late, and nothing else — on a form the paper's own word for a
   thing gone wrong is a correction notice, so the alarm is spent on that. */
.newsstand__erratum {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-bottom: 20px;
    padding: 9px 12px;
    border-left: 3px solid var(--pri-high);
    background: color-mix(in srgb, var(--pri-high) 9%, transparent);
    font-size: 0.84rem;
    line-height: 1.45;
    color: var(--pri-high);
}

.newsstand__erratum-tag {
    flex-shrink: 0;
    font-family: var(--q-font-mono);
    font-size: 0.58rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}

/* ── Fields: ruled lines on a form, not boxes ───────────────────────────── */
.newsstand__field {
    margin-bottom: 19px;
}

.newsstand__label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.newsstand__label {
    display: block;
    margin: 0 0 3px;
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
    transition: color 150ms ease;
}

.newsstand__field:focus-within .newsstand__label {
    color: var(--q-accent-text);
}

.newsstand__label-link {
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
    text-decoration: none;
}

.newsstand__label-link:hover {
    color: var(--q-accent-text);
    text-decoration: underline;
}

.newsstand__input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 7px 2px 8px;
    border: 0;
    border-bottom: 1.5px solid var(--q-line);
    border-radius: 0;
    background: transparent;
    font-family: var(--q-font-body);
    /* 16px — anything smaller and iOS Safari zooms the focused field. */
    font-size: 1rem;
    line-height: 1.35;
    color: var(--q-ink);
    outline: none;
    transition: border-color 150ms ease, box-shadow 150ms ease;
}

.newsstand__input::placeholder {
    color: var(--q-ink-faint);
    opacity: 0.65;
}

/* The rule darkens and doubles rather than growing a focus ring — a pen pressed
   harder on the same line. box-shadow, not border-width, so nothing reflows. */
.newsstand__input:focus {
    border-bottom-color: var(--q-accent-text);
    box-shadow: 0 1.5px 0 0 var(--q-accent-text);
}

/* Chrome paints autofilled fields with its own opaque background, which on a
   transparent ruled input shows up as a block sitting on the coupon. */
.newsstand__input:-webkit-autofill,
.newsstand__input:-webkit-autofill:hover,
.newsstand__input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--q-ink);
    -webkit-box-shadow: inset 0 0 0 100px var(--q-surface-raised);
    caret-color: var(--q-ink);
}

.newsstand__validation {
    display: block;
    margin-top: 5px;
    font-size: 0.76rem;
    color: var(--pri-high);
}

/* Blazor stamps .invalid on failed fields; app.css gives that a full outline,
   which on a ruled field boxes in the one control that has no box. */
.newsstand__input.invalid {
    outline: none;
    border-bottom-color: var(--pri-high);
}

/* ── Keep-this-copy ─────────────────────────────────────────────────────── */
.newsstand__keep {
    display: flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 22px;
    font-size: 0.84rem;
    color: var(--q-ink-muted);
    cursor: pointer;
    user-select: none;
}

/* A printed tick box: square, ruled, and filled with the marker when set —
   accent as a fill with accent-ink laid on it, never as the tick's own color. */
.newsstand__check {
    appearance: none;
    -webkit-appearance: none;
    flex-shrink: 0;
    width: 15px;
    height: 15px;
    margin: 0;
    padding: 0;
    border: 1.5px solid var(--q-ink-faint);
    border-radius: 0;
    background: transparent;
    background-position: center;
    background-repeat: no-repeat;
    background-size: 11px 11px;
    cursor: pointer;
    transition: background-color 130ms ease, border-color 130ms ease;
}

.newsstand__check:checked {
    border-color: var(--q-accent);
    background-color: var(--q-accent);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2316140f' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

.newsstand__check:focus-visible {
    outline: 2px solid var(--q-accent-text);
    outline-offset: 2px;
}

/* Small print on the coupon — what happens after it is sent. Deliberately has no
   rule of its own: it follows a ruled field, and a border here landed a second
   hairline a few pixels under the field's own, reading as a printing fault. */
.newsstand__note {
    margin: -2px 0 18px;
    font-size: 0.78rem;
    line-height: 1.5;
    color: var(--q-ink-faint);
}

/* ── Submit: the stamp ──────────────────────────────────────────────────── */
.newsstand__stamp {
    display: block;
    width: 100%;
    padding: 13px 20px;
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--q-accent);
    color: var(--q-accent-ink);
    font-family: var(--q-font-mono);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.26em;
    text-transform: uppercase;
    text-indent: 0.26em; /* centers wide tracking, which hangs right otherwise */
    cursor: pointer;
    transition: background-color 130ms ease, transform 90ms ease, box-shadow 130ms ease;
}

.newsstand__stamp:hover {
    background: var(--q-accent-hover);
    box-shadow: 0 3px 10px color-mix(in srgb, var(--q-accent) 34%, transparent);
}

.newsstand__stamp:active {
    transform: translateY(1px);
    box-shadow: none;
}

.newsstand__stamp:focus-visible {
    outline: 2px solid var(--q-accent-text);
    outline-offset: 2px;
}

.newsstand__or {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 16px 0;
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
}

.newsstand__or::before,
.newsstand__or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--q-line);
}

.newsstand__alt {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    padding: 12px 20px;
    border: 1px solid var(--q-line);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--q-ink-muted);
    font-family: var(--q-font-mono);
    font-size: 0.68rem;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color 130ms ease, color 130ms ease, background-color 130ms ease;
}

.newsstand__alt:hover {
    border-color: var(--q-accent-text);
    color: var(--q-accent-text);
    background: var(--q-accent-softer);
}

.newsstand__alt:focus-visible {
    outline: 2px solid var(--q-accent-text);
    outline-offset: 2px;
}

/* Sits outside the coupon: it is a line of the paper, not part of the scrap. */
.newsstand__subscribe {
    margin: 18px 0 0;
    text-align: center;
    font-size: 0.85rem;
    color: var(--q-ink-muted);
}

.newsstand__subscribe a {
    color: var(--q-accent-text);
    font-weight: 600;
    text-decoration: none;
}

.newsstand__subscribe a:hover {
    text-decoration: underline;
}

/* ── Foot ───────────────────────────────────────────────────────────────── */

/* The printer's end mark, set between hairlines that run out to the trim. */
.newsstand__foot {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-top: clamp(26px, 3.5vw, 40px);
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.24em;
    color: var(--q-ink-faint);
    animation: ns-rise 0.5s ease 0.52s both;
}

.newsstand__foot::before,
.newsstand__foot::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--q-line);
}

/* ══ Wide sheet — the leader sets in two columns, as a broadsheet does ═══ */
@media (min-width: 1180px) {
    .newsstand__lead {
        columns: 2;
        column-gap: 28px;
        column-rule: 1px solid var(--q-line-soft);
    }

    /* Without this the drop-cap paragraph can be broken across the gutter,
       stranding the cap at the foot of the first column. */
    .newsstand__lead p {
        break-inside: avoid;
    }
}

/* ══ < 900px — one column, coupon first ════════════════════════════════════
   Someone who opened this page came to sign in; the editorial follows it. */
@media (max-width: 899.98px) {
    .newsstand {
        padding: 14px 20px 40px;
    }

    .newsstand__cols {
        display: flex;
        flex-direction: column;
        gap: 30px;
    }

    .newsstand__coupon-well {
        order: -1;
        padding-left: 0;
    }

    .newsstand__leader {
        padding-right: 0;
        padding-top: 26px;
        border-right: 0;
        border-top: 1px solid var(--q-line);
    }

    /* The coupon is the page's own business here, so it leads the reveal and the
       editorial follows rather than the other way round. */
    .newsstand__coupon-well {
        animation-delay: 0.32s;
    }

    .newsstand__leader {
        animation-delay: 0.42s;
    }
}

/* ══ < 560px — the folio drops its issue line; the coupon squares up ═══════ */
@media (max-width: 559.98px) {
    .newsstand {
        padding: 12px 14px 32px;
    }

    .newsstand__folio-issue {
        display: none;
    }

    .newsstand__creed {
        gap: 10px;
        font-size: 0.56rem;
        letter-spacing: 0.16em;
    }

    /* The date alone is the useful half on a phone. */
    .newsstand__creed > span:first-child {
        display: none;
    }

    .newsstand__creed-date::before {
        content: none;
    }

    /* A tilt reads as a printing quirk at column width and as a mistake at phone
       width, where the coupon runs to the trim on both sides. */
    .newsstand__coupon {
        padding: 26px 18px 22px;
        transform: none;
    }

    @keyframes ns-clip {
        from { opacity: 0; transform: translateY(-12px); }
    }

    .newsstand__stamp,
    .newsstand__alt {
        min-height: 46px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .newsstand *,
    .newsstand *::before,
    .newsstand *::after {
        animation: none !important;
    }
}

/* Screen-reader / programmatic click targets (avoid display:none — iOS may skip .click()) */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE REGISTER — assets and their upkeep
   ───────────────────────────────────────────────────────────────────────────
   Global rather than scoped, and deliberately so: this one surface is drawn by
   five components (AssetsPage, AssetPage, ServiceLogDialog, PhotoStrip,
   PhotoPicker), and Blazor's scoped CSS cannot cross a component boundary.
   Splitting it — some scoped, some here — is how a rule ends up defined twice
   with the global copy quietly winning, which is a trap this codebase has
   already been caught by once. One definition, one place.

   The register is the same printed ledger column the timesheet is: headings in
   the display face, rows ruled, every figure hung right in tabular mono. No
   table — the app still has no .q-table primitive (D5), and a register really
   is a ledger column rather than a grid.

   Two gutters hold the columns: --rg-meter and --rg-money. Group heads, year
   heads and entries all reserve them, so the figures line up whatever a row
   happens to say. Break that and the columns stop reading as columns.
   ═══════════════════════════════════════════════════════════════════════════ */

.register,
.asset {
    --rg-measure: 900px;
    --rg-meter: 5.4rem;
    --rg-money: 5.4rem;
    --rg-bleed: 8px;
    /* The shell hands the page column over flush — .bs__page carries no padding
       of its own — so the ledger sets its own gutters, the way the timesheet
       does. It must stay wider than --rg-bleed, or a bled row crosses the index
       rule on one side and the edge of the sheet on the other. */
    --rg-gutter: 0px;

    max-width: calc(var(--rg-measure) + var(--rg-gutter) * 2);
    padding: 0 var(--rg-gutter) 56px;
    box-sizing: border-box;
}

/* Only the wide sheet rules a line down the left of the page column, and type
   set hard against that rule reads as a printing fault. Below 1024px the index
   is a strip overhead and the shell's own padding is already the gutter. */
@media (min-width: 1024px) {
    .register,
    .asset { --rg-gutter: 24px; }
}

.register__bar,
.asset__bar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 8px;
}

.asset__spacer { flex: 1; }

/* ── Shared ledger primitives ───────────────────────────────────────────── */

.register__rule {
    flex: 1 1 auto;
    min-width: 14px;
    height: 0;
    margin: 0 8px 0.34em;
    border-bottom: 1px dotted var(--q-line);
    align-self: flex-end;
}

.register__figure {
    font-family: var(--q-font-mono);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
    color: var(--q-ink);
}

.register__figure--meter { color: var(--q-ink-muted); }
.register__figure--group { font-size: 0.82rem; font-weight: 600; }

/* ── The register: groups and rows ──────────────────────────────────────── */

.register__group { margin-top: 28px; }

.register__group-head {
    display: grid;
    grid-template-columns: auto 1fr var(--rg-money);
    align-items: baseline;
    gap: 0 10px;
    padding-bottom: 7px;
    border-bottom: 1.5px solid color-mix(in srgb, var(--q-ink) 30%, var(--q-line));
}

.register__group-name {
    font-family: var(--q-font-display);
    font-size: 1.02rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--q-ink);
}

.register__row {
    display: grid;
    grid-template-columns: 8px minmax(0, 11em) minmax(0, 1fr) var(--rg-meter) var(--rg-money);
    align-items: center;
    gap: 0 10px;
    width: calc(100% + var(--rg-bleed) * 2);
    margin: 0 calc(var(--rg-bleed) * -1);
    padding: 10px var(--rg-bleed);
    border: none;
    border-bottom: 1px solid var(--q-line-soft);
    background: none;
    font: inherit;
    text-align: left;
    color: var(--q-ink);
    cursor: pointer;
    transition: background-color 0.12s;
    box-sizing: border-box;
}

.register__row:hover { background: color-mix(in srgb, var(--q-ink) 5%, transparent); }
.register__row:focus-visible { outline: 2px solid var(--q-accent-text); outline-offset: -2px; }

/* The asset's color is always a mark, never type — the ink stays readable and
   a badly chosen swatch cannot wreck contrast. */
.register__dot,
.asset__bar-mark {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

.register__name {
    font-size: 0.9rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.register__lead,
.asset__entry-lead {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
}

.register__sub {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.82rem;
    color: var(--q-ink-muted);
}

.register__empty {
    margin-top: 40px;
    padding-top: 20px;
    border-top: 1.5px solid var(--q-line);
    max-width: 46ch;
}

.register__empty-line {
    font-family: var(--q-font-display);
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0 0 6px;
    color: var(--q-ink);
}

.register__empty-note { margin: 0; font-size: 0.9rem; color: var(--q-ink-muted); }

/* ── One asset ──────────────────────────────────────────────────────────── */

.asset__missing { margin-top: 40px; color: var(--q-ink-muted); }

.asset__head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-top: 20px;
}

.asset__bar-mark {
    width: 4px;
    height: 2.6rem;
    border-radius: 2px;
    margin-top: 4px;
}

.asset__name {
    font-family: var(--q-font-display);
    font-size: 1.9rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.1;
    margin: 0;
    color: var(--q-ink);
}

.asset__made {
    margin: 4px 0 0;
    font-size: 0.86rem;
    color: var(--q-ink-muted);
}

/* The figures that answer "what is it on, and what has it cost" without
   reading the history. */
.asset__figures {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 40px;
    margin: 22px 0 0;
    padding: 16px 0;
    border-top: 1px solid var(--q-line);
    border-bottom: 1px solid var(--q-line);
}

.asset__figure { margin: 0; }

.asset__figure dt {
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
    margin-bottom: 3px;
}

.asset__figure dd {
    margin: 0;
    font-family: var(--q-font-mono);
    font-size: 1.05rem;
    font-variant-numeric: tabular-nums;
    color: var(--q-ink);
}

.asset__figure-note {
    font-size: 0.72rem !important;
    color: var(--q-ink-faint);
    margin-top: 2px !important;
}

.asset__details { margin-top: 16px; display: grid; gap: 6px; }

.asset__detail { display: flex; gap: 12px; font-size: 0.85rem; }

.asset__detail-key {
    flex: 0 0 5.5rem;
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
    padding-top: 0.25em;
}

.asset__detail-val { color: var(--q-ink-muted); min-width: 0; }

.asset__section { margin-top: 34px; }

.asset__section-head {
    display: grid;
    grid-template-columns: auto 1fr var(--rg-money);
    align-items: baseline;
    gap: 0 10px;
    padding-bottom: 7px;
    margin-bottom: 10px;
    border-bottom: 1.5px solid color-mix(in srgb, var(--q-ink) 30%, var(--q-line));
}

.asset__section-name {
    font-family: var(--q-font-display);
    font-size: 1.02rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--q-ink);
}

.asset__none { font-size: 0.88rem; color: var(--q-ink-muted); margin: 10px 0 0; }

.asset__year {
    display: grid;
    grid-template-columns: auto 1fr var(--rg-money);
    align-items: baseline;
    gap: 0 10px;
    margin-top: 18px;
    padding-bottom: 5px;
}

.asset__year-name {
    font-family: var(--q-font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.16em;
    color: var(--q-ink-faint);
}

.asset__entry {
    display: grid;
    grid-template-columns: 4.2rem minmax(0, 1fr) var(--rg-meter) var(--rg-money);
    align-items: center;
    gap: 0 10px;
    width: calc(100% + var(--rg-bleed) * 2);
    margin: 0 calc(var(--rg-bleed) * -1);
    padding: 9px var(--rg-bleed);
    border: none;
    border-bottom: 1px solid var(--q-line-soft);
    background: none;
    font: inherit;
    text-align: left;
    color: var(--q-ink);
    cursor: pointer;
    transition: background-color 0.12s;
    box-sizing: border-box;
}

.asset__entry:hover { background: color-mix(in srgb, var(--q-ink) 5%, transparent); }
.asset__entry:focus-visible { outline: 2px solid var(--q-accent-text); outline-offset: -2px; }

.asset__entry-date {
    font-family: var(--q-font-mono);
    font-size: 0.74rem;
    font-variant-numeric: tabular-nums;
    color: var(--q-ink-faint);
}

.asset__entry-what {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.85rem;
    font-weight: 600;
}

/* How many photographs hang off a job, as a count rather than a paperclip —
   the app has no clip glyph and does not need one for this. */
.asset__entry-clip {
    flex-shrink: 0;
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    line-height: 1;
    padding: 2px 5px;
    border: 1px solid var(--q-line);
    border-radius: 2px;
    color: var(--q-ink-faint);
}

/* ── Off the register ───────────────────────────────────────────────────── */

/* Ochre, not vermilion: an archived asset is a state, not an alarm, and
   vermilion in this design means late and nothing else. */
.asset__archived {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    margin-top: 18px;
    padding: 12px 14px;
    border: 1px solid color-mix(in srgb, var(--pri-medium) 32%, transparent);
    border-left: 3px solid var(--pri-medium);
    border-radius: var(--radius-sm, 4px);
    background: color-mix(in srgb, var(--pri-medium) 10%, transparent);
}

.asset__archived-text {
    flex: 1 1 16rem;
    min-width: 0;
    font-size: 0.86rem;
    color: var(--q-ink-muted);
}

.asset__archived-text strong { color: var(--q-ink); }

.asset__foot {
    display: flex;
    align-items: baseline;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 46px;
    padding-top: 14px;
    border-top: 1px solid var(--q-line-soft);
}

/* Deliberately not a .q-btn--danger: nothing is destroyed, and dressing a
   reversible act in the alarm colour teaches the reader to distrust the colour. */
.asset__retire {
    padding: 0;
    border: none;
    background: none;
    font: inherit;
    font-size: 0.82rem;
    color: var(--q-ink-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

.asset__retire:hover { color: var(--q-ink); }
.asset__retire:focus-visible { outline: 2px solid var(--q-accent-text); outline-offset: 2px; }
.asset__retire:disabled { opacity: 0.5; cursor: default; }

.asset__foot-note {
    font-size: 0.76rem;
    color: var(--q-ink-faint);
}

/* ── The Reckoning — the register stood back from ───────────────────────── */

/* Drawn with the ledger's own primitives wherever one exists: .asset__figures
   for the band, .asset__section-head for the heads, .register__rule and
   .register__figure for the leaders and the figures, .asset__entry for the
   recent jobs. A column here therefore lines up with the same column on an
   asset's own page, which is the whole reason those gutters are variables.

   What is new below is only what a ledger has no shape for: twelve months as
   bars, a two-up, and a marked line.

   The bars are divs with a percentage height, deliberately, and not a chart
   library. ApexCharts renders literal SVG and cannot read a custom property, so
   /stats has to re-theme itself imperatively on every dark-mode toggle and
   re-key every chart; a div inherits this token table for free, prints, and
   adds no package. */

.review__months {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 6px;
    align-items: end;
    margin-top: 6px;
}

.review__month { display: grid; gap: 6px; }

.review__track {
    height: 88px;
    display: flex;
    align-items: flex-end;
    border-bottom: 1px solid var(--q-line);
}

.review__bar { width: 100%; background: color-mix(in srgb, var(--q-ink) 72%, transparent); }

/* The month in hand, in the marker — a fill, never type. See the two rules at
   the head of this sheet. */
.review__bar--now { background: var(--q-accent); }

/* Jobs were logged and not one of them carried a price. Ochre, because it is a
   caveat and not an alarm: vermilion means late and nothing else. */
.review__tick {
    width: 100%;
    height: 0;
    border-top: 2px dashed var(--pri-medium);
}

.review__month-name {
    font-family: var(--q-font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.04em;
    text-align: center;
    color: var(--q-ink-faint);
}

.review__legend {
    margin: 10px 0 0;
    font-family: var(--q-font-mono);
    font-size: 0.66rem;
    color: var(--q-ink-faint);
}

/* Folds to one column on its own, without a breakpoint: two money columns at
   less than 280px each stop being readable long before the sheet does. */
.review__two {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 0 40px;
}

.review__kicker {
    font-family: var(--q-font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--q-ink-faint);
    margin-bottom: 3px;
}

.review__kicker--second { margin-top: 20px; }

/* Named areas rather than three columns, so a row that carries a note puts it on a
   line of its own underneath. Nested inside the name instead, the note makes that
   cell two lines tall and the leader — which aligns to the end of its row — drops to
   the second line, where it reads as a rule ruled under the wrong thing. */
.review__row {
    display: grid;
    grid-template-columns: minmax(0, auto) 1fr var(--rg-money);
    grid-template-areas:
        "name rule figure"
        "note note note";
    align-items: baseline;
    gap: 0 6px;
    width: 100%;
    padding: 7px 0;
    border: none;
    border-bottom: 1px solid var(--q-line-soft);
    background: none;
    font: inherit;
    text-align: left;
    color: var(--q-ink);
    box-sizing: border-box;
}

.review__row-name {
    grid-area: name;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 0.88rem;
}

.review__row > .register__rule { grid-area: rule; }
.review__row > .register__figure { grid-area: figure; }

.review__row-note {
    grid-area: note;
    margin-top: 2px;
    font-size: 0.72rem;
    color: var(--q-ink-faint);
}

.review__row--link {
    width: calc(100% + var(--rg-bleed) * 2);
    margin: 0 calc(var(--rg-bleed) * -1);
    padding: 7px var(--rg-bleed);
    cursor: pointer;
    transition: background-color 0.12s;
}

.review__row--link:hover { background: color-mix(in srgb, var(--q-ink) 5%, transparent); }
.review__row--link:focus-visible { outline: 2px solid var(--q-accent-text); outline-offset: -2px; }

/* Ochre down the edge, the same mark an archived asset carries. No radius: a
   corner rounded on one side only reads as a printing fault.

   Flush at the column edge rather than bled into the gutter like a ledger row,
   and that is load-bearing rather than taste. Below 1024px --rg-gutter is 0, so a
   -8px bleed puts this border outside .bs__page — which is the scroll container at
   those widths, and therefore clips it. The mark would simply not be there on a
   phone, which is the one place a row of unmarked sentences reads as a mistake.
   Flush also lines the ochre up with the section rule above it. */
.review__mark {
    display: block;
    width: 100%;
    margin: 0 0 6px;
    padding: 8px 0 8px 11px;
    border: none;
    border-left: 3px solid var(--pri-medium);
    border-radius: 0;
    background: none;
    font: inherit;
    font-size: 0.88rem;
    text-align: left;
    color: var(--q-ink);
    cursor: pointer;
    transition: background-color 0.12s;
    box-sizing: border-box;
}

.review__mark:hover { background: color-mix(in srgb, var(--q-ink) 5%, transparent); }
.review__mark:focus-visible { outline: 2px solid var(--q-accent-text); outline-offset: -2px; }

/* ── Photographs ────────────────────────────────────────────────────────── */

.photo-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 10px 0;
}

.photo-grid__cell { position: relative; margin: 0; width: 92px; height: 92px; }

.photo-grid__open {
    display: block;
    width: 100%;
    height: 100%;
    padding: 0;
    border: 1px solid var(--q-line);
    border-radius: 3px;
    background: none;
    cursor: pointer;
    overflow: hidden;
}

.photo-grid__open:focus-visible { outline: 2px solid var(--q-accent-text); outline-offset: 2px; }

.photo-grid__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    border-radius: 2px;
}

.photo-grid__cell > .photo-grid__img {
    border: 1px solid var(--q-line);
    border-radius: 3px;
}

.photo-grid__remove {
    position: absolute;
    top: -6px;
    right: -6px;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--q-line);
    border-radius: 50%;
    background: var(--q-surface-raised);
    color: var(--q-ink-muted);
    cursor: pointer;
}

.photo-grid__remove:hover { color: var(--pri-high); border-color: var(--pri-high); }
.photo-grid__remove:focus-visible { outline: 2px solid var(--q-accent-text); outline-offset: 1px; }

.photo-picker { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* The real <input type="file"> is kept in the layout but visually hidden, so the
   label stays a real label and the control keeps its keyboard behaviour. */
.photo-picker__input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    overflow: hidden;
}

.photo-picker__btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 1px dashed var(--q-line);
    border-radius: var(--radius-md, 6px);
    font-size: 0.82rem;
    color: var(--q-ink-muted);
    cursor: pointer;
}

.photo-picker__btn:hover { border-color: var(--q-accent-text); color: var(--q-ink); }
.photo-picker__btn:focus-within { outline: 2px solid var(--q-accent-text); outline-offset: 2px; }

.photo-picker__busy { font-size: 0.78rem; color: var(--q-ink-faint); }
.photo-picker__error { font-size: 0.78rem; color: var(--pri-high); }

.photo-lightbox {
    position: fixed;
    inset: 0;
    z-index: 1500;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 32px;
    background: color-mix(in srgb, #16140f 82%, transparent);
    cursor: zoom-out;
}

.photo-lightbox__img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: 3px;
    cursor: default;
}

.photo-lightbox__close {
    position: absolute;
    top: 16px;
    right: 16px;
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--q-line);
    border-radius: 50%;
    background: var(--q-surface-raised);
    color: var(--q-ink);
    cursor: pointer;
}

/* ── The dialogs ────────────────────────────────────────────────────────── */

.reg-dialog-overlay {
    position: fixed;
    inset: 0;
    z-index: 1400;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: color-mix(in srgb, #16140f 55%, transparent);
    overflow-y: auto;
}

.reg-dialog {
    width: 100%;
    max-width: 520px;
    max-height: 90vh;
    overflow-y: auto;
    padding: 22px;
    border: 1px solid var(--q-line);
    border-radius: var(--radius-md, 6px);
    background: var(--q-surface-raised);
    box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22);
}

.reg-dialog__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 18px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--q-line-soft);
}

.reg-dialog__row { display: flex; gap: 12px; }
.reg-dialog__col { flex: 1 1 0; min-width: 0; }
.reg-dialog__col--narrow { flex: 0 0 5.5rem; }

.reg-dialog__footer {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 20px;
    padding-top: 16px;
    border-top: 1px solid var(--q-line-soft);
}

.reg-dialog__spacer { flex: 1; }

.reg-color-row { display: flex; align-items: center; gap: 10px; }

.reg-color-pick {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1.5px solid var(--q-line);
    border-radius: 50%;
    background: none;
    cursor: pointer;
    overflow: hidden;
}

.reg-color-pick:focus-visible { outline: 2px solid var(--q-accent-text); outline-offset: 2px; }
.reg-color-pick::-webkit-color-swatch-wrapper { padding: 0; }
.reg-color-pick::-webkit-color-swatch { border: none; border-radius: 50%; }
.reg-color-pick::-moz-color-swatch { border: none; border-radius: 50%; }

.reg-color-value { font-family: var(--q-font-mono); font-size: 0.78rem; color: var(--q-ink-faint); }

@media (max-width: 700px) {
    .register__row { grid-template-columns: 8px minmax(0, 1fr) var(--rg-money); }

    /* Below the fold the subtitle and the meter drop out rather than wrapping —
       a ledger row that becomes three lines has stopped being a row. */
    .register__lead,
    .register__figure--meter { display: none; }

    .asset__entry { grid-template-columns: 4.2rem minmax(0, 1fr) var(--rg-money); }

    /* Twelve mono labels will not fit across a phone, so every other one carries
       the strip. The bars all stay — it is the reading that thins out, not the
       year. */
    .review__months { gap: 3px; }
    .review__month:nth-child(even) .review__month-name { visibility: hidden; }

    /* The recent-jobs rows carry the asset's name as well as the job's, which the
       asset page's own history does not, so they run out of line first. The leader
       goes rather than the words: on a phone it spans 30px, which is not a gap the
       eye needs help crossing. Scoped to this page for that reason. */
    .review .asset__entry-lead .register__rule { display: none; }

    .reg-dialog__row { flex-direction: column; gap: 0; }
    .reg-dialog__col--narrow { flex: 1 1 auto; }
}
