/**
 * File: /assets/css/monitor.css
 * Module: Dashboard tabs, plan comparison, and the Speed & Traffic panel
 *
 * Loaded after portal.css and depending on it, so every token here resolves
 * through the same ladder the rest of the dashboard uses - --ch-bg-card,
 * --ch-border, --ch-text-main, --ch-text-muted, --ch-cta - rather than a second
 * palette that happens to look similar. The Store Speed Test made that mistake
 * once and had its stylesheet rebuilt for it.
 *
 * It is a separate file rather than another two thousand lines on the end of
 * portal.css because portal.css is already five thousand lines, and because this
 * one only has to load on a page that renders the dashboard.
 *
 * WHY THERE ARE SO FEW HARDCODED COLOURS. Three exist and all three carry
 * meaning rather than decoration: the verdict green, amber and red. They are the
 * thresholds Google publishes, so they must read identically in both themes and
 * cannot be allowed to drift with a surface token. Everything else is a token.
 *
 * Section map:
 *   1. Tokens
 *   2. Dashboard tab bar
 *   3. Plan comparison
 *   4. Monitoring: shell, header, stages
 *   5. Monitoring: connect and install
 *   6. Monitoring: vitals cards
 *   7. Monitoring: charts
 *   8. Monitoring: tables and locked panels
 *   9. Responsive
 *  10. Dark mode
 *  11. Reduced motion
 */

/* ==========================================================================
   1. TOKENS
   ========================================================================== */

.ch-portal-dash {
    --ch-mon-good:      #047857;
    --ch-mon-good-bg:   rgba(16, 185, 129, 0.10);
    --ch-mon-good-line: rgba(16, 185, 129, 0.42);

    /* Premium ink, the same royal blue as /premium/ (--p-pro). A verdict
       color (good, warn, stop) never marks a plan: Premium is blue sitewide. */
    --ch-mon-pro:       #1E40AF;
    --ch-mon-pro-hi:    #1D4ED8;

    --ch-mon-warn:      #92400E;
    --ch-mon-warn-bg:   rgba(245, 158, 11, 0.10);
    --ch-mon-warn-line: rgba(245, 158, 11, 0.38);

    --ch-mon-stop:      #B91C1C;
    --ch-mon-stop-bg:   rgba(239, 68, 68, 0.08);
    --ch-mon-stop-line: rgba(239, 68, 68, 0.34);

    --ch-mon-well:      #F1F5F9;
    --ch-mon-tile:      #F8FAFC;
    --ch-mon-ink-deep:  #0F172A;

    /* TABLE SURFACE LADDER.
       A data table needs four steps of its own, not the panel's two. It sits
       INSIDE a panel, so it has to be a step above that panel or it reads as
       loose text with hairlines through it - which is exactly how the dark
       theme rendered before these existed: rows on the same value as the panel
       they sat in, separated only by a Slate 700 line that all but vanished
       against Slate 800. Head, body, alternate row and hover are all separate
       values, and all four flip. */
    --ch-mon-table:     #FFFFFF;
    --ch-mon-thead:     #F1F5F9;
    --ch-mon-zebra:     #FAFBFD;
    --ch-mon-row-hover: #ECFDF5;
    --ch-mon-line:      #E2E8F0;
    --ch-mon-line-hard: #CBD5E1;

    --ch-mon-radius:    12px;
    --ch-mon-ease:      cubic-bezier(0.22, 1, 0.36, 1);
    --ch-mon-mono:      'JetBrains Mono', 'Roboto Mono', ui-monospace, monospace;

    /* Categorical series for the donut and its legend. Five is the ceiling on
       purpose: a sixth device category does not exist, and a browser chart with
       nine colours is a chart nobody reads. */
    --ch-mon-s0: #10B981;
    --ch-mon-s1: #3B82F6;
    --ch-mon-s2: #8B5CF6;
    --ch-mon-s3: #F59E0B;
    --ch-mon-s4: #64748B;
}

/* ==========================================================================
   2. DASHBOARD TAB BAR
   ========================================================================== */

.ch-dash-tabs {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
    margin: 0 0 1.6rem;
    padding-bottom: 0.9rem;
    border-bottom: 1px solid var(--ch-border);
    animation: ch-fade-up 0.45s var(--ch-mon-ease) both;
}

.ch-dash-tab,
.ch-dash-tab:hover,
.ch-dash-tab:focus {
    appearance: none;
    background: transparent !important;
    border: 1px solid transparent;
}

.ch-dash-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.6rem 1.05rem;
    border-radius: 999px;
    font-family: var(--ch-mon-mono);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--ch-text-muted) !important;
    cursor: pointer;
    white-space: nowrap;
    transition: color 0.2s, border-color 0.2s, background-color 0.2s;
}

.ch-dash-tab-ico {
    display: inline-flex;
    opacity: 0.75;
    transition: opacity 0.2s;
}

.ch-dash-tab:hover {
    color: var(--ch-cta) !important;
    border-color: var(--ch-border);
}

.ch-dash-tab:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.25);
}

.ch-dash-tab.is-active,
.ch-dash-tab.is-active:hover {
    background-color: var(--ch-bg-card) !important;
    border-color: var(--ch-cta);
    color: var(--ch-text-main) !important;
    box-shadow: 0 2px 8px -2px rgba(16, 185, 129, 0.25);
}

.ch-dash-tab.is-active .ch-dash-tab-ico {
    opacity: 1;
    color: var(--ch-cta);
}

.ch-dash-panel[hidden] {
    display: none;
}

/* The stagger on .ch-dash-card is keyed to nth-child, and every panel now
   starts its own child count. That is the correct result: each panel animates
   in as its own thing when it becomes visible. */
.ch-dash-panel.is-active {
    animation: ch-fade-up 0.35s var(--ch-mon-ease) both;
}

.ch-dash-panel:focus {
    outline: none;
}

/* ==========================================================================
   3. PLAN COMPARISON
   ========================================================================== */

.ch-card-compare {
    grid-column: 1 / -1;
}

.ch-cmp-head {
    margin-bottom: 1.4rem;
}

.ch-cmp-title {
    margin: 0.2rem 0 0.5rem;
    font-size: 1.35rem;
    line-height: 1.25;
    color: var(--ch-text-main);
}

.ch-cmp-sub {
    margin: 0;
    max-width: 62ch;
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--ch-text-muted);
}

.ch-cmp-table {
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-mon-radius);
    overflow: hidden;
}

.ch-cmp-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 8.5rem 8.5rem;
    align-items: center;
    gap: 0.75rem;
    padding: 0.7rem 1rem;
    border-top: 1px solid var(--ch-border);
    font-size: 0.9rem;
    color: var(--ch-text-main);
}

.ch-cmp-row:first-child {
    border-top: 0;
}

.ch-cmp-row.is-head {
    background-color: var(--ch-mon-well);
    padding-top: 0.85rem;
    padding-bottom: 0.85rem;
}

.ch-cmp-col {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    font-size: 0.86rem;
    font-weight: 600;
}

.ch-cmp-row.is-head .ch-cmp-col {
    font-family: var(--ch-mon-mono);
    font-size: 0.66rem;
    font-weight: 800;
    letter-spacing: 1px;
    text-transform: uppercase;
    /* Slate 600 on the well: Slate 500 measured 4.34:1 there (2026-10-03). */
    color: #475569;
}

html[data-theme="dark"] .ch-cmp-row.is-head .ch-cmp-col {
    color: var(--ch-text-muted);
}

.ch-cmp-row.is-head .ch-cmp-col em {
    margin-top: 0.15rem;
    font-style: normal;
    font-size: 0.95rem;
    letter-spacing: 0;
    text-transform: none;
    color: var(--ch-text-main);
}

.ch-cmp-row.is-head .ch-cmp-col.is-pro,
.ch-cmp-row.is-head .ch-cmp-col.is-pro em {
    color: var(--ch-mon-pro-hi);
}

.ch-cmp-col.is-pro {
    color: var(--ch-mon-pro);
    font-weight: 700;
}

.ch-cmp-group {
    padding: 0.85rem 1rem 0.45rem;
    border-top: 1px solid var(--ch-border);
    background-color: var(--ch-mon-tile);
    font-family: var(--ch-mon-mono);
    font-size: 0.64rem;
    font-weight: 800;
    letter-spacing: 1.4px;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-cmp-label {
    color: var(--ch-text-main);
}

.ch-cmp-no {
    display: inline-flex;
    color: var(--ch-text-muted);
    opacity: 0.55;
}

.ch-cmp-foot {
    display: flex;
    align-items: center;
    gap: 1.2rem;
    flex-wrap: wrap;
    margin-top: 1.2rem;
}

.ch-cmp-note {
    flex: 1 1 18rem;
    font-size: 0.82rem;
    line-height: 1.55;
    color: var(--ch-text-muted);
}

/* ==========================================================================
   4. MONITORING: SHELL, HEADER, STAGES
   ========================================================================== */

.ch-card-mon {
    grid-column: 1 / -1;
}

.ch-mon-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
    margin-bottom: 1.4rem;
}

.ch-mon-head-main {
    flex: 1 1 26rem;
}

.ch-mon-title {
    margin: 0.25rem 0 0.5rem;
    font-size: 1.45rem;
    line-height: 1.22;
    letter-spacing: -0.022em;
    color: var(--ch-text-main);
}

.ch-mon-sub {
    margin: 0;
    max-width: 68ch;
    font-size: 0.95rem;
    line-height: 1.62;
    color: var(--ch-text-muted);
}

.ch-mon-head-side {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.ch-mon-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--ch-border);
    border-radius: 999px;
    background-color: var(--ch-mon-well);
    font-family: var(--ch-mon-mono);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-mon-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background-color: var(--ch-text-muted);
}

.ch-mon-pill[data-status="verified"] {
    background-color: var(--ch-mon-good-bg);
    border-color: var(--ch-mon-good-line);
    color: var(--ch-mon-good);
}

.ch-mon-pill[data-status="verified"] .ch-mon-dot {
    background-color: var(--ch-mon-good);
    animation: ch-mon-live 2.4s ease-in-out infinite;
}

.ch-mon-pill[data-status="pending"] {
    background-color: var(--ch-mon-warn-bg);
    border-color: var(--ch-mon-warn-line);
    color: var(--ch-mon-warn);
}

.ch-mon-pill[data-status="pending"] .ch-mon-dot { background-color: var(--ch-mon-warn); }
.ch-mon-pill[data-status="paused"] .ch-mon-dot { background-color: var(--ch-text-muted); }

@keyframes ch-mon-live {
    0%, 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.5); }
    50%      { box-shadow: 0 0 0 5px rgba(16, 185, 129, 0); }
}

/* WHOSE STORE THIS IS, ABOVE THE HEADLINE.
   Mono because it is an address, emerald because it is the subject of every
   number underneath it, and a real link because the reader will want to open
   the shop it names. The arrow only moves on hover; a chip that animates at
   rest competes with the live dot in the pill beside it. */
.ch-mon-headhost {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.15rem;
    padding: 0.24rem 0.6rem 0.24rem 0.5rem;
    border: 1px solid var(--ch-mon-good-line);
    border-radius: 99px;
    background-color: var(--ch-mon-good-bg);
    font-family: var(--ch-mon-mono);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--ch-mon-good) !important;
    text-decoration: none !important;
    max-width: 100%;
    transition: background-color 0.2s, border-color 0.2s;
}

.ch-mon-headhost[hidden] { display: none; }

.ch-mon-headhost:hover {
    background-color: var(--ch-mon-good-bg);
    border-color: var(--ch-cta);
}

.ch-mon-headhost-ico { display: inline-flex; flex-shrink: 0; opacity: 0.9; }

.ch-mon-headhost-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ch-mon-headhost-go {
    display: inline-flex;
    flex-shrink: 0;
    opacity: 0;
    transform: translateX(-3px);
    transition: opacity 0.2s, transform 0.2s;
}

.ch-mon-headhost:hover .ch-mon-headhost-go { opacity: 1; transform: translateX(0); }

.ch-mon-headhost:focus-visible {
    outline: 2px solid var(--ch-cta);
    outline-offset: 2px;
}

.ch-mon-gear,
.ch-mon-gear:hover,
.ch-mon-gear:focus {
    appearance: none;
    background: transparent !important;
}

.ch-mon-gear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 1px solid var(--ch-border);
    border-radius: 8px;
    color: var(--ch-text-muted) !important;
    cursor: pointer;
    transition: color 0.2s, border-color 0.2s;
}

.ch-mon-gear:hover {
    color: var(--ch-cta) !important;
    border-color: var(--ch-cta);
}

/* A raw SVG dropped straight into a flex container rendered as nothing at all.
   Every icon elsewhere in this dashboard sits inside a span, so this one does
   too, with the size pinned rather than inherited. */
.ch-mon-gear-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    line-height: 0;
}

.ch-mon-gear-ico svg {
    display: block;
    width: 16px;
    height: 16px;
    stroke: currentColor;
}

.ch-mon-menu {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    z-index: 30;
    min-width: 15rem;
    padding: 0.35rem;
    background-color: var(--ch-bg-card);
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    box-shadow: 0 18px 34px -18px rgba(15, 23, 42, 0.35);
}

.ch-mon-menu[hidden] {
    display: none;
}

.ch-mon-menu button,
.ch-mon-menu button:hover,
.ch-mon-menu button:focus {
    appearance: none;
    background: transparent !important;
    border: 0;
}

/* The storefront entry is an anchor because it opens a page, and everything
   else in this menu is a button because it performs an action. They are
   selected together so the one that is a link does not read as a different
   kind of row. */
.ch-mon-menu button,
.ch-mon-menu .ch-mon-menu-link {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    width: 100%;
    padding: 0.55rem 0.7rem;
    border-radius: 7px;
    font-size: 0.86rem;
    text-align: left;
    text-decoration: none !important;
    color: var(--ch-text-main) !important;
    cursor: pointer;
}

.ch-mon-menu button:hover,
.ch-mon-menu .ch-mon-menu-link:hover {
    background-color: var(--ch-mon-well) !important;
}

.ch-mon-menu button.is-danger:hover {
    background-color: var(--ch-mon-stop-bg) !important;
    color: var(--ch-mon-stop) !important;
}

/* Stage switching. The panel renders all three and shows one. */
.ch-mon-stage { display: none; }

/* The headline follows the stage on the same principle. A shop that pasted the
   snippet weeks ago does not need to be told that setup takes two minutes. */
[data-rum-head] { display: none; }
.ch-card-mon[data-state="connect"] [data-rum-head="connect"],
.ch-card-mon[data-state="install"] [data-rum-head="install"],
.ch-card-mon[data-state="live"]    [data-rum-head="live"] {
    display: block;
}
.ch-card-mon[data-state="connect"] [data-rum-stage="connect"],
.ch-card-mon[data-state="install"] [data-rum-stage="install"],
.ch-card-mon[data-state="live"]    [data-rum-stage="live"] {
    display: block;
    animation: ch-fade-up 0.4s var(--ch-mon-ease) both;
}

/* ==========================================================================
   5. MONITORING: CONNECT AND INSTALL
   ========================================================================== */

/* THE CONNECT SCREEN.
   The field is the point of this screen, so it comes first and carries the
   only saturated colour above the fold. The preview on the right is a
   picture of the Speed & Traffic tab in the visitor's own theme (light card
   in light, Slate 900 panel in dark), so it reads as "the thing you are about
   to get" rather than as another card of text.

   These classes are new names on purpose. The old grid was .ch-mon-why, a
   name the automatic tests table also used for its page-reason line further
   down this file; that later rule won and stacked the four cards as
   full-width slabs. .ch-mon-why now belongs to the table alone. */

.ch-mon-hero {
    display: grid;
    grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr);
    gap: 1.5rem;
    align-items: start;
    margin-bottom: 2.2rem;
}

.ch-mon-hero-main {
    display: flex;
    flex-direction: column;
    gap: 1.4rem;
    min-width: 0;
}

.ch-mon-connect {
    position: relative;
    padding: 1.25rem 1.3rem 1.1rem;
    background-color: var(--ch-bg-card);
    border: 1px solid var(--ch-mon-good-line);
    border-radius: var(--ch-mon-radius);
    box-shadow: 0 0 0 4px var(--ch-mon-good-bg), 0 14px 32px -22px rgba(4, 120, 87, 0.55);
    animation: ch-fade-up 0.45s var(--ch-mon-ease) both;
}

.ch-mon-connect-label {
    display: block;
    margin-bottom: 0.6rem;
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ch-mon-good);
}

.ch-mon-connect-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.ch-mon-connect-field {
    position: relative;
    flex: 1 1 16rem;
    min-width: 0;
}

.ch-mon-connect-ico {
    position: absolute;
    left: 0.85rem;
    top: 50%;
    display: inline-flex;
    color: var(--ch-text-muted);
    transform: translateY(-50%);
    pointer-events: none;
}

.ch-mon-connect-field .ch-input {
    display: block;
    height: 48px;
    margin: 0;
    padding-left: 2.4rem;
    font-size: 1rem;
}

/* Wins over .ch-btn-auth's width: 100% on load order alone: same specificity,
   and monitor.css loads after portal.css. */
.ch-mon-connect-btn {
    width: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    height: 48px;
    margin: 0;
    padding: 0 1.35rem;
    line-height: 1;
    white-space: nowrap;
}

.ch-mon-connect-btn .ch-btn-label {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
}

.ch-mon-connect-btn .ch-btn-label svg {
    transition: transform 0.2s var(--ch-mon-ease);
}

.ch-mon-connect-btn:hover .ch-btn-label svg {
    transform: translateX(3px);
}

.ch-mon-connect-hint {
    margin: 0.7rem 0 0;
    font-size: 0.8rem;
    line-height: 1.5;
    color: var(--ch-text-muted);
}

/* Three steps joined by a rail, so they read as one short sequence. */
.ch-mon-flow {
    position: relative;
    display: grid;
    gap: 0.95rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ch-mon-flow::before {
    content: "";
    position: absolute;
    left: 13px;
    top: 14px;
    bottom: 14px;
    width: 2px;
    background: linear-gradient(to bottom, var(--ch-mon-good-line), var(--ch-border));
}

.ch-mon-flow-step {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 0.85rem;
    animation: ch-fade-up 0.45s var(--ch-mon-ease) both;
    animation-delay: calc(0.12s + var(--ch-i, 0) * 0.09s);
}

.ch-mon-flow-n {
    position: relative;
    z-index: 1;
    flex: 0 0 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background-color: var(--ch-bg-card);
    border: 2px solid var(--ch-mon-good-line);
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--ch-mon-good);
}

.ch-mon-flow-step:first-child .ch-mon-flow-n {
    background-color: var(--ch-cta);
    border-color: var(--ch-cta);
    color: #FFFFFF;
}

.ch-mon-flow-body {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding-top: 0.2rem;
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--ch-text-muted);
}

.ch-mon-flow-body strong:first-child {
    font-size: 0.94rem;
    color: var(--ch-text-main);
}

.ch-mon-flow-body span strong {
    color: var(--ch-text-main);
    font-weight: 650;
}

.ch-mon-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    margin: auto 0 0;
    padding: 0;
    list-style: none;
}

.ch-mon-facts li {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.7rem;
    border: 1px solid var(--ch-border);
    border-radius: 999px;
    background-color: var(--ch-mon-tile);
    font-size: 0.76rem;
    font-weight: 550;
    color: var(--ch-text-main);
}

.ch-mon-facts-ico {
    display: inline-flex;
    color: var(--ch-mon-good);
}

/* THE PREVIEW PANEL.
   A picture of the Speed & Traffic tab the owner is about to get, so in each
   theme it looks like that tab does: a white card with an emerald wash in
   light, the Slate 900 panel in dark (section 10). It was #0F172A in both
   themes until 2026-10-04; Adrian ruled out dark panels on a light page, and
   a dark preview of a light dashboard also misdescribed the product. Every
   colour reads a --ch-mon-pv-* token, so dark mode resets tokens only. */
.ch-mon-preview {
    --ch-mon-pv-bg:     #FFFFFF;
    --ch-mon-pv-glow:   rgba(16, 185, 129, 0.12);
    --ch-mon-pv-line:   #D1E7DD;
    --ch-mon-pv-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 24px 48px -30px rgba(4, 120, 87, 0.4);
    --ch-mon-pv-ink:    #0F172A;
    --ch-mon-pv-text:   #475569;
    --ch-mon-pv-mute:   #64748B;
    --ch-mon-pv-tile:   #F8FAFC;
    --ch-mon-pv-tile-line: #E2E8F0;
    --ch-mon-pv-track:  #E2E8F0;
    --ch-mon-pv-row:    #F1F5F9;
    --ch-mon-pv-row-hit: rgba(16, 185, 129, 0.18);
    --ch-mon-pv-code:   #334155;
    --ch-mon-pv-accent: #059669;
    --ch-mon-pv-good:   #047857;
    --ch-mon-pv-ni:     #B45309;
    --ch-mon-pv-mark:   #94A3B8;
    --ch-mon-pv-chip:   #CBD5E1;

    position: relative;
    display: flex;
    flex-direction: column;
    gap: 1rem;
    min-width: 0;
    padding: 1.15rem 1.2rem 1.2rem;
    overflow: hidden;
    border-radius: 14px;
    background:
        radial-gradient(120% 70% at 100% 0%, var(--ch-mon-pv-glow), rgba(16, 185, 129, 0) 60%),
        var(--ch-mon-pv-bg);
    border: 1px solid var(--ch-mon-pv-line);
    box-shadow: var(--ch-mon-pv-shadow);
    color: var(--ch-mon-pv-ink);
    animation: ch-fade-up 0.55s var(--ch-mon-ease) 0.1s both;
}

.ch-mon-preview-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.ch-mon-preview-live {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-size: 0.68rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ch-mon-pv-text);
}

.ch-mon-preview-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background-color: #10B981;
    animation: ch-mon-pv-pulse 2s ease-out infinite;
}

.ch-mon-preview-chip {
    padding: 0.2rem 0.55rem;
    border: 1px solid var(--ch-mon-pv-chip);
    border-radius: 999px;
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-size: 0.62rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ch-mon-pv-text);
}

.ch-mon-preview-vitals {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.55rem;
}

.ch-mon-preview-vital {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    padding: 0.65rem 0.7rem;
    border-radius: 10px;
    background-color: var(--ch-mon-pv-tile);
    border: 1px solid var(--ch-mon-pv-tile-line);
    animation: ch-fade-up 0.45s var(--ch-mon-ease) both;
    animation-delay: calc(0.25s + var(--ch-i, 0) * 0.1s);
}

.ch-mon-preview-k {
    font-size: 0.66rem;
    letter-spacing: 0.02em;
    color: var(--ch-mon-pv-text);
}

.ch-mon-preview-vital strong {
    font-size: 1.15rem;
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--ch-mon-pv-ink);
}

.ch-mon-preview-bar {
    display: block;
    height: 4px;
    overflow: hidden;
    border-radius: 999px;
    background-color: var(--ch-mon-pv-track);
}

.ch-mon-preview-bar span {
    display: block;
    width: var(--ch-w, 80%);
    height: 100%;
    border-radius: inherit;
    background: linear-gradient(90deg, #10B981, var(--ch-mon-pv-accent));
    transform-origin: left center;
    animation: ch-mon-pv-grow 1.1s var(--ch-mon-ease) both;
    animation-delay: calc(0.5s + var(--ch-i, 0) * 0.12s);
}

.ch-mon-preview-chart {
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
    padding: 0.75rem 0.8rem 0.5rem;
    border-radius: 10px;
    background-color: var(--ch-mon-pv-tile);
    border: 1px solid var(--ch-mon-pv-tile-line);
}

.ch-mon-preview-chart-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
}

.ch-mon-preview-mark-k {
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-size: 0.6rem;
    letter-spacing: 0.04em;
    color: var(--ch-mon-pv-mute);
}

.ch-mon-preview-chart svg {
    display: block;
    width: 100%;
    height: 82px;
    overflow: visible;
}

.ch-mon-preview-mark {
    stroke: var(--ch-mon-pv-mark);
    stroke-width: 1;
    stroke-dasharray: 3 4;
    vector-effect: non-scaling-stroke;
}

.ch-mon-preview-area {
    fill: rgba(16, 185, 129, 0.14);
    animation: ch-mon-pv-fade 0.8s ease-out 1.5s both;
}

.ch-mon-preview-line {
    fill: none;
    stroke: var(--ch-mon-pv-accent);
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1;
    stroke-dashoffset: 0;
    vector-effect: non-scaling-stroke;
    animation: ch-mon-pv-draw 1.6s var(--ch-mon-ease) 0.45s both;
}

.ch-mon-preview-end {
    fill: var(--ch-mon-pv-accent);
    transform-box: fill-box;
    transform-origin: center;
    animation: ch-mon-pv-fade 0.3s ease-out 1.9s both, ch-mon-pv-beat 2.4s ease-in-out 2.2s infinite;
}

.ch-mon-preview-feed {
    display: grid;
    gap: 0.35rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ch-mon-preview-feed li {
    display: grid;
    grid-template-columns: 4.2rem minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.6rem;
    padding: 0.45rem 0.65rem;
    border-radius: 8px;
    background-color: var(--ch-mon-pv-row);
    font-size: 0.74rem;
    color: var(--ch-mon-pv-text);
    animation:
        ch-fade-up 0.45s var(--ch-mon-ease) both,
        ch-mon-pv-visit 7.5s ease-in-out infinite;
    animation-delay:
        calc(1s + var(--ch-i, 0) * 0.15s),
        calc(2.5s + var(--ch-i, 0) * 2.5s);
}

.ch-mon-preview-feed code {
    overflow: hidden;
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-size: 0.72rem;
    color: var(--ch-mon-pv-code);
    text-overflow: ellipsis;
    white-space: nowrap;
    background: none;
    padding: 0;
}

.ch-mon-preview-feed b {
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-weight: 700;
}

.ch-mon-preview-feed b.is-good { color: var(--ch-mon-pv-good); }
.ch-mon-preview-feed b.is-ni   { color: var(--ch-mon-pv-ni); }

@keyframes ch-mon-pv-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.55); }
    80%  { box-shadow: 0 0 0 8px rgba(52, 211, 153, 0); }
    100% { box-shadow: 0 0 0 0 rgba(52, 211, 153, 0); }
}

@keyframes ch-mon-pv-grow {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}

@keyframes ch-mon-pv-draw {
    from { stroke-dashoffset: 1; }
    to   { stroke-dashoffset: 0; }
}

@keyframes ch-mon-pv-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes ch-mon-pv-beat {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.45); }
}

/* A row lights up as if a visit just arrived, then settles. Each row has its
   own offset, so the feed never flashes all three at once. */
@keyframes ch-mon-pv-visit {
    0%, 12%, 100% { background-color: var(--ch-mon-pv-row); }
    4%            { background-color: var(--ch-mon-pv-row-hit); }
}

.ch-mon-subhead {
    margin: 0 0 0.9rem;
    font-size: 1.05rem;
    letter-spacing: -0.01em;
    color: var(--ch-text-main);
}

.ch-mon-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr));
    gap: 0.9rem;
}

.ch-mon-tile {
    padding: 1.05rem 1.1rem 1.1rem;
    background-color: var(--ch-mon-tile);
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-mon-radius);
    transition: transform 0.2s var(--ch-mon-ease), border-color 0.2s, box-shadow 0.2s;
    animation: ch-fade-up 0.45s var(--ch-mon-ease) both;
    animation-delay: calc(0.35s + var(--ch-i, 0) * 0.07s);
}

.ch-mon-tile:hover {
    transform: translateY(-2px);
    border-color: var(--ch-mon-good-line);
    box-shadow: 0 12px 24px -18px rgba(15, 23, 42, 0.45);
}

.ch-mon-tile-ico {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-bottom: 0.7rem;
    border-radius: 9px;
    background-color: var(--ch-mon-good-bg);
    color: var(--ch-mon-good);
}

.ch-mon-tile h4 {
    margin: 0 0 0.35rem;
    font-size: 0.95rem;
    line-height: 1.3;
    color: var(--ch-text-main);
}

.ch-mon-tile p {
    margin: 0;
    font-size: 0.84rem;
    line-height: 1.55;
    color: var(--ch-text-muted);
}

.ch-mon-tile p strong {
    color: var(--ch-text-main);
    font-weight: 650;
}

@media (max-width: 900px) {
    .ch-mon-hero {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 600px) {
    .ch-mon-connect-btn {
        width: 100%;
    }

    /* The feed is the most decorative part of the preview; on a phone the
       height it costs is better spent getting to the tiles. */
    .ch-mon-preview-feed {
        display: none;
    }

    /* Three columns leave about 50px per label at this width, and
       "Responsiveness" does not fit in 50px at any readable size. Rows keep
       the full word: label left, value right, bar underneath. */
    .ch-mon-preview-vitals {
        grid-template-columns: minmax(0, 1fr);
        gap: 0.4rem;
    }

    .ch-mon-preview-vital {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: baseline;
        row-gap: 0.35rem;
        padding: 0.55rem 0.7rem;
    }

    .ch-mon-preview-vital .ch-mon-preview-bar {
        grid-column: 1 / -1;
    }

    .ch-mon-preview-vital .ch-mon-preview-k {
        font-size: 0.72rem;
    }

    .ch-mon-preview-vital strong {
        font-size: 1rem;
        white-space: nowrap;
    }
}

.ch-mon-progress {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin: 0 0 1.4rem;
    padding: 0;
    list-style: none;
}

.ch-mon-progress li {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.85rem 0.4rem 0.4rem;
    border: 1px solid var(--ch-border);
    border-radius: 999px;
    font-size: 0.8rem;
    color: var(--ch-text-muted);
}

.ch-mon-progress li span {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background-color: var(--ch-mon-well);
    font-family: var(--ch-mon-mono);
    font-size: 0.7rem;
    font-weight: 800;
}

.ch-mon-progress li.is-done {
    border-color: var(--ch-mon-good-line);
    color: var(--ch-mon-good);
}

.ch-mon-progress li.is-done span {
    background-color: var(--ch-mon-good);
    color: #FFFFFF;
}

.ch-mon-progress li.is-current {
    border-color: var(--ch-cta);
    color: var(--ch-text-main);
    font-weight: 600;
}

.ch-mon-snippet-block {
    margin-bottom: 1.5rem;
}

.ch-mon-snippet-head h3 {
    margin: 0 0 0.35rem;
    font-size: 1.05rem;
    color: var(--ch-text-main);
}

.ch-mon-snippet-head p {
    margin: 0 0 0.8rem;
    max-width: 70ch;
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--ch-text-muted);
}

.ch-mon-code {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.9rem 1rem;
    background-color: var(--ch-mon-ink-deep);
    border: 1px solid #334155;
    border-radius: var(--ch-mon-radius);
    overflow: hidden;
}

.ch-mon-code code {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    white-space: pre;
    font-family: var(--ch-mon-mono);
    font-size: 0.8rem;
    line-height: 1.65;
    color: #E2E8F0;
    background: transparent;
    padding: 0;
}

.ch-mon-code.is-block {
    margin: 0.9rem 0;
}

.ch-mon-code.is-block code {
    white-space: pre-wrap;
    word-break: break-word;
}

.ch-mon-copy,
.ch-mon-copy:hover,
.ch-mon-copy:focus {
    appearance: none;
    background-color: #1E293B !important;
}

.ch-mon-copy {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.75rem;
    border: 1px solid #334155;
    border-radius: 7px;
    font-family: var(--ch-mon-mono);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    color: #94A3B8 !important;
    cursor: pointer;
    transition: color 0.2s, border-color 0.2s;
}

.ch-mon-copy:hover {
    color: #34D399 !important;
    border-color: #34D399;
}

.ch-mon-copy.is-done {
    color: #34D399 !important;
    border-color: #34D399;
}

.ch-mon-platforms {
    margin-bottom: 1.5rem;
}

.ch-mon-guide[hidden] {
    display: none;
}

.ch-mon-guide-lead {
    margin: 0 0 1rem;
    max-width: 70ch;
    font-size: 0.92rem;
    line-height: 1.62;
    color: var(--ch-text-main);
}

.ch-mon-steps {
    margin: 0 0 1rem;
    padding: 0;
    list-style: none;
    counter-reset: none;
}

.ch-mon-steps li {
    display: flex;
    gap: 0.85rem;
    padding: 0.75rem 0;
    border-top: 1px solid var(--ch-border);
}

.ch-mon-steps li:first-child {
    border-top: 0;
    padding-top: 0;
}

.ch-mon-step-n {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background-color: var(--ch-mon-good-bg);
    color: var(--ch-mon-good);
    font-family: var(--ch-mon-mono);
    font-size: 0.75rem;
    font-weight: 800;
}

.ch-mon-step-body {
    display: block;
    min-width: 0;
}

.ch-mon-step-body > strong {
    display: block;
    margin-bottom: 0.2rem;
    font-size: 0.92rem;
    color: var(--ch-text-main);
}

.ch-mon-step-body > span {
    display: block;
    font-size: 0.88rem;
    line-height: 1.62;
    color: var(--ch-text-muted);
}

.ch-mon-step-body code,
.ch-mon-gotcha code {
    padding: 0.1em 0.35em;
    background-color: var(--ch-mon-well);
    border-radius: 4px;
    font-family: var(--ch-mon-mono);
    font-size: 0.85em;
    color: var(--ch-text-main);
}

.ch-mon-gotcha {
    display: flex;
    gap: 0.6rem;
    margin: 0 0 0.7rem;
    padding: 0.75rem 0.9rem;
    background-color: var(--ch-mon-warn-bg);
    border: 1px solid var(--ch-mon-warn-line);
    border-radius: 10px;
    font-size: 0.86rem;
    line-height: 1.58;
    color: var(--ch-text-main);
}

.ch-mon-gotcha svg {
    flex: 0 0 auto;
    margin-top: 0.15rem;
    color: var(--ch-mon-warn);
}

.ch-mon-safety {
    margin: 0;
    font-size: 0.82rem;
    line-height: 1.55;
    color: var(--ch-text-muted);
}

.ch-mon-verify {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.2rem;
    flex-wrap: wrap;
    padding: 1.15rem 1.3rem;
    background-color: var(--ch-mon-well);
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-mon-radius);
}

.ch-mon-verify.is-done {
    background-color: var(--ch-mon-good-bg);
    border-color: var(--ch-mon-good-line);
}

.ch-mon-verify-main {
    display: flex;
    align-items: flex-start;
    gap: 0.9rem;
    flex: 1 1 22rem;
}

.ch-mon-verify-main strong {
    display: block;
    margin-bottom: 0.2rem;
    font-size: 0.98rem;
    color: var(--ch-text-main);
}

.ch-mon-verify-main span[data-rum-verify-note] {
    display: block;
    max-width: 60ch;
    font-size: 0.86rem;
    line-height: 1.58;
    color: var(--ch-text-muted);
}

.ch-mon-verify-ico {
    position: relative;
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
}

.ch-mon-radar,
.ch-mon-radar::before,
.ch-mon-radar::after {
    position: absolute;
    inset: 0;
    margin: auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background-color: var(--ch-cta);
}

.ch-mon-radar::before,
.ch-mon-radar::after {
    content: '';
    background-color: transparent;
    border: 2px solid var(--ch-cta);
    animation: ch-mon-ping 2.2s var(--ch-mon-ease) infinite;
}

.ch-mon-radar::after {
    animation-delay: 1.1s;
}

@keyframes ch-mon-ping {
    0%   { width: 10px; height: 10px; opacity: 0.65; }
    100% { width: 34px; height: 34px; opacity: 0; }
}

/* ==========================================================================
   6. MONITORING: VITALS CARDS
   ========================================================================== */

.ch-mon-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.3rem;
}

.ch-mon-toolbar .ch-stack-switch {
    margin-bottom: 0;
}

.ch-stack-btn.is-locked {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.42rem 0.85rem;
    text-decoration: none;
    opacity: 0.75;
}

.ch-mon-updated {
    font-family: var(--ch-mon-mono);
    font-size: 0.72rem;
    letter-spacing: 0.3px;
    color: var(--ch-text-muted);
}

.ch-mon-vitals {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}

.ch-mon-vital {
    padding: 1.15rem 1.25rem;
    background-color: var(--ch-mon-tile);
    border: 1px solid var(--ch-border);
    border-left: 3px solid var(--ch-border);
    border-radius: var(--ch-mon-radius);
    transition: border-color 0.25s;
}

.ch-mon-vital[data-verdict="good"] { border-left-color: var(--ch-mon-good); }
.ch-mon-vital[data-verdict="ni"]   { border-left-color: var(--ch-mon-warn); }
.ch-mon-vital[data-verdict="poor"] { border-left-color: var(--ch-mon-stop); }

.ch-mon-vital header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.35rem;
}

.ch-mon-vital-name {
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--ch-text-main);
}

.ch-mon-vital-code {
    font-family: var(--ch-mon-mono);
    font-size: 0.64rem;
    font-weight: 800;
    letter-spacing: 1px;
    color: var(--ch-text-muted);
}

.ch-mon-vital-value {
    display: block;
    font-size: 1.85rem;
    line-height: 1.1;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--ch-text-main);
}

.ch-mon-vital-verdict {
    display: inline-block;
    margin: 0.35rem 0 0.7rem;
    padding: 0.16rem 0.5rem;
    border-radius: 5px;
    font-family: var(--ch-mon-mono);
    font-size: 0.63rem;
    font-weight: 800;
    letter-spacing: 0.7px;
    text-transform: uppercase;
    background-color: var(--ch-mon-well);
    color: var(--ch-text-muted);
}

.ch-mon-vital-verdict[data-verdict="good"] { background-color: var(--ch-mon-good-bg); color: var(--ch-mon-good); }
.ch-mon-vital-verdict[data-verdict="ni"]   { background-color: var(--ch-mon-warn-bg); color: var(--ch-mon-warn); }
.ch-mon-vital-verdict[data-verdict="poor"] { background-color: var(--ch-mon-stop-bg); color: var(--ch-mon-stop); }

.ch-mon-bar {
    display: flex;
    height: 6px;
    border-radius: 999px;
    overflow: hidden;
    background-color: var(--ch-mon-well);
}

.ch-mon-bar span {
    display: block;
    height: 100%;
    width: 0;
    transition: width 0.5s var(--ch-mon-ease);
}

.ch-mon-bar .is-good { background-color: var(--ch-mon-good); }
.ch-mon-bar .is-ni   { background-color: #F59E0B; }
.ch-mon-bar .is-poor { background-color: #EF4444; }

.ch-mon-vital-share {
    display: block;
    margin-top: 0.45rem;
    font-size: 0.78rem;
    color: var(--ch-text-muted);
}

.ch-mon-vital-blurb {
    margin: 0.7rem 0 0;
    font-size: 0.82rem;
    line-height: 1.55;
    color: var(--ch-text-muted);
}

/* ==========================================================================
   7. MONITORING: CHARTS
   ========================================================================== */

.ch-mon-panel {
    margin-bottom: 1.4rem;
    padding: 1.25rem;
    background-color: var(--ch-mon-tile);
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-mon-radius);
}

/* PANEL HEAD.
   Title on the left, a control cluster on the right, and the cluster keeps its
   own internal order at every width: counters first, then the buttons that
   change them. The old rule put badges and buttons in the same wrapping flex
   row as the title, so at tablet width a button could end up between two
   badges, and at phone width the whole cluster stacked into a ragged column.

   align-items is flex-start rather than center. The subtitle under these
   titles runs to four lines on the automatic-tests panel, and centring a
   two-line control cluster against it floats the buttons in the middle of the
   paragraph. */
.ch-mon-panel-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem 1.25rem;
    flex-wrap: wrap;
    margin-bottom: 1.1rem;
}

.ch-mon-panel-head h3 {
    margin: 0;
    font-size: 1.06rem;
    font-weight: 750;
    letter-spacing: -0.018em;
    color: var(--ch-text-main);
}

.ch-mon-panel-side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.55rem;
    flex: 0 1 auto;
}

.ch-mon-panel-actions {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    justify-content: flex-end;
}

/* THE TWO PANEL BUTTONS.
   Both were the same neutral chip, so nothing on screen said which one a
   reader presses first. Update my page list is maintenance and stays neutral.
   Test them all now is the action the panel exists for, and it takes an
   emerald tint rather than a full solid fill: a solid CTA here would push
   people to spend a monthly budget they did not know they had, which is the
   opposite of what the badge beside it is for. */
.ch-mon-panel-actions .ch-qrlib-btn {
    padding: 0.48rem 0.8rem;
    font-size: 0.78rem;
}

.ch-mon-panel-actions .ch-qrlib-btn.is-accent {
    color: var(--ch-mon-good);
    border-color: var(--ch-mon-good-line);
    background-color: var(--ch-mon-good-bg);
}

.ch-mon-panel-actions .ch-qrlib-btn.is-accent svg { opacity: 1; }

.ch-mon-panel-actions .ch-qrlib-btn.is-accent:hover {
    color: #FFFFFF;
    border-color: var(--ch-cta);
    background-color: var(--ch-cta);
}

html[data-theme="dark"] .ch-mon-panel-actions .ch-qrlib-btn.is-accent {
    background-color: var(--ch-mon-good-bg);
    color: var(--ch-mon-good);
}

html[data-theme="dark"] .ch-mon-panel-actions .ch-qrlib-btn.is-accent:hover {
    background-color: var(--ch-cta);
    color: #04241C;
}

.ch-mon-figures {
    display: flex;
    gap: 1.2rem;
    flex-wrap: wrap;
    font-size: 0.84rem;
    color: var(--ch-text-muted);
}

.ch-mon-figures strong {
    font-size: 1.05rem;
    color: var(--ch-text-main);
}

.ch-mon-chart {
    width: 100%;
    min-height: 60px;
}

.ch-mon-svg {
    display: block;
    width: 100%;
    height: auto;
}

.ch-mon-chart-empty {
    margin: 0;
    padding: 1.6rem 0;
    text-align: center;
    font-size: 0.86rem;
    color: var(--ch-text-muted);
}

/* THE SHARED TOOLTIP.
   One floating element, reused by the Traffic bars, the Speed over time
   points, and the Devices donut slices, appended straight to <body> rather
   than nested inside the chart it belongs to. Two reasons: position:fixed
   coordinates are relative to the viewport regardless of where the element
   lives in the DOM, and --ch-text-main / --ch-bg-card / --ch-border are
   declared on :root and html[data-theme="dark"] in style.css and
   dark-mode.css - global tokens, not scoped to a component - so this reads
   the correct theme colours from anywhere in the page without needing to be
   nested inside .ch-portal-dash for inheritance to reach it.

   Replaces the native SVG <title> tooltip everywhere it was used. <title>
   renders however the browser and OS feel like rendering it - colour, delay,
   position are all outside CSS's reach - and on touch it does nothing at all
   in most browsers, or needs a long-press in the rest. This account's own
   traffic is 74-77% mobile; a tooltip that only works with a mouse fails
   most of the people this panel is for. */
.ch-mon-tooltip {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 200;
    padding: 0.4rem 0.65rem;
    border-radius: 7px;
    background-color: var(--ch-bg-card);
    border: 1px solid var(--ch-border);
    box-shadow: 0 10px 24px -10px rgba(15, 23, 42, 0.4);
    color: var(--ch-text-main);
    font-family: var(--ch-mon-mono);
    font-size: 0.74rem;
    font-weight: 650;
    line-height: 1.4;
    pointer-events: none;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.12s ease, transform 0.12s ease;
}

.ch-mon-tooltip.is-visible {
    opacity: 1;
    transform: translateY(0);
}

/* The caret. Points down when the tooltip sits above its target (the usual
   case) and flips to point up on .is-below, which JS adds when there is not
   enough room above the target - the top row of the Devices donut, or the
   first days of a Traffic chart that has not scrolled into view yet. */
.ch-mon-tooltip::after {
    content: '';
    position: absolute;
    left: 50%;
    bottom: -5px;
    width: 0;
    height: 0;
    transform: translateX(-50%);
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-top: 5px solid var(--ch-bg-card);
}

.ch-mon-tooltip.is-below::after {
    bottom: auto;
    top: -5px;
    border-top: none;
    border-bottom: 5px solid var(--ch-bg-card);
}

.ch-mon-grid {
    stroke: var(--ch-border);
    stroke-width: 1;
}

.ch-mon-axis {
    fill: var(--ch-text-muted);
    font-family: var(--ch-mon-mono);
    font-size: 10px;
}

/* PAIRED COLUMNS.
   Page views is the pale outer bar and unique visitors the solid inner one,
   which encodes a fact a second line never could: visitors are a subset of
   views, so the inner bar is always contained by the outer and the difference
   between them is pages per visit, read at a glance.

   The opacity split rather than two hues: one measurement in two shades reads
   as one quantity described twice, where two colours read as two unrelated
   things that happen to share an axis. */
.ch-mon-col {
    fill: var(--ch-cta);
    opacity: 0.26;
}

.ch-mon-col.is-views {
    opacity: 0.24;
}

.ch-mon-col.is-visitors {
    opacity: 0.85;
}

/* Verdict-coloured columns, used when a metric chart has one point and there
   is no line for the reader to judge a position against. */
.ch-mon-col.is-good { fill: var(--ch-mon-good); opacity: 0.75; }
.ch-mon-col.is-ni   { fill: #F59E0B; opacity: 0.75; }
.ch-mon-col.is-poor { fill: #EF4444; opacity: 0.75; }

/* Full-height hover target per band. A 3px column is not something anybody
   should have to aim at, least of all on a phone. */
.ch-mon-hit {
    fill: transparent;
    pointer-events: all;
}

.ch-mon-hit:hover {
    fill: currentColor;
    opacity: 0.05;
}

.ch-mon-point-label {
    fill: var(--ch-text-main);
    font-family: var(--ch-mon-mono);
    font-size: 11px;
    font-weight: 700;
}

.ch-mon-line {
    stroke: var(--ch-cta);
    stroke-width: 2.2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

/* The chart legend. Needed the moment two series share one axis: without it
   the pale bar and the solid bar are a pattern rather than two numbers. */
.ch-mon-keys {
    display: flex;
    flex-wrap: wrap;
    gap: 1.1rem;
    margin: 0.6rem 0 0;
    padding: 0;
    list-style: none;
    font-size: 0.78rem;
    color: var(--ch-text-muted);
}

.ch-mon-keys li {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.ch-mon-key {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 3px;
    background-color: var(--ch-cta);
}

.ch-mon-key.is-views    { opacity: 0.26; }
.ch-mon-key.is-visitors { opacity: 0.85; }

.ch-mon-dot {
    stroke: var(--ch-bg-card);
    stroke-width: 1.5;
}

.ch-mon-dot.is-good { fill: var(--ch-mon-good); }
.ch-mon-dot.is-ni   { fill: #F59E0B; }
.ch-mon-dot.is-poor { fill: #EF4444; }

/* The threshold bands. Deliberately very low contrast: they are a backdrop
   telling you which zone the line is in, not data in their own right. */
.ch-mon-band.is-good { fill: rgba(16, 185, 129, 0.09); }
.ch-mon-band.is-ni   { fill: rgba(245, 158, 11, 0.08); }
.ch-mon-band.is-poor { fill: rgba(239, 68, 68, 0.06); }

/* THE LEGEND UNDER (OR OVER) A TABLE.
   It explains what the colours and the scale mean, so it is part of the table
   rather than a stray paragraph - and 0.9rem of air above it was not enough to
   say so: on the automatic-tests panel, where the legend prints BEFORE the
   table, it read as a caption glued to the header row. It is a block with its
   own edge now, with room on both sides, because the same class appears above
   one table and below another. */
.ch-mon-note {
    margin: 1.15rem 0;
    max-width: 78ch;
    padding: 0.75rem 0.9rem;
    background-color: var(--ch-mon-well);
    border-left: 3px solid var(--ch-mon-line-hard);
    border-radius: 0 8px 8px 0;
    font-size: 0.82rem;
    line-height: 1.65;
    color: var(--ch-text-muted);
}

.ch-mon-note strong { color: var(--ch-text-main); font-weight: 650; }

/* A legend that is the last thing in its panel needs no bottom margin, and one
   that leads the panel needs none on top. :first-child and :last-child do that
   without a second class to remember. */
.ch-mon-panel > .ch-mon-note:last-child { margin-bottom: 0; }

.ch-mon-splits {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 1rem;
    margin-bottom: 1.4rem;
}

.ch-mon-panel.is-third {
    margin-bottom: 0;
}

.ch-mon-donut {
    display: block;
    width: 100%;
    max-width: 160px;
    height: auto;
    margin: 0 auto;
}

.ch-mon-slice.is-0 { stroke: var(--ch-mon-s0); }
.ch-mon-slice.is-1 { stroke: var(--ch-mon-s1); }
.ch-mon-slice.is-2 { stroke: var(--ch-mon-s2); }
.ch-mon-slice.is-3 { stroke: var(--ch-mon-s3); }
.ch-mon-slice.is-4 { stroke: var(--ch-mon-s4); }

/* THE DONUT/BAR CHART'S COLOUR KEY.
   Named .ch-mon-key-list rather than .ch-mon-legend, which this file also
   uses - unrelated to this one - for the three-column text explainer under
   each data table (see "THE STATUS STRIP AND THE LEGEND GRID" below). Same
   class name, two different components, and the later definition's
   display:grid and border-top were winning the cascade over this one's
   display:block list styling for every property they both touched. A colour
   key rendering as an unwanted grid with a stray top border is exactly the
   kind of thing that looks like "the Devices card is broken somehow" without
   ever pointing at why. */
.ch-mon-key-list {
    margin: 0.9rem 0 0;
    padding: 0;
    list-style: none;
}

.ch-mon-key-list li {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0;
    font-size: 0.84rem;
    color: var(--ch-text-muted);
}

.ch-mon-swatch {
    width: 10px;
    height: 10px;
    border-radius: 3px;
    flex: 0 0 auto;
}

.ch-mon-swatch.is-0 { background-color: var(--ch-mon-s0); }
.ch-mon-swatch.is-1 { background-color: var(--ch-mon-s1); }
.ch-mon-swatch.is-2 { background-color: var(--ch-mon-s2); }
.ch-mon-swatch.is-3 { background-color: var(--ch-mon-s3); }
.ch-mon-swatch.is-4 { background-color: var(--ch-mon-s4); }

.ch-mon-legend-name { flex: 1 1 auto; color: var(--ch-text-main); }
.ch-mon-legend-val  { font-family: var(--ch-mon-mono); font-size: 0.78rem; }

.ch-mon-bars {
    margin: 0;
    padding: 0;
    list-style: none;
}

.ch-mon-bar-track {
    flex: 1 1 auto;
    height: 6px;
    border-radius: 999px;
    background-color: var(--ch-mon-well);
    overflow: hidden;
}

.ch-mon-bar-fill {
    display: block;
    height: 100%;
    background-color: var(--ch-cta);
    transition: width 0.5s var(--ch-mon-ease);
}

/* Not .ch-mon-bar. That class is the distribution meter inside a vitals
   card, and it sets height:6px, overflow:hidden and width:0 on every child
   span. A list row sharing the name inherited all three and rendered as an
   empty grey slab. */
.ch-mon-brow {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0;
    font-size: 0.84rem;
    color: var(--ch-text-main);
}

.ch-mon-bar-name {
    flex: 0 0 7.5rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ch-mon-bar-val {
    flex: 0 0 2.6rem;
    text-align: right;
    font-family: var(--ch-mon-mono);
    font-size: 0.78rem;
    color: var(--ch-text-muted);
}

/* ==========================================================================
   7.5 STRUCTURE READOUT
   The two buttons above this used to change nothing visible: discovery filled
   a database column and the sweep quietly read a few URLs out of it. Pressing
   a button and watching nothing happen is indistinguishable from a broken
   button, so what was found and what will be audited are printed here.
   ========================================================================== */

/* ==========================================================================
   THE HIDDEN ATTRIBUTE WINS. ALWAYS.
   `[hidden] { display: none }` is a user-agent rule, so ANY author rule that
   sets display beats it on specificity. `.ch-mon-working { display: flex }`
   therefore made its own `hidden` attribute inert, and a progress bar that
   should have been invisible sat empty on the panel from page load.

   Two rules in this file already carried a per-class guard for exactly this,
   which means the trap was known and stepped in anyway. One rule for the whole
   panel is the version that cannot be forgotten. The !important is deliberate:
   `hidden` is a semantic contract about whether an element exists for the
   reader, and no layout declaration should ever be allowed to overrule it.
   ========================================================================== */
.ch-card-mon [hidden] {
    display: none !important;
}

/* The progress line for work happening on the worker. Emerald, because it is
   a report of normal operation - the amber and red in this panel are reserved
   for things that are actually wrong. */
.ch-mon-working {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin: 0.9rem 0 0;
    padding: 0.7rem 0.9rem;
    border-radius: 8px;
    background-color: var(--ch-mon-good-bg);
    color: var(--ch-mon-good);
    font-size: 0.83rem;
    line-height: 1.5;
}

.ch-mon-working::before {
    content: "";
    flex: 0 0 auto;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background-color: currentColor;
    animation: chMonWorking 1.4s ease-in-out infinite;
}

@keyframes chMonWorking {
    0%, 100% { opacity: 0.25; transform: scale(0.8); }
    50%      { opacity: 1;    transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .ch-mon-working::before { animation: none; opacity: 1; }
}

/* ==========================================================================
   THE STATUS STRIP AND THE LEGEND GRID

   These two replace a layout that put roughly fifteen lines of prose between
   the panel heading and the first row of the table. A four sentence subtitle,
   then a full width box printing two values, then a six line legend that ran
   to 78ch and left the rest of an 1100px row empty. Somebody opens this panel
   to see whether a page got slower, and had to scroll past all of it first.

   The strip is what belongs above a table: short labelled facts, laid out
   across the width rather than down it. The legend is what belongs below one:
   the explanation you read once and then never need again, in columns so it
   fills the row instead of trailing off into white space.
   ========================================================================== */

.ch-mon-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: 1px;
    margin: 0 0 1.1rem;
    padding: 0;
    border: 1px solid var(--ch-mon-line);
    border-radius: 10px;
    overflow: hidden;
    /* The gap IS the hairline. One background under a 1px grid gap draws every
       divider, interior only, with no rule to remove from the last cell in a
       row and nothing to recalculate when the columns rewrap. */
    background-color: var(--ch-mon-line);
}

.ch-mon-strip[hidden] { display: none; }

/* The strip sits on the same surface as the table below it, because both are
   data and a reader should not have to work out that they are two kinds of
   thing. --ch-mon-well was the first choice and it is wrong in dark: the well
   is Slate 900 there, which is DARKER than the panel it sits in, so the strip
   read as a hole rather than as a row of readings. */
.ch-mon-strip-cell {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    min-width: 0;
    padding: 0.8rem 0.95rem 0.85rem;
    background-color: var(--ch-mon-table);
}

.ch-mon-strip-k {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    font-family: var(--ch-mon-mono);
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-mon-strip-k svg { flex-shrink: 0; opacity: 0.8; }

.ch-mon-strip-v {
    font-size: 0.86rem;
    font-weight: 650;
    line-height: 1.4;
    color: var(--ch-text-main);
}

/* The second line a cell gets when the value alone would be a claim the reader
   has to take on trust. Only the cost cell uses it today: the price is the
   value, and the reason the price is nothing goes underneath. */
.ch-mon-strip-hint {
    font-size: 0.72rem;
    line-height: 1.45;
    color: var(--ch-text-muted);
}

/* The one cell that answers "what does this cost me". It is the question a
   member asks before pressing anything in this panel, and the answer is good
   news, so it gets the emerald treatment rather than another grey cell. */
/* The tint is a background IMAGE over the solid surface, not a background
   colour instead of it. --ch-mon-good-bg is translucent, and a translucent
   fill on a grid cell shows the gap colour underneath it - which here is the
   hairline, so the cell came out tinted with the divider rather than with
   emerald. Two layers keeps the tint and keeps the surface. */
.ch-mon-strip-cell.is-good {
    background-color: var(--ch-mon-table);
    background-image: linear-gradient(var(--ch-mon-good-bg), var(--ch-mon-good-bg));
}

.ch-mon-strip-cell.is-good .ch-mon-strip-k { color: var(--ch-mon-good); }

/* --- The legend grid ------------------------------------------------------ */

.ch-mon-legend {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: 1.1rem 1.6rem;
    margin: 1.2rem 0 0;
    padding-top: 1.1rem;
    border-top: 1px solid var(--ch-mon-line);
}

.ch-mon-legend-col { min-width: 0; }

.ch-mon-legend-k {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 0.35rem;
    font-family: var(--ch-mon-mono);
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-mon-legend-k svg { flex-shrink: 0; opacity: 0.8; }

.ch-mon-legend-col p {
    margin: 0;
    font-size: 0.82rem;
    line-height: 1.65;
    color: var(--ch-text-muted);
}

.ch-mon-legend-col p strong { color: var(--ch-text-main); font-weight: 650; }

/* A legend that is the last thing in its panel needs no trailing gap. */
.ch-mon-panel > .ch-mon-legend:last-child { margin-bottom: 0; }

/* ==========================================================================
   METRIC CELLS
   A column of plain numbers wastes the one thing that makes a table readable
   at a glance. The thresholds behind these colours are Google's own and are
   ABSOLUTE on purpose: 2.5 seconds is the pass mark whatever else the store is
   doing, and shading relative to the store's own average would tell somebody
   their slowest page is fine because the rest are slower.
   ========================================================================== */
.ch-mon-table td.is-metric {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.ch-mon-metric-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    margin-right: 0.45rem;
    border-radius: 50%;
    vertical-align: 0.05em;
}

.ch-mon-table td.is-metric.is-good .ch-mon-metric-dot { background-color: var(--ch-mon-good); }
.ch-mon-table td.is-metric.is-ni   .ch-mon-metric-dot { background-color: var(--ch-mon-warn); }
.ch-mon-table td.is-metric.is-poor .ch-mon-metric-dot { background-color: var(--ch-mon-stop); }

.ch-mon-table td.is-metric.is-good { color: var(--ch-mon-good); font-weight: 600; }
.ch-mon-table td.is-metric.is-ni   { color: var(--ch-mon-warn); font-weight: 600; }
.ch-mon-table td.is-metric.is-poor { color: var(--ch-mon-stop); font-weight: 600; }

.ch-mon-table td.is-none {
    color: var(--ch-text-muted);
}

.ch-mon-table td.is-num {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   THE STORE-RELATIVE CELL
   Absolute bands produced a column of identical red on a real store: every
   page between 38 and 46, every pill the same colour, and no answer to the one
   question the column exists for. The number stays absolute and the BAR does
   the ranking, against this store's own best and worst - so there is always a
   spread to read, at any absolute level.
   ========================================================================== */
.ch-mon-table td.is-rel {
    min-width: 6.5rem;
    white-space: nowrap;
}

.ch-mon-relwrap {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.ch-mon-relval {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--ch-text-main);
}

/* Google's pass mark, kept as one dot. The bar says which page is worst in
   this store; the dot says whether it passes at all, and losing that second
   fact to make the first one readable would be a trade in the wrong direction. */
.ch-mon-absdot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex: 0 0 auto;
}

.ch-mon-absdot.is-good { background-color: var(--ch-mon-good); }
.ch-mon-absdot.is-ni   { background-color: var(--ch-mon-warn); }
.ch-mon-absdot.is-poor { background-color: var(--ch-mon-stop); }

.ch-mon-reltrack {
    display: block;
    width: 100%;
    max-width: 6rem;
    height: 5px;
    border-radius: 3px;
    background-color: rgba(15, 23, 42, 0.09);
    overflow: hidden;
}

.ch-mon-relbar {
    display: block;
    height: 100%;
    border-radius: 3px;
    transition: width 0.3s;
}

/* Relative, so a store always gets the full range across its own pages. */
.ch-mon-relbar.is-good { background-color: var(--ch-mon-good); }
.ch-mon-relbar.is-ni   { background-color: var(--ch-mon-warn); }
.ch-mon-relbar.is-poor { background-color: var(--ch-mon-stop); }

.ch-mon-table td.is-rel.is-none {
    color: var(--ch-text-muted);
}

/* ==========================================================================
   SORTABLE HEADERS
   ========================================================================== */
.ch-mon-table th[data-sort] {
    cursor: pointer;
    user-select: none;
    padding-right: 1.2rem;
    position: relative;
    transition: color 0.15s;
}

.ch-mon-table th[data-sort]:hover,
.ch-mon-table th[data-sort]:focus,
.ch-mon-table th[data-sort][data-active="1"] {
    color: var(--ch-text-main);
}

/* The arrow is always present at low opacity, so the column reads as sortable
   before anybody clicks it. A control that only announces itself on hover does
   not exist on a touch screen. */
.ch-mon-table th[data-sort]::after {
    content: "";
    position: absolute;
    right: 0.35rem;
    top: 50%;
    margin-top: -2px;
    border-left: 3.5px solid transparent;
    border-right: 3.5px solid transparent;
    border-top: 4px solid currentColor;
    opacity: 0.25;
}

.ch-mon-table th[data-sort][data-active="1"]::after {
    opacity: 1;
}

.ch-mon-table th[data-sort][data-dir="asc"]::after {
    border-top: 0;
    border-bottom: 4px solid currentColor;
}

.ch-mon-table th[data-sort]:focus-visible {
    outline: 2px solid var(--ch-cta);
    outline-offset: -2px;
}

/* ==========================================================================
   PAGER
   ========================================================================== */
.ch-mon-pager {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 0.8rem;
    flex-wrap: wrap;
}

.ch-mon-page-count {
    font-family: var(--ch-mon-mono);
    font-size: 0.72rem;
    color: var(--ch-text-muted);
    order: -1;
    margin-right: auto;
}

.ch-mon-page-btn {
    padding: 0.35rem 0.75rem;
    border: 1px solid var(--ch-border);
    border-radius: 6px;
    background-color: #FFFFFF;
    color: var(--ch-text-main);
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}

/* Background and color on every state. GeneratePress paints button:hover and
   button:focus #3f4047, and with only a color here the hover read Emerald 500
   on that grey at 4.07:1 (Emerald 500 on white would be 2.5:1). */
.ch-mon-page-btn:hover:not(:disabled),
.ch-mon-page-btn:focus:not(:disabled),
.ch-mon-page-btn:active:not(:disabled) {
    border-color: var(--ch-cta);
    background-color: #FFFFFF;
    color: #047857;
}

.ch-mon-page-btn:disabled,
.ch-mon-page-btn:disabled:hover,
.ch-mon-page-btn:disabled:focus {
    background-color: #FFFFFF;
    color: var(--ch-text-main);
}

.ch-mon-page-btn:disabled {
    opacity: 0.4;
    cursor: default;
}

/* ==========================================================================
   PANEL HEADERS AND LIMIT BADGES
   The two tables were indistinguishable: same shape, same scores, same
   buttons, and nothing saying that one is measured from real visits and the
   other is an audit we run. The subtitle says which, the badges say what each
   one costs, and the cost was previously buried in a paragraph nobody reads.
   ========================================================================== */
.ch-mon-panel-title {
    min-width: 0;
    flex: 1 1 22rem;
}

.ch-mon-panel-sub {
    margin: 0.3rem 0 0;
    max-width: 62ch;
    font-size: 0.84rem;
    line-height: 1.55;
    color: var(--ch-text-muted);
}

/* THE COUNTERS.
   Two different kinds of number end up in this row and the first version drew
   them identically: how many pages exist, which is a fact about the store, and
   how many tests are left, which is a fact about the plan and the only one the
   reader can spend. Two grey pills at 0.64rem, both uppercase, both the same
   width - so the number that runs out was as quiet as the number that does not.

   Now the figure is set larger than its label and in the ink colour, the two
   kinds carry different tints, and amber still means nearly spent. */
.ch-mon-badges {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.ch-mon-badge {
    display: inline-flex;
    align-items: baseline;
    gap: 0.3rem;
    padding: 0.3rem 0.68rem;
    border-radius: 20px;
    background-color: var(--ch-mon-well);
    border: 1px solid var(--ch-border);
    font-family: var(--ch-mon-mono);
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--ch-text-muted);
}

.ch-mon-badge b {
    font-size: 0.82rem;
    font-weight: 800;
    letter-spacing: 0;
    color: var(--ch-text-main);
    font-variant-numeric: tabular-nums;
}

/* An inventory count. Slate, because nothing about it is spendable. */
.ch-mon-badge.is-count {
    background-color: var(--ch-mon-well);
    border-color: var(--ch-mon-line-hard);
}

/* An allowance. Emerald while there is room in it, because this is the number
   a reader checks before pressing a button that consumes one. */
.ch-mon-badge.is-quota {
    background-color: var(--ch-mon-good-bg);
    border-color: var(--ch-mon-good-line);
    color: var(--ch-mon-good);
}

.ch-mon-badge.is-quota b { color: var(--ch-mon-good); }

/* Nearly spent. The only state worth a second colour: a badge that is amber
   whatever the number carries no news, and a colour that never changes stops
   being read as a colour at all. */
.ch-mon-badge.is-low,
.ch-mon-badge.is-quota.is-low {
    color: var(--ch-mon-warn);
    background-color: var(--ch-mon-warn-bg);
    border-color: var(--ch-mon-warn-line);
}

.ch-mon-badge.is-low b { color: var(--ch-mon-warn); }

/* Spent. Struck through rather than red: nothing is broken, the reader simply
   has nothing left to press today. */
.ch-mon-badge.is-empty {
    color: var(--ch-text-muted);
    background-color: var(--ch-mon-well);
    border-color: var(--ch-border);
    opacity: 0.85;
}

.ch-mon-badge.is-empty b { color: var(--ch-text-muted); }

/* WHY THIS PAGE IS ON THE LIST.
   Printed under the path rather than in its own column: the reason is the
   longest string in the row and a column sized for it would squeeze the four
   numbers that are the point of the table. */
.ch-mon-why {
    display: block;
    margin-top: 0.2rem;
    font-size: 0.75rem;
    line-height: 1.4;
    color: var(--ch-text-muted);
    white-space: normal;
}

/* A result for a page that has since dropped off the watch list. Kept, because
   deleting a score somebody remembers seeing is worse than the untidiness, and
   dimmed so it cannot be mistaken for current. */
.ch-mon-table tr.is-stale {
    opacity: 0.62;
}

.ch-mon-table tr.is-stale td.is-name a {
    text-decoration-style: dashed;
}

/* ==========================================================================
   8. MONITORING: TABLES AND LOCKED PANELS
   ========================================================================== */

/* WIDE TABLES SCROLL IN A WRAPPER, NEVER BY BECOMING A BLOCK.
   `display: block` on a <table> was my bug, and it produced two symptoms that
   looked unrelated: the table stopped filling its container - ending around
   three fifths of the width - and the cell borders went ragged. Both because a
   table that is not `display: table` has no table layout algorithm any more, so
   column widths stop being negotiated across rows.
   The wrapper is what scrolls. The table stays a table. */
.ch-mon-scroll {
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    /* The frame belongs to the wrapper, not the table. A radius on the table
       itself does not clip the cells inside it, so the corners of the header
       row square off over the rounded edge. */
    background-color: var(--ch-mon-table);
    border: 1px solid var(--ch-mon-line);
    border-radius: 10px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
    scrollbar-width: thin;
}

/* THE SCROLL AFFORDANCE.
   The wide tables scroll sideways on anything narrower than a laptop and there
   was nothing on screen saying so, so a phone reader saw a table with its last
   two columns simply missing. The shadow is painted by the background itself:
   two fixed gradients that stay put and two scrolling gradients that cover
   them, so the shadow shows only on the side that still has content off
   screen. No script, no resize listener. */
.ch-mon-scroll {
    background-image:
        linear-gradient(to right, var(--ch-mon-table) 30%, rgba(255, 255, 255, 0)),
        linear-gradient(to left,  var(--ch-mon-table) 30%, rgba(255, 255, 255, 0)),
        linear-gradient(to right, rgba(15, 23, 42, 0.16), rgba(15, 23, 42, 0)),
        linear-gradient(to left,  rgba(15, 23, 42, 0.16), rgba(15, 23, 42, 0));
    background-position: 0 0, 100% 0, 0 0, 100% 0;
    background-repeat: no-repeat;
    background-size: 44px 100%, 44px 100%, 14px 100%, 14px 100%;
    background-attachment: local, local, scroll, scroll;
}

/* Per table, not one number for all three. A four column table squashes
   gracefully and should only scroll on a phone; the six column audit table
   needs half as much again before its columns stop being readable. One shared
   min-width would either scroll the small table on a tablet or squash the wide
   one on a phone. */
.ch-mon-scroll > .ch-mon-table {
    min-width: 34rem;
}

.ch-mon-scroll > [data-rum-table="topPages"] {
    min-width: 50rem;
}

/* Five columns now that the two lab durations have gone back inside the
   report they belong to. */
.ch-mon-scroll > [data-rum-table="checks"] {
    min-width: 40rem;
}

/* THE SCROLL PILL.
   The gradient shadow above is real but easy to miss on a phone screen full
   of other things to look at - it is a passive cue and a reader scanning for
   numbers is not looking for a shadow. This is the active version: a small
   "Scroll" pill that nudges sideways while there is unscrolled content and
   fades once the reader has found it. Same mechanism as the blog post
   tables (main-ui.js / style.css .ch-table-scroll-pill), restyled for this
   panel's dark tile surface instead of a white content card.
   dashboard-monitor.js wraps .ch-mon-scroll in this at runtime - it has no
   non-scrolling parent to pin an overlay against on its own. */
.ch-mon-scroll-outer {
    position: relative;
}

.ch-mon-scroll-pill {
    position: absolute;
    top: -12px;
    right: 14px;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    background-color: var(--ch-cta);
    color: #FFFFFF;
    font-family: var(--ch-mon-mono);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    box-shadow: 0 6px 14px -6px rgba(15, 23, 42, 0.45);
    opacity: 0;
    transform: translateY(4px);
    pointer-events: none;
    transition: opacity 0.2s, transform 0.2s;
}

.ch-mon-scroll-pill svg {
    width: 12px;
    height: 12px;
    stroke: currentColor;
    fill: none;
}

.ch-mon-scroll-outer.is-scrollable:not(.is-scrolled) .ch-mon-scroll-pill {
    opacity: 1;
    transform: translateY(0);
    animation: ch-mon-scroll-nudge 2s infinite cubic-bezier(0.4, 0, 0.2, 1);
}

@keyframes ch-mon-scroll-nudge {
    0%, 100% { transform: translateX(0); }
    50%      { transform: translateX(4px); }
}

/* Above the laptop width where the tables never scroll, the pill has nothing
   to announce - .is-scrollable is only ever added by JS once scrollWidth
   actually exceeds clientWidth, but this keeps a no-JS or slow-script load
   from ever showing a pill floating over a table that fits. */
.ch-mon-scroll-pill { display: none; }
.ch-mon-scroll-outer.is-scrollable .ch-mon-scroll-pill { display: inline-flex; }

/* Same reason: a <td> cannot be a flex container without leaving the table
   layout. The flex box goes inside it. */
.ch-mon-table td.is-test {
    white-space: nowrap;
}

.ch-mon-test-wrap {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

/* THE TWO ROW CONTROLS.
   One starts work, one opens a result, and they sit in adjacent columns of a
   dense table - so they have to be legible at a glance and quiet enough that
   twelve of them do not out-shout the numbers they belong to. Neither gets a
   border: at this size an outline is a second full-strength accent, which is
   what made the first attempt look like a form control. */
.ch-mon-testbtn {
    display: inline-flex;
    align-items: center;
    gap: 0.34rem;
    padding: 0.3rem 0.6rem;
    border: 0;
    border-radius: 6px;
    background-color: var(--ch-mon-good-bg);
    color: var(--ch-mon-good);
    font-family: var(--ch-mon-mono);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background-color 0.18s, color 0.18s, transform 0.18s;
}

.ch-mon-testbtn svg {
    display: block;
    width: 11px;
    height: 11px;
    stroke: currentColor;
    fill: none;
}

.ch-mon-testbtn:hover:not(:disabled) {
    background-color: var(--ch-cta);
    color: #FFFFFF;
    transform: translateY(-1px);
}

/* An explicit focus ring, because removing the border removed the only thing a
   keyboard user had. :focus-visible so a mouse click never paints it - that
   dark filled slab after clicking was the browser's default doing. */
.ch-mon-testbtn:focus { outline: none; }

.ch-mon-testbtn:focus-visible {
    outline: 2px solid var(--ch-cta);
    outline-offset: 2px;
}

.ch-mon-testbtn:disabled {
    background-color: var(--ch-mon-tile);
    color: var(--ch-text-muted);
    cursor: default;
    transform: none;
}

.ch-mon-testing {
    font-family: var(--ch-mon-mono);
    font-size: 0.66rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

/* THE RESULT LINK.
   --ch-mon-tile is #F8FAFC in light mode, which is a hair off the white card
   behind it: the button had a label and effectively no shape, and looked like
   an accident. It gets a real surface and a hairline instead. */
.ch-mon-report {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.32rem 0.62rem;
    border: 1px solid var(--ch-border);
    border-radius: 6px;
    background-color: #FFFFFF;
    color: var(--ch-text-muted) !important;
    font-family: var(--ch-mon-mono);
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-decoration: none !important;
    transition: background-color 0.18s, color 0.18s;
}

.ch-mon-report svg {
    display: block;
    width: 11px;
    height: 11px;
    stroke: currentColor;
    fill: none;
}

.ch-mon-report:hover {
    border-color: var(--ch-cta);
    background-color: var(--ch-cta);
    color: #FFFFFF !important;
}

.ch-mon-report:focus-visible {
    outline: 2px solid var(--ch-cta);
    outline-offset: 2px;
}

/* THE TABLE IS AN OBJECT, NOT A RUN OF TEXT WITH LINES IN IT.
   Four things carry that and all four resolve through the --ch-mon-table-*
   ladder so they flip: its own surface a step above the panel, a filled header
   row, alternating body rows, and a hover row. Before this the only structure
   in the whole table was a single hairline per row, and in dark that hairline
   was Slate 700 against Slate 800, which is why the tables looked like they
   had not rendered. */
.ch-mon-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background-color: var(--ch-mon-table);
    font-size: 0.86rem;
}

.ch-mon-table thead th {
    padding: 0.6rem;
    background-color: var(--ch-mon-thead);
    border-bottom: 1px solid var(--ch-mon-line-hard);
    text-align: left;
    font-family: var(--ch-mon-mono);
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.9px;
    text-transform: uppercase;
    /* Slate 600, not the muted Slate 500: 0.62rem capitals on the header
       tint measured 4.34:1 in Slate 500, under the 4.5:1 that small text
       needs. Dark mode keeps --ch-text-muted (below). */
    color: #475569;
    white-space: nowrap;
}

.ch-mon-table tbody tr {
    transition: background-color 0.15s ease;
}

/* Zebra first, hover second. A hover colour that loses to the zebra makes
   every other row unhoverable, which reads as the pointer being ignored. */
.ch-mon-table tbody tr:nth-child(even) {
    background-color: var(--ch-mon-zebra);
}

.ch-mon-table tbody tr:hover {
    background-color: var(--ch-mon-row-hover);
}

.ch-mon-table td {
    padding: 0.65rem 0.6rem;
    border-bottom: 1px solid var(--ch-mon-line);
    color: var(--ch-text-main);
    vertical-align: middle;
}

.ch-mon-table tbody tr:last-child td {
    border-bottom: 0;
}

.ch-mon-table td.is-name {
    max-width: 22rem;
}

/* THE WRAPPER, NOT THE PERCENTAGE.
   code used to carry max-width:100% directly inside the <td>, which is a
   percentage resolved against a table cell under table-layout:auto - a width
   the browser has not necessarily finished negotiating when it runs the
   truncation math. On the narrow end, where this column is squeezed
   hardest, that let a long path overrun the tag next to it and the views
   column after it instead of eliding. A flex row with min-width:0 on its
   text child is the reliable version of the same truncation: a <td> cannot
   itself be the flex container (see the note above .ch-mon-test-wrap), so
   the row goes in this inner wrapper instead. */
.ch-mon-name-wrap {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.3rem;
    min-width: 0;
}

.ch-mon-table td.is-name code {
    display: inline-block;
    flex: 1 1 auto;
    min-width: 4rem;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
    padding: 0.1em 0.35em;
    background-color: var(--ch-mon-well);
    border-radius: 4px;
    font-family: var(--ch-mon-mono);
    font-size: 0.8em;
}

.ch-mon-table td.is-name .ch-mon-tag {
    flex: 0 0 auto;
}

.ch-mon-table td.is-name a {
    font-family: var(--ch-mon-mono);
    font-size: 0.85em;
}

.ch-mon-table td.is-empty {
    color: var(--ch-text-muted);
    text-align: center;
    padding: 1.2rem 0.6rem;
}

.ch-mon-table td.is-good { color: var(--ch-mon-good); font-weight: 600; }
.ch-mon-table td.is-ni   { color: var(--ch-mon-warn); font-weight: 600; }
.ch-mon-table td.is-poor { color: var(--ch-mon-stop); font-weight: 600; }

.ch-mon-tag {
    display: inline-block;
    margin-left: 0.45rem;
    padding: 0.08rem 0.4rem;
    border-radius: 4px;
    background-color: var(--ch-mon-well);
    font-family: var(--ch-mon-mono);
    font-size: 0.62rem;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

.ch-mon-score {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.2rem;
    padding: 0.15rem 0.45rem;
    border-radius: 6px;
    font-family: var(--ch-mon-mono);
    font-weight: 800;
    font-size: 0.82rem;
}

.ch-mon-score.is-good { background-color: var(--ch-mon-good-bg); color: var(--ch-mon-good); }
.ch-mon-score.is-ni   { background-color: var(--ch-mon-warn-bg); color: var(--ch-mon-warn); }
.ch-mon-score.is-poor { background-color: var(--ch-mon-stop-bg); color: var(--ch-mon-stop); }

.ch-mon-panel.is-locked {
    background-color: var(--ch-mon-well);
    border-style: dashed;
}

.ch-mon-lock {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.4rem;
    flex-wrap: wrap;
}

.ch-mon-lock p {
    flex: 1 1 24rem;
    margin: 0;
    max-width: 66ch;
    font-size: 0.9rem;
    line-height: 1.62;
    color: var(--ch-text-muted);
}

.ch-mon-empty {
    padding: 1.4rem;
    text-align: center;
    color: var(--ch-text-muted);
}

.ch-mon-empty.is-big {
    padding: 2.4rem 1.4rem;
    background-color: var(--ch-mon-tile);
    border: 1px dashed var(--ch-border);
    border-radius: var(--ch-mon-radius);
}

.ch-mon-empty p {
    margin: 0.7rem auto 0;
    max-width: 58ch;
    font-size: 0.9rem;
    line-height: 1.62;
}

.ch-mon-empty svg {
    color: var(--ch-cta);
    opacity: 0.5;
}

/* LIMITS AND QUESTIONS.
   Two full-width disclosure rows that open into a two-column card grid. The
   old version was a plain summary line over one 76ch column of paragraphs,
   which left the right third of a 1100px panel empty. */
.ch-mon-foot {
    display: grid;
    gap: 0.75rem;
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--ch-border);
}

.ch-mon-ref {
    overflow: hidden;
    background-color: var(--ch-mon-tile);
    border: 1px solid var(--ch-border);
    border-radius: var(--ch-mon-radius);
    transition: border-color 0.2s, box-shadow 0.2s;
}

.ch-mon-ref:hover,
.ch-mon-ref[open] {
    border-color: var(--ch-mon-good-line);
}

.ch-mon-ref[open] {
    box-shadow: 0 14px 30px -24px rgba(15, 23, 42, 0.45);
}

.ch-mon-ref-head {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 1rem 1.15rem;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.ch-mon-ref-head::-webkit-details-marker {
    display: none;
}

.ch-mon-ref-head:focus-visible {
    outline: 2px solid var(--ch-cta);
    outline-offset: -2px;
    border-radius: var(--ch-mon-radius);
}

.ch-mon-ref-ico {
    flex: 0 0 38px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background-color: var(--ch-mon-good-bg);
    color: var(--ch-mon-good);
}

.ch-mon-ref-titles {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    flex: 1 1 auto;
    min-width: 0;
}

.ch-mon-ref-title {
    font-size: 0.98rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--ch-text-main);
}

.ch-mon-ref-sub {
    font-size: 0.82rem;
    line-height: 1.45;
    color: var(--ch-text-muted);
}

.ch-mon-ref-count {
    flex: 0 0 auto;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--ch-border);
    border-radius: 999px;
    background-color: var(--ch-bg-card);
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ch-text-muted);
}

/* A chevron drawn from two borders, so it needs no icon and rotates cleanly. */
.ch-mon-ref-chev {
    flex: 0 0 auto;
    width: 9px;
    height: 9px;
    margin: 0 0.3rem 0.25rem 0.1rem;
    border-right: 2px solid var(--ch-text-muted);
    border-bottom: 2px solid var(--ch-text-muted);
    transform: rotate(45deg);
    transition: transform 0.25s var(--ch-mon-ease), border-color 0.2s, margin 0.25s var(--ch-mon-ease);
}

.ch-mon-ref[open] .ch-mon-ref-chev {
    margin-bottom: -0.2rem;
    border-color: var(--ch-cta);
    transform: rotate(-135deg);
}

/* Two balanced columns rather than a grid. Answers run from 25 to 90 words,
   and a grid stretches every card to the tallest one in its row, which left
   blocks of empty card and a lone last card under an odd count (5 limits,
   9 answers). Columns stack each card at its own height and balance the two
   sides; reading order runs down the first column, then the second. */
.ch-mon-ref-grid {
    columns: 2;
    column-gap: 0.75rem;
    padding: 0.25rem 1.15rem 0.4rem;
}

.ch-mon-ref-card {
    display: flex;
    align-items: flex-start;
    gap: 0.8rem;
    margin: 0 0 0.75rem;
    padding: 1rem 1.1rem;
    break-inside: avoid;
    background-color: var(--ch-bg-card);
    border: 1px solid var(--ch-border);
    border-radius: 10px;
    animation: ch-fade-up 0.35s var(--ch-mon-ease) both;
    animation-delay: calc(var(--ch-i, 0) * 0.04s);
}

.ch-mon-ref-card-ico {
    flex: 0 0 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 8px;
    background-color: var(--ch-mon-good-bg);
    color: var(--ch-mon-good);
}

.ch-mon-ref-card h4 {
    margin: 0 0 0.35rem;
    font-size: 0.92rem;
    line-height: 1.35;
    color: var(--ch-text-main);
}

.ch-mon-ref-card p {
    margin: 0;
    font-size: 0.85rem;
    line-height: 1.6;
    color: var(--ch-text-muted);
}

.ch-mon-ref-card p strong {
    color: var(--ch-text-main);
    font-weight: 650;
}

.ch-mon-ref-card p code {
    padding: 0.05rem 0.35rem;
    border-radius: 4px;
    background-color: var(--ch-mon-well);
    font-family: 'JetBrains Mono', 'Roboto Mono', monospace;
    font-size: 0.8em;
    color: var(--ch-text-main);
}

/* Questions read as questions: an emerald rule on the left instead of an
   icon, because nine identical question-mark icons would be decoration. */
.ch-mon-ref-card.is-qa {
    border-left: 3px solid var(--ch-mon-good-line);
}

@media (max-width: 760px) {
    .ch-mon-ref-grid {
        columns: 1;
        padding: 0.1rem 0.55rem 0.1rem;
    }

    /* Icon above the text on a phone: beside it, the icon plus two layers of
       padding left about 150px for a paragraph of reference copy. */
    .ch-mon-ref-card {
        flex-direction: column;
        gap: 0.55rem;
        margin-bottom: 0.55rem;
        padding: 0.9rem 0.95rem;
    }

    .ch-mon-ref-head {
        gap: 0.7rem;
        padding: 0.9rem 0.9rem;
    }

    /* The count repeats what the open panel shows; on a phone the width it
       takes is better spent on the subtitle. */
    .ch-mon-ref-count {
        display: none;
    }
}

/* ==========================================================================
   9. RESPONSIVE
   ========================================================================== */

@media (max-width: 900px) {
    .ch-cmp-row {
        grid-template-columns: minmax(0, 1fr) 5.5rem 5.5rem;
        font-size: 0.84rem;
    }

    .ch-cmp-col {
        font-size: 0.78rem;
    }
}

@media (max-width: 768px) {
    .ch-dash-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }

    .ch-dash-tabs::-webkit-scrollbar {
        display: none;
    }

    .ch-dash-tab {
        padding: 0.55rem 0.85rem;
        font-size: 0.68rem;
    }

    .ch-mon-title {
        font-size: 1.2rem;
    }

    .ch-mon-head-side {
        width: 100%;
    }

    .ch-mon-code {
        flex-direction: column;
    }

    .ch-mon-code .ch-mon-copy {
        align-self: flex-start;
    }

    /* THE display:block RULE THAT USED TO LIVE HERE IS GONE.
       Section 8 opens with a comment saying a <table> must never be made a
       block to scroll it, because a table that is not display:table loses the
       table layout algorithm: column widths stop being negotiated across rows,
       the table stops filling its container and the cell borders go ragged.
       That comment was written after fixing it once - and this media query put
       it straight back for every screen under 768px, which is most of the
       traffic. The .ch-mon-scroll wrapper already scrolls, at every width. */
    .ch-mon-panel .ch-mon-table {
        white-space: nowrap;
    }

    /* The counters and the buttons stop being a right-aligned cluster and
       become a full-width row under the title. Right-aligning two badges and
       two buttons against a 320px column stacks them into a ragged staircase. */
    /* FLEX-BASIS FOLLOWS THE MAIN AXIS, AND THE MAIN AXIS JUST TURNED.
       .ch-mon-panel-title carries flex: 1 1 22rem so the title column has a
       sensible minimum width while the head is a row. Switching the head to a
       column makes that 22rem a minimum HEIGHT instead, and the panel rendered
       with 352px of dead space between the subtitle and the badges on every
       phone. Resetting the basis is the whole fix. */
    .ch-mon-panel-head { flex-direction: column; align-items: stretch; }
    .ch-mon-panel-title { flex: 0 0 auto; }

    .ch-mon-panel-side {
        align-items: stretch;
        width: 100%;
    }

    .ch-mon-badges,
    .ch-mon-panel-actions { justify-content: flex-start; }

    .ch-mon-panel-actions .ch-qrlib-btn { flex: 1 1 auto; justify-content: center; }

    .ch-mon-bar-name {
        flex-basis: 5.5rem;
    }

    /* The panel's own 1.25rem side padding was the same on a 1400px monitor
       and a 375px phone - fine on the first, a meaningful bite out of the
       second, where it is one of several paddings stacking up between the
       viewport edge and the actual numbers. */
    .ch-mon-panel {
        padding: 1rem;
    }

    /* A 15rem dropdown anchored right:0 assumes 240px of room to its left.
       That holds at desk width; on a phone where the gear sits close to the
       right edge already, 240px can run past the left edge of the screen
       before the menu itself does anything to stop it. */
    .ch-mon-menu {
        max-width: calc(100vw - 2.5rem);
    }

    .ch-mon-figures {
        gap: 0.9rem;
        font-size: 0.8rem;
    }
}

@media (max-width: 560px) {
    .ch-cmp-row {
        grid-template-columns: minmax(0, 1fr) 4.5rem 4.5rem;
        gap: 0.4rem;
        padding: 0.6rem 0.7rem;
    }

    .ch-dash-tab-label {
        display: none;
    }

    .ch-dash-tab.is-active .ch-dash-tab-label {
        display: inline;
    }

    .ch-mon-vital-value {
        font-size: 1.55rem;
    }

    .ch-mon-panel {
        padding: 0.85rem;
    }

    /* The scroll pill sits 14px in from the table's right edge; at this width
       that is close enough to the panel's own reduced padding that the two
       started to visually crowd each other. */
    .ch-mon-scroll-pill {
        top: -11px;
        right: 10px;
        padding: 0.26rem 0.6rem;
        font-size: 0.62rem;
    }

    .ch-mon-toolbar {
        gap: 0.6rem;
    }

    .ch-mon-updated {
        font-size: 0.68rem;
    }

    .ch-mon-bar-name {
        flex-basis: 4.5rem;
    }
}

/* ==========================================================================
   9.5 GRID RELIABILITY - min-width:0 AND ONE FLOOR, NOT MANY
   ==========================================================================
   THE JUMPING LAYOUT, DIAGNOSED.
   .ch-mon-vitals, .ch-mon-splits and .ch-mon-strip each auto-fit their cards
   against a minmax() floor, and a PREVIOUS version of this file changed that
   floor at 768px and again at 560px - a smaller floor to "help" narrower
   screens fit more comfortably. That is backwards: a SMALLER floor lets MORE
   columns fit in LESS space, so crossing 768px going narrower could jump a
   row from 2 cards to 3, and crossing 560px could snap it back to 1. Three
   different rules computing three different answers for the same pixel is
   exactly what produced the flicker between 2 and 3 columns around 755px -
   at that width the browser was straddling two of those rules and recomputing
   which one applied on every resize tick.

   auto-fit with a SINGLE, unchanging floor is provably monotonic - more
   container width never yields fewer columns, less never yields more - so
   the fix is subtraction: one floor per grid, defined once in section 7/7.5,
   never overridden. Nothing above this block touches these four selectors'
   grid-template-columns again.

   THE OVERFLOW AT THE SMALLEST WIDTHS, DIAGNOSED SEPARATELY.
   A grid item's default min-width is `auto`, which resolves to its content's
   min-content size - and .ch-mon-bar-name inside the Devices/Browsers/OS
   cards is a flex child pinned to a fixed basis with flex-shrink:0. That
   fixed width is a real floor that survives ellipsis and white-space:nowrap,
   and it propagates straight up through the flex row, the list, and the
   card to the GRID ITEM's own min-content contribution - which auto-fit's
   minmax() floor does not know about and cannot shrink. The grid track was
   sized as if the card could go narrower than the row inside it actually
   allows, so the row won the argument and the card spilled past its track.
   min-width:0 on the grid item is the standard release valve: it tells the
   item "you are allowed to be narrower than your content wants," at which
   point overflow-hidden and ellipsis further down the tree do their job.

   .ch-mon-strip-cell and .ch-mon-legend-col already carry min-width:0 -
   that is exactly why the status strip and the below-table legend never
   showed this symptom while the vitals cards and the Devices/Browsers/OS
   cards did. Only the two that were missing it need it here. */
.ch-mon-vitals > .ch-mon-vital,
.ch-mon-splits > .ch-mon-panel {
    min-width: 0;
}

/* Belt and braces: whatever the grid math above computes, this tab must
   never be the reason the page itself gains a horizontal scrollbar. A
   contained inner scroller (.ch-mon-scroll) is a deliberate, visible
   affordance; the whole dashboard shifting sideways because one card
   somewhere miscalculated is not. */
.ch-card-mon {
    overflow-x: hidden;
}

/* ==========================================================================
   10. DARK MODE
   ========================================================================== */

/* The connect-screen preview is the Slate 900 panel in dark only: its tokens
   reset to the values it carried in both themes until 2026-10-04. */
html[data-theme="dark"] .ch-mon-preview {
    --ch-mon-pv-bg:     #0F172A;
    --ch-mon-pv-glow:   rgba(16, 185, 129, 0.16);
    --ch-mon-pv-line:   #1E293B;
    --ch-mon-pv-shadow: 0 24px 48px -28px rgba(0, 0, 0, 0.75);
    --ch-mon-pv-ink:    #F8FAFC;
    --ch-mon-pv-text:   #94A3B8;
    /* Slate 400, not 500: #64748B was 3.1:1 on the #1E293B chart tile. */
    --ch-mon-pv-mute:   #94A3B8;
    --ch-mon-pv-tile:   #1E293B;
    --ch-mon-pv-tile-line: #273549;
    --ch-mon-pv-track:  #334155;
    --ch-mon-pv-row:    rgba(30, 41, 59, 0.6);
    --ch-mon-pv-row-hit: rgba(16, 185, 129, 0.22);
    --ch-mon-pv-code:   #CBD5E1;
    --ch-mon-pv-accent: #34D399;
    --ch-mon-pv-good:   #34D399;
    --ch-mon-pv-ni:     #FBBF24;
    --ch-mon-pv-mark:   #475569;
    --ch-mon-pv-chip:   #334155;
}

html[data-theme="dark"] .ch-portal-dash {
    /* Verdict colours lighten rather than swap: the same three meanings, at the
       luminance that holds contrast on Slate 800 instead of on white. */
    --ch-mon-good:      #34D399;
    --ch-mon-good-bg:   rgba(16, 185, 129, 0.14);
    --ch-mon-good-line: rgba(52, 211, 153, 0.42);

    --ch-mon-pro:       #93C5FD;
    --ch-mon-pro-hi:    #BFDBFE;

    --ch-mon-warn:      #FBBF24;
    --ch-mon-warn-bg:   rgba(245, 158, 11, 0.14);
    --ch-mon-warn-line: rgba(251, 191, 36, 0.38);

    --ch-mon-stop:      #F87171;
    --ch-mon-stop-bg:   rgba(239, 68, 68, 0.14);
    --ch-mon-stop-line: rgba(248, 113, 113, 0.34);

    --ch-mon-well:      #0F172A;
    --ch-mon-tile:      #172033;
    --ch-mon-ink-deep:  #020617;

    /* The table lifts ABOVE its panel here rather than sinking below it. Both
       readings are legible in light; in dark only one is, because there is no
       room left underneath #172033 before the rows stop being distinguishable
       from the page itself. */
    --ch-mon-table:     #1B2740;
    --ch-mon-thead:     #26334C;
    --ch-mon-zebra:     #1F2C47;
    --ch-mon-row-hover: #2A3A57;
    --ch-mon-line:      #33425F;
    --ch-mon-line-hard: #4A5B78;

    --ch-mon-s0: #34D399;
    --ch-mon-s1: #60A5FA;
    --ch-mon-s2: #A78BFA;
    --ch-mon-s3: #FBBF24;
    --ch-mon-s4: #94A3B8;
}

html[data-theme="dark"] .ch-dash-tab.is-active,
html[data-theme="dark"] .ch-dash-tab.is-active:hover {
    background-color: #1E293B !important;
    box-shadow: none;
}

html[data-theme="dark"] .ch-mon-line.is-visitors,
html[data-theme="dark"] .ch-mon-dot-u {
    stroke: #60A5FA;
    fill: #60A5FA;
}

html[data-theme="dark"] .ch-mon-dot-u {
    stroke: none;
}

html[data-theme="dark"] .ch-mon-band.is-good { fill: rgba(16, 185, 129, 0.13); }
html[data-theme="dark"] .ch-mon-band.is-ni   { fill: rgba(245, 158, 11, 0.11); }
html[data-theme="dark"] .ch-mon-band.is-poor { fill: rgba(239, 68, 68, 0.09); }

html[data-theme="dark"] .ch-mon-progress li.is-done span {
    color: #0F172A;
}

/* ==========================================================================
   11. REDUCED MOTION
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    .ch-dash-tabs,
    .ch-dash-panel.is-active,
    .ch-mon-stage {
        animation: none !important;
    }

    .ch-mon-pill[data-status="verified"] .ch-mon-dot,
    .ch-mon-radar::before,
    .ch-mon-radar::after {
        animation: none !important;
    }

    .ch-mon-bar span,
    .ch-mon-bar-fill {
        transition: none !important;
    }

    /* The connect screen. Every animation there starts from a hidden or
       zero-width frame and ends on the resting state, so removing them lands
       each element directly on its final frame: bars full, line drawn. */
    .ch-mon-connect,
    .ch-mon-flow-step,
    .ch-mon-preview,
    .ch-mon-preview-vital,
    .ch-mon-preview-bar span,
    .ch-mon-preview-area,
    .ch-mon-preview-line,
    .ch-mon-preview-end,
    .ch-mon-preview-dot,
    .ch-mon-preview-feed li,
    .ch-mon-tile,
    .ch-mon-ref-card {
        animation: none !important;
    }

    .ch-mon-ref-chev {
        transition: none !important;
    }

    .ch-mon-tile,
    .ch-mon-connect-btn .ch-btn-label svg {
        transition: none !important;
    }
}

html[data-theme="dark"] .ch-mon-testbtn:disabled,
html[data-theme="dark"] .ch-mon-report {
    background-color: rgba(148, 163, 184, 0.14);
}

html[data-theme="dark"] .ch-mon-report {
    color: var(--ch-text-main) !important;
}

html[data-theme="dark"] .ch-mon-testbtn:hover:not(:disabled),
html[data-theme="dark"] .ch-mon-report:hover {
    background-color: var(--ch-cta);
    color: #04241C !important;
}

html[data-theme="dark"] .ch-mon-report,
html[data-theme="dark"] .ch-mon-page-btn {
    background-color: rgba(148, 163, 184, 0.1);
    border-color: #334155;
}

html[data-theme="dark"] .ch-mon-report:hover,
html[data-theme="dark"] .ch-mon-page-btn:hover:not(:disabled) {
    border-color: var(--ch-cta);
}

html[data-theme="dark"] .ch-mon-page-btn:hover:not(:disabled),
html[data-theme="dark"] .ch-mon-page-btn:focus:not(:disabled),
html[data-theme="dark"] .ch-mon-page-btn:active:not(:disabled) {
    background-color: rgba(148, 163, 184, 0.16);
    color: #6EE7B7;
}

html[data-theme="dark"] .ch-mon-page-btn:disabled,
html[data-theme="dark"] .ch-mon-page-btn:disabled:hover,
html[data-theme="dark"] .ch-mon-page-btn:disabled:focus {
    background-color: rgba(148, 163, 184, 0.1);
    color: var(--ch-text-main);
}

html[data-theme="dark"] .ch-mon-table thead th {
    color: var(--ch-text-muted);
}

html[data-theme="dark"] .ch-mon-table th[data-sort]:hover,
html[data-theme="dark"] .ch-mon-table th[data-sort]:focus,
html[data-theme="dark"] .ch-mon-table th[data-sort][data-active="1"] {
    color: var(--ch-text-main);
}

html[data-theme="dark"] .ch-mon-report:hover {
    background-color: var(--ch-cta);
    color: #04241C !important;
}

/* The badges take their colours from --ch-mon-well, --ch-mon-good-* and
   --ch-mon-warn-* now, and all of those already flip at the top of this
   section. The two hardcoded overrides that used to live here won on
   specificity and repainted the quota badge grey in dark, so the one number a
   reader spends looked exactly like the one they cannot. Only the plain
   variant needs a nudge, because rgba slate over the dark well reads flatter
   than the well alone. */
html[data-theme="dark"] .ch-mon-badge {
    background-color: rgba(148, 163, 184, 0.1);
}

html[data-theme="dark"] .ch-mon-badge.is-quota {
    background-color: var(--ch-mon-good-bg);
    border-color: var(--ch-mon-good-line);
}

html[data-theme="dark"] .ch-mon-badge.is-low,
html[data-theme="dark"] .ch-mon-badge.is-quota.is-low {
    background-color: var(--ch-mon-warn-bg);
    border-color: var(--ch-mon-warn-line);
}

/* Table shadows are painted from the wrapper background, and the fade
   gradients above hardcode a light overlay because a gradient cannot read the
   surface it covers. In dark they have to be repainted or the two ends of a
   scrolling table carry a pale smear. */
html[data-theme="dark"] .ch-mon-scroll {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
    background-image:
        linear-gradient(to right, var(--ch-mon-table) 30%, rgba(27, 39, 64, 0)),
        linear-gradient(to left,  var(--ch-mon-table) 30%, rgba(27, 39, 64, 0)),
        linear-gradient(to right, rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0)),
        linear-gradient(to left,  rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0));
}

/* The path cell prints a <code> chip against --ch-mon-well. In dark that token
   is Slate 900 and the table surface is lighter than it, so the chip reads as
   a hole; the row-relative step is what keeps it a chip. */
html[data-theme="dark"] .ch-mon-table td.is-name code {
    background-color: rgba(148, 163, 184, 0.14);
}

html[data-theme="dark"] .ch-mon-reltrack {
    background-color: rgba(148, 163, 184, 0.18);
}

/* ==========================================================================
   12. CORE WEB VITALS REPORT (Adrian, 2026-10-10)
   The WordPress plugin's report on the dashboard's tokens: the verdict card
   with the totals and the daily limit, three score tiles with the band strip
   and the share bar, the server row, the visitors card and the reading
   notes. Light and dark both come from tokens. dashboard-monitor.js writes
   the values; every box has its final size before they arrive, so nothing
   moves when the numbers land.
   ========================================================================== */

.ch-portal-dash {
    --ch-mon-card:        #FFFFFF;
    --ch-mon-card-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 22px 44px -30px rgba(15, 23, 42, 0.35);
    --ch-mon-band-good:   rgba(16, 185, 129, 0.12);
    --ch-mon-band-ni:     rgba(245, 158, 11, 0.14);
    --ch-mon-band-poor:   rgba(239, 68, 68, 0.11);
    --ch-mon-mark:        #0F172A;
    --ch-mon-mark-ring:   #FFFFFF;
    --ch-mon-track:       #E2E8F0;
    --ch-mon-share-ni:    #D97706;
    --ch-mon-share-poor:  #DC2626;
    --ch-mon-kpi-views:   #ECFDF5;
    --ch-mon-kpi-visits:  #ECFEFF;
    --ch-mon-teal:        #0E7490;
    --ch-mon-teal-line:   rgba(14, 116, 144, 0.32);
    --ch-mon-pro-fill:    #2563EB;
    --ch-mon-pro-line:    rgba(37, 99, 235, 0.35);
    --ch-mon-pro-wash:    rgba(37, 99, 235, 0.08);
    --ch-mon-body:        #475569;
}

html[data-theme="dark"] .ch-portal-dash {
    --ch-mon-card:        #172033;
    --ch-mon-card-shadow: 0 1px 2px rgba(0, 0, 0, 0.30), 0 22px 44px -30px rgba(0, 0, 0, 0.75);
    --ch-mon-band-good:   rgba(16, 185, 129, 0.18);
    --ch-mon-band-ni:     rgba(245, 158, 11, 0.18);
    --ch-mon-band-poor:   rgba(239, 68, 68, 0.18);
    --ch-mon-mark:        #F8FAFC;
    --ch-mon-mark-ring:   #0F172A;
    --ch-mon-track:       #33425F;
    --ch-mon-share-ni:    #FBBF24;
    --ch-mon-share-poor:  #F87171;
    --ch-mon-kpi-views:   rgba(16, 185, 129, 0.14);
    --ch-mon-kpi-visits:  rgba(34, 211, 238, 0.12);
    --ch-mon-teal:        #67E8F9;
    --ch-mon-teal-line:   rgba(103, 232, 249, 0.32);
    --ch-mon-pro-fill:    #60A5FA;
    --ch-mon-pro-line:    rgba(147, 197, 253, 0.38);
    --ch-mon-pro-wash:    rgba(96, 165, 250, 0.10);
    --ch-mon-body:        #94A3B8;
}

/* Verdict tags: the same three meanings as the charts, plus Waiting. */
.ch-mon-grade {
    display: inline-flex;
    align-items: center;
    padding: 3px 9px;
    border: 1px solid var(--ch-border);
    border-radius: 999px;
    background-color: var(--ch-mon-well);
    color: var(--ch-mon-body);
    font-size: 12.5px;
    font-weight: 700;
    line-height: 1.3;
    white-space: nowrap;
}
.ch-mon-grade.is-good { border-color: var(--ch-mon-good-line); background-color: var(--ch-mon-good-bg); color: var(--ch-mon-good); }
.ch-mon-grade.is-warn { border-color: var(--ch-mon-warn-line); background-color: var(--ch-mon-warn-bg); color: var(--ch-mon-warn); }
.ch-mon-grade.is-poor { border-color: var(--ch-mon-stop-line); background-color: var(--ch-mon-stop-bg); color: var(--ch-mon-stop); }

/* ---- Verdict card ---- */
.ch-mon-verdict {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.25rem 2rem;
    margin: 0 0 1.25rem;
    padding: clamp(1.15rem, 2.5vw, 1.6rem);
    border: 1px solid var(--ch-border);
    border-radius: 16px;
    background-color: var(--ch-mon-card);
    box-shadow: var(--ch-mon-card-shadow);
}
.ch-mon-verdict[data-verdict="pass"] { background-image: radial-gradient(120% 90% at 0% 0%, var(--ch-mon-good-bg), transparent 62%); }
.ch-mon-verdict[data-verdict="fail"] { background-image: radial-gradient(120% 90% at 0% 0%, var(--ch-mon-warn-bg), transparent 62%); }
@media (min-width: 960px) {
    .ch-mon-verdict { grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr); align-items: center; }
}
.ch-mon-verdict-main { min-width: 0; }
.ch-mon-verdict-label {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    color: var(--ch-mon-good);
    font-family: var(--ch-mon-mono);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    line-height: 1.2;
    text-transform: uppercase;
}
.ch-mon-verdict-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background-color: currentColor; box-shadow: 0 0 0 3px var(--ch-mon-good-bg); }
.ch-mon-verdict-h {
    margin: 0.6rem 0 0.6rem;
    color: var(--ch-text-main);
    font-size: clamp(1.3rem, 1rem + 1.1vw, 1.85rem);
    font-weight: 800;
    letter-spacing: -0.03em;
    line-height: 1.15;
    text-wrap: balance;
    overflow-wrap: anywhere;
}
.ch-mon-verdict-lead { max-width: 40em; margin: 0; color: var(--ch-mon-body); font-size: 1rem; line-height: 1.6; }
.ch-mon-verdict-lead strong { color: var(--ch-text-main); font-weight: 700; }
.ch-mon-verdict-cta,
.ch-mon-verdict-cta:visited {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1.05rem;
    padding: 0.7rem 1.1rem;
    border-radius: 10px;
    background-color: #047857;
    color: #FFFFFF;
    font-size: 0.95rem;
    font-weight: 800;
    text-decoration: none;
    box-shadow: 0 10px 22px -12px rgba(4, 120, 87, 0.8);
}
.ch-mon-verdict-cta:hover,
.ch-mon-verdict-cta:focus,
.ch-mon-verdict-cta:active { background-color: #065F46; color: #FFFFFF; text-decoration: none; }
.ch-mon-verdict-cta:focus-visible { outline: 2px solid var(--ch-mon-good); outline-offset: 2px; }
.ch-mon-verdict-cta[hidden] { display: none; }

.ch-mon-kpis { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0.75rem; align-content: center; }
.ch-mon-kpi {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    min-width: 0;
    padding: 0.9rem 1rem;
    border: 1px solid var(--ch-mon-good-line);
    border-radius: 14px;
    background-color: var(--ch-mon-card);
    background-image: linear-gradient(160deg, var(--ch-mon-kpi-views) 0%, transparent 72%);
}
.ch-mon-kpi.is-visitors { border-color: var(--ch-mon-teal-line); background-image: linear-gradient(160deg, var(--ch-mon-kpi-visits) 0%, transparent 72%); }
.ch-mon-kpi-ico {
    flex: none;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    background-color: var(--ch-mon-card);
    color: var(--ch-mon-good);
    box-shadow: inset 0 0 0 1px var(--ch-mon-good-line);
}
.ch-mon-kpi.is-visitors .ch-mon-kpi-ico { color: var(--ch-mon-teal); box-shadow: inset 0 0 0 1px var(--ch-mon-teal-line); }
.ch-mon-kpi-tx { display: flex; flex-direction: column; min-width: 0; }
.ch-mon-kpi-tx b { color: var(--ch-text-main); font-size: 1.6rem; font-weight: 800; letter-spacing: -0.02em; line-height: 1.05; font-variant-numeric: tabular-nums; }
.ch-mon-kpi-tx > span { margin-top: 0.2rem; color: var(--ch-mon-body); font-size: 0.85rem; font-weight: 600; }

.ch-mon-quota {
    grid-column: 1 / -1;
    padding: 0.9rem 1rem 0.95rem;
    border: 1px solid var(--ch-mon-good-line);
    border-radius: 14px;
    background-color: var(--ch-mon-card);
}
.ch-mon-quota.is-pro { border-color: var(--ch-mon-pro-line); background-image: linear-gradient(160deg, var(--ch-mon-pro-wash) 0%, transparent 72%); }
.ch-mon-quota-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin: 0 0 0.5rem; }
.ch-mon-quota-label { color: var(--ch-mon-body); font-family: var(--ch-mon-mono); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
.ch-mon-quota-chip { flex: none; padding: 0.2rem 0.55rem; border: 1px solid var(--ch-mon-good-line); border-radius: 999px; background-color: var(--ch-mon-good-bg); color: var(--ch-mon-good); font-size: 0.72rem; font-weight: 800; line-height: 1.2; }
.ch-mon-quota.is-pro .ch-mon-quota-chip { border-color: #1D4ED8; background-color: #1D4ED8; background-image: linear-gradient(135deg, #2563EB 0%, #1D4ED8 60%, #1E40AF 100%); color: #FFFFFF; }
.ch-mon-quota-text { margin: 0 0 0.55rem; color: var(--ch-mon-body); font-size: 0.92rem; font-weight: 600; }
.ch-mon-quota-text b { color: var(--ch-text-main); font-weight: 800; font-variant-numeric: tabular-nums; }
.ch-mon-quota-track { position: relative; display: block; height: 8px; overflow: hidden; border-radius: 999px; background-color: var(--ch-mon-track); }
.ch-mon-quota-fill { position: absolute; inset: 0; border-radius: inherit; background-color: var(--ch-mon-good); transform: scaleX(var(--q, 0)); transform-origin: left center; transition: transform 0.8s var(--ch-mon-ease); }
.ch-mon-quota.is-pro .ch-mon-quota-fill { background-color: var(--ch-mon-pro-fill); }
.ch-mon-quota.is-near .ch-mon-quota-fill { background-color: var(--ch-mon-warn); }
.ch-mon-quota-foot { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.25rem 0.75rem; margin: 0.55rem 0 0; color: var(--ch-mon-body); font-size: 0.78rem; font-weight: 600; }
.ch-mon-quota-up,
.ch-mon-quota-up:visited { background-color: transparent; color: var(--ch-mon-pro-hi); font-weight: 700; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 2px; }
.ch-mon-quota-up:hover,
.ch-mon-quota-up:focus,
.ch-mon-quota-up:active { background-color: transparent; color: var(--ch-mon-pro); text-decoration: underline; }
.ch-mon-quota-up:focus-visible { outline: 2px solid var(--ch-mon-pro-hi); outline-offset: 2px; border-radius: 3px; }

/* ---- Score tiles ---- */
.ch-mon-vtiles { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; margin: 0 0 1rem; }
@media (min-width: 860px) {
    .ch-mon-vtiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.ch-mon-vtile {
    position: relative;
    min-width: 0;
    padding: 1.1rem 1.15rem 1.15rem;
    border: 1px solid var(--ch-border);
    border-radius: 14px;
    background-color: var(--ch-mon-card);
    box-shadow: var(--ch-mon-card-shadow);
}
.ch-mon-vtile[data-verdict="good"] { background-image: linear-gradient(180deg, var(--ch-mon-good-bg), transparent 60%); }
.ch-mon-vtile[data-verdict="ni"]   { border-color: var(--ch-mon-warn-line); background-image: linear-gradient(180deg, var(--ch-mon-warn-bg), transparent 60%); }
.ch-mon-vtile[data-verdict="poor"] { border-color: var(--ch-mon-stop-line); background-image: linear-gradient(180deg, var(--ch-mon-stop-bg), transparent 60%); }
.ch-mon-vtile-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.6rem; }
.ch-mon-vtile-head .ch-mon-grade { flex: none; }
.ch-mon-vtile-name { margin: 0; color: var(--ch-text-main); font-size: 0.95rem; font-weight: 700; line-height: 1.3; }
.ch-mon-vtile-name span { display: block; color: var(--ch-mon-body); font-size: 0.8rem; font-weight: 600; }
.ch-mon-vtile-v { display: block; margin: 0.5rem 0 0.7rem; color: var(--ch-text-main); font-size: 1.85rem; font-weight: 800; letter-spacing: -0.02em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.ch-mon-vband {
    position: relative;
    display: block;
    direction: ltr;
    height: 28px;
    border-radius: 8px;
    background-color: var(--ch-mon-card);
    background-image: linear-gradient(90deg, var(--ch-mon-band-good) 0 45%, var(--ch-mon-band-ni) 45% 72%, var(--ch-mon-band-poor) 72% 100%);
    box-shadow: inset 0 0 0 1px var(--ch-border);
}
.ch-mon-vband-pass { position: absolute; top: -3px; bottom: -3px; left: 45%; width: 0; border-left: 2px dashed var(--ch-mon-body); opacity: 0.55; }
.ch-mon-p75 { position: absolute; inset: -5px 0; z-index: 2; pointer-events: none; transform: translateX(var(--x, 0%)); transition: transform 0.9s var(--ch-mon-ease); }
.ch-mon-p75::before { content: ""; position: absolute; top: 0; bottom: 0; left: -1px; width: 2px; border-radius: 2px; background-color: var(--ch-mon-mark); box-shadow: 0 0 0 2px var(--ch-mon-mark-ring); }
.ch-mon-vtile[data-verdict=""] .ch-mon-p75 { visibility: hidden; }
.ch-mon-vband-foot { position: relative; display: block; height: 1.1rem; margin: 0.25rem 0 0.5rem; direction: ltr; }
.ch-mon-vband-tick { position: absolute; top: 0; left: 45%; transform: translateX(-50%); color: var(--ch-mon-body); font-family: var(--ch-mon-mono); font-size: 0.66rem; font-weight: 700; white-space: nowrap; }
.ch-mon-share { position: relative; display: block; height: 6px; margin: 0.35rem 0 0.3rem; border-radius: 999px; background-color: var(--ch-mon-track); }
.ch-mon-share-fill { position: absolute; inset: 0; display: block; border-radius: inherit; background: linear-gradient(90deg, var(--ch-mon-good) 0 var(--g, 0%), var(--ch-mon-share-ni) var(--g, 0%) var(--n, 0%), var(--ch-mon-share-poor) var(--n, 0%) 100%); }
.ch-mon-share.is-empty .ch-mon-share-fill { display: none; }
.ch-mon-share-tick { position: absolute; top: -4px; bottom: -4px; left: 75%; width: 2px; margin-left: -1px; border-radius: 1px; background-color: var(--ch-mon-mark); box-shadow: 0 0 0 1.5px var(--ch-mon-mark-ring); }
.ch-mon-vtile-share { margin: 0 0 0.35rem; color: var(--ch-text-main); font-size: 0.85rem; font-weight: 600; }
.ch-mon-vtile-rule { margin: 0 0 0.3rem; color: var(--ch-mon-body); font-size: 0.82rem; line-height: 1.55; }
.ch-mon-vtile-blurb { margin: 0.65rem 0 0; padding: 0.6rem 0.75rem; border-radius: 10px; background-color: var(--ch-mon-well); color: var(--ch-mon-body); font-size: 0.84rem; line-height: 1.5; }
.ch-mon-vtile-blurb b { color: var(--ch-text-main); font-weight: 700; }
.ch-mon-vtile[data-verdict="good"] .ch-mon-vtile-blurb { background-color: var(--ch-mon-good-bg); }
.ch-mon-vtile[data-verdict="ni"]   .ch-mon-vtile-blurb { background-color: var(--ch-mon-warn-bg); }
.ch-mon-vtile[data-verdict="poor"] .ch-mon-vtile-blurb { background-color: var(--ch-mon-stop-bg); }

/* ---- Server row ---- */
.ch-mon-server {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem 1rem;
    margin: 0 0 1.4rem;
    padding: 0.9rem 1.15rem;
    border: 1px solid var(--ch-border);
    border-radius: 14px;
    background-color: var(--ch-mon-card);
    box-shadow: var(--ch-mon-card-shadow);
}
.ch-mon-server-ico { display: grid; place-items: center; flex: none; width: 34px; height: 34px; border-radius: 9px; background-color: var(--ch-mon-good-bg); color: var(--ch-mon-good); }
.ch-mon-server-name { margin: 0; color: var(--ch-text-main); font-size: 0.95rem; font-weight: 700; line-height: 1.3; }
.ch-mon-server-v { color: var(--ch-text-main); font-size: 1.25rem; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.ch-mon-server-p { flex: 1 1 18rem; margin: 0; color: var(--ch-mon-body); font-size: 0.875rem; line-height: 1.5; }

/* ---- Who's visiting: one card, three columns ---- */
.ch-mon-who-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem 2rem; }
@media (min-width: 860px) {
    .ch-mon-who-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.ch-mon-who-col { min-width: 0; }
.ch-mon-who-h { margin: 0 0 0.75rem; color: var(--ch-mon-body); font-family: var(--ch-mon-mono); font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
.ch-mon-who-donut { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.25rem; }
.ch-mon-who-donut .ch-mon-chart.is-donut { flex: none; width: 128px; }
.ch-mon-who-donut .ch-mon-key-list { flex: 1 1 8rem; margin: 0; }

/* ---- Reading notes ---- */
.ch-mon-notes { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.55rem 1.75rem; margin: 0; padding: 0.35rem 1.15rem 1.15rem; list-style: none; }
@media (min-width: 760px) {
    .ch-mon-notes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.ch-mon-notes li { position: relative; margin: 0; padding-left: 1rem; color: var(--ch-mon-body); font-size: 0.875rem; line-height: 1.5; }
.ch-mon-notes li::before { content: ""; position: absolute; top: 0.6em; left: 0; width: 6px; height: 6px; border-radius: 50%; background-color: var(--ch-mon-good); }
.ch-mon-notes strong { color: var(--ch-text-main); font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
    .ch-mon-p75,
    .ch-mon-quota-fill { transition: none; }
}

@media (max-width: 480px) {
    .ch-mon-kpi { gap: 0.6rem; padding: 0.75rem 0.8rem; }
    .ch-mon-kpi-ico { width: 34px; height: 34px; border-radius: 10px; }
    .ch-mon-kpi-tx b { font-size: 1.35rem; }
    .ch-mon-kpi-tx > span { font-size: 0.8rem; }
}
