/* ============================================================
   Gantry shared component classes
   ------------------------------------------------------------
   Canonical home of the cross-surface component CSS (.cg-*).
   Lifted verbatim from web/theme.css (2026-07-05); web/theme.css
   keeps only web-specific styles. Extension and landing consume
   bundled copies via `make ui-sync`.
   Depends on tokens.css being loaded first.

   2026-08-04: EDITORIAL RESTYLE. Ink-on-paper chrome: hairline
   (1px var(--line)) borders carry structure, shadows only on true
   overlays (modal, dropdown panel), near-square radii, serif
   display type (--font-serif) for section titles and stat
   numerals, sans micro-labels (11px / 500 / .08em / uppercase).
   Buttons are ink-inverse (--fg on --bg), never brand-filled —
   gold is an accent (focus rings, active ticks, small highlights).
   ============================================================ */

/* The brand lockup (GantryLogo / logo.js LOGO_HTML). Geometry — mark:word
   ratio, gap, and the baseline drop that sits the mark's ground line on the
   wordmark baseline — is inline on the element and derived from one `height`,
   so a surface can NEVER restyle the logo's proportions. This class exists as
   the layout hook (and as the marker that the element is DS-owned): position
   it, colour it, hide .logo-word responsively — nothing else.
   NOTE display:inline-flex is inline ON the element (the lockup has to stand
   up in token-less contexts too), so it beats any display here or in a
   surface stylesheet — wrap the logo to show/hide it responsively.
   NOTE the name: `.cg-logo` was already taken by <OrgLogo/>'s 36px tile
   further down this file, so the lockup is `.cg-lockup`. Do not "tidy" it
   back — the collision made the logo render as a bordered grey square. */
.cg-lockup { flex: none; }
.cg-lockup .logo-word { white-space: nowrap; }

.cg-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px; border-radius: var(--r-pill);
  font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
  font-family: var(--font-mono); font-weight: 500;
  border: 1px solid var(--line);
  color: var(--fg-2);
  background: var(--bg-2);
}
.cg-pill.good { color: var(--good); border-color: color-mix(in oklab, var(--good) 35%, var(--line)); background: color-mix(in oklab, var(--good) 8%, var(--bg-2)); }
.cg-pill.warn { color: var(--warn); border-color: color-mix(in oklab, var(--warn) 35%, var(--line)); background: color-mix(in oklab, var(--warn) 8%, var(--bg-2)); }
.cg-pill.bad  { color: var(--bad);  border-color: color-mix(in oklab, var(--bad)  35%, var(--line)); background: color-mix(in oklab, var(--bad)  8%, var(--bg-2)); }

.cg-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.cg-dot.good { background: var(--good); box-shadow: 0 0 0 3px var(--good-glow); }
.cg-dot.warn { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-glow); }
.cg-dot.bad  { background: var(--bad);  box-shadow: 0 0 0 3px var(--bad-glow); }
/* `none` = no confidence available. Deliberately NOT a fourth status colour:
   red/amber/green each say "we measured this and here is the result", so an
   unmeasured value needs to sit outside that vocabulary entirely. A hollow
   ring with no glow reads as absence. See api/derived_meta.py — absent is not
   low, and rendering it as a low score is the bug this styling prevents. */
.cg-dot.none { background: transparent; box-shadow: none;
               border: 1px solid var(--line-strong); }

/* WatchMark — "you watch this". A gold ring with a gold core (the watched
   eye) plus the unread count in a gold hairline pill. Gold is the accent for
   "yours" and never a fill behind text. The core breathes while there is
   something unread; the count pops once on arrival. */
.cg-watchmark { display: inline-flex; align-items: center; gap: 5px; vertical-align: middle;
                color: var(--brand-text); flex: 0 0 auto; }
.cg-watchmark-glyph {
  position: relative; display: inline-block; box-sizing: border-box;
  width: 10px; height: 10px; border-radius: 50%;
  border: 1.5px solid var(--brand);
}
.cg-watchmark-glyph::after {
  content: ""; position: absolute; inset: 2px; border-radius: 50%;
  background: var(--brand);
  transform: scale(.7); opacity: .85;
}
.cg-watchmark.has-unread .cg-watchmark-glyph::after {
  animation: cg-watchmark-breathe 2.4s var(--ease) infinite;
}
.cg-watchmark-count {
  font-size: 10px; line-height: 1; padding: 2px 5px;
  border: 1px solid color-mix(in oklab, var(--brand) 45%, var(--line));
  border-radius: var(--r-pill);
  color: var(--brand-text);
  animation: cg-watchmark-pop .35s var(--ease) both;
}
.cg-watchmark-md .cg-watchmark-glyph { width: 12px; height: 12px; }
.cg-watchmark-md .cg-watchmark-count { font-size: 11px; padding: 3px 6px; }
@keyframes cg-watchmark-breathe {
  0%, 100% { transform: scale(.7); opacity: .85; }
  50%      { transform: scale(1);  opacity: 1; }
}
@keyframes cg-watchmark-pop {
  0%   { transform: scale(.6); opacity: 0; }
  60%  { transform: scale(1.1); opacity: 1; }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .cg-watchmark-glyph::after, .cg-watchmark-count { animation: none !important; }
}

/* Confidence readout — <CoverageDot>-sized dot plus its number. */
.cg-conf { display: inline-flex; align-items: center; gap: 6px; }
.cg-conf-val { font-size: 11px; color: var(--fg-3); }
.cg-conf-none { font-size: 11px; color: var(--fg-4); font-style: italic; }

/* Trend readout — direction glyph, current reading, prior reading.
   DELIBERATELY OUTSIDE THE STATUS PALETTE: whether "up" is good or bad belongs
   to the measure, not to the direction (rising ED visits are bad, a rising
   quality score is good), so colouring an arrow green or red would state a
   judgement the component cannot make. The modifier classes exist for a
   caller that legitimately knows the sentiment and wants to say so; they carry
   no colour of their own, and they are namespaced (`.cg-trend-up`) because a
   bare `up`/`down`/`flat` token would collide with surface CSS far more
   readily than <Pill/>'s `good`/`warn`/`bad` ever could.
   See <Trend/> in components/status.jsx. */
.cg-trend { display: inline-flex; align-items: baseline; gap: 5px;
            font-family: var(--font-mono); font-size: 11px; color: var(--fg-2); }
.cg-trend-mark { font-size: 9px; line-height: 1; color: var(--fg-3); }
.cg-trend-val { font-variant-numeric: tabular-nums lining-nums; }
.cg-trend-prev { color: var(--fg-4); }

/* Buttons — ink-inverse doctrine (2026-08-04): the DEFAULT variant is the
   hairline "secondary" (transparent bg, 1px --line-strong); `primary` is the
   one filled button — ink on paper (--fg on --bg), NOT brand-filled; `brand`
   is a gold-outline ACCENT button (gold is never a fill); `ghost` is text
   only; `danger` is the destructive outline on the status red. No glows;
   hover states are flat washes. */
.cg-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 16px;
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 13px; font-weight: 500; line-height: 1.2;
  transition: background .15s var(--ease), border-color .15s var(--ease),
    color .15s var(--ease), opacity .15s var(--ease), transform .1s var(--ease);
}
.cg-btn:hover { background: var(--bg-hover); }
.cg-btn:active { transform: translateY(1px); }
.cg-btn.primary {
  background: var(--fg);
  color: var(--bg);
  border-color: var(--fg);
}
.cg-btn.primary:hover { background: var(--fg); border-color: var(--fg); opacity: .9; }
/* Kept class, new meaning: a gold-outline accent action — the editorial
   contract bans gold FILLS, so the brand variant reads as accent, not CTA.
   --brand-text keeps small gold text AA on both themes. */
.cg-btn.brand {
  background: transparent;
  color: var(--brand-text);
  border-color: var(--brand);
}
.cg-btn.brand:hover { background: var(--brand-glow); border-color: var(--brand); }
.cg-btn.ghost { background: transparent; border-color: transparent; }
.cg-btn.ghost:hover { background: var(--bg-hover); }
.cg-btn.danger {
  background: transparent;
  color: var(--bad);
  border-color: color-mix(in oklab, var(--bad) 55%, var(--line));
}
.cg-btn.danger:hover {
  background: color-mix(in oklab, var(--bad) 10%, transparent);
  border-color: var(--bad);
}
.cg-btn:disabled { opacity: .5; cursor: not-allowed; }
/* <Button as="a"> renders an anchor; keep it reading as a button */
a.cg-btn { text-decoration: none; }

.cg-input {
  width: 100%;
  padding: 11px 14px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 14px;
  outline: none;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
/* Light theme keeps fields paper-white even on tinted panels. */
[data-theme="light"] .cg-input { background: var(--bg); }
.cg-input:hover:not(:disabled):not(:focus) { border-color: var(--line-strong); }
.cg-input:focus { border-color: var(--fg); }
.cg-input::placeholder { color: var(--fg-4); }
.cg-input:disabled { opacity: .5; cursor: not-allowed; }

/* Textarea (<Textarea/>) — .cg-input's border/focus/placeholder rules apply
   unchanged; this adds only what a multi-line field needs on top: a sane
   starting height, vertical-only resize (a form column must never grow
   sideways), and the body font — .cg-input's line-height is tuned for a
   single line and reads cramped across several. */
.cg-textarea {
  min-height: 96px;
  resize: vertical;
  line-height: 1.5;
  font-family: var(--font-sans);
}

/* Checkbox — the tick box (<Checkbox/>). A native input with its own
   drawing replaced: a hairline square that fills ink-inverse with a paper
   tick when checked, the same language as .cg-btn.primary and .cg-switch.on
   (gold is never a fill). Sized to sit on a 13–14px text line. */
.cg-check {
  appearance: none; -webkit-appearance: none;
  flex: none; display: inline-grid; place-content: center;
  width: 16px; height: 16px; margin: 0; padding: 0;
  background: var(--bg); border: 1px solid var(--line-strong);
  border-radius: var(--r-sm); color: var(--bg);
  cursor: pointer; vertical-align: -3px;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.cg-check::before {
  content: ""; width: 9px; height: 5px;
  border-left: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: translateY(-1px) rotate(-45deg) scale(0); transform-origin: center;
  transition: transform .12s var(--ease);
}
.cg-check:hover:not(:disabled) { border-color: var(--fg-3); }
.cg-check:checked { background: var(--fg); border-color: var(--fg); }
.cg-check:checked::before { transform: translateY(-1px) rotate(-45deg) scale(1); }
.cg-check:indeterminate { background: var(--fg); border-color: var(--fg); }
.cg-check:indeterminate::before {
  width: 8px; height: 0; border-left: 0; border-bottom: 2px solid currentColor;
  transform: none;
}
.cg-check:disabled { opacity: .5; cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) {
  .cg-check, .cg-check::before { transition: none; }
}

/* Switch — the on/off control (<Switch/>). Ink-inverse like .cg-btn.primary:
   ON fills with --fg, because gold is an accent and never a fill. OFF is a
   hairline well with a muted thumb, so the two states differ in FILL and in
   thumb POSITION — not in hue alone, which is what keeps them apart for a
   red/green-blind reader and in a screenshot. */
.cg-switch {
  position: relative;
  flex: none;
  display: inline-flex; align-items: center;
  width: 34px; height: 20px; padding: 0;
  background: var(--bg-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition: background .15s var(--ease), border-color .15s var(--ease),
    opacity .15s var(--ease);
}
.cg-switch-thumb {
  position: absolute; top: 2px; left: 2px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--fg-3);
  transition: transform .15s var(--ease), background .15s var(--ease);
}
.cg-switch:hover:not(:disabled) { border-color: var(--fg-3); }
.cg-switch.on { background: var(--fg); border-color: var(--fg); }
.cg-switch.on .cg-switch-thumb { background: var(--bg); transform: translateX(14px); }
.cg-switch.on:hover:not(:disabled) { opacity: .9; }
.cg-switch:disabled { opacity: .5; cursor: not-allowed; }
.cg-switch.sm { width: 28px; height: 16px; }
.cg-switch.sm .cg-switch-thumb { width: 10px; height: 10px; }
.cg-switch.sm.on .cg-switch-thumb { transform: translateX(12px); }
@media (prefers-reduced-motion: reduce) {
  .cg-switch, .cg-switch-thumb { transition: none; }
}

/* Card — hairline surface, no shadow. Padding stays the CALLER's: shipped
   cards compose flush internal sections (.cg-card-head et al. in the
   surfaces), so a default padding here would double-pad them. Give content
   generous room (20px+) at the call site. */
.cg-card {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.cg-divider { height: 1px; background: var(--line); border: 0; }

/* Section title — display serif (Newsreader), small-heading cut.
   Sentence case; an <em> inside renders as the italic accent word. */
.cg-section-title {
  font-family: var(--font-serif);
  font-size: 20px;
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: 0;
  color: var(--fg);
}

/* Numeric stat block — big serif numerals with lining + tabular figures
   forced on so stat columns still align (Newsreader's defaults drift
   without them). */
.cg-stat-num {
  font-family: var(--font-serif);
  font-weight: 500;
  font-feature-settings: "lnum" 1, "tnum" 1;
  font-variant-numeric: lining-nums tabular-nums;
  letter-spacing: 0;
  line-height: 1.1;
}

/* Grid background utility */
.cg-grid-bg {
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 32px 32px;
}

/* Smooth fade on screen transitions */
@keyframes cg-fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.cg-fade { animation: cg-fade-in .18s var(--ease); }

/* Skeleton/placeholder — flat panel tone, no stripes/shimmer. */
.cg-placeholder {
  background: var(--bg-3);
  border: 1px dashed var(--line-2);
  color: var(--fg-3);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-sans); font-size: 11px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .08em;
}

/* Avatar (initials) — round is the sanctioned exception (--r-pill). */
.cg-avatar {
  width: 32px; height: 32px;
  border-radius: var(--r-pill);
  background: var(--bg-3);
  border: 1px solid var(--line);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 11px;
  color: var(--fg-2);
  flex: 0 0 auto;
}

/* Org "logo" tile — text-based, monogram-style */
.cg-logo {
  width: 36px; height: 36px;
  border-radius: var(--r-md);
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-sans); font-weight: 600;
  font-size: 13px;
  color: var(--fg);
  border: 1px solid var(--line);
  background: var(--bg-2);
  letter-spacing: 0;
  flex: 0 0 auto;
}

/* Tabs — editorial: no boxes, text on a hairline track; the active tab's own
   1px underline sits ON the track (margin-bottom -1px overlaps it). */
.cg-tabs { display: flex; gap: 18px; border-bottom: 1px solid var(--line); }
.cg-tab {
  padding: 10px 2px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid transparent;
  margin-bottom: -1px;
  color: var(--fg-3);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  transition: color .15s var(--ease), border-color .15s var(--ease);
}
.cg-tab:hover { color: var(--fg-2); }
.cg-tab[data-active="true"] {
  color: var(--fg);
  border-bottom-color: var(--fg);
}

/* Segmented — <Segmented options value onChange> (tabs.jsx). A pill track
   with one lit option: the same shape as the app's section bar, sized for a
   form. Options wrap rather than clip so a six-way choice survives a narrow
   modal. */
.cg-segmented {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  padding: 3px;
  max-width: 100%;
  background: var(--bg-3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
}
.cg-segmented-opt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--fg-3);
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.2;
  cursor: pointer;
  white-space: nowrap;
  transition: color .15s var(--ease), background-color .15s var(--ease);
}
.cg-segmented-opt:hover { color: var(--fg); }
.cg-segmented-opt[data-active="true"] {
  background: var(--bg-2);
  color: var(--fg);
  box-shadow: var(--shadow-md);
}
.cg-segmented-opt:disabled { color: var(--fg-4); cursor: not-allowed; }
.cg-segmented-opt:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.cg-segmented-sm .cg-segmented-opt { padding: 3px 9px; font-size: 11.5px; }

/* Kicker — THE canonical micro-label: 11px sans, 500, .08em, uppercase. */
.cg-kicker {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* Modal (lifted verbatim from web/connect.css, 2026-07-05 — the wizard
   step indicator stayed in connect.css). A true overlay, so it keeps its
   shadow; the scrim is a touch lighter than the pre-editorial .4. */
.cg-modal-wrap {
  position: fixed; inset: 0; z-index: 220;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
.cg-modal-scrim {
  position: absolute; inset: 0;
  background: rgba(0,0,0,.32); /* theme-neutral black wash; no scrim token */
  backdrop-filter: blur(2px);
}
.cg-modal {
  position: relative;
  width: min(560px, 100%);
  max-height: min(86vh, 760px);
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column;
  overflow: hidden;
}
.cg-modal-head {
  display: flex; align-items: center; gap: 12px;
  padding: 20px 24px;
  border-bottom: 1px solid var(--line);
}
.cg-modal-body {
  padding: 24px;
  overflow-y: auto;
  flex: 1;
}
.cg-modal-foot {
  display: flex; align-items: center; gap: 8px;
  padding: 16px 24px;
  border-top: 1px solid var(--line);
}

/* Popover — anchored flyout (design-system/components/popover.jsx). The
   trigger is inline (span, not a positioned box); the panel is absolutely
   positioned off the root, which IS position:relative so align works without
   a portal. Kept small and light — this is inline help, not a modal. */
.cg-popover-root { position: relative; display: inline-flex; }
.cg-popover-trigger { display: inline-flex; cursor: pointer; }
.cg-popover {
  position: absolute;
  z-index: 260;
  width: max-content;
  max-width: 280px;
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  padding: 12px 14px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--fg-2);
}
.cg-popover-bottom { top: calc(100% + 8px); left: 0; }
.cg-popover-top { bottom: calc(100% + 8px); left: 0; }
.cg-popover-right { top: 0; left: calc(100% + 8px); }
.cg-popover-left { top: 0; right: calc(100% + 8px); }
.cg-popover-title {
  font-weight: 600;
  font-size: 12.5px;
  color: var(--fg);
  margin-bottom: 6px;
}
.cg-popover-body ol, .cg-popover-body ul { margin: 0; padding-left: 18px; }
.cg-popover-body li { margin: 2px 0; }

/* ---- Table -------------------------------------------------------------
   `.cg-table` is ALREADY styled per-surface (web/theme.css owns its look), so
   everything below is scoped INSIDE `.cg-table-wrap` — a table only gets the
   frozen header, the editorial baseline, and its own scroll pane when a
   surface explicitly wraps it:

       <div class="cg-table-wrap"><table class="cg-table"> … </table></div>

   Nothing that ships today wraps its tables except admin/, so an unwrapped
   `.cg-table` (all of web/) is untouched by these rules. Do NOT hoist them onto
   bare `.cg-table` — that would silently freeze every header in the web app.

   EDITORIAL BASELINE (2026-08-04, contract rule 8): wrapped tables get
   micro-label header cells, hairline row separators, generous row height,
   and no zebra. Numeric-column right alignment stays per-surface markup —
   CSS can't detect it. A surface stylesheet may still override any of it.

   FROZEN HEADER. `thead th { position: sticky }` pins to the nearest scrollport.
   At wide widths the wrapper has no overflow, so that scrollport is the VIEWPORT
   and the header freezes against the top of the window while rows scroll under
   it. A surface with its own fixed chrome offsets the header past it by setting
   --cg-table-sticky-top.

   HORIZONTAL SCROLL. Below 1100px the wrapper takes `overflow:auto`, so a wide
   table scrolls sideways instead of forcing the page to. That makes the wrapper
   a scrollport, which would otherwise defeat viewport-sticky — so it is also
   given a bounded height and the header pins to the top of that pane instead.
   The header stays frozen at every width.

   `border-collapse: separate` is deliberate: with `collapse` the row rules
   belong to the table rather than its cells, and Chrome/Safari drop them from a
   sticky header. `border-spacing: 0` keeps the collapsed look. */
.cg-table-wrap { width: 100%; }

.cg-table-wrap .cg-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}

.cg-table-wrap .cg-table thead th {
  position: sticky;
  top: var(--cg-table-sticky-top, 0px);
  z-index: 2;
  background: var(--cg-table-head-bg, var(--bg-1));
  /* rule 8: header cells in micro-label style over a hairline rule */
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--fg-3);
  text-align: left;
  border-bottom: 1px solid var(--line);
}

.cg-table-wrap .cg-table tbody td {
  border-bottom: 1px solid var(--line);
}

/* Generous row height (44px+ with 13px type). */
.cg-table-wrap .cg-table th,
.cg-table-wrap .cg-table td { padding: 13px 12px; }

@media (max-width: 1100px) {
  .cg-table-wrap {
    overflow: auto;
    max-height: var(--cg-table-pane-h, 70dvh);
    -webkit-overflow-scrolling: touch;
  }
  /* Inside the pane the header pins to the pane's top, not the window's. */
  .cg-table-wrap .cg-table thead th { top: 0; }
}

/* ---------------------------------------------------------------
   Web-presence links (<SocialLinks/> — design-system/components/social-icon.jsx)
   Round icon buttons, one per platform the entity has a *_url for.
   Moved here from surfaces/web/explore.css when SocialLinks became a DS
   component: the extension renders the same markup, so the rule cannot live
   in a surface stylesheet. `flex-wrap` matters — an entity can carry up to
   18 social links and the row sits in a narrow detail card.
   --------------------------------------------------------------- */
.cg-socials {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.cg-social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Never shrink: as a flex item the 30px width is only a basis, and with 18
     platforms in a narrow detail card the row would squash every button into a
     16px-wide oval instead of wrapping. */
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: transparent;
  color: var(--fg-2);
  transition: background .12s var(--ease), color .12s var(--ease),
    border-color .12s var(--ease);
}
.cg-social-link:hover {
  background: var(--bg-hover);
  color: var(--fg);
  border-color: var(--line-2);
}

/* ---------- MeterRow ---------- */
/* Labeled distribution bar: micro-label · hairline track · value. Stacked
   rows align their label columns via --meter-label-w on any wrapping
   element. */
.cg-meter-row {
  display: grid;
  grid-template-columns: var(--meter-label-w, 92px) 1fr max-content;
  align-items: center;
  gap: 10px;
  min-height: 20px;
}
.cg-meter-row-label {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--fg-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cg-meter-row-track {
  height: 4px;
  border-radius: var(--r-xs);
  background: var(--line);
  overflow: hidden;
}
.cg-meter-row-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-xs);
  background: var(--brand);
  transition: width .4s var(--ease);
}
.cg-meter-row-value {
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--fg-3);
  min-width: 34px;
  text-align: right;
}

/* ---------- DecileGauge ---------- */
/* Ten segments, hairline-separated, on a flat track. Colour is the shared
   good/warn/bad vocabulary (see gauge.jsx for the bucket rule); `neutral`
   draws ink for a strength that is not a grade; `none` is hollow — an
   unmeasured reading, never an empty red one. */
.cg-decile { display: inline-flex; align-items: center; gap: 7px; vertical-align: middle; }
.cg-decile-segs {
  display: inline-grid;
  grid-template-columns: repeat(10, 6px);
  gap: 2px;
  align-items: end;
}
.cg-decile-seg {
  display: block;
  height: 10px;
  border-radius: var(--r-xs);
  background: var(--line);
  transform-origin: bottom;
  transition: background .25s var(--ease);
}
.cg-decile-seg[data-on="true"] { background: var(--fg-3); }
.cg-decile.good    .cg-decile-seg[data-on="true"] { background: var(--good); }
.cg-decile.warn    .cg-decile-seg[data-on="true"] { background: var(--warn); }
.cg-decile.bad     .cg-decile-seg[data-on="true"] { background: var(--bad); }
.cg-decile.neutral .cg-decile-seg[data-on="true"] { background: var(--fg); }
.cg-decile.none    .cg-decile-seg { background: transparent; box-shadow: inset 0 0 0 1px var(--line-2); }
.cg-decile-val, .cg-decile-none {
  font-size: 11px;
  min-width: 14px;
  text-align: right;
  color: var(--fg-3);
}
.cg-decile-none { color: var(--fg-4); }
.cg-decile.compact .cg-decile-segs { grid-template-columns: repeat(10, 5px); }
.cg-decile.compact .cg-decile-seg { height: 8px; }
/* Segments rise in sequence on mount (~300ms total) so a gauge reads as a
   measurement being taken rather than a bar that was always there. */
@keyframes cg-decile-fill {
  from { transform: scaleY(.15); opacity: .35; }
  to   { transform: scaleY(1);   opacity: 1; }
}
.cg-decile.animate .cg-decile-seg[data-on="true"] {
  animation: cg-decile-fill .3s var(--ease) both;
  animation-delay: calc(var(--seg, 0) * 26ms);
}
@media (prefers-reduced-motion: reduce) {
  .cg-decile-seg { animation: none !important; transition: none; }
}

/* ---------- TreePicker ---------- */
/* A browsable hierarchy: caret, tick box, label, count. Indentation is the
   only structure — no rails, no boxes, in keeping with the editorial set. */
.cg-tree { display: flex; flex-direction: column; }
.cg-tree-empty { padding: 18px; color: var(--fg-3); font-size: 12.5px; }
.cg-tree-row {
  display: grid;
  grid-template-columns: 16px 16px minmax(0, 1fr) max-content;
  align-items: center;
  gap: 8px;
  padding: 5px 8px 5px calc(8px + var(--depth, 0) * 18px);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 13px;
  color: var(--fg);
  transition: background .12s var(--ease), color .12s var(--ease);
  animation: cg-tree-in .26s var(--ease) both;
  animation-delay: calc(var(--i, 0) * 12ms);
}
.cg-tree-row:hover { background: var(--bg-hover); }
.cg-tree-row:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.cg-tree-row[data-selected="true"] { background: var(--bg-3); }
/* A branch nobody is in stays visible and reads zero — absent and empty are
   different facts, and only one of them is true here. */
.cg-tree-row[data-zero="true"] .cg-tree-label,
.cg-tree-row[data-zero="true"] .cg-tree-count { color: var(--fg-4); }
.cg-tree-caret {
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--fg-4);
  transition: transform .18s var(--ease), color .12s var(--ease);
}
.cg-tree-caret[data-open="true"] { transform: rotate(90deg); }
.cg-tree-caret:hover { color: var(--fg); }
.cg-tree-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px;
  border: 1px solid var(--line-strong); border-radius: var(--r-xs);
  color: var(--on-brand); background: transparent;
  transition: background .12s var(--ease), border-color .12s var(--ease);
}
.cg-tree-check[data-on="true"] { background: var(--fg); border-color: var(--fg); color: var(--bg); }
/* Indeterminate: a bar, not a tick — "parts of this branch", which must not
   be confusable with "this branch". */
.cg-tree-check[data-partial="true"]::after {
  content: ""; width: 7px; height: 2px; background: var(--fg-3); border-radius: var(--r-xs);
}
.cg-tree-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cg-tree-count { font-size: 11px; color: var(--fg-3); font-variant-numeric: tabular-nums; }
@keyframes cg-tree-in {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .cg-tree-row { animation: none; transition: none; }
  .cg-tree-caret { transition: none; }
}

/* ---------- HierarchyTree (design-system/components/hierarchy-tree.jsx) ---------- */
/* The TreePicker's rows with a link where the tick box was: caret, name,
   kind, count. The anchor (the page's own node) is bold and unlinked, as on
   the breadcrumb; a "See N more …" row is a link to the parent's own page. */
.cg-tree[data-variant="hierarchy"] .cg-tree-row {
  grid-template-columns: 16px minmax(0, 1fr) max-content max-content;
  cursor: default;
}
.cg-tree[data-variant="hierarchy"] .cg-tree-row[data-tier="organization"],
.cg-tree[data-variant="hierarchy"] .cg-tree-row[data-tier="facility"] { cursor: pointer; }
.cg-tree[data-variant="hierarchy"] .cg-tree-row[data-note="true"],
.cg-tree[data-variant="hierarchy"] .cg-tree-row[data-more="true"] {
  grid-template-columns: 16px minmax(0, 1fr);
  animation: none;
}
.cg-tree[data-variant="hierarchy"] .cg-tree-row[data-note="true"]:hover { background: transparent; }
.cg-tree-name {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--fg); text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  cursor: pointer;
}
.cg-tree-name:hover { color: var(--brand-ink); text-decoration: underline; text-underline-offset: 2px; }
.cg-tree-name:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--r-xs); }
.cg-tree-anchor { font-weight: 600; cursor: default; }
.cg-tree-anchor:hover { color: var(--fg); text-decoration: none; }
.cg-tree-kind {
  font-family: var(--font-mono); font-size: 10.5px; color: var(--fg-4);
  white-space: nowrap;
}
.cg-tree-more {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  font: inherit; font-size: 12px; color: var(--fg-3); text-align: left; cursor: pointer;
}
.cg-tree-more:hover { color: var(--brand-ink); text-decoration: underline; text-underline-offset: 2px; }
.cg-tree-more:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--r-xs); }
.cg-tree-note { font-size: 12px; color: var(--fg-4); }

/* ---------------------------------------------------------------
   Filter controls (<FilterChip/> / <MultiSelect/> / <RangeInput/>
   — design-system/components/filters.jsx)
   --------------------------------------------------------------- */

/* FilterChip — dismissible applied-filter chip. Pill-adjacent but
   interactive: body activates, the nested × dismisses. Chips are one of the
   sanctioned --r-pill exceptions; no hover shadow (shadows = overlays only). */
.cg-filter-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 6px 4px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--fg-2);
  font-size: 12px;
  cursor: pointer;
  user-select: none;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.cg-filter-chip:hover:not([aria-disabled="true"]) {
  background: var(--bg-hover);
  border-color: var(--line-2);
}
.cg-filter-chip[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
.cg-filter-chip-label {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--fg-3);
  white-space: nowrap;
}
.cg-filter-chip-value {
  padding: 1px 7px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--bg-3);
  color: var(--fg);
  font-size: 11.5px;
  white-space: nowrap;
}
.cg-filter-chip-more {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  color: var(--fg-3);
}
/* The × is its own interactive zone with its own hover state. */
.cg-filter-chip-x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  padding: 0; border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--fg-3);
  cursor: pointer;
  flex: 0 0 auto;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.cg-filter-chip-x:hover:not(:disabled) {
  background: color-mix(in oklab, var(--bad) 12%, var(--bg-2));
  color: var(--bad);
}
.cg-filter-chip-x:disabled { cursor: not-allowed; color: var(--fg-4); }

/* MultiSelect — trigger button + absolutely-positioned panel. Panel
   styling follows the in-app dropdown precedent (.cg-region-menu,
   surfaces/web/explore.css): z-index 30 sits above sticky headers and
   under .cg-modal-wrap (220). The panel is a true overlay — it keeps
   its shadow. */
.cg-mselect { position: relative; display: inline-block; }
.cg-mselect-trigger {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 12px;
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.cg-mselect-trigger:hover:not(:disabled) {
  background: var(--bg-hover);
  border-color: var(--line-strong);
}
.cg-mselect-trigger:disabled { opacity: .5; cursor: not-allowed; }
.cg-mselect-summary[data-empty="true"] { color: var(--fg-3); font-weight: 400; }
.cg-mselect-caret { color: var(--fg-3); transition: transform .15s var(--ease); }
.cg-mselect-trigger[aria-expanded="true"] .cg-mselect-caret {
  transform: rotate(180deg);
}
.cg-mselect-panel {
  position: absolute; left: 0; top: calc(100% + 6px);
  min-width: max(100%, 240px);
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  z-index: 30;
  overflow: hidden;
  animation: cg-mselect-in .12s var(--ease);
}
@keyframes cg-mselect-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
.cg-mselect-search {
  width: 100%;
  padding: 10px 12px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 13px;
  outline: none;
}
.cg-mselect-search::placeholder { color: var(--fg-4); }
.cg-mselect-list { max-height: 260px; overflow-y: auto; padding: 6px; }
.cg-mselect-opt {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--fg);
  cursor: pointer;
  user-select: none;
}
.cg-mselect-opt[data-active="true"] { background: var(--bg-hover); }
/* Square check tile; the gold fill is a sanctioned active-state tick. */
.cg-mselect-check {
  width: 16px; height: 16px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-xs);
  background: transparent;
  color: var(--on-brand);
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.cg-mselect-check svg { opacity: 0; }
.cg-mselect-opt[aria-selected="true"] .cg-mselect-check {
  background: var(--brand);
  border-color: var(--brand);
}
.cg-mselect-opt[aria-selected="true"] .cg-mselect-check svg { opacity: 1; }
.cg-mselect-status {
  padding: 9px 10px;
  font-family: var(--font-sans);
  font-size: 12px;
  color: var(--fg-3);
}

/* SingleSelect — MultiSelect's shell with RADIO rows. The two swap in the
   same slot when a filter's selector changes between "equals" and "is one
   of", so they share every structural rule above and differ only in the
   glyph: a round mark says "one of these", a square tick says "any of
   these", and that is the whole signal a reader gets before clicking. */
.cg-sselect-radio { border-radius: 50%; }
.cg-sselect .cg-mselect-opt[aria-selected="true"] .cg-sselect-radio {
  /* A ring, not a filled tile: the dot is an inset shadow so the brand
     colour reads at 8px inside the 16px mark rather than flooding it. */
  background: transparent;
  border-color: var(--brand);
  box-shadow: inset 0 0 0 4px var(--brand);
}
/* The clear row is a way OUT of the choice, not another choice — separated
   from the values so it is not read as one of them. */
.cg-sselect .cg-mselect-opt[data-clear="true"] {
  color: var(--fg-3);
  margin-bottom: 4px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}

/* RangeInput — paired compact numeric fields, en-dash joined, with a
   layout-stable clear ×. Invalid pair (min > max) tints both fields
   with the danger token; the component keeps firing onChange. */
.cg-range { display: inline-flex; align-items: center; gap: 6px; }
.cg-range-field {
  width: 76px;
  padding: 8px 10px;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  outline: none;
  transition: border-color .15s var(--ease), background .15s var(--ease);
  appearance: textfield;
  -moz-appearance: textfield;
}
[data-theme="light"] .cg-range-field { background: var(--bg); }
.cg-range-field::-webkit-outer-spin-button,
.cg-range-field::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.cg-range-field:focus { border-color: var(--fg); }
.cg-range-field::placeholder { color: var(--fg-4); }
.cg-range-field:disabled { opacity: .5; cursor: not-allowed; }
.cg-range-field.cg-range-invalid {
  border-color: color-mix(in oklab, var(--bad) 55%, var(--line));
  background: color-mix(in oklab, var(--bad) 8%, var(--bg-1));
}
.cg-range-field.cg-range-invalid:focus {
  border-color: var(--bad);
}
.cg-range-sep { color: var(--fg-4); font-size: 13px; user-select: none; }
.cg-range-clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px;
  padding: 0; border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--fg-3);
  cursor: pointer;
  flex: 0 0 auto;
  transition: background .15s var(--ease), color .15s var(--ease);
}
.cg-range-clear:hover:not(:disabled) { background: var(--bg-3); color: var(--fg); }
/* Hidden-but-present keeps the row from reflowing as values come and go. */
.cg-range-clear[data-visible="false"] { visibility: hidden; }
.cg-range-clear:disabled { cursor: not-allowed; }

/* Open/close motion honors the OS setting. */
@media (prefers-reduced-motion: reduce) {
  .cg-mselect-panel { animation: none; }
  .cg-mselect-caret { transition: none; }
}

/* ---------------------------------------------------------------
   Lineage breadcrumb (<LineageBreadcrumb/> —
   design-system/components/lineage-breadcrumb.jsx)
   The Organization → Sub-organization → Facility → Person line at the
   top of every node page (G10). Names are editorial serif — this is the
   page's second headline, not a control row; the ↓ trigger and its menu
   are controls and stay sans/mono. The open menu composes the shared
   dropdown panel (.cg-mselect-panel/-list/-opt/-status above) so it is
   the same overlay the filter controls use.
   --------------------------------------------------------------- */
.cg-lineage { margin: 18px 0 2px; min-width: 0; }
.cg-lineage-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: baseline;
  flex-wrap: nowrap;
  gap: 6px 10px;
  min-width: 0;
}
/* One line on a desk; the tiers wrap to a second line on a narrow screen. */
@media (max-width: 700px) {
  .cg-lineage-list { flex-wrap: wrap; }
}
/* An ancestor may give up space; the ANCHOR never does. That one pair of
   flex rules is what keeps the node you are on legible and on the line at
   every width — the component's fitter folds tiers from the root end long
   before this matters, but if it is ever inert (no ResizeObserver, a first
   paint), the line still degrades from the correct end rather than pushing
   the anchor off the right edge. */
.cg-lineage-level {
  display: inline-flex; align-items: baseline; gap: 6px;
  min-width: 0;
  flex: 0 1 auto;
}
.cg-lineage-level[data-anchor="true"] { flex: 0 0 auto; }
.cg-lineage-name {
  font-family: var(--font-serif);
  font-size: 22px;
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.005em;
  color: var(--fg-2);
  background: none; border: 0; padding: 0; margin: 0;
  cursor: pointer;
  text-align: left;
  /* No blanket cap: the component measures the line and caps the longest
     remaining ANCESTOR in px, as a last resort before folding it into the
     elision mark. The title attribute carries the whole name. */
  display: inline-block;
  flex: 0 1 auto;
  min-width: 0;
  max-width: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  vertical-align: bottom;
  text-decoration: none;
  transition: color .15s var(--ease);
}
button.cg-lineage-name:hover {
  color: var(--fg);
  text-decoration: underline;
  text-decoration-color: var(--brand);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}
/* The page's own entity: bold, ink, not a link. */
.cg-lineage-current { color: var(--fg); font-weight: 600; cursor: default; }
.cg-lineage[data-size="lg"] .cg-lineage-name { font-size: 26px; }
/* In the wrap band the fitter stands down (the tiers get a second line), so
   the cap here is the only one — and it still spares the anchor. */
@media (max-width: 700px) {
  .cg-lineage-name { font-size: 19px; max-width: 22ch; }
  .cg-lineage-level[data-anchor="true"] .cg-lineage-name { max-width: 100%; }
  .cg-lineage[data-size="lg"] .cg-lineage-name { font-size: 22px; }
}
.cg-lineage-sep,
.cg-lineage-fold,
.cg-lineage-ellipsis {
  font-family: var(--font-serif);
  font-size: 20px;
  line-height: 1.15;
  color: var(--fg-4);
  user-select: none;
}
/* Neither the separators nor the elision mark ever shrink — they are what
   the line degrades TO. */
.cg-lineage-sep, .cg-lineage-fold { flex: 0 0 auto; }
.cg-lineage-fold { display: inline-flex; align-items: baseline; }
/* The mark is a trigger, not decoration: it is the only way back to a tier
   the fitter took off the line. */
.cg-lineage-ellipsis {
  letter-spacing: .1em;
  background: none; border: 0; margin: 0; padding: 0 2px;
  cursor: pointer;
  transition: color .15s var(--ease);
}
.cg-lineage-ellipsis:hover,
.cg-lineage-ellipsis[aria-expanded="true"] { color: var(--fg); }
/* The ↓ trigger sits beside the name, centred on its x-height. */
.cg-lineage-menu { position: relative; display: inline-flex; align-self: center; }
.cg-lineage-trigger {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg-3);
  cursor: pointer;
  transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}
.cg-lineage-trigger:hover {
  color: var(--fg);
  background: var(--bg-hover);
  border-color: var(--line-strong);
}
.cg-lineage-trigger[aria-expanded="true"] {
  color: var(--brand-text);
  border-color: color-mix(in oklab, var(--brand) 40%, var(--line));
  background: color-mix(in oklab, var(--brand) 8%, var(--bg-2));
}
/* The menu: the shared panel, sized for names + a mono meta column. */
.cg-lineage-panel {
  min-width: 300px;
  max-width: min(440px, 90vw);
}
.cg-lineage-panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-sans);
  font-size: 11px; font-weight: 500;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--fg-3);
}
.cg-lineage-panel-count { font-family: var(--font-mono); letter-spacing: .04em; color: var(--fg-4); }
.cg-lineage-opt {
  width: 100%;
  justify-content: space-between;
  border: 0; background: transparent;
  font-family: var(--font-sans);
  text-align: left;
}
.cg-lineage-opt[aria-current="true"] .cg-lineage-opt-name { font-weight: 600; }
.cg-lineage-opt-name {
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cg-lineage-opt-meta {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: 10.5px; letter-spacing: .04em;
  color: var(--fg-4);
  white-space: nowrap;
}

/* ---------- HistogramRange ----------
   A distribution with dual handles over it (kepler.gl's time-range control
   is the reference). Bars are equal-width — one per bin — so the track is
   linear in bin INDEX even when the values behind it are not; see the
   component doc in filters.jsx. Interaction geometry (11px hit areas,
   floating bound bubbles) is deliberately unchanged by the editorial
   restyle — only colors/borders/radii moved. */
.cg-hrange { width: 100%; padding-top: 22px; }
.cg-hrange[data-disabled="true"] { opacity: .5; pointer-events: none; }

.cg-hrange-track {
  position: relative;
  height: 46px;
  border-radius: var(--r-sm);
  background: var(--bg-2);
  border: 1px solid var(--line);
  /* Handles sit on the edges and their bubbles rise above the track. */
  overflow: visible;
  touch-action: none;
}

/* Bars grow from the baseline; the row is inset by a pixel so the outermost
   bars don't sit on the border. */
.cg-hrange-bars {
  position: absolute;
  inset: 1px 1px 1px 1px;
  display: flex;
  align-items: flex-end;
  gap: 1px;
  border-radius: inherit;
  overflow: hidden;
}
.cg-hrange-bar {
  flex: 1 1 0;
  min-width: 0;
  border-radius: 0;
  background: var(--fg-4);
  opacity: .35;
  transition: background .12s var(--ease), opacity .12s var(--ease);
}
/* In-range bars take the brand; out-of-range stay as ghosted context rather
   than disappearing, so the shape of the whole distribution is always read. */
.cg-hrange-bar[data-in="true"] { background: var(--brand); opacity: .85; }
/* At rest — the whole axis selected, i.e. no filter — nothing is "chosen",
   so the bars are ink at mid strength and the selection band disappears.
   Gold means a range is actually applied, the same rule <BandPicker/>
   follows. */
.cg-hrange[data-full="true"] .cg-hrange-bar[data-in="true"] {
  background: var(--fg-3);
  opacity: .5;
}
.cg-hrange[data-full="true"] .cg-hrange-sel {
  background: transparent;
  border-color: transparent;
}

.cg-hrange-sel {
  position: absolute;
  top: 0; bottom: 0;
  border-radius: var(--r-xs);
  background: color-mix(in oklab, var(--brand) 12%, transparent);
  border-left: 1px solid color-mix(in oklab, var(--brand) 55%, transparent);
  border-right: 1px solid color-mix(in oklab, var(--brand) 55%, transparent);
  pointer-events: none;
}

.cg-hrange-handle {
  position: absolute;
  top: -3px; bottom: -3px;
  width: 11px;
  margin-left: -5.5px;
  cursor: ew-resize;
  touch-action: none;
}
/* The grab bar itself — drawn as a child box so the hit area stays a
   comfortable 11px while the visual stays a crisp, square 3px. The 1px
   --bg rim is contrast separation from the bars, not a glow. */
.cg-hrange-handle::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 0; bottom: 0;
  width: 3px;
  border-radius: var(--r-xs);
  background: var(--brand);
  box-shadow: 0 0 0 1px color-mix(in oklab, var(--bg) 70%, transparent);
}
.cg-hrange-handle:focus-visible { outline: none; }
.cg-hrange-handle:focus-visible::after {
  box-shadow: 0 0 0 2px var(--brand), 0 0 0 4px color-mix(in oklab, var(--bg) 70%, transparent);
}

/* Floating bound label above its handle. */
.cg-hrange-bubble {
  position: absolute;
  bottom: calc(100% + 4px);
  left: 50%;
  transform: translateX(-50%);
  padding: 1px 6px;
  border-radius: var(--r-xs);
  background: var(--bg-3);
  border: 1px solid var(--line);
  color: var(--fg-2);
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1.6;
  white-space: nowrap;
  pointer-events: none;
}

/* Compact: rides alongside the collapsed filter chip, so it loses the
   bubbles and shrinks to chip height. */
.cg-hrange-compact { width: 108px; padding-top: 0; }
.cg-hrange-compact .cg-hrange-track { height: 22px; border-radius: var(--r-xs); }
.cg-hrange-compact .cg-hrange-handle { width: 9px; margin-left: -4.5px; }
.cg-hrange-compact .cg-hrange-handle::after { left: 3px; width: 2px; }

@media (prefers-reduced-motion: reduce) {
  .cg-hrange-bar { transition: none; }
}

/* ---------------------------------------------------------------
   <ToggleChips/> — a wrap of pill toggles (design-system/components/
   filters.jsx). Pressed = ink-inverse fill, the button voice; the check
   tile carries the gold tick the MultiSelect rows use, so a chosen chip
   and a chosen row read as the same state.
   --------------------------------------------------------------- */
.cg-tchips { display: flex; flex-wrap: wrap; gap: 6px; }
.cg-tchip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px 5px 7px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--fg-2);
  font-family: var(--font-sans);
  font-size: 12.5px;
  line-height: 1.2;
  cursor: pointer;
  user-select: none;
  transition: background .15s var(--ease), border-color .15s var(--ease),
              color .15s var(--ease);
}
.cg-tchip:hover:not(:disabled) { background: var(--bg-hover); border-color: var(--line-2); color: var(--fg); }
.cg-tchip:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.cg-tchip:disabled { opacity: .5; cursor: not-allowed; }
.cg-tchip-check {
  width: 14px; height: 14px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  color: var(--on-brand);
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.cg-tchip-check svg { width: 8px; height: 8px; opacity: 0; }
.cg-tchip[aria-pressed="true"] {
  border-color: var(--fg);
  color: var(--fg);
  background: color-mix(in oklab, var(--fg) 7%, transparent);
}
.cg-tchip[aria-pressed="true"] .cg-tchip-check { background: var(--brand); border-color: var(--brand); }
.cg-tchip[aria-pressed="true"] .cg-tchip-check svg { opacity: 1; }
.cg-tchip-count {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--fg-3);
}

/* ---------------------------------------------------------------
   <StateTileMap/> — equal-tile US grid. 11 columns × 8 rows; each tile a
   square button. With nothing chosen every tile is quiet; once a state is
   chosen the rest recede so the selection reads as a shape.
   --------------------------------------------------------------- */
.cg-statemap {
  display: grid;
  grid-template-columns: repeat(11, minmax(0, 1fr));
  grid-template-rows: repeat(8, auto);
  gap: 3px;
  max-width: 440px;
}
.cg-statemap-tile {
  aspect-ratio: 1 / 1;
  min-width: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--bg-2);
  color: var(--fg-3);
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: .02em;
  cursor: pointer;
  transition: background .12s var(--ease), color .12s var(--ease),
              border-color .12s var(--ease);
}
.cg-statemap-tile:hover:not(:disabled) { border-color: var(--line-strong); color: var(--fg); }
.cg-statemap-tile:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.cg-statemap-tile:disabled { opacity: .5; cursor: not-allowed; }
.cg-statemap[data-any="true"] .cg-statemap-tile { color: var(--fg-4); }
.cg-statemap .cg-statemap-tile[aria-pressed="true"] {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
}

/* ---------------------------------------------------------------
   <BandPicker/> — selectable bars. Columns share the row equally; a bar
   grows from the well's floor. At rest every bar is ink at mid strength;
   with a selection, chosen bars take the brand and the rest ghost back —
   the same in/out language as <HistogramRange/>.
   --------------------------------------------------------------- */
.cg-bandpick {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 4px;
}
.cg-bandpick-band {
  display: flex; flex-direction: column; align-items: stretch; gap: 4px;
  min-width: 0;
  padding: 4px 2px 6px;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--fg-2);
  cursor: pointer;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.cg-bandpick-band:hover:not(:disabled) { background: var(--bg-hover); }
.cg-bandpick-band:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.cg-bandpick-band:disabled { opacity: .5; cursor: not-allowed; }
.cg-bandpick-band[aria-pressed="true"] {
  border-color: color-mix(in oklab, var(--brand) 55%, transparent);
  background: color-mix(in oklab, var(--brand) 9%, transparent);
}
.cg-bandpick-count {
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--fg-3);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cg-bandpick-well {
  position: relative;
  height: 54px;
  border-bottom: 1px solid var(--line-2);
}
.cg-bandpick-bar {
  position: absolute;
  left: 18%; right: 18%; bottom: 0;
  border-radius: var(--r-xs) var(--r-xs) 0 0;
  background: var(--fg-3);
  opacity: .55;
  transition: height .25s var(--ease), background .15s var(--ease),
              opacity .15s var(--ease);
}
.cg-bandpick[data-any="true"] .cg-bandpick-bar { opacity: .25; }
.cg-bandpick .cg-bandpick-band[aria-pressed="true"] .cg-bandpick-bar {
  background: var(--brand);
  opacity: .9;
}
.cg-bandpick-label {
  font-family: var(--font-sans);
  font-size: 10.5px;
  line-height: 1.25;
  color: var(--fg-2);
  text-align: center;
}
.cg-bandpick[data-loading="true"] .cg-bandpick-count { color: var(--fg-4); }

/* ---------------------------------------------------------------
   <CompositionPicker/> — a stacked reference bar over a legend. Segments
   alternate four neutral steps so neighbours separate without a
   categorical palette; the chosen one takes the brand everywhere it
   appears (segment and swatch).
   --------------------------------------------------------------- */
.cg-comppick { display: flex; flex-direction: column; gap: 8px; }
.cg-comppick-bar {
  display: flex;
  height: 12px;
  gap: 2px;
  border-radius: var(--r-xs);
  overflow: hidden;
}
.cg-comppick-seg {
  flex: 1 1 0;
  min-width: 3px;
  cursor: pointer;
  transition: background .15s var(--ease), opacity .15s var(--ease);
}
.cg-comppick-seg[data-step="0"],
.cg-comppick-opt[data-step="0"] .cg-comppick-swatch { background: var(--fg-3); }
.cg-comppick-seg[data-step="1"],
.cg-comppick-opt[data-step="1"] .cg-comppick-swatch { background: var(--line-strong); }
.cg-comppick-seg[data-step="2"],
.cg-comppick-opt[data-step="2"] .cg-comppick-swatch { background: var(--fg-4); }
.cg-comppick-seg[data-step="3"],
.cg-comppick-opt[data-step="3"] .cg-comppick-swatch { background: var(--line-2); }
.cg-comppick[data-chosen="true"] .cg-comppick-seg { opacity: .35; }
.cg-comppick .cg-comppick-seg[data-on="true"] { background: var(--brand); opacity: 1; }
.cg-comppick-legend { display: flex; flex-wrap: wrap; gap: 4px 6px; }
.cg-comppick-opt {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px 3px 6px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--fg-2);
  font-family: var(--font-sans);
  font-size: 12px;
  cursor: pointer;
  transition: background .15s var(--ease), border-color .15s var(--ease);
}
.cg-comppick-opt:hover:not(:disabled) { background: var(--bg-hover); }
.cg-comppick-opt:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.cg-comppick-opt:disabled { opacity: .5; cursor: not-allowed; }
.cg-comppick-opt[aria-pressed="true"] { border-color: var(--line-strong); color: var(--fg); }
.cg-comppick-swatch {
  width: 9px; height: 9px;
  border-radius: var(--r-xs);
  flex: 0 0 auto;
}
.cg-comppick .cg-comppick-opt[aria-pressed="true"] .cg-comppick-swatch { background: var(--brand); }
.cg-comppick-share {
  font-family: var(--font-mono);
  font-size: 10.5px;
  color: var(--fg-3);
}

@media (prefers-reduced-motion: reduce) {
  .cg-tchip, .cg-tchip-check, .cg-statemap-tile, .cg-bandpick-band,
  .cg-bandpick-bar, .cg-comppick-seg, .cg-comppick-opt { transition: none; }
}

/* ---------------------------------------------------------------
   <StepSlider/> — discrete slider over a small integer range
   (design-system/components/slider.jsx)

   A native <input type="range"> painted over a drawn track. The input
   is transparent and sits on top so it keeps every keyboard, touch and
   assistive-technology behaviour the platform already gives a slider;
   only the thumb is styled. Never replace it with a div — the
   affordance does not survive the swap.
   --------------------------------------------------------------- */
.cg-stepslider { display: flex; flex-direction: column; gap: 7px; }
.cg-stepslider-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
}
.cg-stepslider-label { font-size: 11px; color: var(--fg-3); }
.cg-stepslider-value { font-size: 12px; color: var(--fg); }
.cg-stepslider-track { position: relative; height: 18px; display: flex; align-items: center; }
.cg-stepslider-track::before {
  content: ""; position: absolute; left: 0; right: 0; height: 2px;
  border-radius: var(--r-xs); background: var(--line);
}
.cg-stepslider-fill {
  position: absolute; left: 0; height: 2px; width: var(--pct, 0%);
  border-radius: var(--r-xs); background: var(--brand);
  transition: width .22s var(--ease);
}
.cg-stepslider-ticks {
  position: absolute; left: 0; right: 0;
  display: flex; justify-content: space-between; align-items: center;
  pointer-events: none;
}
.cg-stepslider-tick {
  width: 2px; height: 6px; border-radius: var(--r-xs);
  background: var(--line-2);
  transition: background .22s var(--ease), height .22s var(--ease);
}
.cg-stepslider-tick[data-on="true"] { background: var(--brand); height: 8px; }

/* The control itself: transparent, full-width, on top of the drawing. */
.cg-stepslider-input {
  position: relative; z-index: 1;
  width: 100%; margin: 0; height: 18px;
  background: transparent; -webkit-appearance: none; appearance: none;
  cursor: pointer;
}
.cg-stepslider-input::-webkit-slider-runnable-track { background: transparent; height: 18px; }
.cg-stepslider-input::-moz-range-track { background: transparent; height: 18px; }
.cg-stepslider-input::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; margin-top: 3px;
  border-radius: var(--r-pill);
  background: var(--bg); border: 2px solid var(--brand);
  transition: transform .16s var(--ease), box-shadow .16s var(--ease);
}
.cg-stepslider-input::-moz-range-thumb {
  width: 13px; height: 13px; box-sizing: border-box;
  border-radius: var(--r-pill);
  background: var(--bg); border: 2px solid var(--brand);
  transition: transform .16s var(--ease), box-shadow .16s var(--ease);
}
.cg-stepslider-input:hover::-webkit-slider-thumb { transform: scale(1.15); }
.cg-stepslider-input:hover::-moz-range-thumb { transform: scale(1.15); }
.cg-stepslider-input:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 3px var(--brand-glow);
}
.cg-stepslider-input:focus-visible::-moz-range-thumb {
  box-shadow: 0 0 0 3px var(--brand-glow);
}
/* The input carries the focus ring on its thumb, so suppress the default
   outline on the element box, which would draw a rectangle the width of
   the whole track. */
.cg-stepslider-input:focus { outline: none; }
.cg-stepslider-ends {
  display: flex; justify-content: space-between;
  font-size: 10px; color: var(--fg-4);
}
.cg-stepslider.disabled { opacity: .5; }
.cg-stepslider.disabled .cg-stepslider-input { cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) {
  .cg-stepslider-fill, .cg-stepslider-tick,
  .cg-stepslider-input::-webkit-slider-thumb,
  .cg-stepslider-input::-moz-range-thumb { transition: none; }
}

/* ---------- ActivityGrid ----------
   A commit-style calendar heat grid: seven rows, one column per week, the
   trailing year read left to right.

   THE GRID IS FLUID, NOT FIXED-CELL. Columns are
   minmax(--cg-activity-cell-min, 1fr) so 53 weeks divide whatever width the
   page gives them — that is what lets the chart span a full page rather
   than sit as a fixed 600px block in a 1600px shell. Below the width those
   minimums need, .cg-activity-cal scrolls sideways instead of squeezing the
   cells into invisibility.

   The month row and the day grid are two grids with the SAME column
   template and gap, so a month label sits over its own week without any
   measurement in JS. --cg-activity-weeks (set inline by the component) is
   what keeps them in step. */
.cg-activity {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  --cg-activity-gap: 3px;
  --cg-activity-cols:
    repeat(var(--cg-activity-weeks, 53), minmax(var(--cg-activity-cell-min, 9px), 1fr));
}
/* Wraps rather than breaking at a viewport width: the readout's natural
   width is its full text, so flex drops it to its own line exactly when the
   caption and it stop fitting SIDE BY SIDE — which depends on the grid's
   container, not on how wide the window happens to be. */
.cg-activity-head {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: 2px 16px; min-height: 18px;
}
.cg-activity-caption { flex: 0 1 auto; font-size: 12px; color: var(--fg-3); }
/* The readout is a live region that rewrites in place on hover/focus, so it
   must not reflow the chart under the pointer as it changes width. */
.cg-activity-readout {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--fg-2);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cg-activity-readout-hint { color: var(--fg-4); font-family: var(--font-sans); }

.cg-activity-plot { display: flex; gap: 8px; align-items: flex-start; }
.cg-activity-weekdays {
  display: grid;
  grid-template-rows: repeat(7, 1fr);
  gap: var(--cg-activity-gap);
  /* Clears the month row above so the weekday labels line up with row 1. */
  margin-top: calc(1em + 4px);
  font-size: 9.5px;
  line-height: 1;
  color: var(--fg-4);
  text-align: right;
  min-width: 22px;
}
.cg-activity-weekdays > span { display: flex; align-items: center; justify-content: flex-end; }

.cg-activity-cal { flex: 1; min-width: 0; overflow-x: auto; overflow-y: hidden; }
.cg-activity-months {
  display: grid;
  grid-template-columns: var(--cg-activity-cols);
  gap: var(--cg-activity-gap);
  margin-bottom: 4px;
  font-size: 9.5px; line-height: 1; color: var(--fg-4);
  white-space: nowrap;
}
.cg-activity-grid {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(7, auto);
  grid-template-columns: var(--cg-activity-cols);
  gap: var(--cg-activity-gap);
}
.cg-activity-cell {
  aspect-ratio: 1;
  border-radius: var(--r-sm);
  background: var(--activity-0);
  /* An inset hairline rather than a border: it keeps every cell exactly one
     grid track wide, so the columns stay aligned with the month row. */
  box-shadow: inset 0 0 0 1px rgba(128, 128, 128, .12);
  transition: outline-color .12s var(--ease);
  outline: 2px solid transparent;
  outline-offset: 1px;
}
.cg-activity-cell[data-level="1"] { background: var(--activity-1); }
.cg-activity-cell[data-level="2"] { background: var(--activity-2); }
.cg-activity-cell[data-level="3"] { background: var(--activity-3); }
.cg-activity-cell[data-level="4"] { background: var(--activity-4); }
/* Padding cells complete the first and last weeks; they are not days and
   must not read as quiet ones. */
.cg-activity-cell[data-level="pad"] { background: transparent; box-shadow: none; }
.cg-activity-grid .cg-activity-cell:hover { outline-color: var(--fg-3); }
.cg-activity-grid .cg-activity-cell:focus-visible { outline-color: var(--brand); }
/* Only a day that leads somewhere takes the pointer — a cell that opened an
   empty view would teach the reader the whole grid is not worth clicking. */
.cg-activity-cell[data-selectable="true"] { cursor: pointer; }
/* The day currently drilled into. A full-strength ring rather than a fill:
   the fill is the DATA, and overriding it would misreport the day's volume. */
.cg-activity-cell[data-selected="true"] { outline-color: var(--brand); }

.cg-activity-legend {
  display: flex; align-items: center; justify-content: flex-end;
  font-size: 10px; color: var(--fg-4);
}
.cg-activity-legend-scale { display: inline-flex; align-items: center; gap: 4px; }
.cg-activity-legend-scale .cg-activity-cell { width: 10px; aspect-ratio: 1; }

.cg-activity-skeleton {
  height: 112px;
  border-radius: var(--r-md);
  background: var(--activity-0);
  box-shadow: inset 0 0 0 1px var(--line);
}
.cg-activity-empty { font-size: 11.5px; color: var(--fg-4); }
@media (prefers-reduced-motion: reduce) {
  .cg-activity-cell { transition: none; }
}

/* ---------- Focus ----------
   One visible keyboard-focus treatment across the system: the 2px gold
   ring (contract rule 7). The HistogramRange handle keeps its own
   box-shadow ring — its hit area is an invisible 11px strip, and the
   StepSlider thumb does the same, because an outline on its input box
   would draw a rectangle the full width of the track. */
.cg-btn:focus-visible,
.cg-input:focus-visible,
.cg-check:focus-visible,
.cg-switch:focus-visible,
.cg-tab:focus-visible,
.cg-logo:focus-visible,
.cg-filter-chip:focus-visible,
.cg-filter-chip-x:focus-visible,
.cg-mselect-trigger:focus-visible,
.cg-mselect-search:focus-visible,
.cg-range-field:focus-visible,
.cg-range-clear:focus-visible,
.cg-social-link:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

/* ---- <RampLegend> — the health-concern burden key (legend.jsx) ----
   The bar is the --heat-* gradient; ticks and words wear text tokens,
   never the ramp colours. The hatch marks an ESTIMATED place — the same
   45° stripe the heatmaps draw over estimated shapes. */
.cg-ramp-legend {
  display: inline-grid;
  grid-template-columns: auto minmax(120px, 1fr);
  align-items: center;
  column-gap: 10px;
  row-gap: 2px;
  font-size: 11px;
  color: var(--fg-3);
}
.cg-ramp-legend__label { color: var(--fg-2); font-weight: 500; }
.cg-ramp-legend__scale { display: flex; flex-direction: column; gap: 3px; min-width: 120px; }
.cg-ramp-legend__bar {
  display: block;
  height: 8px;
  border-radius: var(--r-sm);
  box-shadow: inset 0 0 0 1px var(--line);
}
.cg-ramp-legend__ticks,
.cg-ramp-legend__ends {
  display: flex;
  justify-content: space-between;
  font-variant-numeric: tabular-nums;
  color: var(--fg-4);
}
.cg-ramp-legend__ends { grid-column: 2; }
.cg-ramp-legend__est {
  grid-column: 1 / -1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--fg-3);
}
.cg-ramp-legend__hatch,
.cg-hatch {
  background-image: repeating-linear-gradient(
    45deg, var(--fg-3) 0, var(--fg-3) 1px, transparent 1px, transparent 4px);
}
.cg-ramp-legend__hatch {
  display: inline-block;
  width: 14px;
  height: 10px;
  border-radius: var(--r-xs);
  box-shadow: inset 0 0 0 1px var(--line);
}
/* COMPACT STACKS: the title sits ABOVE the scale, never beside it. Side by
   side, the `auto` label track sized to the title's max-content, and the
   scale's 120px floor then pushed the bar and its tick labels past the right
   edge of a fixed-width host (a map key ~260px wide) — WebKit, not Blink, is
   where it showed, and only with a title that wraps ("Strangles (equine)
   burden score"). One `minmax(0, 1fr)` column is the same in every engine:
   the title wraps inside the host and the scale is exactly the host's width. */
.cg-ramp-legend--compact {
  grid-template-columns: minmax(0, 1fr);
  row-gap: 4px;
  font-size: 10.5px;
}
.cg-ramp-legend--compact .cg-ramp-legend__scale { min-width: 0; }

/* ---- <ChoroplethMap> — pre-projected SVG heatmap (choropleth.jsx) ----
   Fill is set inline per shape (GantryUI.heatCss), so the CSS here only
   owns structure, hover/selection and the hatch pattern's stroke colour —
   never a second colour rule for the ramp itself. */
.cg-choropleth { display: block; width: 100%; height: auto; }
.cg-choropleth__outline {
  fill: var(--bg-2);
  stroke: var(--line);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.cg-choropleth__shape {
  stroke: var(--bg);
  stroke-width: 0.4;
  cursor: pointer;
  transition: opacity 100ms ease;
}
.cg-choropleth__shape:hover { opacity: 0.92; }
/* No reading is a stated absence, not a zero — the same neutral fill as
   RampLegend's "unmeasured", never a colour off the ramp. */
.cg-choropleth__shape[data-known="false"] {
  stroke: var(--line);
  stroke-width: 0.3;
}
.cg-choropleth__borders {
  fill: none;
  stroke: var(--bg);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
}
/* Drawn as its own top path (never a restyle of the hovered/selected shape
   in place) so the highlight is never hidden under the borders or hatch
   layers painted after the base fills. */
.cg-choropleth__highlight {
  fill: none;
  stroke: var(--fg);
  stroke-width: 1.6;
  vector-effect: non-scaling-stroke;
  pointer-events: none;
}
.cg-choropleth__highlight[data-selected="true"] { stroke-width: 2.2; }
.cg-choropleth__hatch-bg { fill: transparent; }
.cg-choropleth__hatch-line { stroke: var(--fg-3); stroke-width: 1.4; }
