/* Reconciliation — deep-glass surfaces, state-driven hue, fixed layout.
 *
 * Two rules govern everything here:
 *
 * 1. STATE IS SHOWN AS HUE, NOT AS A PILL. Each panel carries a broad gradient
 *    bloom rising from its bottom centre — green when healthy, amber degraded,
 *    red failing. There are no coloured left borders anywhere on this page.
 *
 * 2. THE LAYOUT IS FIXED. Every panel reserves its final height before content
 *    arrives, so an htmx swap changes content but never geometry. Skeletons
 *    occupy the exact space the real rows will, numbers sit in tabular-figure
 *    slots, and an empty section keeps its height rather than collapsing.
 */

/* --- Glass foundation ------------------------------------------------------ */

.recon {
  /* Tuned per theme below. Declared here so every glass surface reads one set. */
  --glass-bg: rgba(255, 255, 255, 0.42);
  --glass-border: rgba(255, 255, 255, 0.75);
  --glass-blur: 20px;
  /* Tight contact shadow + wide ambient + hairline ring, rather than one diffuse
     blur — a single soft shadow doesn't lift the panel off a light ground. */
  --glass-shadow:
    0 0 0 1px rgba(20, 21, 31, 0.035),
    0 1px 2px rgba(20, 21, 31, 0.05),
    0 12px 28px -8px rgba(20, 21, 31, 0.1),
    0 32px 64px -24px rgba(20, 21, 31, 0.08);
  --glass-inner: inset 0 1px 0 rgba(255, 255, 255, 0.7);
  /* A specular must read as light, so it stays white-ish in both themes and
     varies only by alpha. Deriving it from --surface made it invisible in dark
     (surface is darker than the glass it sat on) and near-invisible in light. */
  --glass-specular: rgba(255, 255, 255, 0.55);
  /* The bloom colour; each panel overrides via a state class. */
  --bloom: var(--good);
  /* Asymmetric by necessity: a saturated colour at low alpha shifts luminance
     far more over a light ground than a dark one. Equal values gave ~25 ΔL in
     light and ~4 ΔL in dark, i.e. invisible in dark. */
  --bloom-strength: 0.16;
}

[data-theme="dark"] .recon {
  --glass-bg: rgba(30, 32, 41, 0.42);
  --glass-border: rgba(255, 255, 255, 0.09);
  --glass-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.045),
    0 2px 4px rgba(0, 0, 0, 0.4),
    0 16px 40px -12px rgba(0, 0, 0, 0.55);
  --glass-inner: inset 0 1px 0 rgba(255, 255, 255, 0.06);
  --glass-specular: rgba(255, 255, 255, 0.055);
  --bloom-strength: 0.55;
  /* Muted enough that light-mode's darker muted token isn't needed here. */
  --text-muted: #9799a8;
}

/* The bloom eats the contrast margin under small muted text (0.71rem labels sit
   well under the large-text threshold), so light mode gets a darker muted tone
   on this page only. */
.recon {
  --text-muted: #5c5e6d;
}

.recon-daybar,
.recon-account,
/* Composition sits outside .recon-groups (full width, above both columns), so
   it needs its own bottom margin rather than inheriting the columns' flex gap. */
#recon-group-composition {
  margin-bottom: 1rem;
}

/* Two independent columns, not a grid or CSS multi-column: each is its own
   explicit stack of groups from the backend (see overview.py::_LEFT_COLUMN /
   _RIGHT_COLUMN), so a column's height is whatever its own groups add up to —
   never rebalanced against the other column, and which group lands in which
   column never shifts as row counts change day to day. Composition sits above
   both, full width, since it's an inherently two-panel side-by-side comparison.
   Below the breakpoint the columns stack into one. */
.recon-groups {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
}

.recon-groups__col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* Without backdrop-filter the panel is a plain translucent fill; at 0.42 that
   would be marginal for text contrast, so raise it to near-opaque. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .recon { --glass-bg: rgba(255, 255, 255, 0.92); }
  [data-theme="dark"] .recon { --glass-bg: rgba(30, 32, 41, 0.94); }
}

/* The glazed panel. `isolation` keeps the bloom's blend inside the panel, and
   the ::after highlight gives the top edge its wet, glassy catch of light. */
.glass {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border: 1px solid var(--glass-border);
  border-radius: 16px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(180%);
  box-shadow: var(--glass-shadow), var(--glass-inner);
}

/* The bloom: a wide, soft ellipse of state colour rising from bottom centre.
   Sits behind content (z-index -1) so text never fights it. */
/* A wide, shallow lens concentrated at the bottom centre. The earlier near-circular
   wash tinted the whole lower half, which read as "coloured card" rather than a
   bloom of light. */
.glass::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -45%;
  z-index: -1;
  width: 130%;
  height: 90%;
  transform: translateX(-50%);
  background: radial-gradient(
    ellipse 60% 100% at 50% 100%,
    color-mix(in srgb, var(--bloom) calc(var(--bloom-strength) * 100%), transparent) 0%,
    color-mix(in srgb, var(--bloom) calc(var(--bloom-strength) * 32%), transparent) 30%,
    transparent 62%
  );
  pointer-events: none;
  transition: background 0.5s ease;
}

/* In dark mode the bloom must add light rather than tint, which is what the
   panel's `isolation: isolate` exists to contain. */
[data-theme="dark"] .glass::before {
  mix-blend-mode: screen;
}

/* Specular top edge — the "glazed" catch of light. */
.glass::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 38%;
  z-index: -1;
  background: linear-gradient(to bottom, var(--glass-specular), transparent 90%);
  pointer-events: none;
}

/* State drives hue only. */
.is-good { --bloom: var(--good); }
.is-warn { --bloom: var(--warn); }
.is-bad { --bloom: var(--bad); }
.is-idle { --bloom: var(--text-muted); --bloom-strength: 0.1; }

/* --- Day bar --------------------------------------------------------------- */

.recon-daybar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  min-height: 62px;
  padding: 0.75rem 1rem;
}

.recon-daybar__nav {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.recon-daystep {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--glass-border);
  border-radius: 9px;
  background: color-mix(in srgb, var(--surface) 40%, transparent);
  color: var(--text-muted);
  font-size: 0.9rem;
  line-height: 1;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.recon-daystep:hover {
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent);
}

.recon-daybar__current {
  display: flex;
  flex-direction: column;
  gap: 0.12rem;
  min-width: 11rem;
  padding: 0 0.4rem;
}

.recon-daybar__date {
  font-size: 1rem;
  font-weight: 660;
  letter-spacing: -0.015em;
}

.recon-daybar__note {
  font-size: 0.71rem;
  color: var(--text-muted);
}

.recon-daybar__form {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-left: auto;
}

.recon-daybar__form input[type="date"] {
  flex: 0 0 auto;
  width: 11.5rem;
  min-width: 0;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--glass-border);
  border-radius: 9px;
  background: color-mix(in srgb, var(--surface) 55%, transparent);
  color: var(--text);
  color-scheme: light dark;
  font-size: 0.82rem;
}

/* .btn is a shared auth-form style (width: 100%) — undo that here so it sits
   inline at its natural size instead of stretching across the day bar. */
.recon-daybar__form .btn {
  width: auto;
  padding: 0.4rem 0.9rem;
  font-size: 0.82rem;
}

/* --- Headline account ------------------------------------------------------ */

/* Fixed height: the verdict, figures, bar and reading line all have reserved
   space, so the skeleton and the loaded state are exactly the same size. */
.recon-account {
  min-height: 216px;
  padding: 1.1rem 1.25rem;
}

.recon-account__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
}

.recon-account__verdict {
  font-size: 1.05rem;
  font-weight: 680;
  letter-spacing: -0.015em;
}

.recon-account__stamp {
  font-size: 0.74rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* Pinned to 5 columns rather than auto-fit: auto-fit silently dropped to 4-up
   below ~649px, which wrapped the row and grew the panel by ~63px. Values shrink
   instead, and the narrow breakpoint below switches deliberately. */
.recon-account__figures {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0.75rem 1rem;
  margin-bottom: 1.05rem;
}

.recon-figure {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  /* Reserved so the row never changes height between skeleton and data. */
  min-height: 46px;
}

.recon-figure__value {
  font-size: 1.6rem;
  font-weight: 690;
  line-height: 1;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
}

.recon-figure__value--bad { color: var(--bad); }
.recon-figure__value--good { color: var(--good); }

.recon-figure__label {
  font-size: 0.71rem;
  line-height: 1.3;
  color: var(--text-muted);
}

/* The day's account bar: each term of
   dispatched = batched_live + cancelled + rejected + awaiting
   drawn to scale, with any shortfall hatched so a gap reads as an absence. */
.recon-bar {
  display: flex;
  height: 10px;
  border-radius: 999px;
  overflow: hidden;
  background: color-mix(in srgb, var(--text) 9%, transparent);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12);
}

.recon-bar__seg {
  height: 100%;
  transition: width 0.4s ease;
}

.recon-bar__seg--batched { background: linear-gradient(180deg, color-mix(in srgb, var(--good) 82%, white), var(--good)); }
.recon-bar__seg--cancelled { background: linear-gradient(180deg, color-mix(in srgb, var(--bad) 70%, white), var(--bad)); }
.recon-bar__seg--rejected { background: linear-gradient(180deg, color-mix(in srgb, var(--bad) 55%, black), color-mix(in srgb, var(--bad) 80%, black)); }
.recon-bar__seg--awaiting { background: linear-gradient(180deg, color-mix(in srgb, var(--warn) 80%, white), var(--warn)); }

.recon-bar__seg--unaccounted {
  background: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, var(--text-muted) 70%, transparent),
    color-mix(in srgb, var(--text-muted) 70%, transparent) 3px,
    transparent 3px,
    transparent 6px
  );
}

/* Two rows reserved unconditionally: the number of key items varies by day
   (each is conditional on a non-zero value), so a single-row reservation made
   the panel a different height on different days at the same width.
   overflow:hidden guarantees a third row can never appear. */
.recon-key {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 0.85rem;
  margin-top: 0.55rem;
  min-height: 49.6px;
  overflow: hidden;
}

.recon-key__item {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.73rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.recon-key__swatch {
  width: 14px;
  height: 9px;
  border-radius: 3px;
  flex: none;
}

/* 3em is exactly two lines at line-height 1.5 — the previous 2.5em reserved only
   1.67 lines. Clamped as well as reserved, because the reading concatenates up to
   four clauses and ran to four lines on precisely the worst days. */
.recon-account__reading {
  margin: 0.85rem 0 0;
  font-size: 0.83rem;
  line-height: 1.5;
  color: var(--text-muted);
  min-height: 3em;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.recon-account__reading--error {
  color: var(--bad);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.76rem;
  -webkit-line-clamp: 3;
}

/* --- Groups ----------------------------------------------------------------- */

/* Every group is a fixed, always-open panel — never a collapsible section — so
   the header is just a static row, same height regardless of load state. */
.recon-group__head {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  width: 100%;
  min-height: 48px;
  padding: 0.55rem 0.9rem;
  text-align: left;
}

.recon-group__title {
  font-size: 0.86rem;
  font-weight: 660;
  letter-spacing: -0.01em;
}

/* Sits in the head's flex gap, so no margin of its own. Muted and slightly
   optically lowered so it reads as a marker beside the title, not a second
   emphasis competing with it. */
.recon-group__icon {
  flex: none;
  color: var(--text-muted);
  transform: translateY(0.5px);
}

.recon-group__figures {
  display: flex;
  align-items: baseline;
  gap: 1.1rem;
  margin-left: auto;
}

.recon-group__stat {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  font-variant-numeric: tabular-nums;
}

.recon-group__stat-value {
  font-size: 1.05rem;
  font-weight: 680;
  letter-spacing: -0.02em;
}

.recon-group__stat-label {
  font-size: 0.71rem;
  color: var(--text-muted);
}

.recon-group__body {
  padding: 0 0.9rem 0.85rem;
}

/* --- Rows ------------------------------------------------------------------ */

/* Fixed row height, so a list of skeletons is exactly as tall as the real list
   that replaces it. */
.recon-row {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto;
  align-items: center;
  gap: 1rem;
  min-height: 52px;
  padding: 0.6rem 0.75rem;
  border-radius: 11px;
  border: 1px solid transparent;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.recon-row + .recon-row {
  margin-top: 0.3rem;
}

.recon-row:hover {
  border-color: var(--glass-border);
  background: color-mix(in srgb, var(--surface) 40%, transparent);
}

.recon-row__primary {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.recon-row__code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.recon-row__sub {
  font-size: 0.72rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.recon-row__meta {
  font-size: 0.75rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* State marker: a small hue dot, not a pill.
   Colour alone is not sufficient — --good and --bad are near-identical under
   deuteranopia — so each state also carries a distinct SHAPE core, and every
   marker is paired with visually-hidden text in the template. */
.recon-dot {
  position: relative;
  width: 9px;
  height: 9px;
  border-radius: 999px;
  flex: none;
  background: var(--bloom);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--bloom) 18%, transparent);
}

/* Good keeps the plain disc. Warn gets a square core, bad a diamond. */
.is-warn .recon-dot::after,
.is-bad .recon-dot::after {
  content: "";
  position: absolute;
  inset: 2px;
  background: var(--glass-bg);
}

.is-bad .recon-dot::after {
  transform: rotate(45deg);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Skeleton primitives (.skel*) now live in skeleton.css, shared by every page. */

@media (prefers-reduced-motion: reduce) {
  .recon-bar__seg,
  .glass::before { transition: none; }
}

@media (max-width: 720px) {
  .recon-row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .recon-row__meta {
    display: none;
  }

  .recon-group__figures {
    margin-left: 0;
  }
}

/* --- Composition (two systems, two partitions) ------------------------------ */

.recon-comp {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.4rem;
}

.recon-comp__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
}

.recon-comp__system {
  font-size: 0.85rem;
  font-weight: 650;
}

.recon-comp__total {
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* The column the partition is taken over — shown so the reader knows what they
   are looking at rather than trusting the labels. */
.recon-comp__dimension {
  display: block;
  margin-bottom: 0.7rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.68rem;
  color: var(--text-muted);
}

.recon-pot {
  display: grid;
  grid-template-columns: 2.4rem minmax(0, 1fr);
  align-items: baseline;
  gap: 0.15rem 0.6rem;
  min-height: 40px;
  padding: 0.3rem 0;
}

.recon-pot__count {
  font-size: 1rem;
  font-weight: 680;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.recon-pot__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.recon-pot__label {
  font-size: 0.82rem;
  font-weight: 550;
}

.recon-pot__note {
  font-size: 0.71rem;
  line-height: 1.35;
  color: var(--text-muted);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  overflow: hidden;
}

.recon-pot__rail {
  grid-column: 2;
  height: 3px;
  margin-top: 0.3rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--text) 8%, transparent);
}

.recon-pot__fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 70%, transparent);
}

.recon-comp__empty {
  font-size: 0.8rem;
  color: var(--text-muted);
}

/* --- Check rows ------------------------------------------------------------ */

.recon-check {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.6rem;
  min-height: 46px;
  padding: 0.45rem 0.3rem;
}

.recon-check + .recon-check {
  border-top: 1px solid color-mix(in srgb, var(--text) 7%, transparent);
}

.recon-check__marker {
  display: flex;
  align-items: center;
}

.recon-check__body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.recon-check__name {
  font-size: 0.85rem;
  font-weight: 620;
}

/* Two lines reserved and clamped — reasons quote live figures and vary in length
   between days, which would otherwise change the row height. */
.recon-check__reason {
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--text-muted);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.recon-check__reason--error {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  color: var(--bad);
}

.recon-check__figure {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.recon-check__count {
  font-size: 1.2rem;
  font-weight: 690;
  letter-spacing: -0.02em;
  color: var(--bloom);
}

.recon-check__of {
  font-size: 0.78rem;
  color: var(--text-muted);
}

.recon-check__link {
  font-size: 0.78rem;
  font-weight: 560;
  color: var(--accent);
  white-space: nowrap;
  text-decoration: none;
}

.recon-check__link:hover {
  text-decoration: underline;
}

/* The hatch needs a ground to read against; the bar has one, a bare swatch does
   not. background-color must follow the shorthand, which clears it. */
.recon-key__swatch.recon-bar__seg--unaccounted {
  background-color: color-mix(in srgb, var(--text) 9%, transparent);
  background-size: 8.49px 8.49px;
}

/* --cancelled and --rejected are both --bad-derived and merge under protanopia,
   so rejected is separated by texture rather than lightness alone. */
.recon-bar__seg--rejected {
  background-image: repeating-linear-gradient(
    45deg,
    rgba(0, 0, 0, 0.28),
    rgba(0, 0, 0, 0.28) 2px,
    transparent 2px,
    transparent 4px
  );
}

@media (prefers-reduced-motion: reduce) {
  .recon-daystep,
  .recon-row {
    transition: none;
  }
}

@media (max-width: 900px) {
  .recon-groups {
    flex-direction: column;
  }
}

@media (max-width: 720px) {
  .recon-account__figures {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .recon-account {
    min-height: 320px;
  }
}

/* --- Hub coverage page ------------------------------------------------------ */

.coverage-filters {
  display: flex;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.coverage-filters__field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.coverage-filters__field select {
  min-width: 12rem;
}

.coverage-table {
  display: flex;
  flex-direction: column;
}

.coverage-row {
  grid-template-columns: minmax(0, 1.3fr) auto minmax(0, 1fr) auto;
}

.coverage-row__distance {
  font-variant-numeric: tabular-nums;
  font-size: 0.9rem;
  font-weight: 650;
  color: var(--bloom);
  white-space: nowrap;
}

.coverage-row__links {
  display: flex;
  gap: 0.75rem;
  font-size: 0.75rem;
  white-space: nowrap;
}

.coverage-row__links a {
  color: var(--accent);
  text-decoration: none;
}

.coverage-row__links a:hover {
  text-decoration: underline;
}
