/* Shared Cyticx data-page patterns.
 * Ported from the effective ads-dashboard rules. Keep selectors generic so
 * pages can reuse the approved dashboard construction without ads-* hooks.
 */

.kt-filter-bar {
    --kt-control-height: 2.375rem;
    --kt-pattern-lime: #b7f34a;
    --kt-pattern-green: var(--color-green-500);
    --kt-pattern-orange: #ffad5b;
    align-items: flex-end;
    background:
        linear-gradient(120deg, color-mix(in srgb, var(--primary) 7%, transparent), transparent 34%),
        var(--card);
    display: grid;
    gap: .75rem;
    grid-template-columns:
        minmax(23rem, 1.5fr)
        minmax(14rem, .75fr)
        minmax(13rem, .65fr)
        minmax(10rem, auto)
        auto;
    padding: .875rem 1rem;
    position: relative;
    z-index: 12;
}

.kt-filter-group {
    display: grid;
    gap: .35rem;
    min-width: 0;
}

.kt-filter-label {
    color: var(--muted-foreground);
    /* 2026-08-05: 12px admin-wide label floor (ui-qa visible-text-floor-12px
       theme lift) - was 10px. Not a table cell, no documented dense exemption. */
    font-size: 12px;
    font-weight: var(--font-weight-medium);
    letter-spacing: .08em;
    text-transform: uppercase;
}

.kt-filter-note {
    align-self: center;
    color: var(--muted-foreground);
    font-size: 11px;
    line-height: 1.35;
    max-width: 12rem;
    padding-top: 1rem;
}

.kt-filter-note strong {
    color: var(--kt-pattern-orange);
    font-weight: var(--font-weight-medium);
}

.kt-segment {
    align-items: center;
    background: color-mix(in srgb, var(--muted) 78%, transparent);
    border: 1px solid var(--border);
    border-radius: calc(var(--radius) + 2px);
    display: grid;
    gap: .2rem;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    /* 2026-08-05: ui-qa tap-targets-44px fix - `--kt-control-height` is only
       DECLARED on `.kt-filter-bar`. A `.kt-segment` used outside that wrapper
       (e.g. dashboard's Lead Tracking period toggle, nested in a bare
       `.kt-filter-group`) had an unresolved custom property, which makes
       `height` invalid at computed-value time and fall back to `auto` -
       collapsing the segment (and its buttons) to a 14px text line instead of
       a real control. Fallback value matches `--kt-control-height`'s own
       definition exactly, so every ALREADY-correct `.kt-filter-bar`-nested
       instance is pixel-identical; only the previously-broken standalone case
       is fixed. */
    height: var(--kt-control-height, 2.375rem);
    padding: .2rem;
}

.kt-segment-btn {
    border: 1px solid transparent;
    border-radius: var(--radius);
    color: var(--muted-foreground);
    cursor: pointer;
    font-size: var(--text-xs);
    line-height: 1;
    height: 100%;
    padding: 0 .65rem;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
    white-space: nowrap;
}

.kt-segment-btn:hover,
.kt-segment-btn:focus-visible {
    color: var(--foreground);
}

.kt-segment-btn.is-active {
    background: color-mix(in srgb, var(--primary) 15%, var(--card));
    border-color: color-mix(in srgb, var(--primary) 38%, var(--border));
    color: var(--foreground);
    box-shadow: inset 0 0 16px color-mix(in srgb, var(--primary) 8%, transparent);
}

/* 2026-08-05: ui-qa tap-targets-44px calibration - 2.375rem (38px) is the
   correct dense CONTROL height on desktop (a real mouse target, matches every
   other .kt-filter-bar control), but `.kt-segment-btn`'s `height:100%` still
   inherits from it, so on mobile the period-toggle buttons (Today/Yesterday/
   Last 7 Days) fell short of a real 44px thumb target. Scoped mobile-only
   bump, same pattern as kt-btn-sm - desktop is untouched.
   Two rules, not one: `.kt-segment-btn` gets an explicit 44px height (its own
   `height:100%` cannot exceed the grid track it sits in), and `.kt-segment`
   switches from a fixed `height` to `min-height` so its grid row is free to
   grow around that taller button plus its own `.2rem` padding, instead of
   clipping the button back down to a fixed 44px container. */
@media (max-width: 1023px) {
    .kt-segment {
        height: auto;
        min-height: 44px;
    }

    .kt-segment-btn {
        height: 44px;
    }
}

.kt-stat-strip {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: .24rem;
}

/* Keep a KPI strip separated from the next framed content block when the
   page is not using a section shell to provide the stack rhythm. */
.kt-stat-strip + .kt-card,
.kt-stat-strip + .cx-gd-section {
    margin-top: 12px;
}

/* A section shell already supplies the stack rhythm with flex gap. */
.sb-section > .kt-stat-strip + .kt-card,
.sb-section > .kt-stat-strip + .cx-gd-section {
    margin-top: 0;
}

.kt-stat-chip {
    /* 2026-08-05: bare chips (no --lime/--green/--blue/etc modifier) defaulted
       their value color to var(--primary) - Cyticx's dark-theme blue-600
       (oklch(54.6% 0.245 262.881) = rgb(21,93,252)) - which measured 3.41:1
       on this chip's own 5%-mix tint background (rgb(19,23,34), "Calls Today"
       kt-stat-chip) and 3.54:1 on another instance (rgb(19,19,22)), both under
       the 4.5:1 WCAG floor (measured via canvas raster + WCAG relative
       luminance, not eyeballed). Lifted to the SAME readable-blue value
       already established elsewhere in this codebase for the identical
       problem (ad-spy.blade.php's `.sb-section .cx-pill-primary { color:
       #7ab5ff; }` override) rather than inventing a new one. This is the
       chip family's shared color variable, so every bare .kt-stat-chip
       app-wide gets the readable blue - text AND its background/border tint
       (both derive from --kt-stat-color) - intentional, keeps the chip
       family internally consistent. Named variants (--lime/--green/--blue/
       etc, below) are untouched. */
    --kt-stat-color: #7ab5ff;
    --kt-pattern-lime: #b7f34a;
    --kt-pattern-green: var(--color-green-500);
    --kt-pattern-blue: #57a6ff;
    --kt-pattern-cyan: #45d5e7;
    --kt-pattern-violet: #a98bff;
    --kt-pattern-pink: #f277b6;
    --kt-pattern-orange: #ffad5b;
    align-items: baseline;
    background: color-mix(in srgb, var(--kt-stat-color) 5%, var(--card));
    border: 1px solid color-mix(in srgb, var(--kt-stat-color) 22%, var(--border));
    border-radius: var(--radius);
    display: inline-flex;
    flex: 0 0 auto;
    gap: .28rem;
    overflow: hidden;
    position: relative;
    width: max-content;
    min-height: 0;
    padding: .45rem .6rem;
    white-space: nowrap;
}

.kt-stat-chip::before {
    background: linear-gradient(90deg, transparent, var(--kt-stat-color) 50%, transparent);
    content: '';
    height: 2px;
    left: 6%;
    opacity: .85;
    pointer-events: none;
    position: absolute;
    right: 6%;
    top: 0;
}

.kt-stat-chip--lime { --kt-stat-color: var(--kt-pattern-lime); }
.kt-stat-chip--green { --kt-stat-color: var(--kt-pattern-green); }
.kt-stat-chip--blue { --kt-stat-color: var(--kt-pattern-blue); }
.kt-stat-chip--cyan { --kt-stat-color: var(--kt-pattern-cyan); }
.kt-stat-chip--violet { --kt-stat-color: var(--kt-pattern-violet); }
.kt-stat-chip--pink { --kt-stat-color: var(--kt-pattern-pink); }
.kt-stat-chip--orange { --kt-stat-color: var(--kt-pattern-orange); }
.kt-stat-chip--danger { --kt-stat-color: var(--destructive-readable); }

.kt-stat-label,
.kt-stat-source {
    color: var(--muted-foreground);
    /* 2026-08-05: 12px admin-wide label floor (ui-qa visible-text-floor-12px
       theme lift) - was 11px. This is a KPI/stat-chip LABEL, not a table cell -
       the documented 11px dense standard stays reserved for .kt-data-grid
       td/th only. */
    font-size: 12px;
    font-weight: var(--font-weight-medium);
    letter-spacing: .045em;
    line-height: 1;
    text-transform: uppercase;
}

/* CYT-644 finding #15 (confirmed live 2026-08-18): .kt-stat-source trails
   directly after .kt-stat-value with only the chip's own small .28rem
   inter-child gap - fine when the source is plain text, but when it starts
   with its own digit ("2 locations" sitting right after the value "0") the
   two numbers read as one ambiguous run at a glance. Adds breathing room
   specifically before a trailing source segment, app-wide, wherever
   .kt-stat-source is used - purely additive spacing, doesn't touch the
   shared .kt-stat-label styling both selectors still share above. */
.kt-stat-source {
    margin-inline-start: .3rem;
}

.kt-stat-main,
.kt-stat-dual {
    align-items: baseline;
    display: inline-flex;
    gap: .2rem;
}

.kt-stat-value {
    color: var(--kt-stat-color);
    font-size: 18px;
    font-variant-numeric: tabular-nums;
    font-weight: 650;
    letter-spacing: -.02em;
    line-height: 1;
    white-space: nowrap;
}

.kt-stat-separator {
    color: var(--muted-foreground);
    /* 2026-08-05: 12px admin-wide label floor - was 10px. */
    font-size: 12px;
}

.kt-stat-difference {
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.kt-stat-difference--up { color: var(--kt-pattern-green); }
.kt-stat-difference--down { color: var(--destructive); }
.kt-stat-difference--flat { color: var(--muted-foreground); }

/* ---- Compact collapsible list ("kt-collapse") -----------------------------
   Adir, 2026-08-18: liked the collapsed-group-per-module layout on the
   Advisor backlog digest (a one-off report) and asked for that "list of one
   under another, laid down in a nice compact way" to become a real pattern
   in the design system, not a one-off. This is that pattern, built on the
   app's own tokens (var(--card)/var(--border), not the report's orange
   scheme - reports and the admin app are deliberately different palettes).
   It also gives a home for the several page-local details/summary reinventions
   already in the codebase (.pb-details, .gss-item, .pbr-sec, the portal's
   .cxp-health-factor) - new usages should reach for this instead of a new
   one-off; existing ones can migrate opportunistically, not as a mass sweep. */
.kt-collapse-group { display: flex; flex-direction: column; gap: 8px; }

.kt-collapse {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 12px;
    overflow: hidden;
}

.kt-collapse > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 14px;
    cursor: pointer;
    list-style: none;
    font-weight: 700;
    font-size: var(--text-sm, .875rem);
    color: var(--foreground);
}
.kt-collapse > summary::-webkit-details-marker { display: none; }
.kt-collapse > summary::after {
    content: '';
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-right: 1.5px solid var(--muted-foreground);
    border-bottom: 1.5px solid var(--muted-foreground);
    transform: rotate(45deg);
    transition: transform .15s ease;
    margin-left: 4px;
}
.kt-collapse[open] > summary::after { transform: rotate(-135deg); }

.kt-collapse-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 500;
    font-size: var(--text-xs, .75rem);
    color: var(--muted-foreground);
    margin-left: auto;
    margin-right: 6px;
}
.kt-collapse-meta strong { color: var(--primary); font-weight: 700; }

.kt-collapse-body { padding: 0 14px 12px; }

.kt-collapse-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.kt-collapse-tag {
    font-size: 10.5px;
    font-weight: 600;
    padding: 2px 9px;
    border-radius: 20px;
    background: var(--muted);
    color: var(--muted-foreground);
    border: 1px solid var(--border);
    white-space: nowrap;
}

/* The "list of one under another" rows themselves - compact, zero gap,
   thin hairline between rows instead of card-per-row. */
.kt-collapse-list { display: flex; flex-direction: column; }
.kt-collapse-item {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 6px 0;
    border-top: 1px solid var(--border);
    font-size: var(--text-sm, .875rem);
}
.kt-collapse-item:first-child { border-top: none; }
.kt-collapse-item-id { flex: 0 0 auto; font-size: var(--text-xs, .75rem); color: var(--muted-foreground); }
.kt-collapse-item-title { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.kt-collapse-item-tag {
    flex: 0 0 auto;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .02em;
    padding: 1px 7px;
    border-radius: 20px;
    background: var(--muted);
    color: var(--muted-foreground);
}

.kt-delta {
    align-items: center;
    display: inline-flex;
    /* 2026-08-05: 12px admin-wide label floor - was 9px. `.kt-data-grid
       .kt-delta` (a few lines below) stays untouched at 10.5px - that one is
       a table-cell metric under the documented dense-grid exemption; this
       bare rule is the KPI-tile/card delta ("-5 vs yesterday") which is not
       a table cell and gets the theme lift like every other label. */
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    font-weight: var(--font-weight-medium);
    gap: .18rem;
    white-space: nowrap;
}

.kt-delta--up { color: var(--kt-pattern-green); }
/* 2026-08-05: raw var(--destructive) (red-600, rgb(231,0,11)) measured
   3.89:1 on a kt-stat card surface (rgb(19,19,22), the dashboard hero's
   "-5 vs yesterday" delta) - under the 4.5:1 floor (canvas raster + WCAG
   luminance, measured not eyeballed). Lifted to --destructive-readable
   (#ff6b70), the token this codebase already uses for exactly this - dark-
   surface-readable red (see .kt-stat-chip--danger a few lines up). Keeps
   the up=green/down=red delta semantics, only the red shade changes. */
.kt-delta--down { color: var(--destructive-readable); }
.kt-delta--flat,
.kt-delta--none { color: var(--muted-foreground); }

.kt-status-dot {
    background: var(--muted-foreground);
    border-radius: 50%;
    flex: 0 0 auto;
    height: .46rem;
    width: .46rem;
}

.kt-status-dot--success { background: var(--kt-pattern-green); }
.kt-status-dot--warning { background: var(--kt-pattern-orange); }
.kt-status-dot--danger { background: var(--destructive); }

.kt-table-wrap {
    border-radius: var(--radius);
    /* 2026-08-07 PHANTOM SCROLLBAR FIX: was `overflow-x: scroll`, which
       reserves a permanently-visible scrollbar strip (styled blue via the
       ::-webkit-scrollbar rules below) even when the table doesn't actually
       overflow - the owner reported "bar on the bottom" / blue slivers at
       the table's bottom corners TWICE before this got correctly diagnosed.
       `auto` still shows the same styled scrollbar the instant a table
       genuinely overflows (::-webkit-scrollbar rules below are unchanged),
       it just stops drawing one when there's nothing to scroll. */
    overflow-x: auto;
    padding: 0;
    scrollbar-color: color-mix(in srgb, var(--primary) 55%, var(--muted)) var(--muted);
    scrollbar-width: thin;
}

/* 2026-08-24 NESTED-RADIUS SEAM: `.kt-table-wrap` sets `border-radius:
   var(--radius)` (8px) AND a non-visible overflow, so it CLIPS the table it
   wraps. Inside a card it is nested in `.kt-card-content`, which carries the
   card's own `0 0 16px 16px`. Two disagreeing clip radii on the same stack of
   rows is what the owner sees as "rounded corners in the table": the thead
   band's top corners get bitten out at 8px while the card around it turns at
   16px, and the last row rounds twice.

   The card is the thing with a shape; the scroller inside it is not. Scoped so
   a standalone `.kt-table-wrap` used outside a card keeps its own 8px. */
.kt-card-content > .kt-table-wrap,
.kt-card-content > * > .kt-table-wrap,
.kt-card-table .kt-table-wrap {
    border-radius: 0;
}

.kt-table-wrap::-webkit-scrollbar {
    height: 9px;
}

.kt-table-wrap::-webkit-scrollbar-track {
    background: var(--muted);
}

.kt-table-wrap::-webkit-scrollbar-thumb {
    background: color-mix(in srgb, var(--primary) 55%, var(--muted));
    border: 2px solid var(--muted);
    border-radius: 99px;
}

/* 2026-08-06 STYLE BOOK PARITY: pages.css only styles `.sb-section .sb-table
   thead th` / `tbody td` (scoped to a `.sb-section` ancestor - see the
   "CORRECTION 2026-07-10" comment there). Dashboard cards are plain
   `<article class="kt-card">`, never `.sb-section` (wrapping them in it would
   also reattach the Style Book's own top glow bar - the opposite of what this
   parity pass wants), so a simple `sb-table sb-data-table` class swap on a
   dashboard table would silently fall back to unstyled `.kt-table` defaults
   instead of the Style Book's 13px muted header band. This is the SAME
   declaration set, just usable without a `.sb-section` ancestor - keep it in
   lockstep with pages.css if that ever changes. Used by the dashboard's
   "Needs You" and "Lead Tracking" tables. */
.sb-data-table thead th {
    font-size: var(--text-2sm);
    font-weight: var(--font-weight-medium);
    /* Brightened vs. the bare --muted-foreground token used by the
       .sb-section-scoped original in pages.css - measured 2026-08-06: the
       Style Book's own .sb-section wrapper adds a faint white glass overlay
       (background: rgba(255,255,255,.05/.015)) that lifts the effective
       background enough to clear AA there; a plain .kt-card here has no such
       overlay, so copying --muted-foreground verbatim measured 3.97:1 against
       this header band's rgb(39,39,42) (need 4.5, ui-qa body-text-contrast).
       Global --muted-foreground token is untouched everywhere else. */
    color: color-mix(in srgb, var(--muted-foreground) 65%, white);
    text-align: left;
    padding: 0.75rem 1.25rem;
    border-bottom: 1px solid rgba(255, 255, 255, .06);
}

.sb-data-table tbody td {
    padding: 0.75rem 1.25rem;
    border-bottom: 1px solid rgba(255, 255, 255, .05);
    color: var(--foreground);
}

.sb-data-table tbody tr:last-child td { border-bottom: 0; }

.sb-data-table tbody tr:hover td { background: rgba(255, 255, 255, .05); }

/* CYT-618 scoped this to the Rankings page ONLY (2026-08-17 - was shipped
   app-wide by mistake, painting every .kt-card-title portal-wide red before
   this fix, including Dashboard/Website Health/GBP - caught live by Adir).
   The Signal Red per-title accent treatment is NOT approved for portal-wide
   rollout yet (see project_cyticx_portal_theme_signal_red.md) - scope any
   future expansion of this pattern to a specific page wrapper the same way. */
body.fi-panel-portal .cxp-rankings-page .kt-card-title {
  /* CYT-618 follow-up (2026-08-17): was `display: inline-flex`, which makes
     the heading itself an inline-level box - the sibling .cxp-meta
     description span (no block wrapper between them in the markup, see
     rankings blade header markup) then flowed onto the SAME LINE with zero
     gap ("Keyword resultsShowing 50 of 145..."). `display: flex` keeps the
     icon+text internal alignment but keeps the heading block-level, so the
     description still starts on its own line. */
  --cxp-title-accent: var(--portal-primary, #007A3D);
  display: flex;
  align-items: center;
  gap: .45rem;
  color: var(--cxp-title-accent) !important;
}
body.fi-panel-portal .cxp-rankings-page .kt-card-title::before {
  color: var(--cxp-title-accent);
  content: "\e992";
  font-family: 'keenicons-outline';
  font-size: .9em;
  font-weight: 400;
  line-height: 1;
}
body.fi-panel-portal .cxp-rankings-page .kt-card-title.cxp-card-title--icon::before {
  content: none;
}
body.fi-panel-portal .cxp-rankings-page .kt-card-title .cxp-card-title-icon {
  flex: 0 0 auto;
  color: var(--cxp-title-accent);
  font-size: .9em;
  font-weight: 400;
  line-height: 1;
}

/* Secondary/muted body-cell text inside a .sb-data-table outside a
   .sb-section (Lead Tracking's Client/Service/When columns) - same brightened
   rationale as the thead rule above, tuned against the zebra row background
   (measured rgb(34,34,37), 4.23:1 with bare --muted-foreground; need 4.5). */
.cx-dash-muted-cell {
    color: color-mix(in srgb, var(--muted-foreground) 72%, white);
}

/* Needs You "Count" column - plain bold tabular number per the Style Book
   Clients table's Spend/Leads treatment (sb-data-money/sb-data-success in
   _tables.blade.php), NOT a boxed kt-badge pill. Neutral foreground normally;
   the one critical row reads in the same readable-red TEXT already used
   app-wide for danger (--destructive-readable, see .cx-dash-sub-danger a few
   hundred lines down) - no colored box, matching "status uses colored words,
   not boxes" everywhere else in this file. */
.cx-dash-need-count {
    color: var(--foreground);
    font-size: 15px;
    font-variant-numeric: tabular-nums;
    font-weight: 650;
}

.cx-dash-need-count.is-critical {
    color: var(--destructive-readable);
}

.kt-data-grid {
    --kt-pattern-lime: #b7f34a;
    --kt-pattern-green: var(--color-green-500);
    --kt-pattern-blue: #57a6ff;
    min-width: var(--kt-data-grid-min-width, 0px);
}

.kt-data-grid--fit {
    table-layout: auto;
    width: 100%;
}

.kt-data-grid th,
.kt-data-grid td {
    text-align: left;
    padding-left: .42rem;
    padding-right: .42rem;
}

.kt-data-grid thead th + th {
    border-left: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
}

.kt-data-grid tbody td + td,
.kt-data-grid tfoot td + td {
    border-left: 1px solid color-mix(in srgb, var(--border) 30%, transparent);
}

.kt-data-grid th {
    color: var(--muted-foreground);
    font-size: 11px;
    font-weight: var(--font-weight-normal);
    padding-bottom: .72rem;
    padding-top: .72rem;
    white-space: nowrap;
}

.kt-data-grid thead,
.kt-data-grid thead th {
    background: color-mix(in srgb, var(--muted) 82%, var(--card));
}

.kt-data-grid thead th {
    border-bottom: 1px solid color-mix(in srgb, var(--primary) 20%, var(--border));
}

.kt-data-grid thead th.is-active { color: var(--primary); }

.kt-sort-button {
    align-items: center;
    color: inherit;
    cursor: pointer;
    display: inline-flex;
    font: inherit;
    gap: .3rem;
    justify-content: flex-start;
    width: 100%;
}

.kt-sort-button i {
    color: color-mix(in srgb, currentColor 70%, transparent);
    font-size: 12px;
}

/* Generic active-sort indicator, not scoped to .kt-data-grid so any table
   using window.cxPanelSortTable (public/js/cx-table-sort.js) picks it up -
   see cx-table-sort.js for the th.is-active / aria-sort it toggles. */
.kt-sort-button.is-active,
.cx-panel-sort-button.is-active {
    color: var(--primary);
}

.kt-sort-button.is-active i,
.cx-panel-sort-button.is-active i {
    color: var(--primary);
}

/* Portal: the sort button follows its header cell's alignment (a centred
   number column keeps a centred title) and brightens on hover, so the
   header reads as clickable. */
body.fi-panel-portal .kt-sort-button { width: auto; }
body.fi-panel-portal .kt-sort-button:hover { color: var(--foreground); }

/* "Sort by" bar that cx-table-sort.js injects above a table whose header
   row is hidden (stacked-card phone layouts). Side margins are copied from
   the table's wrapper at runtime. */
.cx-mobile-sort {
    align-items: center;
    display: flex;
    gap: 8px;
    margin-bottom: 12px;
}

.cx-mobile-sort-label {
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    white-space: nowrap;
}

/* Stretch to the row so the select matches the button, which the portal
   skin gives a touch-sized minimum height on phones. */
.cx-mobile-sort select {
    align-self: stretch;
    flex: 1 1 auto;
    height: auto;
    min-width: 0;
}

.cx-mobile-sort .kt-btn {
    flex: 0 0 auto;
    white-space: nowrap;
}

.kt-data-grid tbody tr { transition: background-color .12s ease; }

.kt-data-grid tbody tr:hover td {
    background-color: color-mix(in srgb, var(--primary) 7%, var(--muted));
}

.kt-data-grid td {
    font-size: 11px;
    min-width: 0;
    overflow-wrap: anywhere;
    padding-bottom: .42rem;
    padding-top: .42rem;
    vertical-align: middle;
    word-break: break-word;
    white-space: normal;
}

.kt-data-grid .kt-metric-cell {
    min-width: 5.5rem;
}

.kt-data-grid .kt-metric {
    font-size: 17px;
    line-height: 1;
}

.kt-data-grid .kt-delta {
    font-size: 10.5px;
    line-height: 1;
}

.kt-data-grid thead th:first-child,
.kt-data-grid tbody td:first-child {
    left: 0;
    padding-left: .7rem;
    position: sticky;
}

.kt-data-grid thead th:first-child { z-index: 4; }

.kt-data-grid tbody td:first-child {
    background: var(--card);
    z-index: 2;
}

.kt-data-grid tbody tr:nth-child(odd) td:first-child {
    /* 2026-08-04: kept in lockstep with components.css's
       :is(.kt-table,.cx-table) tbody tr:nth-child(odd):is(.dark *) rule -
       identical color-mix formula - so the sticky first column never seams
       against the rest of the row. This file is admin-panel-only (html
       always carries .dark there), so no :is(.dark *) scoping is required. */
    background: color-mix(in srgb, white 5%, color-mix(in srgb, var(--muted) 75%, var(--card)));
}

.kt-data-grid tbody tr:hover td:first-child {
    background: color-mix(in srgb, var(--primary) 7%, var(--muted));
}

.kt-data-grid tfoot td:first-child {
    left: 0;
    padding-left: .7rem;
    position: sticky;
    z-index: 3;
}

.kt-data-grid tfoot td {
    background: linear-gradient(90deg, color-mix(in srgb, var(--primary) 7%, var(--card)), var(--card));
    border-top: 2px solid color-mix(in srgb, var(--primary) 35%, var(--border));
    padding-bottom: .72rem;
    padding-top: .72rem;
}

.kt-total-label {
    color: var(--foreground);
    font-size: var(--text-sm);
    font-weight: 700;
    letter-spacing: .02em;
    padding-right: 1.5rem;
}

.kt-total-spacer {
    min-width: 5rem;
    width: 5rem;
}

.kt-data-grid .kt-metric-cell {
    border-left: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
}

.kt-metric-line {
    align-items: baseline;
    display: flex;
    gap: .36rem;
    white-space: nowrap;
}

.kt-metric {
    color: var(--foreground);
    font-size: 17px;
    font-variant-numeric: tabular-nums;
    font-weight: 650;
    letter-spacing: -.012em;
}

.kt-metric--cost { color: var(--kt-pattern-lime); }
.kt-metric--conversions { color: var(--kt-pattern-green); }
.kt-metric--calls { color: var(--kt-pattern-blue); }
.kt-metric--clicks,
.kt-metric--impressions { color: var(--foreground); }
.kt-metric--zero {
    color: var(--muted-foreground);
    font-weight: var(--font-weight-normal);
    opacity: .65;
}

.kt-entity {
    align-items: baseline;
    display: flex;
    gap: .45rem;
    min-width: 0;
    white-space: nowrap;
}

.kt-entity--stack {
    align-items: flex-start;
    flex-direction: column;
    gap: .2rem;
    white-space: normal;
}

.kt-entity--stack .kt-entity-name,
.kt-entity--stack .kt-entity-sub {
    display: block;
    max-width: 100%;
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
}

.kt-entity-name {
    color: var(--foreground);
    font-size: 13px;
    font-weight: var(--font-weight-medium);
    max-width: 10.5rem;
    min-width: 0;
    overflow: hidden;
    overflow-wrap: anywhere;
    text-overflow: ellipsis;
    word-break: break-word;
    white-space: normal;
}

.kt-entity-sub {
    color: var(--muted-foreground);
    font-size: 10px;
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: break-word;
    white-space: normal;
}

.kt-clamp-3 {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    max-width: 100%;
    min-width: 0;
    overflow: hidden;
    overflow-wrap: anywhere;
    text-overflow: clip;
    white-space: normal;
    word-break: break-word;
}

.kt-spark-cell {
    min-width: 5rem;
    width: 5rem;
}

.kt-row-spark {
    height: 34px;
    width: 4.35rem;
}

.kt-mobile-cards { display: none; }

/* min-width:0 fixes a systemic gap, not a page-local one (Adir sweep,
   2026-08-18): .kt-mobile-card is always used as a direct child of
   .kt-mobile-cards { display:grid }, and CSS grid items default to
   min-width:auto - refusing to shrink below their own min-content width,
   which grows the whole grid TRACK (and every card in it) to match
   whichever card has the widest un-shrinkable content. Calls and Leads
   happened not to hit this (their content always fit); the Clients page's
   phone-badge row did not, and stretched every card ~40px past the
   viewport. Fixed once here so every current AND future .kt-mobile-card
   is safe by default, instead of a per-page workaround. Same bug family
   as the Command Center Trends chart and the Calls group-header fixes
   earlier this same sweep. */
.kt-mobile-card {
    gap: .7rem;
    padding: .8rem;
    min-width: 0;
}

/* Compact label:value row inside a .kt-mobile-card - Leads/Clients/Twilio/
   Phonecom's mobile-card conversions all use this (Adir sweep, 2026-08-18).
   Promoted here from a page-local copy that only ever worked on the one
   page that defined it. overflow-wrap:anywhere is not decorative - a
   Phone.com number's Label field is sometimes a bare domain with no
   spaces (aroundtheclockgaragedoor.com), which cannot wrap without it and
   was measured pushing 39 of 193 cards past their box before this. */
.ltx-lead-mobile-actions { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .55rem; }
.ltx-lead-mobile-actions .kt-btn { flex: 0 0 auto; }
.ltx-lead-mobile-field { margin-top: .35rem; font-size: var(--text-sm); min-width: 0; overflow-wrap: anywhere; }
.ltx-lead-mobile-field .lbl { color: var(--muted-foreground); font-size: var(--text-xs); margin-right: .3rem; }

.kt-mobile-head {
    align-items: center;
    display: flex;
    gap: .65rem;
    justify-content: space-between;
}

.kt-mobile-metrics {
    display: grid;
    gap: .35rem;
    grid-template-columns: repeat(5, minmax(0, 1fr));
}

.kt-mobile-spark {
    height: 34px;
    width: 100%;
}

.kt-mobile-metric {
    background: color-mix(in srgb, var(--muted) 65%, transparent);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    display: grid;
    gap: .16rem;
    min-width: 0;
    padding: .42rem;
}

.kt-mobile-metric span {
    color: var(--muted-foreground);
    font-size: 9px;
    text-transform: uppercase;
}

.kt-mobile-metric strong {
    font-size: 13px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.kt-card-subtitle {
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    margin-top: .2rem;
}

.kt-toolbar-meta {
    color: var(--muted-foreground);
    font-size: var(--text-xs);
    white-space: nowrap;
}

.kt-toolbar-note {
    border-left: 1px solid var(--border);
    color: var(--muted-foreground);
    /* 2026-08-05: 12px admin-wide label floor - was 10px (e.g. dashboard's
       "updated HH:MM" note). */
    font-size: 12px;
    padding-left: .75rem;
    white-space: nowrap;
}

@media (max-width: 1500px) {
    .kt-filter-bar {
        grid-template-columns:
            minmax(23rem, 1.4fr)
            minmax(14rem, .8fr)
            minmax(13rem, .7fr)
            auto;
    }

    .kt-filter-note {
        grid-column: 1 / 4;
        max-width: none;
        padding-top: 0;
    }
}

@media (max-width: 1180px) {
    .kt-stat-strip { flex-wrap: wrap; }
}

@media (max-width: 860px) {
    .kt-filter-bar {
        align-items: stretch;
        grid-template-columns: 1fr 1fr;
    }

    .kt-table-wrap { display: none; }

    /* data pages without a kt-mobile-cards alternative keep the scrollable table on mobile */
    .kt-table-wrap.kt-table-wrap--keep { display: block; }

    .kt-mobile-cards {
        display: grid;
        gap: .55rem;
        padding: .65rem;
    }

    .kt-mobile-cards .kt-mobile-card:nth-child(odd) {
        background: color-mix(in srgb, var(--muted) 38%, var(--card));
    }

    .kt-mobile-head {
        align-items: flex-start;
        flex-direction: column;
    }
}

@media (max-width: 620px) {
    .kt-filter-bar { grid-template-columns: 1fr; }

    .kt-filter-note { grid-column: auto; }

    .kt-segment { grid-template-columns: repeat(3, minmax(0, 1fr)); }

    .kt-mobile-metrics { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Legacy surfaces use the shared card token, including older inline page rules. */
.fi-main .lm-brain-card,
.fi-main .acc-card {
    background: var(--card);
}

/* The legacy Autopilot partial already marks its surface override important. */
.fi-main .cx-bento-card {
    background: var(--card) !important;
}

/* Shared media frame. Set --kt-thumb-ratio when the source has a known shape. */
.kt-thumb {
    --kt-thumb-ratio: 1 / 1;
    display: block;
    width: 100%;
    aspect-ratio: var(--kt-thumb-ratio);
    overflow: hidden;
}

.kt-thumb > img,
.kt-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

/* Existing shared surfaces use the same crop rules without page-local CSS. */
.fi-main .cx-art-thumb {
    object-fit: cover !important;
    object-position: center !important;
}

.fi-main .cx-seo-prev > img {
    display: block;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
}

/* Cyticx main Dashboard (filament.pages.dashboard) - 2026-08-04 design pass.
 * Scoped to cx-dash-* classes only; .cx-bento / .cx-bento-sm bases stay
 * untouched since gbp-dashboard + seo-autopilot-overview also consume them. */

/* Modules (7 cards) - 4+3 instead of a dangling single card on row 3.
 * Tools (9 cards) - a clean 3x3 instead of auto-fit's uneven wrap. */
@media (min-width: 1200px) {
    .cx-bento.cx-dash-modules {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
    .cx-bento-sm.cx-dash-tools {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

/* Module card headers: force a uniform two-line stack (icon+label row, then
 * badge row) on all 7 cards - otherwise long titles wrap the badge to a
 * second line while short titles keep it inline, giving inconsistent header
 * shapes across the grid. Applies at every width (desktop 4-col + mobile),
 * not just the >=1200px breakpoint above. Scoped to the modules grid only:
 * > .kt-card > .kt-card-header can only match a module card's own header,
 * since the outer section .kt-card-header lives on the outer article, one
 * level BEFORE .cx-dash-modules in the DOM (not inside it at all). */
.cx-bento.cx-dash-modules > .kt-card > .kt-card-header {
    flex-direction: column;
    align-items: flex-start;
    gap: .4rem;
}

/* Needs You mobile cards: dot+label left, badge+link together on the right,
 * on ONE row - overrides the generic .kt-mobile-head column-stack rule
 * (patterns.css ~line 597) which this block does not want. */
.cx-dash-need-card .kt-card-content {
    padding: .6rem .75rem;
}

.cx-dash-need-card .kt-mobile-head {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
}

.cx-dash-need-actions {
    align-items: center;
    display: flex;
    flex: 0 0 auto;
    gap: .5rem;
}

/* Mobile (~640px and under): Lead Tracking toolbar cramps chips + the period
 * segment control into one unwrapping row at ~390px wide. Let it wrap and
 * make the period filter stretch full-width on its own line. */
@media (max-width: 640px) {
    .cx-dash-lt-toolbar {
        flex-wrap: wrap;
        row-gap: .5rem;
    }

    .cx-dash-lt-toolbar .kt-filter-group {
        width: 100%;
    }

    .cx-dash-lt-toolbar .kt-segment {
        width: 100%;
    }

    /* Critical alert banner: let the "Review now ->" toolbar drop to its own
     * full-width line below the message instead of squeezing beside it. */
    .cx-dash-alert {
        flex-wrap: wrap;
    }

    .cx-dash-alert .kt-alert-toolbar {
        flex: 1 0 100%;
        justify-content: flex-end;
        padding-top: 0;
    }

    /* Global, not scoped to this dashboard: stat chips must never clip data
     * anywhere in the app. Long labels ("THIS MONTH, IF BILLED PER-TOKEN
     * (HYPOTHETICAL)") hard-clipped at ~390px because the base .kt-stat-chip
     * is white-space:nowrap + width:max-content. Let the chip wrap and grow
     * with its label, but keep the numeric value itself on one line.
     * flex-wrap is required alongside white-space: the base rule lays
     * label + value out as inline-flex row siblings (align-items:baseline,
     * no wrap) - white-space alone lets text reflow but does not let two
     * flex children drop to a second line, so a long label+value pair
     * (e.g. "DataForSEO checked" + "38 minutes ago") still overflows the
     * row without this. */
    .kt-stat-chip {
        white-space: normal;
        width: auto;
        max-width: 100%;
        flex-wrap: wrap;
    }

    .kt-stat-value {
        white-space: nowrap;
    }
}

/* Dashboard "Lead Tracking" widget table (#cxDashLtTable,
 * dashboard.blade.php) at mobile (<=640px, real defect confirmed at 390px):
 * the desktop <col> widths are fixed px (90+220+120+150 = 580px) so
 * table-layout:fixed renders each column at its literal pixel size no
 * matter how narrow the card wrapper actually is - cell content (badges,
 * the Rec play button, nowrap text) then paints past its own column edge
 * into the next column instead of just being individually narrow (pre-dates
 * the CYT-773 Rec column, which only made it more visible - reported live
 * 2026-09-06). Re-pin every column to a percentage of the CURRENT
 * container width (sums to 100%, so the table always fits its wrapper) and
 * force ellipsis truncation on overflowing cell content - the identical
 * mobile pattern the dedicated Lead Tracking page's own #unifiedTable
 * already ships (CYT-640 finding #4, leadtracking/dashboard.blade.php). The
 * `!important`s are required to beat the higher-specificity inline
 * `style="width: ...px"` on each <col> and inline `text-align` on `td`. */
@media (max-width: 640px) {
    #cxDashLtTable {
        table-layout: fixed !important;
        width: 100% !important;
    }

    #cxDashLtTable th,
    #cxDashLtTable td {
        overflow: hidden !important;
        text-overflow: ellipsis !important;
        white-space: nowrap !important;
        /* .sb-data-table thead th / tbody td both carry padding: .75rem
           1.25rem (20px each side) - 6 columns x 40px = 240px of the
           table's ~274px mobile width was padding alone, leaving ~34px
           total for actual content and forcing every column down to a
           1-3 char ellipsis. Padding-only change, house declaration
           order/values otherwise untouched. */
        padding-inline: .4rem !important;
    }

    #cxDashLtTable colgroup col:nth-child(1) { width: 12% !important; } /* Type */
    #cxDashLtTable colgroup col:nth-child(2) { width: 25% !important; } /* Name / Caller */
    #cxDashLtTable colgroup col:nth-child(3) { width: 15% !important; } /* Client */
    #cxDashLtTable colgroup col:nth-child(4) { width: 14% !important; } /* Service */
    #cxDashLtTable colgroup col:nth-child(5) { width: 14% !important; } /* Rec */
    #cxDashLtTable colgroup col:nth-child(6) { width: 20% !important; } /* When */
}

/* ============================================================
 * Cyticx main Dashboard - EXECUTIVE HERO REDESIGN (2026-08-05).
 * Drawbridge-style summary: one system-score ring + four large KPI
 * tiles + a live status LED row, replacing the old "System Pulse"
 * chip strip. Everything here is scoped under .cx-dash-* so no
 * shared selector (.kt-badge, .kt-status-dot, .cx-stat, .cx-bento)
 * is broadened - only ever matched in compound with a cx-dash-*
 * ancestor/class, exactly like the existing rules above this block.
 * ============================================================ */

.cx-dash-hero-body {
    align-items: stretch;
    display: flex;
    flex-wrap: wrap;
    gap: 1.85rem;
}

/* The shared .kt-toolbar-note carries a border-left + left-padding meant to
   separate it from a PRECEDING sibling in the same toolbar. The hero card's
   toolbar only ever holds this one note (no sibling before it, at any
   viewport width), so that divider renders as an orphaned floating line -
   independent GPT-5.6-sol design review, 2026-08-05: "looks orphaned... with
   a stray divider/indent". Scoped to this card only; the shared class stays
   untouched everywhere else it is legitimately paired with something. */
.cx-dash-hero .kt-toolbar-note {
    border-left: none;
    color: var(--color-zinc-400);
    padding-left: 0;
}

/* Same legibility bump as the ring label/LED words above, applied to the
   hero card's OWN subtitle and to the KPI tile labels - independent
   GPT-5.6-sol design review, 2026-08-05, follow-up pass: "score subtitle,
   timestamp, and KPI labels" still read dim. Scoped to .cx-dash-hero /
   .cx-dash-kpi only - the shared .kt-card-subtitle / .cx-stat-label rules
   are untouched everywhere else in the app. */
.cx-dash-hero .kt-card-subtitle {
    color: var(--color-zinc-400);
}

/* CYT-644 finding #9 (confirmed live 2026-08-18): the 2026-08-06 overflow
   fix above (min-width:0 + overflow-wrap:anywhere) stopped the horizontal
   overflow it targeted, but overflow-wrap:anywhere breaks a word as soon as
   doing so improves line-fill, not only when the word genuinely doesn't fit
   - on Ad Spy's ~113px-wide tiles that turned "COMPETITORS SEEN" into
   COMPE/TITORS/SEEN across 3 lines instead of a clean COMPETITORS/SEEN over
   2. Downgraded to overflow-wrap:break-word (only breaks a word that
   actually overflows its line) and dropped the redundant legacy
   word-break:break-word alias that doubled down on the same behaviour.
   min-width:0 is untouched - it's still what lets the label wrap instead of
   overflowing at all. */
.cx-dash-kpi .cx-stat-label {
    color: var(--color-zinc-400);
    min-width: 0;
    overflow-wrap: break-word;
    white-space: normal;
}

.cx-dash-kpi .cx-stat-sub {
    color: var(--color-zinc-400);
}

.cx-dash-hero-ring-wrap {
    align-items: center;
    display: flex;
    flex: 0 0 auto;
    flex-direction: column;
    gap: .6rem;
    justify-content: center;
    min-width: 190px;
    text-align: center;
}

.cx-dash-hero-ring-head {
    align-items: center;
    display: flex;
    gap: .35rem;
    justify-content: center;
    position: relative;
}

.cx-dash-ring-title {
    /* Lighter than the standard muted-foreground caption on purpose - this
       label sits directly beside the hero's headline number and reads too
       faint at the default shade (independent GPT-5.6-sol design review,
       2026-08-05: "secondary grey text is too dim"). color-zinc-400 is an
       EXISTING neutral token (already used by --ring/--mono), not a new hue. */
    color: var(--color-zinc-400);
    /* 2026-08-05: 12px admin-wide label floor (ui-qa visible-text-floor-12px
       theme lift) - was 11.5px. */
    font-size: 12px;
    font-weight: var(--font-weight-medium);
    letter-spacing: .05em;
    text-transform: uppercase;
}

.cx-dash-hero-kpis {
    display: flex;
    flex: 1 1 320px;
    flex-direction: column;
    gap: 1rem;
    justify-content: center;
    min-width: 260px;
}

/* ---- Score / mini-health ring (shared geometry, size differs) ---- */

.cx-dash-ring {
    /* Bumped from the original 120px spec (2026-08-05, independent GPT-5.6-sol
       design review): at 120px the ring read as "one component among five",
       not the "one big score front and center" the brief calls for - the KPI
       grid beside it had more visual weight. Bigger ring + wider ring column
       (see .cx-dash-hero-ring-wrap min-width) restores that hierarchy without
       shrinking the KPI tiles, which need to stay large too. */
    height: 152px;
    position: relative;
    width: 152px;
}

.cx-dash-ring--sm {
    height: 40px;
    width: 40px;
}

.cx-dash-ring svg {
    height: 100%;
    transform: rotate(-90deg);
    width: 100%;
}

.cx-dash-ring circle {
    fill: none;
    stroke-width: 9;
}

.cx-dash-ring--sm circle {
    stroke-width: 6;
}

.cx-dash-ring-track {
    stroke: color-mix(in srgb, var(--muted-foreground) 24%, transparent);
}

.cx-dash-ring-progress {
    stroke-linecap: round;
    transition: stroke-dasharray 1.1s cubic-bezier(.22, .61, .36, 1);
}

.cx-dash-ring-value {
    align-items: center;
    color: var(--foreground);
    display: flex;
    font-size: 3rem;
    font-weight: 750;
    inset: 0;
    justify-content: center;
    letter-spacing: -.02em;
    line-height: 1;
    position: absolute;
}

.cx-dash-ring--sm .cx-dash-ring-value {
    font-size: .95rem;
    font-weight: 700;
}

.cx-dash-ring-label {
    /* 2026-08-05: 12px admin-wide label floor (ui-qa visible-text-floor-12px
       theme lift, reconciled with the ui-qa marketing-site rules) - was 11px
       (itself a bump from an earlier 10px, see prior note history). */
    color: var(--color-zinc-400);
    font-size: 12px;
    letter-spacing: .05em;
    text-transform: uppercase;
}

/* Tone bands - ONE tone drives both the ring stroke and the number
   (house rule: never split a score's ring color from its text color).
   >=70 primary/normal, 40-69 orange, <40 red - the same readable red
   token (--destructive-readable) used by kt-stat-chip--danger.
   2026-08-05: the "good" band's raw var(--primary) (blue-600, rgb(21,93,
   252)) measured 3.54:1 on this ring's own kt-card surface (rgb(19,19,22),
   a small "cx-dash-ring--sm" ring inside a kt-entity row) - under the 4.5:1
   floor (canvas raster + WCAG luminance, measured not eyeballed). Lifted to
   the same #7ab5ff readable-blue already used for bare .kt-stat-chip
   (identical root cause, same fix), stroke and value together so ring and
   number stay one tone per the rule above. */
.cx-dash-ring.cx-dash-score-good .cx-dash-ring-progress { stroke: #7ab5ff; }
.cx-dash-ring.cx-dash-score-good .cx-dash-ring-value { color: #7ab5ff; }
.cx-dash-ring.cx-dash-score-warn .cx-dash-ring-progress { stroke: #ffae42; }
.cx-dash-ring.cx-dash-score-warn .cx-dash-ring-value { color: #ffae42; }
.cx-dash-ring.cx-dash-score-bad .cx-dash-ring-progress { stroke: var(--destructive-readable); }
.cx-dash-ring.cx-dash-score-bad .cx-dash-ring-value { color: var(--destructive-readable); }

/* ---- Score explainer popover ("how is this worked out?") ---- */

.cx-dash-explainer {
    display: inline-flex;
    position: relative;
}

.cx-dash-explainer-trigger {
    align-items: center;
    background: color-mix(in srgb, var(--primary) 12%, var(--card));
    border: 1px solid color-mix(in srgb, var(--primary) 32%, var(--border));
    border-radius: 50%;
    color: var(--primary);
    cursor: pointer;
    display: inline-flex;
    /* 2026-08-05: ui-qa tap-targets-44px calibration - was 1.2rem (19.2px),
       below the new 24px desktop floor (WCAG 2.2 AA) on every viewport. Bumped
       to the desktop floor here. position:relative is for the mobile-only
       ::before expanded hit-area below. */
    height: 1.5rem;
    justify-content: center;
    padding: 0;
    position: relative;
    width: 1.5rem;
}

.cx-dash-explainer-trigger:hover,
.cx-dash-explainer-trigger:focus-visible {
    background: color-mix(in srgb, var(--primary) 20%, var(--card));
}

/* 2026-08-05: independent design review of the first pass (plain 44x44 box)
   called it out live: "the disc is ~44px against a ~16px label... reads as a
   primary button, not a hint". Tap-targets-44px measures the ELEMENT's own
   box, so the box has to genuinely be 44px - but nothing says the VISIBLE
   disc has to grow with it. Same trick used for real hit-area expansion
   everywhere: the button itself goes to 44x44 and transparent, an absolutely
   positioned ::before repaints only the small round disc centered inside it
   (inset 10px = (44-24)/2, i.e. the same 24px look as desktop), and the svg
   gets z-index so it stays above the ::before instead of under it (a
   position:absolute pseudo-element paints above static in-flow content by
   default, which would otherwise hide the icon). */
@media (max-width: 1023px) {
    .cx-dash-explainer-trigger {
        background: none;
        border-color: transparent;
        height: 44px;
        width: 44px;
    }

    /* The un-scoped `:hover, :focus-visible` rule above (0,2,0 specificity)
       would otherwise still win over the plain mobile rule above (0,1,0) and
       repaint the full 44px box on focus/hover, undoing the small-disc
       illusion the moment a keyboard user tabs to it. Same selector list,
       same specificity, later in the cascade - this wins instead. */
    .cx-dash-explainer-trigger:hover,
    .cx-dash-explainer-trigger:focus-visible {
        background: none;
    }

    .cx-dash-explainer-trigger::before {
        background: color-mix(in srgb, var(--primary) 12%, var(--card));
        border: 1px solid color-mix(in srgb, var(--primary) 32%, var(--border));
        border-radius: 50%;
        content: '';
        inset: 10px;
        position: absolute;
    }

    .cx-dash-explainer-trigger:hover::before,
    .cx-dash-explainer-trigger:focus-visible::before {
        background: color-mix(in srgb, var(--primary) 20%, var(--card));
    }
}

.cx-dash-explainer-trigger svg {
    height: .72rem;
    position: relative;
    width: .72rem;
    z-index: 1;
}

.cx-dash-explainer-panel {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 16px 34px -16px rgba(0, 0, 0, .55);
    left: 50%;
    padding: .8rem .9rem;
    position: absolute;
    text-align: left;
    top: calc(100% + .55rem);
    transform: translateX(-50%);
    width: 250px;
    z-index: 40;
}

.cx-dash-explainer-panel[x-cloak] {
    display: none !important;
}

.cx-dash-explainer-panel p {
    color: var(--muted-foreground);
    /* 2026-08-05: 12px admin-wide label floor - was 11px. */
    font-size: 12px;
    line-height: 1.45;
    margin: .35rem 0 0;
}

.cx-dash-explainer-panel p:first-child {
    margin-top: 0;
}

.cx-dash-explainer-panel ul {
    display: grid;
    gap: .22rem;
    list-style: none;
    margin: .5rem 0 0;
    padding: 0;
}

.cx-dash-explainer-panel li {
    color: var(--foreground);
    display: flex;
    /* 2026-08-05: 12px admin-wide label floor - was 11px. */
    font-size: 12px;
    gap: .5rem;
    justify-content: space-between;
}

.cx-dash-explainer-panel li span:last-child {
    color: var(--destructive-readable);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ---- KPI tiles (built on the existing cx-stat-row/cx-stat large-number
   pattern - api-spend.blade.php's headline tiles - with an added icon
   chip and a bump to a bigger, hero-scale number). ---- */

.cx-dash-kpis {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* 2026-08-06 wide-viewport density fix - owner-escalated: at his real (wide,
 * ~1920+) screen the fixed 2-column grid above gave each of the 4 tiles half
 * of whatever width the hero band had. On .cx-dash-hero that's the flex row
 * remainder after the score ring (still wide); on the four .cx-sect-hero
 * pages (fb-poster, lem, listing-management, seo-autopilot-overview, ad-spy)
 * there is no ring at all, so .cx-dash-kpis gets the FULL hero-card width -
 * two ~800px-wide tiles holding a small icon+label+number stack pinned to
 * the left edge, reading as "big half-empty boxes" (verbatim owner report).
 * Card padding (.cx-stat, 14px/16px) and the grid gap (inherited from the
 * base .cx-stat-row, 0.85rem ~= 13.6px) already match the Style Book's own
 * dense-card tokens - measured live off /cyticx-app/lead-tracking's
 * .kt-stat-chip strip (7.2px/9.6px chip padding, ~4px inter-chip gap) as the
 * reference for "dense" - so neither needed changing. The defect was purely
 * the fixed column COUNT never adapting to available width. Four tiles
 * across in one row at wide viewports halves (dashboard) to quarters
 * (section-hero pages) the per-tile width without touching padding/gap/font,
 * so the tiles stay exactly as tall as their content - only narrower.
 * >=1024px stays 2x2 (already content-hugging at 1440 per the owner's own
 * side-by-side screenshots); <1024px (mobile/tablet stack) is untouched. */
@media (min-width: 1600px) {
    .cx-dash-kpis {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/* 2026-08-06, same wide-viewport pass, mobile-side counterpart: CSS Grid
 * equalizes row height to the tallest cell in the row (2x2 grid, no
 * explicit align-items so it defaults to stretch). At <=390px a longer
 * label ("FIXES APPLIED TODAY", "AI SPEND TODAY") wraps to 2 lines while
 * its row-mate's label ("LEADS TODAY") does not, so that ROW grows to fit
 * the 2-line label - and the shorter-content tile inherits that taller box
 * with nothing to fill it, since .kt-card's own column-flex has no
 * justify-content (defaults to flex-start: icon+label pinned to the top,
 * the rest of the box left empty below). Caught by ui-qa's new
 * stat-tile-density rule at mobile: "AI spend today" 134x160, 52% fill.
 * space-between keeps the icon+label flush at the top and the sub-line
 * flush at the bottom - exactly like the tile that already fits its box -
 * so the tile content-hugs the FULL height it was actually given instead of
 * pooling all the slack under the label row. No effect on tiles/widths
 * where every row-mate is already the same natural height (nothing to
 * distribute), which is every other breakpoint. */
.cx-dash-kpi {
    justify-content: space-between;
}

/* 2026-08-06 - narrow-viewport label overflow fix: this row's main axis is
 * horizontal, so the label span below inherits a flexbox automatic
 * min-width:auto floor from THIS container, propagating its full
 * unwrapped-text width up through .cx-dash-kpi into the grid item and
 * causing horizontal overflow at 390px for longer labels ("Active
 * enrollments", "Total ad impressions", "Own share of voice"). min-width:0
 * here (plus on .cx-stat-label below) breaks that propagation so the label
 * can shrink and wrap instead. Desktop is unaffected - these labels already
 * fit on one line at desktop widths; this only permits wrapping when there
 * isn't room. */
.cx-dash-kpi-top {
    align-items: center;
    display: flex;
    gap: .5rem;
    margin-bottom: .35rem;
    min-width: 0;
}

.cx-dash-kpi-icon {
    align-items: center;
    background: color-mix(in srgb, var(--primary) 16%, transparent);
    border-radius: 8px;
    color: var(--primary);
    display: inline-flex;
    flex: 0 0 auto;
    font-size: .85rem;
    height: 1.6rem;
    justify-content: center;
    width: 1.6rem;
}

.cx-dash-kpis .cx-stat-value {
    font-size: var(--text-3xl, 1.9rem);
}

.cx-dash-sub-danger {
    color: var(--destructive-readable) !important;
}

/* ---- Live status LED row ---- */

.cx-dash-led-row {
    align-items: center;
    display: flex;
    flex-wrap: wrap;
    gap: 1.2rem;
}

.cx-dash-led {
    align-items: center;
    display: inline-flex;
    gap: .42rem;
}

.cx-dash-led-word {
    color: var(--color-zinc-400);
    /* 2026-08-05: 12px admin-wide label floor - was 11px. */
    font-size: 12px;
}

.cx-dash-led-word strong {
    color: var(--foreground);
    font-weight: var(--font-weight-medium);
}

.cx-dash-led-dot {
    background: var(--muted-foreground);
    border-radius: 50%;
    flex: 0 0 auto;
    height: .52rem;
    width: .52rem;
}

.cx-dash-led-dot--success {
    background: var(--color-green-500);
    box-shadow: 0 0 6px color-mix(in srgb, var(--color-green-500) 70%, transparent);
    animation: cx-dash-pulse 2s ease-in-out infinite;
}

.cx-dash-led-dot--danger {
    background: var(--destructive-readable);
    box-shadow: 0 0 6px color-mix(in srgb, var(--destructive-readable) 70%, transparent);
    animation: cx-dash-pulse 2s ease-in-out infinite;
}

.cx-dash-led-dot--warning {
    background: #ffae42;
    box-shadow: 0 0 6px rgba(255, 174, 66, .6);
    animation: cx-dash-pulse 2s ease-in-out infinite;
}

.cx-dash-led-dot--neutral {
    background: var(--muted-foreground);
}

@keyframes cx-dash-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: .45; transform: scale(.78); }
}

/* Module badges: small pulsing dot INSIDE the badge, word stays - scoped
   to the modules grid so .kt-badge itself is never touched globally. */
.cx-dash-modules .kt-badge {
    align-items: center;
    display: inline-flex;
    gap: .32rem;
}

.cx-dash-modules .cx-dash-led-dot {
    height: .42rem;
    width: .42rem;
}

@media (prefers-reduced-motion: reduce) {
    .cx-dash-led-dot {
        animation: none !important;
    }
}

@media (max-width: 900px) {
    .cx-dash-hero-body {
        align-items: center;
        flex-direction: column;
    }

    .cx-dash-hero-kpis {
        width: 100%;
    }
}

@media (max-width: 480px) {
    /* Tiles stay 2x2 at phone width per spec - just tighten the gap and
       let the number size step down a notch so a 2-digit dollar value
       ("$12.34") never has to shrink-to-fit inside a ~155px column. */
    .cx-dash-kpis {
        gap: .55rem;
    }

    .cx-dash-kpis .cx-stat-value {
        font-size: var(--text-2xl, 1.5rem);
    }

    /* At ~155px per tile, the full-size icon chip left 3-word labels like
       "FIXES APPLIED TODAY" wrapping to 3 cramped lines (independent
       GPT-5.6-sol design review, 2026-08-05). Smaller chip claws back width
       for the label without touching the shared .cx-dash-kpi-icon size used
       at every wider breakpoint. */
    .cx-dash-kpi-top {
        gap: .4rem;
    }

    .cx-dash-kpi-icon {
        font-size: .72rem;
        height: 1.35rem;
        width: 1.35rem;
    }

    .cx-dash-led-row {
        gap: .75rem;
    }
}

/* ============================================================
 * Section hero (2026-08-05) - a LIGHTER rollout of the dashboard's executive
 * hero onto secondary overview pages (fb-poster/overview, lem/overview):
 * same KPI-tile + LED-row pattern, no score ring, no extra card header - the
 * page's own Filament heading already names the page. Reuses
 * .cx-dash-kpis/.cx-dash-kpi/.cx-dash-led-row/.cx-dash-led-dot verbatim
 * (design-system level now, per the dashboard build) - only the body
 * stacking wrapper and two conditional number-tone overrides are new. */
.cx-sect-hero-body {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

/* Conditional number-only tone for a KPI tile ("number stays large, tone
 * via the tile's number color") - reuses the existing cx-stat-value.
 * cx-warning/.cx-danger marker classes (cyticx-theme.css) but overrides
 * their color to the same AA-readable tokens the dashboard's score ring/LED
 * rows use. Scoped to .cx-dash-kpi so the shared cx-stat-value.cx-warning/
 * .cx-danger rule is untouched everywhere else it is used. */
.cx-dash-kpi .cx-stat-value.cx-warning {
    color: #ffae42;
}

.cx-dash-kpi .cx-stat-value.cx-danger {
    color: var(--destructive-readable);
}

/* fb-poster/overview.blade.php loads filament.partials.autoseo-style (a
 * pre-existing partial for Autopilot SEO pages, unrelated to this task)
 * which forces `.cx-stat { background: rgba(255,255,255,.045) !important;
 * }`. That rule predates the section-hero rollout and had nothing to style
 * on this page before (fb-poster never used .cx-stat previously) - it now
 * silently applies to the new KPI tiles too, since they carry .cx-stat,
 * giving them a translucent-white wash instead of an opaque card token
 * (caught by ui-qa's card-surfaces-use-tokens rule). Overridden back to the
 * theme token, scoped to elements carrying BOTH classes so nothing the
 * partial legitimately styles elsewhere on that page is touched. */
.cx-dash-kpi.cx-stat {
    background: var(--card) !important;
}

/* ============================================================
 * Section-hero triple-frame fix (2026-08-05). On the three pages whose body
 * is wrapped in <section class="sb-section"> (website-health =
 * seo-autopilot-overview.blade.php, ad-spy = ad-spy.blade.php, email-
 * messaging = lem/overview.blade.php), pages.css's
 * `.sb-section .kt-card { background: var(--card); border-color:
 * rgba(255,255,255,.06); border-radius:16px; box-shadow:none; }` rule matches
 * BOTH the hero's own outer <article class="kt-card kt-card-border
 * cx-sect-hero"> AND, identically, every KPI tile inside it (also .kt-card)
 * - two indistinguishable stacked card frames sitting inside the section's
 * own frosted-glass frame (ui-qa `no-nested-card-frames`: 3 deep). The other
 * three hero pages (dashboard.blade.php's .cx-dash-hero, fb-poster/overview.
 * blade.php, listing-management/dashboard.blade.php) are never wrapped in
 * .sb-section, so this selector cannot reach them - confirmed by grep, no
 * `sb-section` in either file - and they already pass at 2 deep, untouched.
 *
 * Fix: the hero's OWN card frame goes frameless when nested inside
 * .sb-section, so it reads as sitting directly on the section instead of as
 * a second identical box - restoring the 2-deep nesting the other three hero
 * pages already have. Scoped to .cx-sect-hero specifically (not just
 * `.sb-section .kt-card`) so the KPI tiles inside it - plain descendant
 * .kt-card elements with no .cx-sect-hero class - are completely unaffected
 * and keep their own frame. */
.sb-section .cx-sect-hero.kt-card {
    background: transparent;
    border: none;
    box-shadow: none;
}


/* --- Paired KPI chip: one period, BOTH metrics -----------------------------
   Lead Tracking's strip was eight chips - Leads Today/Yesterday/7d/30d, then
   the same four for Calls - so comparing a period's leads against its calls
   meant reading across the whole row. Owner asked for one box per period
   carrying both numbers (2026-08-14).

   Purely ADDITIVE: a `--dual` modifier that turns the chip from a single
   baseline row into a labelled two-row block. Every inner primitive is the
   existing chip family's (.kt-stat-value, .kt-delta), so the 12px label floor
   and the measured contrast values documented above carry over untouched, and
   no existing .kt-stat-chip anywhere else in the app is affected.
   --------------------------------------------------------------------- */
.kt-stat-chip--dual {
    align-items: stretch;
    flex-direction: column;
    gap: .34rem;
    padding: .5rem .65rem .55rem;
    /* Equal widths across the strip. Natural widths were ragged (142/107/118/
       125px) because only the Today chip carries deltas, which left the value
       columns unaligned box-to-box. 12rem also restores roughly the horizontal
       footprint the eight-pill version occupied - at the natural 148px the
       four merged boxes covered half the panel and read as left-heavy against
       the full-width table below. Content fills ~74% here, comfortably above
       the sparse-tile floor. */
    min-width: 12rem;
}

/* Phones: two per row rather than one narrow column down the left, so all four
   periods stay comparable at a glance and the strip does not push the table
   off-screen. */
@media (max-width: 640px) {
    .kt-stat-chip--dual {
        flex: 1 1 calc(50% - .24rem);
        min-width: 0;
    }
}

.kt-stat-period {
    color: var(--muted-foreground);
    font-size: 12px;
    font-weight: var(--font-weight-medium);
    letter-spacing: .045em;
    line-height: 1;
    text-transform: uppercase;
}

.kt-stat-chip__rows {
    display: grid;
    gap: .24rem;
}

.kt-stat-row {
    align-items: baseline;
    color: inherit;
    display: grid;
    gap: .5rem;
    grid-template-columns: 1fr auto auto;
    text-decoration: none;
}

.kt-stat-row:hover .kt-stat-value,
.kt-stat-row:focus-visible .kt-stat-value {
    text-decoration: underline;
}

.kt-stat-row-label {
    align-items: center;
    color: var(--muted-foreground);
    display: inline-flex;
    font-size: 12px;
    gap: .3rem;
    line-height: 1;
}

.kt-stat-row-label i { font-size: 13px; }

/* Distinct accents so the two metrics are separable at a glance while the
   chip frame itself stays one neutral box. Both are existing pattern tokens
   already used elsewhere - no new colour is introduced here. */
.kt-stat-row--leads { --kt-stat-color: var(--kt-pattern-blue); }
.kt-stat-row--calls { --kt-stat-color: var(--kt-pattern-cyan); }


/* Universal horizontal-scroll affordance - see cyticx-workbench.js for the
   JS that toggles .cx-h-scroll and injects .cx-scroll-hint. Sticky so it
   stays pinned to the visible left edge while the user scrolls the table
   sideways, instead of scrolling away with the content. Desktop never
   overflows these containers by design, but the min-width guard keeps this
   inert there regardless. */
.cx-h-scroll { position: relative; }
.cx-scroll-hint {
    position: sticky;
    left: 0;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-top: .5rem;
    padding: .25rem .6rem;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--muted-foreground);
    background: color-mix(in srgb, var(--primary) 14%, var(--muted));
    border: 1px solid color-mix(in srgb, var(--primary) 25%, var(--border));
    border-radius: 999px;
    pointer-events: none;
    z-index: 5;
}

@media (min-width: 861px) {
    .cx-scroll-hint { display: none !important; }
}

/* ==========================================================================
   CYT-657 - Google Business Profile page: KPI sizing, score-row redesign,
   detail split into sub-pages.  (patterns.css loads AFTER cxp.css, so equal
   specificity wins here; cxp.css is owned by CYT-654 and is not touched.)

   EVERY rule below is scoped to .cxp-gbp-analysis-page on purpose.
   .cxp-health-factor / .cxp-health-progress are SHARED with
   resources/views/filament/portal/pages/website-health.blade.php - an
   unscoped rule would silently restyle that page too.
   ========================================================================== */

/* --- 1. KPI row -----------------------------------------------------------
   Measured before: "Aug 5, 2026" rendered at 44px, height 101.2px against a
   50.6px line-height, i.e. TWO lines at both 1920 and 1440. A date and a
   status word are not KPI magnitudes; only counts and scores are. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-kpi-text .cxp-stat-card__value--text {
  font-size: 1.375rem;
  line-height: 1.25;
  white-space: nowrap;
  letter-spacing: -.01em;
}

/* The caption is the LAST grid track and it is auto-sized, so a caption that
   wraps to two lines on one tile and one line on its neighbour gives the two
   cards different content heights. The card stretches to the tallest sibling
   and centres its content, so the shorter tile drifts ~7px down and its
   caption ~20px down - measured at 1440 on "total left on your profile".
   Reserving two lines makes every tile's content block the same height, so
   values and captions share a baseline at every width. Costs no extra card
   height: the cards are already stretched to a common height by the grid. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-kpi .cxp-stat-card__meta {
  min-height: 40px;
}

/* Supporting counts step down so they no longer shout over the headline
   score. Measured before: counts 44px vs the overall score at 15.5px - the
   least important number on the page was ~3x the most important one. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-kpi .cxp-stat-card__value:not(:where(.cxp-stat-card__value--text)) {
  font-size: 2rem;
  line-height: 1.15;
}

/* ...and the headline score steps up to actually lead the row.
   The card is a grid whose second track is a hard 96px
   (grid-template-rows: 24px 96px 20px 40px), so growing the ring alone spilled
   it 36px down over the verdict and meta lines - and min-height on the zone
   was inert, because the TRACK is what constrains it. Grow the track with the
   ring, never one without the other. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card {
  grid-template-rows: 24px 126px 20px auto;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-score-hero-card .cxp-stat-card__ring {
  width: 120px;
  height: 120px;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-score-hero-card .cxp-score-number {
  font-size: 2.25rem;
  line-height: 1.05;
  letter-spacing: -.02em;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-score-hero-card .cxp-score-unit {
  font-size: .8rem;
}

/* Below 620px cxp.css drops this ring to 88px and pins the number to 14px with
   a 6-class selector. Matching that specificity (and loading later) is what
   lets the headline stay the headline on a phone; the supporting counts step
   down in the same breath so the order is preserved rather than reversed. */
@media (max-width: 620px) {
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card .cxp-stat-card__ring {
    width: 96px;
    height: 96px;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card .cxp-stat-card__ring .cxp-score-number {
    font-size: 1.75rem;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-kpi .cxp-stat-card__value {
    font-size: 1.6rem;
  }
}

/* --- 2. "What makes up your score" rows -----------------------------------
   Measured before: 92.5px collapsed x 9 rows = 832px, the +/- marker pinned
   to the row top (~45px above centre), the progress bar hidden inside the
   expander, and a ~1300px void between the label (x~433) and the score
   (x~1760). The bar now occupies that middle track, which closes the void
   and gives the row something to say at a glance. */

/* The marker is a real <span> now so it can be measured with
   getBoundingClientRect(); a ::before pseudo cannot be. Switch the old
   pseudo off (both the closed "+" and the [open] variant, which carries an
   extra attribute selector and so needs matching specificity). */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor > summary::before,
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor[open] > summary::before {
  content: none;
}

body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor {
  gap: 0;
  padding: .55rem 1.25rem;
}
body.fi-panel-portal .cxp-gbp-analysis-page summary.cxp-health-factor-top {
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: .75rem;
}

body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-marker {
  grid-column: 1;
  align-self: center;
  display: inline-grid;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  border: 1px solid var(--portal-border);
  border-radius: 999px;
  color: var(--portal-primary);
  font-size: .85rem;
  line-height: 1;
  flex: none;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-marker::before { content: "\002B"; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor[open] .cxp-hf-marker::before { content: "\2212"; }

body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-main {
  grid-column: 2;
  display: grid;
  gap: .3rem;
  min-width: 0;
}
/* cxp.css styles `.cxp-health-factor-top > div > span` as muted .8rem body
   copy; the label row must not inherit that. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-labelrow {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  min-width: 0;
  font-size: .9rem;
  line-height: 1.35;
  color: var(--portal-heading);
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-labelrow > strong {
  color: var(--portal-heading);
  font-size: .9rem;
  font-weight: 600;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-hint {
  color: var(--portal-muted);
  font-size: .78rem;
  line-height: 1.35;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor[open] .cxp-hf-hint { display: none; }

/* The bar is part of the row, not part of the expander. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-main .cxp-health-progress {
  height: 6px;
}

body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-score {
  grid-column: 3;
  align-self: center;
  display: flex;
  align-items: baseline;
  gap: .3rem;
  white-space: nowrap;
  text-align: right;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-score-num {
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -.01em;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-score-den {
  color: var(--portal-muted);
  font-size: .78rem;
  font-weight: 500;
}

body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor-detail {
  padding: .1rem 1rem .6rem 2rem;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor-detail > p {
  margin: 0 0 .4rem;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor-detail > p:last-child { margin-bottom: 0; }

body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-link {
  color: var(--portal-primary);
  font-size: .8rem;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* --- 3. Signpost cards to the detail sub-pages ---------------------------- */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-subnav {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: .75rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-subnav-item {
  display: grid;
  gap: .25rem;
  padding: .8rem .9rem;
  border: 1px solid var(--portal-border);
  border-radius: .6rem;
  background: #fff;
  min-width: 0;
  transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-subnav-item:hover {
  border-color: var(--portal-primary);
  box-shadow: 0 2px 10px rgba(15, 23, 42, .07);
  transform: translateY(-1px);
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-subnav-title {
  color: var(--portal-heading);
  font-size: .9rem;
  font-weight: 600;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-subnav-meta {
  color: var(--portal-muted);
  font-size: .78rem;
  line-height: 1.45;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-subnav-score {
  color: var(--portal-heading);
  font-size: .78rem;
  font-weight: 600;
}

/* --- 4. Detail sub-pages -------------------------------------------------- */
body.fi-panel-portal .cxp-gbp-sub-page .cxp-gbp-sub-grid {
  /* max-content, not 1fr: auto-fit already drops empty tracks to 0, but 1fr
     also stretches the remaining real tracks to fill the leftover row width -
     wrong for a KPI tile row (2 tiles on a single-section page like Review
     strength were ballooning to ~714px each). max-content keeps every tile
     sized to its own content regardless of how many siblings exist. */
  grid-template-columns: repeat(auto-fit, minmax(13rem, max-content));
  justify-content: start;
}

/* --- 5. Motion ------------------------------------------------------------
   The bar draws in; the DIGITS never animate. The shared counter binder
   lives in a file this ticket does not own and still runs a null
   Motion.animate(X, X) timeline, so nothing here uses cxp-motion-count.
   Numbers are server-rendered and correct from the first frame. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-gbp-analysis-page summary.cxp-health-factor-top {
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-score { text-align: right; }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-hf-hint { display: none; }
}

/* ---------------------------------------------------------------------------
   Autopilot SEO > Websites (/cyticx-app/command-center/scc-sites)
   2026-08-24. The table overflowed its wrapper at 1390 - the width the owner
   actually browses - giving him an inner horizontal scrollbar, the "page inside
   a page" the standing rule forbids. Meanwhile the row-actions cell measured
   128px for a single 36px kebab (painted lighter than the row, so it read as a
   dead column) while the DOMAIN - the row's identity, and the only column whose
   text was being truncated - was squeezed to 156px.

   SPECIFICITY, resolved with CDP CSS.getMatchedStylesForNode after two failed
   attempts (do this FIRST next time - `document.styleSheets` enumeration is
   unreliable here and reported only 2 rules for a 73KB file). The rules that
   actually win on these cells are:

     .dark .fi-ta .fi-ta-table .fi-ta-cell         { padding: .6rem .8rem !important }
     .dark .fi-ta .fi-ta-table .fi-ta-actions-cell { min-width: max-content !important;
                                                     width: 1% !important }

   Both score (0,4,0). A sensible-looking page-scoped rule like
   `html body .fi-resource-... .fi-ta-table :is(th, td)` is only (0,2,3) - note
   `:is(th, td)` contributes an ELEMENT's specificity, not a class's - so it
   loses even with !important, silently. Matching their own class chain
   (.fi-ta .fi-ta-table .fi-ta-cell) plus the page scope gives (0,4,2).

   `min-width: max-content` is the specific declaration that made the actions
   column 128px wide, so it has to be overridden by name; width alone does not
   do it. Scoped to this one resource page so no other table inherits it. --- */
html body .fi-resource-command-center-scc-sites .fi-ta .fi-ta-table .fi-ta-cell,
html body .fi-resource-command-center-scc-sites .fi-ta .fi-ta-table .fi-ta-header-cell {
    padding-inline: 6px !important;
}

/* NOT FIXED, documented so the next person does not repeat the attempt: the
   trailing actions cell renders 128px, and it is NOT that column's own width -
   it is the leftover. Every other column here is hard-pinned via
   extraAttributes, totalling 896px in a 1024px table, so table-auto hands the
   remaining 128px to the only unpinned column, which happens to hold a 36px
   kebab. Tried and MEASURED as having no effect: width/min-width/max-width
   !important on .fi-ta-actions-cell and .fi-ta-actions-header-cell via the
   (0,4,2) chain that DOES win for padding here; an inline width:34% on the
   domain th via extraHeaderAttributes; and the same width via this stylesheet.
   Pinning .fi-ta-actions itself to 36px did work (it was 102px) but the column
   did not follow. The real fix is structural - stop hard-pinning every other
   column so the domain can absorb slack - which is a bigger change than this
   pass. Domain went 156px -> 196px by reclaiming 40px from Trend instead. */

/* The domain is the row's identity - it gets a real gutter. */
html body .fi-resource-command-center-scc-sites .fi-ta .fi-ta-table .fi-ta-cell:first-child,
html body .fi-resource-command-center-scc-sites .fi-ta .fi-ta-table .fi-ta-header-cell:first-child {
    padding-inline-start: 12px !important;
}

/* It is a 36px icon button, so size the column like one and give the ~82px it
   was hoarding back to the domain column. */
html body .fi-resource-command-center-scc-sites .fi-ta .fi-ta-table .fi-ta-actions-cell,
html body .fi-resource-command-center-scc-sites .fi-ta .fi-ta-table .fi-ta-actions-header-cell {
    min-width: 46px !important;
    width: 46px !important;
    max-width: 46px !important;
    padding-inline: 4px !important;
}
/* A table cell reports its USED width, so capping the <td> alone does nothing -
   the auto-layout sizes the column from its CONTENT. The action group measured
   102px for a single 36px trigger (Filament's gap-3 + min-width:max-content), so
   the group itself is what has to be pinned. */
html body .fi-resource-command-center-scc-sites .fi-ta .fi-ta-table .fi-ta-actions-cell .fi-ta-actions {
    min-width: 0 !important;
    width: 36px !important;
    gap: 0 !important;
    justify-content: flex-end !important;
}
html body .fi-resource-command-center-scc-sites .fi-ta .fi-ta-table .fi-ta-actions-cell > * {
    padding-inline: 0 !important;
}
