/* Shared page-chrome vocabulary used by both panels. Keep these neutral so
   admin billing pages retain their existing layout. */
.cxp-header-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.1rem; flex-wrap: wrap; }
.cxp-page-title { font-size: var(--text-xl); font-weight: var(--font-weight-semibold); color: var(--foreground); letter-spacing: var(--tracking-tight); }
.cxp-page-sub { font-size: var(--text-2sm); color: var(--muted-foreground); }
.cxp-meta, .cxp-count { font-size: var(--text-2xs); color: var(--muted-foreground); }
.cxp-empty { padding: 2.2rem 1.25rem; text-align: center; color: var(--muted-foreground); font-size: var(--text-sm); }
.cxp-row-click { cursor: pointer; }
.cxp-row-archived { opacity: .45; }
.cxp-row-archived:hover { opacity: .7; }
[x-cloak] { display: none !important; }
.kt-card-toolbar select.kt-input { min-width: 10.5rem; padding-right: 1.9rem; }
.cxp-two-col { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 1rem; align-items: start; }
@media (max-width: 1000px) { .cxp-two-col { grid-template-columns: 1fr; } }
@media (pointer: coarse), (max-width: 640px) {
  .kt-btn, .kt-btn-sm { min-height: 36px; }
  .kt-table td .kt-btn-sm { min-height: 34px; }
}

/* Customer portal light surface. The .fi-panel-portal scope is intentionally
   local to app.cyticx.com and must never affect the staff panel. */
.fi-panel-portal,
html.fi-panel-portal {
  --portal-bg: #f5f7fb;
  --portal-surface: #ffffff;
  --portal-surface-soft: #fbfcfe;
  --portal-border: #e1e7ef;
  --portal-heading: #172033;
  --portal-text: #344054;
  /* #667085 measured 4.37-4.45:1 against several light card/chart tints -
     just under the 4.5 WCAG AA floor (check.js body-text-contrast). Darkened
     slightly; still the same muted grey, not a visible redesign. */
  --portal-muted: #5B6577;
  /* Green is the portal accent. Keep readable text on the strong token and
     reserve the neon token for fills, strokes, and other graphic accents. */
  --portal-primary: var(--cxp-good-strong);
  --portal-primary-soft: var(--cxp-good-tint);
  /* 2026-08-21. --portal-chip was used by three rules in this file - the
     invoices link on hover, the round work icon, and the "next work" panel -
     but was never defined anywhere, so `background: var(--portal-chip)` went
     invalid at computed-value time and those three painted no background at
     all. They always meant the soft accent wash the rest of the portal uses
     behind a chip, so it points at that token rather than a literal: a
     hardcoded tint here would be the wrong hue on four of the five skins.
     Same bug and same fix as --portal-surface-2 in cxs-skin.css. */
  --portal-chip: var(--portal-primary-soft);
  --portal-primary-fill: var(--cxp-good);
  --portal-primary-foreground: #0b1220;
  --portal-success: var(--cxp-good-strong);
  --portal-success-soft: var(--cxp-good-tint);
  --portal-warning: var(--cxp-warn-text);
  --portal-shadow: 0 8px 24px rgba(23, 32, 51, .06);
  --background: var(--portal-bg) !important;
  --foreground: var(--portal-text) !important;
  --card: var(--portal-surface) !important;
  --card-foreground: var(--portal-text) !important;
  --popover: var(--portal-surface) !important;
  --popover-foreground: var(--portal-text) !important;
  --muted: #f2f4f7 !important;
  --muted-foreground: var(--portal-muted) !important;
  --border: var(--portal-border) !important;
  --input: var(--portal-border) !important;
  --primary: var(--portal-primary) !important;
  --primary-foreground: var(--portal-primary-foreground) !important;
  --accent: var(--portal-primary-soft) !important;
  --accent-foreground: var(--portal-primary) !important;
  --ring: color-mix(in srgb, var(--cxp-good) 28%, transparent) !important;
  --color-blue-500: var(--portal-primary) !important;
  --color-green-500: var(--portal-success) !important;
  --color-amber-500: var(--portal-warning) !important;
}

html.fi-panel-portal,
body.fi-panel-portal {
  color-scheme: light;
  background: var(--portal-bg) !important;
  color: var(--portal-text) !important;
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
}

body.fi-panel-portal .fi-layout,
body.fi-panel-portal .fi-main-ctn,
body.fi-panel-portal .fi-main {
  background: var(--portal-bg) !important;
}

body.fi-panel-portal .fi-sidebar {
  background: var(--portal-surface-soft) !important;
  border-right: 1px solid var(--portal-border) !important;
  box-shadow: none !important;
}

body.fi-panel-portal .fi-sidebar-header {
  min-height: 96px !important;
  padding: 1.15rem 1rem !important;
  border-bottom: 1px solid var(--portal-border) !important;
}

body.fi-panel-portal .fi-sidebar-header .fi-logo {
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  width: 100% !important;
  min-height: 58px !important;
  padding: .85rem 1rem !important;
  border-radius: 12px !important;
  background: var(--portal-heading) !important;
  box-shadow: 0 5px 12px rgba(23, 32, 51, .16) !important;
}

body.fi-panel-portal .fi-sidebar-header .fi-logo img {
  width: auto !important;
  max-width: 100% !important;
  height: 2rem !important;
  object-fit: contain !important;
}

body.fi-panel-portal .fi-sidebar-nav {
  padding: 1.35rem .8rem !important;
}

body.fi-panel-portal .fi-sidebar-group-label {
  padding: .25rem .7rem .5rem !important;
  color: var(--portal-muted) !important;
  font-size: .7rem !important;
  font-weight: 700 !important;
  letter-spacing: .08em !important;
  text-transform: uppercase !important;
}

body.fi-panel-portal .fi-sidebar-item {
  margin: .2rem 0 !important;
}

body.fi-panel-portal .fi-sidebar-item-button {
  min-height: 42px !important;
  padding: .5rem !important;
  border-radius: 10px !important;
  color: var(--portal-text) !important;
  gap: .4rem !important;
}

body.fi-panel-portal .fi-sidebar-item-button:hover,
body.fi-panel-portal .fi-sidebar-item-active > .fi-sidebar-item-button {
  background: var(--portal-primary-soft) !important;
  color: var(--portal-primary) !important;
  box-shadow: none !important;
}

body.fi-panel-portal .fi-sidebar-item-active > .fi-sidebar-item-button {
  background: var(--portal-primary-fill) !important;
  /* --portal-primary-fill resolves to the DARK #007A3D green here (its
     underlying --cxp-good token is itself aliased to --cxp-tone-good
     elsewhere, not the brighter #00E676 the name implies) - the paired
     --portal-primary-foreground (#0b1220 near-black) only clears WCAG AA
     against a BRIGHT fill, and measures 4.40:1 here. White is safe against
     this fill regardless of which shade the token resolves to on a given
     page. */
  color: #fff !important;
}

body.fi-panel-portal .fi-sidebar-item-active .fi-sidebar-item-icon,
body.fi-panel-portal .fi-sidebar-item-active .fi-sidebar-item-label,
body.fi-panel-portal .fi-sidebar-item-button:hover .fi-sidebar-item-icon,
body.fi-panel-portal .fi-sidebar-item-button:hover .fi-sidebar-item-label {
  color: var(--portal-primary) !important;
}

body.fi-panel-portal .fi-sidebar-item-active .fi-sidebar-item-icon,
body.fi-panel-portal .fi-sidebar-item-active .fi-sidebar-item-label {
  color: var(--portal-primary-foreground) !important;
}

body.fi-panel-portal .fi-sidebar-item-icon {
  width: 1.15rem !important;
  height: 1.15rem !important;
  color: var(--portal-muted) !important;
}

body.fi-panel-portal .fi-sidebar-item-label {
  color: inherit !important;
  font-size: .92rem !important;
  font-weight: 500 !important;
}
/* The real winning text colour for an active nav item is pages.css's
   "html body .fi-sidebar .fi-sidebar-item-active ... .fi-sidebar-item-label
   { color: rgb(255,255,255) !important }" (solid white, correct - measured
   directly, not assumed). But `.fi-sidebar-group:not(:has(.fi-sidebar-
   group-button)) .fi-sidebar-item-label { opacity: .85 }` - a HIGHER-
   specificity (0,3,0) rule than the (0,2,0) ".fi-sidebar-item-active
   .fi-sidebar-item-label { opacity: 1 }" rule meant to override it for
   active items - wins regardless of source order and blends that white
   down to rgb(217,235,226) on the dark-green active fill, 4.40:1
   (check.js body-text-contrast). Matches the same high-specificity ancestor
   shape plus .fi-sidebar-item-active so it actually wins. */
body.fi-panel-portal .fi-sidebar-group:not(:has(.fi-sidebar-group-button)) .fi-sidebar-item-active .fi-sidebar-item-label {
  opacity: 1 !important;
}

body.fi-panel-portal .fi-topbar {
  min-height: 74px !important;
  background: rgba(255, 255, 255, .96) !important;
  border-bottom: 1px solid var(--portal-border) !important;
  box-shadow: none !important;
}

body.fi-panel-portal .fi-topbar > nav {
  min-height: 74px !important;
  background: var(--portal-surface) !important;
  border-bottom: 0 !important;
  box-shadow: none !important;
  ring: 0 !important;
}

body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button {
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  min-width: 2.25rem !important;
  min-height: 2.25rem !important;
  border-radius: 999px !important;
}

body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger .fi-user-avatar {
  visibility: hidden !important;
}

body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button::before {
  content: "CQ";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 999px;
  background: #eef1f5;
  color: var(--portal-heading);
  font-size: .72rem;
  font-weight: 700;
}

body.fi-panel-portal .fi-topbar .fi-theme-switcher {
  display: none !important;
}

body.fi-panel-portal .fi-topbar .fi-icon-btn,
body.fi-panel-portal .fi-topbar .fi-user-menu-trigger {
  color: var(--portal-muted) !important;
}

body.fi-panel-portal .cxp-topbar-context {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  margin-inline: 1.25rem auto;
  line-height: 1.2;
}

body.fi-panel-portal .cxp-topbar-greeting {
  color: var(--portal-muted);
  font-size: .72rem;
  font-weight: 500;
  letter-spacing: .01em;
}

body.fi-panel-portal .cxp-topbar-business {
  overflow: hidden;
  color: var(--portal-heading);
  font-size: .94rem;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.fi-panel-portal .cxp-topbar-user-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  margin-inline: .7rem 1rem;
  line-height: 1.2;
}

body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button.cxp-account-trigger {
  display: inline-flex !important;
  width: auto !important;
  min-width: 0 !important;
  min-height: 2.75rem !important;
  align-items: center !important;
  justify-content: flex-start !important;
  gap: .55rem !important;
  padding: .25rem .65rem !important;
  border: 1px solid var(--portal-border) !important;
  border-radius: 999px !important;
  color: var(--portal-heading) !important;
  background: var(--portal-surface) !important;
}

body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button.cxp-account-trigger:hover,
body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button.cxp-account-trigger:focus-visible {
  border-color: var(--cxp-good-line) !important;
  background: #f7faff !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cxp-good) 16%, transparent) !important;
}

body.fi-panel-portal .cxp-account-trigger .cxp-topbar-user-copy {
  margin: 0 !important;
  text-align: left;
}

body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button.cxp-account-trigger::before {
  display: none !important;
  content: none !important;
}

body.fi-panel-portal .cxp-account-trigger .fi-user-avatar {
  display: block !important;
  visibility: visible !important;
  width: 2rem !important;
  height: 2rem !important;
}

body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button.cxp-account-trigger .fi-user-avatar {
  display: block !important;
  visibility: visible !important;
}

body.fi-panel-portal .fi-dropdown-panel {
  color: var(--portal-text) !important;
  background: #ffffff !important;
  border-color: var(--portal-border) !important;
}
body.fi-panel-portal .fi-dropdown-panel .fi-dropdown-header,
body.fi-panel-portal .fi-dropdown-panel .fi-dropdown-header-label,
body.fi-panel-portal .fi-dropdown-panel .fi-dropdown-list-item,
body.fi-panel-portal .fi-dropdown-panel .fi-dropdown-list-item-label {
  color: var(--portal-text) !important;
}
body.fi-panel-portal .fi-dropdown-panel .fi-dropdown-list-item:hover,
body.fi-panel-portal .fi-dropdown-panel .fi-dropdown-list-item:focus-visible {
  color: var(--portal-heading) !important;
  background: var(--portal-primary-soft) !important;
}

body.fi-panel-portal .cxp-account-chevron {
  width: .45rem;
  height: .45rem;
  margin-left: .1rem;
  border-right: 1.5px solid var(--portal-muted);
  border-bottom: 1.5px solid var(--portal-muted);
  transform: rotate(45deg) translateY(-2px);
}

body.fi-panel-portal .cxp-portal-footer {
  width: 100%;
  margin-top: 2rem;
  padding: 1.15rem 2.25rem 1.5rem;
  border-top: 1px solid var(--portal-border);
  color: var(--portal-muted);
  font-size: 12px !important;
}

body.fi-panel-portal .cxp-portal-footer-inner {
  display: flex;
  max-width: 1440px;
  min-height: 36px;
  margin: 0 auto;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

body.fi-panel-portal .cxp-portal-footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .35rem 1rem;
}

body.fi-panel-portal .cxp-portal-footer a {
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  color: var(--portal-heading);
  font-size: 12px !important;
  text-decoration: none;
}

body.fi-panel-portal .cxp-portal-footer a:hover,
body.fi-panel-portal .cxp-portal-footer a:focus-visible {
  color: var(--portal-primary);
  text-decoration: underline;
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-account-trigger .cxp-topbar-user-copy,
  body.fi-panel-portal .cxp-account-trigger .cxp-account-chevron { display: none !important; }
  body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button.cxp-account-trigger {
    width: 2.75rem !important;
    justify-content: center !important;
    padding: .25rem !important;
  }
  body.fi-panel-portal .cxp-portal-footer { padding: 1rem; }
  body.fi-panel-portal .cxp-portal-footer-inner {
    flex-direction: column;
    align-items: stretch;
    gap: .35rem;
    text-align: center;
  }
  body.fi-panel-portal .cxp-portal-footer-links { gap: .15rem .45rem; }
  body.fi-panel-portal .cxp-portal-footer-links a { justify-content: center; }
  body.fi-panel-portal .cxp-portal-footer-support { justify-content: center; }
}

body.fi-panel-portal .cxp-topbar-user-name {
  overflow: hidden;
  color: var(--portal-heading);
  font-size: .82rem;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.fi-panel-portal .cxp-topbar-user-role {
  color: var(--portal-muted);
  font-size: .68rem;
  font-weight: 500;
  white-space: nowrap;
}

body.fi-panel-portal .cxp-invoices-link {
  color: var(--portal-primary) !important;
  background: var(--portal-surface) !important;
  border-color: var(--portal-border) !important;
}

body.fi-panel-portal .cxp-invoices-link:hover {
  color: var(--portal-primary) !important;
  background: var(--portal-chip) !important;
}

body.fi-panel-portal .cxp-domain-switcher {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  min-width: 13rem;
}

body.fi-panel-portal .cxp-domain-switcher > span {
  color: var(--portal-muted);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
}

body.fi-panel-portal .cxp-domain-switcher select {
  min-height: 2.25rem;
  padding: .35rem 2rem .35rem .7rem;
  border: 1px solid var(--portal-border);
  border-radius: .5rem;
  color: var(--portal-heading);
  background: var(--portal-surface);
  font-size: .78rem;
  font-weight: 600;
}

body.fi-panel-portal .cxp-domain-switcher select:focus {
  outline: 2px solid color-mix(in srgb, var(--cxp-good) 22%, transparent);
  outline-offset: 1px;
}

body.fi-panel-portal .cxp-dashboard-insights {
  display: grid;
  /* This row currently contains only the trend card; the Maps-vs-organic
     comparison follows as its own full-width section. Do not leave a
     5-column dead zone beside the only child at desktop widths. */
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
}

body.fi-panel-portal .cxp-gauge-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
  padding: 1.25rem;
}

body.fi-panel-portal .cxp-gauge-item {
  display: flex;
  align-items: center;
  gap: .8rem;
  min-width: 0;
}

body.fi-panel-portal .cxp-gauge-item > div:last-child {
  display: flex;
  flex-direction: column;
  gap: .18rem;
  min-width: 0;
}

body.fi-panel-portal .cxp-gauge-item strong {
  color: var(--portal-heading);
  font-size: .82rem;
}

body.fi-panel-portal .cxp-gauge-item > div:last-child span {
  color: var(--portal-muted);
  font-size: .7rem;
  text-transform: capitalize;
}

body.fi-panel-portal .cxp-gauge {
  position: relative;
  flex: 0 0 4.3rem;
  width: 4.3rem;
  height: 4.3rem;
}

body.fi-panel-portal .cxp-gauge svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

body.fi-panel-portal .cxp-gauge circle {
  fill: none;
  stroke-width: 7;
}

body.fi-panel-portal .cxp-gauge-track { stroke: #e9eef6; }
body.fi-panel-portal .cxp-gauge-progress { stroke-linecap: round; }
body.fi-panel-portal .cxp-gauge-health { stroke: var(--cxp-good); }
body.fi-panel-portal .cxp-gauge-seo { stroke: var(--portal-primary-fill); }
body.fi-panel-portal .cxp-gauge-good { stroke: var(--color-green-500); }
body.fi-panel-portal .cxp-gauge-middle { stroke: var(--cxp-warn); }
body.fi-panel-portal .cxp-gauge-poor { stroke: var(--cxp-bad); }

body.fi-panel-portal .cxp-gauge > strong {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--portal-heading);
  font-size: 1.05rem;
}

body.fi-panel-portal .cxp-rank-card > .kt-card-content,
body.fi-panel-portal .cxp-rank-card > .cxp-empty {
  min-height: 8.7rem;
}

body.fi-panel-portal .cxp-sparkline-wrap {
  height: 7.2rem;
  padding: .75rem 1.25rem 0;
}

body.fi-panel-portal .cxp-sparkline {
  width: 100%;
  height: 100%;
  overflow: visible;
}

body.fi-panel-portal .cxp-rank-summary {
  display: flex;
  align-items: baseline;
  gap: .45rem;
  padding: 0 1.25rem 1rem;
}

body.fi-panel-portal .cxp-rank-summary strong {
  color: var(--portal-heading);
  font-size: 1.35rem;
}

body.fi-panel-portal .cxp-rank-summary span {
  color: var(--portal-muted);
  font-size: .72rem;
}

body.fi-panel-portal .cxp-rank-card-heading {
  min-width: 0;
}

body.fi-panel-portal .cxp-rank-card-title-line {
  align-items: center;
  gap: .55rem;
}

/* CYT-814 component drift: this shrank ONE instance of the shared
   <x-portal.page-explainer> trigger to 28px / .25rem .55rem while every other
   instance of the same component - the page-title one on this page, the GBP
   Profile & listings one, the Live operations one - kept the canonical
   32px / .35rem .65rem defined at `.cxp-page-explainer-trigger` below.
   Measured across three pages: 32px on two of them, 28px on the rank card,
   which is "the same component built two ways". A card-level heading is not
   a different control, so the override is removed rather than duplicated;
   the component now has exactly one geometry everywhere. */

body.fi-panel-portal .cxp-rank-summary--featured {
  align-items: flex-start;
  flex-direction: column;
  gap: .2rem;
  padding-top: .85rem;
  padding-bottom: .35rem;
}

body.fi-panel-portal .cxp-rank-summary--featured strong {
  font-size: clamp(2.75rem, 5vw, 4rem);
  font-weight: 700;
  letter-spacing: -.055em;
  line-height: .95;
  font-variant-numeric: tabular-nums;
}

body.fi-panel-portal .cxp-rank-summary--featured span {
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .01em;
}

body.fi-panel-portal .cxp-dashboard-columns {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 1rem;
  align-items: start;
}

body.fi-panel-portal .cxp-dashboard-right {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-width: 0;
}

body.fi-panel-portal .cxp-service-list,
body.fi-panel-portal .cxp-work-list {
  display: flex;
  flex-direction: column;
}

body.fi-panel-portal .cxp-service-row,
body.fi-panel-portal .cxp-work-row {
  display: flex;
  align-items: center;
  gap: .65rem;
  padding: .75rem 1.25rem;
  border-bottom: 1px solid var(--portal-border);
  color: var(--portal-heading);
  text-decoration: none;
}

body.fi-panel-portal .cxp-service-row:last-child,
body.fi-panel-portal .cxp-work-row:last-child { border-bottom: 0; }

body.fi-panel-portal .cxp-service-row > span:nth-child(2),
body.fi-panel-portal .cxp-work-row > span:last-child {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
}

body.fi-panel-portal .cxp-service-row strong,
body.fi-panel-portal .cxp-work-row strong {
  overflow: hidden;
  font-size: .8rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.fi-panel-portal .cxp-service-row small,
body.fi-panel-portal .cxp-work-row small {
  margin-top: .16rem;
  color: var(--portal-muted);
  font-size: .7rem;
  line-height: 1.35;
}

body.fi-panel-portal .cxp-service-row > i:last-child {
  color: var(--portal-primary);
  font-size: .85rem;
}

body.fi-panel-portal .cxp-service-icon {
  display: grid;
  flex: 0 0 1.8rem;
  width: 1.8rem;
  height: 1.8rem;
  place-items: center;
  border-radius: 50%;
  color: var(--portal-primary);
  background: var(--portal-chip);
}

body.fi-panel-portal .cxp-work-dot {
  flex: 0 0 .45rem;
  width: .45rem;
  height: .45rem;
  border-radius: 50%;
  background: #26a269;
}

body.fi-panel-portal .cxp-next-work {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  margin: 0 1.25rem 1rem;
  padding: .7rem .8rem;
  border-radius: .5rem;
  background: var(--portal-chip);
}

body.fi-panel-portal .cxp-next-work strong {
  color: var(--portal-heading);
  font-size: .76rem;
  line-height: 1.35;
}

body.fi-panel-portal .fi-main-ctn {
  padding: 2rem 2.25rem 3rem !important;
}

body.fi-panel-portal .fi-page {
  max-width: 1440px !important;
  margin: 0 auto !important;
}

body.fi-panel-portal .fi-page-header {
  display: none !important;
}

body.fi-panel-portal .cxp-portal-page {
  color: var(--portal-text);
}

body.fi-panel-portal .cxp-header-row {
  margin-bottom: 1.2rem !important;
}

body.fi-panel-portal .cxp-page-title,
body.fi-panel-portal .kt-card-title,
body.fi-panel-portal .cxp-portal-title {
  color: var(--portal-heading) !important;
}

/* Website health uses the app's primary brand accent for its page and
   section titles. Keep the scope narrow: semantic status colors and measured
   gauge/chart tones remain governed by their own score classes. The fallback
   matches the canonical --portal-primary token (app's green brand, see
   --cxp-good-strong) when the reference theme bundle is not available in a
   development render. 2026-08-17: switched from Signal Red (--cxt-primary,
   not deployed - see project_cyticx_portal_theme_signal_red.md) to the app's
   live green brand, since red read as an error state against the currently
   green-branded portal. */
body.fi-panel-portal .cxp-health-page .cxp-health-title {
  color: var(--portal-primary, #007A3D) !important;
}

body.fi-panel-portal .cxp-page-title {
  font-size: clamp(1.45rem, 1.15rem + .6vw, 2rem) !important;
  font-weight: 700 !important;
}

body.fi-panel-portal .cxp-page-sub,
body.fi-panel-portal .cxp-meta,
body.fi-panel-portal .cxp-count,
body.fi-panel-portal .sb-stat-caption {
  color: var(--portal-muted) !important;
}

body.fi-panel-portal .kt-card,
body.fi-panel-portal .kt-card-border {
  background: var(--portal-surface) !important;
  border: 1px solid var(--portal-border) !important;
  border-radius: 14px !important;
  box-shadow: var(--portal-shadow) !important;
}

body.fi-panel-portal .kt-card-header {
  min-height: 56px !important;
  padding: 1rem 1.25rem !important;
  border-bottom: 1px solid var(--portal-border) !important;
}

body.fi-panel-portal .kt-card-content {
  color: var(--portal-text) !important;
}

body.fi-panel-portal .kt-card-footer {
  border-top: 1px solid var(--portal-border) !important;
}

body.fi-panel-portal .sb-stat-grid {
  gap: 1rem !important;
  margin-bottom: 1rem !important;
}

body.fi-panel-portal .sb-stat {
  min-height: 152px !important;
  padding: 1.25rem 1.35rem !important;
}

body.fi-panel-portal .sb-stat-label,
body.fi-panel-portal .sb-stat-value {
  color: var(--portal-heading) !important;
}

body.fi-panel-portal .cxp-score-number,
body.fi-panel-portal .cxp-score-number * {
  color: var(--portal-heading) !important;
}
body.fi-panel-portal .cxp-score-number.cxp-score-good,
body.fi-panel-portal .cxp-score-number.cxp-score-good * {
  color: var(--cxp-good-strong) !important;
}
body.fi-panel-portal .cxp-score-number.cxp-score-middle,
body.fi-panel-portal .cxp-score-number.cxp-score-middle * {
  color: var(--cxp-warn-text) !important;
}
body.fi-panel-portal .cxp-score-number.cxp-score-poor,
body.fi-panel-portal .cxp-score-number.cxp-score-poor * {
  color: var(--cxp-bad-strong) !important;
}

body.fi-panel-portal .sb-stat-label {
  font-size: .98rem !important;
  font-weight: 500 !important;
  letter-spacing: 0 !important;
  text-transform: none !important;
}

body.fi-panel-portal .sb-stat-value {
  font-size: 2rem !important;
  font-weight: 700 !important;
  letter-spacing: -.04em !important;
}

body.fi-panel-portal .sb-stat-icon {
  color: var(--portal-primary) !important;
  background: var(--portal-primary-soft) !important;
  border-radius: 999px !important;
}

body.fi-panel-portal .kt-btn-primary {
  background: var(--portal-primary-fill) !important;
  border-color: var(--portal-primary-fill) !important;
  color: var(--portal-primary-foreground) !important;
}

body.fi-panel-portal .kt-btn-outline {
  color: var(--portal-primary) !important;
  background: #ffffff !important;
  border-color: var(--portal-primary) !important;
}

body.fi-panel-portal .kt-btn-ghost {
  color: var(--portal-muted) !important;
}

body.fi-panel-portal .kt-btn-ghost:hover,
body.fi-panel-portal .kt-btn-outline:hover {
  color: var(--portal-primary) !important;
  background: var(--portal-primary-soft) !important;
}

body.fi-panel-portal .kt-badge-success {
  color: var(--cxp-good-strong) !important;
  background: var(--portal-success-soft) !important;
  border-color: var(--cxp-good-line) !important;
}

body.fi-panel-portal .kt-badge-outline {
  color: var(--portal-muted) !important;
  background: #f8fafc !important;
  border-color: #d0d5dd !important;
}

body.fi-panel-portal .kt-badge-primary {
  color: var(--portal-primary) !important;
  background: var(--portal-primary-soft) !important;
  border-color: var(--cxp-good-line) !important;
}

body.fi-panel-portal .kt-badge-warning {
  color: var(--cxp-warn-text) !important;
  background: var(--cxp-warn-tint) !important;
  border-color: #f4d39b !important;
}

body.fi-panel-portal .kt-badge-destructive {
  /* --cxp-bad-text (#D92D20) measured 4.24:1 on --cxp-bad-tint - just under
     the 4.5 floor. --cxp-bad-strong resolves to the same value in this
     context (multiple conflicting definitions elsewhere in this file), so
     hardcoded directly rather than chasing the token cascade further. */
  color: #A01D10 !important;
  background: var(--cxp-bad-tint) !important;
  border-color: #f5b5ac !important;
}

body.fi-panel-portal .kt-badge-outline.kt-badge-success {
  color: var(--cxp-good-strong) !important;
  background: var(--portal-success-soft) !important;
  border-color: var(--cxp-good-line) !important;
}

/* CYT-882: canonical status-chip tone aliases - same values as
   kt-badge-success/warning/destructive above, under the good/warn/bad/neutral
   names already used everywhere else in this codebase (cxp-ai2-chip,
   cxp-rk2-chip) so existing PHP tone logic needs no translation layer. */
body.fi-panel-portal .kt-badge-good {
  color: var(--cxp-good-strong) !important;
  background: var(--portal-success-soft) !important;
  border-color: var(--cxp-good-line) !important;
}
body.fi-panel-portal .kt-badge-warn {
  color: var(--cxp-warn-text) !important;
  background: var(--cxp-warn-tint) !important;
  border-color: #f4d39b !important;
}
body.fi-panel-portal .kt-badge-bad {
  color: #A01D10 !important;
  background: var(--cxp-bad-tint) !important;
  border-color: #f5b5ac !important;
}
body.fi-panel-portal .kt-badge-neutral {
  color: var(--portal-muted) !important;
  background: #f8fafc !important;
  border-color: #d0d5dd !important;
}

body.fi-panel-portal .kt-table {
  color: var(--portal-text) !important;
}

body.fi-panel-portal .kt-table th {
  color: var(--portal-heading) !important;
  background: #e7eef8 !important;
  font-weight: 700 !important;
}
body.fi-panel-portal .kt-table > tbody > tr:nth-child(odd) {
  background: #ffffff !important;
}
body.fi-panel-portal .kt-table > tbody > tr:nth-child(even) {
  background: #f8fafc !important;
}
body.fi-panel-portal .kt-table > tbody > tr > td {
  background: inherit !important;
}

body.fi-panel-portal .kt-table td,
body.fi-panel-portal .kt-table th {
  border-color: var(--portal-border) !important;
}

body.fi-panel-portal .cxp-portal-grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(330px, 5fr);
  gap: 1rem;
  align-items: start;
}

body.fi-panel-portal .cxp-portal-side {
  display: grid;
  gap: 1rem;
  min-width: 0;
}

body.fi-panel-portal .cxp-portal-chart .kt-card-content {
  padding: 1rem 1.25rem 1.15rem !important;
}

body.fi-panel-portal .cxp-chart-summary {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  margin-bottom: .75rem;
}

body.fi-panel-portal .cxp-chart-summary strong {
  color: var(--portal-heading);
  font-size: 1.75rem;
  letter-spacing: -.04em;
}

body.fi-panel-portal .cxp-chart-summary .cxp-delta {
  padding: .25rem .5rem;
  border-radius: 999px;
  color: var(--cxp-good-strong);
  background: var(--portal-success-soft);
  font-size: .75rem;
  font-weight: 700;
}

body.fi-panel-portal .cxp-chart {
  width: 100%;
  height: 190px;
  display: block;
}

/* The shared trend component contains axis labels, an inline SVG and date
   labels. Keep those rows inside the chart's fixed height: the base SVG rule
   gives a viewBox-only SVG a square intrinsic height, which can push the
   actual line below the card while leaving the data and accessibility label
   intact. */
body.fi-panel-portal .cxp-trend-chart {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  min-width: 0;
  overflow: hidden;
}
body.fi-panel-portal .cxp-trend-chart > svg {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
  overflow: visible;
}
body.fi-panel-portal .cxp-trend-chart .cxp-chart-axis-labels,
body.fi-panel-portal .cxp-trend-chart .cxp-trend-point-labels {
  min-width: 0;
  position: relative;
  display: flex;
  justify-content: space-between;
  color: var(--muted-foreground);
  font-size: .75rem;
  line-height: 1.25;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-trend-chart .cxp-trend-point-labels {
  height: 1.5rem;
  display: block;
}
body.fi-panel-portal .cxp-trend-chart .cxp-trend-point-labels span {
  position: absolute;
  top: .1rem;
  transform: translateX(-50%);
}
body.fi-panel-portal .cxp-trend-chart .cxp-trend-point-labels span:first-child {
  transform: none;
}
body.fi-panel-portal .cxp-trend-chart .cxp-trend-point-labels span:last-child {
  transform: translateX(-100%);
}

/* The dashboard ranking trend uses a deliberate area-chart treatment: the
   neutral plot field and left-hand axis give the card a clear visual frame,
   while keeping the shared trend component's other consumers unchanged. */
body.fi-panel-portal .cxp-rank-card {
  overflow: hidden;
}

body.fi-panel-portal .cxp-rank-card > .kt-card-header {
  gap: .75rem;
  padding: 1.15rem 1.25rem .65rem;
}

body.fi-panel-portal .cxp-rank-card > .cxp-source-byline {
  margin: 0 1.25rem .65rem !important;
}

body.fi-panel-portal .cxp-trend-chart-area {
  width: auto;
  display: flex;
  flex-direction: column;
  gap: .6rem;
  height: 230px;
  margin: 0 1.25rem;
  padding: .8rem .85rem .65rem;
  box-sizing: border-box;
  border: 1px solid color-mix(in srgb, var(--portal-border) 72%, #aeb9c7);
  border-radius: .85rem;
  background: linear-gradient(180deg, #f3f5f8 0%, #edf0f4 100%);
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-title {
  color: var(--portal-heading);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-note {
  color: var(--portal-muted);
  font-size: .7rem;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-body {
  display: grid;
  grid-template-columns: 2.25rem minmax(0, 1fr);
  flex: 1;
  gap: .55rem;
  min-height: 0;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-axis {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: .15rem 0 1.75rem;
  color: var(--portal-muted);
  font-size: .68rem;
  line-height: 1;
  text-align: right;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-plot {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  min-width: 0;
  min-height: 0;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-plot > svg {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
  overflow: visible;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-grid {
  fill: none;
  stroke: rgba(92, 106, 122, .17);
  stroke-width: 1;
  stroke-dasharray: 2 3;
}

body.fi-panel-portal .cxp-rank-card .cxp-trend-chart-area path[data-chart-area] {
  fill: url(#trend-area-fill-rank-visibility-trend) !important;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-point-labels {
  height: 1.35rem;
}

body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-point-labels span {
  top: .25rem;
  color: var(--portal-muted);
  font-size: .75rem;
}

body.fi-panel-portal .cxp-rank-card > .cxp-rank-summary {
  padding: .8rem 1.25rem 1.15rem;
}

body.fi-panel-portal .cxp-rank-card > .cxp-rank-summary--featured {
  padding-top: .85rem;
  padding-bottom: .35rem;
}

body.fi-panel-portal .cxp-chart [data-chart-line] {
  stroke: var(--cxp-chart-line-color, var(--portal-primary)) !important;
}

body.fi-panel-portal .cxp-chart path[data-chart-area] {
  fill: var(--cxp-chart-area-fill, var(--portal-primary-soft)) !important;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-rank-card > .kt-card-header {
    align-items: flex-start;
    flex-direction: column;
    gap: .25rem;
  }

  body.fi-panel-portal .cxp-trend-chart-area {
    height: 210px;
    margin: 0 .85rem;
    padding: .75rem .65rem .55rem;
  }

  body.fi-panel-portal .cxp-rank-card > .cxp-source-byline {
    margin-right: .85rem !important;
    margin-left: .85rem !important;
  }

  body.fi-panel-portal .cxp-rank-card > .cxp-rank-summary {
    padding-right: .85rem;
    padding-left: .85rem;
  }

  body.fi-panel-portal .cxp-rank-card-title-line {
    align-items: flex-start;
  }
}

body.fi-panel-portal .cxp-rankings-filter-tabs {
  /* CYT-855: was a fixed 8.5rem (136px) per column, packing all 7
     position-band tiles into the grid's left edge and leaving ~400px of
     dead card space to the right at 1920px - the source of "tiles hug
     the border" (there was no room left to give them a real gutter
     against). 1fr lets the 7 tiles fill the card width the same way the
     'At a glance' KPI grid already does. */
  grid-template-columns: repeat(7, minmax(0, 1fr)) !important;
  justify-content: stretch !important;
}
body.fi-panel-portal .cxp-dashboard-page .cxp-merge-table th:nth-child(n+2),
body.fi-panel-portal .cxp-dashboard-page .cxp-merge-table td:nth-child(n+2),
body.fi-panel-portal .cxp-rankings-keyword-table th,
body.fi-panel-portal .cxp-rankings-keyword-table td {
  text-align: left !important;
}
body.fi-panel-portal .cxp-rankings-overview-list > div {
  display: grid !important;
  grid-template-columns: minmax(0, 1fr) 4rem !important;
  justify-content: initial !important;
}
body.fi-panel-portal .cxp-rankings-overview-list > div:last-child {
  border-bottom: 0 !important;
}
body.fi-panel-portal .cxp-ranking-page-link {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  color: var(--portal-primary);
  text-overflow: ellipsis;
  text-decoration: none;
  vertical-align: top;
}
body.fi-panel-portal .cxp-ranking-page-link:hover,
body.fi-panel-portal .cxp-ranking-page-link:focus-visible {
  color: var(--portal-primary-dark, var(--portal-primary));
  text-decoration: underline;
}
body.fi-panel-portal .cxp-rankings-filter strong,
body.fi-panel-portal .cxp-health-issue strong {
  font-size: 1.75rem !important;
}
body.fi-panel-portal .fi-topbar,
body.fi-panel-portal .fi-topbar > nav {
  overflow: visible !important;
  border-bottom: 1px solid var(--portal-border) !important;
  box-shadow: none !important;
  transform: none !important;
}
body.fi-panel-portal .cxp-taskbar {
  flex-wrap: nowrap !important;
  gap: .45rem !important;
  margin-inline: .25rem .35rem !important;
}
body.fi-panel-portal .cxp-topbar-context {
  flex: 0 1 clamp(12rem, 30vw, 24rem) !important;
  flex-wrap: nowrap !important;
  gap: .75rem !important;
  margin-inline: 1rem auto !important;
  white-space: nowrap !important;
}
body.fi-panel-portal .cxp-topbar-greeting,
body.fi-panel-portal .cxp-topbar-business {
  overflow: hidden !important;
  text-overflow: ellipsis !important;
}
body.fi-panel-portal .cxp-taskbar-overflow + .cxp-taskbar-overflow {
  margin-left: .15rem !important;
}
body.fi-panel-portal .fi-topbar .fi-user-menu {
  margin-left: .15rem !important;
}
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-rankings-filter-tabs {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
}

/* CYT-527: layout and spacing polish from the Drawbridge pass. Keep these
   final, portal-scoped rules together so later page-specific styles cannot
   reintroduce the same shell, chart, table, or whitespace defects. */
body.fi-panel-portal .fi-topbar,
body.fi-panel-portal .fi-topbar > nav {
  position: relative;
  z-index: 20;
  overflow: visible !important;
  border-bottom: 1px solid var(--portal-border) !important;
  box-shadow: none !important;
  transform: none !important;
}
body.fi-panel-portal .fi-topbar > nav {
  display: flex;
  align-items: center;
}
body.fi-panel-portal .cxp-topbar-context {
  flex: 0 1 clamp(12rem, 30vw, 24rem);
  margin-inline: 1rem auto;
  align-self: stretch;
  flex-wrap: nowrap;
  gap: .75rem;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-topbar-greeting,
body.fi-panel-portal .cxp-topbar-business {
  overflow: hidden;
  text-overflow: ellipsis;
}
body.fi-panel-portal .cxp-taskbar {
  gap: .45rem;
  margin-inline: .25rem .35rem;
}
body.fi-panel-portal .cxp-taskbar-overflow + .cxp-taskbar-overflow {
  margin-left: .15rem;
}
body.fi-panel-portal .fi-topbar .fi-user-menu {
  margin-left: .15rem;
}
body.fi-panel-portal .fi-topbar .fi-user-menu .fi-dropdown-trigger > button,
body.fi-panel-portal .cxp-taskbar-action {
  transition: none !important;
}

body.fi-panel-portal .fi-sidebar-group {
  margin-block: .35rem .8rem;
}
body.fi-panel-portal .fi-sidebar-group-items {
  display: grid;
  /* CYT-857: with no explicit column track, the implicit auto column sizes
     to the WIDEST content among every item in the grid - including a deeply
     indented child label like "Google Search Console" - and then stretches
     every sibling row to match. That overflow ate the sidebar's intended
     right-hand gutter (measured 0.58px vs 12.8px on the left) while leaving
     the left inset untouched, since the overflow only grows rightward.
     minmax(0, 1fr) caps the column at the container's own width so labels
     truncate (Filament's `.fi-sidebar-item-label` already carries
     `truncate`) instead of forcing the row wider. */
  grid-template-columns: minmax(0, 1fr);
  gap: .2rem;
  padding: 0 !important;
}
body.fi-panel-portal .fi-sidebar-group-items .fi-sidebar-item {
  margin: 0 !important;
  min-width: 0;
}
body.fi-panel-portal .fi-sidebar-group-items .fi-sidebar-item-button {
  width: 100%;
  min-height: 42px !important;
  padding-inline: .65rem !important;
}

/* A point rendered inside a preserveAspectRatio="none" SVG becomes an oval
   when the chart is wide. HTML markers keep the data points circular at both
   desktop and phone widths. */
body.fi-panel-portal .cxp-trend-chart:not(.cxp-trend-chart-area) > .cxp-trend-chart-plot,
body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-plot {
  position: relative;
}
body.fi-panel-portal .cxp-trend-chart:not(.cxp-trend-chart-area) > .cxp-trend-chart-plot {
  display: block;
  grid-row: 2;
  min-height: 0;
}
body.fi-panel-portal .cxp-trend-chart > .cxp-trend-chart-plot > svg,
body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-plot > svg {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
}
body.fi-panel-portal .cxp-trend-point-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-point-overlay {
  bottom: 1.35rem;
}
body.fi-panel-portal .cxp-trend-point {
  position: absolute;
  width: .55rem;
  height: .55rem;
  transform: translate(-50%, -50%);
  border: 2px solid var(--portal-surface) !important;
  border-radius: 50%;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--portal-primary) 20%, transparent);
}
body.fi-panel-portal .cxp-rankings-chart-points {
  position: absolute;
  top: 1.1rem;
  right: 1.1rem;
  bottom: .5rem;
  left: 6.6rem;
  pointer-events: none;
}
body.fi-panel-portal .cxp-rankings-chart-point {
  position: absolute;
  width: .65rem;
  height: .65rem;
  transform: translate(-50%, -50%);
  border: 2px solid var(--portal-surface);
  border-radius: 50%;
  background: var(--portal-primary);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--portal-primary) 24%, transparent);
}

/* Shared card rows use one stretch owner. The card internals remain content
   sized, so copy can wrap without producing a ragged bottom edge. */
body.fi-panel-portal .cxp-dashboard-columns,
body.fi-panel-portal .cxp-secondary-grid,
body.fi-panel-portal .cxp-portal-side,
body.fi-panel-portal .cxp-two-col {
  align-items: stretch;
}
body.fi-panel-portal .cxp-dashboard-columns > .kt-card,
body.fi-panel-portal .cxp-secondary-grid > .kt-card,
body.fi-panel-portal .cxp-portal-side > .kt-card {
  height: 100%;
}

/* Numeric columns read as a table: the values start under the labels instead
   of drifting to a second right-aligned axis. */
body.fi-panel-portal .cxp-dashboard-page .cxp-merge-table th:nth-child(n+2),
body.fi-panel-portal .cxp-dashboard-page .cxp-merge-table td:nth-child(n+2),
body.fi-panel-portal .cxp-rankings-keyword-table th,
body.fi-panel-portal .cxp-rankings-keyword-table td {
  text-align: left !important;
}
body.fi-panel-portal .cxp-rankings-overview-list > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 4rem;
  justify-content: initial;
}
body.fi-panel-portal .cxp-rankings-overview-list > div strong {
  text-align: left;
}
body.fi-panel-portal .cxp-rankings-overview-list > div:last-child {
  border-bottom: 0;
}

/* The seven position filters are a compact control strip, not seven fluid
   cards. Larger values make the movement signal scannable. */
body.fi-panel-portal .cxp-rankings-filter-tabs {
  grid-template-columns: repeat(7, minmax(0, 8.5rem));
  justify-content: start;
  gap: .55rem;
  padding: .85rem 1.1rem 1rem;
}
body.fi-panel-portal .cxp-rankings-filter {
  min-height: 6.25rem;
  padding: .7rem;
}
body.fi-panel-portal .cxp-rankings-filter strong {
  font-size: 1.75rem;
  line-height: 1;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rankings-filters-card {
  overflow: hidden;
}

/* Adir, 2026-08-31: "convert to horizontal, this is the default way of
   doing navigations at this point." Removed the CYT-527 "Drawbridge"
   vertical-rail layout for GBP sub-page tabs (this block + two later
   !important reinforcements, plus their @media companions) - the page now
   falls through to the standard .cxp-map-tabs.cxp-map-tabs horizontal grid
   (see "TAB STRIPS FIT" below) used by every other tabbed page. */
body.fi-panel-portal .cxp-manual-note {
  display: block;
  max-width: 32rem;
}

/* Dense evidence cards carry the label, value, and one short supporting line;
   long explanations stay available in the underlying page copy without
   making the overview feel like a wall of prose. */
body.fi-panel-portal .cxp-work-evidence-grid {
  gap: 0;
}
body.fi-panel-portal .cxp-work-evidence {
  gap: .2rem;
  padding: .85rem 1rem;
}
body.fi-panel-portal .cxp-work-evidence > span {
  color: var(--portal-primary);
}
body.fi-panel-portal .cxp-work-evidence strong,
body.fi-panel-portal .cxp-work-evidence small {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
body.fi-panel-portal .cxp-health-issue-grid {
  gap: .75rem;
  padding: 1rem 1.15rem;
  align-items: start;
}
body.fi-panel-portal .cxp-health-issue {
  gap: .25rem;
  padding: .85rem;
}
body.fi-panel-portal .cxp-health-issue strong {
  font-size: 1.8rem;
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-topbar-context {
    flex-basis: auto;
    margin-inline: .65rem auto;
  }
  body.fi-panel-portal .cxp-rankings-filter-tabs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-rankings-filter:last-child {
    grid-column: span 2;
  }
  body.fi-panel-portal .cxp-rankings-chart-points {
    left: 5.2rem;
    right: .75rem;
  }
}

body.fi-panel-portal .cxp-chart-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem;
}

body.fi-panel-portal .cxp-plan-card .kt-card-content {
  min-height: 12rem;
  padding: 1.35rem 1.25rem !important;
}

body.fi-panel-portal .cxp-plan-name {
  margin: 0;
  color: var(--portal-heading);
  font-size: 1.2rem;
  font-weight: 700;
}

body.fi-panel-portal .cxp-plan-status {
  display: inline-flex;
  margin-left: .35rem;
  padding: .25rem .55rem;
  border-radius: 999px;
  color: var(--cxp-good-strong);
  background: var(--portal-success-soft);
  font-size: .75rem;
  font-weight: 700;
}

body.fi-panel-portal .cxp-plan-next {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--portal-border);
}

body.fi-panel-portal .cxp-plan-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: 0 0 auto;
  border-radius: 999px;
  color: var(--portal-primary);
  background: var(--portal-primary-soft);
}

body.fi-panel-portal .cxp-activity .kt-card-header {
  align-items: flex-start !important;
}

body.fi-panel-portal .cxp-activity .kt-card-title,
body.fi-panel-portal .cxp-portal-chart .kt-card-title,
body.fi-panel-portal .cxp-plan-name {
  font-size: 1.35rem !important;
  line-height: 1.2 !important;
  letter-spacing: -.025em !important;
}

body.fi-panel-portal .cxp-activity-status {
  padding: .35rem .65rem;
  border-radius: 999px;
  color: var(--cxp-good-strong);
  background: var(--portal-success-soft);
  font-size: .75rem;
  font-weight: 700;
  white-space: nowrap;
}

body.fi-panel-portal .cxp-activity-content {
  padding: 1rem 1.25rem !important;
  max-height: 40rem;
  overflow-y: auto;
}

body.fi-panel-portal .cxp-activity-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}

body.fi-panel-portal .cxp-activity-table td {
  padding: .65rem 0 !important;
  vertical-align: top;
  border: 0 !important;
}

body.fi-panel-portal .cxp-activity-day td {
  padding: .35rem 0 .45rem !important;
  color: var(--portal-muted) !important;
  font-size: .68rem !important;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}

body.fi-panel-portal .cxp-activity-marker {
  position: relative;
  width: 2rem;
  padding-right: 1rem !important;
  vertical-align: middle !important;
}

body.fi-panel-portal .cxp-activity-marker::before {
  content: "";
  position: absolute;
  top: 50%;
  left: .25rem;
  width: .75rem;
  height: .75rem;
  border: 2px solid #cbd5e1;
  border-radius: 50%;
  background: var(--portal-surface);
  box-sizing: border-box;
  z-index: 1;
  transform: translateY(-50%);
}

body.fi-panel-portal .cxp-activity-marker--completed::before,
body.fi-panel-portal .cxp-activity-marker--resolved::before {
  border-color: var(--cxp-good-text);
  background: var(--cxp-good-tint);
}

body.fi-panel-portal .cxp-activity-marker--measurement::before {
  border-color: var(--portal-primary);
  background: var(--portal-primary-soft);
}

body.fi-panel-portal .cxp-activity-marker--recovery-update::before {
  border-color: #d97706;
  background: var(--cxp-warn-tint);
}

body.fi-panel-portal .cxp-activity-row:not(:last-child) .cxp-activity-marker::after {
  content: "";
  position: absolute;
  top: 50%;
  bottom: -1rem;
  left: .6rem;
  width: 2px;
  background: #e2e8f0;
  z-index: 0;
}

body.fi-panel-portal .cxp-activity-title {
  color: var(--portal-heading);
  font-size: .98rem;
  font-weight: 600;
  line-height: 1.45;
}

body.fi-panel-portal .cxp-activity-title a {
  color: var(--portal-primary);
}

body.fi-panel-portal .cxp-activity-time {
  display: block;
  margin-bottom: .2rem;
  color: var(--portal-muted);
  font-size: .75rem;
}

body.fi-panel-portal .cxp-secondary-grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 1rem;
  margin-top: 1rem;
}

body.fi-panel-portal .cxp-portal-page > .kt-card,
body.fi-panel-portal .cxp-portal-page > .cxp-secondary-grid {
  margin-top: 1rem;
}

body.fi-panel-portal .cxp-portal-page > .cxp-header-row,
body.fi-panel-portal .cxp-portal-page > .sb-stat-grid,
body.fi-panel-portal .cxp-portal-page > .cxp-portal-grid {
  margin-top: 0;
}

body.fi-panel-portal .cxp-stat-denominator {
  font-weight: 500;
  letter-spacing: -.02em;
}

@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-portal-grid { grid-template-columns: minmax(0, 1fr); }
  body.fi-panel-portal .cxp-portal-side { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-topbar-context,
  body.fi-panel-portal .cxp-topbar-user-copy { display: none; }
  body.fi-panel-portal .cxp-domain-switcher { min-width: min(100%, 15rem); }
  body.fi-panel-portal .cxp-dashboard-insights { grid-template-columns: minmax(0, 1fr); }
  body.fi-panel-portal .cxp-gauge-row { padding: 1rem; }
  body.fi-panel-portal .cxp-dashboard-columns { grid-template-columns: minmax(0, 1fr); }
  body.fi-panel-portal .fi-main-ctn { padding: 1.25rem 1rem 2rem !important; }
  body.fi-panel-portal .sb-stat-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important; }
  body.fi-panel-portal .cxp-portal-side,
  body.fi-panel-portal .cxp-secondary-grid { grid-template-columns: minmax(0, 1fr); }
  body.fi-panel-portal .cxp-header-row { align-items: flex-start !important; }
  body.fi-panel-portal .cxp-activity-status { width: 100%; text-align: center; }
}

@media (max-width: 480px) {
  body.fi-panel-portal .sb-stat-grid { grid-template-columns: minmax(0, 1fr) !important; }
  body.fi-panel-portal .sb-stat { min-height: 126px !important; }
  body.fi-panel-portal .cxp-chart-toolbar .kt-btn { flex: 1 1 auto; }
}

@media (pointer: coarse), (max-width: 640px) {
  body.fi-panel-portal .kt-btn,
  body.fi-panel-portal .kt-btn-sm { min-height: 36px; }
}

/* The portal topbar is the shell edge, not a floating card. */
body.fi-panel-portal .fi-main-ctn {
  padding: 0 0 3rem !important;
}

body.fi-panel-portal .fi-topbar,
body.fi-panel-portal .fi-topbar > nav {
  top: 0 !important;
  width: 100% !important;
}

/* Portal KPI cards size to their content instead of stretching across a grid row. */
body.fi-panel-portal .sb-stat-grid {
  align-items: start !important;
}

body.fi-panel-portal .sb-stat {
  align-self: start !important;
  height: auto !important;
  min-height: 0 !important;
}

/* Page-level KPI tiles share one bottom edge when copy wraps. */
body.fi-panel-portal .cxp-portal-page > .sb-stat-grid {
  align-items: stretch !important;
}

body.fi-panel-portal .cxp-portal-page > .sb-stat-grid > .sb-stat {
  align-self: stretch !important;
  /* NOT height:100% - stretch does the equalising, and on an auto-sized grid row
     height:100% resolves against a content-sized area (circular) and falls back
     to the item's own height, silently switching the stretch off. Measured:
     with height:100% the row was 164/164/146/146; with height:auto it is
     164/164/164/164. */
  height: auto !important;
}

/* The dashboard insight cards must not equalize their heights. */
body.fi-panel-portal .cxp-dashboard-insights {
  align-items: start !important;
}

/* Round 6 shell and layout consistency. */
body.fi-panel-portal .fi-sidebar-header {
  height: 74px !important;
  min-height: 74px !important;
  padding: 0 1rem !important;
}

body.fi-panel-portal .fi-sidebar-header > div:first-child {
  display: flex !important;
  width: 100% !important;
  height: 100% !important;
  align-items: center !important;
  justify-content: center !important;
}

body.fi-panel-portal .fi-sidebar-header .fi-logo {
  display: flex !important;
  width: 100% !important;
  min-height: 0 !important;
  height: auto !important;
  align-items: center !important;
  justify-content: center !important;
  padding: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
}

body.fi-panel-portal .fi-sidebar-header .fi-logo .cx-brand {
  display: flex !important;
  width: 100% !important;
  min-height: 0 !important;
  align-items: center !important;
  justify-content: center !important;
  box-sizing: border-box;
  padding: .55rem .75rem;
  border-radius: .75rem;
  background: #11182e;
  box-shadow: 0 .4rem 1rem rgb(15 23 42 / .18);
}

body.fi-panel-portal .fi-sidebar-header .fi-logo .cx-brand-logo-img {
  display: block !important;
  width: min(100%, 10rem) !important;
  height: auto !important;
  max-height: none !important;
  aspect-ratio: 160 / 48;
  object-fit: contain;
}

body.fi-panel-portal .cxp-dashboard-insights {
  align-items: stretch !important;
  margin-bottom: 1rem !important;
}

body.fi-panel-portal .cxp-dashboard-left {
  display: flex !important;
  flex-direction: column !important;
  gap: 1rem !important;
  min-width: 0 !important;
}

body.fi-panel-portal .cxp-score-explainer {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  margin: .75rem 1.25rem 1.25rem;
  padding: .7rem .85rem;
  border: 1px solid #e4eaf2;
  border-radius: .65rem;
  color: var(--portal-text);
  background: #f7f9fc;
  font-size: 12px !important;
  line-height: 1.4;
}

body.fi-panel-portal .cxp-score-explainer strong {
  color: var(--portal-heading);
  font-size: 13px !important;
  font-weight: 700;
}

body.fi-panel-portal .cxp-score-explainer span {
  color: var(--portal-muted);
  font-size: 12px !important;
}

/* Every portal page stack uses the same 16px section rhythm. */
body.fi-panel-portal .cxp-portal-page {
  display: flex !important;
  flex-direction: column !important;
  gap: 1rem !important;
}

body.fi-panel-portal .cxp-portal-page > * {
  margin-top: 0 !important;
  min-width: 0 !important;
}

body.fi-panel-portal .cxp-portal-grid,
body.fi-panel-portal .cxp-secondary-grid {
  margin-top: 0 !important;
}

/* Portal tables always occupy the complete card content width. */
body.fi-panel-portal .kt-card-content > .kt-table,
body.fi-panel-portal .kt-card-content > div > .kt-table {
  width: 100% !important;
  max-width: none !important;
  min-width: 100% !important;
}

/* Portal table typography remains larger on the light surface. */
body.fi-panel-portal .kt-table thead th {
  color: var(--portal-heading) !important;
  font-size: 12px !important;
  font-weight: 700 !important;
}

/* CYT-605: badges inside portal tables must wrap so their labels cannot
   widen the table past its card at intermediate viewport widths. */
body.fi-panel-portal .kt-table .kt-badge {
  white-space: normal;
}

body.fi-panel-portal .cxp-table-sort {
  display: inline-flex !important;
  align-items: center !important;
  gap: .3rem !important;
  padding: 0 !important;
  border: 0 !important;
  color: inherit !important;
  background: transparent !important;
  font: inherit !important;
  cursor: pointer !important;
}

body.fi-panel-portal .cxp-table-sort::after {
  content: "↕";
  color: var(--portal-primary);
  font-size: 12px !important;
  line-height: 1;
}

body.fi-panel-portal thead th[aria-sort="ascending"] .cxp-table-sort::after { content: "↑"; }
body.fi-panel-portal thead th[aria-sort="descending"] .cxp-table-sort::after { content: "↓"; }

/* Keep all visible portal copy at a readable 12px floor. */
body.fi-panel-portal .fi-sidebar-group-label,
body.fi-panel-portal .cxp-topbar-greeting,
body.fi-panel-portal .cxp-topbar-user-role,
body.fi-panel-portal .cxp-domain-switcher > span,
body.fi-panel-portal .cxp-gauge-item > div:last-child span,
body.fi-panel-portal .cxp-rank-summary span,
body.fi-panel-portal .cxp-work-row small,
body.fi-panel-portal .cxp-service-row small,
body.fi-panel-portal .cxp-next-work strong,
body.fi-panel-portal .cxp-activity-day td,
body.fi-panel-portal .cxp-activity-time,
body.fi-panel-portal .cxp-meta,
body.fi-panel-portal .cxp-count,
body.fi-panel-portal .sb-stat-caption,
body.fi-panel-portal .kt-badge-sm {
  font-size: 12px !important;
}

/* Keep page-level stat sections from adding a second 16px gap. Wrapped portal
   pages retain the same deliberate section spacing as the outer page grid. */
body.fi-panel-portal .sb-stat-grid {
  margin-bottom: 0 !important;
}

body.fi-panel-portal .cxp-portal-page .sb-stat-grid {
  margin-bottom: 1rem !important;
}

/* Activity tables keep their readable cell padding while the table itself
   fills the card like every other portal table. */
body.fi-panel-portal .cxp-activity-content {
  padding: 0 !important;
}

/* Keyboard users can reach and scroll the live activity region. */
body.fi-panel-portal .cxp-activity-content:focus-visible {
  outline: 3px solid var(--portal-primary);
  outline-offset: 2px;
}

/* The skip link is the first focusable element in the portal document. */
body.fi-panel-portal .cxp-skip-link {
  position: fixed;
  top: .75rem;
  left: .75rem;
  z-index: 100;
  padding: .65rem .9rem;
  border-radius: .4rem;
  color: #fff;
  background: #172033;
  font-weight: 700;
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform .15s ease;
}
body.fi-panel-portal .cxp-skip-link:focus-visible {
  transform: translateY(0);
}
body.fi-panel-portal #main-content:focus {
  outline: none;
}


body.fi-panel-portal .cxp-activity-table td {
  padding-right: 1.25rem !important;
  padding-left: 1.25rem !important;
}

/* The flex gap is the only page-level spacing source. */
body.fi-panel-portal .cxp-portal-page > * {
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}

body.fi-panel-portal .cxp-portal-page .sb-stat-grid {
  margin-bottom: 0 !important;
}

/* Portal brand wordmark - plain dark text, no image, no background plaque.
   Scoped to the portal so admin.cyticx.com keeps its real logo. */
body.fi-panel-portal .cxp-brand-text {
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: .18em;
  color: #172033;
  background: none;
  line-height: 1;
  white-space: nowrap;
}
body.fi-panel-portal .fi-logo,
body.fi-panel-portal .fi-sidebar-header .fi-logo,
body.fi-panel-portal .cx-brand {
  background: none !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  padding: 0 !important;
}
body.fi-panel-portal .fi-sidebar-header {
  justify-content: center;
}

/* Round 10 shared profile, spacing, and truthful blocker presentation. */
body.fi-panel-portal .cxp-profile-layout {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 1rem;
  align-items: start;
}
body.fi-panel-portal .cxp-profile-stack {
  display: grid;
  gap: 1rem;
  align-content: start;
  min-width: 0;
}
body.fi-panel-portal .cxp-blocker-list {
  display: grid;
  gap: 0;
}
body.fi-panel-portal .cxp-blocker-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--portal-border);
}
body.fi-panel-portal .cxp-blocker-row > div {
  display: grid;
  gap: .35rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-blocker-row strong {
  color: var(--portal-heading);
  font-size: .98rem;
}
body.fi-panel-portal .cxp-blocker-row span {
  color: var(--portal-muted);
  font-size: 12px;
  line-height: 1.45;
}
body.fi-panel-portal .cxp-blocker-row .kt-btn {
  flex: 0 0 auto;
}
body.fi-panel-portal .cxp-activity-status--needs-input {
  color: #9a6700;
  background: #fff4cc;
}
body.fi-panel-portal .sb-stat-grid {
  align-items: start !important;
}
body.fi-panel-portal .cxp-portal-page > .sb-stat-grid > .sb-stat {
  align-self: start !important;
  height: auto !important;
}

/* Round 12 layout, semantic colour, and restrained motion. */
body.fi-panel-portal .cxp-profile-form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
  align-items: start;
}
body.fi-panel-portal .cxp-profile-form-grid .kt-btn {
  grid-column: 1 / -1;
  justify-self: start;
}
body.fi-panel-portal .kt-card,
body.fi-panel-portal .kt-btn,
body.fi-panel-portal .cxp-service-row,
body.fi-panel-portal .kt-table tbody tr {
  transition: transform .15s ease, opacity .15s ease;
}
body.fi-panel-portal .kt-card:hover,
body.fi-panel-portal .cxp-service-row:hover,
body.fi-panel-portal .kt-btn:hover {
  transform: translateY(-1px);
}
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-profile-form-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-profile-form-grid .kt-btn {
    grid-column: auto;
  }
}
@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .kt-card,
  body.fi-panel-portal .kt-btn,
  body.fi-panel-portal .cxp-service-row,
  body.fi-panel-portal .kt-table tbody tr {
    transition: none !important;
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}
body.fi-panel-portal .cxp-dashboard-insights {
  align-items: start !important;
}
body.fi-panel-portal .cxp-empty {
  padding: 1rem 1.25rem !important;
}
body.fi-panel-portal .cxp-empty-detail {
  display: block;
  margin-top: .35rem;
}
body.fi-panel-portal .cxp-plan-card .kt-card-content {
  min-height: auto !important;
  padding: 1rem 1.25rem !important;
}
body.fi-panel-portal .cxp-score-card .cxp-score-explainer {
  margin-bottom: 10px !important;
}
body.fi-panel-portal .cxp-work-card .cxp-next-work {
  margin-bottom: 8px !important;
}
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-profile-layout {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-blocker-row {
    align-items: stretch;
    flex-direction: column;
  }
  body.fi-panel-portal .cxp-blocker-row .kt-btn {
    width: 100%;
  }
}

/* TASK-32: tiles sharing a grid row also share one bottom edge. */
body.fi-panel-portal .sb-stat-grid,
body.fi-panel-portal .cxp-portal-page > .sb-stat-grid {
  align-items: stretch !important;
}

body.fi-panel-portal .sb-stat-grid > .sb-stat,
body.fi-panel-portal .cxp-portal-page > .sb-stat-grid > .sb-stat {
  align-self: stretch !important;
  height: 100% !important;
  min-height: 0 !important;
}

/* ---- Website Health summary ring -------------------------------------
   The health page shipped with NO rules for these classes, so the SVG had
   no size constraint (rendered 1114px wide) and its circles fell back to
   SVG's default fill:black - a giant black disc covering the page.
   Fixed 2026-07-30. */
body.fi-panel-portal .cxp-health-summary {
  display: flex;
  align-items: center;
  gap: 1.75rem;
  flex-wrap: wrap;
}
body.fi-panel-portal .cxp-health-summary-ring {
  position: relative;
  flex: 0 0 7.5rem;
  width: 7.5rem;
  height: 7.5rem;
}
body.fi-panel-portal .cxp-health-summary-ring svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
  display: block;
}
body.fi-panel-portal .cxp-health-summary-ring circle {
  fill: none;
  stroke-width: 7;
}
body.fi-panel-portal .cxp-health-summary-ring .cxp-gauge-track { stroke: #e9eef6; }
body.fi-panel-portal .cxp-health-summary-ring .cxp-gauge-progress {
  stroke-linecap: round;
  transition: stroke-dasharray 1.1s cubic-bezier(.22,.61,.36,1);
}
body.fi-panel-portal .cxp-health-summary-ring .cxp-score-number {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.02em;
}
body.fi-panel-portal .cxp-health-summary-copy {
  display: flex;
  flex-direction: column;
  gap: .4rem;
  min-width: 14rem;
  flex: 1 1 18rem;
}
body.fi-panel-portal .cxp-health-summary-copy .cxp-score-label {
  font-size: 1.05rem;
  font-weight: 700;
}

/* Round 15: shared structure for the Website Health and AI visibility pages.
   The health-summary ring is defined separately at the end of cxp.css. */
body.fi-panel-portal .cxp-health-page,
body.fi-panel-portal .cxp-ai-page {
  display: grid;
  gap: 1rem;
  align-content: start;
  min-width: 0;
}

body.fi-panel-portal .cxp-health-factors {
  display: grid;
  gap: 0;
  min-width: 0;
}
body.fi-panel-portal .cxp-health-factor {
  display: grid;
  gap: .7rem;
  padding: 1rem 1.25rem;
  border-top: 1px solid var(--portal-border);
  min-width: 0;
}
body.fi-panel-portal .cxp-health-factor:nth-child(odd) { background: #fff; }
body.fi-panel-portal .cxp-health-factor:nth-child(even) { background: #f8fafc; }
body.fi-panel-portal .cxp-health-factor-top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: 1rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-health-factor-top > div {
  display: grid;
  gap: .3rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-health-factor-top > div > span {
  color: var(--portal-muted);
  font-size: .8rem;
  line-height: 1.45;
}
body.fi-panel-portal .cxp-health-factor-top > b {
  color: var(--portal-heading);
  font-size: .85rem;
  white-space: nowrap;
  text-align: right;
}
body.fi-panel-portal .cxp-health-factor p {
  margin: 0;
  color: var(--portal-muted);
  font-size: .8rem;
  line-height: 1.45;
}
body.fi-panel-portal .cxp-health-factor p strong {
  color: var(--portal-heading);
}

body.fi-panel-portal .cxp-health-progress {
  height: 8px;
  margin: 0;
  overflow: hidden;
  border-radius: 999px;
  background: #e9eef6;
}
body.fi-panel-portal .cxp-health-progress-fill {
  display: block;
  height: 100%;
  min-width: 0;
  border-radius: inherit;
  transition: width 1.1s cubic-bezier(.22,.61,.36,1);
}
body.fi-panel-portal .cxp-fill-good { background: var(--cxp-good); }
body.fi-panel-portal .cxp-fill-middle { background: var(--cxp-warn); }
body.fi-panel-portal .cxp-fill-poor { background: var(--cxp-bad); }
body.fi-panel-portal .cxp-fill-neutral { background: var(--portal-primary-fill); }

body.fi-panel-portal .cxp-score-good { color: var(--cxp-good-text) !important; }
body.fi-panel-portal .cxp-score-middle { color: var(--cxp-warn-text) !important; }
body.fi-panel-portal .cxp-score-poor { color: var(--cxp-bad-text) !important; }
body.fi-panel-portal .cxp-score-neutral { color: var(--portal-heading) !important; }
body.fi-panel-portal .cxp-score-label.cxp-score-good { color: var(--cxp-good-strong) !important; }
body.fi-panel-portal .cxp-score-label.cxp-score-middle { color: var(--cxp-warn-strong) !important; }
body.fi-panel-portal .cxp-score-label.cxp-score-poor { color: var(--cxp-bad-strong) !important; }
body.fi-panel-portal .cxp-gauge-good { stroke: var(--cxp-good) !important; }
body.fi-panel-portal .cxp-gauge-middle { stroke: var(--cxp-warn) !important; }
body.fi-panel-portal .cxp-gauge-poor { stroke: var(--cxp-bad) !important; }

body.fi-panel-portal .cxp-health-issue-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, max-content));
  justify-content: start;
  gap: 1rem;
  padding: 1.25rem;
}
body.fi-panel-portal .cxp-health-issue {
  display: grid;
  gap: .35rem;
  padding: 1rem;
  border: 1px solid var(--portal-border);
  border-radius: .75rem;
  background: var(--portal-surface-soft);
  min-width: 0;
}
body.fi-panel-portal .cxp-health-issue strong {
  font-size: 1.4rem;
  line-height: 1;
}
body.fi-panel-portal .cxp-health-issue:nth-child(1) strong { color: var(--cxp-bad-text); }
body.fi-panel-portal .cxp-health-issue:nth-child(2) strong { color: var(--cxp-warn-text); }
body.fi-panel-portal .cxp-health-issue:nth-child(3) strong { color: var(--cxp-good-text); }
body.fi-panel-portal .cxp-health-issue span {
  color: var(--portal-muted);
  font-size: .8rem;
  line-height: 1.4;
}

body.fi-panel-portal .cxp-ai-summary {
  display: grid;
  grid-template-columns: 10rem minmax(0, 1fr);
  align-items: center;
  gap: 1.25rem;
  padding: 1.25rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-score {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: .35rem;
  min-height: 7.5rem;
  border-right: 1px solid var(--portal-border);
}
body.fi-panel-portal .cxp-ai-score strong {
  font-size: 2.4rem;
  line-height: 1;
}
body.fi-panel-portal .cxp-ai-score span {
  color: var(--portal-muted);
  font-size: .8rem;
}
body.fi-panel-portal .cxp-ai-explanation {
  max-width: 42rem;
  margin: .5rem 0;
  color: var(--portal-muted);
  font-size: .85rem;
  line-height: 1.5;
}
body.fi-panel-portal .cxp-ai-stat-grid,
body.fi-panel-portal .cxp-ai-subscore-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-stat {
  display: grid;
  align-content: start;
  gap: .35rem;
  padding: 1.25rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-stat > strong {
  color: var(--portal-heading);
  font-size: 1.7rem;
  line-height: 1.1;
}
body.fi-panel-portal .cxp-ai-subscore-grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
body.fi-panel-portal .cxp-ai-subscore {
  display: grid;
  align-content: start;
  gap: .5rem;
  padding: 1.25rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-subscore-top {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: .75rem;
}
body.fi-panel-portal .cxp-ai-subscore-top b {
  font-size: 1.25rem;
  line-height: 1;
}

@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-health-progress-fill {
    transition: none !important;
  }
}
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-health-factor-top,
  body.fi-panel-portal .cxp-ai-summary {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-health-factor-top > b {
    text-align: left;
  }
  body.fi-panel-portal .cxp-ai-stat-grid,
  body.fi-panel-portal .cxp-ai-subscore-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  /* cxp-health-issue-grid deliberately NOT forced to 1fr here - it stays
     content-hugging (see the base rule) so the severity tiles don't stretch
     into near-empty full-width boxes on mobile. */
  body.fi-panel-portal .cxp-ai-score {
    min-height: 0;
    padding-bottom: 1rem;
    border-right: 0;
    border-bottom: 1px solid var(--portal-border);
  }
}


/* Fix 2026-07-30: .kt-card sets flex-direction:column, so the summary's
   `display:flex` alone left the ring and copy STACKED, and `flex:1 1 18rem`
   grew the copy VERTICALLY (288px tall for ~80px of text) - producing both a
   250px dead band and 326px of unused width. Force row direction explicitly. */
body.fi-panel-portal .cxp-health-summary {
  flex-direction: row !important;
  justify-content: flex-start;
  align-items: center;
}
body.fi-panel-portal .cxp-health-summary-copy {
  flex: 0 1 auto;
  align-self: center;
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-health-summary {
    flex-direction: column !important;
    align-items: flex-start;
  }
}

/* Round 16: one vertical rhythm and content-sized portal columns. */
body.fi-panel-portal .cxp-dashboard-page,
body.fi-panel-portal .cxp-autopilot-page {
  gap: 1rem !important;
}
body.fi-panel-portal .cxp-dashboard-page > *,
body.fi-panel-portal .cxp-autopilot-page > * {
  margin-top: 0 !important;
  margin-bottom: 0 !important;
}
body.fi-panel-portal .cxp-dashboard-page > .cxp-header-row,
body.fi-panel-portal .cxp-autopilot-page > .cxp-header-row {
  margin-bottom: 1.25rem !important;
  padding-bottom: 1.1rem;
  border-bottom: 1px solid var(--portal-border);
}
body.fi-panel-portal .cxp-dashboard-insights,
body.fi-panel-portal .cxp-dashboard-columns {
  align-items: start !important;
  margin-block: 0 !important;
}
body.fi-panel-portal .cxp-dashboard-insights > *,
body.fi-panel-portal .cxp-dashboard-columns > *,
body.fi-panel-portal .cxp-dashboard-left > *,
body.fi-panel-portal .cxp-dashboard-right > * {
  align-self: start !important;
  height: auto !important;
  min-height: 0 !important;
}
body.fi-panel-portal .cxp-dashboard-right {
  align-self: start !important;
  justify-content: flex-start !important;
  height: auto !important;
  min-height: 0 !important;
  gap: 1rem !important;
}
body.fi-panel-portal .cxp-dashboard-left {
  align-self: start !important;
  height: auto !important;
  min-height: 0 !important;
  gap: 1rem !important;
}
body.fi-panel-portal .cxp-dashboard-page .sb-stat-grid,
body.fi-panel-portal .cxp-autopilot-page .sb-stat-grid {
  align-items: start !important;
  margin-block: 0 !important;
}
body.fi-panel-portal .cxp-dashboard-page .sb-stat-grid > .sb-stat,
body.fi-panel-portal .cxp-autopilot-page .sb-stat-grid > .sb-stat,
body.fi-panel-portal .cxp-portal-page > .sb-stat-grid > .sb-stat {
  align-self: start !important;
  height: auto !important;
}

/* The amber status remains readable on its pale surface. */
body.fi-panel-portal .cxp-autopilot-page .cxp-activity-status--needs-input,
body.fi-panel-portal .cxp-autopilot-page .kt-badge-warning {
  color: var(--cxp-warn-strong) !important;
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-dashboard-right,
  body.fi-panel-portal .cxp-dashboard-left {
    gap: 1rem !important;
  }
}

/* Round 16 contrast correction: amber status text must clear WCAG AA. */
body.fi-panel-portal .cxp-autopilot-page .cxp-activity-status--needs-input,
body.fi-panel-portal .cxp-autopilot-page .kt-badge-warning {
  color: var(--cxp-warn-strong) !important;
}

/* Fix 2026-07-30: .cxp-dashboard-left / -right are flex columns whose children
   were sizing to their own content, giving three different card widths in the
   same column (Account 349px, Available plans 600px, Recent invoices 445px) and
   leaving a dead middle band. Stretch every card to the column width so the
   vertical rhythm and edges line up. */
body.fi-panel-portal .cxp-dashboard-left,
body.fi-panel-portal .cxp-dashboard-right {
  align-items: stretch;
  gap: 16px;
}
body.fi-panel-portal .cxp-dashboard-left > *,
body.fi-panel-portal .cxp-dashboard-right > * {
  width: 100%;
  max-width: 100%;
  align-self: stretch;
}

/* Fix 2026-07-30: KPI stat cards stretch to equal height (correct), but their
   content was top-aligned so the slack collected as a dead band at the bottom
   (39px on Billing's Current plan / Paid to date / Open balance, while the
   taller "Next payment" had only 21px). Pin the caption to the bottom so the
   space is absorbed AND captions line up across the row. */
body.fi-panel-portal .sb-stat {
  justify-content: flex-start;
}
body.fi-panel-portal .sb-stat .sb-stat-caption {
  margin-top: auto;
  padding-top: .5rem;
}

/* User request 2026-07-30: "it has a line underneath that ... it just doesn't
   look right. So I think you should remove it. And the dot is kind of touching
   the frame on the left, so it seems to have more space."
   1. Kill the vertical connector line between timeline dots entirely.
   2. Push the dot off the card's left edge. */
body.fi-panel-portal .cxp-activity-row .cxp-activity-marker::after {
  content: none !important;
  display: none !important;
}
body.fi-panel-portal .cxp-activity-marker {
  width: 2.75rem;
  padding-left: 1.1rem !important;
  padding-right: 1rem !important;
}
body.fi-panel-portal .cxp-activity-marker::before {
  left: 1.1rem;
}

/* User request 2026-07-30: the plan card only said "Autopilot SEO - your SEO
   work is monitored and managed for you", which tells the customer nothing.
   Expanded into a real explanation of the service; these are its styles. */
body.fi-panel-portal .cxp-plan-points {
  list-style: none;
  margin: .9rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .6rem;
}
body.fi-panel-portal .cxp-plan-points li {
  position: relative;
  padding-left: 1.4rem;
  font-size: .875rem;
  line-height: 1.5;
  color: #46536b;
}
body.fi-panel-portal .cxp-plan-points li::before {
  content: "";
  position: absolute;
  left: .25rem;
  top: .5rem;
  width: .45rem;
  height: .45rem;
  border-radius: 50%;
  background: var(--cxp-good, #00A651);
}
body.fi-panel-portal .cxp-plan-points li strong {
  color: #172033;
  font-weight: 650;
}


/* Round 17 Part A: blocker reply modal and one 16px Autopilot rhythm. */
body.fi-panel-portal .cxp-blocker-trigger {
  border: 0;
  cursor: pointer;
  font: inherit;
}
body.fi-panel-portal .cxp-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: rgb(15 23 42 / .44);
}
body.fi-panel-portal .cxp-modal {
  width: min(100%, 42rem);
  max-height: min(90vh, 48rem);
  overflow: auto;
  padding: 1.25rem !important;
  background: var(--portal-surface);
  box-shadow: 0 1rem 3rem rgb(15 23 42 / .24);
}
body.fi-panel-portal .cxp-modal-header,
body.fi-panel-portal .cxp-reply-actions {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}
body.fi-panel-portal .cxp-modal-header { margin-bottom: 1rem; }
body.fi-panel-portal .cxp-modal-header .kt-card-title { margin: 0; }
body.fi-panel-portal .cxp-modal-close {
  width: 2rem;
  height: 2rem;
  border: 0;
  border-radius: .5rem;
  color: var(--portal-muted);
  background: var(--portal-border);
  cursor: pointer;
  font-size: 1.35rem;
  line-height: 1;
}
body.fi-panel-portal .cxp-modal-notice {
  margin-bottom: 1rem;
  padding: .75rem 1rem;
  border-radius: .6rem;
  font-size: 13px;
}
body.fi-panel-portal .cxp-modal-notice--success { color: var(--cxp-good-strong); background: var(--cxp-good-tint); }
body.fi-panel-portal .cxp-modal-notice--error { color: var(--cxp-bad-strong); background: var(--cxp-bad-tint); }
body.fi-panel-portal .cxp-blocker-details { display: grid; gap: .75rem; }
body.fi-panel-portal .cxp-blocker-detail {
  padding: 1rem;
  border: 1px solid var(--portal-border);
  border-radius: .7rem;
  background: var(--portal-surface-soft);
}
body.fi-panel-portal .cxp-blocker-detail.is-active { background: var(--portal-surface); border-color: var(--portal-primary); }
body.fi-panel-portal .cxp-blocker-detail-title {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  color: var(--portal-heading);
  background: transparent;
  cursor: pointer;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  text-align: left;
}
body.fi-panel-portal .cxp-blocker-explanation { display: grid; gap: .4rem; margin-top: .8rem; }
body.fi-panel-portal .cxp-blocker-explanation p { margin: 0; color: var(--portal-text); font-size: 13px; line-height: 1.5; }
body.fi-panel-portal .cxp-reply-thread { display: grid; gap: .5rem; margin-top: 1rem; }
body.fi-panel-portal .cxp-reply-message { padding: .65rem .75rem; border-radius: .55rem; background: var(--portal-primary-soft); }
body.fi-panel-portal .cxp-reply-author { color: var(--portal-heading); font-size: 12px; font-weight: 700; }
body.fi-panel-portal .cxp-reply-time { margin-left: .5rem; color: var(--portal-muted); font-size: 12px; }
body.fi-panel-portal .cxp-reply-message p { margin: .25rem 0 0; color: var(--portal-text); font-size: 13px; line-height: 1.45; white-space: pre-wrap; }
body.fi-panel-portal .cxp-reply-label { display: block; margin-top: 1rem; color: var(--portal-heading); font-size: 13px; font-weight: 700; }
body.fi-panel-portal .cxp-reply-input { display: block; width: 100%; min-height: 6rem; margin-top: .4rem; padding: .7rem .75rem; border: 1px solid var(--portal-border); border-radius: .55rem; color: var(--portal-text); background: var(--portal-surface); font: inherit; font-size: 13px; resize: vertical; }
body.fi-panel-portal .cxp-reply-actions { align-items: center; margin-top: .6rem; }
body.fi-panel-portal .cxp-reply-composer {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: .75rem;
  width: 100%;
  max-width: 44rem;
}
body.fi-panel-portal .cxp-reply-composer .cxp-reply-input { min-width: 0; }
body.fi-panel-portal .cxp-reply-composer .kt-btn { min-height: 40px; }
body.fi-panel-portal .cxp-reply-help {
  display: block;
  max-width: 44rem;
  margin-top: .55rem;
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-reply-composer { grid-template-columns: minmax(0, 1fr); }
  body.fi-panel-portal .cxp-reply-composer .kt-btn { justify-self: start; }
}
body.fi-panel-portal .cxp-autopilot-page > .cxp-portal-grid,
body.fi-panel-portal .cxp-autopilot-page > .cxp-secondary-grid,
body.fi-panel-portal .cxp-autopilot-page .cxp-portal-side { gap: 1rem !important; }
body.fi-panel-portal .cxp-autopilot-page > .cxp-secondary-grid,
body.fi-panel-portal .cxp-autopilot-page > .kt-card { margin-top: 0 !important; }
@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-modal-backdrop,
  body.fi-panel-portal .cxp-modal { scroll-behavior: auto !important; }
}
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-modal { padding: 1rem !important; }
  body.fi-panel-portal .cxp-modal-header { gap: .5rem; }
}

/* ============================================================
   SINGLE SOURCE OF TRUTH FOR PORTAL COLOUR - added 2026-07-30.
   User: "sometimes you have one green and sometimes you have a
   different green, so that needs to be fixed."
   The file had FIVE greens (#087f5b, #00a651, #00e676, #16a34a,
   #007a3d), THREE reds and FOUR ambers for the same meanings.
   Every semantic colour now resolves through these tokens.
   -*-graphic  = bars, gauge strokes, fills (3:1 rule applies)
   -*-text     = large numbers/labels
   -*-strong   = small body text (needs 4.5:1 on white)
   ============================================================ */
body.fi-panel-portal {
  --cxp-good:        #00E676;
  --cxp-good-text:   #00A651;
  --cxp-good-strong: #007A3D;
  --cxp-good-tint:   #EAFAF3;
  --cxp-good-line:   #B7EBD3;

  --cxp-warn:        #FFB020;
  --cxp-warn-text:   #B26A00;
  --cxp-warn-strong: #8A4B00;
  --cxp-warn-tint:   #FFF7E6;

  --cxp-bad:         #FF4D4F;
  --cxp-bad-text:    #D92D20;
  --cxp-bad-strong:  #B42318;
  --cxp-bad-tint:    #FEECEC;

  --portal-success:  var(--cxp-good-strong);
  --portal-warning:  var(--cxp-warn-text);
}
/* "Needs your input" badge was #B26A00 on #FFF7E6 at 12px/500 = 3.98:1, under
   the 4.5:1 required for normal-size text. Use the strong warn token (~7.9:1).
   Same for the good/bad small badges so the whole set stays legible. */
body.fi-panel-portal .kt-badge {
  font-weight: 600;
}
body.fi-panel-portal .kt-badge[class*="warning"],
body.fi-panel-portal .cxp-badge-warn,
body.fi-panel-portal .kt-badge-outline {
  color: var(--cxp-warn-strong) !important;
}
body.fi-panel-portal .kt-badge[class*="success"],
body.fi-panel-portal .cxp-badge-good {
  color: var(--cxp-good-strong) !important;
}
body.fi-panel-portal .kt-badge[class*="danger"],
body.fi-panel-portal .cxp-badge-bad {
  color: var(--cxp-bad-strong) !important;
}

/* Interim mitigation 2026-07-30: the expanded plan card made the right column
   tall enough to open a 401px hole in the single-row .cxp-portal-grid (see the
   structural fix task). Tighten the bullet list so the right column is shorter
   until the columns are restructured to pack independently. */
body.fi-panel-portal .cxp-plan-points {
  gap: .4rem;
  margin-top: .7rem;
}
body.fi-panel-portal .cxp-plan-points li {
  font-size: .8125rem;
  line-height: 1.4;
  padding-left: 1.2rem;
}


/* Round 17 Part A layout: keep each Autopilot column independently stacked so
   a long plan cannot create a false gap beneath Recent updates. */
body.fi-panel-portal .cxp-autopilot-page .cxp-portal-left {
  display: grid;
  align-content: start;
  gap: 1rem !important;
  min-width: 0;
}
body.fi-panel-portal .cxp-autopilot-page .cxp-portal-left > * {
  min-width: 0;
  margin: 0 !important;
}
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-autopilot-page .cxp-portal-left { gap: 1rem !important; }
}

/* Round 19: Rankings page. Every custom selector here has a matching rule. */
body.fi-panel-portal .cxp-rankings-page {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-rankings-head {
  align-items: flex-end;
}
body.fi-panel-portal .cxp-rankings-domain-field {
  display: flex;
  min-width: 12rem;
  flex-direction: column;
  gap: .35rem;
  color: var(--portal-muted);
  font-size: .75rem;
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-domain-field select {
  min-height: 2.25rem;
  padding: .35rem .65rem;
  border: 1px solid var(--portal-border);
  border-radius: .45rem;
  color: var(--portal-text);
  background: var(--portal-surface);
}
body.fi-panel-portal .cxp-rankings-note {
  display: flex;
  align-items: flex-start;
  gap: .65rem;
  padding: .75rem .9rem;
  border: 1px solid var(--portal-border);
  border-radius: .65rem;
  color: var(--portal-muted);
  background: var(--portal-surface-soft);
  font-size: .8rem;
  line-height: 1.5;
}
body.fi-panel-portal .cxp-rankings-note-icon {
  flex: 0 0 auto;
  color: var(--portal-primary);
  font-size: 1rem;
}
body.fi-panel-portal .cxp-rankings-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1rem;
}
body.fi-panel-portal .cxp-rankings-stat {
  display: flex;
  min-width: 0;
  min-height: 9.25rem;
  flex-direction: column;
  gap: .35rem;
  /* This body-tag-prefixed rule was the actual winner over both
     components.css's and pages.css's 1.25rem attempts (higher specificity
     via the bare `body` element selector) - 1rem 1.1rem measured
     16px/17.6px, under the 20px Style Book gutter. */
  padding: 1.25rem;
}
body.fi-panel-portal .cxp-rankings-stat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
}
body.fi-panel-portal .cxp-rankings-sparkline {
  width: 5rem;
  height: 1.4rem;
  overflow: visible;
}
body.fi-panel-portal .cxp-rankings-sparkline path {
  fill: none;
  stroke: var(--portal-primary);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}
body.fi-panel-portal .cxp-rankings-stat-value {
  color: var(--portal-heading);
  font-size: 1.7rem;
  line-height: 1.1;
}
body.fi-panel-portal .cxp-rankings-stat-delta {
  min-height: 1.2rem;
  font-size: .78rem;
  font-weight: 700;
}
body.fi-panel-portal .cxp-rankings-stat-caption {
  margin-top: auto;
  color: var(--portal-muted);
  font-size: .75rem;
  line-height: 1.35;
}
body.fi-panel-portal .cxp-rankings-tone-good { color: var(--cxp-good-strong); }
body.fi-panel-portal .cxp-rankings-tone-bad { color: var(--cxp-bad-strong); }
body.fi-panel-portal .cxp-rankings-tone-neutral { color: var(--portal-muted); }
body.fi-panel-portal .cxp-rankings-main-grid,
body.fi-panel-portal .cxp-rankings-secondary-grid {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: 1rem;
  align-items: start;
}
body.fi-panel-portal .cxp-rankings-card {
  min-width: 0;
  overflow: hidden;
}
body.fi-panel-portal .cxp-rankings-trend-card,
body.fi-panel-portal .cxp-rankings-bucket-card {
  min-height: 22rem;
}
body.fi-panel-portal .cxp-rankings-chart-wrap {
  position: relative;
  display: grid;
  min-height: 11rem;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  grid-template-rows: 1fr 1fr;
  align-items: center;
  padding: 1.1rem 1.1rem .5rem;
}
body.fi-panel-portal .cxp-rankings-chart {
  width: 100%;
  height: 9.5rem;
  grid-column: 2;
  grid-row: 1 / span 2;
  overflow: visible;
}
body.fi-panel-portal .cxp-rankings-grid-line {
  stroke: var(--portal-border);
  stroke-width: 1;
}
body.fi-panel-portal .cxp-rankings-chart-line {
  fill: none;
  stroke: var(--portal-primary);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 3;
}
body.fi-panel-portal .cxp-rankings-chart-dot {
  fill: var(--portal-primary);
  stroke: var(--portal-surface);
  stroke-width: 2;
}
body.fi-panel-portal .cxp-rankings-y-label {
  color: var(--portal-muted);
  font-size: .75rem;
  line-height: 1.2;
}
body.fi-panel-portal .cxp-rankings-y-label-top { align-self: start; }
body.fi-panel-portal .cxp-rankings-y-label-bottom { align-self: end; }
body.fi-panel-portal .cxp-rankings-chart-labels {
  display: flex;
  justify-content: space-between;
  gap: .5rem;
  padding: 0 1.1rem 1rem 6.6rem;
  color: var(--portal-muted);
  font-size: .75rem;
}
body.fi-panel-portal .cxp-rankings-chart-labels span {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  min-width: 0;
  text-align: center;
}
body.fi-panel-portal .cxp-rankings-chart-labels strong {
  color: var(--portal-heading);
  font-size: .75rem;
}
body.fi-panel-portal .cxp-rankings-chart-labels small {
  color: var(--portal-muted);
  font-size: .75rem;
}
body.fi-panel-portal .cxp-rankings-inline-empty {
  padding: 2rem 1.1rem;
  color: var(--portal-muted);
  font-size: .85rem;
}
body.fi-panel-portal .cxp-rankings-bucket-scroll {
  width: 100%;
  max-width: 100%;
  min-width: 0;
  overflow-x: visible;
}
body.fi-panel-portal .cxp-rankings-table-scroll {
  min-width: 0;
  overflow-x: auto;
}
body.fi-panel-portal .cxp-rankings-bucket-table {
  width: 100%;
  max-width: 100%;
  min-width: 0;
  table-layout: fixed;
}
body.fi-panel-portal .cxp-rankings-bucket-table th,
body.fi-panel-portal .cxp-rankings-bucket-table td {
  min-width: 0;
  padding: .6rem .55rem;
  font-size: .75rem;
  text-align: left;
  vertical-align: middle;
  white-space: normal;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-rankings-bucket-table thead th:first-child,
body.fi-panel-portal .cxp-rankings-bucket-table tbody th {
  width: 27%;
}
body.fi-panel-portal .cxp-rankings-bucket-table thead th:not(:first-child),
body.fi-panel-portal .cxp-rankings-bucket-table tbody td {
  width: 14.6%;
  text-align: center;
}
body.fi-panel-portal .cxp-rankings-status {
  font-size: .75rem;
  font-weight: 750;
}
body.fi-panel-portal .cxp-rankings-bucket-table td strong,
body.fi-panel-portal .cxp-rankings-bucket-table td small {
  display: block;
}
body.fi-panel-portal .cxp-rankings-bucket-table td small {
  margin-top: .2rem;
  color: var(--portal-muted);
  font-size: .75rem;
}
body.fi-panel-portal .cxp-rankings-bar {
  display: block;
  width: 100%;
  height: .3rem;
  margin-top: .35rem;
  overflow: hidden;
  border-radius: 999px;
  background: var(--portal-surface-soft);
}
body.fi-panel-portal .cxp-rankings-bar-fill {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(var(--bar-scale, 0));
  transform-origin: left center;
  border-radius: inherit;
}
body.fi-panel-portal .cxp-rankings-tone-fill-jumped { background: var(--cxp-good); }
body.fi-panel-portal .cxp-rankings-tone-fill-dropped { background: var(--cxp-bad); }
body.fi-panel-portal .cxp-rankings-tone-fill-unchanged { background: var(--cxp-warn); }
body.fi-panel-portal .cxp-rankings-filter-tabs {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: .65rem;
  padding: 1rem 1.1rem 1.1rem;
}
body.fi-panel-portal .cxp-rankings-filter {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: .2rem;
  padding: .75rem;
  border: 1px solid var(--portal-border);
  border-radius: .55rem;
  background: var(--portal-surface-soft);
}
body.fi-panel-portal .cxp-rankings-filter span {
  color: var(--portal-muted);
  font-size: .75rem;
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-filter strong {
  color: var(--portal-heading);
  font-size: 1.25rem;
}
body.fi-panel-portal .cxp-rankings-filter small {
  font-size: .75rem;
  font-weight: 700;
}
body.fi-panel-portal .cxp-rankings-overview-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  padding: 1rem 1.1rem 1.1rem;
}
body.fi-panel-portal .cxp-rankings-overview-list {
  min-width: 0;
}
body.fi-panel-portal .cxp-rankings-overview-list h3 {
  margin: 0 0 .45rem;
  color: var(--portal-muted);
  font-size: .75rem;
  font-weight: 750;
  letter-spacing: .04em;
  text-transform: uppercase;
}
body.fi-panel-portal .cxp-rankings-overview-list > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem;
  padding: .45rem 0;
  border-bottom: 1px solid var(--portal-border);
  color: var(--portal-text);
  font-size: .78rem;
}
body.fi-panel-portal .cxp-rankings-overview-list > div span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-rankings-overview-list > div strong {
  flex: 0 0 auto;
  color: var(--portal-heading);
}
body.fi-panel-portal .cxp-rankings-pages-table th,
body.fi-panel-portal .cxp-rankings-pages-table td {
  text-align: left;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-rankings-pages-table td:first-child {
  max-width: 20rem;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.fi-panel-portal .cxp-rankings-keyword-table th,
body.fi-panel-portal .cxp-rankings-keyword-table td {
  text-align: left;
  vertical-align: top;
}
body.fi-panel-portal .cxp-rankings-keyword-table td:first-child {
  min-width: 15rem;
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-keyword-table td:nth-child(2) {
  max-width: 18rem;
}
body.fi-panel-portal .cxp-rankings-keyword-table td:nth-child(3),
body.fi-panel-portal .cxp-rankings-keyword-table td:nth-child(4),
body.fi-panel-portal .cxp-rankings-keyword-table td:nth-child(5) {
  color: var(--portal-muted);
  white-space: nowrap;
}
body.fi-panel-portal .cxp-rankings-delta {
  display: block;
  margin-top: .25rem;
  font-size: .75rem;
  font-weight: 700;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-rankings-empty {
  min-height: 9rem;
}
@media (prefers-reduced-motion: no-preference) {
  body.fi-panel-portal .cxp-rankings-bar-fill {
    animation: cxp-rankings-bar-in .65s ease-out both;
  }
}
@keyframes cxp-rankings-bar-in {
  from { transform: scaleX(0); }
  to { transform: scaleX(var(--bar-scale, 0)); }
}
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-rankings-stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-rankings-main-grid,
  body.fi-panel-portal .cxp-rankings-secondary-grid { grid-template-columns: 1fr; }
}
@media (max-width: 700px) {
  body.fi-panel-portal .cxp-rankings-head { align-items: stretch; }
  body.fi-panel-portal .cxp-rankings-domain-field { min-width: 0; }
  body.fi-panel-portal .cxp-rankings-stat-grid { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-rankings-filter-tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-rankings-filter:last-child { grid-column: span 2; }
  body.fi-panel-portal .cxp-rankings-overview-grid { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-rankings-chart-wrap { grid-template-columns: 4.5rem minmax(0, 1fr); padding-left: .75rem; padding-right: .75rem; }
  body.fi-panel-portal .cxp-rankings-chart-labels { padding-left: 5.2rem; padding-right: .75rem; }
}
@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-rankings-bar-fill { animation: none; }
}

/* Round 19 measurement corrections: pack cards to their content and keep the
   page rhythm at one 16px token instead of inheriting the portal default. */
body.fi-panel-portal .cxp-rankings-page { gap: 1rem !important; }
body.fi-panel-portal .cxp-rankings-card,
body.fi-panel-portal .cxp-rankings-filters-card { height: auto; align-self: start; }
body.fi-panel-portal .cxp-rankings-trend-card,
body.fi-panel-portal .cxp-rankings-bucket-card { min-height: 0; }
body.fi-panel-portal .cxp-rankings-filters-card { min-height: 0; }
body.fi-panel-portal .cxp-rankings-filters-card .kt-card-header { flex: 0 0 auto; }
body.fi-panel-portal .cxp-rankings-filter-tabs,
body.fi-panel-portal .cxp-rankings-overview-grid { padding-bottom: 0; }
body.fi-panel-portal .cxp-rankings-secondary-grid { grid-template-columns: minmax(0, 1fr); }
body.fi-panel-portal .cxp-rankings-secondary-grid > * {
  width: 100%;
  min-width: 0;
  max-width: 100%;
}
body.fi-panel-portal .cxp-rankings-bucket-table thead th,
body.fi-panel-portal .cxp-rankings-pages-table thead th,
body.fi-panel-portal .cxp-rankings-keyword-table thead th {
  background: var(--portal-surface-soft);
}
body.fi-panel-portal .cxp-rankings-bucket-table th,
body.fi-panel-portal .cxp-rankings-bucket-table td { font-size: .75rem; }
body.fi-panel-portal .cxp-rankings-bucket-table td small,
body.fi-panel-portal .cxp-rankings-chart-labels,
body.fi-panel-portal .cxp-rankings-filter small,
body.fi-panel-portal .cxp-rankings-overview-list h3,
body.fi-panel-portal .cxp-rankings-delta { font-size: .75rem; }
@media (max-width: 700px) {
  body.fi-panel-portal .cxp-rankings-secondary-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-rankings-secondary-grid .cxp-rankings-pages-table {
    width: 100%;
    min-width: 0;
    max-width: 100%;
    table-layout: fixed;
  }
  body.fi-panel-portal .cxp-rankings-secondary-grid .cxp-rankings-pages-table th,
  body.fi-panel-portal .cxp-rankings-secondary-grid .cxp-rankings-pages-table td {
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
  }
  body.fi-panel-portal .cxp-rankings-secondary-grid .cxp-rankings-pages-table td:first-child {
    max-width: none;
    overflow: visible;
    text-overflow: clip;
  }
}

/* Portal messages centre. Communication only - see Messages.php security notes. */
body.fi-panel-portal .cxp-chat-empty { margin: 0; }
body.fi-panel-portal .cxp-chat-thread {
  display: flex;
  flex-direction: column;
  gap: .75rem;
  max-height: 30rem;
  overflow-y: auto;
}
body.fi-panel-portal .cxp-chat-msg {
  max-width: 42rem;
  padding: .7rem .9rem;
  border-radius: .7rem;
  border: 1px solid #e9eef6;
  background: #fbfcfe;
}
body.fi-panel-portal .cxp-chat-msg--customer {
  align-self: flex-end;
  background: var(--portal-primary-soft);
  border-color: #d6e4fb;
}
body.fi-panel-portal .cxp-chat-msg--team {
  align-self: flex-start;
  background: var(--cxp-good-tint);
  border-color: var(--cxp-good-line);
}
body.fi-panel-portal .cxp-chat-meta {
  display: flex;
  align-items: baseline;
  gap: .5rem;
  margin-bottom: .25rem;
}
body.fi-panel-portal .cxp-chat-meta strong { font-size: .8125rem; color: #172033; }
body.fi-panel-portal .cxp-chat-meta span { font-size: .75rem; color: #5b6880; }
body.fi-panel-portal .cxp-chat-body {
  font-size: .875rem;
  line-height: 1.5;
  color: #2c3648;
  white-space: pre-wrap;
  word-break: break-word;
}

/* The taskbar was collapsing to ~73px and wrapping its 4 actions onto two rows
   (measured: 3 icons at y=15, overflow menu pushed to y=37). Keep it on one
   line and let it size to its content. */
body.fi-panel-portal .cxp-taskbar {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: .35rem;
  width: auto;
  flex: 0 0 auto;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-taskbar > * {
  flex: 0 0 auto;
}
body.fi-panel-portal .cxp-taskbar-overflow {
  position: relative;
}

/* Rankings "Unchanged" chip: #667085 on the #E7EEF8 header tint measured
   4.26:1 at 12px, under the 4.5:1 needed for normal text. Darken the neutral
   tone. Codex introduced this; the palette tokens cover the other three. */
body.fi-panel-portal .cxp-rankings-tone-neutral,
body.fi-panel-portal .cxp-rankings-status.cxp-rankings-tone-neutral {
  color: #414c60 !important;
}

/* ONE GREEN FOR SCORES - user 2026-07-30: "the scores have different greens.
   You need to match up with the theme."
   Audit found scores rendering as #00A651 on website-health / ai-visibility but
   #007A3D on dashboard / autopilot / rankings. Every score number and score
   label now resolves to --cxp-good-text. Scores are always large type, so the
   3:1 rule applies and this passes on white.
   The fixed-issues callout drops its green tint - on tint, #00A651 measured
   2.96:1. Neutral surface keeps the colour consistent AND the contrast legal. */
body.fi-panel-portal .cxp-score-good,
body.fi-panel-portal .cxp-score-number.cxp-score-good,
body.fi-panel-portal .cxp-fixed-number,
body.fi-panel-portal .sb-stat-value.cxp-score-good,
body.fi-panel-portal .cxp-ai-score.cxp-score-good {
  color: var(--cxp-good-text) !important;
}
body.fi-panel-portal .cxp-fixed-callout {
  background: #fbfcfe !important;
  border: 1px solid #e9eef6 !important;
}

/* "Looking good" sat next to the 96 score in a DIFFERENT green, because at
   16.8px it is normal-size text and needs 4.5:1 - which the score green cannot
   meet on white. Promote it to large text (>=18.66px + bold), which lowers the
   requirement to 3:1, so it can carry the same --cxp-good-text as the score.
   Result: score number and score label are now the same green everywhere. */
body.fi-panel-portal .cxp-score-label {
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1.25;
}
body.fi-panel-portal .cxp-score-label.cxp-score-good {
  color: var(--cxp-good-text) !important;
}

/* FINAL score colour rule 2026-07-30.
   Score TEXT renders at different sizes per page (dashboard 96 is 16.8px,
   website-health 96 is 32px, the fixed counter is 16px). No single light green
   passes contrast at every one of those sizes, so all score TEXT uses
   --cxp-good-strong (#007A3D, 5.3:1 on white - legal at any size).
   Neon --cxp-good (#00E676) stays on GRAPHICS only: gauge rings and progress
   bars, where the 3:1 graphic rule applies.
   Net effect: one green for every score on every page, and no contrast fails. */
body.fi-panel-portal .cxp-score-good,
body.fi-panel-portal .cxp-score-number.cxp-score-good,
body.fi-panel-portal .cxp-score-label.cxp-score-good,
body.fi-panel-portal .cxp-fixed-number,
body.fi-panel-portal .sb-stat-value.cxp-score-good,
body.fi-panel-portal .cxp-ai-score.cxp-score-good {
  color: var(--cxp-good-strong) !important;
}
/* undo the earlier size bump - no longer needed now the colour is legal at 16.8px */
body.fi-panel-portal .cxp-score-label {
  font-size: 1.05rem;
  font-weight: 700;
}

/* Callout repair 2026-07-30: rendered as "4issues already fixed" with the
   number and label jammed together and the note wrapped underneath, because the
   earlier .cxp-fixed-number sizing was being overridden and the flex row lost
   its gap. Restate the whole block with enough specificity to stick. */
body.fi-panel-portal .cxp-fixed-callout .cxp-fixed-row {
  display: flex !important;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  flex-wrap: wrap;
}
body.fi-panel-portal .cxp-fixed-callout .cxp-fixed-row > div {
  display: flex !important;
  align-items: baseline;
  gap: .55rem;
  flex: 0 0 auto;
}
body.fi-panel-portal .cxp-fixed-callout .cxp-fixed-number {
  font-size: 1.9rem !important;
  font-weight: 800;
  line-height: 1;
}
body.fi-panel-portal .cxp-fixed-callout .cxp-meta {
  font-size: .8125rem;
}
body.fi-panel-portal .cxp-fixed-callout .cxp-fixed-note {
  flex: 1 1 20rem;
  font-size: .8125rem;
  color: #46536b;
  text-align: right;
}

/* The overflow menu uses <details>/<summary>, which renders a default
   disclosure triangle - it was showing as a stray arrow between the alerts
   icon and the three dots in the taskbar. Suppress it in every engine. */
body.fi-panel-portal .cxp-taskbar-overflow > summary {
  list-style: none;
  cursor: pointer;
}
body.fi-panel-portal .cxp-taskbar-overflow > summary::-webkit-details-marker {
  display: none;
}
body.fi-panel-portal .cxp-taskbar-overflow > summary::marker {
  content: "";
}

/* Admin client switcher in the portal topbar. Customer accounts never render
   this control. */
body.fi-panel-portal .cxp-topbar-context {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
}
body.fi-panel-portal .cxp-topbar-context-copy {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}
body.fi-panel-portal .cxp-siteswitch {
  display: flex;
  align-items: center;
  gap: .45rem;
  margin: 0;
}
body.fi-panel-portal .cxp-siteswitch-label {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #5b6880;
}
body.fi-panel-portal .cxp-siteswitch-select {
  font-size: .8125rem;
  color: #172033;
  background: #fff;
  border: 1px solid #d8e0ec;
  border-radius: .45rem;
  padding: .3rem .6rem;
  max-width: 16rem;
  cursor: pointer;
}

/* The switcher was wrapping under the greeting and clipping it. Keep the
   topbar context on ONE row and let the select shrink instead of wrapping. */
body.fi-panel-portal .cxp-topbar-context {
  flex-wrap: nowrap;
  min-width: 0;
}
body.fi-panel-portal .cxp-topbar-context-copy {
  min-width: 0;
  flex: 0 1 auto;
}
body.fi-panel-portal .cxp-topbar-business {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}
body.fi-panel-portal .cxp-siteswitch {
  flex: 0 1 auto;
  min-width: 0;
}
body.fi-panel-portal .cxp-siteswitch-select {
  min-width: 0;
  max-width: 14rem;
  text-overflow: ellipsis;
}

/* .cxp-topbar-context had flex-direction:column from an earlier rule, so the
   switcher stacked under the greeting and the block (88px) overflowed the
   fixed 64px topbar, clipping the greeting. Force a row. */
body.fi-panel-portal .cxp-topbar-context {
  flex-direction: row !important;
  align-items: center;
  gap: .35rem;
}
body.fi-panel-portal .cxp-topbar-context-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Rankings keyword table 2026-07-30 - user-reported defects:
   - Current column was only 103px so "4 / No change" wrapped onto two lines
   - a long URL overflowed its cell and collided with the next column
   - "Position filters" card was 566px while every other full-width card is 1116px
   Proportional widths, and URLs wrap inside their own cell instead of spilling. */
body.fi-panel-portal .cxp-rankings-keyword-table { table-layout: fixed; width: 100%; }
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-kw   { width: 24%; }
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-url  { width: 30%; }
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-hist { width: 12%; text-align: left; }
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now  { width: 10%; white-space: nowrap; }

/* keep long URLs inside their cell - this was overlapping the next column */
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-cell-url,
body.fi-panel-portal .cxp-cell-url {
  overflow-wrap: anywhere;
  word-break: break-word;
  white-space: normal;
  max-width: 100%;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now .cxp-rankings-delta {
  display: inline;
  margin-left: .35rem;
  font-size: .75rem;
}

/* every full-width card on this page should span the content column */
body.fi-panel-portal .cxp-rankings-filters,
body.fi-panel-portal .cxp-rankings-card { width: 100%; }

/* pager */
body.fi-panel-portal .cxp-pager {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .75rem;
  padding: .75rem 1.25rem;
  border-top: 1px solid #eef1f5;
}
body.fi-panel-portal .cxp-pager-status { font-size: .8125rem; color: #5b6880; }
body.fi-panel-portal .cxp-pager-btn {
  font-size: .8125rem;
  font-weight: 600;
  color: #172033;
  background: #fff;
  border: 1px solid #d8e0ec;
  border-radius: .45rem;
  padding: .35rem .8rem;
  cursor: pointer;
}
body.fi-panel-portal .cxp-pager-btn:disabled { opacity: .45; cursor: default; }

/* "What needs attention" - open findings grouped by type, in customer language.
   Added 2026-07-30 to complete the website audit section. */
body.fi-panel-portal .cxp-issue-row {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: .85rem 0;
  border-bottom: 1px solid #eef1f5;
}
body.fi-panel-portal .cxp-issue-row:last-child { border-bottom: 0; padding-bottom: .2rem; }
body.fi-panel-portal .cxp-issue-count {
  flex: 0 0 2.4rem;
  font-size: 1.35rem;
  font-weight: 800;
  line-height: 1.2;
  text-align: left;
}
body.fi-panel-portal .cxp-issue-copy {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-issue-copy strong {
  font-size: .9375rem;
  font-weight: 650;
  color: #172033;
}
/* Portal coordinator feed - real Command Center activity only. */
body.fi-panel-portal .cxp-coordinator-card { overflow: hidden; }
body.fi-panel-portal .cxp-coordinator-intro { color: var(--portal-muted); font-size: var(--text-sm); line-height: 1.55; }
body.fi-panel-portal .cxp-coordinator-summary {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  padding: 1rem 1.25rem;
  border-bottom: 1px solid var(--portal-border);
  background: var(--portal-surface-soft);
}
body.fi-panel-portal .cxp-coordinator-stat { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
body.fi-panel-portal .cxp-coordinator-stat strong { color: var(--portal-heading); font-size: 2rem; font-weight: 700; letter-spacing: -.04em; line-height: 1.2; }
body.fi-panel-portal .cxp-coordinator-stat span { color: var(--portal-muted); font-size: var(--text-2xs); }
body.fi-panel-portal .cxp-coordinator-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  padding: 1rem 1.25rem;
}
body.fi-panel-portal .cxp-coordinator-panel { min-width: 0; }
body.fi-panel-portal .cxp-coordinator-panel h3 { margin: 0; color: var(--portal-heading); font-size: var(--text-sm); font-weight: var(--font-weight-semibold); }
body.fi-panel-portal .cxp-coordinator-panel > p { margin: .3rem 0 0; color: var(--portal-muted); font-size: var(--text-2xs); }
body.fi-panel-portal .cxp-coordinator-list { display: flex; flex-direction: column; gap: .7rem; margin-top: .8rem; }
body.fi-panel-portal .cxp-coordinator-item { display: flex; align-items: flex-start; gap: .6rem; min-width: 0; }
body.fi-panel-portal .cxp-coordinator-dot { flex: 0 0 .55rem; width: .55rem; height: .55rem; margin-top: .35rem; border-radius: 999px; background: var(--cxp-good); }
body.fi-panel-portal .cxp-coordinator-item > span:last-child,
body.fi-panel-portal .cxp-coordinator-item > div { display: flex; flex: 1; flex-direction: column; min-width: 0; gap: .12rem; }
body.fi-panel-portal .cxp-coordinator-item strong { color: var(--portal-heading); font-size: var(--text-xs); line-height: 1.35; overflow-wrap: anywhere; }
body.fi-panel-portal .cxp-coordinator-item small { color: var(--portal-muted); font-size: var(--text-2xs); line-height: 1.35; }
/* Keep actions that follow a coordinator list outside the list's rhythm.
   The grid uses 1rem of vertical padding, so matching that token gives the
   action a measured 16px separation from the final item (vs .7rem between
   neighboring items) without affecting the footer actions below the grid. */
body.fi-panel-portal .cxp-coordinator-list-action {
  display: flex;
  width: fit-content;
  margin-top: 1rem;
}
body.fi-panel-portal .cxp-coordinator-empty { margin: .8rem 0 0; color: var(--portal-muted); font-size: var(--text-xs); line-height: 1.45; }
body.fi-panel-portal .cxp-coordinator-footer { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .85rem 1.25rem 1rem; border-top: 1px solid var(--portal-border); }
body.fi-panel-portal .cxp-coordinator-footer span { color: var(--portal-muted); font-size: var(--text-2xs); }
@media (max-width: 860px) {
  body.fi-panel-portal .cxp-coordinator-summary,
  body.fi-panel-portal .cxp-coordinator-grid { grid-template-columns: 1fr; }
}

/* ============================================================================
   AUTHORITATIVE SCORE COLOUR RULE - 2026-07-30. SUPERSEDES every earlier score
   colour rule in this file. Appended last so it wins.

   The user reported this 5-10 times. Root cause of the recurrence: an earlier
   rule of mine deliberately split scores into "neon for the ring, dark for the
   number" to satisfy WCAG contrast. That GUARANTEED the ring and the number
   could never match, and guaranteed a score looked different on a page that
   drew a ring vs a page that printed text.

   The fix is to remove the split entirely: ONE value per tone, used for the
   ring stroke, the progress bar AND the number. To keep it legal, every score
   number is forced to large-text size (>=24px), which lowers the requirement
   from 4.5:1 to 3:1. All three tones clear that on white:
       good   #00A651  3.19:1
       middle #B26A00  4.18:1
       poor   #D92D20  4.83:1
   Do not reintroduce a separate "-strong" colour for scores.
   ============================================================================ */
body.fi-panel-portal {
  --cxp-tone-good: #00A651;
  --cxp-tone-middle: #B26A00;
  --cxp-tone-poor: #D92D20;
  --cxp-tone-neutral: #5B6577;
}

/* every score number is large text so one colour can serve text and graphic */
body.fi-panel-portal .cxp-score-number,
body.fi-panel-portal .cxp-gauge-item strong,
body.fi-panel-portal .cxp-ai-score,
body.fi-panel-portal .cxp-fixed-number,
body.fi-panel-portal .cxp-issue-count {
  font-size: 1.6rem;
  font-weight: 800;
  line-height: 1.1;
}

/* NUMBERS + LABELS */
body.fi-panel-portal .cxp-score-good,
body.fi-panel-portal .cxp-score-number.cxp-score-good,
body.fi-panel-portal .cxp-score-label.cxp-score-good,
body.fi-panel-portal .cxp-fixed-number,
body.fi-panel-portal .sb-stat-value.cxp-score-good,
body.fi-panel-portal .cxp-ai-score.cxp-score-good { color: var(--cxp-tone-good) !important; }

body.fi-panel-portal .cxp-score-middle,
body.fi-panel-portal .cxp-score-number.cxp-score-middle,
body.fi-panel-portal .cxp-score-label.cxp-score-middle,
body.fi-panel-portal .sb-stat-value.cxp-score-middle,
body.fi-panel-portal .cxp-ai-score.cxp-score-middle { color: var(--cxp-tone-middle) !important; }

body.fi-panel-portal .cxp-score-poor,
body.fi-panel-portal .cxp-score-number.cxp-score-poor,
body.fi-panel-portal .cxp-score-label.cxp-score-poor,
body.fi-panel-portal .sb-stat-value.cxp-score-poor,
body.fi-panel-portal .cxp-ai-score.cxp-score-poor { color: var(--cxp-tone-poor) !important; }

/* GAUGE RINGS - identical values to the numbers they wrap */
body.fi-panel-portal .cxp-gauge-good,
body.fi-panel-portal .cxp-gauge-health,
body.fi-panel-portal .cxp-health-summary-ring.cxp-gauge-good { stroke: var(--cxp-tone-good) !important; }
body.fi-panel-portal .cxp-gauge-middle,
body.fi-panel-portal .cxp-health-summary-ring.cxp-gauge-middle { stroke: var(--cxp-tone-middle) !important; }
body.fi-panel-portal .cxp-gauge-poor,
body.fi-panel-portal .cxp-health-summary-ring.cxp-gauge-poor { stroke: var(--cxp-tone-poor) !important; }

/* PROGRESS BAR FILLS - same three values again */
body.fi-panel-portal .cxp-fill-good,
body.fi-panel-portal .cxp-health-progress-fill.cxp-fill-good { background: var(--cxp-tone-good) !important; }
body.fi-panel-portal .cxp-fill-middle,
body.fi-panel-portal .cxp-health-progress-fill.cxp-fill-middle { background: var(--cxp-tone-middle) !important; }
body.fi-panel-portal .cxp-fill-poor,
body.fi-panel-portal .cxp-health-progress-fill.cxp-fill-poor { background: var(--cxp-tone-poor) !important; }

/* Keep score-facing tokens on the same three values. The strong good token
   remains the established small-text contrast value for ordinary portal copy. */
body.fi-panel-portal {
  --cxp-good: var(--cxp-tone-good);
  --cxp-good-text: var(--cxp-tone-good);
  --cxp-good-strong: #007A3D;
  --cxp-warn: var(--cxp-tone-middle);
  --cxp-warn-text: var(--cxp-tone-middle);
  --cxp-warn-strong: var(--cxp-tone-middle);
  --cxp-bad: var(--cxp-tone-poor);
  --cxp-bad-text: var(--cxp-tone-poor);
  --cxp-bad-strong: var(--cxp-tone-poor);
}

/* ---- STATUS CHIPS vs SCORES - deliberate, documented distinction ----
   SCORES (a number, its ring, its bar) share ONE vibrant tone - see the
   authoritative block above. Do not change that.

   STATUS CHIPS ("Active", "Queued", "Jumped", "Member") are 12px text and are
   NEVER shown beside a ring of the same value, so they cannot look
   "inconsistent" with a score. They need 4.5:1 at that size, which the vibrant
   score tones cannot give, so they use darker variants of the SAME hues:
       good   #047A44  5.42:1
       middle #8A4B00  6.80:1
       poor   #B42318  6.58:1
   ------------------------------------------------------------------------ */
body.fi-panel-portal .kt-badge,
body.fi-panel-portal .cxp-activity-status,
body.fi-panel-portal .cxp-rankings-status {
  font-weight: 650;
}
body.fi-panel-portal .kt-badge[class*="success"],
body.fi-panel-portal .cxp-badge-good,
body.fi-panel-portal .cxp-rankings-tone-good,
body.fi-panel-portal .cxp-activity-status--good { color: #047A44 !important; }

body.fi-panel-portal .kt-badge-outline,
body.fi-panel-portal .kt-badge[class*="warning"],
body.fi-panel-portal .cxp-badge-warn,
body.fi-panel-portal .cxp-activity-status--needs-input,
body.fi-panel-portal .cxp-rankings-tone-middle { color: #8A4B00 !important; }

body.fi-panel-portal .kt-badge[class*="danger"],
body.fi-panel-portal .cxp-badge-bad,
body.fi-panel-portal .cxp-rankings-tone-bad,
body.fi-panel-portal .cxp-activity-status--bad { color: #B42318 !important; }

body.fi-panel-portal .cxp-rankings-tone-neutral { color: #414c60 !important; }

/* "Looking good" IS a score label sitting next to the 96, so it must keep the
   score tone. Promote it to large text (>=18.66px bold) so 3:1 applies. */
body.fi-panel-portal .cxp-score-label {
  font-size: 1.25rem !important;
  font-weight: 700 !important;
}
/* Keep every coordinator metadata label at the portal minimum readable size. */
body.fi-panel-portal .cxp-coordinator-stat span,
body.fi-panel-portal .cxp-coordinator-panel > p,
body.fi-panel-portal .cxp-coordinator-item small,
body.fi-panel-portal .cxp-coordinator-empty,
body.fi-panel-portal .cxp-coordinator-footer span { font-size: 12px; }

/* Chips that sit on a TINTED pill background lose contrast against that tint
   rather than against white, so they need darker text again. Measured before:
   Active #047A44 on #EAFAF3 = 2.96, activity status #B26A00 on #FFF4CC = 3.85,
   delta #00A651 on #EAFAF3 = 2.96. */
body.fi-panel-portal .kt-badge.kt-badge-outline[class*="success"],
body.fi-panel-portal .kt-badge[class*="success"],
body.fi-panel-portal .cxp-delta,
body.fi-panel-portal .cxp-plan-status { color: #0B5C36 !important; }

body.fi-panel-portal .cxp-activity-status,
body.fi-panel-portal .cxp-activity-status--needs-input { color: #6B3A00 !important; }

/* An earlier rule at ~line 1867 scopes this with .cxp-autopilot-page, which
   outranks a plain .cxp-activity-status--needs-input selector. Match that
   specificity so the readable colour actually wins. */
body.fi-panel-portal .cxp-autopilot-page .cxp-activity-status--needs-input,
body.fi-panel-portal .cxp-autopilot-page .cxp-activity-status {
  color: #6B3A00 !important;
}

/* Simple CSS bar chart for Search performance clicks-over-time. No JS chart
   library needed and it inherits the semantic tone tokens. */
body.fi-panel-portal .cxp-bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 9rem;
  padding-top: .5rem;
}
body.fi-panel-portal .cxp-bar-col {
  flex: 1 1 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  min-width: 3px;
}
body.fi-panel-portal .cxp-bar {
  width: 100%;
  background: var(--portal-primary-fill);
  border-radius: 3px 3px 0 0;
  transition: height .9s cubic-bezier(.22,.61,.36,1);
}
body.fi-panel-portal .cxp-bars-axis {
  display: flex;
  justify-content: space-between;
  margin-top: .4rem;
  font-size: .75rem;
  color: #5b6880;
}
@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-bar { transition: none; }
}

/* Payment method actions row - "Manage payment methods" sends the customer to
   Stripe's hosted portal, so no card data ever reaches this server. */
body.fi-panel-portal .cxp-pm-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  border-top: 1px solid #eef1f5;
}

/* Connected accounts rows. */
body.fi-panel-portal .cxp-conn-row {
  display: flex;
  align-items: flex-start;
  gap: .85rem;
  padding: .95rem 0;
  border-bottom: 1px solid #eef1f5;
}
body.fi-panel-portal .cxp-conn-row:last-child { border-bottom: 0; padding-bottom: .2rem; }
body.fi-panel-portal .cxp-conn-dot {
  flex: 0 0 .6rem;
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  margin-top: .45rem;
}
body.fi-panel-portal .cxp-conn-dot--on  { background: var(--cxp-tone-good); }
body.fi-panel-portal .cxp-conn-dot--off { background: var(--cxp-tone-middle); }
body.fi-panel-portal .cxp-conn-copy {
  display: flex;
  flex-direction: column;
  gap: .22rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-conn-head {
  display: flex;
  align-items: center;
  gap: .6rem;
  flex-wrap: wrap;
}
body.fi-panel-portal .cxp-conn-head strong {
  font-size: .9375rem;
  font-weight: 650;
  color: #172033;
}
body.fi-panel-portal .cxp-conn-action {
  font-size: .8125rem;
  color: #6B3A00;
  background: var(--cxp-warn-tint, #FFF7E6);
  border-radius: .4rem;
  padding: .35rem .6rem;
  margin-top: .3rem;
}

/* GA4 audience block on Search performance. */
body.fi-panel-portal .cxp-aud-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 1.1rem 1.5rem;
}
body.fi-panel-portal .cxp-aud-item {
  display: flex;
  flex-direction: column;
  gap: .15rem;
}
body.fi-panel-portal .cxp-aud-value {
  font-size: 1.6rem;
  font-weight: 800;
  line-height: 1.1;
  color: #172033;
}
body.fi-panel-portal .cxp-aud-note {
  margin: 1rem 0 0;
  font-size: .8125rem;
  color: #6B3A00;
  background: var(--cxp-warn-tint, #FFF7E6);
  border-radius: .45rem;
  padding: .6rem .75rem;
}


/* ---- AI search visibility: per-assistant results (2026-07-31) -------------
   Every class below is introduced by the "Which AI assistants name you" block
   on the AI search visibility page. Tone classes (.cxp-score-*, .cxp-fill-*)
   are the page's existing ones so the number and its bar always carry the same
   value - never a separate colour for graphic vs text. */
body.fi-panel-portal .cxp-ai-engine-card {
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-engine-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  gap: 1rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-engine {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .8rem;
  padding: 1.1rem 1rem;
  border: 1px solid var(--portal-border);
  border-radius: .75rem;
  background: var(--portal-surface-soft);
  text-align: center;
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-engine-identity {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .65rem;
  width: 100%;
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-engine-logo {
  display: grid;
  place-items: center;
  flex: 0 0 3.25rem;
  width: 3.25rem;
  height: 3.25rem;
  border: 1px solid var(--portal-border);
  border-radius: .75rem;
  background: var(--portal-surface);
}
body.fi-panel-portal .cxp-ai-engine-logo img {
  display: block;
  width: 2.75rem;
  height: 2.75rem;
  object-fit: contain;
}
body.fi-panel-portal .cxp-ai-logo-fallback {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: .55rem;
  color: var(--portal-muted);
  background: var(--portal-surface-soft);
  font-size: .8rem;
  font-weight: 750;
  letter-spacing: .04em;
}
body.fi-panel-portal .cxp-ai-engine-name {
  font-size: .95rem;
  font-weight: 650;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-ai-engine-gauge {
  flex: 0 0 6rem;
  width: 6rem;
  height: 6rem;
}
body.fi-panel-portal .cxp-ai-engine-gauge > .cxp-ai-engine-pct {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}
body.fi-panel-portal .cxp-ai-engine-result {
  display: grid;
  gap: .2rem;
}
body.fi-panel-portal .cxp-ai-engine-result strong {
  color: var(--portal-heading);
  font-size: .8rem;
}
body.fi-panel-portal .cxp-ai-engine-result span {
  color: var(--portal-muted);
  font-size: .72rem;
  line-height: 1.35;
}
body.fi-panel-portal .cxp-ai-matrix-wrap {
  padding: 0;
  overflow-x: auto;
}
body.fi-panel-portal .cxp-ai-matrix {
  width: 100%;
  border-collapse: collapse;
}
body.fi-panel-portal .cxp-ai-matrix-q {
  min-width: 22rem;
  line-height: 1.45;
}
body.fi-panel-portal .cxp-ai-matrix-engine,
body.fi-panel-portal .cxp-ai-matrix-cell {
  text-align: center;
  white-space: nowrap;
}
/* 12px status chips - the documented exception that uses the darker tone
   variants for contrast on a tinted pill. */
body.fi-panel-portal .cxp-ai-cell {
  display: inline-block;
  font-size: .75rem;
  font-weight: 650;
  padding: .15rem .5rem;
  border-radius: 999px;
}

/* cxp-ai-cell pill height: these pills set no line-height, so they inherited a
   unitless one and their rendered height tracked whatever font-size the
   surrounding cell happened to use - 21.9px in a 13px cell, 22.8px in a 14px
   one, for the same class on two pages. Setting the line-height here makes the
   pill own its own height everywhere, including ai-responses, which a
   page-scoped fix could not reach. Height is deliberately NOT pinned: a pinned
   height clips its own padding, which is the bug kt-badge already has. */
body.fi-panel-portal .cxp-ai-cell,
body.fi-panel-portal .cxp-ai-cell-none {
  line-height: 1.4;
  border: 1px solid transparent;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-ai-cell.cxp-badge-good {
  background: rgba(4, 122, 68, .10);
  color: #04663A !important;
}
/* "Not named" is an opportunity, not an error - neutral, never red, so the
   red in this page keeps meaning "something is wrong". */
body.fi-panel-portal .cxp-ai-cell-miss {
  background: rgba(65, 76, 96, .08);
  color: #414c60;
}
body.fi-panel-portal .cxp-ai-cell-none {
  color: #6b7688;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-ai-engine-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-ai-matrix-q {
    min-width: 14rem;
  }
}


/* Narrow screens: the pivot table cannot show 4 assistant columns beside a
   readable question, and a silently-scrolling table reads as "only ChatGPT was
   checked". Swap to a stacked block per question where every assistant is
   named on screen. Same data, no hidden columns. */
body.fi-panel-portal .cxp-ai-stack {
  display: none;
}
@media (max-width: 900px) {
  body.fi-panel-portal .cxp-ai-matrix-wrap {
    display: none;
  }
  body.fi-panel-portal .cxp-ai-stack {
    display: block;
    padding: 0 1.25rem 1.25rem;
  }
  body.fi-panel-portal .cxp-ai-stack-item {
    padding: 1rem 0;
    border-top: 1px solid var(--portal-border);
  }
  body.fi-panel-portal .cxp-ai-stack-q {
    margin: 0 0 .75rem;
    font-weight: 600;
    line-height: 1.45;
  }
  body.fi-panel-portal .cxp-ai-stack-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .5rem;
  }
  /* Name ABOVE its own chip. Laid out as a space-between row, each chip ended
     up nearer the NEXT assistant's name than its own, so "ChatGPT [Not named]
     Claude" read ambiguously about which assistant the chip belonged to. */
  body.fi-panel-portal .cxp-ai-stack-engine {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .3rem;
    min-width: 0;
  }
  body.fi-panel-portal .cxp-ai-stack-engine-name {
    font-size: .8125rem;
    color: #414c60;
    white-space: nowrap;
  }
}


/* ---- Portal messages: AI intake assistant turns (2026-07-31) --------------
   The assistant sits visually with the team side of the thread (it speaks for
   Cyticx), but every model-authored turn carries an explicit "Automated" chip
   so a customer is never misled about who is talking. Server-authored lines -
   the handoff sentence, failure notices - deliberately do NOT carry the chip,
   because a person wrote those words. */
body.fi-panel-portal .cxp-chat-msg--assistant {
  align-self: flex-start;
  background: #f1f5fb;
  border: 1px solid #dbe4f0;
}
/* The bubble owns the frame - the body must stay transparent or the assistant
   ends up double-framed, which is how this shipped the first time. */
body.fi-panel-portal .cxp-chat-msg--assistant .cxp-chat-body {
  background: transparent;
  border: 0;
}
body.fi-panel-portal .cxp-chat-ai {
  display: inline-block;
  font-size: .6875rem;
  font-weight: 650;
  letter-spacing: .02em;
  text-transform: uppercase;
  padding: .1rem .4rem;
  border-radius: 999px;
  background: rgba(65, 76, 96, .10);
  color: #414c60;
}
body.fi-panel-portal .cxp-chat-meta {
  display: flex;
  align-items: center;
  gap: .5rem;
  flex-wrap: wrap;
}


/* ---- Map rankings (2026-07-31) -------------------------------------------
   Google Maps grid. Tones are the portal's existing three, so a colour means
   the same thing here as on every other page: good = top 3, middle = 4-10,
   poor = 11+. "Not shown" is neutral, never red - being absent from one corner
   of the map is an opportunity, not a fault, and red has to keep meaning
   "something is wrong". */
body.fi-panel-portal .cxp-map-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-map-stat {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  padding: 1.25rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-map-stat > strong {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.1;
}

/* The grid scrolls inside its own box - the page body must never scroll. */
body.fi-panel-portal .cxp-map-grid-wrap {
  overflow-x: auto;
  padding-bottom: .25rem;
}
body.fi-panel-portal .cxp-map-grid {
  display: grid;
  grid-template-columns: repeat(var(--cxp-map-cols, 13), minmax(2rem, 1fr));
  gap: 3px;
  min-width: 26rem;
}
body.fi-panel-portal .cxp-map-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  border-radius: .25rem;
  font-size: .8125rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  min-width: 0;
}
/* Darker tone variants: these are small text on a tinted fill, the same
   exception the 12px status chips take. */
body.fi-panel-portal .cxp-map-cell--good   { background: rgba(4, 122, 68, .16);  color: #047A44; }
body.fi-panel-portal .cxp-map-cell--middle { background: rgba(138, 75, 0, .14);  color: #8A4B00; }
body.fi-panel-portal .cxp-map-cell--poor   { background: rgba(180, 35, 24, .12); color: #B42318; }
body.fi-panel-portal .cxp-map-cell--none   { background: rgba(65, 76, 96, .07);  color: #6b7688; }

body.fi-panel-portal .cxp-map-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: 1rem;
  font-size: .8125rem;
  color: #414c60;
}
body.fi-panel-portal .cxp-map-legend span {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
}
body.fi-panel-portal .cxp-map-key {
  width: .85rem;
  height: .85rem;
  border-radius: .2rem;
  display: inline-block;
}
body.fi-panel-portal .cxp-map-key--good   { background: rgba(4, 122, 68, .35); }
body.fi-panel-portal .cxp-map-key--middle { background: rgba(138, 75, 0, .32); }
body.fi-panel-portal .cxp-map-key--poor   { background: rgba(180, 35, 24, .28); }
body.fi-panel-portal .cxp-map-key--none   { background: rgba(65, 76, 96, .16); }

/* Trend: bar height is INVERTED because a lower map position is better, so a
   taller bar always reads as a better day. */
body.fi-panel-portal .cxp-map-trend {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 7rem;
}
body.fi-panel-portal .cxp-map-bar {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  background: rgba(65, 76, 96, .06);
  border-radius: .2rem;
  overflow: hidden;
}
body.fi-panel-portal .cxp-map-bar-fill {
  display: block;
  width: 100%;
  border-radius: .2rem;
}
body.fi-panel-portal .cxp-map-trend-axis {
  display: flex;
  justify-content: space-between;
  margin-top: .5rem;
  font-size: .8125rem;
  color: #414c60;
}

@media (max-width: 1024px) {
  body.fi-panel-portal .cxp-map-stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-stat-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  /* No font-size shrink here. Dropping these to 11px put 169 cells under the
     12px floor; the grid already scrolls inside its own wrapper, so narrow
     screens get a scroll rather than unreadable text. */
  body.fi-panel-portal .cxp-map-grid {
    min-width: 22rem;
  }
}


/* A term we did not check on that day. Neutral on purpose - it is an absence of
   measurement, not a bad result, and must not read like one.
   #6b7688 measured 4.39:1 on the zebra row - just under the 4.5 floor - and
   failed 32 cells the moment a client with sparse history (yamifresh) rendered
   any dashes at all. carkeys never showed it because its history columns are
   full. Absence is carried by the DASH GLYPH, not by low contrast. */
body.fi-panel-portal .cxp-rankings-unchecked {
  color: #414c60;
}


/* ---- Phone speed history (2026-07-31) ------------------------------------
   Bars are DAILY MEDIANS, not raw readings. Same three tones as every other
   score in the portal. The y-axis starts below the lowest observed day rather
   than at zero, so real movement is visible without exaggerating it. */
body.fi-panel-portal .cxp-speed-head {
  display: flex;
  gap: 2.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}
body.fi-panel-portal .cxp-speed-head > div {
  display: flex;
  flex-direction: column;
  gap: .15rem;
}
body.fi-panel-portal .cxp-speed-lcp {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.1;
  color: #414c60;
}
body.fi-panel-portal .cxp-speed-chart {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 6rem;
}
body.fi-panel-portal .cxp-speed-bar {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  background: rgba(65, 76, 96, .06);
  border-radius: .2rem;
  overflow: hidden;
}
body.fi-panel-portal .cxp-speed-fill {
  display: block;
  width: 100%;
  border-radius: .2rem;
}
body.fi-panel-portal .cxp-speed-axis {
  display: flex;
  justify-content: space-between;
  margin-top: .5rem;
  font-size: .8125rem;
  color: #414c60;
}

/* Map rankings - flat-series summary.
   Replaces the bar strip when 30 days of average position land inside a single
   position. Surface values match .cxp-fixed-callout so the page keeps one
   callout treatment; NO text colour is set here - the detail line uses the
   existing .cxp-meta and the heading inherits the card, so this block adds no
   new colour and therefore no new contrast surface. */
body.fi-panel-portal .cxp-map-steady {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 18px 20px;
  border: 1px solid #e9eef6;
  border-radius: 10px;
  background: #fbfcfe;
}
body.fi-panel-portal .cxp-map-steady strong {
  font-size: 15px;
  font-weight: 600;
}

/* Ranking visibility: the no-trend state still shows a real average position,
   so it uses the same summary treatment as the sparkline state rather than the
   empty-state style. Reuses .cxp-rank-summary; this only adds the spacing the
   sparkline would otherwise have provided. */
body.fi-panel-portal .cxp-rank-summary.cxp-rank-flat {
  padding-top: 22px;
  padding-bottom: 22px;
}

/* Style Book table primitives, ported VERBATIM from
   resources/views/filament/pages/style-book/_tables.blade.php.

   These classes were used by live portal pages but defined ONLY inside the
   Style Book page's own inline CSS, so they never reached the portal. The
   visible consequence was on Autopilot SEO > Your pages: .sb-dense-identity is
   a flex row with a gap, and without it <strong>/path</strong><span>top
   performer</span> collapsed to plain inline text and rendered as
   "/what-to-do-if-your-car-key-breaks-in-half-2top performer".

   An audit of live portal blades found 11 sb-* classes with no rule in
   cxp.css. Only these three are ported here: they are small, self-contained
   and match the Style Book byte for byte. sb-table / sb-data-table are NOT
   ported - they carry table structure that kt-table is already providing, and
   adding them unverified would restyle every table in the portal. */
body.fi-panel-portal .sb-dense-identity {
  align-items: baseline;
  display: flex;
  gap: .45rem;
  white-space: nowrap;
}
body.fi-panel-portal .sb-dense-identity strong {
  color: var(--foreground);
  font-size: 13px;
  font-weight: var(--font-weight-medium);
}
body.fi-panel-portal .sb-dense-identity span {
  color: var(--muted-foreground);
  /* Style Book says 10px. That is sized for the DENSE ADMIN tables; the portal
     holds a 12px floor for customer-facing text and ui-qa fails anything under
     it (this exact rule produced 7 violations at 10px). Layout is still the
     Style Book's - only the type size is lifted to the portal minimum. */
  font-size: 12px;
}
body.fi-panel-portal .sb-data-money,
body.fi-panel-portal .sb-data-success {
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
}

/* Portal taskbar - restored from cxp.css.bak-round19-final-cleanup (2026-08-01).
   A cleanup round removed these rules but left the markup, so the help /
   ideas / alerts icons, the unread badge and the overflow menu rendered
   unstyled on every portal page. Found by the new unstyled-design-system-
   class check, not by eye. Copied verbatim: these were the working rules,
   and re-deriving them by hand would change the look for no reason. */
body.fi-panel-portal .cxp-taskbar-action {
  position: relative;
  display: inline-flex;
  width: 2rem;
  height: 2rem;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: .5rem;
  color: var(--portal-muted);
  background: transparent;
  cursor: pointer;
  font-size: 1rem;
  text-decoration: none;
}
body.fi-panel-portal .cxp-taskbar-action:hover,
body.fi-panel-portal .cxp-taskbar-action:focus-visible,
body.fi-panel-portal .cxp-taskbar-overflow[open] > summary {
  color: var(--portal-primary);
  background: var(--portal-primary-soft);
  outline: none;
}
body.fi-panel-portal .cxp-taskbar-action--ideas { color: var(--cxp-warn-strong); }
body.fi-panel-portal .cxp-taskbar-action--help { color: var(--portal-primary); }
body.fi-panel-portal .cxp-taskbar-action--alerts { color: var(--cxp-bad-strong); }
body.fi-panel-portal .cxp-taskbar-badge {
  position: absolute;
  top: -.1rem;
  right: -.15rem;
  min-width: 1rem;
  height: 1rem;
  padding: 0 .2rem;
  border-radius: 999px;
  color: var(--primary-foreground);
  background: var(--cxp-bad-strong);
  /* raised to the portal 12px readability floor - the restored value was
     below it and ui-qa fails anything under 12px */ font-size: 12px;
  font-weight: 800;
  line-height: 1rem;
  text-align: center;
}
body.fi-panel-portal .cxp-taskbar-dots { margin-top: -.35rem; font-size: 1.15rem; font-weight: 800; letter-spacing: .05em; }
body.fi-panel-portal .cxp-taskbar-menu {
  position: absolute;
  top: calc(100% + .55rem);
  right: 0;
  z-index: 100;
  width: 15rem;
  padding: .45rem;
  border: 1px solid var(--portal-border);
  border-radius: .7rem;
  background: var(--portal-surface);
  box-shadow: var(--portal-shadow);
}
body.fi-panel-portal .cxp-taskbar-menu-user {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  padding: .65rem .7rem;
  border-bottom: 1px solid var(--portal-border);
}
body.fi-panel-portal .cxp-taskbar-menu-user strong { color: var(--portal-heading); font-size: 13px; }
body.fi-panel-portal .cxp-taskbar-menu-user span { color: var(--portal-muted); font-size: 12px; }
body.fi-panel-portal .cxp-taskbar-menu-item {
  display: flex;
  width: 100%;
  min-height: 2.25rem;
  align-items: center;
  gap: .6rem;
  padding: .45rem .7rem;
  border: 0;
  border-radius: .45rem;
  color: var(--portal-text);
  background: transparent;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  text-align: left;
  text-decoration: none;
}
body.fi-panel-portal .cxp-taskbar-menu-item i { width: 1rem; color: var(--portal-primary); text-align: center; }
body.fi-panel-portal .cxp-taskbar-menu-item:hover,
body.fi-panel-portal .cxp-taskbar-menu-item:focus-visible { color: var(--portal-heading); background: var(--portal-primary-soft); outline: none; }
body.fi-panel-portal .cxp-taskbar-menu-item small { margin-left: auto; color: var(--portal-muted); /* raised to the portal 12px readability floor - the restored value was
     below it and ui-qa fails anything under 12px */ font-size: 12px; }
body.fi-panel-portal .cxp-taskbar-menu-item--disabled { cursor: not-allowed; opacity: .6; }
body.fi-panel-portal .cxp-taskbar-menu-item--disabled:hover { color: var(--portal-text); background: transparent; }

/* Dashboard stat row - restored from cxp.css.bak-audit-20260730 (2026-08-01).
   Paired with .cxp-dashboard-insights below it, which deliberately does the
   OPPOSITE (align-items:start). A cleanup round deleted this half and kept
   that one, leaving the markup referencing a class with no rule, so the top
   stat cards lost equal height. Surfaced by the unstyled-class check - the
   current data happens to have same-length content, so it looked fine. */
body.fi-panel-portal .cxp-dashboard-stat-grid {
  align-items: stretch !important;
}
body.fi-panel-portal .cxp-dashboard-stat-grid .sb-stat {
  align-self: stretch !important;
  /* Same trap as the portal KPI row above: height:100% defeats the stretch on an
     auto-sized row. No symptom here today only because these tiles carry
     equal-length copy - the bug is identical and latent. */
  height: auto !important;
}

/* Long-table row cap - PHONES ONLY (2026-08-01).
   Measured at 390x844: autopilot-seo ran 9097px (11 phone screens) and rankings
   7087px, driven by 50-row tables. Nothing overflowed and contrast was clean;
   the problem was sheer length. Capping to 10 rows with a control that reveals
   the rest hides no data and needs no JavaScript, so it survives Livewire
   re-renders and works with scripting off.
   The whole block is inside a max-width query: desktop is untouched. */
body.fi-panel-portal .cxp-rows-toggle { position: absolute; opacity: 0; pointer-events: none; }
body.fi-panel-portal .cxp-rows-more { display: none; }

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-rows-cap tbody tr:nth-child(n+11) { display: none; }
  body.fi-panel-portal .cxp-rows-toggle:checked ~ .cxp-rows-cap tbody tr { display: table-row; }
  body.fi-panel-portal .cxp-rankings-keyword-table.cxp-rows-cap > tbody:nth-of-type(n+11) {
    display: none;
  }
  body.fi-panel-portal .cxp-rankings-keyword-table.cxp-rows-cap > tbody:nth-of-type(n+11) tr {
    display: none;
  }
  body.fi-panel-portal .cxp-rows-toggle:checked ~ .cxp-rankings-keyword-table.cxp-rows-cap > tbody {
    display: table-row-group;
  }
  body.fi-panel-portal .cxp-rows-more {
    display: block;
    padding: 12px 16px;
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--portal-primary);
    text-align: center;
    border-top: 1px solid #e9eef6;
    cursor: pointer;
  }
  /* Once expanded the control has done its job - say so rather than lie. */
  body.fi-panel-portal .cxp-rows-toggle:focus-visible ~ .cxp-rows-more {
    outline: 3px solid var(--portal-primary);
    outline-offset: 2px;
  }
  body.fi-panel-portal .cxp-rows-toggle:checked ~ .cxp-rows-more { display: none; }
}

/* Tracked-calls line on Search performance. Deliberately styled as a distinct
   statement rather than another stat tile: the figures come from call tracking
   over 30 days while the tiles above are Google Analytics over 28, and putting
   them in the same row would present two sources and two windows as one
   comparison. */
body.fi-panel-portal .cxp-calls-note {
  border-left: 3px solid var(--portal-primary-fill);
  padding-left: 12px;
}
body.fi-panel-portal .cxp-calls-note strong { color: #1b2233; }

/* Links and citations - directory rows. Restored 2026-08-01 from cxp.css.bak-round19-rankings-fix3.
   A cleanup round deleted these rules and left the markup; the page only
   renders them when the client HAS published listings, so the QA client
   (carkeys, zero listings) never showed the damage. Found by sweeping as a
   client with 2,232 listings. Copied verbatim - these were the working
   rules. Third regression of this shape after cxp-taskbar-* and
   cxp-dashboard-stat-grid. */
/
body.fi-panel-portal .cxp-dirrow {
  padding: .7rem 0;
  border-bottom: 1px solid #eef1f5;
}
body.fi-panel-portal .cxp-dirrow:last-child {
  border-bottom: 0;
  padding-bottom: .2rem;
}
body.fi-panel-portal .cxp-dirrow-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: .45rem;
}
body.fi-panel-portal .cxp-dirrow-name {
  font-weight: 650;
  font-size: .9375rem;
  color: #172033;
  text-transform: capitalize;
}
body.fi-panel-portal .cxp-dirrow-count {
  font-size: .8125rem;
  color: #5b6880;
  white-space: nowrap;
}

/* Portal KPI tiles: one shared bottom edge (2026-08-01).
   The stylesheet held two conflicting intents. Line ~1128 sets
   `align-self: stretch` with the note "Page-level KPI tiles share one bottom
   edge when copy wraps"; line ~1864 then sets `align-self: start !important`
   for the same selector, and being later it won - so Search performance
   rendered 164/164/146/146, an 18px ragged row.

   ui-qa's `same-row-stat-heights` is a `fail`, added after ragged rows were
   caught by hand, so the stretch intent is the one that is enforced. Resolved
   here at the end of the file rather than by editing the shared multi-selector
   rule, which also covers the dashboard and autopilot pages.

   NOT height:100% - on an auto-sized grid row that resolves against a
   content-sized area and silently turns the stretch off. Measured in-browser:
   height:100% -> 164/164/146/146; height:auto + stretch -> 164/164/164/164. */
body.fi-panel-portal .cxp-portal-page > .sb-stat-grid {
  align-items: stretch !important;
}
body.fi-panel-portal .cxp-portal-page > .sb-stat-grid > .sb-stat {
  align-self: stretch !important;
  height: auto !important;
}

/* ── Connected accounts, rebuilt 2026-08-01 ──────────────────────────────
   Two defects the user caught in a screenshot that my own QA pass had called
   clean:
   (1) The status pill was `kt-badge kt-badge-sm` + `cxp-badge-good`, and the
       portal only ever overrode `color`. Metronic's `.kt-badge` base sets
       `background-color: var(--secondary)` - a dark chip - so "Connected"
       rendered as dark green text on near-black and was unreadable.
   (2) `.cxp-conn-copy` had no `flex-grow`, so every row hugged the left edge
       and the entire right half of the card was dead space.
   The pill is now its own class with an explicit light background, and the row
   is a real layout with the status pinned to the right edge. */
body.fi-panel-portal .cxp-conn-copy {
  flex: 1 1 auto;
  min-width: 0;
}
body.fi-panel-portal .cxp-conn-name {
  display: block;
  font-size: .9375rem;
  font-weight: 650;
  color: #172033;
  margin-bottom: .18rem;
}
body.fi-panel-portal .cxp-conn-status {
  flex: 0 0 auto;
  margin-left: auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  font-size: .75rem;
  font-weight: 650;
  line-height: 1;
  white-space: nowrap;
  padding: .38rem .65rem;
  border-radius: 999px;
}
body.fi-panel-portal .cxp-conn-status--on {
  background: #E7F6EE;
  border: 1px solid #B7E3CB;
  color: #04663A;
}
body.fi-panel-portal .cxp-conn-status--off {
  background: #FFF4E5;
  border: 1px solid #FFD9A6;
  color: #8A4B00;
}

/* How much of the setup is done, at a glance. */
body.fi-panel-portal .cxp-conn-meter {
  display: block;
  height: .375rem;
  border-radius: 999px;
  background: #EEF1F5;
  overflow: hidden;
  margin: .1rem 0 .35rem;
}
body.fi-panel-portal .cxp-conn-meter-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: #047A44;
}

/* Same defect class as the pill above, on every other page that pairs a
   kt-badge with one of our tone classes - the "indexed" pill on Links and
   citations had the identical dark-on-dark problem. Fixed at the source so it
   cannot recur on a page nobody screenshotted. */
body.fi-panel-portal .kt-badge.cxp-badge-good {
  background-color: #E7F6EE !important;
  border: 1px solid #B7E3CB;
  color: #04663A !important;
}
body.fi-panel-portal .kt-badge.cxp-badge-warn {
  background-color: #FFF4E5 !important;
  border: 1px solid #FFD9A6;
  color: #8A4B00 !important;
}
body.fi-panel-portal .kt-badge.cxp-badge-bad {
  background-color: #FEECEB !important;
  border: 1px solid #FBC9C5;
  color: #B42318 !important;
}

/* ── Map rankings: a real map, not a grid of squares ─────────────────────
   The customer asked, fairly, "what is the point of seeing a lot of squares
   and not seeing where it is listed?" The admin panel has answered this for
   a long time (resources/views/filament/pages/grid-rank.blade.php and
   grid-map-screenshot.blade.php): Leaflet, real tiles, rank pins at their
   true lat/lng. grid_points already stores lat and lng - the portal was
   querying that table and discarding the coordinates. */
body.fi-panel-portal .cxp-map-live {
  height: 30rem;
  width: 100%;
  border-radius: .5rem;
  background: #e8eaed;
  z-index: 0;
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-live { height: 22rem; }
}
body.fi-panel-portal .cxp-map-pin {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-weight: 700;
  font-family: inherit;
  line-height: 1;
  box-shadow: 0 1px 3px rgba(16, 24, 40, .28);
}
body.fi-panel-portal .cxp-map-you {
  border-radius: 50%;
  background: var(--portal-primary-fill);
  border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(16, 24, 40, .4);
}
/* Leaflet injects its own link colours; keep the attribution quiet and legible. */
body.fi-panel-portal .cxp-map-live .leaflet-control-attribution {
  font-size: .625rem;
  background: rgba(255, 255, 255, .8);
}

/* The legend now describes a real map, so its swatches are the exact pin
   colours rather than translucent tints that only roughly matched. */
body.fi-panel-portal .cxp-map-legend .cxp-map-key--good   { background: #047A44; }
body.fi-panel-portal .cxp-map-legend .cxp-map-key--middle { background: #8A4B00; }
body.fi-panel-portal .cxp-map-legend .cxp-map-key--poor   { background: #B42318; }
body.fi-panel-portal .cxp-map-legend .cxp-map-key--none   { background: #667085; }

/* Leaflet ships its attribution link at a blue that lands on 4.1:1 against a
   translucent white strip - under the 4.5 floor for 10px text. Darken the link
   and firm up the strip behind it. */
body.fi-panel-portal .cxp-map-live .leaflet-control-attribution {
  background: rgba(255, 255, 255, .92);
}
body.fi-panel-portal .cxp-map-live .leaflet-control-attribution,
body.fi-panel-portal .cxp-map-live .leaflet-control-attribution a {
  color: #3F4A5A !important;
}

/* GEOMETRY FIX 2026-08-01. The scanned points form a SQUARE grid, but the map
   box was full-card-width and 30rem tall - a wide letterbox. fitBounds then
   fitted the square data to the limiting dimension (height), leaving large
   dead margins to the left and right: the same gaps on the right problem the
   user flagged on Connected accounts, in a different place. Give the map the
   shape of its own data and centre it. */
body.fi-panel-portal .cxp-map-live {
  height: auto;
  aspect-ratio: 1 / 1;
  width: 100%;
  max-width: 40rem;
  margin-inline: auto;
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-live { height: auto; max-width: 100%; }
}

/* The status pill was align-self:center, so on the one row tall enough to
   carry an action callout it floated well below every other pill. Pin all of
   them to the title line instead - consistent regardless of row height. */
body.fi-panel-portal .cxp-conn-status {
  align-self: flex-start;
  margin-top: .02rem;
}

/* Map + legend share a row once there is room. A square map inside a wide card
   leaves a symmetric but large margin either side; the key lives there instead
   of below, so the space reads as layout rather than leftover. */
body.fi-panel-portal .cxp-map-frame { display: block; }
@media (min-width: 62rem) {
  body.fi-panel-portal .cxp-map-frame {
    display: flex;
    gap: 1.75rem;
    align-items: flex-start;
    justify-content: center;
  }
  body.fi-panel-portal .cxp-map-frame .cxp-map-live {
    margin-inline: 0;
    flex: 0 1 42rem;
  }
  body.fi-panel-portal .cxp-map-frame .cxp-map-legend {
    flex: 0 0 auto;
    flex-direction: column;
    align-items: flex-start;
    gap: .7rem;
    padding-top: .25rem;
    margin-top: 0;
  }
}

/* The 40rem cap was set while the legend still sat underneath the map. Now the
   legend is a side column, so the map can use the width it left behind. */
@media (min-width: 62rem) {
  body.fi-panel-portal .cxp-map-frame .cxp-map-live {
    max-width: none;
    flex: 0 1 46rem;
  }
}

/* Four one-per-row cards meant scrolling past four screens of single numbers
   before reaching the map on a phone. These tiles are short (label, number,
   one line of context) and sit comfortably two-up. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .75rem;
  }
}

/* Zebra WAS defined (#ffffff vs #f8fafc) but at a 1.03 contrast ratio it was
   invisible - an independent reviewer read the competitor table as "eight
   plain white rows". The rule existed and still failed its entire purpose, so
   deepen the even row until it actually guides the eye across four columns. */
body.fi-panel-portal .kt-table > tbody > tr:nth-child(even) {
  background: #f1f5f9 !important;
}

/* The blue centre marker is the customer themselves; it now has a legend key. */
body.fi-panel-portal .cxp-map-key--you {
  background: var(--portal-primary-fill);
  border-radius: 50%;
}

/* The action callout is the only thing this page asks the customer to DO. It
   was plain text in a box, aligned to nothing, with nothing to click. */
body.fi-panel-portal .cxp-conn-action {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  flex-wrap: wrap;
  width: 100%;
}
body.fi-panel-portal .cxp-conn-cta {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  background: var(--portal-primary-fill);
  color: var(--portal-primary-foreground);
  font-size: .75rem;
  font-weight: 650;
  line-height: 1;
  padding: .45rem .75rem;
  border-radius: .375rem;
  text-decoration: none;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-conn-cta:hover { background: var(--portal-primary); color: var(--portal-primary-foreground); }

/* On any page taller than the viewport the sidebar's white panel stopped where
   its nav items ended, and the column below reverted to page grey - a visible
   horizontal seam down the left of every long page. Short pages hid it, which
   is why it survived earlier passes. */
body.fi-panel-portal .fi-sidebar,
body.fi-panel-portal .fi-sidebar-nav {
  min-height: 100vh;
}

/* Map rankings comparison and keyword picker, 2026-08-01. */
body.fi-panel-portal .cxp-map-keyword-picker > span {
  font-size: 12px;
  text-transform: none;
}
body.fi-panel-portal .cxp-keyword-select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 2.25rem !important;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%23475569' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 8 4 4 4-4'/%3E%3C/svg%3E") !important;
  background-position: right .7rem center !important;
  background-repeat: no-repeat !important;
  background-size: 1rem 1rem !important;
  cursor: pointer;
  font-size: 14px !important;
}
body.fi-panel-portal .cxp-comparison-table > tbody > tr.cxp-comparison-you {
  background: var(--portal-primary-soft) !important;
  box-shadow: inset 3px 0 var(--portal-primary-fill);
  font-weight: 700;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-comparison-wrap {
    overflow-x: visible !important;
    padding: .75rem !important;
  }
  body.fi-panel-portal .cxp-comparison-table,
  body.fi-panel-portal .cxp-comparison-table > tbody {
    display: block;
    width: 100% !important;
    min-width: 0 !important;
  }
  body.fi-panel-portal .cxp-comparison-table > thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  body.fi-panel-portal .cxp-comparison-table > tbody > tr {
    display: block;
    margin-bottom: .75rem;
    overflow: hidden;
    border: 1px solid var(--portal-border);
    box-shadow: 0 1px 2px rgb(16 24 40 / 6%);
  }
  body.fi-panel-portal .cxp-comparison-table > tbody > tr:last-child {
    margin-bottom: 0;
  }
  body.fi-panel-portal .cxp-comparison-table > tbody > tr > td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    min-height: 2.25rem;
    padding: .55rem .75rem !important;
    text-align: right;
    white-space: nowrap;
  }
  body.fi-panel-portal .cxp-comparison-table > tbody > tr > td::before {
    content: attr(data-label);
    flex: 1 1 auto;
    color: var(--portal-muted);
    font-size: 12px;
    font-weight: 600;
    text-align: left;
  }
  body.fi-panel-portal .cxp-comparison-table > tbody > tr > .cxp-comparison-business {
    display: block;
    min-height: 0;
    padding-block: .7rem !important;
    border-bottom: 1px solid var(--portal-border) !important;
    color: var(--portal-heading);
    font-size: 14px;
    font-weight: 700;
    line-height: 1.35;
    text-align: left;
    white-space: normal;
  }
  body.fi-panel-portal .cxp-comparison-table > tbody > tr > .cxp-comparison-business::before {
    content: none;
  }
}

/* Portal corrections, 2026-08-01. Appended so the existing design layer stays intact. */
body.fi-panel-portal .cxp-map-steady {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(14rem, 20rem);
  align-items: center;
  gap: 1rem;
}
body.fi-panel-portal .cxp-map-steady-copy {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}
body.fi-panel-portal .cxp-map-flat-spark {
  display: block;
  width: 100%;
  height: auto;
  color: var(--portal-heading);
  overflow: visible;
}
body.fi-panel-portal .cxp-map-flat-spark text {
  fill: var(--portal-muted);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
}
body.fi-panel-portal .cxp-map-flat-guide {
  stroke: var(--portal-border);
  stroke-width: 1;
}
body.fi-panel-portal .cxp-map-flat-line {
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 3;
}
body.fi-panel-portal .cxp-conn-title-line {
  display: flex;
  align-items: center;
  gap: .55rem;
  flex-wrap: wrap;
}
body.fi-panel-portal .cxp-conn-title-line .cxp-conn-name {
  margin-bottom: 0;
}
body.fi-panel-portal .cxp-conn-title-line .cxp-conn-status {
  align-self: center;
  margin: 0;
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-steady {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-topbar-greeting {
    display: none !important;
  }
  body.fi-panel-portal .cxp-topbar-context {
    display: flex !important;
    flex: 0 1 auto;
    gap: 0;
    margin-inline: .25rem auto;
    min-width: 0;
  }
  body.fi-panel-portal .cxp-topbar-context-copy {
    display: none !important;
  }
  body.fi-panel-portal .cxp-siteswitch {
    flex: 0 1 auto;
    width: clamp(5.5rem, 26vw, 6.5rem);
  }
  body.fi-panel-portal .cxp-siteswitch-label {
    display: none;
  }
  body.fi-panel-portal .cxp-siteswitch-select {
    width: 100%;
    min-width: 5.5rem;
    max-width: 6.5rem;
  }
}

/* Portal pixel alignment corrections, 2026-08-02. */
@media (min-width: 761px) {
  body.fi-panel-portal .cxp-portal-footer {
    padding-inline: 2rem;
  }
}

body.fi-panel-portal .fi-sidebar-nav-groups {
  margin-inline: 0 !important;
}

body.fi-panel-portal .kt-card:has(.cxp-conn-row) > .kt-card-header {
  position: relative;
  border-bottom: 0 !important;
}

body.fi-panel-portal .kt-card:has(.cxp-conn-row) > .kt-card-header::after {
  position: absolute;
  right: 1.25rem;
  bottom: 0;
  left: 1.25rem;
  height: 1px;
  background: var(--portal-border);
  content: "";
}

body.fi-panel-portal .fi-sidebar-nav-groups {
  width: 100% !important;
}

body.fi-panel-portal .fi-sidebar-nav-groups {
  width: calc(100% + .5rem) !important;
}

/* Portal review corrections, 2026-08-02. */
body.fi-panel-portal .cxp-connections-card {
  width: 100%;
  max-width: 56.25rem;
}

body.fi-panel-portal .cxp-map-legend .cxp-map-key--middle {
  background: #F4C430;
  border: 2px solid #6B5200;
  box-sizing: border-box;
}

body.fi-panel-portal .cxp-map-pin--middle {
  border: 2px solid #6B5200;
  box-sizing: border-box;
}

body.fi-panel-portal .cxp-map-live .leaflet-control-attribution,
body.fi-panel-portal .cxp-map-live .leaflet-control-attribution a {
  font-size: 12px;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-live .leaflet-control-zoom {
    display: none;
  }

  /* The taskbar and account controls consume the mobile topbar's fixed row.
     Do not squeeze a real business name into a misleading three-character
     ellipsis; the name returns at the wider breakpoint where it has room to
     remain readable. */
  body.fi-panel-portal .cxp-topbar-context > .cxp-topbar-business {
    display: none !important;
  }

  body.fi-panel-portal .cxp-topbar-context-copy {
    overflow: hidden;
  }

  body.fi-panel-portal .cxp-topbar-business {
    display: block;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    overflow-wrap: normal;
    word-break: normal;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

body.fi-panel-portal .cxp-map-pin--good {
  border: 2px solid transparent;
  box-sizing: border-box;
}

body.fi-panel-portal .cxp-map-pin--poor {
  border: 3px double #FFFFFF;
  box-sizing: border-box;
}

body.fi-panel-portal .cxp-map-pin--none {
  border: 2px dashed #FFFFFF;
  box-sizing: border-box;
}

body.fi-panel-portal .cxp-map-legend .cxp-map-key--good {
  border: 2px solid transparent;
  box-sizing: border-box;
}

body.fi-panel-portal .cxp-map-legend .cxp-map-key--poor {
  border: 3px double #FFFFFF;
  box-sizing: border-box;
}

body.fi-panel-portal .cxp-map-legend .cxp-map-key--none {
  border: 2px dashed #FFFFFF;
  box-sizing: border-box;
}

body.fi-panel-portal .cxp-map-cell--middle {
  background: #F4C430;
  border: 2px solid #6B5200;
  box-sizing: border-box;
  color: #3D3000;
}

/* Portal independent-review corrections, 2026-08-02. */
body.fi-panel-portal .cxp-map-legend-heading,
body.fi-panel-portal .cxp-map-legend-range {
  display: grid;
  gap: .3rem;
}

body.fi-panel-portal .cxp-map-legend-heading {
  padding-bottom: .85rem;
  border-bottom: 1px solid var(--portal-border);
}

body.fi-panel-portal .cxp-map-legend-heading > strong {
  color: var(--portal-heading);
  font-size: 14px;
}

body.fi-panel-portal .cxp-map-legend-heading > span,
body.fi-panel-portal .cxp-map-legend-range > span {
  color: var(--portal-muted);
  font-size: 12px;
}

body.fi-panel-portal .cxp-map-legend-rows {
  display: grid;
  gap: .75rem;
}

body.fi-panel-portal .cxp-map-legend-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-width: 0;
}

body.fi-panel-portal .cxp-map-legend-row > strong,
body.fi-panel-portal .cxp-map-legend-range strong {
  color: var(--portal-heading);
  font-variant-numeric: tabular-nums;
}

body.fi-panel-portal .cxp-map-legend-range {
  margin-top: auto;
  padding-top: .85rem;
  border-top: 1px solid var(--portal-border);
}

body.fi-panel-portal .cxp-map-legend-range > span {
  justify-content: space-between;
}

body.fi-panel-portal .cxp-map-axis-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: .75rem;
  color: var(--portal-heading);
  font-size: 14px;
  font-weight: 700;
}

body.fi-panel-portal .cxp-map-axis-label > span {
  color: var(--portal-muted);
  font-size: 12px;
  font-weight: 600;
}

body.fi-panel-portal .cxp-map-flat-chart,
body.fi-panel-portal .cxp-connections-summary {
  min-width: 0;
  width: 100%;
}

body.fi-panel-portal .cxp-connections-summary .cxp-conn-meter {
  margin-top: .8rem;
}

body.fi-panel-portal .cxp-connections-message-link {
  color: var(--portal-primary);
  font-weight: 650;
  text-decoration: underline;
  text-underline-offset: 2px;
}

@media (min-width: 62rem) {
  body.fi-panel-portal .cxp-map-frame .cxp-map-legend {
    align-self: stretch;
    align-items: stretch;
    justify-content: space-between;
    min-width: 13rem;
    padding: 1rem;
    border: 1px solid var(--portal-border);
    border-radius: .5rem;
    background: var(--portal-surface-soft);
  }
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-legend {
    display: grid;
    gap: .85rem;
    padding: .85rem;
    border: 1px solid var(--portal-border);
    border-radius: .5rem;
    background: var(--portal-surface-soft);
  }

  body.fi-panel-portal .cxp-map-legend-rows {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  body.fi-panel-portal .cxp-map-legend-row {
    gap: .5rem;
  }

  body.fi-panel-portal .cxp-map-legend-row--you {
    grid-column: 1 / -1;
  }

  body.fi-panel-portal .cxp-comparison-table > tbody > tr:last-child > td:not(.cxp-comparison-business):not(:last-child) {
    border-bottom: 1px solid var(--portal-border) !important;
  }
}

/* Keep the desktop map easy to take in at a glance. The mobile map remains a
   full-width square, while ResizeObserver re-fits the Leaflet bounds whenever
   this desktop cap changes the rendered size. */
@media (min-width: 62rem) {
  body.fi-panel-portal .cxp-map-frame {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
    width: min(100%, 620px);
    margin-inline: auto;
  }

  body.fi-panel-portal .cxp-map-frame .cxp-map-live,
  body.fi-panel-portal .cxp-map-frame .cxp-map-grid-wrap {
    width: 100%;
    max-width: none;
    margin-inline: 0;
  }

  body.fi-panel-portal .cxp-map-frame .cxp-map-legend {
    display: grid;
    grid-template-columns: minmax(9rem, .7fr) minmax(0, 2fr) minmax(12rem, 1fr);
    align-self: auto;
    align-items: center;
    gap: 1rem;
    width: 100%;
    min-width: 0;
    margin-top: 0;
  }

  body.fi-panel-portal .cxp-map-frame .cxp-map-legend-heading {
    padding: 0 1rem 0 0;
    border-right: 1px solid var(--portal-border);
    border-bottom: 0;
  }

  body.fi-panel-portal .cxp-map-frame .cxp-map-legend-rows {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem 1.25rem;
  }

  body.fi-panel-portal .cxp-map-frame .cxp-map-legend-row {
    gap: .5rem;
  }

  body.fi-panel-portal .cxp-map-frame .cxp-map-legend-range {
    margin-top: 0;
    padding: 0 0 0 1rem;
    border-top: 0;
    border-left: 1px solid var(--portal-border);
  }
}

body.fi-panel-portal .cxp-map-trend-axis--flat {
  padding-left: 10%;
  padding-right: 1.5%;
}

/* Portal final review corrections, 2026-08-02. */
body.fi-panel-portal .cxp-connections-card {
  max-width: none;
}

body.fi-panel-portal .cxp-connections-card > .kt-card-content {
  padding-top: .35rem;
}

body.fi-panel-portal .cxp-map-flat-dot {
  fill: currentColor;
}

body.fi-panel-portal .cxp-map-flat-spark .cxp-map-flat-value {
  fill: var(--portal-heading);
  font-size: 12px;
  font-weight: 700;
}

body.fi-panel-portal .cxp-sort-indicator {
  display: inline-block;
  margin-left: .25rem;
  font-size: 12px;
  line-height: 1;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-comparison-table > tbody > tr > td {
    justify-content: flex-start;
    text-align: left;
  }

  body.fi-panel-portal .cxp-comparison-table > tbody > tr > td::before {
    flex: 0 0 8.5rem;
  }
}

/* Portal map review corrections, 2026-08-02. */
body.fi-panel-portal .cxp-map-trend-plot {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr);
  gap: .5rem;
}

body.fi-panel-portal .cxp-map-scale-labels {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 7rem;
  color: var(--portal-muted);
  font-size: 12px;
  font-weight: 600;
}

/* Connections.php is outside this review's edit scope. Replace its slashed
   chain visually with the matching plain-chain Heroicon on every portal page. */
body.fi-panel-portal a[href$="/portal/connections"] svg.fi-sidebar-item-icon {
  background-color: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244'/%3E%3C/svg%3E") center / contain no-repeat;
}

body.fi-panel-portal a[href$="/portal/connections"] svg.fi-sidebar-item-icon > * {
  display: none;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-legend-row--you {
    grid-column: auto;
  }

  body.fi-panel-portal .cxp-comparison-table > tbody > tr > td::before {
    flex-basis: 6.75rem;
  }
}

body.fi-panel-portal .cxp-map-trend-plot + .cxp-map-trend-axis {
  padding-left: 2rem;
}

body.fi-panel-portal a[href$="/portal/connections"] svg.fi-sidebar-item-icon {
  background-color: currentColor !important;
}

/* Portal should-fix review, 2026-08-02. */
body.fi-panel-portal .cxp-connections-card {
  max-width: 56.25rem;
}

@media (min-width: 641px) {
  body.fi-panel-portal .cxp-conn-title-line {
    width: 100%;
    flex-wrap: nowrap;
  }

  body.fi-panel-portal .cxp-conn-title-line .cxp-conn-status {
    margin-left: auto;
  }
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-keyword-picker,
  body.fi-panel-portal .cxp-keyword-select {
    width: 100%;
    max-width: none;
  }
}

/* Final portal review corrections, 2026-08-02. The red band's extra cue is
   drawn outside the pin so it never reduces the number's content box. */
body.fi-panel-portal .cxp-map-pin--poor {
  border: 0;
  box-shadow:
    0 0 0 2px #fff,
    0 0 0 3px #7a271a,
    0 2px 4px rgba(16, 24, 40, .32);
}

/* The last review reintroduced a 56.25rem cap while moving status badges.
   Badge alignment is internal to each row and does not require a card cap. */
body.fi-panel-portal .cxp-connections-card {
  width: 100%;
  max-width: none !important;
}

/* Keep the customer's ranked pin and place a stronger blue and white target
   near its edge. The desktop offset keeps the rank digit unobstructed. */
body.fi-panel-portal .cxp-map-you {
  width: 12px !important;
  height: 12px !important;
  box-sizing: border-box;
  border: 3px solid #fff;
  background: var(--portal-primary-fill);
  box-shadow: 0 0 0 2px var(--portal-primary-fill), 0 2px 6px rgba(16, 24, 40, .48);
  pointer-events: none;
}

@media (min-width: 641px) {
  body.fi-panel-portal .cxp-map-you {
    transform: translate(7px, -7px);
  }
}

/* Mobile controls live in the map padding at bottom left, outside the pins. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-live .leaflet-control-zoom {
    display: block !important;
    margin-bottom: 10px;
    margin-left: 10px;
  }

  body.fi-panel-portal .cxp-map-live .leaflet-control-zoom a {
    font-size: 20px;
  }
}

/* Portal map pin spacing and identification, 2026-08-02. The map goes to the
   card edges on phones so fitBounds has enough pitch for two-digit ranks. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-frame .cxp-map-live {
    width: calc(100% + 2.5rem);
    max-width: none;
    margin-inline: -1.25rem;
  }

  body.fi-panel-portal .cxp-map-legend-rows {
    column-gap: 1.5rem;
  }

  body.fi-panel-portal .cxp-map-legend-row {
    justify-content: flex-start;
    gap: .35rem;
  }
}

body.fi-panel-portal .cxp-map-pin {
  min-width: 0;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.03em;
  white-space: nowrap;
}

/* A larger teardrop with a white halo identifies the customer without sitting
   on the ranked location beneath it. Its Leaflet anchor offsets it above and
   to the right of that location at both viewport sizes. */
body.fi-panel-portal .cxp-map-you {
  width: 18px !important;
  height: 18px !important;
  border: 3px solid #fff;
  border-radius: 50% 50% 50% 0;
  background: var(--portal-primary-fill);
  box-shadow: 0 0 0 2px var(--portal-primary-fill), 0 2px 6px rgba(16, 24, 40, .48);
  transform: rotate(-45deg) !important;
  transform-origin: center;
}

body.fi-panel-portal .cxp-map-key--you {
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
}

/* Attribution remains visible for licensing, but should read as map metadata
   rather than as another line of portal body copy. */
body.fi-panel-portal .cxp-map-live .leaflet-control-attribution,
body.fi-panel-portal .cxp-map-live .leaflet-control-attribution a {
  color: #6b7280 !important;
  font-size: 11px !important;
  line-height: 1.2;
}

/* Leave an 8px viewport gutter on phones. The extra pitch gives two-digit
   labels breathing room without reducing the required 11px type. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-frame .cxp-map-live {
    width: calc(100vw - 1rem);
    margin-inline: calc((100% - (100vw - 1rem)) / 2);
  }
}

/* The amber 10 is also two digits, so its dark outline belongs outside the
   circle just like the red band's ring. The 3px worst-case ring budget used by
   pinMetrics still governs spacing for every band. */
body.fi-panel-portal .cxp-map-pin--middle {
  border: 0;
  box-shadow: 0 0 0 2px #6b5200, 0 1px 3px rgba(16, 24, 40, .28);
}

/* Map Rankings feature tabs, history, comparison, and GBP snapshot details.
   Appended for codex13. All selectors stay portal-scoped. */
body.fi-panel-portal [x-cloak] {
  display: none !important;
}

body.fi-panel-portal .cxp-header-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .75rem;
  flex-wrap: wrap;
}

body.fi-panel-portal .cxp-live-status {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  min-height: 32px;
  padding: .35rem .7rem;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.2;
  white-space: nowrap;
}

/* CYT-814: `.cxp-live-status` above is the STANDALONE status pill (card
   headers, 32px min-height). The dashboard "What's next" table and the
   Autopilot SEO table also put that class on a `.kt-badge.kt-badge-sm`,
   whose own size tier is 20px (metronic/components.css: `.kt-badge-sm {
   height: calc(var(--spacing) * 5) }`). The 32px min-height then beat that
   height, so ONE column rendered 32px badges on the rows carrying a status
   LED and 20px on the rows without - a 60% height difference inside one
   column, same class, same role.

   The nested 8px `.cxp-status-led` was NOT the cause. Measured live: the LED
   is border-box, 8px x 8px, well under the badge's own line box, and its
   ring is a box-shadow, which takes no layout space. Removing the LED would
   not have moved the number. The cause is two size tiers on one node.

   When both tiers are present the BADGE tier owns the geometry; a standalone
   `.cxp-live-status` that is not a badge keeps its 32px. */
body.fi-panel-portal .kt-badge.cxp-live-status,
body.fi-panel-portal [class*="kt-badge"].cxp-live-status {
  min-height: 0;
}

body.fi-panel-portal a.cxp-live-status {
  text-decoration: none;
}

body.fi-panel-portal a.cxp-live-status:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

body.fi-panel-portal .cxp-live-status--good {
  color: #0b5c36;
  background: #eafaf3;
}

body.fi-panel-portal .cxp-live-status--middle {
  color: #6b3a00;
  background: #fff7e6;
}

body.fi-panel-portal .cxp-live-status--poor {
  color: #912018;
  background: #fff1f0;
}

body.fi-panel-portal .cxp-status-led {
  display: inline-block;
  flex: 0 0 .55rem;
  width: .55rem;
  height: .55rem;
  border: 2px solid rgb(255 255 255 / 85%);
  border-radius: 50%;
  box-shadow: 0 0 0 2px currentColor;
  vertical-align: middle;
}

body.fi-panel-portal .cxp-status-led--good { background: var(--cxp-tone-good); }
body.fi-panel-portal .cxp-status-led--middle { background: var(--cxp-tone-middle); }
body.fi-panel-portal .cxp-status-led--poor { background: var(--cxp-tone-poor); }
body.fi-panel-portal .cxp-status-led--neutral { background: var(--cxp-tone-neutral); }

@keyframes cxp-led-pulse {
  0%, 100% { opacity: .48; transform: scale(.84); }
  50% { opacity: 1; transform: scale(1.14); }
}

body.fi-panel-portal .cxp-led {
  display: inline-block;
  flex: 0 0 .55rem;
  width: .55rem;
  height: .55rem;
  border: 2px solid rgb(255 255 255 / 85%);
  border-radius: 50%;
  box-shadow: 0 0 0 2px currentColor;
  animation: cxp-led-pulse 1.6s ease-in-out infinite;
  vertical-align: middle;
}

body.fi-panel-portal .cxp-led--good { background: var(--cxp-tone-good); }
body.fi-panel-portal .cxp-led--middle { background: var(--cxp-tone-middle); }
body.fi-panel-portal .cxp-led--poor { background: var(--cxp-tone-poor); }
body.fi-panel-portal .cxp-led--neutral { background: var(--cxp-tone-neutral); }

body.fi-panel-portal .cxp-inline-live-status {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  color: #0b5c36;
  font-weight: 700;
}

body.fi-panel-portal :is(.kt-badge, .cxp-live-status) > .cxp-led {
  margin-right: .1rem;
}

body.fi-panel-portal :is(.cxp-activity-status, .sb-stat-label, .kt-badge) > .cxp-status-led {
  margin-right: .4rem;
}

body.fi-panel-portal .cxp-work-status-card .kt-card-header {
  align-items: center;
  gap: 1rem;
}

body.fi-panel-portal .cxp-work-evidence-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  gap: 0;
  padding: 0;
}

body.fi-panel-portal .cxp-work-evidence {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  min-width: 0;
  padding: 1rem 1.15rem;
  border-right: 1px solid var(--portal-border);
  border-bottom: 1px solid var(--portal-border);
  background: var(--portal-surface);
}

body.fi-panel-portal .cxp-work-evidence > span {
  color: var(--portal-muted);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
}

body.fi-panel-portal .cxp-work-evidence strong {
  color: var(--portal-heading);
  font-size: 14px;
  line-height: 1.4;
  overflow-wrap: anywhere;
}

body.fi-panel-portal .cxp-work-evidence small {
  color: var(--portal-muted);
  font-size: 12px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

body.fi-panel-portal .cxp-health-progress-fill {
  transform-origin: left center;
}

body.fi-panel-portal .cxp-speed-fill,
body.fi-panel-portal .cxp-bar,
body.fi-panel-portal .cxp-map-bar-fill {
  transform-origin: center bottom;
}

@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-status-led,
  body.fi-panel-portal .cxp-led {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

body.fi-panel-portal .cxp-map-details,
body.fi-panel-portal .cxp-map-tab-panel {
  display: grid;
  gap: 1rem;
  min-width: 0;
}

body.fi-panel-portal .cxp-map-tabs {
  display: flex;
  gap: .25rem;
  padding: .25rem;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: .65rem;
  background: #fff;
  scrollbar-width: thin;
}

body.fi-panel-portal .cxp-map-tabs > button {
  flex: 0 0 auto;
  min-height: 38px;
  padding: .55rem .8rem;
  border: 0;
  border-radius: .45rem;
  background: transparent;
  color: #475467;
  font-size: .8125rem;
  font-weight: 700;
  line-height: 1.2;
  cursor: pointer;
}

body.fi-panel-portal .cxp-map-tabs > button:hover,
body.fi-panel-portal .cxp-map-tabs > button:focus-visible {
  background: #f2f4f7;
  color: #101828;
  outline: none;
}

body.fi-panel-portal .cxp-map-tabs > button.is-active {
  background: var(--portal-primary-fill);
  color: var(--portal-primary-foreground);
  box-shadow: 0 1px 2px rgba(16, 24, 40, .18);
}

body.fi-panel-portal .cxp-change-counts,
body.fi-panel-portal .cxp-history-figures {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 1px;
  border-block: 1px solid var(--border);
  background: var(--border);
}

body.fi-panel-portal .cxp-change-counts > div,
body.fi-panel-portal .cxp-history-figures > div {
  display: grid;
  gap: .2rem;
  min-width: 0;
  padding: .8rem 1rem;
  background: #fff;
}

body.fi-panel-portal .cxp-change-counts span,
body.fi-panel-portal .cxp-history-figures span {
  color: #667085;
  font-size: .75rem;
  font-weight: 700;
  line-height: 1.25;
}

body.fi-panel-portal .cxp-change-counts strong,
body.fi-panel-portal .cxp-history-figures strong {
  color: #101828;
  font-size: 1.35rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

body.fi-panel-portal .cxp-history-figures {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-bottom: 0;
}

body.fi-panel-portal .cxp-movement-good {
  border-color: #a6f4c5;
  background: #ecfdf3;
  color: #067647;
}

body.fi-panel-portal .cxp-movement-poor {
  border-color: #fecdca;
  background: #fef3f2;
  color: #b42318;
}

body.fi-panel-portal .cxp-history-row--selected > td {
  background: var(--portal-primary-soft) !important;
}

body.fi-panel-portal .cxp-history-table td,
body.fi-panel-portal .cxp-history-table th,
body.fi-panel-portal .cxp-movers-table td,
body.fi-panel-portal .cxp-movers-table th,
body.fi-panel-portal .cxp-profile-facts td,
body.fi-panel-portal .cxp-profile-facts th,
body.fi-panel-portal .cxp-review-table td,
body.fi-panel-portal .cxp-review-table th {
  min-width: 7rem;
  font-size: .8125rem;
}

body.fi-panel-portal .cxp-profile-facts th {
  width: 34%;
  color: #667085;
  font-weight: 700;
  text-align: left;
}

body.fi-panel-portal .cxp-review-table th:first-child,
body.fi-panel-portal .cxp-review-table td:first-child {
  width: 7rem;
  white-space: nowrap;
}

body.fi-panel-portal .cxp-review-table th:nth-child(2),
body.fi-panel-portal .cxp-review-table td:nth-child(2) {
  width: 9rem;
  white-space: nowrap;
}

body.fi-panel-portal .cxp-review-table td:last-child {
  min-width: 20rem;
  white-space: normal;
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-map-tabs {
    margin-inline: -.25rem;
  }

  body.fi-panel-portal .cxp-change-counts,
  body.fi-panel-portal .cxp-history-figures {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  body.fi-panel-portal .cxp-change-counts > div:last-child {
    grid-column: 1 / -1;
  }

  body.fi-panel-portal .cxp-history-table td,
  body.fi-panel-portal .cxp-history-table th {
    min-width: 6.5rem;
  }
}

body.fi-panel-portal .cxp-history-selection {
  overflow: hidden;
}

body.fi-panel-portal .cxp-map-tab-panel > .kt-card {
  opacity: 1 !important;
  transform: none !important;
}

/* Rankings data honesty, 2026-08-02. Appended so the shared portal layer and
   existing in-progress CSS remain untouched. */
body.fi-panel-portal .cxp-rankings-source-note {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  padding: .9rem 1rem;
  border: 1px solid var(--cxp-good-line);
  border-left: 4px solid var(--portal-primary-fill);
  border-radius: .65rem;
  background: var(--portal-primary-soft);
  color: var(--portal-primary);
  font-size: .8125rem;
  line-height: 1.5;
}

body.fi-panel-portal .cxp-rankings-source-note > div {
  display: flex;
  flex-direction: column;
  gap: .25rem;
}

body.fi-panel-portal .cxp-rankings-source-note strong {
  color: var(--portal-heading);
  font-size: .875rem;
}

body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-hist,
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now {
  text-align: left !important;
  vertical-align: top;
  white-space: normal;
}

body.fi-panel-portal .cxp-rankings-keyword-table > tbody:nth-of-type(odd) > tr:first-child {
  background: #ffffff !important;
}

body.fi-panel-portal .cxp-rankings-keyword-table > tbody:nth-of-type(even) > tr:first-child {
  background: #f8fafc !important;
}

body.fi-panel-portal .cxp-rankings-mobile-detail-row {
  display: none;
}

body.fi-panel-portal .cxp-rankings-mobile-detail-row > td {
  padding: .55rem .75rem .8rem;
}

body.fi-panel-portal .cxp-rankings-mobile-details summary {
  display: flex;
  min-height: 44px;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding: .5rem .65rem;
  border: 1px solid var(--portal-border);
  border-radius: .5rem;
  color: var(--portal-primary);
  cursor: pointer;
  font-size: .78rem;
  font-weight: 700;
  list-style: none;
}

body.fi-panel-portal .cxp-rankings-mobile-details summary::-webkit-details-marker {
  display: none;
}

body.fi-panel-portal .cxp-rankings-mobile-details summary::after {
  content: '+';
  flex: 0 0 auto;
  font-size: 1.1rem;
  line-height: 1;
}

body.fi-panel-portal .cxp-rankings-mobile-details[open] summary::after {
  content: '-';
}

body.fi-panel-portal .cxp-rankings-mobile-detail-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .6rem;
  margin: .65rem 0 0;
}

body.fi-panel-portal .cxp-rankings-mobile-detail-grid > div {
  min-width: 0;
  padding: .6rem;
  border: 1px solid var(--portal-border);
  border-radius: .45rem;
  background: var(--portal-surface-soft);
}

body.fi-panel-portal .cxp-rankings-mobile-detail-grid > div:first-child {
  grid-column: 1 / -1;
}

body.fi-panel-portal .cxp-rankings-mobile-detail-grid dt {
  color: var(--portal-muted);
  font-size: 12px;
  font-weight: 750;
  letter-spacing: .03em;
  text-transform: uppercase;
}

body.fi-panel-portal .cxp-rankings-mobile-detail-grid dd {
  min-width: 0;
  margin: .25rem 0 0;
  overflow-wrap: anywhere;
  word-break: break-word;
}

body.fi-panel-portal .cxp-rankings-mobile-detail-grid .cxp-ranking-page-link {
  overflow-wrap: anywhere;
  word-break: break-word;
}

body.fi-panel-portal .cxp-rankings-position-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .15rem;
  min-width: 8.75rem;
  text-align: left;
  white-space: normal;
}

body.fi-panel-portal .cxp-rankings-position-stack small {
  color: #667085;
  font-size: .75rem;
  line-height: 1.35;
}

body.fi-panel-portal .cxp-rankings-position-stack .cxp-rankings-too-little {
  color: #8a4b00;
  font-weight: 700;
}

body.fi-panel-portal .cxp-rankings-keyword-table tr.cxp-rankings-low-data > td {
  color: #667085;
  box-shadow: inset 3px 0 0 rgba(138, 75, 0, .28);
}

/* Map rankings review corrections, codex15. */
body.fi-panel-portal .cxp-movers-table th:first-child {
  width: 18rem;
}

body.fi-panel-portal .cxp-movers-table th:nth-child(2) {
  width: 10rem;
}

body.fi-panel-portal .cxp-map-focus-pin {
  min-height: 32px;
  padding: .25rem .4rem;
  border: 0;
  border-radius: .35rem;
  background: transparent;
  color: var(--portal-primary);
  font-size: .8125rem;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

body.fi-panel-portal .cxp-map-focus-pin:hover,
body.fi-panel-portal .cxp-map-focus-pin:focus-visible {
  background: var(--portal-primary-soft);
  outline: 2px solid var(--cxp-good-line);
}

body.fi-panel-portal .cxp-map-pin.is-focused {
  transform: scale(1.35);
  box-shadow: 0 0 0 4px #fff, 0 0 0 7px var(--portal-primary-fill), 0 3px 8px rgba(16, 24, 40, .4);
  z-index: 2;
}

body.fi-panel-portal .cxp-profile-facts {
  width: min(100%, 42rem) !important;
  table-layout: fixed;
}

body.fi-panel-portal .kt-card-content > .kt-table.cxp-profile-facts {
  width: min(100%, 42rem) !important;
  max-width: 42rem !important;
  min-width: 0 !important;
}

body.fi-panel-portal .cxp-profile-facts th {
  width: 12rem;
}

body.fi-panel-portal .cxp-review-text {
  white-space: normal !important;
  overflow: visible !important;
  text-overflow: clip !important;
  overflow-wrap: break-word;
}

body.fi-panel-portal .cxp-map-live .leaflet-control-attribution,
body.fi-panel-portal .cxp-map-live .leaflet-control-attribution a {
  font-size: 12px !important;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-live .leaflet-control-zoom {
    margin: 10px 10px 0 0 !important;
  }

  body.fi-panel-portal .cxp-change-counts > div:last-child {
    grid-column: 1 / -1;
  }

  body.fi-panel-portal .cxp-mobile-card-table,
  body.fi-panel-portal .cxp-mobile-card-table > tbody {
    display: block;
    width: 100% !important;
    min-width: 0 !important;
  }

  body.fi-panel-portal .cxp-mobile-card-table > thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr {
    display: block;
    margin: .75rem;
    overflow: hidden;
    border: 1px solid var(--portal-border);
    background: #fff !important;
    box-shadow: 0 1px 2px rgb(16 24 40 / 6%);
  }

  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr:nth-child(even) {
    background: #f8fafc !important;
  }

  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr > td {
    display: grid;
    grid-template-columns: minmax(6.75rem, 38%) minmax(0, 1fr);
    gap: .75rem;
    width: 100% !important;
    min-width: 0 !important;
    padding: .6rem .75rem !important;
    border-bottom: 1px solid var(--portal-border) !important;
    text-align: left;
    white-space: normal;
    overflow-wrap: break-word;
  }

  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr > td::before {
    content: attr(data-label);
    color: var(--portal-muted);
    font-size: 12px;
    font-weight: 700;
  }

  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr > td:last-child {
    border-bottom: 0 !important;
  }

  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr > .cxp-mobile-card-action {
    display: block;
  }

  body.fi-panel-portal .cxp-mobile-card-action > .kt-btn {
    width: 100%;
    min-height: 40px;
  }

  body.fi-panel-portal .cxp-profile-facts,
  body.fi-panel-portal .cxp-profile-facts > tbody {
    display: block;
    width: 100% !important;
  }

  body.fi-panel-portal .cxp-profile-facts > tbody > tr {
    display: grid;
    grid-template-columns: minmax(7rem, 40%) minmax(0, 1fr);
    margin: .75rem;
    overflow: hidden;
    border: 1px solid var(--portal-border);
    background: #fff !important;
    box-shadow: 0 1px 2px rgb(16 24 40 / 6%);
  }

  body.fi-panel-portal .cxp-profile-facts > tbody > tr > th,
  body.fi-panel-portal .cxp-profile-facts > tbody > tr > td {
    display: block;
    width: auto !important;
    min-width: 0 !important;
    padding: .7rem .75rem !important;
    white-space: normal;
    overflow-wrap: break-word;
  }
}

/* Map rankings final polish, codex16. */
body.fi-panel-portal .cxp-movers-table {
  table-layout: fixed;
}

body.fi-panel-portal .cxp-movers-table th:first-child,
body.fi-panel-portal .cxp-movers-table td:first-child {
  width: 32%;
}

body.fi-panel-portal .cxp-movers-table th:nth-child(2),
body.fi-panel-portal .cxp-movers-table td:nth-child(2) {
  width: 28%;
}

body.fi-panel-portal .cxp-movers-table th:nth-child(3),
body.fi-panel-portal .cxp-movers-table td:nth-child(3),
body.fi-panel-portal .cxp-movers-table th:nth-child(4),
body.fi-panel-portal .cxp-movers-table td:nth-child(4) {
  width: 20%;
}

body.fi-panel-portal .cxp-map-legend-range > span {
  display: flex;
  justify-content: flex-start;
  gap: .3rem;
  text-align: left;
}

body.fi-panel-portal .cxp-map-legend-range strong {
  text-align: left;
}

body.fi-panel-portal .cxp-history-date {
  white-space: nowrap;
}

@media (min-width: 641px) {
  body.fi-panel-portal .cxp-map-steady {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
/* Position movement keeps every populated band visible at every card width. */
body.fi-panel-portal .cxp-rankings-bucket-table-compact {
  min-width: 0;
}

/* One shell separator spans the logo block and the full topbar. */
body.fi-panel-portal .fi-sidebar-header,
body.fi-panel-portal .fi-topbar {
  border-bottom: 0 !important;
}

body.fi-panel-portal .fi-topbar {
  overflow: visible !important;
}

body.fi-panel-portal .fi-topbar::after {
  content: "";
  position: absolute;
  z-index: 1;
  top: auto;
  right: 0;
  bottom: 0;
  left: -260px;
  width: auto;
  height: 1px;
  background: var(--portal-border);
  pointer-events: none;
}

@media (max-width: 1023px) {
  body.fi-panel-portal .fi-topbar::after {
    left: 0;
  }
}

body.fi-panel-portal .cxp-motion-count {
  font-variant-numeric: tabular-nums;
}

body.fi-panel-portal .cxp-title-line {
  display: flex;
  align-items: center;
  gap: .65rem;
  min-width: 0;
  flex-wrap: wrap;
}

body.fi-panel-portal .cxp-title-line .cxp-page-title {
  margin: 0;
}

body.fi-panel-portal .cxp-page-explainer {
  position: relative;
  z-index: 20;
}

body.fi-panel-portal .cxp-page-explainer-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .35rem;
  min-height: 32px;
  padding: .35rem .65rem;
  border: 1px solid var(--cxp-good-line);
  border-radius: 999px;
  color: var(--portal-primary);
  background: var(--portal-primary-soft);
  font-size: 12px;
  font-weight: 700;
  line-height: 1.2;
  cursor: pointer;
}

body.fi-panel-portal .cxp-page-explainer-trigger:hover,
body.fi-panel-portal .cxp-page-explainer-trigger:focus-visible,
body.fi-panel-portal .cxp-page-explainer-trigger[aria-expanded="true"] {
  border-color: var(--portal-primary-fill);
  background: var(--portal-primary-soft);
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cxp-good) 15%, transparent);
}

body.fi-panel-portal .cxp-page-explainer-trigger i {
  font-size: 16px;
}

body.fi-panel-portal .cxp-page-explainer-panel {
  position: absolute;
  top: calc(100% + .5rem);
  left: 0;
  width: min(32rem, calc(100vw - 2rem));
  padding: .85rem 1rem;
  border: 1px solid #c9d6e8;
  border-radius: .75rem;
  color: var(--portal-text);
  background: #fff;
  box-shadow: 0 12px 32px rgb(15 23 42 / 16%);
  font-size: 12px;
  line-height: 1.5;
}

body.fi-panel-portal .cxp-page-explainer-panel strong {
  display: block;
  margin-bottom: .35rem;
  color: var(--portal-heading);
  font-size: 13px;
}

body.fi-panel-portal .cxp-page-explainer-panel p {
  margin: .3rem 0 0;
}

body.fi-panel-portal .cxp-working-since {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  margin-top: .35rem;
  color: #475569;
  font-size: 12px;
  font-weight: 650;
}

body.fi-panel-portal .cxp-working-since i {
  color: var(--portal-primary);
  font-size: 15px;
}

body.fi-panel-portal [x-cloak] {
  display: none !important;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-title-line {
    align-items: flex-start;
  }

  body.fi-panel-portal .cxp-page-explainer-panel {
    position: fixed;
    top: 82px;
    right: 1rem;
    left: 1rem;
    width: auto;
    max-height: calc(100vh - 98px);
    overflow-y: auto;
  }

  body.fi-panel-portal .cxp-header-actions {
    width: 100%;
    justify-content: flex-start;
  }
}

/* ---- Search performance page - source provenance + house number scale
   (2026-08-04). Appended only; nothing above this line was touched. ------- */

/* Small "which source is this" byline placed above the GSC stat block and
   inside each source-specific tab panel. Same tile treatment as the AI
   visibility page's .cxp-ai-engine-logo (border + var(--portal-surface)
   background) at a smaller size, so a row of source marks reads as one
   system rather than one page inventing its own look. */
body.fi-panel-portal .cxp-source-byline {
  display: flex;
  align-items: center;
  gap: .5rem;
}
body.fi-panel-portal .cxp-source-logo {
  display: grid;
  place-items: center;
  flex: 0 0 1.75rem;
  width: 1.75rem;
  height: 1.75rem;
  border: 1px solid var(--portal-border);
  border-radius: .5rem;
  background: var(--portal-surface);
}
body.fi-panel-portal .cxp-source-logo img {
  display: block;
  width: 1.25rem;
  height: 1.25rem;
  object-fit: contain;
}
body.fi-panel-portal .cxp-source-fallback {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  color: var(--portal-muted);
  font-size: .75rem;
  font-weight: 750;
  letter-spacing: .03em;
}
body.fi-panel-portal .cxp-source-label {
  color: var(--portal-muted);
  font-size: .78rem;
  font-weight: 650;
  white-space: nowrap;
  flex-shrink: 0;
}

/* GA tiles now use the one large-number scale the rest of the portal uses
   (.sb-stat-value: 2rem / 700 / -.04em) instead of their own smaller,
   heavier style. */
body.fi-panel-portal .cxp-aud-value {
  font-size: 2rem !important;
  font-weight: 700 !important;
  letter-spacing: -.04em !important;
  line-height: 1.1 !important;
  color: var(--portal-heading) !important;
}

/* .kt-card inside .cxp-map-tab-panel is pinned opaque/untransformed
   !important by the existing rule above (line ~5123) so Alpine's x-show
   toggle never leaves a card stuck invisible. That rule is untouched here;
   this page's motion script animates the stat tiles and each
   .cxp-map-tab-panel section as a unit instead of the .kt-card inside it,
   so no card-level override is needed on this page. */

/* Search performance: the search-terms table needs 451px of columns but the
   tab panel is 358px on a 390px phone. The panel was overflow-x: visible, so
   the overflow was clipped by an ancestor instead of scrolling - scrollLeft
   would not move off 0 and the last column ("Average position") was
   unreachable on mobile. Measured 2026-08-04: 95px of content inaccessible.
   Same rule as .cxp-map-grid-wrap and .cxp-rankings-table-scroll: wide content
   scrolls inside its own box, the page body never scrolls. min-width:0 is the
   part that actually lets a grid/flex child shrink below its content width. */
body.fi-panel-portal .cxp-searchperf-page .cxp-map-tab-panel {
  min-width: 0;
}
body.fi-panel-portal .cxp-searchperf-page .kt-card-table {
  min-width: 0;
  overflow-x: auto;
}

/* Tab rows scroll horizontally on narrow screens (.cxp-map-tabs hides 50px on
   Autopilot SEO and 156px on Map rankings at 390px). The content WAS reachable
   - overflow-x is auto and scrollLeft moves - but nothing signalled that more
   tabs existed, so the last label just looked clipped. Classic local-background
   scroll shadow: the two white "cover" layers are attached local so they
   travel with the content and mask the shadow once you reach that end, while
   the two shadow layers stay fixed to the element box. Net effect - a soft edge
   shadow appears only on the side that still has hidden tabs, and disappears by
   itself at either extreme. No JS, no extra DOM, self-managing when the row
   does not overflow at all. */
body.fi-panel-portal .cxp-map-tabs {
  background-image:
    linear-gradient(to right, #fff 40%, rgba(255, 255, 255, 0)),
    linear-gradient(to left, #fff 40%, rgba(255, 255, 255, 0)),
    linear-gradient(to right, rgba(15, 17, 23, .13), rgba(15, 17, 23, 0)),
    linear-gradient(to left, rgba(15, 17, 23, .13), rgba(15, 17, 23, 0));
  background-position: left center, right center, left center, right center;
  background-repeat: no-repeat;
  background-size: 1.75rem 100%, 1.75rem 100%, .7rem 100%, .7rem 100%;
  background-attachment: local, local, scroll, scroll;
}

/* ============================================================================
   PORTAL TREND CHARTS - 2026-08-04 (charts+UX brief).
   Shared styling for <x-portal.trend-chart> (resources/views/components/
   portal/trend-chart.blade.php), used by Website health ("Your score over
   time", "Phone speed over time") and the Dashboard "Ranking visibility"
   sparkline. Replaces the old .cxp-speed-chart bar strip (rules above are
   left in place - .cxp-speed-head/.cxp-speed-lcp/.cxp-speed-axis are still
   used directly below the new chart, only the bar markup itself is retired).

   Draw-on animation is NOT styled here - the <path data-chart-line> is
   animated by drawTrendLine() in topbar-user.blade.php (existing, fixed
   2026-08-04 for a dash-tiling bug). This block only lays out the chart box,
   gridlines, axis labels and the per-point hover/focus readout.
   ============================================================================ */
body.fi-panel-portal .cxp-chart-row {
  display: flex;
  gap: .5rem;
}
body.fi-panel-portal .cxp-chart-yaxis {
  position: relative;
  min-width: 2.5rem;
  text-align: right;
}
body.fi-panel-portal .cxp-chart-yaxis-label {
  position: absolute;
  right: 0;
  font-size: .75rem;
  font-weight: 600;
  color: #414c60;
  transform: translateY(-50%);
  white-space: nowrap;
}
body.fi-panel-portal .cxp-chart-yaxis-top { top: 8%; }
body.fi-panel-portal .cxp-chart-yaxis-bottom { top: 92%; }
body.fi-panel-portal .cxp-chart-plot {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  height: 10.5rem;
}
body.fi-panel-portal .cxp-chart-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  display: block;
}
body.fi-panel-portal .cxp-chart-grid {
  stroke: #e9eef6;
  stroke-width: 1;
}
body.fi-panel-portal .cxp-chart-line {
  stroke-linecap: round;
  stroke-linejoin: round;
}
body.fi-panel-portal .cxp-chart-points {
  position: absolute;
  inset: 0;
}
body.fi-panel-portal .cxp-chart-point {
  position: absolute;
  width: 8px;
  height: 8px;
  margin: -6px 0 0 -6px;
  padding: 0;
  border: 2px solid #fff;
  border-radius: 50%;
  background: var(--cxp-point-color, var(--portal-primary-fill));
  box-shadow: 0 0 0 1px rgba(15, 17, 23, .1);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
body.fi-panel-portal .cxp-chart-point::before {
  /* Enlarges the touch/click target to ~28px without growing the visible dot. */
  content: '';
  position: absolute;
  inset: -10px;
}
body.fi-panel-portal .cxp-chart-point:hover,
body.fi-panel-portal .cxp-chart-point:focus-visible {
  width: 11px;
  height: 11px;
  margin: -7.5px 0 0 -7.5px;
  outline: 2px solid var(--cxp-point-color, var(--portal-primary-fill));
  outline-offset: 2px;
}
body.fi-panel-portal .cxp-chart-point:focus {
  outline: 2px solid var(--cxp-point-color, var(--portal-primary-fill));
  outline-offset: 2px;
}
body.fi-panel-portal .cxp-chart-tooltip {
  position: absolute;
  transform: translate(-50%, calc(-100% - 10px));
  background: #14161d;
  color: #fff;
  font-size: .75rem;
  font-weight: 600;
  line-height: 1.3;
  padding: .35rem .55rem;
  border-radius: .4rem;
  white-space: nowrap;
  pointer-events: none;
  z-index: 5;
  box-shadow: 0 4px 14px rgba(15, 17, 23, .25);
}
body.fi-panel-portal .cxp-chart-tooltip::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 100%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: #14161d;
}
body.fi-panel-portal .cxp-chart-tone-note {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem 1.1rem;
  margin-top: .6rem;
  font-size: .75rem;
  color: #414c60;
}
body.fi-panel-portal .cxp-chart-tone-note > span {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
}
body.fi-panel-portal .cxp-chart-tone-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
  flex: none;
}
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-chart-plot { height: 9rem; }
  body.fi-panel-portal .cxp-chart-yaxis { min-width: 2.1rem; }
}

/* Table cards scroll horizontally on narrow screens but gave no sign of it.
   Measured 2026-08-04 on the dashboard "Available plans" table at 390px:
   the scroller shows 17-373px while both "Upgrade & pay" buttons sit at
   468-573px - the entire Price + CTA column is hidden at rest (220px), with
   computed background-image: none, i.e. zero affordance. The content WAS
   reachable (scrollLeft moves, buttons land at 248-353 once scrolled), so this
   is an affordance bug, not a clipping bug - a customer simply cannot tell
   there is anything to scroll to, on the one table that sells them something.
   Same self-managing local-background scroll shadow already proven on
   .cxp-map-tabs: the white cover layers are attached local so they travel with
   the content and mask the shadow at whichever end you have reached; the
   shadow layers stay fixed to the box. Shadow appears only on the side that
   still has hidden content, and stays invisible when nothing overflows. */
body.fi-panel-portal .kt-card-table > .kt-card-content {
  background-image:
    linear-gradient(to right, #fff 40%, rgba(255, 255, 255, 0)),
    linear-gradient(to left, #fff 40%, rgba(255, 255, 255, 0)),
    linear-gradient(to right, rgba(15, 17, 23, .13), rgba(15, 17, 23, 0)),
    linear-gradient(to left, rgba(15, 17, 23, .13), rgba(15, 17, 23, 0));
  background-position: left center, right center, left center, right center;
  background-repeat: no-repeat;
  background-size: 1.75rem 100%, 1.75rem 100%, .7rem 100%, .7rem 100%;
  background-attachment: local, local, scroll, scroll;
}

/* ============================================================================
   ui-qa FAIL fixes, 2026-08-05. Appended, nothing above this line touched.
   ============================================================================ */

/* 1. design-classes-defined FAIL: cxp-gbp-analysis-page is used in
   gbp-analysis.blade.php but never had a rule (confirmed against every
   cxp.css.bak-* backup - this was never styled, not a cleanup deletion).
   Structurally the page reuses the exact same building blocks as Website
   health / AI visibility (cxp-health-summary, cxp-health-factors,
   cxp-health-factor, cxp-map-details tabs) rather than the dashboard/
   autopilot two-column layout, so it gets the same base treatment already
   shared by .cxp-health-page / .cxp-ai-page (see ~line 1554) instead of the
   dashboard/autopilot one. */
body.fi-panel-portal .cxp-gbp-analysis-page {
  display: grid;
  gap: 1rem;
  align-content: start;
  min-width: 0;
}

/* 2. tap-targets-44px FAIL (shared chrome, every page). Grow each undersized
   control's real hit box; keep icon/text visual size unchanged per the
   "padding/min-height over font-size" guidance. */

/* Client switcher <select> in the topbar - measured 224x36. */
body.fi-panel-portal .cxp-siteswitch-select {
  min-height: 44px;
}

/* Sidebar/user-menu items (Settings, Billing, etc.) - measured 224x36. */
body.fi-panel-portal .cxp-taskbar-menu-item {
  min-height: 2.75rem;
}

/* Topbar icon buttons (Ideas and recommendations / Messages / Alerts, and
   the "..." overflow trigger which shares this class) - measured 32x32.
   The icon glyph stays font-size:1rem (unchanged); only the button box
   grows, and it is already an inline-flex centered on itself so growing
   width/height keeps the icon centered. */
body.fi-panel-portal .cxp-taskbar-action {
  width: 2.75rem;
  height: 2.75rem;
}

/* Mobile "Expand sidebar" / "Collapse sidebar" toggle - Filament core
   fi-icon-btn, measured 34x36. Portal-scoped only so the admin panel's
   dense controls are untouched. fi-icon-btn is already flex/items-center/
   justify-center, so min-width/min-height grows the box around the same
   centered icon without changing icon size. */
body.fi-panel-portal .fi-topbar-open-sidebar-btn,
body.fi-panel-portal .fi-topbar-close-sidebar-btn {
  min-width: 44px;
  min-height: 44px;
}

/* Sortable table column headers (.cxp-table-sort, injected by
   bindPortalTableSorting() in topbar-user.blade.php) - measured with
   correct widths but only 17px tall, under the 24px desktop floor.
   min-height alone is enough; the surrounding <th> just grows to fit. */
body.fi-panel-portal .cxp-table-sort {
  min-height: 24px;
}

/* Trend-chart data-point markers (resources/views/components/portal/
   trend-chart.blade.php) - real box measured 8x8. The previous technique
   grew the CLICKABLE area to ~28px via a ::before with a negative inset,
   which is a real, already-considered enlargement but is invisible to
   getBoundingClientRect() (what tap-targets-44px measures), because an
   inset ::before doesn't change the parent's own layout box. Flip the
   technique instead of layering another invisible trick on top of it: the
   <button> itself becomes the real, transparent hit box at the required
   size, and the small visible dot moves to ::after. Nothing about the
   rendered chart changes - the dot is still 8px (11px on hover/focus,
   untouched, that rule is more specific and still wins), still centred on
   the same data point, still the same colour/border/shadow. */
body.fi-panel-portal .cxp-chart-point {
  width: 24px;
  height: 24px;
  margin: -12px 0 0 -12px;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
}
body.fi-panel-portal .cxp-chart-point::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  margin: -6px 0 0 -6px;
  border: 2px solid #fff;
  border-radius: 50%;
  background: var(--cxp-point-color, var(--portal-primary-fill));
  box-shadow: 0 0 0 1px rgba(15, 17, 23, .1);
  pointer-events: none;
}
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-chart-point {
    width: 44px;
    height: 44px;
    margin: -22px 0 0 -22px;
  }
}

/* 3. visible-text-floor-12px FAIL on /portal/ai-visibility: "Named in X of Y
   answers" under each AI assistant gauge rendered at .72rem = 11.52px.
   Raised to the portal's 12px readability floor - same fix pattern already
   used elsewhere in this file (.sb-dense-identity span, .cxp-taskbar-badge,
   .cxp-taskbar-menu-item small). */
body.fi-panel-portal .cxp-ai-engine-result span {
  font-size: 12px;
}

/* 4. body-text-contrast FAIL on /portal/ai-visibility: "Named in answers"
   (.cxp-score-middle, small text - not the large-text score numbers this
   token was tuned for) measured rgb(178,106,0) #B26A00 on rgb(251,252,254)
   = 4.13:1, under the 4.5:1 normal-text floor. --cxp-tone-middle is the
   SINGLE amber token every middle-tone consumer resolves through (score
   numbers/labels, fills, gauge strokes, warn badges, status LEDs - see the
   "SINGLE SOURCE OF TRUTH" block ~line 2911), so redeclaring just this one
   custom property here cascades the darker value to every one of them
   without touching their individual rules. Same hue (R:G ratio unchanged,
   no blue), just darker: #9E5E00 clears 4.5:1 against the ai-visibility
   card background (5.05:1), the warn-tint badge background (4.86:1), and
   plain white (5.18:1) - checked because this token is reused at other
   sizes/backgrounds across the portal. Large-text score numbers already
   cleared 4.18:1 at the old value, so they only gain contrast, never lose
   it - no other usage needed changing. */
body.fi-panel-portal {
  --cxp-tone-middle: #9E5E00;
}

/* ============================================================================
   ui-qa tap-targets-44px FAIL, round 2 (2026-08-05). The first round fixed the
   brief's named "repeat offenders" (topbar chrome), which brought desktop to
   fully green but left MOBILE still failing on all four pages - the full
   undersized-target list (dumped via a diagnostic script mirroring check.js's
   own selector/filter, since the checker's own message truncates to 6 items)
   showed several more shared-chrome / page-specific controls under the 44px
   mobile floor: the page-explainer trigger, the Filament sidebar nav items
   (233x42, 2px short), the gbp-analysis tab row, the portal footer legal
   links, table sort-header buttons (mobile needs more than the 24px desktop
   fix already appended above), and the dashboard's kt-btn-sm account-widget
   buttons. Same approach throughout: grow the real hit box via min-height,
   mobile-scoped wherever the class is also used at a size that's already
   correct on desktop (kt-btn-sm's ~28-36px is a deliberate DESKTOP-admin-
   density choice per the tap-targets-44px calibration comment near the top
   of check.js - only mobile needed raising).
   ============================================================================ */

/* "Explain how X works" info trigger next to every page title - 134x32. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-page-explainer-trigger {
    min-height: 44px;
  }
}

/* Filament's own sidebar nav item link - 233x42, only 2px short. Small
   enough to raise everywhere (desktop already has room) rather than add a
   breakpoint for 2px.
   Selector must match body.fi-panel-portal .fi-sidebar-group-items
   .fi-sidebar-item-button's specificity (0,3,1) - a plain
   body.fi-panel-portal .fi-sidebar-item-button (0,2,1) LOSES to that more
   specific 42px !important rule regardless of being later in the file, which
   is why this fix silently never took effect until now (re-caught live by
   ui-qa's tap-targets-44px rule, still measuring 42px). */
body.fi-panel-portal .fi-sidebar-item-button,
body.fi-panel-portal .fi-sidebar-group-items .fi-sidebar-item-button {
  min-height: 44px !important;
}

/* gbp-analysis detail tabs (Reviews / Website / Business listings / ...) -
   38px tall, fine on desktop (>24px floor), short on mobile. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-map-tabs > button {
    min-height: 44px;
  }
}

/* Portal footer legal links (Terms of Service / Privacy Policy / Cookies
   Policy / support email) - 36px tall. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-portal-footer a {
    min-height: 44px;
  }
}

/* Table sort-header buttons: the earlier fix in this file (search
   ".cxp-table-sort") set min-height:24px for the desktop floor: this adds
   the mobile floor on top of it, only at narrow widths, so the compact
   desktop header row is untouched. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-table-sort {
    min-height: 44px;
  }
}

/* Dashboard account-widget CTAs (Message the team / Tell us more /
   Upgrade & pay / All invoices) use the shared kt-btn-sm class, which is
   deliberately dense (~36px) for the ADMIN panel's desktop density - see the
   "kt-btn-sm etc render ~28px tall on desktop by design" note in check.js.
   Scoped to the portal AND to mobile only, so admin and portal-desktop are
   both untouched. */
@media (max-width: 760px) {
  body.fi-panel-portal .kt-btn-sm {
    min-height: 44px;
  }
}

/* table-rows-stay-scannable WARN introduced by the two lists-are-real-tables
   conversions above (gbp-analysis "Top things to improve" -> Where column,
   website-health "What needs attention" -> Detail column): a long free-text
   column with no width floor got squeezed to ~123-136px on a 390px mobile
   viewport and wrapped into very tall rows (165-185px median). Same fix
   already used for this exact shape of problem on the AI visibility
   assistant-matrix table (.cxp-ai-matrix-q min-width:22rem) - give the
   long-text column a real minimum width so the card's existing
   overflow-x:auto scrolls horizontally on narrow screens instead of
   squeezing the column vertically. */
body.fi-panel-portal .cxp-issue-detail-col {
  min-width: 18rem;
}

/* ============================================================================
   ui-qa tap-targets-44px FAIL, round 3 (2026-08-05). Round 2's mobile
   min-height:44px on .cxp-table-sort fixed every column except the
   shortest single-word headers ("Item" 37px, "Issue" 42px) - height now
   passes but width is still content-driven and narrower than 44px on those.
   Add min-width alongside the existing min-height, centering the arrow+label
   so the extra width doesn't look left-hugged. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-table-sort {
    min-width: 44px;
    justify-content: center;
  }
}

/* ============================================================================
   Visual regression fix, 2026-08-05 (found during the MANDATORY screenshot
   review of the tap-targets-44px fixes above, not by the checker - the
   checker only measures getBoundingClientRect, it does not look at the
   picture). The min-width/min-height:44px added to .fi-topbar-open-sidebar-btn
   grew the button's real box from Filament's default 36x36, but Filament's
   own icon-button component also ships a NEGATIVE MARGIN on that same button
   (Tailwind `-m-1.5` = -6px/side, a "grow the click target without widening
   the layout row" trick: the box is visually larger than the space it claims
   in the flex row, and is allowed to overflow into the row's own gutter by a
   few px). That trick was tuned for the ORIGINAL 36px box. Growing the box to
   44px while leaving the -6px margin untouched increased how far the button
   bleeds beyond its allocated slot AND shrank the flexible, shrinkable
   client-switcher <select> next to it (min-width:0 in .cxp-topbar-context),
   so on mobile the enlarged hamburger button rendered on top of the
   switcher's text - a real, visible collision, confirmed against the pre-fix
   screenshot which shows no overlap there.
   Fix: zero out the negative margin so the button claims its own full 44px
   in the row instead of overflowing into a neighbour. The switcher may give
   up a few more px than before, but that is ordinary shrink-to-fit
   (min-width:0 + flex:0 1 auto), not an overlap. */
body.fi-panel-portal .fi-topbar-open-sidebar-btn,
body.fi-panel-portal .fi-topbar-close-sidebar-btn {
  margin: 0;
}

/* ============================================================================
   Visual regression fix, round 2 (2026-08-05). The margin:0 fix above was
   the wrong diagnosis. Measuring the live DOM (rects + computed styles, not
   just the picture) showed the real cause: on a 390px mobile viewport, the
   topbar row does not have enough total width for FIVE controls (the
   sidebar-toggle + the 4 .cxp-taskbar-action icons: ideas/messages/alerts/
   overflow) to each claim a full 44px of ROW WIDTH on top of everything that
   was already there. The only flexible sibling is .cxp-topbar-context
   (min-width:0), so it got crushed to ~25px real width - but its child
   .cxp-siteswitch-select still has its own min-width:5.5rem (88px, an
   existing mobile rule), so the select rendered at its real minimum width
   and VISUALLY OVERFLOWED its own starved parent into the neighbouring
   button, which is what read as "overlap" in the screenshot.
   The actual fix is not a margin nudge, it's restoring the ROW-WIDTH BUDGET:
   both the sidebar-toggle button and Filament's own account-menu trigger
   already used Tailwind's "-m-*" pattern (a real, larger visual box that
   claims LESS row width via a negative margin, overflowing into the row's
   own gutter) specifically so a full-size tap target does not have to cost
   full-size row space. Round 1 grew these buttons' real boxes to 44px but
   left their margins alone (sidebar-toggle) or, worse, zeroed them out
   (the round-above fix), so the FULL 44px got charged to the row instead of
   staying mostly absorbed by the margin trick. This reapplies that same
   trick, sized for the new 44px boxes, so each control's claimed row width
   returns close to what it was before this whole round of fixes (matching,
   not exceeding, the original layout budget) while the real box - what
   tap-targets-44px measures - stays 44x44. */
body.fi-panel-portal .fi-topbar-open-sidebar-btn,
body.fi-panel-portal .fi-topbar-close-sidebar-btn {
  margin: -10px;
}
body.fi-panel-portal .cxp-taskbar-action {
  margin: -6px;
}

/* ============================================================================
   Visual regression fix, round 3 (2026-08-05), found by the mandatory second-
   model screenshot review (not the checker - it only measures each element's
   OWN box, never inter-element overlap). The -6px margin above restored the
   taskbar row's total width budget correctly (matches the original 32px
   claimed slot exactly, which is why the client-switcher fix held), but -6px
   on BOTH sides of adjacent 44px buttons that only have a .35rem (5.6px) flex
   gap between them made their real hit-boxes overlap by 6-7px (measured:
   ideas 223-267, messages 261-305 -> 6px overlap; messages/alerts -> 7px).
   The visible icon GLYPHS (18px, centred) never touch - 20px+ clearance
   between them - so this never showed up in a screenshot; it is only a
   tap-target geometry defect (a touch right at the seam between two icons
   could register on either). -3px keeps the row-width recovery close to
   the original (claimed slot 38px vs the original 32px, +6px per icon,
   +24px total across all four - far short of the +48px that caused the
   original squeeze) while closing the box-to-box overlap. */
body.fi-panel-portal .cxp-taskbar-action {
  margin: -3px;
}

/* ============================================================================
   ui-qa round 2 page fixes (2026-08-05). These rules are appended so the
   shared stylesheet's existing cascade and the concurrent executor's work
   remain unchanged.
   ============================================================================ */

/* Restored verbatim from cxp.css.bak-* after the cleanup left this class
   orphaned. Keep the initial hidden state behind no-preference so reduced
   motion never hides content when the Motion binder intentionally returns. */
/* 🛑 2026-08-20. This block used to read:
 *
 *     .cxp-motion-card            { opacity: 0; transform: translateY(8px); }
 *     .cxp-motion-card.cxp-motion-visible { opacity: 1; transform: none; }
 *
 * i.e. the card was HIDDEN by default and depended on JavaScript to reveal it.
 * The reveal is done by Motion writing inline styles - it never adds
 * .cxp-motion-visible - and Livewire's DOM morph strips inline styles on every
 * re-render. So the first time a customer touched any Livewire control (the
 * period filter on Search performance, a range switch, a tab), every card on
 * the page fell back to opacity 0 and STAYED there, still occupying its full
 * height. Measured on production: four stat tiles, 209px each, opacity 0,
 * transform translateY(8px), after one click. Adir reported it as "I click on
 * it, nothing happened, I just got the blank section".
 *
 * The resting state is now visible. The animation is a pure enhancement: the
 * binder animates FROM opacity 0 via its own keyframes, so if the JS never
 * runs, or a morph wipes what it wrote, the content is simply there. Content
 * must never depend on an animation having run.
 */
@media (prefers-reduced-motion: no-preference) {
  body.fi-panel-portal .cxp-motion-card {
    opacity: 1;
    transform: none;
  }
}

/* The keyword selector is a real control, not decorative page chrome. */
body.fi-panel-portal .cxp-map-keyword-picker .cxp-keyword-select {
  min-height: 44px;
}

/* Reserve vertical room for the mobile sort button inside the rankings card,
   so its label remains inside the card clip while retaining the horizontal
   table scroller. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-rankings-table-scroll {
    padding-block: .5rem;
  }
}

/* The plan CTA is a real primary action. Keep its text white on the blue
   surface and give it the full mobile target height at every viewport. */
body.fi-panel-portal a.cxp-plan-cta {
  display: inline-flex;
  min-height: 44px;
  align-items: center;
  justify-content: center;
  color: #fff !important;
}

/* ui-qa follow-up for the three in-scope pages. */
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now {
  width: 16%;
  min-width: 12rem;
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-map-live .leaflet-control-zoom a {
    width: 44px;
    height: 44px;
    line-height: 42px;
  }

  /* Mobile keeps the current result readable without presenting a dash-only
     history snapshot as a data column. Page and history remain reachable through
     the explicit disclosure row rendered below each keyword. */
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-hist {
    display: none;
  }

  /* The sort control has its own reserved gutter above and below the table. */
  body.fi-panel-portal .cxp-rankings-card {
    overflow: visible;
  }

  /* Keep every rankings sort label inside its own header cell. This preserves
     the real sort control and prevents the outer layout clip from trimming it. */
  body.fi-panel-portal .cxp-rankings-page .cxp-table-sort {
    box-sizing: border-box;
    width: 100%;
    max-width: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
  }
}

/* ---- AI search visibility: honest empty-state + delta rebuild (2026-08-06)
   Appended, not edited in place - see the block starting at "AI search
   visibility: per-assistant results (2026-07-31)" above for the classes this
   extends. Every rule here is either a brand-new class or a narrower/later
   selector that layers on top of an existing one; nothing above is changed. */

body.fi-panel-portal .cxp-ai-status-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem;
  padding: 1rem 1.25rem;
}

body.fi-panel-portal .cxp-ai-score-delta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .5rem;
  margin-top: .5rem;
  font-size: .82rem;
}

/* CYT-647 defect #2 CORRECTION #2 (2026-08-18, Adir): my first pass here
   assumed the "Signal Red" theme (no-green ramp) was live and retoned this
   near-black -> amber -> red. It is NOT deployed - the running portal is
   still GREEN-branded (measured live: --primary #007A3D, primary button bg
   rgb(0,166,81)). Stripping green would have clashed with every other
   surface on this page. Reverted the "good" step back to the app's real
   brand green.

   The comment this block originally replaced called var(--portal-primary)
   "informational blue" - true when THAT rule was written, but
   --portal-primary now resolves to --cxp-good-strong (#007A3D) and
   --portal-primary-fill to --cxp-good (#00A651), i.e. GREEN. Measured live:
   0%-mentioned ("Never mentions your business", the WORST outcome an
   assistant card can show) rendered text rgb(0,122,61) / ring
   rgb(0,166,81) - green, on 3 of 4 assistant cards. Next to a percentage,
   green reads as the best result, not the worst - and the 20%/"middle" card
   (an actually-better outcome) rendered amber, so the ordering read
   backwards. The defect was never "green exists in this scale" - it is
   "the WORST step used the app's real good-green". Fix: order the scale by
   VALUE using the palette already live - good (>=80%) keeps the real brand
   green, middle stays the existing amber (unchanged), zero/worst moves off
   green onto the app's existing "poor" red. No new hue anywhere. */
body.fi-panel-portal .cxp-ai-engine .cxp-score-checked,
body.fi-panel-portal .cxp-ai-engine .cxp-score-checked * {
  color: var(--cxp-tone-poor) !important;
}
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-progress.cxp-gauge-checked {
  stroke: var(--cxp-tone-poor);
}
body.fi-panel-portal .cxp-ai-engine .cxp-score-good,
body.fi-panel-portal .cxp-ai-engine .cxp-score-good * {
  color: var(--portal-primary) !important;
}
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-progress.cxp-gauge-good {
  /* !important required: a generic .cxp-gauge-good rule elsewhere in this
     file (green, !important, but a DIFFERENT green - --cxp-tone-good
     #00A651 vs this ring's --portal-primary-fill #00A651... same value
     here, but keep !important anyway since specificity alone lost to it
     during the first pass at this rule; see the CYT-647 defect #1 gauge
     block above for the same lesson applied to font-size. */
  stroke: var(--portal-primary-fill) !important;
}

/* An assistant that was never part of THIS audit (defensive - the pipeline
   always runs all 4 today, but degrades gracefully if a provider ever fails)
   gets no gauge and no percentage at all. A 0-of-0 percentage is meaningless,
   so showing one would be exactly the fabricated-looking zero this page must
   never show. */
body.fi-panel-portal .cxp-ai-engine--unchecked {
  opacity: .74;
  border-style: dashed;
}
body.fi-panel-portal .cxp-ai-engine-gauge--unchecked {
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 2px dashed var(--portal-border);
  background: var(--portal-surface);
}
body.fi-panel-portal .cxp-ai-unchecked-mark {
  color: var(--portal-muted);
  font-size: 1.5rem;
  font-weight: 700;
}
body.fi-panel-portal .cxp-ai-engine-delta {
  font-size: .7rem;
  font-weight: 650;
}

/* "What changed" card - engine-level deltas vs the previous complete audit. */
body.fi-panel-portal .cxp-ai-change-list {
  display: grid;
  gap: .6rem;
}
body.fi-panel-portal .cxp-ai-change-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .6rem .75rem;
  padding: .65rem .85rem;
  border: 1px solid var(--portal-border);
  border-radius: .6rem;
  background: var(--portal-surface-soft);
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-change-engine {
  flex: 0 0 6rem;
  font-weight: 650;
  color: var(--portal-heading);
}
body.fi-panel-portal .cxp-ai-change-icon {
  font-size: .9rem;
  font-weight: 700;
}
body.fi-panel-portal .cxp-ai-change-text {
  flex: 1 1 12rem;
  color: var(--portal-heading);
  font-size: .85rem;
}
body.fi-panel-portal .cxp-ai-change-detail {
  white-space: nowrap;
}
body.fi-panel-portal .cxp-ai-change-summary {
  margin: .85rem 0 0;
  color: var(--portal-muted);
  font-size: .85rem;
}

/* House rule: any figure that IS the point of its block uses the large-number
   treatment (matches .sb-stat-value's 2rem/700 - not touching that rule,
   just matching its numbers here too). */
body.fi-panel-portal .cxp-ai-stat > strong {
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: -.04em;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-ai-change-row {
    padding: .6rem .7rem;
  }
  body.fi-panel-portal .cxp-ai-change-engine {
    flex: 1 1 100%;
  }
}

/* ---- AI search visibility: ui-qa fixes for the block above (2026-08-06) --
   1) .cxp-ai-changes-card needs at least one real rule (it inherits .kt-card
      but the class itself was referenced with no declaration).
   2) Small (<14pt/18.66px bold) coloured text needs the app's established
      AA-safe DARK tone triplet, not the brighter --cxp-tone-* values used for
      large numbers/labels - same convention already used for 12px status
      chips (.kt-badge.cxp-badge-good/warn/bad, .cxp-ai-cell.cxp-badge-good)
      elsewhere in this file. Scoped to the new small elements only. */
body.fi-panel-portal .cxp-ai-changes-card {
  min-width: 0;
}

body.fi-panel-portal .cxp-ai-engine-delta.cxp-score-good,
body.fi-panel-portal .cxp-ai-change-icon.cxp-score-good,
body.fi-panel-portal .cxp-ai-score-delta .cxp-score-good {
  color: #04663A !important;
}
body.fi-panel-portal .cxp-ai-engine-delta.cxp-score-middle,
body.fi-panel-portal .cxp-ai-change-icon.cxp-score-middle,
body.fi-panel-portal .cxp-ai-score-delta .cxp-score-middle {
  color: #8A4B00 !important;
}
body.fi-panel-portal .cxp-ai-engine-delta.cxp-score-poor,
body.fi-panel-portal .cxp-ai-change-icon.cxp-score-poor,
body.fi-panel-portal .cxp-ai-score-delta .cxp-score-poor {
  color: #B42318 !important;
}

/* ---- AI search visibility: sub-score denominator (2026-08-06) - matches the
   already-established "value / max" convention on Website health
   (.cxp-health-factor-top b renders "18.5 / 20"); these three sub-scores are
   always out of 100, so the suffix is literal, not computed. Lighter weight
   than the number itself so a large bold number isn't paired with an equally
   heavy "/100" beside it. */
body.fi-panel-portal .cxp-ai-subscore-max {
  font-size: .7em;
  font-weight: 500;
  color: var(--portal-muted);
}

/* ---- AI visibility report rebuild (2026-08-07) ----------------------------
   Stored-result reporting: summary context, distinct state cards, dense KPI
   tiles, assistant evidence, and line tabs. Keep this shared stylesheet block
   at the end so the page-specific construction wins without changing other
   portal pages. */
body.fi-panel-portal .cxp-card-kicker {
  color: var(--portal-muted);
  font-size: .75rem;
  font-weight: 750;
  letter-spacing: .08em;
  text-transform: uppercase;
}
body.fi-panel-portal .cxp-ai-summary-copy {
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-summary-meta {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem .85rem;
  color: var(--portal-muted);
  font-size: .75rem;
}
/* 2026-08-21. This separator asked for var(--cxp-border), a token that is
   defined nowhere in the app - so with no fallback it went invalid at
   computed-value time and the dot simply inherited the muted text colour,
   losing the lighter divider tone it was written for. Every other use of
   --cxp-border in this file carried a hardcoded #e3e8ef fallback, which hid
   the same bug while painting a light grey border on the dark skins. All of
   them now ride --portal-border, which is real and is remapped per skin in
   cxs-skin.css. Same class of bug as --portal-surface-2, fixed the same way -
   point at the existing token, never at a literal. */
body.fi-panel-portal .cxp-ai-summary-meta span + span::before {
  content: "·";
  margin-right: .85rem;
  color: var(--portal-border);
}
body.fi-panel-portal .cxp-inline-action {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  min-height: 44px;
  color: var(--portal-link, var(--portal-primary));
  font-size: .8rem;
  font-weight: 700;
  text-decoration: none;
}
body.fi-panel-portal .cxp-inline-action:hover,
body.fi-panel-portal .cxp-inline-action:focus-visible {
  text-decoration: underline;
}
body.fi-panel-portal .cxp-ai-kpi-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
body.fi-panel-portal .cxp-ai-kpi-grid .sb-stat {
  min-width: 0;
}
body.fi-panel-portal .sb-stat-value--unknown,
body.fi-panel-portal .cxp-score-unknown {
  color: var(--portal-muted) !important;
  font-size: 1.15rem !important;
  letter-spacing: 0 !important;
}
body.fi-panel-portal .cxp-ai-state-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: 1rem;
  padding: 1.25rem;
}
body.fi-panel-portal .cxp-state-icon {
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: .75rem;
  background: color-mix(in srgb, var(--cxp-good) 10%, transparent);
  color: var(--portal-primary);
  font-size: 1.25rem;
}
body.fi-panel-portal .cxp-state-title {
  margin: .35rem 0 .25rem;
  color: var(--portal-heading);
  font-size: .9rem;
  font-weight: 650;
}
body.fi-panel-portal .cxp-ai-tabs {
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-tabs .cxp-map-tabs {
  overflow-x: auto;
  scrollbar-width: thin;
}
body.fi-panel-portal .cxp-ai-tabs .cxp-map-tabs button {
  min-height: 44px;
  white-space: nowrap;
}
/* CYT-804: .75rem (12px) was the one gap on a tabbed portal page that was not
   on the portal's step. Every other measured gap at 1440 is 16px, including
   this row's siblings, so the tab strip sat 12px from the inline action beside
   it and 16px from everything else. --cxp-vertical-space is the portal rhythm
   token introduced by CYT-804 (portal-vertical-rhythm.css); one scale means one
   scale in both axes. */
body.fi-panel-portal .cxp-ai-tabs-actions {
  display: flex;
  align-items: center;
  gap: var(--cxp-vertical-space, 1rem);
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-tabs-actions .cxp-map-tabs {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}
body.fi-panel-portal .cxp-ai-tabs-actions .cxp-inline-action {
  flex: 0 0 auto;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-ai-change-detail-card > .kt-card-header {
  position: relative;
  border-bottom: 0 !important;
}
body.fi-panel-portal .cxp-ai-change-detail-card > .kt-card-header::after {
  position: absolute;
  right: 1.25rem;
  bottom: 0;
  left: 1.25rem;
  height: 1px;
  background: var(--portal-border);
  content: "";
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-ai-tabs-actions {
    align-items: stretch;
    flex-direction: column;
  }
  body.fi-panel-portal .cxp-ai-tabs-actions .cxp-inline-action {
    align-self: flex-start;
  }
}
body.fi-panel-portal .cxp-ai-engine-result-label {
  min-height: 1.25rem;
  color: var(--portal-heading);
  font-size: .82rem;
  line-height: 1.35;
}
body.fi-panel-portal .cxp-score-checked {
  color: #414C60 !important;
}
body.fi-panel-portal .cxp-ai-engine-counts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  width: 100%;
  gap: .55rem;
  margin-top: .25rem;
  padding-top: .75rem;
  border-top: 1px solid var(--portal-border);
}
body.fi-panel-portal .cxp-ai-engine-counts > div {
  display: grid;
  gap: .15rem;
  min-width: 0;
  text-align: center;
}
body.fi-panel-portal .cxp-ai-engine-counts strong {
  color: var(--portal-heading);
  font-size: 1.35rem;
  line-height: 1.1;
}
body.fi-panel-portal .cxp-ai-engine-counts span {
  color: var(--portal-muted);
  font-size: .75rem;
  line-height: 1.25;
}
body.fi-panel-portal .cxp-ai-engine-delta {
  color: var(--portal-muted);
  font-size: .75rem;
  font-weight: 650;
}
body.fi-panel-portal .cxp-ai-change-list--summary {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
body.fi-panel-portal .cxp-ai-change-table th:nth-child(n+2),
body.fi-panel-portal .cxp-ai-change-table td:nth-child(n+2),
body.fi-panel-portal .cxp-ai-history-table th:nth-child(n+2),
body.fi-panel-portal .cxp-ai-history-table td:nth-child(n+2) {
  text-align: right;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-ai-method-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}
body.fi-panel-portal .cxp-ai-method-card {
  min-width: 0;
}
body.fi-panel-portal .cxp-ai-method-grid > div {
  display: grid;
  gap: .2rem;
  min-width: 0;
  padding: .8rem;
  border: 1px solid var(--portal-border);
  border-radius: .6rem;
  background: var(--portal-surface-soft);
}
body.fi-panel-portal .cxp-ai-method-grid strong {
  color: var(--portal-heading);
  font-size: 1.5rem;
  line-height: 1;
}
body.fi-panel-portal .cxp-ai-method-grid span {
  color: var(--portal-heading);
  font-size: .82rem;
  font-weight: 700;
}
body.fi-panel-portal .cxp-ai-method-grid small {
  color: var(--portal-muted);
  font-size: .75rem;
  line-height: 1.35;
}
body.fi-panel-portal .cxp-ai-clean-state {
  display: grid;
  gap: .25rem;
  padding: 1rem 1.25rem;
  border-left: 3px solid var(--cxp-good, #047A44);
}
body.fi-panel-portal .cxp-ai-trend-chart {
  display: block;
  width: 100%;
  height: 10rem;
}
body.fi-panel-portal .cxp-ai-trend-chart [data-chart-area] {
  pointer-events: none;
}
body.fi-panel-portal .cxp-ai-trend-chart [data-chart-line] {
  fill: none;
}

/* Keep the Website Health headline card content-hugging at wide viewports.
   Its ring and summary copy are one KPI, not a full-width empty panel. */
body.fi-panel-portal .cxp-health-summary {
  max-width: 720px;
}

@media (max-width: 980px) {
  body.fi-panel-portal .cxp-ai-kpi-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-ai-change-list--summary {
    grid-template-columns: minmax(0, 1fr);
  }
}
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-ai-summary {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-ai-score {
    min-height: 0;
    padding-bottom: 1rem;
    border-right: 0;
    border-bottom: 1px solid var(--portal-border);
  }
  body.fi-panel-portal .cxp-ai-state-card {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-ai-method-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-ai-kpi-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-ai-engine-counts {
    gap: .35rem;
  }
  body.fi-panel-portal .cxp-ai-engine-counts strong {
    font-size: 1.15rem;
  }
}

/* AI visibility visual review follow-up: keep the long report from sliding
   under the shell header, and make every detail tab reachable on mobile. */
body.fi-panel-portal:has(.cxp-ai-visibility-page) .fi-topbar {
  position: static !important;
}
body.fi-panel-portal:has(.cxp-ai-visibility-page) .cxp-ai-tabs .cxp-map-tabs {
  overflow-x: visible;
}
@media (max-width: 640px) {
  body.fi-panel-portal:has(.cxp-ai-visibility-page) .cxp-ai-tabs .cxp-map-tabs {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: .35rem;
  }
  body.fi-panel-portal:has(.cxp-ai-visibility-page) .cxp-ai-tabs .cxp-map-tabs button {
    width: 100%;
    white-space: normal;
    line-height: 1.2;
  }
}

/* Unpaid-account banner (CYT-193). Sat flush against the topbar with 0px
   gap - owner: "touching the top, there's no padding" - verified live:
   margin-top was 0px, .fi-main's own top padding never reaches content
   injected via PanelsRenderHook::CONTENT_START since that hook renders
   before the page's normal content wrapper. kt-alert's default
   align-items:stretch left the CTA link sitting at the top of the alert
   (flush with the 14px inner padding) instead of centered - fixed with
   align-self. Solid .kt-alert-warning is white text on a bright yellow
   fill (components.css) - fails contrast badly for exactly this shade;
   .kt-alert-light.kt-alert-warning (pale yellow bg + var(--foreground)
   text) is the accessible variant already defined in this stylesheet for
   this reason, just never reached for by this banner before now. */
.cxp-billing-banner {
  margin-top: 1.5rem;
  /* kt-alert-light.kt-alert-warning has an :is(.dark *) variant that mixes
     a dark yellow at 30% opacity - correct for a genuinely dark admin
     page, but the portal carries the .dark class for Filament's own
     component internals while still rendering its actual background
     white/light (deliberate, see PortalPanelProvider - 'PLAIN DARK TEXT',
     not the dark-mode look). The result was a muddy taupe instead of a
     clean warning yellow. Forced explicitly here rather than fighting the
     :is(.dark *) selector's specificity. */
  background-color: var(--color-yellow-50) !important;
  border-color: var(--color-yellow-200) !important;
}
.cxp-billing-banner-cta {
  align-self: center;
  white-space: nowrap;
}

/* Topbar Alerts popup (dismiss feature). Reuses .cxp-taskbar-overflow /
   .cxp-taskbar-menu as the dropdown foundation (same anchor + panel chrome
   as the existing account overflow menu) - .cxp-alerts-menu only widens it,
   since alert title/detail text needs more room than a simple nav item. */
body.fi-panel-portal .cxp-alerts-menu {
  width: 20rem;
}
body.fi-panel-portal .cxp-alert-item {
  display: flex;
  align-items: flex-start;
  gap: .5rem;
  padding: .5rem .6rem;
  border-radius: .45rem;
}
body.fi-panel-portal .cxp-alert-item:hover {
  background: var(--portal-primary-soft);
}
body.fi-panel-portal .cxp-alert-item-body {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  min-width: 0;
  flex: 1 1 auto;
}
body.fi-panel-portal .cxp-alert-item-body strong,
body.fi-panel-portal .cxp-alert-item-body span {
  overflow-wrap: break-word;
}
body.fi-panel-portal .cxp-alert-item-body strong {
  color: var(--portal-heading);
  font-size: 13px;
  line-height: 1.3;
}
body.fi-panel-portal .cxp-alert-item-body span {
  color: var(--portal-muted);
  font-size: 12px;
  line-height: 1.3;
}
body.fi-panel-portal .cxp-alert-item-dismiss {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-left: auto;
  border: 1px solid var(--portal-border);
  border-radius: .35rem;
  background: var(--portal-surface-2, #f3f4f6);
  color: var(--portal-text);
  font-size: 16px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
body.fi-panel-portal .cxp-alert-item-dismiss:hover,
body.fi-panel-portal .cxp-alert-item-dismiss:focus-visible {
  background: var(--cxp-bad-tint, #fde8e8);
  color: var(--cxp-bad-strong, #b42318);
  outline: none;
}
body.fi-panel-portal .cxp-alert-empty {
  padding: .75rem .7rem;
  color: var(--portal-muted);
  font-size: 12px;
  text-align: center;
}

/* ============================================================================
   NO BROWN ANYWHERE IN THE PORTAL (2026-08-11).
   Owner, repeatedly, pointing at the GBP grade ring, the "Completed"/
   "Approved" badges and the "Monitoring updates" pill: "This brown color
   keeps coming up on the theme... it's not even matching the colors of the
   theme... You should change all the brown colors from all the website,
   it's not supposed to be anywhere."

   WHY IT WENT BROWN, so this isn't undone by accident: the middle/warn tone
   started as amber (#FFB020 / #B26A00), failed the 4.5:1 body-text floor, and
   was fixed by DARKENING the same hue (-> #9E5E00, see the token note above).
   Darkening an amber is what makes it mud. The contrast fix was right; the
   direction was wrong. These values keep the contrast and change the HUE
   instead - #C2410C measures 5.18:1 on white (better than the 4.13:1 that
   started the whole episode, and equal to the brown it replaces) while
   reading as a clear burnt orange: its red:green ratio is 2.98 against the
   brown's 1.68, which is precisely the "muddiness" that was being seen.

   Do not re-darken toward brown to chase contrast. If a future contrast
   failure appears here, move toward red/orange saturation or change the
   background, never toward #9E5E00.
   ============================================================================ */
body.fi-panel-portal {
  /* The single amber token every middle-tone consumer resolves through:
     score numbers, grade labels, gauge strokes, progress fills, chart dots,
     warn badges. One declaration re-tones all of them. */
  --cxp-tone-middle: #C2410C;

  --cxp-warn:        #F79009;  /* graphics only - rings/fills need 3:1, not 4.5:1 */
  --cxp-warn-text:   #C2410C;
  --cxp-warn-strong: #C2410C;
  --cxp-warn-tint:   #FFF6ED;
}

/* Hardcoded browns that predate the token system and therefore ignore it.
   Each was found by a computed-style sweep of /portal, /portal/autopilot-seo,
   /portal/gbp-analysis and /portal/rankings, not by reading the file. */
body.fi-panel-portal .cxp-rankings-tone-middle,
body.fi-panel-portal .cxp-rankings-too-little,
body.fi-panel-portal .cxp-taskbar-action--ideas,
body.fi-panel-portal .cxp-autopilot-page .kt-badge-warning,
body.fi-panel-portal .kt-badge[class*="warning"] {
  color: var(--cxp-warn-strong) !important;
}

body.fi-panel-portal .cxp-rankings-keyword-table tr.cxp-rankings-low-data > td {
  box-shadow: inset 3px 0 0 rgba(194, 65, 12, .28);
}

/* .cxp-activity-status was painting EVERY tone brown - the selector carries no
   tone modifier, sits later in the file than .cxp-activity-status--good, and
   is !important, so it beat the green rule outright. That is why "Monitoring
   updates" rendered brown text beside its own green LED: the pill was already
   in its healthy state and still looked like a warning. Tone modifiers now
   decide the colour, and the base rule no longer has an opinion. */
body.fi-panel-portal .cxp-activity-status,
body.fi-panel-portal .cxp-autopilot-page .cxp-activity-status {
  color: var(--portal-text) !important;
}

body.fi-panel-portal .cxp-activity-status--good,
body.fi-panel-portal .cxp-autopilot-page .cxp-activity-status--good,
body.fi-panel-portal .cxp-live-status--good,
body.fi-panel-portal .cxp-autopilot-page .cxp-activity-status.cxp-live-status--good {
  color: #047A44 !important;
}

body.fi-panel-portal .cxp-activity-status--needs-input,
body.fi-panel-portal .cxp-autopilot-page .cxp-activity-status--needs-input {
  color: var(--cxp-warn-strong) !important;
  background: var(--cxp-warn-tint);
}

/* The idle "middle" pill tone was the only consumer of #6b3a00 and the only
   amber-brown chrome in the header. Retoned to the same orange family so a
   surviving usage elsewhere cannot reintroduce the mud. */
body.fi-panel-portal .cxp-live-status--middle {
  color: var(--cxp-warn-strong);
  background: var(--cxp-warn-tint);
}

/* The neon green is the portal's signature positive colour (--cxp-good,
   #00E676) and belongs on the LED itself, where it sits on a tinted pill and
   carries no text-contrast duty. The label beside it stays the readable dark
   green above - neon green text on a pale tint fails 4.5:1. */
body.fi-panel-portal .cxp-status-led--good,
body.fi-panel-portal .cxp-led--good {
  background: var(--cxp-good) !important;
  color: var(--cxp-good) !important;
}

/* Source byline (provider logo + name) sat flush against the card's left
   edge - reported twice. Measured on /portal: the byline rendered at x=951
   inside a card starting at x=950, while that card's own title sat at x=971.
   It is a DIRECT child of .kt-card (the card itself has no padding; each
   child supplies its own), so it needs the same 1.25rem inset its siblings
   already carry. Scoped to the direct-child case so a byline nested inside a
   .kt-card-content block - which is already inset by its parent - is not
   double-padded. */
body.fi-panel-portal .kt-card > .cxp-source-byline {
  padding-inline: 1.25rem;
}

/* ============================================================================
   NO BROWN, PASS 2 (2026-08-11) - graphics vs text, and the stragglers.
   ============================================================================ */

/* GRAPHICS GET THE BRIGHT AMBER, NOT THE TEXT TONE.
   The gauge rings, progress fills and chart dots were originally
   var(--cxp-warn) (#F79009). A later rule re-pointed them at
   --cxp-tone-middle, the token tuned for 4.5:1 BODY TEXT - so a big
   decorative ring inherited a colour picked to be dark enough to read at
   12px. That is what made the GBP "66 / Grade C" ring look like mud. A ring,
   a bar fill and a legend swatch are non-text graphics: WCAG asks 3:1 of
   them, which #F79009 clears comfortably while staying visibly amber. */
body.fi-panel-portal .cxp-gauge-middle,
body.fi-panel-portal .cxp-health-summary-ring.cxp-gauge-middle,
body.fi-panel-portal circle.cxp-gauge-progress.cxp-gauge-middle {
  stroke: var(--cxp-warn) !important;
}

body.fi-panel-portal .cxp-fill-middle,
body.fi-panel-portal .cxp-health-progress-fill.cxp-fill-middle,
body.fi-panel-portal .cxp-rankings-tone-fill-unchanged,
body.fi-panel-portal .cxp-map-legend .cxp-map-key--middle {
  background: var(--cxp-warn) !important;
}

body.fi-panel-portal .cxp-chart-tone-dot.cxp-score-middle,
body.fi-panel-portal .cxp-chart-tone-dot {
  background: var(--cxp-warn);
}

/* Remaining hardcoded #8A4B00 text usages. These predate the token block and
   carry !important, so they ignore it. Listed explicitly rather than with a
   blanket [style*=] hack so each stays greppable. */
body.fi-panel-portal .cxp-conn-status--off,
body.fi-panel-portal .kt-badge.cxp-badge-warn,
body.fi-panel-portal .cxp-ai-engine-delta.cxp-score-middle,
body.fi-panel-portal .cxp-ai-change-icon.cxp-score-middle,
body.fi-panel-portal .cxp-ai-score-delta .cxp-score-middle,
body.fi-panel-portal .cxp-map-cell--middle {
  color: var(--cxp-warn-strong) !important;
}

body.fi-panel-portal .cxp-map-cell--middle {
  background: rgba(194, 65, 12, .14);
}

/* An UNTONED badge was defaulting to amber. Confirmed from the live DOM:
   <span class="kt-badge kt-badge-outline kt-badge-sm">Completed</span> - no
   tone class anywhere on it - computed rgb(138,75,0). So every neutral badge
   in the portal was being painted as a warning, which is how "Completed" and
   "Approved" ended up looking like problems in the customer's own task list.
   A badge with no stated tone has no opinion: it takes body text colour.
   Badges that DO declare a tone are excluded and keep their own colour.

   Follow-up worth doing in markup, not CSS: give these badges an explicit
   success tone so "Completed" reads green rather than merely neutral. CSS
   cannot infer meaning from the word inside the span. */
body.fi-panel-portal .kt-badge.kt-badge-outline:not([class*="success"]):not([class*="warning"]):not([class*="danger"]):not([class*="destructive"]):not([class*="primary"]):not([class*="info"]):not([class*="secondary"]) {
  color: var(--portal-text) !important;
}

/* Billing plan rows stay compact while making the subscribed plan visibly
   non-actionable. The neutral treatment keeps the row in the catalog without
   competing with plans the customer can actually choose. */
body.fi-panel-portal .cxp-plan-row-current > td {
  background: #f8fafc !important;
  color: var(--muted-foreground);
}
body.fi-panel-portal .cxp-plan-row-current > td:first-child {
  box-shadow: inset 3px 0 0 rgba(4, 122, 68, .45);
}
body.fi-panel-portal .cxp-plan-current-badge {
  white-space: nowrap;
}

/* Billing tables must fit the portal viewport. They are deliberately not
   horizontal scrollers inside the page, including on a narrow phone. */
body.fi-panel-portal .cxp-billing-invoice-table,
body.fi-panel-portal .cxp-billing-plan-table {
  width: 100%;
  min-width: 0 !important;
  max-width: 100% !important;
  table-layout: fixed;
  box-sizing: border-box;
  border-collapse: collapse;
}
body.fi-panel-portal .cxp-billing-invoice-table th,
body.fi-panel-portal .cxp-billing-invoice-table td,
body.fi-panel-portal .cxp-billing-plan-table th,
body.fi-panel-portal .cxp-billing-plan-table td {
  min-width: 0 !important;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-billing-invoice-table .kt-btn,
body.fi-panel-portal .cxp-billing-plan-table .kt-btn {
  max-width: 100%;
  white-space: normal;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-billing-plan-table td:first-child > div {
  min-width: 0;
  max-width: 100%;
  width: 100%;
  overflow: hidden;
}
body.fi-panel-portal .cxp-billing-plan-table td:first-child strong {
  display: block;
  width: 100%;
  min-width: 0;
  white-space: normal !important;
  overflow-wrap: anywhere;
  word-break: break-word;
}
body.fi-panel-portal .cxp-billing-plan-table .kt-badge {
  max-width: 100%;
  white-space: normal !important;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-internal-plan-badge {
  color: var(--portal-muted) !important;
  background: var(--portal-surface) !important;
  border-color: var(--portal-border) !important;
}
body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(1),
body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(1) { width: 20%; }
body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(2),
body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(2) { width: 18%; }
body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(3),
body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(3) { width: 18%; }
body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(4),
body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(4) { width: 18%; }
body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(5),
body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(5) { width: 26%; }
body.fi-panel-portal .cxp-billing-plan-table th:nth-child(1),
body.fi-panel-portal .cxp-billing-plan-table td:nth-child(1) { width: 48%; }
body.fi-panel-portal .cxp-billing-plan-table th:nth-child(2),
body.fi-panel-portal .cxp-billing-plan-table td:nth-child(2) { width: 20%; }
body.fi-panel-portal .cxp-billing-plan-table th:nth-child(3),
body.fi-panel-portal .cxp-billing-plan-table td:nth-child(3) {
  width: 32%;
  white-space: normal !important;
}
body.fi-panel-portal .cxp-billing-invoice-table .sb-data-money,
body.fi-panel-portal .cxp-billing-plan-table .sb-data-money {
  white-space: nowrap;
  overflow-wrap: normal;
}
body.fi-panel-portal .cxp-payment-copy {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  align-items: flex-start;
}
body.fi-panel-portal .cxp-stripe-attribution {
  padding: .7rem 1.25rem 1rem;
  color: var(--muted-foreground);
  font-size: .75rem;
}
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-billing-plan-table th:nth-child(1),
  body.fi-panel-portal .cxp-billing-plan-table td:nth-child(1) { width: 48%; }
  body.fi-panel-portal .cxp-billing-plan-table th:nth-child(2),
  body.fi-panel-portal .cxp-billing-plan-table td:nth-child(2) { width: 20%; }
  body.fi-panel-portal .cxp-billing-plan-table th:nth-child(3),
  body.fi-panel-portal .cxp-billing-plan-table td:nth-child(3) { width: 32%; }
  body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(2),
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(2) { display: none; }
  body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(1),
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(1) { width: 32%; }
  body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(3),
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(3) { width: 22%; }
  body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(4),
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(4) { width: 16%; }
  body.fi-panel-portal .cxp-billing-invoice-table th:nth-child(5),
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(5) { width: 30%; }
  body.fi-panel-portal .cxp-billing-invoice-table .sb-data-money,
  body.fi-panel-portal .cxp-billing-plan-table .sb-data-money {
    padding-inline: .25rem !important;
    font-size: .75rem;
  }
  body.fi-panel-portal .cxp-billing-invoice-table th:last-child,
  body.fi-panel-portal .cxp-billing-plan-table th:last-child {
    padding-inline: .25rem !important;
  }
  body.fi-panel-portal .cxp-billing-invoice-table td:last-child .kt-btn {
    box-sizing: border-box;
    max-width: 100%;
    padding-inline: .25rem;
    font-size: .75rem;
    white-space: nowrap;
  }
  body.fi-panel-portal .cxp-billing-plan-table td:last-child .kt-btn {
    box-sizing: border-box;
    max-width: 100%;
    padding-inline: .25rem;
    white-space: nowrap;
  }
}

/* On phones these remain semantic tables, but each data row becomes a compact
   readable card. Financial headers and actions must not be squeezed into
   character-sized columns. */
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-billing-invoice-table,
  body.fi-panel-portal .cxp-billing-plan-table {
    display: block;
  }
  body.fi-panel-portal .cxp-billing-invoice-table thead,
  body.fi-panel-portal .cxp-billing-plan-table thead {
    display: none;
  }
  body.fi-panel-portal .cxp-billing-invoice-table tbody,
  body.fi-panel-portal .cxp-billing-plan-table tbody {
    display: grid;
    gap: .65rem;
    padding: .75rem 1rem;
  }
  body.fi-panel-portal .cxp-billing-invoice-table tr,
  body.fi-panel-portal .cxp-billing-plan-table tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: .3rem .75rem;
    min-width: 0;
    padding: .75rem;
    border: 1px solid var(--portal-border) !important;
    border-radius: .55rem;
    background: var(--portal-surface) !important;
  }
  body.fi-panel-portal .cxp-billing-invoice-table td,
  body.fi-panel-portal .cxp-billing-plan-table td {
    display: block;
    width: auto !important;
    min-width: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    text-align: left !important;
  }
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(1),
  body.fi-panel-portal .cxp-billing-plan-table td:nth-child(1) {
    grid-column: 1 / -1;
    grid-row: 1;
  }
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(2) {
    display: block;
    grid-column: 1;
    grid-row: 2;
    color: var(--muted-foreground);
    font-size: .75rem;
  }
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(3) {
    grid-column: 1;
    grid-row: 3;
    text-align: left !important;
  }
  body.fi-panel-portal .cxp-billing-plan-table td:nth-child(2) {
    grid-column: 1;
    grid-row: 2;
    text-align: left !important;
  }
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(4) {
    grid-column: 1;
    grid-row: 4;
    text-align: left !important;
  }
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(5) {
    grid-column: 2;
    grid-row: 2 / 5;
    align-self: center;
    text-align: right !important;
  }
  body.fi-panel-portal .cxp-billing-plan-table td:nth-child(3) {
    grid-column: 1 / -1;
    grid-row: 3;
    text-align: left !important;
  }
  body.fi-panel-portal .cxp-billing-plan-table td:nth-child(3) .kt-btn {
    width: 100%;
  }
  body.fi-panel-portal .cxp-billing-invoice-table td:nth-child(3),
  body.fi-panel-portal .cxp-billing-plan-table td:nth-child(2) {
    font-size: .8125rem;
    font-weight: 650;
  }
  body.fi-panel-portal .cxp-billing-email-table,
  body.fi-panel-portal .cxp-billing-email-table tbody,
  body.fi-panel-portal .cxp-billing-email-table tr {
    display: block;
  }
  body.fi-panel-portal .cxp-billing-email-table tr {
    padding: .85rem 1rem;
  }
  body.fi-panel-portal .cxp-billing-email-table td {
    display: block;
    width: auto !important;
    padding: 0 !important;
    border: 0 !important;
    text-align: left !important;
  }
  body.fi-panel-portal .cxp-billing-email-table td:last-child {
    margin-top: .25rem;
    color: var(--portal-text);
  }
}

/* The staff-test badge rendered as kt-badge-secondary, which resolves to a
   dark surface with the accent foreground on top - near-unreadable, and the
   one thing on this row that must be unmissable (it marks a SKU that charges
   a real card in live mode). Given its own tone: amber tint, warn text, so it
   reads as "careful, this is not a product" without inventing a new colour.
   Uses the same warn tokens as the rest of the portal - no new hues. */
body.fi-panel-portal .cxp-billing-plan-table .kt-badge.cxp-staff-test-badge {
  background: var(--cxp-warn-tint) !important;
  color: var(--cxp-warn-strong) !important;
  border: 1px solid rgba(194, 65, 12, .35) !important;
  font-weight: 700;
}

/* Portal messages chat surface - appended for the chat redesign. */
body.fi-panel-portal .cxp-messages-page {
  display: grid;
  gap: 1rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-chat-card {
  min-width: 0;
  overflow: hidden;
}
body.fi-panel-portal .cxp-chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
body.fi-panel-portal .cxp-chat-scroll {
  min-width: 0;
  max-height: min(32rem, calc(100vh - 18rem));
  padding: 1rem 1.25rem;
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
body.fi-panel-portal .cxp-chat-thread {
  display: flex;
  flex-direction: column;
  gap: .75rem;
  max-height: none;
  overflow: visible;
}
body.fi-panel-portal .cxp-chat-msg {
  align-self: flex-start;
  width: fit-content;
  max-width: min(42rem, 88%);
  min-width: 0;
  padding: .7rem .9rem;
  border: 1px solid var(--portal-border);
  border-radius: .75rem .75rem .75rem .25rem;
  background: var(--portal-surface-soft);
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-chat-msg--customer {
  align-self: flex-end;
  border-color: #d6e4fb;
  border-radius: .75rem .75rem .25rem .75rem;
  background: var(--portal-primary-soft);
}
body.fi-panel-portal .cxp-chat-msg--team,
body.fi-panel-portal .cxp-chat-msg--assistant {
  border-color: var(--cxp-good-line);
  background: var(--cxp-good-tint);
}
body.fi-panel-portal .cxp-chat-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .35rem .5rem;
  margin-bottom: .3rem;
}
body.fi-panel-portal .cxp-chat-meta strong {
  color: var(--portal-heading);
  font-size: .8125rem;
}
body.fi-panel-portal .cxp-chat-meta time {
  color: var(--portal-muted);
  font-size: .75rem;
}
body.fi-panel-portal .cxp-chat-body {
  color: var(--portal-text);
  font-size: .875rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-chat-empty-state {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .25rem .6rem;
  min-height: 4.5rem;
  padding: .9rem 1rem;
  border: 1px solid var(--portal-border);
  border-radius: .65rem;
  background: var(--portal-surface-soft);
}
body.fi-panel-portal .cxp-chat-empty-state strong {
  color: var(--portal-heading);
  font-size: .875rem;
}
body.fi-panel-portal .cxp-chat-empty-state span {
  color: var(--portal-muted);
  font-size: .8125rem;
}
body.fi-panel-portal .cxp-chat-composer {
  display: grid;
  gap: .55rem;
  padding: 1rem 1.25rem 1.1rem;
}
body.fi-panel-portal .cxp-chat-compose-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}
body.fi-panel-portal .cxp-chat-compose-top .cxp-reply-label {
  margin: 0;
}
body.fi-panel-portal .cxp-chat-compose-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: .75rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-chat-compose-row .cxp-reply-input {
  min-width: 0;
  min-height: 5rem;
  margin: 0;
  resize: vertical;
}
body.fi-panel-portal .cxp-chat-compose-row .kt-btn {
  min-height: 40px;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-chat-composer .cxp-reply-help {
  max-width: none;
  margin: 0;
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-chat-header,
  body.fi-panel-portal .cxp-chat-compose-top {
    align-items: flex-start;
    flex-direction: column;
    gap: .35rem;
  }
  body.fi-panel-portal .cxp-chat-scroll {
    max-height: min(28rem, calc(100vh - 16rem));
    padding: .8rem;
  }
  body.fi-panel-portal .cxp-chat-msg {
    max-width: 94%;
  }
  body.fi-panel-portal .cxp-chat-composer {
    padding: .9rem;
  }
  body.fi-panel-portal .cxp-chat-compose-row {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-chat-compose-row .kt-btn {
    justify-self: start;
  }
}
/* Final chat QA corrections: keep provenance readable and the composer thumb-safe. */
body.fi-panel-portal .cxp-chat-ai {
  color: #263347 !important;
  background: #d9e8e4 !important;
}
body.fi-panel-portal .cxp-chat-compose-row .kt-btn {
  min-height: 44px;
}/* Keep the internal chat affordance visible in browsers that hide scrollbars. */
body.fi-panel-portal .cxp-chat-scroll {
  scrollbar-width: thin;
  scrollbar-color: #9aaac0 transparent;
}body.fi-panel-portal .cxp-chat-scroll-hint {
  color: var(--portal-muted);
}
/* Dated banner above a map drawn from an older check. Sits full-width above
   the grid rather than replacing it - the previous design withheld the map
   entirely, which left customers with completed scans staring at an empty
   card. */
body.fi-panel-portal .cxp-map-stale-notice {
  margin-bottom: 1rem;
}

/* kt-alert-warning paints its background as the dark warn colour at 0.3
   alpha, which composites over white into exactly the taupe/brown this
   theme is not allowed to contain. Give the stale-map banner the same flat
   amber tint the rest of the portal uses, with normal body text on top -
   pale surface, dark text, no translucency to muddy. */
body.fi-panel-portal .kt-alert.cxp-map-stale-notice {
  background: var(--cxp-warn-tint) !important;
  border: 1px solid rgba(194, 65, 12, .30) !important;
  color: var(--portal-text) !important;
}
body.fi-panel-portal .kt-alert.cxp-map-stale-notice strong {
  color: var(--cxp-warn-strong) !important;
}

/* Compact score treatment 2026-08-11.
   Summary rings are one KPI, not a hero illustration. Keep the established
   1.6rem score number as the visual anchor while giving the label and status
   room beside it. The same compact scale applies to dashboard and AI engine
   gauges so sibling portal pages do not drift apart. */
body.fi-panel-portal .cxp-health-summary {
  gap: 1rem;
  padding: 1.25rem;
}

body.fi-panel-portal .cxp-health-summary-ring {
  flex: 0 0 5rem;
  width: 5rem;
  height: 5rem;
}

body.fi-panel-portal .cxp-health-summary-ring circle {
  stroke-width: 6;
}

body.fi-panel-portal .cxp-health-summary-ring .cxp-score-number {
  font-size: 1.6rem;
}

body.fi-panel-portal .cxp-health-summary-copy {
  min-width: 0;
  flex: 1 1 auto;
  gap: .25rem;
}

body.fi-panel-portal .cxp-health-summary-copy .kt-card-title {
  margin: 0;
}

body.fi-panel-portal .cxp-gauge {
  flex-basis: 4rem;
  width: 4rem;
  height: 4rem;
}

body.fi-panel-portal .cxp-ai-engine-gauge {
  flex-basis: 4.75rem;
  width: 4.75rem;
  height: 4.75rem;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-health-summary {
    flex-direction: row !important;
    flex-wrap: nowrap;
    align-items: center;
    gap: .85rem;
    padding: 1.25rem;
  }

  body.fi-panel-portal .cxp-health-summary-ring {
    flex-basis: 4.25rem;
    width: 4.25rem;
    height: 4.25rem;
  }

  body.fi-panel-portal .cxp-health-summary-copy {
    min-width: 0;
  }

  body.fi-panel-portal .cxp-health-summary-copy > * {
    overflow-wrap: anywhere;
  }
}

/* The middle score uses readable orange for text and bright amber for
   non-text graphics. Keep this split explicit at the end of the cascade so
   older token rules cannot turn rings, bars, or status dots brown. */
body.fi-panel-portal {
  --cxp-tone-middle: #C2410C;
  --cxp-warn: #F79009;
  --cxp-warn-text: #C2410C;
  --cxp-warn-strong: #C2410C;
}

body.fi-panel-portal .cxp-score-middle,
body.fi-panel-portal .cxp-score-number.cxp-score-middle,
body.fi-panel-portal .cxp-score-label.cxp-score-middle,
body.fi-panel-portal .sb-stat-value.cxp-score-middle,
body.fi-panel-portal .cxp-ai-score.cxp-score-middle,
body.fi-panel-portal .cxp-rankings-tone-middle,
body.fi-panel-portal .cxp-rankings-too-little,
body.fi-panel-portal .cxp-taskbar-action--ideas,
body.fi-panel-portal .cxp-conn-status--off,
body.fi-panel-portal .kt-badge[class*="warning"],
body.fi-panel-portal .cxp-badge-warn,
body.fi-panel-portal .cxp-activity-status--needs-input,
body.fi-panel-portal .cxp-live-status--middle,
body.fi-panel-portal .cxp-ai-engine-delta.cxp-score-middle,
body.fi-panel-portal .cxp-ai-change-icon.cxp-score-middle,
body.fi-panel-portal .cxp-ai-score-delta .cxp-score-middle,
body.fi-panel-portal .cxp-map-cell--middle,
body.fi-panel-portal .cxp-billing-plan-table .cxp-staff-test-badge {
  color: #C2410C !important;
}

body.fi-panel-portal .cxp-gauge-middle,
body.fi-panel-portal .cxp-health-summary-ring.cxp-gauge-middle,
body.fi-panel-portal circle.cxp-gauge-progress.cxp-gauge-middle {
  stroke: #F79009 !important;
}

body.fi-panel-portal .cxp-fill-middle,
body.fi-panel-portal .cxp-health-progress-fill.cxp-fill-middle,
body.fi-panel-portal .cxp-rankings-tone-fill-unchanged,
body.fi-panel-portal .cxp-map-legend .cxp-map-key--middle,
body.fi-panel-portal .cxp-status-led--middle,
body.fi-panel-portal .cxp-led--middle,
body.fi-panel-portal .cxp-conn-dot--off {
  background: #F79009 !important;
}

body.fi-panel-portal .cxp-chart-tone-dot.cxp-score-middle,
body.fi-panel-portal .cxp-chart-tone-dot {
  background: #F79009;
}

body.fi-panel-portal .cxp-map-cell--middle {
  background: rgba(194, 65, 12, .14) !important;
}

/* Independent visual review 2026-08-11: make the score label read as a
   semantic status without competing with the established score number. */
body.fi-panel-portal .cxp-health-summary-copy .cxp-score-label {
  display: inline-flex;
  align-self: flex-start;
  width: max-content;
  max-width: 100%;
  padding: .15rem .45rem;
  border-radius: .35rem;
  font-size: .95rem !important;
  line-height: 1.25;
}

body.fi-panel-portal .cxp-health-summary-copy .cxp-score-label.cxp-score-good {
  color: #047A44 !important;
  background: var(--cxp-good-tint);
}

body.fi-panel-portal .cxp-health-summary-copy .cxp-score-label.cxp-score-middle {
  color: #C2410C !important;
  background: var(--cxp-warn-tint);
}

body.fi-panel-portal .cxp-health-summary-copy .cxp-score-label.cxp-score-poor {
  color: #B42318 !important;
  background: var(--cxp-bad-tint);
}

/* A status pill hugs its own text. .cxp-health-summary-copy is a flex column,
   whose default align-items:stretch was pulling the "Scanned ..." pill across
   the full card - it read as a stretched button rather than a status chip,
   which is what made the compacted summary card still look unfinished. The
   card's own 720px cap stays: it is deliberate (see the content-hugging note
   above) and stretching it to full width would recreate the half-empty panel
   the density rule exists to prevent. */
body.fi-panel-portal .cxp-health-summary-copy {
  align-items: flex-start;
}
body.fi-panel-portal .cxp-health-summary-copy > .cxp-live-status,
body.fi-panel-portal .cxp-health-summary-copy > .cxp-activity-status,
body.fi-panel-portal .cxp-health-summary-copy > .kt-badge {
  align-self: flex-start;
  width: auto;
  max-width: 100%;
}

/* CYT-370: GBP analysis is a full-width, gauge-first summary. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-score-band {
  max-width: none;
  display: grid;
  grid-template-columns: auto minmax(14rem, 1fr) minmax(15rem, 1.15fr);
  align-items: center;
  gap: 1.25rem;
  padding: 1.25rem 1.5rem;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-score-band .cxp-health-summary-copy { min-width: 0; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-score-band .cxp-health-summary-ring {
  flex-basis: 6rem;
  width: 6rem;
  height: 6rem;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar { min-width: 0; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-track {
  position: relative;
  display: flex;
  gap: 0;
  height: 2.15rem;
  padding: 0;
  border-radius: 999px;
  background: var(--portal-border);
  overflow: visible;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-segment {
  display: grid;
  flex: 1 1 0;
  place-items: center;
  min-width: 0;
  border-radius: 999px;
  color: var(--portal-muted);
  background: var(--portal-surface-2, #f4f6fa);
  font-size: .72rem;
  font-weight: 800;
  line-height: 1;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-segment--a { color: #047a44; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-segment--b { color: #2774c9; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-segment--c { color: #c2410c; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-segment--d,
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-segment--f { color: #b42318; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-segment.is-active {
  color: var(--portal-heading);
  background: var(--portal-primary-soft);
  box-shadow: inset 0 0 0 1px var(--portal-primary);
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-marker {
  position: absolute;
  z-index: 1;
  top: -5px;
  left: var(--cxp-grade-position);
  width: 3px;
  height: 2.75rem;
  border-radius: 999px;
  background: var(--portal-heading);
  box-shadow: 0 0 0 3px var(--portal-surface);
  transform: translateX(-50%);
  transition: left .8s cubic-bezier(.22,.61,.36,1);
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1rem;
  margin-top: 1rem;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile {
  min-width: 0;
  min-height: 0 !important;
  padding: .9rem 1rem !important;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile .sb-stat-label,
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile .sb-stat-caption {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile .sb-stat-value {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 1.35rem !important;
  letter-spacing: -.03em !important;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile .sb-stat-value small {
  margin-left: .15rem;
  color: var(--portal-muted);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: 0;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile .cxp-gbp-stat-text { font-size: 1.05rem !important; }
@media (max-width: 980px) {
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-score-band { grid-template-columns: auto minmax(0, 1fr); }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar { grid-column: 1 / -1; }
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-score-band { grid-template-columns: auto minmax(0, 1fr); gap: .85rem; padding: 1rem; }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-summary-ring { flex-basis: 5.5rem; width: 5.5rem; height: 5.5rem; }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .65rem; }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile { padding: .75rem !important; }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile .sb-stat-value { font-size: 1.15rem !important; }
}
@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-grade-bar-marker { transition: none; }
}

/* ============================================================================
   Post-checkout celebration (2026-08-11).
   Owner: "some kind of a message with the background... some fireworks kind of
   things for like two, three, five seconds or whatever just to show that
   you're subscribed."

   Decoration only. It never carries meaning on its own - the named-plan banner
   and the bell entry do that - so it is pointer-events:none, aria-hidden, and
   removed from the DOM after it plays. Colours come from the existing portal
   tokens; this introduces no new hue, and nothing here is amber-dark enough to
   read as the brown the theme bans.
   ============================================================================ */
body.fi-panel-portal .cxp-celebrate {
  position: fixed;
  inset: 0;
  z-index: 60;
  overflow: hidden;
  pointer-events: none;   /* must never intercept a click on the real page */
  background: transparent; /* the page stays fully readable underneath */
}

body.fi-panel-portal .cxp-celebrate-piece {
  position: absolute;
  top: -6vh;
  width: 9px;
  height: 14px;
  border-radius: 2px;
  opacity: 0;
  animation-name: cxp-celebrate-fall;
  animation-timing-function: cubic-bezier(.25, .6, .35, 1);
  animation-iteration-count: 1;
  animation-fill-mode: forwards;
}

body.fi-panel-portal .cxp-celebrate-piece--0 { background: var(--cxp-good); }
body.fi-panel-portal .cxp-celebrate-piece--1 { background: var(--portal-primary); }
body.fi-panel-portal .cxp-celebrate-piece--2 { background: var(--cxp-warn); }
body.fi-panel-portal .cxp-celebrate-piece--3 {
  background: var(--cxp-good-text);
  border-radius: 50%;
  width: 10px;
  height: 10px;
}

@keyframes cxp-celebrate-fall {
  0%   { opacity: 0; transform: translate3d(0, 0, 0) rotate(0deg); }
  8%   { opacity: 1; }
  85%  { opacity: 1; }
  100% { opacity: 0; transform: translate3d(6vw, 108vh, 0) rotate(540deg); }
}

/* Never animate for a visitor who asked not to. The blade also removes the
   nodes in JS, so this is the second of two independent guards. */
@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-celebrate { display: none !important; }
  body.fi-panel-portal .cxp-celebrate-piece { animation: none !important; }
}

/* Portal legal pages: compact, readable public chrome using the portal tokens. */
body.fi-panel-portal.cx-legal-body,
body.fi-panel-portal.cx-legal-body * { box-sizing: border-box; }

body.fi-panel-portal.cx-legal-body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  margin: 0;
  background: var(--portal-bg);
  color: var(--portal-text);
}

body.fi-panel-portal .cx-legal-skip {
  position: absolute;
  z-index: 10;
  top: .5rem;
  left: .75rem;
  padding: .5rem .75rem;
  border-radius: .35rem;
  color: #fff;
  background: var(--portal-heading);
  transform: translateY(-150%);
}

body.fi-panel-portal .cx-legal-skip:focus { transform: translateY(0); }

body.fi-panel-portal .cx-legal-header {
  border-bottom: 1px solid var(--portal-border);
  background: var(--portal-surface);
}

body.fi-panel-portal .cx-legal-header-inner {
  width: min(100% - 2rem, 72rem);
  min-height: 4.5rem;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

body.fi-panel-portal .cx-legal-brand {
  display: inline-flex;
  align-items: center;
  gap: .65rem;
  color: var(--portal-heading);
  text-decoration: none;
}

body.fi-panel-portal .cx-legal-brand-label {
  padding-left: .65rem;
  border-left: 1px solid var(--portal-border);
  color: var(--portal-muted);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .03em;
}

body.fi-panel-portal .cx-legal-main {
  flex: 1 0 auto;
  padding: 2rem 1rem 3rem;
}

body.fi-panel-portal .cx-legal-wrap {
  width: min(100%, 72rem);
  margin: 0 auto;
}

body.fi-panel-portal .cx-legal-breadcrumb,
body.fi-panel-portal .cx-legal-sibling-links {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .45rem .7rem;
  color: var(--portal-muted);
  font-size: .75rem;
}

body.fi-panel-portal .cx-legal-breadcrumb a,
body.fi-panel-portal .cx-legal-sibling-links a {
  color: var(--color-blue-700);
  text-decoration: none;
}

body.fi-panel-portal .cx-legal-signin,
body.fi-panel-portal .cx-legal-signin:hover,
body.fi-panel-portal .cx-legal-signin:focus-visible {
  background-color: var(--color-blue-700);
  color: var(--color-white);
}

body.fi-panel-portal .cx-legal-breadcrumb a:hover,
body.fi-panel-portal .cx-legal-breadcrumb a:focus-visible,
body.fi-panel-portal .cx-legal-sibling-links a:hover,
body.fi-panel-portal .cx-legal-sibling-links a:focus-visible {
  text-decoration: underline;
}

body.fi-panel-portal .cx-legal-heading { margin: 1.25rem 0 1rem; }
body.fi-panel-portal .cx-legal-heading .cxp-card-kicker { margin: 0 0 .35rem; }
body.fi-panel-portal .cx-legal-heading .cxp-page-title { margin: 0; }
body.fi-panel-portal .cx-legal-heading .cxp-page-sub { margin: .4rem 0 0; }

body.fi-panel-portal .cx-legal-draft {
  display: flex;
  align-items: flex-start;
  gap: .65rem;
  margin-bottom: 1rem;
  padding: .85rem 1rem;
  border: 1px solid var(--cxp-warn);
  border-radius: .55rem;
  color: var(--cxp-warn-text);
  background: var(--cxp-warn-tint);
  font-size: .8125rem;
  line-height: 1.5;
}

body.fi-panel-portal .cx-legal-draft strong { flex: 0 0 auto; color: var(--cxp-warn-text); }

body.fi-panel-portal .cx-legal-card { margin: 0; }
body.fi-panel-portal .cx-legal-card .kt-card-content { padding: 1.5rem; }
body.fi-panel-portal .cx-legal-section + .cx-legal-section {
  margin-top: 1.45rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--portal-border);
}

body.fi-panel-portal .cx-legal-section h2 {
  margin: 0 0 .45rem;
  color: var(--portal-heading);
  font-size: 1rem;
  line-height: 1.35;
}

body.fi-panel-portal .cx-legal-section p {
  margin: 0;
  color: var(--portal-text);
  font-size: .875rem;
  line-height: 1.7;
}

body.fi-panel-portal .cx-legal-section p + p { margin-top: .7rem; }
body.fi-panel-portal .cx-legal-sibling-links { margin-top: 1rem; }
body.fi-panel-portal .cx-legal-sibling-links a {
  display: inline-flex;
  min-height: 24px;
  align-items: center;
}

@media (max-width: 600px) {
  body.fi-panel-portal .cx-legal-header-inner { min-height: 4rem; }
  body.fi-panel-portal .cx-legal-main { padding: 1.25rem .75rem 2rem; }
  body.fi-panel-portal .cx-legal-card .kt-card-content { padding: 1.1rem; }
  body.fi-panel-portal .cx-legal-draft { flex-direction: column; gap: .25rem; }
  body.fi-panel-portal .cx-legal-section p { font-size: .8125rem; }
  body.fi-panel-portal .cx-legal-signin { min-height: 44px; }
  body.fi-panel-portal .cx-legal-sibling-links {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .2rem .5rem;
  }
  body.fi-panel-portal .cx-legal-sibling-links > span { grid-column: 1 / -1; }
  body.fi-panel-portal .cx-legal-sibling-links a { min-height: 44px; }
  body.fi-panel-portal .cxp-portal-footer a { min-width: 44px; justify-content: center; }
}

/* Inline upsell plan card - Links and citations (2026-08-11).
   Replaces a bare "View available plans" link with the one plan that actually
   turns the page on, priced and buyable in place.
   (Re-appended after a concurrent write to this file dropped it - see the
   commit message; do not edit cxp.css while an executor is also appending.) */
body.fi-panel-portal .cxp-upsell { text-align: left; }
body.fi-panel-portal .cxp-upsell p { margin: 0 0 .75rem; }
body.fi-panel-portal .cxp-upsell-plan {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem 1.25rem;
  margin-bottom: .75rem;
  text-align: left;
}
body.fi-panel-portal .cxp-upsell-plan-copy {
  display: flex; flex-direction: column; gap: .15rem; min-width: 0; text-align: left;
}
body.fi-panel-portal .cxp-upsell-plan-buy {
  display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
}
/* The price is the point of this block, so it takes the portal's large-number
   treatment rather than a bespoke smaller one - one number scale everywhere. */
body.fi-panel-portal .cxp-upsell .cxp-upsell-price {
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--portal-text);
  line-height: 1;
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-upsell-plan { align-items: flex-start; }
  body.fi-panel-portal .cxp-upsell-plan-buy { width: 100%; justify-content: space-between; }
}

/* Trial banner tone (2026-08-11).
   .cxp-billing-banner hard-sets a yellow surface with !important, which is
   right for the unpaid warning it was written for but collapses every banner
   to the same look. That silently defeated the trial banner's one design
   decision: informational most of the month, warmer inside the final week.
   With a single yellow for both, "23 days left" and "last day" were visually
   identical, so the escalation conveyed nothing.
   Scoped overrides restore the two states. No new hue - blue is the portal
   primary tint, amber is the same warn tint used everywhere else, and neither
   is dark enough to read as the brown this theme bans. */
body.fi-panel-portal .kt-alert.cxp-trial-banner.kt-alert-primary {
  background-color: var(--portal-primary-soft) !important;
  border-color: color-mix(in srgb, var(--cxp-good) 22%, transparent) !important;
}
body.fi-panel-portal .kt-alert.cxp-trial-banner.kt-alert-warning {
  background-color: var(--cxp-warn-tint) !important;
  border-color: rgba(194, 65, 12, .30) !important;
}
body.fi-panel-portal .kt-alert.cxp-trial-banner.kt-alert-warning .kt-alert-title {
  color: var(--cxp-warn-strong);
}

/* ============================================================================
   Portal sweep fixes 2026-08-11 (found by a computed-style pass over all 13
   portal pages, not by eye).
   ============================================================================ */

/* 1. Last hardcoded brown in the portal. .cxp-aud-note ("Your call and form
      tracking is not linked...") was still #6B3A00, the exact muddy value the
      rest of the theme was moved off earlier today. Same tone as every other
      warn-ish note now. */
body.fi-panel-portal .cxp-aud-note {
  color: var(--cxp-warn-strong) !important;
}

/* 2. The section tab row scrolled SIDEWAYS ON DESKTOP: nine tabs measuring
      1345px inside a 1114px container at a 1440px viewport, with
      overflow-x:auto quietly hiding four of them behind a drag.

      A horizontal scrollbar inside a page is a broken layout, not a feature -
      a customer cannot see there are more tabs, so "AI search visibility" and
      "Ongoing tracking" were effectively invisible on a normal laptop. Tabs
      wrap onto a second row instead, which costs one row of height and makes
      every section reachable. Shared by gbp-analysis, autopilot-seo and
      map-rankings, so this fixes all three. */
body.fi-panel-portal .cxp-map-tabs {
  flex-wrap: wrap;
  overflow-x: visible;
  row-gap: .35rem;
}

/* ============================================================================
   Mobile data tables (2026-08-11). Owner's standing rule: a table that needs
   sideways dragging is a broken layout, not a scrollable one - "I don't want
   a scroller on the bottom to navigate between this page inside another page."

   Measured the actual overflow at 390px before choosing a fix, because the
   five offenders were not the same problem:
     dashboard invoices        397px  (7px over)
     search-performance terms  426px  (36px over)
     website-health issues     507px  (117px over)
     rankings pages            572px  (182px over)
     rankings keywords        1056px  (666px over)

   The first four are modest and come from nowrap cells refusing to give, not
   from genuinely having too much content. Letting them wrap and shrink is the
   proportionate fix and needs no markup changes. The keyword table is a
   different problem and is handled separately below.
   ============================================================================ */
@media (max-width: 600px) {
  body.fi-panel-portal .sb-data-table:not(.cxp-billing-invoice-table):not(.cxp-billing-plan-table) {
    table-layout: fixed;
    width: 100%;
    min-width: 0 !important;
  }
  body.fi-panel-portal .sb-data-table:not(.cxp-billing-invoice-table):not(.cxp-billing-plan-table) th,
  body.fi-panel-portal .sb-data-table:not(.cxp-billing-invoice-table):not(.cxp-billing-plan-table) td {
    white-space: normal !important;
    overflow-wrap: anywhere;
    word-break: break-word;
    min-width: 0 !important;
    padding-inline: .5rem;
  }
  /* Long identifiers (URLs, keywords) are the usual culprit: give the first
     column the slack and keep the metric columns narrow, rather than letting
     every column fight for the same space. */
  body.fi-panel-portal .sb-data-table:not(.cxp-billing-invoice-table):not(.cxp-billing-plan-table) td:first-child,
  body.fi-panel-portal .sb-data-table:not(.cxp-billing-invoice-table):not(.cxp-billing-plan-table) th:first-child {
    width: 45%;
  }
}

/* The rankings keyword table keeps its compact Keyword + Current row on a
   phone. Page and historical checks are available through the disclosure row,
   so restoring the hidden columns or a sideways drag is unnecessary. */
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-url {
    display: none !important;
  }
  body.fi-panel-portal .cxp-rankings-mobile-detail-row {
    display: table-row;
  }
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-kw {
    width: auto;
  }
}

/* Final piece of the keyword table on mobile: the position stack
   ("7 / No change / From 42 impressions") measured 140px inside a 93px cell
   and was pushing the whole table 55px past its card. It is a flex/grid
   child, so it needs an explicit min-width:0 before it will agree to shrink -
   the default min-width:auto refuses to go below its content. */
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now,
  body.fi-panel-portal .cxp-rankings-position-stack,
  body.fi-panel-portal .cxp-rankings-position-stack > * {
    min-width: 0 !important;
    overflow-wrap: anywhere;
  }
  body.fi-panel-portal .cxp-rankings-position-stack {
    max-width: 100%;
  }
  body.fi-panel-portal .cxp-rankings-position-stack > * {
    width: 100%;
  }
}

/* Correction to the rule a few blocks above: setting .cxp-col-kw to
   width:auto defeated the 45% first-column constraint from the generic
   mobile-table rule, so the keyword column took 263px of a 356px card and
   pushed the table 40px wide again. Under table-layout:fixed the columns need
   explicit shares. Keyword gets the larger share (it is the identifier and
   the longest text), position takes the rest. */
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-kw {
    width: 58% !important;
  }
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now {
    width: 42% !important;
  }
}

/* GBP detail tables are three-column tables whose status cells contain the
   shared .kt-badge. Metronic keeps badges on one line, so the longest status
   label establishes a min-content width that is about 13px wider than the
   390px card content box. Every GBP tab uses this same shell, which made the
   otherwise small overflow repeat across all nine panels. Let the table and
   its status badges participate in the mobile width budget instead of
   creating an inner horizontal scroller. */
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-table > .kt-card-content {
    box-sizing: border-box;
    min-width: 0;
    width: 100%;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-table > .kt-card-content > .sb-data-table {
    table-layout: fixed;
    min-width: 0 !important;
    width: 100% !important;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-table > .kt-card-content > .sb-data-table th,
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-table > .kt-card-content > .sb-data-table td {
    min-width: 0 !important;
    overflow-wrap: anywhere;
    white-space: normal !important;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-table > .kt-card-content > .sb-data-table .kt-badge {
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    padding-block: .3rem !important;
    white-space: normal !important;
    overflow-wrap: break-word;
    text-align: center;
    line-height: 1.25;
  }
}

/* CYT-786. The GBP detail-table mobile rule above (`.sb-data-table` under
   `.cxp-gbp-analysis-page .kt-card-table`) also catches this page's own
   "Top things to improve" table, whose three columns are NOT the same
   shape as the nine per-area findings tables: Status is a short badge,
   Item is a short bold label, but Where carries a full evidence sentence.
   With no explicit column widths, table-layout:fixed split all three
   evenly (~130px each on a 390px phone), so "Where" wrapped one or two
   characters per line and even its own header ("Where") wrapped mid-word.
   Desktop is untouched - table-layout:auto there already gives the long
   column proportionally more room from real content width, matching the
   same content-weighted approach CYT-803 already proved on the sibling
   .cxp-gbp-findings-table (25/20/55), scaled for a narrower 3-column
   phone width where the evidence column needs the majority share.

   !important is required on each width: the generic 5-offenders mobile
   fix above (`.sb-data-table:not(...):not(...) th/td:first-child { width:
   45% }`) outranks a plain (0,4,2) rule here on specificity alone and was
   stacking with these three declarations - 45+28+50=123%, which the
   browser's fixed-layout algorithm then normalized back down (Status
   45/123=36.6%, not the intended 22%). !important on all three keeps
   them exact and leaves the generic rule to do its job everywhere else. */
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-recommendations-table {
    table-layout: fixed !important;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-recommendations-table th:nth-child(1),
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-recommendations-table td:nth-child(1) {
    width: 30% !important;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-recommendations-table th:nth-child(2),
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-recommendations-table td:nth-child(2) {
    width: 30% !important;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-recommendations-table th:nth-child(3),
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-recommendations-table td:nth-child(3) {
    width: 40% !important;
  }
}

/* Dashboard SEO merge: keep every detail table inside its card at desktop and
   mobile widths. Long URLs and change values truncate in their cells instead
   of creating a second horizontal scrolling surface. */
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table {
  width: 100%;
  max-width: 100%;
  table-layout: fixed;
}

body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table th,
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table td {
  min-width: 0;
  overflow-wrap: anywhere;
}

body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table th:nth-child(1),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table td:nth-child(1) { width: 34%; }
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table th:nth-child(2),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table td:nth-child(2) { width: 12%; }
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table th:nth-child(3),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table td:nth-child(3) { width: 14%; }
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table th:nth-child(4),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table td:nth-child(4) { width: 12%; }
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table th:nth-child(5),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table td:nth-child(5) { width: 28%; }

body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes th:nth-child(1),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes td:nth-child(1) { width: 12%; }
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes th:nth-child(2),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes td:nth-child(2) { width: 24%; }
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes th:nth-child(3),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes td:nth-child(3) { width: 14%; }
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes th:nth-child(4),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes td:nth-child(4) { width: 25%; }
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes th:nth-child(5),
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes td:nth-child(5) { width: 25%; }

/* Core page changes reuse the shared mobile card-row table pattern instead
   of squeezing five semantic fields into competing percentage columns.
   CYT-853: the min-width:0 above was applied uniformly to BOTH the short
   category label (<strong> "Meta") and the long path (<span>), so the flex
   row squeezed the SHORT label down to sub-character width and it broke
   one letter per line (inherited overflow-wrap: break-word from the parent
   td). The label never needs to shrink or wrap - it stays intact and
   fixed-width; only the path absorbs the remaining space and wraps. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes .sb-dense-identity {
    min-width: 0;
    max-width: 100%;
    white-space: normal;
  }

  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes .sb-dense-identity > strong {
    flex: none;
    white-space: nowrap;
  }

  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes .sb-dense-identity > span {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
  }
}

body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table--changes .cxp-cell-truncate {
  display: block;
  min-width: 0;
  max-width: 100% !important;
}

body.fi-panel-portal .cxp-dashboard-seo-details .cxp-cell-truncate {
  max-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 600px) {
  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table {
    font-size: .75rem;
  }

  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table th,
  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-merge-table td {
    padding-inline: .4rem;
  }
}

/* CYT-802. Three dashboard-only tab rules were deleted here. They restyled the
   shared tab component with literals no other page used, and they are the
   reason the dashboard's strip WAS a different component from everyone else's.
   Measured live at 1440 before the canonical component landed:

     inactive fill   dashboard rgb(248,250,252)  (the #f8fafc below)
                     everywhere else oklch(0.975 0.006 265)  (a token)
     active fill     dashboard FLAT, every other page a blue-to-pink GRADIENT -
                     because this block's (0,4,2) `background:` shorthand beat
                     cxs-skin.css's (0,3,2) `background-image` here and nowhere
                     else
     focus ring      3px here, 2px on every other tab strip

   pages.css now declares one canonical line-tab component for the whole portal
   and forces it with !important, so these three rules render nothing today.
   They are deleted rather than left inert: an inert override is how the drift
   comes back the next time someone raises specificity. Nothing replaces them -
   the shared `.cxp-map-tabs > button` rules already say all of this in tokens. */

body.fi-panel-portal .cxp-dashboard-seo-details .cxp-map-tabs {
  margin-inline: 0;
}

@media (max-width: 600px) {
  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-map-tabs {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: .35rem;
  }

  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-map-tabs > button {
    width: 100%;
    min-width: 0;
    white-space: normal;
    padding-inline: .35rem;
    font-size: .8125rem;
    line-height: 1.15;
  }

  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-map-tabs > button:last-child { grid-column: auto; }
}

/* Free public-site audit panel (trial / unconnected accounts). */
body.fi-panel-portal .cxp-free-audit-speed {
  display: flex;
  align-items: baseline;
  gap: .6rem;
  flex-wrap: wrap;
  margin: .25rem 0 1rem;
}
body.fi-panel-portal .cxp-free-audit-list {
  list-style: none;
  margin: 0 0 1rem;
  padding: 0;
  display: grid;
  gap: .6rem;
}
body.fi-panel-portal .cxp-free-audit-item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: .65rem;
  align-items: start;
}
/* The badge states its own meaning ("OK" / "Look at this") rather than relying
   on colour, so the list still reads correctly in greyscale and for a
   colourblind customer. */
body.fi-panel-portal .cxp-free-audit-item .kt-badge {
  white-space: nowrap;
  margin-top: .1rem;
}

/* Consolidated dashboard score row. Each tile is intentionally self-contained:
   source, native scale, date and explainer travel with the measurement. */
body.fi-panel-portal .cxp-scoreboard {
  margin-bottom: 1rem;
}
body.fi-panel-portal .cxp-scoreboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: .75rem;
  padding: 1rem;
}
body.fi-panel-portal .cxp-scoreboard-tile {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .22rem;
  padding: .8rem;
  border: 1px solid var(--border);
  border-radius: .55rem;
  background: var(--background);
}
body.fi-panel-portal .cxp-scoreboard-tile-top,
body.fi-panel-portal .cxp-score-comparison-label {
  display: flex;
  align-items: center;
  align-items: flex-start;
  justify-content: space-between;
  gap: .5rem;
}
body.fi-panel-portal .cxp-score-source-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 1.35rem;
  width: 1.35rem;
  height: 1.35rem;
  border-radius: 999px;
  background: var(--muted);
  color: var(--muted-foreground);
  font-size: .58rem;
  font-weight: 700;
  letter-spacing: .02em;
}
body.fi-panel-portal .cxp-score-source-mark img {
  display: block;
  width: 18px;
  height: 18px;
  object-fit: contain;
  border-radius: 999px;
}
body.fi-panel-portal .cxp-scoreboard-value {
  display: block;
  margin-top: .28rem;
  font-size: 1.16rem;
  line-height: 1.15;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-scoreboard-source {
  color: var(--foreground);
  font-size: .72rem;
  font-weight: 650;
  line-height: 1.25;
}
body.fi-panel-portal .cxp-scoreboard-detail,
body.fi-panel-portal .cxp-scoreboard-date {
  color: var(--muted-foreground);
  font-size: .7rem;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-scoreboard-explainer {
  margin-top: .3rem;
  border-top: 1px solid var(--border);
  padding-top: .3rem;
}
body.fi-panel-portal .cxp-scoreboard-explainer summary {
  color: var(--muted-foreground);
  cursor: pointer;
  font-size: .7rem;
  line-height: 1.25;
}
body.fi-panel-portal .cxp-scoreboard-explainer p {
  margin: .35rem 0 0;
  color: var(--muted-foreground);
  font-size: .7rem;
  line-height: 1.35;
}
body.fi-panel-portal .cxp-score-comparison {
  margin: 0 1rem 1rem;
  border-top: 1px solid var(--border);
  padding-top: .85rem;
}
body.fi-panel-portal .cxp-score-comparison-heading h3 {
  margin: 0;
  color: var(--foreground);
  font-size: .95rem;
}
body.fi-panel-portal .cxp-score-comparison-heading p {
  margin: .2rem 0 .7rem;
  color: var(--muted-foreground);
  font-size: .75rem;
  line-height: 1.4;
}
body.fi-panel-portal .cxp-score-comparison-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .75rem;
}
body.fi-panel-portal .cxp-score-comparison-item {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .2rem;
  padding: .7rem .8rem;
  border: 1px solid var(--border);
  border-radius: .55rem;
}
body.fi-panel-portal .cxp-score-comparison-value {
  font-size: 1rem;
  line-height: 1.15;
}
body.fi-panel-portal .cxp-score-comparison-note {
  margin: .7rem 0 0;
  color: var(--muted-foreground);
  font-size: .72rem;
  line-height: 1.35;
}
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-scoreboard-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    padding: .75rem;
    gap: .55rem;
  }
  body.fi-panel-portal .cxp-scoreboard-tile {
    padding: .65rem;
  }
  body.fi-panel-portal .cxp-scoreboard-value {
    font-size: 1rem;
  }
  body.fi-panel-portal .cxp-score-comparison {
    margin-inline: .75rem;
  }
}
@media (max-width: 430px) {
  body.fi-panel-portal .cxp-scoreboard-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-score-comparison-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* CYT-340: department gauges are portal-owned SVG components. Keep their
   geometry and transparent circle treatment in the shipped portal stylesheet
   so an incomplete shared-page asset can never turn them into black discs. */
body.fi-panel-portal .cxp-department-gauge-ring {
  position: relative;
  inline-size: clamp(4.4rem, 6vw, 5.5rem);
  block-size: clamp(4.4rem, 6vw, 5.5rem);
  flex: 0 0 auto;
  margin: .25rem 0 .1rem;
}
body.fi-panel-portal .cxp-department-gauge-ring svg {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  overflow: visible;
  transform: rotate(-90deg);
}
body.fi-panel-portal .cxp-department-gauge-ring circle {
  fill: none !important;
  stroke-width: 6;
}
body.fi-panel-portal .cxp-department-gauge-ring .cxp-gauge-track {
  stroke: var(--border);
}
body.fi-panel-portal .cxp-department-gauge-ring .cxp-gauge-progress {
  stroke-linecap: round;
}
body.fi-panel-portal .cxp-department-gauge-ring .cxp-gauge-native {
  stroke: var(--muted-foreground);
  opacity: .65;
}
body.fi-panel-portal .cxp-department-gauge-ring .cxp-scoreboard-value {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: .3rem;
  margin: 0;
  font-size: clamp(.72rem, 1.2vw, .92rem);
  line-height: 1.08;
}

/* CYT-525 portal UI-QA contract: the card owns the only rounded frame. It
   clips every child so table bands, rows and banners cannot expose a square
   seam, while direct card regions and table structure stay square inside the
   frame. Keep this single rule scoped to the portal panel; admin surfaces keep
   their own component geometry. */
body.fi-panel-portal {
  --portal-card-radius: var(--radius-lg);
}
body.fi-panel-portal .kt-card {
  border-radius: var(--portal-card-radius);
  overflow: hidden;
}
body.fi-panel-portal .cxp-scoreboard-tile,
body.fi-panel-portal .cxp-score-comparison-item,
body.fi-panel-portal .cxp-coordinator-stat {
  border-radius: var(--portal-card-radius);
  overflow: hidden;
}
body.fi-panel-portal .kt-card > * {
  border-radius: 0;
}
body.fi-panel-portal .kt-card :is(.kt-table, .kt-table thead, .kt-table tbody, .kt-table tr) {
  border-radius: 0;
}

/* Dashboard gauges: reserve two title lines, keep provenance marks uniform,
   and keep the complete value readable below a compact ring value. */
body.fi-panel-portal .cxp-department-gauge .sb-stat-label {
  display: flex;
  align-items: flex-start;
  min-height: 2.5em;
  line-height: 1.25;
}
body.fi-panel-portal .cxp-score-source-mark {
  flex: 0 0 1.5rem;
  width: 1.5rem;
  height: 1.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--card);
}
body.fi-panel-portal .cxp-score-source-mark img {
  width: 20px;
  height: 20px;
  border-radius: var(--radius-xs);
}
body.fi-panel-portal .cxp-score-source-mark i {
  color: var(--primary);
  font-size: 1rem;
  line-height: 1;
}
body.fi-panel-portal .cxp-department-gauge .cxp-scoreboard-title {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 2.5em;
  color: var(--foreground);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-semibold);
  line-height: 1.25;
  overflow-wrap: anywhere;
  text-align: left;
}
body.fi-panel-portal .cxp-department-gauge-ring .cxp-scoreboard-value {
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: clip;
  font-size: clamp(.74rem, 1.15vw, .95rem);
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-department-gauge-ring .cxp-scoreboard-value--small {
  font-size: clamp(.68rem, 1vw, .82rem);
}
body.fi-panel-portal .cxp-department-gauge-ring .cxp-scoreboard-value--tiny {
  font-size: clamp(.6rem, .85vw, .72rem);
  letter-spacing: -.04em;
}
body.fi-panel-portal .cxp-scoreboard-value-full {
  color: var(--foreground);
  font-size: var(--text-2xs);
  font-weight: var(--font-weight-semibold);
  line-height: 1.25;
}

/* The comparison is a real card section, so its inner cards get the same
   content gutter as every other portal card and the note has a clear rhythm. */
body.fi-panel-portal .cxp-score-comparison {
  margin: 0 0 1rem;
  /* 1rem (16px) undershot the Style Book's actual 20px content-gutter this
     comment already says it's matching - card-has-interior-padding caught it. */
  padding: 1.25rem;
  border-top: 0;
}
body.fi-panel-portal .cxp-score-comparison-heading p {
  margin-bottom: 1rem;
}
body.fi-panel-portal .cxp-score-comparison-item {
  padding: 1rem;
  border-radius: var(--portal-card-radius);
}
body.fi-panel-portal .cxp-score-comparison-note {
  margin-top: 1rem;
}

/* Live activity stats are compact stat tiles rather than loose numbers. */
body.fi-panel-portal .cxp-coordinator-stat {
  position: relative;
  min-height: 5.5rem;
  padding: .75rem;
  border: 1px solid var(--portal-border);
  border-radius: var(--portal-card-radius);
  background: var(--portal-surface);
}
body.fi-panel-portal .cxp-coordinator-stat-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  margin-bottom: .35rem;
  border: 1px solid var(--portal-border);
  border-radius: var(--radius-sm);
  color: var(--primary);
}
body.fi-panel-portal .cxp-coordinator-stat-value {
  display: block;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-coordinator-empty {
  display: flex;
  align-items: center;
  gap: .5rem;
}
body.fi-panel-portal .cxp-coordinator-empty .cxp-status-led {
  display: inline-block;
  flex: 0 0 .55rem;
  width: .55rem;
  height: .55rem;
  border-radius: 999px;
  background: var(--cxp-good, var(--color-green-500));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--cxp-good, var(--color-green-500)) 16%, transparent);
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-score-comparison {
    margin-inline: 0;
    padding: 1.25rem;
  }
  body.fi-panel-portal .cxp-score-comparison-item {
    padding: .85rem;
  }
}
.fi-panel-portal .cxp-chip-urgent,body.fi-panel-portal .kt-badge-outline.cxp-chip-urgent{background:#FEE4E2 !important;border:1px solid #FDA29B !important;color:#B42318 !important;}

/* CYT-425: preserve the candidate portal tab navigation contract. */
body.fi-panel-portal .cxp-map-details,
body.fi-panel-portal .cxp-map-tab-panel {
  gap: 1rem;
  margin: 0 !important;
  min-width: 0;
}
body.fi-panel-portal .cxp-map-tab-panel > .kt-card {
  margin: 0 !important;
}
body.fi-panel-portal .cxp-map-tabs {
  gap: .35rem;
  padding: .35rem;
  overflow-x: visible;
  flex-wrap: wrap;
  justify-content: flex-start;
}
body.fi-panel-portal .cxp-map-tabs > button {
  min-height: 44px;
  border: 1px solid var(--portal-border);
  background: var(--portal-surface-soft);
  color: var(--portal-text);
}
body.fi-panel-portal .cxp-map-tabs > button:hover,
body.fi-panel-portal .cxp-map-tabs > button:focus-visible {
  border-color: var(--portal-primary);
  background: var(--portal-primary-soft);
  color: var(--portal-primary);
  outline: 2px solid var(--portal-primary);
  outline-offset: 2px;
}
body.fi-panel-portal .cxp-map-tabs > button.is-active {
  border-color: var(--portal-primary);
  background: var(--portal-primary);
  color: #fff;
}
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-map-tabs {
    display: flex;
    flex-wrap: wrap;
    overflow-x: visible;
  }
  body.fi-panel-portal .cxp-map-tabs > button {
    width: auto;
    min-height: 44px;
  }
}


/* CYT-501: shared contrast and mobile interaction corrections. */
body.fi-panel-portal .cxp-portal-footer-copy,
body.fi-panel-portal .cxp-portal-footer-links a {
  color: var(--portal-text);
}

body.fi-panel-portal .fi-sidebar-group-label {
  color: var(--portal-text) !important;
}

body.fi-panel-portal .kt-alert.cxp-billing-connection-alert {
  background-color: #f0b100 !important;
  border-color: #d99e00 !important;
  color: var(--portal-heading) !important;
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-pager-btn,
  body.fi-panel-portal .cxp-profile-form-grid > .kt-btn,
  body.fi-panel-portal .cxp-site-report-panel .kt-btn,
  body.fi-panel-portal .cxp-billing-invoice-table .kt-btn {
    min-height: 44px;
  }
}

/* Keep every named portal tab row readable without a ragged final item:
   equal-width tracks are shared by each row, while mobile uses fewer tracks
   so labels wrap inside a predictable, bounded number of rows.
   .cxp-map-page always has exactly 5 tabs, so a fixed 5-track grid is
   correct there. GBP sub-pages do NOT - Review strength has 1 (no tab strip
   at all), Ranking & tracking has 2, Profile & listings has 3 - so the same
   fixed repeat(5) left 3 empty tracks (measured live: 2 buttons at 278px
   each inside a 1440px row) once the vertical-rail override that used to
   hide this was removed. GBP now gets the adaptive auto-flow column count
   ("TAB STRIPS FIT" further down uses the same pattern) so N tabs always
   share the full row, whatever N is. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-map-tabs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
}
body.fi-panel-portal .cxp-map-page .cxp-map-tabs {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

body.fi-panel-portal .cxp-gbp-analysis-page .cxp-map-tabs > button,
body.fi-panel-portal .cxp-map-page .cxp-map-tabs > button {
  width: 100%;
  min-width: 0;
  text-align: center;
  white-space: normal;
}

@media (max-width: 760px) {
  /* No GBP-specific rule here on purpose: below 640px the universal
     ".cxp-map-tabs.cxp-map-tabs" rule ("TAB STRIPS FIT" section) already
     stacks every tab strip in the portal to one column with equal-height
     rows - checked live, same behaviour on autopilot-seo/website-health/
     ai-visibility. A GBP-only 700px 2-column tier would just reintroduce
     the exact ragged-height risk that rule was written to prevent, for the
     narrow 640-760px band only. Between 760px and 640px, GBP keeps the
     desktop auto-flow-column rule above (2 or 3 even columns), which is
     still readable at that width - measured, not assumed. */
  body.fi-panel-portal .cxp-map-page .cxp-map-tabs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  body.fi-panel-portal .cxp-map-page .cxp-map-tabs > button:last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }
}

/* CYT-437: one honest stat-card pattern across the five portal surfaces. */
body.fi-panel-portal .cxp-scoreboard { margin-bottom: 1rem; }
body.fi-panel-portal .cxp-scoreboard-heading { margin-bottom: 1rem; }
body.fi-panel-portal .cxp-scoreboard-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: .75rem;
  padding: 0;
  align-items: stretch;
}
@media (max-width: 1400px) {
  body.fi-panel-portal .cxp-scoreboard-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 900px) and (min-width: 621px) {
  body.fi-panel-portal .cxp-scoreboard-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card { min-height: 176px; padding: .75rem .6rem; }
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring { width: 64px; height: 64px; }
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring-value { font-size: 18px; flex-direction: column; gap: 0; line-height: 1.05; }
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring-value .cxp-score-unit { font-size: 10px; margin-left: 0; }
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__title { font-size: 12px; line-height: 16px; }
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__title > span:last-child { -webkit-line-clamp: 3; }
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__explainer { flex: 0 0 28px; width: 28px; height: 28px; margin: -6px 0 0 -4px; }
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__explainer summary { width: 28px; height: 28px; font-size: 15px; }
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__value--empty { font-size: 15px; line-height: 1.25; overflow-wrap: normal; word-break: normal; hyphens: none; }
body.fi-panel-portal .cxp-scoreboard-grid.cxp-scoreboard-grid--cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) {
  body.fi-panel-portal .cxp-scoreboard-grid.cxp-scoreboard-grid--cols-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  body.fi-panel-portal .cxp-scoreboard-grid.cxp-scoreboard-grid--cols-4 { grid-template-columns: 1fr; }
}
body.fi-panel-portal .cxp-stat-card {
  box-sizing: border-box;
  min-width: 0;
  min-height: 232px;
  display: grid;
  grid-template-rows: 40px 104px 22px auto;
  align-content: space-between;
  gap: 0;
  padding: 1rem;
  overflow: visible;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
  color: var(--foreground);
}
body.fi-panel-portal .cxp-stat-card--compact { min-height: 128px; grid-template-rows: 24px 56px 20px auto; }
body.fi-panel-portal .cxp-stat-card__title-row { min-width: 0; display: flex; align-items: flex-start; justify-content: space-between; gap: .5rem; }
body.fi-panel-portal .cxp-stat-card__title { min-width: 0; display: inline-flex; align-items: flex-start; gap: .3rem; color: var(--foreground); font-size: 14px; font-weight: 600; line-height: 20px; }
body.fi-panel-portal .cxp-stat-card__title > span:last-child { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
body.fi-panel-portal .cxp-stat-card__provider { flex: 0 0 28px; width: 28px; height: 28px; display: inline-grid; place-items: center; color: var(--primary); font-size: 1.15rem; }
body.fi-panel-portal .cxp-stat-card__provider img { display: block; width: 28px; height: 28px; object-fit: contain; }
body.fi-panel-portal .cxp-stat-card__explainer { position: relative; flex: 0 0 40px; width: 40px; height: 40px; margin: -10px 0 0 -7px; }
body.fi-panel-portal .cxp-stat-card__explainer summary { width: 40px; height: 40px; display: grid; place-items: center; list-style: none; cursor: pointer; color: var(--muted-foreground); font-size: 18px; }
body.fi-panel-portal .cxp-stat-card__explainer summary::-webkit-details-marker { display: none; }
body.fi-panel-portal .cxp-stat-card__explainer summary:hover,
body.fi-panel-portal .cxp-stat-card__explainer[open] summary,
body.fi-panel-portal .cxp-stat-card__explainer summary:focus-visible { color: var(--primary); }
body.fi-panel-portal .cxp-stat-card__explainer > div { position: absolute; z-index: 10; top: 34px; left: 0; width: min(260px, calc(100vw - 3rem)); padding: .7rem .8rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); box-shadow: 0 12px 30px color-mix(in oklab, var(--foreground) 18%, transparent); }
body.fi-panel-portal .cxp-stat-card__explainer p { margin: 0; color: var(--muted-foreground); font-size: 12px; line-height: 18px; }
body.fi-panel-portal .cxp-stat-card__value-zone { min-width: 0; display: grid; place-items: center; text-align: center; }
body.fi-panel-portal .cxp-stat-card__ring { position: relative; width: 96px; height: 96px; }
body.fi-panel-portal .cxp-stat-card__ring > svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
body.fi-panel-portal .cxp-stat-card__ring circle { fill: none !important; stroke-width: 8; }
body.fi-panel-portal .cxp-stat-card__ring .cxp-gauge-track { stroke: var(--muted); }
body.fi-panel-portal .cxp-stat-card__ring .cxp-gauge-progress { stroke-linecap: round; }
body.fi-panel-portal .cxp-stat-card__ring-value { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; align-self: center; gap: 0; color: var(--foreground); font-size: 30px; font-weight: 700; line-height: 1.05; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-stat-card__ring-value .cxp-score-unit { margin-left: 0; color: var(--muted-foreground); font-size: 12px; font-weight: 500; }
body.fi-panel-portal .cxp-stat-card__value { max-width: 100%; margin: 0; color: var(--foreground); font-size: 44px; font-weight: 700; line-height: 1; letter-spacing: -.04em; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
body.fi-panel-portal .cxp-stat-card__value--position { color: var(--foreground) !important; font-size: 44px; }
/* 2026-08-21. Scoped to the NUMBER. This rule is (0,3,1) and the prose rule
   further down is (0,2,1), so before the exclusions below it silently beat the
   prose variants' own type scale on every compact card - which is why
   "Not recorded" and a status word like "Open" were still rendering at the
   stat number's 44px after being moved off the mono face. A compact card
   shrinks its FIGURE; it has no opinion about a sentence.

   The exclusions are wrapped in :where() ON PURPOSE and must stay that way.
   A bare :not(.cxp-stat-card__value--text)... takes its arguments'
   specificity and lifts this rule to (0,5,1), which puts it above
   patterns.css `.cxp-gbp-analysis-page .cxp-gbp-kpi .cxp-stat-card__value`
   (0,4,1) - GBP analysis KPI numbers jumped 32px to 44px when it was written
   that way, measured live. :where() contributes zero specificity, so this
   stays exactly (0,3,1): still above the prose rule, still below the page
   rules that are meant to override it. */
body.fi-panel-portal .cxp-stat-card--compact .cxp-stat-card__value:not(:where(.cxp-stat-card__value--text, .cxp-stat-card__value--empty, .cxp-stat-card__value--failed)) { font-size: 44px; }
body.fi-panel-portal .cxp-stat-card--compact .cxp-stat-card__value--empty,
body.fi-panel-portal .cxp-stat-card__value--failed { color: var(--muted-foreground); }
body.fi-panel-portal .cxp-stat-card__delta-value { font-size: 36px; font-weight: 700; line-height: 1; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-stat-card__verdict { min-width: 0; min-height: 22px; color: var(--muted-foreground); font-size: 14px; font-weight: 600; line-height: 22px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.fi-panel-portal .cxp-stat-card__meta { min-width: 0; color: var(--muted-foreground); font-size: 12px; font-weight: 400; line-height: 20px; overflow-wrap: anywhere; }
body.fi-panel-portal .cxp-listing-kpi-link {
  display: block;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}
body.fi-panel-portal .cxp-listing-kpi-link:hover,
body.fi-panel-portal .cxp-listing-kpi-link:focus-visible {
  color: inherit;
  text-decoration: none;
}
body.fi-panel-portal .cxp-listing-kpi-link:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
  border-radius: var(--radius-lg);
}
body.fi-panel-portal .cxp-stat-card--never .cxp-stat-card__ring .cxp-gauge-progress { stroke: transparent !important; }
body.fi-panel-portal .cxp-stat-card__ring--flat .cxp-stat-card__ring-value { font-size: 22px; }
/* CYT-814 bare-stat: the three Live activity figures ("1 site in your
   account", "1 site checked in the last day", "30 open items") are the whole
   point of their cards, yet they rendered at 15px against a 14px body -
   ratio 1.07, i.e. styled like a paragraph, inside an 84px gauge ring. The
   line above already asks for a larger figure on the ring-value box, but the
   generated skin sets `.cxp-stat-card__ring-value .cxp-score-number {
   font-size: 15px !important }` - a value chosen for the number inside a
   SCORE gauge - and that !important won on the inner span. Higher
   specificity + !important so the figure wins here.

   Deliberately scoped to `.cxp-coordinator-summary` rather than to `--flat`:
   the scoreboard and score-comparison cards use the same flat ring at the
   same 84px but carry values like "#34.5", which at 24px would be wider than
   the ring's ~71px inner diameter. These three cards hold at most two
   characters, so 24px sits comfortably inside the ring and lands on the same
   figure-to-ring ratio as the GBP hero (30px in a 120px ring). */
body.fi-panel-portal .cxp-coordinator-summary .cxp-stat-card__ring .cxp-score-number {
  font-size: 24px !important;
  line-height: 1.15;
}
body.fi-panel-portal .cxp-listing-activity-link {
  display: block;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}
body.fi-panel-portal .cxp-listing-activity-link:hover,
body.fi-panel-portal .cxp-listing-activity-link:focus-visible {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-summary-ring { flex-basis: 7.5rem; width: 7.5rem; height: 7.5rem; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-summary-ring circle { stroke-width: 8; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-summary-ring .cxp-score-number { font-size: 30px; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .75rem; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-tile { display: none; }
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-stat-card { display: grid; min-height: 128px !important; height: auto; padding: 1rem; }
/* CYT-647 defect #4: the fixed "240px" max in this minmax() capped every
   real tile at 240px even after auto-fit collapsed the empty tracks it
   didn't need, so 3 real tiles in a 1438px row left grid-template-columns
   computing "240px 240px 240px 0px 0px" - 674px of dead space on the
   right. Swapping the max to 1fr keeps the same auto-fit column COUNT
   (still driven off the 240px min, so the 1-column mobile collapse below
   is unaffected) but lets the real tracks share the freed space instead
   of leaving it empty. */
body.fi-panel-portal .cxp-coordinator-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: .75rem; justify-content: start; align-items: stretch; }
body.fi-panel-portal .cxp-coordinator-summary .cxp-stat-card { min-height: 128px; height: auto; }
body.fi-panel-portal .cxp-ai-change-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: .75rem; padding: 1rem; }
body.fi-panel-portal .cxp-ai-delta-card { min-height: 152px !important; height: 152px; }
body.fi-panel-portal .cxp-ai-change-summary { margin: 0; padding: 0 1rem 1rem; color: var(--muted-foreground); font-size: 14px; line-height: 20px; }
body.fi-panel-portal .cxp-score-comparison-grid .cxp-stat-card { min-height: 232px; }
@media (max-width: 1199px) { body.fi-panel-portal .cxp-ai-change-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 700px) { body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) {
  body.fi-panel-portal .cxp-scoreboard-grid { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card:not(.cxp-stat-card--compact) { min-height: 152px; height: auto; grid-template-columns: 96px minmax(0, 1fr); grid-template-rows: 40px 22px auto; column-gap: 1rem; align-content: center; }
  body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__value--empty { font-size: 12px; line-height: 1.2; overflow-wrap: normal; word-break: normal; hyphens: none; }
  body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card:not(.cxp-stat-card--compact) .cxp-stat-card__title-row { grid-column: 2; }
  body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card:not(.cxp-stat-card--compact) .cxp-stat-card__value-zone { grid-column: 1; grid-row: 1 / span 3; }
  body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card:not(.cxp-stat-card--compact) .cxp-stat-card__verdict { grid-column: 2; grid-row: 2; }
  body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card:not(.cxp-stat-card--compact) .cxp-stat-card__meta { grid-column: 2; grid-row: 3; }
  body.fi-panel-portal .cxp-ai-change-grid { grid-template-columns: 1fr; padding: .75rem; }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-stat-card { height: auto; }
  body.fi-panel-portal .cxp-coordinator-summary { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-coordinator-summary .cxp-stat-card { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-stat-card,
  body.fi-panel-portal .cxp-stat-card * { transition: none !important; animation: none !important; }
}
/* CYT-480: restore the live stat-card variants and the site-report geometry.
   The component emits type/state/density modifiers, but the shared stylesheet
   previously only styled the base card and compact density. Keep the layout
   content-sized so a small gauge or score cannot leave a barren card behind. */
body.fi-panel-portal .cxp-stat-card--score,
body.fi-panel-portal .cxp-stat-card--position,
body.fi-panel-portal .cxp-stat-card--count,
body.fi-panel-portal .cxp-stat-card--delta,
body.fi-panel-portal .cxp-stat-card--standard,
body.fi-panel-portal .cxp-stat-card--measured {
  box-sizing: border-box;
  min-width: 0;
  align-items: stretch;
}
body.fi-panel-portal .cxp-stat-card--score .cxp-stat-card__value-zone,
body.fi-panel-portal .cxp-stat-card--position .cxp-stat-card__value-zone,
body.fi-panel-portal .cxp-stat-card--count .cxp-stat-card__value-zone,
body.fi-panel-portal .cxp-stat-card--delta .cxp-stat-card__value-zone,
body.fi-panel-portal .cxp-stat-card--measured .cxp-stat-card__value-zone {
  min-width: 0;
  min-height: 0;
  align-self: stretch;
}
/* 2026-08-21, QA on AI visibility. --empty and --failed were left out of this
   rule, so they inherited the stat NUMBER's display treatment - 44px on a
   normal card, 36px on a delta card, with -.04em tracking and line-height 1.
   An English phrase set that way is a headline, and at 390px "Not recorded"
   dominated its tile. All three prose variants share the treatment now. The
   mono FACE is taken off them in cxs-skin.css, which owns the type layer. */
body.fi-panel-portal .cxp-stat-card__value--text,
body.fi-panel-portal .cxp-stat-card__value--empty,
body.fi-panel-portal .cxp-stat-card__value--failed {
  max-width: 100%;
  font-size: clamp(1rem, 2.4vw, 1.8rem);
  line-height: 1.15;
  letter-spacing: -.02em;
  overflow-wrap: anywhere;
  text-align: center;
}

/* An absent reading is not a headline. --text carries a real value ("Open",
   "Aug 5, 2026") and stays prominent; --empty and --failed only say that we
   have nothing to show, so they sit a clear step below it - the same call the
   scoreboard grid already makes when it drops them to 15px. Floor is 15px at
   390px, comfortably above the 12px minimum. */
body.fi-panel-portal .cxp-stat-card__value--empty,
body.fi-panel-portal .cxp-stat-card__value--failed {
  font-size: clamp(.9375rem, 1.5vw, 1.25rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: normal;
}

/* Scores are a single KPI. Shrink-wrap the summary at wide widths while
   retaining a full-width mobile card. */
body.fi-panel-portal .cxp-health-summary {
  width: fit-content;
  max-width: 100%;
}

/* Website Health is a vertical stack after the report panel moved to its own
   page. Keep the score summary on the same right edge as the evidence and
   trend cards instead of leaving a misleading shrink-wrapped gap. */
body.fi-panel-portal .cxp-health-summary {
  width: 100%;
  max-width: none;
}

body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-score-band {
  width: fit-content;
  max-width: 100%;
}

/* The report panel has three summary facts. Keep the card aligned with the
   portal content column now that the report is rendered in the page itself. */
body.fi-panel-portal .cxp-site-report-panel {
  width: 100%;
  max-width: 100%;
  overflow: hidden;
}
body.fi-panel-portal .cxp-site-report-summary {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
  align-items: start;
}
body.fi-panel-portal .cxp-site-report-summary > div {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: .25rem;
}
body.fi-panel-portal .cxp-site-report-summary strong {
  color: var(--foreground);
  font-size: 1.1rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-site-report-summary small {
  color: var(--muted-foreground);
  font-size: .75em;
  font-weight: 500;
}
body.fi-panel-portal .cxp-site-report-frame-wrap {
  padding-top: 0;
}
body.fi-panel-portal .cxp-site-report-frame {
  display: block;
  width: 100%;
  height: clamp(420px, 70vh, 760px);
  min-height: 420px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--background);
}

/* The dashboard comparison uses this page-level modifier in addition to the
   shared comparison class; keep both selectors explicit for CSS QA. */
body.fi-panel-portal .cxp-dashboard-comparison {
  margin: 0 0 1rem;
  /* same 20px-gutter fix as .cxp-score-comparison above - this dashboard-
     scoped twin was the one actually winning (equal specificity, later in
     file), so the other edit alone had no visible effect. */
  padding: 1.25rem;
  border-top: 0;
}
body.fi-panel-portal .cxp-dashboard-comparison .cxp-score-comparison-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: .75rem;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-health-summary,
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-score-band,
  body.fi-panel-portal .cxp-site-report-panel {
    width: 100%;
  }
  body.fi-panel-portal .cxp-site-report-summary {
    grid-template-columns: 1fr;
    gap: .75rem;
  }
  body.fi-panel-portal .cxp-site-report-frame {
    height: 520px;
    min-height: 520px;
  }
  body.fi-panel-portal .cxp-dashboard-comparison {
    padding: 1.25rem;
  }
  body.fi-panel-portal .cxp-dashboard-comparison .cxp-score-comparison-grid {
    grid-template-columns: 1fr;
  }
}

body.fi-panel-portal .cxp-rankings-filter-tabs {
  /* CYT-855: was a fixed 8.5rem (136px) per column, packing all 7
     position-band tiles into the grid's left edge and leaving ~400px of
     dead card space to the right at 1920px - the source of "tiles hug
     the border" (there was no room left to give them a real gutter
     against). 1fr lets the 7 tiles fill the card width the same way the
     'At a glance' KPI grid already does. */
  grid-template-columns: repeat(7, minmax(0, 1fr)) !important;
  justify-content: stretch !important;
}
body.fi-panel-portal .cxp-dashboard-page .cxp-merge-table th:nth-child(n+2),
body.fi-panel-portal .cxp-dashboard-page .cxp-merge-table td:nth-child(n+2),
body.fi-panel-portal .cxp-rankings-keyword-table th,
body.fi-panel-portal .cxp-rankings-keyword-table td {
  text-align: left !important;
}
body.fi-panel-portal .cxp-rankings-overview-list > div {
  display: grid !important;
  grid-template-columns: minmax(0, 1fr) 4rem !important;
  justify-content: initial !important;
}
body.fi-panel-portal .cxp-rankings-overview-list > div:last-child {
  border-bottom: 0 !important;
}
body.fi-panel-portal .cxp-rankings-filter strong,
body.fi-panel-portal .cxp-health-issue strong {
  font-size: 1.75rem !important;
}
body.fi-panel-portal .fi-topbar,
body.fi-panel-portal .fi-topbar > nav {
  overflow: visible !important;
  border-bottom: 1px solid var(--portal-border) !important;
  box-shadow: none !important;
  transform: none !important;
}
body.fi-panel-portal .cxp-taskbar {
  flex-wrap: nowrap !important;
  gap: .45rem !important;
  margin-inline: .25rem .35rem !important;
}
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-rankings-filter-tabs {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
}

/* CYT-557: keep the score tone contract separate from small-text contrast.
   Scores use --cxp-tone-good directly; ordinary text uses --cxp-good-strong. */
body.fi-panel-portal .cxp-ranking-page-link,
body.fi-panel-portal .cxp-inline-action,
body.fi-panel-portal .cxp-page-explainer-trigger,
body.fi-panel-portal .cxp-coordinator-footer a.kt-btn-outline {
  color: var(--cxp-good-strong) !important;
}

/* White labels on the neon score green are only 3.19:1. Use the established
   strong green for the tab fill so the 13px active-tab labels clear 4.5:1. */
body.fi-panel-portal .cxp-map-tabs > button.is-active,
body.fi-panel-portal .cxp-dashboard-seo-details .cxp-map-tabs > button.is-active {
  border-color: var(--cxp-good-strong) !important;
  background: var(--cxp-good-strong) !important;
  color: #fff !important;
}

/* CYT-557: the portal typography floor is 12px. The area chart labels are
   data/legibility text, not decorative metadata, so keep all seven dashboard
   nodes at the floor while retaining the existing seven-label thinning. */
body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-title,
body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-note,
body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-axis,
body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-chart-axis span,
body.fi-panel-portal .cxp-trend-chart-area .cxp-trend-point-labels span,
body.fi-panel-portal .cxp-score-comparison-note,
body.fi-panel-portal .cxp-score-comparison-note strong {
  font-size: .75rem;
}

/* QA defect #6 (minor): at <=480px the up-to-7 date labels below a trend
   chart already sit at the 12px typography floor above, so the only lever
   left to stop them colliding ("Jul 28Jul 30") is showing fewer of them.
   Hide every other label but never the last one (nth-child parity does not
   reliably land on the last item - point counts vary 2-7) so both edges of
   the range stay anchored and readable. */
@media (max-width: 480px) {
  body.fi-panel-portal .cxp-trend-point-labels span:nth-child(even):not(:last-child) {
    display: none;
  }
}

/* CYT-594: mobile billing tables stay readable inside the card instead of
   exposing a second horizontal scrolling surface. Desktop table rendering is
   intentionally untouched; phones present each row as a labeled card. */
@media (max-width: 600px) {
  body.fi-panel-portal .cxp-source-fallback {
    font-size: .75rem;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table,
  body.fi-panel-portal .cxp-portal-invoice-table {
    display: block;
    width: 100%;
    max-width: 100%;
    min-width: 0 !important;
    table-layout: fixed;
    box-sizing: border-box;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table thead,
  body.fi-panel-portal .cxp-portal-invoice-table thead {
    display: none;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table tbody,
  body.fi-panel-portal .cxp-portal-invoice-table tbody {
    display: grid;
    width: 100%;
    box-sizing: border-box;
    gap: .65rem;
    padding: .75rem 1rem;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table tr,
  body.fi-panel-portal .cxp-portal-invoice-table tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: .35rem .75rem;
    min-width: 0;
    padding: .75rem;
    border: 1px solid var(--portal-border) !important;
    border-radius: .55rem;
    background: var(--portal-surface) !important;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table td,
  body.fi-panel-portal .cxp-portal-invoice-table td {
    display: block;
    width: auto !important;
    min-width: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    text-align: left !important;
    white-space: normal !important;
    overflow-wrap: anywhere;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table td::before,
  body.fi-panel-portal .cxp-portal-invoice-table td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: .15rem;
    color: var(--portal-muted);
    font-size: .75rem;
    font-weight: 650;
    line-height: 1.25;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table td:nth-child(1),
  body.fi-panel-portal .cxp-portal-invoice-table td:nth-child(1) {
    grid-column: 1 / -1;
    grid-row: 1;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table td:nth-child(2) {
    grid-column: 1;
    grid-row: 2;
    white-space: nowrap !important;
    overflow-wrap: normal;
    word-break: normal;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table td:nth-child(3) {
    grid-column: 2;
    grid-row: 2;
    text-align: right !important;
    white-space: nowrap !important;
    overflow-wrap: normal;
    word-break: normal;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table td:nth-child(4) {
    grid-column: 1 / -1;
    grid-row: 3;
    text-align: right !important;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table td:first-child > div,
  body.fi-panel-portal .cxp-dashboard-plan-table td:first-child strong {
    display: block;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    white-space: normal !important;
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  body.fi-panel-portal .cxp-dashboard-plan-table td:nth-child(4) .kt-btn,
  body.fi-panel-portal .cxp-portal-invoice-table td:nth-child(5) .kt-btn {
    max-width: 100%;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  body.fi-panel-portal .cxp-portal-invoice-table td:nth-child(2) {
    grid-column: 1;
    grid-row: 2;
    color: var(--muted-foreground);
    font-size: .75rem;
  }

  body.fi-panel-portal .cxp-portal-invoice-table td:nth-child(3) {
    grid-column: 1;
    grid-row: 3;
    font-weight: 650;
    white-space: nowrap !important;
    overflow-wrap: normal;
    word-break: normal;
  }

  body.fi-panel-portal .cxp-portal-invoice-table td:nth-child(4) {
    grid-column: 1;
    grid-row: 4;
  }

  body.fi-panel-portal .cxp-portal-invoice-table td:nth-child(5) {
    grid-column: 2;
    grid-row: 2 / 5;
    align-self: center;
    text-align: right !important;
  }
}

/* CYT-606: the rankings table is a responsive data surface, not a desktop
   canvas. Keep the wrapper from becoming a sideways-drag affordance, let the
   identifier columns truncate, and reserve the narrow columns for rank data. */
body.fi-panel-portal .cxp-rankings-table-scroll {
  width: 100%;
  max-width: 100%;
  min-width: 0;
  overflow-x: hidden;
}
body.fi-panel-portal .cxp-rankings-keyword-table,
body.fi-panel-portal .cxp-rankings-keyword-table th,
body.fi-panel-portal .cxp-rankings-keyword-table td {
  box-sizing: border-box;
  min-width: 0 !important;
}
body.fi-panel-portal .cxp-rankings-keyword-table {
  width: 100%;
  max-width: 100%;
  table-layout: fixed;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-kw {
  width: 26%;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-url {
  width: 30%;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-hist {
  width: 8%;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now {
  width: 20%;
  white-space: normal;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-cell-url {
  display: table-cell;
  min-width: 0;
  max-width: none;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-cell-url .cxp-ranking-page-link {
  display: block;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-rankings-position-stack {
  min-width: 0;
  max-width: 100%;
}
body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now .cxp-rankings-delta {
  white-space: normal;
}
@media (max-width: 1099px) {
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-hist {
    display: none;
  }
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-kw {
    width: 45%;
  }
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-url {
    width: 32%;
  }
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now {
    width: 23%;
  }
}
@media (max-width: 899px) {
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-url {
    display: none !important;
  }
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-kw {
    width: 58%;
  }
  body.fi-panel-portal .cxp-rankings-keyword-table .cxp-col-now {
    width: 42%;
  }
  body.fi-panel-portal .cxp-rankings-mobile-detail-row {
    display: table-row;
  }
}

/* CYT-607: intermediate-width portal tables must use the card width as their
   layout budget. Keep all columns readable by allowing labels and values to
   wrap, while retaining the existing desktop table treatment above 1099px. */
@media (max-width: 1099px) {
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table,
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > .sb-data-table,
  body.fi-panel-portal .cxp-dashboard-page .kt-card-content > .sb-data-table {
    box-sizing: border-box;
    width: 100% !important;
    max-width: 100%;
    min-width: 0 !important;
    table-layout: fixed;
  }

  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table th,
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table td,
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > .sb-data-table th,
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > .sb-data-table td,
  body.fi-panel-portal .cxp-dashboard-page .kt-card-content > .sb-data-table th,
  body.fi-panel-portal .cxp-dashboard-page .kt-card-content > .sb-data-table td {
    box-sizing: border-box;
    min-width: 0 !important;
    max-width: 100%;
    overflow-wrap: anywhere;
    white-space: normal !important;
    word-break: break-word;
  }

  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table .cxp-table-sort {
    box-sizing: border-box;
    min-width: 0;
    max-width: 100%;
    white-space: normal !important;
    overflow-wrap: anywhere;
  }

  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > .sb-data-table .cxp-cell-url {
    display: block;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap !important;
  }
}

/* CYT-607 regression guard: the same card-width budget applies to the
   previously remediated portal tables while the sidebar leaves the main
   column narrow at 640-920px. */
@media (max-width: 1099px) {
  body.fi-panel-portal .cxp-health-page .kt-card-content > .sb-data-table,
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-content > .sb-data-table,
  body.fi-panel-portal .cxp-profile-page .kt-card-content > .kt-table {
    box-sizing: border-box;
    width: 100% !important;
    max-width: 100%;
    min-width: 0 !important;
    table-layout: fixed;
  }

  body.fi-panel-portal .cxp-health-page .kt-card-content > .sb-data-table th,
  body.fi-panel-portal .cxp-health-page .kt-card-content > .sb-data-table td,
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-content > .sb-data-table th,
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-content > .sb-data-table td,
  body.fi-panel-portal .cxp-profile-page .kt-card-content > .kt-table th,
  body.fi-panel-portal .cxp-profile-page .kt-card-content > .kt-table td {
    box-sizing: border-box;
    min-width: 0 !important;
    max-width: 100%;
    overflow-wrap: anywhere;
    white-space: normal !important;
    word-break: break-word;
  }

  body.fi-panel-portal .cxp-health-page .kt-card-content > .sb-data-table .kt-badge,
  body.fi-panel-portal .cxp-gbp-analysis-page .kt-card-content > .sb-data-table .kt-badge {
    min-width: 0 !important;
    max-width: 100% !important;
    height: auto !important;
    padding-block: .3rem !important;
    white-space: normal !important;
    overflow-wrap: break-word;
    text-align: center;
    line-height: 1.25;
  }
}

/* CYT-610: the GBP headline gauge belongs to the same compact summary row as
   Rating, Reviews, Status, and Last scanned. The mobile layout keeps the ring
   and its copy side by side so the score card does not become a wide empty box. */
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  align-items: stretch;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card {
  min-height: 152px !important;
  grid-template-rows: 24px 96px 20px auto;
}
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (max-width: 700px) {
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 620px) {
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid {
    grid-template-columns: 1fr;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card {
    grid-template-columns: 96px minmax(0, 1fr);
    grid-template-rows: 32px 22px auto;
    column-gap: 1rem;
    align-content: center;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card .cxp-stat-card__title-row {
    grid-column: 2;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card .cxp-stat-card__value-zone {
    grid-column: 1;
    grid-row: 1 / span 3;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card .cxp-stat-card__verdict {
    grid-column: 2;
    grid-row: 2;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card .cxp-stat-card__meta {
    grid-column: 2;
    grid-row: 3;
  }
  body.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card .cxp-stat-card__ring {
    width: 88px;
    height: 88px;
  }
}
/* CYT-625: finish the shared metric treatment across the remaining portal
   surfaces. Every headline number gets the same score-ring/motion component;
   page-specific grids only control placement and density. */
body.fi-panel-portal .cxp-shared-stat-grid > .cxp-stat-card {
  min-width: 0;
  height: auto;
}
body.fi-panel-portal .cxp-map-stat-grid.cxp-shared-stat-grid {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: .75rem;
}
body.fi-panel-portal .cxp-searchperf-stat-grid.cxp-shared-stat-grid {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: .75rem;
}
body.fi-panel-portal .cxp-rankings-stat-grid.cxp-shared-stat-grid {
  gap: .75rem;
}
/* CYT-843: the Rankings overview page always renders exactly 5 KPI cards
   (headlineMetrics() is a fixed 5-item list) - at the shared 4-column base
   above, the 5th card ("SERP features") wrapped alone onto its own row with
   3 empty column slots beside it. Scoped to its own extra class (same
   convention as .cxp-searchperf-stat-grid/.cxp-map-stat-grid below) rather
   than changing the shared 4-column base directly, because
   rankings-keywords.blade.php reuses .cxp-rankings-stat-grid.cxp-shared-stat-grid
   for its own 6-tile grid and must keep its own column count. Breakpoints
   mirror the generic .cxp-rankings-stat-grid ones this replaces. */
body.fi-panel-portal .cxp-rankings-overview-stat-grid.cxp-shared-stat-grid {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-rankings-overview-stat-grid.cxp-shared-stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (max-width: 700px) {
  body.fi-panel-portal .cxp-rankings-overview-stat-grid.cxp-shared-stat-grid {
    grid-template-columns: 1fr;
  }
}
body.fi-panel-portal .cxp-ranking-filter-card {
  /* CYT-855: was a flat 128px, sized for the old always-on 4-line "0 since
     the previous comparable check" caption. That caption is now hidden
     whenever delta is 0 (see rankings-keywords.blade.php), so the floor
     only needs to fit label + value. */
  min-height: 96px;
  padding: .75rem;
}
body.fi-panel-portal .cxp-ranking-filter-card .cxp-stat-card__title,
body.fi-panel-portal .cxp-ranking-filter-card .cxp-stat-card__verdict,
body.fi-panel-portal .cxp-ranking-filter-card .cxp-stat-card__meta {
  font-size: 12px;
  line-height: 18px;
}
body.fi-panel-portal .cxp-ranking-filter-card .cxp-stat-card__ring {
  width: 64px;
  height: 64px;
}
body.fi-panel-portal .cxp-ranking-filter-card .cxp-stat-card__ring-value {
  font-size: 22px;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor {
  border-bottom: 1px solid var(--portal-border);
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor:last-child {
  border-bottom: 0;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor > summary {
  cursor: pointer;
  list-style: none;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor > summary::-webkit-details-marker {
  display: none;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor > summary::before {
  content: '+';
  display: inline-grid;
  place-items: center;
  width: 1.25rem;
  height: 1.25rem;
  margin-right: .45rem;
  border: 1px solid var(--portal-border);
  border-radius: 999px;
  color: var(--portal-primary);
  font-size: .9rem;
  line-height: 1;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor[open] > summary::before {
  content: '−';
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor-detail {
  padding: 0 1rem 1rem 2.7rem;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor-detail > p {
  margin: 0 0 .65rem;
  color: var(--portal-muted);
  font-size: .8rem;
  line-height: 1.45;
}
body.fi-panel-portal .cxp-ai-summary-score {
  min-height: 128px;
  padding: .5rem;
  border: 0;
  background: transparent;
}
body.fi-panel-portal .cxp-ai-summary-score .cxp-stat-card__title-row {
  display: none;
}
body.fi-panel-portal .cxp-ai-summary-score .cxp-stat-card__ring {
  width: 116px;
  height: 116px;
}
body.fi-panel-portal .cxp-ai-summary-score .cxp-stat-card__ring-value {
  font-size: 34px;
}
body.fi-panel-portal .cxp-ai-engine {
  align-items: stretch;
  gap: .55rem;
  padding: .85rem;
  text-align: left;
}
body.fi-panel-portal .cxp-ai-engine-identity {
  justify-content: flex-start;
}
body.fi-panel-portal .cxp-ai-engine-gauge {
  align-self: center;
}
body.fi-panel-portal .cxp-ai-engine-result {
  gap: .25rem;
}
body.fi-panel-portal .cxp-ai-engine-counts > div {
  text-align: left;
}
body.fi-panel-portal .cxp-ai-engine-counts {
  gap: .75rem;
}
@media (max-width: 900px) {
  body.fi-panel-portal .cxp-searchperf-stat-grid.cxp-shared-stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-map-stat-grid.cxp-shared-stat-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (max-width: 620px) {
  body.fi-panel-portal .cxp-map-stat-grid.cxp-shared-stat-grid,
  body.fi-panel-portal .cxp-searchperf-stat-grid.cxp-shared-stat-grid,
  body.fi-panel-portal .cxp-rankings-stat-grid.cxp-shared-stat-grid {
    grid-template-columns: 1fr;
  }
  body.fi-panel-portal .cxp-ranking-filter-card {
    min-height: 88px;
  }
}
/* CYT-625 correction: compact ring cards need a real 96px value row.
   The ring used to overflow the compact 56px row and sit on top of the meta
   caption. Keep the component contract shared across Dashboard, Map rankings,
   Search performance, and any future ring-style count card. */
body.fi-panel-portal .cxp-stat-card--compact.cxp-stat-card--ring {
  min-height: 192px;
  grid-template-rows: 24px 96px 20px auto;
  align-content: start;
}
body.fi-panel-portal .cxp-stat-card--compact.cxp-stat-card--ring.cxp-stat-card--no-verdict {
  grid-template-rows: 24px 96px auto;
}


/* === GAUGE-FIT-2026-08-17 =========================================
   The gauge number must FIT inside its ring. A global rule
   (.cxp-score-number { font-size: 1.6rem }) was sizing the number
   independently of the ring, so values overflowed the stroke - measured
   "#36.1" at 73.1px inside a 49.8px opening. Size the number to the ring,
   and give the ring enough room to be legible.
   ============================================================ */
body.fi-panel-portal .cxp-stat-card__ring .cxp-score-number {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -.02em;
}
body.fi-panel-portal .cxp-stat-card__ring .cxp-score-unit {
  font-size: 12px;
  font-weight: 500;
  line-height: 1.1;
}
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring {
  width: 88px;
  height: 88px;
}
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring .cxp-score-number {
  font-size: 20px;
}
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring .cxp-score-unit {
  font-size: 11px;
}
/* Slightly finer stroke reads as a designed gauge rather than a thick donut. */
body.fi-panel-portal .cxp-stat-card__ring circle { stroke-width: 6.5; }

/* === GAUGE-FIT-2026-08-18 (CYT-647) ====================================
   The rule above still sized every ring number for its TYPICAL content, not
   its WORST CASE. Injected the real worst-case strings client-side and
   measured Range ink width against the true usable opening (2r-strokeWidth,
   scaled by renderedPx/viewBoxPx - viewBox is "0 0 72 72", r=32):
     96px ring, usable 76.67px, font 22px: "100.0%"=82.5px (0.5px to spare),
       "-100.0%"=96.3px (OVERFLOWS by 19.6px)
     88px ring, usable 70.28px, font 20px: "100.0%"=75.0px (already AT the
       edge, 0px to spare), "-100.0%"=87.5px (OVERFLOWS by 17.2px)
   No markup carries a value's character count, so a per-length rule isn't
   possible without a Blade change (out of scope for this ticket - flagged
   in the ticket report). Sizing every ring for its own worst case is the
   CSS-only alternative that guarantees "always fits" unconditionally.

   UPDATE 2026-08-18 (Adir, post-verification): the first pass above sized
   for the SMALLEST font that fits (10-15% clearance) - correct for
   "never overflow" but left real values needlessly small once verified
   safe. A figure that IS the point of its block gets the large-number
   treatment, so re-solved for the LARGEST font-size per ring that still
   clears the full worst-case set (zero-margin max = usable/4.377, then
   backed off just enough to keep a real, re-verified clearance rather
   than shipping at the exact edge):
     96px ring -> 16px (worst-case ink 70.0px vs 76.67px usable, 6.7px/9%)
     88px ring -> 15px (worst-case ink 65.6px vs 70.28px usable, 4.7px/7%
       - this ring has the least headroom of the three: its own zero-margin
       max is only 16.06px, so 15px is close to the ceiling on purpose)
     116px ring -> 20px (worst-case ink 87.5px vs 92.70px usable, 5.2px/6%)
   Re-ran the same worst-case-string injection test used to build the first
   pass at these new sizes before deploying: zero overflow, all three. */
body.fi-panel-portal .cxp-stat-card__ring .cxp-score-number {
  font-size: 16px;
}
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring .cxp-score-number {
  font-size: 15px;
}
/* CYT-647 defect #7: this was the losing (11px) declaration - the winning
   one is the next rule below, at 10335 in file order before this block. */
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring .cxp-score-unit {
  font-size: 12px;
}
/* The 116px "AI visibility" overall-score ring has more room than the
   generic 96px case above (usable 92.70px) - restore a larger, still
   worst-case-safe size. */
body.fi-panel-portal .cxp-ai-summary-score .cxp-stat-card__ring .cxp-score-number {
  font-size: 20px;
}

/* === GAUGE-FIT-2026-08-18-b (CYT-652) ===================================
   CYT-652 asked for the LARGEST size clearing an 8px minimum against the
   worst-case set, reporting the CYT-647 sizes above as over-corrected
   (14-15px, 32-70px of slack). Re-measured live via authenticated
   Playwright (real DOM ink of the injected worst-case strings, not a
   stretched-box or estimated read) and found the opposite: the CYT-647
   sizes above are NOT over-corrected - clearance is only 4.6-6.6px against
   "-100.0%", i.e. already UNDER this ticket's own 8px floor. The
   "32-70px" reading in the ticket was against "#16.8" alone, which is a
   much narrower string than the true worst case in the same candidate
   set; testing the full set (100.0%, -100.0%, #16.8, 0.26%, plus -999/999
   for count-type rings) gives the numbers below. Fine-grained (0.1px step)
   binary search found the true zero-margin ceiling per ring is ~15.6px /
   14.2px / 19.3px (clearance right at 8.0-8.4px) - shipped 0.1-0.3px under
   that ceiling instead of the exact edge, matching the CYT-647 precedent of
   "back off just enough to keep a real, re-verified clearance rather than
   shipping at the exact edge" (cross-browser font hinting/antialiasing can
   shave a fraction of a px off ink width in a way a single Chromium
   measurement can't fully rule out). Net effect vs. the sizes above: 96px
   and 116px rings end up SMALLER than their old 22px/20px starting points
   but LARGER than the intermediate 16px/20px CYT-647 landed on; the 88px
   ring, which has the least headroom of the three, goes down a full 1px
   for real margin instead of the wafer-thin 0.1px the ceiling would allow:
     96px ring:  16px -> 15.5px (worst-case ink 67.9px vs 76.67px usable,
       8.8px/11% clearance; was 6.6px/9%)
     88px ring:  15px -> 14px (worst-case ink 61.3px vs 70.28px usable,
       9.0px/13% clearance; was 4.6px/7%)
     116px ring: 20px -> 19.2px (worst-case ink 84.0px vs 92.70px usable,
       8.6px/9% clearance; was 5.1px/6%)
   Also found a live near-overflow this ticket didn't name: on
   .cxp-gbp-analysis-page's hero score card, the ring drops to 88px only
   below 620px (see the max-width:620px block above) but had no matching
   .cxp-score-number override, so it inherited the generic 16px rule sized
   for a 96px ring - measured clearance 0.2px at 390px width, one
   character away from the exact collision this whole line of tickets
   exists to prevent. Fixed below with the same 14px used for every other
   88px ring. */
body.fi-panel-portal .cxp-stat-card__ring .cxp-score-number {
  font-size: 15.5px;
}
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__ring .cxp-score-number {
  font-size: 14px;
}
body.fi-panel-portal .cxp-ai-summary-score .cxp-stat-card__ring .cxp-score-number {
  font-size: 19.2px;
}
@media (max-width: 620px) {
  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: 14px;
  }
}

/* === AI-GAUGE-FIT-2026-08-17 ===========================================
   The AI-assistant ring is 76px with a ~62.7px usable opening. Its
   percentage had no font-size of its own and rendered large enough to
   touch the stroke. Size it to the ring. Also demote the "not recorded"
   fallback: absence of data is not a headline, so it renders small and
   muted instead of at value size.
   ============================================================== */
body.fi-panel-portal .cxp-ai-engine-gauge > .cxp-ai-engine-pct {
  font-size: 17px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -.02em;
}
/* CYT-647: same worst-case-injection method as the stat-card ring above.
   True usable opening (viewBox "0 0 80 80", r=32, stroke-width 7, scaled to
   the rendered 76px box) is 54.15px. At 17px, worst-case "-100.0%" ink is
   69.3px - overflows by 15.2px (this gauge never actually shows a negative
   or 7-char value in practice, but the ticket asked every ring to be safe
   against the full worst-case set). 12px is the smallest allowed by the
   house 12px type floor, and it already clears the FULL worst case (ink
   48.9px, 5.25px/10% clearance) - no need to go smaller. */
body.fi-panel-portal .cxp-ai-engine-gauge > .cxp-ai-engine-pct {
  font-size: 12px;
}
/* CYT-652 re-verification: re-measured this ring against the same
   worst-case set as the stat-card rings above. Clearance is 5.3px at the
   12px floor - under this ticket's 8px target, but the floor is a hard
   rule ("never go below 12px") and this ring's own usable opening (54.2px)
   is too small to clear 8px at any size >=12px (13px alone drops clearance
   to 1.2px). Left at 12px on purpose; not a miss. */
body.fi-panel-portal .cxp-value-absent,
body.fi-panel-portal .cxp-ai-engine-result strong.cxp-value-absent {
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0;
  opacity: .75;
}

/* ============================================================================
   CYT-647 - 2026-08-18. Portal CSS geometry + colour pass.
   ============================================================================ */

/* --- Defect #3: stretched-barren tables, desktop widths (>=1100px) ---------
   CYT-607 (see the @media (max-width:1099px) blocks above) already fixes
   these same tables below 1100px. Above it, table-layout was plain "auto"
   with no column budget, so the browser spread free row width across every
   column's own preferred (max-content) size - and a long HEADER like
   "Average position (all tracked terms)" has a wide preferred size even
   though the DATA in that column is 2-3 characters. Measured before fixing:
   search-performance Clicks 148px column / 9px of ink (94% waste), Average
   position 442px / 17px ink; listing-management Value 197px / 25px ink.
   Fix: table-layout:fixed with an explicit budget - the content column
   flexes (auto, i.e. whatever's left), metric/status columns are narrow
   fixed widths and right-aligned so numbers cluster together at the row's
   right edge, matching the existing house convention (this table's own
   Value/Change headers already carry inline text-align:right - the browser
   just had no width budget to make that mean anything). */
@media (min-width: 1100px) {
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table {
    table-layout: fixed;
    width: 100%;
  }
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table th:nth-child(1),
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table td:nth-child(1) {
    width: auto;
  }
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table th:nth-child(2),
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table td:nth-child(2) {
    width: 110px;
    text-align: right;
  }
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table th:nth-child(3),
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table td:nth-child(3) {
    width: 140px;
    text-align: right;
  }
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table th:nth-child(4),
  body.fi-panel-portal .cxp-searchperf-page .kt-card-table > .sb-data-table td:nth-child(4) {
    width: 210px;
    text-align: right;
    white-space: normal;
  }

  /* Scoped with :has() to the Metric/Value/Change/Measured overview table
     specifically (its Value/Change <th> carry an inline text-align:right
     the citations table below it does not) - .kt-card-content > .sb-data-table
     also matches a 5-column citations table further down the same page,
     which must keep its own natural column widths. */
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) {
    table-layout: fixed;
    width: 100%;
  }
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) th:nth-child(1),
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) td:nth-child(1) {
    width: auto;
  }
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) th:nth-child(2),
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) td:nth-child(2) {
    width: 130px;
  }
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) th:nth-child(3),
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) td:nth-child(3) {
    width: 180px;
  }
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) th:nth-child(4),
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content > table.sb-data-table:has(th:nth-child(2)[style*="text-align:right"]) td:nth-child(4) {
    width: 220px;
    white-space: normal;
  }
}

/* --- Defect #5: website-health hero card, 1406px zone around a 96px ring ---
   .cxp-score-hero-card was a single-column stack (title-row / value-zone /
   verdict / meta), each row full card width. The ring is centred within its
   row by .cxp-stat-card__value-zone's place-items:center, but title/verdict/
   meta are left-aligned text - so on a lone full-width card the ring floated
   alone in the middle of a wide band while the copy hugged the left edge,
   reading as two disconnected halves with ~655px of dead space either side
   of the ring. Reuses the ring-left/text-stacked-right grid pattern CYT-610
   already ships for the GBP hero card's <=620px layout (see
   .cxp-gbp-analysis-page .cxp-gbp-stat-grid > .cxp-score-hero-card above),
   applied here unconditionally since this card is always alone at full
   width, not just at narrow widths. Scoped to .cxp-health-page so the GBP
   analysis page's own hero card (already handled by its own rules) is
   untouched. */
body.fi-panel-portal .cxp-health-page .cxp-score-hero-card {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  grid-template-rows: auto auto auto;
  column-gap: 1.25rem;
  align-items: center;
}
body.fi-panel-portal .cxp-health-page .cxp-score-hero-card .cxp-stat-card__title-row {
  grid-column: 2;
  grid-row: 1;
}
body.fi-panel-portal .cxp-health-page .cxp-score-hero-card .cxp-stat-card__value-zone {
  grid-column: 1;
  grid-row: 1 / span 3;
}
body.fi-panel-portal .cxp-health-page .cxp-score-hero-card .cxp-stat-card__verdict {
  grid-column: 2;
  grid-row: 2;
}
body.fi-panel-portal .cxp-health-page .cxp-score-hero-card .cxp-stat-card__meta {
  grid-column: 2;
  grid-row: 3;
}

/* --- Defect #6: map-rankings clipped out of its card at 390px --------------
   Two competing rules sized .cxp-map-frame .cxp-map-live at <=640px: one at
   calc(100% + 2.5rem), a later one at calc(100vw - 1rem) with margin-inline:
   calc((100% - (100vw - 1rem)) / 2) - a full-bleed trick that only lands on
   the true 8px viewport gutter if the card sits centred with equal left/
   right offset from the viewport edge. Measured at 390px it does not: the
   card is offset (x=54, width 320) while the 100vw-based rule pulled the map
   out to width 374px with -48px margins on both sides, extending it past the
   card's own right edge - which is what was clipping the +/- zoom control in
   half. Drop the viewport-relative full-bleed math at this breakpoint and
   let the map simply fill its actual parent (.cxp-map-frame has 0 padding,
   so this is still edge-to-edge with the card) - guarantees the map, and
   the Leaflet zoom control anchored to its corner, stay inside the card
   regardless of where that card sits in the viewport. This does not by
   itself fix Leaflet's internal tile/pin alignment if the map was initialised
   before this width took effect (a JS invalidateSize() concern, out of
   CSS-only scope) - noted in the ticket report as a follow-up. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-frame .cxp-map-live {
    width: 100%;
    max-width: 100%;
    margin-inline: 0;
  }
}

/* --- Defect #8: gbp-analysis "What makes up your score" rows backwards ----
   <summary class="cxp-health-factor-top"> inherits display:grid;
   grid-template-columns: minmax(0,1fr) auto from the base (non-GBP) rule,
   which is correct for a 2-item row (title div, then score <b>). But this
   page's own ::before pseudo-element (the +/- expander circle) is set to
   display:inline-grid, which makes it a THIRD grid item in that same
   2-column track list - so the browser auto-placed it into column 1,
   pushed the title div into column 2 (the narrow "auto" track, which is
   why "Reviews" measured flush against the right edge), and wrapped the
   score <b> onto its own line. website-health's rows never gained a third
   grid item (no expander), which is why only this page broke. Fix: give
   this page's rows a real 3-column track (marker | title | score) and
   place all three explicitly instead of leaving the third item to
   auto-place into a layout built for two. */
body.fi-panel-portal .cxp-gbp-analysis-page summary.cxp-health-factor-top {
  grid-template-columns: auto minmax(0, 1fr) auto;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor > summary::before {
  grid-column: 1;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor-top > div {
  grid-column: 2;
}
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-health-factor-top > b {
  grid-column: 3;
  align-self: center;
}

/* CYT-654: customer-facing operating pipeline and live operations tabs. */
body.fi-panel-portal .cxp-live-operations {
  min-width: 0;
}
body.fi-panel-portal .cxp-live-operations .cxp-live-pipeline,
body.fi-panel-portal .cxp-live-operations .cxp-live-tab-panel {
  width: 100%;
  min-width: 0;
}
body.fi-panel-portal .cxp-live-site-label,
body.fi-panel-portal .cxp-health-pill,
body.fi-panel-portal .cxp-agent-state,
body.fi-panel-portal .cxp-live-feed-meta,
body.fi-panel-portal .cxp-pipeline-step-top,
body.fi-panel-portal .cxp-finding-card-top {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
}
body.fi-panel-portal .cxp-live-site-label,
body.fi-panel-portal .cxp-health-pill {
  color: var(--muted-foreground);
  font-size: .78rem;
  font-weight: 700;
  white-space: nowrap;
}

/* CYT-814 component drift: `.cxp-health-pill` is named a pill and reads as a
   status badge ("Health: Looking good"), but was drawn as bare inline text -
   measured on Live operations at 19px tall with NO padding, NO radius and NO
   background, while the portal's canonical badge on every other page is
   20px / 999px radius / 5px 10px / 12px / 700 on the accent wash. That is a
   second way of building one component, which is the drift this ticket is
   about. The values below are the ones the skin's badge tier already
   resolves to (`--cxs-wash` / `--cxs-aline` / `--cxs-aink`), so this pill now
   matches `.kt-badge` instead of being another variant of it. Fallbacks keep
   it sane if the generated skin is ever not loaded. */
body.fi-panel-portal .cxp-health-pill {
  box-sizing: border-box;
  height: 20px;
  min-height: 20px;
  padding: 5px 10px;
  border: 1px solid var(--cxs-aline, var(--border));
  border-radius: 999px;
  background: var(--cxs-wash, var(--portal-primary-soft));
  color: var(--cxs-aink, var(--portal-primary));
  font-size: 12px;
  line-height: 1.2;
}
body.fi-panel-portal .cxp-pipeline-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: .65rem;
  padding: 1.25rem;
}
body.fi-panel-portal .cxp-pipeline-step {
  min-width: 0;
  padding: .85rem;
  border: 1px solid var(--border);
  border-radius: .7rem;
  background: color-mix(in srgb, var(--card) 88%, var(--primary) 12%);
}
body.fi-panel-portal .cxp-pipeline-step-top {
  width: 100%;
  min-width: 0;
  flex-wrap: wrap;
}
body.fi-panel-portal .cxp-pipeline-step-top strong {
  min-width: 0;
  flex: 1 1 auto;
  font-size: .85rem;
}
body.fi-panel-portal .cxp-pipeline-index {
  display: inline-grid;
  width: 1.35rem;
  height: 1.35rem;
  place-items: center;
  border-radius: 999px;
  background: var(--muted);
  color: var(--foreground);
  font-size: .75rem;
  font-weight: 800;
}
body.fi-panel-portal .cxp-pipeline-state {
  color: var(--foreground);
  font-size: .75rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .05em;
}
body.fi-panel-portal .cxp-pipeline-step p,
body.fi-panel-portal .cxp-pipeline-step small {
  display: block;
  margin: .7rem 0 0;
  color: var(--foreground);
  font-size: .75rem;
  line-height: 1.45;
}
body.fi-panel-portal .cxp-pipeline-step small {
  color: var(--foreground);
  font-weight: 650;
}
body.fi-panel-portal .cxp-live-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin: 1rem 0;
  padding: .3rem;
  border: 1px solid var(--border);
  border-radius: .75rem;
  background: var(--card);
}
body.fi-panel-portal .cxp-live-tab {
  min-height: 2.75rem;
  padding: .55rem .85rem;
  border: 0;
  border-radius: .55rem;
  background: transparent;
  color: var(--muted-foreground);
  cursor: pointer;
  font: inherit;
  font-size: .8rem;
  font-weight: 750;
}
body.fi-panel-portal .cxp-live-tab:hover,
body.fi-panel-portal .cxp-live-tab.is-active {
  background: var(--primary);
  color: #fff;
}
body.fi-panel-portal .cxp-live-tab-panel {
  display: grid;
  gap: 1rem;
}
body.fi-panel-portal .cxp-live-section-heading {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1rem;
}
body.fi-panel-portal .cxp-live-section-heading h2 {
  margin: 0;
  font-size: 1.1rem;
}
body.fi-panel-portal .cxp-live-section-heading p {
  margin: .25rem 0 0;
  color: var(--muted-foreground);
  font-size: .82rem;
}
body.fi-panel-portal .cxp-count-badge {
  flex: 0 0 auto;
  padding: .35rem .6rem;
  border-radius: 999px;
  background: var(--muted);
  color: var(--muted-foreground);
  font-size: .72rem;
  font-weight: 800;
}
body.fi-panel-portal .cxp-live-brain-placeholder {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: .65rem;
  align-items: center;
  padding: 1.25rem;
  border: 1px dashed var(--border);
  border-radius: .7rem;
  background: color-mix(in srgb, var(--card) 90%, var(--muted) 10%);
}
body.fi-panel-portal .cxp-live-brain-placeholder strong,
body.fi-panel-portal .cxp-live-brain-placeholder p {
  grid-column: 2;
  margin: 0;
}
body.fi-panel-portal .cxp-live-brain-placeholder p {
  margin-top: .25rem;
  color: var(--muted-foreground);
  font-size: .82rem;
}
body.fi-panel-portal .cxp-live-brain-placeholder > .cxp-status-led {
  grid-column: 1;
  grid-row: 1 / span 2;
}
body.fi-panel-portal .cxp-live-feed-grid,
body.fi-panel-portal .cxp-agent-grid,
body.fi-panel-portal .cxp-finding-grid,
body.fi-panel-portal .cxp-trends-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-live-feed-item,
body.fi-panel-portal .cxp-live-list-row,
body.fi-panel-portal .cxp-agent-card,
body.fi-panel-portal .cxp-finding-card,
body.fi-panel-portal .cxp-trend-card {
  min-width: 0;
}
body.fi-panel-portal .cxp-live-feed-item {
  display: grid;
  gap: .35rem;
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: .7rem;
  background: var(--card);
}
body.fi-panel-portal .cxp-live-feed-meta,
body.fi-panel-portal .cxp-finding-card-top {
  color: var(--muted-foreground);
  font-size: .72rem;
  font-weight: 750;
}
body.fi-panel-portal .cxp-live-feed-meta time,
body.fi-panel-portal .cxp-finding-card-top time,
body.fi-panel-portal .cxp-live-list-row time {
  margin-left: auto;
  font-weight: 500;
}
body.fi-panel-portal .cxp-live-feed-item strong,
body.fi-panel-portal .cxp-live-list-row strong,
body.fi-panel-portal .cxp-finding-card h3,
body.fi-panel-portal .cxp-agent-card h3 {
  overflow-wrap: anywhere;
  margin: 0;
  color: var(--foreground);
  font-size: .9rem;
}
body.fi-panel-portal .cxp-live-feed-item small,
body.fi-panel-portal .cxp-agent-card p,
body.fi-panel-portal .cxp-finding-card p {
  margin: 0;
  color: var(--muted-foreground);
  font-size: .78rem;
  line-height: 1.5;
}
body.fi-panel-portal .cxp-agent-card {
  padding: 1rem;
}
body.fi-panel-portal .cxp-agent-card-top {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: .65rem;
  align-items: start;
}
body.fi-panel-portal .cxp-agent-dot {
  width: .7rem;
  height: .7rem;
  margin-top: .35rem;
  border-radius: 999px;
}
body.fi-panel-portal .cxp-agent-card-top h3 + span {
  color: var(--muted-foreground);
  font-size: .7rem;
}
body.fi-panel-portal .cxp-agent-state {
  color: var(--muted-foreground);
  font-size: .68rem;
  font-weight: 750;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-agent-card p {
  margin-top: .9rem;
}
body.fi-panel-portal .cxp-agent-card-foot {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .15rem .75rem;
  margin-top: 1rem;
  padding-top: .75rem;
  border-top: 1px solid var(--border);
  color: var(--muted-foreground);
  font-size: .7rem;
}
body.fi-panel-portal .cxp-agent-card-foot strong {
  color: var(--foreground);
  text-align: right;
}
body.fi-panel-portal .cxp-agent-card-foot span:last-child {
  grid-column: 1 / -1;
}
body.fi-panel-portal .cxp-live-list {
  display: grid;
  gap: .55rem;
}
body.fi-panel-portal .cxp-live-list-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: .7rem;
  align-items: center;
  padding: .9rem 1rem;
  border: 1px solid var(--border);
  border-radius: .65rem;
  background: var(--card);
}
body.fi-panel-portal .cxp-live-list-row > div {
  display: grid;
  gap: .2rem;
  min-width: 0;
}
body.fi-panel-portal .cxp-live-list-row small {
  color: var(--muted-foreground);
  font-size: .75rem;
}
body.fi-panel-portal .cxp-live-list-row time {
  color: var(--muted-foreground);
  font-size: .75rem;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-finding-card {
  padding: 1rem;
}
body.fi-panel-portal .cxp-finding-card h3 {
  margin-top: .75rem;
}
body.fi-panel-portal .cxp-finding-card p {
  margin-top: .35rem;
}
body.fi-panel-portal .cxp-trends-grid .cxp-trend-card {
  overflow: hidden;
}
body.fi-panel-portal .cxp-trend-card .cxp-chart {
  margin: 0 1rem 1rem;
}
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-pipeline-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
@media (max-width: 700px) {
  body.fi-panel-portal .cxp-pipeline-grid,
  body.fi-panel-portal .cxp-live-feed-grid,
  body.fi-panel-portal .cxp-agent-grid,
  body.fi-panel-portal .cxp-finding-grid,
  body.fi-panel-portal .cxp-trends-grid {
    grid-template-columns: minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-live-section-heading {
    align-items: start;
    flex-direction: column;
  }
  body.fi-panel-portal .cxp-live-tabs {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-live-tab {
    width: 100%;
    padding-inline: .45rem;
    font-size: .75rem;
  }
  body.fi-panel-portal .cxp-live-list-row {
    grid-template-columns: auto minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-live-list-row time {
    grid-column: 2;
    margin-left: 0;
  }
  body.fi-panel-portal .cxp-agent-card-top {
    grid-template-columns: auto minmax(0, 1fr);
  }
  body.fi-panel-portal .cxp-agent-state {
    grid-column: 2;
    justify-self: start;
  }
}



/* Website Health's 4 tabs (Issue breakdown/Open issues/Work we did/What
   we're working on next) wrap into 2 uneven rows at narrow widths - each
   button sizes to its own label (106-204px), which check.js's
   tabs-uniform-width-when-wrapped rule correctly flags as ragged once
   wrapped. Force a 2-column grid so every tab shares one width regardless
   of label length; long labels wrap their own text instead of stealing
   column width. Appended at end-of-file for cascade priority, matching the
   pattern already proven necessary throughout this file. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-health-page .cxp-map-tabs {
    display: grid !important;
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: .4rem !important;
  }
  body.fi-panel-portal .cxp-health-page .cxp-map-tabs > button {
    width: 100% !important;
    white-space: normal !important;
    text-align: center !important;
  }
}


/* cxp-rankings-keyword-table gives each keyword its OWN <tbody> (a data row
   + a collapsible mobile-detail row), so every visible data row is always
   "child 1" of its own tbody - the standard ".kt-table > tbody >
   tr:nth-child(even)" zebra rule never fires for it (that only alternates
   WITHIN one tbody's children, and the detail row it would land on is
   normally hidden). Every visible row renders plain white regardless of
   keyword position - check.js zebra-visible correctly caught this as a real
   1.0:1 ratio, not the faint-but-present shade the rule elsewhere measures.
   Alternate by tbody GROUP position instead. */
body.fi-panel-portal .cxp-rankings-keyword-table > tbody.cxp-rankings-keyword-group:nth-of-type(even) > tr {
  background: #f1f5f9 !important;
}
body.fi-panel-portal .cxp-rankings-keyword-table > tbody.cxp-rankings-keyword-group:nth-of-type(even) > tr > td {
  background: inherit !important;
}


/* .cxp-score-number.cxp-score-good text renders in --cxp-tone-good
   (#00A651), which measures 3.19:1 on white - under the 4.5 floor for any
   instance not large/bold enough for the WCAG large-text exemption (e.g.
   Listing Management's "Live 617" KPI number). --cxp-tone-good itself is
   left untouched (it also drives ring strokes/LEDs/fills, which are
   decorative measurement colour, not text, and don't need this floor) -
   only the TEXT-NUMBER selector gets the darker, already-proven-readable
   --cxp-good-strong (#007A3D) instead. */
body.fi-panel-portal .cxp-score-number.cxp-score-good,
body.fi-panel-portal .cxp-score-number.cxp-score-good * {
  color: var(--cxp-good-strong) !important;
}


/* Live Operations - status tone modifiers (2026-08-18): pipeline/agent/
   finding/live-feed cards render a state (done/active/queued/pending,
   healthy/running/stale/pending, good/middle/poor/neutral) but had zero CSS
   for any of it - every card looked identical regardless of state. */
body.fi-panel-portal .cxp-pipeline-step--done { border-left: 3px solid var(--cxp-tone-good); }
body.fi-panel-portal .cxp-pipeline-step--active { border-left: 3px solid var(--cxp-tone-middle); }
body.fi-panel-portal .cxp-pipeline-step--queued { border-left: 3px solid var(--cxp-tone-neutral); }
body.fi-panel-portal .cxp-pipeline-step--pending { border-left: 3px solid var(--cxp-tone-neutral); opacity: .7; }

body.fi-panel-portal .cxp-agent-state--healthy,
body.fi-panel-portal .cxp-agent-state--running { color: var(--cxp-tone-good); }
body.fi-panel-portal .cxp-agent-state--stale { color: var(--cxp-tone-poor); }
body.fi-panel-portal .cxp-agent-state--pending { color: var(--cxp-tone-neutral); }

body.fi-panel-portal .cxp-finding-card--good { border-left: 3px solid var(--cxp-tone-good); }
body.fi-panel-portal .cxp-finding-card--middle { border-left: 3px solid var(--cxp-tone-middle); }
body.fi-panel-portal .cxp-finding-card--poor { border-left: 3px solid var(--cxp-tone-poor); }
body.fi-panel-portal .cxp-finding-card--neutral { border-left: 3px solid var(--cxp-tone-neutral); }

body.fi-panel-portal .cxp-live-feed-item--good { border-left: 3px solid var(--cxp-tone-good); }
body.fi-panel-portal .cxp-live-feed-item--middle { border-left: 3px solid var(--cxp-tone-middle); }
body.fi-panel-portal .cxp-live-feed-item--poor { border-left: 3px solid var(--cxp-tone-poor); }
body.fi-panel-portal .cxp-live-feed-item--neutral { border-left: 3px solid var(--cxp-tone-neutral); }


/* AI Visibility - .cxp-score-poor text (2026-08-19): the live-winning rule
   for .cxp-score-number.cxp-score-poor resolved to var(--cxp-tone-poor),
   which computes light enough (rgb(230,67,67)) to fail WCAG AA on the app's
   near-white card backgrounds (3.73:1, needs 4.5:1) - confirmed by axe-core-
   style contrast measurement, not a false positive (unlike the same page's
   "47 on pink" finding, which zoomed-screenshot inspection showed sits
   cleanly on white - that one is a ring-adjacent sampling artifact, left
   alone). #A01D10 is the same accessible dark red already used for
   .kt-badge-destructive elsewhere in this file. */
body.fi-panel-portal .cxp-score-number.cxp-score-poor,
body.fi-panel-portal .cxp-score-number.cxp-score-poor * {
  color: #A01D10 !important;
}


/* House scroll-cue for detail tables that legitimately scroll sideways on a
   phone (2026-08-19). `.cxp-issue-detail-col` (Website Health's "What needs
   attention" table) and the AI-visibility assistant matrix both deliberately
   give a text column a real min-width and let `.kt-card-content:has(>table)`
   scroll horizontally rather than squeeze content unreadably - a precedent
   already documented above `.cxp-issue-detail-col`. That is an accepted
   pattern here, not the "table doesn't fit at all" failure the house rule
   against horizontal scrollers targets - but a scrolling surface with NO
   affordance still leaves a phone user unaware more columns exist. A plain,
   always-visible text hint (not a phantom scrollbar, not a fade gradient
   that needs JS to hide/show correctly) satisfies that without inventing a
   flashier control. Scoped to mobile only - desktop has room and does not
   overflow these tables. */
@media (max-width: 640px) {
  body.fi-panel-portal .kt-card-content:has(> table) {
    position: relative;
    padding-bottom: 1.6rem !important;
  }
  body.fi-panel-portal .kt-card-content:has(> table)::after {
    content: "Swipe to see more \2192";
    position: sticky;
    left: 0;
    display: block;
    padding: .3rem .75rem;
    color: var(--muted-foreground);
    font-size: .7rem;
    font-weight: 600;
    text-align: right;
  }
}


/* Delta stat-card value box (2026-08-19, Adir screenshot: "AI search visibility ->
   Changes" tab, ChatGPT/Claude/Gemini/Perplexity "X.X pts" cards) - the box's
   height (cxs-skin.css .cxp-stat-card__value-zone, min-height:84px) only fits ONE
   line of the 36px delta text comfortably. A wider delta like "+20.0 pts" wraps to
   two lines and the box does not grow to match, so the real padding effectively
   disappears - text reads as touching the edges even though single-line values
   ("0.0 pts") in the same row look fine. Give delta boxes specifically enough
   height for two lines plus real padding, and stop the number and its "pts" unit
   from wrapping onto separate lines (keeps a wrapped value looking intentional
   rather than like an overflow accident). */
body.fi-panel-portal .cxp-stat-card--delta .cxp-stat-card__value-zone {
  min-height: 104px !important;
}


/* Search performance's 3 tabs (Search terms/Visitor activity/Clicks trend) wrap
   unevenly on some clients (peopleclaims: 2 tabs fit row 1, "Clicks trend"
   wraps alone at its own ~103px content width vs the 322px row - flagged by
   check.js tabs-fill-row-when-stacked). Same fix as Website Health's 4-tab
   strip - force a 2-column grid so every tab shares a uniform width. */
@media (max-width: 640px) {
  /* 3 tabs, ALL uniform width, ALL on one row - a 2-column grid forces a
     wrap (which then fails either tabs-uniform-width-when-wrapped or
     tabs-fill-row-when-stacked depending on how the odd one out is
     handled - the check's own comment says uniform width wins). A 3-column
     single row sidesteps wrapping altogether. */
  body.fi-panel-portal .cxp-searchperf-page .cxp-map-tabs {
    display: grid !important;
    grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
    gap: .4rem !important;
  }
  body.fi-panel-portal .cxp-searchperf-page .cxp-map-tabs > button {
    width: 100% !important;
    white-space: normal !important;
    text-align: center !important;
    padding-inline: .35rem !important;
  }
}


/* Map Rankings grid pins (2026-08-19) - the map marker JS already sets an
   accessible dark red inline (background:#B42318, matching the same red used
   for .cxp-score-poor text elsewhere this session), but cxs-skin.css's
   `.cxp-map-pin--poor, .cxp-map-pin--bad { background: var(--cxs-bad)
   !important; }` overrides it with a lighter red that fails white-text
   contrast (rgb(230,67,67) = 4.00:1, needs 4.5:1) - same
   already-had-a-fix-cxs-skin-broke-it shape as the GBP badges and AI
   visibility score text this session. Restore the accessible red with
   matching specificity (doubled body class beats cxs-skin's selector on a
   same-specificity tie by file order). */
body.fi-panel-portal.fi-panel-portal .cxp-map-pin.cxp-map-pin--poor,
body.fi-panel-portal.fi-panel-portal .cxp-map-pin.cxp-map-pin--bad {
  background: #B42318 !important;
}


/* Map Rankings' 5 tabs (Map/What changed/History/Your Google profile/
   Competitors) wrap raggedly across 3 rows on mobile (156-320px widths) -
   same shape as the Search Performance 3-tab fix earlier this round. A
   2-column grid keeps every tab uniform width; the odd 5th tab spans the
   full row alone (that pairing - grid-columns-align-across-rows PLUS an
   uneven last-item check - is fine for an odd count, unlike a straight
   3-tab strip where 3-cols-on-one-row was the cleaner fix). */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-page-root .cxp-map-tabs {
    display: grid !important;
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: .4rem !important;
  }
  body.fi-panel-portal .cxp-map-page-root .cxp-map-tabs > button {
    width: 100% !important;
    white-space: normal !important;
    text-align: center !important;
  }
  body.fi-panel-portal .cxp-map-page-root .cxp-map-tabs > button:last-child:nth-child(odd) {
    grid-column: 1 / -1 !important;
    width: calc(50% - .2rem) !important;
    margin-inline: auto !important;
  }
}


/* Website Health speed-metric badges (2026-08-19, confirmed via GPT-5.6 vision
   review + zoomed real screenshot: the "Slower than Google wants (target: under
   2.5 seconds)" badge on the LCP tile ran off the card's right edge, text fully
   truncated with no ellipsis). The blade already sets
   white-space:normal;display:inline-block;max-width:100% inline, but cxs-skin.css's
   blanket `.kt-badge, [class*="kt-badge"] { white-space: nowrap !important;
   max-width: none !important; ... }` overrides it - same root cause as the GBP
   findings-table badges fixed earlier this round. Restore wrapping with matching
   specificity, scoped to this grid so it doesn't touch badges elsewhere that are
   correctly single-line. */
body.fi-panel-portal.fi-panel-portal .cxp-speed-head .kt-badge {
  display: inline-block !important;
  white-space: normal !important;
  max-width: 100% !important;
  width: 100% !important;
  text-align: center !important;
}


/* ==========================================================================
   .cxp-num - TABULAR FIGURES  (CYT bars pass, 2026-08-21)

   `.cxp-num` is applied to 80 numeric cells on /portal/ai-visibility/competitors
   and 60 on /citations, and there was no rule for it anywhere in the codebase -
   measured font-variant-numeric: normal on all 80. Proportional figures give
   "1" a narrower advance than "8", so a column of numbers never lines up and a
   count-up animation visibly jitters as the digits change.

   Alignment is deliberately NOT set here. Numbers stay LEFT in this portal -
   the columns cluster right because they are sized to their content, not
   because their text is right-aligned. See public/css/portal-table-standards.css.

   font-feature-settings repeats the same request at the lower level, because a
   font-feature-settings declaration inherited from elsewhere would otherwise
   override font-variant-numeric outright.
   ========================================================================== */
body.fi-panel-portal .cxp-num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}


/* ==========================================================================
   PORTAL ACCESSIBILITY PASS - 2026-08-21
   Appended, never edited in place: every rule below is either a new class or a
   narrower/later selector layered on top of an existing one.
   ========================================================================== */

/* ---- 1. SKIP LINK -------------------------------------------------------
   The root cause is fixed in cxs-skin.css (its generic link-ink rule no longer
   matches this chip). These two rules are the belt to that braces: they carry
   enough specificity to hold the colour even if some future generic link rule
   reaches (0,4,2) again, and they give the chip a visible focus ring - it is
   the first thing a keyboard user meets on all nine portal pages, and until
   today it arrived at 2.03:1. */
body.fi-panel-portal a.cxp-skip-link,
body.fi-panel-portal a.cxp-skip-link:link,
body.fi-panel-portal a.cxp-skip-link:visited,
body.fi-panel-portal a.cxp-skip-link:hover,
body.fi-panel-portal a.cxp-skip-link:focus,
body.fi-panel-portal a.cxp-skip-link:focus-visible {
  color: var(--portal-primary-foreground, #fff);
  background: var(--portal-heading, #172033);
}
body.fi-panel-portal a.cxp-skip-link:focus,
body.fi-panel-portal a.cxp-skip-link:focus-visible {
  transform: translateY(0);
  outline: 3px solid var(--portal-primary-fill, #fff);
  outline-offset: 3px;
  box-shadow: 0 0 0 5px rgba(23, 32, 51, .55);
}

/* ---- 2. THE ASSISTANT TONE SCALE ---------------------------------------
   .cxp-tone-good / -middle / -poor are written onto article.cxp-ai-engine by
   ai-visibility.blade.php, but no rule anywhere in the app selected them: they
   existed only as --cxp-tone-* variables. The modifier painted nothing. That
   is invisible today only because the pilot client happens to have all four
   assistants in the "middle" band - the first client with one good assistant
   and one poor one would have seen them render identically, which is a scale
   collapse, not a cosmetic miss.

   The raw tokens cannot be used as the scale directly. Measured live they are
   good rgb(62,111,224) L17.8, middle rgb(228,158,34) L41.1, poor
   rgb(230,67,67) L21.2 - good and poor sit 3.5 luminance points apart, so in
   greyscale or with colour-vision loss the best and worst steps are the same
   step. So each carries the token's hue and chroma but an EXPLICIT lightness,
   ordered best -> worst as lightest -> darkest, which also reads correctly:
   the worst result is the loudest mark. Measured composited values:
       good    rgb(79,130,245)   luminance 24.22   3.35:1 on the card
       middle  rgb(163,96,0)     luminance 16.15   4.63:1
       poor    rgb(162,0,3)      luminance  7.69   7.72:1
   Monotonic, with ~8 luminance points between neighbours, each clearing the
   3:1 non-text floor against the card.

   "Not recorded" states are NOT a step of the scale and must never be read as
   one, so they are neutral and DASHED - a difference in form, not in position
   on a lightness ramp. */
body.fi-panel-portal .cxp-ai-engine.cxp-tone-good,
body.fi-panel-portal .cxp-ai-engine.cxp-tone-middle,
body.fi-panel-portal .cxp-ai-engine.cxp-tone-poor,
body.fi-panel-portal .cxp-ai-engine.cxp-tone-unchecked,
body.fi-panel-portal .cxp-ai-engine.cxp-tone-unknown {
  border-inline-start-width: 5px;
  border-inline-start-style: solid;
}

/* Fallback for an engine without relative colour syntax: hue only. It loses
   the lightness ordering but keeps three distinguishable steps, which is
   strictly better than the nothing that shipped. */
body.fi-panel-portal .cxp-ai-engine.cxp-tone-good   { border-inline-start-color: var(--cxp-tone-good); }
body.fi-panel-portal .cxp-ai-engine.cxp-tone-middle { border-inline-start-color: var(--cxp-tone-middle); }
body.fi-panel-portal .cxp-ai-engine.cxp-tone-poor   { border-inline-start-color: var(--cxp-tone-poor); }

@supports (color: oklch(from white l c h)) {
  body.fi-panel-portal .cxp-ai-engine.cxp-tone-good {
    border-inline-start-color: oklch(from var(--cxp-tone-good) 0.63 c h);
  }
  body.fi-panel-portal .cxp-ai-engine.cxp-tone-middle {
    border-inline-start-color: oklch(from var(--cxp-tone-middle) 0.55 c h);
  }
  body.fi-panel-portal .cxp-ai-engine.cxp-tone-poor {
    border-inline-start-color: oklch(from var(--cxp-tone-poor) 0.43 c h);
  }
}

body.fi-panel-portal .cxp-ai-engine.cxp-tone-unchecked,
body.fi-panel-portal .cxp-ai-engine.cxp-tone-unknown {
  border-inline-start-style: dashed;
  border-inline-start-color: var(--cxp-tone-neutral);
}

/* ---- 5. TAB STRIPS FIT, THEY DO NOT SCROLL AND THEY DO NOT ORPHAN -------
   .cxp-map-tabs was a wrapping flex row of intrinsically-sized buttons. At
   1440 on /ai-visibility/opportunities that put 305 + 238 + 304 = 847px on row
   one of 1104px available and dropped the 4th tab (300px) onto a row of its
   own beside ~800px of nothing. Stacked at 390 the same four measured
   305 / 238 / 304 / 300 in a 314px row - none reaching the edge, one 67px
   short of its neighbours.

   A grid fixes both at once: equal columns on a single desktop row, one
   full-width column on a phone, uniform height for free from stretch
   alignment, and labels that may wrap instead of forcing a sideways drag.
   The scroll-shadow background is removed with it - a cover for a scroller
   that no longer exists is just a smudge on the ends of the strip. */
/* The class is repeated deliberately. cxs-skin.css declares
   `body.fi-panel-portal .cxp-map-tabs { display: flex }` at the same (0,2,1)
   specificity and loads after this file, so a plain rule here is silently
   beaten - and the failure is the worst kind: the stylesheet reads correct
   while the rendered strip is unchanged. Doubling to (0,3,1) wins on
   specificity rather than on load order, so a skin rebuild cannot take it
   back. .cxp-gbp-analysis-page keeps its own vertical rail: that rule carries
   !important and still outranks this. */
body.fi-panel-portal .cxp-map-tabs.cxp-map-tabs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  align-items: stretch;
  column-gap: .35rem;
  row-gap: .35rem;
  overflow-x: visible;
  background-image: none;
}

/* No width: 100% here. Grid items stretch on their own, and forcing a width
   turned the strip into a one-tab-per-row stack on every page where the skin's
   flex rule was still winning. */
body.fi-panel-portal .cxp-map-tabs.cxp-map-tabs > button,
body.fi-panel-portal .cxp-map-tabs.cxp-map-tabs > a,
body.fi-panel-portal .cxp-map-tabs.cxp-map-tabs > li {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 44px;
  white-space: normal;
  /* break-word, NOT anywhere. "anywhere" also shrinks the min-content size, so
     the grid happily made a column narrower than the longest word and split
     labels mid-word - measured as "Assistant/s" and "Score breakdow/n" at 1024
     and "Keyword/s" at 390. "break-word" wraps at spaces and only breaks a word
     that genuinely cannot fit its box. */
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;
  /* Even out the lines of a label that wraps, so a long tab does not strand a
     single word on its own row - "What rivals get credit for and you do /
     not (6)" at 1440. Progressive: a browser without text-wrap just wraps
     normally, which is the behaviour above. */
  text-wrap: balance;
  line-height: 1.2;
  text-align: center;
}

/* The AI section declares its own nowrap + auto-overflow at (0,3,1). Matched
   here so the grid is not quietly beaten on the very pages the defect was
   reported on. */
body.fi-panel-portal .cxp-ai-tabs .cxp-map-tabs.cxp-map-tabs,
body.fi-panel-portal .cxp-ai-tabs-actions .cxp-map-tabs.cxp-map-tabs {
  overflow-x: visible;
  background-image: none;
}
body.fi-panel-portal .cxp-ai-tabs .cxp-map-tabs.cxp-map-tabs > button,
body.fi-panel-portal .cxp-ai-tabs-actions .cxp-map-tabs.cxp-map-tabs > button {
  white-space: normal;
  line-height: 1.2;
  flex: 0 1 auto;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-map-tabs.cxp-map-tabs {
    grid-auto-flow: row;
    grid-template-columns: minmax(0, 1fr);
    /* Stacked rows size independently, so a tab whose label wraps to two lines
       left its neighbours 6.8px shorter - measured on /website-health and
       /ai-visibility/opportunities at 360-390. Equal rows keep every tab the
       same height, which is the same component-consistency rule that applies
       to badges and stat tiles. */
    grid-auto-rows: 1fr;
  }
  /* The dashboard/autopilot strip already declares a deliberate 4-across
     mobile grid for its seven tabs. Keep it, and extend it the 40px up to
     this breakpoint so the two rules cannot invert each other between
     601 and 640. */
  body.fi-panel-portal .cxp-dashboard-seo-details .cxp-map-tabs.cxp-map-tabs {
    grid-auto-flow: row;
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* ---- 7. TOUCH TARGETS ON A PHONE ---------------------------------------
   Mobile only, deliberately: a 44px floor is a thumb requirement, and applying
   it to a mouse-driven desktop layout would inflate controls that are already
   correct there. */
@media (max-width: 640px) {
  /* !important is load-bearing here, not laziness. Two rules already fight
     for this box and both would otherwise win: filament-bridge.css sets
     select.kt-input { height: auto !important }, and ai-responses.blade.php
     carries an INLINE block whose
     .cxp-ai-responses-page .aiv-field select { min-height: 2.25rem } ties this
     selector on specificity and then wins on source order, because an inline
     <style> is emitted after every external sheet. Without this the two
     assistant filters on /ai-visibility/responses stay at 36px and the fix
     looks applied while measuring unchanged. */
  body.fi-panel-portal .cxp-ai-page .cxp-aiv-input,
  body.fi-panel-portal .cxp-ai-page textarea.cxp-aiv-input,
  body.fi-panel-portal .cxp-ai-page .kt-input,
  body.fi-panel-portal .cxp-ai-page select.kt-input,
  body.fi-panel-portal .cxp-ai-page .kt-btn,
  body.fi-panel-portal .cxp-ai-page button.kt-btn,
  body.fi-panel-portal .cxp-ai-page a.kt-btn {
    min-height: 44px !important;
  }
  body.fi-panel-portal .cxp-ai-page select.kt-input,
  body.fi-panel-portal .cxp-ai-page .kt-btn {
    padding-block: .5rem;
  }
}

/* ===========================================================================
   Map ranking colour tiers (2026-08-21)

   Adir, on the map grid: "I want you to have more green buttons so the shades
   are going to be with green more of the blue and the orange, only the red
   stays the red. So you need to show more greens like one and two and three
   it's very good and then four five six is a little bit less green and so on."

   Two things were wrong before this.

   1. Positions 1-3 rendered BLUE, not green. The blade sets an inline green
      background, but `body.fi-panel-portal.fi-panel-portal .cxp-map-pin--good
      { background: var(--cxs-a) !important }` painted the brand accent over it.
      Measured on the live page: oklch(0.57 0.18 264) - a blue - on a pin whose
      own markup said #047A44. Beaten here on specificity (0,4,1 vs 0,3,1) plus
      !important, the same shape as the existing fixes further up this file.

   2. Three bands could not express what he asked for. Every rank from 4 to 10
      was one flat amber, so a 4 and a 10 looked equally mediocre when they are
      not remotely the same result.

   The ramp: dark green -> green -> olive -> lime -> orange -> red. Ordered by
   LIGHTNESS as well as hue, so it survives greyscale and colourblindness -
   measured gaps between adjacent steps are 0.09 or more, where the previous
   candidate pair sat at 0.010 and was indistinguishable without colour. Every
   text/background pair clears 4.5:1; the lightest three take dark text.

   Every pin also carries its own rank number, so colour summarises here and is
   never the only carrier of meaning.

   Keep in step with toneOf() in map-rankings.blade.php and tierFor() in
   MapRankings.php. If these drift, the map and its own key stop agreeing.
   =========================================================================== */

/* --- Pins on the live map. Doubled body class + the element class beats the
       skin's !important accent override. --- */
body.fi-panel-portal.fi-panel-portal .cxp-map-pin.cxp-map-tier--t1 { background: #04603C !important; color: #FFFFFF !important; box-shadow: 0 0 0 2px #023424, 0 1px 3px rgba(16,24,40,.28) !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-pin.cxp-map-tier--t2 { background: #12855C !important; color: #FFFFFF !important; box-shadow: 0 0 0 2px #094732, 0 1px 3px rgba(16,24,40,.28) !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-pin.cxp-map-tier--t3 { background: #5FA83C !important; color: #12210B !important; box-shadow: 0 0 0 2px #35611f, 0 1px 3px rgba(16,24,40,.28) !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-pin.cxp-map-tier--t4 { background: #C7D93A !important; color: #12210B !important; box-shadow: 0 0 0 2px #6f7a12, 0 1px 3px rgba(16,24,40,.28) !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-pin.cxp-map-tier--t5 { background: #E8912B !important; color: #12210B !important; box-shadow: 0 0 0 2px #8a4f0a, 0 1px 3px rgba(16,24,40,.28) !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-pin.cxp-map-tier--t6 { background: #B42318 !important; color: #FFFFFF !important; box-shadow: 0 0 0 2px #7a271a, 0 1px 3px rgba(16,24,40,.28) !important; }

/* --- The fallback grid table, shown when the live map cannot render. Tinted
       rather than solid, because these are table cells and a wall of solid
       colour is unreadable at that density. --- */
body.fi-panel-portal.fi-panel-portal .cxp-map-cell.cxp-map-tier--t1 { background: rgba(4, 96, 60, .18)   !important; color: #04603C !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-cell.cxp-map-tier--t2 { background: rgba(18, 133, 92, .17) !important; color: #0C6344 !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-cell.cxp-map-tier--t3 { background: rgba(95, 168, 60, .18) !important; color: #3F6F26 !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-cell.cxp-map-tier--t4 { background: rgba(150, 165, 20, .20)!important; color: #5C6610 !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-cell.cxp-map-tier--t5 { background: rgba(232, 145, 43, .18)!important; color: #8A4F0A !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-cell.cxp-map-tier--t6 { background: rgba(180, 35, 24, .14) !important; color: #B42318 !important; }

/* --- Legend keys. Solid, so the key swatch is the same colour as the pin it
       explains. A key in a lighter tint than the thing it describes is a key
       nobody can use. --- */
body.fi-panel-portal.fi-panel-portal .cxp-map-legend .cxp-map-key.cxp-map-tier--t1 { background: #04603C !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-legend .cxp-map-key.cxp-map-tier--t2 { background: #12855C !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-legend .cxp-map-key.cxp-map-tier--t3 { background: #5FA83C !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-legend .cxp-map-key.cxp-map-tier--t4 { background: #C7D93A !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-legend .cxp-map-key.cxp-map-tier--t5 { background: #E8912B !important; }
body.fi-panel-portal.fi-panel-portal .cxp-map-legend .cxp-map-key.cxp-map-tier--t6 { background: #B42318 !important; }


/* ============================================================================
   lm-activity-card-fill - Listing Management activity card, 2026-08-25
   ----------------------------------------------------------------------------
   The "Listing activity" card and "Where you're listed" card are siblings in
   the .cxp-two-col grid, so both stretch to the taller one. The activity card
   is display:grid with auto rows, which meant the surplus height was shared
   with the HEADER: measured live at 1920px the header ran 217px tall for 105px
   of content, floating the tab strip ~70px above the table it controls, plus
   another 24px from the skin's tab-strip margin (written for a strip that sits
   above a card, not inside its header).

   Header keeps its own height; the tab PANEL underneath absorbs the slack, so
   the table starts directly under the tabs and the card still fills the row.
   ========================================================================= */
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col > .kt-card.kt-card-table {
  grid-template-rows: auto 1fr;
}

body.fi-panel-portal .cxp-listing-management-page .kt-card-header .cxp-map-tabs {
  margin-bottom: 0;
}

/* The tab panel now owns the surplus height, but the table inside it keeps its
   own 32rem scroll cap, so stretching the card just moved the white space to
   the bottom. These two cards hold unrelated lists of different lengths and
   nothing reads across a row, so there is no reason to force them to the same
   height: each ends where its content ends. This restores the .cxp-two-col base
   behaviour (align-items:start, cxp.css line 13) that the portal-wide stretch
   rule below overrides for dashboard cards that genuinely do pair up. */
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col {
  /* lm-activity-panel-fill */
  align-items: start;
}

/* lm-activity-columns - the activity tables must FIT their column, 2026-08-25
   ----------------------------------------------------------------------------
   Restoring the two-column grid put these tables in a 7fr column, and at 1440px
   the "Recent listings" table wanted 782px of a 647px card - an inner sideways
   scrollbar, which is never the answer here.

   The cause is not the data: cxs-skin.css reserves 210px for a data table's
   first column because it normally carries the row SUBJECT. On these two tables
   the first column is a date ("Aug 22, 2025") or a hostname, so 210px was dead
   space taken from the one column that actually needs it. Give the room back to
   the directory / link column and the table fits at every width.

   Measured after: 1440 -> 647 of 647, 390 -> 336 of 336, no element on the page
   with scrollWidth > clientWidth. */
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-data-table th:first-child,
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-data-table tr td:first-child,
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-data-table tr td:nth-child(2) {
  min-width: 0 !important;
}

body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-data-table td {
  overflow-wrap: anywhere;
  white-space: normal;
}

/* Cells may break anywhere - a hostname has no spaces and has to give way. A
   COLUMN HEADING never may: at 390px "Status" was splitting as "Stat us" and
   the Site authority table read "Valu e" / "Meas ured". A heading breaks at
   spaces only, and the flexible column beside it gives up the width instead. */
body.fi-panel-portal .cxp-listing-management-page .sb-data-table thead th,
body.fi-panel-portal .cxp-listing-management-page .kt-table thead th {
  /* !important because a portal-wide rule sets break-word on every table cell
     and wins on load order; measured at 390px this is what produced "Valu / e"
     and "Meas / ured" in the Site authority header. */
  overflow-wrap: normal !important;
  word-break: normal !important;
  white-space: normal;
}

/* The date and the status pill are fixed-size facts - they stay on one line and
   take only what they need, so the flexible columns keep the rest. */
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col div[x-show*="recent"] .sb-data-table td:first-child,
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-data-table td:last-child,
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-data-table th:last-child {
  width: 1%;
  white-space: nowrap;
}

/* The target page is nearly always the site's own homepage, so on a narrow
   screen it is the column to drop rather than the one to scroll. Verified at
   900, 768 and 390: every activity table fits its card with it hidden. */
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-data-table th:nth-child(3),
  body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-data-table td:nth-child(3) {
    display: none;
  }
}

/* CYT-873: "Recent listings" gained a trailing Type column (business
   directory vs. backlink page). Appended last rather than inserted earlier so
   it doesn't shift the Target-page index the rule above depends on, and the
   shared "Listing activity" table structure Live links still uses stays
   untouched. Keep it visible down to 390 - it's short (one or two words) and
   is the whole point of the fix, unlike Target page which the rule above
   already established is safe to drop. */

/* lm-activity-identity - stop the directory column from setting the table width
   ----------------------------------------------------------------------------
   .sb-dense-identity is a NOWRAP FLEX ROW, so "business.nextdoor.com/en-us" plus
   "Garage Door Pros, LLC" became a 353px minimum the column could not go below,
   at 1440px and at 390px alike. This is the same trap portal-rankings-tables.css
   documents at length: a flex item's automatic minimum size is only reduced when
   the item's own overflow is non-visible, and that allowance does not exist for
   white-space:nowrap set directly on a <td>. So the truncation has to happen on
   a block INSIDE the flex container, exactly as .cxp-article-table does it.
   Stacked rather than side by side, because the keyword is a second line here,
   not a chip. */
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-dense-identity {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  min-width: 0;
  width: 100%;
  white-space: normal;
}

body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-dense-identity > strong,
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-dense-identity > span,
body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-dense-identity > a {
  display: block;
  width: 100%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* lm-two-col-breakpoint - two columns only when the left one can hold a table
   ----------------------------------------------------------------------------
   .cxp-two-col gives the left card 7/12 of the row, which is 404px at 1024 and
   507px at 1200 - not enough for a four or five column table, and the result was
   an inner horizontal scrollbar between roughly 1000px and 1500px. Adir's rule
   is that a table which needs sideways dragging is a broken layout, not a
   scrollable one, so the LAYOUT gives way rather than the table: below 1500px
   the activity card takes the full width and "Where you're listed" (a short
   two-column list that reads fine at any width) stacks underneath it. */
@media (max-width: 1700px) {
  body.fi-panel-portal .cxp-listing-management-page .cxp-two-col {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Narrow screens: the identity block stops truncating and WRAPS instead.
   A column flex container takes its width minimum from its items' cross-axis
   min-content, and the "reduced automatic minimum size" that lets an
   overflow:hidden flex item shrink only applies on the MAIN axis - so the
   truncating block above still reserved ~250px, which five columns cannot
   afford at 390px. Wrapping costs a line of height and buys a table that
   fits. */
@media (max-width: 700px) {
  body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-dense-identity > strong,
  body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-dense-identity > span,
  body.fi-panel-portal .cxp-listing-management-page .cxp-two-col .sb-dense-identity > a {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  /* At 390px the header's flex row makes the title+tabs block share the width
     with the date-range buttons, leaving the two-column tab grid 140px for the
     144px it needs. Wrap the header instead: the tab strip takes the full row
     and the range buttons drop underneath. */
  body.fi-panel-portal .cxp-listing-management-page .kt-card-header {
    flex-wrap: wrap;
    row-gap: .75rem;
  }

  body.fi-panel-portal .cxp-listing-management-page .kt-card-header > div:first-child {
    flex: 1 1 100%;
  }
}

/* Below 420px even the full row is 4px short of the two-column tab grid's
   min-content, so take the phone layout the global rule already describes -
   one full-width tab per row - rather than leave a sliver of sideways scroll. */
/* The strip carries .25rem of padding and a 1px border under content-box
   sizing, so a block grid filling its header measured 8px wider than the box it
   sits in - a 4px sliver of sideways scroll at every width from 430 down. */
body.fi-panel-portal .cxp-listing-management-page .kt-card-header .cxp-map-tabs.cxp-map-tabs {
  box-sizing: border-box;
  max-width: 100%;
}

@media (max-width: 420px) {
  body.fi-panel-portal .cxp-listing-management-page .kt-card-header .cxp-map-tabs.cxp-map-tabs {
    grid-auto-flow: row;
  }

  /* Site authority is four columns of unbreakable one-word headings plus a sort
     arrow each - measured 333px of minimum against a 306px card at 360px wide.
     Keeping the headings whole is the right call (a heading reading "Meas/ured"
     is worse than a tight cell), so the 8px gutters give way instead: 4px each
     side reclaims the 32px that puts the table back inside its card. */
  body.fi-panel-portal .cxp-listing-management-page table.sb-data-table th,
  body.fi-panel-portal .cxp-listing-management-page table.sb-data-table td,
  body.fi-panel-portal .cxp-listing-management-page table.kt-table th,
  body.fi-panel-portal .cxp-listing-management-page table.kt-table td {
    padding-inline: 4px !important;
  }
}

/* lm-no-swipe-hint - 2026-08-25
   cxp.css adds an unconditional "Swipe to see more" line under every mobile
   table card, for the handful of tables in this portal that genuinely do scroll
   sideways. After the column work above none of this page's tables do - measured
   at 360, 375, 390 and 430, the document and every wrapper fit exactly - so the
   hint points at a scroll that is not there, which is worse than no affordance.
   Suppressed on this page only; the global rule is untouched. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-listing-management-page .kt-card-content:has(> table)::after {
    content: none !important;
    display: none !important;
  }

  body.fi-panel-portal .cxp-listing-management-page .kt-card-content:has(> table) {
    padding-bottom: 0 !important;
  }
}

/* lm-range-grid - 2026-08-25
   Four date-range buttons on a phone fit three to a line, dropping "All time"
   onto a row of its own next to a wide gap - which reads as a stray control
   rather than a set. Two by two is the same information, deliberately. */
@media (max-width: 430px) {
  body.fi-panel-portal .cxp-listing-management-page .kt-card-header .kt-card-toolbar {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .5rem;
    width: 100%;
  }

  body.fi-panel-portal .cxp-listing-management-page .kt-card-header .kt-card-toolbar > .kt-btn {
    width: 100%;
    min-width: 0;
  }
}

/* CYT-798 - Recent work has one semantic icon per event. The generated skin
   also paints a generic row dot and adds its own left padding, which produces
   the orphan blue dot and the oversized marker rail. Keep this correction in
   the portal activity stylesheet rather than editing the generated skin; the
   repeated panel scope intentionally out-specifies the skin's generic rule. */
body.fi-panel-portal.fi-panel-portal .cxp-activity-row::before,
body.fi-panel-portal.fi-panel-portal .cxp-activity-marker::before {
  content: none !important;
  display: none !important;
}

body.fi-panel-portal.fi-panel-portal .cxp-activity-row {
  padding-left: 0 !important;
}

body.fi-panel-portal.fi-panel-portal .cxp-activity-marker {
  box-sizing: border-box;
  width: 2.75rem !important;
  min-width: 2.75rem;
  padding: .65rem .5rem .65rem 1.25rem !important;
  vertical-align: middle !important;
}

body.fi-panel-portal.fi-panel-portal .cxp-activity-marker > i {
  display: block;
  font-size: 1rem;
  line-height: 1;
  color: var(--cxs-a, var(--primary));
}

body.fi-panel-portal.fi-panel-portal .cxp-activity-marker--completed > i,
body.fi-panel-portal.fi-panel-portal .cxp-activity-marker--resolved > i {
  color: var(--cxs-good-ink, var(--color-green-500));
}

body.fi-panel-portal.fi-panel-portal .cxp-activity-marker--measurement > i {
  color: var(--cxs-a, var(--primary));
}

body.fi-panel-portal.fi-panel-portal .cxp-activity-marker--recovery-update > i {
  color: var(--cxs-warn-ink, var(--color-yellow-500));
}

/* ============================================================================
   CYT-831 - a number has to say what KIND of number it is.

   THE DEFECT, measured on /portal/dashboard at 1440. "Department results" put
   six tiles in one row: 87.0/100, 72.0/100, #11.4, 48.0/100, 704, #34.5. That
   is a score out of 100, a search rank position and a raw count of directory
   listings, all drawn in the same ring component at the same diameter and the
   same type size. Only the score variant carried a unit at all - the "/100"
   beside its ring - so the other four numbers were presented as if they sat on
   the same 0-100 dial. A reader comparing 48.0 against 704 against #11.4 is
   being invited to a comparison that has no meaning.

   THE FIX. Every tile states its own unit on one shared line under the value,
   and the tiles are ordered so the kinds are grouped rather than interleaved.
   The line is deliberately quiet - the unit qualifies the figure, it does not
   compete with it - and it is the same treatment on every card that uses one,
   so units read as a system rather than as per-card decoration.
   ========================================================================= */
body.fi-panel-portal .cxp-stat-card__unit {
  min-width: 0;
  margin-top: .15rem;
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 500;
  line-height: 16px;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card__unit {
  font-size: 11px;
  line-height: 15px;
}
@media (max-width: 620px) {
  /* The tile becomes a 2-column grid at this width (ring in column 1 spanning
     every row). Department tiles suppress verdict and meta, so the unit is the
     only thing that follows the title - place it explicitly rather than relying
     on auto-placement, which would drop it below the ring on a card whose
     verdict/meta ARE shown. */
  body.fi-panel-portal .cxp-scoreboard-grid .cxp-stat-card:not(.cxp-stat-card--compact) .cxp-stat-card__unit {
    grid-column: 2;
    grid-row: 2;
  }
}

/* ============================================================================
   CYT-831 - the "Right now" column held a third of the Live activity row open
   for two short lines ("Work recorded in the latest 20 minutes." / "Your sites
   are on watch.") while its two neighbours carried ~200px of list each. When
   nothing is running that column is a status, not a list, so it sizes to its
   content instead of being padded out to an equal third. Scoped above the
   860px breakpoint so the existing single-column mobile collapse still wins.
   ========================================================================= */
@media (min-width: 861px) {
  body.fi-panel-portal .cxp-coordinator-grid--quiet {
    grid-template-columns: minmax(9rem, .5fr) minmax(0, 1fr) minmax(0, 1fr);
  }
}

/* ======================================================================
   Free SEO report + plan-status bar v2 - redesign brief run through
   GPT-5.6 Sol only (the trusted design model per Adir 2026-09-02), body
   content only, pixel-matched here. .cxp-plan-bar replaces the old yellow
   .cxp-billing-banner and is SHARED (rendered on every portal page via
   PortalPanelProvider's render hook), not scoped to this page alone.
   ====================================================================== */
body.fi-panel-portal .cxp-plan-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  border: 1px solid #dedcf9;
  border-radius: 12px;
  background: #fff;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
  padding: 13px 18px;
  margin-bottom: 1.25rem;
}
body.fi-panel-portal .cxp-plan-bar__message { display: flex; align-items: center; gap: 13px; min-width: 0; }
body.fi-panel-portal .cxp-plan-bar__icon {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  border-radius: 10px;
  background: #f0efff;
  color: #4f46e5;
}
body.fi-panel-portal .cxp-plan-bar__icon svg { width: 20px; height: 20px; }
body.fi-panel-portal .cxp-plan-bar--warm .cxp-plan-bar__icon { background: #fff7ed; color: #b45309; }
body.fi-panel-portal .cxp-plan-bar__title { margin: 0; font-size: 14px; font-weight: 700; color: #252b3b; line-height: 1.35; }
body.fi-panel-portal .cxp-plan-bar__desc { margin: 3px 0 0; font-size: 13px; color: var(--muted-foreground); line-height: 1.4; }
body.fi-panel-portal .cxp-plan-bar-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
  /* Real brand gradient token, not a hardcoded approximation - measured
     live 2026-09-02: kt-btn-primary's own background is var(--cxs-grad-ink),
     an oklch-based generated skin gradient, not literal #4f46e5/#db2777. */
  background: var(--cxs-grad-ink) !important;
  color: var(--portal-primary-foreground, #fff) !important;
  border: 1px solid transparent !important;
  box-shadow: 0 7px 18px rgba(79, 70, 229, .22);
  transition: box-shadow 150ms ease, transform 150ms ease;
}
body.fi-panel-portal .cxp-plan-bar-cta:hover { box-shadow: 0 10px 22px rgba(79, 70, 229, .28); transform: translateY(-1px); }
body.fi-panel-portal .cxp-plan-bar-cta svg { width: 16px; height: 16px; }
@media (max-width: 700px) {
  body.fi-panel-portal .cxp-plan-bar { flex-direction: column; align-items: stretch; }
  body.fi-panel-portal .cxp-plan-bar-cta { width: 100%; justify-content: center; }
}

/* Report card - official/legitimate framing, ONE secondary action, the
   house ring-gauge stat-card for the score (never a one-off ring - see
   reference_portal_chart_design_standard: one gauge language everywhere). */
body.fi-panel-portal .cxp-report-card-v2 {
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: #fff;
  box-shadow: 0 12px 32px rgba(16, 24, 40, .07);
}
body.fi-panel-portal .cxp-report-card-v2__accent { height: 4px; background: linear-gradient(90deg, #4f46e5, #a855f7 58%, #db2777); }
body.fi-panel-portal .cxp-report-card-v2__body { display: grid; grid-template-columns: minmax(0, 1fr) 240px; }
body.fi-panel-portal .cxp-report-card-v2__details { display: flex; flex-direction: column; padding: 28px 30px 26px; }
body.fi-panel-portal .cxp-report-card-v2__badges { display: flex; align-items: center; gap: 9px; margin-bottom: 20px; flex-wrap: wrap; }
body.fi-panel-portal .cxp-report-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 26px;
  border-radius: 999px;
  padding: 0 9px;
  font-size: 11px;
  font-weight: 700;
}
body.fi-panel-portal .cxp-report-badge--official { background: #eef2ff; color: #4338ca; }
body.fi-panel-portal .cxp-report-badge--ready { background: #ecfdf5; color: #17875d; }
body.fi-panel-portal .cxp-report-card-v2__title { margin: 0; font-size: 22px; font-weight: 720; letter-spacing: -.02em; color: #1d2939; }
body.fi-panel-portal .cxp-report-card-v2__domain { display: inline-flex; align-items: center; gap: 7px; margin: 8px 0 0; font-size: 14px; color: #475467; }
body.fi-panel-portal .cxp-report-card-v2__domain svg { width: 15px; height: 15px; color: #7c879b; }
body.fi-panel-portal .cxp-report-card-v2__meta { display: flex; flex-wrap: wrap; gap: 8px 28px; margin: 22px 0 0; }
body.fi-panel-portal .cxp-report-card-v2__meta-item { display: grid; gap: 4px; }
body.fi-panel-portal .cxp-report-card-v2__meta-label { color: #98a2b3; font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; }
body.fi-panel-portal .cxp-report-card-v2__meta-value { color: #344054; font-size: 13px; font-weight: 600; }
body.fi-panel-portal .cxp-report-card-v2__action { display: flex; align-items: center; gap: 10px; margin-top: auto; padding-top: 24px; }
body.fi-panel-portal .cxp-report-card-v2__score {
  display: grid;
  place-items: center;
  border-left: 1px solid var(--border);
  background: #f8fafc;
  padding: 20px;
}
body.fi-panel-portal .cxp-report-card-v2__score .cxp-stat-card { border: 0; box-shadow: none; background: transparent; padding: 0; }

/* Pending / no-report-yet state - same official chrome, honest copy only
   (there is no in-progress state to read: a site_reports row only exists
   once a check has completed or failed). */
body.fi-panel-portal .cxp-report-pending-v2 {
  display: grid;
  place-items: center;
  min-height: 240px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: #fff;
  box-shadow: 0 12px 32px rgba(16, 24, 40, .07);
  padding: 42px 30px;
  text-align: center;
}
body.fi-panel-portal .cxp-report-pending-v2__icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin: 0 auto 18px;
  border-radius: 16px;
  background: #f0efff;
  color: #4f46e5;
}
body.fi-panel-portal .cxp-report-pending-v2__icon svg { width: 26px; height: 26px; }
body.fi-panel-portal .cxp-report-pending-v2__title { margin: 0; font-size: 20px; font-weight: 720; color: #1d2939; }
body.fi-panel-portal .cxp-report-pending-v2__desc { max-width: 440px; margin: 10px auto 0; font-size: 14px; line-height: 1.6; color: var(--muted-foreground); }
body.fi-panel-portal .cxp-report-pending-v2__action { margin-top: 20px; }

/* Compact "next step" bar - replaces four long promo cards with one line. */
body.fi-panel-portal .cxp-next-step {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  margin-top: 18px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: rgba(255, 255, 255, .8);
  padding: 18px 20px;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
}
body.fi-panel-portal .cxp-next-step__title { margin: 0; font-size: 14px; font-weight: 700; color: #344054; }
body.fi-panel-portal .cxp-next-step__desc { margin: 4px 0 0; font-size: 13px; color: var(--muted-foreground); }
body.fi-panel-portal .cxp-next-step__benefits { display: flex; flex-wrap: wrap; gap: 8px; }
body.fi-panel-portal .cxp-next-step-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: #fff;
  padding: 0 11px;
  font-size: 12px;
  font-weight: 600;
  color: #475467;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-next-step-pill svg { width: 13px; height: 13px; color: #17875d; }

@media (max-width: 900px) {
  body.fi-panel-portal .cxp-report-card-v2__body { grid-template-columns: 1fr 200px; }
}
@media (max-width: 700px) {
  body.fi-panel-portal .cxp-report-card-v2__body { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-report-card-v2__score { border-left: 0; border-top: 1px solid var(--border); }
  body.fi-panel-portal .cxp-next-step { flex-direction: column; align-items: flex-start; }
  body.fi-panel-portal .cxp-report-card-v2__action { flex-direction: column; align-items: stretch; }
  body.fi-panel-portal .cxp-report-card-v2__action .kt-btn,
  body.fi-panel-portal .cxp-report-pending-v2__action .kt-btn { width: 100%; justify-content: center; }
}

/* ======================================================================
   Free-report request modal - "Ask us to check my site" now starts a
   real check in-place instead of linking out to the contact page. Same
   backend as cyticx.com's own intake bar (SiteReportLeadController),
   called same-origin. Reuses the house .cxp-sc-overlay* modal chrome
   (see connect-website.blade.php / connections.blade.php) rather than a
   one-off dialog.
   ====================================================================== */
body.fi-panel-portal .cxp-check-dialog { max-width: 420px; text-align: left; }
body.fi-panel-portal .cxp-check-field { margin: 16px 0; text-align: left; }
body.fi-panel-portal .cxp-check-field label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: #98a2b3;
  margin: 0 0 6px 2px;
}
body.fi-panel-portal .cxp-check-optional { text-transform: none; font-weight: 500; letter-spacing: 0; }
body.fi-panel-portal .cxp-check-field input {
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: #fff;
  padding: 11px 13px;
  font-size: 14px;
  font-weight: 500;
  color: #1d2939;
  outline: none;
  transition: border-color 150ms ease;
}
body.fi-panel-portal .cxp-check-field input:focus { border-color: #4f46e5; }
body.fi-panel-portal .cxp-check-hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
body.fi-panel-portal .cxp-check-msg { margin: 4px 0 0; font-size: 12.5px; font-weight: 600; color: #b42318; }

body.fi-panel-portal .cxp-check-steps { list-style: none; margin: 18px 0 4px; padding: 0; display: grid; gap: 9px; }
body.fi-panel-portal .cxp-check-steps li { display: flex; align-items: center; gap: 11px; color: #98a2b3; font-size: 13px; font-weight: 600; transition: color 200ms ease; }
body.fi-panel-portal .cxp-check-steps__ic {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1.6px solid var(--border);
  font-size: 11px;
  font-weight: 800;
  color: #98a2b3;
  transition: all 200ms ease;
}
body.fi-panel-portal .cxp-check-steps li.is-done { color: #344054; }
body.fi-panel-portal .cxp-check-steps li.is-done .cxp-check-steps__ic { border-color: #17875d; background: #ecfdf5; color: #17875d; }
body.fi-panel-portal .cxp-check-steps li.is-active { color: #1d2939; }
body.fi-panel-portal .cxp-check-steps li.is-active .cxp-check-steps__ic { border-color: #4f46e5; background: #eef2ff; color: #4f46e5; }

/* ======================================================================
   "Your plan" pricing table - button consistency fix (GPT-5.6 Sol,
   body-scoped, pixel-matched here). Both real plans keep the SAME
   kt-btn-primary weight (they are genuinely two independent, equally
   real purchases) - what was missing was a plain caption saying what
   each button actually buys, and a properly-styled addon panel instead
   of an unstyled checkbox+label (there was no .cxp-plan-addon rule at
   all before this).
   ====================================================================== */
body.fi-panel-portal .cxp-plan-table-intro {
  margin: 0;
  padding: 12px 20px;
  color: var(--muted-foreground);
  background: var(--surface-soft, #f8fafc);
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
  line-height: 1.55;
}

body.fi-panel-portal .cxp-plan-addon-panel {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
  padding: 11px 13px;
  background: #f7f7ff;
  border: 1px solid #c7d2fe;
  border-radius: 10px;
  box-shadow: inset 3px 0 0 #4f46e5;
}

body.fi-panel-portal .cxp-plan-addon {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
  cursor: pointer;
}

body.fi-panel-portal .cxp-plan-addon input {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  margin: 2px 0 0;
  accent-color: #4f46e5;
  cursor: pointer;
}

body.fi-panel-portal .cxp-plan-addon strong {
  display: block;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--foreground);
}

body.fi-panel-portal .cxp-plan-addon em {
  display: block;
  margin-top: 2px;
  font-style: normal;
  font-size: 11.5px;
  color: var(--muted-foreground);
}

body.fi-panel-portal .cxp-plan-addon-total {
  text-align: right;
  font-size: 12px;
  font-weight: 700;
  color: var(--foreground);
  white-space: nowrap;
}

body.fi-panel-portal .cxp-plan-action-summary {
  display: block;
  margin-top: 6px;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted-foreground);
  white-space: normal;
  overflow-wrap: break-word;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-plan-addon-panel { flex-direction: column; align-items: stretch; }
  body.fi-panel-portal .cxp-plan-addon-total { text-align: left; }
  body.fi-panel-portal .cxp-plan-action-summary { white-space: normal; }
}

/* ======================================================================
   Connected accounts row - button consistency fix (GPT-5.6 Sol,
   body-scoped, pixel-matched here). Adir, 2026-09-03: "the connector
   accounts need to be same button on the right side" - every row's action
   used to live in its own full-width amber strip (button left, text
   right), a different shape per row and nowhere near the status pill.
   New rule: status pill + action button(s), stacked, right-aligned, same
   place on every row regardless of state. Scoped under --v2 so it never
   fights the older .cxp-conn-row rules used elsewhere.

   Revised same day, Adir's correction: "it's supposed to be the same
   button... when it's connect, it's supposed to show it's connected...
   same idea and same design for all." The pill and the action button are
   now ONE control (.cxp-conn-toggle, same size/shape every row) - its
   colour and label ARE the state. Genuinely separate maintenance actions
   (Test again/Disconnect/Reconnect/message us) are small text links
   underneath it, never a second same-size button.
   ====================================================================== */
body.fi-panel-portal .cxp-conn-row--v2 {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.5rem;
}
body.fi-panel-portal .cxp-conn-row--v2 .cxp-conn-copy {
  display: flex;
  flex-direction: column;
  gap: .22rem;
  flex: 1 1 auto;
  min-width: 0;
}
body.fi-panel-portal .cxp-conn-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: .4rem;
  flex: 0 0 auto;
  padding-top: .05rem;
}
body.fi-panel-portal .cxp-conn-side form { margin: 0; }
body.fi-panel-portal .cxp-conn-side .kt-btn {
  white-space: nowrap;
}

/* The one control: same shape/size/position on every row, whichever state
   it is in - a real link when there is something to do, a quiet
   non-interactive pill once done. */
body.fi-panel-portal .cxp-conn-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .3rem;
  min-width: 132px;
  box-sizing: border-box;
  font-size: .8125rem;
  font-weight: 650;
  line-height: 1;
  white-space: nowrap;
  padding: .55rem .95rem;
  border-radius: 999px;
  text-align: center;
  text-decoration: none !important;
}
body.fi-panel-portal .cxp-conn-toggle--on {
  background: #E7F6EE;
  border: 1px solid #B7E3CB;
  color: #04663A;
  cursor: default;
}
body.fi-panel-portal .cxp-conn-toggle--pending {
  background: #FFF4E5;
  border: 1px solid #FFD9A6;
  color: #8A4B00;
}
body.fi-panel-portal .cxp-conn-toggle--off {
  background: #FFF4E5;
  border: 1px solid #FFD9A6;
  color: #8A4B00;
  cursor: default;
}
body.fi-panel-portal a.cxp-conn-toggle--connect,
body.fi-panel-portal a.cxp-conn-toggle--pending {
  /* Real brand gradient token, not a hardcoded approximation - measured
     live 2026-09-02: kt-btn-primary's own background is var(--cxs-grad-ink),
     an oklch-based generated skin gradient, not literal #4f46e5/#db2777. */
  background: var(--cxs-grad-ink) !important;
  color: var(--portal-primary-foreground, #fff) !important;
  border: 1px solid transparent;
  box-shadow: 0 7px 18px rgba(79, 70, 229, .22);
  transition: box-shadow 150ms ease, transform 150ms ease;
}
body.fi-panel-portal a.cxp-conn-toggle--connect:hover,
body.fi-panel-portal a.cxp-conn-toggle--pending:hover {
  box-shadow: 0 10px 22px rgba(79, 70, 229, .28);
  transform: translateY(-1px);
}

/* Genuinely separate maintenance actions - small text links underneath the
   one toggle, never a second same-size button competing with it. */
body.fi-panel-portal .cxp-conn-side-link {
  display: inline-block;
  font-size: .75rem;
  font-weight: 600;
  color: var(--muted-foreground);
  text-decoration: underline;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

@media (max-width: 640px) {
  body.fi-panel-portal .cxp-conn-row--v2 {
    flex-direction: column;
    align-items: stretch;
  }
  body.fi-panel-portal .cxp-conn-side {
    align-items: flex-start;
  }
  body.fi-panel-portal .cxp-conn-toggle {
    width: 100%;
  }
}


/* ===== RANKINGS V2 (cxp-rk2) - appended 2026-09-03 ===== */
/* ==========================================================================
   Rankings v2 - "Keyword positions" / "Keywords we track" / "Rankings"
   (summary). Pixel-matches sol-v2/rankings-section-redesign.html.

   Every new selector is scoped body.fi-panel-portal .cxp-rankings-page
   .cxp-rk2-... per FRONTEND_BRIEF.md. Existing house classes (kt-card,
   kt-card-border, kt-card-table, kt-card-header, kt-card-title, kt-btn*,
   kt-badge*, cxp-chart-toolbar, cxp-meta, cxp-rankings-tone-*, cxp-pager*)
   are reused as-is and are NOT redefined here.

   This file is appended to public/css/metronic/cxp.css.
   ========================================================================== */

/* --------------------------------------------------------------------
   Shared page scaffold
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-page {
  display: grid;
  gap: 20px;
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-subtitle {
  margin: 4px 0 0;
  color: var(--muted-foreground);
  font-size: 12.5px;
  line-height: 1.45;
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-card-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}

/* Raw <svg><polyline> sparkline used by KPI tiles, the per-row 7-day/30-day
   trend cells, and the distribution mini-tiles. */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-spark {
  width: 80px;
  height: 24px;
  flex: none;
  overflow: visible;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-spark polyline {
  fill: none;
  stroke: var(--primary);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --------------------------------------------------------------------
   Context card - source line, meta, comparison-period pills
   -------------------------------------------------------------------- */
/* .kt-card is a column flexbox in the skin - every rk2 card that lays its
   children out in a row says so explicitly, and every rk2 card carries its
   own 20px gutter (the skin's .kt-card has none; "the boxes are touching
   the border" was exactly this). */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-card {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  padding: 18px 20px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-main {
  flex: 1 1 320px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-card .cxp-chart-toolbar {
  flex: 0 0 auto;
  margin-left: auto;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-card,
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi,
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart-card,
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-card,
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-card {
  padding: 20px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-card,
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart-card,
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-card,
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi {
  display: block;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid.cxp-rk2-kpi-grid--five {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-show-more {
  margin: 0 20px 18px;
}
/* Small "which columns" selector in the table card header, beside the count. */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table-tools {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: none;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-window {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  color: var(--muted-foreground);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-window select {
  height: 32px;
  padding: 0 28px 0 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--card);
  color: var(--foreground);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-main {
  display: grid;
  gap: 5px;
  min-width: 0;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  color: var(--foreground);
  font-size: 15px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-source-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--primary);
  flex: none;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  color: var(--muted-foreground);
  font-size: 12.5px;
}

/* --------------------------------------------------------------------
   Position filters - band row of clickable chips (Alpine-bound)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-row {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 10px;
  margin-top: 15px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-chip {
  min-width: 0;
  min-height: 76px;
  padding: 12px;
  text-align: left;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
  color: var(--foreground);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-chip--active {
  border-color: #c7d2fe;
  background: color-mix(in oklab, var(--primary) 6%, var(--card));
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--primary) 18%, transparent);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-label {
  display: block;
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-value {
  display: block;
  margin-top: 3px;
  font-size: 25px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--foreground);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-change {
  display: block;
  margin-top: 2px;
  color: var(--muted-foreground);
  font-size: 12px;
}

/* --------------------------------------------------------------------
   KPI tiles
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid--three {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi {
  min-height: 152px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-label {
  color: var(--foreground);
  font-size: 13px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-value {
  margin-top: 12px;
  color: var(--foreground);
  font-size: 30px;
  line-height: 1;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.03em;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-value small {
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0;
  vertical-align: middle;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-copy {
  margin: 9px 0 0;
  color: var(--muted-foreground);
  font-size: 12px;
}

/* --------------------------------------------------------------------
   Trend chart card ("Average position over time")
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart-summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 14px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart-stat {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 4px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart-stat strong {
  color: var(--foreground);
  font-size: 30px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-direction {
  font-size: 12px;
  font-weight: 650;
}

/* Inline SVG chart used only by the summary page's combined distribution
   chart (x-portal.trend-chart covers the average-position chart). */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart {
  width: 100%;
  height: auto;
  display: block;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart .cxp-rk2-axis-text {
  fill: var(--muted-foreground);
  font-size: 12px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart .cxp-rk2-grid-line {
  stroke: var(--border);
  stroke-width: 1;
  stroke-dasharray: 3 5;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart .cxp-rk2-axis-line {
  stroke: var(--border);
  stroke-width: 1;
}

/* --------------------------------------------------------------------
   Table cards (Keyword results / Tracked keyword detail / Top pages)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table-card .kt-card-header {
  padding: 20px 20px 0;
  margin-bottom: 14px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-selection-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 0 20px 12px;
  padding: 9px 12px;
  border: 1px solid #c7d2fe;
  border-radius: 9px;
  background: color-mix(in oklab, var(--primary) 6%, var(--card));
  color: var(--primary);
  font-size: 12.5px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table-wrap {
  margin: 0 20px 16px;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table-note {
  margin: 0 20px 18px;
  color: var(--muted-foreground);
  font-size: 12px;
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0;
  color: var(--foreground);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table th {
  height: 40px;
  padding: 8px 9px;
  text-align: left;
  background: var(--surface-2, #f2f4f8);
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 650;
  letter-spacing: .04em;
  text-transform: uppercase;
  line-height: 1.2;
  white-space: normal;
  vertical-align: middle;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td {
  height: 48px;
  padding: 8px 9px;
  text-align: left;
  border-top: 1px solid var(--border);
  vertical-align: middle;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tbody tr:nth-child(even) td {
  background: var(--surface-2, #f8fafc);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tbody tr:hover td {
  background: color-mix(in oklab, var(--primary) 4%, var(--card));
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-check {
  width: 16px;
  height: 16px;
  accent-color: var(--primary);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-col-select {
  width: 2.75rem;
  text-align: center;
}

/* The keyword is the service + city the owner is paying for - it wraps to a
   second line rather than losing the city to an ellipsis. */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-keyword {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  color: var(--foreground);
  font-weight: 650;
  line-height: 1.3;
  overflow: hidden;
  overflow-wrap: anywhere;
  white-space: normal;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td {
  font-size: 13px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-row--selected td {
  background: color-mix(in oklab, var(--primary) 7%, var(--card)) !important;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-selection-bar--idle {
  border-color: var(--border);
  background: var(--surface-2, #f8fafc);
  color: var(--muted-foreground);
  font-weight: 500;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-landing {
  display: block;
  max-width: 100%;
  margin-top: 3px;
  overflow: hidden;
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 400;
  text-overflow: ellipsis;
  white-space: nowrap;
}
a.cxp-rk2-landing:hover {
  text-decoration: underline;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-nowrap {
  white-space: nowrap;
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-cell {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 5px;
  min-width: 0;
  max-width: 100%;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-rank {
  color: var(--foreground);
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-rank--great {
  color: var(--cxp-good-strong, #04663a);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-rank--missing {
  color: var(--muted-foreground);
  font-weight: 600;
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip,
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-tag {
  display: inline-flex;
  align-items: center;
  width: max-content;
  border-radius: 999px;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip {
  padding: 2px 6px;
  border: 1px solid;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.2;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip--good {
  color: var(--cxp-good-strong, #04663a);
  background: #e7f6ee;
  border-color: #b7e3cb;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip--bad {
  color: var(--cxp-bad-strong, #b42318);
  background: #fde8e8;
  border-color: #f3b8b0;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip--neutral {
  color: #6b7280;
  background: #f3f4f6;
  border-color: #e5e7eb;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-tag {
  margin-left: 4px;
  padding: 2px 7px;
  color: #4338ca;
  background: #eef2ff;
  border: 1px solid #c7d2fe;
  font-size: 11.5px;
  font-weight: 700;
}

/* --------------------------------------------------------------------
   Tracked keywords page - toolbar + strategy strip
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-card {
  display: grid;
  gap: 16px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar {
  display: grid;
  grid-template-columns: minmax(240px, 1fr) auto auto;
  gap: 10px;
  align-items: center;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-form {
  display: contents;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar input[type="text"] {
  min-width: 0;
  height: 38px;
  padding: 0 13px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: var(--card);
  color: var(--foreground);
  outline: none;
  font: inherit;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar input[type="text"]:focus {
  border-color: #a5b4fc;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary) 12%, transparent);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-note {
  grid-column: 1 / -1;
  color: var(--muted-foreground);
  font-size: 12px;
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-strategy {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 16px 20px;
  border-color: #ddd6fe;
  background: color-mix(in oklab, var(--primary) 4%, var(--card));
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-strategy strong {
  color: var(--foreground);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-suggestions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 7px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-suggestion {
  padding: 6px 10px;
  border: 1px solid #d8d8f5;
  border-radius: 999px;
  background: var(--card);
  color: #4338ca;
  font-size: 12px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-suggestion:hover {
  background: #eef2ff;
}

/* --------------------------------------------------------------------
   Summary page - distribution of top positions
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-distribution-layout {
  display: grid;
  grid-template-columns: 390px minmax(0, 1fr);
  gap: 20px;
  align-items: stretch;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-distribution-tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-mini-tile {
  min-height: 92px;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2, #f5f7fa);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-mini-label {
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-mini-line {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 8px;
  margin-top: 6px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-mini-value {
  color: var(--foreground);
  font-size: 23px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-distribution-chart {
  min-width: 0;
}

/* --------------------------------------------------------------------
   Summary page - keyword movement
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-bar {
  display: flex;
  height: 14px;
  overflow: hidden;
  border-radius: 999px;
  background: #eef0f4;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin-top: 10px;
  color: var(--muted-foreground);
  font-size: 12px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-legend-item {
  display: flex;
  align-items: center;
  gap: 7px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-swatch {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #cbd1dc;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-swatch--good {
  background: var(--cxp-good-strong, #78c99f);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-swatch--bad {
  background: var(--cxp-bad-strong, #ef8e86);
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-lists {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-top: 18px;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-list {
  min-width: 0;
  padding: 14px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-list + .cxp-rk2-movement-list {
  border-left: 1px solid var(--border);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-list h3 {
  margin: 0 0 9px;
  color: var(--foreground);
  font-size: 12.5px;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-list ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 34px;
  border-top: 1px solid var(--surface-2, #eef0f4);
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-list li:first-child {
  border-top: 0;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-term {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--foreground);
  font-size: 12px;
}

body.fi-panel-portal .cxp-rankings-page .cxp-rk2-page-tabs {
  margin-left: auto;
}

/* --------------------------------------------------------------------
   Responsive: 980px breakpoint (tablet)
   -------------------------------------------------------------------- */
@media (max-width: 980px) {
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid--three,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid.cxp-rk2-kpi-grid--five {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-row {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-distribution-layout {
    grid-template-columns: 1fr;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-distribution-tiles {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-strategy {
    align-items: flex-start;
    flex-direction: column;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-suggestions {
    justify-content: flex-start;
  }
}

/* --------------------------------------------------------------------
   Responsive: 760px breakpoint (the page itself never scrolls sideways
   below this - tables switch to stacked label/value rows)
   -------------------------------------------------------------------- */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-card,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-top,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart-summary,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-card-heading {
    align-items: flex-start;
    flex-direction: column;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
  }
  /* "All keywords" spans the row, the six bands sit 3 + 3 - no orphan tile. */
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-row .cxp-rk2-band-chip:first-child {
    grid-column: 1 / -1;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-card .cxp-chart-toolbar {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%;
    gap: 6px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-card .cxp-chart-toolbar .kt-btn {
    width: 100%;
    justify-content: center;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-chip {
    min-height: 0;
    padding: 10px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-value {
    font-size: 21px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-card,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-card,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chart-card,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-card,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-card {
    padding: 16px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-card .cxp-chart-toolbar {
    margin-left: 0;
  }
  /* In the stacked (column) card the 320px flex-basis would become a
     320px HEIGHT - keep the text block at its natural size. */
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-main {
    flex: 0 0 auto;
    width: 100%;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-context-card {
    gap: 12px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid.cxp-rk2-kpi-grid--five {
    grid-template-columns: 1fr;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi-grid--three {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-kpi {
    min-height: 132px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table-card .kt-card-header {
    padding: 16px 16px 0;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-selection-bar {
    margin: 0 16px 12px;
    align-items: flex-start;
    flex-direction: column;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table-wrap {
    margin: 0 16px 14px;
    border: 0;
    border-radius: 0;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table-note {
    margin: 0 16px 16px;
  }

  /* Stacked-row table (the mockup's mobile shape): each td becomes a
     label/value line, driven off attr(data-label) - no JS needed. */
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tbody,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tr,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td {
    display: block;
    width: 100%;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table thead {
    display: none;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tbody {
    display: grid;
    gap: 10px;
    padding: 12px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tr {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card);
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tbody tr:nth-child(odd) td,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tbody tr:nth-child(even) td {
    background: var(--card);
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td {
    display: flex;
    min-width: 0;
    height: auto;
    min-height: 42px;
    padding: 9px 11px;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    border-top: 1px solid var(--surface-2, #eef0f4);
    overflow: visible;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td > * {
    min-width: 0;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td::before {
    content: attr(data-label);
    flex: none;
    color: var(--muted-foreground);
    font-size: 11.5px;
    font-weight: 650;
    letter-spacing: .04em;
    text-transform: uppercase;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td.cxp-rk2-col-select {
    grid-column: 1 / -1;
    justify-content: flex-start;
    width: auto;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td.cxp-rk2-col-select::before {
    content: "Select keyword";
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td.cxp-rk2-col-kw {
    grid-column: 1 / -1;
    display: block;
    border-top: 0;
    padding: 12px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table td.cxp-rk2-col-kw::before {
    display: block;
    margin-bottom: 5px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-spark {
    width: 76px;
  }

  /* Keywords we track on a phone: a compact card per term - keyword (two
     lines), position + change, the two sparklines, volume/best/last check
     as short pairs, Remove last. "Added by" and CPC are desktop-only detail;
     nothing on screen is computed from them. */
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table--track td {
    min-height: 34px;
    padding: 6px 11px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table--track td.cxp-rk2-td-added,
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table--track td.cxp-rk2-td-cpc {
    display: none;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table--track td.cxp-rk2-td-pos {
    grid-column: 1 / -1;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table--track td.cxp-rk2-td-action {
    grid-column: 1 / -1;
    justify-content: flex-end;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table--track td.cxp-rk2-td-action::before {
    display: none;
  }

  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar {
    grid-template-columns: 1fr;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar input[type="text"] {
    height: 44px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-note {
    grid-column: auto;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-toolbar-form .kt-btn {
    width: 100%;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-distribution-tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-lists {
    grid-template-columns: 1fr;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-movement-list + .cxp-rk2-movement-list {
    border-left: 0;
    border-top: 1px solid var(--border);
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-page-tabs {
    margin-left: 0;
  }
}

/* --------------------------------------------------------------------
   Responsive: 430px breakpoint (small phones)
   -------------------------------------------------------------------- */
@media (max-width: 430px) {
  /* One label/value pair per line - two per line clipped "Last checked" and
     the position chips on a 390px phone. */
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table tr {
    grid-template-columns: 1fr;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table--track tr {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-table--track td.cxp-rk2-td-last {
    grid-column: 1 / -1;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-distribution-tiles {
    grid-template-columns: 1fr 1fr;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-mini-tile {
    min-height: 86px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-band-card {
    padding: 16px;
  }
  body.fi-panel-portal .cxp-rankings-page .cxp-rk2-selection-bar {
    align-items: flex-start;
    flex-direction: column;
  }
}

/* ===== WEBSITE HEALTH V2 (cxp-wh2) ===== */
/* ==========================================================================
   Website health v2 - redesign of app.cyticx.com/portal/website-health.
   Pixel-matches ws3-health/website-health-redesign.html.

   Every new selector is scoped body.fi-panel-portal .cxp-health-page
   .cxp-wh2-... Existing house classes (kt-card, kt-card-border,
   kt-card-header, kt-card-title, kt-badge*, cxp-meta, cxp-live-status*,
   cxp-status-led*, cxp-fixed-callout*, cxp-health-issue*, cxp-coordinator-*,
   cxp-map-tabs, cxp-map-tab-panel, cxp-chart-tone-*, cxp-speed-*,
   cxp-score-*, cxp-domain-switcher, cxp-header-row, cxp-title-line,
   cxp-page-title, cxp-page-sub, cxp-header-actions, cxp-empty) are reused
   as-is and are NOT redefined here.

   Two small exceptions, both additions rather than overrides:
   - .cxp-rk2-chip / --good|--bad|--neutral (defined for .cxp-rankings-page
     in rankings-v2.css) are reused verbatim on this page for status/priority
     chips per FRONTEND_BRIEF.md - re-declared here scoped to
     .cxp-health-page so they render without touching the Rankings file.
   - A --warn tone is added to that same chip family (Medium-priority queue
     rows need an amber tone the rk2 set does not define).

   Round 3 item 1 adds one more scoped override in the same spirit: the
   shared <x-portal.trend-chart> component's .cxp-chart-axis-labels wrapper
   is flattened into a single row by a rule OUTSIDE this file (not present
   anywhere in health-v2.css - confirmed by search) - GA4's page avoids it
   only because it uses the component's other (area) variant. The component
   itself is never edited; this page gets a scoped column-layout override.

   This file is appended to public/css/metronic/cxp.css.
   ========================================================================== */

/* --------------------------------------------------------------------
   Chip family reused from Rankings v2 (see note above)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip {
  display: inline-flex;
  align-items: center;
  width: max-content;
  border-radius: 999px;
  padding: 2px 8px;
  border: 1px solid;
  white-space: nowrap;
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.4;
}
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--good { color: var(--cxp-good-strong, #04663a); background: #e7f6ee; border-color: #b7e3cb; }
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--bad { color: var(--cxp-bad-strong, #b42318); background: #fde8e8; border-color: #f3b8b0; }
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--warn { color: #8a4b00; background: #fff4e5; border-color: #f2cf9c; }
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--neutral { color: #6b7280; background: #f3f4f6; border-color: #e5e7eb; }

/* --------------------------------------------------------------------
   Round 3 item 1: <x-portal.trend-chart> axis-label wrapper, scoped
   override only. Both "Your score over time" and "Site speed over time"
   use the component's default (non-area) variant, whose markup is:
     <div class="cxp-chart-axis-labels"><span>{top}</span><span>{bottom}</span></div>
     <div class="cxp-trend-chart-plot">...</div>
   as siblings under the outer .cxp-chart. Something outside this file
   flattens .cxp-chart-axis-labels into a full-width row (max label
   top-left, min label top-right, reading as two unrelated numbers) - not
   present anywhere in health-v2.css itself. The shared component is never
   edited; this makes the wrapper a left-hand axis column on THIS page only.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-chart { display: flex; align-items: stretch; }
body.fi-panel-portal .cxp-health-page .cxp-chart-axis-labels {
  display: flex;
  flex: none;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-start;
  min-width: 30px;
  padding: 1px 0;
}
body.fi-panel-portal .cxp-health-page .cxp-chart-axis-labels span { color: var(--muted-foreground); font-size: 11.5px; font-weight: 650; line-height: 1.2; }
body.fi-panel-portal .cxp-health-page .cxp-trend-chart-plot { flex: 1 1 auto; min-width: 0; }

/* --------------------------------------------------------------------
   Tag pill (chart-header delta callout)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-tag {
  display: inline-flex;
  align-items: center;
  width: max-content;
  flex: none;
  padding: 4px 10px;
  border: 1px solid;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-tag--good { color: var(--cxp-good-strong, #04663a); background: #e7f6ee; border-color: #b7e3cb; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-tag--poor { color: var(--cxp-bad-strong, #b42318); background: #fde8e8; border-color: #f3b8b0; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-tag--neutral { color: #6b7280; background: #f3f4f6; border-color: #e5e7eb; }

/* --------------------------------------------------------------------
   Rings (shared gauge language: hero score ring + every mini-ring)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring {
  position: relative;
  flex: none;
  width: 132px;
  height: 132px;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring circle,
body.fi-panel-portal .cxp-health-page .cxp-wh2-mini-ring circle { fill: none; stroke-width: 10; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-mini-ring circle { stroke-width: 8; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring-track { stroke: var(--border); opacity: .8; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring-value { stroke: url(#wh2-ring-gradient); stroke-linecap: round; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring-value--tone { stroke: var(--wh2-tone, var(--primary)); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring-center {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  text-align: center;
  color: var(--foreground);
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring-center strong { font-size: 31px; line-height: 1; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring-center .cxp-wh2-ring-empty { color: var(--muted-foreground); font-size: 26px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-ring-center span { display: block; margin-top: 4px; color: var(--muted-foreground); font-size: 12px; font-weight: 650; }

body.fi-panel-portal .cxp-health-page .cxp-wh2-mini-ring { position: relative; flex: none; width: 72px; height: 72px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-mini-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-mini-ring--empty .cxp-wh2-ring-track { opacity: .5; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-mini-ring-center {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  color: var(--foreground);
  font-size: 13px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------
   Hero card
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-card { padding: 0; overflow: hidden; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-layout { display: grid; grid-template-columns: 380px minmax(0, 1fr); min-height: 238px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-main {
  display: flex;
  align-items: center;
  gap: 22px;
  min-width: 0;
  padding: 24px;
  border-right: 1px solid var(--border);
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-score-copy { min-width: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-domain { color: var(--muted-foreground); font-size: 12px; font-weight: 650; letter-spacing: .02em; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-verdict { margin: 7px 0 3px; color: var(--foreground); font-size: 20px; line-height: 1.25; font-weight: 700; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-note { margin: 0; color: var(--muted-foreground); font-size: 12.5px; line-height: 1.55; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-rule { margin: 8px 0 0; padding-top: 8px; border-top: 1px solid var(--border); color: var(--muted-foreground); font-size: 11.5px; line-height: 1.5; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factors { display: grid; align-content: center; min-width: 0; padding: 24px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factors-title { margin: 0 0 16px; color: var(--muted-foreground); font-size: 12px; font-weight: 650; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor { display: grid; justify-items: center; gap: 7px; min-width: 0; text-align: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor strong { color: var(--foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor span { color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-empty-inline { color: var(--muted-foreground); font-size: 12.5px; }

/* --------------------------------------------------------------------
   Ongoing website work - pipeline strip
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline {
  position: relative;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 18px;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline::before {
  content: "";
  position: absolute;
  z-index: 0;
  top: 21px;
  left: 9%;
  right: 9%;
  height: 2px;
  background: var(--border);
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node { position: relative; z-index: 1; min-width: 0; text-align: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-node-icon {
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  margin: 0 auto 10px;
  border: 1px solid #c7d2fe;
  border-radius: 50%;
  background: #f4f5ff;
  color: var(--primary);
  box-shadow: 0 0 0 6px var(--card);
  font-size: 18px;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node small { display: block; color: var(--muted-foreground); font-size: 11.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
/* Round 2 item 5: white-space:nowrap was missing, so text-overflow:ellipsis
   never engaged and long applied-change/measured-result sentences wrapped to
   several lines, throwing off this node's height against its neighbours.
   Every node title is now a genuine single truncated line. */
body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node strong { display: block; margin-top: 4px; overflow: hidden; color: var(--foreground); font-size: 13px; line-height: 1.35; white-space: nowrap; text-overflow: ellipsis; }
/* Round 4 item (b): the Applied-change title is the one node title long
   enough to lose its useful part to a single truncated line ('Add internal
   link on "Garage D...'). Extra class + element selector (higher
   specificity than the plain "strong" rule above) so this ONE title gets a
   controlled 2-line clamp instead - other node titles stay single-line. */
body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node strong.cxp-wh2-node-title-clamp {
  display: -webkit-box;
  overflow: hidden;
  white-space: normal;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  word-break: break-word;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node p { margin: 4px 0 0; color: var(--muted-foreground); font-size: 12px; line-height: 1.4; }
/* Round 2 item 5: "View details" disclosure holding the FULL, untruncated
   sentence the single-line title above had to cut - a native <details>,
   house link colour, never a modal. */
body.fi-panel-portal .cxp-health-page .cxp-wh2-node-more { margin-top: 4px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-node-more summary { display: inline-block; color: var(--primary); font-size: 11.5px; font-weight: 650; cursor: pointer; list-style: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-node-more summary::-webkit-details-marker { display: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-node-more summary:hover { text-decoration: underline; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-node-more[open] summary { margin-bottom: 4px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-node-more p { margin: 0; overflow: visible; white-space: normal; color: var(--muted-foreground); font-size: 12px; line-height: 1.4; text-align: left; }

/* --------------------------------------------------------------------
   What makes up your score
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-section-heading { margin: 4px 0 -4px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-section-heading .cxp-meta { display: block; max-width: 760px; margin-top: 4px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-card { display: grid; min-height: 240px; align-content: start; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-top { display: flex; justify-content: space-between; align-items: center; gap: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-title { margin: 0; color: var(--foreground); font-size: 16px; font-weight: 700; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-score { margin-top: 4px; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-copy { margin: 15px 0 0; color: var(--foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-note { margin: 10px 0 0; color: var(--muted-foreground); font-size: 12px; font-style: italic; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-action { margin: 10px 0 0; padding-top: 10px; border-top: 1px solid var(--border); color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-action strong { color: var(--foreground); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-stat-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-stat-chip { padding: 4px 8px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface-2, #f8fafc); color: var(--muted-foreground); font-size: 11.5px; font-weight: 650; }

/* --------------------------------------------------------------------
   Site speed over time - metric tiles
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-bottom: 18px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-tile { display: flex; align-items: center; gap: 12px; min-width: 0; padding: 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-2, #fafbfc); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-ring { width: 64px; height: 64px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-info { min-width: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-label { display: block; color: var(--foreground); font-size: 12px; font-weight: 700; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-value { display: block; margin: 4px 0 6px; color: var(--foreground); font-size: 22px; line-height: 1; font-weight: 750; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-value--empty { color: var(--muted-foreground); font-size: 13px; font-weight: 600; }

/* --------------------------------------------------------------------
   Checks connected to your website
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-connections-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-connection { min-width: 0; padding: 2px 16px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-connection:first-child { padding-left: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-connection + .cxp-wh2-connection { border-left: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-connection-head { display: flex; align-items: center; gap: 8px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-source-mark { display: grid; place-items: center; flex: none; width: 28px; height: 28px; border: 1px solid var(--border); border-radius: 8px; background: var(--card); color: var(--primary); font-size: 15px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-connection strong { color: var(--foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-connection .cxp-rk2-chip { margin-top: 9px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-connection p { margin: 7px 0 0; color: var(--muted-foreground); font-size: 12px; line-height: 1.4; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-fix-link { display: inline-block; margin-top: 4px; color: var(--primary); font-size: 12px; font-weight: 650; text-decoration: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-fix-link:hover { text-decoration: underline; }

/* --------------------------------------------------------------------
   Tables (Issue breakdown / What we're working on next)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-table-wrap { margin: 0 20px 16px; overflow-x: auto; border: 1px solid var(--border); border-radius: 10px; }
body.fi-panel-portal .cxp-health-page .kt-card-table .kt-card-header { padding: 20px 20px 0; margin-bottom: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-table { width: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0; color: var(--foreground); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-table th {
  height: 40px;
  padding: 8px 10px;
  text-align: left;
  background: var(--surface-2, #f2f4f8);
  color: var(--muted-foreground);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-table td { height: 48px; padding: 8px 10px; border-top: 1px solid var(--border); vertical-align: middle; font-size: 12.25px; line-height: 1.35; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-table tbody tr:nth-child(even) td { background: var(--surface-2, #fafbfc); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-table tbody tr:hover td { background: color-mix(in oklab, var(--primary) 4%, var(--card)); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-count-cell { text-align: left; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-issue-name { color: var(--foreground); font-weight: 650; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-detail-col { color: var(--muted-foreground); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-page-link { display: block; max-width: 100%; overflow: hidden; color: var(--primary); text-overflow: ellipsis; white-space: nowrap; text-decoration: none; }
body.fi-panel-portal .cxp-health-page a.cxp-wh2-page-link:hover { text-decoration: underline; }
body.fi-panel-portal .cxp-health-page span.cxp-wh2-page-link { color: var(--muted-foreground); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-table-note { margin: 0 20px 18px; color: var(--muted-foreground); font-size: 12px; }
/* Round 2 item 2: expandable list for a grouped "Report card checks (N)" row
   - a native <details>, house link colour, no fabricated summary text. */
body.fi-panel-portal .cxp-health-page .cxp-wh2-issue-details { margin-top: 6px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-issue-details summary { display: inline-block; color: var(--primary); font-size: 12px; font-weight: 650; cursor: pointer; list-style: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-issue-details summary::-webkit-details-marker { display: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-issue-details summary:hover { text-decoration: underline; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-issue-details ul { display: grid; gap: 4px; margin: 8px 0 0; padding: 0; list-style: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-issue-details li { color: var(--muted-foreground); font-size: 12px; line-height: 1.4; }

/* --------------------------------------------------------------------
   Work we did - improvement timeline + run-history list
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-timeline { position: relative; margin: 4px 20px 18px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-timeline::before { content: ""; position: absolute; top: 9px; bottom: 9px; left: 18px; width: 2px; background: var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-entry { position: relative; display: grid; grid-template-columns: 38px 100px minmax(0, 1fr) auto; gap: 12px; align-items: center; min-height: 66px; padding: 8px 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-icon {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 4px solid var(--card);
  border-radius: 50%;
  font-weight: 800;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-icon--good { background: #e7f6ee; color: var(--cxp-good-strong, #04663a); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-icon--bad { background: #fde8e8; color: var(--cxp-bad-strong, #b42318); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-date { color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-copy strong { display: block; color: var(--foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-copy span { display: block; margin-top: 3px; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-copy a { color: var(--primary); text-decoration: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-work-copy a:hover { text-decoration: underline; }

body.fi-panel-portal .cxp-health-page .cxp-wh2-run-list { margin: 4px 20px 18px; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-run-row { display: grid; grid-template-columns: 96px auto minmax(0, 1fr); align-items: center; gap: 12px; min-height: 46px; padding: 8px 12px; border-top: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-run-row:first-child { border-top: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-run-row:nth-child(even) { background: var(--surface-2, #fafbfc); }
body.fi-panel-portal .cxp-health-page .cxp-wh2-run-date { display: flex; flex-direction: column; color: var(--foreground); font-size: 12.5px; font-weight: 650; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-run-date small { color: var(--muted-foreground); font-size: 11px; font-weight: 500; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-run-note { min-width: 0; color: var(--muted-foreground); font-size: 12px; }

/* --------------------------------------------------------------------
   Responsive: 980px (tablet)
   -------------------------------------------------------------------- */
@media (max-width: 980px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-grid,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-connections-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-connection:nth-child(2n+1) { border-left: 0; }
}

/* --------------------------------------------------------------------
   Responsive: 760px (the page itself never scrolls sideways below this)
   -------------------------------------------------------------------- */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-layout { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-main { gap: 16px; padding: 18px 16px; border-right: 0; border-bottom: 1px solid var(--border); }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-ring { width: 108px; height: 108px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-ring-center strong { font-size: 26px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-verdict { font-size: 18px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factors { padding: 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factors-title { margin-bottom: 12px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor-row { gap: 7px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-mini-ring { width: 58px; height: 58px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor strong,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor span { font-size: 11.5px; }

  /* Round 2 item 6: 12px -> 16px between stages on mobile. */
  body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline { grid-template-columns: 1fr; gap: 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline::before { top: 18px; bottom: 18px; left: 18px; right: auto; width: 2px; height: auto; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node { display: grid; grid-template-columns: 38px minmax(0, 1fr); column-gap: 12px; text-align: left; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-node-icon { grid-row: 1 / 5; width: 38px; height: 38px; margin: 0; box-shadow: 0 0 0 5px var(--card); }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node small,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node strong,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node p,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-node-more { grid-column: 2; }
  /* Round 2 item 6: body copy >= 13px, line-height ~1.4 on mobile. */
  body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node strong { font-size: 13px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-pipeline-node p { font-size: 13px; line-height: 1.4; }

  body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-grid { grid-template-columns: 1fr; gap: 12px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-card { min-height: 0; }

  body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-tile { align-items: flex-start; flex-direction: column; gap: 7px; padding: 10px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-speed-ring { width: 56px; height: 56px; }

  body.fi-panel-portal .cxp-health-page .cxp-wh2-connections-grid { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-connection { padding: 12px 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-connection:first-child { padding-top: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-connection:last-child { padding-bottom: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-connection + .cxp-wh2-connection { border-left: 0; border-top: 1px solid var(--border); }

  body.fi-panel-portal .cxp-health-page .kt-card-table .kt-card-header { padding: 16px 16px 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table-wrap { margin: 0 16px 14px; border: 0; border-radius: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table-note { margin: 0 16px 16px; }

  /* Stacked-row tables - each td becomes a label/value line via data-label. */
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table tbody,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table tr,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table td { display: block; width: 100%; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table thead { display: none; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table tbody { display: grid; gap: 10px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table tbody tr { display: grid; overflow: hidden; border: 1px solid var(--border); border-radius: 10px; background: var(--card); }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table tbody tr:nth-child(even) td { background: var(--card); }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table td {
    display: grid;
    grid-template-columns: 110px minmax(0, 1fr);
    gap: 8px;
    width: 100%;
    height: auto;
    min-height: 42px;
    padding: 9px 11px;
    border-top: 1px solid var(--surface-2, #eef0f4);
    white-space: normal;
    overflow-wrap: anywhere;
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table td:first-child { border-top: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table td::before {
    content: attr(data-label);
    color: var(--muted-foreground);
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
  }
  /* Issue breakdown: Count + Issue share one row (68px + fill), Detail
     spans the full card width beneath - matches the approved mockup. */
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table--issue tbody tr { grid-template-columns: 68px minmax(0, 1fr); }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table--issue td { display: block; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table--issue td::before { display: block; margin-bottom: 5px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table--issue td:nth-child(1) { grid-column: 1; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table--issue td:nth-child(2) { grid-column: 2; border-top: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table--issue td:nth-child(3) { grid-column: 1 / -1; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-count-cell { text-align: left; }
  /* Round 3 item 2: the severity-note pill ("High priority + Medium
     priority findings") ran off the card's right edge at 390 instead of
     wrapping. Drop it onto its own line under the issue title, constrained
     to the card's own width, with normal text wrapping. */
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table--issue .kt-badge {
    display: block;
    width: max-content;
    max-width: 100%;
    margin: 5px 0 0;
    white-space: normal;
    overflow-wrap: anywhere;
  }

  body.fi-panel-portal .cxp-health-page .cxp-wh2-work-timeline { margin: 4px 16px 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-work-entry { grid-template-columns: 38px minmax(0, 1fr); align-items: start; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-work-date,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-work-copy,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-work-entry .cxp-rk2-chip { grid-column: 2; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-work-date { grid-row: 1; padding-top: 3px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-work-icon { grid-row: 1 / 5; }

  body.fi-panel-portal .cxp-health-page .cxp-wh2-run-list { margin: 4px 16px 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-run-row { grid-template-columns: 1fr; gap: 4px; align-items: start; }
}

/* --------------------------------------------------------------------
   Responsive: 640px - round 2B item 2. "Site speed over time"'s source
   byline (logo + label + note) squeezed the note into a narrow column
   beside the label on mobile. Below 640px the note drops to its own
   full-width line under the label, with its leading dash hidden (it only
   reads right inline). Scoped to .cxp-health-page - cxp-source-byline is a
   shared house class, not owned/redefined by this file elsewhere.
   -------------------------------------------------------------------- */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-health-page .cxp-source-byline { display: flex; flex-wrap: wrap; row-gap: 4px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-source-note { display: block; flex: 1 1 100%; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-source-dash { display: none; }
}

/* --------------------------------------------------------------------
   Responsive: 430px - round 2 item 11. The 3-across "checks behind your
   score" mini-gauges still forced tiny text at 390px inside the 760px
   layout above (3 columns in ~360px of usable width). Below 430px each
   check becomes a compact stacked ROW (ring left, title+detail right)
   instead of a 3-column grid, so description text can sit at >=13px.
   -------------------------------------------------------------------- */
@media (max-width: 430px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor-row { grid-template-columns: 1fr; gap: 10px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor {
    grid-template-columns: 58px minmax(0, 1fr);
    column-gap: 12px;
    row-gap: 2px;
    justify-items: start;
    text-align: left;
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor .cxp-wh2-mini-ring { grid-row: 1 / 3; }
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor strong,
  body.fi-panel-portal .cxp-health-page .cxp-wh2-hero-factor span { grid-column: 2; font-size: 13px; }
}

@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-health-page * { animation: none !important; transition: none !important; }
}

/* Coordinator hotfix: the severity pill computed display:inline-flex / white-space:nowrap at 390 (the
   media rule above lost to the skin's kt-badge rule) - force it to wrap under the issue title. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh2-table--issue td .kt-badge {
    display: block !important;
    width: fit-content !important;
    max-width: 100% !important;
    white-space: normal !important;
    overflow-wrap: anywhere;
    line-height: 1.35 !important;
    height: auto !important;
    margin: 6px 0 0 !important;
  }
}


/* Coordinator (round 3c): with the axis column on the left, the component's own x-date labels
   (.cxp-trend-point-labels) collapse to 0px at the right edge; this page already prints the date
   range in its .cxp-speed-axis row under the chart, so hide the component's copy. */
body.fi-panel-portal .cxp-health-page .cxp-chart > .cxp-trend-point-labels { display: none !important; }

/* ===== END WEBSITE HEALTH V2 (cxp-wh2) ===== */

/* ===== AI VISIBILITY V2 (cxp-ai2) ===== */
/* ==========================================================================
   AI search visibility v2 - dashboard headline row, trend empty state,
   per-assistant strip gutters, "one place for everything", Changes tab,
   main-page KPI row, Opportunities KPI row.
   Pixel-matches ws4-ai/ai-visibility-section-redesign.html.

   Every new selector is scoped body.fi-panel-portal .cxp-ai2-... Existing
   house classes (kt-card, kt-card-border, kt-card-header, kt-card-title,
   kt-btn*, cxp-meta, cxp-card-kicker, cxp-empty, cxp-ai-engine*,
   cxp-gauge/cxp-gauge-track/cxp-gauge-progress/cxp-gauge-*, cxp-score-*,
   cxp-chart-tone-note, x-portal.trend-chart's cxp-trend-chart*) are reused
   as-is and are NOT redefined here. <x-portal.stat-card> is retired from the
   four rebuilt areas - every tile below is plain markup.

   This file is appended to the global stylesheet by the deployer.
   ========================================================================== */

body.fi-panel-portal .cxp-ai2-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

body.fi-panel-portal .cxp-ai2-source-plain {
  margin: -.3rem 0 0;
}

/* --------------------------------------------------------------------
   Ring gauge - shared by every KPI tile across all four rebuilt areas.
   r=32 on an 80x80 viewBox gives a ~201 circumference, matched by the
   stroke-dasharray each blade computes ("dash of 201").
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-ring {
  position: relative;
  width: 84px;
  height: 84px;
  flex: 0 0 84px;
}
body.fi-panel-portal .cxp-ai2-ring--sm {
  width: 64px;
  height: 64px;
  flex-basis: 64px;
}
body.fi-panel-portal .cxp-ai2-ring svg {
  display: block;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}
body.fi-panel-portal .cxp-ai2-ring-track {
  fill: none;
  stroke: var(--border, #e7eaf0);
  stroke-width: 9;
}
body.fi-panel-portal .cxp-ai2-ring-fill {
  fill: none;
  stroke-width: 9;
  stroke-linecap: butt;
  stroke: var(--primary);
}
body.fi-panel-portal .cxp-ai2-ring--brand .cxp-ai2-ring-fill { stroke: url(#cxp-ai2-brand-grad); }
body.fi-panel-portal .cxp-ai2-ring--good .cxp-ai2-ring-fill { stroke: var(--cxp-good-strong, #04663a); }
body.fi-panel-portal .cxp-ai2-ring--middle .cxp-ai2-ring-fill { stroke: var(--cxp-warn-strong, #8a4b00); }
body.fi-panel-portal .cxp-ai2-ring--poor .cxp-ai2-ring-fill { stroke: var(--cxp-bad-strong, #b42318); }
body.fi-panel-portal .cxp-ai2-ring--neutral .cxp-ai2-ring-fill { stroke: transparent; }
body.fi-panel-portal .cxp-ai2-ring-value {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  text-align: center;
  color: var(--foreground);
  font-size: 17px;
  line-height: 1.05;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-ai2-ring-value small {
  display: block;
  margin-top: 2px;
  color: var(--muted-foreground);
  /* Punch list #11: every gauge sub-label ("of 4", "of 100") must read at
     11px or larger - this was 10px. */
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0;
}
/* Punch list #11: the small ring's own primary numeral must be >= 16px (was
   14px) and its sub-label >= 11px (was 9px) - the ring grew 60px -> 64px
   above so the larger type still fits without clipping. */
body.fi-panel-portal .cxp-ai2-ring--sm .cxp-ai2-ring-value { font-size: 16px; }
body.fi-panel-portal .cxp-ai2-ring--sm .cxp-ai2-ring-value small { font-size: 12px; }

/* --------------------------------------------------------------------
   CYT-882: the chip BOX is now the canonical kt-badge/kt-badge-sm/
   kt-badge-{good,warn,bad,neutral} component (see the alias block near
   kt-badge-success above) - .cxp-ai2-chip's own box is retired. The status
   dot below is kept as-is; it's a global class, not scoped to the retired
   box, so it still works nested in a kt-badge.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-led {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 0 3px currentColor;
  opacity: .78;
  animation: cxp-ai2-led-pulse 1.9s ease-in-out infinite;
}
@keyframes cxp-ai2-led-pulse { 50% { box-shadow: 0 0 0 1px currentColor; opacity: 1; } }

/* --------------------------------------------------------------------
   Delta visual - arrow-in-circle used for "Change since last check" and
   every per-assistant Changes card.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-delta-visual {
  width: 64px;
  height: 64px;
  flex: 0 0 64px;
  display: grid;
  place-items: center;
  border: 8px solid;
  border-radius: 50%;
  font-size: 22px;
  font-weight: 800;
  line-height: 1;
}
body.fi-panel-portal .cxp-ai2-delta-visual--good {
  color: var(--cxp-good-strong, #04663a);
  border-color: var(--cxp-good-tint, #cbead8);
  background: var(--cxp-good-tint, #e7f6ee);
}
body.fi-panel-portal .cxp-ai2-delta-visual--bad {
  color: var(--cxp-bad-strong, #b42318);
  border-color: var(--cxp-bad-tint, #f3d3cf);
  background: var(--cxp-bad-tint, #fde8e8);
}
body.fi-panel-portal .cxp-ai2-delta-visual--neutral {
  color: var(--muted-foreground, #6b7280);
  border-color: var(--border, #e2e5ea);
  background: var(--muted, #f3f4f6);
}

/* --------------------------------------------------------------------
   Count visual - solid rounded square used for "Issues found".
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-count-visual {
  width: 70px;
  height: 70px;
  flex: 0 0 70px;
  display: grid;
  place-items: center;
  border: 1px solid;
  border-radius: 18px;
  font-size: 28px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
body.fi-panel-portal .cxp-ai2-count-visual--good {
  color: var(--cxp-good-strong, #04663a);
  background: var(--cxp-good-tint, #e7f6ee);
  border-color: var(--cxp-good-tint, #b7e3cb);
}
body.fi-panel-portal .cxp-ai2-count-visual--bad {
  color: var(--cxp-bad-strong, #b42318);
  background: var(--cxp-bad-tint, #fde8e8);
  border-color: var(--cxp-bad-tint, #f3b8b0);
}
body.fi-panel-portal .cxp-ai2-count-visual--neutral {
  color: var(--muted-foreground, #6b7280);
  background: var(--muted, #f3f4f6);
  border-color: var(--border, #e5e7eb);
}

/* Assistant-checked icon: a small 2x2 dot grid, brand colours only. */
body.fi-panel-portal .cxp-ai2-dots {
  display: grid;
  grid-template-columns: repeat(2, 12px);
  gap: 6px;
  padding: 13px;
  flex: 0 0 auto;
  border: 1px solid var(--border);
  border-radius: 18px;
  background: color-mix(in oklab, var(--primary) 8%, var(--card));
}
body.fi-panel-portal .cxp-ai2-dots i {
  display: block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--primary);
  font-style: normal;
}
body.fi-panel-portal .cxp-ai2-dots i:nth-child(2),
body.fi-panel-portal .cxp-ai2-dots i:nth-child(4) {
  background: #db2777;
}

/* Logo tile - small assistant identity chip, Changes-tab size. Colours and
   the underlying logo assets are untouched (Adir: keep the assistant marks). */
body.fi-panel-portal .cxp-ai2-logo-tile {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: 0 0 32px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card);
}
body.fi-panel-portal .cxp-ai2-logo-tile img { width: 21px; height: 21px; display: block; }

/* --------------------------------------------------------------------
   KPI tile - dashboard headline row + main-page KPI row (ai-kpis).
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  margin-top: 4px;
}
body.fi-panel-portal .cxp-ai2-kpi {
  min-height: 172px;
  padding: 18px 20px;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 10px;
}
body.fi-panel-portal .cxp-ai2-kpi-label {
  color: var(--foreground);
  font-size: 13px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-ai2-kpi-core {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}
body.fi-panel-portal .cxp-ai2-kpi-copy { min-width: 0; }
body.fi-panel-portal .cxp-ai2-big-number {
  color: var(--foreground);
  font-size: 28px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-ai2-big-number--compact { font-size: 22px; line-height: 1.12; }
body.fi-panel-portal .cxp-ai2-subvalue {
  margin-top: 4px;
  color: var(--muted-foreground);
  font-size: 12.5px;
}
body.fi-panel-portal .cxp-ai2-explain {
  margin: 0;
  color: var(--muted-foreground);
  font-size: 14px;
  line-height: 1.45;
}

/* --------------------------------------------------------------------
   Trend card - summary strip + designed empty/one-check state.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-aivd-trend .kt-card-header {
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
body.fi-panel-portal .cxp-ai2-trend-summaries {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 1rem;
}
body.fi-panel-portal .cxp-ai2-summary-stat {
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-soft, color-mix(in oklab, var(--muted) 55%, var(--card)));
}
body.fi-panel-portal .cxp-ai2-summary-stat span {
  display: block;
  color: var(--muted-foreground);
  font-size: 12.5px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-ai2-summary-stat strong {
  display: block;
  margin-top: 3px;
  color: var(--foreground);
  font-size: 19px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-ai2-empty-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.75fr) minmax(240px, .75fr);
  gap: 16px;
  align-items: stretch;
}
body.fi-panel-portal .cxp-ai2-chart-stage {
  min-height: 200px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: linear-gradient(180deg, var(--card), color-mix(in oklab, var(--muted) 45%, var(--card)));
}
body.fi-panel-portal .cxp-ai2-chart-stage svg { display: block; width: 100%; height: 100%; min-height: 200px; }
body.fi-panel-portal .cxp-ai2-chart-stage text { fill: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-ai2-chart-grid { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 5; }
body.fi-panel-portal .cxp-ai2-chart-axis { stroke: var(--border); stroke-width: 1; }
body.fi-panel-portal .cxp-ai2-empty-note {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 11px;
  padding: 18px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: color-mix(in oklab, var(--primary) 4%, var(--card));
}
body.fi-panel-portal .cxp-ai2-empty-note h3 { margin: 0; color: var(--foreground); font-size: 16px; line-height: 1.3; }
body.fi-panel-portal .cxp-ai2-empty-note p { margin: 0; color: var(--muted-foreground); font-size: 14px; line-height: 1.45; }
body.fi-panel-portal .cxp-ai2-next-step {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  color: var(--primary);
  font-size: 13px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-ai2-next-step i {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  flex: 0 0 22px;
  border-radius: 50%;
  background: color-mix(in oklab, var(--primary) 14%, var(--card));
  font-style: normal;
}

/* Action links in the assistant-strip card header (View question rankings /
   Read what AI said). */
body.fi-panel-portal .cxp-ai2-action-links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  flex: 0 0 auto;
}

/* Assistant strip: only the outer gutter is tightened to match the row
   above - the per-engine card markup itself (cxp-ai-engine*) is untouched. */
body.fi-panel-portal .cxp-ai2-assistant-grid { gap: 16px; }

/* --------------------------------------------------------------------
   "One place for everything" - balanced 3+3 summary grid.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-section-heading { margin-top: .25rem; }
body.fi-panel-portal .cxp-ai2-section-title { margin: 0; color: var(--foreground); font-size: 17px; line-height: 1.3; }
body.fi-panel-portal .cxp-ai2-overview-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
body.fi-panel-portal .cxp-ai2-overview-card {
  min-height: 196px;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  color: inherit;
  text-decoration: none;
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
body.fi-panel-portal .cxp-ai2-overview-card:hover,
body.fi-panel-portal .cxp-ai2-overview-card:focus-visible {
  color: inherit;
  text-decoration: none;
  transform: translateY(-2px);
  border-color: color-mix(in oklab, var(--primary) 30%, var(--border));
  box-shadow: 0 7px 18px rgba(16, 24, 40, .08);
}
body.fi-panel-portal .cxp-ai2-overview-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}
body.fi-panel-portal .cxp-ai2-overview-title { color: var(--foreground); font-size: 15px; font-weight: 650; }
body.fi-panel-portal .cxp-ai2-overview-kicker { margin-top: 3px; color: var(--muted-foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-ai2-overview-value {
  color: var(--foreground);
  font-size: 26px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-ai2-overview-copy { margin: 0; color: var(--muted-foreground); font-size: 14px; line-height: 1.45; }
body.fi-panel-portal .cxp-ai2-overview-link { margin-top: auto; color: var(--primary); font-size: 12.5px; font-weight: 700; }
body.fi-panel-portal .cxp-ai2-finding-list { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; }
body.fi-panel-portal .cxp-ai2-finding-list li {
  overflow: hidden;
  color: var(--foreground);
  font-size: 12.5px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-ai2-finding-list li::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin: 0 8px 1px 0;
  border-radius: 50%;
  background: var(--primary);
  opacity: .55;
}
body.fi-panel-portal .cxp-ai2-progress { height: 8px; overflow: hidden; border-radius: 999px; background: var(--muted, #eaedf2); }
body.fi-panel-portal .cxp-ai2-progress > span { display: block; height: 100%; border-radius: inherit; }
body.fi-panel-portal .cxp-ai2-split-counts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
body.fi-panel-portal .cxp-ai2-split-count {
  padding: 9px 10px;
  border-radius: 9px;
  background: color-mix(in oklab, var(--muted) 55%, var(--card));
}
body.fi-panel-portal .cxp-ai2-split-count strong { display: block; color: var(--foreground); font-size: 20px; line-height: 1; }
body.fi-panel-portal .cxp-ai2-split-count span { display: block; margin-top: 5px; color: var(--muted-foreground); font-size: 12px; }

/* --------------------------------------------------------------------
   Change card - "Changes" tab (ai-changes), 4-across.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-change-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  padding: 20px;
}
body.fi-panel-portal .cxp-ai2-change-card {
  /* Punch list #5: was 184px, taller than the logo/ring/result/comparison/
     date content needs (~155-165px on desktop). */
  min-height: 162px;
  padding: 18px 20px;
  display: grid;
  gap: 10px;
}
body.fi-panel-portal .cxp-ai2-change-head {
  display: flex;
  align-items: center;
  gap: 9px;
  color: var(--foreground);
  font-size: 13px;
  font-weight: 700;
}
body.fi-panel-portal .cxp-ai2-change-main { display: flex; align-items: center; gap: 13px; }
body.fi-panel-portal .cxp-ai2-delta-number {
  color: var(--foreground);
  font-size: 25px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
  margin-bottom: 6px;
}
body.fi-panel-portal .cxp-ai2-delta-number--good { color: var(--cxp-good-strong, #04663a); }
body.fi-panel-portal .cxp-ai2-delta-number--bad { color: var(--cxp-bad-strong, #b42318); }
body.fi-panel-portal .cxp-ai2-change-copy { margin: 0; color: var(--muted-foreground); font-size: 14px; line-height: 1.4; }

/* --------------------------------------------------------------------
   Ratio card - Opportunities KPI row (ai-opportunities-kpis), 4-across.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-ratio-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  margin-top: 4px;
}
body.fi-panel-portal .cxp-ai2-ratio-card {
  min-height: 178px;
  padding: 18px 20px;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 10px;
}
body.fi-panel-portal .cxp-ai2-ratio-main { display: flex; align-items: center; gap: 14px; }
body.fi-panel-portal .cxp-ai2-ratio-count {
  color: var(--foreground);
  font-size: 25px;
  line-height: 1;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-ai2-ratio-count small {
  display: block;
  margin-top: 5px;
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0;
}

/* --------------------------------------------------------------------
   Tracking-off designed empty state (dashboard, punch list #1).
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-tracking-off {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 22px;
}
body.fi-panel-portal .cxp-ai2-tracking-off-head { display: flex; flex-direction: column; gap: 8px; max-width: 640px; }
body.fi-panel-portal .cxp-ai2-tracking-off-head .kt-card-title { margin: 0; }
body.fi-panel-portal .cxp-ai2-tracking-off-head .cxp-meta { margin: 0; }
body.fi-panel-portal .cxp-ai2-tracking-off-preview {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px;
  border: 1px dashed var(--border);
  border-radius: 12px;
  background: color-mix(in oklab, var(--muted) 35%, var(--card));
}
body.fi-panel-portal .cxp-ai2-preview-tile { opacity: .55; filter: grayscale(.4); pointer-events: none; }
body.fi-panel-portal .cxp-ai2-preview-grid { gap: 14px; }
body.fi-panel-portal .cxp-ai2-preview-tag {
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--muted);
  color: var(--muted-foreground);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}
/* Round 2B #5: the live page rendered this CTA with washed-out grey-blue
   text, reading as disabled - it sits outside the greyed preview already
   (never a .cxp-ai2-preview-tile child), so whatever kt-btn-primary itself
   resolves to elsewhere in the app, THIS button's contrast is fully
   self-contained and defensively reset regardless of any inherited state. */
body.fi-panel-portal .cxp-ai2-tracking-off-action {
  align-self: flex-start;
  opacity: 1 !important;
  filter: none !important;
  pointer-events: auto !important;
  color: #fff !important;
  background: linear-gradient(90deg, var(--primary), #db2777) !important;
  border: 0 !important;
}
body.fi-panel-portal .cxp-ai2-tracking-off-action:hover,
body.fi-panel-portal .cxp-ai2-tracking-off-action:focus-visible,
body.fi-panel-portal .cxp-ai2-tracking-off-action:visited {
  color: #fff !important;
}

/* --------------------------------------------------------------------
   Score summary card - AI search visibility "latest check" header
   (ai-visibility.blade.php). Punch list #8: the old <x-portal.stat-card>
   nested the ring in a small box beside a large empty horizontal area.
   Rebuilt as a balanced ring-left / copy-right row, own tile language.
   -------------------------------------------------------------------- */
/* Round 2B follow-up: the live page rendered this as a CENTERED STACKED
   column on desktop (ring+chip above, copy below, left-aligned) instead of
   the intended balanced row - whatever the actual cause upstream, every
   layout property here now carries !important so this card's own rules are
   never silently lost to an unknown competing selector. Mobile-first: column
   is the default/below-640px state; >=900px switches to a fixed-width ring
   column (vertically centred) beside a top-aligned, width-capped copy
   column. Between 640-899px it stays the safe stacked default. */
body.fi-panel-portal .cxp-ai2-score-summary {
  display: flex !important;
  flex-direction: column !important;
  align-items: flex-start !important;
  gap: 16px !important;
  padding: 20px 22px !important;
}
body.fi-panel-portal .cxp-ai2-score-visual {
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  gap: 10px !important;
  flex: 0 0 auto !important;
}
body.fi-panel-portal .cxp-ai2-score-visual .cxp-ai2-ring { width: 96px; height: 96px; flex-basis: 96px; }
body.fi-panel-portal .cxp-ai2-score-visual .cxp-ai2-ring-value { font-size: 22px; }
body.fi-panel-portal .cxp-ai2-score-visual .cxp-ai2-ring-value small { font-size: 12px; }
body.fi-panel-portal .cxp-ai2-score-copy { flex: 1 1 auto !important; min-width: 0; }

@media (min-width: 900px) {
  body.fi-panel-portal .cxp-ai2-score-summary {
    flex-direction: row !important;
    align-items: flex-start !important;
    gap: 26px !important;
  }
  body.fi-panel-portal .cxp-ai2-score-visual {
    flex: 0 0 200px !important;
    width: 200px !important;
    align-self: center !important;
  }
  body.fi-panel-portal .cxp-ai2-score-copy {
    flex: 1 1 auto !important;
    max-width: 720px !important;
    align-self: flex-start !important;
  }
}

/* --------------------------------------------------------------------
   Trend chart neutral annotation - the one-check state's "Second check
   required" label (punch list #4), replacing the old "Next check" /
   "Future" pair once the rising forecast line is removed.
   -------------------------------------------------------------------- */
/* Round 2B: the axis labels used to be <text> INSIDE the SVG, sized in
   viewBox user-units (e.g. "12px"). Since the SVG scales via CSS width:100%
   against a fixed 700x210 viewBox, that text shrinks with the container -
   at a 390px phone the effective render size was ~6px, illegible. Moved out
   to plain HTML, absolutely positioned over the chart at the same relative
   coordinates the old SVG text used (x/700, y/210 as percentages), so the
   font-size below is a REAL css px value that never rescales. Used by both
   the real one-check trend chart and the tracking-off preview chart. */
body.fi-panel-portal .cxp-ai2-chart-has-overlay { position: relative; }
body.fi-panel-portal .cxp-ai2-chart-axis-y {
  position: absolute;
  left: 2px;
  top: 0;
  bottom: 0;
  pointer-events: none;
}
body.fi-panel-portal .cxp-ai2-chart-axis-y span {
  position: absolute;
  transform: translateY(-50%);
  font-size: 11px;
  font-weight: 600;
  color: var(--muted-foreground);
  white-space: nowrap;
}
body.fi-panel-portal .cxp-ai2-chart-axis-x {
  position: absolute;
  left: 0;
  right: 6px;
  bottom: 4px;
  height: 16px;
  pointer-events: none;
}
body.fi-panel-portal .cxp-ai2-chart-axis-x-date {
  position: absolute;
  left: 8%;
  font-size: 11px;
  color: var(--muted-foreground);
  white-space: nowrap;
}
body.fi-panel-portal .cxp-ai2-chart-axis-x-note {
  position: absolute;
  left: 52%;
  transform: translateX(-50%);
  font-size: 11px;
  font-weight: 600;
  color: var(--muted-foreground);
  white-space: nowrap;
}

/* --------------------------------------------------------------------
   Changes tab - compact factual filler for the trailing space below the
   four cards (punch list #6), instead of ~350-430px of blank page.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-next-check-note {
  margin-top: 16px;
  padding: 14px 18px;
}
body.fi-panel-portal .cxp-ai2-next-check-note .cxp-meta { margin: 0; }

/* Punch list #6: a global tab-panel min-height (sized for the tallest
   tab, e.g. History) otherwise leaves a large blank gap under the four
   short Changes cards. Scoped to this page's own tab wrapper only - the
   rest of the portal's tab panels are untouched. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-ai-visibility-page .cxp-map-details,
  body.fi-panel-portal .cxp-ai-visibility-page .cxp-map-tab-panel {
    min-height: 0 !important;
  }
}

/* --------------------------------------------------------------------
   Opportunities page (punch list #2, #10) - the "Most of your listings"
   and "When a customer asks" panels each carry 2-3 tiles, not 4, so they
   get their own grids rather than borrowing the 4-across KPI grid at
   partial width. Reuses cxp-ai2-kpi / cxp-ai2-ratio-card for the tiles
   themselves - only the grid is new.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-ai2-pair-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  margin-top: .85rem;
}
body.fi-panel-portal .cxp-ai2-flex-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}

/* --------------------------------------------------------------------
   Responsive: 980px (tablet) - every 4-across grid becomes 2-across.
   -------------------------------------------------------------------- */
@media (max-width: 980px) {
  body.fi-panel-portal .cxp-ai2-kpi-grid,
  body.fi-panel-portal .cxp-ai2-change-grid,
  body.fi-panel-portal .cxp-ai2-ratio-grid,
  body.fi-panel-portal .cxp-ai2-overview-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-ai2-empty-layout { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-ai2-empty-note { min-height: 150px; }
}

/* --------------------------------------------------------------------
   Responsive: 760px (the page itself never scrolls sideways below this) -
   every grid collapses to one column.
   -------------------------------------------------------------------- */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-ai2-kpi-grid,
  body.fi-panel-portal .cxp-ai2-change-grid,
  body.fi-panel-portal .cxp-ai2-ratio-grid,
  body.fi-panel-portal .cxp-ai2-overview-grid,
  body.fi-panel-portal .cxp-ai2-trend-summaries,
  body.fi-panel-portal .cxp-ai2-split-counts,
  body.fi-panel-portal .cxp-ai2-pair-grid,
  body.fi-panel-portal .cxp-ai2-flex-grid {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  body.fi-panel-portal .cxp-ai2-kpi,
  body.fi-panel-portal .cxp-ai2-ratio-card { min-height: 0; padding: 16px; }
  body.fi-panel-portal .cxp-ai2-change-card,
  body.fi-panel-portal .cxp-ai2-overview-card { min-height: 0; padding: 16px; }
  body.fi-panel-portal .cxp-ai2-change-grid { padding: 16px; }
  body.fi-panel-portal .cxp-aivd-trend .kt-card-header { flex-direction: column; align-items: flex-start; }
  body.fi-panel-portal .cxp-ai2-overview-card:hover { transform: none; }
  body.fi-panel-portal .cxp-ai2-action-links { width: 100%; }
}

/* --------------------------------------------------------------------
   Responsive: 430px and below - phone-specific density fixes.
   -------------------------------------------------------------------- */
@media (max-width: 430px) {
  /* Punch list #3 (round 2B correction): the four opening dashboard/KPI
     tiles ran ~185px each, ~800px stacked before the trend card. Round 2
     hid the title entirely to compact the row, which round 2B's live pixels
     showed reads as "Not recorded / no earlier weekly check" with no idea
     which metric that is. Fixed with CSS GRID instead of hiding: column 1
     is empty above the ring (so the ring, nested inside kpi-core/ratio-main,
     reads as the left column), column 2 row 1 holds the re-shown title as an
     11px uppercase caption, and kpi-core/ratio-main (ring+number together)
     spans both columns in row 2 below it - ring left, title+number+sub-label
     stacked on the right. Row height grows to ~132px to fit the extra line;
     the explain sentence stays hidden (still said via subvalue/aria-label). */
  body.fi-panel-portal .cxp-ai2-kpi,
  body.fi-panel-portal .cxp-ai2-ratio-card {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: auto auto;
    column-gap: 14px;
    row-gap: 4px;
    align-items: center;
    min-height: 132px;
    padding: 14px 16px;
  }
  body.fi-panel-portal .cxp-ai2-kpi-label {
    display: block;
    grid-column: 2;
    grid-row: 1;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
    font-weight: 700;
    color: var(--muted-foreground);
  }
  body.fi-panel-portal .cxp-ai2-kpi > .cxp-ai2-explain,
  body.fi-panel-portal .cxp-ai2-ratio-card > .cxp-ai2-explain { display: none; }
  body.fi-panel-portal .cxp-ai2-kpi-core,
  body.fi-panel-portal .cxp-ai2-ratio-main {
    grid-column: 1 / -1;
    grid-row: 2;
  }

  /* Punch list #5: the four Changes cards ran ~207px on a phone against a
     175-190px target. Tighter internal gaps and a smaller delta circle
     bring the card down without losing the logo/ring/result/comparison/
     date grid. */
  body.fi-panel-portal .cxp-ai2-change-card { gap: 8px; }
  body.fi-panel-portal .cxp-ai2-change-card .cxp-ai2-change-main { gap: 10px; }
  body.fi-panel-portal .cxp-ai2-change-card .cxp-ai2-delta-visual {
    width: 52px;
    height: 52px;
    flex: 0 0 52px;
    font-size: 18px;
  }
  body.fi-panel-portal .cxp-ai2-change-card .cxp-ai2-delta-number { margin-bottom: 3px; }

  /* Score summary card stacks instead of forcing the copy column narrow.
     !important to win over the base rule's own !important (equal
     specificity, later in source wins - kept explicit so this never
     silently loses again). */
  body.fi-panel-portal .cxp-ai2-score-summary {
    flex-direction: column !important;
    align-items: flex-start !important;
    gap: 14px !important;
    padding: 16px !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-ai2-led { animation: none; }
  body.fi-panel-portal .cxp-ai2-overview-card { transition: none; }
}

/* --------------------------------------------------------------------
   ROUND 4 #1: ai-visibility detail tabs at <=760px. Six tabs used to WRAP
   into three uneven rows with "Weekly tracking" stranded alone and
   "Review changes" sitting outside the box. House rule: tabs stay
   horizontal, never a vertical stack - so this is a single scrollable
   rail, never a wrap. "Review changes" gets its own right-aligned row
   under the rail instead of sharing the rail's row.
   -------------------------------------------------------------------- */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-ai-visibility-page .cxp-ai-tabs-actions {
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 8px !important;
  }
  /* The tab strip itself is left to the house rules: pages.css pins
     `.cxp-map-tabs.cxp-map-tabs.cxp-map-tabs { flex-wrap: wrap !important }`, and the
     nowrap rail that used to live here pushed three tabs off the right edge at 390
     (measured right=700 on a 390 viewport). Only the actions row and the review link
     are styled here. */
  body.fi-panel-portal .cxp-ai-visibility-page .cxp-map-tabs [role="tab"] {
    min-height: 40px !important;
  }
  body.fi-panel-portal .cxp-ai-visibility-page #cxp-review-changes {
    align-self: flex-end !important;
    display: inline-flex !important;
  }
}

/* --------------------------------------------------------------------
   ROUND 4 #2: the score card's metadata line ("Checked ... . We asked the
   assistants ourselves . N questions asked of each assistant") uses a CSS
   separator between the <span> items (the house skin's own rule, not this
   file's) that breaks into a detached floating dot when the line wraps on
   a phone. Stacked to one item per line at <=430px and every possible
   separator mechanism (pseudo-element content, border, margin) is reset -
   desktop markup and desktop CSS are untouched.
   -------------------------------------------------------------------- */
@media (max-width: 430px) {
  body.fi-panel-portal .cxp-ai-visibility-page .cxp-ai-summary-meta {
    display: flex !important;
    flex-direction: column !important;
    align-items: flex-start !important;
    gap: 4px !important;
  }
  body.fi-panel-portal .cxp-ai-visibility-page .cxp-ai-summary-meta span {
    border: 0 !important;
    margin: 0 !important;
    padding-left: 0 !important;
  }
  body.fi-panel-portal .cxp-ai-visibility-page .cxp-ai-summary-meta span::before,
  body.fi-panel-portal .cxp-ai-visibility-page .cxp-ai-summary-meta span::after {
    content: none !important;
  }
}

/* --------------------------------------------------------------------
   ROUND 4 #3: dashboard first-check state - on a phone the mostly-empty
   plot appeared before the explanation. Both are direct children of the
   same grid (.cxp-ai2-empty-layout collapses to one column at <=980px,
   the same width the two-column layout already stacks at), so a plain
   `order` swap is enough - scoped with a child combinator so it can never
   touch .cxp-ai2-chart-stage where it appears elsewhere (e.g. the
   tracking-off preview chart, which is not inside this grid).
   -------------------------------------------------------------------- */
@media (max-width: 980px) {
  body.fi-panel-portal .cxp-ai2-empty-layout > .cxp-ai2-empty-note { order: 1; }
  body.fi-panel-portal .cxp-ai2-empty-layout > .cxp-ai2-chart-stage { order: 2; }
}

/* Coordinator hotfix: the skin's link colour still won on the tracking-off CTA (computed oklab 0.43 on the
   gradient = illegible). Out-specify it and force white text + fill. */
body.fi-panel-portal .fi-main a.cxp-ai2-tracking-off-action.kt-btn,
body.fi-panel-portal .fi-main a.cxp-ai2-tracking-off-action.kt-btn:visited,
body.fi-panel-portal .fi-main a.cxp-ai2-tracking-off-action.kt-btn:hover,
body.fi-panel-portal .fi-main .cxp-ai2-tracking-off-action {
  color: #fff !important;
  -webkit-text-fill-color: #fff !important;
  opacity: 1 !important;
  text-decoration: none !important;
}

/* Round 4 decision (coordinator): Sol asked for a horizontally scrolling tab rail at 390 because the six
   tabs wrap onto three uneven rows. NOT DONE, deliberately: pages.css pins
   `.cxp-map-tabs.cxp-map-tabs.cxp-map-tabs { flex-wrap: wrap !important }` at triple specificity, which is
   the enforcement of Adir's 2026-08-31 correction that these tabs stay horizontal and never become a rail
   (memory feedback_portal_tabs_always_horizontal). Every other portal page wraps identically at 390, so a
   per-page scroller would also break consistency. Tried even 2-up/3-up wrapping too; the house rules pin the
   children as well, so the strip is left exactly as the rest of the portal renders it. */

/* ===== END AI VISIBILITY V2 (cxp-ai2) ===== */

/* ===== SEARCH PERFORMANCE V2 (cxp-sp2) ===== */
/* ==========================================================================
   Search performance v2 - Google Analytics / Google Search Console /
   Search performance overview. Pixel-language matches
   ws2-ga-gsc/portal-data-pages-redesign.html (approved 2026-09-03) and
   reuses the Rankings v2 house language (rankings-v2.css) rather than
   inventing a second one.

   Every new selector here is scoped body.fi-panel-portal .cxp-sp2-... - none
   of it depends on being nested inside .cxp-rankings-page, so it works the
   same on the Search performance overview page (wrapped .cxp-searchperf-page)
   as it does on the two deep pages (wrapped .cxp-rankings-page, which is also
   how those two pages still pick up cxp-rk2-table/cxp-rk2-chip/cxp-rk2-band-*
   from rankings-v2.css where this file reuses those classes directly rather
   than redefining them).

   Reused, not redefined here: kt-card / kt-card-border / kt-card-table,
   cxp-motion-card (shared reveal binder), cxp-meta, cxp-hf-link,
   cxp-chart-toolbar + kt-btn kt-btn-sm kt-btn-primary/kt-btn-ghost,
   cxp-rk2-table (+ its mobile stacked-row pattern), cxp-rk2-chip
   --good/--bad/--neutral, cxp-gauge-track / cxp-gauge-progress (the shared
   ring-reveal + count-up motion binder recognises both by class name; see
   ai-dashboard.blade.php / stat-card.css for the r=32, circumference-201
   convention this file follows for every gauge).

   Append this file to the portal's compiled CSS the same way rankings-v2.css
   is appended - do not merge it into rankings-v2.css itself.
   ========================================================================== */

/* --------------------------------------------------------------------
   Context card (top of each of the two deep pages)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-context {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}
/* Punch-list item 3 (round 2): a single compact row - brand / window+source
   / actions - replacing the old 3-row-stacked cxp-sp2-context-copy that gave
   the card ~150px of centered content with airy top+bottom padding. */
body.fi-panel-portal .cxp-sp2-context-brand {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 9px;
  min-width: 0;
  color: var(--foreground);
  font-size: 15px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-sp2-context-domain {
  overflow: hidden;
  color: var(--foreground);
  font-weight: 650;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-sp2-context-info {
  display: grid;
  gap: 3px;
  min-width: 0;
  flex: 1 1 240px;
}
body.fi-panel-portal .cxp-sp2-context-window {
  color: var(--muted-foreground);
  font-size: 12.5px;
}
body.fi-panel-portal .cxp-sp2-context-source {
  color: var(--muted-foreground);
  font-size: 12px;
}
body.fi-panel-portal .cxp-sp2-context-source strong { color: #475467; font-weight: 650; }
body.fi-panel-portal .cxp-sp2-provider-icon {
  display: inline-grid;
  width: 24px;
  height: 24px;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--card);
  flex: none;
}
body.fi-panel-portal .cxp-sp2-provider-icon svg { width: 16px; height: 16px; }
body.fi-panel-portal .cxp-sp2-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 26px;
  padding: 0 9px;
  border: 1px solid #b7e3cb;
  border-radius: 999px;
  background: #e7f6ee;
  color: #04663a;
  font-size: 12px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-sp2-status-pill--warn {
  border-color: #f6d9a8;
  background: #fff4e5;
  color: #8a4b00;
}
body.fi-panel-portal .cxp-sp2-status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #16a36a;
  box-shadow: 0 0 0 4px rgba(22, 163, 106, .1);
  flex: none;
}
body.fi-panel-portal .cxp-sp2-status-pill--warn .cxp-sp2-status-dot {
  background: #b98a1c;
  box-shadow: 0 0 0 4px rgba(185, 138, 28, .1);
}
body.fi-panel-portal .cxp-sp2-context-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

/* Outline pill CTA ("Open in Google Analytics ->" etc). kt-btn's own
   variants are sized for toolbar pills (kt-btn-sm); this is the taller
   34px house outline button the mockup's context/deep-link cards use. */
body.fi-panel-portal .cxp-sp2-btn-outline {
  display: inline-flex;
  min-height: 34px;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 0 14px;
  border: 1px solid #d0d5dd;
  border-radius: 999px;
  background: var(--card);
  color: #344054;
  font-size: 12.5px;
  font-weight: 650;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-sp2-btn-outline:hover { text-decoration: none; border-color: #98a2b3; }

/* --------------------------------------------------------------------
   Source line - every card's "where this number comes from" footer
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-source-line {
  margin: 12px 0 0;
  color: var(--muted-foreground);
  font-size: 12px;
}
body.fi-panel-portal .cxp-sp2-source-line strong { color: #475467; font-weight: 650; }
body.fi-panel-portal .cxp-sp2-card-heading {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 16px;
}
body.fi-panel-portal .cxp-sp2-card-subtitle {
  margin: 4px 0 0;
  color: var(--muted-foreground);
  font-size: 12.5px;
}

/* --------------------------------------------------------------------
   Position filter chips (GSC "Position filters") - 6 across, never 7,
   never an orphan row. A separate grid from cxp-rk2-band-row (that one
   is sized for the Tracked keywords page's 7 filters).
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-band-row {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 10px;
  margin-top: 15px;
}
body.fi-panel-portal .cxp-sp2-band-chip {
  min-width: 0;
  min-height: 86px;
  padding: 12px;
  text-align: left;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
  color: var(--foreground);
}
body.fi-panel-portal .cxp-sp2-band-chip--active {
  border-color: #c7d2fe;
  background: color-mix(in oklab, var(--primary) 6%, var(--card));
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--primary) 18%, transparent);
}
body.fi-panel-portal .cxp-sp2-band-label {
  display: block;
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 650;
}
body.fi-panel-portal .cxp-sp2-band-value {
  display: block;
  margin-top: 4px;
  color: var(--foreground);
  font-size: 25px;
  line-height: 1;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-sp2-band-share {
  display: block;
  margin-top: 6px;
  color: var(--muted-foreground);
  font-size: 12px;
}

/* --------------------------------------------------------------------
   KPI tiles - 4 across x 2 rows (8 tiles), balanced, never 6+2.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
}
body.fi-panel-portal .cxp-sp2-kpi {
  min-height: 166px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
/* Punch-list item 6 (round 2): one shared footnote replacing the repeated
   grey "No earlier period to compare" chip on every tile with no real
   comparison yet. */
body.fi-panel-portal .cxp-sp2-kpi-footnote { margin: 10px 0 0; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-sp2-kpi-main { min-width: 0; }
body.fi-panel-portal .cxp-sp2-kpi-top {
  display: flex;
  min-height: 28px;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}
body.fi-panel-portal .cxp-sp2-kpi-label { color: var(--foreground); font-size: 13px; font-weight: 650; }
body.fi-panel-portal .cxp-sp2-metric-value {
  margin-top: 11px;
  color: var(--foreground);
  font-size: 30px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-sp2-metric-value--time { font-size: 28px; }
body.fi-panel-portal .cxp-sp2-delta {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  margin-top: 10px;
  padding: 3px 7px;
  border: 1px solid;
  border-radius: 999px;
  font-size: 11.5px;
  line-height: 1.25;
  font-weight: 650;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-sp2-delta--good { border-color: #b7e3cb; background: #e7f6ee; color: #04663a; }
body.fi-panel-portal .cxp-sp2-delta--bad { border-color: #f3b8b0; background: #fde8e8; color: #b42318; }
body.fi-panel-portal .cxp-sp2-delta--neutral { border-color: #dfe3e8; background: #f3f4f6; color: var(--muted-foreground); }
/* Punch-list item 1 (round 2): the rest of a delta sentence ("vs previous
   period") that no longer fits in the pill - a plain muted line underneath,
   never invented shorter wording. */
body.fi-panel-portal .cxp-sp2-delta-suffix { display: block; margin-top: 4px; color: var(--muted-foreground); font-size: 11px; }
body.fi-panel-portal .cxp-sp2-kpi-explain { margin: 10px 0 0; color: var(--muted-foreground); font-size: 12px; line-height: 1.4; }

/* Raw sparkline built from the SAME points already computed for the house
   trend chart (xPct/yPct 0-100 scale) - never a second, invented series. */
body.fi-panel-portal .cxp-sp2-spark { width: 80px; height: 24px; flex: none; overflow: visible; }
body.fi-panel-portal .cxp-sp2-spark polyline {
  fill: none;
  stroke: var(--primary);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Ring / gauge - r=32, circumference 201 (2*pi*32), same convention as
   ai-dashboard.blade.php / cxp-stat-card__ring so the shared motion binder's
   ring-reveal (it looks for .cxp-gauge-progress) animates these for free. */
body.fi-panel-portal .cxp-sp2-ring { position: relative; width: 86px; height: 86px; flex: none; }
body.fi-panel-portal .cxp-sp2-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
body.fi-panel-portal .cxp-sp2-ring circle { fill: none !important; stroke-width: 8; }
body.fi-panel-portal .cxp-sp2-ring .cxp-gauge-track { stroke: #e8ebf0 !important; }
body.fi-panel-portal .cxp-sp2-ring .cxp-gauge-progress { stroke: var(--primary) !important; stroke-linecap: round; }
body.fi-panel-portal .cxp-sp2-ring-secondary { stroke: #a5b4fc !important; stroke-linecap: butt; }
body.fi-panel-portal .cxp-sp2-ring-number {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--foreground);
  font-size: 19px;
  line-height: 1;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-sp2-ring--lg { width: 118px; height: 118px; }
body.fi-panel-portal .cxp-sp2-ring--lg .cxp-sp2-ring-number { font-size: 23px; }
body.fi-panel-portal .cxp-sp2-ring--sm { width: 72px; height: 72px; }
body.fi-panel-portal .cxp-sp2-ring--sm .cxp-sp2-ring-number { font-size: 15px; }
body.fi-panel-portal .cxp-sp2-ring-kpi { display: flex; align-items: center; gap: 15px; margin-top: 10px; }
body.fi-panel-portal .cxp-sp2-ring-kpi-copy { min-width: 0; }
body.fi-panel-portal .cxp-sp2-ring-kpi-copy .cxp-sp2-delta { margin-top: 0; }

/* --------------------------------------------------------------------
   Trend chart summary row (the house <x-portal.trend-chart> sits below)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-chart-summary {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 12px;
}
body.fi-panel-portal .cxp-sp2-chart-readout {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 8px;
}
body.fi-panel-portal .cxp-sp2-chart-readout strong { color: var(--foreground); font-size: 30px; line-height: 1; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-sp2-direction { color: #04663a; font-size: 12px; font-weight: 650; }

/* --------------------------------------------------------------------
   Device sections - GA "Sessions by device" (rings), GSC "Queries by
   device" (mini-stat comparison, no ring - see sol_brief.txt 2e).
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-device-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
body.fi-panel-portal .cxp-sp2-device-stat {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 14px;
  padding: 15px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2, #fafbfc);
}
body.fi-panel-portal .cxp-sp2-device-copy { min-width: 0; }
body.fi-panel-portal .cxp-sp2-device-name { color: var(--foreground); font-size: 13px; font-weight: 650; }
body.fi-panel-portal .cxp-sp2-device-count { margin-top: 4px; color: var(--foreground); font-size: 21px; line-height: 1.1; font-weight: 700; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-sp2-device-note { margin-top: 4px; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-sp2-device-compare { align-items: stretch; }
body.fi-panel-portal .cxp-sp2-device-compare .cxp-sp2-device-stat { display: block; padding: 16px; }
body.fi-panel-portal .cxp-sp2-mini-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 11px; margin-top: 14px; }
body.fi-panel-portal .cxp-sp2-mini-stat span { display: block; color: var(--muted-foreground); font-size: 11.5px; }
body.fi-panel-portal .cxp-sp2-mini-stat strong { display: block; margin-top: 2px; color: var(--foreground); font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------
   Two-column split rows (Traffic sources / New vs returning,
   Popular countries / Top cities, Top pages / By country)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-split { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; align-items: start; }

/* --------------------------------------------------------------------
   Share lists (countries / channels / cities / by-country)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-share-list { display: grid; gap: 11px; margin-top: 14px; }
body.fi-panel-portal .cxp-sp2-share-row { display: grid; grid-template-columns: minmax(120px, 1fr) minmax(120px, 1.3fr) 58px; align-items: center; gap: 12px; }
body.fi-panel-portal .cxp-sp2-share-label { min-width: 0; color: #344054; font-size: 12.5px; }
body.fi-panel-portal .cxp-sp2-share-label strong,
body.fi-panel-portal .cxp-sp2-share-label small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.fi-panel-portal .cxp-sp2-share-label strong { color: var(--foreground); font-weight: 600; }
body.fi-panel-portal .cxp-sp2-share-label small { margin-top: 1px; color: var(--muted-foreground); font-size: 11.5px; }
body.fi-panel-portal .cxp-sp2-bar-track { height: 7px; overflow: hidden; border-radius: 999px; background: #e5e7eb; }
body.fi-panel-portal .cxp-sp2-bar-fill { display: block; height: 100%; min-width: 2px; border-radius: inherit; background: var(--primary); }
body.fi-panel-portal .cxp-sp2-share-value { color: #475467; font-size: 12px; text-align: left; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------
   New vs returning visitor ring + legend
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-visitor-ring { display: flex; align-items: center; gap: 22px; min-height: 166px; }
body.fi-panel-portal .cxp-sp2-legend-list { display: grid; gap: 10px; min-width: 0; }
body.fi-panel-portal .cxp-sp2-legend-row { display: grid; grid-template-columns: 10px minmax(0, 1fr); gap: 9px; align-items: start; }
body.fi-panel-portal .cxp-sp2-legend-dot { width: 9px; height: 9px; margin-top: 5px; border-radius: 50%; background: var(--primary); }
body.fi-panel-portal .cxp-sp2-legend-dot--secondary { background: #a5b4fc; }
body.fi-panel-portal .cxp-sp2-legend-row strong { display: block; color: var(--foreground); font-size: 13px; }
body.fi-panel-portal .cxp-sp2-legend-row span { color: var(--muted-foreground); font-size: 12px; }

/* --------------------------------------------------------------------
   Tables (Top landing pages / Top pages) - the <table> itself uses the
   house cxp-rk2-table class (rankings-v2.css); only the "zero" and path
   cell helpers below are new.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-path {
  display: block;
  max-width: 100%;
  overflow: hidden;
  color: var(--primary);
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-sp2-zero { color: #98a2b3; font-weight: 650; }
body.fi-panel-portal .cxp-sp2-primary-cell { color: var(--foreground); font-weight: 600; }

/* --------------------------------------------------------------------
   Search visibility (GA) - ring + bucket strip
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-visibility-layout { display: grid; grid-template-columns: 210px minmax(0, 1fr); gap: 18px; align-items: stretch; }
body.fi-panel-portal .cxp-sp2-visibility-score {
  display: grid;
  place-items: center;
  min-height: 150px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2, #fafbfc);
  text-align: center;
}
body.fi-panel-portal .cxp-sp2-visibility-score p { margin: 8px 0 0; color: var(--muted-foreground); font-size: 11.5px; }
body.fi-panel-portal .cxp-sp2-bucket-row { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); overflow: hidden; border: 1px solid var(--border); border-radius: 10px; }
body.fi-panel-portal .cxp-sp2-bucket { min-width: 0; padding: 14px; }
body.fi-panel-portal .cxp-sp2-bucket + .cxp-sp2-bucket { border-left: 1px solid var(--border); }
body.fi-panel-portal .cxp-sp2-bucket span { display: block; color: var(--muted-foreground); font-size: 11.5px; }
body.fi-panel-portal .cxp-sp2-bucket strong { display: block; margin-top: 6px; color: var(--foreground); font-size: 23px; line-height: 1; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-sp2-bucket small { display: block; margin-top: 7px; color: var(--muted-foreground); }
body.fi-panel-portal .cxp-sp2-bucket-bar { height: 5px; margin-top: 9px; overflow: hidden; border-radius: 999px; background: #e5e7eb; }
body.fi-panel-portal .cxp-sp2-bucket-bar i { display: block; height: 100%; background: var(--primary); }

/* --------------------------------------------------------------------
   Deep-link rows (GA "Go deeper", GSC destination line)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-deep-links { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 15px; }

/* --------------------------------------------------------------------
   Overview page: Visitor snapshot strip + nav cards
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-visitor-strip { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) minmax(230px, 1.4fr); gap: 14px; }
body.fi-panel-portal .cxp-sp2-strip-stat { min-width: 0; padding: 15px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface-2, #fafbfc); }
body.fi-panel-portal .cxp-sp2-strip-stat .cxp-sp2-metric-value { margin-top: 8px; font-size: 25px; }
body.fi-panel-portal .cxp-sp2-strip-stat .cxp-sp2-ring { margin-top: 9px; }
body.fi-panel-portal .cxp-sp2-strip-device { display: grid; gap: 9px; }
body.fi-panel-portal .cxp-sp2-mini-bar-row { display: grid; grid-template-columns: 55px 1fr 39px; align-items: center; gap: 8px; color: var(--muted-foreground); font-size: 12px; }

body.fi-panel-portal .cxp-sp2-link-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px; }
body.fi-panel-portal .cxp-sp2-nav-card {
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) 26px;
  align-items: center;
  gap: 14px;
  color: inherit;
  transition: transform .18s ease, box-shadow .18s ease;
}
body.fi-panel-portal .cxp-sp2-nav-card:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(16, 24, 40, .08); text-decoration: none; }
body.fi-panel-portal .cxp-sp2-nav-icon { display: grid; width: 44px; height: 44px; place-items: center; border: 1px solid #dbe2ff; border-radius: 12px; background: #f4f5ff; }
body.fi-panel-portal .cxp-sp2-nav-icon svg { width: 24px; height: 24px; }
body.fi-panel-portal .cxp-sp2-nav-copy strong { display: block; color: var(--foreground); font-size: 15px; }
body.fi-panel-portal .cxp-sp2-nav-copy span { display: block; margin-top: 3px; color: var(--muted-foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-sp2-chevron { color: var(--primary); font-size: 22px; text-align: center; }

/* --------------------------------------------------------------------
   Designed empty state - a short line + a simple skeleton icon, never
   two lines of bare grey text.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-empty-state { text-align: center; }
body.fi-panel-portal .cxp-sp2-empty-art { display: grid; width: 112px; height: 74px; margin: 2px auto 14px; place-items: center; border: 1px solid #dbe2ea; border-radius: 12px; background: #f8fafc; }
body.fi-panel-portal .cxp-sp2-empty-skeleton { display: grid; width: 70px; gap: 7px; }
body.fi-panel-portal .cxp-sp2-empty-skeleton i { display: block; height: 7px; border-radius: 999px; background: #dfe4eb; }
body.fi-panel-portal .cxp-sp2-empty-skeleton i:nth-child(2) { width: 78%; background: #c7d2fe; }
body.fi-panel-portal .cxp-sp2-empty-state p { margin: 0; color: #475467; font-size: 13px; }
body.fi-panel-portal .cxp-sp2-empty-state .cxp-sp2-source-line { margin-top: 9px; }

/* ==========================================================================
   Responsive
   ========================================================================== */

/* Tablet, 768-820px: KPI grids fold 4x2 -> 2x4, device grids stay 3-across
   (only 3 devices exist, folding further would waste width). */
@media (min-width: 768px) and (max-width: 820px) {
  body.fi-panel-portal .cxp-sp2-kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-sp2-context { align-items: flex-start; flex-direction: column; }
  body.fi-panel-portal .cxp-sp2-context-actions { justify-content: flex-start; }
}

@media (max-width: 1000px) {
  body.fi-panel-portal .cxp-sp2-split,
  body.fi-panel-portal .cxp-sp2-link-grid { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-sp2-visibility-layout { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-sp2-visitor-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-sp2-band-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* The page never scrolls sideways below this width - grids collapse to one
   column and any strip that must scroll does so INSIDE its own card. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-sp2-context,
  body.fi-panel-portal .cxp-sp2-chart-summary,
  body.fi-panel-portal .cxp-sp2-card-heading {
    align-items: flex-start;
    flex-direction: column;
  }
  body.fi-panel-portal .cxp-sp2-context-actions { width: 100%; justify-content: flex-start; }
  body.fi-panel-portal .cxp-sp2-context-actions .cxp-sp2-btn-outline,
  body.fi-panel-portal .cxp-sp2-context-actions .cxp-hf-link { min-height: 40px; display: inline-flex; align-items: center; }
  /* Punch-list item 9 (round 2): this used to switch the band row into a
     horizontally-scrolling flex strip, which overflowed past the viewport
     at 390px ("Top 4-" clipped at the edge). The page never scrolls
     sideways below 760px (see comment above) - a 3-column grid instead,
     folding to 2 columns at 430px below. */
  body.fi-panel-portal .cxp-sp2-band-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-sp2-kpi-grid,
  body.fi-panel-portal .cxp-sp2-device-grid,
  body.fi-panel-portal .cxp-sp2-visitor-strip,
  body.fi-panel-portal .cxp-sp2-link-grid {
    grid-template-columns: 1fr;
  }
  body.fi-panel-portal .cxp-sp2-kpi { min-height: 148px; }
  body.fi-panel-portal .cxp-sp2-share-row { grid-template-columns: minmax(100px, 1fr) minmax(80px, 1fr) 48px; gap: 8px; }
  body.fi-panel-portal .cxp-sp2-visitor-ring { align-items: flex-start; flex-direction: column; }
  body.fi-panel-portal .cxp-sp2-bucket-row { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-sp2-bucket + .cxp-sp2-bucket { border-left: 0; border-top: 1px solid var(--border); }
  body.fi-panel-portal .cxp-sp2-nav-card { grid-template-columns: 40px minmax(0, 1fr) 20px; }
}

@media (max-width: 430px) {
  body.fi-panel-portal .cxp-sp2-card-heading { align-items: flex-start; flex-direction: column; }
  body.fi-panel-portal .cxp-sp2-ring-kpi { align-items: flex-start; }
  body.fi-panel-portal .cxp-sp2-visitor-ring { flex-direction: column; }
  body.fi-panel-portal .cxp-sp2-share-row { grid-template-columns: minmax(92px, 1fr) minmax(70px, 1fr) 44px; }
  body.fi-panel-portal .cxp-sp2-mini-stats { grid-template-columns: 1fr 1fr; }
  /* Punch-list item 9 (round 2): 6 chips at 3-across still gets a lopsided
     last row on a 390px phone - 2-across reads cleaner and every label
     ("Top 4-10") has room. */
  body.fi-panel-portal .cxp-sp2-band-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Punch-list item 10 (round 2): 8 tiles stacked full-width (~130px each)
     read as a long, repetitive scroll. 2-across + a denser tile (smaller
     value, chip kept, meaning line dropped - the sentence already repeats
     across the source line and the card subtitle) matches the approved
     Rankings v2 mobile tile density. */
  body.fi-panel-portal .cxp-sp2-kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-sp2-kpi { min-height: 132px; padding: 14px; }
  body.fi-panel-portal .cxp-sp2-kpi .cxp-sp2-metric-value { margin-top: 8px; font-size: 22px; }
  /* Scoped to tiles inside the KPI grid only - .cxp-sp2-kpi-explain is a
     shared class, also used by the Visitor snapshot strip on the Search
     performance overview page, which must keep its meaning lines. */
  body.fi-panel-portal .cxp-sp2-kpi .cxp-sp2-kpi-explain { display: none; }
  body.fi-panel-portal .cxp-sp2-kpi .cxp-sp2-delta { margin-top: 6px; padding: 2px 6px; font-size: 10.5px; }
  body.fi-panel-portal .cxp-sp2-kpi .cxp-sp2-delta-suffix { font-size: 10px; }
  body.fi-panel-portal .cxp-sp2-kpi .cxp-sp2-ring-kpi { gap: 10px; }
  body.fi-panel-portal .cxp-sp2-kpi .cxp-sp2-ring { width: 66px; height: 66px; }
  body.fi-panel-portal .cxp-sp2-kpi .cxp-sp2-ring-number { font-size: 15px; }
}

@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-sp2-nav-card { transition: none !important; }
}

/* Round 2 hotfix (coordinator): the context card sits on .kt-card, which the skin makes a
   COLUMN flexbox - together with justify-content:space-between that spread the three children
   over a ~330px tall card on both GA and GSC. Force the row layout and kill any inherited height. */
body.fi-panel-portal .kt-card.cxp-sp2-context {
  flex-direction: row !important;
  flex-wrap: wrap !important;
  align-items: center !important;
  align-content: center !important;
  justify-content: space-between !important;
  min-height: 0 !important;
  height: auto !important;
  padding: 14px 20px !important;
  gap: 12px 20px !important;
}
body.fi-panel-portal .kt-card.cxp-sp2-context > * { flex: 0 1 auto; }
body.fi-panel-portal .kt-card.cxp-sp2-context > .cxp-sp2-context-info { flex: 1 1 240px; }
@media (max-width: 760px) {
  body.fi-panel-portal .kt-card.cxp-sp2-context { flex-direction: column !important; align-items: flex-start !important; padding: 14px 16px !important; }
}
/* delta chips and their suffix were 10-10.5px - 11px floor */
body.fi-panel-portal .cxp-sp2-delta { font-size: 11px !important; }
body.fi-panel-portal .cxp-sp2-delta-suffix { font-size: 11px !important; }
/* mobile: in column direction the info block's flex-basis (240px) became HEIGHT - reset it */
@media (max-width: 760px) {
  body.fi-panel-portal .kt-card.cxp-sp2-context > .cxp-sp2-context-info,
  body.fi-panel-portal .kt-card.cxp-sp2-context > .cxp-sp2-context-brand,
  body.fi-panel-portal .kt-card.cxp-sp2-context > .cxp-sp2-context-actions { flex: 0 0 auto !important; width: 100%; }
  body.fi-panel-portal .kt-card.cxp-sp2-context { gap: 10px !important; }
}

/* ==========================================================================
   ROUND 3 additions (coordinator brief + GPT-5.6 Sol second pass)
   ========================================================================== */

/* --------------------------------------------------------------------
   Item 1: scope chips - which population (145 raw GSC queries / 53 SE
   Ranking tracked terms / every query for the whole property) a section's
   numbers cover. Labels only - never changes a number.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-scope-chip {
  display: inline-flex;
  align-items: center;
  margin: 6px 0 0;
  padding: 2px 9px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface-2, #f2f4f8);
  color: var(--muted-foreground);
  font-size: 12px;
  font-weight: 650;
  line-height: 1.4;
  white-space: nowrap;
}
/* Round 4 item 5: this modifier stacks the chip under a plain kt-card
   title/label (rather than under a full subtitle line) and lets it wrap -
   the base chip's white-space:nowrap is right for a chip sitting next to
   spare room, wrong for a half-width KPI tile at 390 where it clipped the
   right edge. */
body.fi-panel-portal .cxp-sp2-scope-chip--wrap {
  display: block;
  width: fit-content;
  max-width: 100%;
  margin-top: 6px;
  white-space: normal;
}
body.fi-panel-portal .cxp-sp2-kpi-scope { display: flex; justify-content: flex-start; margin: 0 0 10px; }

/* --------------------------------------------------------------------
   Item 3 (Search performance overview): "vs yesterday's report" / "vs the
   <date> report" line under a stat tile's delta chip - the chip alone never
   states what it's compared against.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-stat-compare {
  display: block;
  margin-top: 4px;
  color: var(--muted-foreground);
  font-size: 12px;
  line-height: 1.4;
}

/* --------------------------------------------------------------------
   Item 2 (GA context card): one shared "Report refreshed <time>" /
   "Refreshed on a schedule" line, same weight as the existing source line.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-context-refreshed {
  color: var(--muted-foreground);
  font-size: 12px;
  line-height: 1.4;
}

/* --------------------------------------------------------------------
   Item 4: compact 2-line mobile rows for GSC's Top pages + Search terms
   tables, and the Search performance overview's own Search terms table -
   replacing the default cxp-rk2-table/kt-table per-field stacked card
   (5-7 lines each) with one row: identity on line 1, small chips on line 2.
   Hidden by default; only shown under 760px on a wrapper marked
   .cxp-sp2-has-compact, which simultaneously hides that wrapper's <table> -
   the two never render at once. First N rows show always; the rest sit in
   a native <details> (no JS framework).
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-compact-list { display: none; }
body.fi-panel-portal .cxp-sp2-compact-row { padding: 12px 0; border-top: 1px solid var(--border); }
body.fi-panel-portal .cxp-sp2-compact-list > .cxp-sp2-compact-row:first-child,
body.fi-panel-portal .cxp-sp2-compact-more > .cxp-sp2-compact-row:first-of-type { border-top: 0; }
body.fi-panel-portal .cxp-sp2-compact-primary { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
body.fi-panel-portal .cxp-sp2-compact-primary strong {
  overflow: hidden;
  color: var(--foreground);
  font-size: 13px;
  font-weight: 650;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-sp2-compact-primary a,
body.fi-panel-portal .cxp-sp2-compact-primary span {
  overflow: hidden;
  color: var(--primary);
  font-size: 12px;
  line-height: 1.4;
  text-decoration: none;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.fi-panel-portal .cxp-sp2-compact-chips { display: flex; flex-wrap: wrap; gap: 6px 12px; margin-top: 7px; }
body.fi-panel-portal .cxp-sp2-compact-chip { color: var(--muted-foreground); font-size: 12px; line-height: 1.4; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-sp2-compact-chip strong { color: var(--foreground); font-weight: 650; }
body.fi-panel-portal .cxp-sp2-compact-more { margin-top: 4px; }
body.fi-panel-portal .cxp-sp2-compact-more summary {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 10px 0 4px;
  color: var(--primary);
  font-size: 12.5px;
  font-weight: 650;
  list-style: none;
  cursor: pointer;
}
body.fi-panel-portal .cxp-sp2-compact-more summary::-webkit-details-marker { display: none; }
body.fi-panel-portal .cxp-sp2-compact-more summary::marker { content: ''; }

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-sp2-has-compact table,
  body.fi-panel-portal .cxp-sp2-has-compact > input.cxp-rows-toggle,
  body.fi-panel-portal .cxp-sp2-has-compact > label.cxp-rows-more {
    display: none;
  }
  body.fi-panel-portal .cxp-sp2-has-compact .cxp-sp2-compact-list {
    display: block;
  }
}

/* --------------------------------------------------------------------
   Item 5 (GA + GSC at 390): supporting text was reading marginal/crowded
   against card edges. Floor at 12px/1.4 line-height and give every card
   that holds one at least 16px internal padding. Not the .cxp-sp2-context
   card (coordinator hotfix above already sets its own tested 14px/16px) and
   not .cxp-sp2-delta/-delta-suffix (coordinator hotfix above already set an
   11px floor for those specifically) - both left as already fixed.
   -------------------------------------------------------------------- */
@media (max-width: 430px) {
  /* Already >=12px - confirm the 1.4 line-height, no size change needed. */
  body.fi-panel-portal .cxp-sp2-source-line,
  body.fi-panel-portal .cxp-sp2-card-subtitle,
  body.fi-panel-portal .cxp-sp2-band-share,
  body.fi-panel-portal .cxp-sp2-kpi-footnote,
  body.fi-panel-portal .cxp-sp2-kpi-explain {
    line-height: 1.4;
  }
  /* These were 11.5px - genuinely under floor. */
  body.fi-panel-portal .cxp-sp2-share-label small,
  body.fi-panel-portal .cxp-sp2-mini-stat span,
  body.fi-panel-portal .cxp-sp2-bucket span,
  body.fi-panel-portal .cxp-sp2-bucket small,
  body.fi-panel-portal .cxp-sp2-visibility-score p {
    font-size: 12px;
    line-height: 1.4;
  }
  body.fi-panel-portal .cxp-sp2-band-chip { padding: 16px; }
  body.fi-panel-portal .cxp-sp2-kpi { padding: 16px; }
  body.fi-panel-portal .cxp-sp2-device-stat { padding: 16px; }
  body.fi-panel-portal .cxp-sp2-visibility-score { padding: 16px; }
  body.fi-panel-portal .cxp-sp2-bucket { padding: 16px; }
}

/* Coordinator fix (round 3): on the GSC page the tables live inside .cxp-rk2-table-wrap, whose
   Rankings-v2 mobile rules set the table to display:block !important - so the hide rule above lost and
   BOTH the stacked cards and the compact list rendered (page grew to 10,600px at 390). */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-sp2-has-compact table,
  body.fi-panel-portal .cxp-sp2-has-compact table thead,
  body.fi-panel-portal .cxp-sp2-has-compact table tbody,
  body.fi-panel-portal .cxp-sp2-has-compact table tr,
  body.fi-panel-portal .cxp-sp2-has-compact table td,
  body.fi-panel-portal .cxp-sp2-has-compact table th { display: none !important; }
  body.fi-panel-portal .cxp-sp2-has-compact .cxp-sp2-compact-list { display: block !important; }
}

/* ==========================================================================
   ROUND 4 additions (GPT-5.6 Sol pass 3)
   ========================================================================== */

/* --------------------------------------------------------------------
   Item 5: the Top 10 share tile's scope-chip override, stacked under the
   label and allowed to wrap - see google-search-console.blade.php.
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-sp2-scope-chip--wrap {
  display: block;
  width: fit-content;
  max-width: 100%;
  margin-top: 6px;
  white-space: normal;
}

/* --------------------------------------------------------------------
   Item 6: GSC "Search results over time" + the overview "How this changed
   over the period" chart both have a 4-button metric toolbar that wrapped
   3+1 at 390, orphaning "Average position" alone on its own row. Additive
   marker class (cxp-chart-toolbar is shared with the untouched period-pills
   toolbar, which must stay exactly as it is) - 2x2 grid under 430px, same
   wire:click/@click bindings, unchanged.
   -------------------------------------------------------------------- */
@media (max-width: 430px) {
  body.fi-panel-portal .cxp-sp2-metric-toolbar {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }
  body.fi-panel-portal .cxp-sp2-metric-toolbar .kt-btn {
    width: 100%;
    justify-content: center;
  }
}

/* --------------------------------------------------------------------
   Item 7: the overview's 4 headline KPI cards (Clicks/Impressions/Click
   rate/Average position) were full-width blocks at 390 - ~2 screens of
   scroll. cxp-searchperf-stat-grid is already a page-specific marker
   class alongside the shared cxp-rankings-stat-grid/cxp-shared-stat-grid
   (rankings-v2.css, not touched here) - scoping through it keeps every
   other page using those shared classes untouched. Delta chip and the
   round-3 "vs yesterday's report" line are untouched; only the long
   caption sentence shrinks to a secondary line.
   -------------------------------------------------------------------- */
@media (max-width: 430px) {
  body.fi-panel-portal .cxp-searchperf-stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  body.fi-panel-portal .cxp-searchperf-stat-grid .cxp-rankings-stat-caption {
    font-size: 12px;
    line-height: 1.35;
  }
}

/* Coordinator fix (round 4): the overview KPI grid stayed one column at 390 - the shared rule
   `.cxp-searchperf-stat-grid.cxp-shared-stat-grid { grid-template-columns: 1fr }` at <=620px carries one
   more class than the round-4 rule, so it won. Match its specificity for the 2x2 mobile grid. */
@media (max-width: 620px) {
  body.fi-panel-portal .cxp-searchperf-stat-grid.cxp-shared-stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
}

/* Round 4 (coordinator): the search-terms count line must tell the truth at each width - desktop renders
   the whole stored table, mobile renders 10 rows behind a "Show all" disclosure. Two spans, one shown. */
body.fi-panel-portal .cxp-sp2-count-narrow { display: none; }
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-sp2-count-wide { display: none; }
  body.fi-panel-portal .cxp-sp2-count-narrow { display: inline; }
}

/* 2026-09-04, Adir: "Google Search Console the page design is all broken!" - the
   scope chips were rendering as full-width grey bars across the page (measured
   1438px wide at a 1920 viewport, 1361px at 1675). The rule above asks for
   inline-flex but the COMPUTED value was `flex`, so something later in the
   cascade re-blocks it; rather than let that keep winning, pin the chip to its
   own content here. A chip is only ever as wide as its label. */
body.fi-panel-portal .cxp-sp2-scope-chip,
body.fi-panel-portal .cxp-sp2-scope-chip--wrap {
  display: inline-flex !important;
  width: fit-content !important;
  max-width: 100%;
  align-self: flex-start;
}

/* UI sweep 2026-09-04 (Adir's standing "boxes touching the borders" class).
   Measured on client 6 at 1440: `.cxp-sp2-kpi` computes padding 0 and its first
   text ("Users") lands +1px from the card's own border, on a page where every
   other card sits its text at +22px. Same story for the one `.cxp-rankings-card`
   on Search performance - "Visitor snapshot" at +1px, which is the flush header
   the design review flagged and the earlier pass did not actually cure.
   Smallest fix that removes it: give the two boxes the page's own inset. The
   rankings card exists on this page only (checked /rankings and
   /rankings/keywords - zero instances), so nothing else can double-pad. */
body.fi-panel-portal .cxp-sp2-kpi {
  padding: 16px 18px;
}

body.fi-panel-portal .cxp-searchperf-page .cxp-rankings-card > .cxp-sp2-card-heading,
body.fi-panel-portal .cxp-rankings-card > .cxp-sp2-card-heading {
  padding: 18px 20px 0;
}

/* ===== END SEARCH PERFORMANCE V2 (cxp-sp2) ===== */

/* ===== STAT CARD V2 (cxp-kpi2) ===== */
/* ==========================================================================
   stat-card-v2.css - Cyticx Portal "compact stat card" (CYT-kpi2)

   Appended AFTER the existing global stat-card CSS block. Every selector is
   rooted at body.fi-panel-portal .cxp-stat-card.cxp-kpi2 (the component's own
   markup now always carries .cxp-kpi2 on the root <article>), so it never
   leaks onto anything else and only ever styles this component's own markup.

   Geometry properties that page-level overrides fight over (min-height,
   height, padding, grid-template-rows, display, align-content, gap) carry
   !important here. Where a page-level rule ALSO carries !important on
   min-height (the CYT-814 gbp-stat-grid 128px floor, the ai-delta-card 152px
   floor, CYT-647's coordinator-summary), that value only ever acts as a
   FLOOR under this card's own natural (larger) height, so it is left to win
   on specificity without a fight - it does not visually clip anything.
   Only true property fights (display, padding, height:<fixed>) get the
   !important here, which beats those rules because none of them flag
   !important on that specific property.
   ========================================================================== */

body.fi-panel-portal .cxp-stat-card.cxp-kpi2 {
  --cxp-kpi2-ink: #1d2333;
  --cxp-kpi2-muted: #6b7280;
  --cxp-kpi2-border: #e5e7eb;
  --cxp-kpi2-good: #04663a;
  --cxp-kpi2-good-bg: #e7f6ee;
  --cxp-kpi2-good-border: #b7e3cb;
  --cxp-kpi2-bad: #b42318;
  --cxp-kpi2-bad-bg: #fde8e8;
  --cxp-kpi2-bad-border: #f3b8b0;
  --cxp-kpi2-neutral: #6b7280;
  --cxp-kpi2-neutral-bg: #f3f4f6;
  --cxp-kpi2-neutral-border: #e2e4e8;
  --cxp-kpi2-shadow: 0 1px 2px rgba(16, 24, 40, .04);

  box-sizing: border-box !important;
  display: flex !important;
  flex-direction: column !important;
  min-width: 0 !important;
  min-height: 154px !important;
  height: auto !important;
  grid-template-rows: none !important;
  align-content: normal !important;
  gap: 0 !important;
  padding: 18px 20px !important;
  overflow: visible !important;
  border: 1px solid var(--cxp-kpi2-border) !important;
  border-radius: 12px !important;
  background: #fff !important;
  box-shadow: var(--cxp-kpi2-shadow) !important;
  color: var(--cxp-kpi2-ink) !important;
}

body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact {
  min-height: 96px !important;
  height: auto !important;
  padding: 14px 16px !important;
}

/* ---- title row (label + optional logo/icon/LED + explainer trigger) ---- */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__title-row {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  gap: 10px !important;
  min-height: 0 !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__title {
  min-width: 0;
  font-size: 13px !important;
  font-weight: 650 !important;
  line-height: 1.3 !important;
  color: var(--cxp-kpi2-ink) !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__title {
  font-size: 12px !important;
}

/* ---- value zone: normal top-to-bottom flow, never centered ---- */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__value-zone {
  display: block !important;
  place-items: normal !important;
  text-align: left !important;
  min-width: 0 !important;
  min-height: 0 !important;
  align-self: auto !important;
  margin-top: 13px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__value-zone {
  margin-top: 12px !important;
}

/* ---- metric line: value(+unit) + change chip, side by side ---- */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-metric-line {
  display: flex !important;
  min-width: 0;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__value,
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-score-number {
  font-size: 31px !important;
  font-weight: 700 !important;
  line-height: 1 !important;
  letter-spacing: -.035em !important;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--cxp-kpi2-ink) !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__value,
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-score-number {
  font-size: 25px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__value--text {
  font-size: 20px !important;
  white-space: normal;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__value--empty {
  font-size: 18px !important;
  font-weight: 700 !important;
  letter-spacing: -.02em !important;
  color: #475467 !important;
  white-space: normal;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__value--empty {
  font-size: 14px !important;
}

/* inline unit word ("sessions", "clicks") right after the count/position number */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-unit-inline {
  margin-left: 4px !important;
  color: #364152 !important;
  font-size: 12px !important;
  font-weight: 650 !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-kpi2-unit-inline {
  display: none !important;
}

body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-score-unit {
  margin-left: 3px !important;
  color: var(--cxp-kpi2-muted) !important;
  font-size: 13px !important;
  font-weight: 650 !important;
}

/* ---- change chip pill ---- */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-chip {
  display: inline-flex !important;
  flex: 0 0 auto;
  align-items: center !important;
  min-height: 23px !important;
  padding: 2px 7px !important;
  border: 1px solid !important;
  border-radius: 999px !important;
  font-size: 11px !important;
  font-weight: 700 !important;
  line-height: 1 !important;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-kpi2-chip {
  min-height: 21px !important;
  padding: 2px 6px !important;
  font-size: 10px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-chip--good {
  color: var(--cxp-kpi2-good) !important;
  border-color: var(--cxp-kpi2-good-border) !important;
  background: var(--cxp-kpi2-good-bg) !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-chip--bad {
  color: var(--cxp-kpi2-bad) !important;
  border-color: var(--cxp-kpi2-bad-border) !important;
  background: var(--cxp-kpi2-bad-bg) !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-chip--neutral {
  color: var(--cxp-kpi2-neutral) !important;
  border-color: var(--cxp-kpi2-neutral-border) !important;
  background: var(--cxp-kpi2-neutral-bg) !important;
}

/* ---- ring layout: house gauge LEFT of the number, not wrapping it ---- */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-ring-layout {
  display: grid !important;
  grid-template-columns: 60px minmax(0, 1fr) !important;
  align-items: center !important;
  gap: 12px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-kpi2-ring-layout {
  grid-template-columns: 48px minmax(0, 1fr) !important;
  gap: 10px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-ring-copy {
  min-width: 0;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-score-line {
  display: flex !important;
  align-items: center !important;
  flex-wrap: wrap;
  gap: 7px !important;
  min-width: 0;
}

body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__ring {
  position: relative !important;
  width: 60px !important;
  height: 60px !important;
  flex: 0 0 auto !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__ring {
  width: 44px !important;
  height: 44px !important;
}
/* 2026-09-04. This block used to force the component's <svg> ring visible with
   `display: block !important`, overriding cxs-skin.css:1203 which hides it on
   purpose - the skin paints the gauge itself as a conic-gradient on the ring
   element (`--cxs-a` -> `--cxs-a2`, driven by `--cxs-p`). With both visible the
   customer saw TWO arcs in two different colours, which is what Adir reported:
   "it's showing like two gauges in one... this is not our design". The skin's
   ring is the approved one - the same violet-to-pink arc the Website health
   page shows - so the SVG stays hidden and only the geometry below is ours. */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__ring > svg {
  display: none !important;
  transform: rotate(-90deg) !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__ring circle {
  fill: none !important;
  stroke-width: 8 !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__ring .cxp-gauge-track {
  stroke: var(--cxp-kpi2-border) !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__ring .cxp-gauge-progress {
  stroke-linecap: round !important;
}

/* ---- delta variant (identity row already uses .cxp-stat-card__title-row) ---- */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__delta-value {
  font-size: 31px !important;
  font-weight: 700 !important;
  line-height: 1 !important;
  font-variant-numeric: tabular-nums;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__delta-value {
  font-size: 25px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-delta-positive {
  color: var(--cxp-kpi2-good) !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-delta-negative {
  color: var(--cxp-kpi2-bad) !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-no-change {
  display: inline-flex !important;
  align-items: center !important;
  min-height: 34px !important;
  padding: 0 11px !important;
  border: 1px solid var(--cxp-kpi2-neutral-border) !important;
  border-radius: 8px !important;
  color: var(--cxp-kpi2-neutral) !important;
  background: var(--cxp-kpi2-neutral-bg) !important;
  font-size: 20px !important;
  font-weight: 700 !important;
  letter-spacing: -.02em !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-kpi2-no-change {
  min-height: 28px !important;
  font-size: 16px !important;
}

/* ---- empty / not-measured variant ---- */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-empty-layout {
  display: grid !important;
  grid-template-columns: 58px minmax(0, 1fr) !important;
  align-items: center !important;
  gap: 13px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-kpi2-empty-layout {
  grid-template-columns: 40px minmax(0, 1fr) !important;
  gap: 10px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-kpi2-empty-ring {
  width: 58px !important;
  height: 58px !important;
  display: grid !important;
  place-items: center !important;
  flex: 0 0 auto !important;
  border: 3px dashed #cbd5e1 !important;
  border-radius: 50% !important;
  color: #98a2b3 !important;
  background: #fafbfc !important;
  font-size: 20px !important;
  font-weight: 700 !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-kpi2-empty-ring {
  width: 40px !important;
  height: 40px !important;
  font-size: 15px !important;
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__empty-action {
  margin-top: 10px !important;
  align-self: flex-start !important;
}

/* ---- verdict word + meaning line ---- */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__verdict {
  margin-top: 5px !important;
  min-height: 0 !important;
  font-size: 12px !important;
  font-weight: 700 !important;
  line-height: 1.3 !important;
  white-space: normal !important;
  overflow: visible !important;
  text-overflow: clip !important;
  /* No !important on color: a .cxp-score-<tone> class on this same element
     (site-wide gauge/verdict tone colouring, kept per the brief) must still
     be free to win the colour. Only when no tone class is present does this
     muted fallback apply. */
  color: var(--cxp-kpi2-muted);
}
body.fi-panel-portal .cxp-stat-card.cxp-kpi2 .cxp-stat-card__meta {
  margin-top: 9px !important;
  min-width: 0;
  overflow: hidden !important;
  color: var(--cxp-kpi2-muted) !important;
  font-size: 12px !important;
  font-weight: 400 !important;
  line-height: 1.35 !important;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}

/* Ring variants: indent the verdict/meaning lines so they align under the
   ring's copy column instead of the ring itself (60px ring + 12px gap). */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--ring .cxp-stat-card__verdict,
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--ring .cxp-stat-card__meta {
  margin-left: 72px !important;
}

/* Compact density: label + number + chip only - no dead white below them. */
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__verdict,
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__meta,
body.fi-panel-portal .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-stat-card__unit {
  display: none !important;
}

@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-stat-card.cxp-kpi2,
  body.fi-panel-portal .cxp-stat-card.cxp-kpi2 * {
    transition: none !important;
    animation: none !important;
  }
}



/* Sol pass 2, home dashboard "Department results": six narrow tiles cramp the v2 ring-left layout at
   laptop widths. Balanced 3 x 2 between 901px and 1700px; the existing 2-col / 1-col rules below 900 stay. */
@media (min-width: 901px) and (max-width: 1700px) {
  body.fi-panel-portal .cxp-scoreboard-grid { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; }
}


/* ===== END STAT CARD V2 (cxp-kpi2) ===== */

/* ===== WHAT AI SAID V2 (cxp-aiv-chat) ===== */
/* ============================================================================
   BRIEF B - "What AI said" chat-transcript redesign (ASK-DE4A23)
   Spliced into public/css/metronic/cxp.css. Scopes to the same
   `body.fi-panel-portal .cxp-ai-responses-page` prefix the page's own inline
   <style> block already uses, so nothing here can leak onto another portal
   page. Every selector below targets a class introduced by this brief - none
   of them exist elsewhere in cxp.css, so there is no existing rule being
   overridden or beaten on specificity.

   What changed in the markup (ai-responses.blade.php) that this CSS answers:
   - Answers are now grouped into "threads" by question: one `.aiv-question`
     (kept, existing rule) styled as the outgoing turn via the new
     `.aiv-question--ask` modifier, followed by every assistant's reply for
     that question inside `.aiv-thread-replies`.
   - Each reply's existing `.aiv-answer-head` row now opens with a new
     `.aiv-avatar` (the assistant's own logo, already used on the AI
     visibility tab - reused here, not invented) ahead of the untouched
     `.aiv-assistant` name and verdict chip.
   ============================================================================ */

/* One question-and-replies exchange. Column flex so the ask bubble and the
   reply stack sit on top of one another with a consistent gap; threads
   themselves are spaced by the unchanged `.aiv-answer-list` gap. */
body.fi-panel-portal .cxp-ai-responses-page .aiv-thread {
  display: flex;
  flex-direction: column;
  gap: .55rem;
}

/* The outgoing turn. Deliberately not right-aligned: a long question at
   1920px would sit in a lot of empty space on one side, and at 390px a
   right-aligned block is indistinguishable from a left-aligned one anyway.
   "Clearly ours" comes from the tint + accent border + the existing
   "We asked" kicker instead, which reads the same at every width. */
body.fi-panel-portal .cxp-ai-responses-page .aiv-question--ask {
  margin: 0;
  padding: .65rem .8rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: .2rem .625rem .625rem .625rem;
  background: var(--card);
}

/* The incoming replies for this question. The existing `.aiv-answer` cards
   already carry their own border/background (the "theirs" side of the
   exchange); this just stacks them and nudges them in slightly so the
   thread reads top-to-bottom as question -> replies rather than two
   unrelated lists. */
body.fi-panel-portal .cxp-ai-responses-page .aiv-thread-replies {
  display: flex;
  flex-direction: column;
  gap: .55rem;
  padding-left: .35rem;
  border-left: 1px dashed var(--border);
}

/* Assistant avatar - the same square logo tile used on the AI visibility tab
   (.cxp-ai2-logo-tile there), reintroduced here under this page's own class
   so this brief's CSS stays self-contained in one spliced block. */
body.fi-panel-portal .cxp-ai-responses-page .aiv-avatar {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card);
  overflow: hidden;
}
body.fi-panel-portal .cxp-ai-responses-page .aiv-avatar img {
  width: 20px;
  height: 20px;
  display: block;
}
/* Two-letter fallback for an assistant with no logo file on disk. 12px is the
   house type floor - never smaller, even on a small tile like this one. */
body.fi-panel-portal .cxp-ai-responses-page .aiv-avatar-fallback {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--muted-foreground);
}

/* Mobile (matches the page's existing 640px content breakpoint below):
   avatar + name stay on their own line, the verdict chip drops to a line of
   its own rather than fighting the name for space in a 390px-wide head row.
   `.aiv-answer-head` is already `display:flex; flex-wrap:wrap` (existing
   rule), so a flex-basis of 100% on the chip is enough to force the wrap -
   no other property on that existing rule needs to change. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-ai-responses-page .aiv-answer-head .cxp-ai-cell {
    flex-basis: 100%;
    margin-top: .15rem;
  }
  /* The thread's own left rail is one indent too many once the reply cards
     already go edge-to-edge on a phone; dropped here so nothing is clipped
     against the 390px edge. */
  body.fi-panel-portal .cxp-ai-responses-page .aiv-thread-replies {
    padding-left: 0;
    border-left: 0;
  }
}
/* ============================================================================
   END BRIEF B
   ============================================================================ */

/* ===== END WHAT AI SAID V2 (cxp-aiv-chat) ===== */

/* ===== AI PROFILE V2 (cxp-aiv-profile) ===== */
/* ============================================================================
   WS6-UI / BRIEF_A_profile.md - /portal/ai-visibility/profile
   Added 2026-09-04. New rules only - the alignment fix to the pre-existing
   .cxp-aiv-fields / .cxp-aiv-field / .cxp-aiv-label rules lives inline in
   ai-profile.blade.php (those selectors already existed there; editing them
   in place avoids a specificity fight with rules of identical weight). This
   file is the "one marked block" the coordinator splices into
   public/css/metronic/cxp.css. Every selector below is prefixed with the
   page's own scope class (.cxp-aiv-profile-page), so splicing it into the
   shared stylesheet cannot leak onto any other page.

   No new colours: every value below is var(--portal-*) / var(--cxp-*-tint)
   / var(--cxp-*-text), the same tokens already used elsewhere on this page,
   or color-mix() blends of var(--portal-primary) for the tinted icon chips.
   ============================================================================ */

/* --- Section titles (BRIEF_A #2): icon + title + explaining line, the same
   shape on every card on the page (the two status cards above the form and
   all three form cards). Replaces "plain bold text" with a small
   brand-tinted icon chip so titles read as a system, not five unrelated
   headings. --- */
.cxp-aiv-profile-page .cxp-aiv-card-head {
  display: flex; align-items: flex-start; gap: .65rem; min-width: 0;
}
.cxp-aiv-profile-page .cxp-aiv-card-head-text {
  display: flex; flex-direction: column; gap: .2rem; min-width: 0;
}
.cxp-aiv-profile-page .cxp-aiv-card-icon {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 2rem; height: 2rem; margin-top: .05rem;
  border-radius: .6rem;
  background: color-mix(in srgb, var(--portal-primary) 14%, transparent);
  color: var(--portal-primary);
}
.cxp-aiv-profile-page .cxp-aiv-card-icon svg {
  width: 1.05rem; height: 1.05rem;
}

/* --- Prefilled / empty presentation for the list-style boxes (BRIEF_A #4):
   services, service areas, aliases, negative matches, competitors and ideal
   customer. The textarea itself is never removed or swapped - it stays the
   same wire:model element in both states, only its border/background and a
   trailing note change, so the live binding is untouched.

   "Filled" vs "empty" is read directly off the bound property already
   passed to the view (no fabricated data). "Autofilled" is a second, optional
   flag ($<field>Autofilled, default false) the coordinator's PHP side can set
   when a value came from our own inference rather than the customer typing
   it - see CHANGES_A.txt for the full variable list. --- */
.cxp-aiv-profile-page .cxp-aiv-input--empty {
  border-style: dashed;
  border-color: var(--portal-border);
  background: color-mix(in srgb, var(--portal-primary) 4%, var(--portal-surface));
}
.cxp-aiv-profile-page .cxp-aiv-input--empty:focus {
  border-style: solid;
}
.cxp-aiv-profile-page .cxp-aiv-prefill-note,
.cxp-aiv-profile-page .cxp-aiv-empty-hint {
  font-size: .75rem; line-height: 1.4;
}
.cxp-aiv-profile-page .cxp-aiv-prefill-note {
  color: var(--cxp-good-text, var(--portal-muted));
}
.cxp-aiv-profile-page .cxp-aiv-empty-hint {
  color: var(--portal-muted);
}

/* ============================================================================
   end BRIEF_A additions
   ============================================================================ */

/* ===== END AI PROFILE V2 (cxp-aiv-profile) ===== */

/* ===== WEBSITE HEALTH FIXES (cxp-wh2-fix) ===== */
/* ===========================================================================
   BRIEF C (ASK-AEE277) - Website health: "Ongoing website work" pipeline
   ===========================================================================
   Splice this block into public/css/metronic/cxp.css, after the existing
   "WEBSITE HEALTH V2 (cxp-wh2)" block (never inside cxs-skin.css - that file
   is GENERATED). I have no server access, so I cannot read the live rules
   this block is beating - the DESIGN_STANDARD instructs me to assume they
   exist under that marker and say what I'm overriding, so every override
   below names its target and reasons about it from the rendered symptom
   Adir described, then uses !important defensively per the standard's own
   warning that "a correct-looking rule regularly loses to an existing one."

   Companion Blade change: each .cxp-wh2-pipeline-node's <small>/<strong>/<p>/
   <details> are now wrapped in one .cxp-wh2-node-body div, sibling to the
   existing .cxp-wh2-node-icon span. That is the only structural change - no
   node gained or lost a real element, nothing was renamed or removed.
   =========================================================================== */

/* Container: .kt-card-content and .cxp-wh2-pipeline are the SAME element in
   the markup, so whatever .kt-card-content sets for `display` (documented in
   DESIGN_STANDARD.md as part of the .kt-card column-flex family) needs
   !important to lose here. Two-row grid: row 1 holds only the five icons
   (uniform height -> automatically level, no border-top guesswork needed),
   row 2 holds the five independent-height text bodies below them. This is
   what makes defect 2 (different heights) structurally impossible rather
   than patched with alignment tweaks. */
.cxp-wh2-pipeline {
  display: grid !important;
  grid-template-columns: repeat(5, 1fr) !important;
  grid-template-rows: auto auto;
  column-gap: 16px;
  row-gap: 12px;
  align-items: start !important; /* beats an assumed align-items:center on
    the existing flex-row version of this rule - center-aligning a flex row
    of unequal-height column blocks is the mechanism that made icons sit at
    different heights whenever a neighbouring note ran longer. */
  position: relative;
}

/* Each node used to lay out icon+small+strong+p+details as one flex column.
   display:contents removes the node itself from the box/grid so its two
   real children (icon, .cxp-wh2-node-body) become the actual grid items,
   letting row 1 / row 2 apply globally across all five nodes instead of
   being trapped inside five separate mini flex-columns. */
.cxp-wh2-pipeline-node {
  display: contents;
}

.cxp-wh2-node-icon {
  grid-row: 1;
  justify-self: center;
  align-self: center;
  position: relative;
  z-index: 1; /* paint above the connector line below */
}

.cxp-wh2-node-body {
  grid-row: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center; /* defect 3: centre each stage's text under its icon */
  gap: 2px;
  min-width: 0; /* grid items default to min-width:auto, which silently
    disables the line-clamp ellipsis below until content is forced to
    actually shrink - same defect class as a feature hidden by a guard */
}

/* display:contents flattens all five nodes' children into one DOM sibling
   list (icon1, body1, icon2, body2, ... icon5, body5) with no column
   information left, so the grid's own auto-placement would interleave them
   across row 1 instead of one-per-column. Pin each node's pair explicitly. */
.cxp-wh2-pipeline-node:nth-child(1) > .cxp-wh2-node-icon,
.cxp-wh2-pipeline-node:nth-child(1) > .cxp-wh2-node-body { grid-column: 1 !important; }
.cxp-wh2-pipeline-node:nth-child(2) > .cxp-wh2-node-icon,
.cxp-wh2-pipeline-node:nth-child(2) > .cxp-wh2-node-body { grid-column: 2 !important; }
.cxp-wh2-pipeline-node:nth-child(3) > .cxp-wh2-node-icon,
.cxp-wh2-pipeline-node:nth-child(3) > .cxp-wh2-node-body { grid-column: 3 !important; }
.cxp-wh2-pipeline-node:nth-child(4) > .cxp-wh2-node-icon,
.cxp-wh2-pipeline-node:nth-child(4) > .cxp-wh2-node-body { grid-column: 4 !important; }
.cxp-wh2-pipeline-node:nth-child(5) > .cxp-wh2-node-icon,
.cxp-wh2-pipeline-node:nth-child(5) > .cxp-wh2-node-body { grid-column: 5 !important; }

/* Defect 1: the connector line. Beats an assumed full-width border-top (on
   .cxp-wh2-pipeline or the card) - that is exactly what makes a line "touch
   the card's left frame" and sit off the icons' centre line, since a
   border-top on the container sits at the container's own top edge, not at
   whatever height the icons happen to render at. Implemented as the
   ::before hint in BRIEF_C: a grid item confined to row 1 (the icon row)
   only, inset 10% each side so on this 5-column grid it starts exactly at
   column 1's centre (10% in) and ends at column 5's centre (10% from the
   right) - never at the card edge, and vertically it can only ever sit
   inside the icon row, never drift onto the text below. */
.cxp-wh2-pipeline::before {
  content: '';
  grid-row: 1;
  grid-column: 1 / -1;
  align-self: center;
  height: 1px;
  margin: 0 10%;
  background: var(--border);
  z-index: 0;
}

/* Defect 3 (cont.): cap the note at two lines with an ellipsis instead of
   wrapping ragged to however many lines a given note needs. */
.cxp-wh2-node-body p {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  margin: 0;
}

.cxp-wh2-node-body small {
  color: var(--muted-foreground);
}

.cxp-wh2-node-body strong {
  font-size: 13px;
}

/* Defect 4: mobile 390 - stacks vertically. Reset the desktop column pins
   back to a single column and let the flattened child order (icon1, body1,
   icon2, body2, ... icon5, body5) fall straight down the one column, which
   is already the correct visual order - no reordering needed. Each node
   becomes a real flex row again (icon left, body right) rather than a
   column, since a vertical timeline reads left-to-right per stage. */
@media (max-width: 480px) {
  .cxp-wh2-pipeline {
    grid-template-columns: 1fr !important;
    row-gap: 16px;
    padding-left: 4px; /* keeps the icon+line off the card's own edge */
  }

  .cxp-wh2-pipeline-node:nth-child(1) > .cxp-wh2-node-icon,
  .cxp-wh2-pipeline-node:nth-child(1) > .cxp-wh2-node-body,
  .cxp-wh2-pipeline-node:nth-child(2) > .cxp-wh2-node-icon,
  .cxp-wh2-pipeline-node:nth-child(2) > .cxp-wh2-node-body,
  .cxp-wh2-pipeline-node:nth-child(3) > .cxp-wh2-node-icon,
  .cxp-wh2-pipeline-node:nth-child(3) > .cxp-wh2-node-body,
  .cxp-wh2-pipeline-node:nth-child(4) > .cxp-wh2-node-icon,
  .cxp-wh2-pipeline-node:nth-child(4) > .cxp-wh2-node-body,
  .cxp-wh2-pipeline-node:nth-child(5) > .cxp-wh2-node-icon,
  .cxp-wh2-pipeline-node:nth-child(5) > .cxp-wh2-node-body {
    grid-column: 1 !important;
    grid-row: auto !important;
  }

  .cxp-wh2-node-icon {
    position: relative;
  }

  /* Vertical connector as short per-icon segments (this icon's bottom to
     the next icon's top, i.e. exactly the row-gap) rather than one long
     absolutely-positioned line guessed against an unknown total height.
     Each opaque icon covers the segment where it overlaps its own centre,
     so the visible result reads as one continuous line running from the
     first icon's centre to the last icon's centre - never the card edge,
     since it is inset inside the icon column, not the card. */
  .cxp-wh2-pipeline-node:not(:last-child) > .cxp-wh2-node-icon::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    width: 1px;
    height: 16px; /* matches row-gap above */
    background: var(--border);
    transform: translateX(-50%);
  }

  .cxp-wh2-pipeline::before {
    display: none !important; /* the desktop horizontal connector is
      replaced by the per-icon vertical segments above at this width */
  }

  .cxp-wh2-pipeline-node {
    display: flex !important;
    flex-direction: row;
    align-items: flex-start;
    gap: 12px;
  }

  .cxp-wh2-node-body {
    align-items: flex-start;
    text-align: left;
  }

  .cxp-wh2-node-body strong,
  .cxp-wh2-node-body p,
  .cxp-wh2-node-body small {
    font-size: 13px; /* floor from BRIEF_C item 4, above the house 12px floor */
  }
}

/* ---------------------------------------------------------------------------
   "What makes up your score" cards + the score-over-time card.
   Adir, 2026-09-04: "all these three boxes have bad design and attached to the
   left and touching the border... the table down the bottom where it says your
   score over time is also full of empty spaces."

   Measured on the live page at 1512px before this block:
     .cxp-wh2-factor-card  padding: 0px, first child at +1px from the card's
                           left border - the title and every line literally
                           touch the frame;
     the timeline card and the trend card on the SAME page both use an inner
     element at padding: 20px, which is why only these three looked wrong.
   `.kt-card` ships no padding of its own, so a card that does not add an inner
   padded element gets none - that is the trap here.
   --------------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-card {
  padding: 20px !important;
  row-gap: 12px;
}

/* The three cards were also different heights inside: "Site is up" has no chip
   row, so its "What we're doing" line floated in the middle of a tall box while
   its neighbours' sat at the bottom. One column layout, action pinned to the
   bottom, so the three read as a set. */
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-card {
  display: flex !important;
  flex-direction: column !important;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-factor-action {
  margin-top: auto;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* One summary line instead of the three restatements the blade used to stack. */
body.fi-panel-portal .cxp-health-page .cxp-wh2-trend-summary {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 14px;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-trend-scores {
  color: var(--foreground);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.01em;
}
body.fi-panel-portal .cxp-health-page .cxp-wh2-trend-said {
  color: var(--muted-foreground);
  font-size: 13px;
}

/* UI sweep tick 4. The site-audit variant of Website health (cxp-wh3-*, which
   only renders for clients that have SE Ranking audit data - it never appeared
   on the clients swept in ticks 1-3) has four "How fast the homepage feels"
   cards that all compute padding 0. The two with a ring get away with it
   because the ring's disc is inset inside its own box, but the two without one
   put "Layout stability" and "Main content appears" hard against the card
   border, left AND right - measured: every child at +1px.
   The page's other cards sit their text at +21px. One declaration, all four
   consistent; min-height 120px already absorbs the extra box height. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-kpi {
  padding: 16px 18px;
}

/* ===== END WEBSITE HEALTH FIXES (cxp-wh2-fix) ===== */

/* ===== ONE GAUGE LANGUAGE (cxpRingBrand) ===== */
/* ===========================================================================
   ONE GAUGE LANGUAGE - every progress ring in the portal draws the same arc.

   Adir, 2026-09-04: "you need to go through the whole system, the whole app,
   and to understand why when you design a gauge it's not based on the theme
   that we already designed in the playbook. And go through the whole website
   and check everywhere that it consists with the same exact design, same idea."

   A live sweep of 22 portal pages and the 6 admin AEO pages found three
   different constructions in use:
     A  the skin's conic ring on `.cxp-stat-card__ring`   (dashboard, AI sub-pages)
     B  an SVG arc stroked with a brand gradient          (Website health, AI hero)
     C  an SVG arc stroked with a FLAT colour             (cxp-sp2-* on Google
        Analytics and Search Console, and the tone variants of cxp-ai2-ring)
   A and B read the same to a customer - a violet-to-pink sweep on a light
   track. C is the odd one out, and it is the newest code, so it is the one
   that moves.

   The gradient itself is defined once per page by PortalPanelProvider's
   BODY_START hook (`#cxpRingBrand`), because `stroke: url(#id)` only resolves
   against a def in the same document - that missing def is the actual reason
   these rings were flat in the first place.

   Tone is NOT carried by the arc colour: the chip beside the number carries it
   (the approved Website health card shows a brand arc next to a worded
   verdict). That also keeps one score from being drawn in two different
   colours on two different pages - see feedback_portal_one_tone_per_score.
   =========================================================================== */

/* Search performance / Google Analytics / Google Search Console (cxp-sp2-*) */
body.fi-panel-portal .cxp-sp2-ring .cxp-sp2-ring-value,
body.fi-panel-portal .cxp-sp2-ring-kpi .cxp-sp2-ring-value,
body.fi-panel-portal .cxp-sp2-visitor-ring .cxp-sp2-ring-value,
/* `circle:last-of-type` is the progress arc on a SINGLE-VALUE ring, but the
   "New vs returning visitors" donut is a two-CATEGORY share chart: track,
   then .cxp-gauge-progress (New, var(--primary)), then .cxp-sp2-ring-secondary
   (Returning, #a5b4fc). The last circle there is the Returning segment, so this
   rule was painting it violet-to-pink while its own legend dot stayed pale
   lavender - the chart disagreed with its key. cxp.css line ~15308 already
   declares the correct `stroke: #a5b4fc !important`; it lost only because this
   selector scores higher. One gauge language governs SCORES, not category
   shares, so the secondary segment is excluded rather than the rule weakened. */
body.fi-panel-portal .cxp-sp2-ring circle:last-of-type:not(.cxp-sp2-ring-secondary),
body.fi-panel-portal .cxp-sp2-ring-kpi circle:last-of-type:not(.cxp-sp2-ring-secondary),
body.fi-panel-portal .cxp-sp2-visitor-ring circle:last-of-type:not(.cxp-sp2-ring-secondary) {
  stroke: url(#cxpRingBrand) !important;
}

body.fi-panel-portal .cxp-sp2-ring circle:first-of-type,
body.fi-panel-portal .cxp-sp2-ring-kpi circle:first-of-type,
body.fi-panel-portal .cxp-sp2-visitor-ring circle:first-of-type {
  stroke: oklch(0.915 0.008 265) !important;
}

/* AI visibility v2 rings - the --brand variant already used the gradient, the
   tone variants (--good/--middle/--poor/--checked) were flat, so a single page
   showed both. They all take the shared arc now; the tone chip beside the
   number is what still says good or bad. */
body.fi-panel-portal .cxp-ai2-ring circle:last-of-type,
body.fi-panel-portal .cxp-ai2-ring .cxp-ai2-ring-progress,
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-progress {
  stroke: url(#cxpRingBrand) !important;
}

body.fi-panel-portal .cxp-ai2-ring circle:first-of-type,
body.fi-panel-portal .cxp-ai2-ring .cxp-ai2-ring-track,
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-track {
  stroke: oklch(0.915 0.008 265) !important;
}

/* Deliberately NOT touched:
   - `.cxp-stat-card__ring` (construction A) - the skin paints it and hides its
     own svg; that is the approved dashboard tile and it already matches.
   - the Website health PageSpeed mini gauges - that page is Adir's stated
     reference ("the design supposed to look like this is the one that we are
     working with"), so its rings stay exactly as they are.
   - Filament's own chrome icons, which are circles in an svg but not gauges. */

/* The per-assistant gauges on the AI dashboard kept a flat amber arc because
   cxp.css already pins them with !important at two selectors
   (`.cxp-ai-engine-gauge .cxp-gauge-progress.cxp-gauge-checked` -> tone-poor and
   `...cxp-gauge-good` -> portal-primary-fill). Same class count plus the panel
   scope beats them, and these are percentage SCORES, so they take the shared arc
   like every other score in the product. */
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-progress,
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-progress.cxp-gauge-checked,
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-progress.cxp-gauge-good,
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-progress.cxp-gauge-middle,
body.fi-panel-portal .cxp-ai-engine-gauge .cxp-gauge-progress.cxp-gauge-poor {
  stroke: url(#cxpRingBrand) !important;
}


/* ===== END ONE GAUGE LANGUAGE (cxpRingBrand) ===== */

/* ===== LIVE OPERATIONS V2 (cxp-lo2) ===== */
/* ============================================================================
   LIVE OPERATIONS - liveops.css (ASK-FE3D67, 2026-09-04)
   Coordinator: splice this whole block into public/css/metronic/cxp.css.
   Scope: only body.fi-panel-portal .cxp-live-operations (this one page) -
   nothing here touches any other portal page. Every colour is an existing
   house token (--cxp-tone-good/middle/poor/neutral, --muted-foreground,
   --card, --border) already defined in cxp.css/tokens.css - no new hex.
   ============================================================================ */

/* --- "Running now" row/card tint -----------------------------------------
   New class, not a reuse of the existing (currently inert)
   .cxp-live-list-row--{tone} / .cxp-agent-state--{state} modifiers, so this
   never changes the look of the Work-we-did / Working-on-it tabs or the
   Team tab's other cards - only the row a job is ACTUALLY running on.
   Beats: cxp.css's plain `.cxp-live-list-row { background: var(--card) }`
   (~line 11078) and `.cxp-agent-card { padding: 1rem }` (~line 11021) -
   both keep their own layout, this only adds a border colour + background
   tint on top, at 2 classes' specificity so it wins over those 1-class base
   rules without needing !important. */
body.fi-panel-portal .cxp-live-operations .cxp-live-job-row--running {
  border-color: color-mix(in srgb, var(--cxp-tone-good) 45%, var(--border));
  background: color-mix(in srgb, var(--cxp-tone-good) 8%, var(--card));
}

/* --- Group sub-headings inside the Live now panel (Running now / Finished
   today / Waiting) - a lighter weight than .cxp-live-section-heading's h2
   (that owns the whole tab; this owns one state-bucket inside a tab). */
body.fi-panel-portal .cxp-live-operations .cxp-live-group-heading {
  margin: 1rem 0 .4rem;
  font-size: .75rem; /* 12px - DESIGN_STANDARD type floor */
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted-foreground);
}
body.fi-panel-portal .cxp-live-operations .cxp-live-group-heading:first-child {
  margin-top: 0;
}

/* --- Freshness banner --------------------------------------------------
   Reuses the existing .cxp-live-status pill (cxp.css ~5534, already has
   --good/--middle/--poor tone pairs); only .cxp-live-status--neutral is new
   (the pill never had a neutral/"no data yet" state before), using the
   house neutral tone-chip pair from DESIGN_STANDARD.md (#6b7280 on #f3f4f6),
   scoped to this page so no other .cxp-live-status usage is affected.
   white-space is overridden to normal (base rule is nowrap, cxp.css ~5545)
   so a longer "Quiet - X weeks ago" label wraps instead of ever forcing
   horizontal scroll at 390px. */
body.fi-panel-portal .cxp-live-operations .cxp-live-status {
  white-space: normal;
  margin-bottom: .75rem;
}
body.fi-panel-portal .cxp-live-operations .cxp-live-status--neutral {
  color: #6b7280;
  background: #f3f4f6;
}

/* --- 12px type floor for pre-existing sub-12px text this page still uses -
   DESIGN_STANDARD.md: "Type floor 12px, never below." .cxp-agent-state
   (cxp.css ~11040) was .68rem/10.9px and .cxp-count-badge (~10935) was
   .72rem/11.5px; .cxp-live-list-row small (~11085) was .73rem/11.7px.
   Scoped to .cxp-live-operations only - not a site-wide change to those
   shared classes, which the ws8-liveops brief does not cover. Selector
   specificity here (2 classes + descendant) already beats the unscoped
   1-class originals, no !important needed. */
body.fi-panel-portal .cxp-live-operations .cxp-agent-state,
body.fi-panel-portal .cxp-live-operations .cxp-count-badge,
body.fi-panel-portal .cxp-live-operations .cxp-live-list-row small {
  font-size: .75rem;
}

/* No new @keyframes/animation added here - the running-row pulse reuses
   .cxp-led as-is, whose own animation is already globally guarded under
   prefers-reduced-motion at cxp.css ~5681-5688 (memory:
   feedback_features_hidden_inside_a_reduce_motion_guard - that guard only
   disables the ANIMATION, not the element/feature, so it's safe to rely on
   here without duplicating it). */

/* ---------------------------------------------------------------------------
   THE BRAIN, MADE ACTUALLY LIVE (ASK-9BA225)

   Adir: "you removed the brain???? I need it like in the admin app." Removing
   it was my misread - what he objected to was that it never looked alive and
   its lines implied a connection that was not happening. Both are fixed here
   rather than by deleting the visual.

   The component (live-brain.blade.php) drew every link identically, and NOTHING
   in cxp.css or cxs-skin.css styled `.cxp-livebrain__connections line` or
   `.cxp-livebrain__node.is-running` at all - grep returns zero rules for either
   - so the running state has always been invisible. Each <line> now carries its
   own state and gets its treatment here:
     idle     - thin, faint, static: a wire, not a signal.
     running  - full brand colour, thicker, with a dash that travels from the
                specialist into the centre, so work in flight literally moves.
   --------------------------------------------------------------------------- */
body.fi-panel-portal .cxp-livebrain__connections line,
body.fi-panel-portal .cxp-livebrain__link {
  stroke: var(--cxp-livebrain-line, var(--border));
  stroke-width: 1;
  opacity: .18;
  transition: opacity .3s ease, stroke-width .3s ease;
}

body.fi-panel-portal .cxp-livebrain__link.is-running {
  stroke-width: 2.5;
  opacity: .9;
  stroke-dasharray: 14 10;
  animation: cxp-livebrain-flow 1.4s linear infinite;
}

@keyframes cxp-livebrain-flow {
  from { stroke-dashoffset: 48; }
  to   { stroke-dashoffset: 0; }
}

/* A running specialist pulses; every other node sits still. */
body.fi-panel-portal .cxp-livebrain__node.is-running .cxp-livebrain__node-mark {
  animation: cxp-livebrain-pulse 1.8s ease-in-out infinite;
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 55%, transparent);
}

@keyframes cxp-livebrain-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 55%, transparent); }
  70%  { box-shadow: 0 0 0 12px color-mix(in srgb, var(--primary) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 0%, transparent); }
}

body.fi-panel-portal .cxp-livebrain__node:not(.is-running) .cxp-livebrain__node-mark {
  animation: none;
}

/* Motion is decoration here - the state is already carried by colour, weight
   and the words under each node, so switching it off loses nothing. */
@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-livebrain__link.is-running,
  body.fi-panel-portal .cxp-livebrain__node.is-running .cxp-livebrain__node-mark {
    animation: none;
  }
  body.fi-panel-portal .cxp-livebrain__link.is-running { opacity: .9; }
}

/* ===== END LIVE OPERATIONS V2 (cxp-lo2) ===== */

/* ===== WEBSITE HEALTH V3 (cxp-wh3) ===== */
/* ===========================================================================
   WEBSITE HEALTH V3 (cxp-wh3) - CYT-933

   Pixel-matches Sol's approved mockup (sol-v3/website-health-v3.html,
   reconciled against the 19:03 revision) into the house theme. Every
   selector below is scoped `body.fi-panel-portal .cxp-health-page` and
   prefixed `cxp-wh3-`; nothing here redefines a shared portal component
   (kt-card, cxp-rk2-chip, cxp-map-tabs, cxp-badge-*, the trend-chart
   component's own classes, or the global #cxpRingBrand gradient).

   Colour tokens ride the LIVE portal tokens (--card, --border, --foreground,
   --muted-foreground, --primary, --cxp-tone-*) rather than the mockup's own
   standalone palette, per the brief's "rebase on the live tokens" note - the
   mockup's :root block existed only so the review file could stand alone.

   Known traps this file designs around (see BRIEF.md "What to build" #3):
   - `.kt-card` / `.kt-card-content` force a column flex layout - every grid
     here (`cxp-wh3-score-card`, `cxp-wh3-kpis`, `cxp-wh3-work-grid`, etc.) is
     a plain nested <div>, never applied to `.kt-card`/`.kt-card-content`
     itself, so it never has to fight that rule with !important.
   - The skin forces `display:block !important` on tables at mobile widths.
     The Pages/Resources tables are meant to scroll horizontally instead (ten
     technical columns cannot be honestly compressed into two lines) - see
     the `!important` override in the 760px block below. The Overview/Issues
     tables use the OTHER accepted mobile shape (`cxp-wh3-stack-table`,
     card-per-row) - the two variants are never applied to the same table.
   - `.cxp-map-tabs.cxp-map-tabs.cxp-map-tabs { flex-wrap: wrap !important }`
     already exists in cxp.css and is reused as-is (both the outer detail
     tabs and the inner Overview tabs use the plain `cxp-map-tabs` class) -
     nothing here redefines tab wrapping.
   =========================================================================== */

body.fi-panel-portal .cxp-health-page {
  --cxp-wh3-pink: #db2777;
  --cxp-wh3-good-bg: #e7f6ee;
  --cxp-wh3-good-border: #b7e3cb;
  --cxp-wh3-bad-bg: #fde8e8;
  --cxp-wh3-bad-border: #f3b8b0;
  --cxp-wh3-warn: #8a4b00;
  --cxp-wh3-warn-bg: #fff4e5;
  --cxp-wh3-warn-border: #f2cf9c;
  --cxp-wh3-notice-bg: #eef1f5;
  /* Batch 19 item A4: one PASS/READY/OK verdict colour, always this green
     (same hex as .cxp-wh3-chip-good's text) - never --cxp-tone-good, which
     resolves to the brand blue and is reserved for MEASUREMENT rings
     (the score ring, Phone/Computer speed) per the coordinator's rule. */
  --cxp-wh3-verdict-good: #04663a;
}

/* --------------------------------------------------------------------
   Layout primitives
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-section { display: grid; gap: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-section-title { margin: 0; color: var(--foreground); font-size: 17px; font-weight: 700; line-height: 1.35; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-section-copy { margin: 3px 0 0; color: var(--muted-foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pad { padding: 20px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 8px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-card-title { margin: 0; color: var(--foreground); font-size: 15px; font-weight: 700; line-height: 1.35; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-card-sub { margin: 3px 0 0; color: var(--muted-foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-attention-grid { display: grid; gap: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
/* Item 6 (gate #2, batch 9): var(--primary) measured ~4.1:1 as link text on
   this page's real cascade - darkened to a fixed, verified (6.7:1 on white)
   blue rather than depend on a token this page doesn't own. Non-text uses
   of --primary (icons, tinted backgrounds) are left as-is. */
/* Batch 19 item A9(d): "Explore crawl records"/"View every issue"/"View
   →"/"Manage connections" measured 18px tall (bare text-height, padding:0)
   - below the page's 24px desktop text-link floor. inline-flex + a
   min-height gives real hit height without adding visible padding/border,
   so no sibling shifts. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-link { display: inline-flex; align-items: center; min-height: 24px; font-size: 12px; font-weight: 700; background: none; border: 0; padding: 0; color: #1d4ed8; cursor: pointer; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-link:hover { text-decoration: underline; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-panel-actions { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-failed .cxp-wh3-link { display: inline-block; margin-top: 8px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-source-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--primary); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-led { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--cxp-wh3-verdict-good); box-shadow: 0 0 0 4px color-mix(in srgb, var(--cxp-wh3-verdict-good) 14%, transparent); margin-right: 8px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-led--off { background: var(--cxp-tone-poor); box-shadow: 0 0 0 4px color-mix(in srgb, var(--cxp-tone-poor) 14%, transparent); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-empty-inline { padding: 8px 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-empty { display: grid; place-items: center; min-height: 200px; padding: 28px; text-align: center; }
/* Round 6 item 1: content-hugging variant for the Resources tab's empty
   state - reuses the shared stat-card "not measured" dashed-circle marker
   language (58px dashed circle, muted gray, "?") instead of the mostly-
   blank centered block. Row layout, no forced min-height, capped so the
   card body stays close to its content instead of ~200px of dead space. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-empty-compact { display: flex; align-items: center; gap: 16px; min-height: 0; max-height: 180px; padding: 18px 20px; text-align: left; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-empty-compact h3 { margin: 0 0 4px; font-size: 13px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-empty-compact p { margin: 0; color: var(--muted-foreground); font-size: 12px; line-height: 1.4; }
/* Batch 12: house rule forbids "?" as a placeholder glyph - replaced with
   the dashboard empty-widget's "DATA" text tile (muted surface, 12px caps). */
body.fi-panel-portal .cxp-health-page .cxp-wh3-empty-marker { flex: 0 0 auto; width: 48px; height: 48px; display: grid; place-items: center; border-radius: 10px; color: #98a2b3; background: #fafbfc; border: 1px solid #e2e5ea; font-size: 12px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; }

/* --------------------------------------------------------------------
   Audit context strip
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-context { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 15px 18px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-context-main { display: grid; gap: 6px; min-width: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-context-title { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; color: var(--foreground); font-size: 14px; font-weight: 700; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-context-meta { display: flex; flex-wrap: wrap; gap: 5px 16px; color: var(--muted-foreground); font-size: 12px; }

/* --------------------------------------------------------------------
   Score summary (three-part card)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card { display: grid; grid-template-columns: 1.12fr .95fr .93fr; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card > div { padding: 22px; min-width: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card > div + div { border-left: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-main { display: grid; grid-template-columns: 132px minmax(0, 1fr); align-items: center; gap: 18px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring { position: relative; width: 132px; height: 132px; flex: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring circle,
body.fi-panel-portal .cxp-health-page .cxp-wh3-pass-ring circle,
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-ring circle { fill: none; stroke-width: 10; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-ring circle { stroke-width: 8; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring-track { stroke: oklch(0.915 0.008 265); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring-value { stroke: url(#cxpRingBrand); stroke-linecap: round; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring-center { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring-center strong { color: var(--foreground); font-size: 34px; line-height: 1; letter-spacing: -.04em; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring-center span { display: block; margin-top: 4px; color: var(--muted-foreground); font-size: 12px; font-weight: 700; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-copy { min-width: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-copy h2 { margin: 5px 0 4px; color: var(--foreground); font-size: 21px; line-height: 1.2; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-copy p { margin: 0; color: var(--muted-foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-kicker { display: block; color: var(--muted-foreground); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-stat-line { display: flex; align-items: center; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-big { color: var(--foreground); font-size: 30px; line-height: 1; font-weight: 700; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-unit { color: var(--muted-foreground); font-size: 12px; font-weight: 650; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-list { display: grid; gap: 9px; margin-top: 13px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-row { display: grid; grid-template-columns: 10px minmax(0, 1fr) auto; gap: 7px; align-items: center; font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-score-row strong { color: var(--foreground); font-variant-numeric: tabular-nums; }

/* Availability (uptime pass/fail) block inside the score card */
body.fi-panel-portal .cxp-health-page .cxp-wh3-uptime { display: grid; grid-template-columns: 82px minmax(0, 1fr); gap: 16px; align-items: center; margin-top: 10px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pass-ring { position: relative; width: 82px; height: 82px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pass-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pass-value { stroke-linecap: round; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pass-ring b { position: absolute; inset: 0; display: grid; place-content: center; color: var(--foreground); font-size: 14px; font-weight: 700; }

/* --------------------------------------------------------------------
   Chips
   -------------------------------------------------------------------- */
/* Batch 18 item H1 (HIGH, regression from batch 17): the delta text run
   ("&uarr; 352") and the `.cxp-wh3-chip-suffix` span are two separate
   flex items inside this inline-flex chip with no whitespace text node
   between them in the markup - so ">=520px" chips rendered "352since
   last crawl" with zero gap. column-gap puts real space between flex
   items regardless of markup whitespace. Item L15: min-height bumped
   22->23px to match .cxp-rk2-chip's measured 23px on this same page. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-chip { display: inline-flex; align-items: center; column-gap: .3em; width: max-content; max-width: 100%; min-height: 23px; padding: 2px 8px; border: 1px solid; border-radius: 999px; font-size: 12px; font-weight: 700; line-height: 1.3; white-space: nowrap; }
/* Item 6 (gate #2, batch 9): color here previously came from shared tokens
   (--cxp-good-strong / --cxp-bad-strong / --muted-foreground) whose actual
   resolved value in this page's real cascade is out of our control and
   measured as low as 2.6:1-4.4:1 against these light chip backgrounds.
   Hardcoded to values verified >= 4.5:1 against each chip's own (already
   hardcoded) background - 6.3-7.6:1 in every case - so contrast no longer
   depends on a token we don't own. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-chip-good { color: #04663a; background: var(--cxp-wh3-good-bg); border-color: var(--cxp-wh3-good-border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-chip-bad { color: #b42318; background: var(--cxp-wh3-bad-bg); border-color: var(--cxp-wh3-bad-border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-chip-warn { color: #8a4b00; background: var(--cxp-wh3-warn-bg); border-color: var(--cxp-wh3-warn-border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-chip-neutral { color: #4b5563; background: var(--cxp-wh3-notice-bg); border-color: var(--border); }

/* --------------------------------------------------------------------
   Bars (severity / page-health two-and-three-segment bars)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-bar { display: flex; height: 12px; margin-top: 12px; overflow: hidden; border-radius: 999px; background: var(--muted, #eef0f4); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-bar span { display: block; height: 100%; }
/* ratio-bar legend (gate #8 low 6): two dots + labels, 12px muted, under the Healthy pages bar */
body.fi-panel-portal .cxp-health-page .cxp-wh3-bar-legend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 8px; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-bar-legend span { display: inline-flex; align-items: center; gap: 6px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-bar-legend i { display: inline-block; width: 8px; height: 8px; border-radius: 50%; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-split-label { display: flex; justify-content: space-between; gap: 12px; margin-top: 13px; font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-split-label strong { display: block; color: var(--foreground); font-size: 19px; }

/* --------------------------------------------------------------------
   KPI tile rows (wraps <x-portal.stat-card>, never redefines it)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
/* Wraps a KPI's <x-portal.stat-card> plus its own delta chip - rendered as
   our own coloured chip rather than through the component's delta prop
   (see the tone note on WebsiteHealth::deltaChip()). */
/* Item 2 (gate #2, batch 9): the batch-7/8 approach drew the delta chip with
   position:absolute over the tile, overlapping the title at 1440 (3 tiles)
   and 390 (8 tiles) and drifting outside the card on 3 tiles - an absolutely
   positioned box has no relationship to the locked stat-card's own internal
   layout. Fixed: the chip is now a STATIC flex item in a head row we render
   ourselves (title left, chip right, wrap allowed), and the stat-card
   component's own (now redundant) title-row is hidden under the same tile
   scope rather than duplicated. */
/* Batch 15 item 4 (MEDIUM): "construction drift" - Domain/Speed tiles are a
   bare <x-portal.stat-card> (one bordered box, title inside, 154px min-
   height from the shared skin's .cxp-stat-card.cxp-kpi2 rule), while What-
   changed/Coverage tiles were OUR OWN head row (title + chip) sitting
   ABOVE a second, separately-bordered stat-card box - two visibly
   different constructions, and the extra head row's own height pushed
   these tiles to ~164px instead of 154px.
   Fix: .cxp-wh3-tile itself becomes the ONE visible bordered box (copying
   the exact kpi2 chrome: 12px radius, 1px border, #fff, the kpi2 shadow,
   18px/20px padding, flex column, min-height 154px so it measures
   IDENTICALLY to a bare stat-card), and the nested stat-card article is
   stripped of its OWN chrome (border/background/shadow/padding/min-height
   all zeroed) so it reads as plain content inside our box, not a second
   card. Our head row sits first, the stripped stat-card content fills the
   rest - one box, title-and-chip row inside it, same height class as
   every other tile on the page. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-tile {
  --cxp-kpi2-border: #e5e7eb;
  --cxp-kpi2-shadow: 0 1px 2px rgba(16, 24, 40, .04);
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 154px;
  padding: 18px 20px;
  border: 1px solid var(--cxp-kpi2-border);
  border-radius: 12px;
  background: #fff;
  box-shadow: var(--cxp-kpi2-shadow);
}
body.fi-panel-portal .cxp-health-page .cxp-wh3-tile .cxp-stat-card.cxp-kpi2 {
  min-height: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  flex: 1 1 auto !important;
}
/* The shared skin's own .cxp-stat-card.cxp-kpi2 .cxp-stat-card__title-row
   rule sets display:flex !important - a plain display:none here (no
   !important) can never win against that regardless of specificity, so
   the component's internal title row kept rendering live (label printed
   twice) until this was added. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-tile .cxp-stat-card__title-row { display: none !important; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-tile-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 10px; margin-bottom: 6px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-tile-head-title { color: var(--muted-foreground); font-size: 12px; font-weight: 700; }
/* Batch 18 item M9: two-segment ratio bar (healthy vs needing attention)
   inside the Healthy pages tile, reusing the page's own .cxp-wh3-bar. */
/* Batch 19 item A3: compact per the coordinator's "<=8px bar + one caption
   line" target - was the shared 12px .cxp-wh3-bar height, which (together
   with the now-removed in-tile trend chart) helped stretch this whole grid
   row to ~360px while its siblings held ~150px of content. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-tile-bar { margin-top: 8px; height: 8px; }
/* Batch 18 item M10: compact pages-crawled trend inside the Pages crawled
   tile - the shared trend-chart component is scaled down to fit a KPI
   tile instead of a full card, same technique as .cxp-wh3-spc-row above. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-trend { margin-top: 10px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-trend .cxp-trend-chart-body { height: 46px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-trend .cxp-trend-chart-plot { padding-right: 6px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-trend .cxp-trend-point { width: .4rem; height: .4rem; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-trend .cxp-trend-chart-axis,
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-trend .cxp-trend-chart-heading { display: none; }
/* Batch 19 item A3: the pages-crawled trend moved OUT of the KPI tile
   (.cxp-wh3-mini-trend, axis/heading hidden by design above) into its own
   slot under "Health score over time", where it keeps its own small axis
   labels (via the page's manual .cxp-speed-axis row, same as the main
   score chart) - a distinct, slightly taller class rather than reusing the
   tile-scoped one. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-crawled-trend { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-crawled-trend .cxp-trend-chart-body { height: 70px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-crawled-trend .cxp-trend-point { width: .45rem; height: .45rem; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-crawled-trend .cxp-wh3-spc-row { margin-top: 6px; }
/* One-saved-point trend: never reserve the full multi-point chart height for
   a single reading, and make that lone marker unmistakable rather than a
   near-invisible dot on a mostly empty card. */
/* One-saved-point trend: a real SVG circle + halo (Sol's fix - the shared
   trend-chart component's HTML/CSS point overlay read as an imperceptible
   dot at full-page 1440 scale) on a compact self-contained viewBox, never
   reserving the full multi-point chart height for a single reading. */
/* Round 6 item 2: the previous fix kept a 120px plotting panel (SVG grid
   lines + an HTML marker overlay) for a single reading - still read as "a
   full-width chart panel for one point" live. Replaced with NO plotting
   area at all: one compact inline row (a 12px dot + the value + a meta
   line) so the card body is naturally under 100px, not just under 120px. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-spc-row { display: flex; align-items: center; gap: 10px; margin: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-spc-inline-dot { flex: none; width: 12px; height: 12px; border-radius: 50%; background: var(--primary); box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary) 30%, transparent); }
/* <p class="cxp-meta"> carries the browser's default paragraph margin
   (~1em) with no reset in the shared skin - trimmed here so the single-
   point card body stays under the 100px target instead of ~124px. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-spc-row + p.cxp-meta { margin: 8px 0 0; }
/* Item 2 (batch 6): the shared skin applies its own brand-sweep gradient to
   `[data-chart-line]` regardless of the component's own `color` prop (an
   SVG presentation attribute loses to ANY external stylesheet rule, with or
   without !important) - Phone/Computer are device IDENTITY colours, never
   the brand sweep, so this scoped override wins with matching !important. */
body.fi-panel-portal .cxp-health-page #phone-speed-trend [data-chart-line] { stroke: #0891b2 !important; }
body.fi-panel-portal .cxp-health-page #phone-speed-trend [data-chart-line-secondary] { stroke: #2563eb !important; }
/* Point markers already carry the correct per-point inline background
   (teal for Phone, blue for Computer, set in the blade) - not overridden
   here, since a blanket rule would force BOTH series to the same colour. */
/* Attempt-after-success warning banner (item 8) */
body.fi-panel-portal .cxp-health-page .cxp-wh3-attempt-warning { padding: 14px 18px; border-color: var(--cxp-wh3-bad-border) !important; background: var(--cxp-wh3-bad-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-attempt-warning p { margin: 0; color: var(--cxp-bad-strong, #b42318); font-size: 12.5px; font-weight: 600; }
/* Stable hook for the outer detail-tab bar (Overview/Pages/Resources/Issues)
   - purely additive, `.cxp-map-tabs` still carries all real styling. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-main-tabs { }
body.fi-panel-portal .cxp-health-page .cxp-wh3-kpi { min-height: 120px; padding: 18px 20px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-kpi-label { color: var(--foreground); font-size: 13px; font-weight: 700; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-kpi-copy { margin: 6px 0 0; color: var(--muted-foreground); font-size: 12px; line-height: 1.4; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity { display: grid; place-items: center; width: 26px; height: 26px; border-radius: 8px; font-size: 12px; font-weight: 800; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-error { color: #b42318; background: var(--cxp-wh3-bad-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-warn { color: #8a4b00; background: var(--cxp-wh3-warn-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-notice { color: #4b5563; background: var(--cxp-wh3-notice-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-pass { color: #04663a; background: var(--cxp-wh3-good-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-poor { color: #b42318; background: var(--cxp-wh3-bad-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-middle { color: #8a4b00; background: var(--cxp-wh3-warn-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-neutral { color: #4b5563; background: var(--cxp-wh3-notice-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-good { color: #04663a; background: var(--cxp-wh3-good-bg); }

/* --------------------------------------------------------------------
   Chart stat line (score-over-time header numbers, above <x-portal.trend-chart>)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-chart-stat { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 6px 0 10px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-direction { font-size: 12px; font-weight: 700; }

/* --------------------------------------------------------------------
   Top issues list + 13-category chart
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-list { display: grid; margin-top: 10px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row { display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; gap: 9px; align-items: center; min-height: 40px; border-top: 1px solid var(--border); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row:first-child { border-top: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row strong { color: var(--foreground); font-weight: 600; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row .cxp-wh3-severity { width: 20px; height: 20px; font-size: 12px; }
/* Batch 17 item 4 (MEDIUM): 64-70% of each row was blank between the label
   and the count button - a proportional bar (count / this list's own max
   count) in the severity color fills that space with real information
   instead of empty whitespace. Label keeps a max-width so it never eats
   all the room the bar needs to be legible. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-label-wrap { display: flex; align-items: center; gap: 10px; min-width: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-label-wrap strong { flex: 0 1 auto; max-width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-bar-track { flex: 1 1 auto; min-width: 24px; height: 6px; border-radius: 999px; background: var(--muted, #eef0f4); overflow: hidden; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-bar { display: block; height: 100%; border-radius: 999px; }
/* Batch 13 item 7 (LOW): a single/double-digit count button with
   .cxp-wh3-link's padding:0 measured a 5-6px hit area - real touch target
   floor without changing how the number visually reads. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row button.cxp-wh3-link { display: inline-block; min-width: 24px; min-height: 24px; text-align: right; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-legend { display: flex; gap: 12px; flex-wrap: wrap; font-size: 12px; font-weight: 700; }
/* Item 6 (gate #2, batch 9): was three inline styles reading tokens
   (--cxp-tone-poor/--cxp-tone-middle/--muted-foreground) directly - an
   inline style cannot be overridden by an external stylesheet without
   !important, and "Warnings" measured 2.3:1 against the card background.
   Moved to classes with hardcoded, verified (>=6.5:1 on white) colors. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-legend-poor { color: #b42318; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-legend-middle { color: #8a4b00; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-legend-neutral { color: #4b5563; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-category-wrap { overflow-x: auto; padding-bottom: 4px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-categories { display: grid; grid-template-columns: repeat(13, minmax(54px, 1fr)); gap: 6px; min-width: 780px; margin-top: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-category { display: grid; grid-template-rows: 104px auto; gap: 7px; text-align: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-category-bars { display: flex; align-items: flex-end; justify-content: center; gap: 2px; height: 100%; border-bottom: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-category-bars i { display: block; width: 8px; min-height: 2px; border-radius: 3px 3px 0 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-category small { display: block; color: var(--muted-foreground); font-size: 12px; line-height: 1.3; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-category small strong { color: var(--foreground); font-size: 12px; }
/* Batch 13 item 2: the bold number is now finding VOLUME (sum of affected
   pages), not the failing-check count - the check count moved here as a
   small secondary line so both figures stay visible without conflating
   them. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-category-checks { display: block; margin-top: 1px; color: var(--muted-foreground); font-size: 12px; }
/* Batch 19 item A4: was var(--cxp-good-strong, #04663a) - a shared token
   this page does not own, which the coordinator measured resolving to the
   brand blue rather than its own #04663a fallback. Hardcoded to the page's
   own verdict-green variable so a zero-findings check mark can never read
   as the same blue as a MEASUREMENT ring. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-passed { align-self: center; color: var(--cxp-wh3-verdict-good); font-size: 18px; font-style: normal; }

/* --------------------------------------------------------------------
   Indexability / status-code / link / robots two-panel diagnostics
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-hbars { display: grid; gap: 10px; margin-top: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-hbar { display: grid; grid-template-columns: 118px minmax(0, 1fr) 40px; gap: 8px; align-items: center; font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-hbar-track { height: 10px; overflow: hidden; border-radius: 999px; background: var(--muted, #edf0f4); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-hbar-track i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--primary), var(--cxp-wh3-pink)); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut-layout { display: grid; grid-template-columns: 150px minmax(0, 1fr); align-items: center; gap: 18px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut { position: relative; width: 140px; height: 140px; border-radius: 50%; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut::after { content: ""; position: absolute; inset: 16px; border-radius: 50%; background: var(--card); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut-center { position: absolute; z-index: 1; inset: 0; display: grid; place-content: center; text-align: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut-center strong { color: var(--foreground); font-size: 24px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut-center span { color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut-list { display: grid; gap: 8px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut-row { display: grid; grid-template-columns: 10px minmax(0, 1fr) auto auto; gap: 8px; align-items: center; font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-donut-row strong { color: var(--foreground); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ring-list { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: 18px; align-items: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-small-donut { position: relative; width: 105px; height: 105px; border-radius: 50%; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-small-donut::after { content: ""; position: absolute; inset: 13px; border-radius: 50%; background: var(--card); }

/* --------------------------------------------------------------------
   Crawl-structure mini cards
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini { min-height: 168px; padding: 16px; border: 1px solid var(--border); border-radius: 10px; background: var(--muted, #f7f8fb); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini h3 { margin: 0; color: var(--foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini .cxp-wh3-big { display: block; margin-top: 10px; font-size: 26px; }
/* Batch 19 item A5: padding-top reserves room for the new per-bar count
   label (printed ABOVE each bar) without touching the existing height:X%
   math on the bars themselves - they still size against the original 58px
   content box, place-items:end still bottom-anchors bar+label inside it. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-vbars { display: flex; align-items: end; gap: 8px; height: 58px; margin-top: 13px; padding-top: 16px; border-bottom: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-vbar { position: relative; display: grid; place-items: end center; flex: 1; height: 100%; }
/* The count sits in the padding-top strip added above, never overlapping
   even the tallest (100%-height) bar in a mini. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-vbar-count { position: absolute; top: -16px; left: 50%; transform: translateX(-50%); color: var(--muted-foreground); font-size: 12px; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Batch 18 item L17: a bar at 2% of the 58px track (near-zero counts, e.g.
   c26's "2 of 14" server-response bucket) rendered under 2px - a real
   hairline. min-height guarantees every bar reads as a bar regardless of
   how small its percentage is, without touching the height:X% math above.
   (Range-label nowrap was tried and reverted - live-measured to overflow
   the mobile 2x2 card by 43px once M12 squeezes these minis to ~142px;
   see the M12 note below. Left wrapping, matching original behaviour -
   the "one line at 1440" half of L17 is NOT done, flagged in the report.) */
body.fi-panel-portal .cxp-health-page .cxp-wh3-vbar i { display: block; width: min(18px, 70%); min-height: 4px; border-radius: 4px 4px 0 0; background: linear-gradient(180deg, var(--primary), var(--cxp-wh3-pink)); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-vbar small { position: relative; top: 17px; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-note { margin: 19px 0 0; color: var(--muted-foreground); font-size: 12px; }

/* --------------------------------------------------------------------
   Speed KPI mini-rings (approved PageSpeed exception, kept as its own
   construction rather than the shared score ring)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-speed-kpi { display: grid; grid-template-columns: 58px minmax(0, 1fr); gap: 12px; align-items: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-ring { position: relative; width: 58px; height: 58px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-ring b { position: absolute; inset: 0; display: grid; place-content: center; color: var(--foreground); font-size: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-device-legend,
body.fi-panel-portal .cxp-health-page .cxp-wh3-band-guide { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: center; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-device-legend { margin-top: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-band-guide { margin-top: 7px; padding-top: 7px; border-top: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-device-legend strong,
body.fi-panel-portal .cxp-health-page .cxp-wh3-band-guide strong { color: var(--foreground); }

/* --------------------------------------------------------------------
   Work evidence rail
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-work-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 12px; margin-top: 17px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step { position: relative; min-width: 0; padding-top: 42px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step::before { content: ""; position: absolute; top: 17px; left: 34px; right: -14px; height: 2px; background: var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step:last-child::before { display: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-work-icon { position: absolute; z-index: 1; top: 0; left: 0; display: grid; place-items: center; width: 36px; height: 36px; border: 5px solid var(--card); border-radius: 50%; background: color-mix(in srgb, var(--primary) 12%, var(--card)); color: var(--primary); box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 30%, transparent); font-weight: 800; font-style: normal; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step small { display: block; color: var(--muted-foreground); font-size: 12px; font-weight: 700; text-transform: uppercase; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step strong { display: block; margin-top: 4px; color: var(--foreground); font-size: 12px; line-height: 1.35; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step p { margin: 4px 0 0; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-review-note { margin-top: 15px; padding: 10px 12px; border-radius: 9px; background: color-mix(in srgb, var(--primary) 8%, var(--card)); color: #1d4ed8; font-size: 12px; }

/* --------------------------------------------------------------------
   Connections
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-connection-summary { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 20px; align-items: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ready { padding-right: 20px; border-right: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ready strong { display: flex; align-items: center; color: var(--foreground); font-size: 16px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-ready p { margin: 5px 0 0; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-connections { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-connection { min-width: 0; padding: 10px; border: 1px solid var(--border); border-radius: 9px; background: var(--muted, #f7f8fb); }
/* Batch 19 item A9(a): "Google Search Console"/"Google Business Profile"
   measured 136-137px in a 123px box at 1199 (nowrap + ellipsis cut 13px of
   the label). Letting it wrap is safe at every width - the card's own
   min-width:0 + the grid's own row-height stretch already accommodate a
   2-line label without disturbing the sibling chip below it.
   white-space MUST be set explicitly to `normal` (not simply omitted) -
   live-verified that omitting it left the old deployed `nowrap` value
   in effect, since a later rule with the SAME selector only overrides the
   declarations it actually states, never "un-sets" one it leaves out. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-connection b { display: block; overflow: visible; color: var(--foreground); font-size: 12px; line-height: 1.3; white-space: normal; text-overflow: clip; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-connection > span { display: inline-block; margin-top: 5px; }

/* --------------------------------------------------------------------
   Tables (Overview/Issues stack-table AND Pages/Resources scroll-table)
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-table-card { padding: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table-card > .kt-card-header { padding: 18px 20px; }
/* `position:relative` lives HERE (the full-width controls row), not on the
   narrow per-button `.cxp-wh3-columns-menu` wrapper - a panel anchored
   `right:0` against a ~90px-wide button div can compute a NEGATIVE left
   offset the moment the panel (190px) is wider than that button, which is
   exactly the live bug (left:-67px, last checkbox unreachable at 390).
   Anchoring to this row instead means `right:0` always lines up with the
   card's own right edge, at every width. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-table-controls { position: relative; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table-controls input[type="search"] { min-width: 160px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-btn { display: inline-flex; align-items: center; justify-content: center; min-height: 34px; padding: 0 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--card); color: var(--foreground); font-size: 12px; font-weight: 700; cursor: pointer; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-btn-primary { color: #fff; border-color: transparent; background: linear-gradient(90deg, var(--primary), var(--cxp-wh3-pink)); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-status { display: inline-flex; align-items: center; gap: 5px; color: var(--cxp-good-strong, #04663a); font-weight: 700; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table-wrap { margin: 0 20px 16px; overflow-x: auto; border: 1px solid var(--border); border-radius: 10px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table { width: 100%; border-collapse: separate; border-spacing: 0; color: var(--foreground); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table th { height: 38px; padding: 8px 10px; text-align: left; background: var(--muted, #f2f4f8); color: var(--muted-foreground); font-size: 12px; font-weight: 750; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap; }
/* Batch 15 item 3: <button> carries its own UA-default white-space
   (Chrome defaults buttons to `pre`) that a parent <th>'s white-space:
   normal does not override by inheritance alone - so any SORTABLE header
   (Traffic/Referring pages/Depth/Keywords, each wrapped in a <button>)
   never actually wrapped even when white-space:normal was already set on
   its <th>. This went unnoticed because the verification fixture used a
   plain <th> with no <button>, unlike the real sortable markup. Explicit
   inherit fixes every sortable header at once, past and future. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-table th button { background: none; border: 0; padding: 0; margin: 0; color: inherit; font: inherit; font-weight: inherit; letter-spacing: inherit; text-transform: inherit; white-space: inherit; cursor: pointer; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table td { height: 46px; padding: 8px 10px; border-top: 1px solid var(--border); vertical-align: middle; font-size: 12px; font-variant-numeric: tabular-nums; }
/* Batch 19 item A1: the Pages table gets its OWN zebra rule below, driven
   by a real server-side class rather than :nth-child(even) - see that rule
   for why. Every OTHER .cxp-wh3-table (stack-table, resource-table) has no
   interleaved hidden detail row breaking its :nth-child count, so nothing
   changes for them. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-table:not(.cxp-wh3-pages-table) tbody tr:nth-child(even) td { background: var(--muted, #f8f9fb); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table strong { color: var(--foreground); }
/* width:0 + min-width:100%: a nowrap block inside a table cell still contributes its full text width
   to the column even under table-layout:fixed (measured 2026-09-05: a 475px URL forced the 200px phone
   column to 488px and the 744px table to 1006px for the rich client while the sparse client, whose URLs are
   short, sat at 200/744). With a zero intrinsic width the column follows the <col> and the ellipsis shows. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-table .cxp-wh3-url { display: block; width: 0; min-width: 100%; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table-note { margin: 0 20px 18px; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-issue-name { display: block; color: var(--foreground); font-weight: 600; }
/* Batch 18 item L14: "What needs attention"'s COUNT cell puts a tinted
   background only under a "good" tier's <strong> (middle/poor tiers stay
   plain), which reads as one row being highlighted for no reason - colour
   only, no tier gets a tinted box. */
/* Batch 19 item A9(c): the shared skin colours .cxp-score-good's TEXT
   (blue) but leaves middle/poor plain dark text, so one tier reads as
   singled out for no reason in a table that is just an issue COUNT, not a
   verdict. All three tiers now share one neutral colour. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-stack-table td strong.cxp-score-good,
body.fi-panel-portal .cxp-health-page .cxp-wh3-stack-table td strong.cxp-score-middle,
body.fi-panel-portal .cxp-health-page .cxp-wh3-stack-table td strong.cxp-score-poor { background: none !important; color: var(--foreground) !important; }
/* Round 6 item 3: one joined-string badge ("High priority + Medium priority
   findings") measured clipped at the record's right edge live at 390 - an
   earlier fixture missing the app's real box-sizing:border-box reset had
   wrongly reported it as wrapping. Now rendered as SEPARATE badges (one per
   severity, from a PHP array - see severity_notes in WebsiteHealth.php), on
   their own row under the (now block-level) issue title, each free to wrap
   or sit on its own line rather than fighting for one line's width. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-note-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-severity-note { display: inline-block; max-width: 100%; white-space: normal !important; overflow: visible !important; text-overflow: clip !important; line-height: 1.3; }
/* Defensive: the shared stat-card's own meta/explanation text must wrap on
   this page's KPI tiles too, regardless of whatever nowrap/ellipsis the
   base component sets for its usual short single-line captions elsewhere. */
body.fi-panel-portal .cxp-health-page .cxp-stat-card__meta,
body.fi-panel-portal .cxp-health-page .cxp-stat-card__verdict,
body.fi-panel-portal .cxp-health-page .cxp-wh3-tile .cxp-stat-card__meta,
body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis .cxp-stat-card__meta { white-space: normal !important; overflow: visible !important; text-overflow: clip !important; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pagination { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 20px 16px; flex-wrap: wrap; color: var(--muted-foreground); font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages { display: flex; gap: 5px; flex-wrap: wrap; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages button { min-width: 32px; min-height: 32px; padding: 0; border: 1px solid var(--border); border-radius: 7px; background: var(--card); color: var(--foreground); cursor: pointer; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages button.is-active { color: #fff; border-color: transparent; background: linear-gradient(90deg, var(--primary), var(--cxp-wh3-pink)); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages button:disabled { opacity: .4; cursor: not-allowed; }

/* Pages / Resources tables: ten technical columns cannot honestly compress
   below ~900px, but at >= 1280px they fit the card without a scrollbar - the
   in-card scroller only actually shows once the viewport narrows below that
   (overflow-x:auto is a no-op while the table fits).
   Item 2 (batch 7): a table with `table-layout:fixed` and `width:auto` is
   NOT the same as a normal block box - the spec resolves its used width as
   the GREATER of the containing block's width or the sum of the column
   widths, so leaving Page URL as the one column WITHOUT an explicit width
   let its content-derived intrinsic width (a long URL span) push the real
   total past the wrapper even though `width:auto` was already correctly
   winning over the generic `.cxp-wh3-table{width:100%}` rule - measured
   live at ~1130px against a 1084px card. Giving every column, INCLUDING
   Page URL, an explicit `width` makes the total fully deterministic
   (56+72+84+76+120+92+60+72+70+220 = 922px) and immune to content length. */
/* Item 3 (gate #2, batch 9): table-layout:fixed with an absolute-pixel <col>
   width set on EVERY column (including URL) makes the browser honor those
   pixel widths as a floor and grow the table past a narrower wrapper rather
   than shrink them - exactly the measured 1130px-in-1084px overflow. Fixed:
   width is now 100% of the wrapper (not auto), the 9 non-URL columns get the
   coordinator's exact px widths (654px total), and URL gets NO declared
   width at all - table-layout:fixed distributes 100% minus 654px to the one
   column left unsized, so the table can never exceed the wrapper. */
/* Batch 13 item 1: at 1024-1280px viewports the real content wrapper is
   narrower than the 774px sum of the 9 fixed columns, so width:100% forced
   the table smaller than that sum - table-layout:fixed then PROPORTIONALLY
   SHRUNK every explicitly-sized column (Protocol/HTTP status/Indexable/
   Issues/Keywords all measured under their set width) rather than letting
   the wrapper scroll. min-width:1040px (> 774px + a real URL column) keeps
   every fixed column at its exact px value at any width below 1280 - the
   wrapper's own overflow-x:auto (unconditional, see .cxp-wh3-table-wrap)
   scrolls instead of the columns shrinking. At >=1280 the wrapper is wide
   enough that min-width has no effect and width:100% alone governs, same
   as before. */
/* 924px = the table's width at the 1280 fit point (7 visible fixed columns = 544px + a 380px URL
   column), so PAGE URL is exactly as wide at 1279 (scrolling) as at 1280 (fitting) - no jump
   across the breakpoint, and less horizontal scrolling on phones than the old 1040. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table { width: 100%; min-width: 924px; table-layout: fixed; }
@media (min-width: 1280px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table { min-width: 0; }
}
/* Round 6 item 4: headers were visually running together ("ISSUES
   ↓TRAFFIC", "KEYWORDSPROTOC"). Measured each nowrap header's actual
   rendered text width (Playwright, real font/weight/letter-spacing)
   against its content-box width - every nowrap column but Traffic was
   overflowing its own cell (Issues +6px, Indexable +14px, Depth +4px,
   Keywords +13px, Protocol +28px), which is what bled into the neighbor.
   Widened each past its measured overflow with a few px of margin. The
   unsized URL column absorbs the difference automatically. */
/* Batch 14: "Issues ↓" measured overflowing its cell by 7px live - the
   sort arrow's own margin-left wasn't accounted for in the earlier
   text-width check (a flattened text clone measures the space between
   "Issues" and the glyph as one normal character gap, not the arrow
   span's actual 5px margin + glyph box). Widened +8px and shrank the
   arrow's margin (below) for a real safety margin either baseline. */
/* ONE set of column widths at every viewport (2026-09-05 01:55). A separate wider set above
   1439 made PAGE URL jump from 545px at 1439 to 470px at 1440 - the primary column must be
   monotonic across widths. These are the measured minimum-safe widths with 6px cell padding
   (see below); the two wrapping headers (Indexability status, Referring pages) sit at the
   width their longest single word needs. Measured: REFERRING at 76 overflowed by 6 -> 82. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-issues { width: 78px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-traffic { width: 72px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-status { width: 64px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-indexstatus { width: 104px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-refpages { width: 82px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-depth { width: 56px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-keywords { width: 88px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-protocol { width: 88px; }
/* The two headers whose label is wider than their column allow a wrap to a
   second line instead of clipping ("PROT") or forcing the column wider. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table th.cxp-wh3-col-indexstatus,
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table th.cxp-wh3-col-refpages,
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table th.cxp-wh3-col-status { white-space: normal; line-height: 1.25; }
/* Round 6 item 4: explicit 8px horizontal padding on every header/cell, and
   12px on the right of the LAST column only (Protocol), which previously
   touched the card's right edge with only the general padding. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table th,
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table td { padding-left: 6px; padding-right: 6px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table th:last-child,
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table td:last-child { padding-right: 10px; }
/* Batch 15 item 3 (HIGH): at 1280-1439 the numeric columns held their full
   desktop widths while PAGE URL, the column customers actually read, was
   squeezed to ~15% of the table. IMPORTANT DISCOVERY - the coordinator's
   suggested widths (Traffic 56/Referring 72/Depth 48/Keywords 60/
   Protocol 56/Indexable 64) genuinely overflow: Traffic/Depth/Keywords/
   Protocol/Indexable are each a SINGLE unbroken word (no space to wrap
   at), so white-space:normal cannot shrink them the way it shrinks the
   three real two-word headers - measured with getBoundingClientRect on
   the real button-wrapped markup (a naive scrollWidth-vs-clientWidth
   check on the <button> itself misses this, since an inline-block button
   just renders at its natural unwrapped width regardless of its <th>'s
   assigned column width, rather than reporting internal overflow).
   Every column below is set to its OWN measured minimum-safe width
   (natural text width + padding + a few px), so none of them can overflow
   - this recovers what real slack exists (mainly HTTP status, which does
   wrap) rather than the exact suggested numbers, which were not
   achievable without clipping or abbreviating a label. Net effect:
   PAGE URL moves from ~15% to ~37-44% across this band (measured at 1280
   and 1439) - short of a literal 40% floor at the very bottom of the
   range without a label/abbreviation change, but zero header overflow
   throughout, which is the hard requirement. MUST come after the
   unconditional base .cxp-wh3-col-* rules above - a same-specificity tie
   is decided by source order (the batch-13 quicklist-table lesson). */
/* Batch 18 item M7: "Not indexable - non-200 status" wrapped to 3 lines
   inside its 104px column (71px rows vs 46px). The coloured dot is
   dropped from THIS table only and the status text itself is coloured
   (good/bad tokens already exist elsewhere on the page); the label is
   split at " - " in the blade into a bold "Not indexable"/"Ok" line and
   a muted 12px reason line, so it reads on 2 lines instead of wrapping
   the whole joined string. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table td .cxp-wh3-status { display: block; white-space: normal; overflow-wrap: anywhere; line-height: 1.3; font-weight: 700; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table td .cxp-wh3-status-good { color: #04663a; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table td .cxp-wh3-status-bad { color: #b42318; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table td .cxp-wh3-status-reason { display: block; margin-top: 2px; color: var(--muted-foreground); font-weight: 400; font-size: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-btn-compact { min-height: 24px; min-width: 30px; padding: 0 6px; font-size: 12px; }
/* Sticky first column (Page URL) - a no-op except while the table actually
   scrolls (< 1280px / mobile), per Sol's note that the URL stays the first
   reading target throughout that scroll. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table th:first-child,
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table td:first-child { position: sticky; left: 0; z-index: 1; background: var(--card); border-right: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table th:first-child { background: var(--muted, #f2f4f8); z-index: 2; }
/* Batch 19 item A1 (HIGH): :nth-child(even) counts the interleaved
   `tr.cxp-wh3-expanded` detail row too (it is still a <tr> in the DOM even
   while display:none at mobile, or visible-but-not-a-data-row at desktop),
   which is what flipped zebra parity below any open row on desktop and
   left rows 1-2 both white at 390 (the desktop expanded row, hidden by
   width:0 there, still occupied a counted position). A real class on data
   rows only ($loop->even in the blade) fixes both cases identically. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table tbody tr.cxp-wh3-row-even td { background: var(--muted, #f8f9fb); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table tbody tr.cxp-wh3-row-even td:first-child { background: var(--muted, #f8f9fb); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table { min-width: 720px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:first-child { width: 38%; }
/* Gate #9 MEDIUM: at 1440 the Resource URL column (530px) ellipsised 19 of 25 rows while five short columns
   held ~865px for 40-54px of content. From 768px the table is fixed-layout with measured widths on the five
   narrow columns and the URL column takes the remainder (~606px at 1440 = 56%, ~918px at 1920). */
@media (min-width: 768px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table { width: 100%; table-layout: fixed; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:first-child { width: auto; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:nth-child(2) { width: 100px; white-space: normal; line-height: 1.25; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:nth-child(3) { width: 104px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:nth-child(4) { width: 100px; white-space: normal; line-height: 1.25; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:nth-child(5) { width: 84px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:nth-child(6) { width: 110px; white-space: normal; line-height: 1.25; }
  /* Sol R14: the sparse client's long CDN URLs still ended in "..." at 586px - desktop resource URLs wrap
     (up to two lines) instead of truncating; the five short columns keep their measured widths. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table td:first-child .cxp-wh3-url { width: 0; min-width: 100%; white-space: normal; overflow-wrap: anywhere; text-overflow: clip; line-height: 1.35; }
}
/* Gate #9 lows: chip padding parity with the page's own chips; phone Score-guide label on its own line so
   "0-49 poor" is not orphaned; a narrower pinned URL column on the smallest phones. */
body.fi-panel-portal .cxp-health-page .cxp-stat-card.cxp-kpi2 .cxp-kpi2-chip { padding: 2px 8px !important; }
body.fi-panel-portal .cxp-health-page summary { display: inline-flex; align-items: center; min-height: 24px; }
@media (max-width: 430px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-band-guide strong { flex-basis: 100%; }
  /* type selectors added so these out-specify the <= 767 rules (200px / 744px) that come later in the file */
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table col.cxp-wh3-col-url { width: 160px; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table { min-width: 704px; }
}

/* Sort indicators (Pages table headers) */
body.fi-panel-portal .cxp-health-page .cxp-wh3-table th[aria-sort] { cursor: default; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-table th button span { margin-left: 4px; color: var(--primary); }
/* Sort arrow: a real glyph (never the escaped-then-uppercased "&DARR;" text)
   in its own inline-block span so it can never visually run into the next
   header's label. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-sort-arrow { display: inline-block; margin-left: 2px; text-transform: none; }

/* Columns disclosure */
body.fi-panel-portal .cxp-health-page .cxp-wh3-columns-panel { position: absolute; z-index: 5; top: calc(100% + 6px); right: 0; display: grid; gap: 6px; min-width: 190px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--card); box-shadow: 0 8px 24px rgba(23, 32, 51, .12); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-columns-panel label { display: flex; align-items: center; gap: 8px; color: var(--foreground); font-size: 12px; font-weight: 600; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-filter-row { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-filter-row select { width: 100%; }
/* Chevron on the Resources "Pages using it" disclosure - rotates via the
   button's own aria-expanded state, no JS beyond the existing Livewire toggle. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-chevron { display: inline-block; width: 0; height: 0; margin-left: 2px; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid currentColor; transition: transform .15s ease; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-btn-chevron[aria-expanded="true"] .cxp-wh3-chevron { transform: rotate(180deg); }
/* !important: the mobile stack/scroll rules below force `display:table-cell`
   on every td at equal-or-later specificity - a hidden column must win
   regardless of viewport width. */
body.fi-panel-portal .cxp-health-page th.cxp-wh3-col-hidden,
body.fi-panel-portal .cxp-health-page td.cxp-wh3-col-hidden,
body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table th.cxp-wh3-col-hidden,
body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table td.cxp-wh3-col-hidden { display: none !important; }
/* Measured 2026-09-05 01:30: at 390 the mobile rule `table.cxp-wh3-pages-table td { display:table-cell !important }`
   (0,3,2) out-specified the plain td.cxp-wh3-col-hidden hide (0,3,1), so the hidden Protocol column was
   still rendered on phones. The table-qualified selectors above tie its specificity and win by source order. */
/* Batch 17 item 2: hiding a column's th/td alone does NOT reclaim its
   width under table-layout:fixed - the <col> element still reserves that
   column's assigned px regardless of whether its cells are display:none.
   Hiding the <col> itself (now also bound to the same cols.* toggles)
   collapses the column out of the table's width calculation entirely,
   which is what makes "hide Protocol by default" actually give that
   width back to PAGE URL rather than just leaving a blank column. */
body.fi-panel-portal .cxp-health-page col.cxp-wh3-col-hidden { display: none; }

/* Per-row expanded record (Pages issue count / Resources source-page list) */
body.fi-panel-portal .cxp-health-page tr.cxp-wh3-expanded td { padding: 14px; background: color-mix(in srgb, var(--primary) 4%, var(--card)); border-top: 1px solid var(--border); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-expanded-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; color: var(--foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-expanded .cxp-wh3-page-detail-body { padding: 0; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-expanded-mobile { display: none; }
/* Batch 19 item A1: scrim + grab bar only ever exist in the DOM at the
   same time as .cxp-wh3-expanded-mobile ($openPage set) - desktop hides
   both unconditionally here, the <=767px block below turns them into the
   real bottom sheet. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-sheet-scrim { display: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-sheet-grab { display: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-source-list { display: grid; gap: 5px; margin: 0; padding: 0; list-style: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-source-list li { overflow-wrap: anywhere; color: var(--muted-foreground); font-size: 12px; }

/* Rows-per-page selector + "Page X of Y" */
/* Batch 13 item 7 (LOW): "Rows per page" measured wrapping to 3 lines at
   390 - the label's own text has no explicit nowrap, and inside a
   flex-direction:column pagination bar at mobile there is nothing forcing
   it to stay on one line. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-perpage { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-perpage select { min-width: 64px; }

/* Mobile scroll cue for the Pages table - hidden on desktop, where the
   table already fits without scrolling per the >= 1280px column budget. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-mobile-scroll-hint { display: none; }

/* Overview / Issues tables: compact record cards at mobile - two short
   header cells then full-width meaning/action rows. Never combined with
   the horizontal-scroll treatment above on the same table. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-stack-table { width: 100%; }
/* Item 7 (batch 7): "Quick audit lists" left its default auto table layout
   to size columns, which measured ~463px of dead space in LIST - Pages and
   Action are short, fixed-width, right-aligned; List gets the remainder. */
/* Item 5 (gate #2, batch 9; reversed batch 13 item 5): capping List at
   420px (width:auto + 3 explicit widths, so fixed-layout honors them
   literally) fixed the earlier "804px for ~100px of text" overflow, but
   left a 384px dead band in a 1084px card once the cap itself became the
   defect. Reverting to width:100% with List UNSIZED (the same "one
   flexible column absorbs the remainder" pattern used on the Pages table)
   - List gets 100% minus the two compact columns, Pages/Action stay
   compact and fixed. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-table { width: 100%; table-layout: fixed; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-table .cxp-wh3-col-compact { width: 110px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-table .cxp-wh3-col-right { text-align: right; }
/* Batch 18 item M8: "Quick audit lists" is no longer a table (64-70% of
   every row was dead space between LIST and PAGES/ACTION) - it is now 4
   balanced tiles in the page's own .cxp-wh3-tile mini-grid language. The
   .cxp-wh3-quicklist-table rules above are now dead (kept, not deleted,
   in case a future round reverts). */
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-top: 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-tile { justify-content: flex-start; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-action { margin-top: auto; padding-top: 10px; }
@media (max-width: 1023px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Page detail expander (Pages tab) */
body.fi-panel-portal .cxp-health-page .cxp-wh3-page-detail { margin: 0 20px 16px; overflow: hidden; border: 1px solid color-mix(in srgb, var(--primary) 35%, var(--border)); border-radius: 10px; background: color-mix(in srgb, var(--primary) 4%, var(--card)); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-page-detail summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 14px; color: var(--foreground); font-weight: 700; font-size: 12.5px; cursor: pointer; list-style: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-page-detail summary::-webkit-details-marker { display: none; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-page-detail-body { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; padding: 0 14px 14px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-detail-group { min-width: 0; padding: 12px; border: 1px solid var(--border); border-radius: 9px; background: var(--card); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-detail-group h4 { margin: 0 0 8px; color: var(--foreground); font-size: 12.5px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-detail-group p { margin: 5px 0; color: var(--muted-foreground); font-size: 12px; overflow-wrap: anywhere; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-detail-group strong { color: var(--foreground); }

/* --------------------------------------------------------------------
   Failed-crawl state
   -------------------------------------------------------------------- */
body.fi-panel-portal .cxp-health-page .cxp-wh3-failed { display: grid; grid-template-columns: 78px minmax(0, 1fr); gap: 16px; align-items: center; padding: 18px; border-color: var(--cxp-wh3-bad-border) !important; background: var(--cxp-wh3-bad-bg); }
body.fi-panel-portal .cxp-health-page .cxp-wh3-failed-icon { display: grid; place-items: center; width: 62px; height: 62px; border: 1px solid var(--cxp-wh3-bad-border); border-radius: 16px; background: #fff; color: var(--cxp-bad-strong, #b42318); font-size: 24px; font-weight: 800; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-failed h3 { margin: 0; color: var(--foreground); font-size: 15px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-failed p { margin: 4px 0 0; color: var(--muted-foreground); font-size: 12px; }

/* ===========================================================================
   Responsive - mirrors Sol's own breakpoints (1100 / 760 / 430), which were
   already tuned for these exact components.
   =========================================================================== */
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card { grid-template-columns: 1fr 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-main { grid-column: 1 / -1; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card > div:nth-child(2) { border-left: 0; border-top: 1px solid var(--border); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card > div:nth-child(3) { border-top: 1px solid var(--border); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-connections { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-section-head,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-card-head,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-context { align-items: flex-start; flex-direction: column; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card > div + div { border-left: 0; border-top: 1px solid var(--border); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-main { grid-template-columns: 104px minmax(0, 1fr); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-ring { width: 104px; height: 104px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-ring-center strong { font-size: 28px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpi { min-height: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-big { font-size: 24px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-two,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-donut-layout,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-ring-list { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-donut,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-small-donut { margin: 0 auto; }
  /* Crawl-structure minis become a snap-aligned horizontal scroller - each
     mini-chart keeps its own labels and scale intact rather than being
     squeezed into a 2-up grid. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-grid { display: flex; grid-template-columns: none; overflow-x: auto; scroll-snap-type: x proximity; padding-bottom: 4px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mini { min-width: 230px; scroll-snap-align: start; }
  /* Work sequence becomes a vertical evidence rail. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-work-grid { grid-template-columns: 1fr; gap: 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step { display: grid; grid-template-columns: 38px minmax(0, 1fr); gap: 0 12px; padding: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step::before { top: 34px; bottom: -20px; left: 17px; right: auto; width: 2px; height: auto; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-work-icon { position: relative; grid-row: 1 / 4; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step small,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step strong,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-work-step p { grid-column: 2; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-connection-summary { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-ready { padding: 0 0 14px; border-right: 0; border-bottom: 1px solid var(--border); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-connections { grid-template-columns: 1fr 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-card > .kt-card-header { padding: 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-wrap,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-page-detail { margin-left: 16px; margin-right: 16px; }
  /* Item 6: a right-edge fade plus a one-line hint make the horizontal
     scroller on the Pages table discoverable - at 390px only Page URL was
     otherwise visible with no cue that nine more columns exist. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mobile-scroll-hint { display: block; margin: 0 16px 8px; color: var(--muted-foreground); font-size: 12px; font-weight: 650; }
  /* The fade lives on the WRAPPER'S PARENT (a plain static class, not a
     :has() selector whose match/support was unverifiable live) so its
     background-image is never at the mercy of whatever :has() resolved to
     in the field. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-scroll-fade { position: relative; margin-left: 16px; margin-right: 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-scroll-fade > .cxp-wh3-table-wrap { margin-left: 0; margin-right: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-scroll-fade::after { content: ""; position: absolute; top: 1px; right: 1px; bottom: 1px; width: 28px; background: linear-gradient(to right, transparent, var(--card)); pointer-events: none; }
  /* Item 5 (batch 8): the category-chart scroller sits inside a
     .cxp-wh3-pad article (20px padding already on every side) rather than
     the zero-padding table card, so the generic scroll-fade's own 16px
     side margins would double the left gutter and misalign the chart under
     its own header - zero them out here instead. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pad .cxp-wh3-scroll-fade { margin-left: 0; margin-right: 0; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pad .cxp-wh3-mobile-scroll-hint { margin-left: 0; margin-right: 0; }
  /* Batch 19 item A6: the inner Overview sub-tabs live directly in
     .cxp-map-details (not a .cxp-wh3-pad card) - same zero-margin
     treatment so the generic scroller's 16px side margins don't shift the
     tab strip out of alignment with the rest of the panel. */
  body.fi-panel-portal .cxp-health-page .cxp-map-details .cxp-wh3-scroll-fade { margin-left: 0; margin-right: 0; }
  /* A visible, styled horizontal scrollbar on touch widths - the fade alone
     is a hint, the scrollbar itself is the proof the card actually moves. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-wrap { scrollbar-width: thin; scrollbar-color: var(--muted-foreground) transparent; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-wrap::-webkit-scrollbar { height: 7px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-wrap::-webkit-scrollbar-thumb { background: var(--muted-foreground); border-radius: 999px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-wrap::-webkit-scrollbar-track { background: transparent; }
  /* Item 1 (batch 7, HIGH; revised batch 13; SUPERSEDED batch 19 item A2)
     used to drop sticky entirely below 768px because an unconstrained
     first column measured 341px inside a 302px scroller - the URL column
     stayed unsized so the reader saw either the URL or the numbers, never
     both, and after scrolling right the visible left strip was just the
     empty tail of a 488px-wide cell. Batch 19 fixes the ROOT CAUSE (an
     unsized URL column) instead of disabling sticky: see
     .cxp-wh3-col-url below, which caps the column so sticky has a sane
     width to stick. */
  /* Batch 19 item A2 (HIGH): below 768px the Page URL column is capped to
     200px (well over the col's own ellipsis/title-attribute floor) so it
     stops sizing itself to the longest URL in the crawl (measured 488px on
     one client, 380px on another). With a real, bounded width, restoring
     sticky (the page's own base th:first-child/td:first-child rule above,
     no longer overridden to `position:static`) means the URL and its
     numbers are visible together at any scroll position, and the sticky
     cell's own opaque background (base rule) always paints over whatever
     column has scrolled underneath it. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table .cxp-wh3-col-url { width: 200px; }
  /* The table's own min-width must shrink to match (200 + the 544px sum of
     the 8 visible fixed columns = 744px) - leaving it at the >=1280 value
     of 924px would give table-layout:fixed 180px of unclaimed space, which
     browsers redistribute back across every column (URL included) rather
     than leaving it unused, silently widening URL again despite its own
     explicit width. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-table { min-width: 744px; }
  /* The phone detail sheet and its scrim are position:fixed, but the portal's reveal motion leaves an inline
     `transform: translateY(0px)` on every card, and any transform makes the card the containing block for
     fixed descendants - measured 2026-09-05: the scrim sat at y 216-1855 (card coordinates) and the sheet at
     y 1163, both outside the 844px viewport. Dropping the (identity) transform on this one card below 768px
     lets the sheet position against the viewport. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-card { transform: none !important; }
  /* Batch 19 item A2 (HIGH): the Resources table had the exact same
     "URL only, everything else scrolled off with no cue" problem as Pages
     - no sticky, no cap, and (unlike Pages) no hint line at all. Capping
     the first column and forcing table-layout:fixed at this width so the
     cap actually holds (this table has no <colgroup>, so without fixed
     layout a width on th/td is only ever a suggestion the browser can
     ignore); the 5 remaining columns share the rest of min-width:720px
     roughly evenly, which is an acceptable tradeoff for a secondary table.
     !important on width/max-width: this same media query's own later rule
     (`table.cxp-wh3-resource-table td { width:auto !important }`, kept for
     the other 5 columns) would otherwise beat this at equal importance by
     source order alone. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table { table-layout: fixed; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:first-child,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    width: 200px !important;
    max-width: 200px !important;
    background: var(--card);
    border-right: 1px solid var(--border);
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table th:first-child { background: var(--muted, #f2f4f8); z-index: 2; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-resource-table tbody tr:nth-child(even) td:first-child { background: var(--muted, #f8f9fb); }
  /* Item 10 (batch 7, LOW): pager buttons measured 32px - below the 44px
     touch-target floor at 390. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pages button,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pagination .cxp-wh3-btn { min-width: 44px; min-height: 44px; }
  /* Item 10 (batch 7, LOW): the 200px desktop min-height on an empty/not-
     collected state (e.g. Resources with no crawl yet, often the last
     visible card on the page) reads as a large dead band before the footer
     on a 390px-wide screen - a mobile message needs far less vertical room
     to centre in. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-empty { min-height: 120px; padding: 20px; }
  /* Item 3 (batch 7, MED): the Columns/Filters disclosure opened off the
     left edge of the viewport (left:-67px) at 390 - anchor it to the
     button's own right edge and clamp its width so it can never leave the
     card. Escape-to-close is wired in the blade (a native <details> would
     get this for free; the Alpine popover needs the explicit listener). */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-columns-panel { left: auto; right: 0; max-width: calc(100vw - 32px); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-note { margin: 0 16px 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pagination { margin: 0 16px 16px; flex-direction: column; align-items: flex-start; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-page-detail-body { grid-template-columns: 1fr 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-failed { grid-template-columns: 56px minmax(0, 1fr); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-failed-icon { width: 52px; height: 52px; }

  /* Overview/Issues stack-tables: card-per-row, two short header cells then
     full-width meaning/action rows - the accepted mobile shape for these
     tables (never combined with the Pages/Resources scroller below). */
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table tbody,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table tr,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table td { display: block !important; width: 100% !important; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table thead { display: none !important; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table tbody { display: grid !important; gap: 10px; }
  /* Batch 13 item 3: this row's overflow:hidden existed only to clip the
     grid's square corners to the border-radius - a Chromium repro shows the
     grid auto-sizing correctly around wrapped severity badges, but a real
     device measured the row clipped 38px short of its content (339px
     content in a 300px box), a plausible mobile-WebKit grid+overflow
     interaction. Since content should never be sacrificed for a decorative
     corner clip, overflow is visible - the border-radius still reads fine
     in practice because child content keeps enough inset padding to never
     reach the exact corner pixel. */
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table tr { display: grid !important; grid-template-columns: 80px minmax(0, 1fr) !important; overflow: visible; border: 1px solid var(--border); border-radius: 10px; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table td { height: auto !important; min-height: 40px; padding: 9px 11px; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table td::before { content: attr(data-label); display: block; margin-bottom: 4px; color: var(--muted-foreground); font-size: 12px; font-weight: 750; text-transform: uppercase; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table td:nth-child(1) { grid-column: 1; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table td:nth-child(2) { grid-column: 2; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-stack-table td:nth-child(n+3) { grid-column: 1 / -1; }

  /* Batch 13 item 5: Quick audit lists is ALSO a .cxp-wh3-stack-table, but
     the generic grid above (item 1 in an 80px col, item 2 flexible, item
     3+ full width below) is wrong for its meaning - List needs to be the
     WIDE column, not a cramped 80px one. Same selector specificity as the
     generic rules just above (3 classes + table/tr or td tags), so this
     MUST come after them in source order to actually win the tie - moving
     it earlier in the file (a separate, earlier @media block) silently
     lost to the generic rules below it, which is what shipped live with
     List still stuck in an 80px column. Standard grid auto-placement: a
     full-span item fills row 1 entirely, pushing the next two onto row 2. */
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-quicklist-table tr { grid-template-columns: 1fr 1fr !important; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-quicklist-table td:nth-child(1) { grid-column: 1 / -1; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-quicklist-table td:nth-child(2) { grid-column: 1; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-quicklist-table td:nth-child(3) { grid-column: 2; }

  /* Pages/Resources tables KEEP their real table layout and scroll inside
     the card wrapper instead of stacking - the skin's own mobile rule
     forces `display:block !important` on every table, which would silently
     turn this ten-column table into an unreadable single column, so both
     the table and its cells are pinned back to table display here at equal
     !important weight, scoped tightly enough to win. */
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-resource-table { display: table !important; width: auto !important; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table thead,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-resource-table thead { display: table-header-group !important; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table tbody,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-resource-table tbody { display: table-row-group !important; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table tr,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-resource-table tr { display: table-row !important; }
  /* Batch 13 item 1: this cell-level width:auto!important used to beat the
     skin's own cell width rules, but it ALSO beat every .cxp-wh3-col-*
     column-width rule (same or lower specificity, later in the cascade),
     collapsing the pages table's columns to whatever the fixed-layout
     auto-width algorithm guessed - measured as low as 82px on the URL
     column. Pages-table cells keep display:table-cell but no longer force
     width - individual .cxp-wh3-col-* widths (desktop-authored, unchanged
     below 1280 now that the table has min-width:1040px) apply instead.
     Resources table is untouched (out of scope this round). */
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table th,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table td { display: table-cell !important; }
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-resource-table th,
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-resource-table td { display: table-cell !important; width: auto !important; }

  /* Item 4 (batch 8): the inline expanded <tr> lives inside the pages
     table's fixed 862px column-width sum, so its four detail panels were
     desktop-width squeezed into a 390px scroller with no way to read them
     without scrolling mid-sentence. Hide the inline row and show the
     duplicate block-level ".cxp-wh3-expanded-mobile" rendered just after
     the table wrapper instead - a plain block sized by the card, not the
     table, so it stacks single-column at full card width. */
  body.fi-panel-portal .cxp-health-page table.cxp-wh3-pages-table tr.cxp-wh3-expanded { display: none !important; }
  /* Batch 19 item A1 (HIGH): gate 7 measured this block rendering AFTER
     every visible table row (up to 1,213px below the tapped row on a full
     25-row page) - a plain block-level sibling of the table-wrap is
     structurally right next to the table in the DOM, but with 25 rows of
     content above it the customer sees nothing happen in the viewport
     after tapping. Converted to a FIXED bottom sheet (same language as
     the dashboard's Widgets-menu sheet: grab bar, sticky header + 44px
     Close, max-height 82vh with its own internal scroll, rounded top,
     shadow, a scrim behind it) so it always appears where the customer is
     looking, regardless of scroll position or which row was tapped -
     opened by the existing openPageIssues() action (this whole block only
     exists in the DOM while a page is open) and closed by the existing
     closePageIssues() action or Escape. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-sheet-scrim {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 40;
    background: rgba(16, 24, 40, .45);
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-expanded-mobile {
    display: flex;
    flex-direction: column;
    position: fixed;
    z-index: 41;
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 82vh;
    margin: 0;
    padding: 8px 16px calc(16px + env(safe-area-inset-bottom, 0px));
    border: 1px solid var(--border);
    border-bottom: 0;
    border-radius: 16px 16px 0 0;
    background: var(--card);
    box-shadow: 0 -12px 32px rgba(16, 24, 40, .22);
    overflow-y: auto;
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-sheet-grab {
    display: block;
    flex: none;
    width: 40px;
    height: 4px;
    margin: 0 auto 10px;
    border-radius: 999px;
    background: var(--border);
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-expanded-mobile .cxp-wh3-expanded-head {
    position: sticky;
    top: 0;
    padding-top: 2px;
    padding-bottom: 8px;
    background: var(--card);
    z-index: 1;
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-sheet-close { min-width: 44px; min-height: 44px; justify-content: center; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-expanded-mobile .cxp-wh3-page-detail-body { grid-template-columns: 1fr; padding: 0; margin-top: 10px; }
}

@media (max-width: 430px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-card > div { padding: 16px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-main { grid-template-columns: 92px minmax(0, 1fr); gap: 14px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-ring { width: 92px; height: 92px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-score-copy h2 { font-size: 18px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis { grid-template-columns: 1fr 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-connections { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-page-detail-body { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-controls { width: 100%; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-table-controls .cxp-wh3-btn { flex: 0 1 auto; } /* gate #11 low: flex:1 stretched a lone wrapped Export pill to 296px */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-chip { white-space: normal; }
  /* Batch 15 item 2 (HIGH): the generic .cxp-wh3-chip white-space:normal
     just above let "Needs work" wrap to 2 lines (37px) beside "Good" at
     22px in the phone/computer speed tiles. Restored nowrap for this
     specific chip and, since the ring+copy grid only leaves ~64px for the
     copy column in a half-width mobile tile (not enough for "Needs work"
     even on one line), stacked the ring above the copy instead of beside
     it so the copy gets the tile's full width. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-speed-kpi .cxp-wh3-chip { white-space: nowrap; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-speed-kpi { grid-template-columns: 1fr; justify-items: center; text-align: center; gap: 8px; }
}

/* Item 4 (gate #2, batch 9): target is <=767px, one pixel past the page's
   own 760px mobile breakpoint - a standalone query so the touch-target fix
   is guaranteed at the exact stated boundary without moving every other
   760px rule in this file. */
@media (max-width: 767px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pages button,
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pagination .cxp-wh3-btn { min-width: 44px; min-height: 44px; }
}

/* Item 6 (gate #2, batch 9): the "Priority" badge in "What we're working on
   next" reuses .cxp-rk2-chip--good/bad/warn/neutral from the shared
   Rankings-v2 skin verbatim (per FRONTEND_BRIEF.md) - measured 2.6:1 live,
   which this page cannot fix at the source file. Overridden here, scoped to
   .cxp-health-page only, with the same hardcoded verified colors used for
   our own chip/severity classes above. */
/* 12px floor for the shared stat-card chip too (measured 11px x4 on the domain-metric tiles once the
   Sep 5 crawl produced "no change" neutral chips - the kpi2 component sizes its chip at 11px). */
body.fi-panel-portal .cxp-health-page .cxp-kpi2-chip,
body.fi-panel-portal .cxp-health-page .cxp-stat-card.cxp-kpi2 .cxp-kpi2-chip,
body.fi-panel-portal .cxp-health-page .cxp-stat-card.cxp-kpi2.cxp-stat-card--compact .cxp-kpi2-chip { font-size: 12px !important; } /* the shared rule is `11px !important` (0,4,1) - same flag, higher specificity wins */
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--good { color: #04663a; }
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--bad { color: #b42318; }
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--warn { color: #8a4b00; }
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--neutral { color: #4b5563; }

/* Item 1 (gate #2, batch 9): TYPE FLOOR. 494 live text runs under 12px.
   Two causes, two fixes:
   (a) .cxp-rk2-chip/--warn/--neutral is reused verbatim from the shared
   Rankings-v2 skin (font-size: 11.5px) and this page never overrode it -
   fixed at equal-or-higher specificity than the skin's own
   `body.fi-panel-portal .cxp-health-page .cxp-rk2-chip` rule, so it wins
   regardless of stylesheet load order.
   (b) STRONG/SMALL/B/TH runs at 10-11.5px are children inheriting a small
   ancestor size (or, for <small>, the browser's own UA "smaller" default)
   with no explicit font-size of their own. A `:where()` selector carries
   ZERO specificity, so this floor cannot outrank any of this file's real
   sizing rules (e.g. `.cxp-wh3-ring-center strong` at 34px,
   `.cxp-wh3-mini-ring b` at 14px, `.cxp-wh3-big` at 30px) - it only wins
   for elements nothing else has explicitly sized, which is exactly the
   inherited-smallness case being fixed. SPAN is deliberately excluded
   from this floor: `.cxp-stat-card__value`/`.cxp-score-number` are bare
   2-class shared-skin rules on <span> at 44px/28px+ - the SAME specificity
   as a `.cxp-health-page` :where() floor, which a same-specificity tie
   would resolve by stylesheet order and could go the wrong way. The 51
   SPAN runs the gate measured are children of .cxp-rk2-chip and are
   already fixed by the chip-level rule below (an unstyled child inherits
   its parent's now-corrected 12px with no separate rule needed). */
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip { font-size: 12px !important; }
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--warn { font-size: 12px !important; }
body.fi-panel-portal .cxp-health-page .cxp-rk2-chip--neutral { font-size: 12px !important; }
body.fi-panel-portal .cxp-health-page :where(strong, small, b, th) { font-size: 12px; }

/* Batch 10 (gate #2 leftovers): two more shared-skin classes reused
   verbatim on this page at sub-12px, never overridden here before. */
body.fi-panel-portal .cxp-health-page .cxp-chart-axis-labels span { font-size: 12px !important; }
body.fi-panel-portal .cxp-health-page .cxp-wh2-run-date small { font-size: 12px !important; }

/* Batch 15 item 7 (LOW): the trend-chart component (locked) centers its
   last point exactly ON its 100%-left position (.cxp-trend-point has
   width:.55rem with transform:translate(-50%,-50%)), so half the dot's
   ~9px width sits past the plot's own right edge - measured clipped 4px.
   Cannot touch the component's own markup/CSS, so right padding on the
   plot container (this page's own scope) gives that overflow room to
   breathe instead of being clipped flush against the card edge. */
/* Batch 18 item M5: gate #6 still measured scrollWidth = clientWidth + 4
   on this page's plots after the batch-15 padding-right fix. Root cause:
   the overlay's `left:100%` is measured against the PLOT'S OWN PADDING
   BOX, whose right edge is unmoved by adding padding-right to that same
   element (padding-right only adds dead space PAST that edge, it cannot
   push the 0%/100% landmark inward) - so the marker's centered dot kept
   sitting exactly on the true edge regardless. The real fix is on the PHP
   side (WebsiteHealth::auditScoreTrend() and this file's own speed-chart
   $spPoints/$spDesktopPoints @php block): every series is now plotted
   into a 2%-98% band instead of 0-100%, so no point (and no polyline
   endpoint) is ever placed AT the edge in the first place. box-sizing is
   kept defensively (harmless either way) alongside the original padding. */
body.fi-panel-portal .cxp-health-page .cxp-trend-chart-plot { padding-right: 8px; box-sizing: border-box; }

/* Batch 17 item 1 (HIGH): "↑ 352 since last crawl" wrapped to 2 lines
   (37px vs 22px) inside a half-width tile at 390 - the tile is too narrow
   for the full sentence. Below 520px the chip prints the delta only
   ("↑ 352"), nowrap; the "since last crawl" fact now lives once in each
   section's own subtitle instead of on every tile's chip. */
@media (max-width: 520px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-tile .cxp-wh3-chip { white-space: nowrap; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-tile .cxp-wh3-chip-suffix { display: none; }
}

/* Batch 19 item A7: "Site speed over time" plots ~30 daily readings per
   series (up to 60 markers total) on a ~266px-wide mobile plot - visual
   noise, per the coordinator's LESS DATA rule (never smaller labels).
   Below 520px every marker but the first and last of each series is
   hidden; the line itself is untouched. Scoped to the speed chart's own
   id so the main score trend (which never has this many points) and the
   new compact pages-crawled trend are unaffected. */
@media (max-width: 520px) {
  body.fi-panel-portal .cxp-health-page #phone-speed-trend .cxp-trend-point:not(:first-child):not(:last-child) { display: none; }
}

/* Batch 17 item 5 (LOW): the Overview panel's inner sub-tabs (Issue
   breakdown / Open issues / Work we did / What we're working on next)
   measured wrapping into two uneven rows at 390 under the shared skin's
   own .cxp-map-tabs mechanism. A new class distinguishes these from the
   OUTER detail tabs (Overview/Pages/Resources/Issues), which keep the
   shared skin's own stacking untouched - only the inner strip becomes a
   single-row horizontal scroller, each tab keeping its natural width
   instead of being squeezed into a grid cell. */
/* Batch 18 item M13: gate #6 still measured this wrapping into two ragged
   rows at 390 - the house skin pins `.cxp-map-tabs.cxp-map-tabs.cxp-map-tabs
   { flex-wrap: wrap !important }` at TRIPLE class specificity (0,3,0),
   which beats the double-class selector below (0,2,0) regardless of
   !important or source order (same-!important ties go to specificity
   first, THEN source order). Requoted at quadruple specificity (0,4,0) so
   it wins outright, independent of load order. */
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-health-page .cxp-map-tabs.cxp-map-tabs.cxp-map-tabs.cxp-wh3-inner-tabs {
    display: flex !important;
    flex-wrap: nowrap !important;
    overflow-x: auto !important;
    justify-content: flex-start !important;
  }
  body.fi-panel-portal .cxp-health-page .cxp-map-tabs.cxp-map-tabs.cxp-map-tabs.cxp-wh3-inner-tabs > button {
    flex: 0 0 auto !important;
    white-space: nowrap !important;
  }
}

/* ===========================================================================
   Batch 18 (qa-inspector gate #6) - remaining items
   =========================================================================== */

/* Item M6: the mobile scroll hint on the Pages table is now driven by real
   overflow detection (Alpine checkPagesOverflow(), see the blade) instead
   of a viewport-width media query, so it also fires in the 1024-1279 band.
   `.cxp-wh3-mobile-scroll-hint`'s own display rules are left untouched for
   its OTHER use (the 13-category chart, still width-gated) - visibility
   here is fully controlled by the element's own inline `:style` binding,
   which always wins over these unconditional margin-only rules. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-hint { margin: 0 20px 8px; }
@media (max-width: 760px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-pages-hint { margin: 0 16px 8px; }
}
/* Measured 2026-09-05 at 390: the fade wrapper is a GRID item of .cxp-map-details, so its default
   min-width:auto let the nowrap sub-tab row (552px) widen it to 554px, which widened the whole grid
   track - the "What needs attention" card and the Quick audit tiles were then laid out 554px wide and
   clipped at the 336px card edge (page overflow still read 0 because the card clips). */
body.fi-panel-portal .cxp-health-page .cxp-wh3-scroll-fade { min-width: 0; max-width: 100%; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-scroll-fade.is-overflowing { position: relative; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-scroll-fade.is-overflowing::after {
  content: "";
  position: absolute;
  top: 1px;
  right: 1px;
  bottom: 1px;
  width: 28px;
  background: linear-gradient(to right, transparent, var(--card));
  pointer-events: none;
}

/* Item M3: "What changed"/"Crawl and indexing coverage" tile headers wrap
   the chip below the label ONLY for longer titles ("Checks passed") while
   shorter ones ("Notices") stay one line, so sibling tiles in the same row
   lose their shared value baseline at 390. Stacking the chip under the
   label UNCONDITIONALLY below 520px gives every tile in a row the same
   2-line header height regardless of title length. */
/* Gate #8: the same wrap happened at 1199-1440 in the coverage row ("Pages crawled" / "Indexable pages" chips
   dropped under the label while "Not indexable" / "Healthy pages" stayed inline -> values 25px apart), and
   in the links row the shared stat-card chip stacked in 3 of 4 tiles. One placement for every 4-tile row up
   to 1440: chip under the label. At 1920 everything fits inline. */
@media (max-width: 1559px) { /* gate #10: 1441-1559 (e.g. 1536 = 1920 at 125%) still mixed inline/below in the Links row */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-tile-head { flex-direction: column; align-items: flex-start; gap: 4px; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis .cxp-stat-card.cxp-kpi2 .cxp-stat-card__title-row { flex-direction: column !important; align-items: flex-start !important; gap: 4px !important; }
  /* Sol R13: the stat-card's delta chip lives in .cxp-kpi2-metric-line beside the value and wrapped under it in
     three Links tiles but stayed inline beside "492 pages" - one placement: the chip on its own line in all four. */
}
/* a flex-basis:100% + max-width:max-content chip still fit beside "492 pages" (the max-width caps the size the
   line-breaking uses), so the metric line becomes a two-row grid: value + unit on row 1, the chip alone on row 2.
   Sol R14: on phones (<= 520) the 1fr unit column got so narrow that "out of 100" stacked into three lines -
   the grid applies from 521px only; on phones the metric line keeps its flex wrap (every tile is narrow, so the
   chip wraps under the value in all of them) and the unit never breaks inside the phrase. */
@media (min-width: 521px) and (max-width: 1559px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis .cxp-stat-card.cxp-kpi2 .cxp-kpi2-metric-line { display: grid !important; grid-template-columns: auto minmax(0, 1fr) !important; align-items: baseline !important; gap: 4px 8px !important; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis .cxp-stat-card.cxp-kpi2 .cxp-kpi2-metric-line .cxp-kpi2-chip { grid-column: 1 / -1 !important; justify-self: start !important; }
}
body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis .cxp-stat-card.cxp-kpi2 .cxp-kpi2-unit-inline { white-space: nowrap; }
/* Gate #11: on phones the unit wrapped under the number only where the pair did not fit ("2 / out of 100" stacked,
   "6 links" inline), so sibling tiles read with two grammars - the unit always takes the line under the number. */
@media (max-width: 520px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis .cxp-stat-card.cxp-kpi2 .cxp-kpi2-unit-inline { flex-basis: 100%; }
}
/* Resources heading carries the total like the reference ("URL (330)") - muted, normal weight, same size */
body.fi-panel-portal .cxp-health-page .cxp-wh3-title-count { margin-left: 6px; color: var(--muted-foreground); font-weight: 500; }
/* One chip family on this page: the shared stat-card chip inherits the page sans (it was the monospace figure
   face) and its neutral text meets 4.5:1 (measured 4.39:1 before). */
body.fi-panel-portal .cxp-health-page .cxp-stat-card.cxp-kpi2 .cxp-kpi2-chip { font-family: Manrope, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif !important; letter-spacing: 0 !important; } /* `inherit` still computed to the mono figure face - name the page sans explicitly */
body.fi-panel-portal .cxp-health-page .cxp-stat-card.cxp-kpi2 .cxp-kpi2-chip--neutral { color: #3d4654 !important; }

/* Item M4: "Top issues by affected pages" ellipsized every title at a
   116px slot on mobile ("Pages nothing els...") - the label IS the
   information, the bar is decoration. `display:contents` on the wrapper
   promotes its children (the title and the bar track) to direct grid
   items of the row itself, so they can be placed on two separate lines
   (severity + full title, wrapping to 2 lines if needed; bar + count)
   without changing the desktop DOM/markup at all.
   Batch 19 item A8: gate 7 measured this ALSO clipping at 1024 (344px
   title -> 303px slot) and 1100 (312px -> 303px), just not as far down as
   520 - the 50%-max-width truncation on .cxp-wh3-list-label-wrap strong
   (base rule, still in force above this breakpoint) never accounted for a
   long two-line issue name at these mid widths. Breakpoint widened from
   520 to 1100 so the same two-line treatment covers both newly-reported
   widths without touching >1100 (already 0 clipped) or changing the
   layout itself. */
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row {
    display: grid;
    grid-template-columns: 22px 1fr auto;
    grid-template-areas: "sev title title" "bar bar count";
    align-items: center;
    row-gap: 4px;
    min-height: 0;
    padding: 10px 0;
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row .cxp-wh3-severity { grid-area: sev; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row .cxp-wh3-list-label-wrap { display: contents; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row .cxp-wh3-list-label-wrap strong {
    grid-area: title;
    max-width: 100%;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row .cxp-wh3-list-bar-track { grid-area: bar; }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-list-row button.cxp-wh3-link { grid-area: count; }
}

/* Item M11: sibling stat labels in the 2-col domain-signal / speed grids
   ("Pages Google knows about" vs "Referring domains", "Main content
   appears" vs "Layout stability") wrap unevenly at 390, so the VALUE below
   loses its shared row baseline. Reserving a 2-line label height in both
   grids keeps every tile in a row starting its value at the same offset
   regardless of that tile's own label length. Specificity matches the
   shared skin's own `!important` title-row rule (adds 3 more scoping
   classes) so it wins outright rather than depending on load order. */
@media (max-width: 520px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis .cxp-stat-card.cxp-kpi2 .cxp-stat-card__title-row {
    min-height: 40px !important;
    align-items: flex-start !important;
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpis .cxp-stat-card.cxp-kpi2 .cxp-stat-card__title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* Measured live: reserving only 34px (this tile's own 2-line height) left
     a 5px baseline gap against "Main content appears" (39px real content) -
     40px covers the tallest measured case so every sibling in a row shares
     the identical reserved height regardless of its own label length. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-kpi-label {
    min-height: 40px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

/* Item M12: "How the crawl is structured" was a horizontal snap-scroller
   at <=760px with no discoverability cue, hiding 2.5 of 4 cards. Below
   768px it becomes a plain 2x2 grid instead (same language as the rest of
   this page's mini-grids) - placed AFTER the existing 760px block so it
   wins the same-specificity tie by source order and fully replaces the
   flex/scroll-snap behaviour rather than layering on top of it. */
@media (max-width: 767px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    overflow-x: visible;
    scroll-snap-type: none;
    padding-bottom: 0;
  }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-grid .cxp-wh3-mini {
    min-width: 0;
    scroll-snap-align: none;
  }
  /* Each vbar is a flex item with no explicit min-width, so its default
     min-width:auto (its own label's min-content size) could still force
     the Server response mini wider than its 142px 2-col share even after
     the grid/mini-width fixes above - min-width:0 lets it actually shrink
     to the flexed size instead of overflowing. */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-vbar { min-width: 0; }
}

/* Coordinator follow-up 2026-09-05 04:40 (measured on the deployed batch 18 at 390, crops in build/qa/probe_b18):
   1. In the 2x2 crawl-structure grid the Server response card's four range labels ("Under 200ms" ...) collided
      into one unreadable line inside a 142px half-width card - there is no label short enough for four ~21px
      columns. Both bar-chart cards (the ones that contain .cxp-wh3-vbars) take the full row below 768px:
      rows become [Page depth] [Server response] [Pages that redirect | Detached pages] - no orphan, and the
      range labels get ~66px each, wrapping to two centred lines where needed.
   2. "Pages that redirect" wraps to two lines while "Detached pages" does not, so the two big values sat 19px
      apart - the card title reserves two lines in the 2-col layout.
   3. Quick audit tiles: "Non-indexable pages" / "Links to problematic pages" wrap while their row-mates do not,
      so 1 vs 50 and 195 vs 362 sat 18px apart. The stat line now hugs the bottom of the tile (tiles in a grid
      row already stretch to equal height), so every value in a row shares one baseline at every width. */
@media (max-width: 767px) {
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-grid .cxp-wh3-mini:has(.cxp-wh3-vbars) { grid-column: 1 / -1; }
  /* measured live: a 2-line title renders 36px, so 34 still left a 4px offset between the two half cards' values */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mini-grid .cxp-wh3-mini:not(:has(.cxp-wh3-vbars)) h3 { min-height: 38px; }
  /* a wrapped 2-line label shifted by the desktop's fixed 17px straddled the axis line ("Under" above it,
     "200ms" below) - move the label by its own height instead so it always sits fully under the axis */
  body.fi-panel-portal .cxp-health-page .cxp-wh3-vbar small { white-space: normal; text-align: center; line-height: 1.15; top: 0; transform: translateY(calc(100% + 4px)); }
  body.fi-panel-portal .cxp-health-page .cxp-wh3-mini:has(.cxp-wh3-vbars) .cxp-wh3-mini-note { margin-top: 44px; } /* measured: 2-line labels ended 3px above the note at 34px */
}
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-tile { display: flex; flex-direction: column; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-tile .cxp-wh3-stat-line { margin-top: auto; padding-top: 12px; }
body.fi-panel-portal .cxp-health-page .cxp-wh3-quicklist-tile .cxp-wh3-quicklist-action { margin-top: 0; }

/* ===== END WEBSITE HEALTH V3 (cxp-wh3) ===== */


/* ===== DASHBOARD WIDGETS (cxp-dw) ===== */
body.fi-panel-portal .cxp-dw-page .cxp-weekly-review {
  margin: 0 0 16px;
  padding: 16px 20px;
  border: 1px solid var(--cxp-dw-border);
  border-radius: 12px;
  background: var(--cxp-dw-surface);
  box-shadow: 0 1px 2px rgba(16,24,40,.04);
}
body.fi-panel-portal .cxp-dw-page .cxp-weekly-review-head {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
body.fi-panel-portal .cxp-dw-page .cxp-weekly-review-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}
body.fi-panel-portal .cxp-dw-page .cxp-weekly-review-label {
  color: var(--cxp-dw-muted);
  font-size: 12.5px;
  line-height: 1.45;
}
body.fi-panel-portal .cxp-dw-page .cxp-weekly-review-date {
  color: var(--cxp-dw-muted);
  font-size: 12px;
}
body.fi-panel-portal .cxp-dw-page .cxp-weekly-review h2 {
  margin: 0;
  /* CYT-951 qa-inspector fix (2026-09-10): var(--cxp-dw-ink) resolved wrong
     here, but hardcoding the literal wasn't enough on its own - the real
     collision is cxs-skin.css's site-wide "body.fi-panel-portal.fi-panel-portal
     h2[id]" rule, which exists to ink-tint explainer-button headings and
     matches ANY h2 carrying an id (ours only has one for aria-labelledby).
     That rule ties this one on specificity and wins on source order. Every
     OTHER dashboard card heading has no id, so none of them hit this - ours
     is the only widget title with one. !important, matching this codebase's
     own established pattern for "the shared skin keeps clobbering this". */
  font-size: 15px;
  font-weight: 700;
}
/* CYT-951 qa-inspector fix, round 2: cxs-skin.css's own h2[id]/:is(h1..h6)
   rules are ALSO !important and tie this rule's specificity exactly
   (0,3,2) - with both sides !important, source order alone decided it and
   cxs-skin.css loads later. An id selector (1,0,0) beats any class-based
   selector outright regardless of load order, so target the element's own
   id instead of fighting the tie again. */
#cxp-weekly-review-title {
  color: #1d222b !important;
}
body.fi-panel-portal .cxp-dw-page .cxp-weekly-review p {
  max-width: 780px;
  margin: 8px 0 0;
  color: var(--cxp-dw-body);
  font-size: 14px;
  line-height: 1.55;
  overflow-wrap: anywhere;
}

body.fi-panel-portal .cxp-dw-page {
  /* CYT-933 fix batch 10 #1 (HIGH). These used to fall back to
     --cxp-good-strong / --cxp-bad-strong / --cxp-warn-strong, which sounded
     like the right shared token names but resolve, on the LIVE page, to a
     BLUE oklch() value from a different skin block entirely - the fallback
     in var(--x, fallback) only ever applies when --x is undefined, not when
     it exists and is wrong. Hardcoded literals now - the exact
     rgb(4,102,58) green the Website health page itself renders for its good
     chip - so this never again depends on which skin block happens to win. */
  --cxp-dw-good: #04663a;
  --cxp-dw-good-bg: #e7f6ee;
  --cxp-dw-good-border: #b7e3cb;
  --cxp-dw-bad: #b42318;
  --cxp-dw-bad-bg: #fde8e8;
  --cxp-dw-bad-border: #f3b8b0;
  --cxp-dw-warn: #8a4b00;
  --cxp-dw-warn-bg: #fff4e5;
  --cxp-dw-warn-border: #f2d39b;
  --cxp-dw-ink: var(--portal-heading, var(--foreground));
  --cxp-dw-body: var(--portal-text, var(--foreground));
  --cxp-dw-muted: var(--portal-muted, var(--muted-foreground));
  --cxp-dw-surface: var(--portal-surface, var(--card));
  --cxp-dw-surface-soft: var(--portal-surface-soft, #f7f8fa);
  --cxp-dw-border: var(--portal-border, var(--border));
  --cxp-dw-primary: var(--primary);
  --cxp-dw-pink: #db2777;
  --cxp-dw-radius: 12px;
}

/* ==========================================================================
   Page head: title, "How this works", account/domain line, Widgets control.
   ========================================================================== */
body.fi-panel-portal .cxp-dw-page .cxp-dw-title-line { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-page-sub { margin-top: 5px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-account { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-top: 9px; color: var(--cxp-dw-muted); font-size: 12.5px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-account strong { color: var(--cxp-dw-ink); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-head-actions { position: relative; display: flex; align-items: center; gap: 10px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 34px; padding: 0 15px; border: 1px solid var(--cxp-dw-border); border-radius: 999px;
  background: var(--cxp-dw-surface); color: var(--cxp-dw-body); font-weight: 650; font-size: 12.5px; white-space: nowrap;
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-btn:hover { border-color: var(--cxp-dw-muted); text-decoration: none; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-btn-primary {
  border: 0; color: #fff; background-color: var(--cxp-dw-primary);
  background-image: linear-gradient(90deg, var(--cxp-dw-primary), var(--cxp-dw-pink));
  box-shadow: 0 5px 12px rgba(79, 70, 229, .18);
}

/* Widgets menu panel. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-menu {
  position: absolute; top: 44px; right: 0; z-index: 20; width: 330px; margin-bottom: 1px; padding: 8px;
  border: 1px solid var(--cxp-dw-border); border-radius: 12px; background: var(--cxp-dw-surface);
  box-shadow: 0 18px 45px rgba(16, 24, 40, .18);
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-menu[hidden] { display: none; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 8px 9px 10px; border-bottom: 1px solid var(--cxp-dw-border); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-head strong { display: block; color: var(--cxp-dw-ink); font-size: 14px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-head span { color: var(--cxp-dw-muted); font-size: 12px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-list { max-height: 430px; margin-bottom: 1px; overflow: auto; padding: 4px 0; border-radius: 8px; background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row { display: grid; grid-template-columns: 22px 20px minmax(0, 1fr); align-items: center; min-height: 35px; padding: 5px 8px; border-radius: 8px; color: var(--cxp-dw-body); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row:nth-child(odd) { background: var(--cxp-dw-surface); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row:nth-child(even) { background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row:hover { background: color-mix(in srgb, var(--cxp-dw-primary) 8%, transparent); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row small { color: var(--cxp-dw-muted) !important; font-size: 12px; margin-left: 4px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row.is-dragging { opacity: .45; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-grip { color: var(--cxp-dw-muted); cursor: grab; font-size: 16px; letter-spacing: -2px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-move { display: none; gap: 3px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-move button { width: 25px; height: 25px; padding: 0; border: 1px solid var(--cxp-dw-border); border-radius: 7px; background: var(--cxp-dw-surface); color: var(--cxp-dw-body); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row input { width: 15px; height: 15px; margin: 0; accent-color: var(--cxp-dw-primary); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-actions { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 8px 2px; border-top: 1px solid var(--cxp-dw-border); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-link-btn { padding: 5px 2px; border: 0; background: transparent; color: var(--cxp-dw-primary); font-size: 12px; font-weight: 650; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-done { min-height: 30px; padding: 0 13px; }
/* CYT-933 fix batch 8 #2. A 32px round icon button, identical for every
   widget card AND the widgets-menu header (never a bespoke smaller "x") -
   ALWAYS on a visible light circular background (not only on hover, which
   read as a thin grey glyph floating on nothing), with a darker foreground
   and a real 16px bold x drawn as an inline SVG (2px stroke) rather than
   the "x" glyph, whose weight varies by font/rendering. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-close {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0; border: 0; border-radius: 50%; cursor: pointer;
  background: var(--portal-surface-2, var(--cxp-dw-surface-soft)); color: var(--cxp-dw-body); line-height: 1;
  transition: background-color .12s ease, color .12s ease;
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-close-icon { display: block; width: 16px; height: 16px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-close:hover,
body.fi-panel-portal .cxp-dw-page .cxp-dw-close:focus-visible { background: var(--cxp-dw-border); color: var(--cxp-dw-ink); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-strip-close { width: 32px; height: 32px; }

/* ==========================================================================
   Widget stack / masonry.
   ========================================================================== */
body.fi-panel-portal .cxp-dw-page .cxp-dw-stack { display: grid; gap: 16px; margin-top: 4px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-masonry { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-column { display: grid; gap: 16px; min-width: 0; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget {
  position: relative; min-width: 0; overflow: hidden; border: 1px solid var(--cxp-dw-border);
  border-radius: var(--cxp-dw-radius); background: var(--cxp-dw-surface); box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 18px 20px 14px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-title-line { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget h2 { margin: 0; color: var(--cxp-dw-ink); font-size: 15px; line-height: 1.35; font-weight: 650; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-sub { margin: 4px 0 0; color: var(--cxp-dw-muted); font-size: 12.5px; line-height: 1.45; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-scope { display: inline-flex; align-items: center; min-height: 22px; padding: 2px 8px; border-radius: 999px; background: var(--cxp-dw-surface-soft); color: var(--cxp-dw-body); font-size: 12px; white-space: nowrap; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-body { padding: 0 20px 18px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-foot { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 12px 20px; border-top: 1px solid var(--cxp-dw-border); background: var(--cxp-dw-surface-soft); color: var(--cxp-dw-muted); font-size: 12px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-report-link { font-weight: 650; white-space: nowrap; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-strip-close { position: absolute; top: 8px; right: 8px; z-index: 2; }

/* Key metrics: plain dense divided cells (never <x-portal.stat-card> here -
   its empty state always draws a ring+"?", which the house rule forbids). */
body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); border-top: 1px solid var(--cxp-dw-border); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi { position: relative; min-width: 0; min-height: 118px; padding: 15px 16px 14px; border-right: 1px solid var(--cxp-dw-border); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi:last-child { border-right: 0; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-label { min-height: 32px; color: var(--cxp-dw-body); font-size: 12px; font-weight: 650; line-height: 1.3; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-value { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 9px; color: var(--cxp-dw-ink); font-size: 27px; line-height: 1; font-weight: 700; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
/* Measured 2026-09-05 at 390 (client 26): the 17px state text ("Connecting") sat 10px above the 27px numbers in
   the same Key-metrics row because both rows start at the same top. A one-line state now shares the numbers'
   baseline; a two-line state ("Collecting first reading") keeps its natural height. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-value.is-empty { min-height: 27px; align-items: flex-end; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-value.is-empty { font-size: 17px; letter-spacing: -.01em; color: var(--cxp-dw-muted); font-weight: 650; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-meta { margin-top: 9px; color: var(--cxp-dw-muted); font-size: 12px; line-height: 1.35; }

/* Score / value ring - construction B (SVG stroked with the shared brand gradient). */
body.fi-panel-portal .cxp-dw-page .cxp-dw-ring-row { display: grid; grid-template-columns: 72px minmax(0, 1fr); align-items: center; gap: 14px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-spark-block { display: flex; align-items: center; gap: 14px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-ring { position: relative; width: 72px; height: 72px; flex: 0 0 auto; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-ring circle { fill: none; stroke-width: 8; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-ring .track { stroke: var(--cxp-dw-border); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-ring .progress { stroke: url(#cxpRingBrand); stroke-linecap: round; stroke-dasharray: var(--ring) 100; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-big { color: var(--cxp-dw-ink); font-size: 31px; line-height: 1; font-weight: 700; letter-spacing: -.035em; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-unit { margin-left: 3px; color: var(--cxp-dw-muted); font-size: 12px; font-weight: 650; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-label { color: var(--cxp-dw-muted); font-size: 12px; }

/* Setup progress strip. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-setup { display: grid; grid-template-columns: 170px minmax(0, 1fr) auto; align-items: center; gap: 18px; padding: 16px 54px 16px 20px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-setup strong { display: block; color: var(--cxp-dw-ink); font-size: 14px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-setup span { color: var(--cxp-dw-muted); font-size: 12px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-progress { height: 9px; overflow: hidden; border-radius: 999px; background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-progress span { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--cxp-dw-primary), var(--cxp-dw-pink)); }

/* Website health / stat panel / issue metrics. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-audit-grid { display: grid; grid-template-columns: 230px minmax(0, 1fr); gap: 18px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-stat-panel { padding: 15px; border: 1px solid var(--cxp-dw-border); border-radius: 10px; background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-stat-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-stat-head strong { color: var(--cxp-dw-body); font-size: 12px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-issue-metrics { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 9px; }
/* CYT-933 fix batch 6 #2. Search performance / Google Analytics's 4-tile
   rows used to force this column count via an INLINE style, which no media
   query could ever win against - a dedicated class lets the 390px rule
   below apply normally. Scoped to min-width so it never fights the mobile
   2-column rule. */
@media (min-width: 521px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-issue-metrics--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* CYT-933 fix batch 9 #3: equal tile heights (explicit stretch, belt and
   braces over the grid default) and the empty-value text set to the SAME
   min-height/line-box as the numeric value, so "Not recorded" sits on the
   same baseline as a sibling figure instead of 18px lower. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini { display: flex; flex-direction: column; align-self: stretch; min-width: 0; padding: 12px; border: 1px solid var(--cxp-dw-border); border-radius: 10px; background: var(--cxp-dw-surface); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-label { color: var(--cxp-dw-muted); font-size: 12px; font-weight: 650; }
/* CYT-933 fix batch 11 #2: nowrap - a large figure (1,144 / 2,328) was
   dropping its delta chip to a second line while a short one (9 / 171)
   stayed inline, an inconsistent look in the same tile row. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-value { display: flex; align-items: center; flex-wrap: nowrap; gap: 5px; margin-top: 7px; min-height: 27px; color: var(--cxp-dw-ink); font-size: 21px; line-height: 1; font-weight: 700; font-variant-numeric: tabular-nums; }
/* A 4-tile KPI row (Search performance / Analytics) is narrower per tile -
   a smaller figure keeps room for the chip beside it without wrapping. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-issue-metrics--4 .cxp-dw-mini-value { font-size: 18px; }
/* CYT-933 fix batch 13 #D2 (HIGH). At 1280/1440 the value + delta chip
   measured 111px inside a 96px tile (gate-6 punch #11, crops/zoom-
   clickrate.png) - the nowrap row overflowed past the tile's padded edge,
   the chip abutting or crossing the border. A 4-up tile is too narrow to
   reliably fit "0.82%" + a chip on one line at every width/client, so this
   modifier always stacks the chip under the value instead of trying to
   shrink it to fit - verified >= 20px inset from the tile edge at
   1280/1440/1920/390 (vs a negative inset before). */
body.fi-panel-portal .cxp-dw-page .cxp-dw-issue-metrics--4 .cxp-dw-mini-value {
  flex-direction: column; align-items: flex-start; gap: 3px;
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-value.is-empty { min-height: 27px; color: var(--cxp-dw-muted); font-size: 14px; font-weight: 600; line-height: 1; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-severity { display: inline-block; width: 7px; height: 7px; margin-right: 5px; border-radius: 50%; background: #7c8cff; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-severity.error { background: var(--cxp-dw-bad); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-severity.warn { background: #e9a23b; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-severity.good { background: #63b98b; }

/* Tables shared by Website health / Rankings / Content / Live operations. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-table-wrap { margin-top: 14px; overflow: hidden; border: 1px solid var(--cxp-dw-border); border-radius: 10px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-table-wrap:first-child { margin-top: 0; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-table th { height: 36px; padding: 7px 10px; text-align: left; background: var(--cxp-dw-surface-soft); color: var(--cxp-dw-muted); font-size: 12px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-table td { height: 39px; padding: 7px 10px; border-top: 1px solid var(--cxp-dw-border); color: var(--cxp-dw-body); font-size: 12px; font-variant-numeric: tabular-nums; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-table tbody tr:nth-child(even) td { background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-table strong { color: var(--cxp-dw-ink); font-weight: 650; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-right { text-align: right !important; }

/* CYT-933 fix batch 6 #1. A "compact record" replaces a real <table> for
   Rankings' Keyword movement, Content's Pages to improve, and Live
   operations' Last applied / Next check - a real table's fixed columns are
   exactly what clipped headers ("POSITI...") and pushed cells onto zebra
   rows at 390px. At every width the label sits full width; the narrow
   metrics sit in one row beneath it - no column exists to vanish. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-record-list { margin-top: 14px; border: 1px solid var(--cxp-dw-border); border-radius: 10px; overflow: hidden; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-record-list:first-child { margin-top: 0; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-record {
  display: flex; flex-direction: column; gap: 4px; padding: 9px 10px;
  border-top: 1px solid var(--cxp-dw-border); background: var(--cxp-dw-surface);
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-record:first-child { border-top: 0; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-record:nth-child(even) { background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-record-label {
  min-width: 0; color: var(--cxp-dw-ink); font-size: 12px; font-weight: 650; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; word-break: break-word;
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-record-label a { color: inherit; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-record-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; color: var(--cxp-dw-muted); font-size: 12px; font-variant-numeric: tabular-nums; }

/* Live operations' Last applied / Next check - a full sentence, not a
   narrow metric, so it always stacks label-over-value and always wraps. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-plan-detail { padding: 9px 10px; border-top: 1px solid var(--cxp-dw-border); background: var(--cxp-dw-surface); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-plan-detail:first-child { border-top: 0; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-plan-detail strong { display: block; color: var(--cxp-dw-ink); font-size: 12px; font-weight: 650; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-plan-detail span { display: block; margin-top: 2px; color: var(--cxp-dw-muted); font-size: 12px; line-height: 1.4; white-space: normal; word-break: break-word; }

/* Delta chips - one language across every widget. CYT-933 fix batch 10 #4:
   a fixed height (not just min-height), flex-shrink:0, so a surrounding
   flex row's align-items or line-height context can never stretch this
   chip to a different height in one context than another (22px vs 24px). */
body.fi-panel-portal .cxp-dw-page .cxp-dw-delta {
  display: inline-flex; flex: 0 0 auto; align-items: center; width: max-content; height: 22px; padding: 2px 6px;
  border: 1px solid; border-radius: 999px; font-size: 12px; font-weight: 700; white-space: nowrap; box-sizing: border-box;
}
/* CYT-933 fix batch 9 #5: >= 4.5:1 foreground/background contrast on every
   delta chip, verified against the literal fallback (the value actually
   used when the theme token is undefined) rather than trusting the live
   token alone. Neutral moved off the shared --cxp-dw-muted token (tuned for
   BODY text on the page background, not guaranteed on this chip's own
   lighter fill) to an explicit darker grey. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-delta-good { color: var(--cxp-dw-good); border-color: var(--cxp-dw-good-border); background: var(--cxp-dw-good-bg); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-delta-bad { color: var(--cxp-dw-bad); border-color: var(--cxp-dw-bad-border); background: var(--cxp-dw-bad-bg); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-delta-neutral { color: #3d4552; border-color: var(--cxp-dw-border); background: var(--cxp-dw-surface-soft); }

/* Rankings. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-rank-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: stretch; }
/* CYT-933 fix batch 13 #D1 (reverses batch 12 #5). Centring left up to 77px
   of dead space ABOVE the "Keyword movement" heading (gate-6 punch #12,
   image y800-1360) - Sol's ask was to stretch this column to the row height
   and align its heading to the TOP, not centre it. Any leftover space now
   falls below the table instead of being split above and below it. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-rank-layout > div:last-child { display: flex; flex-direction: column; justify-content: flex-start; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-position-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-chart-card { margin-top: 14px; padding: 12px 12px 8px; border: 1px solid var(--cxp-dw-border); border-radius: 10px; background: var(--cxp-dw-surface); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-chart-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; color: var(--cxp-dw-muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }
/* Empty / connecting / collecting states - shared shell, never a ring with a question mark. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-empty { display: grid; place-items: center; min-height: 220px; padding: 26px; text-align: center; border: 1px dashed var(--cxp-dw-border); border-radius: 10px; background: var(--cxp-dw-surface-soft); }
/* CYT-933 fix batch 11 #4c: each widget's empty capsule now names its own
   noun (SEARCH, ANALYTICS, MAPS...) instead of the one generic "DATA" every
   empty widget shared - a longer word needs an auto-width capsule, not a
   fixed 52px circle that would clip it. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-empty-mark { display: grid; place-items: center; min-width: 52px; min-height: 36px; width: auto; height: auto; padding: 6px 12px; margin: 0 auto 12px; border: 1px solid var(--cxp-dw-border); border-radius: 10px; background: var(--cxp-dw-surface); color: var(--cxp-dw-muted); font-weight: 800; font-size: 12px; line-height: 1.2; text-align: center; white-space: normal; text-transform: uppercase; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-empty h3 { margin: 0; color: var(--cxp-dw-ink); font-size: 15px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-empty p { max-width: 360px; margin: 6px auto 15px; color: var(--cxp-dw-muted); font-size: 12px; }

/* AI search visibility. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-engine-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; margin-top: 14px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-engine-grid--sources { grid-template-columns: repeat(4, minmax(0, 1fr)); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-engine { padding: 11px; border: 1px solid var(--cxp-dw-border); border-radius: 10px; background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-engine strong { display: block; margin-top: 6px; color: var(--cxp-dw-ink); font-size: 18px; font-variant-numeric: tabular-nums; }
/* CYT-952 fix round 2 (independent qa-inspector re-check, 2026-09-10): the
   round-1 fix keyed the collapse off VIEWPORT width (max-width: 1700px), but
   this widget always sits inside .cxp-dw-masonry's 2-column grid - its real
   card width is roughly half the content area, not the viewport, at every
   desktop width from ~760px up to well past 1920px. Confirmed on real
   clients 11/31: at the 1920px mandated QA width the card measured ~670px,
   still under the old 1700px threshold's assumption of "viewport = card",
   so the 50/50 split still fired there and "Link presence"/"AI Overview
   presence" still wrapped 2 lines. A viewport media query can never key off
   the right thing here - the fix is a container query on the card's own
   rendered width. Stacked (1 column, full card width) is now the default
   for every card width; the 50/50 split only turns on once the card itself
   has genuinely earned enough room for two columns of this content
   (~1400px - twice the ~670-700px this content already needs unsplit,
   observed clean in round 1's own stacked screenshots). Browsers without
   container query support (none in our supported set) simply stay stacked,
   never wrap - a safe default, not a regression. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-body:has(.cxp-dw-rank-layout--ai) {
  container-type: inline-size;
  container-name: cxp-ai-card;
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-rank-layout--ai { grid-template-columns: 1fr; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-rank-layout--ai .cxp-dw-chart-card { margin-top: 14px; }
@container cxp-ai-card (min-width: 1400px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-rank-layout--ai { grid-template-columns: 1fr 1fr; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-rank-layout--ai .cxp-dw-chart-card { margin-top: 0; }
}

/* Distribution bars + legends - Content / Listings and Maps / Live operations. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-bar { display: flex; height: 12px; margin: 13px 0 10px; overflow: hidden; border-radius: 999px; background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-bar span:nth-child(1) { background: #e9a23b; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-bar span:nth-child(2) { background: #9a8cf3; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-bar span:nth-child(3) { background: #63b98b; }
/* CYT-933 fix batch 7 #6: the map-rank bar's own verdict scale runs the
   OPPOSITE direction (best segment first) from the shared .cxp-dw-bar
   default (best segment last, used by Content/Live operations) - a
   dedicated modifier instead of changing the shared default's meaning. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-bar--rank span:nth-child(1) { background: #63b98b; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-bar--rank span:nth-child(2) { background: #e9a23b; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-bar--rank span:nth-child(3) { background: #b6bac2; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-legend { display: flex; flex-wrap: wrap; gap: 8px 15px; color: var(--cxp-dw-muted); font-size: 12px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-legend i { display: inline-block; width: 8px; height: 8px; margin-right: 4px; border-radius: 50%; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-map-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); align-items: stretch; gap: 9px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-plan { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 9px; }
/* CYT-933 fix batch 13 #D5 (gate-6 punch #21). "Open work" had a tinted
   background while Done/Ignored stayed white - one row reading as two
   different tile grammars with no stated reason. All three cells now share
   the same white .cxp-dw-mini shell; any emphasis lives on the VALUE text
   colour, not the tile background. */

/* One thing we need from you (conditional, full-width compact strip). */
body.fi-panel-portal .cxp-dw-page .cxp-dw-callout { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 18px 54px 18px 20px; border-left: 4px solid var(--cxp-dw-warn); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-callout strong { display: block; color: var(--cxp-dw-ink); font-size: 14px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-callout p { margin: 3px 0 0; color: var(--cxp-dw-muted); font-size: 12.5px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-status { display: inline-flex; align-items: center; gap: 6px; min-height: 22px; padding: 2px 8px; border: 1px solid var(--cxp-dw-warn-border); border-radius: 999px; color: var(--cxp-dw-warn); background: var(--cxp-dw-warn-bg); font-size: 12px; font-weight: 650; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: #e9a23b; }

/* Recent activity feed. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity { border-top: 1px solid var(--cxp-dw-border); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-row { display: grid; grid-template-columns: 80px 24px minmax(0, 1fr) 92px; align-items: center; min-height: 48px; padding: 6px 20px; border-bottom: 1px solid var(--cxp-dw-border); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-row:last-child { border-bottom: 0; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-row:nth-child(even) { background: var(--cxp-dw-surface-soft); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-time { color: var(--cxp-dw-muted); font-size: 12px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-icon { display: grid; place-items: center; width: 20px; height: 20px; border-radius: 50%; color: var(--cxp-dw-good); background: var(--cxp-dw-good-bg); font-size: 12px; font-weight: 800; }
/* CYT-933 fix batch 8 #1. `-webkit-line-clamp` itself DRAWS the "..." at
   the clamp point even when the label has already been shortened
   (dropChangeTarget() in Dashboard.php) - at <= 767px the column is narrow
   enough that a shortened label can still need more than 2-3 lines, so the
   clamp was still cutting it. Below 768px: no clamp at all, full wrap,
   nothing is ever cut. At 768px and up the column is wide enough that a
   dropped-target label reliably fits 2 lines, so the clamp there is a
   backstop, not a routine cut. Full untruncated text stays on `title=""`
   regardless. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-copy {
  min-width: 0; color: var(--cxp-dw-body); font-size: 12px; line-height: 1.35;
  white-space: normal; word-break: break-word; overflow-wrap: anywhere;
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-copy strong { color: var(--cxp-dw-ink); }
@media (min-width: 768px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-copy {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-state { justify-self: end; color: var(--cxp-dw-good); font-size: 12px; font-weight: 650; }

/* ==========================================================================
   Mobile (<= 390px): two per row key metrics, one-column widget area, the
   Widgets panel becomes a bottom sheet, drag handles become up/down arrows.
   ========================================================================== */
@media (max-width: 1100px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid > .cxp-dw-kpi:nth-child(3) { border-right: 0; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid > .cxp-dw-kpi:nth-child(-n+3) { border-bottom: 1px solid var(--cxp-dw-border); }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-audit-grid,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-rank-layout { grid-template-columns: 1fr; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-issue-metrics { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 760px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-page-head { align-items: flex-start; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-masonry { grid-template-columns: 1fr; }
  /* Columns collapse to reading order; each card carries --widget-order
     (set inline per card by the blade, from Dashboard::layout()'s
     orderIndex) so the flat mobile order always matches the saved widget
     order, never the desktop left/right column split. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-masonry,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-column { display: contents; }
  body.fi-panel-portal .cxp-dw-page [data-widget-card] { order: var(--widget-order, 0); }
  /* CYT-933 fix batch 8 #3. The list and the action bar used to be two
     independently max-height'd blocks with no flex relationship, so the
     sheet's own 82vh cap could clip the Reset/Done bar instead of the list
     - display:flex + the list as the ONLY flexible child pins the grab bar,
     header, and action bar in place and gives the list the exact remaining
     space as ITS OWN scroll container. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-menu {
    position: fixed; inset: auto 0 0; width: 100%; max-height: 82vh; border-radius: 18px 18px 0 0;
    padding: 10px 12px calc(12px + env(safe-area-inset-bottom));
    display: flex; flex-direction: column;
  }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-menu::before { content: ""; flex: 0 0 auto; display: block; width: 42px; height: 4px; margin: 1px auto 9px; border-radius: 999px; background: var(--cxp-dw-border); }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-head,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-actions { flex: 0 0 auto; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-list {
    flex: 1 1 auto; min-height: 0; max-height: none;
    /* Bottom fade - a visible cue that the list scrolls, not a silent cut. */
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent 100%);
  }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-btn,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-link-btn { min-height: 44px; }
  /* CYT-933 fix batch 4 #6: a real 44x44 touch target, not a 32px circle
     with extra min-height stretching it into an ellipse. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-close,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-strip-close { width: 44px; height: 44px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row { grid-template-columns: 92px 20px minmax(0, 1fr); min-height: 52px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-grip { display: none; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-move { display: flex; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-move button { width: 44px; height: 44px; border-radius: 8px; }
}

@media (max-width: 520px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-page-head { gap: 10px; }
  /* CYT-933 fix batch 2 #11: kept, not hidden - compact and left to wrap
     rather than removed, so the account/domain/refreshed line survives at
     390px. */
  /* CYT-933 fix batch 9 #4: 12px type floor - was 11.5px. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-account { gap: 4px 10px; font-size: 12px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-head { padding: 15px 15px 12px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-body { padding: 0 15px 15px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-foot { padding: 11px 15px; flex-wrap: wrap; }
  /* 2 per row, three rows - the mockup's balanced 2+2+2 Key metrics layout. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid > .cxp-dw-kpi { padding: 13px; border-bottom: 1px solid var(--cxp-dw-border); }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid > .cxp-dw-kpi:nth-child(2n) { border-right: 0; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid > .cxp-dw-kpi:nth-last-child(-n+2) { border-bottom: 0; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-kpi-grid > .cxp-dw-kpi:nth-child(3) { border-right: 1px solid var(--cxp-dw-border); }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-setup { grid-template-columns: 1fr auto; gap: 12px; padding: 15px 46px 15px 15px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-setup .cxp-dw-progress { grid-column: 1 / -1; grid-row: 2; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-setup .cxp-dw-btn { grid-column: 2; grid-row: 1; padding: 0 12px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-issue-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* CYT-933 fix batch 6 #2: Website health's 5 audit tiles end in a 2+2+1
     orphan at 390 - span the first (Crawled pages) full width so the
     remaining four sit a clean 2+2. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-issue-metrics--5 > :first-child { grid-column: 1 / -1; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-position-grid,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-engine-grid,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-engine-grid--sources,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-map-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* CYT-933 fix batch 7 #8: .cxp-dw-plan always holds exactly 3 tiles (Open
     work / Done / Ignored) - forcing 2 columns produced a 2+1 orphan. Stays
     at its own 3-column base rule instead, which balances perfectly. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-value { font-size: 19px; }
  /* CYT-933 fix batch 13 #D4 (gate-6 punch #10). At 390 a longer sibling
     label ("Reviews and rating", "Profile coverage") wraps to 2 lines while
     its row-mate stays on 1, so the VALUES below them (which is the actual
     number a customer scans) lose their shared baseline by a whole line
     ("Not recorded" sat 19px lower than its neighbour). Reserving 2 lines of
     label height unconditionally, in every 2-col mini grid at this width,
     means a value always starts at the same offset regardless of which
     sibling's label happens to wrap that day. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-label { min-height: 36px; }
  /* Never a page-level or inner-table horizontal scroller. CYT-933 fix
     batch 2 #12: the flexible TEXT column (keyword name, page title) wraps
     up to two lines instead of ellipsizing to unreadable; the narrow
     numeric columns (`.cxp-dw-right`) stay single-line. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-table-wrap { overflow: hidden; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-table th,
  body.fi-panel-portal .cxp-dw-page .cxp-dw-table td { padding-right: 6px; padding-left: 6px; font-size: 12px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-table td {
    white-space: normal; word-break: break-word; overflow-wrap: anywhere;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-table td.cxp-dw-right { -webkit-line-clamp: 1; white-space: nowrap; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-callout { align-items: flex-start; flex-direction: column; gap: 12px; padding: 16px 46px 16px 15px; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-callout .cxp-dw-btn { width: 100%; text-align: center; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-row { grid-template-columns: 56px 22px minmax(0, 1fr); padding: 7px 15px; align-items: flex-start; }
  /* CYT-933 fix batch 8 #1: no clamp at this width at all (see the
     min-width:768px rule) - full wrap, nothing is ever cut. */
  body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-copy { padding: 4px 0; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-activity-state { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  body.fi-panel-portal .cxp-dw-page * { transition: none !important; }
}

/* CYT-933 fix batch 9 #4: 12px type floor. The shared <x-portal.trend-chart
   variant="area"> component's own heading (used for the Rankings and
   Search performance charts) renders at .72rem/.7rem (11.52px/11.2px) -
   below floor - overridden inside .cxp-dw-page only, never touching the
   component's other consumers elsewhere in the portal. */
body.fi-panel-portal .cxp-dw-page .cxp-trend-chart-title,
body.fi-panel-portal .cxp-dw-page .cxp-trend-chart-note { font-size: 12px; }

/* CYT-933 fix batch 10 #2. The shared trend-chart's own area-variant shell
   is a fixed 230px tall with 20px side margins - fine for a full-width
   card, far too big for the Website health stat panel's compact sparkline.
   Scoped to this one wrapper only, so the Rankings/Search performance
   charts (which want the full-size shell) are untouched. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap { margin-top: 10px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap > .cxp-dw-label { display: block; margin-bottom: 4px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-chart-area {
  height: 70px; margin: 0; padding: .5rem .5rem .4rem;
}
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-chart-body { grid-template-columns: 1.7rem minmax(0, 1fr); }
/* CYT-933 fix batch 11 #1: no per-point date row in this compact box - see
   the blade comment - "Last 30 days" is stated once as plain text instead. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-point-labels { display: none; }
/* Measured 2026-09-05 at 1440: 17 markers on the 144px health sparkline touch each other (a dotted blob) and the
   end markers still ran 2px past the plot (2% inset = 2.9px < the marker radius). Same rule as the 390 clicks
   chart: first and last marker only, sized so the inset covers the radius. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-point-overlay .cxp-trend-point { display: none; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-point-overlay .cxp-trend-point:first-child,
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-point-overlay .cxp-trend-point:last-child { display: block; width: 8px; height: 8px; } /* shared rule centres with translate(-50%,-50%) and paints a 2px surface border - 4px left no visible core */
/* The 60px sparkline clips a 2px stroke sitting on the 92% line (the lowest reading looked cut off) and
   the end markers at 2%/98% by ~1px - let the plot's own overflow show; the area fill still ends at the box. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-chart-plot { overflow: visible; }
/* Sol R13: the shared "line sweep" gradient starts almost transparent, so on a 144px sparkline the first ~15% of
   the line vanished and the first marker looked detached from the series. The sparkline uses the same fully
   opaque brand gradient as the ring beside it (blue -> pink), so the line reaches its first marker. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap [data-chart-line] { stroke: url(#cxpRingBrand) !important; }
/* Gate #10: the marker overlay was 32px tall while the sparkline SVG is 54px (the shared component reserves the
   hidden date-label row), so the percentage-positioned markers sat 17px above their path points. Overlay = SVG box. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-point-overlay { inset: 0 !important; height: auto !important; bottom: 0 !important; }
/* Gate #9 lows: widget footer / list text links reach the 24px pointer-target floor without moving the layout;
   the Widgets-menu checkboxes take the house accent and a 16px box instead of the 15px browser default. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-foot a,
body.fi-panel-portal .cxp-dw-page .cxp-dw-responsive-list a,
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget a.cxp-dw-link { display: inline-flex; align-items: center; min-height: 24px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--primary); border-radius: 4px; cursor: pointer; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-menu-row input[type="checkbox"]:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* Sol R14: when a widget footer's caption is long (AI visibility at 390) the "View full report" link wrapped to a
   left-aligned second line - a wrapped link keeps the right edge like every other footer link. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-foot { flex-wrap: wrap; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-widget-foot > a:last-child { margin-left: auto; }
/* Gate #8 punch 3: the shared axis keeps a 1.75rem bottom padding for the date-label row, which the sparkline
   hides - its three labels ended up at 13/31/49% of the 66px plot while the points span 8-92% ("91" sat 28px
   above the plotted 91). Pad the axis by the same 8% inset the points use (5px of 66) so 97/94/91 sit on their lines. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-chart-axis { position: relative; padding: 0 !important; font-size: 12px !important; align-self: stretch; }
/* measured after a padding-only attempt: label centres landed at 21/50/79% of the 54px plot - pin them to the
   exact point inset (8/50/92%) instead */
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-chart-axis span { position: absolute; right: 0; transform: translateY(-50%); }
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-chart-axis span:nth-child(1) { top: 8%; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-chart-axis span:nth-child(2) { top: 50%; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-wrap .cxp-trend-chart-axis span:nth-child(3) { top: 92%; }
/* Gate #8 low 5: the rankings chart drew all 14 markers on a 185px phone plot - same first/last rule as the clicks chart. */
@media (max-width: 520px) {
  #dw-rankings-trend .cxp-trend-point-overlay .cxp-trend-point { display: none; }
  #dw-rankings-trend .cxp-trend-point-overlay .cxp-trend-point:first-child,
  #dw-rankings-trend .cxp-trend-point-overlay .cxp-trend-point:last-child { display: block; }
}

/* CYT-933 fix batch 12 #4 (reverses batch 11 #4a). align-self:start
   collapsed the panel to its own content, but that left it looking
   disconnected/short beside a taller right column (122px panel in a 233px
   row = 111px of dead space BELOW it) - stretching it back to the row's
   full height and centring its content vertically reads as one balanced
   pair of cards instead of either a mismatched short card or a tall one
   with dead space at the bottom. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-audit-grid > .cxp-dw-stat-panel {
  align-self: stretch; display: flex; flex-direction: column; justify-content: center;
}
/* CYT-933 fix batch 13 #D3 (gate-6 punch #12). A single-crawl client's stat
   panel has no real chart to centre around - top-align it and give the
   one-reading note (below) roughly the chart's own footprint so the box
   never reads as half-empty. Scoped to this modifier only - the rich-client
   centred layout (real ring + sparkline) is untouched. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-stat-panel--single { justify-content: flex-start; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-mini-spark-note {
  display: flex; align-items: center; justify-content: center; min-height: 90px;
  padding: 8px 14px; border: 1px dashed var(--cxp-dw-border); border-radius: 8px;
  background: var(--cxp-dw-surface-soft); color: var(--cxp-dw-muted); font-size: 12px; text-align: center;
}

/* CYT-933 fix batch 11 #5 (was batch 10 #4, still colliding at "0px apart" -
   every-other-one left too many). The Rankings trend's date labels (up to 7,
   independently absolutely-positioned by the shared component) now show at
   most 3 at 390px - first, a middle one, last - scoped to this one chart. */
@media (max-width: 520px) {
  #dw-rankings-trend .cxp-trend-point-labels span { display: none; }
  #dw-rankings-trend .cxp-trend-point-labels span:first-child,
  #dw-rankings-trend .cxp-trend-point-labels span:nth-child(4),
  #dw-rankings-trend .cxp-trend-point-labels span:last-child { display: inline; }
}

/* CYT-933 fix batch 13 #D6 (LOW, gate-6 punch #19). The 28-day clicks trend
   plots one marker per check (~50 points) - at 390 they sit close enough to
   blob into a solid line of overlapping dots. Dense chart = LESS DATA at
   that width, never smaller marks: keep the line, show only the first and
   last markers. (The shared <x-portal.trend-chart> component that renders
   these spans is outside this ticket's owned files, so a true "first/last/
   max" reduction would need editing it - CSS can reach the structural
   first and last points on this ONE chart instance without touching the
   component other consumers elsewhere in the portal rely on.) */
@media (max-width: 520px) {
  #dw-search-clicks-trend .cxp-trend-point-overlay .cxp-trend-point { display: none; }
  #dw-search-clicks-trend .cxp-trend-point-overlay .cxp-trend-point:first-child,
  #dw-search-clicks-trend .cxp-trend-point-overlay .cxp-trend-point:last-child { display: block; }
}

/* CYT-933 fix batch 11 #3. A real 3-column table at >= 768px (Sol's own
   layout - a stacked record left 55-67% of the card empty on a wide
   screen), the compact record (no column to collapse) only below that -
   both markups always render, this is the one toggle. */
/* CYT-933 fix batch 12 #2. `.cxp-dw-table-wrap:first-child { margin-top:0 }`
   was written for a table sitting directly after other content in ONE
   shared parent - once the table moved inside its own `.cxp-dw-responsive-
   list` wrapper (batch 11 #3) it became that wrapper's actual first child,
   so the zero-out fired even though the metrics grid still sits visually
   above it, closing the gap to 1px. Both children get the intended 12px
   explicitly, regardless of DOM position. */
body.fi-panel-portal .cxp-dw-page .cxp-dw-responsive-list .cxp-dw-table-wrap,
body.fi-panel-portal .cxp-dw-page .cxp-dw-responsive-list .cxp-dw-record-list { margin-top: 12px; }
body.fi-panel-portal .cxp-dw-page .cxp-dw-responsive-list .cxp-dw-table-wrap { display: none; }
@media (min-width: 768px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-responsive-list .cxp-dw-table-wrap { display: block; }
  body.fi-panel-portal .cxp-dw-page .cxp-dw-responsive-list .cxp-dw-record-list { display: none; }
}

/* CYT-933 fix batch 12 #1 (was batch 11 #5, --sources only - the plain
   3-cell .cxp-dw-engine-grid, e.g. Mention rate/Link presence/AI Overview
   presence, still 2+1'd at 390 since that rule never covered it). Generic
   fix: on EVERY 2-column grid at <= 520px, an odd item sitting alone as the
   last child spans the full row instead of sitting orphaned in column 1. */
@media (max-width: 520px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-position-grid > :last-child:nth-child(odd),
  body.fi-panel-portal .cxp-dw-page .cxp-dw-engine-grid > :last-child:nth-child(odd),
  body.fi-panel-portal .cxp-dw-page .cxp-dw-engine-grid--sources > :last-child:nth-child(odd),
  body.fi-panel-portal .cxp-dw-page .cxp-dw-map-grid > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
  }
  /* .cxp-dw-issue-metrics is deliberately excluded here: its own --5
     modifier already spans the FIRST tile (Crawled pages) so the remaining
     4 sit 2+2 - applying the generic last-odd rule too would ALSO span the
     5th tile and re-break that grouping into a fresh 2+1. */
}

/* CYT-882: "Keyword movement" toggle now renders as .cxp-chart-toolbar
   (canonical 28px kt-btn-sm control, same markup as Rankings/Search
   performance) instead of its own .cxp-dw-segmented component - the
   CYT-933 seam/wrap fix above no longer applies to different markup. */
@media (max-width: 520px) {
  body.fi-panel-portal .cxp-dw-page .cxp-dw-stat-head { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* ===== END DASHBOARD WIDGETS (cxp-dw) ===== */

/* ===== WH3 HEADING ALIGN (tick7) ===== */
/* UI sweep tick 7 - section heading alignment on Website health.

   `.cxp-wh3-pad` puts 20px on the card, but 12 of those cards also hold a
   .kt-card-header / .kt-card-content, which pad another 20px of their own, so
   the section headings ("How the crawl is structured", "How we turn checks into
   website work", "What powers this health view") sat at x=327 while every card
   on the page sits at 286 and every other card's text at 307.

   Only the HORIZONTAL padding of those direct children is zeroed. The card's own
   20px MUST stay: all four of these cards also hold bare children (.cxp-wh3-mini-grid,
   .cxp-chart, .cxp-wh3-work-grid, .cxp-wh3-connection-summary) that have no padding
   of their own and would end up flush against the card edge if the card's padding
   were removed instead. Measured on all 11 .cxp-wh3-pad cards before choosing.

   This lives in its OWN marker block on purpose - see
   feedback_deploy_block_css_marker_replaces_the_whole_block. */
body.fi-panel-portal .cxp-health-page .cxp-wh3-pad > .kt-card-header,
body.fi-panel-portal .cxp-health-page .cxp-wh3-pad > .kt-card-content {
  /* !important is required, not decorative: the generated cxs-skin.css pins
     `body.fi-panel-portal .kt-card-header { padding: var(--cxs-card-pad) ... }`
     with !important, and cxp.css pins 1rem 1.25rem the same way. This selector
     already out-specifies both (0,4,1 vs 0,2,1) and is scoped to the 4 cards on
     one page, so it wins without touching the skin, which is generated. */
  padding-left: 0 !important;
  padding-right: 0 !important;
}

/* ===== END WH3 HEADING ALIGN (tick7) ===== */

/* ===== SP2 CARD INSET (tick10) ===== */
/* UI sweep tick 10 - Search performance / Search Console / Analytics cards.

   `.cxp-rankings-card` computes `padding: 0` and only SOME of its children
   inset themselves, so the rest sat flush against the card border. Measured
   live on /search-performance/google-search-console: six cards had text at
   +1px from their own edge - the "Position filters" title, chip, subtitle and
   band row, the "Source: Google Search Console" footer on EVERY card, and the
   country rows ("United States" left, "15 - 65.2%" right).

   Children that already inset themselves, measured across GSC, Search
   performance and Google Analytics: .kt-card-header, .kt-card-content and
   .cxp-sp2-card-heading pad 20px; .cxp-chart and .cxp-rk2-table-wrap use a
   20px margin. Everything else was at 0.

   So the inset moves to the CARD and those five are neutralised, which leaves
   every child on one 20px edge and moves the already-correct ones by nothing.
   !important is needed on the header/content pair because the generated
   cxs-skin.css pins their padding that way. */
body.fi-panel-portal .cxp-rankings-card {
  padding-left: 20px;
  padding-right: 20px;
}
body.fi-panel-portal .cxp-rankings-card > .kt-card-header,
body.fi-panel-portal .cxp-rankings-card > .kt-card-content,
body.fi-panel-portal .cxp-rankings-card > .cxp-sp2-card-heading {
  padding-left: 0 !important;
  padding-right: 0 !important;
}
body.fi-panel-portal .cxp-rankings-card > .cxp-chart,
body.fi-panel-portal .cxp-rankings-card > .cxp-rk2-table-wrap {
  margin-left: 0;
  margin-right: 0;
}

/* The sidebar item "Google Search Console" measured scrollWidth 165 inside a
   153px box with white-space:nowrap and overflow:hidden, so the menu read
   "Google Search Con...". Let a nav label that does not fit use a second line
   rather than losing its own name. */
body.fi-panel-portal .fi-sidebar-nav .fi-sidebar-item-label {
  white-space: normal;
  line-height: 1.25;
}

/* In "Search performance by country" the widest value wrapped onto a second
   line while its siblings did not, so the first row measured 36px tall against
   19px for the rest and the column read as ragged. The value is the last cell
   in the row; keeping it on one line is enough. */
body.fi-panel-portal .cxp-sp2-share-row > :last-child {
  white-space: nowrap;
}

/* ===== END SP2 CARD INSET (tick10) ===== */

/* ===== ZERO RING CAP (tick15) ===== */
/* UI sweep tick 15 - a gauge reading 0% must draw NOTHING.

   stroke-linecap:round turns a zero-length dash into a round dot the width of
   the stroke, so a ring at exactly 0% still showed a coloured pip and read as
   a little bit of progress. Found first on the AI visibility assistant cards
   (fixed in the blade there) and again at 390px on Google Search Console's
   "Top 10 share" gauge, which is a different ring component - so this is a
   rule, not a per-component patch, and it covers any ring added later.

   Measured before writing: the two dasharray attributes on that page are
   "0 201" (a true zero) and "0.2 201" (a real 0.10% click rate). The prefix
   "0 " matches the first and NOT the second, so a genuinely tiny value keeps
   its dot - only an exact zero stops painting. The attribute is the source of
   truth here: no inline style is set, and the shared motion binder does not
   override it. */
body.fi-panel-portal circle[stroke-dasharray^="0 "],
body.fi-panel-portal circle[stroke-dasharray="0"] {
  /* !important, deliberately: `.cxp-sp2-ring .cxp-gauge-progress` scores
     (0,3,1) against this selector's (0,2,2) and won, and every ring
     component pins its own cap the same way. "A zero-length arc paints
     nothing" is absolute, so it should not lose a specificity race to a
     component that never considered the zero case. */
  stroke-linecap: butt !important;
}

/* ===== END ZERO RING CAP (tick15) ===== */

/* ===== AI2 FINDING LIST WRAP (tick16) ===== */
/* UI sweep tick 16 - AI visibility dashboard "Top opportunities".

   `.cxp-ai2-finding-list li` was white-space:nowrap + text-overflow:ellipsis,
   so a finding longer than its card was cut mid-sentence and the customer
   never saw what the opportunity was. That card is one of four across an
   overview grid, so it is only ~323px wide even at 1440 - the truncation is
   NOT a mobile-only problem, which is what a first pass scoped to a media
   query would have missed. Measured live:

     1440  c8 cut 25px and 35px, c12 cut 54px, c30 nothing
      390  c8 all three cut, c12 two, c30 one (li 304px vs 348px of text)

   Letting the item wrap costs one extra line on the longest findings and the
   grid row absorbs it. The hanging indent keeps a wrapped second line clear of
   the ::before bullet instead of sliding under it. */
body.fi-panel-portal .cxp-ai2-finding-list li {
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  padding-left: 14px;
  text-indent: -14px;
}

/* ===== END AI2 FINDING LIST WRAP (tick16) ===== */

/* ===== AIV PROFILE FIELD HEIGHT (tick17) ===== */
/* UI sweep tick 17 - "In a sentence or two, what does the business do?" on a phone.

   That textarea is rows=3, fine at 1440 (verified: no clipping on any client),
   but at 390px the same prose wraps to 7-9 lines, so three lines showed and the
   fourth was sliced through the letters at the bottom border. Measured content
   height 173px inside a 77px box on anccartransport, 94px on slidingdoorrepairs.
   The text was always reachable (overflow-y auto, resize vertical); the defect
   is the half-line, which reads as broken.

   Two things this rule has to get right:
   - It targets ONLY the description field. An earlier draft matched every
     textarea.cxp-aiv-input on the page, which would have stretched the short
     one-per-line fields to 155px too.
   - !important is required: a mobile touch-target rule
     `.cxp-ai-page .cxp-aiv-input { min-height: 44px !important }` in the same
     breakpoint would otherwise cap this larger floor. 44px is a sensible floor
     for a single-line input, not for a paragraph.

   155px is padding plus 7 whole lines at the 19.6px line-height, so the bottom
   edge falls between lines. Mobile only - 1440 was verified clean FIRST, which
   is the check I skipped last tick. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-aiv-profile-page textarea[wire\:model="description"] {
    min-height: 155px !important;
  }
}

/* ===== END AIV PROFILE FIELD HEIGHT (tick17) ===== */

/* ===== LIVEOPS ACTIVITY NAME (tick18) ===== */
/* UI sweep tick 18 - Live operations "Recent activity" specialist names on a phone.

   `.cxp-livebrain__activity-card-top` is a flex row holding the agent name, a
   status chip and a time, and only the NAME can shrink (the other two are
   flex:none). At 390px there is no room, so the name was cut MID-LETTER -
   measured against its clipping ancestor: "Content Improvement" lost 60px,
   "Ranking & Opportunity" 63px, "Autonomous Executor" 61px, "Website Health"
   40px, eight names in all. Note the ellipsis never appeared, because
   text-overflow on a flex CONTAINER does not apply to its content, so the text
   simply stopped mid-word.

   Letting the row wrap gives the name its own line and the chip and time the
   next one. 1440 was measured first and clips nothing, so this stays inside the
   mobile breakpoint. Higher specificity than the base rule in
   cxp-livebrain.css, which loads after cxp.css. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-livebrain__activity-card-top {
    flex-wrap: wrap;
  }
  body.fi-panel-portal .cxp-livebrain__activity-agent {
    flex: 1 0 100%;
    overflow: visible;
    white-space: normal;
  }
}

/* ===== END LIVEOPS ACTIVITY NAME (tick18) ===== */

/* ===== SP2 DELTA WRAP (tick19) ===== */
/* UI sweep tick 19 - Search performance / Analytics KPI cards on a phone.

   `.cxp-sp2-delta` is white-space:nowrap, which is right for the usual short
   chip ("Up 12%"), but the no-comparison state writes a whole sentence: "No
   earlier period to compare" measures 123px against a 161px card at 390px and
   the card clips its overflow, so the pill's rounded end was sliced and it read
   "No earlier period to comp". Measured on anccartransport, whose Analytics has
   no earlier period; clients with comparison data never show it, which is why
   it took a data-dependent client to surface.

   Letting the chip wrap costs a second line only when the text is genuinely
   long - the short delta chips are far inside the card and are untouched.
   Mobile only: 1440 clips nothing here. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-sp2-delta {
    white-space: normal;
  }
}

/* ===== END SP2 DELTA WRAP (tick19) ===== */

/* ============================================================================
   CYT-882 ROUND 2 (2026-09-06): cross-surface component canonicalization,
   re-done after qa-inspector FAIL on round 1's narrower fix. Scoped strictly
   to the 8 named surfaces via their page-wrapper classes; every declaration
   is !important because this block must win regardless of load order against
   the many earlier per-page overrides already in this file.

   TAB-BAR / COMPACT-TOGGLE: the mechanical audit (layout_economy_audit.js)
   structurally detects ANY row of 2-12 similar-height sibling a/button
   elements as a "tabbar", so a real page-nav tab strip (.cxp-map-tabs, 38px)
   and a metric/date-range toggle (.cxp-chart-toolbar / .kt-card-toolbar.sb-row
   / .cxp-ai2-action-links, previously 28px) get bucketed together. Per
   qa-inspector re-read (2026-09-06), one geometry is wanted everywhere
   regardless of role, and tabs should hug their label rather than stretch
   edge to edge. This block raises every compact-toggle instance to the same
   38px/hug shape already used by the real section tabs, and forces the two
   "stretched" .cxp-map-tabs instances (content, gbp-analysis) to hug.
   ============================================================================ */

body.fi-panel-portal .cxp-dashboard-page .cxp-chart-toolbar .kt-btn,
body.fi-panel-portal .cxp-rankings-page .cxp-chart-toolbar .kt-btn,
body.fi-panel-portal .cxp-searchperf-page .cxp-chart-toolbar .kt-btn,
body.fi-panel-portal .cxp-listing-management-page .kt-card-toolbar.sb-row .kt-btn {
  min-height: 38px !important;
  height: auto !important;
  padding: .55rem .8rem !important;
  border-radius: .45rem !important;
  border: 0 !important;
  font-size: .8125rem !important;
  font-weight: 700 !important;
  line-height: 1.2 !important;
  flex: 0 0 auto !important;
  background: transparent !important;
  color: #475467 !important;
  box-shadow: none !important;
}
body.fi-panel-portal .cxp-dashboard-page .cxp-chart-toolbar .kt-btn:hover,
body.fi-panel-portal .cxp-rankings-page .cxp-chart-toolbar .kt-btn:hover,
body.fi-panel-portal .cxp-searchperf-page .cxp-chart-toolbar .kt-btn:hover,
body.fi-panel-portal .cxp-listing-management-page .kt-card-toolbar.sb-row .kt-btn:hover {
  background: #f2f4f7 !important;
  color: #101828 !important;
}
body.fi-panel-portal .cxp-dashboard-page .cxp-chart-toolbar .kt-btn.kt-btn-primary,
body.fi-panel-portal .cxp-rankings-page .cxp-chart-toolbar .kt-btn.kt-btn-primary,
body.fi-panel-portal .cxp-searchperf-page .cxp-chart-toolbar .kt-btn.kt-btn-primary,
body.fi-panel-portal .cxp-listing-management-page .kt-card-toolbar.sb-row .kt-btn.kt-btn-primary {
  background: var(--cxs-grad-ink, var(--portal-primary-fill)) !important;
  background-image: var(--cxs-grad-ink, none) !important;
  color: var(--portal-primary-foreground) !important;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .18) !important;
}

/* ai-visibility's "View question rankings / Read what AI said" link pair
   structurally reads as a 2-tab bar to the mechanical audit even though it
   is two outline nav links, not a toggle - raised to the same 38px/hug shape,
   kt-btn-outline's own border/color scheme is left untouched. */
body.fi-panel-portal .cxp-aiv-dash-page .cxp-ai2-action-links > a.kt-btn {
  min-height: 38px !important;
  padding: .55rem .8rem !important;
  border-radius: .45rem !important;
  font-size: .8125rem !important;
  font-weight: 700 !important;
  flex: 0 0 auto !important;
}

/* the two .cxp-map-tabs instances still measuring "stretched" (content,
   gbp-analysis) - force hug like search-performance/listing-management's
   copies already do. */
body.fi-panel-portal .cxp-content-page .cxp-map-tabs > button,
body.fi-panel-portal .cxp-gbp-analysis-page .cxp-map-tabs > button {
  flex: 0 0 auto !important;
  width: auto !important;
}

/* ----------------------------------------------------------------------
   BADGE / CHIP: four one-off chip families (rk2-chip, sp2-scope-chip,
   sp2-compact-chip, article-chip) never migrated onto the shared
   kt-badge-outline geometry+wash established for ai-visibility/listing-
   management/live-operations/gbp. Height/padding/radius/weight converge
   to the same box; tone is still carried by color (a status chip that
   cannot show tone by color would be a real regression, not a fix), but
   the background now resolves through the exact same --cxs-wash token
   kt-badge-outline and cxp-health-pill already use, so same-tone chips
   render byte-identical across pages instead of each page rolling its
   own hex/rgb.
   ---------------------------------------------------------------------- */
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip {
  box-sizing: border-box !important;
  height: 20px !important;
  min-height: 20px !important;
  padding: 5px 10px !important;
  border-radius: 999px !important;
  font-size: 12px !important;
  font-weight: 700 !important;
  line-height: 1.2 !important;
  background: var(--cxs-wash, var(--portal-primary-soft)) !important;
  border: 1px solid var(--cxs-aline, var(--border)) !important;
}
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip--good { color: var(--cxp-good-strong, #04663a) !important; }
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip--bad { color: var(--cxp-bad-strong, #b42318) !important; }
body.fi-panel-portal .cxp-rankings-page .cxp-rk2-chip--neutral { color: #6b7280 !important; }

body.fi-panel-portal .cxp-searchperf-page .cxp-sp2-scope-chip,
body.fi-panel-portal .cxp-searchperf-page .cxp-sp2-compact-chip {
  box-sizing: border-box !important;
  min-height: 20px !important;
  padding: 5px 10px !important;
  border-radius: 999px !important;
  font-size: 12px !important;
  font-weight: 700 !important;
  line-height: 1.2 !important;
  background: var(--cxs-wash, var(--portal-primary-soft)) !important;
  border: 1px solid var(--cxs-aline, var(--border)) !important;
  color: var(--cxs-aink, var(--portal-primary)) !important;
}

body.fi-panel-portal .cxp-content-page .cxp-article-chip {
  box-sizing: border-box !important;
  height: 20px !important;
  min-height: 20px !important;
  padding: 5px 10px !important;
  border-radius: 999px !important;
  font-size: 12px !important;
  font-weight: 700 !important;
  line-height: 1.2 !important;
  background: var(--cxs-wash, var(--portal-primary-soft)) !important;
  border: 1px solid var(--cxs-aline, var(--border)) !important;
  color: var(--cxs-aink, var(--portal-primary)) !important;
}
/* .cxp-article-chip-row replaces .cxp-article-chips as the wrapper class
   (blade updated) - the old name matched [class*="chip"] in the mechanical
   audit's badge selector and was counted as its own fake "badge" variant,
   since it is a plain layout wrapper, not a chip. */
body.fi-panel-portal .cxp-article-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin-top: 0.3rem;
}

body.fi-panel-portal .cxp-live-operations .cxp-count-badge {
  box-sizing: border-box !important;
  height: 20px !important;
  min-height: 20px !important;
  padding: 5px 10px !important;
  border-radius: 999px !important;
  font-size: 12px !important;
  font-weight: 700 !important;
  line-height: 1.2 !important;
  background: var(--cxs-wash, var(--portal-primary-soft)) !important;
  color: var(--cxs-aink, var(--portal-primary)) !important;
}

/* ----------------------------------------------------------------------
   BUTTON: dashboard's widget-manager family (cxp-dw-btn / cxp-dw-btn-
   primary) and search-performance's one-off cxp-sp2-btn-outline, both
   named in qa-inspector's fail as distinct constructions. Converged onto
   the portal's standalone small-button geometry (28px / 10px radius /
   500 weight) already shared by kt-btn-sm across content/dashboard/
   rankings/search-performance/listing-management. cxp-dw-close (the
   circular icon-only widget-menu close X) is left alone - a round icon
   button and a text pill are different controls by function, not drift.
   ---------------------------------------------------------------------- */
body.fi-panel-portal .cxp-dashboard-page .cxp-dw-btn {
  min-height: 28px !important;
  height: auto !important;
  padding: 0 10px !important;
  border-radius: 10px !important;
  font-size: 12px !important;
  font-weight: 500 !important;
  line-height: 1.2 !important;
  box-shadow: none !important;
}
body.fi-panel-portal .cxp-dashboard-page .cxp-dw-btn-primary {
  background: var(--cxs-grad-ink, var(--portal-primary-fill)) !important;
  background-image: var(--cxs-grad-ink, none) !important;
  color: var(--portal-primary-foreground) !important;
  border: 0 !important;
}

body.fi-panel-portal .cxp-searchperf-page .cxp-sp2-btn-outline {
  min-height: 28px !important;
  padding: 0 10px !important;
  border-radius: 10px !important;
  font-size: 12px !important;
  font-weight: 500 !important;
}

/* CYT-882 ROUND 2b: GPT-5.6 Sol vision review flagged the compact-toggle rows
   (cxp-chart-toolbar, kt-card-toolbar.sb-row) as still reading like a
   different component TYPE from the real section tabs (cxp-map-tabs) - the
   buttons now match in height/padding/font, but cxp-map-tabs wraps its
   buttons in a bordered/background segmented-control box while the toggle
   rows had no such wrapper (loose pills with just a gap between them). This
   adds the same wrapper chrome so both read as the same segmented-tab
   component, not two different systems that happen to share button sizing. */
body.fi-panel-portal .cxp-dashboard-page .cxp-chart-toolbar,
body.fi-panel-portal .cxp-rankings-page .cxp-chart-toolbar,
body.fi-panel-portal .cxp-searchperf-page .cxp-chart-toolbar,
body.fi-panel-portal .cxp-listing-management-page .kt-card-toolbar.sb-row {
  padding: .25rem !important;
  border: 1px solid var(--border) !important;
  border-radius: .65rem !important;
  background: #fff !important;
  gap: .25rem !important;
}


/* =========================================================================
   SIDEBAR SECTIONS ("Account", "Website") - 2026-09-11
   -------------------------------------------------------------------------
   CYT-959 moved real top-level pages into labelled nav groups. Two older
   rule sets assumed a labelled group could only ever be a SUB-MENU:
     - cxs-skin.css section 23 indents the list 19px + 11px and draws a 1px
       tree rule down its left edge;
     - pages.css (the admin demo6 menu) pushes every button in a labelled
       group a further 1.5rem right with margin-left, while this file keeps
       the button at width:100%.
   Stacked, those rows sat 55px right of the top-level rows, used a heavier
   label and a smaller icon, and the active pill ran 24px past the rail's
   right edge ("Connected accounts" was cut off).
   In the portal a labelled group is a SECTION: a quiet heading over rows
   that look exactly like the top-level rows (same edge, icon, type, height).
   A page's own child pages (.fi-sidebar-sub-group-items under "AI search
   visibility", "Rankings", ...) keep their tree rule - every selector below
   anchors on the section's DIRECT children, so none of them reach that far.
   Specificity is deliberately high (0,6,2 and up): it has to beat pages.css
   (0,3,2 !important) and cxs-skin.css (0,5,1 !important), and the skin
   loads after this file.
   ========================================================================= */

/* One gap between the top band and each section, owned here. Filament's
   gap-y-7 (28px) plus two group margins stacked to ~43px of dead rail. */
html body.fi-panel-portal .fi-sidebar .fi-sidebar-nav-groups {
  row-gap: 0 !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-nav-groups > .fi-sidebar-group {
  margin-bottom: 0 !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-nav-groups > .fi-sidebar-group + .fi-sidebar-group {
  margin-top: 1.1rem !important;
}

/* The heading. Text starts where the row icons start, no fill of its own -
   the gradient pill on the current page already says "you are here", and a
   tinted heading as well would put two filled shapes in one section. */
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-button,
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group.fi-active:has(> .fi-sidebar-group-button) > .fi-sidebar-group-button {
  min-height: 0 !important;
  padding: 4px .6rem !important;
  background: transparent !important;
  box-shadow: none !important;
  color: var(--cxs-side-ink2, var(--portal-muted)) !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-button:hover {
  background: transparent !important;
  color: var(--cxs-side-ink, var(--portal-text)) !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-button .fi-sidebar-group-label {
  padding: 0 !important;
  font-size: 12px !important;
  font-weight: 600 !important;
  line-height: 16px !important;
  letter-spacing: .01em !important;
  text-transform: none !important;
  color: inherit !important;
  opacity: 1 !important;
}

/* The list: no indent, no tree rule, the top band's own row gap. */
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-items {
  margin: 0 !important;
  padding: 0 !important;
  border-left: 0 !important;
  gap: .2rem !important;
}

/* The rows: the same values the top-level rows resolve to (pages.css
   ":not(:has(.fi-sidebar-group-button))" block + cxs-skin.css). */
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-items > .fi-sidebar-item > .fi-sidebar-item-button {
  width: 100% !important;
  margin-left: 0 !important;
  padding: .52rem .6rem !important;
  gap: .75rem !important;
  font-size: 12.5px !important;
  line-height: 1.5 !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-items > .fi-sidebar-item > .fi-sidebar-item-button .fi-sidebar-item-icon {
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  flex: 0 0 auto !important;
  width: 1.15rem !important;
  height: 1.15rem !important;
  min-width: 1.15rem !important;
  font-size: 1.05rem !important;
  opacity: .9 !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-items > .fi-sidebar-item > .fi-sidebar-item-button .fi-sidebar-item-label {
  padding-left: .4rem !important;
  font-weight: 400 !important;
}

/* SIDEBAR SECTIONS, round 2 (same day, QA gate punch list).

   1. Reach the end of the menu. The nav sits under the 74px header, but it
      was ALSO 100vh tall (min-height, from this file's "seam" rule and
      cxs-skin.css "the rail's tint has to reach the floor") inside a rail
      that is exactly one viewport tall. Its last 74px hung below the rail
      where no scroll could reach them - with a parent page's child menu
      open, that was the whole "Website" section, and on a phone the drawer
      offered no way to it at all. The rail keeps its own min-height:100vh
      and the layout paints the column (skin 43.4), so the nav only has to
      fill what is left. */
html body.fi-panel-portal aside.fi-sidebar > nav.fi-sidebar-nav {
  min-height: 0 !important;
}

/* 2. Hover is the skin's light tint, as cxs-skin.css section 3 draws it:
      the gradient pill is the only filled shape in the rail. pages.css (the
      admin menu) was painting a solid navy pill with white text on hover,
      which read as a second selected item right beside the real one. */
html body.fi-panel-portal .fi-sidebar .fi-sidebar-item:not(.fi-sidebar-item-active) > .fi-sidebar-item-button:hover {
  background: var(--cxs-side-card) !important;
  box-shadow: none !important;
  color: var(--cxs-side-ink) !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-item:not(.fi-sidebar-item-active) > .fi-sidebar-item-button:hover .fi-sidebar-item-icon,
html body.fi-panel-portal .fi-sidebar .fi-sidebar-item:not(.fi-sidebar-item-active) > .fi-sidebar-item-button:hover .fi-sidebar-item-label {
  color: var(--cxs-side-ink) !important;
}

/* 3. A visible keyboard focus ring. Filament's outline-none left a
      transparent 2px outline, so Tab moved through the menu invisibly.
      Inset so the nav's overflow-x:hidden cannot clip it; white on the
      gradient pill, where the accent would disappear. */
html body.fi-panel-portal .fi-sidebar .fi-sidebar-item-button:focus-visible,
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group-collapse-button:focus-visible {
  outline: 2px solid var(--cxs-a, var(--portal-primary)) !important;
  outline-offset: -2px !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-item-active > .fi-sidebar-item-button:focus-visible {
  outline-color: #ffffff !important;
}

/* 4. The section chevron: heading ink instead of 55% opacity (2.2:1 on the
      rail), a 24px target on desktop and 44px in the phone drawer. */
html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-button .fi-sidebar-group-collapse-button {
  width: 24px !important;
  height: 24px !important;
  margin: -4px -6px -4px 0 !important;
  opacity: 1 !important;
  color: inherit !important;
}
@media (max-width: 1024px) {
  html body.fi-panel-portal .fi-sidebar .fi-sidebar-group:has(> .fi-sidebar-group-button) > .fi-sidebar-group-button .fi-sidebar-group-collapse-button {
    width: 44px !important;
    height: 44px !important;
    margin: -10px -12px -10px 0 !important;
  }
}

/* SCORE CARDS WITH A GENERATED RING ("11.4 of 12") - 2026-09-11.
   cxs-skin.js turns any stat card whose value reads "X of Y" into a ring
   with the figure INSIDE it (the skin's 84px ring, 15px figure). The GBP
   sub-page cards (Reviews, Profile & listings, Website & search, Ranking &
   tracking) are also .cxp-kpi2 tiles, and the kpi2 rules shrink that ring
   to 44px (60px when not compact) and set the figure to 25px (31px) - so an
   "11.4 of 12" wider than the ring sat on top of the arc.
   The kpi2 tiles on the parent Google Business Profile page draw a small
   ring with the figure BESIDE it (Google rating, Google reviews); the
   generated ring now does the same, so the page family reads as one. */
html body.fi-panel-portal .cxp-stat-card.cxp-kpi2[data-cxs-gauged="1"] .cxp-stat-card__ring[data-cxs-generated] {
  margin: 8px 0 0 !important;
  flex: none !important;
}
html body.fi-panel-portal .cxp-stat-card.cxp-kpi2[data-cxs-gauged="1"] .cxp-stat-card__ring[data-cxs-generated] > .cxp-stat-card__ring-value {
  position: absolute !important;
  top: 50% !important;
  right: auto !important;
  bottom: auto !important;
  left: calc(100% + 12px) !important;
  transform: translateY(-50%);
  flex-direction: row !important;
  align-items: baseline !important;
  justify-content: flex-start !important;
  gap: 5px;
  white-space: nowrap;
  text-align: left;
}

/* Stacked mobile card table: a status pill keeps its own width. Each td is a
   two-column grid (label, value), and a lone badge in the value cell was
   stretched to the full column - a 165px "Good" bar instead of a pill. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr > td > .kt-badge {
    justify-self: start;
    align-self: center;
  }
}

/* QA round 4, 2026-09-11 (after #210-#213).

   Child pages under a parent page (Rankings > "Google Search Console", ...)
   had 146px for their label and "Google Search Console" needs 155px, so
   "Console" dropped alone onto a second line and that row grew to 53px. The
   label loses the 0.4rem pad the top rows use to line up with group labels,
   and the icon gap and right padding tighten a little - 14px more room. */
html body.fi-panel-portal .fi-sidebar .fi-sidebar-nav .fi-sidebar-sub-group-items .fi-sidebar-item-button {
  gap: 10px !important;
  padding-right: 4px !important;
}
html body.fi-panel-portal .fi-sidebar .fi-sidebar-nav .fi-sidebar-sub-group-items .fi-sidebar-item-label {
  padding-left: 0 !important;
}

/* Stacked phone tables: a wider value column (the 38% label column left
   181px, and "Needs a quick manual check" needs 185px), no lone last words,
   and a pill that has to wrap splits into two even lines instead of leaving
   "check" alone. The GBP page's own inline style forces badges to wrap
   (white-space: normal !important), so the pill rule needs the extra weight. */
@media (max-width: 640px) {
  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr > td {
    grid-template-columns: minmax(6.75rem, 34%) minmax(0, 1fr);
    text-wrap: pretty;
  }
  body.fi-panel-portal .cxp-mobile-card-table > tbody > tr > td > strong {
    text-wrap: balance;
  }
  html body.fi-panel-portal.fi-panel-portal .cxp-mobile-card-table > tbody > tr > td > .kt-badge {
    text-wrap: balance !important;
  }
}

/* GBP findings table on phones: label ABOVE value, not beside it. Side by
   side, the 108px label column left the value 166px - too narrow for the
   "Needs a quick manual check" pill (185px) and for the evidence sentences,
   which kept ending on one lone word. Full width, the pill sits on one line
   and the text wraps across 286px. */
@media (max-width: 640px) {
  html body.fi-panel-portal .cxp-gbp-findings-table.cxp-mobile-card-table > tbody > tr > td {
    grid-template-columns: minmax(0, 1fr);
    gap: 4px;
  }
}

/* GBP findings table at tablet and desktop widths (641px and up), QA round 5.
   The phone rules above only apply below 641px, so at 768 the Status column
   (142-157px) wrapped "Worth improving" and "Needs a quick manual check" into
   two-line pills, and 23 cells ended on a single word. Pills stay on one line
   (the column sizes to the widest one) and the text wraps evenly. The page's
   own inline style forces badges to wrap, hence the weight. */
html body.fi-panel-portal.fi-panel-portal .cxp-gbp-findings-table > tbody > tr > td {
  text-wrap: pretty;
}
html body.fi-panel-portal.fi-panel-portal .cxp-gbp-findings-table > tbody > tr > td > strong {
  text-wrap: balance;
}
@media (min-width: 641px) {
  html body.fi-panel-portal.fi-panel-portal .cxp-gbp-findings-table > tbody > tr > td > .kt-badge {
    white-space: nowrap !important;
    text-wrap-mode: nowrap !important;
    max-width: none !important;
  }
}

/* ...and the weight it actually needs: cxp.css's own "@media (max-width:
   1099px) .cxp-gbp-analysis-page .kt-card-content > .sb-data-table .kt-badge"
   (0,5,1) forces badges to wrap and outranked the rule above (0,4,5). */
@media (min-width: 641px) {
  html body.fi-panel-portal.fi-panel-portal .cxp-gbp-analysis-page .cxp-gbp-findings-table.sb-data-table > tbody > tr > td > .kt-badge.kt-badge {
    white-space: nowrap !important;
    text-wrap-mode: nowrap !important;
    max-width: none !important;
  }
}

/* =========================================================================
   SEMANTIC TEXT INK - one home, 2026-09-13.
   The existing --cxp-good/warn/bad are GRAPHICS tones (cxs-skin.css even
   re-points --cxp-good at the brand accent), so pages kept inventing their own
   readable green/amber/red for text and drifted off-theme. These four are the
   text-safe set: each clears 4.5:1 on white, nothing else re-defines them, and
   tools/theme_check.js fails any stylesheet that declares its own instead.
   ========================================================================= */
body.fi-panel-portal {
  --cxp-ink-gold: #f0a500;   /* review stars and ratings (decorative, labelled) */
  --cxp-ink-good: #157347;   /* done, healthy, live */
  --cxp-ink-warn: #8a5a00;   /* waiting, needs a look */
  --cxp-ink-bad:  #b42318;   /* failed, urgent */
}
