/* AUTO-SYNCED from design-system/components.css — edit there, run make ui-sync */
/* ============================================================
   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); }

/* 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; }

/* 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);
}

/* 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);
}

/* ---- 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;
}

/* ---------------------------------------------------------------
   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);
}

/* 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; }
}

/* ---------- 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; }

.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; }
}

/* ---------- 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. */
.cg-btn:focus-visible,
.cg-input: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;
}
