/* Component styles for Vantage dashboard.
   All values reference --vantage-* tokens defined in theme.css. */

[x-cloak] { display: none !important; }

/* -------------------------------------------------------------------------
   Base
   ------------------------------------------------------------------------- */

html, body {
  font-family: var(--vantage-font), system-ui, sans-serif;
  color: var(--vantage-text-body);
}

/* -------------------------------------------------------------------------
   Navigation
   ------------------------------------------------------------------------- */

.vantage-nav {
  background: var(--vantage-nav-bg);
  border-bottom: 1px solid var(--vantage-border);
  height: var(--vantage-nav-height);
}

.vantage-nav .navbar-brand .navbar-item {
  font-weight: 700;
  font-size: 1.1rem;
  color: var(--vantage-nav-text);
  letter-spacing: -0.01em;
}

.vantage-nav .navbar-item,
.vantage-nav .navbar-link {
  color: var(--vantage-text-body);
}

.vantage-nav .navbar-item:hover,
.vantage-nav .navbar-link:hover {
  color: var(--vantage-brand);
  background: transparent;
}

/* -------------------------------------------------------------------------
   App layout (authenticated pages)
   ------------------------------------------------------------------------- */

body.app-layout {
  background: var(--vantage-app-bg);
  min-height: 100vh;
}

.vantage-page {
  padding: 2rem 1.5rem;
}

/* -------------------------------------------------------------------------
   Cards
   ------------------------------------------------------------------------- */

.vantage-card {
  background: var(--vantage-surface);
  border: 1px solid var(--vantage-border);
  border-radius: var(--vantage-card-radius);
  box-shadow: var(--vantage-card-shadow);
}

/* -------------------------------------------------------------------------
   Login layout — Option A (Floating Card)
   ------------------------------------------------------------------------- */

body.login-layout-a {
  background: var(--vantage-login-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

.login-layout-a .login-box {
  width: 100%;
  max-width: var(--vantage-login-card-width);
  background: var(--vantage-surface);
  border-radius: var(--vantage-login-card-radius);
  box-shadow: var(--vantage-login-card-shadow);
  padding: 2.5rem 2rem;
}

.login-layout-a .login-wordmark {
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--vantage-brand);
  letter-spacing: -0.02em;
  margin-bottom: 0.25rem;
}

.login-layout-a .login-tagline {
  font-size: 0.875rem;
  color: var(--vantage-text-muted);
  margin-bottom: 2rem;
}

.login-layout-a .login-heading {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--vantage-text-heading);
  margin-bottom: 1.25rem;
}

.login-layout-a .field label {
  color: var(--vantage-text-label);
  font-size: 0.875rem;
  font-weight: 500;
}

.login-layout-a .input {
  border-color: var(--vantage-border);
  border-radius: 6px;
  background-color: var(--vantage-surface);
  color: var(--vantage-text-body);
}

.login-layout-a .input:focus {
  border-color: var(--vantage-brand);
  box-shadow: 0 0 0 2px rgba(34,55,125,0.1);
}

/* Override browser autofill dark background */
.login-layout-a .input:-webkit-autofill,
.login-layout-a .input:-webkit-autofill:hover,
.login-layout-a .input:-webkit-autofill:focus,
.login-layout-a .input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 30px var(--vantage-surface) inset !important;
  -webkit-text-fill-color: var(--vantage-text-body) !important;
}

.login-layout-a .button.is-primary {
  background: var(--vantage-brand);
  border-color: transparent;
  color: #ffffff;
  font-weight: 600;
  letter-spacing: 0.01em;
}

.login-layout-a .button.is-primary:hover {
  background: var(--vantage-brand-dark);
  color: #ffffff;
}

/* -------------------------------------------------------------------------
   Portfolio table
   ------------------------------------------------------------------------- */

.portfolio-sort-btn {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
  color: inherit;
  font-weight: 600;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
}

.portfolio-sort-btn:hover {
  color: var(--vantage-brand);
}

.portfolio-sort-icon {
  font-size: 0.65rem;
  color: var(--vantage-brand-muted);
}

/* Grouped header: the group-name row */
.portfolio-group-row th {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--vantage-brand-muted);
  font-weight: 600;
  border-bottom: 2px solid var(--vantage-brand-muted);
  padding-bottom: 0.25rem;
}

.portfolio-num {
  font-variant-numeric: tabular-nums;
}

.portfolio-share {
  color: var(--vantage-brand-muted);
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
}

.portfolio-coverage-chip {
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0.05rem 0.4rem;
  border-radius: 0.5rem;
  font-size: 0.7rem;
  color: var(--vantage-brand-muted);
  background: rgba(100, 116, 139, 0.12);
  white-space: nowrap;
  cursor: default;
}

/* Stacked mix bar (classification + size) */
.mix-bar {
  display: flex;
  width: 120px;
  height: 12px;
  border-radius: 3px;
  overflow: hidden;
}

.mix-seg {
  display: block;
  height: 100%;
  min-width: 2px;
}

.portfolio-size-xs { background-color: var(--vantage-size-xs); }
.portfolio-size-s  { background-color: var(--vantage-size-s); }
.portfolio-size-m  { background-color: var(--vantage-size-m); }
.portfolio-size-l  { background-color: var(--vantage-size-l); }
.portfolio-size-xl { background-color: var(--vantage-size-xl); }

/* PR label tag colours — consistent with vantage-label-* CSS tokens */
.portfolio-label-feature  { background-color: var(--vantage-label-feature);  color: #fff; }
.portfolio-label-bugfix   { background-color: var(--vantage-label-bugfix);   color: #fff; }
.portfolio-label-ktlo     { background-color: var(--vantage-label-ktlo);     color: #fff; }
.portfolio-label-refactor { background-color: var(--vantage-label-refactor); color: #fff; }
.portfolio-label-test     { background-color: var(--vantage-label-test);     color: #fff; }
.portfolio-label-docs     { background-color: var(--vantage-label-docs);     color: #fff; }

/* Roster tables — consistent cell padding and vertical alignment */
.roster-table td,
.roster-table th {
  padding: 0.6rem 0.75rem;
  vertical-align: middle;
}

/* Prevent SVG classification bars from scaling with column width */
.roster-table td svg {
  height: 16px;
  max-height: 16px;
}

/* Exclusion-rule preview samples — the two tables are read side by side and
   compared row against row, so every row in both has to be exactly one line
   tall. Three things are needed together, and each was arrived at by measuring
   in a browser rather than by reasoning:

   1. Every forge-supplied cell is clipped to one line (.exclusion-clip*). PR
      titles reach 75 characters and author handles 20, and in a half-width
      column those wrap — measured at 43/61/61.5/79px in one table against a
      uniform 43px in the other, which is the mismatch this pair keeps hitting.
      A blanket `white-space: nowrap` instead pushed the *page* 205px wider at
      1280; clipping the cells keeps the width bounded.
   2. The wrapper scrolls (.exclusion-sample-wrap). Below roughly 1100px the
      remaining columns genuinely cannot fit on one line, so something has to
      give: scrolling one table is contained, growing the page is not.
   3. The column may shrink (.exclusion-sample-col). A flex item will not go
      below its content width without `min-width: 0`, so without this the
      wrapper never clips and (2) silently does nothing — the page overflowed
      by 125px with the wrapper in place and no scrollbar anywhere.

   The max-widths are in `ch` so they track the font rather than a pixel guess.
   Keep anything taller than a line of text out of these cells: a Bulma .tag in
   the Reason column is what broke the pitch the first time. */
.exclusion-sample-col {
  min-width: 0;
}

.exclusion-sample-wrap {
  overflow-x: auto;
}

/* `nowrap` is safe here only because the unbounded cells are clipped below: on
   its own it pushed the page 205px wider at 1280. With the clips in place the
   remaining cells are a date, a tier and a number, and holding them to one
   line is what keeps the row at one line — the date was the last thing
   wrapping, needing ~70px in a 57px column at 1024. */
.exclusion-sample td,
.exclusion-sample th {
  padding: 0.4rem 0.6rem;
  line-height: 1.5;
  vertical-align: middle;
  white-space: nowrap;
}

.exclusion-sample td.exclusion-clip {
  max-width: 22ch;
}

.exclusion-sample td.exclusion-clip-narrow {
  max-width: 14ch;
}

/* The clip lives on a block child, not the cell: a cell's own width is
   negotiated by the table, and only a block inside it takes that width as
   given and ellipsises against it. */
.exclusion-sample td.exclusion-clip > span,
.exclusion-sample td.exclusion-clip-narrow > span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Mini horizontal stacked bar — used in roster table classification column */
.mini-bar {
  display: flex;
  width: 100%;
  height: 10px;
  border-radius: 3px;
  overflow: hidden;
  min-width: 80px;
  gap: 1px;
}

/* -------------------------------------------------------------------------
   Company dashboard
   ------------------------------------------------------------------------- */

.company-back-link {
  font-size: 0.875rem;
  color: var(--vantage-brand-muted);
  text-decoration: none;
}

.company-back-link:hover {
  color: var(--vantage-brand);
}

.pr-breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.pr-breadcrumb-sep {
  color: var(--vantage-brand-muted);
  font-size: 0.875rem;
}

.company-section-title {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--vantage-text-muted);
  margin-bottom: 1rem;
}

/* Row of stat cards — flex so they fill available width evenly. */
.company-stat-row {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

.company-stat-card {
  flex: 1;
  min-width: 160px;
  padding: 1.25rem 1.5rem;
}

.company-stat-label {
  font-size: 0.8rem;
  color: var(--vantage-text-muted);
  margin-bottom: 0.4rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.company-stat-value {
  font-size: 2rem;
  font-weight: 700;
  color: var(--vantage-text-heading);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  margin-bottom: 0.4rem;
}

.company-stat-delta {
  font-size: 0.8rem;
  font-weight: 600;
}

.company-stat-prior {
  font-weight: 400;
  color: var(--vantage-text-muted);
}

/* Chart containers — responsive height for the canvas. */
.company-chart-label {
  font-size: 0.8rem;
  color: var(--vantage-text-muted);
  margin-bottom: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.company-chart-container {
  position: relative;
  height: 220px;
}

.company-chart-container.company-chart-short {
  height: 180px;
}

.company-chart-container.company-chart-tall {
  height: 400px;
}

/* Pattern View scatter: fill whatever height the row has, without being able to
   set it.

   The attribution aside beside it is content-sized and taller than the chart
   wants to be, so a fixed chart height leaves dead space under the canvas. The
   obvious fix — let the chart stretch — is the one that breaks: the chart runs
   maintainAspectRatio:false, so the canvas resizes to its container, and if the
   container's height can be raised by its own content the two feed each other
   and grow every frame. This codebase has had that bug.

   `flex: 1 1 0` with `min-height: 0` is what makes stretching safe. The height
   is distributed from the flex line, and the automatic minimum size — which is
   the canvas's own rendered height, and is the actual feedback path — is
   switched off. The canvas can then never raise an ancestor, so the loop has no
   edge to travel along.

   The floor sits on the section rather than on the chart, so it bounds the row
   rather than the canvas: an org without OTel has three panels in the aside
   instead of five, and the chart should not collapse along with it.

   Scoped so it doesn't affect the org woven timeline, which also uses
   company-chart-tall. */
.per-dev-scatter-section {
  min-height: 420px;
}
.per-dev-scatter-section .scatter-chart-col {
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.per-dev-scatter-section .scatter-chart-col .company-chart-container {
  flex: 1 1 0;
  min-height: 0;
  height: auto;
}

/* The woven timeline's own height, rather than the shared .company-chart-tall
   that four other pages use at 400px.

   `flex: none` is the load-bearing half. .intersection-chart-card gives its
   chart container `flex: 1`, which means `flex-basis: 0%`, and a basis beats
   `height` for a flex item's main size. The card is content-sized here, so
   there is no free space for the basis to grow into and the container would
   collapse to almost nothing while stating a height it does not use.
   Neutralising the flex is what lets the height mean anything.

   440px rather than the shared 400px because the bars span the full page
   width here, where the same height reads as a much flatter chart. */
.ai-output-panel .intersection-chart-card .company-chart-container.company-chart-tall {
  flex: none;
  height: 440px;
}

.intersection-charts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(420px, 1fr));
  gap: 1rem;
  margin-bottom: 1.5rem;
}

/* The woven timeline's panel on Delivery, which is its only user. */
.ai-output-panel {
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.ai-output-panel .intersection-chart-card {
  /* No `flex: 1`: the card is alone in its panel, so there is no sibling
     height to match and stretching would only let it grow. */
  margin-bottom: 0;
}

.intersection-chart-card {
  display: flex;
  flex-direction: column;
  padding: 1rem;
}

.intersection-chart-card .company-chart-container {
  flex: 1;
}

/* Prose or an empty state standing in for a chart in the slot beside the woven
   timeline. Takes the chart container's flex space and centres within it so the
   text sits level with the neighbouring chart rather than riding its top edge. */
.intersection-chart-note {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.6rem;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--vantage-text-muted);
}

.intersection-chart-footer {
  font-size: 0.75rem;
  color: var(--vantage-text-muted);
  margin-top: 0.5rem;
  line-height: 1.4;
}

/* -------------------------------------------------------------------------
   Team comparison — /companies/{id}/teams

   Colour here is a selection state, never a per-team hue: the population stays
   the neutral ground and one team lights up, consistently across the chips, the
   scatter, the rollout, the strips and the roster. Only the chips and the roster
   are CSS; the two charts are painted from the same tokens in company_teams.js.
   ------------------------------------------------------------------------- */

/* Aligning digits across rows so a column can be scanned rather than read.
   Named as a utility because the roster needs it on three columns that share
   nothing else. */
.tabular-nums {
  font-variant-numeric: tabular-nums;
}

/* The selection bar. Sticky rather than fixed, so it pins only while the four
   sections it drives are on screen and lets go at the end of the roster instead
   of following the reader down the rest of the page.

   Opaque, because content scrolls underneath it. The negative inline margin and
   matching padding let the bar's background span the page gutter, so a chart
   sliding under it disappears at the page edge rather than at the text column. */
.team-highlight-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin: 0 -1.5rem 1.25rem;
  padding: 0.6rem 1.5rem;
  background: var(--vantage-app-bg);
  border-bottom: 1px solid var(--vantage-border);
}

.team-highlight-label {
  flex-shrink: 0;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--vantage-text-muted);
}

.team-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  /* Wraps freely up to about three rows, then scrolls inside itself. Nothing
     caps how many teams an org has, and a bar tall enough to hold thirty chips
     would pin more of the viewport than it leaves for what it is changing. */
  max-height: 7.5rem;
  overflow-y: auto;
}

/* A chip does two jobs — select the team, and route into its filtered
   dashboard — so it is a button beside a link rather than one control trying to
   be both. The border carries the selection so the chip does not resize when it
   lights, which would reflow the whole row on every click. */
.team-chip {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--vantage-border);
  border-radius: 999px;
  background: var(--vantage-surface);
  font-size: 0.8rem;
  overflow: hidden;
}

.team-chip-select {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  /* 24px minimum hit target, matching the scatter's pointHitRadius. */
  min-height: 24px;
  padding: 0.15rem 0.7rem;
  border: none;
  background: none;
  font: inherit;
  color: var(--vantage-text-label);
  cursor: pointer;
}

.team-chip-select:focus-visible {
  outline: 2px solid var(--vantage-focus-ring);
  outline-offset: -2px;
}

.team-chip-count {
  color: var(--vantage-text-muted);
}

.team-chip.is-selected {
  background: var(--vantage-team-selected);
  border-color: var(--vantage-team-selected);
}

/* The resting chip is active, not lit. Fuchsia means exactly one thing on this
   page — the team currently highlighted on every chart — and wearing it here
   would put the hue on screen at the one moment nothing anywhere is wearing it.
   Text stays body-on-raised, the pairing used throughout, rather than the
   surface colour the lit chips reverse to. */
.team-chip.is-all.is-selected {
  background: var(--vantage-surface-raised);
  /* text-body rather than text-muted: the border is the only thing separating
     an active resting chip from an inactive one, and muted reads 2.41:1 against
     the raised fill — under the 3:1 a non-text boundary needs. */
  border-color: var(--vantage-text-body);
}

.team-chip.is-all.is-selected .team-chip-select {
  color: var(--vantage-text-body);
  font-weight: 600;
}

/* The surface colour, not a literal white. The selected fill is dark-on-light
   and light-on-dark by construction, so whatever the card background is, it is
   the value that contrasts with the fill in either theme. */
.team-chip.is-selected .team-chip-select,
.team-chip.is-selected .team-chip-count,
.team-chip.is-selected .team-chip-link {
  color: var(--vantage-surface);
}

.team-chip-link {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 0.15rem 0.6rem 0.15rem 0.1rem;
  color: var(--vantage-text-muted);
  text-decoration: none;
}

/* Unassigned is a legend key, not a control: it is not a team, so it can never
   be the lit one. Hollow, matching its scatter marks. */
.team-chip.is-unassigned {
  background: none;
  border-style: dashed;
  color: var(--vantage-text-muted);
  padding: 0.15rem 0.7rem;
  gap: 0.4rem;
  min-height: 24px;
}

/* -------------------------------------------------------------------------
   What stands out — each team's distance from the org's own median.

   Positions are computed server-side onto a 0–1 track; this only draws them.
   Deliberately not five more Chart.js canvases: the ticks carry no axis, no
   scale and no interaction beyond hover, and every value is also in the roster.
   ------------------------------------------------------------------------- */

.deviation-row {
  display: grid;
  grid-template-columns: 13rem 1fr 11rem;
  align-items: start;
  gap: 1rem;
  margin-bottom: 1.1rem;
}

/* Type in this section runs 15% larger than it first shipped. Every size here
   had been set relative to the marks it labels rather than to the prose around
   it, which left the whole block reading smaller than the page it sits in. */
.deviation-label {
  display: flex;
  flex-direction: column;
  font-size: 0.92rem;
  color: var(--vantage-text-label);
}

/* The org's own figure, named rather than bracketed. "(90%)" beside the metric
   reads as "adoption is 90%"; what it is, is the number the dashed line marks,
   and the reader has no way to connect the two without being told. */
.deviation-typical {
  font-size: 0.83rem;
  color: var(--vantage-text-muted);
}

.deviation-plot {
  display: flex;
  flex-direction: column;
}

/* Zero on the left, the row's ceiling on the right. Every row starts at zero,
   so that end is stated once in the legend; the ceiling differs per row and has
   to be stated on the row. */
.deviation-scale {
  display: flex;
  justify-content: space-between;
  font-size: 0.78rem;
  color: var(--vantage-text-muted);
  padding: 0 5px;
  margin-top: -2px;
}

.deviation-strip {
  position: relative;
  height: 24px;
}

/* The rule the ticks are read against. Dashed, so it reads as a reference
   rather than as another team's mark. */
.deviation-strip::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  border-top: 1px solid var(--vantage-border);
}

/* Inset by half a tick at each end, so a team sitting at either extreme of the
   observed range still draws inside the rule rather than hanging off it. */
.deviation-track {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 5px;
  right: 5px;
}

.deviation-centre {
  position: absolute;
  top: 2px;
  bottom: 2px;
  width: 0;
  border-left: 1px dashed var(--vantage-text-muted);
}

.deviation-tick {
  position: absolute;
  top: 50%;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: var(--vantage-brand-muted);
  cursor: help;
}

/* One mark standing for several teams that share a value. It carries the count
   rather than stacking dots nobody can separate — which is the ordinary case
   here, since every zero-adoption team sits at zero together. Wider, so the
   numeral fits and so the mark reads as "more than one" before it is hovered.

   A step darker than a single mark, because the numeral sits on it: white on
   the ground grey is 2.96:1, which fails its own label. On text-body it is
   4.9:1 light and 5.6:1 dark. */
.deviation-tick.is-cluster {
  background: var(--vantage-text-body);
  /* Grown with its numeral. Two digits at 11.5px need about 13px of width, so
     holding the mark at 18px would have crowded the count on any org with ten
     or more teams sharing a value. */
  width: 20px;
  height: 20px;
  margin: -10px 0 0 -10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--vantage-surface);
}

/* Keyboard readers land on these one at a time, and the marks are small enough
   that the browser default outline is easy to lose against the track. */
.deviation-tick:focus-visible {
  outline: 2px solid var(--vantage-focus-ring);
  outline-offset: 2px;
}

/* Hollow, the same grammar the unassigned scatter marks use: present, but not a
   full participant in this comparison. Below five contributors a team's median
   is noise, and marking it beats dropping it — a reader cannot tell a hidden
   team from a team with nothing to say. */
.deviation-tick.is-low-confidence {
  background: none;
  border: 1.5px solid var(--vantage-brand-muted);
}

/* A hollow cluster has no fill for its numeral to sit on, so the count takes
   the ground colour as text instead. */
.deviation-tick.is-low-confidence.is-cluster {
  color: var(--vantage-brand-muted);
}

/* Bigger as well as brighter. A 10px dot changing hue is easy to miss among
   twelve of them; growing is the change the eye catches first. */
.deviation-tick.is-selected {
  background: var(--vantage-team-selected);
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  box-shadow: 0 0 0 2px var(--vantage-surface);
  z-index: 1;
}

.deviation-tick.is-cluster.is-selected {
  width: 24px;
  height: 24px;
  margin: -12px 0 0 -12px;
}

.deviation-tick.is-low-confidence.is-selected {
  background: none;
  border-color: var(--vantage-team-selected);
}

/* The furthest team from the centre, named in the markup. Tooltips enhance and
   never gate, so the reading this section exists to deliver has to survive
   without a pointer — when there is one to deliver. */
.deviation-outlier {
  font-size: 0.86rem;
  color: var(--vantage-text-label);
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding-top: 0.15rem;
}

/* Nothing unusual on this row, said out loud. Lighter than a named team, so a
   column of quiet rows reads as quiet at a glance. */
.deviation-quiet {
  color: var(--vantage-text-muted);
  font-style: italic;
  cursor: help;
}

/* -------------------------------------------------------------------------
   Strip legend. Six of the seven marks in this section were unlabelled; the
   caption explained the idea and nothing connected it to what was drawn.
   ------------------------------------------------------------------------- */

.deviation-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1.25rem;
  align-items: center;
  margin-top: 1.25rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--vantage-border);
  font-size: 0.83rem;
  color: var(--vantage-text-muted);
}

.legend-key {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

/* The legend's own swatches are in normal flow, unlike the marks on a track. */
.deviation-tick.is-static {
  position: static;
  margin: 0;
  flex-shrink: 0;
}

.legend-centre {
  display: inline-block;
  width: 0;
  height: 14px;
  border-left: 1px dashed var(--vantage-text-muted);
}

/* -------------------------------------------------------------------------
   Team roster — the table view for the scatter, so every value above is
   reachable without hovering.
   ------------------------------------------------------------------------- */

/* The same hue as the lit dots, at wash strength. --vantage-highlight is the
   company page's scatter→table flash and means something else. */
.team-roster-table tr.is-selected {
  background: var(--vantage-team-selected-wash);
}

.team-roster-table tbody tr {
  cursor: pointer;
}

/* One dot per person, ascending by session-days. Every dot is somebody, which
   is why each carries its own tooltip — this strip is otherwise the page's
   largest identification gap. */
.usage-spread {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex-wrap: wrap;
}

.usage-spread-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--vantage-brand-muted);
  cursor: help;
}

/* A zero is a measurement, not a gap — hollow says "counted, and it was none"
   rather than removing the person from their team's strip. */
.usage-spread-dot.is-zero {
  background: none;
  border: 1px solid var(--vantage-border);
}

/* -------------------------------------------------------------------------
   Info tooltip — weighted output explanation
   .info-tip-icon: small circled "i" rendered inline next to a label.
   .info-tip-bubble: fixed-position bubble teleported to <body> by Alpine so it
   is never clipped by table-container overflow.
   ------------------------------------------------------------------------- */

.info-tip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--vantage-text-muted);
  color: #fff;
  font-size: 8px;
  font-weight: 700;
  line-height: 1;
  margin-left: 0.3em;
  cursor: help;
  vertical-align: middle;
  user-select: none;
  flex-shrink: 0;
  font-style: normal;
  text-transform: none;
  letter-spacing: normal;
}

/* Where an icon carries tabindex, the ring has to be visible against the muted
   grey disc it wraps — at 13px the browser default is easy to lose. Icons
   without tabindex never match this and are unaffected. */
.info-tip-icon:focus-visible {
  outline: 2px solid var(--vantage-focus-ring);
  outline-offset: 2px;
}

.info-tip-bubble {
  position: fixed;
  transform: translateX(-50%) translateY(-100%);
  background: var(--vantage-text-heading);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 400;
  line-height: 1.5;
  padding: 0.45rem 0.65rem;
  border-radius: 5px;
  max-width: 320px;
  white-space: pre-line;
  pointer-events: none;
  z-index: 1000;
  margin-top: -8px;
}

/* Flipped below the icon (set by tooltip.js when there is no room above). */
.info-tip-bubble.info-tip-bubble--below {
  transform: translateX(-50%);
  margin-top: 0;
}

/* Dark mode: --vantage-text-heading is near-white, so the default light-mode
   bubble (heading-colored background + white text) becomes white-on-white.
   Use a raised dark surface with light text and a border instead. */
:root[data-theme="dark"] .info-tip-bubble {
  background: var(--vantage-surface-raised);
  color: var(--vantage-text-heading);
  border: 1px solid var(--vantage-border);
}

/* -------------------------------------------------------------------------
   Point picker — the disambiguation panel for a scatter click that hit several
   coincident points. Built by chart-init.js, which owns the click handler that
   knows how many points the cursor covered.
   ------------------------------------------------------------------------- */

/* Fixed and placed against viewport coordinates, so it is never clipped by the
   chart container's overflow the way an in-flow panel would be. Above the
   info-tip bubble: a tooltip is what prompted the click, and it must not sit on
   top of the answer. */
.point-picker {
  position: fixed;
  z-index: 1005;
  background: var(--vantage-surface);
  border: 1px solid var(--vantage-border);
  border-radius: 6px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.16);
  padding: 0.5rem 0;
  min-width: 240px;
  max-width: 340px;
}

.point-picker-head {
  padding: 0 0.75rem 0.4rem;
  margin: 0;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--vantage-text-muted);
  border-bottom: 1px solid var(--vantage-border-subtle);
}

/* Scrolls past about eight entries rather than growing off screen. A cursor on
   a dense scatter can cover dozens of points, which is the case this exists
   for — a panel that tall would repeat the problem it is solving. */
.point-picker-list {
  max-height: 15rem;
  overflow-y: auto;
  /* Wheeling past the end of the list must not chain to the page behind it. A
     page scroll is a legitimate reason to dismiss the panel — it is placed
     against a viewport coordinate — so without this, reaching the last row
     closes the thing you were reaching into. */
  overscroll-behavior: contain;
  padding-top: 0.25rem;
}

.point-picker-item {
  display: block;
  padding: 0.35rem 0.75rem;
  color: var(--vantage-text-body);
  text-decoration: none;
}

.point-picker-item:hover,
.point-picker-item:focus-visible {
  background: var(--vantage-surface-raised);
  outline: none;
}

/* The keyboard reader lands on the first item, so focus has to be visible as
   something other than the hover shade it shares. */
.point-picker-item:focus-visible {
  box-shadow: inset 2px 0 0 var(--vantage-focus-ring);
}

.point-picker-name {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--vantage-text-heading);
}

/* Wraps rather than truncating. The summary is what tells two people on the
   same pixel apart, and clipping it mid-word dropped the last measure off every
   row — the one case where the reader is reading these to make a choice. */
.point-picker-summary {
  display: block;
  font-size: 0.72rem;
  line-height: 1.35;
  color: var(--vantage-text-muted);
}

/* -------------------------------------------------------------------------
   Expand-chart modal — renders any chart large (~78vw × 80vh) in an overlay.
   See _chart_modal.html + chart-modal.js; opened via macros.chart_expand_button.
   ------------------------------------------------------------------------- */

/* Above the info-tip bubble (1000); Bulma's own .modal default is 40. */
.chart-expand-modal {
  z-index: 1010;
}

.chart-expand-modal .chart-expand-modal-box {
  /* The .modal-background sibling is position:absolute; without a positioned
     box it would paint on top and dim the card (Bulma's own .modal-content
     sets position:relative for the same reason). */
  position: relative;
  width: 78vw;
  height: 80vh;
  max-width: 1600px;
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
}

.chart-expand-modal-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

/* Chart.js responsive sizing needs a positioned, explicitly sized parent. */
.chart-expand-modal-body {
  position: relative;
  flex: 1;
  min-height: 0;
}

/* Icon-only button in the .company-chart-label row; the border and hover
   surface make it read as clickable, the title attr says what it does. */
.chart-expand-btn {
  float: right;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.1rem 0.3rem;
  border: 1px solid var(--vantage-border);
  border-radius: 4px;
  background: none;
  cursor: pointer;
  font-size: 1.25rem;
  line-height: 1;
  color: var(--vantage-text-body);
}

.chart-expand-btn:hover {
  color: var(--vantage-text-heading);
  border-color: var(--vantage-text-muted);
  background: var(--vantage-surface-raised);
}

/* -------------------------------------------------------------------------
   Credentials table — the Tool cell carries a name over a description.
   ------------------------------------------------------------------------- */

.credentials-tool-name .tool-label {
  font-weight: 600;
}

/* --vantage-text-body, not has-text-grey and not --vantage-text-muted:
   has-text-grey is hardcoded in the vendored Bulma sheet and lands under AA
   on the dark surface at this size, and --vantage-text-muted is as far under
   it on the light one. Body text clears AA in both, so the hierarchy comes
   from size and weight rather than colour — which is also why no dark-theme
   rule follows: the theme overrides target .table td, not its children.

   The width cap is load-bearing. The Tool column auto-sizes to its content,
   and an unbounded ~170-character line in a fullwidth table takes that width
   from Status and Last Collected until both wrap. */
.credentials-tool-name .tool-description {
  font-size: 0.75rem;
  color: var(--vantage-text-body);
  margin-top: 0.15rem;
  max-width: 28rem;
}

/* -------------------------------------------------------------------------
   Error pages
   ------------------------------------------------------------------------- */

body.error-layout {
  background: var(--vantage-app-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

/* Added alongside .error-layout by the pages that keep the nav bar. Column flow
   stacks the nav above the box, and the box's auto margins centre it in
   whatever height is left over. */
body.error-layout-nav {
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
}

body.error-layout-nav .error-box {
  margin: auto;
}

.error-box {
  width: 100%;
  max-width: 480px;
  background: var(--vantage-surface);
  border: 1px solid var(--vantage-border);
  border-radius: var(--vantage-login-card-radius);
  box-shadow: var(--vantage-card-shadow);
  padding: 2.5rem 2rem;
  text-align: center;
}

.error-box .error-icon {
  font-size: 2.5rem;
  margin-bottom: 1rem;
  color: var(--vantage-brand-muted);
}

.error-box h1 {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--vantage-text-heading);
  margin-bottom: 0.75rem;
}

.error-box p {
  font-size: 0.9rem;
  color: var(--vantage-text-body);
  margin-bottom: 0.5rem;
}

/* The reference a user quotes when reporting a 5xx; it matches the Slack alert
   and the log line carrying the traceback. */
.error-box .error-ref {
  margin-top: 1rem;
  font-size: 0.8rem;
  color: var(--vantage-text-muted);
}

.error-box .error-ref code {
  background: var(--vantage-surface-raised);
  color: var(--vantage-text-body);
  font-size: 0.8rem;
}

.error-box .back-link {
  display: inline-block;
  margin-top: 1.25rem;
  font-size: 0.875rem;
  color: var(--vantage-brand);
  font-weight: 500;
}

.error-box .back-link:hover {
  color: var(--vantage-brand-light);
}

/* -------------------------------------------------------------------------
   Dark mode — Bulma component overrides
   Bulma's is-light / is-success / is-warning / is-danger modifier classes
   use hardcoded light-palette values that never adapt to our dark theme.
   These rules restore legibility when data-theme="dark".
   ------------------------------------------------------------------------- */

/* Neutral tag (size tier, file status, "not configured") */
[data-theme="dark"] .tag.is-light {
  background-color: var(--vantage-surface-raised);
  color: var(--vantage-text-label);
}

/* Semantic status tags */
[data-theme="dark"] .tag.is-success {
  background-color: rgba(20, 184, 166, 0.2);
  color: #5eead4;
}
[data-theme="dark"] .tag.is-warning {
  background-color: rgba(245, 158, 11, 0.2);
  color: #fbbf24;
}
[data-theme="dark"] .tag.is-danger {
  background-color: rgba(239, 68, 68, 0.2);
  color: #f87171;
}

/* Sign-out button and other is-light buttons */
[data-theme="dark"] .button.is-light {
  background-color: var(--vantage-surface-raised);
  color: var(--vantage-text-label);
  border-color: var(--vantage-border);
}
[data-theme="dark"] .button.is-light:hover {
  background-color: var(--vantage-border);
  color: var(--vantage-text-heading);
}

/* Bulma table — defaults to white background */
[data-theme="dark"] .table {
  background-color: var(--vantage-surface);
  color: var(--vantage-text-body);
  border-color: var(--vantage-border);
}
[data-theme="dark"] .table thead th,
[data-theme="dark"] .table thead td {
  color: var(--vantage-text-heading);
  border-bottom-color: var(--vantage-border);
}
[data-theme="dark"] .table td,
[data-theme="dark"] .table th {
  border-color: var(--vantage-border-subtle);
  color: var(--vantage-text-body);
}
[data-theme="dark"] .table.is-hoverable tbody tr:hover {
  background-color: var(--vantage-surface-raised);
}

/* Bulma notification — is-warning is-light washes out on dark */
[data-theme="dark"] .notification.is-warning.is-light {
  background-color: rgba(245, 158, 11, 0.12);
  color: var(--vantage-text-body);
}

/* -------------------------------------------------------------------------
   Spoof banner

   Sticky rather than fixed: it stays in the document flow so it cannot cover
   the top of a page that has scrolled, and its z-index sits above the navbar
   so nothing in the nav can overlap it. Wraps on narrow viewports instead of
   truncating — the subject's name is the whole point of the bar, so it is the
   last thing that may be cut.
   ------------------------------------------------------------------------- */
.spoof-banner {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  padding: 0.55rem 1.5rem;
  background: var(--vantage-spoof-bg);
  color: var(--vantage-spoof-text);
  border-bottom: 2px solid var(--vantage-spoof-border);
  font-size: 0.9rem;
}

.spoof-banner-text {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.55rem;
  min-width: 0;
}

.spoof-banner-label {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.72rem;
}

.spoof-banner-text strong {
  color: var(--vantage-spoof-text);
}

/* Secondary, but not dimmed to the point of being decorative: which org is on
   screen is the thing being confirmed. */
.spoof-banner-detail {
  opacity: 0.85;
}

.spoof-banner-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.spoof-banner-actions form {
  margin: 0;
}

/* Bulma's is-light button keeps its own light ground in both themes, which is
   what gives the two actions contrast against the amber. */
.spoof-banner .button.is-light.is-outlined {
  background: transparent;
  border-color: currentColor;
  color: var(--vantage-spoof-text);
}

/* Cross-tab identity warning. Raised by identity-watch.js when another tab
   changes who this browser is acting as, so it has to read as urgent while a
   form on this page is still filled in. Shares the spoof banner's ground --
   the two describe the same condition -- but sits in the flow rather than
   sticky, since it is a one-time notice rather than persistent state. */
.identity-warning {
  padding: 0.6rem 1.5rem;
  background: var(--vantage-spoof-bg);
  color: var(--vantage-spoof-text);
  border-bottom: 2px solid var(--vantage-spoof-border);
  font-size: 0.9rem;
}

.identity-warning[hidden] {
  display: none;
}
