/* components.css - the reusable pieces.
 *
 * Components size themselves with CONTAINER queries, not media queries.
 * A card does not care how wide the viewport is; it cares how much room
 * it has been given. That is what lets the same card sit in a narrow
 * rail and a wide grid without a special case for each.
 */

/* --- Verdict: the lead answer on a page ----------------------------- */
.verdict {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-5, var(--space-4));
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-6);
}
.verdict__label {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}
/* The lead answer. Numbers use the tabular mono face so they stay
 * aligned and do not jitter as they change; a verdict that is PROSE
 * uses the display face, because setting a sentence in a numeral font
 * reads as a readout rather than an answer. */
.verdict__value {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  line-height: 1.15;
  text-wrap: balance;
}
.verdict__value--num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-3xl);
  line-height: 1.1;
}
.verdict__note { color: var(--ink-muted); font-size: var(--text-sm); margin: 0; }

/* --- Stat row ------------------------------------------------------- */
.stats {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
}
@media (min-width: 480px) { .stats { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 768px) { .stats { grid-template-columns: repeat(4, 1fr); } }

.stat {
  padding: var(--space-4);
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.stat__label { font-size: var(--text-xs); color: var(--ink-faint); display: block; margin-bottom: var(--space-1); }
.stat__value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xl);
  display: block;
}
.stat__note { font-size: var(--text-xs); color: var(--ink-muted); }

/* --- Cards ---------------------------------------------------------- */
.card-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
/* Two-step only: 1 to 2 at 480, 2 to 3 at 1024. NOTHING transitions
 * between 600 and 800, which is exactly where iPad portrait (768, 810,
 * 834) lands - a breakpoint in that band is what makes tablet layouts
 * break. */
@media (min-width: 480px) { .card-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .card-grid { grid-template-columns: repeat(3, 1fr); } }

.card {
  container-type: inline-size;
  display: grid;
  gap: var(--space-2);
  align-content: start;
  padding: var(--space-4);
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.card__head {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
}
.card__title { font-size: var(--text-base); font-weight: 600; margin: 0; font-family: var(--font-body); }
.card__meta { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); font-size: var(--text-xs); color: var(--ink-muted); }
.card__body { font-size: var(--text-sm); color: var(--ink-muted); margin: 0; }

/* --- Chips ---------------------------------------------------------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  border-radius: 999px;
  font-size: var(--text-xs);
  background: var(--paper-sunken);
  border: 1px solid var(--rule);
  color: var(--ink-muted);
  white-space: nowrap;
}
.chip--ok    { background: var(--ok-soft);    border-color: var(--ok);    color: var(--ok); }
.chip--warn  { background: var(--warn-soft);  border-color: var(--warn);  color: var(--warn); }
.chip--alert { background: var(--alert-soft); border-color: var(--alert); color: var(--alert); }
.chip--info  { background: var(--info-soft);  border-color: var(--info);  color: var(--info); }
.chip--accent{ background: var(--accent-soft);border-color: var(--accent-edge); color: var(--accent); }

/* Provenance marker. Unconfirmed figures must never read like checked
 * ones - this is the visual half of the rule the database enforces. */
.prov {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  color: var(--ink-faint);
}
.prov--unconfirmed { color: var(--warn); }
.prov--unconfirmed::before {
  content: "";
  width: 0.5em; height: 0.5em;
  border-radius: 999px;
  border: 1.5px dashed currentColor;
}
.prov--confirmed::before {
  content: "";
  width: 0.5em; height: 0.5em;
  border-radius: 999px;
  background: currentColor;
}
.prov--confirmed { color: var(--ok); }

/* A value whose provenance is untrusted is shown, but visibly held at
 * arm's length: it is never presented as a fact. */
.value--provisional {
  color: var(--ink-muted);
  text-decoration: underline dotted var(--warn);
  text-underline-offset: 0.25em;
}

/* --- Funding bar ---------------------------------------------------- */
.fund {
  display: grid;
  gap: var(--space-1);
}
.fund__track {
  height: 8px;
  border-radius: 999px;
  background: var(--paper-sunken);
  border: 1px solid var(--rule);
  overflow: hidden;
}
/* The width is the one thing a script may set inline, and only as a
 * custom property consumed here - never as a style attribute on the
 * element. */
.fund__fill {
  height: 100%;
  width: var(--pct, 0%);
  background: var(--accent);
  border-radius: 999px;
}
.fund__legend {
  display: flex;
  justify-content: space-between;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--ink-muted);
}

/* --- Roadmap bands -------------------------------------------------- */
.bands { display: grid; grid-template-columns: 1fr; gap: var(--space-4); }
@media (min-width: 480px) { .bands { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .bands { grid-template-columns: repeat(4, 1fr); } }

.band { display: grid; gap: var(--space-2); align-content: start; }
.band__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-2);
  padding-bottom: var(--space-2);
  border-bottom: 2px solid var(--accent-edge);
}
.band__title { font-family: var(--font-display); font-size: var(--text-lg); margin: 0; }
.band__count { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--ink-faint); }

/* --- Tables --------------------------------------------------------- */
/* A table is the one thing allowed to be wider than the page, and only
 * inside its own scroll container so the PAGE never scrolls sideways. */
.table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--paper-raised);
}
.table { font-size: var(--text-sm); min-width: 34rem; }
.table th, .table td {
  padding: var(--space-2) var(--space-3);
  text-align: left;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
.table th {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  font-weight: 600;
  position: sticky; top: 0;
  background: var(--paper-raised);
}
.table tr:last-child td { border-bottom: 0; }
.table td.num, .table th.num { text-align: right; }

/* --- Detail disclosure ---------------------------------------------- */
/* Depth on demand: the full context of an item is one interaction away
 * from anywhere it appears, and never the default view. */
details.detail { border-top: 1px solid var(--rule); padding-top: var(--space-2); }
details.detail > summary {
  cursor: pointer;
  min-height: var(--tap-min-floor);
  display: flex; align-items: center;
  font-size: var(--text-sm);
  color: var(--accent);
  list-style: none;
}
details.detail > summary::-webkit-details-marker { display: none; }
details.detail > summary::after { content: " +"; font-family: var(--font-mono); }
details.detail[open] > summary::after { content: " \2212"; }
details.detail > div { padding-block: var(--space-2); font-size: var(--text-sm); color: var(--ink-muted); }

.dl { display: grid; grid-template-columns: auto 1fr; gap: var(--space-1) var(--space-3); margin: 0; font-size: var(--text-sm); }
.dl dt { color: var(--ink-faint); }
.dl dd { margin: 0; }

/* --- Notices -------------------------------------------------------- */
.notice {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  border: 1px solid var(--rule);
  background: var(--paper-sunken);
  font-size: var(--text-sm);
  margin-bottom: var(--space-4);
}
.notice--warn { background: var(--warn-soft); border-color: var(--warn); }
.notice__title { font-weight: 600; display: block; margin-bottom: var(--space-1); }

.empty {
  padding: var(--space-8) var(--space-4);
  text-align: center;
  color: var(--ink-muted);
  border: 1px dashed var(--rule-strong);
  border-radius: var(--radius);
}

.section { margin-bottom: var(--space-12); }
.section__head {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: var(--space-2);
  margin-bottom: var(--space-4);
}
.lede { color: var(--ink-muted); max-width: var(--measure); }

/* --- Form controls --------------------------------------------------
 * The only inputs in this system are filters - nothing here writes. But
 * a filter is still a control a person has to hit, so it obeys the same
 * target and focus rules as anything else. An unstyled <select> renders
 * around 19px tall, under the 24px WCAG 2.5.8 floor, which the browser
 * check catches. */
label {
  font-size: var(--text-sm);
  color: var(--ink-muted);
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  margin-right: var(--space-1);
}

select, input, textarea {
  font: inherit;
  /* 1rem floor: below 16px, iOS Safari zooms the viewport on focus and
   * the user is left scrolled sideways on a page that had no overflow. */
  font-size: max(1rem, var(--text-sm));
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-3);
  color: var(--ink);
  background: var(--paper-raised);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  max-width: 100%;
}
select { padding-right: var(--space-6); }
select:hover, input:hover { border-color: var(--ink-faint); }

fieldset[role="group"] {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}

/* --- Login -----------------------------------------------------------
 * The only form in the system. Centred in the viewport on a phone
 * without using vh, which would misreport the visible area under mobile
 * browser chrome. */
.login {
  display: grid;
  gap: var(--space-4);
  width: 100%;
  max-width: 24rem;
  margin-inline: auto;
  margin-block: var(--space-12);
  padding: var(--space-6);
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
}
.login__title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  margin: 0;
}
.login__lede { color: var(--ink-muted); margin: 0; font-size: var(--text-sm); }
.login__error {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--alert-soft);
  border: 1px solid var(--alert);
  color: var(--alert);
  font-size: var(--text-sm);
}

.field { display: grid; gap: var(--space-1); }
.field label { min-height: auto; }
.field input { width: 100%; }

.btn {
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-5, var(--space-4));
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--accent-ink);
  font: inherit;
  font-size: max(1rem, var(--text-sm));
  font-weight: 600;
  cursor: pointer;
}
.btn:hover { background: color-mix(in oklch, var(--accent) 88%, var(--ink)); }
.btn[disabled] { opacity: 0.6; cursor: default; }

.login__demo { margin-top: var(--space-6); margin-bottom: 0; }

/* Sign-out sits in the header next to the theme control. */
.signout {
  min-height: var(--tap-min);
  display: inline-flex;
  align-items: center;
  padding-inline: var(--space-3);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-muted);
  font: inherit;
  font-size: var(--text-sm);
  cursor: pointer;
}
.signout:hover { border-color: var(--rule-strong); color: var(--ink); }

.header-actions { display: flex; gap: var(--space-2); align-items: center; }

/* --- Roadmap toolbar -------------------------------------------------
 * The one place with real controls. They wrap freely so the row reflows
 * on a phone instead of scrolling sideways. */
.toolbar {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--paper-sunken);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  margin-bottom: var(--space-6);
}
.toolbar__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}
.control { display: inline-flex; align-items: center; gap: var(--space-2); }
.control--grow { flex: 1 1 14rem; }
.control--grow input { width: 100%; }
.control--check { gap: var(--space-2); }
/* A checkbox must clear the 24px WCAG 2.5.8 floor in its own right -
 * a large label beside a small box does not make the box hittable.
 * The browser gate measures the input, which is how the 18px default
 * was caught. */
.control--check input {
  min-height: var(--tap-min-floor);
  min-width: var(--tap-min-floor);
  width: var(--tap-min-floor);
  height: var(--tap-min-floor);
  margin: 0;
  padding: 0;
  accent-color: var(--accent);
}
.control--check label { min-height: var(--tap-min); cursor: pointer; }
.toolbar .lede { margin: 0; font-size: var(--text-sm); }

/* Segmented view switch. A pressed state carried by colour AND weight
 * AND a border, never colour alone. */
.seg {
  min-height: var(--tap-min);
  padding-inline: var(--space-4);
  border: 1px solid var(--rule-strong);
  background: var(--paper-raised);
  color: var(--ink-muted);
  font: inherit;
  font-size: max(1rem, var(--text-sm));
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.seg + .seg { margin-left: calc(var(--space-1) * -1); }
.seg:hover { color: var(--ink); }
.seg.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.btn--quiet {
  background: var(--paper-raised);
  border-color: var(--rule-strong);
  color: var(--ink-muted);
  font-weight: 500;
}
.btn--quiet:hover { background: var(--paper-sunken); color: var(--ink); }

/* Board with an arbitrary number of swimlanes: fixed columns only work
 * for the four horizons, and grouping by room can produce a dozen. */
.bands--auto { grid-template-columns: 1fr; }
@media (min-width: 480px) { .bands--auto { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .bands--auto { grid-template-columns: repeat(3, 1fr); } }

/* --- Timeline (waterfall) -------------------------------------------
 * A CSS-grid Gantt. Bars are positioned with custom properties rather
 * than inline styles proper, so the layout stays in the stylesheet and
 * only the two numbers come from the data.
 *
 * The chart is the one thing allowed to be wider than the page, inside
 * its own scroll container, so the page itself never scrolls sideways. */
.gantt-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--paper-raised);
  padding: var(--space-3);
}
.gantt { min-width: 34rem; display: grid; gap: var(--space-1); }
.gantt__group { display: grid; gap: var(--space-1); margin-top: var(--space-4); }
.gantt__group-title {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  margin: 0 0 var(--space-1);
  display: flex; gap: var(--space-2); align-items: baseline;
}
.gantt__row {
  display: grid;
  grid-template-columns: minmax(9rem, 16rem) 1fr;
  gap: var(--space-3);
  align-items: center;
}
.gantt__row--axis { position: sticky; top: 0; z-index: 1; background: var(--paper-raised); }
.gantt__label { display: flex; justify-content: space-between; gap: var(--space-2); align-items: baseline; min-width: 0; }
.gantt__title {
  font-size: var(--text-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gantt__meta { font-size: var(--text-xs); }
.gantt__track {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--months), minmax(1.5rem, 1fr));
  min-height: 1.5rem;
  align-items: center;
  background: repeating-linear-gradient(
    to right, transparent, transparent calc(100% / var(--months) - 1px),
    var(--rule) calc(100% / var(--months) - 1px), var(--rule) calc(100% / var(--months)));
  border-radius: var(--radius-sm);
}
.gantt__bar {
  grid-column: var(--start) / span var(--span);
  height: 1.1rem;
  border-radius: 999px;
  background: var(--accent);
  border: 1px solid var(--accent);
}
.gantt__bar--free { background: var(--ok); border-color: var(--ok); }
/* Hatched, not merely a different colour: "cannot be funded in range"
 * must not depend on hue alone to be legible. */
.gantt__bar--beyond {
  background: repeating-linear-gradient(45deg,
    var(--warn) 0 4px, transparent 4px 8px);
  border-color: var(--warn);
}
.gantt__axis { background: none; }
.gantt__tick {
  grid-column: var(--at);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-faint);
  text-align: center;
}
.gantt__tick--beyond { color: var(--warn); }

/* Filters collapse on a phone and are permanently open from tablet up.
 * A closed <details> hides its non-summary children, so the wide-screen
 * rule has to re-show them explicitly rather than rely on [open]. */
.filters { border: 0; padding: 0; }
.filters > summary {
  cursor: pointer;
  min-height: var(--tap-min);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--accent);
  list-style: none;
}
.filters > summary::-webkit-details-marker { display: none; }
.filters > summary::after { content: " +"; font-family: var(--font-mono); }
.filters[open] > summary::after { content: " \2212"; }
.filters__body { padding-top: var(--space-2); }

@media (min-width: 768px) {
  .filters > summary { display: none; }
  .filters > .filters__body { display: flex; }
}

/* --- Shopping -------------------------------------------------------- */

/* A trip is a section with its own subtotal, so it needs to read as a
 * unit without a card's shadow and border doing the work twice. */
.shop-trip { border-top: 1px solid var(--rule); padding-top: var(--space-4); }

.shop-ready { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.shop-ready > li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--tap-min-floor);
  padding: var(--space-2) var(--space-3);
  background: var(--paper-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
}

/* An estimate outside its benchmark range is the most useful thing on
 * the page, so it is called out in the row rather than left to be
 * inferred from two numbers sitting near each other. */
.shop-flag {
  margin: var(--space-1) 0 0;
  font-size: var(--text-xs);
  color: var(--warn);
}

.shop-marks { list-style: none; margin: var(--space-1) 0 0; padding: 0; display: grid; gap: 2px; }
.shop-marks > li {
  display: flex;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--ink-muted);
}
.shop-mark-age { color: var(--ink-faint); }
