/* Sized for a projector first and a phone second: big numbers, high contrast,
   nothing that depends on a viewer being close to the screen.

   The palette is the wallet app's: a near-neutral ground, violet reserved for
   the brand mark alone, and exactly two data accents. Orange is what is owed,
   teal is what has settled. Red appears only when a verification fails, so
   red on this page always means something is wrong. */

:root {
  color-scheme: light dark;

  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
    Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --maxw: 1600px;

  /* Light ground. */
  --bg: #ffffff;
  --tint: #f6f6f8;
  --border: #e6e6e9;
  --border-strong: #d4d4d8;
  --text: #0a0a0b;
  --muted: #52525b;
  --faint: #70727b;

  /* The brand accent is violet, and it is used for the title underline and
     the eyebrow square and nothing else. Data never borrows it. */
  --brand: #5a1fd6;

  /* Two values per data accent: a darkened one for text, which clears 4.5:1
     against both the white ground and the tinted band, and the bright brand
     hue for shapes that carry no text. The bright hues sit near 3:1 on white,
     which is why they are fills only. A projector loses contrast before it
     loses anything else, so the text tones are the ones that get used at
     size, even on the figures where large-text rules would allow brighter. */
  --owed-text: #ab5c05;
  --owed-fill: #d4720a;
  --paid-text: #087768;
  --paid-fill: #0cb09a;
  --paid-edge: #0cb09a;
  --bad: #c0392f;
  --bad-fill: #d14343;

  --track: rgba(212, 114, 10, 0.15);
  --rule-over: rgba(10, 10, 11, 0.24);
  --paid-wash: rgba(12, 176, 154, 0.12);
  --bad-wash: rgba(209, 67, 67, 0.15);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #141417;
    --tint: #1b1b20;
    --border: #2c2c33;
    --border-strong: #44444d;
    --text: #f5f5f7;
    --muted: #b6b6c0;
    --faint: #8c8c96;

    --brand: #a78bfa;

    /* Dark needs no text/fill split for the accents: every bright hue already
       clears AA on this ground. The bar fill is pulled a shade deeper than the
       text tone so a 44px block of it does not glow. */
    --owed-text: #ffa733;
    --owed-fill: #ffa733;
    --paid-text: #15e0c2;
    --paid-fill: #0fb9a2;
    --paid-edge: #46f0d6;
    --bad: #e0686b;
    --bad-fill: #c0393c;

    --track: rgba(255, 167, 51, 0.17);
    --rule-over: rgba(255, 255, 255, 0.26);
    --paid-wash: rgba(21, 224, 194, 0.11);
    --bad-wash: rgba(224, 104, 107, 0.19);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Bands are full-bleed horizontal zones; the wrap centres their content to a
   shared measure so every band lines up on a wide screen. A hairline is the
   only thing that separates one from the next, and the single tinted band is
   the one the eye should land on first. */
.band { width: 100%; padding: clamp(1.25rem, 2.4vw, 2rem) 0; }
.band + .band { border-top: 1px solid var(--border); }
.band-tint { background: var(--tint); }

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: clamp(1rem, 3vw, 2.5rem);
}

/* Micro-labels are the page's connective tissue: mono, uppercase, tracked
   out. They are deliberately small, so the figures carry the hierarchy on
   their own rather than competing with a label of similar weight. */
.label,
.eyebrow {
  margin: 0;
  font: 600 0.7rem/1 var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--muted);
}

.sq {
  display: inline-block;
  width: 0.55rem;
  height: 0.55rem;
  margin-right: 0.5rem;
  vertical-align: baseline;
}
.sq-brand { background: var(--brand); }
.sq-owed { background: var(--owed-fill); }
.sq-paid { background: var(--paid-fill); }

/* ---- header ---- */

.head {
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1.5rem;
}

h1 {
  margin: 0.6rem 0 0;
  font-size: clamp(1.4rem, 2.2vw, 2rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
}

.rule-accent {
  display: block;
  width: 3rem;
  height: 3px;
  margin: 0.65rem 0 0.7rem;
  background: var(--brand);
}

.lede {
  margin: 0;
  max-width: 62ch;
  font-size: 0.9rem;
  color: var(--muted);
}

/* The connection badge is text-only in the markup, because board.js rewrites
   its text on every state change; the dot is a pseudo-element for that
   reason. */
.conn {
  flex: none;
  display: inline-flex;
  align-items: center;
  font: 600 0.7rem/1 var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.13em;
  padding: 0.5rem 0.7rem 0.5rem 1.55rem;
  position: relative;
  border: 1px solid var(--border-strong);
  color: var(--muted);
  background: var(--bg);
}
.conn::before {
  content: "";
  position: absolute;
  left: 0.7rem;
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: currentColor;
}
.conn[data-state="live"] { color: var(--paid-text); border-color: var(--paid-text); }
.conn[data-state="down"] { color: var(--bad); border-color: var(--bad); }
.conn[data-state="down"]::before { animation: blink 1.1s steps(1) infinite; }

@keyframes blink { 50% { opacity: 0.25; } }

/* ---- the two figures and the meter ---- */

.figures {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.5rem, 4vw, 4rem);
  margin-bottom: clamp(1.25rem, 2.2vw, 1.9rem);
}

/* A hairline between the two figures rather than a box around each: the
   division is the only thing a border needs to say here. */
.figures .figure + .figure {
  border-left: 1px solid var(--border);
  padding-left: clamp(1.5rem, 4vw, 4rem);
}

.fig {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0.7rem 0 0.5rem;
  font-size: clamp(2.6rem, 6.4vw, 6.5rem);
  font-weight: 620;
  line-height: 0.95;
  letter-spacing: -0.035em;
  /* Lining tabular figures in the UI face, not a monospace: the digits stay
     on a fixed pitch so nothing jitters as the meter runs, but the numerals
     keep the proportions of a display type rather than a terminal's. */
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}
.fig.owed { color: var(--owed-text); }
.fig.paid { color: var(--paid-text); }

.unit {
  font: 500 0.85rem/1 var(--sans);
  letter-spacing: 0;
  color: var(--faint);
}

.sub { margin: 0; font-size: 0.82rem; color: var(--muted); }

/* The track is what is owed and the fill is what has settled against it, so
   the bar drains as the meter runs and springs back each time a payment
   lands. That pulse is the discreteness, shown rather than smoothed away.

   The hairlines every tenth are a scale, not decoration: they let the eye
   read roughly how far behind settlement is without reading the number. */
.meter {
  position: relative;
  height: clamp(1.9rem, 3vw, 2.75rem);
  border: 1px solid var(--border-strong);
  background: var(--track);
  overflow: hidden;
}

/* The scale sits above the fill, not under it, so the graduations stay
   readable whatever the bar is doing. Under the fill they would vanish at
   exactly the moment the bar is most worth reading.

   They are ticks off the top and bottom edges rather than rules through the
   whole bar: a full-height line reads as segmentation, as though the bar were
   built out of blocks, and it is not. */
.meter::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(
      90deg, var(--rule-over) 0 1px, transparent 1px 10%
    ),
    repeating-linear-gradient(
      90deg, var(--rule-over) 0 1px, transparent 1px 10%
    );
  background-size: 100% 0.4rem, 100% 0.4rem;
  background-position: left top, left bottom;
  background-repeat: no-repeat;
}

/* The fill is settled-against-accrued, so it lags the meter and springs
   forward when a payment lands. The transition is short enough to read as a
   jump rather than an animation pretending to be a trickle. The bright right
   edge is the settlement front, the one place on the bar the eye should go. */
.meter-fill {
  height: 100%;
  width: 0;
  background: var(--paid-fill);
  box-shadow: inset -3px 0 0 var(--paid-edge);
  transition: width 220ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.meter-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 1.4rem;
  margin-top: 0.7rem;
  font: 0.78rem/1.4 var(--sans);
  color: var(--muted);
}

.legend { display: inline-flex; align-items: center; }

.foot-note { margin-left: auto; color: var(--faint); }

/* A payer that stops reporting freezes the accrued figure, and a frozen
   figure that looks exactly like a running one is the most misleading thing
   this page could show. So the state is named on the label of the number it
   applies to, not only in the counter strip, and the bar recedes. */
:root[data-meter="idle"] .meter,
:root[data-meter="stalled"] .meter,
:root[data-meter="stopped"] .meter { opacity: 0.5; }

.fig-owed .label::after {
  margin-left: 0.55rem;
  padding-left: 0.55rem;
  border-left: 1px solid currentColor;
}
:root[data-meter="stalled"] .fig-owed .label { color: var(--owed-text); }
:root[data-meter="stalled"] .fig-owed .label::after { content: "frozen"; }
:root[data-meter="stopped"] .fig-owed .label::after { content: "stream ended"; }
:root[data-meter="waiting"] .fig-owed .label::after { content: "no report yet"; }
:root[data-meter="idle"] .fig-owed .label::after { content: "no stream yet"; }

:root[data-meter="stalled"] #rate { color: var(--owed-text); }
:root[data-meter="idle"] #rate,
:root[data-meter="stopped"] #rate,
:root[data-meter="waiting"] #rate { color: var(--faint); }

/* ---- secondary counters ---- */

.strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
}

.cell + .cell {
  border-left: 1px solid var(--border);
  padding-left: clamp(1rem, 2.5vw, 2rem);
}

.val {
  margin: 0.5rem 0 0;
  font: 500 clamp(1rem, 1.5vw, 1.35rem)/1.2 var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  color: var(--text);
}

/* ---- the feed ---- */

.feed-title {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem 1.2rem;
  margin-bottom: 0.9rem;
}

.feed-note { margin: 0; font-size: 0.82rem; color: var(--faint); }

.table { border-top: 1px solid var(--border-strong); }

.feed-head,
.row {
  display: grid;
  grid-template-columns: 3.5rem 7rem 1fr 10rem;
  gap: 1.2rem;
  align-items: center;
  padding: 0.62rem 0.75rem;
  border-left: 3px solid transparent;
  /* The feed grows with the viewport so a wall projection stays readable from
     the back of a room without the phone layout turning cavernous. */
  font: clamp(0.85rem, 0.9vw, 1.05rem)/1.4 var(--mono);
  font-variant-numeric: tabular-nums;
}

.feed-head {
  padding-block: 0.55rem;
  font: 600 0.68rem/1 var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--faint);
  border-bottom: 1px solid var(--border);
}

/* The header's check column is a plain caption, not a mark; it only inherits
   the mark's left inset so the two line up in the column. */
.feed-head .col-check {
  display: block;
  padding: 0 0 0 0.5rem;
  border: 0;
  font: inherit;
  letter-spacing: inherit;
}

.row { border-bottom: 1px solid var(--border); }

.col-seq { color: var(--faint); }
.col-amt { color: var(--text); font-weight: 600; }
.col-hash { color: var(--muted); overflow: hidden; text-overflow: ellipsis; }
.col-check { justify-self: start; }

/* A landing payment gets one quiet beat of teal and a leading edge, then goes
   back to being a row. Nothing pulses forever. */
.row.fresh { animation: land 1200ms ease-out; }

@keyframes land {
  0% {
    background: var(--paid-wash);
    border-left-color: var(--paid-fill);
    transform: translateY(-3px);
    opacity: 0;
  }
  12% { transform: translateY(0); opacity: 1; }
  70% {
    background: var(--paid-wash);
    border-left-color: var(--paid-fill);
  }
  100% { background: transparent; border-left-color: transparent; }
}

/* ---- the verification mark ---- */

/* The tick is the most rigorous claim on the page, so it is set as a mark
   rather than a word: boxed, tracked, in the settled accent. */
.col-check {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.5rem;
  border: 1px solid transparent;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  white-space: nowrap;
}
.col-check.checking { color: var(--faint); border-color: var(--border); }
.col-check.ok { color: var(--paid-text); border-color: var(--paid-text); }
.col-check.unknown { color: var(--owed-text); border-color: var(--owed-text); }

/* A failure has to be louder than a pass, so it is the one filled, inverted
   mark on the page, and it takes the whole row with it. */
.col-check.bad {
  color: #fff;
  background: var(--bad-fill);
  border-color: var(--bad-fill);
  font-weight: 700;
}
.row.bad-row {
  background: var(--bad-wash);
  border-left-color: var(--bad);
  animation: none;
}
.row.bad-row .col-amt,
.row.bad-row .col-hash,
.row.bad-row .col-seq { color: var(--bad); }

.empty {
  padding: 2.6rem 0.75rem;
  color: var(--faint);
  font: 0.85rem/1 var(--mono);
  letter-spacing: 0.06em;
  text-align: center;
  border-bottom: 1px solid var(--border);
}

.note { margin: 0; font-size: 0.82rem; color: var(--faint); }
.note.warn { color: var(--owed-text); }

/* board.js leaves the note empty unless it has something to say, and an empty
   band is still a rule and a gutter of space. Fold the whole band away. */
.band:has(.note:empty) { display: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before { animation: none !important; transition: none !important; }
}

/* ---- phone ---- */

@media (max-width: 720px) {
  /* The badge parks in the corner beside the eyebrow rather than taking a
     line of its own: on a phone the header is already the whole first screen
     and the figures should not be pushed any further down. */
  .conn {
    position: absolute;
    top: -0.35rem;
    right: clamp(1rem, 3vw, 2.5rem);
  }
  .lede { font-size: 0.85rem; }

  .figures { grid-template-columns: 1fr; gap: 1.1rem; }
  .figures .figure + .figure {
    border-left: 0;
    border-top: 1px solid var(--border);
    padding-left: 0;
    padding-top: 1.1rem;
  }
  .fig { font-size: clamp(2.6rem, 15vw, 3.6rem); }

  .meter-foot { gap: 0.5rem 1.2rem; }
  .foot-note { margin-left: 0; width: 100%; }

  .strip { grid-template-columns: repeat(2, 1fr); gap: 1rem 0.9rem; }
  .cell:nth-child(odd) { border-left: 0; padding-left: 0; }
  .cell:nth-child(3), .cell:nth-child(4) {
    border-top: 1px solid var(--border);
    padding-top: 0.9rem;
  }

  .feed-head, .row { grid-template-columns: 2.2rem 4.5rem 1fr; gap: 0.7rem; }
  .col-hash { display: none; }
}
