/* Audotoria Cuentas — global styles.
   Kept intentionally small: component-specific styles ship with their own tasks.
   The palette is the Óptima Auto brand, mirroring Theme/AppTheme.cs: the gold #F2B735 is
   the accent, #222222 the readable ink, and the chrome is black in both modes. ⚠ Read
   AppTheme's remarks before using the gold as a TEXT colour — it measures 1.81:1 on
   white. */

html, body {
    margin: 0;
    padding: 0;
    font-family: Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* ---- Startup splash (shown until Blazor boots, then replaced) ---- */
.app-loading {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    /* --optima-black, flat: OptimaAuto's own shell has no gradient, and this splash is
       the first frame of the application. */
    background: #1A1A1A;
    color: #FFFFFF;
    font-family: Inter, Roboto, "Helvetica Neue", Arial, sans-serif;
}

.app-loading__mark {
    width: 3.5rem;
    height: 3.5rem;
    margin-bottom: 0.75rem;
    border-radius: 999px;
    background: #F2B735;
    animation: app-loading-pulse 1.2s ease-in-out infinite;
}

.app-loading__title {
    font-size: 1.6rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.app-loading__subtitle {
    font-size: 0.95rem;
    font-weight: 400;
    opacity: 0.85;
}

.app-loading__hint {
    margin-top: 0.5rem;
    font-size: 0.8rem;
    opacity: 0.7;
}

@keyframes app-loading-pulse {
    0%, 100% { transform: scale(1); opacity: 0.9; }
    50% { transform: scale(1.15); opacity: 0.5; }
}

@media (prefers-reduced-motion: reduce) {
    .app-loading__mark { animation: none; }
}

/* ---- Accessibility: visible keyboard focus using the brand gold ----
   ⚠ A focus RING, not text: WCAG 1.4.11 asks 3:1 against what it sits on, and the gold
   clears that against every surface in this app except plain white cards, where the ring's
   2px width and offset keep it visible. Kept gold in both modes deliberately — a focus
   indicator that changes colour with the theme is one more thing to get wrong. */
:focus-visible {
    outline: 2px solid #F2B735;
    outline-offset: 2px;
}

/* ---- Scrollbar styling ---- */
* {
    scrollbar-width: thin;
    scrollbar-color: #B7C2CC transparent;
}

*::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

*::-webkit-scrollbar-thumb {
    background: #B7C2CC;
    border-radius: 6px;
    border: 2px solid transparent;
    background-clip: content-box;
}

*::-webkit-scrollbar-thumb:hover {
    background: #8A97A3;
    background-clip: content-box;
}

/* ---- Blazor unhandled-error banner ---- */
#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.blazor-error-boundary {
    background: #b32121;
    padding: 1rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "Ha ocurrido un error.";
    }

/* ============================================================================
   Shared component styles (Task 4). These live in the GLOBAL sheet — not in
   scoped .razor.css — because they are applied to markup rendered outside the
   component (MudDataGrid row classes, StatusBadge pills reused across pages).
   Dark-mode variants hang off `.app-dark` on <html>, toggled by ThemeService via
   appSetDarkClass (app.js) — MudBlazor 8.x never adds a `.mud-theme-dark`
   ancestor class; dark mode only swaps :root CSS variables.
   ============================================================================ */

/* Tabular figures for money / KPI hero numbers. */
.tabular-nums {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum";
}

/* ---- Status pill badge (StatusBadge.razor). Colours ported from statusBadge()
        sb-* classes in ProspectsRegistryV2.aspx. ---- */
.status-badge {
    display: inline-block;
    padding: 3px 12px;
    border-radius: 20px;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
    line-height: 1.5;
}
.status-badge.sb-green  { background: #D1FAE5; color: #065F46; }
.status-badge.sb-red    { background: #FEE2E2; color: #991B1B; }
.status-badge.sb-yellow { background: #FEF3C7; color: #92400E; }
.status-badge.sb-gray   { background: #F3F4F6; color: #374151; }

.app-dark .status-badge.sb-green  { background: rgba(16,185,129,.20);  color: #A7F3D0; }
.app-dark .status-badge.sb-red    { background: rgba(220,38,38,.22);   color: #FCA5A5; }
.app-dark .status-badge.sb-yellow { background: rgba(245,158,11,.22);   color: #FCD34D; }
.app-dark .status-badge.sb-gray   { background: rgba(148,163,184,.20);  color: #CBD5E1; }

/* ---- Due-date row tints (AppDataGrid RowClassFunc; ported from renderRow()).
        due-soon = within 3 days (red); due-today = 0/1 day out (amber). ---- */
.due-soon td  { background: #FF8585 !important; }
.due-today td { background: #FFD887 !important; }

.app-dark .due-soon td  { background: rgba(220,38,38,.18)  !important; }
.app-dark .due-today td { background: rgba(245,158,11,.20) !important; }

/* ---- Disabled prospect row (RegistryPage RowClassFunc). Only ever seen under the
        "Registros: Deshabilitados / Todos" filter — the default view returns no such rows.
        Muted rather than tinted: this is the ABSENCE of urgency, the opposite of what the
        due-date tints above communicate, so it reads as greyed-out and set aside. The
        RowClassFunc gives it precedence over those tints (a disabled line's due date no
        longer means anything). Text stays legible — reviewing what was switched off is
        exactly why the filter exists. ---- */
.reg-row-disabled td {
    background: rgba(148,163,184,.14) !important;
    color: var(--mud-palette-text-secondary);
}
/* The prospect name keeps its bold weight from .reg-name; drop the emphasis so a disabled
   row cannot out-shout the active ones around it in the "Todos" view. */
.reg-row-disabled td .reg-name { font-weight: 400; }

.app-dark .reg-row-disabled td { background: rgba(148,163,184,.10) !important; }

/* Neutral outline on the chip itself so it reads as a state marker, not an action. */
.op-disabled-chip { opacity: .85; }

/* Phone-tier equivalent of .reg-row-disabled (RegistryCardList). Same intent: muted, set
   aside, still readable. Kept as opacity on the whole card rather than per-element colours —
   a card is one object, and its border stripe should fade with it. */
.reg-card-disabled { opacity: .62; }
.reg-card-disabled .op-board__name { font-weight: 500; }

/* ---- Deshabilitado banner (ProspectFormPage). Plain flow, NOT a flex row, and no button:
        the Rehabilitar action sits beside "Datos Generales" instead. That is deliberate and
        worth keeping. MudAlert renders ChildContent inside a .mud-alert-message div that does
        not stretch its child, so any attempt to right-pin a control in here shrink-wraps the
        row and paints the control ON TOP of the message. Block-level text simply cannot do
        that. If this banner ever needs an action again, put it in the card header. ---- */
.op-disabled-banner__text {
    min-width: 0;
}
.op-disabled-banner__meta {
    margin-top: 4px;
    font-size: .85em;
    opacity: .9;
}

/* Consequence list in DisableProspectDialog. The app's CSS reset strips list markers, and
   this is the one place where the bullets carry meaning — each line is a separate thing that
   stops happening. */
.op-disable-list {
    margin: 0 0 12px 0;
    padding-left: 20px;
    list-style: disc outside;
    font-size: .875rem;
    line-height: 1.6;
    color: var(--mud-palette-text-secondary);
}
.op-disable-list li { margin-bottom: 2px; }

/* ---- LOB icon (LobIcon.razor) ---- */
.lob-icon { line-height: 1; display: inline-flex; align-items: center; }

/* ---- Registry: sticky right "Acciones" column (RegistryPage grid). Global (not
        scoped) because it attaches to MudDataGrid cells via CellClass/HeaderClass.
        Single-class specificity so the .due-soon/.due-today row tints (which carry
        !important) still win on the sticky cell — matching the legacy edit-cell.
        Uses MudBlazor palette vars so the opaque background is correct in light + dark. ---- */
/* Positioning is MudBlazor's job now — the column sets StickyRight="true", which stamps
   `sticky-right` on BOTH the header and body cells. (The old hand-rolled `position:
   sticky` here lost to MudBlazor's 3-class `.mud-data-grid .mud-table-cell.sticky-right`
   rule on the header, which is why only the icons stayed put.) What's left here is
   cosmetics. */
.reg-actions {
    text-align: center;
    border-left: 1px solid var(--mud-palette-lines-default);
}

/* Match MudBlazor's own specificity (0,3,0) so the sticky cell keeps the surface colour
   instead of its default `background-gray`. app.css loads after MudBlazor.min.css, so an
   equal-specificity rule wins. Deliberately NOT !important: the .due-soon/.due-today row
   tints carry !important and must still paint the sticky cell like the rest of the row. */
.mud-data-grid .mud-table-cell.reg-actions {
    background-color: var(--mud-palette-surface);
}

/* Header must sit above the body's sticky cells so rows scroll cleanly underneath it. */
.mud-data-grid .mud-table-head .mud-table-cell.reg-actions {
    z-index: 4;
}

/* ---- Mirrored horizontal scrollbar above a wide grid (see appSyncTopScroll in app.js).
        The element is created by JS and holds a single spacer div sized to the table's
        scrollWidth; only the scrollbar itself is ever visible. `overflow-x: scroll` (not
        auto) plus an explicit height keeps it present rather than an overlay that fades
        out, which would defeat the point of putting it at the top. ---- */
.top-scrollbar {
    overflow-x: scroll;
    overflow-y: hidden;
    height: 12px;
    margin-bottom: 2px;
}

.top-scrollbar > div { height: 1px; }

.top-scrollbar::-webkit-scrollbar { height: 10px; }

.top-scrollbar::-webkit-scrollbar-thumb {
    background-color: var(--mud-palette-lines-default);
    border-radius: 5px;
}

.top-scrollbar::-webkit-scrollbar-thumb:hover {
    background-color: var(--mud-palette-action-disabled);
}

.top-scrollbar::-webkit-scrollbar-track { background: transparent; }

/* ---- Registry cell text colours (RegistryPage grid). GLOBAL (not scoped) because these attach
        to MudDataGrid cells rendered outside the component's scoped-style boundary — same reason
        as .reg-actions. Each carries an `.app-dark` variant so the text stays readable in dark
        mode (the raw #990906 / #1F1F1F are near-invisible on the dark surface). ---- */
.reg-due { font-weight: bold; color: #990906; }
.app-dark .reg-due { color: #FCA5A5; }

.reg-view { color: #1F1F1F; font-size: 16px; }
.app-dark .reg-view { color: #CBD5E1; }

/* ---- Registry: width compaction (RegistryPage grid). Global for the same reason as the
        .reg-* rules above — these attach to MudDataGrid cells rendered outside the component's
        scoped-style boundary. ---- */

/* Free-text cells (Prospecto, Productor, Compañía, Agencia, Sucursal, Asignado) truncate instead
   of stretching their column to the longest value on the page, which is what pushed the grid past
   the viewport. The max-width is set PER COLUMN inline at each column definition and must live on
   this span, not on the cell: an auto-layout table ignores max-width on a <td>. Each span also
   carries a native `title`, so the full value is one hover away. */
.reg-trunc {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Tighter horizontal padding, this grid only (the wrapper div carries .reg-grid).
   Specificity is load-bearing: MudBlazor 8.15's dense rule is
   `.mud-table-dense * .mud-table-row .mud-table-cell` (0,3,0) and it sets the LOGICAL
   longhands (padding-inline-start/end), so the 2-class physical-only rule that used to live
   here never applied — cells were really 16px/24px, and the 92px date-column widths were
   below min-content and silently ignored. Matching (0,3,0) plus app.css's later load order
   wins without !important, which matters: !important would outrank the .reg-actions
   background (0,3,0) and collide with the .due-soon/.due-today row tints. Both the physical
   and logical longhands are set so neither of MudBlazor's two declarations survives.
   Still padding-only, on purpose.
   NOT overridden: MudBlazor's `….mud-table-cell:last-child { padding-right: 16px }` (0,4,0).
   The last cell is the sticky Acciones column and 16px is the right outer-edge gap. */
.reg-grid .mud-table-row .mud-table-cell {
    padding-left: 8px;
    padding-right: 8px;
    padding-inline-start: 8px;
    padding-inline-end: 8px;
}

/* Date columns (Recibido / Entrado / Efectividad / Entregable) share ONE width so their
   identical MM/dd/yyyy values line up. Measured against this app's own Inter 500 @ 14px
   (--mud-typography-default-size is .875rem and AppTheme only overrides the family):

     date value   81.1px   (Entregable is bold -> 84.1px)   body floor 97.1 / 100.1px
     labels       Recibido 58.2   Entrado 52.1   Efectividad 75.2   Entregable 72.3

   Every label is NARROWER than the date under it, so labels alone would not matter. What made
   the columns diverge is the header chrome MudBlazor always reserves: a sort MudIconButton plus
   (ShowColumnOptions, default true) a MoreVert menu button, each ~28px, both opacity:0 until
   hover. label + ~58px of invisible chrome + 16px padding is why they rendered ~127/125/144/151
   -- each column sized to its own label -- and why the old width:92px was never reachable (the
   digits alone need 97px). The menu is off per-column in RegistryPage.razor; this rule strips
   the sort button's padding so its footprint is exactly 18px + its 4px margin. Worst floor is
   then Efectividad 75.2 + 22 + 16 = 113.2px, so the declared 116px wins on all four -- and
   holds in every sort state, since the gutter is reserved whether the arrow shows or not.
   Keep the 116px in RegistryPage.razor in sync with the 113.2px arithmetic above. */
.reg-grid th.reg-date .sort-direction-icon {
    padding: 0;
    min-width: 18px;
    width: 18px;
    height: 18px;
}

/* ---- Utility: opt out of MudButton's default uppercasing (MainLayout account button). ---- */
.text-transform-none { text-transform: none; }

/* ---- Reportes: shared card + matrix-table language (Pages/Reports/Sections/*). ----
   GLOBAL rather than an isolated .razor.css per section: every report section renders the same
   heading / parameter / result-table structure, and CSS isolation scopes rules per COMPONENT, so
   an isolated copy would have to be duplicated into each new report. Global also drops the ::deep
   that .mtx-export-btn needed to reach the class MudButton renders. */
.mtx-heading {
    display: flex;
    align-items: center;
    gap: 9px;
    font-weight: 800;
    font-size: 1.05rem;
    letter-spacing: -0.01em;
    color: var(--mud-palette-text-primary);
}

.mtx-heading i { color: var(--mud-palette-primary); }

.mtx-field-label {
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 6px;
}

.mtx-export-btn {
    text-transform: none;
    color: var(--op-green-600, #0A8E6E);
    font-weight: 600;
}

.mtx-loading {
    display: flex;
    align-items: center;
    color: var(--mud-palette-text-secondary);
    font-size: 0.9rem;
}

/* Embudo de entrada (Reportes → Tiempo entre estatus). A milestone is a COUNT of lines at a
   point in the flow; a span is the DURATION between two milestones. Keeping them visually
   distinct is the point of the panel — the counts shrink left-to-right while the durations
   sit between them. */
.mtx-mile {
    border: 1px dashed var(--mud-palette-lines-default);
    border-radius: 9px;
    background: var(--mud-palette-background-grey);
    padding: 8px 14px;
    min-width: 96px;
    text-align: center;
}

.mtx-mile-t {
    font-size: .68rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    font-weight: 600;
}

.mtx-mile-v {
    font-size: 1.25rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.25;
}

.mtx-span {
    flex: 1 1 130px;
    min-width: 130px;
    text-align: center;
    padding: 0 8px;
}

.mtx-span-l {
    font-size: .72rem;
    font-weight: 600;
    color: var(--mud-palette-primary);
}

.mtx-span-v {
    font-size: 1.05rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    border-top: 2px solid var(--mud-palette-primary);
    margin-top: 4px;
    padding-top: 4px;
}

.mtx-span-s {
    font-size: .68rem;
    color: var(--mud-palette-text-secondary);
    font-variant-numeric: tabular-nums;
}

.mtx-empty {
    color: var(--mud-palette-text-secondary);
    padding: 12px 4px;
}

.mtx-wrap {
    overflow-x: auto;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--op-r-sm, 10px);
}

.mtx-table thead th {
    background: var(--op-surface-2, #F7F9FB);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
}

.mtx-table th.mtx-num,
.mtx-table td.mtx-num {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Username under the display name (Fuera de oficina summary). */
.mtx-sub {
    font-size: 0.72rem;
    color: var(--mud-palette-text-secondary);
    line-height: 1.2;
}

.mtx-table tfoot th {
    border-top: 2px solid var(--mud-palette-lines-default);
    font-weight: 800;
    background: var(--op-surface-2, #F7F9FB);
}

/* ============================================================================
   Óptima Seguros — redesign layer.
   APPEND this to wwwroot/css/app.css (after the existing content).
   Dark mode keys off `.app-dark` on <html> (set by ThemeService/appSetDarkClass),
   exactly like the existing status-badge / due-row rules.
   ============================================================================ */

:root {
    /* ── Óptima Auto brand, adopted from OptimaAuto's optima-theme.css :root ──
       ⚠ --op-gold IS A DARK-SURFACE COLOUR. Measured 2026-09-03: #F2B735 as text reads
       1.81:1 on white (WCAG AA needs 4.50) and 9.61:1 on --op-sidebar-bg. So it is used
       for text ONLY on the black chrome and in dark mode, and as a FILL — with dark text
       on it — everywhere else. Never set it as a text colour on a white card. */
    --op-navy: #1A1A1A;                       /* was a navy gradient; now --optima-black */
    --op-brand: #222222;                      /* --optima-dark: the readable brand ink */
    --op-brand-050: #F0F0F0;                  /* --optima-light-gray */
    --op-gold: #F2B735;                       /* --optima-primary */
    --op-gold-600: #D9A02E;                   /* --optima-primary-hover */
    --op-gold-050: #FDF4E0;
    --op-green: #4CAF50;                      /* --optima-status-active */
    --op-green-600: #3D9140;
    --op-green-050: #EDF7EE;
    --op-info: #2196F3;                       /* --optima-status-quote */
    --op-info-050: #E9F4FE;
    --op-red: #F44336;                        /* --optima-status-cancelled */
    --op-red-050: #FEECEA;
    --op-amber: #FF9800;                      /* --optima-status-pending */
    --op-bg: #F0F0F0;
    --op-surface: #FFFFFF;
    --op-surface-2: #F7F7F7;
    --op-line: #DDDDDD;
    --op-line-2: #E8E8E8;
    --op-text: #333333;                       /* --optima-text */
    --op-muted: #6B6B6B;
    --op-muted-2: #888888;                    /* --optima-medium-gray */

    /* The chrome is black-and-gold in BOTH modes — it is the most recognizable half of
       the OptimaAuto look, and it is the one surface the gold is legible on. */
    --op-sidebar-bg: #1A1A1A;
    --op-sidebar-line: #2A2A2A;
    --op-shadow-sm: 0 1px 2px rgba(16,32,48,.05);
    --op-shadow: 0 2px 4px rgba(16,32,48,.05), 0 8px 20px rgba(16,32,48,.06);
    --op-shadow-lg: 0 12px 40px rgba(16,32,48,.14);
    --op-r: 14px;
    --op-r-sm: 10px;
}

.app-dark {
    /* Dark mode IS OptimaAuto: --optima-black body, --optima-dark surfaces, gold primary.
       This is the mode the app opens in. */
    --op-navy: #1A1A1A;
    --op-brand: #F2B735;                      /* the gold works as text here: 9.61:1 */
    --op-brand-050: #2A2415;
    --op-gold-600: #F5C85C;                   /* --optima-primary-light */
    --op-gold-050: #2A2415;
    --op-green: #4CAF50;
    --op-green-600: #66BB6A;
    --op-green-050: #16241A;
    --op-info: #2196F3;
    --op-info-050: #13212C;
    --op-red: #F44336;
    --op-red-050: #2A1613;
    --op-amber: #FF9800;
    --op-bg: #1A1A1A;                         /* --rz-body-background-color */
    --op-surface: #222222;                    /* --rz-base-background-color */
    --op-surface-2: #2A2A2A;                  /* --rz-input-background-color */
    --op-line: #444444;
    --op-line-2: #333333;
    --op-text: #FFFFFF;
    --op-muted: #CCCCCC;                      /* --optima-text-light */
    --op-muted-2: #888888;
    --op-shadow-sm: 0 1px 2px rgba(0,0,0,.4);
    --op-shadow: 0 2px 8px rgba(0,0,0,.4);
    --op-shadow-lg: 0 16px 50px rgba(0,0,0,.6);
}

/* ---- App shell (MainLayout) ----
   The sidebar width is a custom property so the collapsed rail is a single override below
   (and a future full-hide state would just be another value).

   --op-topbar-h is the height of the sticky top bar, published as a custom property because
   anything else that pins itself inside the page has to offset by exactly this much (the prospect
   form's tab strip, the docked reference pane). Custom properties inherit through the DOM
   regardless of which stylesheet declared them, so scoped .razor.css files can read it too.
   MudBlazor's global `* { box-sizing: border-box }` means this value already includes the bar's
   1px border-bottom — the offset is 66px, not 67. */
.op-shell { --op-sidebar-w: 260px; --op-topbar-h: 66px; display: grid; grid-template-columns: var(--op-sidebar-w) 1fr; min-height: 100vh; background: var(--op-bg); color: var(--op-text); transition: grid-template-columns .18s ease; }

.op-sidebar { background: var(--op-sidebar-bg); color: #fff; display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; border-right: 1px solid var(--op-sidebar-line); }
.op-sidebar__brand { padding: 22px 22px 18px; display: flex; align-items: center; border-bottom: 1px solid var(--op-sidebar-line); }
.op-sidebar__logo { height: 30px; width: auto; object-fit: contain; }
.op-sidebar__nav { padding: 16px 14px; flex: 1; overflow-y: auto; }
.op-sidebar__section { font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: rgba(255,255,255,.42); padding: 10px 12px 6px; }

/* Drawer footer — the identity + theme + logout controls that the top bar sheds in the phone
   tier. `display: none` here and flipped to flex only in that tier, so it never exists on a
   desktop sidebar (where the top bar still owns those controls). Its buttons are .op-drawerbtn
   rather than .op-iconbtn: this panel is navy, and .op-iconbtn paints a light surface. */
.op-sidebar__foot { display: none; flex-direction: column; gap: 10px; padding: 14px; border-top: 1px solid rgba(255,255,255,.1); }
.op-sidebar__footuser { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.op-sidebar__footname { font-size: 13.5px; font-weight: 700; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.op-sidebar__footrole { font-size: 11.5px; font-weight: 600; color: var(--op-gold); }
.op-sidebar__footactions { display: flex; align-items: center; gap: 8px; }
.op-drawerbtn { flex: 1 1 auto; display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 40px; padding: 0 12px; border: 1px solid rgba(255,255,255,.22); border-radius: 10px; background: rgba(255,255,255,.06); color: rgba(255,255,255,.9); font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer; transition: background .15s; }
.op-drawerbtn:hover { background: rgba(255,255,255,.14); color: #fff; }

/* images/optima-logo.png is the COLOUR wordmark (black type, gold mark) — the only brand art in
   the repo. Both places it appears are navy, so it is flattened to white here rather than shipping
   a second asset. brightness(0) first: plain invert() would turn the gold mark blue. */
.op-sidebar__logo { filter: brightness(0) invert(1); }

/* ---- Nav links (NavMenu) ---- */
.op-nav { display: flex; flex-direction: column; gap: 3px; }
.op-nav__link { display: flex; align-items: center; gap: 13px; padding: 11px 13px; border-radius: 10px; cursor: pointer; color: rgba(255,255,255,.85); font-size: 14.5px; font-weight: 500; position: relative; text-decoration: none; transition: background .15s; }
/* The gold tints are OptimaAuto's own: .08 on hover, .12 on the active item. */
.op-nav__link:hover { background: rgba(242,183,53,.08); color: #fff; }
.op-nav__icon { width: 20px; text-align: center; font-size: 16px; color: rgba(255,255,255,.7); }
.op-nav__link.active { background: rgba(242,183,53,.12); color: var(--op-gold); }
.op-nav__link.active::before { content: ""; position: absolute; left: 0; top: 9px; bottom: 9px; width: 3px; border-radius: 3px; background: var(--op-gold); }
.op-nav__link.active .op-nav__icon { color: var(--op-gold); }

/* ---- Collapsed sidebar: icon-only rail (SidebarService / the top-bar toggle + Ctrl+B) ----
   Everything is driven off the one class on .op-shell, so nothing but the width and the label
   visibility changes — the gold active bar, hover states and dark mode all carry over as is.

   GATED TO >=960px on purpose. Below that the sidebar is an off-canvas drawer (see the drawer
   tier further down), and the drawer is always the full-width, labelled panel. Without this gate
   a user whose wfp.sidebarCollapsed is persisted `true` would slide out a 72px label-less sliver
   on their phone. Gating the whole block is far less code than overriding each rule back, and it
   states the intent: the rail is a desktop affordance. */
@media (min-width: 960px) {
    .op-shell--rail { --op-sidebar-w: 72px; }
    .op-shell--rail .op-nav__link { justify-content: center; gap: 0; padding: 11px 0; }
    /* :not(.op-nav__badge) is load-bearing — the unread count is a <span> in here too, and a bare
       `span` selector would silently hide it instead of just the label. */
    .op-shell--rail .op-nav__link > span:not(.op-nav__badge) { display: none; }
    /* With the label gone `margin-left: auto` (see .op-nav__badge below) has nothing to push
       against, so the count re-anchors as a corner dot — .op-nav__link is already relative. */
    .op-shell--rail .op-nav__badge {
        position: absolute; top: 2px; right: 4px; margin-left: 0;
        min-width: 17px; padding: 0 4px; font-size: 9.5px; line-height: 17px;
        pointer-events: none;
    }
    /* "Flujo de trabajo" cannot wrap into 72px. */
    .op-shell--rail .op-sidebar__section { display: none; }
    .op-shell--rail .op-sidebar__nav { padding: 16px 10px; }
    /* images/optima-logo.png is the wordmark — the repo has no square mark to swap in — so it just
       scales down. The row itself stays for its bottom border, which lines the sidebar's top block
       up with the 66px top bar. */
    .op-shell--rail .op-sidebar__brand { padding: 22px 0 18px; justify-content: center; }
    .op-shell--rail .op-sidebar__logo { max-width: 46px; height: auto; }
}

/* ---- Main column + top bar ---- */
.op-main { display: flex; flex-direction: column; min-width: 0; }
.op-topbar { position: sticky; top: 0; z-index: 20; background: var(--op-surface); border-bottom: 1px solid var(--op-line); padding: 0 28px; height: var(--op-topbar-h); display: flex; align-items: center; gap: 20px; }
.op-topbar__title { display: flex; flex-direction: column; min-width: 0; }
.op-topbar__crumb { font-size: 12px; color: var(--op-muted); font-weight: 600; }
.op-topbar__page { font-size: 19px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }
.op-topbar__actions { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.op-iconbtn { width: 40px; height: 40px; border: 1.5px solid var(--op-line); border-radius: 11px; background: var(--op-surface); color: var(--op-text); cursor: pointer; font-size: 15px; display: flex; align-items: center; justify-content: center; transition: .15s; }
.op-iconbtn:hover { border-color: var(--op-brand); background: var(--op-surface-2); }
/* Busy state for Shared/RefreshButton: spin the icon where it stands. Covers both icon shapes the
   app uses — MudBlazor's Icons.Material.* render as an inline <svg class="mud-icon-root">, the
   vendored FontAwesome ones as <i>. The <i> needs inline-block or the transform is ignored
   (transforms don't apply to non-replaced inline elements), and transform-origin is set explicitly
   rather than trusted to default to the element's centre. */
@keyframes op-spin { to { transform: rotate(360deg); } }
.op-spin .mud-icon-root, .op-spin i { display: inline-block; transform-origin: 50% 50%; animation: op-spin .9s linear infinite; }
/* Sidebar toggle: first item in the row, so it pulls in against .op-topbar's 20px gap and never
   shrinks when the row gets tight. */
.op-topbar__toggle { flex-shrink: 0; margin-right: -6px; }
/* Drawer toggle — the same slot, for the drawer tier only. Two buttons rather than one that
   changes meaning: see the note in the drawer tier below. */
.op-topbar__burger { display: none; flex-shrink: 0; margin-right: -6px; }
/* DELIBERATELY NO `overflow` HERE — do not re-add it.
   This box has no height and no `flex: 1`, so it just grows with its content and never overflows;
   the DOCUMENT is what scrolls. But `overflow-y: auto` still makes an element a scroll container,
   and `position: sticky` resolves against its nearest scroll-container ancestor — so every sticky
   descendant was pinning itself to a scrollport that never moves, i.e. doing nothing at all. That
   silently broke the prospect form's tab strip and the docked reference pane (.dock). .op-topbar
   and .op-sidebar were never affected because they sit ABOVE this boundary, in .op-main/.op-shell.

   It also means this box used to contain horizontal overflow app-wide as a side effect (per spec
   `overflow-x: visible` + `overflow-y: auto` coerces the pair to `auto`). Wide content owns its own
   containment instead — .mud-table-container and .op-board both scroll themselves. If something
   ever does leak a page-level h-scrollbar, use `overflow-x: clip`: unlike `auto`, `clip` does not
   establish a scrollport, so it won't re-break sticky. */
.op-content { padding: 26px 28px 60px; }

/* ---- Responsive shell: two tiers, on MudBlazor 8.15's own boundaries -------------------------
   The boundaries are MudBlazor's md (960) and sm (600) so CSS media queries and C# `MudHidden`
   /`IBreakpointService` can never disagree about which layout is on screen — the registry swaps
   its grid for a card list at the same 600px this file uses for phone density.

     <= 959.98px  "drawer tier"  the sidebar leaves the grid and becomes an off-canvas drawer
     <= 599.98px  "phone tier"   density: padding, top-bar shedding, icon-only tab strip

   Everything at 960px and up is untouched by both blocks.

   Why the sidebar had no mobile story before: .op-shell is `grid-template-columns:
   var(--op-sidebar-w) 1fr` and .op-sidebar is `position: sticky` IN FLOW, so it always reserved
   its 260px track. At a 430px viewport that left ~104px after .op-content's padding, and ~40px
   after the tab panel's and the card's `pa-4` — which is why every field rendered as a ~34px box
   with a truncated label. The fields were never the problem: every MudItem in the prospect form
   already declares xs="12". They were starved. */

/* Backdrop behind the open drawer. A real <button> (MainLayout renders it) so Enter/Space and
   focus-visible work with no JS. touch-action: none keeps the page behind from scrolling under
   the finger — a CSS-only stand-in for a body scroll lock. z-index 30 sits between .op-topbar's
   20 and the drawer's 40. */
.op-scrim { display: none; position: fixed; inset: 0; z-index: 30; margin: 0; padding: 0; border: 0; background: rgba(15, 23, 42, .45); cursor: pointer; touch-action: none; }

/* In-page side-nav paper (ConfigPage / ReportsPage). Both already stack via `flex-md-row`, but a
   fixed 260px box stranded in a ~400px column reads as broken, so it goes full-width when
   stacked. Replaces an inline `Style="flex:0 0 260px; width:260px;"` on both pages, which no
   media query could reach. */
.op-sidenav { flex: 0 0 260px; width: 260px; }

/* ---- Asistente IA rail (ChatRail / ChatRailService) ------------------------------------
   A THIRD grid column on .op-shell, the mirror image of how --op-sidebar-w drives the first.
   MainLayout renders the .op-shell--chat modifier and the --op-chat-w inline value only while
   the rail is open, and renders no <aside> at all while it is closed — so the default
   two-column grid above is byte-for-byte untouched and closing the rail cannot regress it.

   IN-GRID ONLY AT >=1280px, the same boundary the prospect form's docked split uses, so "there
   is room for another column" means one thing across the app. Below it the rail is an overlay
   (see the tier further down) — a 380px third track on a 1024px screen would leave the form
   narrower than the phone layout it is supposed to beat.

   .op-content is deliberately NOT touched: the rail is a sibling of .op-main, so the document
   is still the page's scrollport and the no-overflow rule above still holds. The transcript
   scrolls inside itself (ChatRail.razor.css), which is safe because nothing in the rail uses
   page-level sticky. */
@media (min-width: 1280px) {
    /* minmax(0, 1fr) rather than the bare 1fr the two-column rule uses: the middle track has to
       be allowed to SHRINK when the third column appears, and a bare 1fr floors at min-content. */
    .op-shell--chat { grid-template-columns: var(--op-sidebar-w) minmax(0, 1fr) var(--op-chat-w, 380px); }
}

/* Full-height sticky column, the same recipe as .op-sidebar: the document scrolls, the rail
   rides along. z-index 15 sits below .op-topbar's 20 — the bar stays on top where they meet.
   `position: relative` is what the drag divider's absolute positioning anchors to at the wider
   tier; the overlay tier below replaces it with `fixed`, which anchors just as well. */
.op-chatrail {
    position: sticky;
    top: 0;
    height: 100vh;
    min-width: 0;
    z-index: 15;
    display: flex;
    flex-direction: column;
    background: var(--mud-palette-surface, #fff);
    border-left: 1px solid var(--mud-palette-lines-default, rgba(0,0,0,.12));
}

/* Backdrop for the rail overlay. Its OWN class rather than .op-scrim: that one is display:none
   until 960px (the nav-drawer tier), and the rail starts overlaying at 1280px. z-index 35 puts
   it above .op-topbar (20) and below the nav drawer (40), so with both open the drawer still
   wins — it is the more modal of the two. */
.op-chatscrim { display: none; position: fixed; inset: 0; z-index: 30; margin: 0; padding: 0; border: 0; background: rgba(15, 23, 42, .45); cursor: pointer; touch-action: none; }

@media (max-width: 1279.98px) {
    /* Overlay from the right. Same `inset` (not `height: 100vh`) reasoning as the nav drawer:
       on mobile browsers 100vh is the pre-URL-bar height, so a vh-sized fixed panel hides its
       own composer below the fold. 92vw keeps a strip of page visible as an affordance that
       tapping outside closes it. */
    .op-chatrail {
        position: fixed;
        inset: 0 0 0 auto;
        height: auto;
        width: min(var(--op-chat-w, 380px), 92vw);
        z-index: 35;
        box-shadow: var(--op-shadow-lg);
    }
    .op-chatscrim { display: block; }
}

@media (max-width: 599.98px) {
    /* Phone: a full sheet. Anything narrower than the viewport wastes width the transcript needs
       and leaves a dead strip that is too thin to read through. */
    .op-chatrail { width: 100vw; }
}

@media (max-width: 959.98px) {
    /* One column. The sidebar is out of flow below, so nothing occupies the first track. */
    .op-shell { grid-template-columns: minmax(0, 1fr); }

    /* Off-canvas drawer. `inset: 0 auto 0 0` rather than `height: 100vh` deliberately: on mobile
       browsers 100vh is the pre-URL-bar height, so a vh-sized fixed panel is taller than the
       visual viewport and its footer sits below the fold. Top/bottom insets on a fixed box track
       the visual viewport instead. */
    .op-sidebar {
        position: fixed;
        inset: 0 auto 0 0;
        height: auto;
        width: min(264px, 82vw);
        z-index: 40;
        transform: translateX(-100%);
        transition: transform .22s ease;
        box-shadow: var(--op-shadow-lg);
    }
    .op-shell--navopen .op-sidebar { transform: none; }

    .op-scrim { display: block; }

    /* The two toggles trade places. Keeping them as separate buttons — one per tier, switched by
       CSS — is what keeps ALL viewport knowledge in this stylesheet: the alternative was reusing
       SidebarService.IsCollapsed for the drawer, whose persisted default of `false` would mean
       "drawer open" and greet every phone user with an open menu. */
    .op-topbar__toggle { display: none; }
    .op-topbar__burger { display: flex; }

    /* Containment. `clip`, NEVER `auto` — see the .op-content comment above: `auto` makes this a
       scroll container and silently re-breaks the prospect form's sticky tab strip and the docked
       pane. `clip` establishes no scrollport, so sticky is unaffected. Scoped to this tier so
       desktop keeps today's behavior and a desktop overflow bug still shows itself. Dropdowns are
       safe: MudPopoverProvider renders at the App.razor root, outside this box. */
    .op-content { overflow-x: clip; }

    .op-sidenav { flex: 1 1 auto; width: 100%; }
}

@media (max-width: 599.98px) {
    /* Redeclared on .op-shell so the sticky tab strip and DockPane, which both offset by
       var(--op-topbar-h), follow the shorter bar for free. */
    .op-shell { --op-topbar-h: 56px; }

    .op-content { padding: 14px 12px 48px; }
    .op-topbar { padding: 0 10px; gap: 8px; }

    /* The crumb duplicates what the drawer's active nav item already says. Dropping it and
       ellipsising the title is what buys the title enough room to be legible next to the
       remaining controls. */
    .op-topbar__crumb { display: none; }
    .op-topbar__page { font-size: 17px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

    /* Identity, theme and logout move into the drawer footer — eight controls do not fit in
       430px, and these three are the ones that are not per-page actions. */
    .op-topbar__user, .op-topbar__theme { display: none; }
    .op-sidebar__foot { display: flex; }

    /* The pool badge keeps its icon and count (it is ambient signal worth ~40px) but sheds
       "sin asignar", which is ~70px of nowrap text. Its aria-label and tooltip still say it. */
    .op-pooltag span { display: none; }
    .op-pooltag { padding: 6px 9px; }

    /* Card/panel density. MudBlazor's spacing utilities are `!important`, so beating them needs
       `!important` too. The hook classes are ADDITIVE (`pa-4 op-tabpanel`) — pa-4 still governs
       every other width, and the hook is a selector we own rather than a guess at generated
       markup. */
    .op-tabpanel { padding: 10px !important; }
    .op-formcard { padding: 12px !important; }

    /* Release every inline min-width floor on an input. Inline styles outrank classes, hence
       `!important`. BOTH selectors are needed: MudBlazor lands a component's `Style` on the inner
       div.mud-input (see the comment in SpecialRiskField.razor), while the flex item that refuses
       to shrink is the outer .mud-input-control. Nothing is lost — at this width every field is
       xs="12", i.e. full width, so there is no narrow-column case left for a floor to protect. */
    .op-content .mud-input-control,
    .op-content .mud-input { min-width: 0 !important; }
}

/* Set on <body> by appDockResizeStart for the duration of a divider drag on the prospect form.
   Global because the pointer is captured by the divider and routinely travels far outside it: the
   resize cursor has to survive crossing the form, and text selection has to stay suppressed the
   whole way. `pointer-events: none` on the email iframe belongs here too — pointer capture already
   routes the events, but the iframe would otherwise repaint hover states mid-drag. */
body.op-resizing { cursor: col-resize !important; user-select: none; }
body.op-resizing iframe { pointer-events: none; }

/* ---- Top bar primary action ---- */
.op-btn { display: inline-flex; align-items: center; gap: 9px; padding: 10px 17px; border: none; border-radius: 11px; background: var(--op-brand); color: #fff; font-size: 14px; font-weight: 700; font-family: inherit; text-transform: none; text-decoration: none; white-space: nowrap; cursor: pointer; box-shadow: var(--op-shadow-sm); transition: filter .15s; }
.op-btn:hover { filter: brightness(1.06); color: #fff; }

/* ---- Top bar user chip ---- */
.op-topbar__user { display: flex; align-items: center; gap: 10px; padding-left: 8px; border-left: 1px solid var(--op-line); }
.op-topbar__avatar { width: 38px; height: 38px; border-radius: 50%; background: var(--op-brand); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 14px; flex-shrink: 0; }
.op-topbar__usermeta { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.op-topbar__username { font-size: 13.5px; font-weight: 700; }
/* ⚠ NOT THE GOLD. This sits on --op-surface, which is WHITE in light mode, where the
   gold measures 2.33:1 against WCAG AA's 4.50 — the label was genuinely hard to read and
   a screenshot is what showed it. OptimaAuto uses a quiet gray for the same label in its
   sidebar footer; --op-muted is that, and it is legible in both modes. */
.op-topbar__userrole { font-size: 11.5px; font-weight: 600; color: var(--op-muted); white-space: nowrap; }

/* The row holds two more controls than the mockup did (a notification bell and the sidebar
   toggle), so it needs breakpoints the mockup never had. Shed the least essential pieces first:
   the user's name/role, then the button's label. Each threshold is where the remaining controls
   stop fitting in (viewport - 260px sidebar - 56px padding).
   Deliberately measured against the EXPANDED sidebar: these are viewport media queries, so
   collapsing to the 72px rail does not raise the thresholds. Sized for the worst case rather
   than swapping in container queries for 188px of slack.

   ⚠ That "- 260px sidebar" term only holds at 960px and up. Below it the sidebar leaves the grid
   entirely (drawer tier, above), so the row actually has 260px MORE than these thresholds assume
   and both are over-aggressive by that much — 900px shows an icon-only "Nuevo prospecto" beside
   ~840px of free row. Left as-is rather than re-derived: it errs toward compact, and the phone
   tier hides .op-topbar__user outright, so the only affected band is 600-960px. Re-derive these
   two numbers before treating them as a measurement of anything. */
@media (max-width: 1200px) {
    .op-topbar__usermeta { display: none; }
}

@media (max-width: 900px) {
    .op-btn { padding: 10px 12px; gap: 0; }
    .op-btn span { display: none; }
}

/* ---- Login (Login.razor + LoginLayout) ---- */

/* ---- Card + KPI polish (used across pages) ---- */
.op-card { background: var(--op-surface); border: 1px solid var(--op-line); border-radius: var(--op-r); box-shadow: var(--op-shadow-sm); }

/* ---- Pipeline board (PipelineBoard.razor) ---- */
.op-board { display: grid; grid-template-columns: repeat(5, minmax(230px, 1fr)); gap: 14px; overflow-x: auto; padding-bottom: 8px; }
.op-board__col { background: var(--op-surface-2); border: 1px solid var(--op-line); border-radius: var(--op-r); display: flex; flex-direction: column; min-height: 200px; }
.op-board__head { padding: 13px 15px; display: flex; align-items: center; gap: 9px; border-bottom: 2px solid var(--op-line); }
.op-board__dot { width: 9px; height: 9px; border-radius: 50%; }
.op-board__title { font-weight: 800; font-size: 13.5px; }
.op-board__count { margin-left: auto; background: var(--op-surface); border: 1px solid var(--op-line); font-size: 11.5px; font-weight: 800; padding: 1px 9px; border-radius: 12px; }
.op-board__body { padding: 11px; display: flex; flex-direction: column; gap: 10px; }
.op-board__card { background: var(--op-surface); border: 1px solid var(--op-line); border-left-width: 3px; border-radius: 11px; padding: 13px; cursor: pointer; box-shadow: var(--op-shadow-sm); transition: .16s; text-decoration: none; color: inherit; }
.op-board__card:hover { box-shadow: var(--op-shadow-lg); transform: translateY(-2px); }
.op-board__name { font-weight: 700; font-size: 13.5px; line-height: 1.25; }
.op-board__meta { font-size: 12px; color: var(--op-muted); }
.op-board__foot { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; font-size: 11.5px; }

/* ---- Registry view toggle (segmented control) ---- */
.op-seg { display: inline-flex; background: var(--op-surface-2); border: 1px solid var(--op-line); border-radius: 11px; padding: 3px; gap: 3px; }
.op-seg__btn { padding: 8px 15px; border: none; border-radius: 8px; background: transparent; color: var(--op-muted); font-size: 13px; font-weight: 700; font-family: inherit; cursor: pointer; }
.op-seg__btn.active { background: var(--op-surface); color: var(--op-brand); box-shadow: var(--op-shadow-sm); }

/* ---- LOB coverage chip (reused in table + board) ---- */
.op-lobchip { width: 34px; height: 34px; border-radius: 9px; display: inline-flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; }

/* ============================================================================
   Prospect form shell polish (ProspectFormPage). The tabs already pick up the
   brand Primary for their active underline; these rules refine spacing, the tab
   typography, and card consistency. Global (affects all MudTabs) for a uniform
   feel — remove the .mud-tabs rules if you want it scoped to the form only.
   ============================================================================ */

/* Tab bar: clean underline row, brand-blue active label. */
.mud-tabs .mud-tab { text-transform: none; font-weight: 600; font-size: 0.9rem; letter-spacing: 0; }
.mud-tabs .mud-tab.mud-tab-active { color: var(--mud-palette-primary); }
.mud-tabs .mud-tab-slider { background: var(--mud-palette-primary); height: 2.5px; border-radius: 3px; }

/* Outlined papers (form cards, filter cards, KPI): rounded + soft shadow. */
.op-content .mud-paper-outlined { border-radius: var(--op-r) !important; box-shadow: var(--op-shadow-sm); }

/* Prospect form header row (optional helper — wrap the title/actions row in it). */
.op-formhead { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }
.op-formhead__title { flex: 1; min-width: 0; }
.op-formhead__name { font-size: 22px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }
.op-formhead__meta { font-size: 12.5px; color: var(--op-muted); }

/* Special-risk callout (used on the prospect header). */
.op-srisk { display: flex; align-items: center; gap: 12px; padding: 13px 15px; border: 1.5px solid var(--op-gold); background: var(--op-gold-050); border-radius: 11px; }
.op-srisk__icon { color: var(--op-gold-600); font-size: 18px; }

/* AI summary panel. */
.op-ai { background: var(--op-surface); border: 1px solid var(--op-line); border-radius: var(--op-r); padding: 20px; box-shadow: var(--op-shadow-sm); }
.op-ai__title { font-size: 14px; font-weight: 800; margin-bottom: 6px; }
.op-ai__title i { color: var(--op-gold-600); }
.op-ai__body { font-size: 13px; line-height: 1.6; color: var(--op-muted); margin: 0; }

/* ============================================================================
   Configuración — side-nav + section panels. The page uses MudNavMenu/MudNavLink
   inside a MudPaper (unlike the main app nav, which is the custom .op-nav), so
   these MudNavLink rules brand the CONFIG side-nav without touching the shell.
   ============================================================================ */

/* Config side-nav links: rounded, brand-blue active pill with a gold left mark. */
.mud-navmenu .mud-nav-link { border-radius: var(--op-r-sm); margin: 1px 4px; font-weight: 500; }
.mud-navmenu .mud-nav-link.active {
    background: var(--op-brand-050);
    color: var(--mud-palette-primary);
    font-weight: 700;
    position: relative;
}
.mud-navmenu .mud-nav-link.active::before {
    content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 3px;
    border-radius: 3px; background: var(--op-gold);
}
.mud-navmenu .mud-nav-link.active .mud-nav-link-icon { color: var(--mud-palette-primary); }

/* Section-group overline labels in the config side-nav. */
.mud-navmenu .mud-typography-overline { color: var(--op-muted-2); letter-spacing: .1em; font-weight: 700; }

/* Config cards / section papers already round via DefaultBorderRadius; give them the soft shadow. */
.op-content .mud-simple-table { border-radius: var(--op-r-sm); overflow: hidden; }

/* ============================================================================
   Assignment notifications — nav count badge, top-bar bell + dropdown, and the
   registry read/unread indicator.

   GLOBAL, not scoped .razor.css: the .reg-* rules attach to cells MudDataGrid
   renders outside the owning component, and the nav/bell rules live in markup
   shared across layouts. Same rationale as .reg-actions / .status-badge (line 134).

   Most rules inherit dark mode for free through the --op-* custom properties
   (:root vs .app-dark, line 286). Only rules with LITERAL colours carry an
   explicit .app-dark variant.
   ============================================================================ */

/* ---- Nav count badge (NavMenu). Gold-on-navy: the sidebar is dark in BOTH
        themes, so this deliberately has no .app-dark variant. `margin-left:auto`
        is all that's needed to right-align it — .op-nav__link is already flex. ---- */
.op-nav__badge {
    margin-left: auto; min-width: 22px; padding: 1px 7px; border-radius: 11px;
    background: var(--op-gold); color: #3A2E05;
    font-size: 11px; font-weight: 800; line-height: 1.6; text-align: center;
}

/* ---- Top-bar bell (NotificationBell). .op-topbar has no overflow:hidden, so the
        negatively-offset count dot is not clipped. ---- */
.op-notif__wrap { position: relative; display: inline-flex; }
.op-notif__dot {
    position: absolute; top: -5px; right: -5px;
    min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px;
    background: #C0362C; color: #fff;
    font-size: 10.5px; font-weight: 800; line-height: 18px; text-align: center;
    /* Ring in the surface colour so the dot reads as lifted off the bell. */
    border: 2px solid var(--op-surface); box-sizing: content-box;
    pointer-events: none;   /* never intercept the bell's own click */
}
.app-dark .op-notif__dot { background: #E2574C; }

/* ---- Bell dropdown. MudPopover portals to the provider at document root, well
        above .op-topbar's z-index, so there is no stacking conflict. ---- */
.op-notif__pop { border-radius: var(--op-r-sm); overflow: hidden; }

/* IdCheckPicker's popover. Global, not in IdCheckPicker.razor.css: MudMenu renders this element
   itself, so CSS isolation's generated attribute is absent and a scoped rule never matches. Same
   reason .op-notif__pop and .op-novedades__pop live here. */
.idpick__pop { border-radius: var(--op-r-sm); overflow: hidden; }
.op-notif { width: 360px; max-width: 88vw; color: var(--op-text); }
.op-notif__head {
    display: flex; align-items: center; gap: 8px; padding: 12px 14px;
    border-bottom: 1px solid var(--op-line); font-size: 13px;
}
.op-notif__title { font-weight: 800; }
.op-notif__count {
    margin-left: auto; background: var(--op-brand-050); color: var(--op-brand);
    font-size: 11px; font-weight: 800; padding: 1px 8px; border-radius: 10px;
}
.op-notif__list { max-height: 320px; overflow-y: auto; }
/* Two text lines in column 1, timestamp spanning both rows in column 2. */
.op-notif__item {
    display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
    padding: 10px 14px; text-decoration: none; color: inherit;
    border-bottom: 1px solid var(--op-line-2);
}
.op-notif__item:hover { background: var(--op-surface-2); }
.op-notif__item-name { grid-column: 1; font-weight: 700; font-size: 13px; }
.op-notif__item-meta { grid-column: 1; font-size: 11.5px; color: var(--op-muted); }
.op-notif__item-time {
    grid-column: 2; grid-row: 1 / span 2; align-self: center;
    font-size: 11px; color: var(--op-muted-2); white-space: nowrap;
}
.op-notif__empty {
    padding: 22px 14px; text-align: center; font-size: 12.5px; color: var(--op-muted);
    display: flex; flex-direction: column; gap: 8px; align-items: center;
}
.op-notif__foot {
    display: flex; align-items: center; gap: 10px; padding: 8px 14px;
    border-top: 1px solid var(--op-line); font-size: 12.5px;
}
.op-notif__footbtn {
    background: none; border: 0; padding: 2px 0; font: inherit; font-weight: 700;
    color: var(--op-brand); cursor: pointer;
}
.op-notif__footbtn:hover { text-decoration: underline; }
.op-notif__footlink { margin-left: auto; font-weight: 700; color: var(--op-brand); text-decoration: none; }
.op-notif__footlink:hover { text-decoration: underline; }

/* ---- "?" help affordance (Shared/HelpButton.razor) + its popover.
        The button is deliberately QUIETER than .op-iconbtn's default: it sits beside page
        titles and field labels all over the app, and at full contrast a dozen of them would
        read as a dozen actions. It comes up to full strength on hover/focus. ---- */
.op-helpbtn {
    width: 24px; height: 24px; color: var(--op-muted-2);
    border-color: transparent; background: transparent; font-size: 12px;
}
.op-helpbtn:hover, .op-helpbtn:focus-visible { color: var(--op-brand); border-color: var(--op-line); }

.op-help__pop { border-radius: var(--op-r-sm); overflow: hidden; }
.op-help { width: 380px; max-width: 88vw; color: var(--op-text); }
.op-help__head {
    display: flex; align-items: center; gap: 8px; padding: 11px 14px;
    border-bottom: 1px solid var(--op-line); font-size: 13px;
}
.op-help__head i { color: var(--op-brand); }
.op-help__title { font-weight: 800; }
/* Caps the tallest topic without letting a short one leave dead space. */
.op-help__body { max-height: 340px; overflow-y: auto; padding: 4px 14px 10px; font-size: 12.5px; }
.op-help__body p { margin: 8px 0; line-height: 1.55; }
.op-help__body ul, .op-help__body ol { margin: 8px 0; padding-left: 20px; }
.op-help__body li { margin: 3px 0; line-height: 1.5; }
.op-help__body strong { font-weight: 800; }
.op-help__foot {
    display: flex; align-items: center; padding: 8px 14px;
    border-top: 1px solid var(--op-line); font-size: 12.5px;
}
.op-help__ask {
    display: inline-flex; align-items: center; gap: 7px;
    background: none; border: 0; padding: 2px 0; font: inherit; font-weight: 700;
    color: var(--op-brand); cursor: pointer;
}
.op-help__ask:hover { text-decoration: underline; }

/* ---- Novedades dropdown (Layout/NovedadesPanel.razor). Reuses .op-notif__wrap,
        __dot, __head, __title, __count and __foot from the bell above — same shell,
        same top-bar geometry — and only adds the row shape, which is prose rather
        than the bell's grid of name/meta/time. ---- */
.op-novedades__pop { border-radius: var(--op-r-sm); overflow: hidden; }
.op-novedades { width: 400px; max-width: 90vw; color: var(--op-text); }
.op-novedades__body { max-height: 420px; overflow-y: auto; }
.op-novedades__welcome { padding: 4px 14px 10px; font-size: 12.5px; }
.op-novedades__welcome p { margin: 8px 0; line-height: 1.55; }
.op-novedades__welcome strong { font-weight: 800; }
.op-novedades__sep {
    padding: 7px 14px; background: var(--op-surface-2);
    border-top: 1px solid var(--op-line); border-bottom: 1px solid var(--op-line);
    font-size: 11px; font-weight: 800; letter-spacing: .3px; text-transform: uppercase;
    color: var(--op-muted);
}
.op-novedades__item { padding: 10px 14px; border-bottom: 1px solid var(--op-line-2); }
/* Unseen marker is a left rule, not a background tint: the panel is a reading surface and a
   block of colour behind body text hurts the contrast the text needs. */
.op-novedades__item--new { border-left: 3px solid var(--op-brand); padding-left: 11px; }
.op-novedades__itemhead { display: flex; align-items: baseline; gap: 10px; }
.op-novedades__itemtitle { font-weight: 700; font-size: 12.5px; }
.op-novedades__itemdate {
    margin-left: auto; font-size: 11px; color: var(--op-muted-2); white-space: nowrap;
}
.op-novedades__itembody { margin-top: 3px; font-size: 12px; line-height: 1.5; color: var(--op-muted); }

/* ============================================================================
   Avisos globales — the platform-notice banner (Layout/AnnouncementBanner.razor)
   and the bell's "Avisos" section.

   The banner is a flex ROW of .op-main, between the sticky top bar and
   .op-content. It is deliberately NOT sticky: a second sticky band under
   .op-topbar would have to chase --op-topbar-h and would compete with the
   page-level sticky elements the registry and the prospect form rely on.

   Colours are LITERAL here rather than --op-* custom properties, because
   severity has to survive both themes with its meaning intact — an amber that
   reads as "caution" on white is invisible on navy. Hence the explicit
   .app-dark block: each severity gets a hand-picked dark pair, not an opacity
   trick.
   ============================================================================ */
.op-annc {
    display: flex; align-items: flex-start; gap: 13px;
    padding: 13px 28px; border-bottom: 1px solid var(--op-line);
    font-size: 13px; line-height: 1.5;
}
.op-annc__icon { font-size: 17px; line-height: 1.35; flex: 0 0 auto; }
.op-annc__text { flex: 1 1 auto; min-width: 0; }
.op-annc__title { font-weight: 800; }
/* ⚠ pre-line, and it is load-bearing: the body is plain text and a supervisor's
   line breaks are the only structure it has. Never render this through markdown. */
.op-annc__body { white-space: pre-line; margin-top: 1px; }
/* The window is the point of most notices, so it gets its own line at full
   weight rather than being buried in the body's prose. */
.op-annc__window { margin-top: 4px; font-size: 12px; font-weight: 700; opacity: .85; }
.op-annc__more { margin-top: 5px; font-size: 11.5px; opacity: .75; }
/* Inherits `color` so the ✕ takes the severity's own foreground in both themes. */
.op-annc__close {
    flex: 0 0 auto; width: 28px; height: 28px; margin: -2px -6px 0 0;
    border: 0; border-radius: 8px; background: transparent; color: inherit;
    font-size: 14px; cursor: pointer; opacity: .65; transition: .15s;
}
.op-annc__close:hover, .op-annc__close:focus-visible { opacity: 1; background: rgba(0,0,0,.07); }
.app-dark .op-annc__close:hover, .app-dark .op-annc__close:focus-visible { background: rgba(255,255,255,.12); }

.op-annc--info     { background: #EAF3FA; color: #14425F; border-bottom-color: #C4DDEE; }
.op-annc--warning  { background: #FCF3E0; color: #6A4A08; border-bottom-color: #EDD8A6; }
.op-annc--critical { background: #FBEAE8; color: #7A241C; border-bottom-color: #EFC4BF; }

.app-dark .op-annc--info     { background: #14303F; color: #BFDFF3; border-bottom-color: #1F4557; }
.app-dark .op-annc--warning  { background: #3A2F13; color: #F0D79B; border-bottom-color: #55461E; }
.app-dark .op-annc--critical { background: #3D1C18; color: #F3B9B2; border-bottom-color: #582923; }

/* Phone tier: match .op-content's reduced gutter so the banner lines up with the
   page, and drop the icon — at 430px the text needs every pixel and the colour
   already carries the severity. */
@media (max-width: 599.98px) {
    .op-annc { padding: 11px 16px; gap: 10px; font-size: 12.5px; }
    .op-annc__icon { display: none; }
}

/* ---- The bell's "Avisos" section. A stacked block rather than
        .op-notif__item's name/meta/time grid: the window sentence is far wider
        than that grid's nowrap timestamp column and would blow out the 360px
        panel. A <div>, not an <a> — a notice has nowhere to navigate to. ---- */
.op-notif__annc {
    display: flex; flex-direction: column; gap: 2px;
    padding: 10px 14px; border-bottom: 1px solid var(--op-line-2);
}
.op-notif__annc-title {
    display: flex; align-items: center; gap: 7px; font-weight: 700; font-size: 13px;
}
/* Clamped to three lines: the body may run to 1000 characters, and one long notice
   must not push every other row out of the dropdown. The banner shows it in full. */
.op-notif__annc-body {
    font-size: 11.5px; color: var(--op-muted); white-space: pre-line;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
}
.op-notif__annc-when { font-size: 11px; font-weight: 700; color: var(--op-muted-2); }
.op-notif__sev {
    flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%;
}
.op-notif__sev--info     { background: #1D6FA5; }
.op-notif__sev--warning  { background: #C68A12; }
.op-notif__sev--critical { background: #C0362C; }
.app-dark .op-notif__sev--info     { background: #6FB4E0; }
.app-dark .op-notif__sev--warning  { background: #E3B457; }
.app-dark .op-notif__sev--critical { background: #E2574C; }
/* Dismissed notices stay listed but recede — the bell is where you go to re-read
   one, so removing it would defeat the point of keeping two surfaces. */
.op-notif__annc--read .op-notif__annc-title { font-weight: 600; opacity: .7; }

/* ---- Registry read/unread indicator (RegistryPage column 0 + the name column).
        Deliberately a GLYPH plus font-weight, NEVER a row background: the
        .due-soon / .due-today row tints at line 173 carry !important and would
        win. Out-!important-ing them would be worse than losing — it would bury an
        overdue LOB's tint behind an unread tint, and overdue is the more
        operationally urgent signal. ---- */
.reg-unread {
    display: inline-block; width: 9px; height: 9px; border-radius: 50%;
    background: #1D6FA5; box-shadow: 0 0 0 2px rgba(29,111,165,.18);
}
.app-dark .reg-unread { background: #6FB4E0; box-shadow: 0 0 0 2px rgba(111,180,224,.22); }
.reg-read {
    display: inline-block; width: 9px; height: 9px; border-radius: 50%;
    border: 1.5px solid #C3CDD7; box-sizing: border-box;
}
.app-dark .reg-read { border-color: #3C4753; }

/* Prospect-name weight. .reg-name replaces the old inline font-weight:600. */
.reg-name { font-weight: 600; }
.reg-name--unread { font-weight: 800; }

/* Pipeline card emphasis for an unread assignment. */
.op-board__name--unread { font-weight: 800; }

/* ---- LOB card: assignee-outside-the-eligible-list warning (LobCard.razor).
        Fires when a CREADO LOB's owner isn't in that line's Mesa de Entrada roster. ---- */
.lc-uw-warn {
    display: flex; align-items: center; gap: 6px; margin-top: 4px;
    font-size: 11.5px; font-weight: 600; color: #92400E;
}
.lc-uw-warn i { font-size: 11px; }
.app-dark .lc-uw-warn { color: #FCD34D; }

/* ---- LOB card: the AMS360 Suspense mirror row (LobAms360StatusRow.razor).
        Sits under the workflow grid, above the per-LOB fields. Wraps rather than truncates:
        an AMS360 action can be 35 characters and the divergence sentence names both sides, so
        on a phone this is two or three lines and that is correct — it is the one place a user
        finds out the two systems disagree. ---- */
.lc-ams-row {
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
    margin-top: 12px; padding-top: 10px;
    border-top: 1px dashed var(--mud-palette-lines-default);
    font-size: 12.5px;
}
.lc-ams-ico { font-size: 12px; opacity: .7; }
.lc-ams-help { font-size: 12px; opacity: .55; cursor: help; }
.lc-ams-note { font-size: 12px; }
.lc-ams-warn { font-weight: 600; color: #92400E; }
.app-dark .lc-ams-warn { color: #FCD34D; }
/* Pushed to its own end of the row on desktop, but allowed to wrap under on narrow widths
   (margin-left:auto stops applying once the row has wrapped). */
.lc-ams-actions { display: flex; align-items: center; gap: 6px; margin-left: auto; }

/* ---- Underwriter dropdown: routing-scope pills (UwSelect.razor).
        The roster is the union of every rule group that applies to the LOB, so one option can be
        the riesgo-especial desk and another the normal one with nothing to tell them apart. Each
        option gets a pill per category it covers.

        Colours match the Alcance chips in Configuración -> Underwriters por LOB: sin compromiso is
        the hardest override (red), riesgo especial amber, normal a neutral outline. Inert spans, not
        MudChip — a chip is interactive and would compete with the item's own click target. ---- */
.uw-opt { display: flex; align-items: center; gap: 6px; min-width: 0; }
.uw-opt-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uw-pill {
    flex: 0 0 auto; border: 1px solid transparent; border-radius: 9px;
    padding: 0 6px; font-size: 10px; font-weight: 700; line-height: 16px;
    letter-spacing: .2px; white-space: nowrap; pointer-events: none;
}
.uw-pill-normal  { color: #57534E; border-color: #D6D3D1; background: transparent; }
.uw-pill-special { color: #92400E; border-color: #FDE68A; background: #FEF3C7; }
.uw-pill-nocomp  { color: #991B1B; border-color: #FECACA; background: #FEE2E2; }
/* One blue for every "reachable through a specific routing rule rather than the LOB's general
   desk" category — override productor, agencia, sucursal, combinación. They share a colour
   deliberately: the pill's job is to say WHICH rule put the person here, and the label already
   does that, so four more hues would only make the list harder to scan. */
.uw-pill-agent,
.uw-pill-agencia,
.uw-pill-sucursal,
.uw-pill-combo   { color: #1E5B80; border-color: #BAE0F5; background: #E8F3FA; }

.app-dark .uw-pill-normal  { color: #A8A29E; border-color: #57534E; }
.app-dark .uw-pill-special { color: #FCD34D; border-color: #78350F; background: #3B2A0B; }
.app-dark .uw-pill-nocomp  { color: #FCA5A5; border-color: #7F1D1D; background: #3B0F0F; }
.app-dark .uw-pill-agent,
.app-dark .uw-pill-agencia,
.app-dark .uw-pill-sucursal,
.app-dark .uw-pill-combo   { color: #9FCBE6; border-color: #1E4A66; background: #0E2635; }

/* ---- Productor Status source pills (VendorStatusLabel.razor).
        Two independent systems answer this one field — SunGard/EAS for the vendor agreement,
        AMS360 for the P&C licence — and a bare coloured badge gave no way to tell which one
        went red, even though the two are fixed by different people in different systems. Each
        reading gets a dimmed source tag inside the pill.

        Piggybacks on .status-badge for the sb-* palette and its dark-mode rules; this block MUST
        stay AFTER .status-badge in this sheet, since the display/padding/font-size overrides
        below match its specificity exactly and win only on source order. ---- */
.vs-pill {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 1px 8px; border-radius: 9px;
    font-size: 11px; font-weight: 600; line-height: 16px; letter-spacing: 0;
}
.vs-src {
    font-size: 9.5px; font-weight: 800; letter-spacing: .4px; text-transform: uppercase;
    opacity: .72; padding-right: 5px; border-right: 1px solid currentColor;
}
/* The two producer flags keep their own fills rather than borrowing an sb-* tint: Sin compromiso
   is the strongest signal in the row and must not flatten into the soft sb-red used for an
   expired licence, and Elite is a brand colour, not a status. Neither carries an sb-* class, so
   neither ties with .status-badge.sb-*. --op-gold holds one value in both themes, so Elite's
   dark ink stays readable and needs no .app-dark pairing. */
.vs-pill.vs-nocomp { background: #c0392b; color: #fff; font-weight: 700; }
.app-dark .vs-pill.vs-nocomp { background: #8E2B21; color: #FFE5E2; }
.vs-pill.vs-elite { background: var(--op-gold); color: #3A2E05; font-weight: 700; }

/* ---- "Solo no leídos" toggle, beside the Tabla/Pipeline segmented switch. ---- */
.op-unreadtoggle {
    border: 1px solid var(--op-line); background: var(--op-surface);
    border-radius: 11px; padding: 8px 15px; color: var(--op-muted);
    font-size: 13px; font-weight: 700; font-family: inherit; cursor: pointer;
}
.op-unreadtoggle i { font-size: 8px; vertical-align: middle; color: #1D6FA5; }
.app-dark .op-unreadtoggle i { color: #6FB4E0; }
.op-unreadtoggle.active {
    border-color: var(--op-brand); color: var(--op-brand); background: var(--op-brand-050);
}

/* ---- Mesa de Entrada: registry queue-preset toggle, topbar pool badge, and the
        grid's pool affordances. ---- */
.op-mesatoggle {
    border: 1px solid var(--op-line); background: var(--op-surface);
    border-radius: 11px; padding: 8px 15px; color: var(--op-muted);
    font-size: 13px; font-weight: 700; font-family: inherit; cursor: pointer;
}
.op-mesatoggle i { font-size: 11px; vertical-align: middle; color: #B8860B; }
.app-dark .op-mesatoggle i { color: #E0C36F; }
.op-mesatoggle.active {
    border-color: var(--op-brand); color: var(--op-brand); background: var(--op-brand-050);
}

/* Amber on purpose — visually distinct from the red unread dot: pool work is
   "waiting for ANYONE", not "assigned to you, unread". */
.op-pooltag {
    border: 1px solid #E0C36F; background: rgba(224, 195, 111, 0.14);
    color: #8A6D1D; border-radius: 11px; padding: 6px 12px;
    font-size: 12.5px; font-weight: 800; font-family: inherit; cursor: pointer;
    white-space: nowrap;
}
.op-pooltag i { margin-right: 4px; }
/* The count→label gap must be this margin, not a literal space: Razor trims leading
   whitespace inside the span, and a space OUTSIDE it would survive the phone tier's
   display:none. Margin dies with the span, exactly like .op-btn's gap. */
.op-pooltag span { margin-left: 4px; }
.app-dark .op-pooltag { color: #E0C36F; background: rgba(224, 195, 111, 0.10); }

/* Registry: the pool rows' Asignado label. */
.reg-uw-none { color: var(--op-muted); font-style: italic; }

/* ─────────────────────────────────────────────────────────────────────────────
   Reglas de asignación (Configuración → Asignación).

   TWO GRAMMARS, and the CSS is what keeps them from being confused:
     .mtx-band*  ruteo — additive, ordered, and it carries a PRIORITY NUMBER in the
                 left rail. That number is the whole structure: it is how the engine
                 orders, not decoration.
     .mtx-elig*  elegibilidad — restrictive, unordered, and deliberately carries NO
                 number and NO rail. The absence is the signal.
   Every colour comes from an --op-* token so both themes resolve as a set.
   ───────────────────────────────────────────────────────────────────────────── */
.mtx-num { font-variant-numeric: tabular-nums;
           font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; }

.mtx-band__head { display: flex; align-items: baseline; gap: 11px; padding: 14px 0 7px; }
.mtx-band__num { font-family: ui-monospace, Consolas, monospace; font-size: 20px; font-weight: 700;
                 font-variant-numeric: tabular-nums; color: var(--op-brand);
                 min-width: 3.2ch; text-align: right; }
.mtx-band__name { font-size: 11px; font-weight: 700; letter-spacing: .1em;
                  text-transform: uppercase; color: var(--op-muted); }
.mtx-band__rules { border-left: 2px solid var(--op-line); margin-left: 1.6ch;
                   padding-left: 18px; display: flex; flex-direction: column; gap: 7px; }
.mtx-grp { border: 1px solid var(--op-line) !important; border-radius: var(--op-r-sm) !important; }

.mtx-rule { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
            background: var(--op-surface); border: 1px solid var(--op-line);
            border-radius: var(--op-r-sm); padding: 9px 12px; }
/* A composite rule sits BETWEEN bands, so it gets a rail of its own to say so. */
.mtx-rule--composite { border-color: var(--op-brand); box-shadow: inset 3px 0 0 var(--op-brand); }
.mtx-rule--flagged { border-color: var(--op-red); }

/* No number, no rail — see the header. The dashed top border is the seam between grammars. */
.mtx-elig { margin-top: 30px; border-top: 2px dashed var(--op-line); padding-top: 10px; }
.mtx-elig__head { display: flex; align-items: baseline; gap: 8px; }
.mtx-gate { display: flex; align-items: flex-start; gap: 12px; margin-top: 10px;
            background: var(--op-surface); border: 1px solid var(--op-line);
            border-left: 3px solid var(--op-muted-2);
            border-radius: var(--op-r-sm); padding: 11px 13px; }

.mtx-verdict { border-radius: var(--op-r-sm); padding: 13px 15px; border: 1px solid var(--op-line); }
.mtx-verdict--ok   { border-color: var(--op-green-600); background: var(--op-green-050); }
.mtx-verdict--warn { border-color: var(--op-gold-600);  background: var(--op-gold-050); }
.mtx-verdict--bad  { border-color: var(--op-red);       background: var(--op-red-050); }

.mtx-layer { border: 1px solid var(--op-line); border-left: 3px solid var(--op-muted-2);
             border-radius: var(--op-r-sm); padding: 12px 14px; background: var(--op-surface-2); }
.mtx-layer--bad { border-color: var(--op-red); border-left-color: var(--op-red);
                  background: var(--op-red-050); }

.mtx-trace { display: flex; align-items: flex-start; gap: 11px; padding: 7px 11px;
             border: 1px solid var(--op-line); border-radius: var(--op-r-sm);
             background: var(--op-surface); margin-bottom: 6px; }
.mtx-trace__pri { font-weight: 700; color: var(--op-brand); min-width: 4ch; }
.mtx-trace--win { border-color: var(--op-green-600); background: var(--op-green-050);
                  box-shadow: inset 3px 0 0 var(--op-green-600); }
.mtx-trace--shadow { box-shadow: inset 3px 0 0 var(--op-muted-2); }
.mtx-trace--miss { opacity: .55; }

.mtx-desk { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
            border: 1px solid var(--op-line); border-radius: var(--op-r-sm);
            padding: 10px 12px; margin-top: 8px; }

.mtx-axis { border: 1px solid var(--op-line); border-radius: var(--op-r-sm);
            padding: 10px 12px; margin-top: 8px; }
.mtx-axis--on { border-color: var(--op-brand); background: var(--op-brand-050); }
.mtx-derived { border: 1px solid var(--op-brand); border-radius: var(--op-r-sm);
               background: var(--op-brand-050); padding: 12px 14px; }
.mtx-derived__n { font-family: ui-monospace, Consolas, monospace; font-size: 27px;
                  font-weight: 700; color: var(--op-brand);
                  font-variant-numeric: tabular-nums; line-height: 1.15; }

/* The per-rule "¿qué tiene ahora?" panel — live assignments, loaded ON DEMAND only.
   Dashed border so it reads as a lookup rather than part of the rule's own definition. */
.mtx-work { margin-top: 8px; padding: 9px 11px; border: 1px dashed var(--op-line);
            border-radius: var(--op-r-sm); background: var(--op-surface-2);
            display: flex; flex-direction: column; gap: 4px; }
.mtx-work a { color: var(--op-brand); text-decoration: none; }
.mtx-work a:hover { text-decoration: underline; }

/* ═══════════════════════════════════════════════════════════════════════════
   Transition matrix (Configuración → Estatus). Added for Auditorías; the sister
   app edits transitions one row at a time and has no matrix.

   A plain table rather than MudDataGrid: every cell is a checkbox, there is
   nothing to sort or filter, and the column headers have to rotate to fit — none
   of which a data grid does well.
   ═══════════════════════════════════════════════════════════════════════════ */

.cfg-matrix { border-collapse: separate; border-spacing: 0; font-size: 13px; }

.cfg-matrix th,
.cfg-matrix td { border-bottom: 1px solid var(--op-line); border-right: 1px solid var(--op-line); }

.cfg-matrix thead th { border-bottom: 2px solid var(--op-line); vertical-align: bottom; }

.cfg-matrix__corner {
    text-align: left; padding: 8px 12px; font-weight: 700; color: var(--op-muted);
    white-space: nowrap; background: var(--op-surface);
    /* Sticky in BOTH axes so the row label and the column label stay readable while
       scrolling a wide matrix — without them a checkbox in the middle means nothing. */
    position: sticky; left: 0; z-index: 3;
}

.cfg-matrix__colhead {
    padding: 10px 6px; font-weight: 600; text-align: center;
    /* Rotated: status names are 8-15 characters and a horizontal header would make each
       column that wide, pushing a five-status matrix past any sane viewport. */
    height: 128px; width: 44px; white-space: nowrap;
}

.cfg-matrix__colhead > span {
    display: inline-block; transform: rotate(-60deg); transform-origin: center;
    width: 1em; /* keeps the rotated text from widening the column it labels */
}

.cfg-matrix__rowhead {
    text-align: left; padding: 6px 12px; font-weight: 600; white-space: nowrap;
    position: sticky; left: 0; z-index: 2; background: var(--op-surface);
}

.cfg-matrix__cell { text-align: center; padding: 0; width: 44px; }

.cfg-matrix tbody tr:hover .cfg-matrix__cell,
.cfg-matrix tbody tr:hover .cfg-matrix__rowhead { background: var(--op-hover, rgba(15,83,120,.05)); }

/* The diagonal. A self-transition is meaningless — the server refuses a move to the
   status you are already in — so the cell is blocked rather than offered and rejected. */
.cfg-matrix__cell--self { background: var(--op-bg); }
.cfg-matrix__dash { color: var(--op-muted); }

/* One finding row inside a coverage-line group (FindingsTab). A plain div rather than a
   grid row: the rows are grouped, variable-height and clickable as a whole, none of which
   a data grid does well. */
.op-finding {
    padding: 9px 10px;
    border-radius: 8px;
    cursor: pointer;
    border: 1px solid transparent;
}
.op-finding:hover { background: var(--op-hover, rgba(15,83,120,.05)); border-color: var(--op-line); }
.op-finding + .op-finding { margin-top: 2px; }

/* The finding thread. Auditor-internal notes are visually distinct — they never leave the
   building, and mistaking one for correspondence the underwriter can see would be bad. */
.op-msg { padding: 10px 12px; border-radius: 10px; margin-bottom: 10px; border: 1px solid var(--op-line); }
.op-msg__meta { font-size: 12px; color: var(--op-muted); margin-bottom: 4px; }
.op-msg__body { white-space: pre-wrap; word-break: break-word; }
.op-msg--response { background: rgba(7,168,129,.07); border-color: rgba(7,168,129,.30); }
.op-msg--internal { background: rgba(248,206,76,.12); border-color: rgba(199,125,20,.35); border-style: dashed; }
.op-msg--system { background: var(--op-bg); border-style: dashed; font-size: 13px; }

/* ── Notification bell ────────────────────────────────────────────────────
   Hand-rolled rather than MudBadge/MudList: the top bar is built entirely from
   bespoke .op-* CSS, and Material-palette markup here would need a second theme
   kept in step with this one. */

.op-bell { position: relative; display: inline-flex; }
.op-bell__count {
    position: absolute; top: -2px; right: -4px; min-width: 17px; height: 17px;
    padding: 0 4px; border-radius: 9px; background: var(--op-gold, #F2B735);
    color: #3A2A00; font-size: 10.5px; font-weight: 800; line-height: 17px;
    text-align: center; pointer-events: none;
}

.op-bell__popover { max-width: 380px; min-width: 320px; }
.op-bell__head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 8px 14px; font-weight: 700; border-bottom: 1px solid var(--op-line);
}
.op-bell__empty { padding: 18px 14px; color: var(--op-muted); font-size: 13px; }
.op-bell__item { padding: 9px 14px; cursor: pointer; border-bottom: 1px solid var(--op-line); }
.op-bell__item:hover { background: var(--op-hover, rgba(15,83,120,.05)); }
.op-bell__kind { font-size: 11px; font-weight: 700; letter-spacing: .04em; color: var(--op-muted); text-transform: uppercase; }
.op-bell__title { font-weight: 600; font-size: 13.5px; }
.op-bell__num { color: var(--op-muted); font-weight: 400; }
.op-bell__sub {
    font-size: 12.5px; color: var(--op-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.op-bell__when { font-size: 11.5px; color: var(--op-muted); margin-top: 2px; }
.op-bell__more { padding: 8px 14px; font-size: 12px; color: var(--op-muted); font-style: italic; }

/* ---- CoverAll policy grid (PolicyGridDialog) ----
   ⚠ THE HORIZONTAL SCROLL IS THE POINT. The grid shows all 27 columns of XML_Policies, so
   it is wider than any dialog; it scrolls INSIDE its own container so the dialog and the
   page behind it never do. The sticky first column keeps the pick button reachable at any
   scroll offset — without it, choosing a row means scrolling back left every time. */
.pg-filters { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.pg-filters__row { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; }
.pg-filters__row--actions { align-items: center; gap: 8px; padding-top: 2px; }

.pg-grid { overflow-x: auto; }
/* MudDataGrid renders its own table; this only guarantees the columns keep their width
   instead of compressing them all into the dialog. */
.pg-grid .mud-table-container { overflow-x: auto; }
/* ⚠ THIS TRACKS THE COLUMN COUNT AND HAS TO BE REVISITED WHEN IT CHANGES: set too high it
   invents a horizontal scrollbar for columns that would have fit. Seventeen columns now
   (2026-09-04) — «Transacción», «Registrada por» and «Prima endoso» went, the first two
   into the expansion and the third into «Prima» — so this came down from 1750. Still wider
   than a dialog, which is why the scroll and the sticky pick column remain. */
.pg-grid table { min-width: 1320px; }
.pg-grid th, .pg-grid td { white-space: nowrap; }

/* Numbers right-align, so premiums of different magnitudes compare by eye. */
.pg-num { text-align: right !important; }

/* The policy number reads as an identifier, so it gets a monospace-ish treatment: the
   segments line up down the column and a transposed digit is visible. */
.pg-mono { font-variant-numeric: tabular-nums; letter-spacing: .01em; white-space: nowrap; }

/* The status chip is the widest thing in a narrow column; keep it from wrapping. */
.pg-status { white-space: nowrap; }

/* The endorsement badge: a count, and the last endorsement number beside it. */
.pg-endo { display: inline-flex; align-items: center; gap: 4px; font-weight: 600; }
.pg-endo__last { font-weight: 500; font-size: 11.5px; color: var(--op-muted); }

/* What a transaction MOVED, beside the premium it produced. Subordinate on purpose: the
   column is «Prima» and the total is the answer; the movement is context. Tabular figures
   so the signs line up down the column, and a left margin because the cell is
   right-aligned and the two numbers would otherwise touch. */
.pg-move { margin-left: 8px; font-weight: 500; font-size: 11.5px; color: var(--op-muted);
    font-variant-numeric: tabular-nums; }

/* ---- The endorsement expansion inside the policy grid ----
   A term's transactions, shown inside its own row. Inset and tinted so the nesting is
   obvious at seventeen columns, where an un-inset child table reads as more parent rows. */
.pg-child { background: var(--op-surface-2); padding: 0 0 0 52px !important; }
.pg-child__table { background: transparent; }
.pg-child__table th { font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
    color: var(--op-muted); border-bottom: 1px solid var(--op-line); }
.pg-child__table td { font-size: 12.5px; white-space: nowrap; }
/* A cancelling transaction is the one row in the list that changes what the policy IS. */
.pg-child--cancel td { color: var(--op-red); }
.pg-endo__btn { min-width: 0; padding: 2px 8px; font-variant-numeric: tabular-nums; }

/* The transaction count beside each dropdown value: present so a list of 83 booking users
   or 29 transaction types can be read by significance rather than alphabetically, quiet
   enough not to compete with the value itself. */
.pg-uses { margin-left: 8px; font-size: 11px; color: var(--op-muted-2); font-variant-numeric: tabular-nums; }

/* The four LOB columns are two characters wide; centre them so Y and N line up. */
.pg-lob { text-align: center !important; }
.pg-grid td:nth-last-child(-n+4) { text-align: center; }

/* The insured name is the one column worth extra room — it runs to 50+ characters in the
   live data ("H.I. DEVELOPMENT PR CORP AND OR PONCE RESORTS INC"). */
.pg-grid td:nth-child(4), .pg-grid th:nth-child(4) { max-width: 340px; overflow: hidden;
    text-overflow: ellipsis; }
/* ⚠ The XmlPath column was HIDDEN on 2026-09-03, so the rule that used to truncate it
   is gone with it — left as a note because nth-last-child(2) now names an LOB flag, and
   re-adding that rule would silently clip a one-character cell. */

/* ── Shared/HelpTip ─────────────────────────────────────────────────────────────────────
   The help icon LookupSection's own helper text promises («se muestra en el ícono de ayuda
   junto al campo»). Deliberately quiet: it sits beside a label or a heading it is
   explaining, so it must not compete with it for attention — muted until hovered.
   inline-flex with a baseline-ish alignment keeps it on the text's line; vertical-align
   alone does not centre a flex child. */
.op-helptip { display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px; margin-left: 5px; border-radius: 50%; cursor: help;
    color: var(--op-muted-2); font-size: 12.5px; vertical-align: middle;
    transition: color .15s, background-color .15s; }
.op-helptip:hover { color: var(--op-brand); background: var(--op-surface-2); }
/* ⚠ A VISIBLE FOCUS RING IS NOT OPTIONAL HERE. The tip carries tabindex="0" so it can be
   reached without a mouse, and a focusable control with no focus style is invisible to
   the person who most needs the affordance. */
.op-helptip:focus-visible { color: var(--op-brand); background: var(--op-surface-2);
    outline: 2px solid var(--op-brand); outline-offset: 1px; }
/* The tooltip body renders inside MudBlazor's portal at the document root, OUTSIDE this
   component's box — so it is styled by class here and cannot inherit anything from the
   control it explains. Line height is set because the default is tuned for one-line
   tooltips and these run to three or four. */
.op-helptip__body { line-height: 1.45; font-size: 12.5px; white-space: normal; }
.op-helptip__body ul { list-style: disc; }
