/* ==================== EXECUTIVE OVERVIEW (.xo-*) ====================
   Ten panels on one screen. The layout is a 3-up grid that collapses to 2 then
   1; two panels span two columns because a time series needs the width and a
   twelve-row table does not read in a third of a screen.

   THE GREY STATE IS A FIRST-CLASS DESIGN HERE, not an error style. Three panels
   cannot be computed from what this database holds, and a director needs to see
   the shape of the finished dashboard AND know precisely which parts are
   waiting on data. So a disabled panel keeps its number, its title and its
   position, dims its body, and states the missing input in a sentence. It never
   shows a zero, a dash or a plausible-looking figure. */

/* ---- the headline figures moved to the header ----
   Customers / penetration / revenue are rendered as .hkpi-chip into the shell's
   #headerPageKpis slot (see _paint in exec-overview.js). They were a full-width
   row of three tiles here, ~110px of the one screen this page is budgeted to
   fit, sitting under a header that had the room.

   THIS ELEMENT IS NOT DEAD, AND MUST NOT BE DELETED. It is the loading and
   failure surface: the skeleton, the "snapshot not built yet" warning and the
   load error all render here, where they have width for a sentence and where
   the router cannot wipe them mid-read. It carries no height while empty, so
   the normal case costs nothing — the same arrangement as #xoFoot below, and
   for the same reason: removing the element would remove the alarm with it.

   The tile styling (kpi-ico/-k/-v/-s) went with the tiles. Only the two
   classes the skeleton actually uses are kept; four orphaned rules matching
   nothing is how a stylesheet starts lying about what the page contains. */
.xo-kpis:empty { display: none; }
.xo-kpis { display: block; margin-bottom: .85rem; }
.xo-kpi {
    display: flex; align-items: center; gap: .8rem;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r); padding: .75rem 1rem; box-shadow: var(--lift-1);
    position: relative; overflow: hidden;
    animation: enter .5s var(--ease-out) backwards;
}
.xo-kpi-b { min-width: 0; }

/* ---- the grid ----
   TWELVE COLUMNS, THREE ROWS, TEN PANELS, ONE SCREEN. 4+4+4 / 4+4+4 / 3+3+3+3,
   which is the mock's arrangement exactly.

   HEIGHT IS FIXED, NOT MINIMUM. The first build used `min-height` and let each
   panel grow to fit its content: the revenue bars became half a metre tall, the
   page ran to three screens, and the whole point of an executive overview — all
   of it visible at once — was gone. A panel that cannot fit its detail in 232px
   does not get taller; it gets an expand button. */
.xo-grid {
    display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
    /* ALL THREE ROWS ARE THE SAME HEIGHT. Row 1 used to be 310px for one
       reason: panel 2's donut placed its labels AROUND the ring, so it needed a
       band of clear space above and below for the twelve- and six-o'clock
       slices. Panel 2 now puts those labels in a legend beside the ring, the
       reason is gone, and the grid is uniform. */
    /* Rows 2 and 3 grew by 20px each when the standing page footer came out —
       the same ~40px, moved from a caption nobody needed into the cards, which
       were clipping their last row against .xo-body's overflow:hidden. That
       bought the cards their room and put the page 40px over the fold, which is
       what the block below is for. */

    /* THE ROW HEIGHT IS THE ONE-SCREEN BUDGET, AS ARITHMETIC RATHER THAN A GUESS.
       A flat 272px totalled 843px of grid, and on a 1080p browser — ~920px of
       viewport once the OS and browser chrome are off it — the fourth row of the
       bottom cards sat below the fold. The whole argument for this page is that
       all of it is visible at once, so the rows are measured against the screen:

         header       62px  (--header-h is a 56px FLOOR; the three-line KPI
                             column is what the bar actually measures) + 1 border
         .main-content 40px  (1.25rem top + 1.25rem bottom — the standing
                             2.5rem bottom is cut to match the top for this
                             page only, in layout.css, which owns the shell)
         two gaps     27.2px (2 x .85rem)
         slack         6px
         ------------------
                     ~136px, leaving (100vh - 136px) / 3 per row

       KEEP THIS SUM AND THAT RULE IN STEP. They are two halves of one number:
       trim the padding without lowering the constant and the rows do not grow,
       they just leave the gap somewhere else; lower the constant without the
       padding rule and the page goes back under the fold.

       CLAMPED AT BOTH ENDS, and both ends matter. 272px is the cap because these
       panels are composed for that height and a 1440px monitor would otherwise
       stretch them to 439px of mostly white. 252px is the floor because below it
       the cards start clipping their last row against .xo-body's overflow:hidden
       — on a screen that short a scrollbar is the honest answer, not a crushed
       card. At ~920px of viewport this resolves to ~261px, and the compacted
       .panel-head below hands the difference straight back to the body: content
       height comes out ~5px ABOVE what the flat 272px row gave, because the head
       gave up more than the row did. */
    grid-template-rows: repeat(3, clamp(252px, calc((100vh - 136px) / 3), 272px));
    gap: .85rem; align-items: stretch;
}
.xo-card { grid-column: span 4; }
.xo-card.xo-q { grid-column: span 3; }     /* the four-across bottom row */
@media (max-width: 1499.98px) {
    .xo-card    { grid-column: span 6; }
    .xo-card.xo-q { grid-column: span 6; }
}
@media (max-width: 949.98px) {
    .xo-grid { grid-template-columns: 1fr; }   /* .xo-kpis is no longer a grid */
    .xo-card, .xo-card.xo-q { grid-column: span 1; }
}
.xo-card {
    display: flex; flex-direction: column;
    height: 100%;              /* the grid row sets it — see grid-template-rows */
    min-height: 0; overflow: hidden;
}
/* ---- the title row, compacted ----
   SCOPED TO THIS PAGE, NOT THE SHELL. .panel-head is base.css's and every other
   page uses it; a feature stylesheet loading after base.css and restating a
   shell rule is how portfolio.css silently killed a hover (see CLAUDE.md). This
   overrides padding here and nothing else, anywhere else.

   THE TALLEST THING IN THE ROW IS THE EXPAND BUTTON AT 26px, not the 22px panel
   number — check that before trimming further, or the padding comes off and the
   row does not move. .85rem top and bottom put 27px of air around it for a strip
   carrying a number, four words and a button; .5rem takes the row from 54px to
   43px, and three rows of that is 33px of the ~48px this page had to lose.
   Neither the chip nor the button shrinks: 22px is --brand-logo-min and the
   panel number reads as its sibling, and 26px is already a small click target. */
.xo-card .panel-head { padding: .5rem .9rem; }

.xo-body {
    /* trimmed with the head above — the row is ~17px shorter than it was, and
       these 5px are the rest of what keeps the content box where it was */
    padding: .55rem .85rem .65rem; flex: 1; min-height: 0;
    display: flex; flex-direction: column; overflow: hidden;
}
/* The compact panel shows the shape, the modal shows the detail — but that is
   done by the renderer's `full` flag, not by hiding markup. A show-more class
   was written for it and never used; deferring in JS means the compact card does
   not even build the strings it will not show.
   (Written without the leading dot on purpose — the dead-class detector matches
   selector text, so an example in a comment is read as a real selector.) */

/* ---- the expand control ---- */
.xo-exp {
    margin-left: auto; width: 26px; height: 26px; flex: 0 0 26px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 7px; border: 1px solid var(--border); background: var(--surface-2);
    color: var(--text-dim); font-size: .74rem; cursor: pointer;
    transition: color .14s ease, border-color .14s ease, background .14s ease, transform .14s var(--ease);
}
.xo-exp:hover {
    color: var(--gold-700); border-color: var(--gold-400);
    background: var(--gold-050); transform: scale(1.08);
}
.xo-card.is-off .xo-exp { display: none; }

/* ==================== EXPANDED ====================
   Same render function, `full` mode: the compact panel is not resized, it is
   re-rendered with the parts it had to leave out. 90vw because the detail is
   tables and long series, and the base 95vw rule would make a 12-row table
   wider than it is readable. */
#xoModal .modal-dialog { max-width: 90vw; }
#xoModal .modal-body { padding: 1.15rem 1.35rem 1.5rem; background: var(--surface); }
#xoModal .xo-body { padding: 0; overflow: visible; }
/* In the modal the charts get the room they were denied. */
#xoModal .xo-chart { max-height: 340px; }
#xoModal .xo-chart text { font-size: 13px; }
#xoModal .xo-donut-wrap { gap: 2rem; }

/* ==================== THE DISABLED STATE ====================
   Dimmed, not hidden, and never blank. `filter: grayscale` on the header rather
   than opacity on the whole card, so the panel number and title stay legible —
   the point is that a reader can see WHICH panel is waiting, not just that
   something is missing. */
.xo-card.is-off { background: var(--surface-2); }
.xo-card.is-off .panel-head { filter: grayscale(1); opacity: .75; }
.xo-card.is-off::before { background: linear-gradient(90deg, var(--text-dim), transparent); opacity: .4; }
/* SAFE CENTRE — the third option, and the one that was missing.
   Plain justify-content:center inside a FIXED-height card overflows BOTH ways
   when the content is taller than the box, which is how panels 1 and 6 once had
   a sentence cut off mid-word. The answer was flex-start, and it cost the other
   end: panel 1's block is ~100px in a ~200px body, so a quarter of the top row
   of this page was a column of white under four lines of grey text.
   `safe center` centres while the content fits and falls back to start-alignment
   the moment it would overflow — the exact fault above, ruled out by the keyword
   rather than by giving up the centring. Same pattern, same reason, as the
   sign-in card in login.css.
   PLAIN `center` STAYS ON THE LINE ABOVE. A browser that does not know the
   `safe` keyword discards the whole declaration, not just the keyword, and
   would inherit stretch — so the fallback has to be a separate declaration. */
.xo-off {
    flex: 1; min-height: 0; display: flex; flex-direction: column;
    justify-content: center;
    justify-content: safe center;
    gap: .45rem; text-align: left; overflow: hidden;
}
.xo-off-tag {
    display: inline-flex; align-items: center; gap: .35rem; align-self: flex-start;
    font-size: .62rem; font-weight: 750; letter-spacing: .09em; text-transform: uppercase;
    color: var(--text-soft); background: var(--surface-3);
    border: 1px dashed var(--border); border-radius: 999px; padding: .22rem .6rem;
}
.xo-off-what { font-size: .9rem; font-weight: 650; color: var(--text-soft); }
.xo-off-needs {
    font-size: .78rem; line-height: 1.55; color: var(--text-dim); max-width: 46ch;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
#xoModal .xo-off-needs { -webkit-line-clamp: unset; font-size: .82rem; }
/* A ghost of the chart that will go here, so the panel reads as a placeholder
   for something specific rather than as an empty box. */
.xo-ghost { display: flex; align-items: flex-end; gap: 4px; height: 34px; margin-bottom: .1rem; opacity: .3; }
.xo-ghost i {
    flex: 1; background: repeating-linear-gradient(45deg,
        var(--border) 0 3px, transparent 3px 6px);
    border-radius: 3px 3px 0 0;
}

/* ==================== CHARTS ==================== */
.xo-chart {
    width: 100%; display: block; overflow: visible;
    /* CAPPED. Without this the svg scales to the panel and a bar chart of eight
       years rendered as eight full-height slabs. */
    max-height: 108px;
}
.xo-chart text { fill: var(--text-dim); font: 600 11px var(--bs-body-font-family); }
.xo-chart .xo-val { fill: var(--text-soft); font-weight: 700; font-variant-numeric: tabular-nums; }
.xo-bar { transition: opacity .15s ease; cursor: default; }
.xo-bar:hover { opacity: .78; }

/* ---- donut ---- */
.xo-donut-wrap { display: flex; align-items: center; gap: .9rem; flex-wrap: nowrap; min-width: 0; }
/* A DONUT THAT IS THE WHOLE PANEL CENTRES ITSELF. Panel 7 is a ring and three
   legend rows and nothing else — ~115px of content that sat at the top of a
   ~195px body, leaving the emptiest card in the bottom row next to three that
   fill theirs. `margin-block: auto` on a column flex item splits the free space
   above and below it.
   :only-child IS THE WHOLE SCOPE, and it is doing real work: panel 2 draws the
   same wrap but follows it with the caption about the slices, so it has a
   sibling and is untouched. Centring there would have pushed that caption onto
   the bottom edge of the card. Structure picks the panel, so neither one needs
   an id in the stylesheet and nothing has to be passed down from the renderer. */
.xo-body > .xo-donut-wrap:only-child { margin-block: auto; }

/* ==================== REACH BARS (panel 2) ====================
   The .is-rich legend row, standing on its own instead of beside a ring. The
   rules for the row itself are the .xo-leg.is-rich block above and are unchanged
   — this is only what differs when it is the panel rather than a caption to one:
   it owns the height, so it centres in what the hero and the note leave, and the
   rows tighten slightly because there are three of them carrying a 24px mark.

   THE TRACK IS THE 100%, which is the whole reason this replaced a donut. A fill
   with no rail behind it has no scale — the same note as .xo-pen in panel 9 —
   and here the rail is specifically all 45,102 customers, so three bars against
   three identical rails compare directly. */
.xo-reach { flex: 1 1 auto; min-height: 0; justify-content: center; }
.xo-reach .xo-leg.is-rich { padding: .22rem .3rem; }
/* The dialog has room for the mark and the figures to grow. */
.xo-reach.is-full .xo-leg.is-rich { padding: .45rem .5rem; font-size: 1rem; }
.xo-reach.is-full .xo-leg.is-rich .bmark { height: 30px; width: 30px; flex: 0 0 30px; }
.xo-reach.is-full .xo-leg.is-rich .n { font-size: 1.15rem; }
.xo-reach.is-full .xo-leg.is-rich .bar { height: 7px; }
/* A heading that opens the dialog has nothing above it to be separated from. */
#xoModal .xo-x-h:first-child { margin-top: 0; }
.xo-donut { flex: 0 0 auto; position: relative; }
.xo-donut circle { transition: stroke-width .18s var(--ease), opacity .18s ease; cursor: default; }
.xo-donut circle:hover { stroke-width: 30; }
/* The centre figure sits OVER the svg rather than inside it — the svg is rotated
   so the first arc starts at twelve o'clock, and text within it would rotate too. */
.xo-donut-c {
    position: absolute; inset: 0; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: .1rem; pointer-events: none;
}
.xo-donut-mid { font-size: 1.2rem; font-weight: 780; letter-spacing: -.035em;
                color: var(--text); font-variant-numeric: tabular-nums; line-height: 1; }
.xo-donut-sub { font-size: .58rem; font-weight: 750; letter-spacing: .12em; color: var(--text-dim); }
/* THE LEGEND IS NOT OPTIONAL. Three of the four brand colours are blue-family
   and separate at ΔE 0.3–1.9 on the fills — colour alone cannot identify a
   segment here, so every one is direct-labelled with its name and value. */
.xo-legend { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .12rem; }
.xo-leg {
    display: grid; grid-template-columns: 10px minmax(0,1fr) auto auto; gap: .4rem;
    align-items: center; font-size: .82rem; padding: .16rem .28rem; border-radius: 5px;
    transition: background .14s ease;
}
.xo-leg:hover { background: var(--surface-3); }
.xo-leg.lit { background: var(--surface-3); }

/* the rich variant — logo, figures, and the row's share drawn beneath them */
.xo-legend.is-rich { gap: .3rem; }
.xo-leg.is-rich {
    grid-template-columns: 26px minmax(0,1fr) auto auto;
    row-gap: .28rem; padding: .3rem .35rem; font-size: .86rem;
}
.xo-leg.is-rich .lg { display: flex; align-items: center; }
.xo-leg.is-rich .bmark { height: 24px; width: 24px; flex: 0 0 24px; border-radius: 5px; }
.xo-leg.is-rich .nm { color: var(--text); font-weight: 650; }
.xo-leg.is-rich .n  { font-size: .95rem; }
.xo-leg.is-rich .bar {
    grid-column: 1 / -1; height: 5px; border-radius: 999px;
    background: var(--surface-3); overflow: hidden;
}
.xo-leg.is-rich .bar i {
    display: block; height: 100%; width: auto; border-radius: 999px; min-width: 3px;
    transform-origin: left; animation: growX .8s var(--ease-out) backwards;
}
.xo-leg i { width: 11px; height: 11px; border-radius: 3px; }
.xo-leg .nm { color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.xo-leg .n  { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text); }
.xo-leg .p  { font-size: .78rem; color: var(--text-dim); font-variant-numeric: tabular-nums; min-width: 3.2em; text-align: right; }

/* ---- headline figure inside a panel ---- */
.xo-hero { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; margin-bottom: .2rem; }
.xo-hero b {
    font-size: 1.55rem; font-weight: 780; letter-spacing: -.04em; line-height: 1;
    font-variant-numeric: tabular-nums;
}
.xo-hero span { font-size: .72rem; color: var(--text-soft); }
/* The note is detail, so it only appears in the expanded view — in a 232px
   panel it was eating the space the chart needed. */
.xo-note { font-size: .8rem; line-height: 1.55; color: var(--text-dim); margin-top: auto; padding-top: .6rem; }
#xoModal .xo-note { padding-top: 1rem; border-top: 1px solid var(--border-soft); margin-top: 1rem; }

/* The progress rail went with panel 3's rewrite — a rail showing one percentage
   said less than the curve that replaced it. .xo-pen (the consolidator table)
   and .xo-dist-r carry the remaining bar treatments. */

/* ---- ranked lists & tables ---- */
/* The generic xo-rows/xo-row/xo-num/xo-sub list went with panel 8's rewrite —
   the last panel using it now renders segments, and every remaining ranked list
   on the page is a real table (xo-tbl). Left in place they would have been four
   rules styling nothing, which is how a stylesheet starts lying about the page. */
.xo-rank {
    width: 22px; height: 22px; flex: 0 0 22px; border-radius: 7px; display: grid;
    place-items: center; font-size: .68rem; font-weight: 800;
    background: var(--surface-3); color: var(--text-soft);
}
/* the podium: first three of any ranked list wear gold */
.xo-opp-r:nth-of-type(-n+3) > .xo-rank,
.xo-tbl-r:nth-of-type(-n+3) > .xo-rank {
    background: var(--gold-100); color: var(--gold-700); border: 1px solid var(--gold-300);
}
.xo-nm { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }

/* The consolidator row was a rank + name + split bar + count list. It is a real
   table now (.xo-tbl, further down) because locations alone did not answer the
   question the panel is for — penetration does. */
.xo-both {
    font-size: .64rem; font-weight: 750; letter-spacing: .06em; text-transform: uppercase;
    color: var(--gold-800); background: var(--gold-100); border: 1px solid var(--gold-300);
    border-radius: 999px; padding: .08rem .4rem; margin-left: .4rem; white-space: nowrap;
}
/* ==================== THE SANKEY (panel 10) ====================
   The old three-bar list lives on in the history and nowhere else. Notes worth
   keeping with the CSS:

   - preserveAspectRatio="none" is deliberate. The ribbons are a proportion
     read against each other, not a shape with a correct aspect — letting the
     SVG stretch to the card keeps the band as wide as the card allows at every
     breakpoint, and nothing in it is a circle that would give the distortion
     away.
   - THE LABELS ARE HTML OVER THE SVG, not <text>. They carry the brand mark,
     and the dark-mode rule that inverts brand artwork is a CSS filter on
     .bmark — an <image> inside <svg> never receives it, so every logo would
     have stayed black on a black card.
   - The hatch overlay is a SECOND path on the same geometry rather than a
     pattern blended into the fill, so the gradient underneath survives. */
.xo-sk { position: relative; flex: 1; min-height: 0; display: flex; }
.xo-sk-svg { width: 100%; height: 100%; display: block; overflow: visible; }
.xo-sk.is-full { height: 400px; flex: 0 0 auto; margin-bottom: 1.2rem; }

.xo-sk-rib {
    opacity: .48; cursor: default;
    transition: opacity .16s ease, filter .16s ease;
}
.xo-sk-rib:hover, .xo-sk-rib.lit { opacity: .92; filter: saturate(1.15); }
.xo-sk-hatch { opacity: .5; pointer-events: none; }
.xo-sk-node { cursor: default; }
.xo-sk-node.is-off { fill: var(--border); }

.xo-sk-lab {
    position: absolute; transform: translateY(-50%);
    display: flex; align-items: center; gap: .4rem; min-width: 0;
    pointer-events: none;
}
.xo-sk-lab.at-r { flex-direction: row-reverse; }
/* the company's own book size, under its logo — identity and a number, no word */
.xo-sk-lab .bk {
    font-size: .76rem; font-weight: 750; color: var(--text);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
.xo-sk-lab .bmark { height: 26px; width: 26px; flex: 0 0 26px; border-radius: 5px; }
.xo-sk-lab .bmark.is-wide { width: auto; max-width: 78px; }
.xo-sk.is-full .xo-sk-lab .bk { font-size: .95rem; }
.xo-sk.is-full .xo-sk-lab .bmark { height: 34px; width: 34px; flex: 0 0 34px; }
.xo-sk.is-full .xo-sk-lab .bmark.is-wide { width: auto; max-width: 104px; }
/* a company with no data is present and plainly empty, never quietly absent */
.xo-sk-lab.is-off { opacity: .5; }
.xo-sk-lab.is-off .bk { font-weight: 600; font-style: italic; color: var(--text-dim); }

/* THE FLOW. The ribbon is static because its geometry IS the measurement; the
   dashed centreline carries no quantity, so it is the one thing here that may
   move. Slow, low-contrast, and off entirely under prefers-reduced-motion. */
.xo-sk-flow {
    opacity: .34; stroke-linecap: round; stroke-dasharray: 3 26;
    animation: skflow 5.5s linear infinite; pointer-events: none;
}
@keyframes skflow { from { stroke-dashoffset: 290; } to { stroke-dashoffset: 0; } }

.xo-sk-mid {
    font: 750 11px var(--bs-body-font-family); fill: var(--text);
    paint-order: stroke; stroke: var(--surface); stroke-width: 3.5px;
    stroke-linejoin: round; pointer-events: none;
}
.xo-sk.is-full .xo-sk-mid { font-size: 15px; stroke-width: 4.5px; }

/* the pair table */
.xo-tbl-f .xo-tbl-h, .xo-tbl-f .xo-tbl-r,
#xoModal .xo-tbl-f .xo-tbl-h, #xoModal .xo-tbl-f .xo-tbl-r {
    grid-template-columns: 30px minmax(0,1fr) 110px 120px 130px 190px;
}
.xo-fpair { display: flex; align-items: center; gap: .4rem; }
.xo-fpair .bmark { height: 24px; width: 24px; flex: 0 0 24px; border-radius: 5px; }
.xo-fpair > i { color: var(--text-dim); font-size: .8rem; flex: 0 0 auto; }
.xo-join {
    font-size: .68rem; font-weight: 700; letter-spacing: .03em; white-space: nowrap;
    padding: .12rem .5rem; border-radius: 999px;
    background: var(--ok-weak); color: var(--ok-ink); border: 1px solid var(--ok-line);
}
.xo-join.is-src {
    background: var(--surface-3); color: var(--text-soft); border-color: var(--border);
}

/* Shown ONLY when the snapshot is overdue — see the comment in render(). It is
   an alarm, not a caption, so it is styled as one and is display:none the rest
   of the time. [hidden] needs the explicit rule: `display:flex` beats the UA
   stylesheet's [hidden]{display:none} and the strip would show up empty. */
.xo-foot {
    display: flex; align-items: center; gap: .5rem;
    margin: .9rem 0 0; padding: .6rem .9rem; border-radius: 10px;
    font-size: .84rem; line-height: 1.55; color: var(--text-soft);
    background: var(--warn-weak); border: 1px solid var(--warn-line);
    border-left: 4px solid var(--warn);
}
.xo-foot[hidden] { display: none; }
.xo-foot i { color: var(--warn); flex: 0 0 auto; }
.xo-foot b { color: var(--text); font-weight: 700; }

/* ---- hover readout, shared by every chart ---- */
.xo-tip {
    position: fixed; z-index: 2500; pointer-events: none;
    padding: .5rem .65rem; border-radius: 9px;
    background: var(--shell-800); color: var(--shell-ink);
    border: 1px solid var(--shell-line); box-shadow: var(--lift-3);
    font-size: .74rem; line-height: 1.45; white-space: nowrap;
    opacity: 0; transition: opacity .12s ease;
}
.xo-tip.on { opacity: 1; }
.xo-tip b { color: var(--gold-300); font-weight: 700; }
.xo-tip em { font-style: normal; color: var(--shell-dim); }

@media (prefers-reduced-motion: reduce) {
    /* Every animated element on this page. Keep this list current — a selector
       left here after its element is gone is dead, and an element added without
       one keeps moving for a reader who asked it not to. The old pair-bar went
       with panel 10's Sankey; xo-sk-rib replaced it. */
    .xo-kpi, .xo-pen i, .xo-leg.is-rich .bar i, .xo-dist-r .b i, .xo-chart rect animate {
        animation: none !important; opacity: 1 !important; transform: none !important;
    }
    .xo-sk-rib { transition: none !important; }
    .xo-sk-flow { animation: none !important; opacity: .22 !important; }
    /* .xo-kpi:hover went with the KPI tiles — those three figures are header
       chips now, and .xo-kpi is only the loading skeleton, which does not lift.
       Keeping the selector would have been exactly the dead line this comment
       warns about. */
}

/* ==================== TWO-COLUMN PANEL BODIES ====================
   A chart next to the numbers that explain it. The mock does this on panels 4
   and 5 and it is most of why they read as dense rather than sparse. */
.xo-2col { display: flex; gap: .8rem; align-items: center; min-width: 0; flex: 1; }
.xo-2col-a { flex: 1 1 auto; min-width: 0; }
/* Wide enough for 'Messenger' and a percentage without an ellipsis — the
   legend was rendering 'Messe... 50.8%', which is worse than no legend. */
.xo-2col-b { flex: 0 0 142px; display: flex; flex-direction: column; gap: .1rem; }
.xo-2col-b .xo-leg { grid-template-columns: 10px minmax(0,1fr) auto; }
.xo-2col-fig { flex: 0 0 auto; display: flex; flex-direction: column; gap: .25rem; }
/* (the old figure sub-caption merged into .xo-fig-note) */
.xo-tot {
    display: flex; flex-direction: column; padding: .3rem .45rem .35rem;
    border-radius: 8px; background: var(--surface-3); margin-bottom: .3rem;
}
.xo-tot span { font-size: .6rem; font-weight: 750; letter-spacing: .1em; color: var(--text-dim); }
.xo-tot b { font-size: 1.15rem; font-weight: 780; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.xo-tot em { font-size: .66rem; font-style: normal; color: var(--text-dim); }
.xo-leg-off .nm, .xo-leg-off .p { color: var(--text-dim); font-style: italic; }

/* ==================== THE CONSOLIDATOR TABLE ====================
   Locations says how big a group is. Penetration says how much of it we have
   actually reached, and the gap between the two is the sales conversation —
   which is why this is a table and not a bar chart. */
.xo-tbl { display: flex; flex-direction: column; min-width: 0; }
.xo-tbl-h, .xo-tbl-r {
    display: grid; grid-template-columns: 20px minmax(0,1fr) 46px 46px 66px;
    gap: .4rem; align-items: center;
}
.xo-tbl-h {
    font-size: .62rem; font-weight: 750; letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-dim); padding: 0 .2rem .32rem; border-bottom: 1px solid var(--border);
}
.xo-tbl-h .r, .xo-tbl-r .r { text-align: right; }
.xo-tbl-r {
    padding: .34rem .2rem; border-top: 1px solid var(--border-soft); font-size: .82rem;
    transition: background .14s ease, box-shadow .14s ease;
}
.xo-tbl-r:hover { background: var(--surface-2); box-shadow: inset 3px 0 0 var(--gold-400); }
.xo-tbl-r .n { font-weight: 700; font-variant-numeric: tabular-nums; }
.xo-tbl-r .n.dim { color: var(--text-soft); font-weight: 600; }
/* THE ID RULE MUST NOT SET THE COLUMN COUNT. It did, at specificity (1,1,0),
   which beat .xo-tbl-5's (0,2,0) — so a six-column table was laid out on five
   columns in the dialog and every row's last cell wrapped to a second line.
   Each table variant owns its own grid; this rule only sizes the type. */
#xoModal .xo-tbl-h, #xoModal .xo-tbl-r { font-size: .92rem; }
#xoModal .xo-tbl-h { font-size: .68rem; }
#xoModal .xo-tbl-r { padding: .55rem .3rem; }
/* Penetration reads as a bar AND a number: the bar is scannable down a column,
   the number is the one you quote in a meeting. */
/* A FILL WITHOUT A RAIL HAS NO SCALE. 2.2% drew a one-pixel dot and 33.2% a
   short dash, with nothing behind either to read them against — so the column
   looked like flecks rather than a comparison. The track IS the 100%. */
.xo-pen { display: inline-flex; align-items: center; gap: .4rem; justify-content: flex-end; width: 100%; }
.xo-pen-t {
    display: block; height: 6px; border-radius: 999px; background: var(--surface-3);
    border: 1px solid var(--border-soft); flex: 0 0 auto; width: 46px; overflow: hidden;
}
.xo-pen i {
    display: block; height: 100%; border-radius: 999px; background: var(--gold-500);
    min-width: 2px; transform-origin: left; animation: growX .8s var(--ease-out) backwards;
}
.xo-pen b { font-size: .78rem; font-weight: 750; font-variant-numeric: tabular-nums; color: var(--text); }
#xoModal .xo-pen-t { width: 76px; height: 8px; }
#xoModal .xo-pen b { font-size: .84rem; }

/* ---- the way through to the full list ---- */
.xo-all {
    margin-top: auto; align-self: stretch; padding: .3rem .5rem; border-radius: 7px;
    background: var(--surface-2); border: 1px solid var(--border); color: var(--gold-700);
    font-size: .76rem; font-weight: 700; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center; gap: .15rem;
    transition: background .14s ease, border-color .14s ease, color .14s ease;
}
.xo-all:hover { background: var(--gold-050); border-color: var(--gold-400); color: var(--gold-800); }

/* ==================== RADIAL DONUT (panel 2) ====================
   Labels around the ring, at each slice's own angle. Not decoration: with the
   label AT the slice the eye never has to match a colour swatch to a name,
   which matters because three of the four brand colours are blue-family and
   separate at ΔE 0.3–1.9. Position carries identity; colour reinforces it. */
/* No aspect-ratio. It forced the wrapper to 318px tall inside a 180px panel and
   overflow:hidden ate every label but the top one. The labels are placed in px
   from the centre now, so the box can be any shape. */
.xo-radial {
    position: relative; flex: 1 1 auto; min-width: 0; height: 100%;
    display: grid; place-items: center;
}
.xo-radial-in { position: relative; flex: 0 0 auto; }
.xo-radial circle { transition: stroke-width .18s var(--ease), filter .18s ease; }
.xo-radial circle.lit { stroke-width: 25; filter: brightness(1.06); }
.xo-radial.is-full circle.lit { stroke-width: 38; }

/* One label per slice, absolutely placed at its computed angle. `at-l`/`at-r`
   flip the alignment so a label on the left of the ring reads inward. */
.xo-rl {
    position: absolute; transform: translate(-50%, -50%);
    display: flex; flex-direction: column; line-height: 1.12;
    pointer-events: auto; cursor: default; white-space: nowrap;
    padding: .1rem .2rem; border-radius: 5px;
    transition: background .15s ease, transform .15s var(--ease);
}
.xo-rl.lit { background: var(--surface-3); }
.xo-rl.at-l { align-items: flex-end;   transform: translate(-100%, -50%); }
.xo-rl.at-r { align-items: flex-start; transform: translate(0, -50%); }
.xo-rl.at-c { align-items: center; }
/* logo + name on one line above the figure. --brand-logo-min is 22px and is a
   min-height, so it WINS against any smaller height set here — sizing the mark
   below the floor would not shrink it, it would just make the declaration a
   lie. 22px is the floor and the compact size both. */
.rl-h { display: flex; align-items: center; gap: .3rem; }
.rl-h .bmark { height: 22px; width: 22px; flex: 0 0 22px; border-radius: 4px; }
.xo-radial.is-full .rl-h .bmark { height: 28px; width: 28px; flex: 0 0 28px; }
.xo-rl.at-l .rl-h { flex-direction: row-reverse; }
.rl-n { font-size: .72rem; font-weight: 750; letter-spacing: .01em; }
.rl-v { font-size: 1.08rem; font-weight: 800; letter-spacing: -.03em;
        color: var(--text); font-variant-numeric: tabular-nums; }
.rl-p { font-size: .7rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.xo-radial.is-full .rl-n { font-size: .92rem; }
.xo-radial.is-full .rl-v { font-size: 1.6rem; }
.xo-radial.is-full .rl-p { font-size: .86rem; }
.xo-radial .xo-donut-sub { line-height: 1.25; text-align: center; }

/* The compact card is the ring alone — a figure column was duplicating the total
   already in the middle of it and stealing the room the side labels need. */
.xo-fig-note { font-size: .68rem; line-height: 1.4; color: var(--text-dim); margin-top: .3rem; }
.xo-fig-note.xo-c { text-align: center; padding: 0 .5rem; }

/* ==================== EXPANDED PANEL 2 ====================
   The dialog is where the panel earns 90vw: the same ring, then the four counts
   that reconcile it, the reason the slices exceed 100%, and the three
   breakdowns behind the single figure on the card. */
.xo-x { display: grid; grid-template-columns: minmax(320px, 42%) 1fr; gap: 2rem; align-items: center; }
@media (max-width: 1100px) { .xo-x { grid-template-columns: 1fr; } }
.xo-x-ring { min-width: 0; }
.xo-x-side { min-width: 0; }
.xo-x-h {
    font-size: .76rem; font-weight: 780; letter-spacing: .09em; text-transform: uppercase;
    color: var(--text-dim); margin: 1.5rem 0 .55rem;
    padding-bottom: .35rem; border-bottom: 1px solid var(--border);
}
.xo-x-side .xo-x-h:first-of-type { margin-top: 1.1rem; }
.xo-x-p { font-size: .92rem; line-height: 1.65; color: var(--text-soft); margin: 0; max-width: 62ch; }
.xo-x-p b { color: var(--text); font-weight: 700; }

.xo-mini { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: .55rem; }
/* four across in the dialog rather than 2x2 — same content, ~90px less height */
#xoModal .xo-mini { grid-template-columns: repeat(auto-fit, minmax(230px,1fr)); }
.xo-mini-c {
    display: flex; flex-direction: column; gap: .1rem;
    padding: .6rem .75rem; border-radius: 10px;
    background: var(--surface-2); border: 1px solid var(--border-soft);
    border-left: 3px solid var(--gold-400);
}
.xo-mini-c .k { font-size: .64rem; font-weight: 750; letter-spacing: .07em;
                text-transform: uppercase; color: var(--text-dim); }
.xo-mini-c b { font-size: 1.3rem; font-weight: 780; letter-spacing: -.03em;
               font-variant-numeric: tabular-nums; }
.xo-mini-c em { font-size: .74rem; font-style: normal; color: var(--text-soft); }

/* The expanded table shows the REAL LOGO, not a colour dot. Three of the four
   brand colours are blue-family, so a swatch was the least useful thing that
   could sit there — and the artwork is the identity everywhere else in the app. */
.xo-nm-logo { display: flex; align-items: center; gap: .55rem; min-width: 0; }
.xo-nm-logo .bchip { flex: 0 0 auto; }
.xo-nm-logo > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.xo-tbl-4 .xo-tbl-h, .xo-tbl-4 .xo-tbl-r {
    grid-template-columns: 26px minmax(0,1fr) 110px 90px 130px;
}
#xoModal .xo-tbl-4 .xo-tbl-h, #xoModal .xo-tbl-4 .xo-tbl-r {
    grid-template-columns: 30px minmax(0,1fr) 130px 110px 150px;
}
.xo-tbl-r.is-empty { opacity: .55; }

/* distribution rows, used twice in the expanded view */
.xo-dist { display: flex; flex-direction: column; gap: .3rem; }
.xo-dist-r {
    display: grid; grid-template-columns: minmax(150px, 1.1fr) 2fr 74px 62px;
    gap: .8rem; align-items: center; font-size: .9rem;
}
.xo-dist-r .l { color: var(--text-soft); display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; }
.xo-dist-r .b { height: 9px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.xo-dist-r .b i {
    display: block; height: 100%; border-radius: 999px; background: var(--gold-500);
    transform-origin: left; animation: growX .8s var(--ease-out) backwards;
}
.xo-dist-r .n { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }
.xo-dist-r .p { text-align: right; font-size: .8rem; color: var(--text-dim); font-variant-numeric: tabular-nums; }

/* ==================== LINE CHART (panel 3) ====================
   Points carry their value directly, not only on hover — a screenshot of this
   panel has to still say what it shows, and a chart that is colour-and-hover
   alone fails the accessibility pass. */
.xo-line { max-height: 150px; }
.xo-pt {
    fill: var(--surface); stroke: var(--gold-500); stroke-width: 2.4;
    transition: r .16s var(--ease), fill .16s ease;
    cursor: default;
}
.xo-pt:hover { fill: var(--gold-500); r: 6; }
.xo-chart-t {
    font-size: .7rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
    color: var(--text-dim); margin-bottom: .1rem; text-align: center;
}
/* the big gold rate under the count, as in the brief */
.xo-pct-big {
    font-size: 1.45rem; font-weight: 800; letter-spacing: -.035em; line-height: 1;
    color: var(--gold-600); margin-top: .3rem; font-variant-numeric: tabular-nums;
}
/* panel 3's figure column is narrow and fixed so the chart keeps the width */
.xo-2col-tight { gap: .55rem; align-items: center; }
.xo-2col-tight .xo-2col-fig { flex: 0 0 104px; }
.xo-fig-k {
    font-size: .68rem; font-weight: 750; line-height: 1.3; color: var(--text-soft);
    text-transform: uppercase; letter-spacing: .04em;
}
.xo-2col-tight .xo-hero b { font-size: 1.5rem; }

@media (prefers-reduced-motion: reduce) {
    .xo-pt { transition: none !important; }
}

/* ---- panel 4 expanded ---- */
.xo-tbl-5 .xo-tbl-h, .xo-tbl-5 .xo-tbl-r,
#xoModal .xo-tbl-5 .xo-tbl-h, #xoModal .xo-tbl-5 .xo-tbl-r {
    grid-template-columns: 30px minmax(0,1fr) 130px 110px 90px 140px;
}
.xo-src {
    font-size: .8rem; color: var(--text-dim);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The old indent helper went with the flat completeness list — the per-company cards
   (.xo-qual) group by company, so there is nothing left to indent under. */
.xo-dist-r .l .bchip { flex: 0 0 auto; }

/* ==================== TYPE IN THE DIALOG ====================
   The dialog is 90vw and was inheriting card-sized type — text set for a 232px
   panel, shown at four times the area. Everything here is a step or two up; the
   compact card keeps its own smaller scale, which is what it is for. */
#xoModal .xo-x-h        { font-size: .84rem; letter-spacing: .07em; margin: 2rem 0 .7rem; }
#xoModal .xo-x-p        { font-size: 1rem; line-height: 1.7; max-width: 74ch; }
/* 62ch is the right measure for a paragraph sitting BESIDE a chart. Alone at
   the top of a 90vw dialog it wrapped at about a third of the width with a
   field of white to its right, which reads as a broken column rather than a
   deliberate measure. */
#xoModal .xo-x-lead     { margin-bottom: 1rem; max-width: 104ch; }
#xoModal .xo-mini-c .k  { font-size: .7rem; }
#xoModal .xo-mini-c b   { font-size: 1.6rem; }
#xoModal .xo-mini-c em  { font-size: .82rem; }
#xoModal .xo-mini       { gap: .8rem; }
#xoModal .xo-tbl-h      { font-size: .72rem; letter-spacing: .07em; padding-bottom: .5rem; }
#xoModal .xo-tbl-r      { font-size: .94rem; padding: .62rem .35rem; }
#xoModal .xo-src        { font-size: .88rem; }
#xoModal .xo-pen b      { font-size: .92rem; }
#xoModal .xo-rank       { width: 26px; height: 26px; font-size: .78rem; }
#xoModal .xo-dist-r     { font-size: .95rem; }
#xoModal .xo-dist-r .n  { font-size: 1rem; }
#xoModal .xo-dist-r .p  { font-size: .84rem; }
#xoModal .xo-note       { font-size: .9rem; line-height: 1.65; }
#xoModal .xo-leg        { font-size: .95rem; padding: .3rem .3rem; }
#xoModal .xo-leg .p     { font-size: .88rem; }
#xoModal .xo-tot b      { font-size: 1.5rem; }
#xoModal .xo-tot span   { font-size: .68rem; }
#xoModal .xo-tot em     { font-size: .78rem; }
#xoModal .xo-2col-b     { flex: 0 0 190px; }
#xoModal .xo-nm-logo .bchip img { max-height: 26px; }

/* ==================== COMPLETENESS, PER COMPANY (panel 4) ====================
   One card per company rather than nine rows in a flat list: the question is
   "how good is EFF's data", and that reads as a block, not as three rows a
   reader has to group by eye. */
.xo-qual { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; }
.xo-qual-c {
    padding: .9rem 1rem 1rem; border-radius: 12px;
    background: var(--surface-2); border: 1px solid var(--border-soft);
}
.xo-qual-h {
    display: flex; align-items: center; gap: .6rem; margin-bottom: .7rem;
    padding-bottom: .55rem; border-bottom: 1px solid var(--border);
}
.xo-qual-h > span { font-size: .95rem; font-weight: 700; }
.xo-qual-h em {
    margin-left: auto; font-style: normal; font-size: .78rem; color: var(--text-dim);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
.xo-qual-r {
    display: grid; grid-template-columns: 108px 1fr 52px 96px;
    gap: .6rem; align-items: center; font-size: .88rem; padding: .22rem 0;
}
.xo-qual-r .l { color: var(--text-soft); }
.xo-qual-r .b { height: 8px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.xo-qual-r .b i {
    display: block; height: 100%; border-radius: 999px;
    transform-origin: left; animation: growX .8s var(--ease-out) backwards;
}
.xo-qual-r .b i.ok   { background: var(--ok); }
.xo-qual-r .b i.warn { background: var(--warn); }
.xo-qual-r .b i.bad  { background: var(--bad); }
.xo-qual-r .v { text-align: right; font-weight: 750; font-variant-numeric: tabular-nums; }
.xo-qual-r .m { text-align: right; font-size: .78rem; color: var(--text-dim); white-space: nowrap; }

/* the one sentence that matters, called out rather than buried in a note */
.xo-flag {
    display: flex; gap: .7rem; align-items: flex-start; margin-top: 1.2rem;
    padding: .85rem 1rem; border-radius: 11px;
    background: var(--warn-weak); border: 1px solid var(--warn-line);
    border-left: 4px solid var(--warn);
    font-size: .92rem; line-height: 1.6; color: var(--text-soft);
}
.xo-flag i { color: var(--warn); font-size: 1.05rem; margin-top: .1rem; flex: 0 0 auto; }
.xo-flag b { color: var(--text); font-weight: 700; }

@media (prefers-reduced-motion: reduce) { .xo-qual-r .b i { animation: none !important; } }

/* The cohort comparison (multi-brand vs single-brand average value) went with
   panel 5's rewrite into a per-company revenue view. It is not lost: panel 7
   carries the same argument better, as a three-step ladder rather than a
   two-column split. Its rules are removed rather than left styling nothing. */

/* the multiple, between the two cards */
/* an incomplete period, drawn as one */
.xo-bar.is-part rect { stroke: var(--gold-600); stroke-width: 1; stroke-dasharray: 3 2; }
.xo-bar.is-part .xo-val { fill: var(--text-dim); }

/* the flag, in its neutral form */
.xo-flag.is-info {
    background: var(--gold-050); border-color: var(--gold-300); border-left-color: var(--gold-500);
}
.xo-flag.is-info i { color: var(--gold-600); }

/* the series name under a chart — what makes a single-colour chart say what it is */
.xo-clegend {
    display: flex; align-items: center; justify-content: center; gap: .4rem;
    font-size: .72rem; color: var(--text-soft); margin-top: .1rem;
}
.xo-clegend i { width: 9px; height: 9px; border-radius: 3px; flex: 0 0 9px; }
#xoModal .xo-clegend { font-size: .88rem; margin-top: .5rem; }

/* ---- panel 7 ---- */
.xo-swatch {
    width: 11px; height: 11px; border-radius: 3px; display: inline-block;
    margin-right: .55rem; flex: 0 0 11px; vertical-align: -1px;
}
.xo-tbl-3 .xo-tbl-h, .xo-tbl-3 .xo-tbl-r {
    grid-template-columns: 30px minmax(0,1fr) 200px 150px;
}
/* THE LEGEND MUST SIT WITH THE CHART. In the dialog .xo-leg was a 4-column grid
   across the full 90vw, so the counts ended up against the right-hand edge with
   half a metre of white between them and the label they belong to. */
#xoModal .xo-legend { max-width: 520px; }
#xoModal .xo-donut-wrap { align-items: center; gap: 2.5rem; }

/* ---- panel 8: opportunity segments ---- */
.xo-opps-h {
    display: grid; grid-template-columns: 22px minmax(0,1fr) 78px; gap: .5rem;
    font-size: .58rem; font-weight: 750; letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-dim); padding: 0 .2rem .3rem; border-bottom: 1px solid var(--border);
}
.xo-opps-h .r { text-align: right; }
.xo-opp-r {
    display: grid; grid-template-columns: 22px minmax(0,1fr) 78px; gap: .5rem;
    align-items: center; padding: .34rem .2rem;
    border-top: 1px solid var(--border-soft); font-size: .82rem;
}
.xo-opp-r:first-of-type { border-top: 0; }
.xo-opp-m { display: flex; align-items: center; gap: .4rem; min-width: 0; }
.xo-opp-m > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.xo-opp-m .bmark { height: 24px; width: 24px; flex: 0 0 24px; border-radius: 5px; }
.xo-opp-i { color: var(--text-dim); font-size: .9rem; flex: 0 0 auto; }
/* the count sits above its own share bar, so a glance ranks the segments
   without reading five numbers */
.xo-opp-n { position: relative; text-align: right; padding-bottom: 5px; }
.xo-opp-n b { font-variant-numeric: tabular-nums; font-weight: 700; }
.xo-opp-n em { font-size: .72rem; color: var(--text-dim); font-style: normal; }
.xo-opp-n i {
    position: absolute; right: 0; bottom: 0; height: 3px; border-radius: 2px;
    background: var(--gold-400); display: block;
}
.xo-opp-r.is-off { opacity: .5; }

.xo-seg {
    border: 1px solid var(--border); border-radius: 12px; padding: 1rem 1.1rem;
    margin-bottom: .85rem; background: var(--surface);
}
.xo-seg.is-off { background: var(--surface-3); border-style: dashed; }
.xo-seg-h { display: flex; align-items: center; gap: .6rem; margin-bottom: .45rem; }
.xo-seg-t { font-size: 1.02rem; font-weight: 700; letter-spacing: -.01em; }
.xo-seg-flow { display: flex; align-items: center; gap: .35rem; margin-left: auto; }
.xo-seg-flow i { color: var(--text-dim); font-size: .8rem; }
.xo-seg-p { margin: 0 0 .8rem; font-size: .9rem; line-height: 1.6; color: var(--text-soft); }
.xo-seg-s { display: flex; flex-wrap: wrap; gap: .5rem; }
.xo-seg-k {
    flex: 1 1 150px; background: var(--surface-2); border: 1px solid var(--border-soft);
    border-radius: 9px; padding: .55rem .7rem;
}
.xo-seg-k b { display: block; font-size: 1.25rem; font-weight: 750; letter-spacing: -.02em; }
.xo-seg-k span { font-size: .72rem; color: var(--text-dim); line-height: 1.35; display: block; }
.xo-seg-tbl { margin-top: .9rem; }
.xo-rank.sm { font-size: .66rem; }

/* ---- panel 9: consolidators ---- */
/* TWO FAULTS FROM THE SCREENSHOT, BOTH GEOMETRY.
   1. The header "2+ brands" wrapped under "Locations" because the columns were
      auto-sized off .xo-tbl's default grid and there was no width to hold it.
      Every heading is nowrap now and each column has a declared floor.
   2. The penetration bar overflowed its column and struck through the count in
      the column before it — .xo-pen is justify-content:flex-end, so an
      inline-flex wider than its cell spills LEFT, over its neighbour rather
      than off the edge where it would have been noticed. The bar is capped in
      px against the column it lives in, and the cell clips. */
.xo-tbl-h span { white-space: nowrap; }
.xo-tbl-c2 .xo-tbl-h, .xo-tbl-c2 .xo-tbl-r {
    grid-template-columns: 22px minmax(0,1fr) 62px 96px;
}
.xo-tbl-c4 .xo-tbl-h, .xo-tbl-c4 .xo-tbl-r,
#xoModal .xo-tbl-c4 .xo-tbl-h, #xoModal .xo-tbl-c4 .xo-tbl-r {
    grid-template-columns: 30px minmax(0,1fr) 132px 96px 92px 132px;
}
.xo-tbl-c2 .xo-pen, .xo-tbl-c4 .xo-pen { overflow: hidden; }
.xo-tbl-c2 .xo-pen-t { width: 44px; }
#xoModal .xo-tbl-c4 .xo-pen-t { width: 70px; }
.xo-known { display: flex; align-items: center; gap: .35rem; min-width: 0; }
/* 26px, comfortably above the --brand-logo-min 22px floor, in a 132px cell */
.xo-known .bmark { height: 26px; width: 26px; flex: 0 0 26px; }
.xo-split-h .bmark { height: 26px; width: 26px; flex: 0 0 26px; }

/* the one-line trailer under the compact table — the split-group finding is the
   most valuable thing in this panel and would otherwise be invisible until the
   dialog is opened */
/* --accent-weak/-line/-ink, NOT the raw gold ramp. The gold steps are fixed
   brand values with no dark counterpart, so --gold-050 (#FDFCF8) stays
   near-white in dark mode — a white strip across a black card. --accent-* is
   the theme-aware pair: gold-100/300 in light, a translucent gold wash in dark. */
/* the finding and the way into it are ONE control — see the note in render() */
.xo-all-hint {
    background: var(--accent-weak); border-color: var(--accent-line); color: var(--text-soft);
    gap: .4rem; font-weight: 600; text-align: left;
}
.xo-all-hint:hover { background: var(--accent-weak); border-color: var(--accent); color: var(--text); }
.xo-all-hint > i:first-child { color: var(--accent); font-size: 1rem; flex: 0 0 auto; }
.xo-all-hint > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.xo-all-hint b { color: var(--text); font-weight: 800; }


.xo-splits { display: grid; grid-template-columns: repeat(auto-fit, minmax(330px,1fr)); gap: .7rem; }
.xo-split-c {
    border: 1px solid var(--accent-line); border-radius: 12px; padding: .8rem .9rem;
    background: var(--accent-weak);
}
.xo-split-halves { display: flex; align-items: center; gap: .5rem; }
.xo-split-h {
    flex: 1 1 0; min-width: 0; display: flex; align-items: center; gap: .4rem;
    background: var(--surface); border: 1px solid var(--border); border-radius: 9px;
    padding: .4rem .55rem;
}
.xo-split-h .nm {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: .84rem; font-weight: 650;
}
.xo-split-h b { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 750; }
.xo-split-halves > i { color: var(--gold-600); font-size: .8rem; flex: 0 0 auto; }
.xo-split-sum { display: flex; align-items: flex-end; gap: .9rem; margin-top: .6rem; flex-wrap: wrap; }
.xo-split-k b { display: block; font-size: 1.15rem; font-weight: 750; letter-spacing: -.02em; }
.xo-split-k span { font-size: .7rem; color: var(--text-dim); }
.xo-why {
    margin-left: auto; font-size: .66rem; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--accent); background: var(--surface);
    border: 1px solid var(--accent-line); border-radius: 999px; padding: .1rem .45rem;
    white-space: nowrap;
}

/* ---- panel 6: the fee waterfall ---- */
.xo-wf { display: flex; align-items: stretch; gap: .5rem; margin-bottom: .8rem; flex-wrap: wrap; }
.xo-wf-s {
    flex: 1 1 190px; min-width: 0; padding: .7rem .85rem; border-radius: 11px;
    background: var(--surface-2); border: 1px solid var(--border-soft);
}
.xo-wf-s.is-sub { background: var(--surface); }
.xo-wf-s.is-hi  { background: var(--accent-weak); border-color: var(--accent-line); }
.xo-wf-s .k {
    display: block; font-size: .64rem; font-weight: 750; letter-spacing: .07em;
    text-transform: uppercase; color: var(--text-dim); margin-bottom: .15rem;
}
.xo-wf-s b { display: block; font-size: 1.45rem; font-weight: 780; letter-spacing: -.03em; }
.xo-wf-s .s { display: block; font-size: .74rem; color: var(--text-soft); line-height: 1.35; }
.xo-wf-op {
    align-self: center; font-size: 1.1rem; font-weight: 700; color: var(--text-dim);
    flex: 0 0 auto;
}

/* what is still missing, listed rather than described */
.xo-gaps { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px,1fr)); gap: .45rem; }
.xo-gap {
    display: flex; align-items: flex-start; gap: .5rem; padding: .55rem .75rem;
    border-radius: 9px; background: var(--surface-3); border: 1px dashed var(--border);
    font-size: .84rem; color: var(--text-soft);
}
.xo-gap i { color: var(--text-dim); margin-top: .12rem; flex: 0 0 auto; }

/* ---- panels 5 & 6: the portfolio views ---- */
/* the shared company legend — logo and figure, no company name in words */
.xo-colegend { display: flex; flex-wrap: wrap; gap: .4rem; justify-content: center; margin-top: .4rem; }
.xo-cochip {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .18rem .5rem; border-radius: 999px;
    background: var(--surface-2); border: 1px solid var(--border-soft);
}
.xo-cochip .bmark { height: 22px; width: 22px; flex: 0 0 22px; border-radius: 4px; }
.xo-cochip .bmark.is-wide { width: auto; max-width: 64px; }
.xo-cochip b { font-size: .82rem; font-weight: 750; font-variant-numeric: tabular-nums; }
#xoModal .xo-cochip b { font-size: .95rem; }
#xoModal .xo-cochip .bmark { height: 26px; width: 26px; flex: 0 0 26px; }

/* panel 6's compact card: one row per company, logo + revenue + margin state */
.xo-mrows { display: flex; flex-direction: column; gap: .35rem; flex: 1; min-height: 0; }
.xo-mrow {
    display: grid; grid-template-columns: 30px minmax(0,1fr) auto; gap: .55rem;
    align-items: center; padding: .34rem .45rem; border-radius: 9px;
    background: var(--surface-2); border: 1px solid var(--border-soft);
}
.xo-mrow .bmark { height: 26px; width: 26px; flex: 0 0 26px; border-radius: 5px; }
.xo-mrow .bmark.is-wide { width: auto; max-width: 30px; }
.xo-mrow .v { font-size: 1rem; font-weight: 750; font-variant-numeric: tabular-nums; }
.xo-mrow .m {
    font-size: .68rem; font-weight: 700; white-space: nowrap;
    padding: .1rem .45rem; border-radius: 999px;
    background: var(--surface-3); color: var(--text-dim); border: 1px solid var(--border);
}
.xo-mrow .m.is-known {
    background: var(--ok-weak); color: var(--ok-ink); border-color: var(--ok-line);
}
.xo-mrow.is-off { opacity: .5; }

.xo-tbl-f2 .xo-tbl-h, .xo-tbl-f2 .xo-tbl-r,
#xoModal .xo-tbl-f2 .xo-tbl-h, #xoModal .xo-tbl-f2 .xo-tbl-r {
    grid-template-columns: 30px minmax(0,1fr) 260px 150px 110px;
}
.xo-nm-logo .bmark { height: 24px; width: 24px; flex: 0 0 24px; border-radius: 5px; }
.xo-nm-logo .bmark.is-wide { width: auto; max-width: 70px; }
/* a stacked segment lifts on hover with its siblings dimmed, so a thin brand
   band is still findable in a bar dominated by one company */
.xo-seg { transition: opacity .14s ease; }
.xo-bar:hover .xo-seg { opacity: .55; }
.xo-bar:hover .xo-seg:hover { opacity: 1; }

/* ---- panel 6 modal: the four-company picture, before any one company ---- */
.xo-cocards { display: grid; grid-template-columns: repeat(auto-fit, minmax(215px,1fr)); gap: .6rem; }
.xo-cocard {
    padding: .85rem .9rem; border-radius: 12px;
    background: var(--surface-2); border: 1px solid var(--border-soft);
}
.xo-cocard.is-off { background: var(--surface-3); border-style: dashed; opacity: .72; }
.xo-cocard-h { display: flex; align-items: center; min-height: 30px; margin-bottom: .45rem; }
.xo-cocard-h .bmark { height: 28px; width: 28px; flex: 0 0 28px; border-radius: 5px; }
.xo-cocard-h .bmark.is-wide { width: auto; max-width: 108px; }
.xo-cocard-v { font-size: 1.5rem; font-weight: 780; letter-spacing: -.03em; line-height: 1.1; }
.xo-cocard-k { font-size: .72rem; color: var(--text-dim); margin-bottom: .5rem; }
.xo-cocard-m {
    display: inline-block; font-size: .68rem; font-weight: 750; white-space: nowrap;
    padding: .12rem .5rem; border-radius: 999px; margin-bottom: .45rem;
    background: var(--surface-3); color: var(--text-dim); border: 1px solid var(--border);
}
.xo-cocard-m.is-known {
    background: var(--ok-weak); color: var(--ok-ink); border-color: var(--ok-line);
}
.xo-cocard-s { font-size: .76rem; line-height: 1.45; color: var(--text-soft); }

/* a bounded single-company section inside a portfolio panel, so it reads as a
   part of the whole rather than as what the panel is about */
.xo-sect {
    margin-top: 1.4rem; padding: 1rem 1.1rem 1.1rem; border-radius: 14px;
    border: 1px solid var(--border); background: var(--surface-2);
}
.xo-sect-h { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .3rem; }
.xo-sect-h .bmark { height: 30px; width: 30px; flex: 0 0 30px; border-radius: 6px; }
.xo-sect-h > span { font-size: 1.05rem; font-weight: 750; letter-spacing: -.01em; }
.xo-sect-h em {
    font-size: .74rem; font-style: normal; color: var(--text-dim);
    padding: .1rem .5rem; border-radius: 999px; background: var(--surface-3);
    border: 1px solid var(--border-soft);
}
.xo-sect .xo-x-h:first-of-type { margin-top: .9rem; }
