/* =====================================================================
   Gearshift - the Parts screens and the dashboard

   The component set the reconciliation area and the dashboard are built
   from since the restructure: a page frame with a "Needs attention" rail
   down the right, glance tiles, charts that open into a larger view, the
   ranked exceptions list and the part-category board.

   Prefix `gx-`, so nothing here can collide with the `gs-` rules it sits
   beside, and so a reader can tell at a glance which layer a class is from.

   Rules this file keeps:
     * every colour is a token (tokens.css); tones are the product's five
       meanings — danger, warning, success, info, accent — plus muted;
     * cards are `.gx-card`, which gs-material.css draws as frosted glass;
       everything INSIDE a card stays flat — rows, wells, bars — because
       glass on glass reads as noise;
     * a figure that is incomplete is drawn differently (hatched), never
       the same as a whole one;
     * mobile first where it matters: the rail drops below the content under
       1200px, grids collapse, and nothing is wider than the viewport.
   ===================================================================== */

/* ── the page frame ───────────────────────────────────────────────── */

.gx-page {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--gs-space-4);
  align-items: start;
}
/* The panel beside the content from 992px (PILLAR1_DESIGN.md §5.1a): it
   stacks under the main column below that, never over it. */
@media (min-width: 992px) {
  .gx-page--rail { grid-template-columns: minmax(0, 1fr) 280px; }
}
@media (min-width: 1200px) {
  .gx-page--rail { grid-template-columns: minmax(0, 1fr) 320px; }
}
.gx-main { min-width: 0; display: flex; flex-direction: column;
           gap: var(--gs-space-4); }

/* The head: an eyebrow trail, a big title, and the freshness facts on the
   right — the mock's structure, in this product's type and colour. */
.gx-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--gs-space-4); flex-wrap: wrap;
  /* The header's own gap to what follows, so a button at the foot of the
     header (Categories' "New category") never sits on the first card. */
  margin-bottom: var(--gs-space-5);
}
/* The dashboard's "who, where, when" line belongs to the title above it. */
.gx-head:has(+ .gx-whoami) { margin-bottom: 0; }
.gx-eyebrow {
  margin: 0 0 6px; font-size: var(--gs-fs-xs); font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--gs-muted);
}
.gx-eyebrow a { color: inherit; text-decoration: none; }
.gx-eyebrow a:hover { color: var(--gs-accent); }
.gx-eyebrow span + span::before { content: '·'; margin: 0 .55em;
                                  opacity: .6; }
.gx-title {
  margin: 0; font-size: clamp(1.35rem, 1.1rem + 1vw, 1.8rem);
  font-weight: 800; letter-spacing: -.02em; line-height: 1.1;
}
.gx-title em { font-style: normal; color: var(--gs-accent); }
.gx-sub { margin: 8px 0 0; color: var(--gs-muted); font-size: var(--gs-fs-md);
          max-width: 70ch; }
/* When the screen's figures were last read (`head(checked=…)`): the same
   words in the same place on every Parts tab that reads the nightly
   checks, under the sentence that says what the screen is for. */
.gx-checked { margin: 6px 0 0; color: var(--gs-muted);
              font-size: var(--gs-fs-sm); line-height: 1.45; }
.gx-checked i { font-size: 15px; vertical-align: -2px; margin-right: 6px; }
.gx-meta {
  display: flex; flex-direction: column; align-items: flex-end; gap: 3px;
  font-size: var(--gs-fs-xs); color: var(--gs-muted); text-align: right;
  font-family: var(--gs-font); font-variant-numeric: tabular-nums; letter-spacing: .02em;
}
.gx-meta b { color: var(--gs-ink-2); font-weight: 600; }
.gx-head__actions { display: flex; gap: 10px; flex-wrap: wrap;
                    align-items: center; }
@media (max-width: 767.98px) {
  .gx-meta { align-items: flex-start; text-align: left; }
}

/* ── section tabs ─────────────────────────────────────────────────── */

.gx-tabs {
  display: flex; flex-wrap: nowrap; gap: 4px; padding: 4px; overflow-x: auto;
  scrollbar-width: none; max-width: 100%;
  width: max-content;
}
.gx-tabs::-webkit-scrollbar { display: none; }
.gx-tab {
  display: inline-flex; align-items: center; gap: 7px; flex: none;
  padding: 7px 12px; border-radius: 10px; white-space: nowrap;
  font-size: var(--gs-fs-sm); font-weight: 700; color: var(--gs-ink-2);
  text-decoration: none; transition: background var(--gs-dur) var(--gs-ease),
                                     color var(--gs-dur) var(--gs-ease);
}
.gx-tab:hover { background: var(--gs-accent-wash); color: var(--gs-ink); }
.gx-tab.is-active { background: var(--gs-accent-wash); color: var(--gs-accent); }
.gx-tab i { font-size: 16px; }

/* ── the card ─────────────────────────────────────────────────────── */

.gx-card { position: relative; min-width: 0; overflow: hidden; }
.gx-card__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--gs-space-3); padding: 14px 16px 0;
}
.gx-card__kicker {
  margin: 0 0 4px; font-size: var(--gs-fs-xs); font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--gs-muted);
}
.gx-card__title { margin: 0; font-size: var(--gs-fs-base); font-weight: 800;
                  letter-spacing: -.01em; }
.gx-card__sub { margin: 4px 0 0; font-size: var(--gs-fs-sm);
                color: var(--gs-muted); line-height: 1.45; max-width: 80ch; }
.gx-card__body { padding: 12px 16px 16px; }
.gx-card__body--flush { padding: 8px 0 0; }
.gx-card__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap;
  padding: 10px 16px; border-top: 1px solid var(--gs-card-line);
  font-size: var(--gs-fs-sm); color: var(--gs-muted);
}
.gx-card__foot a { color: var(--gs-accent); font-weight: 700;
                   text-decoration: none; }
.gx-card__foot a:hover { text-decoration: underline; }

/* The expand control on a chart card: a small round glass button. */
.gx-expand {
  flex: none; width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: var(--gs-inset); border: 1px solid var(--gs-inset-line);
  color: var(--gs-ink-2); font-size: 15px;
  transition: color var(--gs-dur) var(--gs-ease),
              border-color var(--gs-dur) var(--gs-ease);
}
.gx-expand:hover { color: var(--gs-accent); border-color: var(--gs-accent-line); }

.gx-grid { display: grid; gap: var(--gs-space-3); }
.gx-grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }
.gx-grid--3 { grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); }
.gx-grid--wide { grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); }
@media (max-width: 991.98px) {
  .gx-grid--wide { grid-template-columns: minmax(0, 1fr); }
}

/* ── glance tiles ─────────────────────────────────────────────────── */

.gx-glances {
  display: grid; gap: var(--gs-space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr));
}
/* ONE SET OF ROWS FOR THE WHOLE STRIP. Each tile is a subgrid of five rows
   (label, figure, change, caveat, description) shared with its neighbours,
   so every label, every figure and every description sits on the same line
   across the strip, whatever the tile beside it holds. As a column of its
   own each tile pushed its description to its foot, and a one-line
   description started lower than a two-line one next to it.

   Rows with nothing in them are empty and cost nothing: the spacing is a
   margin on the parts that exist, not a row gap. */
.gx-glance {
  display: grid; grid-row: span 5; grid-template-rows: subgrid; row-gap: 0;
  align-content: start; padding: 13px 16px 14px;
  text-decoration: none; color: inherit; min-height: 0;
  transition: box-shadow var(--gs-dur) var(--gs-ease),
              transform var(--gs-dur) var(--gs-ease);
}
a.gx-glance:hover { box-shadow: var(--gs-card-edge), var(--gs-card-shadow-hover);
                    transform: translateY(-1px); }
.gx-glance__label {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--gs-fs-xs); font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--gs-muted);
}
.gx-glance__label i { font-size: 15px; letter-spacing: 0; }
/* Each part at the TOP of its row: a one-line label beside a two-line one
   would otherwise sit centred in the taller row and read as lower. */
.gx-glance > * { align-self: start; }
.gx-glance > .gx-glance__label { grid-row: 1; }
.gx-glance > .gx-glance__value { grid-row: 2; margin-top: 6px; }
.gx-glance > :has(> .gx-delta) { grid-row: 3; margin-top: 6px; }
.gx-glance > .gx-caveat { grid-row: 4; margin-top: 6px; }
.gx-glance > .gx-glance__sub { grid-row: 5; margin-top: 6px; }
.gx-glance__value {
  font-size: clamp(1.35rem, 1.15rem + .6vw, 1.7rem); font-weight: 800;
  letter-spacing: -.03em; line-height: 1.05; color: var(--gs-ink);
  font-variant-numeric: tabular-nums;
}
.gx-glance__value small { font-size: .5em; font-weight: 700;
                          color: var(--gs-muted); margin-left: 2px;
                          letter-spacing: 0; }
.gx-glance__sub { font-size: var(--gs-fs-xs); color: var(--gs-muted);
                  margin-top: auto; line-height: 1.35; }
.gx-glance__sub b { color: var(--gs-ink-2); font-weight: 700; }
/* A tile's FIGURE is never coloured. It was red, amber, green or blue by a
   `tone` on some screens and ink on others, so the same kind of number
   changed colour from tab to tab, and a red total on a landing page reads
   as the product being broken. The words carry the state: the label, the
   sentence, and a caveat when a total leaves something out. */
.gx-glance--muted .gx-glance__value { color: var(--gs-muted); }

/* The hero tile is the one figure the page exists for, but it is drawn as a
   card like the rest: a tinted, denser tile beside three plain ones read as
   an inconsistency, not as emphasis. It keeps its place (full width on a
   phone, below) and leads by position. */

/* A card a figure tile links to. The top bar is sticky, so a plain jump put
   the card's head underneath it; this lands it just below the bar. */
.gx-jump {
  scroll-margin-top: calc(var(--gs-topbar-h) + var(--gs-topbar-gap) + 16px);
  border-radius: var(--gs-card-radius);
}
/* The card a tile sent you to POPS OUT for a moment (app.js,
   initJumpTarget): it lifts a few pixels, grows a touch, takes the deeper
   hover shadow and a soft accent glow, then settles back - the cards' own
   hover, held a beat longer and lit. No ring round the border (the
   client's call), and eased both ways, so nothing switches on in one
   frame. */
.gx-jump.is-jumped {
  position: relative; z-index: 2;
  animation: gx-jumped 1.6s ease-in-out both;
}
/* The card pops up a little and its background takes a soft teal wash:
   a tint INSIDE the card that fades in and out, no glow round its edge. */
@keyframes gx-jumped {
  30%, 60% {
    transform: translateY(-4px) scale(1.015);
    box-shadow: var(--gs-card-edge), var(--gs-card-shadow-hover);
  }
}
.gx-jump.is-jumped::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; z-index: 0;
  background:
    radial-gradient(120% 90% at 0% 0%,
                    color-mix(in srgb, var(--gs-accent) 24%, transparent) 0%,
                    transparent 70%),
    color-mix(in srgb, var(--gs-accent) 9%, transparent);
  animation: gx-jumped-wash 2.8s ease-in-out both;
}
@keyframes gx-jumped-wash {
  0%   { opacity: 0; }
  18%, 62% { opacity: 1; }
  100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .gx-jump.is-jumped { animation: none; }
  .gx-jump.is-jumped::after { animation: gx-jumped-wash 2.8s linear both; }
}

.gx-delta {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 2px 8px; border-radius: 999px; font-size: var(--gs-fs-xs);
  font-weight: 700; font-family: var(--gs-font); font-variant-numeric: tabular-nums;
  background: var(--gs-inset); color: var(--gs-ink-2);
}
.gx-delta--up   { background: var(--gs-danger-wash);  color: var(--gs-danger); }
.gx-delta--down { background: var(--gs-success-wash); color: var(--gs-success); }

/* The caveat that travels with an incomplete figure. Same line as the
   figure's sub-text, in the warning colour, never a footnote. */
.gx-caveat {
  display: flex; align-items: flex-start; gap: 6px;
  font-size: var(--gs-fs-xs); color: var(--gs-warning); font-weight: 700;
}
.gx-caveat i { font-size: 14px; margin-top: 1px; }

/* ── the insight line ─────────────────────────────────────────────── */

.gx-insight {
  display: flex; align-items: center; gap: 12px; padding: 11px 16px;
}
.gx-insight__icon {
  width: 30px; height: 30px; border-radius: 10px; flex: none;
  display: grid; place-items: center; font-size: 16px;
  background: var(--gs-accent-wash); color: var(--gs-accent);
}
.gx-insight--risk .gx-insight__icon { background: var(--gs-danger-wash);
                                      color: var(--gs-danger); }
.gx-insight--good .gx-insight__icon { background: var(--gs-success-wash);
                                      color: var(--gs-success); }
/* Information, not an alarm: "being re-checked", "nothing yet". */
.gx-insight--info .gx-insight__icon { background: var(--gs-info-wash);
                                      color: var(--gs-info); }

/* The dashboard's branch filter — a <details> dropdown of checkboxes. */
.gx-branchfilter { position: relative; margin: 0; }
.gx-branchfilter__pop > summary { list-style: none; cursor: pointer; }
.gx-branchfilter__pop > summary::-webkit-details-marker { display: none; }
.gx-branchfilter__menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40;
  width: min(300px, calc(100vw - 32px)); padding: 12px 14px;
}
.gx-branchfilter__list { display: flex; flex-direction: column; gap: 2px;
                         max-height: 260px; overflow-y: auto; margin: 6px 0 10px; }
.gx-branchfilter__opt { display: flex; align-items: center; gap: 8px;
                        padding: 6px 4px; border-radius: 8px; cursor: pointer;
                        font-size: var(--gs-fs-sm); }
.gx-branchfilter__opt:hover { background: var(--gs-accent-wash); }
.gx-branchfilter__foot { display: flex; gap: 8px; }
.gx-insight__text { margin: 0; font-size: var(--gs-fs-md); line-height: 1.45;
                    color: var(--gs-ink); }
.gx-insight__text b { color: var(--gs-accent); }
.gx-insight--risk .gx-insight__text b { color: var(--gs-danger); }
.gx-insight__src { margin-left: auto; flex: none; font-size: var(--gs-fs-xs);
                   color: var(--gs-muted); font-family: var(--gs-font); font-variant-numeric: tabular-nums; }
@media (max-width: 767.98px) {
  .gx-insight { align-items: flex-start; flex-wrap: wrap; }
  .gx-insight__src { margin-left: 52px; }
}

/* ── tones ────────────────────────────────────────────────────────
   A tone is two custom properties any element can pick up. Legend keys
   are small SQUARES in a chart's key — the key of a chart, not a status
   dot beside a label. */

.gx-dotmark { width: 10px; height: 10px; border-radius: 3px; flex: none;
              display: inline-block; background: var(--gs-muted); }
.gx-tone--danger  { --gx-tone: var(--gs-danger);  --gx-wash: var(--gs-danger-wash); }
.gx-tone--warning { --gx-tone: var(--gs-warning); --gx-wash: var(--gs-warning-wash); }
.gx-tone--success { --gx-tone: var(--gs-success); --gx-wash: var(--gs-success-wash); }
.gx-tone--info    { --gx-tone: var(--gs-info);    --gx-wash: var(--gs-info-wash); }
.gx-tone--accent  { --gx-tone: var(--gs-accent);  --gx-wash: var(--gs-accent-wash); }
.gx-tone--muted   { --gx-tone: var(--gs-muted);   --gx-wash: var(--gs-inset); }
/* The outcome ring's own two (ReconBucket.CHART): a status colour in dark,
   a colour of their own in light, where the status teal and grey could not
   be told from "Used"'s green. */
.gx-tone--partly  { --gx-tone: var(--gs-ring-partly);  --gx-wash: var(--gs-accent-wash); }
.gx-tone--nomatch { --gx-tone: var(--gs-ring-nomatch); --gx-wash: var(--gs-inset); }
.gx-tone--shop    { --gx-tone: var(--gs-ring-shop);    --gx-wash: var(--gs-inset); }
.gx-dotmark[class*='gx-tone--'] { background: var(--gx-tone); }

.gx-pill {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  padding: 3px 10px; border-radius: 999px; font-size: var(--gs-fs-xs);
  font-weight: 700; background: var(--gx-wash, var(--gs-inset));
  color: var(--gx-tone, var(--gs-ink-2));
}

/* ── charts: weekly bars ──────────────────────────────────────────── */

.gx-bars { display: flex; flex-direction: column; gap: 8px; }
.gx-bars__plot {
  position: relative; display: grid; align-items: end; gap: 6px;
  grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  height: min(var(--gx-h, 180px), var(--gs-bar-h, 140px)); padding-top: 18px;
  background:
    linear-gradient(var(--gs-chart-grid), var(--gs-chart-grid)) 0 25% / 100% 1px no-repeat,
    linear-gradient(var(--gs-chart-grid), var(--gs-chart-grid)) 0 50% / 100% 1px no-repeat,
    linear-gradient(var(--gs-chart-grid), var(--gs-chart-grid)) 0 75% / 100% 1px no-repeat,
    linear-gradient(var(--gs-chart-grid), var(--gs-chart-grid)) 0 100% / 100% 1px no-repeat;
}
.gx-bars__col { position: relative; height: 100%; display: flex;
                align-items: flex-end; justify-content: center; }
.gx-bars__bar {
  width: min(100%, 34px); border-radius: 8px 8px 3px 3px;
  height: var(--v, 0%); min-height: 3px;
  background: linear-gradient(180deg, var(--gs-chart-1) 0%,
                                      var(--gs-chart-3) 100%);
  transition: filter var(--gs-dur) var(--gs-ease);
}
.gx-bars__col:hover .gx-bars__bar { filter: brightness(1.15); }
/* A week that opens its breakdown: the whole column is the button. */
.gx-bars__col--open {
  appearance: none; border: 0; padding: 0; margin: 0; font: inherit;
  background: none; color: inherit; cursor: pointer; border-radius: 8px;
}
.gx-bars__col--open:focus-visible { outline: 2px solid var(--gs-accent);
                                    outline-offset: 2px; }
/* An understated week: hatched, so it cannot be read as a whole figure. */
.gx-bars__bar--partial {
  background:
    repeating-linear-gradient(135deg, transparent 0 5px,
                              var(--gs-chart-track) 5px 9px),
    linear-gradient(180deg, var(--gs-chart-2) 0%, var(--gs-chart-4) 100%);
  outline: 1px dashed var(--gs-warning); outline-offset: -1px;
}
.gx-bars__bar--last { background: linear-gradient(180deg, var(--gs-accent) 0%,
                                                  var(--gs-chart-2) 100%); }
/* No figure that week: a gap, drawn AS a gap. */
.gx-bars__gap {
  width: min(100%, 34px); height: 100%; border-radius: 8px;
  border: 1px dashed var(--gs-chart-grid);
}
.gx-bars__tip {
  position: absolute; bottom: calc(var(--v, 0%) + 6px); left: 50%;
  transform: translateX(-50%); white-space: nowrap;
  font-size: var(--gs-fs-xs); font-weight: 700; font-family: var(--gs-font);
  font-variant-numeric: tabular-nums; color: var(--gs-muted);
  opacity: 0; transition: opacity var(--gs-dur) var(--gs-ease);
  pointer-events: none;
}
.gx-bars__col:hover .gx-bars__tip,
.gx-bars__col.is-last .gx-bars__tip { opacity: 1; color: var(--gs-ink-2); }
.gx-bars__axis {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: 6px; font-size: var(--gs-fs-xs); color: var(--gs-muted);
  font-family: var(--gs-font); font-variant-numeric: tabular-nums;
  text-align: center;
}
.gx-bars__axis span { overflow: hidden; text-overflow: clip; white-space: nowrap; }
@media (max-width: 575.98px) {
  /* Twelve labels do not fit a phone: every third, unclipped, is enough to
     read the axis by. */
  .gx-bars__axis span { overflow: visible; visibility: hidden; }
  .gx-bars__axis span:nth-child(3n+1) { visibility: visible; }
  .gx-bars__plot { gap: 3px; }
  .gx-glances { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .gx-glance { min-height: 0; padding: 14px 14px 16px; }
  .gx-glance--hero { grid-column: 1 / -1; }
  .gx-glance__label { letter-spacing: .08em; font-size: 10.5px; }
}
/* The two weekly charts sit side by side (`recon/_trends.html`): the
   auto-match columns are drawn in the weekly bars' own hand — the same
   bar shape and fill, the same dashed gap, the same type on the axis — so
   the pair reads as one set rather than two products' charts. */
.gx-trendcol .gs-col__track { max-width: 34px; }
.gx-trendcol .gs-col__bar { border-radius: 8px 8px 3px 3px;
  background: linear-gradient(180deg, var(--gs-chart-1) 0%,
                                      var(--gs-chart-3) 100%); }
.gx-trendcol .gs-col__bar--lead {
  background: linear-gradient(180deg, var(--gs-accent) 0%,
                                      var(--gs-chart-2) 100%); }
.gx-trendcol .gs-col__gap { border-radius: 8px; border: 1px dashed
                            var(--gs-chart-grid); }
.gx-trendcol .gs-col__tick { border-top: 0; margin-top: 8px; padding-top: 0; }
/* A short card beside a long one keeps its own height rather than being
   stretched into a tall empty panel (the dashboard's suppliers, beside the
   branches needing action). */
.gx-dash-short { align-self: start; }
.gx-legend { display: flex; flex-wrap: wrap; gap: 6px 16px;
             font-size: var(--gs-fs-xs); color: var(--gs-muted); }
.gx-legend span { display: inline-flex; align-items: center; gap: 6px; }
.gx-swatch { width: 12px; height: 12px; border-radius: 4px; flex: none;
             background: var(--gs-chart-1); }
.gx-swatch--partial {
  background: repeating-linear-gradient(135deg, transparent 0 3px,
              var(--gs-chart-track) 3px 5px), var(--gs-chart-3);
  outline: 1px dashed var(--gs-warning); outline-offset: -1px;
}
.gx-swatch--gap { background: none; border: 1px dashed var(--gs-muted); }

/* ── charts: donut ────────────────────────────────────────────────── */

.gx-donut { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.gx-donut__ring { position: relative; width: 128px; height: 128px; flex: none; }
.gx-donut__ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.gx-donut__track { fill: none; stroke: var(--gs-chart-track); }
.gx-donut__seg { fill: none; stroke: var(--gx-tone, var(--gs-muted));
                 stroke-linecap: butt;
                 transition: stroke-width var(--gs-dur) var(--gs-ease); }
.gx-donut__hub {
  position: absolute; inset: 0; display: grid; place-content: center;
  text-align: center; gap: 2px;
}
.gx-donut__hub b { font-size: 1.2rem; font-weight: 800; letter-spacing: -.02em;
                   font-variant-numeric: tabular-nums; }
.gx-donut__hub span { font-size: var(--gs-fs-xs); color: var(--gs-muted);
                     max-width: 64px; line-height: 1.25; text-align: center; }
.gx-donut__keys { display: flex; flex-direction: column; gap: 5px;
                  flex: 1 1 180px; min-width: 0; }
/* Under the donut's key: what one count is, in plain words. */
.gx-donut__note {
  margin: 10px 0 0; font-size: var(--gs-fs-xs); line-height: 1.5;
  color: var(--gs-muted);
}
.gx-donut__note b { color: var(--gs-ink-2); font-weight: 700; }
.gx-key { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto;
          align-items: center; gap: 10px; font-size: var(--gs-fs-xs); }
.gx-key__label { color: var(--gs-ink-2); white-space: nowrap; overflow: hidden;
                 text-overflow: ellipsis; }
.gx-key__n { font-family: var(--gs-font); font-variant-numeric: tabular-nums; color: var(--gs-ink);
             font-size: var(--gs-fs-xs); }
.gx-key__pct { font-family: var(--gs-font); font-variant-numeric: tabular-nums; color: var(--gs-muted);
               font-size: var(--gs-fs-xs); min-width: 42px; text-align: right; }

/* ── charts: horizontal bars ──────────────────────────────────────── */

.gx-hbars { display: flex; flex-direction: column; gap: 8px;
             container: gxhbars / inline-size; }
.gx-hbar { display: grid; grid-template-columns: minmax(96px, 32%) minmax(0, 1fr) auto;
           align-items: center; gap: 12px; font-size: var(--gs-fs-sm);
           color: inherit; text-decoration: none; }
a.gx-hbar:hover .gx-hbar__label { color: var(--gs-accent); }
.gx-hbar__label { overflow: hidden; text-overflow: ellipsis;
                  white-space: nowrap; color: var(--gs-ink-2); font-weight: 600; }
.gx-hbar__track { height: 8px; border-radius: 999px;
                  background: var(--gs-chart-track); overflow: hidden; }
.gx-hbar__fill { display: block; height: 100%; width: var(--v, 0%);
                 border-radius: 999px;
                 background: linear-gradient(90deg, var(--gs-chart-3),
                                                    var(--gs-chart-1)); }
.gx-hbar__fill--late { background: linear-gradient(90deg, var(--gs-warning),
                                                          var(--gs-danger)); }
.gx-hbar__value { font-family: var(--gs-font); font-variant-numeric: tabular-nums; font-size: var(--gs-fs-sm);
                  color: var(--gs-ink); text-align: right; min-width: 64px; }
.gx-hbar__value small { display: block; color: var(--gs-muted);
                        font-size: 10px; }
/* A narrow card (a phone, a half-width card): the label and its figure on
   one line and the bar under them, so no label is cut to "Not on a re…". */
@container gxhbars (max-width: 440px) {
  .gx-hbar { grid-template-columns: minmax(0, 1fr) auto;
             grid-template-areas: "label value" "track track"; row-gap: 4px; }
  .gx-hbar__label { grid-area: label; white-space: normal; }
  .gx-hbar__track { grid-area: track; }
  .gx-hbar__value { grid-area: value; }
  .gx-hbar__value small { display: inline; margin-left: 6px; }
}

/* ── sparkline ────────────────────────────────────────────────────── */

.gx-spark { width: 96px; height: 28px; flex: none; }
.gx-spark polyline { fill: none; stroke: var(--gs-chart-1); stroke-width: 2;
                     stroke-linecap: round; stroke-linejoin: round; }
.gx-spark circle { fill: var(--gs-accent); }
.gx-spark--empty { display: inline-grid; place-items: center; width: 96px;
                   height: 28px; font-size: 10px; color: var(--gs-muted);
                   border: 1px dashed var(--gs-chart-grid); border-radius: 8px; }

/* ── lists inside a card ──────────────────────────────────────────── */

.gx-list { display: flex; flex-direction: column; }
.gx-row {
  display: grid; align-items: center; gap: 14px;
  grid-template-columns: minmax(0, 1fr) auto;
  padding: 10px 16px; border-top: 1px solid var(--gs-card-line);
  color: inherit; text-decoration: none;
  transition: background var(--gs-dur) var(--gs-ease);
}
.gx-row:first-child { border-top: 0; }
a.gx-row:hover, .gx-row--link:hover { background: var(--gs-accent-wash); }
.gx-row__main { min-width: 0; display: flex; align-items: center; gap: 12px; }
.gx-row__title { font-weight: 700; color: var(--gs-ink); white-space: nowrap;
                 overflow: hidden; text-overflow: ellipsis; }
.gx-row__meta { font-size: var(--gs-fs-xs); color: var(--gs-muted);
                font-family: var(--gs-font); font-variant-numeric: tabular-nums; letter-spacing: .02em;
                margin-top: 2px; white-space: nowrap; overflow: hidden;
                text-overflow: ellipsis; }
.gx-row__end { display: flex; align-items: center; gap: 14px;
               justify-content: flex-end; }
.gx-money { font-family: var(--gs-font); font-variant-numeric: tabular-nums; font-weight: 600;
            color: var(--gs-ink); white-space: nowrap; }
.gx-money--risk { color: var(--gs-ink); }   /* an amount is never red: red is
   for money already lost or refused (PILLAR1_UI_PATTERNS.md §8.3), and
   parts not yet on a repair order are neither */
.gx-money--big { font-size: var(--gs-fs-base); }
.gx-dash { color: var(--gs-muted); }
.gx-initial {
  width: 30px; height: 30px; border-radius: 9px; flex: none;
  display: grid; place-items: center; font-size: 11px;
  font-weight: 800; background: var(--gs-inset);
  border: 1px solid var(--gs-inset-line); color: var(--gs-ink-2);
}

/* ── the exceptions list ──────────────────────────────────────────── */

.gx-exc {
  display: grid; align-items: center; gap: 16px;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr) auto;
  padding: 11px 16px; border-top: 1px solid var(--gs-card-line);
  color: inherit; text-decoration: none; position: relative;
}
.gx-exc:first-child { border-top: 0; }
.gx-exc:hover { background: var(--gs-accent-wash); }
.gx-exc__part { font-weight: 800; letter-spacing: .01em; color: var(--gs-ink);
                text-transform: uppercase; font-size: var(--gs-fs-md);
                white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gx-exc__ids { font-family: var(--gs-font); font-variant-numeric: tabular-nums; font-size: 11px;
               color: var(--gs-muted); letter-spacing: .04em; margin-top: 3px;
               white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gx-exc__who { font-size: var(--gs-fs-sm); color: var(--gs-ink-2); min-width: 0; }
.gx-exc__who small { display: block; font-size: 11px; color: var(--gs-muted);
                     letter-spacing: .08em; text-transform: uppercase;
                     margin-top: 2px; white-space: nowrap; overflow: hidden;
                     text-overflow: ellipsis; }
.gx-exc__why { font-size: var(--gs-fs-sm); color: var(--gs-ink-2); min-width: 0; }
.gx-exc__why small { display: block; font-size: 11px; color: var(--gs-muted);
                     letter-spacing: .1em; text-transform: uppercase;
                     margin-bottom: 3px; }
.gx-exc__end { text-align: right; display: flex; flex-direction: column;
               align-items: flex-end; gap: 4px; }
.gx-exc__age { font-family: var(--gs-font); font-variant-numeric: tabular-nums; font-size: 11px;
               color: var(--gs-muted); letter-spacing: .06em; }
.gx-exc__age--late { color: var(--gs-danger); font-weight: 700; }
.gx-exc__file { position: relative; z-index: 2; }
.gx-exc__link { position: absolute; inset: 0; z-index: 1; }
@media (max-width: 899.98px) {
  .gx-exc { grid-template-columns: minmax(0, 1fr) auto; row-gap: 8px; }
  .gx-exc__who, .gx-exc__why { grid-column: 1 / 2; }
  .gx-exc__end { grid-row: 1 / 2; grid-column: 2 / 3; }
}

/* ── filter chips ─────────────────────────────────────────────────── */

.gx-filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.gx-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 11px; border-radius: 999px; cursor: pointer;
  font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; text-decoration: none; white-space: nowrap;
  color: var(--gs-ink-2); background: var(--gs-inset);
  border: 1px solid var(--gs-inset-line); font-family: inherit;
  transition: background var(--gs-dur) var(--gs-ease),
              color var(--gs-dur) var(--gs-ease);
}
.gx-chip:hover { color: var(--gs-ink); border-color: var(--gs-accent-line); }
.gx-chip.is-active { background: var(--gs-ink); color: var(--gs-bg);
                     border-color: var(--gs-ink); }
.gx-chip__n { font-family: var(--gs-font); font-variant-numeric: tabular-nums; opacity: .75; }
.gx-select {
  appearance: none; -webkit-appearance: none;
  padding: 7px 32px 7px 13px; border-radius: 999px;
  font: 700 var(--gs-fs-xs)/1.2 var(--gs-font); letter-spacing: .04em;
  color: var(--gs-ink-2); background: var(--gs-caret) no-repeat right 12px center,
                                       var(--gs-inset);
  /* A field's edge, as every `.form-control` has (--gs-field-edge): at
     --gs-inset-line (6%) a filter box in dark was a well with no rim. */
  border: 1px solid var(--gs-field-edge); cursor: pointer; max-width: 100%;
}
.gx-select:hover { border-color: var(--gs-accent-line); }

/* ── the rail ─────────────────────────────────────────────────────── */

.gx-rail { display: flex; flex-direction: column; gap: 0; }
@media (min-width: 992px) {
  .gx-rail {
    position: sticky; top: calc(var(--gs-topbar-h) + 16px);
    max-height: calc(100dvh - var(--gs-topbar-h) - 32px);
  }
  .gx-rail .gx-rail__list { overflow-y: auto; overscroll-behavior: contain;
                            scrollbar-width: thin; }
}
.gx-rail__head { padding: 14px 16px 12px;
                 border-bottom: 1px solid var(--gs-card-line); }
.gx-rail__top { display: flex; align-items: baseline;
                justify-content: space-between; gap: 10px; }
.gx-rail__count { margin: 0 0 10px; font-size: 1.25rem; font-weight: 800;
                  letter-spacing: -.02em; }
/* "Open Exceptions" — the one link on the rail people need to KNOW about, so
   it is a full-width card with a sentence saying what it is, not a footer
   link. Brand accent, not a warning colour: it is a destination, not a
   problem. */
.gx-rail__cta {
  display: grid; grid-template-columns: 32px minmax(0, 1fr) auto; gap: 10px;
  align-items: center; margin: 0 0 12px; padding: 10px 12px;
  border: 1px solid var(--gs-accent); border-radius: var(--gs-card-radius-sm, 12px);
  background: var(--gs-accent-wash); color: inherit; text-decoration: none;
  transition: background var(--gs-dur) var(--gs-ease);
}
.gx-rail__cta:hover { background: var(--gs-accent-ring); color: inherit; }
.gx-rail__cta-icon {
  width: 32px; height: 32px; border-radius: 9px; display: grid;
  place-items: center; font-size: 17px; color: var(--gs-accent);
  background: var(--gs-inset);
}
.gx-rail__cta-body b { display: block; font-size: var(--gs-fs-sm); }
.gx-rail__cta-body small { display: block; color: var(--gs-muted);
                           font-size: var(--gs-fs-xs); line-height: 1.35; }
.gx-rail__cta > .ti-arrow-right { color: var(--gs-accent); }
.gx-rail__high { font-size: var(--gs-fs-xs); font-weight: 800;
                 letter-spacing: .1em; text-transform: uppercase;
                 color: var(--gs-danger); display: inline-flex;
                 align-items: center; gap: 5px; }
.gx-rail__list { display: flex; flex-direction: column; min-height: 0; }
.gx-alert {
  display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 10px;
  padding: 10px 16px; position: relative;
  border-top: 1px solid var(--gs-card-line); color: inherit;
  text-decoration: none;
  transition: background var(--gs-dur) var(--gs-ease);
}
.gx-alert:first-child { border-top: 0; }
a.gx-alert:hover { background: var(--gs-accent-wash); }
/* The level is the ICON's tint — not a bar down the item's edge and not a
   dot beside its title, both of which the product has ruled out. */
.gx-alert__icon {
  width: 28px; height: 28px; border-radius: 9px; display: grid;
  place-items: center; font-size: 15px;
  background: var(--gx-wash, var(--gs-inset));
  color: var(--gx-tone, var(--gs-muted));
}
.gx-alert__body { min-width: 0; }
.gx-alert__top { display: flex; align-items: baseline;
                 justify-content: space-between; gap: 10px; }
.gx-alert__title { font-weight: 700; font-size: var(--gs-fs-sm);
                   color: var(--gs-ink); }
.gx-alert__at { flex: none; font-size: 11px; color: var(--gs-muted);
                font-family: var(--gs-font); font-variant-numeric: tabular-nums; }
.gx-alert__where { margin-top: 3px; font-size: 11px; font-weight: 700;
                   letter-spacing: .12em; text-transform: uppercase;
                   color: var(--gs-muted); }
.gx-alert__text { margin: 4px 0 0; font-size: var(--gs-fs-xs);
                  color: var(--gs-ink-2); line-height: 1.4; }
.gx-alert__value { margin-top: 4px; font-family: var(--gs-font); font-variant-numeric: tabular-nums;
                   font-size: var(--gs-fs-md); font-weight: 600;
                   color: var(--gs-danger); }
.gx-alert__value small { font-size: var(--gs-fs-xs); color: var(--gs-warning);
                         margin-left: 6px; font-family: var(--gs-font); }
.gx-rail__foot { padding: 10px 16px; border-top: 1px solid var(--gs-card-line);
                 font-size: var(--gs-fs-xs); color: var(--gs-muted); }
.gx-rail__empty { padding: 26px 20px; text-align: center;
                  color: var(--gs-muted); font-size: var(--gs-fs-sm); }
.gx-rail__empty i { display: block; font-size: 26px; color: var(--gs-success);
                    margin-bottom: 8px; }
[data-gx-filter-hidden] { display: none !important; }

/* ── the category board ───────────────────────────────────────────── */

.gx-board {
  display: grid; gap: var(--gs-space-4); align-items: start;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.4fr);
}
@media (max-width: 991.98px) {
  .gx-board { grid-template-columns: minmax(0, 1fr); }
}
.gx-cats { display: grid; gap: var(--gs-space-4);
           grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr)); }
.gx-parts { display: flex; flex-direction: column; gap: 8px;
            padding: 12px 14px 16px; max-height: 720px; overflow-y: auto; }
.gx-partcard {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center; gap: 10px; padding: 10px 12px;
  border-radius: 14px; background: var(--gs-inset);
  border: 1px solid var(--gs-inset-line); cursor: grab;
  user-select: none; touch-action: none;
  transition: border-color var(--gs-dur) var(--gs-ease),
              transform var(--gs-dur) var(--gs-ease),
              opacity var(--gs-dur) var(--gs-ease);
}
.gx-partcard:hover { border-color: var(--gs-accent-line); }
.gx-partcard:focus-visible { outline: 2px solid var(--gs-accent);
                             outline-offset: 2px; }
.gx-partcard.is-dragging { opacity: .45; cursor: grabbing; }
.gx-partcard__grip { color: var(--gs-muted); font-size: 16px; }
.gx-partcard__pn { font-weight: 800; font-size: var(--gs-fs-sm);
                   letter-spacing: .02em; white-space: nowrap; overflow: hidden;
                   text-overflow: ellipsis; }
.gx-partcard__desc { font-size: var(--gs-fs-xs); color: var(--gs-muted);
                     white-space: nowrap; overflow: hidden;
                     text-overflow: ellipsis; }
.gx-partcard__end { text-align: right; }
/* How often the check has raised it: a fact under the amount, in the
   sentence case and quiet colour of every other fact on these screens —
   it was amber capitals, which read as an alarm on a filing screen. */
.gx-partcard__seen { font-size: var(--gs-fs-xs); color: var(--gs-muted);
                     font-weight: 600; white-space: nowrap; }
.gx-drop { transition: box-shadow var(--gs-dur) var(--gs-ease),
                       border-color var(--gs-dur) var(--gs-ease); }
.gx-drop.is-over { border-color: var(--gs-accent);
                   box-shadow: var(--gs-card-edge), 0 0 0 3px var(--gs-accent-ring),
                               var(--gs-card-shadow); }
.gx-drop__zone {
  margin: 0 14px 14px; padding: 14px; border-radius: 14px;
  border: 1.5px dashed var(--gs-chart-grid); color: var(--gs-muted);
  font-size: var(--gs-fs-xs); text-align: center;
}
.gx-drop.is-over .gx-drop__zone { border-color: var(--gs-accent);
                                  color: var(--gs-accent); }
.gx-drop__rules { display: flex; flex-direction: column; gap: 6px;
                  padding: 0 14px 12px; max-height: 260px; overflow-y: auto; }
.gx-rule { display: flex; align-items: center; gap: 8px;
           padding: 7px 8px 7px 10px; border-radius: 10px;
           background: var(--gs-inset); font-size: var(--gs-fs-xs); }
.gx-rule__pn { font-weight: 800; letter-spacing: .02em; flex: none; }
.gx-rule__desc { color: var(--gs-muted); min-width: 0; overflow: hidden;
                 white-space: nowrap; text-overflow: ellipsis; flex: 1; }
.gx-rule__x { background: none; border: 0; color: var(--gs-muted);
              cursor: pointer; padding: 2px; font-size: 14px; flex: none; }
.gx-rule__x:hover { color: var(--gs-danger); }
.gx-cat__stat { display: flex; flex-wrap: wrap; gap: 4px 14px; padding: 4px 20px 12px;
                white-space: nowrap;
                font-size: var(--gs-fs-xs); color: var(--gs-muted); }
.gx-cat__stat b { color: var(--gs-ink); font-family: var(--gs-font); font-variant-numeric: tabular-nums;
                  font-size: var(--gs-fs-sm); }
.gx-newcat {
  display: grid; place-content: center; justify-items: center; gap: 8px;
  min-height: 180px; cursor: pointer; text-align: center;
  color: var(--gs-muted); font: inherit; font-weight: 700;
  border: 1.5px dashed var(--gs-chart-grid);
  border-radius: var(--gs-card-radius); background: transparent;
  transition: color var(--gs-dur) var(--gs-ease),
              border-color var(--gs-dur) var(--gs-ease);
}
.gx-newcat:hover { color: var(--gs-accent); border-color: var(--gs-accent); }
.gx-newcat i { font-size: 30px; width: 54px; height: 54px; border-radius: 50%;
               display: grid; place-items: center;
               background: var(--gs-accent-wash); color: var(--gs-accent); }
.gx-toast {
  position: fixed; left: 50%; bottom: 24px; z-index: 80;
  transform: translate(-50%, 20px); opacity: 0; pointer-events: none;
  padding: 11px 18px; border-radius: 999px; font-size: var(--gs-fs-sm);
  font-weight: 700; color: var(--gs-ink);
  background: var(--gs-glass-bg-2); border: 1px solid var(--gs-glass-border);
  -webkit-backdrop-filter: var(--gs-glass-blur);
  backdrop-filter: var(--gs-glass-blur);
  box-shadow: var(--gs-card-shadow);
  transition: opacity var(--gs-dur) var(--gs-ease),
              transform var(--gs-dur) var(--gs-ease);
}
.gx-toast.is-on { opacity: 1; transform: translate(-50%, 0); }
.gx-toast--error { color: var(--gs-danger); }

/* ── a larger chart in a dialog ───────────────────────────────────── */

.gx-dialog .gs-modal__body { max-height: calc(100dvh - 180px); }

/* ── one week's breakdown (a bar of the weekly chart, opened) ──────── */
.gx-week__figs { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
                 gap: var(--gs-space-3); }
.gx-week__fig {
  display: flex; flex-direction: column; gap: 6px; padding: 14px 16px;
  border: 1px solid var(--gs-card-line); border-radius: var(--gs-radius, 12px);
  background: var(--gs-glass-bg-2);
}
.gx-week__label { font-size: var(--gs-fs-xs); font-weight: 700; letter-spacing: .1em;
                  text-transform: uppercase; color: var(--gs-muted); }
.gx-week__value { font-size: 22px; font-weight: 700; color: var(--gs-ink);
                  font-variant-numeric: tabular-nums; line-height: 1.15; }
.gx-week__value small { font-size: var(--gs-fs-sm); font-weight: 600;
                        color: var(--gs-muted); }
.gx-week__note {
  margin-top: var(--gs-space-4); padding: 12px 16px;
  border: 1px dashed var(--gs-warning); border-radius: var(--gs-radius, 12px);
  font-size: var(--gs-fs-sm); color: var(--gs-ink-2);
}
.gx-week__note p { margin: 0 0 6px; }
.gx-week__note b { color: var(--gs-ink); }
.gx-week__note ul { margin: 0; padding-left: 18px; display: grid; gap: 3px; }
.gx-week__h { margin: var(--gs-space-5) 0 0; font-size: var(--gs-fs-xs);
              font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
              color: var(--gs-muted); }
.gx-table.gx-week__table { margin-top: var(--gs-space-2); }
.gx-week__table tfoot td { border-bottom: 0; border-top: 1px solid var(--gs-card-line);
                           font-weight: 700; color: var(--gs-ink); }
.gx-week__link { color: var(--gs-ink); font-weight: 600; text-decoration: none; }
.gx-week__link:hover { color: var(--gs-accent); text-decoration: underline; }
.gx-week__sub { display: block; font-size: var(--gs-fs-xs); color: var(--gs-muted);
                margin-top: 2px; }
/* The week's two bar charts, side by side: one series each, sorted, the
   longest bar set to the week's largest figure, value written at the end. */
.gx-week__charts { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
                   gap: var(--gs-space-3); margin-top: var(--gs-space-4); }
.gx-week__panel {
  min-width: 0; padding: 14px 16px 16px;
  border: 1px solid var(--gs-card-line); border-radius: var(--gs-radius);
  background: var(--gs-card-noise), var(--gs-card-fill);
  background-blend-mode: overlay, normal; box-shadow: var(--gs-card-edge);
}
.gx-week__panel { display: flex; flex-direction: column; }
.gx-week__panel .gx-week__h { margin: 0 0 12px; }
.gx-week__total { display: flex; justify-content: space-between; align-items: baseline;
                  margin: auto 0 0; padding-top: 12px; border-top: 1px solid var(--gs-card-line);
                  font-size: var(--gs-fs-sm); color: var(--gs-muted); }
.gx-week__total b { font-family: var(--gs-font); font-variant-numeric: tabular-nums; color: var(--gs-ink); font-weight: 700; }
.gx-week__panel .gx-wkcols { margin-bottom: 14px; }
.gx-week__panel .gx-week__key { margin-top: auto; }
/* The columns are the weekly chart's own (`gx-bars`), shorter, every one
   labelled: a week has a handful of branches, not twelve points. */
.gx-wkcols .gx-bars__tip { opacity: 1; color: var(--gs-ink-2); }
.gx-wkcols__axis span { white-space: normal; overflow: hidden; line-height: 1.3;
                        display: -webkit-box; -webkit-box-orient: vertical;
                        -webkit-line-clamp: 2; font-family: var(--gs-font);
                        font-size: 11px; color: var(--gs-ink-2); }
.gx-wkcols__axis small { display: block; color: var(--gs-muted); font-size: 10px;
                         font-family: var(--gs-font); font-variant-numeric: tabular-nums; }
/* The weekly chart shows every third date on a phone; a week's few columns
   keep every name, wrapped, since a missing name is a bar nobody can read. */
@media (max-width: 575.98px) {
  .gx-bars__axis.gx-wkcols__axis span { visibility: visible; overflow: hidden;
                                        font-size: 10px; overflow-wrap: anywhere; }
}
/* Supplier identity in the week's ring: fixed per supplier, never by rank.
   Checked as a set for colour-blind separation on the dark and light glass. */
.gx-sup--napa       { --gx-tone: #1ba3bb; }
.gx-sup--advancepro { --gx-tone: #9085e9; }
.gx-sup--autozone   { --gx-tone: #d55181; }
[data-theme='light'] .gx-sup--napa       { --gx-tone: #0e8fa3; }
[data-theme='light'] .gx-sup--advancepro { --gx-tone: #6d5fd6; }
[data-theme='light'] .gx-sup--autozone   { --gx-tone: #c8457a; }
@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme='dark'])) .gx-sup--napa       { --gx-tone: #0e8fa3; }
  :root:where(:not([data-theme='dark'])) .gx-sup--advancepro { --gx-tone: #6d5fd6; }
  :root:where(:not([data-theme='dark'])) .gx-sup--autozone   { --gx-tone: #c8457a; }
}
.gx-dotmark[class*='gx-sup--'] { background: var(--gx-tone); }
/* The ring sits in the middle of its card, the height of the bars beside it. */
.gx-week__donut { flex: 1; align-content: center; justify-content: center; gap: 18px; }
.gx-week__donut .gx-donut__keys { flex: 1 1 160px; }
.gx-week__donut .gx-donut__hub b { font-size: 1.05rem; }
.gx-week__key { display: flex; align-items: center; gap: 8px; margin: 12px 0 0;
                font-size: var(--gs-fs-xs); color: var(--gs-muted); }

/* The week dialog is the card glass itself, over a lighter scrim so the
   page behind it reads through, frosted, instead of a dark slab. */
.gs-modal.gx-week {
  background: var(--gs-card-noise), var(--gs-card-fill-hi);
  background-blend-mode: overlay, normal;
  -webkit-backdrop-filter: var(--gs-card-blur);
  backdrop-filter: var(--gs-card-blur);
  border-color: var(--gs-card-line);
  box-shadow: var(--gs-card-edge), var(--gs-card-shadow);
}
.gs-modal.gx-week::backdrop { background: rgb(0 0 0 / .28); }
/* On light, the brighter fill is near-white: the plain card fill is the glass. */
[data-theme='light'] .gs-modal.gx-week { background: var(--gs-card-noise), var(--gs-card-fill);
                                         background-blend-mode: overlay, normal; }
[data-theme='light'] .gs-modal.gx-week::backdrop { background: rgb(12 50 58 / .18); }
@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme='dark'])) .gs-modal.gx-week {
    background: var(--gs-card-noise), var(--gs-card-fill);
    background-blend-mode: overlay, normal; }
  :root:where(:not([data-theme='dark'])) .gs-modal.gx-week::backdrop { background: rgb(12 50 58 / .18); }
}
.gx-week .gs-modal__head { background: none; border-color: var(--gs-card-line); }
.gx-week .gx-week__fig { background: var(--gs-card-noise), var(--gs-card-fill);
                         background-blend-mode: overlay, normal;
                         box-shadow: var(--gs-card-edge); }

@media (max-width: 767.98px) {
  .gx-week__charts { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 575.98px) {
  .gx-week__figs { grid-template-columns: 1fr 1fr; }
  .gx-week__fig:first-child { grid-column: 1 / -1; }
  .gx-week__table tfoot td:empty { display: none; }
  .gx-week__table td[data-label="Branch"] { display: block; }
  .gx-week__table td[data-label="Branch"]::before { content: none; }
}
.gx-table { width: 100%; border-collapse: collapse; font-size: var(--gs-fs-sm);
            margin-top: var(--gs-space-4); }
.gx-table th { text-align: left; font-size: var(--gs-fs-xs); font-weight: 700;
               letter-spacing: .1em; text-transform: uppercase;
               color: var(--gs-muted); padding: 8px 10px;
               border-bottom: 1px solid var(--gs-card-line); }
.gx-table td { padding: 9px 10px; border-bottom: 1px solid var(--gs-line-soft);
               color: var(--gs-ink-2); vertical-align: top; }
.gx-table td.num, .gx-table th.num { text-align: right; white-space: nowrap;
                                    font-family: var(--gs-font); font-variant-numeric: tabular-nums; }
@media (max-width: 575.98px) {
  .gx-table thead { display: none; }
  .gx-table tr { display: block; padding: 8px 0;
                 border-bottom: 1px solid var(--gs-line-soft); }
  .gx-table td { display: flex; justify-content: space-between; gap: 12px;
                 border: 0; padding: 4px 0; }
  .gx-table td::before { content: attr(data-label); color: var(--gs-muted);
                         font-size: var(--gs-fs-xs); }
  .gx-table td.num { text-align: right; }
}

/* ── the product-line menu: a feature's screens under its row ─────── */

.gs-topnav .gs-topmenu__group { display: flex; flex-direction: column;
                                gap: 2px; margin: 2px 0 6px; }
.gs-topnav .gs-topmenu__sub {
  display: grid; grid-template-columns: 28px minmax(0, 1fr); align-items: center;
  column-gap: 10px; padding: 7px 10px 7px 12px; border-radius: 10px;
  color: var(--gs-ink-2); text-decoration: none; margin-left: 12px;
  border-left: 1px solid var(--gs-card-line);
  border-top-left-radius: 0; border-bottom-left-radius: 0;
}
.gs-topnav .gs-topmenu__sub:hover { background: var(--gs-accent-wash);
                                    color: var(--gs-ink); }
.gs-topnav .gs-topmenu__sub.is-active { color: var(--gs-accent);
                                        background: var(--gs-accent-wash); }
.gs-topnav .gs-topmenu__sub i { font-size: 17px; justify-self: center; }
.gs-topnav .gs-topmenu__subtext { display: flex; flex-direction: column;
                                  min-width: 0; }
.gs-topnav .gs-topmenu__subname { font-weight: 700; font-size: var(--gs-fs-sm); }
.gs-topnav .gs-topmenu__subhint { font-size: 11px; color: var(--gs-muted);
                                  white-space: nowrap; overflow: hidden;
                                  text-overflow: ellipsis; }
@media (min-width: 992px) {
  .gs-topnav .gs-topmenu--wide { width: 360px; }
}

/* ── the drifting globe behind every screen ────────────────────────
   The Personal AI lattice in its `ambient` mode (kate-lattice.js): fixed
   to the viewport, over the backdrop image and under everything else, and
   never in the way of a click. It fades in once the renderer has taken the
   canvas, so a page with no WebGL simply never shows it. It steps aside
   while Kate's own overlay is open, where the full-size globe is the
   subject. */
.gs-ambient {
  position: fixed; inset: 0; z-index: -1;
  width: 100vw; height: 100dvh;
  pointer-events: none;
  opacity: 0; transition: opacity 1.2s var(--gs-ease);
}
.gs-ambient[data-webgl] { opacity: 1; }
/* The same globe carried over through a reload of its page (kate-lattice.js,
   KEEP): it is already there, so it comes back at once rather than fading
   up as if it were new. */
.gs-ambient[data-resumed] { transition-duration: .2s; }
body:has(.gs-kate-overlay:not([hidden])) .gs-ambient { opacity: 0; }
@media print { .gs-ambient { display: none; } }

/* The quiet particle field (gs-particles.js) behind every screen but the
   dashboard and Kate's: fixed, under everything, never in the way of a
   click. It fades up once drawn; no entrance of its own. */
.gs-particles {
  position: fixed; inset: 0; z-index: -1;
  width: 100vw; height: 100dvh;
  pointer-events: none;
  opacity: 0; transition: opacity .5s var(--gs-ease);
}
.gs-particles[data-ready] { opacity: 1; }
body:has(.gs-kate-overlay:not([hidden])) .gs-particles { opacity: 0; }
@media print { .gs-particles { display: none; } }

/* ── "Coming soon", where it can be seen ─────────────────────────────
   Milestone-preview screens show sample figures. Three places say so, in
   the warning amber the preview pill already uses, so the word always
   looks the same:
     the menu item      a tag on every preview row in a product line
     the trigger        "Soon" on a line whose every row is a preview
     the page           a strip above the figures of every preview screen */
.gs-topnav .gs-topmenu__soon {
  font-size: 10px; font-weight: 800; letter-spacing: .05em;
  text-transform: uppercase; white-space: nowrap; flex: none;
  padding: 3px 7px; border-radius: 999px;
  background: var(--gs-warning-wash); color: var(--gs-warning);
}
.gs-toplink__label { position: relative; display: inline-block; }
.gs-toplink__soon {
  /* Under the line's name, centred on the WORD, taking no width: the bar
     measures its items to decide what spills into More. */
  position: absolute; left: 50%; top: calc(100% + 3px);
  transform: translateX(-50%);
  padding: 1px 6px; border-radius: 999px; pointer-events: none;
  font-size: 8px; font-weight: 800; letter-spacing: .08em; line-height: 1.3;
  background: var(--gs-warning-wash); color: var(--gs-warning);
  white-space: nowrap;
}
@media (max-width: 991.98px) {
  /* In the drawer the trigger is a full-width row: the tag sits inline. */
  .gs-toplink__soon { position: static; transform: none; margin-left: 6px;
                      vertical-align: 2px; }
}

/* The account button is the initials alone: the name and the email are
   already the first thing its menu shows. */
.gs-topbar .gs-topbar__user { padding: 4px; border-radius: 50%; gap: 0; }
.gs-topbar .gs-topbar__username,
.gs-topbar .gs-topbar__user .gs-topbar__caret { display: none; }

.gx-soonbar {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: var(--gs-space-4); padding: 10px 14px;
  border-radius: var(--gs-card-radius-sm, 14px);
  background: var(--gs-warning-wash);
  border: 1px solid var(--gs-warning);
  font-size: var(--gs-fs-sm); color: var(--gs-ink-2);
}
.gx-soonbar__tag {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; background: var(--gs-warning);
  color: var(--gs-bg);
}
.gx-soonbar__text b { color: var(--gs-ink); }

/* The dashboard's "who, where, when" line, under the greeting. */
.gx-whoami {
  display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 10px 0 0;
  font-size: var(--gs-fs-xs); color: var(--gs-muted);
}
.gx-whoami span { display: inline-flex; align-items: center; gap: 6px; }
.gx-whoami i { font-size: 14px; color: var(--gs-accent); }

/* ── the setup guide ──────────────────────────────────────────────────
   A drawer that is simply a `gx-card`: the same frosted glass, head and
   rows as the "Needs attention" rail, so it belongs to the pages it opens
   over. Nothing here draws a colour of its own; the rows' tints are the
   same tone washes the rail uses. The only motion is the slide in. */
.gx-setup { justify-content: flex-end; padding: 14px; }
.gx-setup__scrim { background: var(--gs-ground); opacity: .6; }
.gx-setup__panel {
  /* Glass, but dense: it opens over whatever page is behind it, and a
     drawer of instructions has to be readable over any of them. */
  background: var(--gs-card-noise),
              color-mix(in srgb, var(--gs-bg-elevated) 93%, transparent);
  background-blend-mode: overlay, normal;
  -webkit-backdrop-filter: blur(24px) saturate(140%);
  backdrop-filter: blur(24px) saturate(140%);
  position: relative; z-index: 1; margin: 0;
  width: min(420px, 100%); height: 100%;
  display: flex; flex-direction: column;
  animation: gx-setup-in .3s var(--gs-ease) both;
}
@keyframes gx-setup-in {
  from { transform: translateX(24px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
.gx-setup__bar {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; padding: 16px 16px 12px;
  border-bottom: 1px solid var(--gs-card-line);
}
.gx-setup__scroll { flex: 1; min-height: 0; overflow-y: auto;
                    overscroll-behavior: contain; scrollbar-width: thin; }
.gx-setup__progress { padding: 14px 16px 6px; }
.gx-setup__count { font-size: var(--gs-fs-sm); color: var(--gs-ink-2);
                   margin-bottom: 8px; }
.gx-setup__count b { font-size: 1.15rem; color: var(--gs-ink); }
.gx-setup__count span { color: var(--gs-muted); }
.gx-setup__clear { display: flex; gap: 8px; align-items: flex-start;
  margin: 8px 16px 0; font-size: var(--gs-fs-sm); color: var(--gs-ink-2); }
.gx-setup__clear i { color: var(--gs-success); font-size: 16px; }
.gx-setup__group { margin: 18px 16px 6px; }

/* The Guide drawer's two sections: "About this page" and "Getting set up". */
.gx-guide { border-bottom: 1px solid var(--gs-card-line); }
.gx-guide__summary {
  list-style: none; cursor: pointer; display: grid; align-items: center;
  grid-template-columns: 28px minmax(0, 1fr) auto; gap: 10px;
  padding: 14px 16px; font-weight: 700;
}
.gx-guide__summary::-webkit-details-marker { display: none; }
.gx-guide__summary > .ti:first-child {
  width: 28px; height: 28px; border-radius: 9px; display: grid;
  place-items: center; background: var(--gs-accent-wash);
  color: var(--gs-accent);
}
.gx-guide__summary small { display: block; font-weight: 500;
                           color: var(--gs-muted); font-size: var(--gs-fs-xs); }
.gx-guide__chev { color: var(--gs-muted);
                  transition: transform var(--gs-dur) var(--gs-ease); }
.gx-guide[open] .gx-guide__chev { transform: rotate(180deg); }
.gx-guide__body { padding-bottom: 12px; }
.gx-guide__what { margin: 0 16px 4px; line-height: 1.5; }
.gx-guide__list { margin: 0 16px; padding-left: 18px;
                  font-size: var(--gs-fs-sm); line-height: 1.45; }
.gx-guide__list li { margin: 0 0 6px; }
.gx-guide__list--do li::marker { content: "→  "; color: var(--gs-accent); }
.gx-guide__list--how li::marker { color: var(--gs-accent); font-weight: 700; }
.gx-guide__terms { margin: 0 16px; font-size: var(--gs-fs-sm);
                   line-height: 1.45; }
.gx-guide__terms dt { font-weight: 700; margin-top: 8px; }
.gx-guide__terms dt:first-child { margin-top: 0; }
.gx-guide__terms dd { margin: 2px 0 0; color: var(--gs-ink-2); }
.gx-setup__list { border-top: 1px solid var(--gs-card-line); }
.gx-setup__step.is-done .gx-alert__title { color: var(--gs-muted); }
.gx-setup__step.is-next { background: var(--gs-accent-wash); }
.gx-setup__note { color: var(--gs-muted); }
.gx-setup__cta { margin-top: 8px; }
.gs-setup__loading { padding: 16px; margin: 0; }

/* ── the help drawer: Gearshift Assistant, and the setup guide ─────────
   `partials/_setup.html`, `initHelpChat` in app.js. One header that says
   which view is showing (`data-gs-help-view` on the drawer): the assistant,
   with a "Setup guide" button; or the guide, with an arrow back. Below it
   either the conversation and its composer, or the fetched guide.

   Kept plain on purpose: neutral surfaces, one accent, hairlines rather
   than boxes, and every colour a token so both themes follow. */
.gx-assist { width: min(380px, 100%); }
/* A SIDE SHEET, not a floating card: flush to the right edge, the full
   height of the window, square corners, one hairline down its left side,
   sliding in from off-screen (the client's call). */
.gx-setup:has(.gx-assist) { padding: 0; }
.gx-setup__panel.gx-assist {
  height: 100%; margin: 0; border-radius: 0;
  border: 0; border-left: 1px solid var(--gs-card-line);
  box-shadow: -24px 0 60px -30px rgb(0 0 0 / .55);
  animation: gx-sheet-in .28s var(--gs-ease) both;
}
@keyframes gx-sheet-in {
  from { transform: translateX(100%); }
  to   { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .gx-setup__panel.gx-assist { animation: none; }
}
.gx-assist__head {
  flex: none; display: flex; align-items: center; gap: 8px;
  padding: 14px 12px 14px 16px;
  border-bottom: 1px solid var(--gs-card-line);
}
.gx-assist__mark {
  flex: none; width: 34px; height: 34px; border-radius: 11px;
  display: grid; place-items: center; font-size: 17px;
  background: var(--gs-gradient); color: var(--gs-on-accent);
}
.gx-assist__titles { flex: 1; min-width: 0; }
.gx-assist__title {
  margin: 0; font-size: var(--gs-fs-lg); font-weight: 800;
  line-height: 1.2; letter-spacing: -.01em;
  color: var(--gs-ink); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis;
}
.gx-assist__sub {
  margin: 2px 0 0; font-size: var(--gs-fs-xs); color: var(--gs-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The product's outlined button ("Exceptions", "All branches"), at the
   size of "Ask for help" in the bar. */
.gx-assist__guidebtn { flex: none; min-height: 40px; padding: 0 15px; }
.gx-assist__iconbtn {
  flex: none; width: 34px; height: 34px; border-radius: 10px;
  display: grid; place-items: center; font-size: 18px; cursor: pointer;
  border: 1px solid transparent; background: none; color: var(--gs-muted);
  transition: background var(--gs-dur) var(--gs-ease),
              color var(--gs-dur) var(--gs-ease);
}
.gx-assist__iconbtn:hover { background: var(--gs-well); color: var(--gs-ink); }
.gx-assist__iconbtn:focus-visible { outline: 2px solid var(--gs-accent-ring);
                                    outline-offset: 2px; }

/* Which view: the header's words, icon and buttons follow it. */
.gx-assist__back,
[data-gs-help-view="guide"] .gx-assist__when-ask,
[data-gs-help-view="guide"] .gx-assist__guidebtn,
[data-gs-help-view="ask"] .gx-assist__when-guide { display: none; }
[data-gs-help-view="guide"] .gx-assist__back { display: grid; }
[data-gs-help-view="guide"] .gx-assist__mark {
  background: var(--gs-accent-wash); color: var(--gs-accent);
}

.gx-setup__scroll[hidden] { display: none; }

/* ── the assistant ───────────────────────────────────────────────── */
.gx-help {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
}
.gx-help[hidden] { display: none; }
.gx-help.is-drop { box-shadow: inset 0 0 0 2px var(--gs-accent-line); }

.gx-help__log {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin;
  display: flex; flex-direction: column; gap: 14px; padding: 20px 18px;
}

/* The welcome: centred, quiet, then a short list of things to ask. */
.gx-help__welcome {
  display: flex; flex-direction: column; align-items: center;
  text-align: center; padding: 18px 6px 4px;
}
.gx-help__welcome[hidden] { display: none; }
.gx-help__welcome-mark {
  width: 52px; height: 52px; border-radius: 16px; margin-bottom: 14px;
  display: grid; place-items: center; font-size: 26px;
  background: var(--gs-accent-wash); color: var(--gs-accent);
  border: 1px solid var(--gs-accent-line);
}
.gx-help__welcome-title {
  margin: 0; font-size: var(--gs-fs-lg); font-weight: 800; color: var(--gs-ink);
}
.gx-help__welcome-text {
  margin: 6px 0 0; max-width: 32ch; line-height: 1.55;
  font-size: var(--gs-fs-sm); color: var(--gs-muted);
}
.gx-help__label {
  align-self: stretch; text-align: left; margin: 26px 0 8px;
  font-size: 11px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--gs-muted);
}
.gx-help__suggest {
  align-self: stretch; display: flex; flex-direction: column; gap: 8px;
}
.gx-help__chip {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; padding: 11px 14px; text-align: left; cursor: pointer;
  border: 1px solid var(--gs-card-line); border-radius: 12px;
  background: var(--gs-well); color: var(--gs-ink-2);
  font: inherit; font-size: var(--gs-fs-sm);
  transition: border-color var(--gs-dur) var(--gs-ease),
              background var(--gs-dur) var(--gs-ease),
              color var(--gs-dur) var(--gs-ease);
}
.gx-help__chip .ti { flex: none; color: var(--gs-muted); font-size: 16px;
                     transition: transform var(--gs-dur) var(--gs-ease),
                                 color var(--gs-dur) var(--gs-ease); }
.gx-help__chip:hover { border-color: var(--gs-accent-line);
                       background: var(--gs-accent-wash); color: var(--gs-ink); }
.gx-help__chip:hover .ti { color: var(--gs-accent); transform: translateX(2px); }
.gx-help__chip:focus-visible { outline: 2px solid var(--gs-accent-ring);
                               outline-offset: 2px; }

/* The conversation: the person's messages on the right in the accent, the
   assistant's on the left on a plain surface. */
.gx-help__msg {
  max-width: 88%; padding: 10px 14px; border-radius: 14px;
  font-size: var(--gs-fs-sm); line-height: 1.55;
}
.gx-help__msg--you {
  align-self: flex-end; border-bottom-right-radius: 4px;
  background: var(--gs-gradient); background-origin: border-box;
  color: var(--gs-on-accent); font-weight: 600;
}
.gx-help__msg--bot {
  align-self: flex-start; border-bottom-left-radius: 4px;
  background: var(--gs-well); border: 1px solid var(--gs-card-line);
  color: var(--gs-ink);
}
.gx-help__msg.is-pending { color: var(--gs-muted); font-style: italic; }
.gx-help__msg.is-error { border-color: var(--gs-danger); }
.gx-help__text { white-space: pre-wrap; overflow-wrap: anywhere; }
.gx-help__shot {
  display: block; max-width: 100%; max-height: 160px; margin-bottom: 6px;
  border-radius: 8px; border: 1px solid var(--gs-card-line); object-fit: contain;
}
.gx-help__note {
  margin: 8px 0 0; font-size: var(--gs-fs-xs); color: var(--gs-warning);
}
.gx-help__sources {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--gs-card-line);
  font-size: var(--gs-fs-xs);
}
.gx-help__sources-label { color: var(--gs-muted); }
.gx-help__source {
  padding: 2px 9px; border-radius: var(--gs-radius-pill);
  border: 1px solid var(--gs-card-line); color: var(--gs-ink-2);
  white-space: nowrap; text-decoration: none;
}
a.gx-help__source:hover { border-color: var(--gs-accent-line);
                          color: var(--gs-ink); }

/* The composer: one row (attach, the question, send) and a footnote. */
.gx-help__composer {
  flex: none; padding: 12px 16px 12px;
  border-top: 1px solid var(--gs-card-line);
}
.gx-help__box {
  display: flex; align-items: flex-end; gap: 4px; padding: 5px;
  border: 1px solid var(--gs-line-strong); border-radius: 14px;
  background: var(--gs-well);
  transition: border-color var(--gs-dur) var(--gs-ease),
              box-shadow var(--gs-dur) var(--gs-ease);
}
.gx-help__box:focus-within {
  border-color: var(--gs-accent); box-shadow: 0 0 0 3px var(--gs-accent-ring);
}
.gx-help__input {
  flex: 1; min-width: 0; display: block; min-height: 36px; max-height: 140px;
  padding: 8px 6px; border: 0; outline: 0; resize: none;
  background: transparent; color: var(--gs-ink);
  font: inherit; font-size: var(--gs-fs-sm); line-height: 1.45;
}
.gx-help__input::placeholder { color: var(--gs-muted); }
.gx-help__attach {
  flex: none; width: 36px; height: 36px; margin: 0; border-radius: 10px;
  display: grid; place-items: center; font-size: 18px; cursor: pointer;
  color: var(--gs-muted);
  transition: background var(--gs-dur) var(--gs-ease),
              color var(--gs-dur) var(--gs-ease);
}
.gx-help__attach:hover { background: var(--gs-accent-wash); color: var(--gs-accent); }
.gx-help__attach:focus-within { outline: 2px solid var(--gs-accent-ring); }
.gx-help__send {
  flex: none; width: 36px; height: 36px; border-radius: 10px;
  display: grid; place-items: center; font-size: 17px; cursor: pointer;
  border: 0; background: var(--gs-gradient); color: var(--gs-on-accent);
  transition: opacity var(--gs-dur) var(--gs-ease);
}
.gx-help__send:disabled { opacity: .45; cursor: default; }
.gx-help__foot {
  margin: 8px 0 0; text-align: center; font-size: 11px; color: var(--gs-muted);
}
.gx-help__attachment {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  padding: 6px; border: 1px solid var(--gs-card-line);
  border-radius: 10px; font-size: var(--gs-fs-xs); color: var(--gs-ink-2);
}
.gx-help__attachment[hidden] { display: none; }
.gx-help__attachment img {
  width: 44px; height: 32px; object-fit: cover; border-radius: 6px;
}
.gx-help__attachment-name { flex: 1; min-width: 0; overflow: hidden;
                            text-overflow: ellipsis; white-space: nowrap; }
.gx-help__unattach {
  border: 0; background: none; color: var(--gs-muted); cursor: pointer;
  padding: 2px 4px;
}
.gx-help__error { margin: 6px 0 0; font-size: var(--gs-fs-xs);
                  color: var(--gs-danger); }
.gx-help__error[hidden] { display: none; }

/* ── the setup guide, inside the same drawer ─────────────────────── */
.gx-assist__guide { padding: 14px 14px 20px; }
.gx-assist__guide .gx-guide {
  border: 1px solid var(--gs-card-line); border-radius: 14px;
  background: var(--gs-well); overflow: hidden;
}
.gx-assist__guide .gx-guide + .gx-guide { margin-top: 12px; }
.gx-assist__guide .gx-guide__summary { padding: 14px 16px; }
.gx-assist__guide .gx-guide[open] > .gx-guide__summary {
  border-bottom: 1px solid var(--gs-card-line);
}
.gx-assist__guide .gx-guide__body { padding: 4px 0 14px; }
.gx-assist__guide .gx-guide__what {
  margin: 12px 16px 2px; color: var(--gs-ink-2); font-size: var(--gs-fs-sm);
}
.gx-assist__guide .gx-setup__group { margin: 16px 16px 6px; }
.gx-assist__guide .gx-guide__list { color: var(--gs-ink-2); }
.gx-assist__guide .gx-setup__list { border-top: 0; }
.gx-assist__guide .gx-setup__step { border-radius: 10px; margin: 0 8px; }
@media (max-width: 575.98px) {
  .gx-setup { padding: 0; }
  .gx-setup__panel { width: 100%; border-radius: 0; }
}
@media (prefers-reduced-motion: reduce) { .gx-setup__panel { animation: none; } }

/* Kate's orb steps aside while the setup drawer is open; it floats above
   the drawer otherwise and sits on top of the step you are reading.
   `visibility`, because its entrance animation holds `opacity` at 1. */
body:has(.gs-setup:not([hidden])) .gs-kate-fab { visibility: hidden; }

/* ── the backdrop ────────────────────────────────────────────────────
   Here rather than in gs-material.css, which Personal AI's screens do
   not load: every page, Kate's included, sits on the same picture.

   The picture every signed-in page sits on (`--gs-app-image`, one per
   theme), fixed behind the content so the cards slide over it as the page
   scrolls — which is what makes the frost visible at all. It replaces the
   three-gradient mesh on this layer; the mesh token is left defined for the
   screens that still paint it themselves.

   A fixed PSEUDO-ELEMENT rather than `background-attachment: fixed`, which
   iOS Safari ignores and which repaints the whole page on every scroll
   frame elsewhere. `var(--gs-ground)` last, so a slow image load shows the
   theme's ground rather than a white flash. */
.gs-shell::before {
  background: var(--gs-app-scrim),
              var(--gs-app-image) center / cover no-repeat,
              var(--gs-ground);
}

/* The sign-in pages (`layouts/auth.html`: sign in, sign up, password
   reset, not found) sit on the same picture, so the app does not change
   ground between the door and the rooms. Same fixed layer they already
   had; their two soft pools stay on top of the picture, and the auth
   ground under it still shows while the image loads. */
.gs-auth::before {
  background:
    radial-gradient(700px 540px at 84% 6%,  var(--gs-auth-bloom-1), transparent 68%),
    radial-gradient(620px 500px at 10% 94%, var(--gs-auth-bloom-2), transparent 70%),
    var(--gs-app-scrim),
    var(--gs-app-image) center / cover no-repeat;
}

@media (max-width: 991.98px) {
  /* On a phone the call covers the page completely: the stage is a
     scrolling column there and the page showing through it is noise. */
  .gs-kate-overlay__bg { opacity: 1; }
}

/* ── the top bar's panels, in the cards' own glass ─────────────────────
   The product-line menus, notifications and the account menu were drawn
   in `.gs-glass` — a denser, flatter fill than the cards on the pages
   under them, so they read as a different material. Same tokens as
   `.gx-card` now: noise, fill, blur, hairline, radius and shadow.
   Qualified with `.gs-topbar` so it outranks the single-class `.gs-glass`
   and `.gs-pop__panel` rules (see the note in CLAUDE.md), and the product
   menus only above lg — below it they are an accordion inside the drawer
   and must not float as cards. */
.gs-topbar .gs-topbar__actions .gs-pop__panel.gs-glass {
  background: var(--gs-card-noise), var(--gs-card-fill-hi);
  background-blend-mode: overlay, normal;
  -webkit-backdrop-filter: var(--gs-card-blur);
  backdrop-filter: var(--gs-card-blur);
  border: 1px solid var(--gs-card-line);
  border-radius: var(--gs-card-radius);
  box-shadow: var(--gs-card-edge), var(--gs-card-shadow);
}
@media (min-width: 992px) {
  .gs-topbar .gs-topnav .gs-topmenu.gs-glass {
    background: var(--gs-card-noise), var(--gs-card-fill-hi);
    background-blend-mode: overlay, normal;
    -webkit-backdrop-filter: var(--gs-card-blur);
    backdrop-filter: var(--gs-card-blur);
    border: 1px solid var(--gs-card-line);
    border-radius: var(--gs-card-radius);
    box-shadow: var(--gs-card-edge), var(--gs-card-shadow);
  }
}

/* On a phone the preview notice in the bar is its eye icon alone: with
   its label it pushed the bar 48px past the screen. The amber strip on
   the page says "Coming soon" in full. */
@media (max-width: 575.98px) {
  .gs-vision__word, .gs-vision__caret { display: none; }
  .gs-vision__pill { padding: 6px 8px; }
}

/* The page facts line: separated by dots, figures in the ink colour. */

/* The current page in the top bar is simply teal: no underline. */
.gs-topnav .gs-toplink.is-active { color: var(--gs-accent); }
.gs-topnav .gs-toplink::after { display: none; }

/* Manage features: a feature this branch holds is the brand's teal, not
   the "healthy" green — holding a feature is not a health reading. */
.gs-featurecard.is-held {
  background: var(--gs-accent-wash);
  border-color: var(--gs-accent-line);
}
.gs-featurecard.is-held .gs-featurecard__icon { color: var(--gs-accent); }
.gs-featurecard.is-held .gs-badge--success {
  background: var(--gs-accent-wash); color: var(--gs-accent);
}

/* ── native dropdowns follow the theme ──────────────────────────────
   A <select>'s open list is drawn by the browser, not the page, and it
   ignored the dark theme: a light grey list with pale text nobody could
   read. `color-scheme` tells the browser which list to draw; the option
   colours are for the browsers that honour them. */
select, .gx-select, .form-select { color-scheme: dark; }
select option, select optgroup {
  background-color: var(--gs-bg-elevated); color: var(--gs-ink);
}
select option:checked { background-color: var(--gs-accent-wash);
                        color: var(--gs-accent); }
select option:disabled { color: var(--gs-muted); }
[data-theme='light'] select, [data-theme='light'] .gx-select,
[data-theme='light'] .form-select { color-scheme: light; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) select,
  :root:not([data-theme='dark']) .gx-select,
  :root:not([data-theme='dark']) .form-select { color-scheme: light; }
}

/* A page-level grid of cards followed by another card: the cards inside
   the grid drop their bottom margin (the grid's gap spaces them), and the
   grid had none of its own, so the next card sat flush against it. */
.gs-container > .gs-grid:not(:last-child),
.gs-container > .gs-stack:not(:last-child) { margin-bottom: var(--gs-space-5); }

/* Top bar: larger link text and icons, same 64px bar. Qualified with
   `.gs-topbar` to outrank the theme's single-class rules. Above lg only
   for the links: in the phone drawer they are rows with their own size. */
@media (min-width: 992px) {
  .gs-topbar .gs-topnav .gs-toplink { font-size: 13.5px; }
  .gs-topbar .gs-topnav .gs-toplink__caret { font-size: 11px; }
}
.gs-topbar .gs-topbar__actions .gs-iconbtn { width: 42px; height: 42px; }
.gs-topbar .gs-topbar__actions .gs-iconbtn i { font-size: 21px; }
/* A small phone has 320px for the logo and five controls: they shrink
   rather than push the page wider than the screen, which shifted every
   fixed layer (Kate's call included) partly off the side. */
@media (max-width: 575.98px) {
  .gs-topbar .gs-topbar__inner { gap: 6px; }
  .gs-topbar .gs-topbar__actions { gap: 2px; }
  .gs-topbar .gs-topbar__actions .gs-iconbtn { width: 34px; height: 34px; }
  .gs-topbar .gs-topbar__actions .gs-iconbtn i { font-size: 18px; }
  .gs-topbar .gs-topbar__user .gs-avatar { width: 32px; height: 32px; }
  .gs-topbar .gs-brand__logo { max-width: min(120px, 32vw); height: auto; }
}

/* A product-line menu never runs past the bottom of the window: on a
   short laptop screen it scrolls inside itself instead. */
@media (min-width: 992px) {
  .gs-topbar .gs-topnav .gs-topmenu {
    max-height: calc(100dvh - var(--gs-topbar-h) - 36px);
    overflow-y: auto; overscroll-behavior: contain;
  }
}

/* Every panel off the top bar scrolls when the window is short, with NO
   visible scrollbar. A fade along the bottom edge says there is more; it
   is switched on by parts.js only while there is more below, so it never
   greys out the last row. */
.gs-topbar .gs-pop__panel,
.gs-topbar .gs-topnav,
.gs-topbar .gs-topnav__list,
.gs-topbar .gs-notif__list {
  scrollbar-width: none; -ms-overflow-style: none;
}
.gs-topbar .gs-pop__panel::-webkit-scrollbar,
.gs-topbar .gs-topnav::-webkit-scrollbar,
.gs-topbar .gs-topnav__list::-webkit-scrollbar,
.gs-topbar .gs-notif__list::-webkit-scrollbar { display: none; }
.gs-topbar .gs-topbar__actions .gs-pop__panel {
  max-height: calc(100dvh - var(--gs-topbar-h) - 36px); overflow-y: auto;
  overscroll-behavior: contain;
}
.gs-topbar .has-more-below {
  -webkit-mask-image: linear-gradient(to bottom, var(--gs-mask-solid)
                      calc(100% - 44px), transparent);
  mask-image: linear-gradient(to bottom, var(--gs-mask-solid)
              calc(100% - 44px), transparent);
}

/* A long button label ("Open the exception dashboard") wraps on a phone
   rather than pushing the page wider than the screen. */
@media (max-width: 575.98px) {
  .gs-btn { white-space: normal; max-width: 100%; text-align: center; }
}

/* ── every button, in the "Talk to me" material ────────────────────────
   Kate's button (`.gs-kate-btn`, kate.css) became the product's button:
   the teal face, a glint that crosses it every few seconds, a light going
   round the rim, a blurred aurora underneath, and a springy lift on hover.
   Here it is given to every primary action - `.gs-btn--primary`, the
   sign-in pages' `.gs-auth__submit`, and the Personal AI button in the top
   bar (which also loses its pill for the same rounded rectangle). The
   quieter buttons (ghost, danger, the sign-in pages' outline) keep their
   own fills, so a screen still says which action is THE action, and take
   the same shape and the same motion.

   Defined here rather than borrowed from kate.css, which only loads for
   people who hold Personal AI. Same values, same timing. The face and
   glint are painted on ::before (clipped to its padding box, so the
   turning conic shows only in the 1.5px ring around them) and the aurora
   on ::after, both under the label (the button is its own stacking
   context). `inset: -1px` because these buttons keep a 1px transparent
   border that holds their size. */
@property --gs-btn-a {
  syntax: '<angle>'; inherits: false; initial-value: 0deg;
}

.gs-btn--primary,
.gs-auth__submit,
.gs-aibtn {
  position: relative; isolation: isolate;
  background: none; border-color: transparent; box-shadow: none;
  color: var(--gs-btn-ink);
  transition: transform .35s cubic-bezier(.2, 1.4, .35, 1);
}
.gs-btn--primary::before,
.gs-auth__submit::before,
.gs-aibtn::before {
  content: ''; position: absolute; inset: -1px; z-index: -1;
  border: 1.5px solid transparent; border-radius: inherit;
  background:
    linear-gradient(105deg, transparent 42%,
      color-mix(in srgb, var(--gs-white) 32%, transparent) 50%,
      transparent 58%) padding-box no-repeat,
    var(--gs-btn-face) padding-box,
    conic-gradient(from var(--gs-btn-a), var(--gs-btn-line) 0deg,
      var(--gs-cyan-300) 60deg, var(--gs-white) 90deg,
      var(--gs-btn-line) 150deg, var(--gs-btn-line) 360deg) border-box;
  background-size: 250% 100%, 100% 100%, 100% 100%;
  background-position: 130% 0, 0 0, 0 0;
  box-shadow: var(--gs-btn-glow), 0 14px 30px -14px var(--gs-accent-ring);
  animation: gs-btn-glint 5s ease-in-out 1.2s infinite,
             gs-btn-spin 4s linear infinite;
  transition: filter var(--gs-dur) var(--gs-ease);
}
.gs-btn--primary::after,
.gs-auth__submit::after,
.gs-aibtn::after {
  content: ''; position: absolute; inset: 8px 10px -4px; z-index: -2;
  border-radius: inherit; pointer-events: none;
  background: conic-gradient(from var(--gs-btn-a), var(--gs-cyan-300),
              var(--gs-blue-500), var(--gs-green-400), var(--gs-cyan-500),
              var(--gs-cyan-300));
  filter: blur(18px); opacity: .4;
  animation: gs-btn-spin 6s linear infinite;
  transition: opacity .3s var(--gs-ease);
}
/* smaller buttons, a smaller aurora */
.gs-btn--primary.gs-btn--sm::after,
.gs-aibtn::after { inset: 6px 8px -3px; filter: blur(12px); }

.gs-btn--primary:hover,
.gs-auth__submit:hover,
.gs-aibtn:hover {
  transform: translateY(-2px); filter: none; color: var(--gs-btn-ink);
}
.gs-btn--primary:hover::before,
.gs-auth__submit:hover::before,
.gs-aibtn:hover::before { filter: brightness(1.1); }
.gs-btn--primary:hover::after,
.gs-auth__submit:hover::after,
.gs-aibtn:hover::after { opacity: .7; }
.gs-btn--primary:active,
.gs-auth__submit:active,
.gs-aibtn:active { transform: translateY(0) scale(.98); }

/* Disabled: no lift, no glow, nothing moving, so it cannot look like it is
   inviting a click. The face and a plain rim; a stopped sweep would leave
   a bright spot. */
.gs-btn--primary:is(:disabled, [aria-disabled='true']),
.gs-auth__submit:disabled { transform: none; }
.gs-btn--primary:is(:disabled, [aria-disabled='true'])::before,
.gs-auth__submit:disabled::before {
  animation: none; filter: none;
  background: var(--gs-btn-face) padding-box,
              linear-gradient(var(--gs-btn-line), var(--gs-btn-line)) border-box;
}
.gs-btn--primary:is(:disabled, [aria-disabled='true'])::after,
.gs-auth__submit:disabled::after { display: none; }

/* The Personal AI button: the same rounded rectangle, not a pill, in
   proportion to its 36px height, and the same edge as every other button
   on every screen. (It used to wear an extra ring on Kate's own screens to
   say "you are here"; that ring read as a second border next to the rest.) */
.gs-aibtn { border-radius: 10px; }
.gs-aibtn.is-active { background: none; border-color: transparent;
                      color: var(--gs-btn-ink); }

/* The quieter buttons: the same shape and the same springy lift. */
.gs-btn,
.gs-auth__btn-outline {
  transition: transform .35s cubic-bezier(.2, 1.4, .35, 1),
              background var(--gs-dur) var(--gs-ease),
              border-color var(--gs-dur) var(--gs-ease),
              color var(--gs-dur) var(--gs-ease);
}
.gs-btn:not(:disabled):not([aria-disabled='true']):hover,
.gs-auth__btn-outline:hover { transform: translateY(-2px); }
.gs-btn:not(:disabled):not([aria-disabled='true']):active,
.gs-auth__btn-outline:active { transform: translateY(0) scale(.98); }

/* An arrow on any button steps forward on hover, as it does on Kate's. */
.gs-btn .ti-arrow-right,
.gs-auth__submit .ti-arrow-right,
.gs-aibtn .ti-arrow-right {
  display: inline-block; transition: transform .3s var(--gs-ease);
}
.gs-btn:hover .ti-arrow-right,
.gs-auth__submit:hover .ti-arrow-right,
.gs-aibtn:hover .ti-arrow-right { transform: translateX(3px); }

@keyframes gs-btn-spin { to { --gs-btn-a: 360deg; } }
@keyframes gs-btn-glint {
  0%, 70% { background-position: 130% 0, 0 0, 0 0; }
  100%    { background-position: -30% 0, 0 0, 0 0; }
}

@media (prefers-reduced-motion: reduce) {
  .gs-btn, .gs-auth__submit, .gs-auth__btn-outline, .gs-aibtn,
  .gs-btn--primary::before, .gs-btn--primary::after,
  .gs-auth__submit::before, .gs-auth__submit::after,
  .gs-aibtn::before, .gs-aibtn::after { animation: none; transition: none; }
}

/* ── the store's public profile (`stores/profile.html`) ─────────────────
   Two columns whose rows line up, top and bottom, rather than eight
   full-width cards that were each a line of text and an Edit button a
   screen away. Every cell is a column of one or two cards; the grid
   stretches both cells of a row to the taller one's height and the last
   card in each cell grows to fill it, so both sides of a row always end
   on the same line, whatever each card holds.

   A section card's one action, its Edit button, sits in the card's top
   right, level with the title, in every card and at every width. */
.gs-pf {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gs-space-5); align-items: stretch;
}
.gs-pf__cell { display: flex; flex-direction: column; gap: var(--gs-space-5);
               min-width: 0; }
.gs-pf__cell > .gs-card { margin-bottom: 0; position: relative; }
.gs-pf__cell > .gs-card:last-child { flex: 1 1 auto; }
.gs-pf .gs-card__head:has(+ .gs-card__body .gs-splitrow > .gs-splitrow__side > :only-child) {
  padding-right: 104px;
}
.gs-pf .gs-splitrow__side:has(> :only-child) {
  position: absolute; top: 14px; right: 16px; width: auto;
}
@media (max-width: 991.98px) {
  .gs-pf { grid-template-columns: minmax(0, 1fr); }
}

/* ── the vendor breakdown lines up (`.rcx-money`) ───────────────────────
   Each supplier row was a grid of its own, so a row that could not be
   compared (name, then the reason) sized its name column to that one name:
   the reason started right after "NAPA" on one row and after "AdvancePro"
   on the next. The rows now share one set of columns (subgrid): the name
   column is as wide as the longest name, so every reason starts on the
   same line and every figure ends on the same line. A row with a figure
   keeps its layout (name, count, share, money); the phone layout below
   576px keeps its own two-line grid. */
@media (min-width: 576px) {
  .rcx-money__grid {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr) auto 44px 84px;
    column-gap: var(--gs-space-3);
  }
  .rcx-money__row {
    grid-column: 1 / -1; grid-template-columns: subgrid;
  }
  .rcx-money__row > .rcx-money__name  { grid-column: 1 / 3; }
  .rcx-money__row > .rcx-money__meta  { grid-column: 3; }
  .rcx-money__row > .rcx-money__share { grid-column: 4; }
  .rcx-money__row > .rcx-money__val   { grid-column: 5; }
  .rcx-money__row--blind > .rcx-money__name { grid-column: 1; }
  .rcx-money__row--blind > .rcx-money__meta { grid-column: 2 / 5; }
}

/* ── the sign-in pages' other buttons, in the same material ─────────────
   "Continue with Google" and the outline buttons on the check-email and
   activate pages (`.gs-auth__google`, `.gs-auth__btn-outline`) get the
   "Talk to me" treatment too: the light going round the rim, the glint
   crossing the face, a soft glow under it and the springy lift. They keep
   a neutral face rather than the teal one, so the teal Sign in button is
   still THE action on the card (and Google's own button is not repainted
   in our colour). Same layers as the primary buttons above: face and
   glint on ::before, the glow on ::after, both under the label. */
.gs-auth__google:not(.gs-auth__google--off),
.gs-auth__btn-outline {
  position: relative; isolation: isolate;
  background: none; border-color: transparent;
  transition: transform .35s cubic-bezier(.2, 1.4, .35, 1);
}
.gs-auth__google:not(.gs-auth__google--off)::before,
.gs-auth__btn-outline::before {
  content: ''; position: absolute; inset: -1px; z-index: -1;
  border: 1.5px solid transparent; border-radius: inherit;
  background:
    linear-gradient(105deg, transparent 42%,
      color-mix(in srgb, var(--gs-white) 18%, transparent) 50%,
      transparent 58%) padding-box no-repeat,
    linear-gradient(var(--gs-surface), var(--gs-surface)) padding-box,
    conic-gradient(from var(--gs-btn-a), var(--gs-line-strong) 0deg,
      var(--gs-cyan-400) 60deg, var(--gs-accent) 90deg,
      var(--gs-line-strong) 150deg, var(--gs-line-strong) 360deg) border-box;
  background-size: 250% 100%, 100% 100%, 100% 100%;
  background-position: 130% 0, 0 0, 0 0;
  box-shadow: 0 10px 24px -14px var(--gs-accent-ring);
  animation: gs-btn-glint 5s ease-in-out 1.2s infinite,
             gs-btn-spin 4s linear infinite;
  transition: filter var(--gs-dur) var(--gs-ease);
}
.gs-auth__google:not(.gs-auth__google--off)::after,
.gs-auth__btn-outline::after {
  content: ''; position: absolute; inset: 8px 10px -4px; z-index: -2;
  border-radius: inherit; pointer-events: none;
  background: conic-gradient(from var(--gs-btn-a), var(--gs-cyan-300),
              var(--gs-blue-500), var(--gs-green-400), var(--gs-cyan-500),
              var(--gs-cyan-300));
  filter: blur(18px); opacity: .16;
  animation: gs-btn-spin 6s linear infinite;
  transition: opacity .3s var(--gs-ease);
}
.gs-auth__google:not(.gs-auth__google--off):hover,
.gs-auth__btn-outline:hover {
  transform: translateY(-2px); background: none; border-color: transparent;
}
.gs-auth__google:not(.gs-auth__google--off):hover::before,
.gs-auth__btn-outline:hover::before { filter: brightness(1.08); }
.gs-auth__google:not(.gs-auth__google--off):hover::after,
.gs-auth__btn-outline:hover::after { opacity: .4; }
.gs-auth__google:not(.gs-auth__google--off):active,
.gs-auth__btn-outline:active { transform: translateY(0) scale(.98); }

@media (prefers-reduced-motion: reduce) {
  .gs-auth__google, .gs-auth__btn-outline,
  .gs-auth__google::before, .gs-auth__google::after,
  .gs-auth__btn-outline::before, .gs-auth__btn-outline::after {
    animation: none; transition: none;
  }
}

/* ── the sign-in card's edge, in the same light ─────────────────────────
   The light that goes round every button's rim goes round the auth card's
   edge too (sign in, sign up, check your email, reset, the error pages):
   a short bright arc travelling round the border, on the same turning
   angle the buttons use, slower because the card is so much bigger. A
   ring drawn just inside the card's own 1px border (the card clips its
   overflow), masked so only the ring shows, and never in the way of a
   click. Still under reduced motion it is not drawn at all: a stopped arc
   is just a bright mark on one corner. */
.gs-auth__card::after {
  content: ''; position: absolute; inset: 0; z-index: 5;
  border-radius: inherit; padding: 1.5px; pointer-events: none;
  background: conic-gradient(from var(--gs-btn-a),
              transparent 0deg, transparent 200deg,
              color-mix(in srgb, var(--gs-cyan-400) 70%, transparent) 250deg,
              var(--gs-white) 285deg,
              color-mix(in srgb, var(--gs-cyan-300) 70%, transparent) 315deg,
              transparent 360deg);
  -webkit-mask: linear-gradient(var(--gs-mask-solid) 0 0) content-box,
                linear-gradient(var(--gs-mask-solid) 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: gs-btn-spin 7s linear infinite;
}
[data-theme='light'] .gs-auth__card::after {
  background: conic-gradient(from var(--gs-btn-a),
              transparent 0deg, transparent 200deg,
              color-mix(in srgb, var(--gs-cyan-500) 60%, transparent) 250deg,
              var(--gs-cyan-600) 285deg,
              color-mix(in srgb, var(--gs-cyan-500) 60%, transparent) 315deg,
              transparent 360deg);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) .gs-auth__card::after {
    background: conic-gradient(from var(--gs-btn-a),
                transparent 0deg, transparent 200deg,
                color-mix(in srgb, var(--gs-cyan-500) 60%, transparent) 250deg,
                var(--gs-cyan-600) 285deg,
                color-mix(in srgb, var(--gs-cyan-500) 60%, transparent) 315deg,
                transparent 360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .gs-auth__card::after { display: none; }
}

/* ── link tiles, stacked (`components/layout/link_tile.html`) ──────────
   A tile was one row: icon, text, then the badge ("Refreshed 04:10") or
   the arrow. In a six-across grid each tile is about 290px, and the icon
   and badge took theirs first, leaving the text a sliver: titles broke
   mid-word ("Reven / ue") and descriptions ran one word a line, each tile
   to a different height and a different line.

   Stacked instead: the icon and the badge (or arrow) along the top, then
   the title and the description across the tile's full width. Every
   tile's parts start on the same lines, the text has the whole width to
   wrap in, and a title only breaks inside a word when one word alone is
   wider than the tile. Columns are a little wider so a tile is never
   squeezed. Used on the reporting dashboards, payables, statements,
   churn, estimates, inspections, marketing, resources and promotions. */
.gs-tiles {
  grid-template-columns: repeat(auto-fill, minmax(min(250px, 100%), 1fr));
}
.gs-tile {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "mark side" "body body";
  align-items: center; align-content: start;
  column-gap: var(--gs-space-3); row-gap: var(--gs-space-3);
}
.gs-tile > .gs-tile__mark { grid-area: mark; }
.gs-tile > .gs-tile__body { grid-area: body; align-self: start; }
.gs-tile > .gs-badge,
.gs-tile > .gs-tile__go { grid-area: side; justify-self: end; }
.gs-tile__title { overflow-wrap: break-word; }

/* ── every page's entrance (parts.js, `initEntrance`) ───────────────────
   Until the script starts the cards' flight they are held invisible, so
   they never paint in their final places first and then jump to the start
   of the animation. If the script never runs (blocked, errored), they
   reveal themselves after 2.5s. Under reduced motion none of this
   applies: the cards are simply there. The list of cards is the same one
   parts.js animates (`ENTER_CARDS`); keep the two in step. */
@media (prefers-reduced-motion: no-preference) {
  [data-gx-enter].gx-entering :is(.gx-card, .gs-card, .gs-stat, .gs-kpi,
    .gs-metric, .gs-notepanel, .gs-soon, .gs-storepick, .gs-tile,
    .rcx-strip, .rcx-hero, .rcx-tile, .rcx-card, .rcx-note,
    .rcx-old, .rc-tab, .gs-ab__palette, .gs-ab__person,
    .rc-filters, .gs-toolbar, .gx-filters, .rc-period,
    .gs-welcome__copy > *) {
    opacity: 0;
    animation: gx-enter-failsafe 0s linear 2.5s forwards;
  }
}
@keyframes gx-enter-failsafe { to { opacity: 1; } }
/* While the cards are in flight, and only then: see `.gx-flying` in parts.js. */
html.gx-flying { overflow-x: hidden; }

/* ── rings and gauges sweeping in (gx-enter.js, `fillCharts`) ──────────
   While a ring fills, a clockwise conic mask is laid over its own annulus
   mask and opened from 0 to 360deg. The class is removed when the sweep
   ends, so the ring is exactly its own CSS again. */
@property --gx-sweep {
  syntax: '<angle>'; inherits: false; initial-value: 360deg;
}
.rc-ring__arc.gx-sweep {
  -webkit-mask: radial-gradient(farthest-side, transparent 61%, var(--gs-mask-solid) 62%),
                conic-gradient(var(--gs-mask-solid) var(--gx-sweep), transparent 0);
  -webkit-mask-composite: source-in;
          mask: radial-gradient(farthest-side, transparent 61%, var(--gs-mask-solid) 62%),
                conic-gradient(var(--gs-mask-solid) var(--gx-sweep), transparent 0);
          mask-composite: intersect;
}
.gs-gauge__ring.gx-sweep {
  -webkit-mask: radial-gradient(farthest-side, transparent 76%, var(--gs-mask-solid) 77%),
                conic-gradient(var(--gs-mask-solid) var(--gx-sweep), transparent 0);
  -webkit-mask-composite: source-in;
          mask: radial-gradient(farthest-side, transparent 76%, var(--gs-mask-solid) 77%),
                conic-gradient(var(--gs-mask-solid) var(--gx-sweep), transparent 0);
          mask-composite: intersect;
}

/* ── breadcrumbs: where this page sits, above its title ───────────────
   The console style (AWS, Azure): one line of links in the accent colour,
   small chevrons between them, the page itself last in full ink. No box
   around it — the colour is what makes it read as navigation. It never
   wraps; a long name ends in an ellipsis and keeps its full text in
   `title`. On a phone it is one accent link back to the step above. */
.gs-crumbs { margin: 0 0 var(--gs-space-3); min-width: 0; font-size: 14px;
             line-height: 1.4; }
.gs-crumbs__list { display: flex; align-items: center; flex-wrap: nowrap; gap: 8px;
                   margin: 0; padding: 0; list-style: none; min-width: 0; }
.gs-crumbs__item { display: inline-flex; align-items: center; gap: 8px;
                   min-width: 0; white-space: nowrap; }
.gs-crumbs__sep { flex: none; font-size: 15px; color: var(--gs-muted); opacity: .8; }
.gs-crumbs__link { min-width: 0; max-width: 280px; overflow: hidden; text-overflow: ellipsis;
                   color: var(--gs-accent); font-weight: 600; text-decoration: none;
                   border: 0; background: none; padding: 0; font: inherit; font-weight: 600;
                   cursor: pointer; }
.gs-crumbs__link:hover { text-decoration: underline; text-underline-offset: 3px; }
.gs-crumbs__link:focus-visible { outline: 2px solid var(--gs-accent); outline-offset: 2px;
                                 border-radius: 4px; }
.gs-crumbs__current { min-width: 0; max-width: 320px; overflow: hidden;
                      text-overflow: ellipsis; color: var(--gs-ink); font-weight: 700; }
.gs-crumbs__dots { font-weight: 800; letter-spacing: .08em; }
/* The steps folded into "…". */
.gs-crumbs__more { position: relative; }
.gs-crumbs__menu { position: absolute; top: calc(100% + 8px); left: 0; z-index: 60;
  min-width: 220px; padding: 6px; border-radius: var(--gs-radius);
  background: linear-gradient(var(--gs-glass-bg-2), var(--gs-glass-bg-2)), var(--gs-surface);
  border: 1px solid var(--gs-glass-border);
  -webkit-backdrop-filter: var(--gs-glass-blur); backdrop-filter: var(--gs-glass-blur);
  box-shadow: var(--gs-glass-shadow), inset 0 1px 0 var(--gs-glass-edge);
  display: flex; flex-direction: column; gap: 2px;
  animation: gs-pop-in var(--gs-dur) var(--gs-ease); }
.gs-crumbs__menu[hidden] { display: none; }
.gs-crumbs__menuitem { padding: 9px 12px; border-radius: var(--gs-radius-sm);
  color: var(--gs-accent); font-weight: 600; font-size: 13.5px; text-decoration: none;
  white-space: nowrap; }
.gs-crumbs__menuitem:hover { background: var(--gs-accent-wash); }
.gs-crumbs__back { display: none; }
@media (max-width: 599.98px) {
  .gs-crumbs__list { display: none; }
  .gs-crumbs__back { display: inline-flex; align-items: center; gap: 4px; max-width: 100%;
                     color: var(--gs-accent); font-weight: 700; text-decoration: none; }
  .gs-crumbs__back span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .gs-crumbs__back i { flex: none; font-size: 16px; }
}

/* ══════════════════════════════════════════════════════════════════════
   Exceptions and Invoices (PILLAR1_UI_PATTERNS.md §2, §3, §8)

   The inbox list, the case page (and the drawer it opens in), the match
   queue and the compare view. Built from the card material every Parts
   screen uses (`.gx-card`), tokens only. Nothing here draws a coloured bar
   down the edge of a row or a filled dot beside a word: a state is a word
   in a badge, an amount's sign is its text, and the tone is by KIND,
   never urgency. Lists answer to their card, not the screen
   (`container-type`), so a drawer and a phone get the same stacked rows.
   ══════════════════════════════════════════════════════════════════════ */

/* Figures on these screens (Exceptions, an exception, Invoices, an
   invoice line) are set in the page's own face with tabular digits, not in
   `--gs-font-mono`: the mono face is not loaded, so it fell back to
   whatever monospace the machine had, and an amount at stake read as a
   different typeface from the sentence beside it. */

/* Read by a screen reader, not drawn. */
.gs-vh {
  position: absolute !important; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}
/* A badge never wraps (CLAUDE.md, Frontend). */
.gx-nowrap { white-space: nowrap; }
.gx-signed { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* A view tab's count: a quiet figure after the label, as text. */
.gs-pagetab__count {
  font-family: var(--gs-font); font-size: var(--gs-fs-xs);
  font-weight: 700; padding: 1px 7px; border-radius: var(--gs-radius-pill);
  background: var(--gs-surface-3); color: var(--gs-ink-2);
}
.gs-pagetab.is-active .gs-pagetab__count {
  background: var(--gs-surface); color: var(--gs-accent);
  box-shadow: inset 0 0 0 1px var(--gs-accent-line);
}
/* Views of ONE list (Needs a person · Mine · … · Done) are not screens, so
   they do not wear the pill strip the seven tabs wear: they are an
   underlined row that narrows the list beneath, sitting on the list's own
   edge. The same underlined row is every SECOND level under that strip —
   Approvals' requests and receipts, History & settings' six screens — so
   a page never stacks two strips that look alike. */
.gs-pagetabs.gx-views {
  width: 100%; max-width: 100%; margin: 0; padding: 0 2px; gap: 2px;
  flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
  background: none; border: 0; border-bottom: 1px solid var(--gs-line);
  border-radius: 0; box-shadow: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}
.gs-pagetabs.gx-views::-webkit-scrollbar { display: none; }
.gx-views .gs-pagetab { position: relative; flex: none; border-radius: 0;
                        padding: 10px 12px 12px; background: none; }
.gx-views .gs-pagetab:hover { background: none; color: var(--gs-ink); }
.gx-views .gs-pagetab.is-active { background: none; color: var(--gs-ink); }
.gx-views .gs-pagetab.is-active i { color: var(--gs-accent); }
.gx-views .gs-pagetab.is-active::after {
  content: ''; position: absolute; left: 10px; right: 10px; bottom: -1px;
  height: 2px; border-radius: 2px; background: var(--gs-accent);
}
.gx-views .gs-pagetab:focus-visible { outline: 2px solid var(--gs-accent);
                                      outline-offset: -2px;
                                      border-radius: var(--gs-radius-sm); }
.gx-views .gs-pagetab__count { font-variant-numeric: tabular-nums; }

.gx-inbox { display: flex; flex-direction: column; gap: var(--gs-space-3);
            min-width: 0; }
/* History & settings' screens are pages, not views: their underlined strip
   stands on its own under the seven, with the page's rhythm below it. */
.gs-pagetabs.gx-subtabs { margin-bottom: var(--gs-space-4); }

/* ── the filter bar ──────────────────────────────────────────────── */
.gx-fbar { display: flex; flex-direction: column; gap: 10px;
           padding: 12px 16px; }
.gx-fbar__fields { display: flex; flex-wrap: wrap; align-items: flex-end;
                   gap: 10px 12px; }
.gx-fbar__field { display: flex; flex-direction: column; gap: 4px;
                  min-width: 0; }
.gx-fbar__field--search { flex: 1 1 220px; }
.gx-fbar__label { font-size: var(--gs-fs-xs); font-weight: 700;
                  color: var(--gs-muted); }
.gx-fbar input.gx-fbar__search { width: 100%; max-width: none; min-height: 36px;
                              background-image: none; padding-right: 12px; }
/* The filter bar's date boxes: a field, not a pill with a caret. With the
   rest of `filter_bar`'s rules, here, rather than in the screen that used
   it first. */
.gx-fbar input.gx-fbar__date { background-image: none; padding-right: 12px;
                               cursor: text; min-width: 0; }
.gx-fbar .gx-select { min-height: 36px; max-width: 220px; }
/* The selects' wrapper is invisible to layout on a wide screen, so the
   fields sit in one row exactly as before; it exists to fold on a phone. */
.gx-fbar__selects { display: contents; }
.gx-fbar .gx-fbar__toggle { display: none; }
.gx-fbar__n { display: inline-grid; place-items: center; min-width: 20px;
              height: 20px; padding: 0 6px; border-radius: var(--gs-radius-pill);
              background: var(--gs-accent-wash); color: var(--gs-accent);
              font-size: var(--gs-fs-xs); font-weight: 800;
              font-variant-numeric: tabular-nums; }
@media (max-width: 767.98px) {
  .gx-fbar { padding: 12px 14px; }
  .gx-fbar.is-foldable .gx-fbar__field--search { order: 1; flex: 1 1 100%; }
  .gx-fbar.is-foldable .gx-fbar__toggle { display: inline-flex; order: 2;
                                          gap: 6px; min-height: 40px; }
  .gx-fbar.is-foldable .gx-fbar__apply { order: 3; margin-left: auto;
                                         min-height: 40px; }
  .gx-fbar.is-foldable .gx-fbar__selects {
    order: 4; display: none; flex: 1 1 100%;
    grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 12px;
    padding-top: 4px; border-top: 1px solid var(--gs-line-soft);
  }
  .gx-fbar.is-foldable.is-open .gx-fbar__selects { display: grid; }
  .gx-fbar.is-foldable .gx-fbar__selects .gx-select { max-width: none;
                                                      width: 100%; }
  .gx-fbar.is-foldable.is-open .gx-fbar__toggle .ti {
    color: var(--gs-accent); }
}
.gx-fbar__chips { display: flex; flex-wrap: wrap; align-items: center;
                  gap: 6px 8px; font-size: var(--gs-fs-sm); }
.gx-fbar__showing { color: var(--gs-muted); font-weight: 600; }
.gx-chip--remove { gap: 6px; }
.gx-chip--remove i { font-size: 13px; }
.gx-fbar__clear { color: var(--gs-accent); font-weight: 700;
                  text-decoration: none; }
.gx-fbar__clear:hover { text-decoration: underline; }

/* ── what the list counts, and how it is sorted ─────────────────── */
.gx-inbox__sum { display: flex; flex-wrap: wrap; align-items: center;
                 justify-content: space-between; gap: 8px 16px;
                 padding: 0 4px; }
/* "Sort [Largest amount first ▾]" on one line with the count it sorts. */
.gx-inbox__sortfield { flex-direction: row; align-items: center; gap: 8px; }
.gx-inbox__count { margin: 0; color: var(--gs-ink-2);
                   font-size: var(--gs-fs-md); }
.gx-inbox__count b { color: var(--gs-ink); font-variant-numeric: tabular-nums; }
.gx-inbox__sort { margin: 0; }
.gx-inbox__note { display: flex; gap: 8px; align-items: flex-start;
                  margin: 0; padding: 10px 14px; color: var(--gs-ink-2);
                  background: var(--gs-info-wash);
                  border-radius: var(--gs-radius); font-size: var(--gs-fs-sm); }
.gx-inbox__note i { color: var(--gs-info); margin-top: 2px; }
.gx-inbox__empty { text-align: center; padding: var(--gs-space-7) var(--gs-space-4); }
.gx-inbox__emptyicon { font-size: 30px; color: var(--gs-success); }
.gx-inbox__emptytitle { margin: 8px 0 4px; font-weight: 800;
                        color: var(--gs-ink); }
.gx-inbox__emptytext { margin: 0; color: var(--gs-muted); }
.gx-inbox__emptytext a { color: var(--gs-accent); font-weight: 700; }

/* ── the list of exceptions ──────────────────────────────────────── */
.gx-cases { list-style: none; margin: 0; padding: 0;
            container: gxcases / inline-size; }
.gx-crow {
  display: grid; align-items: center; column-gap: 14px; row-gap: 4px;
  grid-template-columns: auto auto minmax(0, 1fr) minmax(150px, 190px)
                         minmax(120px, 170px) minmax(96px, auto);
  grid-template-areas: "pick kind main who due amount";
  padding: 14px 18px; border-top: 1px solid var(--gs-line-soft);
  position: relative;
}
.gx-crow:first-child { border-top: 0; }
.gx-crow:hover { background: var(--gs-surface-2); }
.gx-crow:has(input:checked) { background: var(--gs-accent-wash); }
.gx-crow__pick { grid-area: pick; display: grid; place-items: center;
                 width: 28px; height: 44px; cursor: pointer; }
.gx-crow__pick input { width: 18px; height: 18px;
                       accent-color: var(--gs-accent); cursor: pointer; }
.gx-crow__kind { grid-area: kind; width: 34px; height: 34px;
                 display: grid; place-items: center; font-size: 17px;
                 border-radius: var(--gs-radius-sm);
                 background: var(--gs-accent-wash); color: var(--gs-accent); }
.gx-crow__main { grid-area: main; min-width: 0; display: flex;
                 flex-direction: column; gap: 3px; }
.gx-crow__title { font-weight: 800; color: var(--gs-ink);
                  text-decoration: none; overflow-wrap: anywhere; }
.gx-crow__title:hover { color: var(--gs-accent); text-decoration: underline; }
.gx-crow__title:focus-visible { outline: 2px solid var(--gs-accent);
                                outline-offset: 2px; border-radius: 4px; }
.gx-crow__meta { display: flex; flex-wrap: wrap; gap: 0 8px;
                 font-size: var(--gs-fs-xs); color: var(--gs-muted);
                 font-weight: 600; }
/* The separator follows the item before it with no space between, so a
   wrap can never start a line with it. */
.gx-crow__meta span:not(:last-child)::after { content: '·'; margin-left: 8px; }
.gx-crow__sentence { margin: 2px 0 0; font-size: var(--gs-fs-sm);
                     color: var(--gs-ink-2); line-height: 1.45; }
.gx-crow__tags { display: flex; flex-wrap: wrap; gap: 6px; }
.gx-crow__tags:empty { display: none; }
.gx-crow__tags i { font-size: 12px; }
.gx-crow__who { grid-area: who; display: flex; flex-direction: column;
                gap: 4px; min-width: 0; }
.gx-crow__age { font-size: var(--gs-fs-xs); color: var(--gs-muted); }
.gx-crow__due { grid-area: due; min-width: 0; }
.gx-crow__amount { grid-area: amount; text-align: right;
                   font-family: var(--gs-font); font-weight: 700;
                   font-variant-numeric: tabular-nums; white-space: nowrap;
                   color: var(--gs-ink); }
.gx-crow.is-closed .gx-crow__amount { color: var(--gs-muted); }
.gx-crow--compact { grid-template-columns: auto minmax(0, 1fr) auto;
                    grid-template-areas: "kind main amount"; }

/* Stacked: title and amount, then the sentence, then who and when — the
   order a phone reader needs, in a card-width test rather than a screen
   width, so the drawer and a half-width card agree. */
@container gxcases (max-width: 760px) {
  .gx-crow {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "pick main amount"
                         "pick who  who"
                         "pick due  due";
    padding: 12px 14px; align-items: start;
  }
  .gx-crow__kind { display: none; }
  .gx-crow__who { flex-direction: row; flex-wrap: wrap; align-items: center;
                  gap: 4px 12px; }
  .gx-crow__amount { padding-top: 2px; }
}

/* ── who, and when ───────────────────────────────────────────────── */
.gx-owner { display: inline-flex; align-items: center; gap: 7px;
            min-width: 0; font-size: var(--gs-fs-sm); color: var(--gs-ink-2); }
.gx-owner__disc { width: 24px; height: 24px; flex: none; border-radius: 50%;
                  display: grid; place-items: center; font-size: 10px;
                  font-weight: 800; letter-spacing: .02em;
                  background: var(--gs-surface-3); color: var(--gs-ink-2);
                  border: 1px solid var(--gs-line); }
.gx-owner__name { overflow: hidden; text-overflow: ellipsis;
                  white-space: nowrap; font-weight: 600; }
.gx-owner__me { color: var(--gs-muted); font-weight: 500; }
.gx-owner--none { color: var(--gs-muted); }
.gx-owner--none .gx-owner__disc { background: none; border-style: dashed; }

.gx-due { display: inline-flex; align-items: center; gap: 6px;
          white-space: nowrap; font-size: var(--gs-fs-xs); font-weight: 700;
          padding: 3px 9px; border-radius: var(--gs-radius-pill);
          color: var(--gs-muted); background: var(--gs-surface-3); }
.gx-due i { font-size: 13px; }
.gx-due--warning { color: var(--gs-warning); background: var(--gs-warning-wash); }
/* Never past its column into the amount beside it: a chip longer than
   its column ends in an ellipsis inside its own pill (the whole sentence
   is its `title`) rather than paint over a figure. The list uses the
   short form, so this is the guard, not the layout. */
.gx-crow__due .gx-due { max-width: 100%; }
.gx-due__text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
@container gxcases (max-width: 420px) {
  .gx-due__text { overflow: visible; }
}
@container gxcases (max-width: 420px) {
  .gx-due { white-space: normal; }
}

/* ── the bar for a selection ─────────────────────────────────────── */
.gx-batch {
  position: sticky; bottom: max(12px, env(safe-area-inset-bottom));
  z-index: 20; display: flex; flex-wrap: wrap; align-items: flex-end;
  gap: 10px 16px; padding: 12px 16px;
  background: var(--gs-glass-bg-2); color: var(--gs-ink);
  -webkit-backdrop-filter: var(--gs-glass-blur);
  backdrop-filter: var(--gs-glass-blur);
  border: 1px solid var(--gs-glass-border);
  border-radius: var(--gs-card-radius, var(--gs-radius-lg));
  box-shadow: var(--gs-glass-shadow);
}
.gx-batch[hidden] { display: none; }
.gx-batch__lead { display: flex; align-items: center; gap: 12px;
                  margin-right: auto; align-self: center; }
.gx-batch__count { margin: 0; display: flex; flex-direction: column;
                   gap: 2px; }
.gx-batch__count output { font-weight: 800; }
.gx-batch__max { font-size: var(--gs-fs-xs); color: var(--gs-muted); }
.gx-batch__act { display: flex; align-items: flex-end; gap: 8px;
                 flex-wrap: wrap; }
.gx-batch__field { display: flex; flex-direction: column; gap: 4px; }
.gx-batch__label { font-size: var(--gs-fs-xs); font-weight: 700;
                   color: var(--gs-muted); }
.gx-batch .gx-select { min-height: 34px; max-width: 220px; }
.gx-batch__clear { background: none; border: 0; padding: 8px 4px;
                   min-height: 44px; color: var(--gs-accent);
                   font-weight: 700; cursor: pointer; }
/* Between a phone and a desktop Kate's orb keeps the bottom-right corner
   (from 992px it docks in a lane beside the page — kate.css), so the bar
   keeps clear of it rather than parking its last button under the orb. */
@media (min-width: 768px) and (max-width: 991.98px) {
  body:has(.gs-kate-fab) .gx-batch { margin-right: 128px; }
}
/* A phone: docked at the bottom edge, and compact — one line for the
   count, one line per action ([Me ▾] [Assign], [Choose a reason ▾]
   [Resolve]). It used to stand 128px up the screen to clear Kate's orb,
   240px tall, over the middle of the list being selected from. While a
   selection is open the bar IS the floating action, so the orb steps
   aside, the way it does for the setup panel; it is back the moment the
   selection is cleared. Each select keeps its label for a screen reader
   ("Assign to", "Resolve as"); the button beside it says the rest. */
@media (max-width: 767.98px) {
  body:has(.gx-batch:not([hidden])) .gs-kate-fab { visibility: hidden; }
  .gx-batch { gap: 8px; padding: 10px 12px; }
  .gx-batch__lead { width: 100%; margin-right: 0;
                    justify-content: space-between; }
  .gx-batch__count { flex-direction: row; flex-wrap: wrap;
                     align-items: baseline; gap: 2px 8px; }
  .gx-batch__clear { min-height: 36px; }
  .gx-batch__act { width: 100%; flex-wrap: nowrap; align-items: center; }
  .gx-batch__field { flex: 1 1 auto; min-width: 0; }
  .gx-batch__label {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  .gx-batch .gx-select { max-width: none; width: 100%; min-height: 44px; }
  .gx-batch .gs-btn { flex: none; min-height: 44px; }
}

/* ── the drawer ──────────────────────────────────────────────────── */
/* `max-width` and `max-height` are named because a modal <dialog> carries
   the browser's own (100% less 2em and 6px): without them the "full-screen"
   sheet on a phone kept a 38px strip of the list showing down its left. */
.gs-modal.gx-drawer {
  margin: 0 0 0 auto; height: 100dvh; max-height: 100dvh;
  width: min(820px, 100vw); max-width: 100vw;
  border-radius: var(--gs-radius-xl) 0 0
  var(--gs-radius-xl); display: none; flex-direction: column;
}
.gs-modal.gx-drawer[open] { display: flex;
  animation: gx-drawer-in var(--gs-dur) var(--gs-ease); }
@keyframes gx-drawer-in {
  from { opacity: 0; transform: translateX(24px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .gs-modal.gx-drawer[open] { animation: none; }
}
.gx-drawer__bar { display: flex; align-items: center; justify-content:
                  flex-end; gap: 10px; padding: 10px 16px;
                  border-bottom: 1px solid var(--gs-line);
                  background: var(--gs-surface-2); flex: none; }
.gx-drawer__bar .gs-modal__x { min-width: 44px; min-height: 44px; }
.gx-drawer__slot { flex: 1 1 auto; overflow-y: auto; padding: 18px 20px 28px;
                   overscroll-behavior: contain; }
.gx-drawer__slot [data-gs-drawer-hide] { display: none; }
.gx-drawer__loading { color: var(--gs-muted); padding: 24px 4px; }
@media (max-width: 767.98px) {
  .gs-modal.gx-drawer { width: 100vw; border-radius: 0; }
  .gx-drawer__slot { padding: 14px 16px 24px; }
}

/* ── one exception: the page, and the drawer's contents ─────────── */
.gx-case { container: gxcase / inline-size; display: flex;
           flex-direction: column; gap: var(--gs-space-4); min-width: 0; }
.gx-back { display: inline-flex; align-items: center; gap: 6px;
           align-self: flex-start; color: var(--gs-muted); font-weight: 700;
           text-decoration: none; font-size: var(--gs-fs-sm); }
.gx-back:hover { color: var(--gs-accent); }
.gx-case__head { display: flex; flex-direction: column; gap: 8px; }
.gx-case__kicker { display: flex; flex-wrap: wrap; align-items: center;
                   gap: 8px; }
.gx-case__kind { display: inline-flex; align-items: center; gap: 7px;
                 font-size: var(--gs-fs-sm); font-weight: 800;
                 color: var(--gs-accent); }
.gx-case__kind i { font-size: 16px; width: 28px; height: 28px;
                   display: grid; place-items: center;
                   border-radius: var(--gs-radius-sm);
                   background: var(--gs-accent-wash); }
.gx-case__title { margin: 0; font-size: var(--gs-fs-2xl); font-weight: 800;
                  letter-spacing: -.01em; overflow-wrap: anywhere; }
.gx-case__title:focus { outline: none; }
.gx-case__title:focus-visible { outline: 2px solid var(--gs-accent);
                                outline-offset: 4px; border-radius: 6px; }
.gx-case__summary { margin: 0; color: var(--gs-ink-2);
                    font-size: var(--gs-fs-md); line-height: 1.5;
                    max-width: 70ch; }
.gx-case__stake { margin: 2px 0 0; display: flex; flex-wrap: wrap;
                  align-items: baseline; gap: 4px 10px; }
.gx-case__stakev { font-family: var(--gs-font); font-weight: 800;
                   font-size: var(--gs-fs-xl);
                   font-variant-numeric: tabular-nums; }
.gx-case__stakel { color: var(--gs-muted); font-size: var(--gs-fs-sm); }
.gx-case__note { display: flex; gap: 10px; align-items: flex-start;
                 padding: 12px 14px; border-radius: var(--gs-radius);
                 background: var(--gs-surface-2); color: var(--gs-ink-2);
                 border: 1px solid var(--gs-line); font-size: var(--gs-fs-sm);
                 line-height: 1.45; }
.gx-case__note > i { font-size: 18px; color: var(--gs-muted); margin-top: 1px; }
.gx-case__note b { color: var(--gs-ink); }
.gx-case__note--info { background: var(--gs-info-wash); border-color: transparent; }
.gx-case__note--info > i { color: var(--gs-info); }

/* The drawer and a phone: one column, in the order the page is written —
   what was found, what to do, the timeline, details. */
.gx-case__grid { display: flex; flex-direction: column;
                 gap: var(--gs-space-4); min-width: 0; }
.gx-case__sec { min-width: 0; padding: 16px 18px;
                display: flex; flex-direction: column; gap: 12px; }
/* Wide: two columns that keep that source order. Evidence, history and
   details float left and the actions float right, so each column is only
   as tall as its own cards — a grid row would stretch the shorter side to
   the taller and leave a hole under the evidence. Details sit under the
   timeline rather than under the actions: the resolve form, open by
   default, already makes that column the taller one, and with details
   beneath it the page ended in a 600px column beside nothing. */
@container gxcase (min-width: 901px) {
  .gx-case__grid { display: flow-root; gap: 0;
                   margin-bottom: calc(-1 * var(--gs-space-4)); }
  .gx-case__grid > .gx-case__sec { margin-bottom: var(--gs-space-4); }
  .gx-case__sec--found, .gx-case__sec--time, .gx-case__sec--facts {
    float: left; clear: left;
    width: calc((100% - var(--gs-space-4)) * .615);
  }
  /* `--side`: a card that belongs under the right-hand one instead — the
     invoice line's details, whose right column is otherwise one short
     list of exceptions. */
  .gx-case__sec--act, .gx-case__sec--side {
    float: right; clear: right;
    width: calc((100% - var(--gs-space-4)) * .385);
  }
}

/* The way to the actions, kept at the bottom of a narrow screen until they
   are reached. A zero-height sticky strip at the end of the page, so it
   takes no room of its own; the pill hangs above it. */
.gx-case__jump { display: none; }
@container gxcase (max-width: 900px) {
  .gx-case__jump {
    display: flex; justify-content: center; height: 0;
    position: sticky; bottom: max(14px, env(safe-area-inset-bottom));
    z-index: 3; margin-top: calc(-1 * var(--gs-space-4));
    pointer-events: none;
    transition: opacity var(--gs-dur) var(--gs-ease),
                visibility var(--gs-dur);
  }
  .gx-case__jump.is-reached { opacity: 0; visibility: hidden; }
  .gx-case__jump a {
    pointer-events: auto; transform: translateY(-100%);
    display: inline-flex; align-items: center; gap: 8px; flex: none;
    min-height: 44px; padding: 0 20px; border-radius: var(--gs-radius-pill);
    font-weight: 800; text-decoration: none; white-space: nowrap;
    color: var(--gs-on-accent); background: var(--gs-accent);
    box-shadow: var(--gs-shadow-md);
  }
  .gx-case__jump a:focus-visible { outline: 2px solid var(--gs-ink);
                                   outline-offset: 3px; }
}
.gx-case__h { margin: 0; font-size: var(--gs-fs-base); font-weight: 800; }
.gx-case__h3 { margin: 6px 0 0; font-size: var(--gs-fs-sm); font-weight: 800;
               color: var(--gs-ink-2); }
.gx-case__line { margin: 0; color: var(--gs-ink-2); font-size: var(--gs-fs-sm);
                 line-height: 1.5; }
.gx-case__line--quiet { color: var(--gs-muted); display: flex; gap: 8px;
                        align-items: flex-start; }
.gx-case__hint { margin: 0; color: var(--gs-muted); font-size: var(--gs-fs-sm); }
/* Money owed — Credits nobody claimed (GAPS A6): the phone card, in the
   shape of the other Money owed lists ("the phone card" in gs-theme.css):
   the credit and what is not matched on the first line, then its date,
   branch and vendor, the buttons across the foot. Loaded after
   gs-theme.css, so these win over its generic three-column card. */
@media (max-width: 767.98px) {
  .gx-owed__list--unclaimed .gs-table tr,
  .gx-owed__list--uvendors .gs-table tr {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .gx-owed__list--unclaimed tr {
    grid-template-areas: "credit left" "date branch" "vendor vendor"
                         "acts acts";
  }
  .gx-owed__list--unclaimed tr:not(:has(> td[data-label="Branch"])) {
    grid-template-areas: "credit left" "date vendor" "acts acts";
  }
  .gx-owed__list--unclaimed td[data-label="Credit"] { grid-area: credit;
                                                      padding: 0 0 6px; }
  .gx-owed__list--unclaimed td[data-label="Credit"]::before { display: none; }
  .gx-owed__list--unclaimed td[data-label="Not matched"] {
    grid-area: left; align-items: flex-end; text-align: right;
    padding: 0 0 6px; font-weight: 700;
  }
  .gx-owed__list--unclaimed td[data-label="Date"] { grid-area: date; }
  .gx-owed__list--unclaimed td[data-label="Branch"] {
    grid-area: branch; align-items: flex-end; text-align: right;
  }
  .gx-owed__list--unclaimed td[data-label="Vendor"] { grid-area: vendor; }
  .gx-owed__list--unclaimed tr:not(:has(> td[data-label="Branch"]))
      td[data-label="Vendor"] { align-items: flex-end; text-align: right; }
  .gx-owed__list--unclaimed td[data-label="Actions"] { grid-area: acts; }
  .gx-owed__list--uvendors tr { grid-template-areas: "vendor amount"
                                                     "count count"; }
  .gx-owed__list--uvendors td[data-label="Vendor"] {
    grid-area: vendor; font-weight: 700; padding: 0 0 6px;
  }
  .gx-owed__list--uvendors td[data-label="Vendor"]::before { display: none; }
  .gx-owed__list--uvendors td[data-label="Not matched"] {
    grid-area: amount; align-items: flex-end; text-align: right;
    font-weight: 700; padding: 0 0 6px;
  }
  .gx-owed__list--uvendors td[data-label="Credit lines"] { grid-area: count; }
}

/* A credit's page: under each item it could be for, its kind, what it
   still owes and where it stands, on a line of its own. */
.gx-owed__pickmeta { display: block; margin-top: 2px; }
/* A credit's "Apply it to an item": the picked item's highlight sat on
   the "Amount to count" label with no space between them. */
.gx-owed__applyform { display: flex; flex-direction: column; gap: 12px; }
.gx-owed__applyform > .gs-btn { align-self: flex-start; }
.gx-owed__applyform > .gs-field { margin: 0; }

/* What the exception opened on Money owed, or the item it follows. */
.gx-case__owed { display: flex; gap: 8px; align-items: flex-start;
                 margin-bottom: 10px; }
.gx-case__owed > i { margin-top: 2px; color: var(--gs-muted); }
.gx-case__item { display: grid; gap: 8px; }
.gx-case__item .gx-case__owed { margin-bottom: 0; }
.gx-placed { display: block; white-space: nowrap; }

/* What the nightly check counted, as words beside figures. */
.gx-ladder { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; }
.gx-ladder span { padding: 6px 10px; border-radius: var(--gs-radius-sm);
                  background: var(--gs-surface-2); font-size: var(--gs-fs-sm);
                  color: var(--gs-ink-2); border: 1px solid var(--gs-line-soft); }
.gx-ladder b { font-family: var(--gs-font); color: var(--gs-ink); }
.gx-ladder__left { background: var(--gs-accent-wash) !important; }

/* ── what to do ──────────────────────────────────────────────────── */
.gx-act__quick { display: flex; flex-wrap: wrap; align-items: center;
                 gap: 8px 10px; }
.gx-act__owner { display: flex; flex-direction: column; gap: 4px;
                 margin-right: auto; min-width: 0; }
.gx-act__ownerl { font-size: var(--gs-fs-xs); font-weight: 700;
                  color: var(--gs-muted); }
.gx-acts { display: flex; flex-direction: column; gap: 8px; }
.gx-act { border: 1px solid var(--gs-line); border-radius: var(--gs-radius);
          background: var(--gs-surface-2); }
.gx-act[open] { background: var(--gs-surface); }
.gx-act__sum { list-style: none; cursor: pointer; display: flex;
               align-items: center; gap: 9px; min-height: 44px;
               padding: 0 14px; font-weight: 700; color: var(--gs-ink); }
.gx-act__sum::-webkit-details-marker { display: none; }
.gx-act__chev { margin-left: auto; color: var(--gs-muted) !important;
                transition: transform var(--gs-dur) var(--gs-ease); }
.gx-act[open] > .gx-act__sum .gx-act__chev { transform: rotate(180deg); }
.gx-act__sum:focus-visible { outline: 2px solid var(--gs-accent);
                             outline-offset: -2px; border-radius: var(--gs-radius); }
.gx-act__sum i { color: var(--gs-accent); font-size: 17px; }
.gx-act--primary { border-color: var(--gs-accent-line); }
.gx-act__form { display: flex; flex-direction: column; gap: 10px;
                padding: 4px 14px 14px; }
.gx-act__form > .gs-btn { align-self: flex-start; }
.gx-act__label { font-size: var(--gs-fs-sm); font-weight: 700;
                 color: var(--gs-ink-2); }
.gx-act__opt { font-weight: 500; color: var(--gs-muted); }
.gx-act__hint { margin: 0; font-size: var(--gs-fs-xs); color: var(--gs-muted); }
.gx-act__said { display: flex; gap: 8px; align-items: flex-start; margin: 0;
                padding: 9px 12px; border-radius: var(--gs-radius-sm);
                background: var(--gs-warning-wash); color: var(--gs-ink);
                font-size: var(--gs-fs-sm); }
.gx-act__said i { color: var(--gs-warning); margin-top: 2px; }

/* Reasons: a radio group; a reason's evidence shows when it is chosen. */
.gx-reasons { border: 0; margin: 0; padding: 0; display: flex;
              flex-direction: column; gap: 4px; }
.gx-reasons__legend { font-size: var(--gs-fs-sm); font-weight: 700;
                      color: var(--gs-ink-2); margin-bottom: 4px; padding: 0; }
.gx-reason { border-radius: var(--gs-radius-sm); }
/* The RADIO: a reason's evidence can hold a ticked box of its own ("use
   this part number for every branch"), and `input:checked` lit that
   reason as chosen when nothing had been chosen at all. */
.gx-reason:has(input[type="radio"]:checked) { background: var(--gs-accent-wash); }
.gx-reason__pick { display: flex; align-items: center; gap: 10px;
                   min-height: 40px; padding: 4px 10px; cursor: pointer;
                   color: var(--gs-ink); }
.gx-reason__pick input { width: 17px; height: 17px; flex: none;
                         accent-color: var(--gs-accent); }
.gx-reason__evidence { display: none; flex-direction: column; gap: 6px;
                       padding: 0 10px 10px 37px; }
.gx-reason:has(input[type="radio"]:checked) .gx-reason__evidence { display: flex; }
@supports not selector(:has(*)) {
  .gx-reason__evidence { display: flex; }
}
.gx-reason__elabel { font-size: var(--gs-fs-xs); font-weight: 700;
                     color: var(--gs-muted); }
.gx-reason__hint { margin: 0; font-size: var(--gs-fs-xs); color: var(--gs-muted); }

/* Suggested repair orders. */
.gx-sugs { list-style: none; margin: 0; padding: 0; display: flex;
           flex-direction: column; gap: 10px; }
.gx-sug { display: flex; flex-direction: column; gap: 10px; padding: 12px 14px;
          border: 1px solid var(--gs-line); border-radius: var(--gs-radius);
          background: var(--gs-surface-2); }
.gx-sug__head { display: flex; flex-wrap: wrap; gap: 4px 10px;
                align-items: baseline; }
.gx-sug__ro { font-weight: 800; font-family: var(--gs-font); }
.gx-sug__part { color: var(--gs-ink-2); }
.gx-sug__state { width: 100%; font-size: var(--gs-fs-xs); color: var(--gs-muted); }
.gx-sug__acts { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.gx-sug__form { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; }

/* Match strength: a word in a badge and the reason beside it. */
.gx-strength { display: flex; flex-wrap: wrap; align-items: baseline;
               gap: 4px 8px; font-size: var(--gs-fs-sm); }
.gx-strength__why { color: var(--gs-ink-2); }
.gx-strength__detail { width: 100%; font-size: var(--gs-fs-xs);
                       color: var(--gs-muted); }
.gx-strength--inline { flex-wrap: nowrap; }

/* Side by side. A differing pair says so in words, with an icon. */
.gx-compare .gs-table th[scope="row"] { text-transform: none; letter-spacing: 0;
                                        font-size: var(--gs-fs-sm);
                                        color: var(--gs-ink-2); }
.gx-compare__differs td { font-weight: 700; color: var(--gs-ink); }
.gx-compare__flag { width: 1%; white-space: nowrap; }
.gx-differs { display: inline-flex; align-items: center; gap: 5px;
              font-size: var(--gs-fs-xs); font-weight: 800;
              color: var(--gs-warning); padding: 2px 8px;
              border-radius: var(--gs-radius-pill);
              background: var(--gs-warning-wash); }
@media (max-width: 767.98px) {
  .gx-compare .gs-table th[scope="row"] { display: block; padding: 0 0 4px;
                                          border: 0; font-weight: 800; }
  .gx-compare .gs-table td.gx-compare__flag:empty,
  .gx-compare .gs-table td.gx-compare__flag:not(:has(*)) { display: none; }
  .gx-compare .gs-table td.gx-compare__flag::before { content: none; }
}

.gx-note { display: flex; flex-direction: column; gap: 8px; }
.gx-note .gs-btn { align-self: flex-start; }
.gx-judged { border-top: 1px solid var(--gs-line-soft); padding-top: 10px; }
.gx-judged > summary { cursor: pointer; font-weight: 700;
                       color: var(--gs-ink-2); min-height: 32px; }
.gx-judged[open] > summary { margin-bottom: 8px; }

/* ── Invoices ─────────────────────────────────────────────────────── */
.gx-rate { display: grid; gap: 8px; padding: 14px 18px; }
.gx-rate__head { display: flex; align-items: baseline; justify-content:
                 space-between; gap: 12px; margin: 0; }
.gx-rate__label { font-weight: 800; color: var(--gs-ink-2);
                  font-size: var(--gs-fs-sm); }
.gx-rate__value { font-family: var(--gs-font); font-weight: 800;
                  font-size: var(--gs-fs-xl); }
.gx-rate__track { display: block; height: 8px; border-radius: 999px;
                  background: var(--gs-chart-track); overflow: hidden; }
.gx-rate__fill { display: block; height: 100%; width: var(--v, 0%);
                 border-radius: inherit; background: var(--gs-chart-1); }
.gx-rate__sub { margin: 0; color: var(--gs-muted); font-size: var(--gs-fs-sm); }
.gx-tablecard { padding: 4px 0; }
/* Figures line up on the decimal point. `data_table` marks a column `num`
   and promises right alignment and tabular digits, but `.gs-table` never
   delivered it, so "Lines add up to" and "Total" sat ragged under left-set
   headings. Kept to these screens' tables and to the table layout: the
   stacked phone card already sets its value on the right. */
@media (min-width: 768px) {
  .gx-tablecard .gs-table th.num, .gx-tablecard .gs-table td.num,
  .gx-case .gs-table th.num, .gx-case .gs-table td.num {
    text-align: right; font-variant-numeric: tabular-nums;
  }
}
.gx-tablecard > .gs-text-sm { padding: 18px; }
.gx-diff { font-family: var(--gs-font); font-weight: 700;
           white-space: nowrap; font-variant-numeric: tabular-nums; }
/* "each" belongs to its figure. `.gs-table td .gs-table__meta` gives every
   quiet line in a table normal wrapping (and `overflow-wrap: anywhere`),
   which is right for a note and broke this one as "+$15.00 eac / h". */
.gs-table td .gx-diff .gs-table__meta { white-space: nowrap;
                                         overflow-wrap: normal; }
/* The match reason is a sentence; left to itself the auto table gave it
   half the card and squeezed the repair order beside it to one word a
   line. A measure keeps the columns that hold figures readable. */
@media (min-width: 768px) {
  /* Badge left, reason beside it wrapping in its own box: allowed to wrap
     as a whole, a long reason dropped under its badge while a short one
     sat beside it, and the column read as two layouts. */
  .gx-tablecard .gx-strength { max-width: 34ch; flex-wrap: nowrap; }
  .gx-tablecard .gx-strength__why { min-width: 0; }
}
.gx-diff--flag { color: var(--gs-warning); }
/* "Tax $0.00 · Fees $0.00 · Cores $0.00": each label stays with its
   figure; a narrow column breaks between pairs, never inside one. */
.gx-pairs > span { white-space: nowrap; }
/* The dot rides at the END of the item before it, inside its no-wrap
   span, so a break leaves "Fees $0.00 ·" ending a line rather than a line
   that starts with a stray "·". */
.gx-pairs > span:not(:last-child)::after { content: ' ·'; }
.gs-table__link { color: var(--gs-accent); font-weight: 700;
                  text-decoration: none; }
.gs-table__link:hover { text-decoration: underline; }
.gx-place { display: flex; flex-direction: column; gap: 10px;
            padding-bottom: 12px; border-bottom: 1px solid var(--gs-line-soft); }
.gx-place:last-of-type { border-bottom: 0; }
.gx-place__head { margin: 0; }
/* "Withdraw this placement" (components/parts/withdraw_placement.html): a
   quiet disclosure under a placement, so a list of placements does not read
   as a list of forms. Inside a table cell it takes the cell's width and
   wraps; the summary is a 44px touch target on a phone. */
.gx-withdraw { font-size: var(--gs-fs-sm); }
.gx-withdraw__sum { list-style: none; cursor: pointer; display: inline-flex;
                    align-items: center; gap: 6px; min-height: 32px;
                    color: var(--gs-accent); font-weight: 700;
                    white-space: nowrap; }
.gx-withdraw__sum::-webkit-details-marker { display: none; }
.gx-withdraw__sum:focus-visible { outline: 2px solid var(--gs-accent);
                                  outline-offset: 2px;
                                  border-radius: var(--gs-radius-sm); }
.gx-withdraw[open] > .gx-withdraw__sum { color: var(--gs-ink-2); }
.gx-withdraw__form { display: flex; flex-direction: column; gap: 8px;
                     margin-top: 6px; padding: 10px 12px; white-space: normal;
                     border: 1px solid var(--gs-line); border-radius: var(--gs-radius);
                     background: var(--gs-surface-2); }
.gx-withdraw__form > .gs-btn { align-self: flex-start; }
/* In a table cell (an Actions column takes its content's width) the open
   form claims a readable width rather than the summary's, so the reason
   box is not one word wide. */
.gs-table .gx-withdraw__form { width: min(320px, 78vw); text-align: left; }
.gx-withdraw__what { margin: 0; color: var(--gs-ink-2); line-height: 1.5; }
.gx-withdraw__hint { margin: 0; font-size: var(--gs-fs-xs); color: var(--gs-muted); }
.gx-place__alias { margin: 0; font-size: var(--gs-fs-xs); color: var(--gs-muted); }
/* A cell of a value and the line under it ("Oct 10, 2026" / "by Sam I.,
   adding it by hand") kept as ONE piece. Stacked as a card on a phone the
   cell lays its children in a row, which put the date and who at opposite
   edges, or one beside the label and one under it. */
.gx-cellstack { display: inline-flex; flex-direction: column; gap: 2px;
                min-width: 0; }
@media (max-width: 767.98px) {
  .gs-table td > .gx-cellstack { align-items: flex-end; text-align: right; }
  /* The row's name, which takes a line of its own under its label. */
  .gs-table td > .gx-cellstack--lead { align-items: flex-start;
                                       text-align: left; }
}
/* Invoices, "On open repair orders": what open means, and the way to the
   one setting behind it (Rules & thresholds), which wraps under the
   sentence on a narrow card. */
.gx-openro { display: flex; flex-wrap: wrap; align-items: flex-end;
             justify-content: space-between; gap: 10px 18px;
             padding: 12px 16px; margin-bottom: var(--gs-space-3); }
.gx-openro__text { flex: 1 1 320px; margin: 0; display: flex; gap: 8px;
                   align-items: flex-start; color: var(--gs-ink-2);
                   font-size: var(--gs-fs-sm); line-height: 1.5; }
.gx-openro__text i { color: var(--gs-info); margin-top: 2px; }
.gx-openro__more { flex: none; margin: 0; font-size: var(--gs-fs-sm); }
@media (max-width: 575.98px) {
  .gx-withdraw__sum { min-height: 44px; }
}
.gx-related { display: grid; grid-template-columns: minmax(0, 1fr) auto;
              grid-template-areas: "kind go" "title go"; gap: 2px 10px;
              align-items: center; padding: 10px 12px; text-decoration: none;
              border: 1px solid var(--gs-line); border-radius: var(--gs-radius);
              color: var(--gs-ink); }
.gx-related:hover { border-color: var(--gs-accent-line); }
.gx-related__kind { grid-area: kind; font-size: var(--gs-fs-xs);
                    color: var(--gs-muted); font-weight: 700; }
.gx-related__title { grid-area: title; font-weight: 700; }
.gx-related i { grid-area: go; color: var(--gs-accent); }


/* ── the Overview (recon/overview.html) ──────────────────────────────
   PILLAR1_UI_PATTERNS.md §1 and §8.1. Every colour is a token; nothing
   here draws a bar down the left edge of a row or a dot beside a label. */

.gx-ov-branch { display: inline-flex; }
.gx-ov { margin-top: var(--gs-space-4); }
.gx-ov-lead { margin: 0 0 var(--gs-space-3); color: var(--gs-ink-2);
              font-size: var(--gs-fs-sm); line-height: 1.5; }
.gx-ov-empty { margin: 0; color: var(--gs-muted); font-size: var(--gs-fs-sm);
               line-height: 1.5; }
.gx-ov-setup .gs-task + .gs-task { margin-top: 8px; }

/* "12 need attention ↓": only while the panel is below the content. */
.gx-ov-jump { display: none; }
@media (max-width: 991.98px) {
  .gx-ov-jump {
    display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
    min-height: 44px; padding: 0 14px; border-radius: 999px;
    border: 1px solid var(--gs-accent-line); background: var(--gs-accent-wash);
    color: var(--gs-accent); font-weight: 700; font-size: var(--gs-fs-sm);
    text-decoration: none;
  }
}
#needs-attention { scroll-margin-top: calc(var(--gs-topbar-h) + 12px); }

/* Five tiles: the hero full width and the rest two by two while the
   column is narrow, all five in a row once it is not — sized by the
   COLUMN, so the panel beside it is accounted for. */
.gx-ov-tiles { container: gxovtiles / inline-size; }
.gx-ov-tiles .gx-glances { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.gx-ov-tiles .gx-glance--hero { grid-column: 1 / -1; }
@container gxovtiles (min-width: 860px) {
  .gx-ov-tiles .gx-glances { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .gx-ov-tiles .gx-glance--hero { grid-column: auto; }
}

/* What the totals leave out, by name, under the tiles. */
.gx-ov-note { margin-top: 10px; font-size: var(--gs-fs-sm);
              color: var(--gs-ink-2); }
.gx-ov-note summary { cursor: pointer; font-weight: 700; color: var(--gs-muted);
                      min-height: 32px; display: flex; align-items: center; }
.gx-ov-note__list { margin: 4px 0 0; display: grid; gap: 6px; }
.gx-ov-note__list div { display: grid; grid-template-columns: minmax(0, 14rem) minmax(0, 1fr);
                        gap: 2px 12px; }
.gx-ov-note__list dt { font-weight: 700; color: var(--gs-ink); }
.gx-ov-note__list dd { margin: 0; line-height: 1.45; }
@media (max-width: 575.98px) {
  .gx-ov-note__list div { grid-template-columns: minmax(0, 1fr); }
}

/* The top exceptions: the inbox's own rows, compact. */
.gx-ov-top .gx-cases { padding-top: 6px; }

/* The core & return clock: what, its type, the window meter, the amount. */
.gx-clock { list-style: none; margin: 0; padding: 6px 0 0;
            container: gxclock / inline-size; }
.gx-clock__row {
  display: grid; align-items: center; column-gap: 14px; row-gap: 6px;
  grid-template-columns: minmax(0, 1.2fr) auto minmax(0, 1.4fr) minmax(84px, auto);
  grid-template-areas: "what kind meter amount";
  padding: 12px 18px; border-top: 1px solid var(--gs-line-soft);
}
.gx-clock__row:first-child { border-top: 0; }
.gx-clock__what { grid-area: what; min-width: 0; display: flex;
                  flex-direction: column; gap: 2px; }
.gx-clock__title { font-weight: 800; color: var(--gs-ink);
                   text-decoration: none; overflow-wrap: anywhere; }
a.gx-clock__title:hover { color: var(--gs-accent); text-decoration: underline; }
.gx-clock__meta { display: flex; flex-wrap: wrap; gap: 0 8px;
                  font-size: var(--gs-fs-xs); color: var(--gs-muted);
                  font-weight: 600; }
.gx-clock__meta span:not(:last-child)::after { content: '·'; margin-left: 8px; }
.gx-clock__kind { grid-area: kind; }
.gx-clock__meter { grid-area: meter; min-width: 0; }
.gx-clock__sentence { font-size: var(--gs-fs-xs); color: var(--gs-ink-2);
                      line-height: 1.4; }
.gx-clock__amount { grid-area: amount; text-align: right; font-weight: 700;
                    font-variant-numeric: tabular-nums; white-space: nowrap;
                    color: var(--gs-ink); }
@container gxclock (max-width: 620px) {
  .gx-clock__row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "what amount" "kind kind" "meter meter";
    padding: 12px 14px; align-items: start;
  }
}

/* The weekly bars with a figure on every bar: the tips always show. */
.gx-bars--numbered .gx-bars__tip { opacity: 1; color: var(--gs-ink-2); }
.gx-bars--numbered .gx-bars__plot { padding-top: 22px; }

/* Branch accountability: a badge under the name, never wrapping;
   figures right-aligned so they compare down a column. */
.gx-ov-acc .gs-table__sub .gs-badge { margin-top: 4px; }
.gx-ov-acc .gs-table th.num, .gx-ov-acc .gs-table td.num { text-align: right; }
.gx-ov-acc td .gs-spark { min-width: 72px; max-width: 120px; }
/* Ten columns in the main column beside the panel (about 970px of card on
   a 1,440px screen) once "On time" and "Owed by vendors" joined: a header
   may take two lines BETWEEN words, and no cell breaks INSIDE one — with
   `gs-table td`'s `anywhere` the table squeezed "oldest opened today" to
   one letter a line and pushed its last column past the card. */
@media (min-width: 768px) {
  .gx-ov-acc .gs-table th { white-space: normal; overflow-wrap: normal;
                            vertical-align: bottom; }
  .gx-ov-acc .gs-table td,
  .gx-ov-acc .gs-table__meta { overflow-wrap: normal; word-break: normal; }
  .gx-ov-acc .gs-table th, .gx-ov-acc .gs-table td { padding-left: 10px;
                                                     padding-right: 10px; }
}
/* Stacked on a phone, a branch is a card of figures two by two — the
   name across the top, who looks after it under it — rather than nine
   lines of one figure each, which made fourteen branches most of the
   page. Every figure keeps its label (`data-label`). */
@media (max-width: 767.98px) {
  .gx-ov-acc .gs-table tr { display: grid; column-gap: 16px;
                            grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .gx-ov-acc .gs-table td { flex-direction: column; align-items: flex-start;
                            justify-content: flex-start; gap: 2px;
                            text-align: left; }
  .gx-ov-acc .gs-table td:nth-child(1),
  .gx-ov-acc .gs-table td:nth-child(2) { grid-column: 1 / -1; }
  .gx-ov-acc .gs-table td:nth-child(1)::before { display: none; }
  .gx-ov-acc td .gs-spark { max-width: none; }
}

/* A compact exception row has no select box: stacked, it gives the box's
   column back rather than indenting the title. */
@container gxcases (max-width: 760px) {
  .gx-crow--compact { grid-template-columns: minmax(0, 1fr) auto;
                      grid-template-areas: "main amount"; }
}

/* Patterns: a title that links to the evidence, the sentence under it. */
.gx-ov-patlist { list-style: none; margin: 0; padding: 6px 0 4px; }
.gx-ov-pat { display: flex; flex-direction: column; gap: 3px;
             padding: 10px 18px; border-top: 1px solid var(--gs-line-soft); }
.gx-ov-pat:first-child { border-top: 0; }
.gx-ov-pat__title { font-weight: 800; color: var(--gs-ink); }
.gx-ov-pat__title a { color: inherit; text-decoration: none; }
.gx-ov-pat__title a:hover { color: var(--gs-accent); text-decoration: underline; }
.gx-ov-pat__text { font-size: var(--gs-fs-sm); color: var(--gs-ink-2);
                   line-height: 1.45; }

/* ── "Needs attention now" (the rail macro's newer shape) ─────────── */
.gx-rail__title { margin: 0; font-size: var(--gs-fs-base); font-weight: 800;
                  letter-spacing: -.01em; color: var(--gs-ink); }
.gx-rail__of { margin: 0; font-size: var(--gs-fs-sm); font-weight: 700;
               color: var(--gs-muted); font-variant-numeric: tabular-nums;
               white-space: nowrap; }
.gx-rail--now .gx-rail__top { margin-bottom: 10px; }
.gx-rail--now .gx-chip { min-height: 32px; }
.gx-alert__meta { display: flex; flex-wrap: wrap; gap: 0 6px; margin-top: 3px;
                  font-size: var(--gs-fs-xs); color: var(--gs-muted);
                  font-weight: 600; }
.gx-alert__meta span:not(:last-child)::after { content: '·'; margin-left: 6px; }
.gx-alert--now .gx-alert__title { display: block; overflow-wrap: anywhere; }
.gx-alert--now .gx-alert__text { display: block; }
.gx-alert__due { display: block; margin-top: 6px; }
.gx-rail--now .gx-rail__foot { display: flex; flex-direction: column; gap: 6px; }
.gx-rail__all { color: var(--gs-accent); font-weight: 700; font-size: var(--gs-fs-sm);
                text-decoration: none; display: inline-flex; align-items: center;
                gap: 4px; min-height: 32px; }
.gx-rail__all:hover { text-decoration: underline; }
@media (max-width: 991.98px) {
  .gx-rail--now .gx-alert { min-height: 44px; }
}

/* ── Rules & thresholds (recon/rules.html, rule_history.html) ─────────
   One row per setting: what it is and does on the left, its own small
   form on the right; under 720px of card the form drops below. The value
   in force is a quiet badge — a setting is not a status. */
.gx-rules { display: flex; flex-direction: column; gap: var(--gs-space-4);
            min-width: 0; container: gxrules / inline-size; }
.gx-rules__note { display: flex; gap: 8px; align-items: flex-start; margin: 0;
                  padding: 12px 14px; border-radius: var(--gs-radius-sm);
                  background: var(--gs-surface-2); color: var(--gs-ink-2);
                  font-size: var(--gs-fs-sm); line-height: 1.45; }
.gx-rules__note i { color: var(--gs-muted); margin-top: 2px; }
.gx-rules__list { list-style: none; margin: 0; padding: 0; }
.gx-rule { display: grid; gap: 10px 24px; padding: 16px 18px;
           grid-template-columns: minmax(0, 1fr) minmax(0, 300px);
           border-top: 1px solid var(--gs-line-soft);
           scroll-margin-top: calc(var(--gs-topbar-h) + var(--gs-topbar-gap) + 40px); }
.gx-rule:first-child { border-top: 0; }
.gx-rule__name { margin: 0; display: flex; flex-wrap: wrap; align-items: center;
                 gap: 6px 10px; font-size: var(--gs-fs-base); font-weight: 800;
                 color: var(--gs-ink); }
.gx-rule__help { margin: 4px 0 0; font-size: var(--gs-fs-sm);
                 color: var(--gs-ink-2); line-height: 1.45; }
.gx-rule__meta { margin: 6px 0 0; font-size: var(--gs-fs-xs);
                 color: var(--gs-muted); line-height: 1.45; }
.gx-rule__meta a, .gx-rule__where a { color: var(--gs-accent); font-weight: 700; }
.gx-rule__where { margin: 0; align-self: center; font-size: var(--gs-fs-sm); }
.gx-rule__form { display: grid; grid-template-columns: minmax(0, 1fr) auto;
                 gap: 6px 8px; align-content: start; }
.gx-rule__label { grid-column: 1 / -1; margin: 0; font-size: var(--gs-fs-xs);
                  font-weight: 700; color: var(--gs-muted); }
.gx-rule__box { position: relative; display: flex; align-items: center;
                min-width: 0; }
.gx-rule__box > .form-control { width: 100%; min-height: 40px;
                                font-variant-numeric: tabular-nums; }
.gx-rule__affix { position: absolute; left: 12px; color: var(--gs-muted);
                  font-size: var(--gs-fs-sm); pointer-events: none; }
.gx-rule__affix--end { left: auto; right: 12px; }
.gx-rule__box:has(.gx-rule__affix:not(.gx-rule__affix--end)) > .form-control { padding-left: 26px; }
.gx-rule__box:has(.gx-rule__affix--end) > .form-control { padding-right: 52px; }
.gx-rule__form .gs-btn { min-height: 40px; }
.gx-rule__hint, .gx-rule__error { grid-column: 1 / -1; font-size: var(--gs-fs-xs);
                                  line-height: 1.4; }
.gx-rule__hint { color: var(--gs-muted); }
.gx-rule__error { color: var(--gs-warning); font-weight: 700; }
.gx-rules__foot { margin: 0; font-size: var(--gs-fs-xs); color: var(--gs-muted); }
@container gxrules (max-width: 720px) {
  .gx-rule { grid-template-columns: minmax(0, 1fr); }
  .gx-rule__form .gs-btn { min-height: 44px; }
  .gx-rule__box > .form-control { min-height: 44px; }
}
.gx-rules__lead { margin: 0; font-size: var(--gs-fs-sm); color: var(--gs-ink-2); }
