/* ============================================================
   COUNSEL PORTAL — CSS Design System
   Pixel-matched to Clio Manage (verified from Clio's own CSS)
   BUILD: 2026-08-05a - Communications rebuilt as a two-pane, client-grouped view:
   a list of clients (.co-crow) on the left, and that client's unified thread on the
   right (.co-stream) - email/call/text/internal in one chronological stream, texts as
   .mtx-* bubbles, calls as .co-call cards, emails as collapsible .co-mail cards.
   Backed by ?comms_clients + ?comms_thread. Reuses ic()/avatarMarkup/.tab-strip/uiConfirm.
   BUILD: 2026-07-30b - Settings now uses the sitewide full-width page shell: .settings-page
   pins #main to the full main column (was shrink-wrapping to content width, leaving dead
   grey on the right) and .settings-page-body gives a uniform 12px gutter all round, matching
   Tools / Contacts / Forms. Grid fills the width (max-width dropped).
   BUILD: 2026-07-30a - Settings page rebuilt Clio-style: three balanced columns of titled
   links (.settings-grid/.settings-col-head/.settings-item); the middle Portal-settings
   column's live controls moved into focused detail views (.settings-detail/.settings-field)
   opened via settingsOpen(), so it no longer towers over the two link-list side columns.
   BUILD: 2026-07-19a - Forms fill (2848/2553/56) + picker now on the sitewide 8px-between /
   12px-card-edge standard: killed the .tpl-page .form-group 8px margin bleed into .forms-page-body
   (Forms lives inside the Documents hub now), and set every fill grid gap (.forms-grid/.forms-row/
   .forms-twocol/.forms-col) to 8px. Bottoms now read 12px, field gaps 8px (was 20px / 16px).
   BUILD: 2026-07-18a - Form 2848 = the canonical layout reference. Line-2 rep separators inset
   from the card edges (only between-LINE separators run full card width). Line-3 matters table +
   the new Part II Declaration table (.forms-dtable) center every column header AND the cells under
   it, matching the printed form (rule: centered header -> centered cells). Part II reworked from
   collapsible rep panels into a 6-col table (Representative | Designation | Licensing jurisdiction |
   Bar/license no. | Signature | Date); include checkbox mirrors the Part I line-2 rep. (Line 5b
   merged to one field + wrapped across the form's short/wide blanks - forms.js/app.js.)
   BUILD: 2026-07-14c - Icon system: every markup .icon (147 sites in app.js + 9 in index.html/
   questionnaire) converted from CSS-masked spans to inline <svg> via the ic(name, cls) helper +
   ICON map (app.js). Inline SVG is crisp at fractional DPR (mask blurred); colour preserved via
   fill:currentColor. .icon CSS split: sizing on .icon, fill on svg.icon, mask kept only on
   .icon:not(svg) for legacy. CSS-applied masks (native <select> carets, ::before decorations) and
   background-image SVGs are unchanged (can't be inline / already crisp).
   BUILD: 2026-07-14b - Sort indicator SORTED single carets are now inline <svg fill=currentColor> too
   (were CSS-masked .icon spans) so they rasterize consistently at fractional scaling like the combined
   glyph; removed the now-orphaned caret-updown-fill.svg. Intake date/time columns (Sent, Started, Last
   change, Submitted) pinned to a fixed 175px on both the main + embedded pages (locked in
   INTAKE_COLUMNS so they can't be dragged) - fits the side-by-side date+time with slack for a
   2-digit-hour time so neither page overflows; a narrower width overflowed on the denser
   embedded table while the main table silently stretched it, which is why the two didn't match.
   Cell gap tightened to 4px; embedded Intake column forced flexible to absorb the table's slack.
   BUILD: 2026-07-14a - Sort indicator UNSORTED state is now an inline <svg fill=currentColor> combined
   up/down glyph instead of a CSS mask (center/contain). The masked glyph re-rasterized at each header
   cell's fractional sub-pixel position (varying column width / header height at 125% scaling), so the
   gap rendered "hit or miss" cell-to-cell and worse on taller-header pages. Inline SVG draws the pair
   as one vector unit -> identical everywhere. Applies to all 15 sortable tables. Sorted state unchanged.
   BUILD: 2026-07-12d - New-intake Client field is now the shared ecCombo (caret / recent-5 /
   + New contact / clear-X) with the primary email shown per row (.forms-ta-sub, subFmt);
   Client / Template / Label sit at 1/3 width (.form-row.thirds)
   BUILD: 2026-07-12c - New-intake compose is its own centered .ec-form + .ec-footer page
   (Client / Questionnaire sections, actions in the footer); removed dead #intake-compose-card
   + .forms-q-actions rules
   BUILD: 2026-07-12b - Contact Intake surfacing: red .tab-badge (awaiting-review count) on the Intake
   tab, compact .contact-intake-list card in the Dashboard aside (staff only)
   BUILD: 2026-07-12a - Edit contact: shorter Zip / wider County, compact 90px Type on email/phone/website,
   custom fields one per row (+ first-word pairs like Driver), Company clear-X hidden when empty,
   Remove hidden on a section's only entry
   BUILD: 2026-07-10d - Activities #aa-table-container joined the TR/Contacts single-scroller group
                        (display:flex column + overflow:hidden) so the inner .tr-scroll is the one
                        scroller with BOTH scrollbars pinned to the card edges - the horizontal bar
                        now sits right above the footer instead of at the bottom of the scrolled body.
   BUILD: 2026-07-10c - Activities page now uses the standard .tr-page-body wrapper (like Tasks) and
                        joins #aa-card / #aa-table-container to the shared card-flex + scroller +
                        width:100% + toolbar-padding rule groups. This gives it the identical shell:
                        #main width-pinned, table scrolls inside the card, footer pinned, search +
                        Columns stay in the viewport. (The earlier body:has(#aa-card) #content min-width
                        rule alone was insufficient - it missed the #main width-pin.)
   BUILD: 2026-07-10a - Intake workflow: editable review modal (.rv-*), intake dashboard toolbar
                        (.intake-toolbar / .intake-search / .tab-count / .intake-actions) + status
                        badges (.badge-verified / .badge-voided). Client form moved to
                        /js/questionnaire.js; questionnaire.html restyled (autosave hint, checklist,
                        Lob suggestions, same-as, verify row).
   BUILD: 2026-07-09m - Matter detail: Custom fields merged into the Details card as a subheading (like
                        contacts, no separate empty card); main-column label widened to 195px so
                        "Statute of limitations date" fits on one line.
   BUILD: 2026-07-09l - Matter detail Details card: shows the Statute of limitations date (fetched async
                        via ?matter_sol, injected after Open date; "Satisfied" badge when complete).
   BUILD: 2026-07-09j - SOL: first-SOL creation isn't possible via Clio's API (is_statute_of_limitations
                        ignored on create), so new-SOL entry is disabled with a "Set in Clio" hint;
                        editing an existing SOL still works.
   BUILD: 2026-07-09i - SOL date field sits in a thirds row so it matches Open date's width; the
                        "satisfied" checkbox cell spans the remaining two columns.
   BUILD: 2026-07-09h - Edit/New matter: Statute of limitations date + "satisfied" checkbox (writes the
                        hidden is_statute_of_limitations task: due_at + status; reminders repeater deferred).
   BUILD: 2026-07-09g - Contact cards: Yes/No icons now inline SVG (crisp/uniform at 125%) + line-boxed so
                        they align on the value's first line; label column 120->168px (fits "Claimed as a
                        dependent"); wrapping street addresses break before the secondary unit (Apt/Ste).
   BUILD: 2026-07-09f - Contact Custom fields: Yes/No values render as an icon pair (matches the 2848
                        column check) - Yes = green check-bold #067647, No = red x-bold #b42318, both 18px.
   BUILD: 2026-07-09e - Edit contact Custom fields: grouped instead of a flat interleaved grid - general
                        fields first, then a full-width subheader + prefix-stripped fields per Dependent N.
   BUILD: 2026-07-09d - New/Edit task modal (Clio-style): opens from the New task button + the task-name
                        link. Details (Name/Priority 2:1, Matter, Description, Assign to, Task type,
                        Status/Time estimate 1:1) + Due date; footer left-aligned; body scrolls at 90vh.
                        Backend: task_save now updates (PATCH) + writes the mirror; ?task_meta feeds
                        assignees/task-types/statuses.
   BUILD: 2026-07-09c - Sort indicator: unsorted state is now ONE combined up/down glyph (caret-updown-fill,
                        2px gap baked in) instead of two overlapping icons that rounded inconsistently at
                        fractional scaling. Per-page caret recolored to --text-primary (matched the others).
                        Columns + New buttons: caret now 8px from the right edge (auto-width + 8px right pad);
                        New caret 18->12px. Date-range dash 1.5->2px so it's reliably bold on every tab.
   BUILD: 2026-07-09b - Icon pass: sort caret pair now a true ~2px VISIBLE gap (margin -5.5px, the +2px box
                        gap read as ~9.5px visible). Dropdown carets sit a uniform 8px from the box right
                        edge (per-page 11->8, ec-combo centered->right-8). Text-button icons 18px (Export
                        16->18, Filing 15->18). Doc/folder icons 20->18. Contacts People/Companies icons 18px
                        + exact colors (person rgb(194,78,0), company rgb(117,55,217)) on row + filter. PoA
                        (2848) check 20->18. Overdue icon explicit 18. Activities Time/Expense icons 18px,
                        Time=rgb(194,78,0), Expense=invoice-fill rgb(0,105,209) on rows + tab strip.
   BUILD: 2026-07-09a - Caret sizes standardized portal-wide: dropdown-box carets 12px (cell-select,
                        per-page, stage-select joined select-input/toolbar-select/combo/columns at 12px);
                        pagination 12px (already); collapse-card chevrons 12px (matter-section 11->12,
                        forms-sub 13->12; contact-card already 12). Sort carets are 11px in EVERY state
                        (dropped the 16px sorted bump); unsorted pair now has a 2px gap (was -5px overlap).
   BUILD: 2026-07-08t - Global uniform toolbar gap (.tr-filter-row 8px; removed per-element -4/+4 nudges;
                        date preset back to 8px, coupled pickers stay 5px). Sorted sort-caret enlarged to
                        16px so an active-sort column isn't smaller than unsorted neighbours. Overdue red ->
                        #d30933, warning icon 15->18px, pulled to 4px from the date. Export buttons get a
                        leading download-simple-fill glyph (16px). Topbar logo -> all-white reversed variant.
   BUILD: 2026-07-08s - Global sort caret centered when sorted (sorted-asc lone caret no longer sits 2.5px
                        low). Tasks: matter name is a link, task/matter descriptions now base color
                        (--text-primary), Assigned-to filter sticky, left-cluster gaps uniform (date preset
                        5px, assignee 8px from the date filter). Missing task columns fixed at the source
                        (dropped invalid `complete` field so assigner/task_type/completed_at/estimate fetch).
   BUILD: 2026-07-08r - Tasks page polish: removed KPI cards (kept Assigned-to dropdown); overdue uses
                        warning-circle-fill masked red; status pill now .contact-stage-pill (matches TR);
                        descriptions base color + fs-base; Search<->Columns gap restored (date pulled to tabs).
   BUILD: 2026-07-08q - Tasks filters: KPI-style quick-filter cards (All / My tasks / Overdue / Unassigned,
                        clickable with live counts) + an Assigned-to dropdown in the toolbar; both drive a
                        shared 'view' filter (single-select).
   BUILD: 2026-07-08p - Date-range filter tightened (shared .mc-date-range): 8px->5px gaps, longer dash
                        drawn as a 12px rule (no en-dash char), preset gets 3px air; Tasks tab->date gap
                        12px->8px. Applies to Tasks + Matters + Communications tabs.
   BUILD: 2026-07-08o - Tasks page to match Clio: resizable columns (makeColumnResizer) + horizontal
                        scroller (11 fixed cols summing past the card), MslDp due-date range filter +
                        8 presets, full Clio column set (Action mark-complete / Completed / Assigned by/to /
                        Recorded time [deferred] / Time estimate) via migration 062 + task_complete PATCH.
   BUILD: 2026-07-08n - Tasks page fixes: #tasks-card / #tasks-table-container joined the flex-column +
                        scroll + width rules of #tr-card / #contacts-card (footer + scroller were missing),
                        added the Columns button/menu. Data now mirror-backed (migration 061 + sync
                        phase=tasks, wired into the cron) so the page shows every current task, not the
                        oldest live-fetched 200.
   BUILD: 2026-07-08m - Tasks page rebuilt on the modern list-page chrome (.card / .card-toolbar /
                        .data-table / .card-footer, same as Contacts + TR): Outstanding/Completed tabs +
                        search, sortable columns (Due date / Name / Matter / Assignee / Status / Task type),
                        live Clio fetch (?tasks_list), New task creates in Clio (task_save).
   BUILD: 2026-07-08l - Edit-contact address: Lob autocomplete dropdown (.ec-ac-list) under the Street
                        field + Verify affordance (.ec-addr-verify). Gated on config.addressAutocomplete.
                        Pick fills street/city/state/zip + auto-verifies to fill County.
   BUILD: 2026-07-08k - Edit-contact address grid (has-county): row 2 now fills the full card width
                        (Zip 140 / County 175 / Country 194 / Type 90); row 1 = City (2 cols) + State (1 col),
                        4th col empty for the Remove button. Zip label fits one line, "34221-1306" not
                        truncated; "District of Columbia" fits State; no overflow (grid 636 <= 635.67 avail).
   BUILD: 2026-07-08j - Contact detail address: uniform 24px line height on every address line so
                        County/Country (plain divs) don't sit tighter than Street/ZIP (24px copy-button rows).
                        Measured: gaps 24/24/24.
   BUILD: 2026-07-08i - Edit-contact address grid: County moved into row 2 between Zip and Country
                        (Zip 95 / County 180 / Country 140 / Type 100; City/State span 2 cols on row 1).
                        Measured: no Remove overlap (20px gap), "Hillsborough County" fits.
   BUILD: 2026-07-08h - Contact detail: County is now line 3 of the address block (Country becomes line 4),
                        per-address from address_counties (primary falls back to the Clio county field).
                        Removed the separate "County" row. Works even without migration 059 (custom-field fallback).
   BUILD: 2026-07-08g - Entity-type write-back: same picklist fix (value=<option id>). County picklist
                        write CONFIRMED working (Clio "County - FL" populated). Picklist write shape logged in memory.
   BUILD: 2026-07-08f - contact_update PICKLIST write fix: Clio sets a picklist via value=<option id>,
                        not the nested picklist_option:{id} (which it 200s and silently ignores - verified).
                        The write-verification now confirms whether it lands.
   BUILD: 2026-07-08e - contact_update: verify custom-field writes landed (Clio can 200 while silently
                        dropping a picklist cfv). Re-reads after the PATCH, warns via cfv_not_applied +
                        logs the re-fetched cfv shape to Sentry - to diagnose why County didn't apply.
   BUILD: 2026-07-08d - County field detection hardened: letters-only name match (County - FL / County-FL /
                        County FL all hit the picklist), and the contact/matter custom-field-def cache
                        dropped to a 60s TTL so a just-created Clio field appears without a server recycle.
   BUILD: 2026-07-08c - Edit contact: County field on EVERY address row (FL -> "County - FL" picklist,
                        else "County" text). All counties stored on the portal (clients.address_counties,
                        migration 059); only the PRIMARY address's county writes back to Clio. Hardened per
                        adversarial review: never auto-clear on empty, normalized FL detection, conservative
                        FL<->text migration, conditional 3-row address grid (has-county).
   BUILD: 2026-07-08b - Edit contact: State/Province is now a US-states dropdown when Country is the US
                        (free-text otherwise), rebuilt on Country change. Save reads .ec-province unchanged.
                        (County field + Clio custom-field storage + Lob autofill are the next steps.)
   BUILD: 2026-07-08a - Edit matter: Clients section is single-client-editable / multi-client-read-only.
                        Live test confirmed Clio's API can't write a multi-client list AND setting
                        client{id} on a multi-client matter DROPS the co-clients - so multi-client matters
                        now show a read-only client list + "manage in Clio" note; single-client matters
                        keep the one editable client (client{id}, safe). Removed the client-rows UI.
   BUILD: 2026-07-07w - Edit matter (increment 2): Clients + Related contacts sections. Clients =
                        contact comboboxes + a Default radio (primary via client{id}); the full list
                        writes via client_ids only when changed (best-effort Multi-Client, verify+warn).
                        Related contacts = the separate /relationships resource (add/remove/edit). Rows
                        reuse the .ec-row chrome + ec-* combobox; measured 310.6px cells, no overflow.
   BUILD: 2026-07-07v - Edit matter: Location is now a dropdown (Bradenton, FL / Hollywood, FL; off-list
                        values preserved, blank = clearable).
   BUILD: 2026-07-07u - Edit matter (increment 1): full-page /matters/<id>/edit editor + top-right button,
                        mirroring Edit contact. Matter details + Custom fields sections write to Clio
                        (?matter_for_edit loader + body.matter_update save). Reuses .ec-form/.form-card
                        chrome + the ec-* custom-field subsystem - no new CSS. (Clients + Related contacts next.)
   BUILD: 2026-07-07t - Edit-contact: Address Remove button top 22.5->36 (down 13.5px per user).
   BUILD: 2026-07-07s - Edit-contact: Address Remove button top 65.5->22.5 (up 43px per user).
   BUILD: 2026-07-07r - Edit-contact: Address Remove button top 29->65.5 so it centres on the WHOLE
                        address entry (fields/Street centre 85.5px), not just the first grid row.
   BUILD: 2026-07-07q - Edit-contact: bolder X (new x-heavy.svg, ~2px strokes vs Phosphor x-bold's 1.54px)
                        on BOTH the custom-field X and the Company clear-X (now 16px, no red hover tint);
                        Address Remove button top 26->29 so it centres on the City/State input row (49px).
   BUILD: 2026-07-07p - Edit-contact: Company combobox clear-X moved INSIDE the field (left of the caret,
                        search-box style) + Company field stretched to Email width (310.58); custom-field row gap 6px.
   BUILD: 2026-07-07o - Edit-contact Company combobox clear-X gap 6px->12px to match the custom-field Xs.
   BUILD: 2026-07-07n - Multi-Client fix: fetch client_ids in an ISOLATED Clio call - Clio silently
                        drops the client_ids array when it is combined with client{} in one request
                        (the combined fetch 200s but omits it), so the freshener now fetches it on
                        its own and merges before upsert.
   BUILD: 2026-07-07m - Multi-Client Matters: matter page shows "Clients (N)" - co-clients Clio
                        returns in client_ids (beyond the singular default client) resolve via the
                        contacts mirror. Migration 058 + sync/freshener fetch client_ids.
   BUILD: 2026-07-07l - Matter page always re-fetches on navigation (was reusing a session cache,
                        so reopening never hit the server-side freshener) + freshener logs Clio's
                        client/relationships shape to ?outlook_logs=1 for the 2-clients diagnosis.
   BUILD: 2026-07-07k - Matter page self-heals stale mirror rows: opening a matter re-fetches it
                        from Clio (index endpoint, incl. relationships) when the mirror row is
                        older than 60s - Clio doesn't bump matter.updated_at on client/related-
                        contact changes, so the incremental sync can't see those edits.
   BUILD: 2026-07-07j - X buttons at Clio's measured geometry: 40x40 button (flush with the 40px
                        fields - centers automatically), 16px glyph at natural weight.
   BUILD: 2026-07-07i - Radius consistency sweep: fields now share the BUTTON radius (--r, 6px) -
                        the .input trio was 4px vs .btn's 6px. All interactive controls follow
                        (toolbar search, per-page select, pagination, filter tabs, split actions,
                        datepicker btn/nav/cells, compose-editor chrome, forms tiles/segments, type
                        toggle, X squares, icon/copy buttons, modal close). --r-sm is now reserved
                        for non-interactive chrome (chips/callouts/panels) - rule documented at the
                        radius tokens.
   BUILD: 2026-07-07h - Edit contact X polish: bolder glyph (mask zoomed to 200%) + Clio hover
                        (30px light-grey rounded square, glyph stays dark); the combobox clear X
                        restructured onto the same button so it hovers identically.
   BUILD: 2026-07-07g - Edit contact remove affordances: custom fields (individual values) use
                        Clio's dark X buttons; whole entries (email/phone/website/address) keep
                        Remove links. Spouse combobox drops its internal X - the field is the full
                        310.58 cell width and a row-level X (wired to the combobox clear) lines up
                        with the other custom fields' X buttons.
   BUILD: 2026-07-07f - Edit contact card = 998.25px: the user's th-card-content DevTools capture
                        confirms the tool factor at exactly 1.14 (1138 / 998.25) and anchors the
                        card width directly.
   BUILD: 2026-07-07e - Edit contact DevTools-anchored dimensions: Clio's First name computes
                        230.58 x 39.99, fixing the tool factor at 1.1319 - fields 230.58, prefix 68,
                        big fields 310.58, Type 149, card 1005, padding 19, control height 40
                        (global .input standard 41 -> 40; DOB datepicker pinned to 40 in ec scope).
   BUILD: 2026-07-07d - Edit contact 9% correction: the supplied Clio numbers carried a uniform
                        ~8.75% screenshot inflation - true values are prefix 71 / fields 240 / big
                        fields 323 / Type 155 / card 1046 / padding 20. Address Remove pinned
                        top-right so the grid's second row passes beneath (no more overflow).
   BUILD: 2026-07-07c - Edit contact exact Clio widths: name tracks 77/261/261/261, Company/Email/
                        Phone/Web/Street 350 (end at First name's edge), Title/DOB/City/State/Zip/
                        Country 261, Type 169; custom-field controls + Add picker unified at 350;
                        Country is a dropdown; Company/Spouse comboboxes get Clio's caret + outside X.
   BUILD: 2026-07-07b - Edit contact polish 5: FIXED footer bar (was sticky), 20px bold section
                        headers, 12px field gutters, Clio field widths (email/phone/website 340px
                        left-packed; Company spans the name-row tracks), 41px Person/Company toggle
                        + divider under it, SVG plus icons on Add links, removing a Primary
                        email/phone promotes the next row.
   BUILD: 2026-07-07a - Edit contact Clio-parity styling: 1000px card (user's tool reads it as
                        1138 at their 1.1385 ratio), 19px card padding, 16px bold section headers
                        w/o underlines, no dividers between same-type rows, Clio 2-row address grid
                        (Type in row 2), custom fields 2-per-row, and the STANDARD control height
                        raised 34px -> 41px app-wide (Clio's forms height).
   BUILD: 2026-07-06d - Edit contact round 3: editor loads LIVE from Clio (fixes save-duplicated
                        emails/phones/addresses + stale blanks overwriting Clio), 910px card,
                        Clio-parity rows (Prefix/First/Middle/Last + Company/Title/DOB), 34px DOB
                        field, Primary labels base color. Global search: matter rows drop the
                        appended client name, recents use Clio edit time, clients un-joined.
   BUILD: 2026-07-06c - Edit contact fixes: sticky always-visible footer bar (Save/Cancel moved out
                        of the header into a pinned .ec-footer), 12px (--space-3) vertical rhythm
                        between header/cards/footer, and the save-breaking custom-field-value id bug
                        (Clio composite "<type>-<unique>" ids now sent verbatim, not digit-stripped).
   BUILD: 2026-07-06b - Edit contact round 2: full-width header (body:has(.ec-form) #main rule),
                        879px centered column, one merged card w/ subsection separators + Custom
                        fields card, Company + Spouse comboboxes (recent-5 / filter-as-typed, X
                        clear), DOB on the MslDp mm/dd/yyyy picker. Recency = clients.clio_updated_at
                        (migration 057).
   BUILD: 2026-07-06a - Edit contact polish: form column 922px wide + centered, field labels base
                        color, Spouse/Decedent contact-ref custom fields show the contact's name as
                        a link (read-only, never written back), prefix no longer clearable by accident.
   BUILD: 2026-07-04a - Edit contact full-page editor (/contacts/<id>/edit): core attrs + email/phone/
                        website/address repeating rows (.ec-row) + custom fields (incl. picklist);
                        saves via ?contact_update (Clio PATCH + clients-mirror write-through). Company/
                        tags read-only; billing/photo out of scope.
   BUILD: 2026-07-03j - Matter Notes tab (mirror-first, migration 056): keyword search + Columns +
                        sortable Date/Subject/Author, full-note hover popover, New note / edit / delete
                        modal, footer pager + Export. Notes ride the phase=communications sync.
   BUILD: 2026-07-03i - Export buttons on the Communications + Activities tab footers (CSV of the
                        full filtered/sorted set; Activities appends a Totals row).
   BUILD: 2026-07-03h - Compose: email templates (picker + Save as template + Delete, migration 055)
                        with {{autofill}} tokens (resolve on apply + at send, unresolved blocks the
                        send) + "Add autofill data" caret-insert menus; photo signature (Clio-style
                        two-column block, avatar inlined as a cid attachment on send).
   BUILD: 2026-07-03g - Matter Activities tab: time + expense entries in the standard table shell
                        (All/Time/Expense strip w/ icons, date filters, totals row) + shared New
                        time entry / New expense modals (.ma-money-group $-prefix fields). Comms
                        Add-time now opens the same full modal (inline mini-form removed).
   BUILD: 2026-07-03f - Comms date filters sized to the toolbar chrome: control-h (38px) height,
                        slate border-control border, 6px radius on both pickers + the preset select.
   BUILD: 2026-07-03e - Comms date-range filter: two generic Clio-style datepickers (multi-instance
                        .msl-dp component, same design as the 2848 Signature date) + a Today / This
                        week / This month / This year / All dates quick-preset select (.mc-date-*).
   BUILD: 2026-07-03d - Comms: Recorded time column (clock + hours link, or Add time button opening
                        the modal's entry form); type icon truly vertical-centered; body preview
                        truncates at the cell edge (td max-width:0), not a fixed ch budget.
   BUILD: 2026-07-03c - Comms subject is a link: hover = full-body popover (.mc-body-pop), click =
                        Edit email log modal (date/time + subject + body edit -> Clio PATCH, Delete,
                        Recorded time list + Add time entry). Type icon / date / preview in base
                        color; date cell = date left + time right (.mc-dt).
   BUILD: 2026-07-03b - Communications tab is now an exact Documents-tab table clone: tr-th sortable
                        headers (paired carets, 3-stage), Columns picker, resizable columns, footer
                        pager (prev/next + "start-end of total" + per-page). Compose modal: labels in
                        base color + To prefilled with the matter client's primary email.
   BUILD: 2026-07-03a - Matter detail gains a Communications tab (live Clio comms in the standard
                        table shell) + a compose-email modal (.ce-editor rich-text + .mc-table cols).
                        Send-and-log via Graph (?email_send) - Phase 1 of portal email.
   BUILD: 2026-07-01b - OIC tab is now one row per OFFER (not per client): an OIC covering both a
                        Separate Assessment period and regular periods auto-splits into an "SA" offer
                        + a "main" offer, each with its own status/estimate/current-liability and its
                        own manual overrides (main = legacy cols; SA = clients.oic_sa_override, migr
                        048). Status: a same-day resubmission (more TC 480 receipts than removals on
                        the latest date) reads Pending. Rollup returns an `oic` array (like `fta`).
   BUILD: 2026-07-01a - OIC dollars: offer now DERIVED from the 20% down payment (non-$205 TC 670
                        within +-3d of received) x 5, falling back to the payment-sum; the Liability
                        column shows the compromised amount for ACCEPTED offers and the CURRENT
                        balance (read-only, .oic-current) for all others. Pennies uses the effective
                        pair. Estimate is for every offer now (incl. pending), not just accepted.
   BUILD: 2026-06-30n - Dashboard header renames (IA "Status"; OIC "Filed"/"Status"/"Offer"/
                        "Liability"/"¢/$"; KPIs "Filed"/"Avg ¢/$ paid"). OIC KPI card no longer
                        stretches/wraps - grid 1fr tracks under width:fit-content equalize the 8
                        cards to the widest, packed left like Overview/IA; labels nowrap.
   BUILD: 2026-06-30m - First Time Abate dashboard tab (mirrors PitBull): one row per (client, form,
                        tax period) carrying an FTA-abatable penalty. FTF/FTP/FTD are transcript-
                        derived (posted TCs, NET assessment-minus-abatement); eligibility = clean
                        3-prior-years (same form) + filing compliance, honest "Needs prior years"
                        when the 3 years aren't pulled. Potential abatement KPI + green verdicts.
                        model.js gained the reversal/abatement + amended TCs so the net sum works.
   BUILD: 2026-06-30l - OIC dollar auto-derivation (estimates, shown italic + "estimated"): offer
                        = sum |TC 670| in [filing, acceptance+90d] (low conf, hover lists payments);
                        liability compromised = signed balance before filing (medium). Manual entry
                        overrides; pennies/savings/avg use the effective value. extract.js helper.
   BUILD: 2026-06-30k - OIC increment 2: removed the results chart. Added the dollar layer -
                        click-to-edit Offer amount + Liability compromised cells (manual entry, not
                        on the transcript; migration 047), a computed Pennies column, and Total
                        savings + Avg pennies KPIs. Persisted via poa.js -> clients.oic_offer_amount
                        / oic_liability_compromised. (Auto-derivation from payments/write-offs TBD.)
   BUILD: 2026-06-30j - OIC "Filed by us" column (green-check toggle, reuses .poa-toggle): uncheck
                        to exclude an OIC the client filed before retaining us - it drops from the
                        KPIs/chart + mutes the row. Backed by clients.oic_excluded (migration 046).
   BUILD: 2026-06-30i - OIC chart: replaced the funnel with a horizontal stacked results bar +
                        dot legend (Clio palette). OIC KPI card no longer stretched - matches the
                        other tabs' card height (align-items:flex-start).
   BUILD: 2026-06-30h - OIC review polish: 3rd sort-click resets to the active tab's default
                        column (trxDefaultSort), not a hidden one; OIC KPI cards shrink to fit.
   BUILD: 2026-06-30g - Offer in Compromise tab (increment 1, transcript-derived): Client / OIC
                        filing date / Tax forms & periods / OIC result (pill) / Days in review,
                        with KPI cards + a results funnel (Filed -> Decided -> Accepted). Plus a
                        CSED fix (accepted OICs stop tolling). Dollars (offer / liability
                        compromised / pennies / total savings) are increment 2 (manual layer).
   BUILD: 2026-06-30f - Lien flag pill detail card now click-pins too (shared pin helper); a
                        per-trigger toggle so Various + Lien popups don't fight.
   BUILD: 2026-06-30e - IA "Various" popup: rows now "1040: 2019" (form semibold, year normal),
                        newest year first, and CLICK pins it open (hover still peeks; click away
                        or click Various to close). New hover detail card on the Lien flag pill.
   BUILD: 2026-06-30d - Table form labels: "Civil Penalty" -> "CP" (alongside "Separate
                        Assessment" -> "SA"), display-only via trxShortForm.
   BUILD: 2026-06-30c - IA "Various" status: no longer a link to the contact dashboard - now a
                        hover popup (reusing .trx-status-card) listing each period's IA status
                        (form + year + Active since/Terminated on/Defaulted), backed by ia_modules.
   BUILD: 2026-06-30b - 2848: dropped the native checkbox entirely (appearance:none left residue
                        that composited under the icon). Now a .poa-toggle BUTTON with a clean
                        green check-bold .icon span (toggles like a checkbox, keeps functionality).
                        Also: "Separate Assessment" form label shortened to "SA" in the table.
   BUILD: 2026-06-30a - 2848 indicator -> green shield-check-FILL (solid glyph, no strokes to
                        alias) - the definitive consistency fix; bold/outline glyphs still
                        shimmered per-row at 125% scaling.
   BUILD: 2026-06-29z - 2848 indicator -> green shield-check-bold (an enclosed/bold glyph renders
                        consistently; a thin check's strokes aliased unevenly per row at 125%).
   BUILD: 2026-06-29y - 2848 green check: 14px fixed mask -> 20px mask-size:contain (matches the
                        document file-type icons), so it renders crisp + consistent at 125% scaling.
   BUILD: 2026-06-29x - Flag pills moved onto the Clio/TR stage palette (trxFlagClass ->
                        active/onhold/completed/closed) so the Overview Status column + IA Flags
                        match the Tax Returns table: Lien/unfiled = on hold, Exam/OIC = active,
                        Current/Defaulted = completed, CNC = closed.
   BUILD: 2026-06-29w - IA Flags pills colored: Lien = On hold (amber), Defaulted = Completed
                        (teal). TR 2848 column: removed the visible checkbox - on-file shows a
                        green check-bold.svg, off shows nothing, still click-to-toggle (faint box
                        on hover for discoverability).
   BUILD: 2026-06-29v - IA tab: Lien? + Potential default consolidated into one "Flags" column
                        (red Lien / Defaulted pills; default suppressed once an IA is re-entered).
                        Last paid -> Last payment. .trx-flag pills resized to match TR
                        (.contact-stage-pill: 22px, --fs-sm, bordered) - affects the liability
                        Status column too. TR page: POA column -> "2848" with a green check-bold.svg.
   BUILD: 2026-06-29u - Installment Agreement dashboard tab built (mirrors PitBull): one row
                        per client with IA activity - IA status, Total liability, Lien?, Monthly
                        payment (modal TC 670), Last paid, Potential default. Reuses the shared
                        table shell + canonical money/date widths. IA status/payments derived in
                        api/_lib/transcript/extract.js (reusing the CSED engine's IA regexes).
   BUILD: 2026-06-29t - Dashboard CSED tab dropped (CSED lives on the per-client hub only).
                        Canonical reusable column widths: --trx-w-money 130px (currency),
                        --trx-w-date 120px (every MM/DD/YYYY date col) - Earliest CSED + Last
                        updated now share one width; IA/OIC/FTA/Kwong date+money cols reuse them.
   BUILD: 2026-06-29s - Sitewide sentence-case sweep of UI chrome (nav, home dashboard KPIs +
                        card headers, page titles, buttons, modal titles, settings links, form
                        section titles, collaborator columns, TR section tabs). IRS terms, form
                        names, acronyms (CSED/OIC/IRS/PoA/API/OAuth) + product names kept capitalized.
   BUILD: 2026-06-29r - Tabs renamed to the sentence-case + official-IRS-term convention. Dash:
                        Tax liability | CSED | Installment Agreement | Offer in Compromise |
                        First Time Abate | Kwong | Files. Hub: Tax liability + Bankruptcy
                        dischargeability lowercased. Rest of Transcripts chrome already sentence.
   BUILD: 2026-06-29q - Dropped red from the Total liability column (plain text now). CSED amber
                        darkened #FFBF00 -> #E0A800 for readability.
   BUILD: 2026-06-29p - Dashboard KPI cards now reflect the active Type/search filter (computed
                        from the filtered client set, re-rendered on every filter change).
   BUILD: 2026-06-29o - CSED amber recolored #b54708 -> #FFBF00 (the old amber read too red).
   BUILD: 2026-06-29n - CSED date urgency colors: red within 6 months, amber within 12 months
                        (dashboard Earliest CSED column + the hub card's Earliest/Latest CSED).
   BUILD: 2026-06-29m - CSED: a $0 original return (TC 150) no longer anchors a clock - when the
                        liability was created by a later additional assessment the CSED runs from
                        that (a refund return audited years later). Type filter label removed.
   BUILD: 2026-06-29l - Transcripts dashboard: re-added the entity Type filter (All | Business
                        | Individual) to the Tax-liability toolbar.
   BUILD: 2026-06-29k - KPIs consolidated: the dashboard's 3 cards and the hub's 5 cards are
                        now ONE .trx-kpi-card stat strip (value + label columns, like the
                        Files header) instead of separate icon cards. Dropped the per-card
                        icons/subs + their now-dead CSS.
   BUILD: 2026-06-29j - Unfiled: KPI card "Unfiled periods" -> "Unfiled tax returns" (hub +
                        dashboard); dashboard column "Filing compliance" -> "Unfiled tax
                        returns" - dropped the green check, shows a hyphen when none unfiled.
   BUILD: 2026-06-29i - Transcripts page: section tabs merged UP into the subheader viewbar
                        (Tax Liability | CSED | Installment Agreements | Offer in Compromise
                        filings | First Time Abate | Kwong opportunities | Files). "Dashboard"
                        renamed Tax Liability; Files moved last; separate section bar removed.
   BUILD: 2026-06-29h - Transcripts dashboard: removed Type/CSED filters, added a section tab
                        row (Tax liability | CSED | Installment Agreements | Offer in Compromise
                        filings | First Time Abate | Kwong opportunities). Tax liability = the
                        client table; the rest are placeholders. Search/Columns kept.
   BUILD: 2026-06-29g - Per-client hub retabbed: Tax Liability | Compliance | CSED | First
                        Time Abate | Bankruptcy Dischargeability | Wage and income | Files.
                        Compliance is a real filing-compliance table; FTA + Bankruptcy are
                        placeholders. (Dashboard tab row pending Files/Kwong answers.)
   BUILD: 2026-06-29f - Transcript KPI cards (dashboard + hub) slimmed: 12px inner padding =
                        12px inter-card gap, tighter label/value/sub rows, 20px headline
                        number, hub card width 190->158px.
   BUILD: 2026-06-29e - Per-client hub Overview rebuilt as "Account status by year": one row
                        per tax year (Return filed / Status / Flags / Assessed / Accrued P&I /
                        Current due / As-of), click a year to expand its transaction codes.
                        Hub KPI cards: fixed 190px even width (long CSED subs wrap, no stretch);
                        "Unfiled years" -> "Unfiled periods".
   BUILD: 2026-06-29d - Transcripts KPI cards: actually stop stretching - the parent is a
                        flex column (blockifies inline-grid), so use display:grid +
                        width:fit-content. Tightened the dashboard KPI-to-card gap 22->12px.
   BUILD: 2026-06-29c - Transcripts KPI cards (dashboard + hub): even-width compact cards
                        (not page-stretched), sentence-case labels, fill icons (money-fill,
                        clock-countdown-fill, file-text-fill, clock-fill, files-fill) in
                        semantic colors, vertically centered. Dashboard: "Unfiled periods" =
                        period count across N Clients (dropped "year with balance, no return").
   BUILD: 2026-06-29b - Transcripts dashboard: restored the three summary KPI cards (Total
                        IRS liability / Imminent CSED / Unfiled-gaps) above the table, with
                        Phosphor icons money + clock-countdown + file-text-fill (TR icon).
   BUILD: 2026-06-29a - Transcripts dashboard: Filing-compliance check left-aligned + indented
                        to sit over the "1040" of non-compliant rows. CSED filter is now
                        All / Imminent (Imminent = future CSED within 12 months); imminent
                        CSEDs render red in the table.
   BUILD: 2026-06-28s - CSED: a pending IA filed + removed the SAME day (971 Pending + 972
                        Removed on one date) is a reversal and now tolls nothing (was adding
                        a spurious 30-day appeal window). Live calc - no re-parse needed.
   BUILD: 2026-06-28r - Transcript dashboard Total liability: when an Account Transcript and
                        a Record of Account collide on one tax module, keep the LATEST pull's
                        balance (was: last-processed), so a stale older pull with lower
                        accruals no longer understates the liability.
   BUILD: 2026-06-28q - CSED IA tolling: a pending IA ended by TC 972 "Removed installment
                        agreement" now pairs there (+30) instead of running to the next IA's
                        establishment; an IA termination followed by a levy (collection
                        resumed) no longer adds its +30. Live calc - no re-parse needed.
   BUILD: 2026-06-28p - Dashboard rows no longer whole-row links (only the client name
                        navigates). CSED: TC 550 "collect until" date was being stripped
                        from the explanation and lost (fell back to the posting date);
                        the parser now keeps it as expl_date. Needs a re-parse to take hold.
   BUILD: 2026-06-28o - Filing-compliance checkmark centered. Per-client hub collapses to
                        one row per (form, period) = the LATEST pull, so a stale older pull
                        no longer duplicates a period or wins "earliest CSED".
   BUILD: 2026-06-28n - Per-client hub: recompute CSED LIVE from stored transactions
                        (transcript_detail), so the hub never shows a CSED from an older
                        parse - matches the dashboard's live calc without a re-parse.
   BUILD: 2026-06-28m - "Unfiled returns" column renamed "Filing compliance": compliant
                        clients show a green check-bold (no box); non-compliant show just
                        the missing form + period(s).
   BUILD: 2026-06-28l - Status pills: a status counts only while OPEN - exam 420/424 closed
                        by 421, lien 582 released by 583, OIC 480 closed by 481/482/483 no
                        longer show as active (closed exams were appearing as "under exam").
   BUILD: 2026-06-28k - CSED: project ACTIVE holds to the transcript date. An unresolved
                        520/CDP/pending-IA (no closing transaction) tolls from its start to
                        the transcript's response/as-of date, matching how PitBull projects
                        an open suspension. IA termination is superseded by a later hold.
   BUILD: 2026-06-28j - CSED: ignore $0.00 additional assessments (TC 290/300 with no tax
                        are administrative and start no collection clock) - they were
                        putting the CSED before the real TC 150 base. TC 150 still anchors.
   BUILD: 2026-06-28i - CSED per-assessment: a tax year with multiple assessments (TC 150
                        + TC 290/300...) now yields an EARLIEST and a LATEST CSED (each
                        assessment + 10y + tolling); hub shows both, scoped to liability.
                        IA tolling reworked: pending-IA period tolls (+30 only if rejected);
                        a termination tolls only if it's the final IA state. Matches the
                        real 1040-2017 example (PitBull 11/09/2028 + 11/20/2030).
   BUILD: 2026-06-28h - CSED: honor TC 550 "IRS can collect until <date>" (read from the
                        explanation, not the posting date) as the IRS's authoritative
                        recompute, plus tolling that posts AFTER it; detect IA-termination
                        tolling (+30d). Fixes clients where a 550 reset the statute.
   BUILD: 2026-06-28g - Unfiled returns: now detected by NO TC 150 on a return-form
                        account/RoA transcript (= return never filed), not just the
                        "no record" wording - catches "RETURN NOT PRESENT" / TC 474/590
                        / $0-balance non-filers the old rule missed. Hub unfiled count
                        no longer requires a balance. Pure tested helper isUnfiledReturn.
   BUILD: 2026-06-28f - Per-client hub: Earliest/Latest CSED cards now show the balance
                        tied to that CSED module (current balance; interest-projected
                        amount to follow).
   BUILD: 2026-06-28e - CSED tolling engine: shown CSED now accounts for suspension
                        events (bankruptcy TC520->521 +6mo, OIC TC480->481/482 +30d,
                        TC550 override) via a pure tested module (api/_lib/transcript/
                        csed.js), reproducing PitBull to the day on real transcripts.
                        Dashboard computes it fresh (immediate); hub uses it after parse.
   BUILD: 2026-06-28d - CSED +1-day convention: shown CSED = assessment + 10y + 1 day
                        (first day collection is barred), matching PitBull. Applied at
                        display on the dashboard + the per-client hub (KPI + CSED tab),
                        which was also aligned to liability-only / future-only.
   BUILD: 2026-06-28c - Transcripts Dashboard: Earliest CSED now scoped to liability-
                        bearing modules only (balance > 0) - a zero/credit year's CSED
                        (e.g. an old paid-off return) no longer drags the date down.
   BUILD: 2026-06-28b - Transcripts Dashboard pass 3: Status hides "Current" when there
                        are unfiled returns; Unfiled limited to the IRS 6-year compliance
                        window [curY-6, curY-1]; Status flags suppressed for periods whose
                        CSED already expired. (CSED tolling calc: research in progress.)
   BUILD: 2026-06-28a - Transcripts Dashboard pass 2: collapse MFJ spouses into one
                        household row (keep separate only for MFS); Earliest CSED shows
                        only FUTURE statute dates ("Expired" filter removed); Unfiled
                        returns lists only actual return forms ("1040 Series" -> "1040";
                        Civil Penalty / Separate Assessment excluded); Status pills get a
                        detailed hover card (meaning + years/dates).
   BUILD: 2026-06-27i - Transcripts Dashboard rework: Total liability moved before
                        Forms & periods; Forms & periods now lists the form:year
                        ranges that CARRY the liability (was a flat type list);
                        Earliest CSED is date-only (no colored pills); Compliance
                        renamed Status (Unfiled split out); new Unfiled returns
                        column; client names split into per-contact links (TR-style)
                        routing to the per-client Transcripts hub.
   BUILD: 2026-06-27h - Tabular figures site-wide (font-variant-numeric:tabular-nums
                        on body + form controls) so dates / IDs / amounts align
                        column-to-column (Inter's proportional "1" was misaligning
                        date stacks).
   BUILD: 2026-06-27g - Transcripts: header bottom border = real 1px (no box-shadow,
                        matches top); per-client KPI/tab gap tightened; Files "Show
                        all" stacked directly above Columns; column resizers on both
                        Dashboard + Files tables. (sync) content-parse FIX: bundle
                        pdfjs's worker via vercel.json includeFiles (was "Cannot find
                        pdf.worker.mjs" -> 0 parsed); only parse latest pull per
                        (client,type,period); parallel + skip W&I detail.
   BUILD: 2026-06-27f - Transcripts fixes: #trx-roll-card height-locked (Dashboard
                        internal scroll + footer always visible); thead bottom
                        hairline de-doubled (box-shadow only, border-bottom:0 in
                        scroll) so it's a clean 1px matching the top; Files +
                        Dashboard control rows bottom-aligned (--status), Files
                        "Show all" moved above Columns; Files paginated + Contacts-
                        style footer + Export; Dashboard "Last pull" -> "Last
                        updated" (MM/DD/YYYY).
   BUILD: 2026-06-27e - Contact tab "IRS Account" renamed back to "Transcripts";
                        per-client hub now uses KPI cards for the summary; header
                        border fix corrected (thead bottom -> --border to match the
                        card/toolbar top line, not the other way).
   BUILD: 2026-06-27d - Transcripts polish: .trx-tabbar margin reset (fix left gap +
                        big under-tab gap); Dashboard internal scroll + Contacts-style
                        paginated footer; card-toolbar bottom border -> --grey-250 so it
                        matches the thead's bottom hairline on every table; Files tab
                        search moved right + renamed + Columns button; Filing activity
                        only on Files; Pulled->Last updated, Pulls->Updates.
   BUILD: 2026-06-27c - Transcripts page: Dashboard|Files as .contact-tabbar tabs
                        (dropped the segmented toggle + KPI cards); Entity filter
                        renamed Type (plain text); added Columns picker; search left
                        of Columns; standard scroll/footer; no entity pills/icons.
   BUILD: 2026-06-27b - Contact "IRS Account" tab: per-client transcript hub
                        (Overview / CSED & tolling / Transactions / Penalties /
                        Wage & income / Return summary / Files sub-tabs). The old
                        Transcripts tab is now the "Files" sub-tab.
   BUILD: 2026-06-27 - Transcripts: Dashboard roll-up landing (per-client
                        liability / earliest-CSED / compliance flags + KPIs),
                        Dashboard|All-pulls view toggle. (Per-client hub next.)
   BUILD: 2026-06-26 - Contact card: Street/ZIP copy icons moved inline + RIGHT-
                        aligned (.addr-line flex, margin-left:auto), matching the
                        EIN/phone copy buttons. Decedent custom field -> contact link.
   BUILD: 2026-06-25 - Contact Transcripts tab: type-cards in one no-wrap row
                        (shrink to fit, title ellipsis+tooltip); when the contact
                        has a spouse, stack contact + spouse transcripts in equal
                        viewport halves (fitTranscriptCards splits the cap).
   BUILD: 2026-06-23 - Contact card: per-phone copy buttons + Street/ZIP address
                        chips. Tax Returns: the Type / EF-ack-date values are
                        links to the client copy / e-file ack; single click opens,
                        double click edits (.tr-editlink, no glyph). Shared by the
                        main + embedded (contact-tab) TR table.
   BUILD: 2026-06-20e - PDF annotations: add Draw (freehand ink) + Highlight
                        (translucent multiply marker). Freehand capture in the preview
                        with a live polyline; baked as real /Ink annotations (InkList +
                        appearance stream; highlight uses an ExtGState Multiply blend).
                        Geometric, so rotation-correct on any page. Strokes select /
                        move / recolor / delete. New Phosphor icons: pen, highlighter.
                        Prior:
   BUILD: 2026-06-20d - PDF tool: annotations (Note / File / Text box / Callout).
                        Placed + edited in the large preview (toolbar, click to drop,
                        drag/resize/recolor, inline text, callout target handle),
                        baked as REAL PDF annotations: Note=/Text, File=/FileAttachment
                        (embedded), Text box + Callout=/FreeText with appearance streams.
                        Thumbnail badge shows the per-page count. New Phosphor icons:
                        note, paperclip, textbox, chat-centered-text. Prior:
   BUILD: 2026-06-20c - Fix: .pdf-field[hidden] now hides. .pdf-field's display:flex
                        was overriding the UA [hidden] rule, so the watermark image
                        block stayed visible under Text (and the position block under
                        Tile). Prior:
   BUILD: 2026-06-20b - PDF tool: Save / Save as / Print. Save + Save as use the
                        File System Access API (Edge/Chrome) - Save writes back to
                        the chosen file, Save as always prompts; download fallback
                        otherwise. Print drives the browser PDF viewer via a hidden
                        blob iframe. New Phosphor icons: printer-fill, floppy-disk-fill.
                        Prior:
   BUILD: 2026-06-20a - PDF tool: Fill form fields (AcroForm). Detect fillable
                        fields in the loaded PDF(s), edit them in a modal (text /
                        checkbox / radio / dropdown / list, friendly TU labels +
                        filter), then write values back and flatten so the filled
                        form previews + exports identically. Prior:
   BUILD: 2026-06-19c - PDF Watermark: add Image/logo watermarks (PNG/JPG upload,
                        embedPng/embedJpg), multi-line text (textarea), Tile-across-
                        page (repeat with adjustable spacing), and a Font picker
                        (Helvetica / Times / Courier, regular + bold). The live
                        preview is now an inline SVG (viewBox = page points) that
                        mirrors the bake for text/image, single or tiled. Prior:
   BUILD: 2026-06-19b - PDF Watermark Position: replace the 3x3 anchor grid with
                        Acrobat-style numeric controls - Vertical / Horizontal
                        distance + unit (in/cm/pt) "from" a reference (Center / Top /
                        Bottom, Center / Left / Right). The bake maps display-space
                        distance into unrotated draw coords (handles page /Rotate)
                        and the preview positions the overlay on the letterboxed page
                        box. Keeps Rotation + Apply-to (All / Selected / Range). Prior:
   BUILD: 2026-06-19a - PDF Watermark tool: add Rotation (diagonal / reverse /
                        horizontal), a 3x3 Position anchor grid, and an Apply-to
                        selector (All / Selected / Page range "1-3, 5, 8-"). The
                        live thumbnail preview honours position + rotation and only
                        overlays in-scope pages; the bake (stampWatermarkOnDoc)
                        anchors with an edge margin + bounding-box math. Prior:
   BUILD: 2026-06-18b - Data tables (TR / Matters / Contacts / Transcripts /
                        Documents + embedded): collapse the two-table (thead/tbody)
                        split into ONE table per .tr-scroll with a sticky header.
                        Header/body now share one column-width computation (can't
                        desync on column toggle) and the single vertical scrollbar
                        hugs the table's right edge. Replaces the .tr-h-scroll /
                        .tr-v-scroll pair + the per-card scrollbar-zero patch. Prior:
   BUILD: 2026-06-18a - Two-table pages (TR / Matters / Contacts): zero the tbody
                        .tr-v-scroll scrollbar on all three cards so toggling
                        column visibility no longer misaligns the header. Prior:
   BUILD: 2026-06-17e - PDF tools: page-thumbnail zoom - Ctrl/Cmd+scroll over the
                        grid (or toolbar −/+) scales every card via --pdf-w
                        (grid min-width + thumb height). Prior:
   BUILD: 2026-06-17d - PDF tools: large single-page zoom preview overlay
                        (.pdf-preview-*) - click a page's magnifier (or double-click
                        the card) to open it crisp with zoom in/out, fit, and
                        prev/next. Prior:
   BUILD: 2026-06-17c - Embedded TR Section filter: widen .tr-filter-label to 70px
                        in the embed so the "SECTION" row lines up with Year/Type/
                        Status (the standalone page keeps 56px). Prior:
   BUILD: 2026-06-17b - Transcripts page: Client filter is now a plain live-filter
                        search box (removed the typeahead results dropdown); the
                        single-client coverage matrix now fills the card height via
                        fitTranscriptCards (matches the embedded contact tab) instead
                        of the short 19rem cap. Prior:
   BUILD: 2026-06-17a - Contact detail: new "Tax Returns" tab (before Transcripts)
                        embeds the full TR dashboard scoped to that contact's
                        returns + related-entity returns. Standalone viewport-lock
                        scoped to :not(.is-embedded); the embed flows at natural
                        height and the contact page scrolls (like the Forms tab).
                        Prior:
   BUILD: 2026-06-16o - Matters page: added a Matter description column (after
                        Matter), client names are links with a 358x218 hover card
                        (phone/email/address + copy), and TR-style filter strips for
                        Status / Practice area / Responsible attorney / Responsible
                        staff. Prior:
   BUILD: 2026-06-16n - 2848 page: matter/date now truly top-align (the matter
                        group's top margin moved to the row, so it no longer offsets
                        from the date column); form-tile + Prepare-POA buttons are
                        38px (Add-a-matter height); address textarea has a working
                        corner resize; POA checkbox sits inline after the shrunk
                        Daytime-telephone field. Prior:
   BUILD: 2026-06-16m - Date picker fixes: nav/header buttons work (stop-prop on the
                        popup), opens on field click, keeps focus ring while open,
                        popup hugs the field; popup +44px wider, day rows shorter,
                        day selection = blue outline only, larger bold DOW band +
                        unbold Today, hairlines on both bands. Forms (2848) fields
                        7px taller; date-picker popup buttons match the Add-a-matter
                        height; Edit/Delete icons use the calendar-icon tone. Prior:
   BUILD: 2026-06-16l - Date picker: matter+date now 50/50 side-by-side; calendar
                        gained Clio's drill-up (day→month→year→decade via the header
                        button), caret-fill arrows, bold grey day-header band + grey
                        Today footer, grey (not recolour) icon hover. Prior:
   BUILD: 2026-06-16k - Date picker polish: calendar floats fixed (no card clip),
                        bold calendar icon + separator line, hint text removed.
                        Documents Columns→New gap matches search→Columns. Addresses
                        render as Clio's 3 lines everywhere (Street / City, ST␣␣ZIP+4
                        / Country (Name)) via shared formatAddressBlock. Prior:
   BUILD: 2026-06-16j - Forms 2848 signature-date picker: side-by-side with the
                        matter picker + a custom Clio-style calendar (MM/DD/YYYY
                        input, Sun-first grid, boxed ‹ Month YYYY › nav, today
                        outlined, selected filled, Today link). Matter stage
                        dropdown sizes to its longest option + label centred. Prior:
   BUILD: 2026-06-16i - Matter Contacts card: related contacts now render exactly
                        like the company Employees card (shared renderContactDetailBlock
                        - name link + role + Phone/Email/Address + separators),
                        hydrated mirror-first. Prior:
   BUILD: 2026-06-16h - Matter detail: Documents tab now reuses the EXACT contact
                        Documents card (toolbar, breadcrumb, search, Columns, New,
                        footer pager, drill-in, sort), scoped to the one matter.
                        DocumentsTabState gained rootKind ('contact' | 'matter');
                        the bespoke .matter-docs-* browser was removed. Prior:
   BUILD: 2026-06-16g - Matter detail: Matter stage is now an editable dropdown in
                        the Details card (writes to Clio via the TR PATCH + bumps
                        the matters/tax_returns mirrors). Stages fetched per the
                        matter's practice area (?matter_stages=<paId> +
                        listMatterStagesForPracticeArea) - all practice areas now
                        supported, not just Tax Preparation. Prior:
   BUILD: 2026-06-16f - Matter-detail polish: Court case card only renders on
                        court-case matters (description ~ "<County> County Court
                        Case ..."); title uses display_number as-is (no duplicate
                        client); Contacts card reworked to Clio look (avatar +
                        Bill Recipient badge + typed phone/email/address rows);
                        court-fetch now accepts MULTIPLE screenshots (add/paste/
                        drop a batch, thumbnails, then Read N images - merged into
                        one case); tightened dashboard grid gap. Prior:
   BUILD: 2026-06-16e - Matters page polish: viewport-lock added to #matters-card /
                        #matters-table-container (footer pins, table scrolls
                        internally with the shared h/v scrollers); Columns popup
                        anchored under its button (position:true); 3-way sort on
                        every header (asc/desc/unsorted); +Responsible attorney,
                        Responsible staff, Open date columns (migration 041 mirror
                        cols + sync fields; responsible_staff requested with a
                        drop-on-400 fallback so it can't break the sync). Prior:
   BUILD: 2026-06-16d - Matters list page rebuilt as a faithful clone of the
                        Contacts page chrome (tab-strip status filter, Search
                        matters, Columns menu, two-table layout with pinned
                        .matters-col-* widths, paginated footer, Export), reading
                        real matters from the mirror (?matters_list=1). Prior:
   BUILD: 2026-06-16c - Court-fetch: "Court case" card on the matter Dashboard
                        tab. Paste/drop a county Case Detail screenshot ->
                        Claude-vision (haiku-4-5) extracts -> editable review
                        form (mandatory "which side is our client" pick, low-
                        confidence flags) -> saves to matter_court_cases
                        (migration 040). Document-template court placeholders
                        repointed to the matter's case (matter-aware prefill).
                        Needs ANTHROPIC_API_KEY + migration 040. Prior:
   BUILD: 2026-06-16b - Matter-detail page rebuilt (mirror/Clio-backed, replaces
                        the demo scaffold): header (display number + client) +
                        Dashboard / Documents tabs. Dashboard = Details + Custom
                        fields + Contacts cards (reuses .contact-card chrome);
                        Documents = matter-rooted folder browser. Backed by the
                        new ?matter_id= fetch branch in stages.js. Accounting
                        page made admin-only. Prior:
   BUILD: 2026-06-16a - New Accounting page (sidebar, staff-only): auto-surfaces
                        candidate accounting clients from the matters mirror
                        (practice area OR description keyword), accountant
                        confirms/dismisses, monthly status matrix per year
                        (migration 039 + stages.js accounting_* branches);
                        prior: Sales and Use Tax tab polish: Due date column moved to
                        after Status; Type filter lists form codes (DR-15) not
                        the bucket; Type column narrowed to fit DR-15 on that
                        tab only; stale year/type filter values reset to All per
                        tab; prior: Tax Returns split into Income Tax / Sales and Use Tax
                        tabs (.contact-tabbar above the card); DR-15 expands to
                        monthly returns (Period column shows YYYY-MM); Sales &
                        Use tab adds a Due date column (late-after 20th) and
                        hides the Drake EF-ack columns; prior: Sync hover +
                        click windows consolidated into ONE card:
                        hover opens it (live progress while syncing, else the
                        last auto-sync status per phase), click pins it open;
                        retired the separate read-only hovercard; prior: Sync
                        hovercard actionable (per-phase refresh + Sync all now /
                        Full sync); prior: Image to PDF auto edge-detection (corners
                        on import + Auto-detect button), live warped Result
                        preview, adaptive (local-mean) B&W, Sharpen slider
                        (auto-sharpen 35%); prior: new admin-only
                        "Image to PDF" scan tab (import
                        photos, manual 4-corner dewarp via JS homography, rotate,
                        scan-level filters Color/Gray/Document/B&W + brightness/
                        contrast, assemble PDF, save / file to matter); shared
                        matter-filer; PDF drop-zone adds "watermark"; prior:
                        drop-zone title + sub text bumped to base font size;
                        prior: toolbar even 12px padding all around (bleeds
                        to the content edges; buttons align with the grid);
                        prior: toolbar sticky (pinned below the topbar while
                        the page grid scrolls); Transcripts page guard relaxed
                        to staff (was admin-only); prior: Watermark scale fixed
                        (diagonal fits the shorter side,
                        no overflow) + defaults 30% opacity / 90% scale;
                        Transcripts page now visible to staff; prior:
                        Watermark is now non-destructive: a live spec previewed
                        on the thumbnails, baked only at export, editable +
                        removable on re-open (no compounding); Opacity + Scale
                        % sliders (1-100, Scale relative to the page); Watermark
                        button is icon-only before Delete; prior: Extract
                        is an icon button (arrow-square-out-fill) between
                        Rotate right and Delete; prior: Tools page full-width
                        (#main width-pinned like the
                        contact page); Extract selected -> Extract; Save PDF
                        gets the PDF icon; prior: PDF tools rotate/delete
                        icon-only (one-row
                        toolbar), Download PDF -> Save PDF; all .btn icons
                        18px; Templates page back to admin-only; Form 2848
                        decedent flow now automatic (Clio Title=Decedent +
                        PR/Executor contact custom field, no checkbox);
                        prior: Admin "view as staff" toggle (account menu + topbar
                        pill, reload-applied, also hides admin-only Forms);
                        Tools sidebar icon -> wrench-fill; PDF tools:
                        rotate-left, Extract selected (replaced Split), File
                        to matter (Blob -> Clio, matter search, gray footer);
                        prior: Tools tabbed page +
                        drop zone, drag-drop import, hover page actions,
                        click-to-select, crisper hi-DPI thumbnails, icon
                        toolbar; renamed sidebar PDF tools -> Tools, added
                        /tools + /templates SPA rewrites;
                        prior: Sync button hover hovercard;
                        prior: Transcripts client search typeahead;
                        filing-activity log + failures; cron walk-first;
                       prior: Forms page (PDF generator) styles; sidebar
                       reorder (Contacts ▸ Tax Returns ▸ Forms);
                       Documents tab slate-grey palette (#516378)
   ============================================================ */

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

:root {
  /* ── Core Blue ── */
  --blue:         #0067D1;       /* primary brand blue (top bar, primary buttons) */
  --blue-hover:   #003c93;       /* deeper hover for primary actions */
  --blue-darker:  #002968;
  --blue-light:   #E5F0FA;
  --blue-search:  #0052b7;       /* topbar search input bg */
  --blue-bright:  #73c3ff;       /* active nav icon color */

  /* ── Sidebar ── */
  --sidebar-bg:        #001538;
  --sidebar-logo-bg:   #001538;
  --sidebar-active-bg: #003992;
  --sidebar-hover-bg:  #002968;
  --sidebar-border:    rgba(255,255,255,0.08);
  --sidebar-text:      #fffcff;
  --sidebar-text-dim:  rgba(255,252,255,0.55);
  --sidebar-width:     224px;
  --sidebar-width-collapsed: 56px;

  /* ── Topbar ── */
  --topbar-bg:           var(--blue);
  --topbar-h:            56px;
  --topbar-search-bg:    #0052b7;
  --topbar-text:         #fffcff;
  --topbar-search-text:  #cbe7ff;

  /* ── Page / Surface ── */
  --page-bg:       #f5f3f7;
  --surface:       #FFFFFF;
  --surface-hover: #F5F8FF;
  --border:        #E1E4E8;
  --border-strong: #a7b3c2;      /* form-field resting border (matches Clio) */
  --divider:       #EAECEF;

  /* ── Typography ── */
  --text-primary:   #1e2535;
  --text-secondary: #5A6B7B;
  --text-tertiary:  #8C9BAA;
  --text-link:      #0055b7;
  --text-link-hover:#003c93;

  /* ── Status Colors ── */
  --status-pending-bg:    #FFF3CD;
  --status-pending-text:  #856404;
  --status-pending-border:#FFE08A;
  --status-progress-bg:   #CCE5FF;
  --status-progress-text: #004085;
  --status-progress-border:#99CAFF;
  --status-done-bg:       #D4EDDA;
  --status-done-text:     #155724;
  --status-done-border:   #A3D9B1;
  --status-overdue-bg:    #F8D7DA;
  --status-overdue-text:  #721C24;
  --status-overdue-border:#F5C6CB;
  --status-high-bg:       #F8D7DA;
  --status-high-text:     #721C24;

  /* ── Semantic ── */
  --success:      #28A745;
  --success-bg:   #D4EDDA;
  --danger:       #DC3545;
  --danger-bg:    #F8D7DA;
  --warning:      #FFC107;
  --warning-bg:   #FFF3CD;
  --warning-fg:   #c24e00;   /* readable warning text on light bg (amber --warning fails contrast as text) */
  --info:         #17A2B8;
  --info-bg:      #D1ECF1;

  /* ── Shadows ── */
  --shadow-sm:  0 1px 3px rgba(0,0,0,0.08);
  --shadow:     0 2px 8px rgba(0,0,0,0.10);
  --shadow-lg:  0 8px 24px rgba(0,0,0,0.14);
  --shadow-popup: 0 4px 16px rgba(0,0,0,0.08);  /* dropdown menus / popovers */

  /* ── Radius ──
   * RULE: every interactive control - fields (.input/.select-input/
   * .textarea-input), buttons (.btn and the icon/segmented/split button
   * families), and their attached parts (datepicker/caret sections) -
   * uses --r, so fields and buttons always match. --r-sm is for small
   * NON-interactive chrome only: chips, callouts, panels, thumbnails. */
  --r-sm:  4px;
  --r:     6px;
  --r-lg:  8px;

  /* ── Transitions ── */
  --t: 0.15s ease;

  /* ── Typography scale ──
     Font sizes + weights. Existing literals get swept onto these
     incrementally; new code should use the tokens from the start. */
  --fs-2xs:  10px;
  --fs-xs:   11px;
  --fs-sm:   12px;
  --fs-smd:  12.5px;
  --fs-md:   13px;
  --fs-base: 14px;
  --fs-lg:   16px;   /* small headings / card titles */
  --fs-xl:   18px;
  --fs-2xl:  20px;
  --fs-3xl:  24px;   /* page titles */
  --fs-4xl:  32px;   /* hero / splash */
  --fw-regular:  400;
  --fw-medium:   500;
  --fw-semibold: 600;
  --fw-bold:     700;

  /* ── Spacing scale (4px grid) ── */
  --space-1:  4px;
  --space-2:  8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;

  /* ── Control heights ── */
  --control-h-sm: 28px;   /* small buttons (.btn-sm) */
  --control-h:    38px;   /* all standard buttons (toolbar, footer, base) */

  /* ── Z-index layers (match the live stacking) ──
   *   menus 60 < backdrop 89 < sidebar/panel 90 < topbar 100 <
   *   search-dropdown 101 < modal 200 < toast 999 < profile popover 1000.
   *   (backdrop / search-dropdown / profile-popover stay literal — single-use edge layers.) */
  --z-popup:  95;     /* card dropdown menus (Columns / New / Actions / per-page) — ABOVE sticky table headers/chrome (90), below topbar */
  --z-sticky: 90;     /* sidebar + slide-in detail panel */
  --z-topbar: 100;    /* fixed topbar */
  --z-modal:  200;    /* modal overlay */
  --z-toast:  999;    /* toast notifications */

  /* ── Neutral / chrome colors (the most-repeated raw hexes) ── */
  --text-slate:            #516378;  /* Documents slate text + folder icons */
  --border-control:        #a7b3c2;  /* input / secondary-button borders */
  --surface-titlebar:      #fffcff;  /* page-header + tab-strip surface */
  --grey-150:  #e9e9f0;  /* light grey primitive — table-header wash, light text on dark, menu active item */
  --grey-250:  #d1d6e0;  /* hairline grey primitive — cell borders, page-header rule, darker hover wash */
  --row-selected:          #ebf7ff;  /* selected table row */
  --row-selected-hover:    #cceaff;
  --pill-radius:           999px;

  /* ── Status-pill palette ──
     One source of truth for the stage chips shared by the Tax Returns
     table (.stage-select), the contact-detail matters card
     (.contact-stage-pill), the Documents "Shared" pill, and the
     contact "client" badge. (.status-pill uses a near-duplicate but
     DIFFERENT palette — intentionally left separate for now.) */
  --pill-active-bg:     #d5f4ff;  --pill-active-border:    #b7efff;  --pill-active-fg:    #4b6178;
  --pill-onhold-bg:     #fbf0c8;  --pill-onhold-border:    #f4e39d;  --pill-onhold-fg:    #814e01;
  --pill-completed-bg:  #b5ede3;  --pill-completed-border: #7ed6cb;  --pill-completed-fg: #006868;
  --pill-closed-bg:     #e9e9f0;  --pill-closed-border:    #d1d6e0;  --pill-closed-fg:    #6a7280;

  font-family: Inter, "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: var(--fs-base);
  font-weight: var(--fw-regular);
  line-height: 21px;
  color: var(--text-primary);
}

/* ── Reset ── */
a { color: var(--text-link); text-decoration: none; }
a:hover { color: var(--text-link-hover); text-decoration: underline; }
button { cursor: pointer; font-family: inherit; }
input, select, textarea { font-family: inherit; }
ul { list-style: none; }

/* ============================================================
   LAYOUT
   The page is a top-down stack: full-width topbar on top, then a row
   that contains the sidebar (left) and main content (right).
   ============================================================ */
body { background: var(--page-bg); min-height: 100vh; display: block; }

/* Tabular figures site-wide: every digit occupies the same advance width, so
   dates / IDs / amounts line up column-to-column and row-to-row (the Inter
   default is proportional, which makes "1" narrower and misaligns date stacks).
   Form controls don't inherit font-variant-numeric, so name them explicitly. */
body,
input, select, textarea, button { font-variant-numeric: tabular-nums; }

#app-shell {
  display: block;
  width: 100%;
  min-height: 100vh;
}

/* ── Sidebar ──
 * Sits below the full-width topbar, fixed-position so content can scroll
 * under it. Top offset matches the topbar height. */
#sidebar {
  width: var(--sidebar-width);
  background: var(--sidebar-bg);
  display: flex;
  flex-direction: column;
  position: fixed;
  top: var(--topbar-h);
  left: 0;
  bottom: 0;
  z-index: var(--z-sticky);
  transition: width var(--t), transform var(--t);
  overflow: hidden;
}
#sidebar.collapsed { width: 56px !important; }

.sidebar-nav { flex: 1; padding: 12px 0; overflow-y: auto; overflow-x: hidden; }
.sidebar-section-label {
  padding: 16px 16px 4px;
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold);
  color: var(--sidebar-text-dim);
  text-transform: uppercase; letter-spacing: 0.08em;
  white-space: nowrap;
}

/* Nav item — Clio uses a rounded-corner box with margin on both sides,
 * not a full-width pill. */
.sidebar-item {
  display: flex; align-items: center; gap: var(--space-3);
  height: 36px;
  padding: 0 12px 0 11px;        /* margin-left 8 + padding-left 11 = icon left at 19, center at 28 */
  margin: 1px 8px;
  border-radius: var(--r);
  color: var(--grey-150);
  font-size: var(--fs-base); font-weight: var(--fw-regular); line-height: 20px;
  cursor: pointer;
  transition: background var(--t), color var(--t);
  white-space: nowrap;
  text-decoration: none;
}
.sidebar-item:hover {
  background: var(--sidebar-hover-bg);
  color: var(--grey-150);
  text-decoration: none;
}
.sidebar-item.active {
  background: var(--sidebar-active-bg);
  color: var(--grey-150);
  font-weight: var(--fw-medium);
}
.sidebar-item.active:hover {
  background: var(--sidebar-hover-bg);
}
.sidebar-item svg,
.sidebar-item .icon {
  width: 18px; height: 18px; flex-shrink: 0;
  color: var(--grey-250);
  transition: color var(--t);
}
/* Active state intentionally keeps the bright-blue icon for the
 * "you are here" cue per spec. */
.sidebar-item.active svg,
.sidebar-item.active .icon { color: var(--blue-bright); }
.sidebar-item-label { overflow: hidden; text-overflow: ellipsis; }
#sidebar.collapsed .sidebar-item-label { display: none; }
#sidebar.collapsed .sidebar-item { justify-content: center; padding: 0; margin: 1px 4px; }

.sidebar-badge {
  margin-left: auto;
  background: #d30933;                 /* Clio's red count badge */
  color: #fff;
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  padding: 1px 7px; border-radius: 999px;
  min-width: 20px; text-align: center;
}
.sidebar-item.active .sidebar-badge { background: #d30933; }
#sidebar.collapsed .sidebar-badge { display: none; }

/* Sidebar footer: user block (clickable → opens account popover) + Collapse pill
 * Profile avatar and collapse circle are both 36px, both with the SAME
 * horizontal padding so they align vertically. */
.sidebar-footer {
  padding: 8px 12px 11px 12px;
  flex-shrink: 0;
  position: relative;
}
/* Inset divider line: starts 6px in from left, stops 6px before right.
 * Color matches Clio's sidebar divider (#3c4d62 in measured pixels). */
.sidebar-footer::before {
  content: '';
  position: absolute;
  left: 6px;
  right: 6px;
  top: 0;
  height: 0.5px;
  background: #3c4d62;
}
.sidebar-user {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-1) 0;
  border-radius: var(--r);
  cursor: pointer;
  overflow: hidden;
  border: 0;
  background: transparent;
  width: 100%;
  text-align: left;
  color: inherit;
  font: inherit;
}
/* No hover background on the user block — Clio doesn't show one. */
.sidebar-user-avatar {
  width: 32px !important;
  height: 32px !important;
  min-width: 32px;
  min-height: 32px;
  border-radius: 50%;
  background: var(--blue);
  position: relative;
  overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: #fff;
  flex-shrink: 0;
  overflow: hidden;
  box-sizing: border-box;
}
.sidebar-user-avatar img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%; object-fit: cover;
}
/* Same monogram-suppression treatment when a photo is present. */
.sidebar-user-avatar:has(> img) { background: #fff; color: transparent; }
.sidebar-user-text { flex: 1; min-width: 0; }
.sidebar-user-name {
  font-size: var(--fs-base); font-weight: var(--fw-medium);
  color: var(--sidebar-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-user-firm {
  font-size: var(--fs-md);
  color: var(--sidebar-text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  /* -2 px tightens the vertical gap to the user name above (which
   * inherits the body 20 px line-height). */
  margin-top: -2px;
}
#sidebar.collapsed .sidebar-user-text { display: none; }
#sidebar.collapsed .sidebar-user {
  justify-content: center;
  padding: var(--space-1) 0;
  gap: 0;
}
#sidebar.collapsed .sidebar-footer {
  padding: 8px 0 11px;
}

/* Collapse button — chevron sits in a fixed 36px circle (matches profile
 * circle exactly), label beside it in expanded state. */
.sidebar-collapse-btn {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-1) 0;
  color: var(--sidebar-text);
  font-size: var(--fs-base); font-weight: var(--fw-regular); line-height: 21px;
  cursor: pointer;
  margin-top: var(--space-1);          /* + profile pad-bottom 4 + own pad-top 4 = 11px CSS = ~14px measured */
  background: transparent;
  border: 0;
  width: 100%;
  text-align: left;
  font-family: inherit;
}
.sidebar-collapse-btn-circle {
  width: 32px !important;
  height: 32px !important;
  min-width: 32px;
  min-height: 32px;
  border-radius: 50%;
  border: 0;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  background: var(--grey-150);
  transition: background var(--t);
  box-sizing: border-box;
}
.sidebar-collapse-btn:hover .sidebar-collapse-btn-circle {
  background: var(--grey-250);
}
.sidebar-collapse-btn svg,
.sidebar-collapse-btn .icon {
  width: 18px; height: 18px;
  color: var(--sidebar-bg);     /* dark navy chevron, readable on both white and light-blue */
}
.sidebar-collapse-btn-label { overflow: hidden; }
#sidebar.collapsed .sidebar-collapse-btn { justify-content: center; padding: var(--space-1) 0; }
#sidebar.collapsed .sidebar-collapse-btn-label { display: none; }
/* Expanded → caret-left ("collapse"); collapsed → caret-right ("expand"). */
.sidebar-collapse-btn .icon { --icon-url: url('/img/icons/caret-left-bold.svg'); }
#sidebar.collapsed .sidebar-collapse-btn .icon { --icon-url: url('/img/icons/caret-right-bold.svg'); }

/* Account popover (opens upward from the user block — Clio pattern).
 *
 * `position: fixed` so it escapes #sidebar's overflow:hidden clipping —
 * #sidebar has no transform/filter/will-change, so fixed positions
 * resolve against the viewport and the popover floats above the page.
 * Coordinates (left, bottom) are set in JS by toggleAccountPopover so
 * they track the user button's current location even when the sidebar
 * collapses. Width is 272 CSS px (= 352 DT px at the user's 125% scale). */
/* Position is hardcoded in CSS (no JS-anchored math) because the sidebar
 * is itself fixed and the user button doesn't move within it — so a
 * pair of viewport-relative numbers is more predictable than computing
 * coords from getBoundingClientRect every open.
 *
 *   left:   7.72 CSS px ≈ 10 DT px at the user's 125% scale
 *   bottom: 94   CSS px ≈ 122 DT px at the user's 125% scale
 *   width:  274  CSS px ≈ 352 DT px at the user's 125% scale */
.account-popover {
  position: fixed;
  left: 7.72px;
  /* 96 (was 94) lifts the popover 2px off the user avatar/name chip below it. */
  bottom: 96px;
  width: 273px;
  background: var(--surface);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-popup);
  padding: var(--space-2) 0;
  z-index: 1000;
}
/* Bottom padding intentionally tiny so the email sits flush against the
 * View profile link below — user wants no visual gap between them. */
.account-popover-header {
  padding: 14px 18px 2px;
}
.account-popover-name { font-size: var(--fs-xl); font-weight: var(--fw-semibold); color: var(--text-primary); }
.account-popover-email { font-size: var(--fs-base); color: var(--text-secondary); margin-top: var(--space-1); }
.account-popover-item {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 11px 18px;
  font-size: var(--fs-md);
  color: var(--text-primary);
  cursor: pointer;
  background: transparent;
  border: 0;
  width: 100%;
  text-align: left;
  font-family: inherit;
}
/* View profile (the first <button> child of the popover) is styled as a
 * hyperlink — blue text, underlined — and has no hover background.
 * Sign out (the second / post-divider <button>) stays a plain action with
 * a subtle warm-gray hover. :first-of-type picks View profile out
 * cleanly because both items share the <button> tag. */
.account-popover-item:first-of-type {
  color: var(--blue);
  text-decoration: underline;
  padding-top: 2px;   /* close the gap to the email above */
}
/* Plain action items (View as staff, Sign out) get the standard hover; the
 * first item (View profile) is a hyperlink, so it stays hover-less. */
.account-popover-item:not(:first-of-type):hover { background: var(--surface-hover); }
.account-popover-item svg,
.account-popover-item .icon {
  width: 14px; height: 14px;
  color: var(--text-secondary);
  flex-shrink: 0;
}
.account-popover-divider { height: 1px; background: var(--divider); margin: var(--space-1) 0; }

/* ── Icon system ────────────────────────────────────────────────────────────
 * Phosphor SVGs live in /img/icons/*.svg. They render as INLINE <svg> via the
 * ic(name, cls) helper in app.js (an ICON map holds each icon's viewBox + inner
 * markup, fills stripped so shapes inherit `fill: currentColor`). Inline SVG is
 * vector-drawn by the renderer, so it stays crisp at fractional display scaling
 * - the old CSS-mask approach (kept below for any legacy non-svg .icon) blurred
 * because the mask re-rasterized at each element's sub-pixel position.
 *
 * Size + tint come from the surrounding context: width/height default to 1em
 * (specific elements override), tint via `color` -> `fill: currentColor`.
 */
.icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  flex-shrink: 0;
}
/* Inline SVG icons (the ic() helper) - tint via fill:currentColor, mirroring the
 * mask's background-color:currentColor so every icon keeps its existing colour. */
svg.icon { fill: currentColor; }
/* Legacy CSS-mask fallback for any non-<svg> .icon still driven by --icon-url. */
.icon:not(svg) {
  background-color: currentColor;
  -webkit-mask-image: var(--icon-url);
          mask-image: var(--icon-url);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}

/* Login screen — applied to <body> when renderLogin() runs. Overrides the
 * global `body { display: flex }` rule (which expects an app-shell child)
 * so the login card can center properly. */
body.cp-login-mode {
  display: block;
  min-height: 100vh;
  margin: 0;
  padding: 0;
  background: var(--page-bg);
}

/* ── Main ──
 * Sits below the full-width topbar and to the right of the sidebar. */
#main {
  margin-left: var(--sidebar-width);
  margin-top: var(--topbar-h);
  min-height: calc(100vh - var(--topbar-h));
  display: flex;
  flex-direction: column;
  transition: margin-left var(--t);
}
#sidebar.collapsed ~ #main { margin-left: var(--sidebar-width-collapsed); }

/* ── Topbar ──
 * Full-width: spans the entire viewport. Sidebar starts below it.
 * Layout: [ icon · MSL Portal ] [ rounded search ] [ bell ]
 */
#topbar {
  background: var(--blue) !important;
  height: 56px !important;
  min-height: 56px !important;
  max-height: 56px !important;
  display: flex; align-items: center;
  padding: 0;       /* bell's right-edge gap is owned by .topbar-right margin-right */
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: var(--z-topbar);
  flex-shrink: 0;
  color: var(--topbar-text);
  box-sizing: border-box;
  /* No bottom border here — the .page-header below the topbar carries
   * its own #d1d6e0 hairline at the bottom of the white strip, and
   * having a border on both produced a doubled-line effect at the
   * topbar/page-header seam. */
}
/* Brand block. The wordmark-wrap takes a hardcoded calc width = brand-width
 * minus icon-zone width. No flex math, no absolute positioning ambiguity.
 * Inside the wrap, justify-content centers the text horizontally. */
.topbar-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  color: var(--topbar-text);
  flex-shrink: 0;
  flex-grow: 0;
  width: var(--sidebar-width) !important;
  min-width: var(--sidebar-width);
  max-width: var(--sidebar-width);
  height: 100%;
  box-sizing: border-box;
  cursor: pointer;
}
.topbar-brand:hover { color: var(--topbar-text); text-decoration: none; }
/* Single full logo centered over the 224px sidebar zone (replaces the
 * icon + wordmark). object-fit keeps aspect; max constraints fit the
 * 56px-tall topbar and the sidebar-width brand block. */
.topbar-brand-logo {
  max-height: 40px;
  max-width: calc(var(--sidebar-width) - 24px);
  width: auto;
  height: auto;
  object-fit: contain;
}
.topbar-brand-icon-wrap {
  width: 56px !important;
  min-width: 56px;
  max-width: 56px;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  flex-grow: 0;
}
.topbar-brand-icon {
  width: 32px !important;
  height: 32px !important;
  flex-shrink: 0;
  object-fit: contain;
  background: transparent;
}
.topbar-brand-wordmark-wrap {
  width: calc(var(--sidebar-width) - 56px) !important;
  min-width: calc(var(--sidebar-width) - 56px);
  max-width: calc(var(--sidebar-width) - 56px);
  height: 100%;
  flex-shrink: 0;
  flex-grow: 0;
  display: block;
  text-align: left;
  line-height: 55px;
}
.topbar-wordmark {
  font-size: var(--fs-3xl);
  font-weight: var(--fw-medium);
  color: var(--topbar-text);
  letter-spacing: -0.005em;
  white-space: nowrap;
  display: inline;
  line-height: normal;     /* don't inherit the 56px line-height */
  margin-left: 0;          /* left-aligned wordmark — centering nudge removed */
  vertical-align: middle;
}
.topbar-search {
  width: 326px !important;
  min-width: 326px !important;
  max-width: 326px !important;
  height: 40px !important;
  min-height: 40px !important;
  max-height: 40px !important;
  margin-left: 3px;          /* brand zone ends at sidebar-width=230, search starts at 233 */
  flex-shrink: 0;
  flex-grow: 0;
  display: flex; align-items: center; gap: var(--space-3);
  background: var(--topbar-search-bg);
  border-radius: var(--r);
  padding: 0 var(--space-4);
  cursor: text;
  transition: background var(--t);
  box-sizing: border-box;
}
.topbar-search:hover,
.topbar-search:focus-within { background: var(--blue-hover); }
.topbar-search svg,
.topbar-search .icon {
  width: 18px; height: 18px;
  color: var(--topbar-search-text);
  flex-shrink: 0;
}
.topbar-search input {
  background: transparent; border: none; outline: none;
  color: var(--topbar-text);
  font-size: var(--fs-base); font-weight: var(--fw-regular); line-height: 20px; font-family: inherit;
  flex: 1; min-width: 0;
}
.topbar-search input::placeholder { color: var(--topbar-search-text); }

/* ── Global search dropdown ──
 * Positioned to overlay the .topbar-search box exactly so opening
 * the popup visually replaces the trigger input. Width = trigger
 * width (326) + 17 = 343 px; left = sidebar-width + topbar-search's
 * 3 px margin-left so the left edges align; top = (topbar-h − 41) / 2
 * = 7.5 px so the top edges align too. z-index 101 sits above the
 * topbar (100). Display: none by default → flex column when .is-open.
 * Persistent header on top, scrollable body underneath. */
#global-search-results {
  display: none;
  flex-direction: column;
  position: fixed;
  top: calc((var(--topbar-h) - 41px) / 2);
  left: calc(var(--sidebar-width) + 1px);    /* -2 px vs prior +3 */
  width: 658px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-popup);
  z-index: 101;
  /* No max-height / no scroll — popup is sized to fit 5 results per
   * category (5 rows × ~32 px + category header ~24 + padding) which
   * comfortably fits without the user ever needing to scroll. */
  overflow: visible;
  padding: 0;
}
#global-search-results.is-open { display: flex; }
#sidebar.collapsed ~ #main #global-search-results,
body:has(#sidebar.collapsed) #global-search-results {
  left: calc(var(--sidebar-width-collapsed) + 1px);
}

/* Header — 57 px tall, search icon + functional input, separator
 * (border-bottom) divides it from the categories. */
.gs-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  height: 57px;
  flex-shrink: 0;
  padding: 0 var(--space-4);
  border-bottom: 1px solid var(--border);
}
.gs-header .icon {
  width: 21px;
  height: 21px;
  color: var(--text-slate);
  flex-shrink: 0;
}
.gs-header input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font: inherit;
  font-size: var(--fs-base);
  color: var(--text-primary);
  padding: 0;
}
.gs-header input::placeholder { color: #757375; }

/* Body — beneath the header. No scroll (popup is sized to fit). */
.gs-body {
  flex: 1;
  min-height: 0;
  overflow: visible;
  padding: var(--space-2) 0;
}

/* No visual separator between categories (per spec). Just a small
 * vertical gap (margin-top) so they read as distinct groups. */
.gs-category { padding: 8px 0 4px; }
.gs-category + .gs-category { margin-top: var(--space-1); }
/* Category header — regular case (no uppercase / no letter-spacing
 * per spec), 21 px icon coloured #516378. */
.gs-category-header {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 4px 16px;
  font-size: var(--fs-md); font-weight: var(--fw-medium);
  color: var(--text-secondary);
  text-transform: none;
  letter-spacing: 0;
}
.gs-category-header .icon { width: 21px; height: 21px; color: var(--text-slate); }
.gs-result {
  display: flex; align-items: center; gap: var(--space-2);
  /* +4 px left indent vs the prior 8 16 — visually offsets the result
   * rows from the category header / popup edge. */
  padding: 8px 16px 8px 20px;
  cursor: pointer;
  color: var(--text-primary);
  font-size: var(--fs-base);
}
.gs-result:hover { background: var(--surface-hover); }
/* Per-row icons omitted from results per spec — only the category
 * header carries an icon. The .gs-result .icon rule is kept as a
 * safety net for any stray render. */
.gs-result .icon { display: none; }
.gs-result-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Stacked variant — used for Tax Returns rows so the meta (Form / year
 * / matter) sits on its own line under the client name. Switches the
 * inner .gs-result-text to a block-flow flex column.
 *
 * gap collapsed to 0 + meta gets a -7 px margin-top — net visible gap
 * reduction of 14 px vs the prior 7 px gap. */
.gs-result--stacked .gs-result-text {
  display: flex;
  flex-direction: column;
  gap: 0;
  white-space: normal;
}
.gs-result--stacked .gs-result-meta { margin-top: -4px; }
.gs-result-primary { color: var(--text-primary); }
/* Loading placeholder row — used when the popup opens before the
 * Contacts list has hydrated. Muted text + no hover affordance so
 * it doesn't read as a clickable row. */
.gs-result--placeholder {
  color: #757375;
  cursor: default;
  font-style: italic;
}
.gs-result--placeholder:hover { background: transparent; }
/* Locked to #516378 — !important defends against the parent
 * .gs-result's color cascade (which is #1e2535 for the primary line)
 * leaking into the meta line. */
.gs-result-meta { color: var(--text-slate) !important; font-size: var(--fs-sm); }
.gs-result-meta mark { color: var(--text-slate) !important; }
.gs-result mark { background: #fff3a8; color: inherit; padding: 0; }
.gs-empty {
  padding: 16px;
  color: var(--text-tertiary);
  font-size: var(--fs-md);
}
.gs-empty-summary { font-size: var(--fs-xs); margin-top: 6px; color: var(--text-tertiary); }

/* Right cluster — Clio styles each icon button as a darker-blue rounded
 * box (same hue as search input). Hover deepens. */
/* margin-right: 13 CSS px → ≈17 DevTools at 125% scaling per spec. */
.topbar-right { margin-left: auto; margin-right: 13px; display: flex; align-items: center; gap: var(--space-2); }
.topbar-icon-btn {
  width: 40px !important; height: 40px !important;
  border-radius: var(--r);
  border: none;
  background: var(--topbar-search-bg);
  color: var(--topbar-text);
  display: flex; align-items: center; justify-content: center;
  transition: background var(--t);
  position: relative;
  cursor: pointer;
}
.topbar-icon-btn:hover { background: var(--blue-hover); }
.topbar-icon-btn svg,
.topbar-icon-btn .icon { width: 18px; height: 18px; }
.notif-badge {
  position: absolute; top: -4px; right: -4px;
  background: #DC3545; color: #fff;
  font-size: var(--fs-2xs); font-weight: var(--fw-bold);
  padding: 1px 4px; border-radius: 10px;
  min-width: 18px; text-align: center;
  line-height: 14px;
  border: 1.5px solid var(--topbar-bg);
}

/* ── Global Sync control (topbar) ──
 * One button + a dropdown popover that runs every data sync and shows live
 * per-phase status. Replaces the per-page Refresh buttons. */
.sync-control { position: relative; display: inline-flex; }

/* ── Global "Create new" topbar menu ──
 * Matches the .topbar-icon-btn look exactly (same bg / hover / text color /
 * height / radius), just wider to hold the label. The + inherits currentColor
 * (= --topbar-text) so it's the same color as the label. */
.create-new { position: relative; display: inline-flex; }
.create-new-btn {
  display: inline-flex; align-items: center; gap: 7px;
  height: 40px; padding: 0 14px;
  background: var(--topbar-search-bg); color: var(--topbar-text);
  border: none; border-radius: var(--r);
  font-size: var(--fs-base); font-weight: var(--fw-medium, 500); line-height: 1;
  cursor: pointer; white-space: nowrap;
  transition: background var(--t);
}
.create-new-btn:hover { background: var(--blue-hover); }
.create-new-btn .icon { width: 18px; height: 18px; flex: 0 0 auto; }
.create-new-pop { top: calc(100% + 4px); right: 0; left: auto; min-width: 182px; }
/* A border-top hairline (height:0) rasterizes as one crisp device line at any
 * position; a sub-1px filled block antialiased differently per separator
 * (fractional DPR), making the lower one look bolder. */
.cn-sep { display: block; height: 0; padding: 0; margin: var(--space-2) 0; border-top: 1px solid var(--border); background: none; cursor: default; }

#global-sync-btn.spinning .icon { animation: gs-spin 0.8s linear infinite; }
@keyframes gs-spin { to { transform: rotate(360deg); } }
.sync-popover {
  position: absolute; top: calc(100% + 1px); right: 0;
  width: 320px; z-index: 1200;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  box-shadow: var(--shadow-popup);
  padding: 10px 12px 12px; color: var(--text-primary);
}
.sync-rows { display: flex; flex-direction: column; }
.sync-row { display: flex; align-items: center; gap: 8px; padding: 5px 2px; font-size: var(--fs-sm); border-top: 1px solid #f0f2f6; }
.sync-row:first-child { border-top: 0; }
.sync-row-ico { width: 14px; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; font-weight: var(--fw-bold); color: var(--text-tertiary); }
.sync-row-ico .icon { width: 13px; height: 13px; }
.sync-row.sync-done  .sync-row-ico { color: var(--success); }
.sync-row.sync-error .sync-row-ico { color: var(--danger); }
.sync-row-label { flex: 0 0 auto; }
.sync-row-note { flex: 1 1 auto; min-width: 0; text-align: right; color: var(--text-secondary); font-size: var(--fs-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sync-row-dur { flex: 0 0 auto; color: var(--text-tertiary); font-size: var(--fs-2xs); font-variant-numeric: tabular-nums; }
.sync-row-btn { flex: 0 0 auto; width: 24px; height: 24px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: 1px solid #dde3ea; border-radius: var(--r); background: #fff; color: var(--text-secondary); cursor: pointer; }
.sync-row-btn:hover:not(:disabled) { background: var(--blue-light); color: var(--text-link); border-color: var(--blue-light); }
.sync-row-btn:disabled { opacity: .4; cursor: default; }
.sync-row-btn .icon { width: 13px; height: 13px; }
.sync-pop-foot { font-size: var(--fs-xs); color: var(--text-tertiary); padding: 8px 2px 0; margin-top: 6px; border-top: 1px solid #f0f2f6; }
.sync-pop-actions { display: flex; gap: var(--space-2); margin-top: 10px; }
.sync-pop-actions .btn { flex: 1; justify-content: center; }
.gs-mini-spin { display: inline-block; width: 11px; height: 11px; border: 2px solid var(--blue-light); border-top-color: var(--text-link); border-radius: 50%; animation: gs-spin 0.7s linear infinite; vertical-align: -1px; }

/* Card header: a status line ("Last auto-sync" + glyph, or "Syncing…") with the
   relative time right-aligned, then a sub-line with absolute time + duration. */
.sync-hc-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; font-size: var(--fs-base); font-weight: var(--fw-semibold); }
.sync-hc-title { display: inline-flex; align-items: center; gap: 6px; }
.sync-hc-glyph { display: inline-flex; align-items: center; font-weight: var(--fw-bold); color: var(--text-tertiary); }
.sync-hc-glyph .icon { width: 14px; height: 14px; }
.sync-hc-head.sync-hc-done    .sync-hc-glyph { color: var(--success); }
.sync-hc-head.sync-hc-error   .sync-hc-glyph { color: var(--danger); }
.sync-hc-head.sync-hc-partial .sync-hc-glyph { color: var(--warning-fg); }
.sync-hc-ago { font-size: var(--fs-xs); font-weight: 400; color: var(--text-tertiary); white-space: nowrap; }
.sync-hc-sub { font-size: var(--fs-xs); color: var(--text-tertiary); padding: 1px 0 7px; margin-bottom: 4px; border-bottom: 1px solid #f0f2f6; font-variant-numeric: tabular-nums; }
.sync-row.sync-partial .sync-row-ico { color: var(--warning-fg); }
.sync-row.sync-skip    .sync-row-ico { color: var(--text-tertiary); }

/* ── Accounting tracker ── monthly-bookkeeping matrix + candidate review ──── */
.ac-year-nav { display: inline-flex; align-items: center; gap: var(--space-2); }
.ac-year-nav .btn { padding: 4px 12px; }
.ac-year-label { font-size: var(--fs-base); font-weight: var(--fw-semibold); min-width: 48px; text-align: center; font-variant-numeric: tabular-nums; }

.ac-section { padding: var(--space-3) var(--space-3) 0; }
.ac-section:last-child { padding-bottom: var(--space-3); }
.ac-section-head { display: flex; align-items: baseline; gap: var(--space-2); font-size: var(--fs-base); font-weight: var(--fw-semibold); margin-bottom: var(--space-2); }
.ac-section-head .ac-sub   { font-weight: 400; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.ac-section-head .ac-count { margin-left: auto; font-weight: 400; font-size: var(--fs-sm); color: var(--text-tertiary); }

.ac-matrix-scroll { overflow-x: auto; }
.ac-matrix { width: auto; min-width: 100%; table-layout: auto; }
.ac-matrix th, .ac-matrix td { white-space: nowrap; }
.ac-matrix thead th { font-size: var(--fs-xs); color: var(--text-secondary); font-weight: var(--fw-semibold); text-align: center; }
.ac-matrix .ac-col-client { position: sticky; left: 0; background: #fff; min-width: 200px; max-width: 280px; text-align: left; }
.ac-matrix tbody td.ac-col-client { z-index: 1; }
.ac-matrix thead th.ac-col-client { z-index: 2; }
.ac-matrix .ac-col-mo { text-align: center; padding: 4px; }
.ac-remove { border: 0; background: transparent; color: var(--text-tertiary); cursor: pointer; font-size: 16px; line-height: 1; padding: 0 2px; margin-left: 6px; opacity: 0; vertical-align: middle; }
.ac-matrix tbody tr:hover .ac-remove { opacity: 1; }
.ac-remove:hover { color: var(--danger); }

/* Status pill-selects, color-coded by workflow state. */
.ac-status { border: 1px solid transparent; border-radius: var(--r); padding: 2px 6px; font-size: var(--fs-xs); cursor: pointer; width: 100%; max-width: 132px; -webkit-appearance: none; appearance: none; text-align: center; text-align-last: center; }
.ac-status--not-started        { background: #f0f2f6; color: var(--text-tertiary); }
.ac-status--awaiting-documents { background: #fff4e0; color: #9a6700; }
.ac-status--in-progress        { background: var(--blue-light); color: var(--text-link); }
.ac-status--review             { background: #efe7fb; color: #6b3fb0; }
.ac-status--delivered          { background: #e3f6e9; color: #1a7f43; }
.ac-status--on-hold            { background: #f0f2f6; color: var(--text-secondary); }

.ac-cand-hint { font-size: var(--fs-sm); color: var(--text-secondary); margin-bottom: var(--space-2); }
.ac-candidates { display: flex; flex-direction: column; gap: 1px; background: #e6e9ef; border: 1px solid #e6e9ef; border-radius: var(--r); overflow: hidden; }
.ac-cand-row { display: flex; align-items: center; gap: var(--space-3); padding: 8px 12px; background: #fff; }
.ac-cand-name { flex: 0 0 220px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ac-cand-why { flex: 1 1 auto; min-width: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.ac-cand-matter { font-size: var(--fs-xs); color: var(--text-secondary); background: #f5f7fa; border: 1px solid #e6e9ef; border-radius: 10px; padding: 1px 8px; white-space: nowrap; }
.ac-cand-actions { flex: 0 0 auto; display: flex; gap: var(--space-2); }
.btn-sm { padding: 4px 10px; font-size: var(--fs-sm); }
.ac-dismissed-toggle { padding: var(--space-2) var(--space-3) 0; }

/* Admin "view as staff" pill (topbar) - visible only while previewing the
   non-admin view, so the admin always knows they're in preview mode. */
.vas-pill {
  display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px;
  border: 1px solid var(--warning); border-radius: 16px; background: var(--warning-bg);
  color: var(--warning-fg); font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  cursor: pointer; white-space: nowrap;
}
.vas-pill:hover { background: #ffe8a8; }
.vas-pill-x { font-size: 16px; line-height: 1; opacity: .75; }
.vas-pill[hidden] { display: none; }

/* ── Transcript filing settings (modal + the embedded-tab gear) ── */
.trx-filing-btn .icon { width: 18px; height: 18px; }
.filing-intro { font-size: var(--fs-base); color: var(--text-secondary); line-height: 1.5; margin: 0 0 var(--space-3); }
.filing-check { display: flex; align-items: center; gap: 8px; font-size: var(--fs-base); padding: 4px 0; cursor: pointer; }
.filing-check input { width: 16px; height: 16px; flex: 0 0 auto; }
.filing-enabled { margin-bottom: var(--space-1); }
.filing-section-label { font-size: var(--fs-lg); font-weight: var(--fw-semibold); color: var(--text-primary); margin: var(--space-3) 0 var(--space-2); }
/* A section label that OPENS a container has the container's own padding above
   it already - its top margin stacked on that, leaving 32px over the first
   label against 8-12px everywhere else in the same card. */
.filing-section-label:first-child { margin-top: 0; }
/* Clio-style thin separator between modal sections. */
.filing-sep { border: 0; border-top: 1px solid var(--border); margin: var(--space-3) 0 0; }
.filing-buckets { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--space-3); }
.filing-years { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px 12px; }
.filing-year-chip { display: flex; align-items: center; gap: 6px; font-size: var(--fs-base); padding: 3px 0; cursor: pointer; }
.filing-year-chip input { width: 16px; height: 16px; flex: 0 0 auto; }
/* Filing modal footer (Clio-style): separator above, dismiss button pinned to
 * the left edge, actions grouped on the right. 12px to the modal edge. */
.filing-footer { padding: var(--space-3); border-top: 1px solid var(--border); gap: var(--space-2); justify-content: space-between; background: var(--page-bg); border-bottom-left-radius: var(--r-lg); border-bottom-right-radius: var(--r-lg); }
/* .modal-footer{display:flex} would override the UA [hidden] rule, so the
 * footer must be explicitly hidden until the body loads. */
.filing-footer[hidden] { display: none; }
.filing-footer-actions { display: flex; gap: var(--space-2); }
.filing-result { margin-top: var(--space-3); padding: 8px 10px; background: var(--page-bg); border-radius: var(--r); font-size: var(--fs-sm); color: var(--text-primary); line-height: 1.5; }
/* Transcript filing-activity log table (Transcripts page "Filing activity"). */
.filing-log-wrap { max-height: 60vh; overflow-y: auto; }
.filing-log { width: 100%; border-collapse: collapse; font-size: var(--fs-md); }
.filing-log th, .filing-log td { text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.filing-log th { position: sticky; top: 0; background: var(--page-bg); font-weight: var(--fw-semibold); z-index: 1; }
.filing-log td:nth-child(2) { white-space: normal; }
.filing-log-err { color: var(--danger); max-width: 300px; overflow: hidden; text-overflow: ellipsis; cursor: help; }

/* ── Document templates page ── */
/* Draft page: full-width cards on the portal's regular 12px rhythm - 12px
 * side/bottom gutters and 12px between the tab bar and cards. */
.tpl-page {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-3);
}
.tpl-h { font-size: var(--fs-lg); font-weight: var(--fw-semibold); margin: 0 0 var(--space-2); }
/* Letters approval queue (Documents tab). */
.lq-actions { white-space: nowrap; }   /* a content-sized, right-aligned table-cell - see the queue-column block below */
.lq-reason { margin-top: 4px; font-size: var(--fs-sm); color: var(--status-overdue-text, #842029); }

/* ── Settings: Clio-style index + detail views ──────────────────────────────
 * Index = three balanced columns of titled links + one-line descriptions;
 * clicking a Portal-settings item opens a focused detail view (.settings-detail)
 * with a back link. Keeps the middle column from towering over the sides. */
/* Full-width page + a uniform 12px gutter all round, same as Tools / Contacts /
 * Forms: pin #main so it fills the main column (otherwise it shrink-wraps to the
 * content width and leaves dead space on the right), and put the body padding in
 * one place (.settings-page-body) so every edge gap stays equal. The page-header
 * stays edge-to-edge as its own 56px band. */
body:has(.settings-page) #main { width: calc(100% - var(--sidebar-width)); min-width: 0; }
body:has(.settings-page) #sidebar.collapsed ~ #main { width: calc(100% - var(--sidebar-width-collapsed)); }
body:has(.settings-page) #content { min-width: 0; }
.settings-page { min-width: 0; width: 100%; box-sizing: border-box; }
.settings-page-body { padding: var(--space-3); min-width: 0; }
.settings-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-6) 40px;
  align-items: start;
}
@media (max-width: 960px) { .settings-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 620px) { .settings-grid { grid-template-columns: 1fr; } }
.settings-col-head {
  font-size: var(--fs-md); font-weight: var(--fw-semibold);
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-tertiary);
  padding-bottom: var(--space-2); margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
}
.settings-item { margin-bottom: var(--space-4); }
.settings-item:last-child { margin-bottom: 0; }
.settings-link {
  font-size: var(--fs-base); font-weight: var(--fw-medium);
  color: var(--text-link); cursor: pointer;
}
.settings-link:hover { color: var(--text-link-hover); text-decoration: underline; }
.settings-item-desc {
  font-size: var(--fs-smd); color: var(--text-tertiary);
  line-height: 1.45; margin-top: 2px; max-width: 34ch;
}

.settings-detail { max-width: 640px; display: flex; flex-direction: column; gap: var(--space-5); }
.settings-back {
  display: inline-block; margin-bottom: var(--space-3);
  font-size: var(--fs-md); font-weight: var(--fw-medium); color: var(--text-secondary);
}
.settings-back:hover { color: var(--text-link-hover); text-decoration: underline; }
.settings-field { display: flex; flex-direction: column; }
.settings-field-title { font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--text-primary); }
.settings-field-desc { font-size: var(--fs-smd); color: var(--text-secondary); line-height: 1.5; margin-top: 2px; max-width: 62ch; }
.settings-field-msg { font-size: var(--fs-smd); color: var(--text-tertiary); margin-top: var(--space-1); }

/* Settings: attorney signature rows (name | preview | actions). */
.set-sig-list { display: flex; flex-direction: column; gap: var(--space-2); max-width: 560px; }
.set-sig-row { display: flex; align-items: center; gap: var(--space-3); padding: 8px 12px; border: 1px solid #dde3ea; border-radius: var(--r); }
.set-sig-name { flex: 0 0 180px; font-size: var(--fs-base); font-weight: var(--fw-medium); color: var(--text-primary); }
.set-sig-preview { flex: 1 1 auto; min-width: 0; }
.set-sig-preview img { max-height: 40px; max-width: 200px; object-fit: contain; background: #fff; }
.set-sig-actions { flex: 0 0 auto; display: flex; gap: var(--space-1); }

.tpl-upload { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-3); }
/* Filename readout beside the Choose-file button. Sits between the button and
   the name input, so it gets a little extra room on its right. */
#tpl-file-label { margin-right: var(--space-2); }
#tpl-file-label.tpl-file-set { color: var(--text-primary); }
.tpl-list { display: flex; flex-direction: column; gap: var(--space-2); }
.tpl-row { display: flex; align-items: flex-start; gap: var(--space-3); padding: 10px 12px; border: 1px solid #dde3ea; border-radius: var(--r); }
.tpl-row-main { flex: 1 1 auto; min-width: 0; }
.tpl-row-head { display: flex; align-items: baseline; gap: var(--space-2); }
.tpl-row-name { font-size: var(--fs-base); font-weight: var(--fw-medium); color: var(--text-primary); }
.tpl-row-count { font-size: var(--fs-sm); color: var(--text-secondary); }
.tpl-row-fields { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 4px; }
.tpl-chip { font-size: var(--fs-2xs); background: var(--blue-light); color: var(--text-link); border-radius: var(--pill-radius); padding: 1px 7px; font-family: ui-monospace, Menlo, Consolas, monospace; }
/* Row action matches the Forms matter rows: a quiet icon button, not a
   full-size secondary button competing with the template name. */
.tpl-del { flex: 0 0 auto; margin-top: -2px; }
.tpl-del:hover:not(:disabled) { color: var(--danger); }
.tpl-results { position: absolute; left: 0; right: 0; top: 100%; z-index: 50; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); box-shadow: var(--shadow-popup); max-height: 280px; overflow-y: auto; margin-top: 2px; }
.tpl-result { display: block; width: 100%; text-align: left; padding: 8px 12px; background: none; border: 0; cursor: pointer; font-size: var(--fs-sm); color: var(--text-primary); }
.tpl-result:hover { background: var(--blue-light); }
.tpl-fields-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-2); }
/* These classes set display (grid/flex), which overrides the [hidden] UA rule -
 * so the `hidden` attribute is ignored unless we restore it at higher specificity.
 * (Without this, e.g. the Tyvek weight row stays visible even when hidden.) */
.tpl-fields-grid[hidden], .forms-matter-row[hidden] { display: none; }
/* The cover-letter composer stacks several sibling blocks per section (grids, the
 * SCM block, the address row). Now the pane is .forms-page-body (form-group margin
 * reset to 0), give each block an 8px bottom gap so fields don't touch; the
 * section labels' own 12px top margin still separates sections. */
#tpl-fields > *:not(.filing-section-label),
#ms-fields > *:not(.filing-section-label) { margin-bottom: var(--space-2); }
/* The last row's trailing margin stacked on top of the card's own bottom padding,
 * leaving more space below the final field than above the first one. */
#tpl-fields > *:last-child,
#ms-fields > *:last-child { margin-bottom: 0; }
.tpl-gen-actions { margin-top: var(--space-3); display: flex; gap: var(--space-2); }

/* ── Draft: page tabs + cover-letter composer + preview ── */
/* The Draft page (like every mostly-form page) needs #main pinned to the
 * viewport width - flex sizing otherwise shrinks the whole page to its
 * content's width once the wide Templates card is hidden behind its tab.
 * Same rule set as body:has(.tools-page) / .ec-form / .forms-page-body. */
body:has(.tpl-page) #main { width: calc(100% - var(--sidebar-width)); min-width: 0; }
body:has(.tpl-page) #sidebar.collapsed ~ #main { width: calc(100% - var(--sidebar-width-collapsed)); }
body:has(.tpl-page) #content { min-width: 0; }
/* The Draft page's Create a letter | Templates tab row - same component as
 * the Contact/TR pages, placed on a plain page. Compound selector so this
 * beats .contact-tabbar's own full-bleed negative margin (same specificity,
 * but .contact-tabbar sits LATER in this file and would win otherwise). */
/* Full-bleed bar under the page header, like the TR/Contact pages; the 12px
 * gap to the first card comes from .tpl-page's own padding. */
.contact-tabbar.draft-tabbar { margin: 0; }
/* Template picker cards (the Forms .forms-kpi pattern) carry the MSL brand
 * mark instead of the PDF glyph. */
.tpl-kpi-logo { width: 18px; height: 18px; flex-shrink: 0; object-fit: contain; }
/* The upload row's name input was width:100% (the .input default), wrapping
 * the flex row into stacked full-width lines - cap it so file / name / Upload
 * sit on ONE row. */
.tpl-upload .input { max-width: 320px; }
/* Roomier card inset + a consistent field rhythm (the global .form-group has
 * no bottom margin - each page supplies its own). */
.tpl-page .card-body { padding: var(--space-3); }
.tpl-page .form-group { margin-bottom: var(--space-2); }
/* Dropdown popups (ec-combo lists, enhanced-select listboxes) must escape
 * their card - .card's overflow:hidden paints them cut off at the card's
 * bottom edge. NOTE: the override needs ≥2 selectors of specificity -
 * a bare .forms-pick-card ties with .card and loses on file order (found
 * live: computed overflow stayed hidden). These cards hold padded form
 * bodies only, so nothing else can bleed past the rounded corners. */
.tpl-page > .card,
.forms-page-body .card { overflow: visible; }
/* Enclosure browser: crumbs + drill-down list, Documents-tab style. */
.cl-doc-browser { border: 1px solid var(--border); border-radius: var(--r); margin-bottom: var(--space-3); overflow: hidden; }
.cl-doc-crumbs {
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
  padding: 7px 10px; background: var(--surface-titlebar, #f7f9fb);
  border-bottom: 1px solid var(--border); font-size: var(--fs-sm);
}
.cl-crumb { background: none; border: 0; padding: 0; color: var(--text-link); cursor: pointer; font-size: var(--fs-sm); }
.cl-crumb:hover { text-decoration: underline; }
.cl-crumb-sep { color: var(--text-tertiary); }
.cl-crumb-cur { color: var(--text-primary); font-weight: var(--fw-medium); }
.cl-doc-items { max-height: 260px; overflow-y: auto; padding: 4px 0; }
.cl-doc-row {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 6px 12px; font-size: var(--fs-base); color: var(--text-primary);
  background: none; border: 0; text-align: left; cursor: pointer;
}
.cl-doc-row:hover { background: var(--surface-hover); }
.cl-doc-row .icon { width: 16px; height: 16px; color: var(--text-slate); flex: 0 0 auto; }
.cl-doc-folder span { color: var(--text-link); }
.cl-doc-row input { flex: 0 0 auto; }
/* Selected enclosures: numbered rows with an editable description each. */
.cl-enc-sel { border: 1px solid var(--border); border-radius: var(--r); padding: 10px 12px; margin-bottom: var(--space-3); }
.cl-enc-sel-head { font-size: var(--fs-base); font-weight: var(--fw-semibold); margin-bottom: 8px; }
.cl-enc-sel-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.cl-enc-sel-row:last-child { margin-bottom: 0; }
.cl-enc-sel-n { flex: 0 0 auto; color: var(--text-slate); font-variant-numeric: tabular-nums; }
.cl-enc-sel-row .input { flex: 1 1 auto; }
/* Per-enclosure "copies" stepper (Mail & Ship): a narrow x N count, so the same
 * document can ride in the envelope more than once. */
.cl-enc-copies { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px; color: var(--text-slate); }
.cl-enc-copies-x { font-variant-numeric: tabular-nums; }
.cl-enc-sel-row .cl-enc-copies .input { flex: 0 0 auto; width: 54px; text-align: center; padding-left: 6px; padding-right: 4px; }
/* The per-row reorder arrows reuse the 2848 matter-reorder group
 * (.contacts-pager-group / .contacts-page-btn) and the remove X reuses the
 * Edit-contact custom-field remove (.ec-x / .ec-x-glyph) - same size + look. */
/* SimpleCertifiedMail page-count/envelope block, shown under the Delivery
 * picker when a certified-mail service is chosen. */
.cl-scm {
  border: 1px solid var(--border); border-radius: var(--r);
  padding: 10px 12px; margin-bottom: var(--space-3);
  background: var(--page-bg, #f7f9fb);
}
.cl-scm .form-hint { margin: 6px 2px 0; }
/* An empty note is the normal state, and its 6px top margin was still counted -
   17px under the row against 11px above it. */
.cl-scm .form-hint:empty { display: none; }
/* The pages pair is NOT 50/50: the first cell holds a bare number, while Total
   pages carries the breakdown ("9 (label 2 + letter 2 + enclosures 5)") which
   needs ~256px and wrapped to two lines at the even split's 215px. */
.cl-scm-pages > .form-group:first-child { flex: 0 0 124px; }
.cl-scm-pages > .form-group:last-child  { flex: 1 1 auto; }
/* Tyvek Priority weight entry: [lb] lb  [oz] oz on one line. */
.cl-weight-inputs { display: flex; align-items: center; gap: 8px; }
.cl-weight-inputs .input { width: 72px; flex: 0 0 auto; }
.cl-weight-unit { font-size: var(--fs-base); color: var(--text-secondary); }
/* Mail & Ship Live-mode indicators: an amber banner + a footer chip. */
/* Sits directly under the Contact/Matter row, so it needs a top gap - without it
 * the banner reads as attached to the field above. Matches the 8px the #ms-fields
 * rule gives its bottom, so the banner sits evenly between the two. */
.ms-live-warn { display: flex; align-items: center; gap: 8px;
  margin-top: var(--space-2); margin-bottom: var(--space-2);
  padding: 8px 12px; border: 1px solid #f0c36d; background: #fdf6e3; color: #8a6d1a;
  border-radius: var(--r); font-size: var(--fs-base); }
.ms-live-warn .icon { width: 18px; height: 18px; flex: 0 0 auto; color: #c8901a; }
/* "This will:" list in the Create shipment confirm - what the click actually
 * does, spelled out before real postage is drawn. Scoped to this dialog so the
 * other confirms keep their quieter secondary body text.
 * The sitewide `ul { list-style: none }` reset is why the markers need
 * restoring explicitly here. */
/* Scoped UNDER .ui-confirm-body on purpose: a bare `.ms-confirm p` ties on
 * specificity with `.ui-confirm-body p` (secondary) and loses on source order. */
.ui-confirm-body .ms-confirm p,
.ui-confirm-body .ms-confirm ul,
.ui-confirm-body .ms-confirm li { color: var(--text-primary); }
.ms-confirm-list { list-style: disc; margin: 6px 0 12px; padding-left: 22px; }
.ms-confirm-list li { margin: 3px 0; }
/* Tracking hover card - same surface as the portal's popups (.ui-pop tokens),
 * showing only what matters at a glance before clicking through to USPS. */
.ms-track-card {
  position: fixed; z-index: 60; min-width: 260px; max-width: 320px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-popup);
  padding: 10px 12px; font-size: var(--fs-smd); pointer-events: none;
}
.ms-track-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.ms-track-status { font-size: var(--fs-base); font-weight: 600; color: var(--text-primary); }
.ms-track-demo {
  padding: 1px 7px; border-radius: 999px; font-size: var(--fs-sm); font-weight: 600;
  background: var(--grey-100, #eef1f5); color: var(--text-secondary);
}
.ms-track-row { display: flex; gap: 10px; padding: 2px 0; }
.ms-track-l { flex: 0 0 84px; color: var(--text-tertiary); }
.ms-track-v { flex: 1 1 auto; min-width: 0; color: var(--text-primary); overflow-wrap: anywhere; }
.ms-track-foot {
  margin-top: 8px; padding-top: 7px; border-top: 1px solid var(--border);
  color: var(--brand, #0067D1); font-size: var(--fs-sm);
}
.ms-live-chip { align-self: center; margin-right: auto; padding: 2px 10px; border-radius: 999px;
  background: #fdecc8; color: #8a5b00; border: 1px solid #f0c36d; font-size: var(--fs-smd);
  font-weight: var(--fw-semibold); text-transform: uppercase; letter-spacing: 0.04em; }
/* Editable Form / Reference cells on the Mail & Ship dashboard: read as text,
 * reveal a border on hover, become a real input on focus. */
.ms-cell-edit {
  width: 100%; box-sizing: border-box;
  border: 1px solid transparent; background: transparent;
  padding: 3px 6px; font: inherit; color: inherit; border-radius: 4px;
}
.ms-cell-edit::placeholder { color: var(--text-slate); }
.ms-cell-edit:hover { border-color: var(--border); }
.ms-cell-edit:focus { border-color: #0067D1; background: var(--surface, #fff); outline: none; }
.ms-cell-edit:disabled { opacity: .6; }
.ms-form-edit { min-width: 70px; }
.ms-ref-edit  { min-width: 150px; }
/* The two user-resizable columns (Client, Form) clip to their dragged width -
 * Client shows an ellipsis, the Form input fills and can shrink with the cell -
 * instead of the content forcing the column wider (see makeColumnResizer). */
.data-table td.ms-col-client { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table td.ms-col-form { overflow: hidden; }
.data-table td.ms-col-form .ms-form-edit { min-width: 0; }
/* USPS delivery block: extra-service checkbox grid + postage/tracking readout. */
.usps-extra-grid { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 4px; }
.usps-extra-opt { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-base); cursor: pointer; white-space: nowrap; }
.usps-extra-opt input { width: 16px; height: 16px; flex-shrink: 0; }
.cl-scm-readout { font-weight: var(--fw-semibold); }
.cl-outputs { display: flex; gap: 18px; flex-wrap: wrap; margin-bottom: var(--space-2); }
.cl-outputs label { display: flex; align-items: center; gap: 6px; font-size: var(--fs-base); cursor: pointer; }
.cl-preview-modal { max-width: 760px; width: 92vw; }
.cl-preview-scroll { max-height: 70vh; overflow-y: auto; background: var(--grey-150); padding: 16px; }
/* Letter-shaped page: serif, generous margins - an approximation of the Word
 * output for review, not a pixel render (the letterhead lives in the .docx). */
.cl-page {
  background: #fff; border: 1px solid var(--border); box-shadow: var(--shadow-sm);
  padding: 56px 64px; font-family: "Times New Roman", Times, serif;
  font-size: 15px; line-height: 1.45; color: #111;
}
.cl-page p { margin: 0 0 12px; }
.cl-right { text-align: right; }
.cl-re { margin-left: 48px; }
/* Court-case panel in the template generate view (one saved case per client). */
.tpl-court { border: 1px solid var(--border); border-radius: var(--r-sm); padding: var(--space-3); margin-bottom: var(--space-4); background: var(--page-bg); }
.tpl-court-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-bottom: var(--space-2); }

/* ── PDF tools page ── */
.pdf-tool { display: flex; flex-direction: column; gap: var(--space-3); }
/* Sticky so the actions stay reachable while a tall page grid scrolls. The
   page scrolls at the document level under the fixed 56px topbar, so pin just
   below it; --page-bg background hides cards scrolling underneath. */
.pdf-toolbar { position: sticky; top: var(--topbar-h); z-index: 5; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
  /* Bleed to the tab-content edges, then pad 12px on every side so the buttons
     sit an even 12px from top / bottom / gutters and align with the grid.
     (Cancels .tools-tab-content's 12px top padding that made the top gap large.) */
  margin: calc(var(--space-3) * -1) calc(var(--space-3) * -1) 0; padding: var(--space-3);
  background: var(--page-bg); border-bottom: 1px solid var(--grey-250); }
.pdf-sep { width: 1px; align-self: stretch; min-height: 20px; background: #dde3ea; margin: 0 2px; }
.pdf-count { margin-left: auto; font-size: var(--fs-sm); color: var(--text-secondary); white-space: nowrap; }
/* --pdf-w (set in JS, default 150px) drives the column min-width AND the thumb
   height together, so the page-thumbnail zoom scales every card uniformly. */
.pdf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--pdf-w, 150px), 1fr)); gap: var(--space-3); align-items: start; }
.pdf-zoom { display: inline-flex; align-items: center; gap: 2px; }
.pdf-zoom-btn { width: 26px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid #dde3ea; background: #fff; border-radius: var(--r); cursor: pointer; font-size: 16px; line-height: 1; color: var(--text-secondary); }
.pdf-zoom-btn:hover { background: var(--blue-light); color: var(--text-link); border-color: var(--blue-light); }
.pdf-zoom-ico { width: 14px; height: 14px; color: var(--text-tertiary); margin: 0 2px; }
.pdf-tool.is-dropping .pdf-grid { outline: 2px dashed var(--text-link); outline-offset: 4px; border-radius: var(--r); }

/* Empty-state drop zone (spans the full grid width). */
.pdf-dropzone { grid-column: 1 / -1; display: flex; flex-direction: column; align-items: center; text-align: center;
  border: 2px dashed #cfd8e3; border-radius: var(--r-lg); background: var(--surface-titlebar); padding: 44px 20px;
  transition: border-color .12s, background .12s; }
.pdf-tool.is-dropping .pdf-dropzone { border-color: var(--text-link); background: var(--blue-light); }
.pdf-dropzone-ico { width: 36px; height: 36px; color: var(--text-tertiary); margin-bottom: 10px; }
.pdf-dropzone-title { font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--text-primary); }
.pdf-dropzone-link { color: var(--text-link); cursor: pointer; text-decoration: underline; }
.pdf-dropzone-sub { font-size: var(--fs-base); color: var(--text-secondary); margin-top: 6px; max-width: 460px; }

.pdf-page { position: relative; border: 1px solid #dde3ea; border-radius: var(--r); background: #fff; overflow: hidden; cursor: grab; transition: box-shadow .12s, border-color .12s, opacity .12s; }
.pdf-page:hover { border-color: #c3ccd8; box-shadow: 0 2px 8px rgba(16, 33, 60, .10); }
.pdf-page.is-sel { border-color: var(--text-link); box-shadow: 0 0 0 2px var(--blue-light); }
.pdf-page.dragging { opacity: .4; }
.pdf-page.drop-target { border-color: var(--text-link); box-shadow: 0 0 0 2px var(--text-link); }
.pdf-page-thumb { position: relative; height: calc(var(--pdf-w, 150px) * 1.2); display: flex; align-items: center; justify-content: center; background: var(--page-bg); overflow: hidden; }
.pdf-page-thumb img { max-width: 100%; max-height: 100%; box-shadow: 0 1px 3px rgba(0, 0, 0, .18); transition: transform .15s; }
/* Selected-check badge (top-left), shown only when the card is selected. The
   white drop-shadow keeps the blue check legible on any page background. */
.pdf-page-check { position: absolute; top: 6px; left: 6px; width: 18px; height: 18px; color: var(--text-link);
  display: none; filter: drop-shadow(0 0 1px #fff) drop-shadow(0 0 1px #fff); }
.pdf-page.is-sel .pdf-page-check { display: block; }
/* Per-page actions (top-right), revealed on hover. */
.pdf-page-acts { position: absolute; top: 6px; right: 6px; display: flex; gap: 4px; opacity: 0; transition: opacity .12s; }
.pdf-page:hover .pdf-page-acts { opacity: 1; }
.pdf-mini { flex: 0 0 auto; width: 24px; height: 24px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid #dde3ea; border-radius: 5px; background: #fff; cursor: pointer; color: var(--text-secondary);
  box-shadow: 0 1px 2px rgba(16, 33, 60, .12); }
.pdf-mini:hover { background: var(--blue-light); color: var(--text-link); border-color: var(--blue-light); }
.pdf-mini .icon { width: 13px; height: 13px; }
.pdf-page-foot { display: flex; align-items: center; gap: 6px; padding: 5px 8px; border-top: 1px solid #edf0f4; font-size: var(--fs-xs); }
.pdf-page-n { font-weight: var(--fw-semibold); color: var(--text-primary); }
.pdf-page-src { flex: 1 1 auto; min-width: 0; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Mirror the clockwise icon to read as counter-clockwise (rotate-left). */
.pdf-ico-flip { transform: scaleX(-1); }

/* ── Large single-page zoom preview (overlay) ── */
.pdf-preview-overlay {
  position: fixed; inset: 0; z-index: 1200;
  display: none;                 /* JS sets `flex` when open */
  flex-direction: column;
  background: rgba(8, 20, 40, .82);
}
.pdf-preview-bar { flex: 0 0 auto; height: 52px; display: flex; align-items: center; gap: var(--space-3); padding: 0 14px; color: #fff; }
.pdf-preview-title { font-size: var(--fs-md); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pdf-preview-zoom { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.pdf-preview-pct { min-width: 52px; text-align: center; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.pdf-preview-btn {
  width: 34px; height: 34px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255, 255, 255, .25); background: rgba(255, 255, 255, .08); color: #fff;
  border-radius: var(--r); cursor: pointer; font-size: 18px; line-height: 1;
}
.pdf-preview-btn:hover { background: rgba(255, 255, 255, .2); }
.pdf-preview-btn .icon { width: 16px; height: 16px; }
.pdf-preview-close { margin-left: 4px; }
.pdf-preview-stage { flex: 1 1 auto; min-height: 0; overflow: auto; display: flex; padding: 16px; box-sizing: border-box; }
/* margin:auto centers the canvas when it fits and scrolls (no clip) when it overflows. */
#pdf-preview-canvas { margin: auto; background: #fff; box-shadow: 0 6px 30px rgba(0, 0, 0, .45); }
.pdf-preview-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 44px; height: 66px; display: flex; align-items: center; justify-content: center;
  border: none; background: rgba(0, 0, 0, .32); color: #fff; cursor: pointer; border-radius: var(--r);
}
.pdf-preview-nav:hover { background: rgba(0, 0, 0, .52); }
.pdf-preview-nav:disabled { opacity: .25; cursor: default; }
.pdf-preview-nav .icon { width: 22px; height: 22px; }
.pdf-preview-prev { left: 14px; }
.pdf-preview-next { right: 14px; }

/* File-to-matter modal */
.pdf-field { display: flex; flex-direction: column; gap: 4px; }
/* .pdf-field's display would override the UA [hidden] rule, so toggled fields
   (watermark image block under Text, position block under Tile) need this. */
.pdf-field[hidden] { display: none; }
.pdf-field-label { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-primary); }
.pdf-wm-opt { display: flex; align-items: center; gap: 6px; font-size: var(--fs-base); color: var(--text-primary); cursor: pointer; }
.pdf-wm-lbl { display: flex; justify-content: space-between; align-items: baseline; }
.pdf-wm-pct { font-weight: 400; color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.pdf-wm-range { width: 100%; accent-color: var(--text-link); }
/* Live watermark preview overlaid on each page thumbnail. Rendered as an inline SVG
   whose viewBox = the page's display points, so it mirrors the bake (text / image,
   single or tiled, rotation + opacity) and letterboxes to match the contained thumb. */
.pdf-wm-ov { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.pdf-wm-ov svg { width: 100%; height: 100%; display: block; }
/* Watermark dialog: Acrobat-style distance-from-reference position controls. */
.pdf-wm-pos2 { display: grid; grid-template-columns: auto minmax(56px, 1fr) auto minmax(96px, 128px); gap: 8px; align-items: center; }
.pdf-wm-pos2 > .lbl { font-size: var(--fs-sm); color: var(--text-secondary); white-space: nowrap; }
.pdf-wm-pos2 > .from { font-size: var(--fs-sm); color: var(--text-secondary); text-align: center; }
.pdf-wm-pos2 > .span2 { grid-column: 2 / -1; }
/* Watermark dialog: multi-line text + image-upload controls. */
textarea.pdf-wm-textarea { height: auto; min-height: 56px; padding: 8px 10px; line-height: 1.3; resize: vertical; font: inherit; }
.pdf-wm-img-row { display: flex; align-items: center; gap: var(--space-3); }
.pdf-wm-img-prev { display: flex; align-items: center; gap: 8px; }
.pdf-wm-img-thumb { max-width: 80px; max-height: 48px; border: 1px solid #dde3ea; border-radius: 4px; background: #fff; }
.pdf-wm-img-empty { font-size: var(--fs-sm); color: var(--text-secondary); }
.pdf-help { font-size: var(--fs-sm); color: var(--text-secondary); margin-top: 4px; }
/* Fill form fields (AcroForm) modal. */
.pdf-form-list { max-height: 46vh; overflow-y: auto; border: 1px solid #e3e8ee; border-radius: var(--r); padding: 10px; display: flex; flex-direction: column; gap: 10px; }
.pdf-form-row { display: grid; grid-template-columns: 190px 1fr; gap: 10px; align-items: center; }
.pdf-form-lbl { font-size: var(--fs-sm); color: var(--text-primary); overflow-wrap: anywhere; }
.pdf-form-radios { display: flex; flex-wrap: wrap; gap: 10px; }
.pdf-form-radio, .pdf-form-check { display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-sm); color: var(--text-primary); }
.pdf-form-na { font-size: var(--fs-sm); color: var(--text-secondary); }
.pdf-form-empty { font-size: var(--fs-sm); color: var(--text-secondary); padding: 6px; }
/* Annotation editor (Note / File / Text box / Callout) in the large preview. */
.pdf-annot-tools { display: flex; align-items: center; gap: 4px; margin: 0 auto 0 16px; }
.pdf-annot-tool { width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid rgba(255, 255, 255, .28); background: transparent; border-radius: 6px; cursor: pointer; padding: 0; }
.pdf-annot-tool .icon { width: 18px; height: 18px; color: #fff; }
.pdf-annot-tool:hover { background: rgba(255, 255, 255, .14); }
.pdf-annot-tool.active { background: #fff; border-color: #fff; }
.pdf-annot-tool.active .icon { color: #0067D1; }
.pdf-annot-color { width: 30px; height: 30px; padding: 0; border: 1px solid rgba(255, 255, 255, .28); border-radius: 6px; background: transparent; cursor: pointer; }
.pdf-annot-size { width: 54px; height: 30px; border: 1px solid rgba(255, 255, 255, .28); border-radius: 6px; background: rgba(255, 255, 255, .1); color: #fff; padding: 0 6px; }
.pdf-annot-hint { color: #cdd7e5; font-size: 12px; margin-left: 6px; white-space: nowrap; }
.pdf-annot-wrap { position: relative; display: inline-block; line-height: 0; }
.pdf-annot-layer { position: absolute; inset: 0; }
.pdf-annot-layer.placing { cursor: crosshair; }
.pdf-annot-lines { position: absolute; inset: 0; pointer-events: none; overflow: visible; }
.pdf-annot-live { position: absolute; inset: 0; pointer-events: none; overflow: visible; }
.pdf-annot { position: absolute; box-sizing: border-box; }
.pdf-annot-pin { width: 22px; height: 22px; }
.pdf-annot-pin .pdf-annot-grip { position: absolute; left: 0; top: 0; width: 22px; height: 22px; cursor: move; }
.pdf-annot-pin .pdf-annot-grip .icon { width: 22px; height: 22px; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .45)); }
.pdf-annot-pin.sel .pdf-annot-grip { outline: 2px solid #fff; outline-offset: 1px; border-radius: 3px; }
.pdf-annot-fname { position: absolute; left: 26px; top: 1px; white-space: nowrap; font-size: 11px; background: #fff; border: 1px solid #dde3ea; border-radius: 4px; padding: 1px 5px; max-width: 170px; overflow: hidden; text-overflow: ellipsis; color: var(--text-primary); }
.pdf-annot-pin.sel .pdf-annot-fname { left: 38px; }   /* clear the delete button */
.pdf-annot-note-ta { position: absolute; left: 0; top: 26px; width: 184px; height: 72px; }
.pdf-annot-box { background: rgba(255, 255, 255, .94); border: 1.5px solid; border-radius: 2px; }
.pdf-annot-box.sel { outline: 2px solid var(--text-link); outline-offset: 1px; }
.pdf-annot-ta { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; }
.pdf-annot-box .pdf-annot-ta { position: absolute; inset: 0; width: 100%; height: 100%; box-sizing: border-box; border: 0; background: transparent; resize: none; padding: 4px; color: #000; line-height: 1.2; outline: none; }
.pdf-annot-note-ta { box-sizing: border-box; border: 1px solid #c9a800; background: #fffbe6; border-radius: 3px; padding: 5px; font-size: 12px; resize: none; box-shadow: 0 2px 8px rgba(0, 0, 0, .25); }
.pdf-annot-box .pdf-annot-grip { position: absolute; left: 0; right: 0; top: 0; height: 12px; cursor: move; }
.pdf-annot-del { position: absolute; right: -9px; top: -9px; width: 18px; height: 18px; border-radius: 50%; border: 0; background: #d62828; color: #fff; cursor: pointer; font-size: 13px; line-height: 1; display: flex; align-items: center; justify-content: center; padding: 0; z-index: 3; }
.pdf-annot-resize { position: absolute; right: -4px; bottom: -4px; width: 12px; height: 12px; background: #fff; border: 1.5px solid var(--text-link); border-radius: 2px; cursor: nwse-resize; z-index: 3; }
.pdf-annot-target { position: absolute; width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%; border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .45); cursor: move; }
.pdf-annot-badge { position: absolute; top: 4px; left: 4px; background: #0067D1; color: #fff; font-size: 10px; min-width: 15px; height: 15px; padding: 0 3px; border-radius: 8px; display: flex; align-items: center; justify-content: center; z-index: 2; box-sizing: border-box; }

/* Image-to-PDF editor (scan tool) */
.img-ed-stage { position: relative; margin: 0 auto; background: var(--page-bg); border: 1px solid #dde3ea; border-radius: var(--r); overflow: hidden; touch-action: none; }
.img-ed-stage > svg { position: absolute; inset: 0; pointer-events: none; }
.img-ed-handles { position: absolute; inset: 0; }
.img-ed-handle { position: absolute; width: 18px; height: 18px; margin: -9px 0 0 -9px; border-radius: 50%; background: #fff; border: 2px solid var(--text-link); box-shadow: 0 1px 3px rgba(0, 0, 0, .35); cursor: grab; touch-action: none; }
.img-ed-handle:active { cursor: grabbing; }
.img-mesh-handle { position: absolute; width: 13px; height: 13px; margin: -7px 0 0 -7px; border-radius: 50%; background: var(--text-link); border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0, 0, 0, .3); cursor: grab; touch-action: none; z-index: 2; }
.img-mesh-handle:active { cursor: grabbing; }
#img-mesh-grid path { stroke: rgba(0, 103, 209, .55); stroke-width: 1; }
.img-ed-row { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.img-ed-filters { display: inline-flex; border: 1px solid #dde3ea; border-radius: var(--r); overflow: hidden; }
.img-ed-filter { border: 0; border-left: 1px solid #dde3ea; background: #fff; padding: 6px 10px; font-size: var(--fs-sm); cursor: pointer; color: var(--text-secondary); }
.img-ed-filter:first-child { border-left: 0; }
.img-ed-filter.active { background: var(--blue-light); color: var(--text-link); font-weight: var(--fw-semibold); }
.img-ed-shadow { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-sm); color: var(--text-secondary); cursor: pointer; white-space: nowrap; }
.img-ed-shadow input { cursor: pointer; }
.img-ed-top { display: flex; gap: var(--space-3); align-items: flex-start; }
.img-ed-side { width: 200px; flex: 0 0 auto; display: flex; flex-direction: column; }
.img-ed-side-label { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-secondary); margin-bottom: 6px; }
.img-ed-side > canvas { border: 1px solid #dde3ea; border-radius: var(--r); background: #fff; max-width: 100%; }
.pdf-modal-input { width: 100%; height: 34px; padding: 0 10px; box-sizing: border-box; border: 1px solid #dde3ea; border-radius: var(--r); font-size: var(--fs-base); color: var(--text-primary); background: #fff; }
.pdf-modal-input:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 2px #a2d7ff; }
.pdf-matter-results { display: flex; flex-direction: column; max-height: 240px; overflow: auto; }
.pdf-matter-results:not(:empty) { border: 1px solid #dde3ea; border-radius: var(--r); margin-top: 4px; }
.pdf-matter-row { display: flex; flex-direction: column; gap: 1px; align-items: flex-start; text-align: left; width: 100%; padding: 7px 10px; border: 0; border-top: 1px solid #f0f2f6; background: #fff; cursor: pointer; }
.pdf-matter-row:first-child { border-top: 0; }
.pdf-matter-row:hover { background: #ebf4ff; }
.pdf-matter-row-main { font-size: var(--fs-base); color: var(--text-primary); font-weight: var(--fw-semibold); }
.pdf-matter-row-closed { font-weight: 400; font-size: var(--fs-xs); color: var(--text-tertiary); text-transform: capitalize; }
.pdf-matter-row-sub { font-size: var(--fs-xs); color: var(--text-secondary); }
.pdf-matter-empty { padding: 10px; font-size: var(--fs-sm); color: var(--text-secondary); }
.pdf-matter-chosen { font-size: var(--fs-sm); color: var(--text-secondary); padding: 8px 10px; background: var(--blue-light); border-radius: var(--r); }

/* ── Content ──
 * The page-header lives at the very top edge-to-edge. Everything else
 * sits inside .tr-page-body which has exactly 16px padding on all four
 * sides — that single declaration controls every gap around the table
 * card, so they can't drift apart. */
#content { flex: 1; padding: 0; box-sizing: border-box; }
/* Padding asymmetric to compensate for the bottom/right shrinking the
 * user observes (box-shadow + scrollbar visual eating ~1px right, ~3px
 * bottom in DevTools measurement). Goal: 16 DevTools px gap on every
 * side at the user's 125% scaling. */
.tr-page-body { padding: var(--space-3); }

/* Tax-returns / Contacts views: viewport-lock the layout so the PAGE never
 * scrolls — all row overflow scrolls INSIDE the card's table-container and the
 * footer stays pinned/visible at all times. The card itself hugs its content
 * (#tr-card flex: 0 1 auto, below): few rows = a short card, many rows = the
 * card fills the viewport and the table scrolls internally. The :has() gate
 * scopes this to these two pages. */
/* :not(.is-embedded) keeps this viewport-lock on the STANDALONE pages only
 * (/tax-returns + Transcripts, which use a bare .tr-page-body). The contact-
 * detail Tax Returns tab embeds a .tr-page-body.is-embedded inside .contact-
 * page; that page scrolls as a whole (like the Forms tab), so it must NOT pick
 * up the height-lock + overflow:hidden here, which would clip the contact page. */
body:has(.tr-page-body:not(.is-embedded)) #main {
  height: calc(100vh - var(--topbar-h));
  min-height: 0;
  width: calc(100% - var(--sidebar-width));
  min-width: 0;
  overflow: hidden;
}
body:has(.tr-page-body:not(.is-embedded)) #sidebar.collapsed ~ #main {
  width: calc(100% - var(--sidebar-width-collapsed));
}
body:has(.tr-page-body:not(.is-embedded)) #content {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  height: 100%;
  overflow: hidden;
}
/* Contact-detail page (renders into .contact-page) gets the same #main
 * width pinning so it flexes correctly when the sidebar collapses and
 * doesn't get squeezed by deep child min-content. Same chain — every
 * level below #main gets min-width: 0 so nothing propagates upward.
 * Note .contact-page doesn't need the viewport-locked height: it scrolls
 * the whole page (no inner table-container managing its own scroll). */
body:has(.contact-page) #main {
  width: calc(100% - var(--sidebar-width));
  min-width: 0;
}
body:has(.contact-page) #sidebar.collapsed ~ #main {
  width: calc(100% - var(--sidebar-width-collapsed));
}
body:has(.contact-page) #content { min-width: 0; }
.contact-page { min-width: 0; }
.contact-body { min-width: 0; }
#contact-tab-content { min-width: 0; }
/* Activities page uses the standard .tr-page-body wrapper (like Tasks/TR), so
   the #main width-pin + viewport-lock + #content min-width:0 come from the
   body:has(.tr-page-body:not(.is-embedded)) rules above; #aa-card /
   #aa-table-container are joined to the shared card/scroller rule groups. */

/* Tools page: same #main width-pinning as the contact page so it fills the
   full main column (otherwise #main collapses toward its content width). It
   scrolls the whole page; no inner viewport lock needed. */
body:has(.tools-page) #main { width: calc(100% - var(--sidebar-width)); min-width: 0; }
body:has(.tools-page) #sidebar.collapsed ~ #main { width: calc(100% - var(--sidebar-width-collapsed)); }
body:has(.tools-page) #content { min-width: 0; }
.tools-page { min-width: 0; }
#tools-tab-content { min-width: 0; }
.tr-page-body {
  flex: 1;
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* Embedded in the contact Tax Returns tab: the standalone viewport-lock is
 * scoped out above, so release the flex/overflow column and let the dashboard
 * flow at natural height - the card grows to fit the rows and the contact page
 * scrolls, matching the Forms tab. .contact-body already adds the 12px gutter,
 * so drop our own padding to avoid doubling the card gaps. */
.tr-page-body.is-embedded {
  padding: 0;
  display: block;
  flex: none;
  overflow: visible;
}
.tr-page-body.is-embedded #tr-card { flex: none; }
.tr-page-body.is-embedded #tr-table-container { flex: none; overflow-y: visible; }
#tr-card,
#contacts-card,
#matters-card,
#trx-card,
#trx-roll-card,
#ac-card,
#aa-card,
#tasks-card,
#cx-card {
  flex: 0 1 auto;          /* hug content (dynamic short card for few rows) but
                              cap at the viewport so the footer never scrolls off */
  display: flex;
  flex-direction: column;
  min-height: 0;
}
/* The Transcripts coverage view is cards, not a scrolling table, so don't let
 * the card hug its (often short) content - fill the viewport so the page
 * doesn't look shrunk. */
#trx-card { flex: 1 1 auto; }
#tr-table-container,
#contacts-table-container,
#matters-table-container,
#trx-table-container,
#ac-body,
#aa-table-container,
#tasks-table-container,
#cx-table-container {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;        /* trx matrix/list scrolls inside the card like the others */
}
/* Page header bar — solid #fffcff strip 56px tall with a #d1d6e0
 * bottom border. Title + actions sit 12px from each edge — the same
 * gutter as the content below, so the title aligns with the cards. */
.page-header {
  display: flex; align-items: center; justify-content: space-between;
  margin: 0;
  gap: var(--space-3);
  background: var(--surface-titlebar) !important;     /* matches .tab-strip / .contact-tabbar; !important defends against any rendering that resolves to a slightly darker tone via cascade */
  border-bottom: 1px solid var(--grey-250);
  height: 56px;
  padding: 0 12px;
}
.page-title { font-size: var(--fs-3xl); font-weight: var(--fw-bold); color: var(--text-primary); letter-spacing: -0.015em; line-height: 1.2; margin: 0; }
.page-title-sub { font-size: var(--fs-md); color: var(--text-secondary); margin-top: 2px; }
.page-actions { display: flex; gap: var(--space-2); align-items: center; }
/* Page-header buttons (Edit-contact, Refresh, etc.) sit at 14px bold
 * text — bigger and heavier than the generic .btn 13px / 500
 * defaults. Direct-child selector leaves .btn-sm nested elsewhere
 * alone. */
.page-actions > .btn { font-size: var(--fs-base); font-weight: var(--fw-semibold); }

/* ============================================================
   COMPONENTS
   ============================================================ */

/* ── Buttons ── */
.btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 0 var(--space-4); height: var(--control-h);
  border-radius: var(--r);
  /* 14px is the firm-wide button size — the toolbar/header buttons already
   * override to this (Clio-matching); the base used to lag at 13px, which is
   * what made Export / modal / Restore-defaults buttons read a hair smaller. */
  font-size: var(--fs-base); font-weight: var(--fw-semibold);
  border: 1px solid transparent;
  transition: all var(--t);
  white-space: nowrap;
  line-height: 1;
}
/* Refresh button measured at 52px in Clio DevTools at 125% scaling → 40px CSS.
 * Use the same border-radius as the search box / filter strips. Text is
 * bold and the icon path is stroked thicker to match. */
#tr-refresh-btn { height: var(--control-h); padding: 0 18px; font-weight: var(--fw-semibold); border-radius: var(--r); }
#tr-refresh-btn .icon { width: 18px; height: 18px; }
/* Columns button — 111×40 CSS per spec, with {label + caret} centered
 * horizontally and the label slightly larger than the default .btn 13px
 * so it reads with the toolbar search input. */
/* Columns button — used by both Tax Returns and Contacts pages.
 * Class selector so a single rule covers #tr-columns-btn and
 * #contacts-columns-btn (and any future filter card). */
.tr-columns-btn {
  height: var(--control-h);
  border-radius: var(--r);
  justify-content: center;
  gap: var(--space-1);
  /* Auto-width + 8px right padding puts the caret 8px from the right edge, like
   * every dropdown box (was a fixed 106px centered -> caret ~17px from the edge). */
  padding: 0 8px 0 var(--space-3);
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  /* Uniform 8px .tr-filter-row gap handles spacing now (was -4px). */
  margin-left: 0;
}
.btn svg,
.btn .icon { width: 18px; height: 18px; flex-shrink: 0; }
/* Icons inside text buttons are 18px (the .btn .icon default) - the Export
 * download glyph included. */
.btn .icon-export { width: 18px; height: 18px; }
/* Icon-only toolbar buttons (PDF tools rotate/delete): tighter padding so they
   read as compact square buttons and the whole toolbar fits one row. */
.pdf-iconbtn { padding: 0 9px; }
.tr-columns-btn svg,
.tr-columns-btn .icon { width: 12px; height: 12px; }
.btn-primary   { background: var(--blue); color: #fff; border-color: var(--blue); }
.btn-primary:hover { background: var(--blue-hover); border-color: var(--blue-hover); }
.btn-secondary { background: #fff; color: var(--text-primary); border: 1px solid var(--border-control); }
.btn-secondary:hover { background: var(--page-bg); border-color: var(--border-control); }
.btn-danger    { background: var(--danger); color: #fff; border-color: var(--danger); }
.btn-danger:hover { background: #c82333; }
.btn-ghost     { background: transparent; color: var(--text-link); border-color: transparent; }
.btn-ghost:hover { background: var(--blue-light); }
.btn-sm { padding: var(--space-1) var(--space-3); height: var(--control-h-sm); font-size: var(--fs-smd); }
.btn-lg { padding: 9px 20px; height: var(--control-h); font-size: var(--fs-base); }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* ── Documents toolbar buttons ──
 * Bespoke geometry lifted out of inline styles in app.js so it's tunable
 * here in one place. Placed after .tr-columns-btn so .docs-actions-btn wins
 * the (equal-specificity) width/height overrides by source order. */
.docs-new-btn {
  justify-content: center;
  font-weight: var(--fw-bold);
  /* 8px right padding -> caret 8px from the right edge, like a dropdown. */
  padding: 0 8px 0 var(--space-3);
  gap: var(--space-1);
  /* No negative pull: the uniform 8px .tr-filter-row gap already sets the
   * search->Columns and Columns->next distances. */
  margin-left: 0;
}
/* The New caret is a dropdown chevron - 12px, not the 18px .btn text-button icon. */
.docs-new-btn .icon { width: 12px; height: 12px; }
.docs-trash-btn {
  height: var(--control-h);
  padding: 0 var(--space-4);
  font-weight: var(--fw-bold);
  position: relative;
  left: 4px;
  background: #b90022;        /* destructive red — distinct from --danger */
  border-color: #b90022;
  color: #fff;
}
.docs-actions-btn {
  margin-left: -3px;
  width: 98px;
  min-width: 98px;
  height: 41px;
}

/* ── Cards ── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
}
.card-header {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--divider);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3);
}
.card-header h2 { font-size: var(--fs-lg); font-weight: var(--fw-semibold); }
.card-body { padding: var(--space-5); }
.card-section {
  border-bottom: 1px solid var(--divider);
  padding: var(--space-4) var(--space-5);
}
.card-section:last-child { border-bottom: none; }
.card-section-title {
  font-size: var(--fs-md); font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  display: flex; align-items: center; gap: var(--space-2);
  margin-bottom: 12px;
}
.card-section-title svg { width: 14px; height: 14px; }

/* ── Tables ── */
.table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.table-toolbar {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--divider);
  flex-wrap: wrap;
}
.table-filter-tabs { display: flex; gap: 0; }
.filter-tab {
  padding: 5px 14px; font-size: var(--fs-md);
  border: 1px solid var(--border-strong);
  background: var(--surface); color: var(--text-secondary);
  cursor: pointer; transition: all var(--t);
  white-space: nowrap;
}
.filter-tab:first-child { border-radius: var(--r) 0 0 var(--r); }
.filter-tab:last-child  { border-radius: 0 var(--r) var(--r) 0; border-left: none; }
.filter-tab:not(:first-child):not(:last-child) { border-left: none; }
.filter-tab.active { background: var(--blue-light); color: var(--blue); border-color: var(--blue); font-weight: var(--fw-medium); z-index: 1; }
.table-search {
  display: flex; align-items: center; gap: var(--space-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  padding: 0 var(--space-3); height: 30px; min-width: 200px;
  background: var(--surface);
}
.table-search svg { width: 13px; height: 13px; color: var(--text-tertiary); flex-shrink: 0; }
.table-search input {
  border: none; outline: none; background: transparent;
  font-size: var(--fs-md); color: var(--text-primary); flex: 1; min-width: 0;
}
.table-search input::placeholder { color: var(--text-tertiary); }
.table-actions-right { margin-left: auto; display: flex; gap: var(--space-2); }
.table-scroll { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
thead th {
  background: #F8F9FA;
  padding: 9px 12px;
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  user-select: none;
}
thead th.sortable { cursor: pointer; }
thead th.sortable:hover { color: var(--text-primary); }
thead th.sorted { color: var(--blue); }
.sort-icon { display: inline-block; margin-left: var(--space-1); font-size: var(--fs-2xs); opacity: 0.5; }
thead th.sorted .sort-icon { opacity: 1; color: var(--blue); }
tbody tr { border-bottom: 1px solid var(--divider); transition: background var(--t); }
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: var(--surface-hover); }
tbody td { padding: 10px 12px; font-size: var(--fs-base); vertical-align: middle; }
tbody td.td-check { width: 40px; }
tbody td.td-actions { width: 140px; white-space: nowrap; }
.table-footer {
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--divider);
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--fs-smd); color: var(--text-secondary);
}
.table-footer-right { margin-left: auto; display: flex; align-items: center; gap: var(--space-2); }
.pagination-btn {
  width: 28px; height: 28px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--text-secondary);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; font-size: var(--fs-sm);
  transition: all var(--t);
}
.pagination-btn:hover:not(:disabled) { background: var(--page-bg); color: var(--text-primary); }
.pagination-btn:disabled { opacity: 0.4; cursor: not-allowed; }
select.per-page {
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  padding: 3px 6px; font-size: var(--fs-smd);
  color: var(--text-primary); background: var(--surface);
  outline: none;
}
.expand-rows-toggle {
  display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-smd); cursor: pointer;
}

/* ── Status Badges ── */
.badge {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 3px 8px; border-radius: 4px;
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
  white-space: nowrap;
}
.badge::before { content:''; width:6px; height:6px; border-radius:50%; flex-shrink:0; }
.badge-pending   { background:var(--status-pending-bg);  color:var(--status-pending-text);  border:1px solid var(--status-pending-border); }
.badge-pending::before   { background:var(--status-pending-text); }
.badge-progress  { background:var(--status-progress-bg); color:var(--status-progress-text); border:1px solid var(--status-progress-border); }
.badge-progress::before  { background:var(--status-progress-text); }
.badge-done      { background:var(--status-done-bg);     color:var(--status-done-text);     border:1px solid var(--status-done-border); }
.badge-done::before      { background:var(--status-done-text); }
.badge-overdue   { background:var(--status-overdue-bg);  color:var(--status-overdue-text);  border:1px solid var(--status-overdue-border); }
.badge-overdue::before   { background:var(--status-overdue-text); }
.badge-high      { background:var(--status-high-bg);     color:var(--status-high-text); }
.badge-high::before      { background:var(--status-high-text); }
.badge-open  { background:#CCE5FF; color:#004085; border:1px solid #99CAFF; }
.badge-open::before  { background:#004085; }
.badge-closed { background:#E2E3E5; color:#383D41; border:1px solid #D6D8DB; }
.badge-closed::before { background:#383D41; }
.badge-client { background:#CCE5FF; color:#004085; font-size:var(--fs-xs); padding:var(--space-1) var(--space-2); }
.badge-external { background:#D4EDDA; color:#155724; font-size:var(--fs-xs); padding:var(--space-1) var(--space-2); }

/* ── Action buttons in table ── */
.actions-btn {
  display: inline-flex; align-items: center; gap: 0;
  font-size: var(--fs-md);
}
.actions-btn .act-main {
  padding: var(--space-1) var(--space-3); height: 28px;
  border: 1px solid var(--border-strong);
  border-right: none;
  border-radius: var(--r) 0 0 var(--r);
  background: var(--surface); color: var(--text-primary);
  font-size: var(--fs-smd); font-weight: var(--fw-medium);
  cursor: pointer; transition: background var(--t);
}
.actions-btn .act-main:hover { background: var(--page-bg); }
.actions-btn .act-drop {
  width: 26px; height: 28px;
  border: 1px solid var(--border-strong);
  border-radius: 0 var(--r) var(--r) 0;
  background: var(--surface); color: var(--text-secondary);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; transition: background var(--t);
  font-size: var(--fs-xs);
}
.actions-btn .act-drop:hover { background: var(--page-bg); }

/* ── Tabs ── */
.tabs {
  display: flex; gap: 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 20px;
}
.tab {
  /* Native <button> reset — without this, browsers render the gray
   * "buttonface" background + 2px outset border around each tab cell,
   * which leaks through wherever .tab is applied to a <button>. */
  background: transparent;
  border: 0;
  font-family: inherit;

  /* Active-state foundation. .tab.active flips the border-bottom
   * color to var(--blue) for the underline indicator. */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;

  padding: var(--space-3) var(--space-4);
  font-size: var(--fs-base); color: var(--text-secondary);
  cursor: pointer; transition: all var(--t);
  white-space: nowrap;
}
.tab:hover { color: var(--text-primary); }
.tab.active { color: var(--blue); border-bottom-color: var(--blue); font-weight: var(--fw-medium); }

/* ── Forms ── */
.form-page-bg { background: var(--page-bg); }
.form-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 28px 32px;
  max-width: 680px;
  margin: 0 auto 20px;
  box-shadow: var(--shadow-sm);
}
.form-section { margin-bottom: 28px; }

/* ── Edit contact (full-page editor) ── repeating rows reuse .form-group/.input;
 * the row is a flexbox of a flexible main cell + fixed type/primary/remove.
 * DIMENSION CANON (DevTools-anchored): Clio's First name field computes
 * 230.58 x 39.99 in their DevTools; the user's tool numbers (77/261/169,
 * card 1138) carry a uniform x1.1319 (= 261 / 230.58). Corrected: prefix 68,
 * standard fields 230.58, big fields 310.58 (= 68 + 12 + 230.58, "ends where
 * First name ends"), Type 149, control height 40; card 998.25 (their th-card-content computes 998.25 = the 1138 reading / 1.14). Clio's layout
 * is FLUID (hence the fraction) - these pin the user's current window.
 * width+auto margins keep the column centered. */
/* Same full-width #main treatment as the other standalone pages (tr-page-body /
 * contact-page / forms-page-body all carry one): without it #main shrink-wraps
 * to the fixed-width form column, taking the .page-header strip with it. */
body:has(.ec-form) #main { width: calc(100% - var(--sidebar-width)); min-width: 0; }
body:has(.ec-form) #sidebar.collapsed ~ #main { width: calc(100% - var(--sidebar-width-collapsed)); }
body:has(.ec-form) #content { min-width: 0; }
/* Flex column with a 12px gap owns ALL the vertical rhythm: 12px between the
 * two cards, and 12px from the header's bottom border to the first card. */
.ec-form { width: 998.25px; max-width: calc(100% - 32px); margin: 0 auto;
  display: flex; flex-direction: column; gap: var(--space-3); padding-top: var(--space-3);
  /* Clearance for the FIXED footer bar (12 + 41 + 12 + 1px hairline ≈ 66)
   * plus the standard 12px gap, so the last card never hides under it. */
  padding-bottom: 80px; }
/* 12px so form fields sit the sitewide 12px from the card edge (was 19px). */
.ec-form .form-card { max-width: none; margin: 0; padding: var(--space-3); }
/* Card titles (Contact information / Custom fields): fs-lg semibold, no
 * separator (the card boundary already divides sections on these pages). */
.ec-form .form-section-title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); border-bottom: 0; padding-bottom: 0; margin-bottom: var(--space-4); }
/* The last field in a card owns no bottom margin - the card padding provides the
 * bottom gap (no double space under the last field, e.g. Client / SOL). */
.ec-form .form-card > :last-child { margin-bottom: 0; }
.ec-form .form-card > .form-row:last-child { margin-bottom: 0; }
/* 8px between fields (the sitewide "between items" gap; composite widths below
 * are recomputed for it). */
.ec-form .form-row { gap: var(--space-2); }
/* Standard form-control height on this page follows the app-wide 41px (see
 * .input); rows of repeated info have NO dividers between them. */
/* Clio's exact field widths (user-supplied): Prefix 77, First/Middle/Last
 * 261 each. The Company/Title/DOB row uses the SAME tracks with Company
 * spanning the first two (77 + 12 + 261 = 350 - "ends where First name
 * ends"), so both rows' field edges line up exactly like Clio's. Fixed
 * tracks left-pack the row with whitespace after, like Clio. */
.form-row.ec-name-row,
.form-row.ec-company-row { grid-template-columns: 68px 230.58px 230.58px 230.58px; justify-content: start; }
.ec-company-row > .form-group:first-child { grid-column: span 2; }
/* Company name (company mode): ends where the Email row's Type select ends
 * below it (310.58 field + 12 gap + 90 Type). */
#ec-company-fields .form-group { max-width: 404.58px; }
/* The MslDp input picks up the Forms panel's 41px; match the 34px .input
 * height its row neighbors use. */
/* One card for all regular fields: subsections (Email / Phone / Website /
 * Address / Tags) divided by separator lines; the subtitle repeats the
 * section-title look without its underline (the separator does that job). */
/* Sub-section within a form card (Email / Phone / Address / ...): a full-width
 * separator + a compact fs-md semibold header, matching the Form 2848 sections. */
.ec-subsection { border-top: 1px solid var(--divider); margin-top: var(--space-5); padding-top: var(--space-5); }
.ec-subtitle { font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--text-primary); margin-bottom: var(--space-3); }
/* Field labels read at full strength here (same override the compose modal
 * uses) - the generic .form-group label is the muted secondary. */
.ec-form .form-group label { color: var(--text-primary); }
/* Deceased marker: a checkbox on the Date-of-birth label reveals an inline Date
 * of death picker (below DOB) + a "(deceased)" hint under the last name. The
 * suffix itself is written into the last name on Save (the firm's convention). */
/* Center-align (not baseline): baseline aligned the checkbox bottom to the label
   and pushed the "Deceased" text up. Centering keeps the text on the label's line. */
.ec-dob-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ec-dob-labeltext { font-size: var(--fs-md); font-weight: var(--fw-medium); color: var(--text-primary); }
.ec-form .ec-dob-label .ec-deceased-toggle {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-sm); font-weight: 400; color: var(--text-primary);
  cursor: pointer; white-space: nowrap;
}
.ec-dob-label .ec-deceased-toggle input { width: 14px; height: 14px; margin: 0; cursor: pointer; }
.ec-dod-wrap { margin-top: 10px; }

/* Read-only contact-reference custom fields (Spouse, Decedent): a name link
 * sized to line up with the 34px inputs beside it. */
.ec-cf-ref { min-height: 40px; display: flex; align-items: center; }
/* The global .msl-dp-input is 41px (the 2848 Forms panel's height); this page's
 * standard is Clio's 40 - keep the DOB flush with its row neighbors. */
.ec-form .msl-dp-input { height: 40px; }
/* SOL "satisfied" checkbox: the empty spacer label matches the date field's
   label height so the checkbox lines up UNDER the date input, and the 40px row
   height centers it against that input. */
.em-label-spacer { display: block; }
.em-check {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  font-weight: 400;
  color: var(--text-primary);
  cursor: pointer;
}
.em-check input { margin: 0; width: 16px; height: 16px; cursor: pointer; }
/* SOL row is a thirds grid so the date field matches Open date's 1/3 width;
   the checkbox cell spans the other two columns for its label. */
.em-sol-check-cell { grid-column: span 2; }
/* Shown in place of the "satisfied" checkbox when a matter has no SOL yet -
   Clio's API can't create a first one, so new-SOL entry is disabled + explained. */
.em-sol-hint { color: var(--text-slate); font-weight: 400; cursor: default; }
/* "Satisfied" badge next to the SOL date on the matter-detail Details card. */
.matter-sol-satisfied {
  display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 10px;
  background: #e6f4ea; color: #067647; font-size: 12px; font-weight: 600;
}
/* FIXED action bar pinned to the viewport bottom - always visible while the
 * tall form scrolls (Clio's edit-contact footer behavior). Fixed (not sticky)
 * so it can't unstick with scroll-container quirks; .ec-form carries matching
 * bottom padding so content never hides under it. Spans from the sidebar's
 * right edge to the viewport edge; the inner wrapper matches the card column
 * so the buttons line up with the cards' left edge. */
.ec-footer { position: fixed; left: var(--sidebar-width); right: 0; bottom: 0; z-index: 40;
  background: var(--surface-titlebar); border-top: 1px solid var(--grey-250); }
#sidebar.collapsed ~ #main .ec-footer { left: var(--sidebar-width-collapsed); }
.ec-footer-inner { width: 998.25px; max-width: calc(100% - 32px); margin: 0 auto;
  padding: var(--space-3) 0; display: flex; gap: var(--space-2); align-items: center; }
/* Delete contact sits at the far right, opposite Save/Cancel. */
.ec-delete-btn { margin-left: auto; }
/* Toggle matches the 41px field height; a hairline under the toggle block
 * separates it from the name fields (Clio parity). */
.ec-typetoggle { display: inline-flex; gap: 0; height: 40px; align-items: stretch; margin-bottom: var(--space-4); border: 1px solid var(--border-strong); border-radius: var(--r); overflow: hidden; }
.ec-type-btn { display: inline-flex; align-items: center; gap: var(--space-2); padding: 0 20px; border: 0; background: var(--surface); color: var(--text-secondary); font: inherit; font-weight: var(--fw-medium); cursor: pointer; }
.ec-type-ico { width: 18px; height: 18px; flex: 0 0 auto; }
/* Fixed brand colours (Contacts-page palette) regardless of active/inactive state. */
.ec-type-ico--person  { color: rgb(194, 78, 0); }
.ec-type-ico--company { color: rgb(117, 55, 217); }
#ec-person-fields, #ec-company-fields { border-top: 1px solid var(--divider); padding-top: 20px; }
.ec-type-btn + .ec-type-btn { border-left: 1px solid var(--border-strong); }
.ec-type-btn.is-active { background: var(--blue-light, #ebf4ff); color: var(--blue); }
.ec-row { display: flex; align-items: flex-end; gap: var(--space-2); padding: var(--space-2) 0; }
.ec-cell-main { flex: 1 1 auto; min-width: 0; }
/* Email/phone/website inputs end where First name ends (350 = 77 + 12 + 261),
 * left-packed with whitespace after like Clio. Custom-field controls (incl.
 * Spouse and the Add picker) share the same 350 so they all match. Type
 * dropdowns are Clio's 169. */
#ec-emails .ec-cell-main, #ec-phones .ec-cell-main, #ec-websites .ec-cell-main { flex: 0 0 306.58px; }
.ec-cell-type { flex: 0 0 90px; }   /* match the Address row's compact Type select */
.ec-primary { display: flex; align-items: center; gap: 6px; white-space: nowrap; height: 40px; font-size: var(--fs-base); color: var(--text-primary); }
.ec-primary input { width: auto; }
.ec-remove { flex: 0 0 auto; height: 40px; padding: 0 8px; border: 0; background: transparent; color: var(--text-link); font: inherit; cursor: pointer; }
.ec-remove:hover { text-decoration: underline; }
/* A section's ONLY entry can't be removed - Remove appears from the second
 * entry on (clearing the value is how you empty a sole entry). */
#ec-emails .ec-row:only-child .ec-remove,
#ec-phones .ec-row:only-child .ec-remove,
#ec-websites .ec-row:only-child .ec-remove,
#ec-addresses .ec-addr-row:only-child .ec-addr-remove { display: none; }
/* Block-level (fit-content) rather than inline-flex: inline-flex sat on a line
 * box whose leading added ~3px below the button, making the gap ABOVE a
 * following separator larger than the gap below it. Block-level removes that so
 * the separator spacing is symmetric. */
.ec-add { display: flex; width: fit-content; align-items: center; gap: 6px; margin-top: var(--space-3); padding: 0; border: 0; background: transparent; color: var(--text-link); font: inherit; font-weight: var(--fw-medium); cursor: pointer; }
.ec-add svg { flex: 0 0 auto; display: block; }
.ec-add:hover { text-decoration: underline; }
/* Address rows: the tall Street textarea (350, ends where First name ends)
 * sits beside a 2-ROW grid laid out like Clio's - City + State (261 each) on
 * the first row, Zip + Country (261) + Type (169) on the second. */
.ec-addr-row { align-items: flex-start; position: relative; }
.ec-addr-street { flex: 0 0 306.58px; align-self: stretch; position: relative; }
.ec-addr-street .textarea-input { flex: 1 1 auto; }
.ec-addr-grid { flex: 1 1 auto; display: grid; grid-template-columns: 230.58px 230.58px 149px;
  /* 8px column/row gap (matches the sitewide field gap). */
  grid-template-areas: "city state ." "zip country type"; justify-content: start;
  gap: var(--space-2); min-width: 0; }
/* Rows that carry a County cell use a 4-column layout. Row 2 fills the FULL
 * grid width to the card's right edge - Zip | County | Country | Type (County
 * between Zip and Country) - so Zip gets a real column (label on one line,
 * value not truncated). Row 1 is City (2 cols) + State (1 col); the 4th column
 * is left empty on row 1 so the absolute Remove button lands there, clear of
 * State. Row 2 extends further right than State by design (it runs under the
 * Remove button, which is a row-1-height element). Rows without the Clio county
 * fields keep the original 3-column layout. */
.ec-addr-row.has-county .ec-addr-grid {
  grid-template-columns: 115px 200px 194px 90px;   /* shorter Zip, wider County (long county names) */
  grid-template-areas: "city city state ." "zip county country type"; }
.ec-g-city { grid-area: city; } .ec-g-state { grid-area: state; } .ec-g-zip { grid-area: zip; }
.ec-g-country { grid-area: country; } .ec-g-type { grid-area: type; } .ec-g-county { grid-area: county; }
/* Remove sits at the row's right, near the top of the address entry (per the
 * user's placement). It lands in the empty space right of the grid, clear of
 * the Type column. */
.ec-addr-remove { position: absolute; top: 36px; right: 0; }
/* ── Edit-contact address: Lob autocomplete dropdown + Verify affordance ──
 * Shown only when config.addressAutocomplete (LOB_API_KEY) is set. The list is
 * absolutely positioned inside .ec-addr-street (its position:relative anchor);
 * JS sets `top` to just below the Street textarea. */
.ec-ac-list { position: absolute; left: 0; right: 0; z-index: 30; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r); box-shadow: var(--shadow-popup); }
.ec-ac-item { padding: 7px 11px; cursor: pointer; display: flex; flex-direction: column; gap: 1px; font-size: var(--fs-md); line-height: 1.3; }
.ec-ac-item + .ec-ac-item { border-top: 1px solid var(--divider); }
.ec-ac-item:hover, .ec-ac-item.on { background: var(--surface-hover); }
.ec-ac-sub { color: var(--text-tertiary); font-size: var(--fs-sm); }
.ec-addr-verify { margin-top: 6px; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.ec-addr-verify-btn { padding: 0; border: 0; background: transparent; color: var(--text-link);
  font: inherit; font-size: var(--fs-sm); font-weight: var(--fw-medium); cursor: pointer; }
.ec-addr-verify-btn:hover { text-decoration: underline; }
.ec-addr-verify-msg { font-size: var(--fs-sm); color: var(--text-secondary); }
.ec-addr-verify-msg.ok { color: var(--success); }
.ec-addr-verify-msg.warn { color: var(--danger); }
/* Custom fields: compact 3-column grid - each field is one column (~318px on
 * the 998px form), so the controls read short. Related fields stay adjacent in
 * the flow via ecPairCustomRows. 2 columns on smaller laptops, 1 on mobile. */
#ec-customs { display: grid; grid-template-columns: repeat(3, 1fr); column-gap: var(--space-2); }
#ec-customs .ec-cf-row { grid-column: auto; }
@media (max-width: 1080px) { #ec-customs { grid-template-columns: repeat(2, 1fr); } }
/* Full-width subheader that starts each Dependent group (spans both columns). */
.ec-cf-group-head {
  grid-column: 1 / -1;
  font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--text-primary);
  margin-top: var(--space-4); padding-top: var(--space-3);
  border-top: 1px solid var(--divider);
}
/* Custom-field rows: 6px between the field and its remove-X (tighter than the
 * 12px the email/phone rows use before their Type/Primary/Remove controls). */
#ec-customs .ec-row { gap: 6px; }
.ec-cf-add-group { width: 324px; margin-top: var(--space-3); }   /* one 3-col custom-field column */
/* No custom fields yet -> no extra gap above "Add a custom field" (the card
 * title's margin already spaces it). */
#ec-customs:empty + .ec-cf-add-group { margin-top: 0; }
/* No related rows yet -> drop the button's top margin so it sits close under the
 * "Related contacts" title (matches the Custom fields card's spacing). */
#em-related:empty + .ec-add { margin-top: 0; }
/* First related row (Edit matter): drop its top padding so the Contact field sits
 * the same 16px under the "Related contacts" title as the empty (New) case does. */
#em-related > .ec-row:first-child { padding-top: 0; }
/* Clearable plain text input (e.g. the related-contact Relationship box): an X
 * inside, shown only once text is typed. */
.ec-clearable { position: relative; }
.ec-clearable > .input { padding-right: 30px; }
.ec-clearable-x {
  position: absolute; top: 1px; bottom: 1px; right: 4px; width: 24px;
  display: none; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer; padding: 0;
}
.ec-clearable > .input:not(:placeholder-shown) ~ .ec-clearable-x { display: flex; }
@media (max-width: 1080px) { .ec-cf-add-group { width: 100%; max-width: 480px; } }
/* Combobox (Company / Spouse): Clio's dropdown look. The field fills its cell
 * (so Company = the Email-address field width); the caret AND - for the
 * value-clearing Company field - an inline clear-X both sit INSIDE the field's
 * right edge, like a search box. Spouse omits the inline X (it uses a row-level
 * remove X instead). The results panel anchors to the field wrapper. */
.ec-combo { position: relative; }
.ec-combo-field { position: relative; width: 100%; min-width: 0; }
.ec-combo-field .input { padding-right: 44px; }               /* caret only */
.ec-combo-field.has-clear .input { padding-right: 66px; }     /* clear-X + caret */
/* Inline clear-X: just left of the caret, shown only when a value is set
 * (ecComboToggleClear). Clears the VALUE, keeps the field. */
.ec-combo-clear { position: absolute; top: 1px; bottom: 1px; right: 28px; width: 24px;
  display: inline-flex; align-items: center; justify-content: flex-end;
  border: 0; padding: 0; background: transparent; cursor: pointer; }
/* The explicit display above would otherwise override the hidden attribute the
 * JS sets when the field is empty - nothing to clear, no X. */
.ec-combo-clear[hidden] { display: none; }
/* Company clear-X: shares the 16px bold x-heavy glyph with the custom-field X
 * (inherits size from .ec-x-glyph). No hover recolor - the glyph stays the
 * same weight/colour on hover. */
.ec-combo-caret { position: absolute; top: 1px; right: 1px; bottom: 1px; width: 26px;
  display: flex; align-items: center; justify-content: flex-end; padding-right: 7px;
  background: transparent; border: 0;
  border-radius: 0 calc(var(--r) - 1px) calc(var(--r) - 1px) 0;
  color: var(--text-primary); cursor: pointer; }
.ec-combo-caret .icon { width: 12px; height: 12px; }
/* Plain free-text mode (Mail & Ship "Manual" recipient name): no dropdown caret,
 * so the combo reads as an ordinary text input. */
.ec-combo--plain .ec-combo-caret { display: none; }
/* Clio-style X for INDIVIDUAL fields (every custom-field row + the combobox
 * clear): Clio's DevTools-measured construction - a 40x40 button (flush with
 * the 40px fields, so bottom-aligned rows center it automatically; no offset
 * needed) holding a 16px dark x-bold glyph, with the light-grey rounded hover
 * wash. Whole repeating entries (email/phone/website/address) keep Remove. */
.ec-x { flex: 0 0 auto; width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; padding: 0; background: transparent; border-radius: var(--r); cursor: pointer; }
.ec-x:hover { background: #e4e7ec; }
.ec-x-glyph { width: 16px; height: 16px; background-color: var(--text-primary);
  -webkit-mask: url('/img/icons/x-heavy.svg') center / contain no-repeat;
          mask: url('/img/icons/x-heavy.svg') center / contain no-repeat; }
/* The combobox inline clear-X (Contact/Matter on Forms, Company/Spouse on New
 * contact) matches the list-page search-box clear (.search-clear): the same 10px
 * x-bold glyph in the same muted blue - NOT the 16px dark x-heavy the row-level
 * custom-field remove (.ec-x) uses. */
.ec-combo-clear .ec-x-glyph {
  width: 10px; height: 10px;
  background-color: #385da0;
  -webkit-mask: url('/img/icons/x-bold.svg') center / 250% no-repeat;
          mask: url('/img/icons/x-bold.svg') center / 250% no-repeat;
}
.ec-combo-clear:hover .ec-x-glyph { background-color: #1f3d75; }
.ec-tag { display: inline-flex; align-items: center; gap: 6px; background: var(--grey-150); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 3px 4px 3px 10px; font-size: var(--fs-base); color: var(--text-primary); white-space: nowrap; }
.ec-tag-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.ec-tag-x { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--text-secondary); cursor: pointer; }
.ec-tag-x .icon { width: 13px; height: 13px; }
/* Clio-style combined tag input: pills live INSIDE the field, an inline text
 * input grows to fill the rest, a caret opens the color-dotted dropdown. */
.ec-tagbox { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  min-height: 40px; padding: 5px 38px 5px 8px; background: var(--surface);
  border: 1px solid var(--border-control); border-radius: var(--r); cursor: text; }
.ec-tagbox:focus-within { border-color: var(--blue); box-shadow: 0 0 0 2px #a2d7ff; }
#ec-tagbox-pills { display: contents; }
.ec-tagbox-input { flex: 1 1 80px; min-width: 80px; border: 0; outline: none; background: transparent;
  font: inherit; font-size: var(--fs-base); color: var(--text-primary); padding: 3px 0; }
.ec-tagbox-caret { position: absolute; top: 1px; right: 1px; bottom: 1px; width: 34px;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; color: var(--text-primary); cursor: pointer; }
.ec-tagbox-caret .icon { width: 12px; height: 12px; }
.ec-tagbox-results { position: absolute; left: 0; right: 0; top: calc(100% + 2px); z-index: 40;
  max-height: 260px; overflow-y: auto; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r); box-shadow: var(--shadow-popup); padding: 4px; }
.ec-tagbox-item { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px;
  border: 0; border-radius: var(--r-sm); background: transparent; text-align: left;
  font: inherit; font-size: var(--fs-base); color: var(--text-primary); cursor: pointer; }
.ec-tagbox-item:hover { background: var(--surface-hover); }
.ec-tagbox-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; background: var(--text-tertiary); }
.ec-tagbox-empty { padding: 10px; font-size: var(--fs-sm); color: var(--text-tertiary); }
/* Edit-matter Related-contacts rows: reuse the .ec-row chrome + the ec-* contact
 * combobox. The combobox cell is the standard 306.58px field; the role input is
 * narrower. (Clients: single-client matters use a plain editable combobox in a
 * .form-row; multi-client matters render the read-only list below.) */
.em-related-row .ec-cell-main { flex: 0 0 306.58px; }
.em-related-row .em-rel-desc { flex: 0 0 200px; }
.em-client-ro-row { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) 0; }
@media (max-width: 720px) {
  .ec-row { flex-wrap: wrap; }
  .ec-cell-type { flex-basis: 120px; }
  .ec-addr-street { flex-basis: 100%; }
  /* Stack the address grid (block layout ignores the grid areas) and drop
   * custom fields back to one per row. */
  .ec-addr-grid { display: block; }
  .ec-addr-grid > .form-group { margin-bottom: var(--space-3); }
  #ec-customs { grid-template-columns: 1fr; }
  /* Fixed footer spans the whole viewport once the sidebar is off-canvas. */
  .ec-footer { left: 0; }
  /* Fixed-width fields flex again on narrow screens. */
  #ec-emails .ec-cell-main, #ec-phones .ec-cell-main, #ec-websites .ec-cell-main,
  #ec-customs .ec-cell-main { flex: 1 1 100%; }
  .em-related-row .ec-cell-main, .em-related-row .em-rel-desc { flex: 1 1 100%; }
  .ec-addr-street { flex-basis: 100%; }
  .ec-cf-add-group { width: auto; }
}
/* Form-card title (the card header for New contact / matter / intake / task):
 * fs-lg semibold with a full-width separator, matching the Form 2848 card header. */
.form-section-title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); color: var(--text-primary); margin-bottom: var(--space-4); padding-bottom: var(--space-4); border-bottom: 1px solid var(--divider); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); margin-bottom: var(--space-4); }
.form-row.full { grid-template-columns: 1fr; }
.form-row.thirds { grid-template-columns: 1fr 1fr 1fr; }
.form-row.halves { grid-template-columns: 1fr 1fr; }
.form-group { display: flex; flex-direction: column; gap: var(--space-1); }
.form-group label { font-size: var(--fs-md); font-weight: var(--fw-medium); color: var(--text-primary); }
/* .form-group is display:flex, which defeats the [hidden] attribute's UA
 * display:none (author display wins) - so `el.hidden = true` on a .form-group
 * left it visible sitewide (the Draft Tracking-number field on First-Class was
 * one symptom). Restore hiding. */
.form-group[hidden] { display: none; }
.form-group label .req { color: var(--danger); }
.input, .select-input, .textarea-input {
  /* 40px = Clio's standard control height, DevTools-confirmed (their
   * input wrapper computes 39.99); was 34, briefly 41. */
  padding: 7px 10px; height: 40px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r);
  font-size: var(--fs-base); color: var(--text-primary);
  background: var(--surface);
  outline: none;
  transition: border-color var(--t);
  width: 100%;
}
.textarea-input { height: auto; min-height: 80px; resize: vertical; }

/* ── Rich-text body editor (Letters composer) ── */
.rte-wrap { border: 1px solid var(--border-strong); border-radius: var(--r); overflow: hidden; background: var(--surface); }
.rte-wrap:focus-within { border-color: var(--accent, #0067D1); }
.rte-toolbar { display: flex; align-items: center; gap: 2px; padding: 4px 6px; border-bottom: 1px solid var(--border); background: var(--surface-alt, #f6f8fa); }
.rte-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 28px; height: 26px; padding: 0 6px;
  border: 1px solid transparent; border-radius: 4px; background: transparent;
  cursor: pointer; font-size: 14px; line-height: 1; color: var(--text-secondary);
}
.rte-btn:hover { background: var(--surface); border-color: var(--border-strong); }
.rte-btn .icon { width: 16px; height: 16px; }
.rte-btn .rte-num { font-size: 12px; font-weight: 700; }
.rte-sep { width: 1px; height: 18px; background: var(--border-strong); margin: 0 4px; }
.rte-editor {
  min-height: 180px; max-height: 400px; overflow-y: auto;
  padding: 8px 10px; font-size: var(--fs-base); line-height: 1.5;
  color: var(--text-primary); outline: none;
}
.rte-editor:empty::before { content: attr(data-ph); color: var(--text-tertiary); }
.rte-editor > *:first-child { margin-top: 0; }
.rte-editor p, .rte-editor div { margin: 0 0 8px; }
.rte-editor ul, .rte-editor ol { margin: 0 0 8px 1.5em; padding: 0; }
.rte-editor li { margin: 0 0 2px; }
/* Custom caret on every select — matches the TR/Contacts toolbar dropdowns
 * (caret-down-fill, 12px, 8px from the right edge) so the form pickers are
 * consistent with the rest of the design. */
.select-input {
  appearance: none; -webkit-appearance: none;
  background-image: url('/img/icons/caret-down-fill.svg');
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 12px;
  padding-right: 28px;
}
.input:focus, .select-input:focus, .textarea-input:focus {
  border-color: var(--blue);
  box-shadow: 0 0 0 2px #a2d7ff;
}
.input::placeholder, .textarea-input::placeholder { color: var(--text-tertiary); }
.form-hint { font-size: var(--fs-sm); color: var(--text-tertiary); margin-top: var(--space-1); }

/* ── Matter Communications tab + compose-email modal ─────────────────────────
 * The comms table reuses the standard .card / .tr-scroll / .data-table shell;
 * only the column widths + the subject/preview stack are new. The compose modal
 * reuses .modal / .form-group / .input; the rich-text editor + toolbar are new. */
.ce-modal .form-group { margin-bottom: var(--space-2); }
/* Compose labels read as primary copy (Clio's Send-email modal), not the
 * muted secondary the generic .form-group label uses. */
.ce-modal .form-group label { color: var(--text-primary); }
.ce-static {
  padding: 7px 10px; font-size: var(--fs-base); color: var(--text-primary);
  background: var(--page-bg); border: 1px solid var(--border); border-radius: var(--r);
}
/* Read-only, vertically-resizable detail box (Communications summary/transcript). */
.cx-detail-ta {
  width: 100%; box-sizing: border-box; resize: vertical; max-height: 55vh;
  padding: 7px 10px; font-family: inherit; font-size: var(--fs-base); line-height: 1.5;
  color: var(--text-primary); background: var(--page-bg);
  border: 1px solid var(--border); border-radius: var(--r); white-space: pre-wrap;
}
/* Formatted Ai-recap in the detail popup (bold headings + numbered action list),
   sharing the .cx-detail-ta box; overflow-y makes the resize handle work on a div. */
.cx-recap { white-space: normal; overflow-y: auto; }
.cx-recap .cx-recap-h { font-weight: 600; margin: 8px 0 2px; }
.cx-recap .cx-recap-h:first-child { margin-top: 0; }
.cx-recap p { margin: 0 0 6px; }
.cx-recap p:last-child { margin-bottom: 0; }
.cx-recap ol.cx-recap-list { margin: 2px 0 6px; padding-left: 20px; }
.cx-recap ol.cx-recap-list li { margin: 1px 0; }
/* Row-select checkbox column (Communications tables) - fixed narrow, centered. */
.mc-table .mc-col-check { width: 34px; text-align: center; padding-left: 8px; padding-right: 4px; }
.mc-table .mc-col-check input { cursor: pointer; margin: 0; vertical-align: middle; }
/* Selection actions IN the comms card toolbar - Documents-style: they swap with
   the filter controls when rows are selected, rather than a separate bar. The
   count + Clear-selection styling is inline (commSelActionsHtml) to mirror the
   Documents tab exactly; this only lays them out in a gapped row. */
.comm-sel-actions { display: flex; align-items: center; gap: var(--space-2); }
.comm-sel-actions[hidden] { display: none; }
/* Template picker row + the "Add autofill data" label links. */
.ce-tpl-row { display: flex; gap: var(--space-2); align-items: center; }
.ce-tpl-row .select-input { flex: 1; min-width: 0; }
.ce-label-row { display: flex; align-items: center; gap: var(--space-1); }
.ce-autofill-link { margin-left: auto; font-size: var(--fs-sm); font-weight: var(--fw-medium); }

.ce-toolbar { display: flex; gap: 2px; margin-bottom: -1px; }
.ce-toolbar button {
  min-width: 30px; height: 28px; padding: 0 8px;
  border: 1px solid var(--border-strong); border-bottom: none;
  background: var(--surface); color: var(--text-secondary);
  font-size: var(--fs-md); cursor: pointer; border-radius: var(--r) var(--r) 0 0;
}
.ce-toolbar button:hover { background: var(--page-bg); color: var(--text-primary); }
.ce-editor {
  min-height: 180px; max-height: 340px; overflow-y: auto;
  padding: 10px 12px; border: 1px solid var(--border-strong); border-radius: 0 var(--r) var(--r) var(--r);
  font-size: var(--fs-base); color: var(--text-primary); background: var(--surface);
  line-height: 1.5; outline: none;
}
.ce-editor:focus { border-color: var(--blue); box-shadow: 0 0 0 2px #a2d7ff; }

.mc-table .mc-col-time    { width: 120px; white-space: nowrap; }
.mc-table .mc-col-type    { width: 56px; }
.mc-table td.mc-col-type  { text-align: center; }
.mc-table .mc-col-date    { width: 170px; white-space: nowrap; }
.mc-table .mc-col-from,
.mc-table .mc-col-to      { width: 170px; }
.mc-table .mc-col-duration { width: 90px; white-space: nowrap; }
/* Type icon + date + body text all read in the base color, like Clio.
 * vertical-align:middle lifts the inline-block icon off the text baseline so
 * it truly centers in the row. */
.mc-table td.mc-col-type .icon {
  width: 18px; height: 18px;
  color: var(--text-primary);
  vertical-align: middle;
}
/* Recorded-time cell: blue clock + hours link (or the Add time button). */
.mc-time-link { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.mc-time-link .icon { width: 14px; height: 14px; }
/* Date left, time right within the cell - Clio's justified alignment. */
.mc-table .mc-dt { display: flex; justify-content: space-between; gap: var(--space-3); }
.mc-subject { font-weight: var(--fw-medium); }
/* max-width:0 on the td makes the flexible subject column truncate its preview
 * at the CELL edge instead of a fixed ch budget - full width when there's room. */
.mc-table td.mc-col-subject { max-width: 0; }
.mc-preview {
  font-size: var(--fs-sm); color: var(--text-primary); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Date-range filter (toolbar): two generic Clio-style datepickers (the same
 * .msl-dp-* component the 2848 Signature date uses) + a quick-preset select.
 * Sized to the TOOLBAR chrome, not the form chrome: every control on this row
 * is var(--control-h) tall with the slate --border-control border and --r
 * radius (tab strip, search box, Columns/New buttons) - the base .msl-dp-input
 * / .select-input are 41px/34px form fields, so override all three here. */
.mc-date-range { display: flex; align-items: center; gap: 5px; }
.mc-date-range .msl-datepicker { width: 148px; flex: 0 0 auto; }
/* A touch more air before the preset dropdown than around the dash. */
/* The "All dates" preset is a separate control from the two coupled pickers,
 * so it sits at the standard 8px (5px range gap + 3px) rather than 5px. */
.mc-date-range .mc-date-preset { margin-left: 3px; }
.mc-date-range .msl-dp-input {
  height: var(--control-h);
  border-color: var(--border-control);
  border-radius: var(--r);
}
/* Follow the outer 6px radius (button sits 1px inside the field border). */
.mc-date-range .msl-dp-btn { border-radius: 0 calc(var(--r) - 1px) calc(var(--r) - 1px) 0; }
/* A longer range dash between the two date fields, drawn as a short rule so it
 * has a real box (no overflow) and isn't an en-dash character (no-en-dash rule). */
.mc-date-sep { color: var(--text-secondary); display: inline-flex; align-items: center; font-size: 0; }
/* 2px (was 1.5px): 1.5px rounded to 1px or 2px depending on the row's subpixel
 * position at fractional display scaling, so the dash looked bold on Tasks but
 * thin on Matters/Activities. A whole 2px renders bold consistently everywhere. */
.mc-date-sep::before { content: ''; width: 12px; height: 2px; border-radius: 1px; background: currentColor; }
.mc-date-preset {
  width: auto; min-width: 118px;
  height: var(--control-h);
  border-color: var(--border-control);
  border-radius: var(--r);
}

/* ── Matter Activities tab ──
 * Same shell as Communications; only the column widths, the strip icons, the
 * money cells, and the tfoot totals row are new. */
.tab-strip-btn .strip-icon { width: 14px; height: 14px; margin-right: 6px; }
.ma-table .ma-col-type { width: 56px; }
.ma-table td.ma-col-type { text-align: center; }
.ma-table td.ma-col-type .icon { vertical-align: middle; }
/* Activities Time/Expense identity - 18px, Time = burnt orange, Expense = blue.
 * Applied to both the row Type icon and the Time/Expense tab-strip filter icons
 * (!important overrides .strip-icon's 14px + currentColor and the col-type size). */
.ac-type-time    { color: rgb(194, 78, 0)  !important; width: 18px !important; height: 18px !important; }
.ac-type-expense { color: rgb(0, 105, 209) !important; width: 18px !important; height: 18px !important; }
.ma-table .ma-col-qty  { width: 90px;  white-space: nowrap; }
.ma-table .ma-col-nonbill,
.ma-table .ma-col-rate,
.ma-table .ma-col-bill { width: 130px; white-space: nowrap; text-align: right; }
.ma-table .ma-col-date { width: 110px; white-space: nowrap; }
.ma-table .ma-col-user { width: 160px; }
.ma-table td.ma-col-description { max-width: 0; }
.ma-note { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; }
.ma-table tfoot .ma-total-row td {
  font-weight: 600; background: var(--page-bg);
  border-top: 1px solid var(--border);
}

/* Entry-modal bits: $-prefixed money field + the right-hand option stack. */
.ma-money-group {
  display: flex; align-items: center; gap: 0;
  border: 1px solid var(--border-strong); border-radius: var(--r);
  background: var(--surface); overflow: hidden;
}
.ma-money-group .input { border: 0; border-radius: 0; flex: 1; min-width: 0; }
.ma-money-group .input:focus { box-shadow: none; }
.ma-money-group:focus-within { border-color: var(--blue); box-shadow: 0 0 0 2px #a2d7ff; }
.ma-money-fix {
  padding: 0 10px; font-size: var(--fs-base); color: var(--text-secondary);
  align-self: stretch; display: flex; align-items: center;
  background: var(--page-bg); white-space: nowrap;
}
.ma-money-fix:first-child { border-right: 1px solid var(--border); }
.ma-money-fix:last-child  { border-left: 1px solid var(--border); }
.ma-side-stack { gap: var(--space-1); }

/* ── Matter Notes tab ── same shell; only column widths + the preview cell. */
.mn-table .mn-col-date    { width: 110px; white-space: nowrap; }
.mn-table .mn-col-subject { width: 240px; }
.mn-table .mn-col-author  { width: 170px; }
.mn-table td.mn-col-note  { max-width: 0; }
.mn-note-preview { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mn-subject { font-weight: var(--fw-medium); }

/* Hover body preview - a fixed popover under the subject link showing the
 * full email text. Pointer-events off: it's a peek, not a surface. */
.mc-body-pop {
  position: fixed; z-index: 260; display: none;
  background: var(--surface); border: 1px solid var(--border-strong);
  box-shadow: 0 8px 24px rgba(0, 21, 56, 0.18);
  padding: 14px 18px; font-size: var(--fs-base); color: var(--text-primary);
  line-height: 1.5; white-space: pre-wrap; overflow: hidden;
  pointer-events: none;
}

/* Edit email log modal */
.mc-edit-row { display: flex; gap: var(--space-2); }
.mc-edit-row > .form-group { flex: 1 1 0; min-width: 0; }
.mc-edit-dt { display: flex; gap: var(--space-2); }
.mc-time-section { border-top: 1px solid var(--divider); margin-top: var(--space-4); padding-top: var(--space-3); }
.mc-time-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-2); }
.mc-time-head h3 { font-size: var(--fs-md); font-weight: 600; }
.mc-time-empty {
  display: flex; align-items: center; gap: var(--space-2);
  color: var(--text-secondary); font-size: var(--fs-base);
}
.mc-time-empty .icon { width: 16px; height: 16px; color: var(--text-tertiary); }
.mc-time-item {
  display: flex; gap: var(--space-3); align-items: baseline;
  padding: 6px 0; border-bottom: 1px solid var(--divider); font-size: var(--fs-base);
}
.mc-time-qty  { font-weight: 600; white-space: nowrap; }
.mc-time-note { flex: 1; min-width: 0; }
.mc-time-meta { color: var(--text-secondary); white-space: nowrap; }
.mc-time-form {
  background: var(--page-bg); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: var(--space-3); margin-bottom: var(--space-3);
}
.mc-time-nb { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-base); margin-top: var(--space-2); }
.mc-time-form-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-3); }

/* Forms top card: Matter picker + Signature date side-by-side, 50/50, labels
 * aligned (same as the page's other field rows). */
.forms-matter-row { display: flex; gap: var(--space-2); align-items: flex-start; }
/* Contact + Matter + Signature date share one line (date narrower). */
.forms-matter-row > .forms-contact-group,
.forms-matter-row > #forms-matter-group,
.forms-matter-row > #ms-matter-group,
.forms-matter-row > #tpl-matter-group { flex: 1.4 1 0; min-width: 0; }
.forms-matter-row > .forms-date-group { flex: 0.9 1 0; min-width: 0; }
/* Mail & Ship / Delivery: reuse the Contact/Matter/Mail-date 3-column grid
 * (1.4 / 1.4 / 0.9). .ms-c1 & .ms-c2 are the two wide columns, .ms-c3 the narrow
 * one; a column wraps its short fields in .ms-split to place two side by side. */
.forms-matter-row > .ms-c1,
.forms-matter-row > .ms-c2 { flex: 1.4 1 0; min-width: 0; }
.forms-matter-row > .ms-c3 { flex: 0.9 1 0; min-width: 0; }
.ms-split { display: flex; gap: var(--space-2); align-items: flex-start; }
.ms-split > .form-group { flex: 1 1 0; min-width: 0; }
/* Recipient address: street = column 1; the City/State (row 1) + ZIP/County/
 * Country (row 2) grid = column 2; column 3 empty. Overrides the shared
 * ec-addr-grid's fixed-px template for this block only. */
/* "Address on file" picker, injected above the recipient address row when the
 * picked contact has more than one address. */
.cl-recip-addr-picker { margin-bottom: var(--space-2); }
.cl-recip-addr-picker select { max-width: 480px; }
#cl-recip-addr { display: flex; gap: var(--space-2); align-items: flex-start; }
#cl-recip-addr .ec-addr-street { flex: 1.4 1 0; min-width: 0; }
#cl-recip-addr .ec-addr-grid { flex: 1.4 1 0; min-width: 0;
  /* ZIP is never longer than 10 chars, so give it a fixed narrow column; County
   * (long names) takes the stretch; State/Country keep a readable fixed width. */
  grid-template-columns: 115px 1fr 194px;
  grid-template-areas: "city city state" "zip county country"; }
#cl-recip-addr > .ms-c3 { flex: 0.9 1 0; min-width: 0; }

/* Clio-style date picker: MM/DD/YYYY text input + calendar button + popup. */
.msl-datepicker { position: relative; }
.msl-dp-input { width: 100%; padding-right: 44px; }
/* Keep the date field looking focused (blue ring) while the calendar is open. */
.msl-datepicker.is-open .msl-dp-input {
  border-color: var(--blue); box-shadow: 0 0 0 2px #a2d7ff;
}
.msl-dp-btn {
  position: absolute; top: 1px; right: 1px; bottom: 1px; width: 34px;
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: 0;
  border-left: 1px solid var(--border-strong);   /* Clio-style separator */
  border-radius: 0 calc(var(--r) - 1px) calc(var(--r) - 1px) 0;
  cursor: pointer; color: var(--text-secondary);
}
.msl-dp-btn:hover { background: #eef2f6; }   /* light-grey bg, icon colour unchanged */
.msl-dp-btn .icon {
  width: 18px; height: 18px; display: inline-block; background-color: currentColor;
  -webkit-mask: url('/img/icons/calendar-blank-bold.svg') center / contain no-repeat;
          mask: url('/img/icons/calendar-blank-bold.svg') center / contain no-repeat;
}
.msl-dp-pop {
  position: absolute; z-index: var(--z-popup, 1000); top: calc(100% + 1px); left: 0;
  width: 300px; padding: 10px; box-sizing: border-box;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r); box-shadow: 0 8px 24px rgba(20,30,50,0.16);
}
.msl-dp-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: 8px; }
.msl-dp-nav {
  flex: 0 0 auto; width: 40px; height: 38px;   /* match Add-a-matter (.btn) height */
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-strong); border-radius: var(--r);
  background: var(--surface); color: var(--text-primary); cursor: pointer;
}
.msl-dp-nav:hover { background: #eef2f6; }
.msl-dp-nav .icon { width: 13px; height: 13px; display: inline-block; }
.msl-dp-title {
  flex: 1 1 auto; text-align: center; font-weight: var(--fw-bold); color: var(--text-primary);
  height: 38px; line-height: 36px;             /* match Add-a-matter (.btn) height */
  border: 1px solid var(--border-strong); border-radius: var(--r);
  background: var(--surface); cursor: pointer; font-size: var(--fs-base);
}
.msl-dp-title:hover { background: #eef2f6; }
.msl-dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.msl-dp-grid-4 { grid-template-columns: repeat(4, 1fr); gap: 6px; }
/* Day-of-week header: bold labels on a light-grey full-width band with a subtle
 * hairline above + below. */
.msl-dp-dows {
  margin: 0 -10px 4px; padding: 5px 10px; background: #f3f4f6;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.msl-dp-dow { text-align: center; font-size: var(--fs-base); color: var(--text-secondary); font-weight: var(--fw-bold); }
.msl-dp-cell {
  height: 30px; border: 0; background: transparent; border-radius: var(--r);
  cursor: pointer; font-size: var(--fs-base); color: var(--text-primary); padding: 0;
}
.msl-dp-cell-wide { height: 56px; }
.msl-dp-cell-2line { line-height: 1.15; font-size: var(--fs-smd); }
.msl-dp-cell:hover { background: #eef2f6; }
.msl-dp-cell.is-out { color: var(--text-tertiary); }
.msl-dp-cell.is-today { box-shadow: inset 0 0 0 1px var(--blue); color: var(--blue); font-weight: var(--fw-bold); }
/* Day-grid selection: blue OUTLINE only (Clio). The coarse month/year/decade
 * grids keep a solid blue fill for the focused cell. */
.msl-dp-cell.is-selected, .msl-dp-cell.is-selected:hover {
  box-shadow: inset 0 0 0 1px var(--blue); color: var(--blue); background: transparent; font-weight: var(--fw-bold);
}
.msl-dp-cell-wide.is-selected, .msl-dp-cell-wide.is-selected:hover {
  box-shadow: none; background: var(--blue); color: #fff;
}
/* Today footer: light-grey full-width band hugging the popup's bottom edge, with
 * a subtle hairline at its top. */
.msl-dp-today {
  display: block; width: calc(100% + 20px); margin: 8px -10px -10px; padding: 9px 0;
  border: 0; border-top: 1px solid var(--border); border-radius: 0 0 var(--r) var(--r);
  background: #f3f4f6; color: var(--blue); cursor: pointer; font-size: var(--fs-base);
  font-weight: var(--fw-regular); text-align: center;
}
.msl-dp-today:hover { background: #e9edf2; }

/* Forms (2848) page: fields 7px taller than the app default for readability.
 * Scoped to the Forms fill panel so the rest of the app keeps the 34px control
 * height. The multi-line address textarea is grown proportionately in JS. */
#forms-fill-panel .input,
#forms-fill-panel .select-input { height: 41px; }
.msl-dp-input { height: 41px; }   /* keep the calendar button's separator spanning the field */
/* A text field with a checkbox inline after it (Daytime telephone + POA): the
 * field stretches to fill the row, the checkbox sits just after it at the end. */
.forms-inline-cb { display: flex; align-items: center; gap: var(--space-3); }
.forms-inline-cb-field { flex: 1 1 auto; width: auto; min-width: 0; }
.forms-inline-cb-box { flex: 0 0 auto; white-space: nowrap; }
.form-info-box {
  background: var(--blue-light);
  border: 1px solid #99CAFF;
  border-radius: var(--r-sm);
  padding: 10px 14px;
  font-size: var(--fs-md); color: var(--text-primary);
  display: flex; gap: var(--space-3); align-items: flex-start;
  margin-bottom: var(--space-4);
}
.form-info-box svg { width: 15px; height: 15px; color: var(--blue); flex-shrink: 0; margin-top: 1px; }
.form-footer {
  position: sticky; bottom: 0;
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 12px 32px;
  display: flex; align-items: center; gap: var(--space-3);
}

/* ── Forms (PDF generator) ─────────────────────────────────────────────── */
/* Standalone: full width (pin #main like the contact page) + 12px gutter.
 * Embedded in the contact Forms tab: .contact-body already adds the 12px
 * gutter, so drop our own padding to avoid doubling the card gaps. */
body:has(.forms-page-body) #main { width: calc(100% - var(--sidebar-width)); min-width: 0; }
body:has(.forms-page-body) #sidebar.collapsed ~ #main { width: calc(100% - var(--sidebar-width-collapsed)); }
body:has(.forms-page-body) #content { min-width: 0; }
.forms-page-body { padding: var(--space-3); min-width: 0; }
.forms-page-body.is-embedded { padding: 0; }
/* Hosted Forms (Documents-hub Forms tab + contact-detail Forms tab): the sitewide
 * 12px-from-card-edge / 8px-between standard. .is-embedded strips the page header
 * + outer padding; the pick-card keeps 12px card padding and 8px between the form
 * buttons and the picker row. */
.forms-page-body.is-embedded .card-body { padding: var(--space-3); }
.forms-page-body.is-embedded .forms-pick-card .forms-matter-row { margin-top: var(--space-2); }
/* Mail & Ship Create has NO template-chip row above the pickers, so drop the 8px
 * top gap that separates the pickers from those chips - the pickers sit right
 * under the 12px card padding, symmetric with the 12px below them. */
.forms-page-body.is-embedded.ms-create .forms-pick-card .forms-matter-row { margin-top: 0; }
/* Forms now lives inside the Documents hub (.tpl-page), whose
 * `.tpl-page .form-group { margin-bottom: 8px }` rule bled into the fill layout -
 * stacking an extra 8px under every field (so section/card bottoms read 20px, not
 * 12px, and vertical field gaps read 20px). The fill's vertical rhythm comes from
 * the grid/flex gaps below (8px), so the per-field margin is redundant here.
 * Later in the file than line 1814 -> wins the specificity tie. */
.forms-page-body .form-group { margin-bottom: 0; }
/* When a form is being filled the Generate/Restore buttons sit in a pinned
 * .ec-footer (same as New matter / Edit contact); reserve clearance so the last
 * card never hides under it. Overrides .is-embedded's padding:0 bottom too. */
.forms-page-body.has-fill-footer { padding-bottom: 80px; }
/* Mail & Ship Create tab pins the same .ec-footer (Cancel / Preview / Create).
 * Reserve clearance so the last recipient fields scroll clear of it instead of
 * hiding under the pinned bar. */
.forms-page-body.ms-create { padding-bottom: 88px; }
/* Standalone Forms page keeps its cards FULL-WIDTH (a 998px column reads as a
 * cramped dialog on a full page - fine for the actual dialog-style New matter /
 * Edit contact pages, odd here). So instead of narrowing the page, left-align
 * the pinned .ec-footer buttons with the card's left edge. The cards sit 12px in
 * (the .forms-page-body / .contact-body gutter) from the footer's left edge, so
 * pad the footer-inner to match - aligns on both /forms and the embedded tab. */
#forms-fill-footer .ec-footer-inner,
#tpl-fill-footer .ec-footer-inner,
#ms-footer .ec-footer-inner {
  width: auto; max-width: none; margin: 0;
  padding-left: var(--space-3); padding-right: var(--space-3);
}
/* "Also generate for spouse" toggle in the footer, pushed to the right so the
 * Generate / Restore buttons stay at the card's left edge. */
.forms-spouse-slot { margin-left: auto; }
.forms-spouse-footer { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; font-size: var(--fs-base); color: var(--text-primary); }
.forms-spouse-footer input { width: 16px; height: 16px; flex-shrink: 0; }
/* .btn sets an author display (inline-flex), which overrides the UA [hidden]
 * rule - restore hiding so the footer's Submit-for-approval / Generate-and-file
 * toggle actually hides them. */
.btn[hidden] { display: none; }
.forms-pick-card { margin-bottom: var(--space-3); }
.forms-section-card { margin-bottom: var(--space-3); }
/* Form selector as compact KPI-style clickable chips (quicker than a dropdown). */
.forms-kpi-grid { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* Agency-labeled tile rows: a fixed-width leading label (styled like the
 * Tax Returns YEAR/TYPE/STATUS filter labels - uppercase, right-aligned) keeps
 * the tile columns aligned between the IRS and U.S. Tax Court rows. */
.forms-kpi-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); width: 100%; }
.forms-kpi-agency {
  flex: 0 0 118px; width: 118px;
  font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em; text-align: right;
}
.forms-kpi {
  display: inline-flex; align-items: center; gap: var(--space-2);
  text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); height: 38px; padding: 0 12px; box-shadow: var(--shadow-sm);
  transition: border-color var(--t), box-shadow var(--t);
}
.forms-kpi:hover { border-color: var(--blue); }
.forms-kpi.is-active { border-color: var(--blue); box-shadow: 0 0 0 1px var(--blue) inset; }
.forms-kpi-icon { width: 18px; height: 18px; flex-shrink: 0; color: var(--blue); }
.forms-kpi-title { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text-primary); white-space: nowrap; }
/* One-click "Prepare POA" action tile — same size as a form-picker, pushed to
 * the right edge of the picker row and filled blue so it reads as an action. */
.forms-kpi-action { margin-left: auto; background: var(--blue); border-color: var(--blue); box-shadow: none; }
.forms-kpi-action .forms-kpi-icon,
.forms-kpi-action .forms-kpi-title { color: #fff; }
.forms-kpi-action:hover { background: #004f9e; border-color: #004f9e; }
.forms-kpi-action[disabled] { opacity: 0.6; cursor: default; }
/* Space above the contact search and the matter/date row. The top margin goes on
 * the ROW (not the matter group) so the matter and date columns top-align - a
 * margin on just one column would offset it from the other. */
.forms-pick-card .forms-matter-row { margin-top: var(--space-2); }
/* Before a form is picked the per-field + instructions hints are empty; an empty
 * .form-hint still reserved a line-gap under the fields (extending the Contact/
 * Matter columns and leaving a below-row margin), which made the pick card look
 * mostly empty. Collapse the empty hints; the card keeps its standard 20px bottom
 * padding so the fields sit a normal gap above the card's lower edge. */
.forms-page-body .form-hint:empty { display: none; }
.forms-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}
/* An explicit multi-field row inside a section, mirroring how the paper form
 * groups fields (e.g. Name / Identifying number / Decedent's SSN on one line).
 * Spans the whole section grid; its own columns come from an inline
 * grid-template-columns sized to the form's box widths. align-items:end keeps
 * the inputs bottom-aligned when labels wrap to different heights. */
.forms-row {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--space-2);
  align-items: end;
}
.forms-row > * { min-width: 0; }
.forms-check {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-base); color: var(--text-primary); cursor: pointer;
}
.forms-check input { width: 16px; height: 16px; flex-shrink: 0; }
/* Contact typeahead */
.forms-ta { position: relative; }
/* Clear (X) button anchored inside the contact search input, mirroring the
 * Contacts/TR toolbar search. Same 10px masked x-bold glyph; the input gets
 * right padding so typed text never slides under the button. */
.forms-ta:has(> .forms-ta-clear:not([hidden])) > .input { padding-right: 30px; }
.forms-ta-clear {
  position: absolute; top: 50%; right: 12px; transform: translateY(-50%);
}
.forms-ta-results {
  position: absolute; left: 0; right: 0; top: calc(100% + 2px);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); box-shadow: var(--shadow-lg);
  max-height: 280px; overflow-y: auto; z-index: 50;
}
.forms-ta-item {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; text-align: left; padding: 8px 12px;
  background: none; border: none; cursor: pointer;
  font-size: var(--fs-base); color: var(--text-primary);
}
.forms-ta-item:hover { background: var(--surface-hover); }
/* Keyboard highlight (arrow keys). Stronger than :hover on purpose - the mouse
   may be resting over a different row while you arrow, and the row Enter would
   take has to be the unambiguous one. */
.forms-ta-item.is-active,
.forms-ta-item.is-active:hover { background: var(--blue-light); box-shadow: inset 2px 0 0 var(--text-link); }
/* Muted secondary text inside a combobox row (e.g. the client's primary email
 * after their name on the intake client picker). */
.forms-ta-sub { color: var(--text-tertiary); font-weight: 400; }
.forms-ta-tag { font-size: var(--fs-xs); color: var(--text-tertiary); flex-shrink: 0; }
/* Contact combobox rows: a person / company icon before the name (matching the
 * New-contact type toggle colours). */
.forms-ta-main { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.forms-ta-ico { width: 15px; height: 15px; flex-shrink: 0; }
.forms-ta-ico--person  { color: rgb(194, 78, 0); }
.forms-ta-ico--company { color: rgb(117, 55, 217); }
.forms-ta-empty { padding: 8px 12px; color: var(--text-tertiary); font-size: var(--fs-md); }
/* "+ New company / contact" action row at the bottom of the combobox list -
 * a standard single-line item, tinted like a link and set off by a top rule. */
.ec-combo .forms-ta-new { border-top: 1px solid var(--divider); }
.ec-combo .forms-ta-new .forms-ta-main { color: var(--text-link); font-weight: var(--fw-medium); }
.forms-spouse {
  margin: 0 0 var(--space-4); padding: 12px 14px;
  background: var(--blue-light); border: 1px solid #99CAFF; border-radius: var(--r-sm);
}
/* Forms tab switcher (Fill a form | Client questionnaires) */
.forms-seg {
  display: inline-flex; gap: 2px; padding: 3px; margin-bottom: var(--space-4);
  background: var(--page-bg); border: 1px solid var(--divider); border-radius: var(--r-sm);
}
.forms-seg-btn {
  border: none; background: transparent; cursor: pointer;
  padding: 7px 16px; border-radius: var(--r);
  font-size: var(--fs-base); font-weight: 500; color: var(--text-secondary);
}
.forms-seg-btn.is-active { background: var(--surface); color: var(--text-primary); box-shadow: var(--shadow-sm); }

/* Questionnaire page checklist */
.forms-q-pages { display: flex; flex-direction: column; gap: 2px; margin-bottom: var(--space-2); }
.forms-q-page { display: flex; align-items: center; gap: var(--space-2); padding: 8px 10px;
  border: 1px solid var(--divider); border-radius: var(--r-sm); cursor: pointer; font-size: var(--fs-base); }
.forms-q-page:hover { background: var(--page-bg); }
.forms-q-page input { width: auto; }
/* Fixed 16px box + accent so the checkmark renders centred (default sizing left
 * it looking misaligned). */
.forms-q-page input[type="checkbox"] { width: 16px; height: 16px; margin: 0; flex: 0 0 auto; accent-color: var(--blue, #0067D1); }
/* New-intake compose is now its own .ec-form + .ec-footer page (openIntakeCompose),
 * so the old #intake-compose-card / .forms-q-actions rules are gone - the .ec-form
 * shell owns the layout, .form-row owns the vertical rhythm. */
.btn-link { background: none; border: none; color: var(--blue); cursor: pointer; padding: 0;
  font-size: var(--fs-sm); margin-top: var(--space-1); }

.forms-queue-card { margin-top: var(--space-4); }
.forms-queue-card .card-header { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
/* Editable intake review modal */
.rv-page { margin-bottom: var(--space-3); }
.rv-page-title { font-weight: 600; margin: var(--space-2) 0; }
.rv-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2) var(--space-3); }
.rv-field { grid-column: 1 / -1; }
.rv-field.rv-half { grid-column: auto; }
.rv-label { display: block; font-size: var(--fs-sm); color: var(--text-secondary); margin-bottom: 4px; }
.rv-field.rv-changed .rv-label::after { content: " · edited by client"; color: var(--accent, #0067D1); font-weight: 500; }
.rv-field.rv-changed .input, .rv-field.rv-changed .select-input, .rv-field.rv-changed .textarea-input { background: rgba(0,103,209,.06); }
.rv-cbx { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); }
.rv-err { color: var(--danger, #b3261e); font-size: var(--fs-sm); margin-top: 3px; }
.rv-field.rv-invalid .input, .rv-field.rv-invalid .select-input { border-color: var(--danger, #b3261e); }
@media (max-width: 560px) { .rv-grid { grid-template-columns: 1fr; } .rv-field.rv-half { grid-column: 1 / -1; } }
/* Intake dashboard toolbar + status badges */
.intake-toolbar { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; padding: 0 var(--space-4) var(--space-3); }
.intake-toolbar .tab-strip { flex: 1 1 auto; }
.intake-search { max-width: 240px; }
.tab-count { opacity: .6; font-weight: 400; }
.intake-actions { text-align: right; white-space: nowrap; }
.intake-actions .btn-link + .btn-link, .intake-actions .btn + .btn-link { margin-left: var(--space-2); }
/* Selected-row highlight on the standalone Intake page (the checkbox column +
 * Actions/Delete buttons reuse the Documents .docs-col-check / .docs-*-btn). */
#intake-table-container .data-table tbody tr:has(.intake-row-checkbox:checked) > td { background: #ebf7ff; }
#intake-table-container .data-table tbody tr:has(.intake-row-checkbox:checked):hover > td { background: #cceaff; }
/* Intake list table: date+time cell rendered exactly like the Communications
 * tab (.mc-dt) - date left, time right on one line, same text colour. */
.intake-dt { display: flex; justify-content: space-between; gap: var(--space-1); }
.data-table .intake-col-status      { width: 130px; }
/* Date/time columns are pinned (fixed width, all four identical), sized to hold
 * the side-by-side date + time on one line ("12/31/2026" ... "12:59 AM" at 14px
 * Inter). 175px leaves comfortable slack for the widest 2-digit-hour timestamp
 * ("12/31/2026 12:59 PM") so date + time never touch; locked in INTAKE_COLUMNS
 * so they can't be dragged. This width fits the content so the cell never
 * overflows, which is what keeps the main + embedded pages rendering identically
 * (a narrower column overflowed on the denser embedded table but got silently
 * stretched wider by the roomier main table - the two then looked different). */
/* Sent is 20px wider than its siblings to carry the email-delivery badge after
 * the date/time (a flex row: date+time take the slack, the 15px badge trails). */
.data-table .intake-col-sent        { width: 195px !important; min-width: 195px !important; max-width: 195px !important; white-space: nowrap; }
.intake-sent { display: flex; align-items: center; gap: 6px; }
.intake-sent .intake-dt { flex: 1 1 auto; min-width: 0; }
.intake-deliv { display: inline-flex; width: 15px; height: 15px; flex: 0 0 auto; }
.intake-deliv > svg { width: 15px; height: 15px; }
.intake-deliv--ok   { color: #067647; }
.intake-deliv--wait { color: var(--text-tertiary); }
.intake-deliv--bad  { color: #d30933; }
.data-table .intake-col-started     { width: 175px !important; min-width: 175px !important; max-width: 175px !important; white-space: nowrap; }
.data-table .intake-col-submitted   { width: 175px !important; min-width: 175px !important; max-width: 175px !important; white-space: nowrap; }
.data-table .intake-col-last_change { width: 175px !important; min-width: 175px !important; max-width: 175px !important; white-space: nowrap; }
.data-table .intake-col-actions   { width: 1%; white-space: nowrap; }
/* Embedded Intake tab (contact detail) hides the Client column, which removes
 * one of the two flexible columns the main page relies on to soak up the
 * table's spare width. With table-layout:fixed, spare width that has no auto
 * column to land in gets spread across the FIXED columns instead - inflating
 * the 165px date columns so the embedded page looked wider than the main one.
 * Force the Intake (title) column to stay flexible here so it absorbs the slack
 * and the date columns hold 165px, matching the main page. Scoped to the
 * embedded card so the main page (#intake-card) is untouched. */
.forms-queue-card .data-table .intake-col-intake {
  width: auto !important;
  min-width: 0 !important;
  max-width: none !important;
}
/* Compact intake list in the contact Dashboard aside card. One row per
 * questionnaire: label + status pill on top, sent/submitted date + Review
 * below. Rows divided like the matters list. */
.contact-intake-list { list-style: none; margin: 0; padding: 0; }
/* Match the standard contact-card row spacing (.contact-matter-item): the card
 * body has padding:0, so each row carries the 20px/16px inset + a divider. */
.contact-intake-row { padding: 11px 16px 13px 20px; border-bottom: 1px solid var(--divider, #e9ecf0); }
.contact-intake-row:last-child { border-bottom: 0; padding-bottom: var(--space-5); }
.contact-intake-top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.contact-intake-label { font-weight: var(--fw-medium); }
.contact-intake-meta { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); margin-top: var(--space-1); }
/* Review popup: a large frame-only modal that hosts the client questionnaire
 * (questionnaire.html) in staff review mode via an iframe. */
.intake-review-modal { position: relative; width: min(1120px, 96vw); height: 92vh; max-width: none; padding: 0; overflow: hidden; }
.intake-review-frame { width: 100%; height: 100%; border: 0; display: block; background: var(--page-bg); }
.intake-review-close { position: absolute; top: 8px; right: 12px; z-index: 2;
  width: 32px; height: 32px; border-radius: 999px; background: var(--surface);
  border: 1px solid var(--border); box-shadow: var(--shadow-sm); cursor: pointer; }
/* uiConfirm() dialog - the portal-wide confirm popup (replaces native confirm()). */
.modal.ui-confirm { max-width: 480px; }
.ui-confirm-head { display: flex; align-items: center; gap: var(--space-2); }
.ui-confirm-icon { width: 22px; height: 22px; flex: 0 0 auto; color: var(--danger); }
.ui-confirm-title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); margin: 0; }
.ui-confirm-body p { margin: 0 0 var(--space-2); color: var(--text-secondary); line-height: 1.5; }
.ui-confirm-body p:last-child { margin-bottom: 0; }
.ui-confirm-warn { color: var(--danger) !important; font-weight: var(--fw-semibold); }
.ui-confirm-lead { color: var(--text-primary) !important; font-weight: var(--fw-medium); }
.ui-confirm-body ul { margin: var(--space-1) 0 var(--space-3); padding-left: 22px; color: var(--text-secondary); }
.ui-confirm-body li { margin: 2px 0; }
.ui-confirm-check { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-3); font-size: var(--fs-md); color: var(--text-primary); }
.ui-confirm-check input { width: auto; }
.badge-verified { background: rgba(18,124,63,.12); color: #127c3f; border: 1px solid rgba(18,124,63,.35); }
.badge-verified::before { background: #127c3f; }
.badge-voided { background: var(--surface-alt, #f2f4f6); color: var(--text-tertiary, #8C9BAA); border: 1px solid var(--divider, #E1E4E8); }
.badge-voided::before { background: var(--text-tertiary, #8C9BAA); }
/* Part I / Part II cards with collapsible subsections */
.forms-part-card { margin-bottom: var(--space-3); }
.forms-sub { border-top: 1px solid var(--divider); }
.forms-sub:first-child { border-top: none; }
.forms-sub-header {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; text-align: left; padding: 12px var(--space-3);
  background: none; border: none; cursor: pointer;
  font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text-primary);
}
.forms-sub-header:hover { background: var(--surface-hover); }
.forms-sub-caret { width: 12px; height: 12px; flex-shrink: 0; transition: transform var(--t); }
.forms-sub.is-collapsed .forms-sub-caret { transform: rotate(-90deg); }
.forms-sub-body { padding: 0 var(--space-3) var(--space-3); }
.forms-sub.is-collapsed .forms-sub-body { display: none; }
/* Representative panels (checkbox + collapsible) */
.forms-sub-body.forms-reppanels { padding: 0; }
/* Separators BETWEEN representative panels (line 2) are inset from the card
 * edges - only the separators between whole lines run the full card width. */
.forms-reppanels .forms-reppanel { border-top: none; position: relative; }
.forms-reppanels .forms-reppanel + .forms-reppanel::before {
  content: ''; position: absolute; top: 0; left: var(--space-3); right: var(--space-3);
  border-top: 1px solid var(--divider);
}
.forms-reppanel-header {
  display: flex; align-items: center; gap: var(--space-3);
  padding: 10px var(--space-3);
}
.forms-reppanels .forms-reppanel-header { padding-left: calc(var(--space-3) + 14px); }
.forms-rep-inc { width: 16px; height: 16px; flex-shrink: 0; cursor: pointer; }
.forms-sub-toggle {
  display: flex; align-items: center; gap: var(--space-2); flex: 1; min-width: 0;
  background: none; border: none; cursor: pointer; text-align: left; padding: 0;
  font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text-primary);
}
.forms-sub-toggle:hover { color: var(--blue); }
/* Two-column field layout (left = name/address, right = IDs), matching the form */
.forms-twocol { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); }
.forms-col { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.forms-when[hidden] { display: none; }
/* "Check if new:" inline checkbox row */
.forms-checkrow {
  grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3); font-size: var(--fs-md); color: var(--text-primary);
}
.forms-checkrow-label { font-weight: var(--fw-medium); color: var(--text-primary); }
.forms-checkrow-item { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.forms-checkrow-item input { width: 16px; height: 16px; }
/* Matters builder (Part I line 3) */
/* overflow:visible + right margin so each row's edit/delete can sit just
 * OUTSIDE the right border (absolute, see .forms-mrow-actions). */
.forms-mtable { border: 1px solid var(--border); border-radius: var(--r-sm); overflow: visible; margin-right: 80px; }
.forms-mrow {
  display: grid; grid-template-columns: 1.4fr 1.2fr 1fr; align-items: stretch;
  min-height: 40px; border-top: 1px solid var(--grey-250); font-size: var(--fs-base);
  position: relative;   /* anchor for the outside-the-table row actions */
}
.forms-mrow > div { display: flex; align-items: center; padding: 6px var(--space-3); min-width: 0; }
.forms-mrow:first-child { border-top: none; }
.forms-mhead {
  min-height: 31px; background: var(--page-bg, #f5f3f7);
  font-size: var(--fs-md); font-weight: var(--fw-semibold);
  color: var(--text-primary); text-transform: none; letter-spacing: 0;
}
/* Internal vertical gridlines in both tables (matters builder + Part II decl):
 * a left border on every header/body cell after the first. With the per-row
 * border-top this renders full internal borders inside each table. */
.forms-mhead > div:not(:first-child),
.forms-mcell:not(:first-child) { border-left: 1px solid var(--grey-250); }
/* Center headers + cells to match the printed form: every column header on the
 * IRS table is centered, and a centered header takes centered cells under it. */
.forms-mhead > div, .forms-mcell { justify-content: center; text-align: center; }
.forms-mcell { overflow-wrap: anywhere; }

/* Part II Declaration of Representative - the same bordered table shell as the
 * Line-3 matters builder, 5 columns matching the printed form's measured widths
 * (Designation 65 / Licensing jurisdiction 79 / Bar 115 / Signature 221 /
 * Date 58). Which reps are included is controlled under Part I line 2, so there
 * is no Representative column here. */
.forms-decltable-wrap { padding: var(--space-4) var(--space-3) var(--space-3); }
.forms-dtable { margin-right: 0; }
.forms-dtable .forms-mrow { grid-template-columns: 65fr 79fr 115fr 221fr 58fr; }
.forms-dinput {
  width: 100%; text-align: center; font: inherit; color: var(--text-primary);
  background: transparent; border: 1px solid transparent; border-radius: var(--r-sm);
  padding: 2px 4px;
}
.forms-dinput:hover:not(:disabled) { border-color: var(--border); }
.forms-dinput:focus { border-color: var(--blue); background: var(--surface); outline: none; }
.forms-dinput:disabled { background: transparent; color: var(--text-tertiary); }
/* A Part II cell auto-filled from the selected contact - the WHOLE cell greys
 * ("prefilled"); the input goes transparent so it blends into the greyed cell. */
.forms-mcell.is-auto-cell { background: var(--page-bg, #f5f3f7); }
.forms-dinput.is-auto { background: transparent; color: var(--text-tertiary); }
/* The per-representative contact picker sits above the rep's own fields. */
.forms-rep-picker { margin-bottom: var(--space-4); }
/* Signature is signed on paper, and the Date is set by the top Signature date
 * picker - both are greyed, inactive CELLS (same fill as the Line-7 Date field),
 * not just greyed text. */
.forms-dsig, .forms-dlockcell { background: var(--page-bg, #f5f3f7); }
/* A row whose Part I line-2 rep is unticked is greyed out entirely (its cells
 * are also JS-disabled). Use a background fill, NOT opacity, so the internal
 * gridlines render at the same strength as the active rows. */
.forms-drow.is-excluded { background: var(--page-bg, #f5f3f7); }
.forms-drow.is-excluded .forms-dinput { color: var(--text-tertiary); }

/* Line 5a/5b "form-wrap" field: the textarea is the value source; the preview
 * shows how the text will break across the form's uneven blanks (a short tail
 * then wide continuation lines) as it will print. */
.forms-formwrap { display: flex; flex-direction: column; gap: var(--space-2); }
.forms-formwrap-src { min-height: 54px; }
.forms-formwrap-preview {
  display: flex; flex-direction: column; gap: 8px;
  padding: var(--space-2) var(--space-3) var(--space-3);
  border: 1px dashed var(--border); border-radius: var(--r-sm);
  background: var(--page-bg, #f5f3f7);
}
.forms-formwrap-preview::before {
  content: 'Prints as'; font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  letter-spacing: .04em; text-transform: uppercase; color: var(--text-tertiary);
}
.fwp-line {
  min-height: 17px; border-bottom: 1px solid var(--grey-250);
  font-size: var(--fs-base); color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-bottom: 1px;
}
.fwp-line.is-overflow { border-bottom-color: var(--danger); }

/* Greyed display-only field (e.g. the Line-7 signature line - signed on paper). */
.forms-input-disabled { background: var(--page-bg, #f5f3f7); color: var(--text-tertiary); cursor: not-allowed; }

/* ── Forms preview popup (filled PDF, page controls, actual size) ── */
.fp-overlay {
  position: fixed; inset: 0; z-index: 1000; background: rgba(0, 0, 0, .45);
  display: flex; align-items: center; justify-content: center; padding: var(--space-5);
}
.fp-dialog {
  background: var(--surface, #fff); border-radius: var(--r);
  width: min(960px, 96vw); height: 92vh;
  display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 20px 60px rgba(0, 0, 0, .35);
}
.fp-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--divider);
}
.fp-title { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* SCM tracking + postage surfaced in the letter-preview header (composer readouts
 * sit behind the modal). */
.fp-scm-info { font-size: var(--fs-sm); color: var(--text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; }
.fp-head-actions { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; }
.fp-x { background: none; border: 0; cursor: pointer; color: var(--text-secondary); padding: 4px; border-radius: var(--r); display: inline-flex; }
.fp-x:hover { background: var(--surface-hover); }
.fp-x .icon { width: 16px; height: 16px; }
.fp-body { flex: 1; overflow: hidden; background: #525659; }
/* Web-page preview (intake questionnaire) sits on a light neutral, not the PDF
 * viewer's dark grey; the iframe fills it either way but this avoids a dark flash
 * while the page loads. */
.fp-body-web { background: var(--page-bg, #f5f3f7); }
/* The questionnaire card fills the preview popup (its .wrap drops the centered
 * max-width in q-preview mode) with a 12px gutter; size the dialog so the card
 * lands near its real-client width and the Address row's Type column isn't clipped. */
#intake-preview-modal .fp-dialog { width: min(1080px, 96vw); }
.fp-frame { width: 100%; height: 100%; border: 0; display: block; }
/* Per-row edit/delete sit just past the table's right border (the table
 * reserves 80px via margin-right), vertically centered on the row. */
.forms-mrow-actions {
  position: absolute; left: 100%; top: 0; bottom: 0;
  display: flex; align-items: center; gap: var(--space-1); padding-left: var(--space-2);
}
/* Icon-only edit/delete buttons (Clio Tag-settings style): plain grey glyph,
 * light rounded hover, no border/fill. */
.forms-micon {
  width: 30px; height: 30px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: var(--r);
  color: var(--text-secondary); cursor: pointer;   /* same tone as the date-picker calendar icon */
  transition: background var(--t), color var(--t);
}
.forms-micon:hover:not(:disabled) { background: var(--page-bg); color: var(--text-secondary); }
.forms-micon:disabled { opacity: .35; cursor: default; }
.forms-micon .icon { width: 18px; height: 18px; }
.forms-mtoolbar-hint { font-size: var(--fs-sm); }
.forms-mrow-pick { cursor: pointer; }
.forms-mrow-pick:hover:not(.is-selected) { background: var(--grey-100, #f2f2f5); }
.forms-mrow.is-selected { background: #e8f0fe; }
.forms-mtoolbar { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) 0; }
/* Inline contact/employee picker: size to its content so the dropdown arrow
 * sits next to the selected name instead of at the far right of a full-width
 * box. */
.forms-fid-select { width: auto; max-width: 100%; }
.forms-mempty { padding: 12px; color: var(--text-tertiary); font-size: var(--fs-md); }
/* Reorder arrows + Add button share one controls row under the table
 * ([up][down]  [+ Add a matter]). 8px above (sitewide standard) + 8px between. */
.forms-mcontrols { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); padding: var(--space-2) 0; }
.forms-maddbtn { margin-top: 0; }
/* Match the TR Refresh button text (.page-actions > .btn). */
.forms-maddbtn, #forms-generate-btn { font-size: var(--fs-base); font-weight: var(--fw-semibold); }
.forms-meditor { margin-top: var(--space-3); padding: var(--space-4); border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface-subtle, #f8fafc); }
.forms-meditor-title { font-size: var(--fs-sm); font-weight: var(--fw-bold); letter-spacing: .03em; color: var(--text-secondary); margin-bottom: var(--space-3); }
.forms-yearmode { display: flex; gap: var(--space-4); margin: var(--space-2) 0; }
.forms-yearmode label { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.forms-yearrange { display: flex; align-items: center; gap: var(--space-2); }
.forms-yearrange .input { max-width: 160px; }
.forms-meditor-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-4); }
@media (max-width: 720px) {
  .forms-twocol { grid-template-columns: 1fr; }
  .forms-mrow { grid-template-columns: 1fr; }
  .forms-mhead { display: none; }
  .forms-grid { grid-template-columns: 1fr; }
  .forms-row { grid-template-columns: 1fr !important; }
  .forms-kpi-grid { grid-template-columns: 1fr; }
}

/* ── Sidebar sections in form ── */
.form-sections-nav {
  width: 200px; flex-shrink: 0;
  position: sticky; top: 80px;
}
.form-sections-label { font-size: var(--fs-2xs); font-weight: var(--fw-bold); color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: var(--space-2); }
.form-section-link {
  display: block; padding: 5px 0; font-size: var(--fs-md);
  color: var(--text-link); cursor: pointer;
}
.form-section-link:hover { text-decoration: underline; }
.form-layout { display: flex; gap: 32px; }
.form-content { flex: 1; }

/* ── Modal ── */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.45);
  z-index: var(--z-modal);
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-5);
}
.modal {
  background: var(--surface);
  border-radius: var(--r-lg);
  width: 100%; max-width: 520px;
  max-height: 90vh;
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
  animation: modal-in 0.2s ease;
}
@keyframes modal-in { from { opacity:0; transform:translateY(-10px); } to { opacity:1; transform:none; } }
.modal-header {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: var(--space-3);
}
.modal-header h2 { font-size: var(--fs-lg); font-weight: var(--fw-semibold); flex: 1; }
.modal-close { width: 28px; height: 28px; border-radius: var(--r); border: none; background: transparent; font-size: var(--fs-xl); color: var(--text-primary); display: flex; align-items: center; justify-content: center; transition: background var(--t); }
.modal-close:hover { background: var(--page-bg); }
.modal-body { padding: var(--space-3); overflow-y: auto; flex: 1; }
.modal-footer { padding: var(--space-3); border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: var(--space-2); }
/* Modal-scoped: any multi-column .form-row inside a dialog (invite / upload) uses
 * the sitewide 8px field gap, not the page-wide base .form-row 16px. */
.modal .form-row { gap: var(--space-2); margin-bottom: var(--space-2); }

/* ── Task modal (New / Edit) ── */
.task-modal-section { font-size: var(--fs-lg); font-weight: var(--fw-semibold); color: var(--text-primary); margin-bottom: var(--space-3); }
.task-modal-field { margin-top: var(--space-2); }
.task-modal-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); }
.task-modal-grid--2to1 { grid-template-columns: 2fr 1fr; }
.task-modal-hr { border: 0; border-top: 1px solid var(--border); margin: var(--space-5) 0 var(--space-4); }
.task-modal .modal-footer { justify-content: flex-start; }
.task-name-link { cursor: pointer; }
.task-name-link:hover { text-decoration: underline; }

/* ── KPI cards ── */
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--space-4); margin-bottom: 22px; }
.kpi-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 16px 18px;
  box-shadow: var(--shadow-sm);
}
.kpi-label { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: var(--space-2); }
.kpi-value { font-size: var(--fs-3xl); font-weight: var(--fw-regular); color: var(--text-primary); line-height: 1.2; }
.kpi-value.blue  { color: var(--blue); }
.kpi-value.red   { color: var(--danger); }
.kpi-value.green { color: var(--success); }
.kpi-sub { font-size: var(--fs-sm); color: var(--text-tertiary); margin-top: var(--space-1); }
.kpi-card.accent-top { border-top: 3px solid var(--blue); }

/* Fixed widths. Only `col-client` (Client Name) and `col-notes` (Notes)
 * are flexible — every other column has a strict bounded width so the
 * table layout stays predictable. */
/* Locked column widths — sized to fit the longest possible content in
 * each column (with the header label as the secondary constraint). User
 * cannot resize these (attachColumnResizers skips them). !important so
 * any stale saved-width rule from a previous session is overridden. */
.data-table .col-year         { width:  78px !important; min-width:  78px !important; max-width:  78px !important; white-space: nowrap; }  /* fits "2026-01" */
.data-table .col-due_date     { width: 104px !important; min-width: 104px !important; max-width: 104px !important; white-space: nowrap; }  /* "MM/DD/YYYY"; Sales & Use tab only */
.data-table .col-id           { width: 126px !important; min-width: 126px !important; max-width: 126px !important; white-space: nowrap; }
.data-table .col-type         { width: 120px !important; min-width: 120px !important; max-width: 120px !important; white-space: nowrap; }
/* Sales & Use tab: the Type cell only ever shows "DR-15", so tighten it (for
   now - widen again once other sales/use form codes appear). Higher specificity
   + !important beats the base .col-type rule above. */
#tr-card.tr-sales .data-table .col-type { width: 84px !important; min-width: 84px !important; max-width: 84px !important; }
.data-table .col-ef_status    { width:  74px !important; min-width:  74px !important; max-width:  74px !important; white-space: nowrap; text-align: center; }
.data-table .col-ef_date      { width: 120px !important; min-width: 120px !important; max-width: 120px !important; white-space: nowrap; }
.data-table .col-status       { width: 174px !important; min-width: 174px !important; max-width: 174px !important; white-space: nowrap; }
/* Status cell padding: 4 px left, 0 px right. Longest pill ("Pending
 * Clarifications" ≈ 170 px max) lands exactly at the column's right
 * edge → column is "as tight as possible to the pillbox" per spec.
 * The 4 px on the left keeps the pill's rounded curve clear of the
 * Last change column boundary. Pill itself stays at its natural
 * intrinsic width via sizeStageSelectToText(); shorter pills don't
 * get stretched. Column is locked (TR_COLUMNS row carries
 * locked:true) so the user can't drag it wider. */
.data-table tbody td.col-status,
.data-table thead th.col-status { padding-left: 4px !important; padding-right: 0 !important; }
.data-table .col-last_change  { width: 120px !important; min-width: 120px !important; max-width: 120px !important; white-space: nowrap; }
.data-table .col-poa          { width:  62px !important; min-width:  62px !important; max-width:  62px !important; white-space: nowrap; }
.data-table .col-co_counsel   { width: 116px !important; min-width: 116px !important; max-width: 116px !important; white-space: nowrap; }
/* Flexible columns absorb remaining width. */
/* Client and Notes get explicit widths too (was just min-width). The
 * table-layout:fixed layout demands a width for every column, otherwise
 * unwidthed columns collapse to 0. These can still be resized by the user. */
.data-table .col-client { width: 240px; min-width: 120px; }
.data-table .col-notes  { width: 240px; min-width: 120px; }

/* ── Contacts page columns ──
 * Explicit widths on every column (table-layout: fixed only respects
 * `width` on first-row cells; `min-width` alone is ignored). Total
 * width 110 + 260 + 120 + 280 + 170 + 320 + 130 = 1390 px, which
 * exceeds the card width on typical viewports and triggers the
 * horizontal scrollbar at the bottom of .tr-scroll. */
/* Locked columns pinned with width + min-width + max-width all !important
 * so the column can neither grow nor shrink. Name is the absorber — no
 * !important, no max-width, just a min-width floor. */
.data-table .contacts-col-name    {                              min-width: 220px;                                                }
.data-table .contacts-col-tags    { width: 120px !important; min-width: 120px !important; max-width: 120px !important; }
.data-table .contacts-col-email   { width: 280px !important; min-width: 220px !important; max-width: 280px !important; }
.data-table .contacts-col-phone   { width: 170px !important; min-width: 160px !important; max-width: 170px !important; white-space: nowrap; }
.data-table .contacts-col-address { width: 320px !important; min-width: 260px !important; max-width: 320px !important; }
.data-table .contacts-col-dob     { width: 130px !important; min-width: 130px !important; max-width: 130px !important; white-space: nowrap; }
/* Matters page reuses the same two-table layout - Matter is the absorber, the
 * rest are pinned so thead/tbody columns align. */
.data-table .matters-col-matter      { width: 160px !important; min-width: 140px !important; max-width: 200px !important; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .matters-col-description { width: 260px !important; min-width: 180px !important; max-width: 320px !important; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .matters-col-client      { width: 200px !important; min-width: 160px !important; max-width: 220px !important; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .matters-col-responsible { width: 150px !important; min-width: 130px !important; max-width: 170px !important; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .matters-col-staff       { width: 150px !important; min-width: 130px !important; max-width: 170px !important; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .matters-col-practice    { width: 150px !important; min-width: 130px !important; max-width: 170px !important; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .matters-col-status      { width: 180px !important; min-width: 150px !important; max-width: 200px !important; }
.data-table .matters-col-open_date   { width: 110px !important; min-width: 110px !important; max-width: 110px !important; white-space: nowrap; }
.data-table .matters-col-last_change { width: 120px !important; min-width: 120px !important; max-width: 120px !important; white-space: nowrap; }

/* ── Letters queue (Documents > Dashboard) columns ──
 * Auto layout + width:100% so the table fills the pane and the Actions cell sizes
 * to its buttons. (A fixed-layout width:1% collapsed the Actions cell, and its
 * right-aligned buttons then spilled LEFT over the Status column.) Text columns
 * cap with max-width + ellipsis so a long Subject/Matter can't blow the table out.
 * The queue is a single table (header + body together), so auto layout keeps the
 * header and body columns aligned - the split-table caveat doesn't apply here. */
#lq-table-container .tr-scroll > .data-table { table-layout: auto; width: 100%; min-width: 0; }
.data-table .lq-col-subject   { max-width: 260px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .lq-col-recipient { max-width: 150px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .lq-col-matter    { max-width: 190px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .lq-col-signer    { max-width: 150px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .lq-col-submitted { max-width: 150px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.data-table .lq-col-status    { white-space: nowrap; }
/* Reject reason (2nd line under a rejected letter's Subject): truncate on its own
 * line too, so it can't widen the Subject column. */
.data-table .lq-col-subject .lq-reason { overflow: hidden; text-overflow: ellipsis; }
/* Actions: a content-sized table-cell (width:1px = "only as wide as its buttons"),
 * right-aligned and never wrapping, so the buttons stay in their own column. */
.data-table .lq-actions, .data-table .lq-actions-th { display: table-cell; width: 1px; white-space: nowrap; text-align: right; }
.data-table .lq-actions .btn { display: inline-flex; vertical-align: middle; }
.data-table .lq-actions .btn + .btn { margin-left: var(--space-1); }

/* ── Client hover card (Matters list client names) ── 358x218 contact mini-card */
.client-hovercard {
  position: fixed; z-index: var(--z-popup, 1000);
  width: 358px; height: 218px; box-sizing: border-box;
  padding: 16px 18px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r); box-shadow: 0 10px 28px rgba(20,30,50,0.18);
}
.chc-head { display: flex; align-items: center; gap: var(--space-2); margin-bottom: 16px; }
.chc-name { font-weight: var(--fw-bold); font-size: var(--fs-md); color: var(--text-link); text-decoration: none; }
.chc-name:hover { text-decoration: underline; }
.chc-open { display: inline-flex; color: var(--text-link); }
.chc-open .icon { width: 15px; height: 15px; }
.chc-close { margin-left: auto; background: none; border: 0; font-size: 22px; line-height: 1; color: var(--text-secondary); cursor: pointer; padding: 0 2px; }
.chc-close:hover { color: var(--text-primary); }
.chc-body { display: flex; flex-direction: column; gap: 14px; }
.chc-row { display: flex; align-items: flex-start; gap: var(--space-3); }
.chc-row-icon { width: 18px; height: 18px; flex-shrink: 0; color: var(--text-secondary); margin-top: 2px; }
.chc-val { flex: 1 1 auto; min-width: 0; font-size: var(--fs-base); color: var(--text-primary); line-height: 1.45; word-break: break-word; }
.chc-copy { flex: 0 0 auto; background: none; border: 0; border-radius: var(--r); padding: 4px; cursor: pointer; color: var(--text-secondary); }
.chc-copy:hover { background: #eef2f6; color: var(--text-primary); }
.chc-copy .icon { width: 16px; height: 16px; }
.chc-copy.is-copied { color: #1a7f37; }

/* Matters filter strips (Status / Practice area / Responsible attorney / staff):
 * a FIXED-width label column (wide enough for the longest, "Responsible
 * attorney") so every strip left-aligns - the same right-aligned-label look as
 * the TR page, just wider because the names are longer. Rows top-align so the
 * label sits on the first pill row when a strip wraps. */
.matters-filter-label { flex: 0 0 184px; }
/* Upper rows: label + strip top-align. The LAST row (Responsible staff) carries
 * the Search + Columns and uses the shared --status treatment, so it bottom-
 * aligns them at the right while the staff strip shrinks (tr-filter-strip--multi)
 * to make room - pinning Search + Columns to the filter section's bottom-right. */
.matters-filter-section .tr-filter-row:not(.tr-filter-row--status) { align-items: flex-start; }
.matters-filter-section .matters-filter-label { padding-top: 7px; }
.matters-filter-section .tr-filter-row--status > .matters-filter-label { padding-top: 7px; }
/* Contacts/Matters use the same single-table scroller as TR (.tr-scroll). */
/* Pin the contacts card to the page width regardless of content.
 *   width: 100% — always fill .tr-page-body's content area, so the
 *     card grows when the sidebar collapses and stays put when
 *     columns are hidden (instead of shrinking to fit the table).
 *   min-width: 0 — defeat the flex-item min-width:auto default so
 *     the card never tries to be as wide as its content's min-content
 *     (which would push it past the page on column-rich layouts).
 * TR doesn't need this because TR's table is wider than the card
 * (the table's min-content forces the card to parent width anyway).
 * Contacts has fewer columns so the table's min-content is smaller
 * than the card — without this rule the card collapses to the table. */
#contacts-card,
#matters-card,
#aa-card,
#tasks-card {
  width: 100%;
  min-width: 0;
}
/* (The former .tr-v-scroll width:100% + scrollbar-zero patches lived here.
 * Obsolete under the single-table scroller: the table is now one element so
 * header/body always align, and the vertical scrollbar belongs to .tr-scroll
 * which fills the card, so it sits flush at the table's right edge by
 * construction. See the ".tr-scroll" block further down.) */
.contacts-row-name {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
/* Row-icon base (no size / colour — those come from the per-type modifier
 * so the row icon matches the filter-pill icon exactly). */
.contacts-type-icon { flex-shrink: 0; }
.contacts-type-icon--person  { color: rgb(194, 78, 0); width: 18px; height: 18px; }
.contacts-type-icon--company { color: rgb(117, 55, 217); width: 18px; height: 18px; }
/* Filter-pill icons (People / Companies in the type filter strip).
 * Branded per type and pushed 5 px farther from the label than the
 * default whitespace gap. Same colours + sizes as the row icons. */
.contacts-filter-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  margin-right: 4px;
}
.contacts-filter-icon--person  { color: rgb(194, 78, 0); width: 18px; height: 18px; }
.contacts-filter-icon--company { color: rgb(117, 55, 217); width: 18px; height: 18px; }

/* ── Borderless in-cell editors (EF Status select, date inputs, ID text) ── */
.cell-select, .cell-date, .cell-text {
  border: 0;
  background: transparent;
  font: inherit;
  color: inherit;
  padding: 2px 4px;
  width: 100%;
  min-width: 0;
  cursor: pointer;
}
.cell-text { cursor: text; }
.cell-text:focus { outline: none; }
.cell-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 4px;
  background-image: none;
  background-repeat: no-repeat;
  background-position: right 0 center;
  background-size: 12px;
}
/* Excel-style: dropdown arrow only appears once the user clicks into the cell. */
.cell-select:focus {
  padding-right: var(--space-4);
  background-image: url('/img/icons/caret-down-fill.svg');
}
.cell-select:focus, .cell-date:focus {
  outline: none;
}
/* Type / EF-ack-date cells: the value shows as a link to the client-copy PDF.
 * Single click opens it, double click swaps in the editable control (no glyph). */
.tr-editlink { display: flex; align-items: center; }
.tr-editlink > .cell-select,
.tr-editlink > .ui-select,
.tr-editlink > .cell-date { flex: 1 1 auto; min-width: 0; }
.tr-vallink {
  flex: 1 1 auto; min-width: 0;
  /* Match the .cell-select / .cell-date padding so a linked value lines up
   * exactly with an unlinked one (and with its own edit control). */
  padding: 2px 4px;
  color: var(--blue); text-decoration: none; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tr-vallink:hover { text-decoration: underline; }
/* Display mode hides the control; edit mode (after a double-click) hides the link. */
.tr-editlink[data-editing="0"] > .cell-select,
.tr-editlink[data-editing="0"] > .ui-select,
.tr-editlink[data-editing="0"] > .cell-date { display: none; }
.tr-editlink[data-editing="1"] > .tr-vallink { display: none; }

/* Editable Notes cell — single textarea that shows user_notes if present,
 * else the parser-generated default. Single inline-block child centers
 * cleanly via the table-cell's vertical-align:middle. */
.notes-cell { max-width: 280px; }
/* Notes display+edit is one contenteditable div. Flex centering on the
 * div works even when content wraps to two lines (textareas natively
 * anchor text to the top of the box, which made wrapped content look
 * pinned to the top). */
.notes-view {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 1.4em;
  max-height: 4em;
  overflow: auto;
  line-height: 1.4;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  word-break: break-word;
  cursor: text;
  font: inherit;
  color: var(--text-primary);
}
.notes-view:focus { outline: none; background: var(--surface); }

/* ── Responsible Co-Counsel initials chip ── */
.cc-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  background: #ebf4ff;
  color: var(--blue);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.02em;
}
.data-table .col-co_counsel { text-align: center; }

/* ── Single-table scroller (sticky header, body scrolls) ── */
/* ONE <table class="data-table"> per scroller: <thead> sticks to the top
 * (th.tr-th already carries position:sticky; top:0) while <tbody> scrolls
 * under it. Because the header and body are the SAME table, their column
 * widths are computed once and can never diverge - and the single vertical
 * scrollbar sits flush at the table's right edge in every column-visibility
 * state (it can't drift, because the table always fills to min-width:100%).
 * The scroller owns BOTH axes: vertical for rows, horizontal (header + body
 * together, since they are one table) when columns overflow the card.
 *
 * This replaced an older two-table split (separate thead/tbody tables) whose
 * column widths were only "expected" to match - they desynced on every column
 * toggle because the header table ran table-layout:auto (via --resizable) while
 * the body ran fixed, and the scrollbar lived on a separate element that could
 * never hug the table edge. */
#tr-table-container,
#contacts-table-container,
#matters-table-container,
#trx-roll-table-container,
#aa-table-container,
#cx-table-container {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.tr-scroll {
  flex: 1;
  min-height: 0;
  overflow: auto;
}
.tr-scroll > .data-table {
  table-layout: fixed;
  width: max-content;
  min-width: 100%;
}
/* Sticky header must paint above scrolling body content (stage-select
 * dropdowns, status pills). The th already has position:sticky; top:0
 * (the th.tr-th rule); raise its stacking context so rows can't cover it.
 * The inset box-shadow re-draws the 1px bottom divider as a shadow: with
 * border-collapse:collapse a sticky cell's own border-bottom can drop out
 * mid-scroll in Chromium, so this guarantees the header stays separated. */
.tr-scroll .data-table thead th.tr-th {
  z-index: 2;
  /* Single real 1px --border bottom (from the base th.tr-th rule) - same mechanism
   * and colour as the card/toolbar top line, so the two match exactly. (No
   * box-shadow: it rendered heavier than a real border.) */
}
/* The default overflow:auto handles every host without special-casing:
 *   - Standalone (tr/contacts/matters): the container is a height-locked flex
 *     column, so .tr-scroll scrolls internally (sticky header, pinned footer).
 *   - Embedded (contact-detail tabs) + Transcripts/Documents: .tr-scroll sizes to
 *     its content (auto height), so it never traps a vertical scrollbar - the
 *     contact page / the trx container scrolls vertically - while STILL giving a
 *     horizontal scrollbar for a wide table. (overflow:visible would drop that
 *     horizontal scroll, and CSS can't mix overflow-x:auto with overflow-y:
 *     visible - the visible silently computes to auto.) */

/* Scrollbar styling. 10 CSS px ≈ 13 DevTools at 125% scaling. Colors per spec. */
.tr-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.tr-scroll::-webkit-scrollbar-track { background: var(--page-bg); }
.tr-scroll::-webkit-scrollbar-thumb { background: #898789; border-radius: 999px; }
.tr-scroll::-webkit-scrollbar-thumb:hover { background: #626162; }
.tr-scroll { scrollbar-color: #898789 var(--page-bg); }

/* ── Resizable data-table columns ── */
/* table-layout stays FIXED on the single table (set via .tr-scroll > .data-table
 * above): the resizer writes explicit px width/min/max rules which fixed honors
 * exactly, and fixed is what guarantees header and body share identical widths.
 * (The old design forced table-layout:auto here, which is what desynced the
 * header from the body - deliberately NOT reintroduced.) */
.data-table--resizable thead th { position: relative; overflow: hidden; }
.data-table--resizable thead th .col-resizer {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 6px;
  cursor: col-resize;
  user-select: none;
  background: transparent;
  transition: background 120ms;
}
.data-table--resizable thead th .col-resizer:hover,
.data-table--resizable thead th .col-resizer:active {
  background: rgba(0, 103, 209, 0.35);
}

/* ── Columns visibility dropdown ── */
.columns-menu {
  position: absolute;
  top: 100%;
  right: 16px;
  margin-top: 1px;
  z-index: var(--z-popup);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-popup);
  padding: var(--space-2) 0;
  min-width: 200px;
}
.columns-menu-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: var(--control-h);
  padding: 0 var(--space-4);
  font-size: var(--fs-md);
  cursor: pointer;
}
.columns-menu-row:hover { background: var(--page-bg); }
.columns-menu-row input[type="checkbox"] { margin: 0; }
/* New-menu rows are <button>s, not <label>s. Reset native button chrome
 * (grey bg, border, system font, shrink-to-fit width, centered text) so
 * they render identically to the Columns popup's label rows. */
button.columns-menu-row {
  width: 100%;
  background: transparent;
  border: 0;
  font: inherit;
  font-size: var(--fs-md);
  color: var(--text-primary);
  text-align: left;
}
/* New popup hugs its content (drop the shared .columns-menu 200px floor) so
 * it sizes like the Actions popup. Right-anchored in JS, so it grows left
 * from the top-right corner. Columns keeps 200px (only this ID is reset). */
#documents-new-menu { min-width: 0; }
/* TR + Contacts Columns popups are CSS-positioned (position:absolute) and
 * nudged 4px right (16→12). Vertical offset differs per page: TR sits 15px
 * up from base (margin-top 4 → -15), Contacts 12px up (→ -12). The Documents
 * Columns/New menus are JS-positioned (fixed), unaffected by this. */
#tr-columns-menu {
  right: 12px;
  margin-top: -15px;
}
#contacts-columns-menu {
  right: 12px;
  margin-top: -12px;
}

/* ── Contact detail page ────────────────────────────────────────────────────
 * Pixel-matched to Clio Manage's contact-detail page (trimmed to the four
 * tabs the firm uses). Header row, tab strip, then a 3-column dashboard
 * grid: Contact information, Custom fields, and a right-side aside with
 * Client's matters + Associated matters. */
/* Contact page = global .page-header strip (avatar + name + Client
 * pill + Edit-contact button) followed by .contact-body which holds
 * the tab bar + tab contents. The strip itself reuses .page-header /
 * .page-title / .page-actions so its dimensions, text, button
 * placement, and bottom border match Tax Returns exactly. */
.contact-page {
  /* Block layout — fills #content's width. Explicit width: 100%
   * defends against any inline-flex / shrink-wrap ancestor (so the
   * title bar + tab bar always span page-edge to page-edge, even
   * on the placeholder tabs whose body content is short). */
  display: block;
  width: 100%;
  background: var(--page-bg);
}
.contact-body { width: 100%; box-sizing: border-box; }
#contact-tab-content { display: block; width: 100%; }
.contact-body {
  /* Uniform 12 px gutter on left/right/bottom. Top padding is 0 —
   * the tab bar sits flush against the page-header; the 12 px space
   * above the topmost cards is provided by the tab bar's
   * margin-bottom below. */
  padding: 0 12px 12px;
}
/* Left-hand group inside .page-header on the contact page: avatar,
 * name, Client pill. .page-header is space-between so the right side
 * (.page-actions) automatically slides to the right edge. */
.contact-header-left {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  /* Sit at the page's normal left padding (like Clio's contact header) rather
   * than hugging the edge — no negative pull. */
  margin-left: 0;
}
.contact-avatar {
  width: 34px;
  height: 34px;
  min-width: 34px;
  border-radius: 50%;
  /* Slate gray — Clio uses a single muted color for contact avatars
   * regardless of name, so we override the per-name hash here. */
  background: #6e7e8f;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-base);
  font-weight: var(--fw-regular);
  flex-shrink: 0;
}
/* Contact name reuses .page-title (24px/700) — see .page-title above.
 * The hint below is just here to keep the legacy selector valid for
 * older markup but no longer styles anything visible. */
.contact-page-title-wrap { display: contents; }
.contact-page-title {
  font-size: var(--fs-3xl);
  font-weight: var(--fw-bold);
  line-height: 1.2;
  margin: 0;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.contact-badge {
  display: inline-flex;
  align-items: center;
  height: 19px;
  /* Explicit left + right longhand so it's obvious they match. */
  padding-top: 0;
  padding-bottom: 0;
  padding-left: 10px;
  padding-right: 10px;
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  line-height: 1;
  white-space: nowrap;
  flex-shrink: 0;
  /* 1px transparent border baseline so variants below can swap in
   * the visible border-color without the pill shifting width. */
  border: 1px solid transparent;
}
.contact-badge-client {
  /* Pale teal pill — shares the Completed stage-pill palette via tokens. */
  background: var(--pill-completed-bg);
  border-color: var(--pill-completed-border);
  color: var(--pill-completed-fg);
}
.contact-page-actions {
  /* Legacy — left for backward compat with any markup still using
   * .contact-page-actions. New code uses .page-actions. */
  margin-left: auto;
  display: flex;
  gap: var(--space-2);
}

/* Tab strip — reuses the .tab class but with its own wrapper for the
 * underlying border-bottom + bottom-margin to the content. Scoped
 * overrides below match Clio's contact-page tab chrome: slightly
 * larger font, more vertical padding, and a bolder active-tab
 * weight than the generic .tab styling. */
.contact-tabbar {
  display: flex;
  align-items: center;      /* tabs centered vertically as small rounded buttons */
  gap: var(--space-1);
  height: 40px;
  background: var(--surface-titlebar);
  border-bottom: 1px solid var(--grey-250);
  /* Pull the tab bar out of .contact-body's 12 px horizontal padding so
   * its border-bottom spans the FULL page width. margin-bottom: 12 px
   * matches the inter-card gap so the topmost cards sit 12 px below
   * the tab bar. */
  margin: 0 -12px 12px;
  padding: 0 var(--space-3);
}
/* Tabs are rounded-corner buttons (Clio's KPI-filter style) inside
 * the 40px bar — NOT full-height fills. Active state drops the
 * background fill and uses a 1px blue underline at the bar's
 * bottom edge instead (the .tab.active::after rule below). */
.contact-tabbar .tab {
  position: relative;       /* anchor for the active ::after underline */
  display: flex;
  align-items: center;
  height: 30px;
  padding: 0 8px;
  margin-bottom: 0;         /* override base .tab's -1px */
  border: 0;                /* drop the underline border from base .tab */
  border-radius: 6px;
  font-size: var(--fs-base);
  color: var(--text-slate);
  background: transparent;
}
/* :not(.active) keeps the hover style off the active tab, per spec. */
.contact-tabbar .tab:not(.active):hover {
  color: var(--text-primary);
  background: #ebf4ff;
}
.contact-tabbar .tab.active {
  color: var(--blue);
  font-weight: var(--fw-bold);
}
/* Active-tab underline. Tab is 30px centered in the 41px bar
 * (~5.5px above/below). bottom: -6px puts the line at the bar's
 * bottom border, where its 2px height overlaps the gray #d1d6e0
 * border-bottom and shows blue under the active tab. */
.contact-tabbar .tab.active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -6px;
  height: 2px;
  background: var(--blue);
}
/* Awaiting-review count on the Intake tab - Clio's red pill, same look as
   the sidebar intake badge. */
.contact-tabbar .tab .tab-badge {
  display: inline-block;
  margin-left: 5px;
  background: #d30933;
  color: #fff;
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  line-height: 16px;
  padding: 0 6px; border-radius: 999px;
  min-width: 18px; text-align: center;
}

/* Tools page reuses the .contact-tabbar look but sits directly under the
   56px page-header inside #content (which has no horizontal padding), so it
   doesn't need the -12px full-bleed margin. Defined AFTER .contact-tabbar so
   this margin wins the cascade. The tab body supplies the 12px gap below. */
.tools-tabbar { margin: 0; }
.tools-tab-content { padding: var(--space-3); }

/* Tools: OCR bake-off (compare OCR providers on one sample, side by side). */
.ocr-bakeoff { max-width: 1240px; }
.ocr-intro { font-size: var(--fs-base); color: var(--text-secondary); line-height: 1.5; margin: 0 0 var(--space-3); max-width: 72ch; }
.ocr-drop {
  display: block; border: 2px dashed var(--border); border-radius: var(--r);
  padding: var(--space-5); text-align: center; cursor: pointer;
  transition: border-color .12s, background .12s;
}
.ocr-drop:hover, .ocr-drop.is-over { border-color: var(--text-link); background: var(--blue-light, #f0f6ff); }
.ocr-drop-title { font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--text-primary); }
.ocr-drop-sub { font-size: var(--fs-smd); color: var(--text-tertiary); margin-top: 4px; }
.ocr-providers { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: var(--space-3) 0 0; }
.ocr-prov { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-base); text-transform: capitalize; cursor: pointer; }
.ocr-actions { display: flex; align-items: center; gap: var(--space-3); margin: var(--space-3) 0; }
.ocr-status { font-size: var(--fs-smd); color: var(--text-secondary); }
.ocr-unconf { font-size: var(--fs-smd); color: var(--text-tertiary); margin-bottom: var(--space-2); }
.ocr-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--space-3); align-items: start; }
.ocr-card { border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; display: flex; flex-direction: column; min-width: 0; }
.ocr-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); padding: 8px 10px; background: var(--surface-titlebar); border-bottom: 1px solid var(--border); }
.ocr-card-name { font-size: var(--fs-base); font-weight: var(--fw-semibold); text-transform: capitalize; }
.ocr-card-meta { font-size: var(--fs-2xs); color: var(--text-tertiary); text-align: right; }
.ocr-card-meta.ocr-err { color: #c0392b; }
.ocr-card-text { margin: 0; padding: 10px; font-size: var(--fs-smd); line-height: 1.45; white-space: pre-wrap; word-break: break-word; max-height: 480px; overflow: auto; font-family: ui-monospace, Menlo, Consolas, monospace; }
.ocr-card-error { padding: 10px; font-size: var(--fs-smd); color: #c0392b; white-space: pre-wrap; word-break: break-word; }

/* Matter Communications sub-tabs (Logs / Text messages) - a .contact-tabbar as the
   card's top strip, mirroring the Transcripts dashboard tabs (.irs-card-tabbar). */
.mc-card-tabbar { margin: 0; padding: 0 var(--space-4); border-radius: var(--r) var(--r) 0 0; height: 40px; flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; }
.mc-card-tabbar::-webkit-scrollbar { display: none; }
.mc-card-tabbar .tab { flex: 0 0 auto; }
.mc-card-tabbar .tab.active::after { bottom: -5px; }
.mtx-body { padding: var(--space-4); }
.mtx-threads { display: flex; flex-direction: column; gap: 16px; }
.mtx-thread { border: 1px solid #e6e8ec; border-radius: 8px; overflow: hidden; background: #fff; }
.mtx-thread-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 10px 14px; background: #f7f8fa; border-bottom: 1px solid #edeff2; }
.mtx-thread-who { font-weight: 600; font-size: var(--fs-smd); }
.mtx-thread-count { font-size: 12px; color: #6b7280; }
.mtx-thread-msgs { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px; }
.mtx-msg { max-width: 78%; padding: 8px 12px; border-radius: 12px; }
.mtx-in { align-self: flex-start; background: #f1f3f5; border-bottom-left-radius: 4px; }
.mtx-out { align-self: flex-end; background: #e8f1fd; border-bottom-right-radius: 4px; }
.mtx-msg-meta { font-size: 11px; color: #6b7280; margin-bottom: 2px; }
.mtx-msg-body { font-size: var(--fs-smd); line-height: 1.4; white-space: pre-wrap; word-break: break-word; }

/* Tax Returns top-level tab bar (Income Tax / Sales and Use Tax). Same as the
   Tools page: a full-bleed bar directly under the 56px page-header inside
   #content (no horizontal padding), so margin:0 (NOT the .contact-tabbar -12px
   bleed). The 16px gap to the card comes from .tr-page-body's top padding.
   flex:0 0 auto so it keeps its 40px height in #content's viewport-lock flex
   column and never shrinks under a tall table. */
.tr-tabbar,
.trx-tabbar { flex: 0 0 auto; margin: 0; }

/* Three-column dashboard grid. Right aside is fixed-ish so it doesn't
 * dominate; left two stretch. Wraps to single-column on narrow screens. */
.contact-dashboard-grid {
  display: grid;
  /* Three equal flexible columns — each card stretches horizontally
   * to fill its share. When the sidebar collapses, the columns grow
   * with the freed space (since the grid container widens). */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);                 /* gap BETWEEN cards (matches the uniform 12 px gutter) */
  align-items: start;        /* cards keep their natural height; no vertical stretching */
}
.contact-col { min-width: 0; }
.contact-col-aside,
.contact-col-stack { display: flex; flex-direction: column; gap: var(--space-3); }
/* Async hydration slots (employees / spouse / fiduciary) start as empty divs
 * and are replaced once their fetch lands - or removed when they resolve to
 * nothing. An empty slot left in place (e.g. a rejected relationships fetch)
 * is still a flex item, so its `gap` pushes the following card down by one
 * --space-3. Collapse any empty stack child out of flow so it can never open
 * a phantom gap above the first real card. */
.contact-col-stack > :empty { display: none; }
/* Placeholder tabs (Documents / Communications / Notes) reuse the
 * dashboard grid for spacing but collapse to a single column so the
 * "coming soon" card spans the full body width. */
.contact-placeholder-grid { grid-template-columns: minmax(0, 1fr); }
.contact-placeholder-col { min-width: 0; }
@media (max-width: 1100px) {
  .contact-dashboard-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── Matter detail ── Dashboard tab uses Clio's matter layout (main column +
 * fixed-width aside). Reuses the .contact-card chrome for the Details / Custom
 * fields / Contacts cards so the page reads identically to contact-detail. */
.matter-dashboard-grid {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-3);
  align-items: start;
}
/* With a workflow rail (Tax Prep): rail | Details | Contacts. Non-stage matters
   drop .has-workflow (JS) and the rail column is hidden, restoring the 2-col
   layout - grid auto-placement skips the display:none child. */
.matter-dashboard-grid.has-workflow {
  grid-template-columns: minmax(240px, 1fr) minmax(0, 2.1fr) minmax(0, 1.15fr);
}
.matter-dashboard-grid:not(.has-workflow) .contact-col-workflow { display: none; }
.matter-dashboard-grid .contact-col-stack,
.matter-dashboard-grid .contact-col-aside,
.matter-dashboard-grid .contact-col-workflow { min-width: 0; gap: var(--space-3); }
/* The Details card (main column) is wide, so give its labels room to keep long
   ones like "Statute of limitations date" on one line. */
.matter-dashboard-grid .contact-col-stack .contact-field-label { flex-basis: 195px; }
/* The Contacts aside is narrow and its labels are short (Phone / Email /
   Address), so the shared 168px basis over-reserved space and squeezed the
   values into heavy wrapping - give the aside a compact label column. */
.matter-dashboard-grid .contact-col-aside .contact-field-label { flex-basis: 80px; }
@media (max-width: 1300px) {
  .matter-dashboard-grid.has-workflow { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 1100px) {
  .matter-dashboard-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ── Matter workflow rail (stage progress) ─────────────────────────────────
 * Vertical timeline: date | marker (circle + connecting line) | title + desc.
 * done = brand-blue filled check; current = blue check with focus-ring halo;
 * upcoming = hollow grey numbered circle. The line segment below a step is blue
 * through done steps, grey from the current step down. */
.mw-card .contact-card-body { padding: var(--space-3) var(--space-3) var(--space-2); }
.mw-rail { list-style: none; margin: 0; padding: 2px 0 0; }
.mw-step {
  display: grid;
  grid-template-columns: 82px 26px minmax(0, 1fr);
  column-gap: 10px;
  position: relative;
  padding-bottom: 20px;
}
.mw-step:last-child { padding-bottom: 2px; }
.mw-date {
  font-size: var(--fs-sm); color: var(--text-secondary);
  line-height: 1.25; font-variant-numeric: tabular-nums;
  /* A 25px box pinned to the TOP of the (tall) row and centered, so the date
     lines up with its circle instead of drifting to the row's vertical middle. */
  min-height: 25px; align-self: start;
  display: flex; align-items: center; justify-content: flex-end;
}
.mw-marker { position: relative; display: flex; justify-content: center; }
/* Connecting line down to the next step's circle. */
.mw-marker::before {
  content: ''; position: absolute; top: 25px; bottom: -20px;
  left: 50%; transform: translateX(-50%);
  width: 2px; background: var(--grey-300, #d5dbe2);
}
.mw-step:last-child .mw-marker::before { display: none; }
.mw-dot {
  width: 25px; height: 25px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  box-sizing: border-box; z-index: 1;
}
.mw-dot .icon { width: 15px; height: 15px; }
/* done + current: filled blue circle, white check */
.mw-done .mw-dot, .mw-current .mw-dot { background: #0067d1; color: #fff; }
.mw-done .mw-marker::before { background: #0067d1; }
.mw-current .mw-marker::before { background: linear-gradient(#0067d1, var(--grey-300, #d5dbe2)); }
.mw-current .mw-dot { box-shadow: 0 0 0 3px #a2d7ff; }
/* upcoming: hollow grey numbered circle */
.mw-upcoming .mw-dot {
  background: var(--surface, #fff);
  border: 2px solid var(--grey-300, #d5dbe2);
  color: var(--text-tertiary);
}
.mw-body { min-width: 0; }
.mw-title {
  font-size: var(--fs-base); font-weight: var(--fw-semibold); line-height: 1.25; color: var(--text-primary);
  /* Center the title (first line) on the 25px circle, same as the date. */
  min-height: 25px; display: flex; align-items: center;
}
.mw-done .mw-title { color: var(--text-secondary); }
.mw-current .mw-title { color: #0067d1; }
.mw-upcoming .mw-title { color: var(--text-tertiary); font-weight: var(--fw-medium); }
.mw-desc { font-size: var(--fs-sm); color: var(--text-secondary); margin-top: 2px; line-height: 1.3; }
.mw-upcoming .mw-desc { color: var(--text-tertiary); }
/* Customize button (card-header action) + the inline stage-checklist editor. */
.mw-header { cursor: default; }
.mw-edit-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; border: none; background: transparent;
  border-radius: var(--r); color: var(--text-slate); cursor: pointer;
  transition: background var(--t), color var(--t);
}
.mw-edit-btn:hover { background: var(--grey-150); color: var(--text-primary); }
.mw-edit-btn .icon { width: 16px; height: 16px; }
.mw-edit-hint { font-size: var(--fs-sm); color: var(--text-secondary); margin: 0 0 8px; }
.mw-edit-list { list-style: none; margin: 0 0 12px; padding: 0; }
.mw-edit-item { padding: 7px 0; }
.mw-edit-item + .mw-edit-item { border-top: 1px solid var(--divider); }
.mw-edit-check { display: flex; align-items: center; gap: 8px; cursor: pointer; font-weight: var(--fw-regular); }
.mw-edit-check input { width: 15px; height: 15px; margin: 0; flex: 0 0 auto; cursor: pointer; }
.mw-edit-name { font-size: var(--fs-base); color: var(--text-primary); }
.mw-edit-cur { font-size: var(--fs-sm); color: var(--text-tertiary); }
/* The rich date picker (MslDp) sits on its own line, indented under the stage
   name (checkbox 15 + gap 8). Wrapper sets the width; the input fills it. */
.mw-edit-date { margin: 6px 0 0 23px; width: 168px; max-width: calc(100% - 23px); }
.mw-edit-date .msl-dp-input { height: 34px; }
.mw-edit-actions { display: flex; align-items: center; gap: 8px; }
.mw-edit-actions .mw-edit-reset { margin-left: auto; }
/* Matter-detail header: 2 lines (title + subtitle), so it overrides the fixed
 * 56px single-line page-header height. Same titlebar bg + grey-250 separator. */
/* Compact 2-line header (title close to subtitle), same 56px row as page-header. */
.matter-page-header { height: auto; min-height: 56px; padding-top: 0; padding-bottom: 0; }
.matter-header-titles { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.matter-title { font-size: var(--fs-2xl); font-weight: var(--fw-bold); color: var(--text-primary); line-height: 1.2; letter-spacing: -0.01em; margin: 0; }
.matter-subtitle { font-size: var(--fs-md); color: var(--text-secondary); line-height: 1.2; }
/* Matter stage dropdown reuses the Forms .select-input. JS (sizeMatterStageSelect)
 * sets its width to the LONGEST stage option so it never truncates and doesn't
 * jump width per selection; max-width keeps it inside the value column. */
.matter-stage-select { max-width: 100%; }
/* Vertically centre the "Matter stage" label against the taller dropdown row. */
.contact-field-row:has(.matter-stage-select) { align-items: center; }

/* Contacts card: WHITE card header; "Clients (N)" / "Related contacts (N)" are
 * grey, collapsible section bars (grey-250 hairline = the page-header rule).
 * Content rows pad to the card's 20px-left / 16px-right convention. */
.matter-section-head {
  display: flex; align-items: center; gap: var(--space-2);
  width: 100%; text-align: left; border: 0; cursor: pointer;
  background: var(--surface-subtle, #f4f6f8);
  padding: var(--space-2) var(--space-3) var(--space-2) var(--space-3);
  font-weight: 600;
  font-size: var(--fs-base);
  color: var(--text-primary);
  border-top: 1px solid var(--grey-250);
  border-bottom: 1px solid var(--grey-250);
}
.matter-section-chev {
  width: 12px; height: 12px; flex-shrink: 0; background-color: #001538;
  -webkit-mask: url('/img/icons/caret-down-fill.svg') center / contain no-repeat;
          mask: url('/img/icons/caret-down-fill.svg') center / contain no-repeat;
}
.matter-section.is-collapsed .matter-section-content { display: none; }
.matter-section.is-collapsed .matter-section-chev {
  -webkit-mask-image: url('/img/icons/caret-right-fill.svg');
          mask-image: url('/img/icons/caret-right-fill.svg');
}
/* Multi-Client Matters: each client is its own block; a divider separates the
 * second and later ones from the block above. */
.matter-client-block + .matter-client-block { border-top: 1px solid var(--divider); }
.matter-client-head {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-3) var(--space-1) var(--space-3);
}
/* The 4px bottom hugs the client's Phone/Email/Address rows. A client with NONE
 * (e.g. an Estate entity) leaves an empty contact slot, so that tight bottom
 * would sit the lone name 4px above the next section but 12px below the one
 * above it. When the slot is empty, match the bottom gap to the top so the name
 * is centred in its band. */
.matter-client-head:has(+ .matter-client-contact-slot:empty) { padding-bottom: var(--space-3); }
.matter-contact-avatar {
  width: 30px; height: 30px; min-width: 30px; border-radius: 50%;
  background: #6e7e8f; color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: var(--fw-regular); font-size: var(--fs-sm); flex-shrink: 0;
}
.matter-client-namerow { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; min-width: 0; }
.matter-client-namerow .cell-link { font-weight: 600; }
/* Related-contact header: name link + role stacked (no avatar), aligned with the
 * Client blocks via the shared .matter-client-head padding. */
.matter-related-nameblock { display: flex; flex-direction: column; min-width: 0; }
.matter-related-role { color: var(--text-slate); font-size: var(--fs-base); margin-top: -1px; }
.matter-billrecipient {
  font-size: var(--fs-xs); font-weight: 500; color: var(--text-secondary);
  background: #e9edf2; border-radius: 999px;
  padding: 1px 8px; white-space: nowrap;
}
/* Related contacts on the matter Contacts card now reuse the company Employees
   card's .employee-block (name link + role + Phone/Email/Address + separators);
   the old bespoke .matter-contact-row/name/role rules were removed. */
/* Matter Documents tab now reuses the contact Documents card (.docs-* /
   #documents-* styles); the old bespoke .matter-docs-* rules were removed. */
.row-clickable { cursor: pointer; }
.row-clickable:hover { background: var(--surface-subtle, #f6f8fa); }

/* ── Matter Court case card (screenshot extraction → review → save) ── */
.court-drop {
  border: 1.5px dashed var(--border);
  border-radius: var(--r);
  padding: var(--space-4);
  text-align: center;
  background: var(--surface-subtle, #fafbfc);
  outline: none;
  transition: border-color .12s, background .12s;
}
.court-drop.is-drag { border-color: var(--text-link); background: rgba(0, 103, 209, 0.04); }
.court-drop.is-compact { padding: var(--space-3); margin-top: var(--space-3); }
.court-thumbs { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.court-thumb { position: relative; width: 110px; height: 70px; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; background: #f6f8fa; }
.court-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.court-thumb-x {
  position: absolute; top: 2px; right: 2px; width: 18px; height: 18px;
  border: 0; border-radius: 50%; background: rgba(0, 0, 0, 0.6); color: #fff;
  font-size: 13px; line-height: 1; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
}
.court-thumb-x:hover { background: rgba(0, 0, 0, 0.85); }
.court-drop-main { font-weight: 500; margin: 0 0 var(--space-1); }
.court-drop-hint { font-size: var(--fs-sm); color: var(--text-secondary); margin: 0 0 var(--space-2); }
.court-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }
.court-fields-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2) var(--space-3); }
@media (max-width: 700px) { .court-fields-grid { grid-template-columns: 1fr; } }
.court-role-pick { border: 1px solid var(--border); border-radius: var(--r); padding: var(--space-3); margin-bottom: var(--space-3); }
.court-role-pick.court-needs { border-color: #d9a300; background: #fffbe9; }
.court-role-row { display: flex; gap: var(--space-2); }
.court-role-row select { max-width: 180px; }
.court-hint { font-size: var(--fs-sm); color: var(--text-secondary); margin: var(--space-1) 0 0; }
.court-flag { font-size: var(--fs-xs); font-weight: 600; color: #b97f00; background: #fff3cf; border-radius: 4px; padding: 1px 6px; }
.court-lowconf input { border-color: #d9a300; }
.court-review-banner { background: #fffbe9; border: 1px solid #ecd58a; border-radius: var(--r); padding: var(--space-2) var(--space-3); font-size: var(--fs-sm); margin-bottom: var(--space-3); }
.court-parties { margin-bottom: var(--space-3); }
.court-party-row { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-2); padding: var(--space-2) 0; border-bottom: 1px solid var(--divider); }
.court-party-row:last-child { border-bottom: 0; }
.court-party-info { font-size: var(--fs-sm); min-width: 0; }
.court-atty { color: var(--text-secondary); font-size: var(--fs-xs); margin-top: 2px; }
.court-pill { display: inline-block; font-size: var(--fs-xs); font-weight: 600; color: var(--text-secondary); background: var(--surface-subtle, #eef1f5); border-radius: 4px; padding: 0 6px; }

/* Card framing matches the Tax Returns card style (border, radius,
 * subtle shadow) so the chrome reads as one design language. */
.contact-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.contact-card-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  /* Snapped to the 4px grid: bottom 4 (--space-1) → header→first-row gap
   * = 4 + 8 = 12 px (was 2px / 10px gap before the spacing sweep). */
  padding: var(--space-3) var(--space-3) var(--space-1) var(--space-3);
  /* No separator under the card title — matches the rest of the
   * unsegmented card design (no row separators inside cards either,
   * except .contact-matter-item per spec). */
  background: var(--surface);
  cursor: pointer;
  user-select: none;
}
.contact-card-chev {
  width: 12px;
  height: 12px;
  flex-shrink: 0;
  background-color: #001538;
  -webkit-mask: url('/img/icons/caret-down-fill.svg') center / contain no-repeat;
          mask: url('/img/icons/caret-down-fill.svg') center / contain no-repeat;
}
/* Collapsed state — header still shows; everything else inside the
 * card is hidden. Header gets a balanced 10 px bottom padding (vs 2
 * when expanded) so it doesn't look top-heavy. Chevron swaps from
 * caret-down to caret-right. */
/* Right-aligned action in a card header (e.g. the small New matter button on
 * the Client's matters card). */
.contact-card-action { margin-left: auto; flex-shrink: 0; }
.contact-card.is-collapsed .contact-card-action { display: none; }
.contact-card.is-collapsed .contact-card-header { padding-bottom: var(--space-3); }
.contact-card.is-collapsed > :not(.contact-card-header) { display: none !important; }
.contact-card.is-collapsed .contact-card-chev {
  -webkit-mask-image: url('/img/icons/caret-right-fill.svg');
          mask-image: url('/img/icons/caret-right-fill.svg');
}
.contact-card-title {
  font-size: var(--fs-xl);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.contact-card-body { padding: 0; }
.contact-empty {
  /* Empty card body — treat as both first and last row, so the
   * bottom uses the "after last row" value (18). */
  padding: var(--space-3);
  color: var(--text-primary);
  font-size: var(--fs-base);
  margin: 0;
}

.contact-field-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  /* bottom: 2 between rows; :last-child override below restores the
   * outer 18 px after the last row. */
  padding: 8px var(--space-3) 2px var(--space-3);
  font-size: var(--fs-base);
  /* No row separators inside the Contact information / Custom fields
   * cards — matches Clio. The matters card keeps its per-item
   * separators (.contact-matter-item below). */
}
.contact-field-row:last-child { padding-bottom: var(--space-3); }
/* Subheading inside the merged Contact-info + Custom-fields card.
 * Sits between the contact-info rows and the custom-field rows so
 * the two sections read as one card visually but stay clearly
 * labelled. Faint top border separates without making them feel
 * like separate cards. Matches the contact-card-title weight but
 * a notch smaller so the hierarchy reads cleanly. */
.contact-card-subheading {
  padding: 16px var(--space-3) 4px var(--space-3);
  margin-top: 10px;
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  border-top: 1px solid var(--divider);
}
.contact-field-label {
  flex: 0 0 168px;
  color: var(--text-primary);
  font-weight: var(--fw-bold);
}
.contact-field-value {
  flex: 1;
  color: var(--text-primary);
  word-break: break-word;
}
/* Employees card — each employee is a stacked block with name +
 * title at the top and Phone / Email / Address rows underneath
 * (mirrors Clio's Employees panel). */
.contact-employee-name { font-weight: var(--fw-bold); }
.contact-employee-title { color: var(--text-slate); font-size: var(--fs-base); margin-top: -2px; }
.employee-block {
  padding: var(--space-3) var(--space-3) 14px var(--space-3);
  border-bottom: 1px solid var(--divider, #e9ecf0);
}
.employee-block:last-child { border-bottom: 0; padding-bottom: var(--space-3); }
.employee-header {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  margin-bottom: 10px;
}
.employee-header-text { flex: 1; min-width: 0; }
/* Tighter padding for the field rows nested inside an employee
 * block — the outer block already supplies horizontal padding. */
.employee-field-row {
  padding: var(--space-1) 0 !important;
}
.employee-field-row .contact-field-label { flex: 0 0 100px; }

.contact-info-kind  { color: var(--text-slate); font-size: var(--fs-base); }
.contact-info-more  { color: var(--text-slate); font-size: var(--fs-base); margin-left: var(--space-1); }
.contact-info-empty { color: var(--text-primary); }

/* Copy buttons on Phone / SSN / EIN contact-card rows. The flex row's value
   cell is flex:1, so this fixed-width button sits flush at the card's right
   edge. The icon swaps to a check for ~1.2s after a copy. */
.contact-copy-btn {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  /* The 26px button is taller than the 21px text line; with the row's
   * align-items:flex-start that made copy-button rows (SSN, DOB) taller than
   * text-only rows (uneven vertical rhythm). Negative block margins drop its
   * height contribution to ~20px so every row is text-line-height tall. */
  margin-top: -3px; margin-bottom: -3px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; border: none; background: transparent;
  border-radius: var(--r); color: var(--text-slate); cursor: pointer;
  transition: background var(--t), color var(--t);
}
.contact-copy-btn:hover { background: var(--grey-150); color: var(--text-primary); }
.contact-copy-btn:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }
.contact-copy-btn .icon { --icon-url: url('/img/icons/copy.svg'); width: 16px; height: 16px; }
.contact-copy-btn.is-copied { color: var(--success); }
.contact-copy-btn.is-copied .icon { --icon-url: url('/img/icons/check.svg'); }
/* Inline (per-item) copy button — one per phone number, laid out within the
 * value cell next to its own number (additional numbers each get one too). */
.contact-info-line { display: flex; align-items: center; gap: var(--space-2); }
.contact-info-line + .contact-info-line { margin-top: 2px; }
.contact-info-line-val { flex: 1 1 auto; min-width: 0; word-break: break-word; }
.contact-copy-inline { width: 24px; height: 24px; flex: 0 0 auto; }
/* Address-part copy chips (Street / City / State / ZIP) under the address block,
 * so the user grabs the part they need instead of the whole address. */
.contact-copy-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.contact-copy-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 9px; height: 22px;
  border: 1px solid var(--border-strong); border-radius: 999px;
  background: var(--surface); color: var(--text-slate);
  font-size: var(--fs-sm); font-weight: var(--fw-medium); cursor: pointer;
  transition: background var(--t), color var(--t), border-color var(--t);
}
.contact-copy-chip:hover { background: var(--grey-150); color: var(--text-primary); }
.contact-copy-chip:focus-visible { outline: 2px solid var(--blue); outline-offset: 1px; }
.contact-copy-chip .icon { --icon-url: url('/img/icons/copy.svg'); width: 13px; height: 13px; }
.contact-copy-chip.is-copied { color: var(--success); border-color: var(--success); }
.contact-copy-chip.is-copied .icon { --icon-url: url('/img/icons/check.svg'); }
.contact-info-address { line-height: 1.45; }
/* Uniform 24px line height for EVERY address line so County/Country (plain
 * <div>s, ~20px) don't sit tighter than Street/ZIP (.addr-line rows sized by the
 * 24px inline copy button). Kept as blocks (not flex) so inline text + the
 * "(Home)" kind span stay on one line. */
.contact-info-address > div { line-height: 24px; }
/* Address lines that carry a Street/ZIP copy icon: a flex row so the icon is
 * pushed to the RIGHT edge (margin-left:auto), right-aligned like the EIN/phone
 * copy buttons rather than sitting immediately after the text. */
.addr-line { display: flex; align-items: flex-start; gap: var(--space-2); }
.addr-line > .contact-copy-inline { margin-left: auto; }
/* The line's text is ONE flex child so it wraps at word boundaries within
 * itself; without this the street's plain text and its nowrap .addr-unit span
 * are SEPARATE flex items, and a wide unit can crush the loose text into
 * letter-per-line columns (the "J W Peck Federal Building" bug). */
.addr-line > .addr-text { flex: 1 1 auto; min-width: 0; }
/* When more than one address is listed in the same cell, give each
 * subsequent block a small top-margin so they don't visually run
 * together (each address is its own multi-line block). */
.contact-info-address + .contact-info-address { margin-top: var(--space-2); }
/* The "Home" (address-kind) label sits inside .contact-info-address
 * wrapped in <strong>. Bold weight with the same secondary color
 * (#516378) as the other address-kind labels like "(Mobile)"/"(Home)". */
.contact-info-address strong { font-weight: var(--fw-bold); color: var(--text-slate); }

/* Contact tags — Clio-style pill: bordered rounded box with a coloured
 * dot + label. The dot colour comes from --tag-color (set inline per
 * tag from the Clio `color` field). Used inside the Contact information
 * card's Tags row and the Contacts list Tags column. */
.contact-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);                    /* dot ↔ text spacing */
  height: 29px;
  padding: 0 var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--r);     /* rounded box, matches buttons */
  background: #fff;
  font-size: var(--fs-md);
  color: var(--text-primary);
  white-space: nowrap;
  vertical-align: middle;
  box-sizing: border-box;
}
.contact-tag + .contact-tag { margin-left: var(--space-2); }
.contact-tag-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--tag-color, #d563a4);
  flex-shrink: 0;
}
.contact-tag-label { line-height: 1; }

/* Matter list inside the Client's matters card. */
.contact-matter-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.contact-matter-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  /* padding-bottom 13 (above the separator) + padding-top 11 (below it
   * on the next row) per spec — separator gets 13 above / 11 below. */
  padding: 11px var(--space-3) 13px var(--space-3);
  font-size: var(--fs-base);
  color: var(--text-primary);
  border-bottom: 1px solid var(--divider, #e9ecf0);
}
.contact-matter-item:last-child { border-bottom: 0; padding-bottom: var(--space-3); }
.contact-matter-main { flex: 1; min-width: 0; line-height: 1.4; word-break: break-word; }
.contact-matter-desc { color: var(--text-primary); }
/* Associated matters: Clio-style per-row expander revealing the contact's
 * relationship on that matter ("Curator" - Related contact). Same caret masks
 * as the contact-card collapse chevron; blank spacer keeps role-less rows
 * aligned. */
.cm-role-chev {
  width: 12px; height: 12px; margin-top: 4px; padding: 0; border: 0;
  flex: 0 0 auto; cursor: pointer; background-color: #001538;
  -webkit-mask: url('/img/icons/caret-right-fill.svg') center / contain no-repeat;
          mask: url('/img/icons/caret-right-fill.svg') center / contain no-repeat;
}
.cm-role-chev[aria-expanded="true"] {
  -webkit-mask-image: url('/img/icons/caret-down-fill.svg');
          mask-image: url('/img/icons/caret-down-fill.svg');
}
.cm-role-chev.is-blank { visibility: hidden; cursor: default; }
.cm-role-row {
  display: flex; align-items: center; gap: var(--space-2);
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--divider, #e9ecf0);
}
/* display:flex above would defeat the hidden attribute (UA display:none loses
 * to any author display) - restore it so the caret toggle actually collapses. */
.cm-role-row[hidden] { display: none; }
.cm-role-kind { margin-left: auto; color: var(--text-tertiary); font-style: italic; }
.contact-stage-pill {
  display: inline-flex;
  align-items: center;
  height: 22px;
  /* Literal 10px (NOT --space-3): pill padding is tuned for a tight text fit. */
  padding: 0 10px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: var(--fs-sm);
  white-space: nowrap;
  flex-shrink: 0;
}
/* Variant colors mirror the Tax Returns .stage-select rules so the
 * contact-page matters card uses the same palette as the TR table. */
.contact-stage-pill.is-active    { background: var(--pill-active-bg);    border-color: var(--pill-active-border);    color: var(--pill-active-fg); }
.contact-stage-pill.is-on-hold   { background: var(--pill-onhold-bg);    border-color: var(--pill-onhold-border);    color: var(--pill-onhold-fg); }
.contact-stage-pill.is-completed { background: var(--pill-completed-bg); border-color: var(--pill-completed-border); color: var(--pill-completed-fg); }
/* Closed matters get a muted grey so the eye instantly registers
 * "no active work" — distinct from completed-but-still-open stages
 * like "Mailed" or "Delivered". */
.contact-stage-pill.is-closed    { background: var(--pill-closed-bg);    border-color: var(--pill-closed-border);    color: var(--pill-closed-fg); }
/* Negative terminal state (e.g. a rejected letter) - red, reusing the shared
 * overdue status tokens so it matches the .badge-overdue family. */
.contact-stage-pill.is-rejected  { background: var(--status-overdue-bg);  border-color: var(--status-overdue-border);  color: var(--status-overdue-text); }

/* ── Tax-return filter strips ── */
.tr-filter-section { display: flex; flex-direction: column; gap: var(--space-2); }
/* One global toolbar rhythm: every button/box in a filter row sits a uniform
 * 8px from its neighbour. Coupled sub-groups (the two date pickers inside
 * .mc-date-range) keep their own tighter 5px gap; everything else is 8px. */
.tr-filter-row { display: flex; align-items: center; gap: var(--space-2); }
.tr-filter-label {
  font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text-secondary);
  text-transform: uppercase; letter-spacing: 0.04em;
  min-width: 56px; text-align: right; flex-shrink: 0;
}
/* Embedded Tax Returns tab adds a "SECTION" label, which is wider than the
 * default 56px column - widen the column there so every row's strip lines up
 * (the standalone page, with no SECTION row, keeps the tighter 56px). */
.tr-page-body.is-embedded .tr-filter-label { min-width: 70px; }
.tab-strip.tr-filter-strip { display: inline-flex; flex-wrap: wrap; }
/* The Status row hosts the strip plus Search + Columns on the right.
 * The strip itself wraps to 2 lines as needed and shrinks to leave room
 * for the action controls on the far right. */
/* Status row: label aligns with the FIRST line of the pill strip (top),
 * while Search + Columns align with the LAST line (bottom). */
.tr-filter-row--status { align-items: flex-end; }
.tr-filter-row--status > .tr-filter-label {
  align-self: flex-start;
  /* Label sits 10 px from the top so its midline lines up with the
   * first 40 px pill row's midline. */
  padding-top: 10px;
}
/* Any .spacer inside a filter row is the flex stretcher that
 * pushes the search box + Columns button to the right edge —
 * used by both the TR Status row and the Contacts page filter row. */
.tr-filter-row > .spacer,
.tr-filter-row--status > .spacer { flex: 1 1 0; }
.tab-strip.tr-filter-strip--multi {
  /* Status strip matches the other filter strips at the control height
   * with 32 px pills (see height override on the nested button
   * rule below). */
  min-height: var(--control-h);
  /* Strip is sized to its pills, never stretched and never overflowing.
   *   flex: 0 1 auto     → don't grow past content, but can shrink
   *   min-width: 0       → defeat the default min-width:auto on flex
   *                        items (which resolves to min-content, i.e.
   *                        the widest pill — that's what was forcing
   *                        the strip past the row's edge when total
   *                        pill width exceeded the viewport)
   *   flex-wrap: wrap    → pills wrap to a second row when the strip
   *                        shrinks below their combined width
   *   max-width: 100%    → never overflow the parent
   * The .spacer beside this (flex: 1 1 0) absorbs any leftover slack
   * so Search + Columns stay on the right edge. */
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  flex-wrap: wrap;
  align-items: center;
  min-height: var(--control-h);
}
.tab-strip.tr-filter-strip--multi .tab-strip-btn { flex: 0 0 auto; height: 32px; }

/* ── Clio-style Card (wraps tables, tabs, etc.) ── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.card-toolbar {
  display: flex;
  flex-direction: column;
  /* 8px between stacked filter rows/controls (the sitewide "between items" gap). */
  gap: var(--space-2);
  /* Uniform 12px on every edge so every list-page toolbar sits its controls the
   * same 12px from the card edge (was an asymmetric 15/12 that varied per page). */
  padding: var(--space-3);
  border-bottom: 1px solid var(--border);
  position: relative; /* anchor for the Columns dropdown */
}
/* Contacts card has only one filter row — tighten vertical padding
 * so the toolbar height matches the 64 px card-footer. (12 + 40 px
 * strip + 12 = 64, vs the TR card whose toolbar is taller because
 * it has Year + Type + Status rows.) */
#contacts-card .card-toolbar,
#matters-card .card-toolbar { padding: var(--space-3); }
/* Documents tab card toolbar matches the Contacts header height (64px). */
#documents-card .card-toolbar { padding: 12px 12px 11px; }
/* The bottom row inside the toolbar that holds the search box and
 * Columns button. Filters live above this in their own rows. */
.toolbar-controls {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  position: relative;
}
.toolbar-controls > .spacer { flex: 1; }

/* ── Clio-style Tab Strip (segmented control with the same rounded
 *    rectangle radius as the topbar search box — not a pillbox) ──
 * Strip   = 38px CSS tall (≈ 49px DevTools at user's 125% scaling)
 * Buttons = 30px tall pills (38 − 3px padding × 2 − 2px border = 30)
 * hover / active background = #ebf4ff
 * active text colour        = #0067d1 (bold)
 * Status strip wraps to two rows, so `min-height` not `height`. */
.tab-strip {
  display: inline-flex;
  align-items: center;
  /* #fffcff matches .page-header and .contact-tabbar so every
   * title-bar / tab-bar / filter-strip surface reads as the same
   * off-white tone. !important defends against any rendering that
   * resolves to #fff9ff via cascade / inheritance — the strip is
   * always exactly #fffcff. */
  background: var(--surface-titlebar) !important;
  border-radius: var(--r);
  /* `padding` is the gap between any pill (active or otherwise) and the
   * outer strip border. `gap` is the gap between adjacent pills.
   * Keep them equal so the visual rhythm is uniform. */
  padding: 3px;
  gap: var(--space-1);
  /* Slate-grey #a7b3c2 keeps the KPI-filter strip in the same chrome
   * tone as the toolbar search input and .btn-secondary outline. */
  border: 1px solid var(--border-control);
  min-height: var(--control-h);
  box-sizing: border-box;
}
.tab-strip-btn {
  border: 0;
  background: transparent;
  /* Base + hover text colors and font-weight mirror the contact-page
   * tab bar so the KPI filter pills read with the same visual
   * vocabulary. Active state keeps the KPI filled-pill look
   * (#ebf4ff bg + #0067d1 bold text). */
  color: var(--text-slate);
  font-family: inherit;
  font-size: var(--fs-base);
  font-weight: var(--fw-regular);
  padding: 0 var(--space-3);
  min-width: 48px;
  height: 32px;
  border-radius: calc(var(--r) - 2px);
  cursor: pointer;
  transition: background var(--t), color var(--t);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.tab-strip-btn:hover { background: #ebf4ff; color: var(--text-primary); }
.tab-strip-btn.active {
  background: #ebf4ff;
  color: var(--blue);
  font-weight: var(--fw-bold);
}

/* ── Toolbar form controls (search, dropdowns) ── */
.toolbar-search {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--surface);
  /* Slate-grey border (#a7b3c2) matches the .tab-strip / .btn-secondary
   * border tone so every toolbar control reads as the same chrome. */
  border: 1px solid var(--border-control);
  border-radius: var(--r);
  padding: 0 var(--space-3);
  height: var(--control-h);
  width: 210px;
  flex: 0 0 210px;
  transition: border-color var(--t);
}
/* Active/focus state: keep the same slate-grey border and rely on the
 * outer halo to signal focus — avoids a blue-edge flash that's louder
 * than the rest of the toolbar's chrome. */
.toolbar-search:focus-within {
  border-color: var(--border-control);
  box-shadow: 0 0 0 3px rgba(18, 115, 214, 0.18);
}
.toolbar-search svg { width: 14px; height: 14px; color: var(--text-tertiary); flex-shrink: 0; }
.toolbar-search input {
  border: 0; outline: 0; background: transparent;
  font: inherit; font-size: var(--fs-base);
  width: 100%; min-width: 0;
}

/* Clear-button (X) shown inside a search field when it has content.
 * Used in both the topbar global search and the tax-returns toolbar search.
 * Phosphor x-bold via mask + currentColor-style tint of #385da0.
 *
 * 10 px box per spec, with mask-size 250 % so the SVG renders ~2.5× larger
 * than the visible region — the X corners crop inward and the strokes
 * still read thick on the small button. */
.search-clear {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  border: 0;
  padding: 0;
  cursor: pointer;
  background-color: #385da0;
  -webkit-mask: url('/img/icons/x-bold.svg') center / 250% no-repeat;
          mask: url('/img/icons/x-bold.svg') center / 250% no-repeat;
}
.search-clear:hover { background-color: #1f3d75; }
/* In the topbar (dark blue bg), keep the brand-light tint readable. */
.topbar-search .search-clear { background-color: var(--topbar-search-text, #cbe7ff); }
.topbar-search .search-clear:hover { background-color: #fff; }
.toolbar-select {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0 28px 0 10px;
  height: 32px;
  font: inherit; font-size: var(--fs-md);
  color: var(--text-primary);
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url('/img/icons/caret-down-fill.svg');
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 12px;
}
.toolbar-select:focus { outline: 2px solid var(--blue); outline-offset: -1px; }

/* ── Clio-style Table (lives inside .card) ── */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-base);
  /* Pin base text color explicitly so it can't drift from
   * --text-primary on any browser/render path. */
  color: var(--text-primary);
}
.data-table tbody td { color: var(--text-primary); }
/* Second table header (column-label row inside the table card). The
 * dedicated `.tr-th` class is unique to the tax-returns table thead so
 * no other selector can compete; the rule wins regardless of cascade.
 *
 * Spec: 38 CSS px tall (≈49 DevTools at 125% scaling), normal case (not
 * uppercase), text #1e2535, vertical separators between cells, body hover
 * #e9e9f0. Matches the Clio Matters/Tasks table chrome. */
th.tr-th,
.card .data-table thead th.tr-th,
.data-table thead th.tr-th {
  text-align: left !important;
  font-size: var(--fs-md) !important;
  font-weight: var(--fw-semibold) !important;
  text-transform: none !important;
  letter-spacing: 0 !important;
  color: var(--text-primary) !important;
  background: var(--page-bg, #f5f3f7) !important;
  padding: 0 var(--space-3) !important;   /* 12px both sides: matches the body cells (12px) + the toolbar/footer so the table content aligns at 12px from the card edge on both sides */
  height: 31px !important;
  border-bottom: 1px solid var(--border) !important;   /* match the card/toolbar top line */
  border-right: 1px solid var(--grey-250) !important;
  white-space: nowrap !important;
  position: sticky !important;
  top: 0 !important;
}
/* Last cell has no right border so the table edge isn't doubled. */
.data-table thead th.tr-th:last-child { border-right: 0 !important; }
/* Header interaction states (sortable cells only — non-sortable headers
 * like Notes have no hover effect).
 *
 *   sortable, unsorted, hover         → #e9e9f0  (light wash, "actionable")
 *   sortable, sorted (asc or desc)    → #e9e9f0  (same wash flags active)
 *   sortable, sorted, hover           → #d1d6e0  (darker wash deepens it)
 *
 * Specificity is prefixed with `.card .data-table thead` so these rules
 * win over the base `.card .data-table thead th.tr-th` (0,3,2) background. */
.card .data-table thead th.tr-th.sortable:hover,
.data-table thead th.tr-th.sortable:hover,
.card .data-table thead th.tr-th.sorted-asc,
.card .data-table thead th.tr-th.sorted-desc,
.data-table thead th.tr-th.sorted-asc,
.data-table thead th.tr-th.sorted-desc { background: var(--grey-150) !important; }
.card .data-table thead th.tr-th.sorted-asc:hover,
.card .data-table thead th.tr-th.sorted-desc:hover,
.data-table thead th.tr-th.sorted-asc:hover,
.data-table thead th.tr-th.sorted-desc:hover { background: var(--grey-250) !important; }
/* When ANY column is sorted, suppress the hover wash on the OTHER
 * (non-sorted) sortable cells so only the actively-sorted column reacts
 * to pointer. Uses :has() — modern browsers only, fine for Edge/Chromium. */
.card .data-table thead:has(.tr-th.sorted-asc, .tr-th.sorted-desc) th.tr-th.sortable:not(.sorted-asc):not(.sorted-desc):hover,
.data-table thead:has(.tr-th.sorted-asc, .tr-th.sorted-desc) th.tr-th.sortable:not(.sorted-asc):not(.sorted-desc):hover {
  background: var(--page-bg, #f5f3f7) !important;
}
/* Center-aligned columns (Year, ID, EF ack, POA). Headers + body cells.
 * Specificity prefix matches the base `.card .data-table thead th.tr-th`
 * rule (0,3,2) so this beats its text-align:left !important. */
.card .data-table thead th.tr-th.col-year,
.card .data-table thead th.tr-th.col-id,
.card .data-table thead th.tr-th.col-ef_status,
.card .data-table thead th.tr-th.col-poa,
.data-table thead th.tr-th.col-year,
.data-table thead th.tr-th.col-id,
.data-table thead th.tr-th.col-ef_status,
.data-table thead th.tr-th.col-poa { text-align: center !important; }
.card .data-table tbody td.col-year,
.card .data-table tbody td.col-id,
.card .data-table tbody td.col-ef_status,
.card .data-table tbody td.col-poa,
.data-table tbody td.col-year,
.data-table tbody td.col-id,
.data-table tbody td.col-ef_status,
.data-table tbody td.col-poa { text-align: center !important; }
/* Center the text INSIDE the cell-text input (ID column) and cell-select
 * (EF ack column) — width:100% means the element itself is centered, but
 * the text within still needs its own alignment.
 *
 * For the EF ack <select>, `text-align-last: center` centers the closed
 * (displayed) value WITHOUT propagating to the open dropdown's <option>s,
 * which we explicitly left-align below. */
.data-table tbody td.col-id .cell-text { text-align: center; }
/* Whole EF-ack cell is the click target (forwards to the tiny caret trigger via
 * trCellSelectClick), so signal it's clickable across the cell's dead space. */
.data-table tbody td.col-ef_status { cursor: pointer; }
.data-table tbody td.col-ef_status .cell-select { text-align-last: center; }
.data-table tbody td.col-ef_status .cell-select option { text-align: left; }
/* Enhanced EF-ack select: centre the single short code on the trigger (the pop
 * rows show the long label and stay left-aligned via .ui-pop-row). */
.data-table tbody td.col-ef_status .ui-select-trigger,
.data-table tbody td.col-ef_status .ui-select-label { text-align: center; }
/* EF-ack control reads like the 2848 checkbox: a compact, centered box that
 * reveals its outline on hover (empty OR filled), so the click target is
 * discoverable without hunting for the caret. Pairs with trCellSelectClick,
 * which opens it from a click anywhere in the cell's dead space. */
.data-table tbody td.col-ef_status .ui-select { display: flex; justify-content: center; }
.data-table tbody td.col-ef_status .ui-select-trigger {
  width: auto; min-width: 24px; min-height: 22px; padding: 1px 4px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 4px; box-sizing: border-box;
  /* Centre the Excel-style caret in the box - the base .cell-select anchors it
   * to the right edge, which reads off-centre in this narrow box. */
  background-position: center;
}
.data-table tbody td.col-ef_status:hover .ui-select-trigger { box-shadow: inset 0 0 0 1px var(--grey-250); }
/* Sortable column headers — Clio pattern. Two small fill carets stacked
 * vertically next to the label. Caret color is always the same as the
 * header text (#1e2535) — no opacity dimming. Clicking cycles the sort
 * (none → asc → desc → none); when sorted, the INACTIVE direction
 * disappears (display:none) and only the active caret shows. */
.tr-th.sortable { cursor: pointer; user-select: none; }
.tr-th .th-label {
  vertical-align: middle;
  /* Right buffer so labels can't crash into the absolutely-positioned
   * sort indicator. 16 px = 11 indicator + ~5 px slack — tighter than
   * the earlier 22 so narrow columns (Year, POA, EF ack) don't have
   * their label clipped. */
  padding-right: var(--space-4);
}
.tr-th .sort-indicator {
  /* Pinned to the right edge of the cell so the indicator sits at the
   * END of the header text, matching Clio. position: absolute is
   * resolved against .tr-th's positioned context (the !important
   * position: sticky declaration above).
   * right: 8 (was 14) — tighter to the cell edge so the indicator
   * doesn't intrude into the label area on narrow columns. */
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  line-height: 0;
}
/* SORTED = one 11px caret, centered. Inline <svg fill=currentColor> (same as the
 * unsorted combined glyph) rather than a CSS-masked .icon span, so the caret
 * rasterizes consistently at fractional display scaling too. */
.tr-th .sort-indicator .sc-caret {
  display: block;
  width: 11px;
  height: 11px;
}
/* SORTED = one 11px caret, centered (the other caret + the combined glyph hidden). */
.tr-th.sorted-asc  .sort-indicator .caret-dn { display: none; }
.tr-th.sorted-desc .sort-indicator .caret-up { display: none; }
/* UNSORTED = a single inline-SVG combined up/down glyph (fill: currentColor),
 * NOT a CSS mask. The two carets + the baked gap are drawn as one vector unit,
 * so the glyph looks identical in every header cell. The old approach masked
 * the glyph onto the box with `center / contain`, which re-rasterized it at
 * each cell's fractional sub-pixel position — column width and header height
 * vary per table, and at fractional display scaling that made the gap render
 * "hit or miss" from cell to cell (worse on pages with taller headers). */
.tr-th .sort-indicator .caret-both {
  display: none;
  width: 11px;
  height: 16px;
}
.tr-th.sortable:not(.sorted-asc):not(.sorted-desc) .sort-indicator .sc-caret { display: none; }
.tr-th.sortable:not(.sorted-asc):not(.sorted-desc) .sort-indicator .caret-both { display: block; }

.data-table tbody td {
  padding: 0 var(--space-4) 0 var(--space-3);   /* left 12 to stay aligned with the header */
  height: 40px;
  border-bottom: 1px solid var(--divider, #e9ecf0);
  vertical-align: middle;
  color: var(--text-primary);
}
/* Cap row height ~44 CSS (57 DevTools / 1.295). */
.data-table tbody tr { height: 40px; }
/* Browser-default checkbox margins push the box visibly upward in a
 * tall row. Reset and align it to middle so the PoA box lines up with
 * the row's text. */
.data-table tbody td input[type="checkbox"] {
  margin: 0;
  vertical-align: middle;
}
/* PoA checkbox specifically: text-align:center on the td wasn't
 * centering it (the checkbox is an inline-block that some browsers
 * align to the start of the line-box). Force it to a centered block
 * via auto margins. The td's vertical-align:middle centers it
 * vertically within the row. */
/* 2848: a plain clickable toggle - NO native checkbox (appearance:none still left a residual
 * box/check in Edge that composited under the icon and made it look messy). On file = a green
 * check-bold rendered via the .icon mask mechanism (same as the doc file-type icons, so it's
 * clean + consistent); off = empty. Click toggles; faint box on hover for discoverability. */
/* Reused: the 2848 column AND the OIC "Filed by us" column. */
.poa-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; margin: 0 auto;
  background: transparent; border: 0; border-radius: 4px; cursor: pointer;
}
.poa-toggle:hover { box-shadow: inset 0 0 0 1px var(--grey-250); }
.poa-toggle.cb-busy { pointer-events: none; }
.poa-toggle .icon { width: 18px; height: 18px; color: transparent; }
.poa-toggle.is-on .icon { color: #067647; }

/* Yes/No custom-field values render as an icon pair (matches the 2848
   column's check): Yes = green check, No = red x, both 18px. Inline SVG (see
   cfYesNoIcon) rather than a mask so the glyphs stay crisp + uniform at 125%
   scaling. Boxed to the 21px line-height + top-aligned so the glyph centers on
   the value's FIRST text line, keeping every icon aligned no matter how the
   label wraps. */
.cf-bool {
  display: inline-flex;
  align-items: center;
  height: 21px;
  vertical-align: top;
}
.cf-bool svg { display: block; width: 18px; height: 18px; }
.cf-bool-yes { color: #067647; }
.cf-bool-no  { color: #b42318; }
/* A street's secondary unit (Apt 4B, Ste 260…) never splits, so a wrapping
   address breaks right before it rather than mid-unit. */
.addr-unit, .addr-civic { white-space: nowrap; }
.data-table tbody tr:hover td {
  background: var(--page-bg);
}
.data-table tbody tr:last-child td { border-bottom: 0; }

/* ── Status pills ──
 * NOTE: currently unused (no JS/HTML references). Kept and unified onto the
 * canonical pill palette so it stays consistent if reintroduced — safe to
 * delete outright if you're sure it won't come back. */
.status-pill {
  display: inline-block;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  padding: 3px 10px;
  border: 1px solid transparent;
  border-radius: var(--pill-radius);
  white-space: nowrap;
}
.status-pill.pending     { background: var(--pill-onhold-bg);    border-color: var(--pill-onhold-border);    color: var(--pill-onhold-fg); }
.status-pill.in-progress { background: var(--pill-active-bg);    border-color: var(--pill-active-border);    color: var(--pill-active-fg); }
.status-pill.completed   { background: var(--pill-completed-bg); border-color: var(--pill-completed-border); color: var(--pill-completed-fg); }
.status-pill.neutral     { background: var(--page-bg); color: var(--text-secondary); }

/* ── Tasks page (list) ── reuses the .card / .card-toolbar / .data-table /
 * .card-footer chrome of Contacts + Tax Returns; only column widths + the
 * cell styles are Tasks-specific. */
#tasks-card .card-toolbar,
#aa-card .card-toolbar { padding: var(--space-3); }
/* Widths sum past the card (~1620px) so the .tr-scroll gives a horizontal
 * scrollbar like Contacts/TR; the resizer overrides these per column. */
.data-table .tasks-col-action    { width: 52px; text-align: center; }
.data-table .tasks-col-due       { width: 120px; }
.data-table .tasks-col-completed { width: 120px; }
.data-table .tasks-col-name      { width: 320px; }
.data-table .tasks-col-matter    { width: 190px; }
.data-table .tasks-col-assigner  { width: 150px; }
.data-table .tasks-col-assignee  { width: 150px; }
.data-table .tasks-col-recorded  { width: 130px; }
.data-table .tasks-col-status    { width: 120px; }
.data-table .tasks-col-type      { width: 150px; }
.data-table .tasks-col-estimate  { width: 120px; }
.task-check { cursor: pointer; margin: 0; }
/* Toolbar: pull the date filter closer to the Outstanding/Completed tabs, and
 * give the Columns button a comfortable gap from Search (the shared -4px pulled
 * it too tight for this toolbar). */
/* Tasks toolbar inherits the global uniform 8px .tr-filter-row gap - no
 * per-element nudges needed. */
/* Assigned-to dropdown in the toolbar. */
.tk-assignee-select { width: auto; min-width: 150px; max-width: 210px; height: var(--control-h);
  border-color: var(--border-control); border-radius: var(--r); }
.task-name { color: var(--text-link); font-weight: var(--fw-medium); }
.task-matter-num { color: var(--text-link); text-decoration: none; }
.task-matter-num:hover { text-decoration: underline; }
.task-desc { color: var(--text-primary); font-size: var(--fs-base); margin-top: 2px; white-space: normal; }
/* Overdue indicator: warning-circle-fill (Phosphor), masked to danger red. */
.task-overdue-ico { color: #d30933; width: 18px; height: 18px; font-size: 18px; flex: 0 0 auto; }
.task-due { display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.task-due.overdue { color: #d30933; }
.task-overdue-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--danger); flex: 0 0 auto; }

/* ── Card footer (row count + export) ── 64 CSS px tall (≈83 DevTools at
 * 125% scaling per spec). Height covers the border-top via box-sizing. */
.card-footer {
  display: flex;
  align-items: center;
  height: 64px;
  /* 12px horizontal so the footer's controls sit the same 12px from the card
   * edge as the toolbar + page header (was 16px, the odd one out). */
  padding: 0 var(--space-3);
  border-top: 1px solid var(--border);
  /* Counter ("N returns") at 14 px / #1e2535. */
  font-size: var(--fs-base);
  color: var(--text-primary);
  box-sizing: border-box;
  flex: 0 0 auto;
}
/* Export button (and any future card-footer button) — 39 px tall with
 * 16 px horizontal padding and 14 px bold text. Sits 7 px right of the
 * row-count on TR; the Contacts footer overrides margin-left to 0 so
 * its 8 px flex gap rules. Shared rule keeps TR + Contacts identical. */
.card-footer .btn {
  font-weight: var(--fw-bold);
  font-size: var(--fs-base);
  height: var(--control-h);  /* was 39px — snapped to the 38px control height to match the footer pager buttons */
  padding: 0 var(--space-4);
  margin-left: 7px;
}

/* ── Contacts card footer ── pagination + per-page + export
 * Layout (left → right):
 *   [◀ ▶]  "1–50 of 3399"  [perPage ▾]  [Export]
 * Everything sits left-aligned now — Export hugs the per-page control
 * with the 8px flex gap between them (Export's normal 7px margin-left
 * from .card-footer .btn is zeroed out below so the gap rules cleanly). */
.contacts-footer { gap: var(--space-2); }
/* Prev / next page buttons — read as a single bordered control with a
 * 1px separator between the two halves. The outer .contacts-pager-group
 * owns the border + rounded corners; each inner button is borderless
 * except for the divider added between consecutive buttons. */
.contacts-pager-group {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border-control);
  border-radius: var(--r);
  background: #fff;
  overflow: hidden;
  flex-shrink: 0;
}
.contacts-page-btn {
  width: 39px;
  height: var(--control-h);
  border-radius: 0;
  border: 0;
  background: transparent;
  color: var(--text-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background var(--t);
  padding: 0;
  flex-shrink: 0;
}
.contacts-pager-group .contacts-page-btn + .contacts-page-btn {
  border-left: 1px solid var(--border-control);
}
.contacts-page-btn .icon { width: 12px; height: 12px; }
.contacts-page-btn:hover:not(:disabled) { background: var(--page-bg); }
.contacts-page-btn:disabled { opacity: 0.4; cursor: not-allowed; }
/* Per-page dropdown — borderless select with a chevron caret on
 * the right (the native dropdown arrow is suppressed). */
.contacts-perpage-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  margin-left: 8px;
}
.contacts-perpage-select {
  /* Now a <button> (was <select>) so the popup can be themed. Visual
   * box stays identical to the prior native control.
   * Fixed width (74 px) so the trigger stays the same size whether
   * "50" or "200" is selected — without this the box visibly shrinks
   * when the user picks 50. */
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--border-control);
  border-radius: var(--r);
  background: #fff;
  color: var(--text-primary);
  font: inherit;
  font-size: var(--fs-base);
  font-weight: var(--fw-bold);
  width: 73px;
  height: var(--control-h);
  padding: 0 28px 0 15px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  position: relative;
  text-align: left;
  line-height: 1;
}
.contacts-perpage-caret {
  position: absolute;
  right: 8px;   /* match the 8px caret->right-edge gap of every dropdown (select-input) */
  top: 50%;
  transform: translateY(-50%);
  width: 12px;
  height: 12px;
  color: var(--text-primary);   /* match every other caret (was the lone grey one) */
  pointer-events: none;
}
/* Custom popup — themed to match Clio. Active item background #e9e9f0,
 * hover a slightly lighter grey (#f3f4f6 = our --page-bg).
 * Vertical offset: calc(100% + 1px) sits the popup 3 px lower than the
 * previous calc(100% + 4px) — popup floats just above the trigger
 * with a 1 px gap. */
.contacts-perpage-menu {
  position: absolute;
  bottom: 100%;               /* -1 px vs prior calc(100% + 1px) — sits flush above the trigger */
  left: 0;
  z-index: var(--z-popup);
  list-style: none;
  margin: 0;
  padding: var(--space-2) 0;
  min-width: 100%;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-popup);
}
.contacts-perpage-item {
  /* Symmetric grid padding (snapped from 10 16 11). */
  padding: var(--space-3) var(--space-4);
  font-size: var(--fs-base);
  font-weight: var(--fw-regular);
  color: var(--text-primary);
  cursor: pointer;
  user-select: none;
}
.contacts-perpage-item:hover { background: #f3f4f6; }
.contacts-perpage-item.is-active { background: var(--grey-150); }
/* 1px gap under the trigger, matching every other popup. */
.contacts-perpage-menu { bottom: calc(100% + 1px); }

/* ── The Portal popup STANDARD ────────────────────────────────────────────────
 * One look for every custom popup - column pickers, per-page, custom listboxes
 * (uiSelect), autocomplete. Existing menus are grouped in so they share it:
 * our field radius (--r), standard row height, hover, and selection. Each popup
 * keeps its OWN positioning (fixed/absolute, up/down); only the look is unified.
 * Gap is a uniform 1px (JS positioners use UI_POP_GAP / POPUP_GAP = 1). */
.ui-pop, .columns-menu, .contacts-perpage-menu {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-popup);
  padding: var(--space-2) 0; margin: 0; list-style: none;
}
.ui-pop { position: absolute; z-index: var(--z-popup); min-width: 100%; }   /* content-sized; the JS positioner caps to the viewport only when it'd overflow the screen */
.ui-pop-row, .columns-menu-row, .contacts-perpage-item {
  display: flex; align-items: center; gap: var(--space-2);
  min-height: 34px; height: auto; padding: 0 var(--space-4);
  font-size: var(--fs-base); color: var(--text-primary);
  cursor: pointer; user-select: none; white-space: nowrap;
}
.ui-pop-row:hover, .ui-pop-row.is-hover,
.columns-menu-row:hover, .contacts-perpage-item:hover { background: var(--surface-hover); }
.ui-pop-row.is-active, .contacts-perpage-item.is-active { background: var(--grey-150); }
.ui-pop-row.is-disabled { color: var(--text-tertiary); cursor: default; }
.ui-pop-row.is-disabled:hover { background: transparent; }

/* Custom listbox trigger: a <button> that inherits the enhanced <select>'s own
 * classes (so it keeps the field / stage-pill look + caret); the real <select>
 * stays in the DOM, visually hidden, as the value source. */
.ui-select { position: relative; display: block; }
.ui-select-native { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); border: 0; }
.ui-select-trigger { text-align: left; cursor: pointer; font: inherit; font-size: var(--fs-base); }
.ui-select-trigger .ui-select-label { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-select-trigger[disabled] { cursor: default; opacity: .6; }
.ui-select-pop { left: 0; top: calc(100% + 1px); }
/* Pill selects (stage dropdowns) shrink-wrap to their label instead of filling
 * the cell/row, and open a viewport-fixed pop so the .tr-scroll overflow:auto
 * table body can't clip it. JS sets left/top from the trigger rect. */
.ui-select-inline { display: inline-block; vertical-align: middle; }
.ui-pop-fixed { position: fixed; }
/* Override .card-footer .btn margin-left: 7 so Export sits exactly
 * 8px right of the per-page select (the gap on .contacts-footer). */
.contacts-footer .btn { margin-left: 0; }
.card-footer > .spacer { flex: 1; }

/* ── Link-style buttons inside table cells ── */
.cell-link {
  color: var(--text-link);
  text-decoration: none;
  cursor: pointer;
}
.cell-link:hover { text-decoration: underline; }

/* ── Documents tab (contact detail) ──
 * Folder + file rows render in a single slate-grey tone (#516378) for
 * both the icon mask and the row text. The current segment of the
 * breadcrumb uses the same tone so the whole subheader/table reads as
 * one quiet chrome strip; parent segments stay link-blue and clickable. */
.docs-link {
  color: var(--text-slate);
  text-decoration: none;
  cursor: pointer;
  font-weight: var(--fw-medium);
}
.docs-link:hover { text-decoration: none; }
.docs-row td { color: var(--text-slate); }
/* Selected-row highlight — keyed off the row's own checkbox state via
 * :has() (no JS class needed). Hover deepens the tint. */
.docs-row:has(.docs-row-checkbox:checked) > td { background: #ebf7ff; }
.docs-row:has(.docs-row-checkbox:checked):hover > td { background: #cceaff; }
.docs-breadcrumb {
  display: flex;
  align-items: center;
  /* Single line — no wrapping. The toolbar has plenty of room left
   * of the Search box for a typical matter path; if a segment is
   * truly long it ellipsis-truncates instead. */
  flex-wrap: nowrap;
  gap: var(--space-1) 6px;
  font-size: var(--fs-base);
  color: var(--text-slate);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
}
.docs-breadcrumb .docs-crumb-current {
  /* Match the parent-segment weight — folder name reads as part of
   * the path, not a heading. */
  font-weight: var(--fw-regular);
  color: var(--text-slate);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.docs-breadcrumb .docs-crumb-current .icon {
  /* Folder glyph in front of the current segment, same tone + size
   * as the table rows so the breadcrumb reads as one cohesive
   * "you are here" label (mirrors Clio's breadcrumb pattern in the
   * user's screenshot). */
  width: 18px;
  height: 18px;
  display: inline-block;
  color: var(--text-slate);
  flex-shrink: 0;
}
/* Documents rows: folder + file glyphs - 18px, two-tone (folders slate #516378,
 * files brand-red #d30933), with the row gap after them. These were inline
 * styles on the old mask spans; moved to classes now that icons are inline
 * <svg> (which tints via `color`/currentColor, not `background-color`). */
.docs-folder-ico, .docs-file-ico { width: 18px; height: 18px; vertical-align: middle; margin-right: var(--space-2); flex-shrink: 0; }
.docs-folder-ico { color: #516378; }
.docs-file-ico   { color: #d30933; }
.docs-move-ico   { width: 18px; height: 18px; flex-shrink: 0; }
.docs-breadcrumb .docs-crumb-sep {
  /* Phosphor caret-right-bold, masked + tinted to match the folder
   * icon. 9 px box, mask-size 13 px so the chevron strokes stay
   * thin at the small render size. */
  display: inline-block;
  width: 9px;
  height: 9px;
  background-color: var(--text-slate);
  -webkit-mask: url('/img/icons/caret-right-bold.svg') center / 13px no-repeat;
          mask: url('/img/icons/caret-right-bold.svg') center / 13px no-repeat;
  flex-shrink: 0;
}

/* Selection checkbox column. Fixed narrow width — just enough for the
 * checkbox + a little breathing room. Vertical center via the cell's
 * flex-friendly default vertical-align: middle on table cells. */
.data-table .docs-col-check {
  width: 32px !important;
  min-width: 32px !important;
  max-width: 32px !important;
  padding: 0 !important;
  text-align: center !important;
}
.data-table .docs-col-check input[type="checkbox"] {
  width: 16px; height: 16px; margin: 0; cursor: pointer;
}
/* The select-all header cell also carries .tr-th (left-aligned, text-column
   padding). Override so the box centers in the column exactly like the body
   checkboxes. Specificity (0,3,2) beats the .tr-th rule's (0,2,2). */
.data-table thead th.tr-th.docs-col-check { padding: 0 !important; text-align: center !important; }
/* Documents + Contacts column-header text 1px smaller than the shared .tr-th (13→12). */
#documents-table-container .tr-th,
#contacts-table-container .tr-th,
#matters-table-container .tr-th { font-size: var(--fs-sm) !important; }
/* (select-all checkbox centers via .docs-col-check text-align — no nudge needed) */

/* ── Transcripts page (PitBullTax library) ──
 * Client picker: a native select styled to match the toolbar controls
 * (same border/height as .toolbar-search). Native select = keyboard
 * type-ahead for free, replacing the removed search box. */
/* Client search box: same .toolbar-search look as the Contacts / TR search. A
 * plain live-filter input - no dropdown; typing narrows the table (and swaps to
 * the coverage matrix once it resolves to a single client). */
.trx-client-search { min-width: 280px; max-width: 420px; }

/* Coverage matrix (client picked, latest mode): rows = transcript types,
 * columns = years, cells = latest-pull chips. Empty cells ARE the feature —
 * they make missing years visible at a glance. */
.trx-report-line {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--divider, #e9ecf0);
}
.trx-mx-label { font-weight: var(--fw-semibold); margin-right: var(--space-2); }
.trx-chip {
  display: inline-block;
  padding: 3px 9px;
  margin: 1px;
  border: 1px solid var(--grey-250);
  border-radius: var(--pill-radius);
  background: var(--blue-light);
  color: var(--text-link);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t), color var(--t), border-color var(--t);
}
.trx-chip:hover { background: var(--blue); border-color: var(--blue); color: #fff; }

/* Coverage view (embedded contact tab + main-page picked client): a compact
 * KPI card, then one narrow card per transcript type with its periods as
 * links (newest first). Exactly six columns (capped width so they don't
 * stretch); a 7th+ type wraps to the next row. */
.trx-cov { padding: var(--space-3); }
/* Embedded contact tab: .contact-body already supplies the uniform 12 px
 * portal gutter, so the coverage view adds NO extra inset (otherwise the
 * cards sat 28 px in, out of step with the Dashboard tab's 12 px). */
#contact-trx-body .trx-cov { padding: 0; }
/* Refresh fallback bar (empty / error states only — normally the button sits
 * inline in the KPI card via .trx-kpi-actions). */
.trx-embed-bar { display: flex; justify-content: flex-end; padding: 0 0 var(--space-3); }
/* Match the TR-page Refresh exactly: the .page-actions > .btn type treatment
 * (fs-base / bold) AND its 18px icon (the TR button overrides the default
 * 14px .btn .icon via #tr-refresh-btn .icon). */
.trx-kpi-actions > .btn,
.trx-embed-bar > .btn { font-size: var(--fs-base); font-weight: var(--fw-bold); }
#trx-refresh-btn .icon,
#contact-trx-refresh .icon { width: 18px; height: 18px; }

/* Compact KPI card: counters grouped left (not stretched), Refresh pinned
 * right. Short — just enough to clear the value + label. */
.trx-kpi-card {
  display: flex;
  align-items: center;
  background: #fcfdfe;
  border: 1px solid #dde3ea;
  border-radius: var(--r-lg);
  padding: 8px 14px;
  margin-bottom: var(--space-3);
}
.trx-kpi { text-align: center; padding: 0 20px; }
.trx-kpi:first-child { padding-left: 4px; }
.trx-kpi + .trx-kpi { border-left: 1px solid #edf0f4; }
.trx-kpi-v { font-size: var(--fs-lg); font-weight: var(--fw-semibold); color: var(--text-primary); }
.trx-kpi-l { font-size: var(--fs-base); color: var(--text-slate); margin-top: 2px; }
.trx-kpi-actions { margin-left: auto; }
/* Transcript Report value = clickable pull-date link(s). Matches the other KPI
 * values (fs-lg / semibold via .trx-kpi-v) so it reads identically to
 * "Last updated"; only the link color differs. */
.trx-report-link { background: none; border: 0; padding: 0; font: inherit; color: var(--text-link); cursor: pointer; }
.trx-report-link:hover { text-decoration: underline; }

/* Type cards flex to fill the row. Flexbox (not an equal-width grid) so a card
 * with a long header — e.g. "Wage and Income Summary" — sizes to its one-line
 * title (its min-content floor) while the others shrink to fit, instead of the
 * grid forcing every column equal and wrapping the long title. Wraps extras to
 * the next row when there are too many to fit. */
.trx-cards {
  display: flex;
  flex-wrap: nowrap;      /* all type-cards share ONE row, shrinking to fit */
  gap: var(--space-3);
  align-items: start;     /* cards keep independent heights */
}
/* Contact Transcripts tab with a spouse: stack the two people, each taking half
 * the viewport (the long Civil-Penalty card scrolls within its half). The card
 * max-height per half is set live by fitTranscriptCards. */
.trx-split { display: flex; flex-direction: column; gap: var(--space-4); }
/* Inside the IRS-Account card body, the spouse section is separated from the
 * taxpayer section by a divider line (taxpayer info / separator / spouse info). */
.trx-split .trx-person + .trx-person { border-top: 1px solid var(--border); padding-top: var(--space-4); }
.trx-person-h {
  font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--text-primary);
  margin: 0 0 var(--space-2);
}
.trx-person .trx-cov { padding-top: 0; }
.trx-type-card {
  background: #fcfdfe;
  border: 1px solid #dde3ea;
  border-radius: var(--r-lg);
  overflow: hidden;
  /* Height-locked: the header stays put and the period list scrolls inside
   * when a card has more periods than fit (e.g. Civil Penalty's quarters),
   * so one long card can't blow out the row. */
  display: flex; flex-direction: column;
  max-height: 19rem;
  /* Grow to fill the row, shrink to share space, but never narrower than the
   * header's own width. The card's `overflow: hidden` (for the rounded corners)
   * would otherwise resolve flex `min-width: auto` to 0 and let a long header
   * like "Wage and Income Summary" get clipped — `min-width: min-content` pins
   * the floor to the header instead. max-width caps the grow so one/few cards
   * sit at the widest label's size rather than stretching across the page. */
  /* One no-wrap row: shrink to share the width (min-width:0 lets a card go below
   * its header width - the title ellipsizes, with a tooltip - so the last card
   * always fits). max-width keeps a few cards compact instead of stretching. */
  flex: 1 1 140px;
  min-width: 0;
  max-width: 300px;
}
.trx-type-card-h {
  display: flex; align-items: center; gap: 6px;
  padding: 8px 10px;
  border-bottom: 1px solid #edf0f4;
  flex: 0 0 auto;
}
.trx-type-card-title { flex: 1; min-width: 0; font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.trx-type-card-count {
  font-size: var(--fs-2xs); font-weight: var(--fw-medium);
  color: var(--text-link); background: var(--blue-light);
  border-radius: var(--pill-radius); padding: 1px 7px;
}
.trx-type-card-body { display: flex; flex-direction: column; padding: 6px 10px 9px; flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.trx-period-link {
  background: none; border: 0; padding: 3px 0;
  text-align: left;
  font-size: var(--fs-md); color: var(--text-link);
  cursor: pointer;
}
.trx-period-link:hover { text-decoration: underline; }
/* Blank separator between year groups in quarterly cards (~1 row). flex:0 0 auto
 * keeps the empty div from being squeezed to zero in tall cards that scroll
 * (a flex column would otherwise shrink content-less items away). */
.trx-year-gap { height: 0.5em; flex: 0 0 auto; }
/* The unparsed-files line sits inside .trx-cov — drop its full-bleed border look. */
.trx-cov .trx-report-line { padding: var(--space-3) 0 0; border-bottom: 0; }

/* Transcripts table columns.
 * Same two-table shell as TR/Contacts/Documents. Client is the absorber;
 * every other column is pinned (width+min+max !important) so the split
 * thead/tbody tables compute identical widths. */
.data-table .trx-col-client { min-width: 220px; }
.data-table .trx-col-type   { width: 180px !important; min-width: 180px !important; max-width: 180px !important; white-space: nowrap; }
.data-table .trx-col-period { width: 100px !important; min-width: 100px !important; max-width: 100px !important; white-space: nowrap; }
.data-table .trx-col-pulled { width: 170px !important; min-width: 170px !important; max-width: 170px !important; white-space: nowrap; }
.data-table .trx-col-pulls  { width: 80px  !important; min-width: 80px  !important; max-width: 80px  !important; white-space: nowrap; }
.data-table .trx-col-size   { width: 100px !important; min-width: 100px !important; max-width: 100px !important; white-space: nowrap; }

/* ── Transcript dashboard: roll-up landing (one row per client) ── */
/* Canonical fixed widths reused across ALL dashboard tabs (table-layout:fixed
 * needs an explicit px per column). Every currency column uses --trx-w-money;
 * every MM/DD/YYYY date column uses --trx-w-date - so Earliest CSED, Last updated,
 * and the date/money columns on the IA / OIC / FTA / Kwong tabs all line up. */
.trx-roll-table { --trx-w-money: 130px; --trx-w-date: 120px; }
.trx-roll-table .trx-rc-client { min-width: 220px; }
.trx-roll-table .trx-rc-forms  { width: 230px !important; min-width: 230px !important; max-width: 230px !important; }
.trx-roll-table .trx-rc-liab   { width: var(--trx-w-money) !important; min-width: var(--trx-w-money) !important; max-width: var(--trx-w-money) !important; text-align: right; white-space: nowrap; }
.trx-roll-table .trx-rc-csed   { width: var(--trx-w-date) !important; min-width: var(--trx-w-date) !important; max-width: var(--trx-w-date) !important; white-space: nowrap; }
.trx-roll-table .trx-rc-flags   { width: 150px !important; min-width: 150px !important; max-width: 150px !important; }
.trx-roll-table .trx-rc-unfiled { width: 130px !important; min-width: 130px !important; max-width: 130px !important; }
.trx-roll-table .trx-rc-pull    { width: var(--trx-w-date) !important; min-width: var(--trx-w-date) !important; max-width: var(--trx-w-date) !important; white-space: nowrap; }
/* Installment Agreement tab columns - money/date reuse the canonical vars so they line
 * up with the Tax liability tab; status wraps (e.g. "Active since 04/18/2026"). */
.trx-roll-table .trx-rc-ia-status  { width: 150px !important; min-width: 150px !important; max-width: 150px !important; }
.trx-roll-table .trx-rc-ia-monthly { width: var(--trx-w-money) !important; min-width: var(--trx-w-money) !important; max-width: var(--trx-w-money) !important; text-align: right; white-space: nowrap; }
.trx-roll-table .trx-rc-ia-paydate { width: var(--trx-w-date) !important; min-width: var(--trx-w-date) !important; max-width: var(--trx-w-date) !important; white-space: nowrap; }
.trx-roll-table .trx-rc-ia-flags   { width: 170px !important; min-width: 170px !important; max-width: 170px !important; }
/* Offer in Compromise tab columns (date reuses the canonical width). */
.trx-roll-table .trx-rc-oic-filed  { width: var(--trx-w-date) !important; min-width: var(--trx-w-date) !important; max-width: var(--trx-w-date) !important; white-space: nowrap; }
.trx-roll-table .trx-rc-oic-result { width: 180px !important; min-width: 180px !important; max-width: 180px !important; }
.trx-roll-table .trx-rc-oic-days   { width: 120px !important; min-width: 120px !important; max-width: 120px !important; text-align: right; }
.trx-roll-table .trx-rc-oic-ours   { width: 100px !important; min-width: 100px !important; max-width: 100px !important; text-align: center; }
/* Excluded (not-filed-by-us) OIC rows are muted but the toggle stays full-opacity + clickable. */
.trx-roll-table tr.trx-oic-excluded td:not(.trx-rc-oic-ours) { opacity: 0.5; }
/* OIC KPI card: same content-fit rule as the Overview / IA cards (no stretch to fill the card),
 * but with 8 KPIs it equalizes them - grid 1fr tracks under width:fit-content make every column
 * the width of the widest, and labels never wrap. */
.trx-oic-stats { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; width: fit-content; max-width: 100%; }
.trx-oic-stats .trx-kpi { padding: 0 16px; white-space: nowrap; }
/* Manual-entry dollar cells: offer + liability are click-to-edit; pennies is computed. */
.trx-roll-table .trx-rc-oic-offer     { width: var(--trx-w-money) !important; min-width: var(--trx-w-money) !important; max-width: var(--trx-w-money) !important; text-align: right; white-space: nowrap; }
.trx-roll-table .trx-rc-oic-liability { width: 160px !important; min-width: 160px !important; max-width: 160px !important; text-align: right; white-space: nowrap; }
.trx-roll-table .trx-rc-oic-pennies   { width: 90px  !important; min-width: 90px  !important; max-width: 90px  !important; text-align: right; }
/* First Time Abate tab columns. The three penalty families + potential abatement reuse the
 * canonical money width; period / return-filed are compact; eligibility fits its longest verdict.
 * One row per (client, form, tax period) - a departure from the one-row-per-client tabs. */
.trx-roll-table .trx-rc-fta-form   { width: 92px  !important; min-width: 92px  !important; max-width: 92px  !important; white-space: nowrap; }
.trx-roll-table .trx-rc-fta-period { width: 100px !important; min-width: 100px !important; max-width: 100px !important; white-space: nowrap; }
.trx-roll-table .trx-rc-fta-filed  { width: 120px !important; min-width: 120px !important; max-width: 120px !important; white-space: nowrap; }
.trx-roll-table .trx-rc-fta-ftf    { width: var(--trx-w-money) !important; min-width: var(--trx-w-money) !important; max-width: var(--trx-w-money) !important; text-align: right; white-space: nowrap; }
.trx-roll-table .trx-rc-fta-ftp    { width: var(--trx-w-money) !important; min-width: var(--trx-w-money) !important; max-width: var(--trx-w-money) !important; text-align: right; white-space: nowrap; }
.trx-roll-table .trx-rc-fta-ftd    { width: var(--trx-w-money) !important; min-width: var(--trx-w-money) !important; max-width: var(--trx-w-money) !important; text-align: right; white-space: nowrap; }
.trx-roll-table .trx-rc-fta-elig   { width: 150px !important; min-width: 150px !important; max-width: 150px !important; }
.trx-roll-table .trx-rc-fta-abate  { width: 150px !important; min-width: 150px !important; max-width: 150px !important; text-align: right; white-space: nowrap; }
/* FTA eligibility verdict text (green candidates, amber "needs prior years", muted disqualified)
 * + the potential-abatement dollars (green = an FTA candidate's savings, mirroring .trx-neg's red). */
.trx-fta-elig { font-weight: 500; }
.trx-fta-elig.is-eligible    { color: #067647; }
.trx-fta-elig.is-unknown     { color: #b54708; cursor: help; }
.trx-fta-elig.is-noteligible { color: var(--text-muted); }
.trx-fta-abate { color: #067647; font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.trx-fta-abate.is-muted { color: var(--text-muted); font-weight: 400; }
.trx-kpi-v.trx-pos { color: #067647; }
.oic-amt { display: inline-block; min-width: 44px; cursor: text; border-radius: 3px; padding: 0 3px; }
.oic-amt:hover { background: var(--page-bg); }
.oic-amt:focus { background: var(--page-bg); outline: 1px solid #0067D1; }
.oic-amt:empty::before { content: '-'; color: var(--text-muted); }
/* A transcript-derived estimate (no manual override yet): italic + muted. Hover for the basis. */
.oic-amt.oic-est { font-style: italic; color: var(--text-secondary); }
/* Non-accepted liability = the current balance owed (factual, read-only, not an editable estimate). */
.oic-current { cursor: help; font-variant-numeric: tabular-nums; }
.trx-roll-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.trx-roll-table td { vertical-align: top; }
/* Forms & periods cell: one "1040: 2012-2019, 2023" line per form that owes. */
.trx-fp { line-height: 1.45; }
.trx-fp + .trx-fp { margin-top: 1px; }
.trx-fp-form { font-weight: var(--fw-semibold); }
.trx-sub { font-size: var(--fs-sm); margin-top: 2px; }
.trx-neg { color: var(--danger); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.trx-csed { display: inline-flex; align-items: center; font-size: 11px; border-radius: 6px; padding: 2px 7px; white-space: nowrap; }
.trx-csed-soon    { background: #fdf2dc; color: #b54708; }
.trx-csed-expired { background: #fef0f0; color: var(--danger); }
.trx-csed-imminent { color: var(--danger); font-weight: var(--fw-semibold); } /* CSED <= 6 months out (red) */
.trx-csed-warn { color: #E0A800; font-weight: var(--fw-semibold); }            /* CSED <= 12 months out (amber) */
/* Match the canonical TR-page pill (.contact-stage-pill): 22px tall, --fs-sm, 999px
 * radius, 1px border. Semantic alert COLORS stay (red lien, amber OIC, etc.). Shared by
 * the liability Status column + the IA Flags column so both line up with TR. */
.trx-flag { display: inline-flex; align-items: center; height: 22px; font-size: var(--fs-sm); line-height: 1; border: 1px solid transparent; border-radius: 999px; padding: 0 10px; white-space: nowrap; margin-right: 2px; }
.trx-flag-red   { background: #fef0f0; border-color: #f7d4d4; color: #b42318; }
.trx-flag-amber { background: #fdf2dc; border-color: #f5e0b8; color: #b54708; }
.trx-flag-ok    { background: #ecfdf3; border-color: #c9f0d8; color: #067647; }
.trx-flag-gray  { background: #eef1f4; border-color: #dde3ea; color: #5e6b7b; }
/* Clio/TR stage-palette variants. The dashboard flag pills (trxFlagClass) map onto these so
 * Overview Status + IA Flags use the same colors as the Tax Returns table. */
.trx-flag-onhold    { background: var(--pill-onhold-bg);    border-color: var(--pill-onhold-border);    color: var(--pill-onhold-fg); }
.trx-flag-active    { background: var(--pill-active-bg);    border-color: var(--pill-active-border);    color: var(--pill-active-fg); }
.trx-flag-completed { background: var(--pill-completed-bg); border-color: var(--pill-completed-border); color: var(--pill-completed-fg); }
.trx-flag-closed    { background: var(--pill-closed-bg);    border-color: var(--pill-closed-border);    color: var(--pill-closed-fg); }
.trx-rc-flags.trx-has-status { cursor: help; }
/* IA "Various" + flag pills with a detail card: hover peeks, click pins it open. */
.trx-ia-various { cursor: pointer; border-bottom: 1px dotted var(--text-tertiary); }
.trx-flag.trx-flag-hover { cursor: pointer; }
/* Status hover card: a fixed-position popover (appended to <body>) detailing each
   status pill - its meaning and the years/dates it posted. */
.trx-status-card { position: fixed; z-index: 1000; background: #fff; border: 1px solid var(--border);
  border-radius: 8px; box-shadow: 0 8px 24px rgba(16,24,40,0.16); padding: 6px 10px;
  min-width: 200px; max-width: 320px; font-size: var(--fs-sm); line-height: 1.4; pointer-events: none; }
.trx-status-row { padding: 5px 0; }
.trx-status-row + .trx-status-row { border-top: 1px solid #eef1f4; }
.trx-status-row-mean { margin-top: 4px; }
.trx-status-row-when { margin-top: 2px; color: #5e6b7b; font-variant-numeric: tabular-nums; }

/* ── Contact "IRS Account" tab hub ── */
.irs-hub { display: flex; flex-direction: column; gap: 12px; }
/* ONLY the shell KPI strip (direct child) drops its margin - the hub's 12px flex
 * gap is its spacing. The per-person KPI cards inside the Files sub-tab keep their
 * own margin-bottom so there's a gap before their transcript type cards. */
.irs-hub > .trx-kpi-card { margin-bottom: 0; }
/* Files toolbar: stack "Show all" directly above the Columns button. */
.trx-ctrl-stack { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-2); }
.irs-statbar { display: flex; gap: 28px; flex-wrap: wrap; padding: 10px 2px; border-bottom: 1px solid #e3e6ea; }
.irs-stat-l { font-size: 12px; color: var(--text-tertiary); margin-bottom: 2px; }
.irs-stat-v { font-size: 16px; font-weight: 600; }
/* The IRS Account tab is ONE card: the sub-tabs are the card header (the same
 * .contact-tabbar look as the Contact page's top tabs), and the active sub-tab
 * renders in the card body below. The card keeps .contact-card's overflow:hidden
 * (rounded corners); the active-tab underline sits at the header/body divider,
 * well inside the card, so it isn't clipped. */
.irs-card-tabbar {
  margin: 0;                                 /* override .contact-tabbar's -12px full-bleed */
  padding: 0 var(--space-4);
  border-radius: var(--r) var(--r) 0 0;      /* round the top with the card */
  height: 40px;                              /* single row: the active underline can't track a 2nd row */
  flex-wrap: nowrap;                         /* 7 sub-tabs (one long) scroll rather than wrap */
  overflow-x: auto;                          /* ...so all tabs stay reachable if they exceed the width */
  overflow-y: hidden;                        /* no stray vertical scrollbar from the underline */
  scrollbar-width: none;                     /* clean header - hide the scrollbar (Firefox) */
}
.irs-card-tabbar::-webkit-scrollbar { display: none; }  /* ...and Chromium/Edge */
.irs-card-tabbar .tab { flex: 0 0 auto; }
/* The card clips at its box (overflow:hidden), so pull the active-tab underline
 * just inside the 40px bar - fully visible, sitting on the header/body divider. */
.irs-card-tabbar .tab.active::after { bottom: -5px; }
.irs-card-body { padding: var(--space-4); }
.irs-note { font-size: 12px; color: var(--text-tertiary); padding: 0 0 8px; }
.irs-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.irs-table .irs-code { font-variant-numeric: tabular-nums; color: var(--text-secondary); }
.irs-table tfoot .irs-total td { font-weight: 600; border-top: 2px solid #e3e6ea; background: #fbfcfd; }
.irs-ok { color: #067647; font-weight: 500; }
/* Account-status-by-year: clickable year rows that expand to the year's transaction codes. */
.irs-year-row { cursor: pointer; }
.irs-year-row:hover { background: #f6f8fb; }
.irs-year-row.is-open { background: #f1f5fb; }
.irs-caret { width: 12px; height: 12px; margin-right: 6px; vertical-align: -1px; color: var(--text-tertiary); }
.irs-rf-check { width: 13px; height: 13px; margin-right: 4px; vertical-align: -2px; color: #067647; }
.irs-year-drill > td { padding: 2px 12px 14px 30px; background: #fafbfc; }
.irs-drill-mod { margin-top: 10px; }
.irs-drill-h { font-size: 12px; font-weight: 600; color: var(--text-secondary); margin-bottom: 4px; }
.irs-subtable { font-size: 12px; }
.irs-subtable th { font-weight: 600; }
.irs-toll { display: inline-flex; font-size: 10.5px; background: #fdf2dc; color: #b54708; border-radius: 4px; padding: 1px 6px; margin-right: 3px; }
.irs-fsec { margin-bottom: 16px; }
.irs-fsec-h { font-size: 14px; font-weight: 600; margin-bottom: 8px; }
.irs-fields td:first-child { width: 70%; }

/* Viewport-lock: the Transcripts page reuses .tr-page-body, so the existing
 * body:has(.tr-page-body) rules give it the same dynamic-card behaviour
 * (footer always on-screen, vertical scroll inside the card) for free. */

/* ── Documents tab: paginated two-table scroller + footer (mirrors Contacts) ── */
#documents-card { display: flex; flex-direction: column; min-width: 0; }
/* Table area scrolls internally; the footer sits below it. The contact-detail
 * page whole-page-scrolls, so the table area is bounded with max-height
 * rather than viewport-locking the whole tab. */
#documents-table-container {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 300px);
}
/* Default column widths — resizable: the col-resizer writes later !important
 * rules to override these. Name is the absorber; every other column is pinned.
 * Class-level (not #id) so the resizer rules win cleanly — mirrors Contacts. */
.data-table .docs-col-name      {                          min-width: 220px; }
.data-table .docs-col-category  { width: 130px !important; min-width: 130px !important; max-width: 130px !important; }
.data-table .docs-col-size      { width: 90px  !important; min-width: 90px  !important; max-width: 90px  !important; }
.data-table .docs-col-last_edit { width: 150px !important; min-width: 150px !important; max-width: 150px !important; }
.data-table .docs-col-received  { width: 150px !important; min-width: 150px !important; max-width: 150px !important; }

/* Action menu items — laid out like the Columns menu but each row is a
 * <button>, not a <label>. Disabled rows render dimmed and ignore
 * clicks (the protected-folder guard happens at handler-level too). */
/* Action rows: a <button> reset that otherwise matches the standard popup row
 * (.ui-pop-row / .columns-menu-row) - same fs-base, 34px min-height, flex,
 * surface-hover, tertiary disabled. */
.columns-menu .docs-action-item {
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  font: inherit;
  font-size: var(--fs-base);
  color: var(--text-primary);
  min-height: 34px;
  display: flex;
  align-items: center;
  padding: 0 var(--space-4);
  cursor: pointer;
}
.columns-menu .docs-action-item:hover:not([disabled]) { background: var(--surface-hover); }
.columns-menu .docs-action-item[disabled] {
  color: var(--text-tertiary);
  cursor: default;
}

/* Move-modal folder rows. Pure list-of-buttons; clicking drills in. */
.docs-move-row:hover { background: var(--page-bg) !important; }
.docs-move-row:last-child { border-bottom: 0 !important; }

/* "Shared" pill next to filenames in the Documents tab. Same palette
 * + shape as the TR Active-stage pill (.stage-select.is-active) so
 * every status chip on the portal reads as one family. Static span,
 * not a <select>, so no chevron / cursor / appearance overrides. */
.docs-shared-pill {
  display: inline-block;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  padding: 1px 10px;
  border-radius: var(--pill-radius);
  background: var(--pill-active-bg);
  border: 1px solid var(--pill-active-border);
  color: var(--pill-active-fg);
  margin-left: var(--space-2);
  vertical-align: middle;
  white-space: nowrap;
}

/* The "&" between primary and spouse names on the Tax Returns table
 * (rendered inside <span class="client-amp">). Matches the link color
 * so the joint-return label reads as one cohesive blue chunk. */
.client-amp { color: var(--text-primary); }   /* the "&" between joint names is not a link */

/* Status dropdown inside a table cell — looks like a pill but is a <select>.
 * Excel-style: chevron arrow only shows once the cell is clicked into.
 * Height: 21 CSS px (≈27 DevTools at 125% scaling per spec). The
 * inherited line-height of 21px from body gives the exact height when
 * vertical padding is 0. */
.stage-select {
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  /* Literal 10px (NOT --space-3): sizeStageSelectToText() sizes the pill to
   * textW + 24 assuming 10px padding; the grid's 12px clipped the label. */
  padding: 0 10px;
  border-radius: 999px;
  /* 1px transparent border by default — each variant below sets its
   * own border-color. Without the base 1px, switching states would
   * cause the pill to shift width by 2px. */
  border: 1px solid transparent;
  background-color: var(--page-bg);
  color: var(--text-primary);
  appearance: none;
  -webkit-appearance: none;
  background-image: none;
  background-repeat: no-repeat;
  background-position: right 6px center;
  background-size: 12px;
  cursor: pointer;
  /* Width is set per-instance by sizeStageSelectToText() in app.js so
   * each pill is only as wide as its currently-selected stage label
   * (rather than the widest option). Capped at 170 px — just enough
   * for the longest stage name ("Pending Clarifications") so the
   * column itself can stay tight at 174 px (170 + 4 px left padding
   * on the cell). */
  max-width: 170px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.stage-select:focus {
  outline: none;
  /* padding stays symmetric (0 10) on focus per spec — the chevron
   * still appears via background-image but sits inside the right
   * padding rather than pushing it. */
  background-image: url('/img/icons/caret-down-fill.svg');
}
/* Pending (any active, non-terminal, non-on-hold stage) */
.stage-select.is-active    { background-color: var(--pill-active-bg);    border-color: var(--pill-active-border);    color: var(--pill-active-fg); }
/* On Hold */
.stage-select.is-on-hold   { background-color: var(--pill-onhold-bg);    border-color: var(--pill-onhold-border);    color: var(--pill-onhold-fg); }
/* Completed */
.stage-select.is-completed { background-color: var(--pill-completed-bg); border-color: var(--pill-completed-border); color: var(--pill-completed-fg); }

/* ── Detail panel ── */
.detail-panel {
  position: fixed; right: 0; top: 0;
  width: 380px; height: 100vh;
  background: var(--surface);
  border-left: 1px solid var(--border);
  box-shadow: -4px 0 16px rgba(0,0,0,0.1);
  z-index: var(--z-sticky);
  display: flex; flex-direction: column;
  overflow-y: auto;
}
.detail-header {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: flex-start; gap: var(--space-3);
}
.detail-avatar {
  width: 40px; height: 40px;
  border-radius: 50%;
  background: var(--blue-light);
  color: var(--blue);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-base); font-weight: var(--fw-bold);
  flex-shrink: 0;
}
.detail-title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); margin-bottom: 3px; }
.detail-sub { font-size: var(--fs-smd); color: var(--text-secondary); }
.detail-close { margin-left: auto; background: none; border: none; font-size: var(--fs-2xl); color: var(--text-secondary); padding: 4px; cursor: pointer; }
.detail-field { display: flex; justify-content: space-between; align-items: flex-start; padding: var(--space-2) 0; border-bottom: 1px solid var(--divider); font-size: var(--fs-md); }
.detail-field:last-child { border-bottom: none; }
.detail-field-label { color: var(--text-secondary); font-weight: var(--fw-medium); }
.detail-field-value { color: var(--text-primary); text-align: right; max-width: 60%; }

/* ── Empty state ── */
.empty-state { padding: 60px 20px; text-align: center; color: var(--text-tertiary); }
.empty-state svg { width: 48px; height: 48px; opacity: 0.3; margin-bottom: var(--space-4); }
.empty-state h3 { font-size: var(--fs-lg); color: var(--text-secondary); margin-bottom: var(--space-2); }
.empty-state p  { font-size: var(--fs-md); margin-bottom: var(--space-4); }

/* Inline placeholder paragraph emitted by emptyState()/errorState(): a centered,
   padded, muted (or .is-error danger) <p> shown while a panel loads or when it
   has no data. Distinct from the icon+heading .empty-state block above. */
.state-placeholder { padding: var(--space-6); text-align: center; color: var(--text-secondary); }
.state-placeholder.is-error { color: var(--danger); }

/* ── Toast ──
 * Clio-style: 131 CSS-px wide card (≈169 DevTools at 125% scaling),
 * pinned 70 CSS px from the top edge and 10 CSS px from the right edge.
 * Each toast has a 5-px brand-blue (#0067d1) bar across the top, an
 * info-fill icon on the left, the message body, and a dismissible
 * x-circle on the right. The top bar + info icon are always brand blue;
 * severity variants only swap the bg tint via --toast-bg. */
#toast-container {
  position: fixed;
  top: 53px;
  right: 8px;
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.toast {
  --toast-bg: #ebf4ff;
  position: relative;
  width: 432px;
  background: var(--toast-bg);
  color: var(--text-primary);
  border-radius: var(--r);
  box-shadow: var(--shadow-lg);
  padding: 14px 28px 14px 38px;   /* room for top bar (handled by ::before) + icons */
  font-size: var(--fs-md);
  line-height: 1.4;
  word-wrap: break-word;
  overflow: hidden;
  /* Visible (resting) state. The slide-in/out is driven by a single
   * .toast-offscreen modifier toggled from JS — present before the
   * first paint, then removed (slide in) and added again 5 s later
   * (slide out). CSS transitions on transform + opacity drive the
   * motion. This is more reliable than CSS animations across paint
   * cycles — the earlier animation-based approach left the toast
   * stuck off-screen on some renders. */
  transform: translateX(0);
  opacity: 1;
  transition:
    transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.toast.toast-offscreen {
  transform: translateX(calc(100% + 16px)); /* width + container right gap, fully off the viewport */
  opacity: 0;
  pointer-events: none;
}
.toast::before {
  /* Top accent bar — always brand blue (#0067d1), regardless of variant.
   * Earlier this read from var(--toast-accent), which the success/error/
   * warning variants swap; pinning to the brand color keeps the chrome
   * consistent and only the bg tint signals severity. */
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 5px;
  background: var(--blue);
}
.toast::after {
  /* Info icon on the left — also pinned to #0067d1 for the same reason. */
  content: '';
  position: absolute;
  top: 14px;
  left: 10px;
  width: 21px;
  height: 21px;
  background-color: var(--blue);
  -webkit-mask: url('/img/icons/info-fill.svg') center / contain no-repeat;
          mask: url('/img/icons/info-fill.svg') center / contain no-repeat;
}
.toast-close {
  position: absolute;
  top: 14px;
  right: 6px;
  width: 16px;
  height: 16px;
  background-color: var(--text-slate);
  -webkit-mask: url('/img/icons/x-circle-fill.svg') center / contain no-repeat;
          mask: url('/img/icons/x-circle-fill.svg') center / contain no-repeat;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: background-color var(--t);
}
.toast-close:hover { background-color: #333; }

/* Variant colors — tinted background only (top bar / info icon stay
 * brand blue regardless of severity). */
.toast-success { --toast-bg: #e3f6e9; }
.toast-error   { --toast-bg: #fde9ec; }
.toast-warning { --toast-bg: #fff4d6; }

/* Toast keyframes were removed in favor of CSS transitions driven by
 * the .toast-offscreen modifier — see .toast{} above. */

/* ── Alerts ── */
.alert {
  padding: 12px 16px; border-radius: var(--r-sm);
  font-size: var(--fs-md); display: flex; gap: var(--space-3); align-items: flex-start;
  margin-bottom: var(--space-4);
}
.alert svg { width: 15px; height: 15px; flex-shrink: 0; margin-top: 1px; }
.alert-info    { background: var(--info-bg);    color: #0C5460; border: 1px solid #B8DAFF; }
.alert-success { background: var(--success-bg); color: var(--status-done-text); border: 1px solid var(--status-done-border); }
.alert-warning { background: var(--warning-bg); color: var(--status-pending-text); border: 1px solid var(--status-pending-border); }
.alert-danger  { background: var(--danger-bg);  color: var(--status-overdue-text); border: 1px solid var(--status-overdue-border); }

/* Inline warning lines in the Tax-Returns sync-status block (⚠ …). Not a boxed
   .alert — just readable warning text on the page background. */
.sync-status-warn { color: var(--warning-fg); margin-top: var(--space-2); font-size: var(--fs-sm); }

/* ── Overdue indicator ── */
.overdue-dot { display: inline-flex; align-items: center; gap: var(--space-1); }
.overdue-dot::before { content:''; width:10px; height:10px; background:var(--danger); border-radius:50%; flex-shrink:0; }
.overdue-text { color: var(--danger); font-weight: var(--fw-medium); }

/* ── Dropdown menus ── */
.dropdown { position: relative; display: inline-block; }
/* Row action menus - the standard popup look (matches .ui-pop / .columns-menu). */
.dropdown-menu {
  position: absolute; right: 0; top: calc(100% + 1px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-popup);
  padding: var(--space-2) 0;
  min-width: 160px; z-index: var(--z-popup);
}
.dropdown-item {
  display: flex; align-items: center; gap: var(--space-2);
  min-height: 34px; padding: 0 var(--space-4); font-size: var(--fs-base); color: var(--text-primary);
  cursor: pointer; transition: background var(--t);
}
.dropdown-item:hover { background: var(--surface-hover); }
.dropdown-item.danger { color: var(--danger); }
.dropdown-divider { height: 1px; background: var(--divider); margin: var(--space-1) 0; }

/* ── File upload ── */
.file-drop {
  border: 2px dashed var(--border-strong);
  border-radius: var(--r);
  padding: 32px 20px;
  text-align: center;
  cursor: pointer;
  transition: all var(--t);
  color: var(--text-secondary);
}
.file-drop:hover, .file-drop.drag { border-color: var(--blue); background: var(--blue-light); color: var(--blue); }
.file-drop svg { width: 32px; height: 32px; margin-bottom: var(--space-2); opacity: 0.5; }
.file-drop h4 { font-size: var(--fs-base); font-weight: var(--fw-semibold); margin-bottom: 4px; }
.file-drop p  { font-size: var(--fs-smd); }

/* ── Avatars ── */
/* Avatar overlay: initials render by default, an <img> overlays them
 * when /img/avatars/<email-slug>.png exists. The img is position:absolute
 * so it covers the initials when present; on 404 the inline onerror
 * removes the img and the initials become visible again.
 *
 * When the img IS present, hide the monogram tile underneath so a PNG
 * with any transparency doesn't reveal the colored circle / initials.
 * The :has(> img) selector toggles automatically — if onerror removes
 * the img, the selector no longer matches and the monogram returns. */
.avatar { display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; font-weight: var(--fw-bold); color: #fff; position: relative; overflow: hidden; }
.avatar > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.avatar:has(> img) { background: #fff; color: transparent; }

/* 96×96 stack used in the Profile modal. Same overlay pattern: initials
 * text inside the round box, <img> absolutely positioned to cover it
 * when the static file exists. */
.profile-avatar-stack {
  position: relative;
  width: 96px;
  height: 96px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--blue);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-4xl);
  font-weight: var(--fw-semibold);
  flex-shrink: 0;
}
.profile-avatar-stack img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.profile-avatar-stack:has(> img) { background: #fff; color: transparent; }
.avatar-sm  { width: 24px; height: 24px; font-size: var(--fs-2xs); }
.avatar-md  { width: 32px; height: 32px; font-size: var(--fs-md); }
.avatar-lg  { width: 40px; height: 40px; font-size: var(--fs-lg); }
.avatar-xl  { width: 48px; height: 48px; font-size: var(--fs-xl); }
.avatar-blue   { background: var(--blue); }
.avatar-green  { background: #28A745; }
.avatar-orange { background: #FD7E14; }
.avatar-purple { background: #6F42C1; }
.avatar-teal   { background: #20C997; }

/* ── Checkboxes ──────────────────────────────────────────────────────────────
 * Clio-style custom checkbox. Three visible states:
 *   • unchecked        — white box, light-blue (#a2d7ff) rounded border
 *   • checked + focus  — #0067d1 fill, white check, soft #0067d1 halo
 *   • checked + blur   — #0067d1 fill, white check, no halo
 *
 * `appearance: none` strips the native control so we can draw the box +
 * checkmark ourselves. The check is a CSS mask of /img/icons/check.svg
 * (Phosphor) painted in white over the blue ::after fill — mask: center
 * means it auto-centers regardless of box size. The :focus halo uses
 * an outline (renders OUTSIDE the box even when overflow:hidden is on
 * an ancestor) instead of box-shadow so it survives table-cell clipping. */
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  margin: 0;
  padding: 0;
  border: 1.5px solid #a2d7ff;
  border-radius: 4px;
  background: #fff;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
  vertical-align: middle;
  transition: background-color 80ms, border-color 80ms, box-shadow 80ms;
}
/* Higher-specificity overrides for the same selector pattern used by
 * the data-table cell rules — keeps focus / hover / checked styling
 * winning even where .data-table tbody td input[type="checkbox"] is
 * already targeted. */
input[type="checkbox"]:hover,
.data-table tbody td input[type="checkbox"]:hover {
  border-color: var(--blue);
}
input[type="checkbox"]:checked,
.data-table tbody td input[type="checkbox"]:checked {
  background: var(--blue);
  border-color: var(--blue);
}
input[type="checkbox"]:checked::after {
  content: '';
  position: absolute;
  inset: 0;
  background-color: #fff;
  /* check-bold.svg at 100 % mask-size — natural fit. Previous 120 % was
   * too big, the original 80 % too small / not bold enough. Adjust this
   * single number if the tick still needs a different weight. */
  -webkit-mask: url('/img/icons/check-bold.svg') center / 90% no-repeat;
          mask: url('/img/icons/check-bold.svg') center / 90% no-repeat;
}
/* Indeterminate (some-but-not-all rows selected — e.g. the Documents "select
 * all" header when a subset is checked). appearance:none strips the native
 * dash, so draw a white horizontal bar over the blue fill, mirroring the
 * :checked tick. Matches Clio's tri-state header checkbox. */
input[type="checkbox"]:indeterminate,
.data-table tbody td input[type="checkbox"]:indeterminate {
  background: var(--blue);
  border-color: var(--blue);
}
input[type="checkbox"]:indeterminate::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 2px;
  transform: translate(-50%, -50%);
  background: #fff;
  border-radius: 1px;
}
/* Busy state: used while a POA write is in flight. Blocks further
 * clicks WITHOUT calling checkboxEl.disabled = true, which would blur
 * the input and kill the focus halo. */
input[type="checkbox"].cb-busy {
  pointer-events: none;
}
/* Focus halo. Covers both `:focus` (any focus, including mouse-click)
 * and `:focus-visible` (keyboard-only focus) — older browsers and Safari
 * are inconsistent about which one fires for checkbox clicks, and we
 * want the halo visible in both cases. Solid #0067d1 with a 2 px gap
 * so the halo reads as a discrete ring around the box. */
input[type="checkbox"]:focus,
input[type="checkbox"]:focus-visible,
.data-table tbody td input[type="checkbox"]:focus,
.data-table tbody td input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* ── Misc helpers ── */
.text-muted     { color: var(--text-tertiary); }
.text-secondary { color: var(--text-secondary); }
.text-primary   { color: var(--text-primary); }
.text-blue      { color: var(--blue); }
.text-red       { color: var(--danger); }
.text-green     { color: var(--success); }
.text-sm        { font-size: var(--fs-smd); }
.text-xs        { font-size: var(--fs-sm); }
.subtext        { font-size: var(--fs-sm); color: var(--text-secondary); }  /* small caption under a primary label (list-row secondary line) */
.fw-600         { font-weight: var(--fw-semibold); }
.d-flex         { display: flex; }
.align-center   { align-items: center; }
.gap-8          { gap: var(--space-2); }
.gap-12         { gap: var(--space-3); }
.ml-auto        { margin-left: auto; }
.mt-4           { margin-top: var(--space-1); }
.mt-8           { margin-top: var(--space-2); }
.mt-16          { margin-top: 16px; }
.mb-8           { margin-bottom: var(--space-2); }
.mb-16          { margin-bottom: var(--space-4); }
.tag {
  display: inline-block;
  background: var(--blue-light); color: var(--blue);
  font-size: var(--fs-sm); font-weight: var(--fw-medium);
  padding: var(--space-1) var(--space-2); border-radius: 3px;
}

/* Mobile nav drawer toggle (hamburger) + backdrop. Hidden on desktop;
 * the @media block reveals the hamburger and lets the backdrop show
 * while the drawer is open. */
.mobile-nav-toggle {
  display: none; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin-left: var(--space-1);
  border: 0; background: transparent; color: var(--topbar-text);
  cursor: pointer; flex-shrink: 0;
}
.sidebar-backdrop {
  display: none; position: fixed; inset: 0; z-index: 89;
  background: rgba(0, 0, 0, 0.45);
}

/* ── Responsive ── */
@media (max-width: 768px) {
  .mobile-nav-toggle { display: inline-flex; }
  .sidebar-backdrop:not([hidden]) { display: block; }
  #sidebar { transform: translateX(-100%); }
  #sidebar.mobile-open { transform: none; }
  #main { margin-left: 0 !important; }
  /* Tax Returns / Contacts / contact-detail pin #main to (100% − sidebar).
   * On mobile the sidebar is off-canvas (margin-left:0), so give those pages
   * the full viewport width — otherwise content is squeezed into a
   * ~sidebar-narrow strip and clipped. */
  body:has(.tr-page-body) #main,
  body:has(.tr-page-body) #sidebar.collapsed ~ #main,
  body:has(.contact-page) #main,
  body:has(.contact-page) #sidebar.collapsed ~ #main,
  body:has(.ec-form) #main,
  body:has(.ec-form) #sidebar.collapsed ~ #main { width: 100vw !important; }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  .form-row { grid-template-columns: 1fr; }
  /* .ec-name-row/.ec-company-row's 4-col rule outranks the generic reset
   * above - repeat it, and undo the Company 2-col span. */
  .form-row.ec-name-row, .form-row.ec-company-row { grid-template-columns: 1fr; }
  .ec-company-row > .form-group:first-child { grid-column: auto; }
  .form-layout { flex-direction: column; }
  .form-sections-nav { width: 100%; position: static; }
}

/* ── Tools → Scorer report ── */
.scorer-report .card-title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); color: var(--text-primary); }
.scorer-report .kpi-sub { font-size: var(--fs-sm); color: var(--text-secondary); margin-top: 2px; }
.scorer-table { font-size: var(--fs-base); }
.scorer-table td { padding: 8px var(--space-3); vertical-align: top; border-bottom: 1px solid var(--grey-150); }
.scorer-table .scr-when { white-space: nowrap; color: var(--text-secondary); }
.scorer-table .scr-subj { max-width: 320px; }
.scorer-table .scr-sender { font-size: var(--fs-sm); margin-top: 1px; }
.scorer-report .pill {
  display: inline-block; padding: 1px 8px; border-radius: 999px;
  font-size: var(--fs-xs); font-weight: var(--fw-semibold); white-space: nowrap;
}
.scorer-report .pill-miss { background: #fde8e8; color: #9b1c1c; }
.scorer-report .pill-rank { background: #fef3c7; color: #92400e; }

/* ── Communications: unified client threads ─────────────────────────────── */
.co-card { padding: 0; overflow: hidden; flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* grid-template-rows: minmax(0,1fr) forces the single row to the fixed height and
   lets it shrink below content, so each pane scrolls INTERNALLY (min-height:0 on
   the scroll children) instead of the tall client list stretching the whole row -
   which was leaving a big empty area under a short thread. */
/* Fill the flex parent (.co-card -> .tr-page-body) instead of a magic viewport calc,
   so the pane bottom sits at the normal card gutter rather than leaving a big gap. */
.co-split { display: grid; grid-template-columns: 360px 1fr; grid-template-rows: minmax(0, 1fr); flex: 1; min-height: 0; }
.co-list-pane { display: flex; flex-direction: column; border-right: 1px solid var(--border); min-width: 0; min-height: 0; background: var(--surface); }
.co-list-head { padding: var(--space-3); border-bottom: 1px solid var(--divider); }
.co-search-wrap { width: 100%; }
.co-list { flex: 1; min-height: 0; overflow-y: auto; }
.co-list-empty { padding: 40px var(--space-4); text-align: center; color: var(--text-tertiary); font-size: var(--fs-sm); }
.co-list-foot { border-top: 1px solid var(--divider); padding: var(--space-2) var(--space-3); display: flex; align-items: center; gap: var(--space-2); justify-content: space-between; min-height: 44px; }
.co-page-info { font-size: var(--fs-sm); color: var(--text-secondary); }
.co-page-btn { padding: 4px 10px; }

.co-crow { display: grid; grid-template-columns: 32px 1fr auto; gap: var(--space-3); padding: 10px var(--space-3); border-bottom: 1px solid var(--divider); cursor: pointer; position: relative; align-items: start; }
.co-crow:hover { background: var(--surface-hover); }
.co-crow-sel { background: var(--blue-light); }
.co-crow-sel::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--blue); }
.co-crow-main { min-width: 0; }
.co-crow-name { font-weight: var(--fw-semibold); font-size: var(--fs-smd); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.co-crow-prev { display: flex; align-items: center; gap: 5px; color: var(--text-secondary); font-size: var(--fs-sm); margin-top: 2px; min-width: 0; }
.co-crow-prev > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.co-prev-ic { width: 13px; height: 13px; color: var(--text-tertiary); flex: none; }
.co-crow-mix { display: flex; gap: var(--space-3); margin-top: 4px; }
.co-mix { display: inline-flex; align-items: center; gap: 3px; font-size: var(--fs-xs); color: var(--text-tertiary); }
.co-mix-ic { width: 12px; height: 12px; }
.co-crow-time { font-size: var(--fs-xs); color: var(--text-tertiary); white-space: nowrap; }

.co-thread { display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--surface); }
.co-thread-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-3); color: var(--text-tertiary); font-size: var(--fs-sm); }
.co-empty-ic { width: 40px; height: 40px; color: var(--grey-250); }
.co-thread-head { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--divider); }
.co-thread-who { min-width: 0; }
.co-thread-name { font-weight: var(--fw-semibold); font-size: var(--fs-base); color: var(--text-primary); }
.co-thread-sub { font-size: var(--fs-xs); color: var(--text-tertiary); }
.co-filter-row { padding: var(--space-2) var(--space-4); border-bottom: 1px solid var(--divider); flex-wrap: wrap; row-gap: var(--space-2); }
.co-stream { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-2) var(--space-4) var(--space-4); }
/* Cards MUST keep their natural height. .co-stream is a height-constrained flex
   column and the cards use overflow:hidden (which zeroes a flex item's implicit
   min-height), so without flex:none a long thread shrinks every card to a sliver -
   the more messages, the thinner the cards. This is what made large threads render
   as faint horizontal lines. */
.co-stream > * { flex: none; }
.co-stream-empty { color: var(--text-tertiary); font-size: var(--fs-sm); text-align: center; padding: 40px; }
.co-day { align-self: center; font-size: var(--fs-xs); color: var(--text-primary); background: var(--grey-150); border-radius: 999px; padding: 2px 10px; margin: 2px 0; }
.co-muted { color: var(--text-tertiary); }

/* Dialpad-style call card: a grey header bar (icon + title + route + duration +
   download-recording link) over a Summary + Action items body. */
.co-call { width: 75%; max-width: 75%; border: 1px solid var(--border-strong); border-radius: var(--r-lg); background: var(--surface); overflow: hidden; box-shadow: var(--shadow-sm); }
/* Chat-style log by SENDER: from the client / a third party -> left (light grey);
   from the firm -> right (light blue), even if firm members are cc'd. Each card is
   a fixed 3/4 of the column; internal (mid) spans full width. Colours match the SMS
   bubbles (.mtx-in / .mtx-out), and the header bar goes transparent so the whole
   card reads as one tint. */
.co-call.co-in  { align-self: flex-start; background: #f1f3f5; }
.co-call.co-out { align-self: flex-end;   background: #e8f1fd; }
.co-call.co-mid { align-self: stretch; width: 100%; max-width: 100%; }
.co-call.co-in  .co-call-bar,
.co-call.co-out .co-call-bar { background: transparent; }
/* SMS bubbles in the log take the same fixed 3/4 width; their left/right sides and
   grey/blue tints (.mtx-in / .mtx-out) already match the cards. */
.co-msg { width: 75%; max-width: 75%; }
.co-call-bar { display: flex; align-items: flex-start; gap: 10px; padding: 10px 12px; background: var(--surface-titlebar); border-bottom: 1px solid var(--divider); }
.co-call-ic { width: 30px; height: 30px; border-radius: 8px; background: var(--blue-light); color: var(--blue); display: grid; place-items: center; flex: none; }
.co-call-ic .icon { width: 16px; height: 16px; }
.co-call-tt { min-width: 0; flex: 1; }
.co-call-title { font-weight: var(--fw-semibold); font-size: var(--fs-smd); color: var(--text-primary); }
.co-call-route { font-size: var(--fs-sm); color: var(--text-primary); margin-top: 2px; }
.co-call-route .co-arrow { display: inline-block; width: 15px; height: 15px; vertical-align: middle; margin: 0 5px; position: relative; top: -1px; }
.co-call-meta { font-size: var(--fs-sm); color: var(--text-primary); margin-top: 2px; }
.co-call-acts { display: flex; align-items: center; gap: 2px; flex: none; }
.co-call-act { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: var(--r-sm); border: none; background: none; color: var(--text-primary); cursor: pointer; text-decoration: none; }
.co-call-act:hover { background: var(--surface-hover); }
.co-call-act .icon { width: 16px; height: 16px; }
.co-call-body { padding: 10px 14px 12px; font-size: var(--fs-sm); }
.co-call-body .cx-recap-h { font-size: var(--fs-sm); color: var(--text-primary); font-weight: var(--fw-bold); margin: 10px 0 3px; }
.co-call-body .cx-recap-h:first-child { margin-top: 0; }
.co-call-body .cx-recap-list { margin: 3px 0 0; padding-left: 20px; }
.co-call-body .cx-recap-list li { margin: 3px 0; }
.co-call-missed { border-color: var(--danger); }
.co-call-missed .co-call-ic { background: #fde8e8; color: var(--danger); }
.co-call-missed .co-call-title { color: var(--danger); }
.co-tx-toggle { margin: 6px 0 0; background: none; border: none; color: var(--text-link); font-size: var(--fs-xs); cursor: pointer; padding: 0; }
.co-tx { margin: 6px 0 0; padding: 8px 10px; background: var(--page-bg); border-radius: var(--r-sm); font-size: var(--fs-xs); color: var(--text-primary); line-height: 1.5; max-height: 220px; overflow-y: auto; }
/* Two-column transcript: names left (auto-sized to the widest), text aligned
   right with a hanging indent on wrap. One grid so every row shares the column. */
.co-tx-grid { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; align-items: baseline; }
/* display:grid overrides the [hidden] UA rule, so the toggle needs this to hide. */
.co-tx-grid[hidden] { display: none; }
.co-tx-who { font-weight: var(--fw-semibold); white-space: nowrap; }
.co-tx-said { min-width: 0; }

/* Email / internal / fax card: reuses the call-card chrome (.co-call) so it sits
   in the thread identically. Only the icon tint (neutral, not the call's blue),
   the clamped body, the caret rotation, and expand-only delete are email-specific. */
.co-mail-bar { cursor: pointer; }
.co-mail-ic2 { background: var(--grey-150); color: var(--text-secondary); }
.co-mail2.co-mid { align-self: stretch; max-width: 100%; }
.co-mail-text { font-size: var(--fs-sm); line-height: 1.55; color: var(--text-primary); white-space: pre-wrap; overflow-wrap: anywhere; }
.co-mail2:not(.co-mail-open) .co-mail-text { display: -webkit-box; -webkit-line-clamp: 12; -webkit-box-orient: vertical; overflow: hidden; }
.co-mail-exp .icon { transition: transform var(--t); }
.co-mail2.co-mail-open .co-mail-exp .icon { transform: rotate(180deg); }
/* Email attachments: a paperclip button in the header opens a popup (reusing the
   .ui-pop look, viewport-fixed to clear the card's overflow:hidden) listing each
   file as a download link, plus a Download all action. */
.co-att-wrap { position: relative; display: inline-flex; }
.co-att-btn { position: relative; }
.co-att-badge { position: absolute; top: -1px; right: -1px; min-width: 14px; height: 14px; padding: 0 3px; border-radius: 999px; background: var(--blue); color: #fff; font-size: 9px; line-height: 14px; text-align: center; font-weight: var(--fw-bold); }
.co-att-pop { min-width: 220px; max-width: 340px; max-height: 320px; overflow-y: auto; }
.co-att-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 var(--space-4) 6px; margin-bottom: 4px; border-bottom: 1px solid var(--divider); font-size: var(--fs-xs); color: var(--text-secondary); }
.co-att-all { display: inline-flex; align-items: center; gap: 4px; background: none; border: none; color: var(--text-link); font-size: var(--fs-xs); cursor: pointer; padding: 2px 4px; border-radius: var(--r-sm); white-space: nowrap; }
.co-att-all:hover { background: var(--surface-hover); }
.co-att-all .icon { width: 13px; height: 13px; }
.co-att-row { text-decoration: none; font-size: var(--fs-sm); color: var(--text-primary); }
.co-att-row .icon { width: 14px; height: 14px; flex: none; color: var(--text-tertiary); }
.co-att-row span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.co-del { display: inline-flex; align-items: center; gap: 4px; background: none; border: none; color: var(--text-tertiary); font-size: var(--fs-xs); cursor: pointer; padding: 6px 0 0; }
.co-del:hover { color: var(--danger); }
.co-del .icon { width: 14px; height: 14px; }

@media (max-width: 900px) {
  .co-card { flex: none; display: block; }
  .co-split { grid-template-columns: 1fr; height: auto; flex: none; }
  .co-list-pane { border-right: none; border-bottom: 1px solid var(--border); max-height: 320px; }
}

/* Mail & Ship: checkbox select column (matches the Documents/Communications tables) */
.ms-col-check { width: 40px; text-align: center; padding-left: var(--space-3); }
.ms-col-check input { cursor: pointer; margin: 0; vertical-align: middle; }

/* ── Family / related people (contact-detail Dashboard card) ── */
.fl-list { list-style: none; margin: 0; padding: 0; }
.fl-row { display: flex; align-items: flex-start; gap: var(--space-2); padding: var(--space-2) 0; border-top: 1px solid var(--border); }
.fl-row:first-child { border-top: none; padding-top: 0; }
.fl-main { flex: 1 1 auto; min-width: 0; }
.fl-line { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.fl-name { font-weight: 600; }
.fl-pill { display: inline-flex; align-items: center; height: 20px; padding: 0 8px; border-radius: 999px; background: var(--info-bg, #e8f1fd); color: var(--blue, #0067d1); font-size: var(--fs-sm, 12px); font-weight: 600; white-space: nowrap; }
.fl-dep { display: inline-flex; align-items: center; height: 20px; padding: 0 8px; border-radius: 999px; background: #eaf7ee; color: #1a7f43; font-size: var(--fs-sm, 12px); font-weight: 600; }
.fl-meta { margin-top: 2px; font-size: var(--fs-sm, 12px); color: #6b7a8d; word-break: break-word; }
.fl-actions { display: flex; gap: 2px; flex: none; }
.fl-icon { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: none; background: none; color: #6b7a8d; border-radius: var(--r); cursor: pointer; }
.fl-icon:hover { background: var(--sidebar-hover-light, #eef2f7); color: #22303f; }

/* Add / edit family member modal form */
.fl-form { display: flex; flex-direction: column; }
.fl-lbl { display: block; font-size: var(--fs-md, 13px); color: #516378; font-weight: 600; margin: var(--space-3) 0 var(--space-1); }
.fl-form > .fl-lbl:first-child { margin-top: 0; }
.fl-sub { font-weight: 400; color: #8a97a6; }
.fl-input { width: 100%; height: 40px; padding: 0 var(--space-3); border: 1px solid #a7b3c2; border-radius: var(--r); font-size: var(--fs-base, 14px); outline: none; background: #fff; color: inherit; }
.fl-input:focus { border-color: var(--blue, #0067d1); }
select.fl-input { padding: 0 var(--space-2); }
.fl-check { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-3); font-size: var(--fs-base, 14px); color: #22303f; cursor: pointer; }
.fl-check input { margin: 0; }
.fl-two { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2); margin-top: var(--space-1); }
.fl-hint { font-size: var(--fs-sm, 12px); color: #8a97a6; margin-top: 4px; }
.fl-ta-wrap { position: relative; }
.fl-ta-drop { position: absolute; z-index: 30; left: 0; right: 0; top: calc(100% + 2px); background: #fff; border: 1px solid #a7b3c2; border-radius: var(--r); box-shadow: 0 8px 24px rgba(0,0,0,.14); max-height: 220px; overflow-y: auto; }
.fl-ta-item { padding: 8px var(--space-3); font-size: var(--fs-base, 14px); cursor: pointer; display: flex; align-items: center; gap: 6px; }
.fl-ta-item:hover { background: var(--info-bg, #e8f1fd); }
.fl-ta-tag { font-size: var(--fs-sm, 12px); color: #8a97a6; }

/* Properties & assets: address grid + shared-owner rows */
.fl-three { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: var(--space-2); margin-top: var(--space-1); }
.as-owner-row { display: flex; gap: var(--space-2); align-items: center; margin-bottom: var(--space-2); }
.as-owner-tawrap { flex: 1 1 auto; min-width: 0; }
.as-owner-pct { flex: 0 0 64px; width: 64px; text-align: right; }
.as-owner-del { flex: none; }
.fl-addrow { border: none; background: none; color: var(--blue, #0067d1); font-size: var(--fs-md, 13px); font-weight: 600; cursor: pointer; padding: 4px 0; }
.fl-addrow:hover { text-decoration: underline; }

/* Family card in the centre column: matter-related-style blocks + edit controls */
.fl-blk-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-2); }
.fl-blk-actions { display: flex; gap: 2px; flex: none; }
.fl-blk-meta { font-size: var(--fs-sm, 12px); color: #6b7a8d; margin-top: 2px; word-break: break-word; }
.fl-static { display: flex; align-items: center; background: #f2f5f8; color: #22303f; }
