/* Operational component rules. Tokens live in operational-tokens.css. */
.menu li > a.active, .menu li > a:focus { box-shadow: none; }
.menu .nav-row > a { min-height: 38px; font-size: .875rem; align-content: center; }
.form-input { font-size: .875rem; }
.border-beam.signature::before, .card-interactive.signature::before { display: none; }
.glow-hover:hover { transform: none; }
.swal2-popup .swal2-confirm {
    background: var(--accent-primary) !important;
    box-shadow: none !important;
}
.swal2-popup .swal2-confirm:hover { box-shadow: none !important; transform: none; }

/* Dense working surfaces retain their structure and existing density controls. */
.sub-filter-bar select, .sub-filter-bar input { font-size: .875rem; min-height: 36px; }
.sub-filter-bar { gap: 12px; }
.sub-filter-date { display: flex; align-items: center; gap: 6px; color: var(--text-secondary); font-size: .75rem; }
.sub-filter-date input { width: 145px; }
.sub-filter-bar :is(input, select):focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 2px; }
.ws-pill-label { font-size: .75rem; opacity: 1; color: var(--text-secondary); }
.ws-pill { gap: .4rem; }
.ws-save-status { font-size: .75rem; }
.ws-sidebar-btn { opacity: .8; }
.uo-tabs:has(.ws-tab) { max-width: calc(100% - 2rem); }
.template-card:hover { transform: none; }
#logins-results table { min-width: 48rem; }

/* Home: role-specific work first; reports are available without a wall of charts.
   Compact on a laptop (Joel 2026-09-27): the pulse alone was 262px at 1366x768. */
.portal-home .po-work { padding: 14px 16px; gap: 12px; }
.portal-home .po-work__title { font-size: 1.125rem; font-weight: 650; line-height: 1.3; }
.portal-home .po-work__what { font-size: .875rem; line-height: 1.5; margin-top: 2px; }
.portal-home .po-work__body { min-width: 0; flex-basis: 240px; }
.portal-home .po-work__icon { width: 36px; height: 36px; }
.portal-home .po-deck-wrap::before, .portal-home .po-eyebrow::before { display: none; }
.portal-home .po-eyebrow { font-size: .75rem; letter-spacing: .08em; }
.portal-home .po-tile { border-left: 0; padding: 12px 16px; gap: 4px; }
.portal-home .po-tile + .po-tile { border-left: 1px solid var(--border-default); }
.portal-home .po-tile__label { font-size: .75rem; letter-spacing: .04em; }
.portal-home .po-tile__num { font-family: var(--font-numeric); line-height: 1.2; }
.portal-home .po-tile--accent .po-tile__num { color: var(--text-primary); }
.portal-home .po-livedot { box-shadow: none; animation: none; }
.portal-home .po-area:hover { transform: none; }
.portal-home .po-area__icon { background: var(--bg-input); border-color: var(--border-default); color: var(--text-secondary); }
.portal-home .po-area__title { font-family: var(--font-body); }
.portal-home .po-rise { opacity: 1; animation: none; transform: none; }
.portal-home .ceo-card__title, .portal-home .ceo-bar-name { font-size: .875rem; }
.portal-home .ceo-card__sub, .portal-home .ceo-legend { font-size: .75rem; }
.portal-home .ceo-bar-row { grid-template-columns: minmax(120px, 1fr) 2fr 40px; }
.po-report-group { border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--bg-card); }
.po-report-group > summary { cursor: pointer; padding: 16px 20px; font-size: .875rem; font-weight: 600; color: var(--text-primary); }
.po-report-group > summary:hover { background: var(--bg-card-hover); border-radius: inherit; }
.po-report-group > summary:focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 3px; border-radius: inherit; }
.po-report-group[open] > summary { border-bottom: 1px solid var(--border-default); }
.po-report-group .po-widgets { padding: 16px; }
@media (max-width: 720px) {
    .portal-home .po-tile { padding: 16px; }
    .portal-home .po-tile:nth-child(3) { border-left: 0; }
    .portal-home .po-work { padding: 16px; }
    .portal-home .po-areas, .portal-home .po-areas--compact { grid-template-columns: minmax(0, 1fr); }
    .portal-home .po-work__cta { white-space: normal; }
    .po-report-group .po-widgets { padding: 8px; }
}

/* Search fields. The geometry lives here, not in a utility class on the input:
   .form-input sets the `padding` SHORTHAND in components/_base_styles.html,
   which base.html includes after tailwind.built.css at the same specificity
   (0,1,0), so source order kills any padding-left utility and the icon lands
   on top of the placeholder. This file is the last stylesheet base.html loads,
   so these win without !important. Rendered by components/_search_input.html. */
.uo-search { position: relative; }
/* A search grows with its filter row up to a readable width, then the filters
   sit next to it instead of after a 1,500px box (Joel 2026-10-08). */
.uo-search-field { max-width: 36rem; }
.uo-search__icon {
    position: absolute;
    left: .75rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-muted);
    pointer-events: none;
}
.uo-search .uo-search__input { padding-left: 2.25rem; }
/* The compact variant: a smaller icon, tucked closer in. */
.uo-search--sm .uo-search__icon { left: .5rem; }
.uo-search--sm .uo-search__input { padding-left: 1.75rem; }

/* A confirmation dialog. Native <dialog>, so closed really means display:none
   and "is it open?" is a question the page can answer; DaisyUI's .modal stays
   in the layout and lies about it. The card look comes from the tokens so it
   sits in the same design as everything around it. */
.uo-dialog {
    background: var(--bg-card);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    color: var(--text-primary);
    padding: 1.25rem;
    width: min(28rem, calc(100vw - 2rem));
}
.uo-dialog::backdrop { background: rgb(0 0 0 / 0.5); }

/* Opt-in numeric tables only: the deal queue keeps .sub-th/.sub-td density. */
.data-table th {
    font-size: var(--table-heading-size);
    text-transform: none;
    letter-spacing: normal;
    color: var(--text-secondary);
    padding: var(--table-cell-block) var(--table-cell-inline);
}
.data-table td, .data-table--rowheads :is(tbody, tfoot) th {
    font-size: var(--table-font-size);
    padding: var(--table-cell-block) var(--table-cell-inline);
}
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        scroll-behavior: auto !important;
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
}

/* Status badge — components/_badge.html::status_badge. One label for every
   status: ink text on the card, a neutral line, and the state's colour only as
   a dot (Joel 2026-10-07, "color sólo en un puntito"). The legacy names (the
   workspace's .badge-approved family, daisyUI tones, QA's and Zlur's .b-*) set
   a --tone and share this rule instead of carrying their own tints. */
.uo-badge, .badge-approved, .badge-declined, .badge-pending, .badge-escalated, .badge-review,
.badge-success, .badge-error, .badge-warning, .badge-info {
    --tone: var(--text-muted);
    display: inline-flex; align-items: center; gap: .375rem;
    padding: .125rem .5rem; border-radius: 5px;
    font-size: .6875rem; font-weight: 600; line-height: 1.4; white-space: nowrap;
    color: var(--text-primary); background: var(--bg-card);
    border: 1px solid var(--border-default);
}
:is(.uo-badge:not(.uo-badge--neutral), .badge-approved, .badge-declined, .badge-pending, .badge-escalated,
    .badge-review, .badge-success, .badge-error, .badge-warning, .badge-info)::before {
    content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--tone);
}
.uo-badge--success, .badge-approved, .badge-success { --tone: var(--success); }
.uo-badge--danger, .badge-declined, .badge-error { --tone: var(--danger); }
.uo-badge--warning, .uo-badge--caution, .badge-pending, .badge-review, .badge-warning { --tone: var(--warning-mark); }
.uo-badge--accent, .uo-badge--info, .badge-escalated, .badge-info { --tone: var(--accent-primary); }

/* A filter chip for one state (the Deals / Tickets status strips): neutral,
   the state's colour is the dot inside it. */
.uo-tone-chip { border: 1px solid var(--border-default); background: var(--bg-card); }
.uo-tone-chip:hover { background: var(--bg-card-hover); }

/* Anything painted with a state's background (now one neutral) keeps its
   state as a mark: a box gets a thin bar on the left, an inline chip a dot
   before its label. ~600 inline and class tints follow without a rewrite.
   ponytail: attribute selectors on style=""; a per-class list if one misfires. */
:is([style*="var(--success-bg)"], .bg-uf-success-bg) { --state: var(--success); }
:is([style*="var(--danger-bg)"], .bg-uf-danger-bg) { --state: var(--danger); }
:is([style*="var(--warning-bg)"], [style*="var(--caution-bg)"], .bg-uf-warning-bg) { --state: var(--warning-mark); }
:is([style*="var(--success-bg)"], [style*="var(--danger-bg)"], [style*="var(--warning-bg)"], [style*="var(--caution-bg)"],
    .bg-uf-success-bg, .bg-uf-danger-bg, .bg-uf-warning-bg):not(span, td) { box-shadow: inset 3px 0 0 var(--state); }
:is(span, td):is([style*="var(--success-bg)"], [style*="var(--danger-bg)"], [style*="var(--warning-bg)"], [style*="var(--caution-bg)"],
    .bg-uf-success-bg, .bg-uf-danger-bg, .bg-uf-warning-bg)::before {
    content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
    background: var(--state); margin-right: .375rem; vertical-align: .1em;
}

/* ═══ One bar (Joel 2026-09-27) ═══ padding was 32px from 1024px up — on a
   laptop that is page, not frame. Plain CSS: the purged Tailwind build has no
   utility for these steps. The workspace zeroes it with !important. */
#main-content { padding: 12px; }
@media (min-width: 768px) { #main-content { padding: 16px 20px; } }
@media (min-width: 1600px) { #main-content { padding: 24px; } }
/* The page column may shrink below its widest child: without this a wide page
   (a table, a row of buttons) pushed the whole bar off a tablet screen. */
.drawer-content { min-width: 0; }
/* The page's buttons sit in the bar: one row on a laptop. On a phone the bar
   wraps them to a second row instead of pushing the page sideways
   (/uw-training was 223px wider than a 390px screen). Tablets too: with the
   menu pinned open at 768px, /reports/internal pushed the bar 281px off-screen. */
.navbar .uo-page-actions > * { flex-shrink: 0; }
@media (max-width: 1279.98px) {
    .navbar[role=banner] { flex-wrap: wrap; }
    .navbar .uo-page-actions { order: 3; flex-basis: 100%; flex-wrap: wrap; min-width: 0; }
    .navbar .uo-page-actions > * { flex-shrink: 1; min-width: 0; max-width: 100%; }
    .navbar .uo-page-actions > .flex { flex-wrap: wrap; }
}
/* page_meta (base.html): the record's facts, one small line under the bar. */
.uo-page-meta { margin: 0 0 12px; font-size: 13px; color: var(--text-secondary); }
/* (?) popover — placed under its button by static/js/uo-page-purpose.js. */
#uo-page-purpose {
    position: fixed; inset: auto; margin: 0; max-width: min(360px, calc(100vw - 24px));
    padding: 10px 12px; font-size: 13px; line-height: 1.45;
    color: var(--text-secondary); background: var(--bg-card);
    border: 1px solid var(--border-default); border-radius: var(--radius-md);
    box-shadow: var(--shadow-elevated);
}

/* ── Laptop icon rail (Joel 2026-09-27) ──────────────────────────────────
   768-1599px: the 272px sidebar becomes a 64px rail of icons and opens over
   the page (never pushing it) on hover (aside[data-open], set by
   static/js/uo-sidebar.js), keyboard focus or the New Deal menu.
   "Keep menu open" pins it (html[data-uo-sidebar=pinned], static/js/uo-sidebar.js).
   Not on the workspace (it folds the sidebar into its own pill) nor on a
   portal with its own sub-menu (.area-nav: QA, Zlur, MCA Suite, Client
   Relations). Labels fade with opacity, never display:none, so rows keep
   their place when it opens and screen readers keep every link's name.
   Driven by attributes + CSS: tests pin the drawer's exact class lists. */
/* Keep the navigation scrollable without a native scrollbar crowding icons. */
.drawer-side > aside nav {
    min-height: 0;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-thumb-hover) transparent;
}
.drawer-side > aside nav::-webkit-scrollbar { width: 4px; }
.drawer-side > aside nav::-webkit-scrollbar-button { display: none; }
.drawer-side .nav-row > a > .badge {
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
}
.drawer-side .nav-row > a > .badge:empty { display: none; }
.nav-rail-toggle { display: none; }
@media (min-width: 768px) and (max-width: 1599.98px) {
    .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) .nav-rail-toggle {
        display: flex; align-items: center; gap: 12px; flex: none;
        margin: 6px 12px 10px; padding: 8px 12px; border: 0; border-radius: var(--radius-md);
        background: transparent; color: var(--text-muted); font-size: 13px;
        white-space: nowrap; cursor: pointer;
    }
    .nav-rail-toggle:hover { background: var(--bg-card-hover); color: var(--text-primary); }
    .nav-rail-toggle[aria-pressed="true"] { color: var(--accent-primary); }
    .nav-rail-toggle:focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 1px; }

    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side {
        width: 4rem; overflow: visible;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side > aside {
        height: 100vh; height: 100dvh; min-height: 0; overflow: hidden;
        box-shadow: var(--shadow-elevated);
        transition: width .15s ease, box-shadow .15s ease;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side > aside nav { overflow-x: hidden; }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side > aside :is(.menu li > a, .nav-search, .uo-btn-primary) {
        white-space: nowrap;
    }
    /* Icons sit in the rail's centre line in both states, so nothing jumps. */
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side > aside :is(.uo-btn-primary, .nav-search) {
        justify-content: flex-start; padding-inline: 8px; overflow: hidden;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side > aside :is(.sidebar-brand, .nav-label, .nav-search__label, .nav-search__kbd, .menu-title, .nav-pin) {
        transition: opacity .1s ease .05s;
    }
    /* Closed: icons only; full counts return when the menu opens. */
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side:not(:focus-within) > aside:not([data-open]) {
        width: 4rem; box-shadow: none; transition-delay: 0s;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side:not(:focus-within) > aside:not([data-open]) :is(.sidebar-brand, .nav-label, .nav-search__label, .nav-search__kbd, .menu-title, .nav-pin) {
        opacity: 0; transition-delay: 0s;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side:not(:focus-within) > aside:not([data-open]) .menu li > a {
        max-width: 2.5rem; overflow: hidden;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side:not(:focus-within) > aside:not([data-open]) nav {
        scrollbar-width: none;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side:not(:focus-within) > aside:not([data-open]) nav::-webkit-scrollbar {
        display: none;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side:not(:focus-within) > aside:not([data-open]) .nav-row .badge {
        display: none;
    }
    /* Portal sidebars (#3690): a closed rail shows a group's icon, not its rows. */
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side > aside :is(.menu li > details > summary, .nav-back) {
        white-space: nowrap;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side:not(:focus-within) > aside:not([data-open]) :is(.menu li > details > summary, .nav-back) {
        max-width: 2.5rem; overflow: hidden;
    }
    html:not([data-uo-sidebar="pinned"]) .drawer.md\:drawer-open:not(:has(.ws-shell, .area-nav)) > .drawer-side:not(:focus-within) > aside:not([data-open]) .menu li > details > ul {
        display: none;
    }
}

/* ── Portal sidebar (#3690) ──────────────────────────────────────────────
   Inside a portal the sidebar opens with a way back to the Main Page (/home,
   never history.back) and folds sub-pages into collapsible groups
   (<details>, open on the group that holds the current page). */
.nav-back {
    display: flex; align-items: center; gap: 12px;
    margin: 0 12px 6px; padding: 6px 12px; border-radius: var(--radius-md);
    font-size: 12px; font-weight: 600; color: var(--text-muted); text-decoration: none;
}
.nav-back:hover { background: var(--bg-card-hover); color: var(--text-primary); }
.nav-back:focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 1px; }
.sidebar-nav .menu .nav-group > details > summary { min-height: 38px; font-size: .875rem; gap: 12px; }
/* The group holding the current page keeps the accent, so a closed laptop rail
   (icons only, sub-rows hidden) still shows where you are. */
.sidebar-nav .menu .nav-group:has(a.active) > details > summary { color: var(--accent-primary) !important; }
@media (prefers-reduced-motion: reduce) {
    .drawer-side > aside, .drawer-side > aside * { transition: none !important; }
}

/* Deals bulk bar (deals/dashboard.html): off-screen until a row is checked. */
#bulk-action-bar { transform: translateY(100%); opacity: 0; pointer-events: none; }
#bulk-action-bar[data-open] { transform: none; opacity: 1; pointer-events: auto; }

/* ── Legacy component names wear the shared look (coherence 6b, 2026-09-26) ──
   daisyUI's `.btn` / `.badge` / `.card` still sit in ~400 places (QA, Zlur, MCA
   Suite, a few dashboard pages). Rather than a codemod per page, the names are
   restyled here to measure exactly like .uo-btn-* / .uo-badge (docs/ui/DESIGN.md).
   Geometry only where a page paints its own colours (`badge b-gray`, `badge ok`):
   this file loads last, so a colour set on `.badge` would repaint those.
   `display` is never set, so a call site's `hidden` still hides.
   tests/ui/test_component_coherence.py measures both in a real browser. */
.btn {
    height: auto; min-height: 2.25rem; padding: .5rem 1.25rem; gap: .375rem;
    font-size: .875rem; font-weight: 500; line-height: inherit; text-transform: none;
    border-radius: var(--rounded-btn); border: 1px solid var(--border-default);
    background: var(--bg-input); color: var(--text-secondary);
    box-shadow: none; animation: none; flex-wrap: nowrap;
}
.btn:hover { background: var(--bg-card-hover); border-color: var(--border-strong); color: var(--text-primary); }
.btn-sm { height: auto; min-height: 2rem; padding: .375rem .75rem; font-size: .8125rem; white-space: nowrap; }
.btn-xs { height: auto; min-height: 1.5rem; padding: .25rem .5rem; font-size: .75rem; gap: .25rem; white-space: nowrap; }
.btn.btn-primary { background: var(--accent-primary); border-color: transparent; color: var(--text-on-accent); font-weight: 600; }
.btn.btn-primary:hover { background: var(--accent-primary); border-color: transparent; color: var(--text-on-accent); filter: brightness(1.1); }
.btn.btn-ghost { background: none; border-color: transparent; color: var(--text-muted); }
.btn.btn-ghost:hover { background: var(--bg-card-hover); color: var(--text-primary); }
.btn.btn-error { background: var(--bg-card); border-color: var(--danger); color: var(--danger); font-weight: 600; }
.btn.btn-error:hover { background: var(--danger); color: var(--text-on-accent); }
.btn:is(:disabled, [disabled], .btn-disabled) { opacity: .45; cursor: not-allowed; filter: none; }
.badge, .badge-xs, .badge-sm, .badge-md, .badge-lg {
    height: auto; width: auto; padding: .125rem .5rem; gap: .25rem;
    font-size: .6875rem; font-weight: 600; line-height: 1.4; letter-spacing: normal;
    border-radius: var(--rounded-badge); white-space: nowrap;
}
/* The daisyUI tones that are not a page's own: the shared tone tints. */
.badge-ghost { color: var(--text-secondary); background: var(--bg-input); border-color: color-mix(in srgb, var(--text-secondary) 30%, transparent); }
.card { border-radius: var(--radius-md); box-shadow: none; }
/* A header cell with no weight class of its own reads like .data-table th (600),
   not the browser's bold. Element-level, so any font-* utility still wins. */
th { font-weight: 600; }

/* ── FlowMaze dashboard practices (2026-09-26, docs/ui/DESIGN.md) ── */
/* Text-heavy tables keep the sticky header but read left; mark money/count
   cells with .num to put them back on the right (FlowMaze practice 7). */
.data-table--text th, .data-table--text td { text-align: left; }
.data-table--text td { white-space: normal; } /* descriptions wrap instead of widening the table */
.data-table--text .num { text-align: right; white-space: nowrap; }

/* ── Width and columns (Joel 2026-10-08, docs/ui/DESIGN.md): no page caps its
   width; cards and form fields take as many columns as the screen holds, so a
   wide monitor gets more of them instead of stretched ones or an empty half.
   Cards auto-FIT (a short row still spans the width); fields auto-FILL (a short
   field keeps a field's width instead of stretching across the monitor). ── */
.uo-grid { display: grid; gap: var(--space-4, 16px); grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.uo-fields { display: grid; gap: 12px 16px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr)); align-items: start; container-type: inline-size; }
/* A heading that only repeats the top bar's title: kept for screen readers and
   for a portal's standalone page (no bar there), not drawn twice under the bar.
   Below 1024px the bar truncates long titles, so the heading stays visible there. */
@media (min-width: 1024px) {
    .content-container .uo-bar-title { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
}
/* Two fields wide only when the grid itself holds two (a container query on the
   grid, not the viewport: a narrow card on a wide screen must not grow a stray column). */
.uo-fields > .uo-wide { grid-column: 1 / -1; }
@container (min-width: 31rem) { .uo-fields > .uo-wide { grid-column: span 2; } }
.uo-fields > .uo-full { grid-column: 1 / -1; } /* long text: a textarea, a URL, a dropzone */

/* ── KPI tile (components/_kpi.html) — Quiet Ledger: line, no accent rule.
   Color lives only on the delta, and only when it moved. ── */
.uo-kpi-row { display: grid; gap: var(--space-3, 12px); grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); }
.uo-kpi {
    display: flex; flex-direction: column; gap: 4px; min-width: 0;
    background: var(--bg-card); border: 1px solid var(--border-default);
    border-radius: var(--radius-md); padding: var(--space-4, 16px);
    color: inherit; text-decoration: none;
}
a.uo-kpi:hover, button.uo-kpi:hover { border-color: var(--border-strong); }
button.uo-kpi { text-align: left; font: inherit; cursor: pointer; }
.uo-kpi__label { font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); }
.uo-kpi__value { font-size: 1.5rem; font-weight: 700; color: var(--text-primary); font-variant-numeric: tabular-nums; line-height: 1.2; }
/* A value colored only when it IS a state against a target (kpi_tile value_tone=). */
.uo-kpi__value--success { color: var(--success); }
.uo-kpi__value--warning { color: var(--warning); }
.uo-kpi__value--danger { color: var(--danger); }
.uo-kpi__value--muted { color: var(--text-muted); }
.uo-kpi__delta { font-size: 0.75rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.uo-kpi__delta--success { color: var(--success); }
.uo-kpi__delta--danger { color: var(--danger); }
.uo-kpi__delta--neutral { color: var(--text-secondary); }
.uo-kpi__period { font-size: 0.75rem; font-weight: 400; color: var(--text-secondary); }

/* ── Text hierarchy: three sizes (Joel 2026-10-05, docs/ui/DESIGN.md) ──
   lg = what matters (the answer or figure a section exists for),
   md = section titles and secondary figures, sm = context, labels, notes. */
.uo-text-lg {
    font-family: var(--font-numeric); font-size: clamp(24px, 2.6vw, 32px); font-weight: 700;
    line-height: 1.2; letter-spacing: -.02em; font-variant-numeric: tabular-nums;
    color: var(--text-primary); overflow-wrap: anywhere;
}
.uo-text-md { font-size: 18px; font-weight: 600; line-height: 1.35; color: var(--text-primary); }
.uo-text-sm { font-size: 13px; font-weight: 400; line-height: 1.45; color: var(--text-secondary); }
/* A plain word with its short form and meaning on hover (components/_term.html). */
.uo-term { text-decoration: underline dotted; text-underline-offset: 3px; cursor: help; }

/* ── Section summary (components/_summary.html): the figures a section leads
   with, before its breakdown. ── */
.uo-summary {
    margin: 12px 0 16px; padding: 14px 16px;
    border-radius: var(--radius-lg); border-left: 3px solid var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 6%, var(--bg-card));
}
.uo-summary:first-child { margin-top: 0; }
.uo-summary__figures { display: flex; flex-wrap: wrap; gap: 12px 40px; margin: 0; }
.uo-summary__figures > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.uo-summary__figures dd { margin: 0; }
.uo-summary__line { margin: 8px 0 0; }

/* CEO pivot cards (portal/_ceo_pivot.html, _ceo_late_response.html) sit on
   .data-table; only the totals row, the muted zeros and the quiet drill-down
   links are theirs. Home and /reports/internal both render them. */
.ceo-card .data-table tfoot td { font-weight: 700; color: var(--text-primary); border-top: 2px solid var(--border-default); }
.ceo-card .data-table td.ceo-zero { color: var(--text-muted); opacity: 0.5; }
.ceo-card .data-table a { color: inherit; text-decoration: none; }
.ceo-card .data-table a:hover { color: var(--accent-primary); text-decoration: underline; }

/* ── Skeleton (components/_skeleton.html) ── */
.uo-skeleton { display: flex; flex-direction: column; gap: 10px; padding: var(--space-3, 12px) 0; }
.uo-skeleton__bar { display: block; height: 12px; border-radius: var(--radius-sm); }
.uo-skeleton__bar--lg { height: 22px; }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }

/* ── Show-more (components/_disclosure.html) ── one look for every closed
   detail: a chevron, the title at 13px, the body under it. `--card` is the
   framed one a step lists; plain (a line above) sits inside a card. ── */
.uo-disclosure { border-top: 1px solid var(--border-default); min-width: 0; }
.uo-disclosure > summary {
    cursor: pointer; padding: var(--space-2, 8px) 0; font-size: 13px; font-weight: 600; line-height: 1.4;
    color: var(--text-primary); list-style: none; display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
}
.uo-disclosure > summary::-webkit-details-marker { display: none; }
.uo-disclosure > summary::before {
    content: "›"; display: inline-block; width: 10px; text-align: center; color: var(--text-secondary);
    transition: transform var(--duration-normal) ease;
}
.uo-disclosure[open] > summary::before { transform: rotate(90deg); }
.uo-disclosure > summary:hover { color: var(--accent-primary); }
.uo-disclosure > summary:focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 2px; }
.uo-disclosure__body { padding-bottom: var(--space-3, 12px); min-width: 0; display: grid; gap: var(--space-3, 12px); }
/* A grid item keeps its content's width unless told otherwise: a wide table
   inside would push the page sideways on a phone instead of scrolling itself. */
.uo-disclosure__body > * { min-width: 0; }
.uo-disclosure--card { border: 1px solid var(--border-default); border-radius: var(--radius-lg); background: var(--bg-card); }
.uo-disclosure--card > summary { padding: 14px 16px; }
.uo-disclosure--card > .uo-disclosure__body { padding: 0 16px 16px; }
/* Context under a title, readable while closed (a whole line, small, not bold). */
.uo-disclosure__hint { flex-basis: 100%; padding-left: 16px; font-size: 13px; font-weight: 400; color: var(--text-secondary); white-space: pre-line; }

/* ── Card, label, field (Joel 2026-10-09, "cada cosa está construida en su
   modo"): the one box, the one small label and the one form field every
   workspace section is built from (docs/ui/DESIGN.md "Workspace anatomy").
   A state is the 3px bar on the left, never a tint. No card inside a card;
   a table (.data-table-wrap) is already its own frame. ── */
.uo-card {
    --tone: transparent;
    min-width: 0; padding: var(--space-4, 16px);
    background: var(--bg-card); border: 1px solid var(--border-default); border-radius: var(--radius-md);
    box-shadow: inset 3px 0 0 var(--tone);
}
.uo-card--success { --tone: var(--success); }
.uo-card--danger { --tone: var(--danger); }
.uo-card--warning { --tone: var(--warning-mark); }
.uo-card--accent { --tone: var(--accent-primary); }
.uo-card > :first-child { margin-top: 0; }
.uo-card > :last-child { margin-bottom: 0; }
/* Card titles, field labels, legends, table captions, tile labels: 13px,
   sentence case, the icon the text's size. */
.uo-label {
    display: inline-flex; align-items: center; gap: 6px; margin: 0;
    font-size: 13px; font-weight: 600; line-height: 1.4; letter-spacing: 0; text-transform: none;
    color: var(--text-secondary);
}
:is(h2, h3, h4, legend, caption).uo-label { display: flex; color: var(--text-primary); }
.uo-label :is(svg, i[data-lucide]) { width: 14px; height: 14px; flex: none; }
.uo-field { display: grid; gap: 4px; min-width: 0; align-content: start; }
.uo-field > .uo-label { font-weight: 500; }
.uo-field__value { font-size: 14px; line-height: 1.4; color: var(--text-primary); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.uo-fields--dense { gap: 6px 12px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 9.5rem), 1fr)); align-items: end; }

/* Reference-inspired shell: calm navigation, flat surfaces, restrained blue.
   Scoped to the sidebar nav (_sidebar.html) so other daisyUI menus keep their
   own colours, e.g. red destructive items. */
.sidebar-nav .menu li > a.active, .sidebar-nav .menu li > a:focus {
    border-left: 0;
    margin-left: 0;
}
.sidebar-nav .menu li > a:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}
.sidebar-nav .menu li > a:not(.active), .sidebar-nav .menu li > details > summary {
    color: var(--text-secondary) !important;
}
/* Rows like an elite SaaS (Joel 2026-10-08: "organízalo como un SaaS de élite"):
   32px rows, quiet icons, air between sections. The lit row is a soft fill and a
   3px blue bar: colour only as a mark (docs/ui/DESIGN.md), not a solid pill. */
.sidebar-nav .menu li > a { min-height: 32px; padding-block: 5px; border-radius: var(--radius-md); font-size: 14px; }
.sidebar-nav .menu li > a:not(.active) svg { opacity: .7; }
.sidebar-nav .menu li > a:not(.active):hover { background: var(--bg-card-hover); color: var(--text-primary) !important; }
.sidebar-nav .menu li > a:not(.active):hover svg { opacity: 1; }
.sidebar-nav .menu > .menu-title { margin-top: 16px; padding-block: 2px 6px; font-weight: 600; letter-spacing: .08em; }
.sidebar-nav .menu li > a.active {
    background: var(--bg-card-hover) !important;
    color: var(--text-primary) !important;
    box-shadow: inset 3px 0 0 var(--accent-primary);
    font-weight: 600;
}
.sidebar-nav .menu li > a.active svg { color: var(--accent-primary); }
.sidebar-brand, .brand-wordmark {
    font-family: var(--font-body);
    font-weight: 700;
    letter-spacing: -.035em;
}
.portal-home .po-card:hover { transform: none; }
.portal-home .po-card__icon {
    border-color: transparent;
    background: var(--accent-primary-bg);
}
.portal-home :is(.po-eyebrow, .po-tile__label) {
    text-transform: none;
    letter-spacing: normal;
}

/* DataMerch category capsule — a deliberate exception to "colour is a dot on a
   neutral badge" (Joel 2026-10-13: DataMerch must look like the DataMerch portal).
   Filled pill in the portal's own colours, the same values as the [cd] note's
   _CAT_COLORS in pipeline/orchestration/comment/sections_datamerch_cd.py. Scope:
   DataMerch notes only; nothing else may use .dm-cat. */
.dm-cat {
    display: inline-block; padding: .375rem 1rem; border-radius: 999px;
    font-size: .8125rem; font-weight: 500; line-height: 1.3; white-space: nowrap;
    color: #ffffff; background: #6b7280;
}
.dm-cat--satisfied { background: #43a047; }
.dm-cat--default { background: #0e7b67; }
.dm-cat--stacking { background: #8a1f69; }
.dm-cat--slow { background: #c69745; }
.dm-cat--suspicious { background: #c02b30; }
.dm-cat--open-funding { background: #e26aa9; }
.dm-cat--other { background: #33516e; }
.dm-cat--fraud { background: #ef4444; }
.dm-cat--nsf { background: #a855f7; }
