/* ═══════════════════════════════════════════════════════════════════════════
   THE EVENT-LEDGER ROW  (.el-*)
   ───────────────────────────────────────────────────────────────────────────
   Promoted out of public/events.html, which asked for exactly this: "Promote
   it there … the moment a second surface needs it." /tours is that second
   surface — its tour rows and its stop rows are the same ledger row, because a
   tour stop IS an event and reading one should not mean learning a second
   visual language.

   Shared on purpose. Two copies of this file would drift within a release, and
   the drift would land on the severity ramp — the one place a difference is a
   safety bug rather than a cosmetic one.

   INVARIANTS, all of them load-bearing:
     * The ramp DESCENDS in greyscale (low → critical). Critical is the alarm
       AND the darkest rung; saturated red contributes only 0.2126 of luminance
       so it cannot also be the brightest.
     * Severity carries SHAPE as well as hue. Hue alone fails a deuteranope.
     * Calm quiets by WEIGHT, never opacity — opacity is invisible to
       scripts/test-contrast-tokens.js, which is how a 3.47:1 tier word once
       measured as 7.95:1.
     * A null score is an em-dash in a hollow ring. Never 0, never green.
   ═══════════════════════════════════════════════════════════════════════════ */

/* A page-local non-threat ink. No such token exists in the contract
   today, which is exactly why lifecycle reached for --sg-sev-low and
   green ended up meaning two unrelated things in one row.
   Deliberately NOT named --sg-*: that prefix IS the shared contract,
   and scripts/test-contrast-tokens.js rightly rejects an --sg- name
   styles.css never defines. Promote it there, under an --sg- name, the
   moment a second surface needs it. It introduces no new hue — it is
   text-secondary nudged a little toward the border. */
.el-list, .el-row { --el-state-ink: color-mix(in srgb, var(--sg-text-secondary) 88%, var(--sg-border-strong) 12%); }

/* Column legend — teaches the pillar order ONCE for the whole page
   instead of 25 times down it, and costs no row height. */
.el-head        { display: grid; grid-template-columns: 58px minmax(0,1fr) auto; margin: 0 1px 6px; padding-bottom: 2px; font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 10px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; color: var(--sg-text-muted); }
/* Tailwind's `.hidden` is a single class and ships EARLIER in the
   cascade than this block, so any `display` set here quietly beats it.
   Same specificity means source order decides, so every element that
   sets its own display AND gets toggled with `.hidden` must restate the
   hidden state at winning specificity. `.el-list` was added later and
   reintroduced the exact bug — the list stayed visible in timeline view.
   Anything new in this block that sets `display` belongs on this line. */
.el-head.hidden, .el-list.hidden { display: none; }
.el-head__score { padding: 7px 9px 7px 6px; text-align: right; }
.el-head__main  { padding: 7px 12px; }
.el-head__main span { color: var(--sg-text-secondary); }
.el-head__acts  { padding: 7px 10px 7px 6px; }

/* THE STATE GRAMMAR. All three resolve BELOW the row plane, which is the
   half of the luminance axis severity can never enter — severity only
   ascends from it. A state may be lit by a 1px rim and a glyph; it may
   never own bright AREA, because that is how critical earns its
   dominance now that the alarm red cannot be the brightest ink on a
   dark ground (red carries 21% of perceived luminance, so a red bright
   enough to top the ramp stops reading as red — and red IS the
   convention for critical in a command centre). */
.el-list, .el-row {
  --el-chip-recess: var(--sg-base);            /* under every row  */
  --el-chip-rim:    var(--sg-text-secondary);  /* 1px only         */
  --el-chip-ink:    var(--sg-text-primary);    /* glyph only       */
}

/* SEPARATION — cards on a base-coloured gutter. The measured finding is
   that a plane change CANNOT carry a state on dark: base, elevated,
   surface and border sit within ~1.06:1 of each other, about 35 grey
   levels end to end. So the 1px border does the separating and the fill
   lift on a live row is a supporting whisper, not the signal. 4px, not
   8px — enough to read as discrete without undoing the 110px → 54px
   cut or dissolving the score column. */
.el-row         { position: relative; display: grid; grid-template-columns: 58px minmax(0,1fr) auto; align-items: stretch; width: 100%; text-align: left; background: var(--sg-elevated); border: 1px solid var(--sg-border); border-radius: 8px; cursor: pointer; transition: background .15s, border-color .15s; }
.el-list        { display: flex; flex-direction: column; gap: 4px; }
.el-row:hover   { background: color-mix(in srgb, var(--sg-text-primary) 3%, var(--sg-elevated)); border-color: var(--sg-border-strong); }
/* The live state's fixed-x column: ~88px² of bright pixels, an eighth of
   what a filled near-white chip spent. Consecutive live events fuse into
   one edge down the list. Achromatic, so severity cannot claim it. */
/* Drawn as an inset shadow, not a 2px border: a thicker border makes the
   live row 1px narrower than every other one, which walks the whole of
   line 2 sideways and breaks the venue column this design just built. */
.el-row--live   { background: var(--sg-surface); border-color: var(--sg-border-strong); box-shadow: inset 2px 0 0 var(--el-chip-ink); }
.el-row:focus-visible { outline: 2px solid var(--sg-accent); outline-offset: -2px; }

/* Posture cell — lifted from the console's .lg-score. */
.el-score       { min-width: 0; border-right: 1px solid var(--sg-border); padding: 9px 9px 8px 6px; display: flex; flex-direction: column; align-items: flex-end; justify-content: center; }
.el-num         { font-family: var(--sg-font-mono), ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 21px; font-weight: 700; line-height: 1.05; letter-spacing: -.02em; color: var(--sg-text-primary); }
.el-tier        { font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 12px; font-weight: 600; letter-spacing: .10em; margin-top: 2px; line-height: 1; color: var(--sg-text-muted); }
/* Tier ink on the NUMBER and the WORD only — the rest of the row stays
   flat, so the ladder reads at a glance without tinting everything. */
.el-row--low      .el-num, .el-row--low      .el-tier { color: var(--sg-sev-low); }
.el-row--medium   .el-num, .el-row--medium   .el-tier { color: var(--sg-sev-medium); }
.el-row--high     .el-num, .el-row--high     .el-tier { color: var(--sg-sev-high); }
.el-row--critical .el-num, .el-row--critical .el-tier { color: var(--sg-sev-critical); }
/* Calm quiets by WEIGHT, never opacity. Opacity is invisible to
   scripts/test-contrast-tokens.js — it reported 7.95:1 while the
   shipped pixel measured 3.47:1 on the tier word. */
.el-row--low .el-num  { font-weight: 600; }
.el-row--low .el-tier { font-weight: 500; }
/* SEVERE is the only row allowed background ink. */
.el-row--critical .el-score { background: linear-gradient(90deg, color-mix(in srgb, var(--sg-sev-critical) 9%, transparent), transparent); }
.el-row--critical::after { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: var(--sg-sev-critical); }
/* Clip the rail and the score-cell wash to the card radius. Safe: the
   overflow menu is portalled to <body>, so nothing needs to escape. */
.el-row         { overflow: hidden; }
/* CRITICAL earns its dominance from AREA — this wash plus the rail —
   because on a dark ground the alarm red cannot also be the brightest
   ink. Do not trade either away for another state. */
/* No scan: hollow hairline ring + em-dash, no tier word. ~50 of 186. */
/* Never scanned is a STATE, not a threat, so it takes the state grammar —
   achromatic, recessed, dashed — and never a severity ink. The dashed
   rim says "missing", which is the one thing calm never says. */
.el-score:has(.el-ring) { justify-content: center; align-items: center; }
.el-ring        { display: inline-flex; align-items: center; justify-content: center; width: 25px; height: 25px; border-radius: 50%; border: 1px dashed var(--sg-border-strong); background: var(--el-chip-recess); color: var(--sg-text-muted); font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 12px; font-weight: 500; line-height: 1; }

.el-main        { min-width: 0; padding: 8px 12px; display: flex; flex-direction: column; justify-content: center; gap: 4px; }
.el-l1          { display: flex; align-items: center; gap: 8px; min-width: 0; }
.el-name        { font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--sg-text-primary); min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.el-row--done .el-name { font-weight: 500; color: var(--sg-text-secondary); }
/* LIVE is a lifecycle fact, so it takes neutral ink + form (a dot and a
   hairline). Severity keeps the colour; periwinkle keeps the actions. */
/* LIVE is a HOLE, not a highlight — punched below the row plane and lit
   by a 1px rim. It is the only filled state object in the list, which is
   what makes it findable at a glance without spending the bright area
   that belongs to severity. */
.el-live        { order: 1; flex: none; display: inline-flex; align-items: center; gap: 5px; font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 10px; font-weight: 700; letter-spacing: .12em; line-height: 1; padding: 3px 7px 3px 6px; border-radius: 3px; background: var(--el-chip-recess); border: 1px solid var(--el-chip-rim); color: var(--el-chip-ink); cursor: default; user-select: none; }
.el-live i      { width: 4px; height: 4px; border-radius: 50%; background: currentColor; flex: none; }
/* CLASSIFICATION is found by COLUMN, not by contrast. Un-boxed on
   purpose: a border is state grammar now, and 12 of 14 rows carry a type
   — a boxed chip on nearly every row is noise. The 1px tick gives it a
   fixed left edge to sit against. */
.el-type        { order: 2; flex: none; display: inline-flex; align-items: center; gap: 7px; font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 10px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; line-height: 1; padding: 0; border: 0; border-radius: 0; background: transparent; color: var(--sg-text-secondary); cursor: pointer; }
.el-type::before{ content: ""; width: 1px; height: 10px; flex: none; background: var(--sg-border-strong); }
.el-type:hover  { color: var(--sg-text-primary); }

.el-l2          { display: flex; align-items: center; gap: 7px; min-width: 0; font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 12px; letter-spacing: .03em; font-variant-numeric: tabular-nums; color: var(--sg-text-secondary); }
.el-sep         { display: none; }   /* alignment groups line 2 now, not punctuation */
/* …except inside the tail, where the countdown and the scan age are two
   different facts sitting in one right-flushed group. Without a mark
   between them they read as one string: "T-1d 23:59 scan never". */
.el-tail .el-sep{ display: inline; color: var(--sg-border-strong); font-style: normal; }
.el-venue       { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.el-tail        { margin-left: auto; flex: none; padding-left: 10px; white-space: nowrap; display: flex; align-items: center; gap: 7px; color: var(--sg-text-muted); }
.el-row--done .el-l2, .el-row--done .el-tail { color: var(--sg-text-muted); }

/* GRAFTED FIX — lifecycle carries zero severity hue. Its state is a
   SHAPE plus the word, in a non-threat steel ink. "Active" used to be
   painted --sg-sev-low, the same green that means a calm THREAT
   reading, so one colour answered two unrelated questions. */
/* Lifecycle is pulled left against the fixed-width pillar glyph and given
   a min-width, so its mark AND the venue after it start at the same x on
   every row. That column is what makes it findable — it needs no fill
   and no hue. The SILHOUETTE carries the value: filled square = active,
   hollow diamond = planning, struck bar = completed. */
.el-life        { order: 1; flex: none; min-width: 104px; display: inline-flex; align-items: center; gap: 6px; letter-spacing: .10em; text-transform: uppercase; color: var(--el-state-ink); }
.el-mark        { width: 11px; height: 11px; flex: none; display: grid; place-items: center; border: 0; background: none; }
.el-mark::before{ content: ""; display: block; width: 8px; height: 8px; box-sizing: border-box; border: 1.5px solid var(--el-state-ink); border-radius: 1px; }
.el-row[data-life="active"] .el-mark::before    { background: var(--el-state-ink); }
.el-row[data-life="planning"] .el-mark::before  { width: 7px; height: 7px; transform: rotate(45deg); }
.el-row[data-life="paused"] .el-mark::before    { border-width: 0 1.5px; border-radius: 0; width: 7px; }
.el-row[data-life="completed"] .el-mark::before { width: 9px; height: 0; border: 0; border-radius: 0; border-top: 2px solid var(--el-state-ink); }
.el-row[data-life="completed"] .el-life{ color: var(--sg-text-muted); }
.el-pil         { order: 0; }
.el-venue       { order: 2; }
.el-tail        { order: 3; }

/* Four pillars — fixed CHT·ENV·MOV·CTX, so POSITION names the pillar
   and the row never has to. Fill height + ink give the band. A null
   pillar collected NOTHING, which is ops hygiene rather than a calm
   posture reading, so it is an empty dashed track — the same "hollow
   means no data" grammar as the never-scanned ring. */
.el-pil         { display: inline-flex; align-items: flex-end; gap: 2px; flex: none; height: 13px; }
.el-pil b       { display: block; width: 5px; height: 13px; border-radius: 1px; position: relative; overflow: hidden; background: color-mix(in srgb, var(--sg-border-strong) 55%, transparent); }
.el-pil b::after{ content: ""; position: absolute; left: 0; right: 0; bottom: 0; }
.el-pil b[data-p="low"]::after      { height: 25%;  background: var(--sg-sev-low); }
.el-pil b[data-p="medium"]::after   { height: 50%;  background: var(--sg-sev-medium); }
.el-pil b[data-p="high"]::after     { height: 75%;  background: var(--sg-sev-high); }
.el-pil b[data-p="critical"]::after { height: 100%; background: var(--sg-sev-critical); }
.el-pil b[data-p="null"]            { background: transparent; border: 1px dashed var(--sg-border-strong); }

/* Action gutter — a real third track, never controls floating over the
   name. Periwinkle is ACTION ink and appears nowhere else in the row. */
.el-acts        { display: flex; align-items: center; gap: 5px; padding: 0 10px 0 6px; border-left: 1px solid var(--sg-border); }
.el-act         { font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 11px; font-weight: 600; letter-spacing: .06em; line-height: 1; padding: 6px 9px; border-radius: 4px; cursor: pointer; text-decoration: none; color: var(--sg-accent); background: transparent; border: 1px solid color-mix(in srgb, var(--sg-accent) 32%, transparent); white-space: nowrap; }
.el-act:hover   { background: color-mix(in srgb, var(--sg-accent) 10%, transparent); border-color: color-mix(in srgb, var(--sg-accent) 55%, transparent); }
.el-act:focus-visible, .el-more:focus-visible { outline: 2px solid var(--sg-accent); outline-offset: 1px; }
.el-row--done .el-act { color: var(--sg-text-secondary); border-color: var(--sg-border-strong); }
.el-more        { font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 13px; font-weight: 700; line-height: 1; padding: 6px 8px; border-radius: 4px; cursor: pointer; color: var(--sg-text-secondary); background: transparent; border: 1px solid var(--sg-border-strong); }
.el-more:hover, .el-more[aria-expanded="true"] { color: var(--sg-text-primary); border-color: var(--sg-text-muted); }

/* Overflow menu — every operation the gutter demoted. Destructive sits
   below a rule so it can't be hit on the way past. */
.el-menu        { position: fixed; z-index: 60; min-width: 208px; background: var(--sg-elevated); border: 1px solid var(--sg-border-strong); border-radius: 10px; padding: 5px; box-shadow: 0 18px 40px -16px rgba(0,0,0,.55); }
.el-menu__grp   { font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 9px; font-weight: 600; letter-spacing: .16em; text-transform: uppercase; color: var(--sg-text-muted); padding: 8px 9px 4px; }
.el-menu__item  { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; background: transparent; border: 0; border-radius: 6px; padding: 7px 9px; font-size: 13px; color: var(--sg-text-primary); cursor: pointer; font-family: inherit; }
.el-menu__item:hover:not([disabled]) { background: color-mix(in srgb, var(--sg-accent) 12%, transparent); }
.el-menu__item:focus-visible { outline: 2px solid var(--sg-accent); outline-offset: -2px; }
.el-menu__item[disabled] { color: var(--sg-text-muted); cursor: default; }
.el-menu__item[disabled] .el-menu__why { margin-left: auto; font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 9.5px; letter-spacing: .06em; }
.el-menu__tick  { width: 13px; flex: none; color: var(--sg-accent); font-size: 11px; }
.el-menu__rule  { height: 1px; background: var(--sg-border); margin: 5px 4px; }
.el-menu__item--danger { color: var(--sg-sev-critical); }
.el-menu__item--danger:hover:not([disabled]) { background: color-mix(in srgb, var(--sg-sev-critical) 12%, transparent); }

@media (max-width: 860px) {
  .el-head__acts, .el-head__score { font-size: 9px; }
  .el-tail { width: 100%; margin-left: 0; padding-left: 0; }
  .el-l2 { flex-wrap: wrap; }
}

/* THE SAME REFLOW, KEYED TO THE LIST'S OWN WIDTH.
   The media query above asks the VIEWPORT, which is the wrong question the
   moment a row is narrow for any reason other than a small screen. /tour can
   dock a map pane over ~half the desk: the content column drops well under
   860px while the viewport stays at 1600, the query never fires, .el-l2 never
   wraps, and the date runs straight through the scan stamp. Visible in a
   screenshot at the DEFAULT dock width — this predates the resize handle,
   which only made it easy to reach.

   Container query, so the row reflows whenever ITS OWN box is narrow, whatever
   made it narrow. The media block stays: it still owns .el-head, which is a
   sibling of .el-list and therefore outside this container, and it costs
   nothing where both apply — the declarations are identical. */
.el-list { container-type: inline-size; }
@container (max-width: 860px) {
  .el-tail { width: 100%; margin-left: 0; padding-left: 0; }
  .el-l2 { flex-wrap: wrap; }
}

/* ── Arrived with the second surface (/tours) ──────────────────────────────
   Kept here rather than page-local because a tour row and an event row are
   the same row; a rule that lives on only one of them is the drift this file
   exists to prevent. */

/* A finished stop is ACHROMATIC, not low-severity. Its live severity stopped
   being a claim about the world when the show ended, so it leaves the ramp
   entirely — grey at full height says "this happened" without asserting how
   bad it was. Reusing sev-low here would have read as "it went fine", which
   is a different and unearned statement. */
.el-pil b[data-p="done"]::after { height: 100%; background: color-mix(in srgb, var(--el-state-ink) 38%, transparent); }

/* Strip overflow. The strip caps at 24 segments; past that it is texture, not
   information. The count is stated rather than silently truncated — a strip
   that just stops implies the tour stops too. */
.el-pil-more    { font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 9px; line-height: 13px; color: var(--sg-text-muted); margin-left: 3px; letter-spacing: .04em; }

/* Coverage gap — "3/5 scanned". Only rendered when scanned < total, because
   full coverage is the expected state and announcing it is noise. Muted ink:
   a gap is an absence of reading, not a threat, so it takes the state grammar
   and never the severity ramp. */
.el-gap         { font-family: var(--sg-font-mono), ui-monospace, monospace; font-variant-numeric: tabular-nums; color: var(--sg-text-muted); }

/* ── Arrived with the third surface (/venues) ──────────────────────────────
   A venue's subject is whether it can originate a scan at all, so the row
   needs a vocabulary for "no origin" that the event and tour rows never
   needed. Kept here for the same reason as everything above: three copies
   of a severity-adjacent rule is three chances to drift. */

/* The coordinates ARE the scan origin, so they are stated, not summarised. */
.vn-origin      { font-variant-numeric: tabular-nums; color: var(--sg-text-secondary); }

/* NO ORIGIN is an absence, not a threat: it takes the state grammar — dashed,
   achromatic, recessed — the same grammar the never-scanned ring speaks. It is
   NOT sev-critical, because a venue nobody has geocoded is an admin gap, and
   painting it like an incident spends the alarm on paperwork. */
.vn-noorigin    { font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 10px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--sg-text-muted); border: 1px dashed var(--sg-border-strong); border-radius: 3px; padding: 2px 7px; }
.vn-why         { font-family: var(--sg-font-mono), ui-monospace, monospace; color: var(--sg-text-muted); margin-left: 8px; }

/* The banner names the venues rather than counting them. Amber, not red: the
   events have not failed yet — they will fail, and there is still time. */
.vn-alert       { display: flex; align-items: center; gap: 12px; padding: 11px 14px; border: 1px solid var(--sg-sev-high); border-left-width: 3px; border-radius: 8px; background: color-mix(in srgb, var(--sg-sev-high) 6%, transparent); font-size: 13.5px; color: var(--sg-text-secondary); }
.vn-alert b     { color: var(--sg-text-primary); font-weight: 600; }
.vn-alert__k    { flex: none; font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--sg-sev-high); }
.vn-alert__names{ display: block; font-family: var(--sg-font-mono), ui-monospace, monospace; font-size: 11px; color: var(--sg-text-muted); margin-top: 3px; }
.vn-alert .el-act { margin-left: auto; flex: none; }

/* An action that fixes a gap rather than opening a view. Amber so it reads as
   "this one needs you", without claiming the row is a threat. */
.el-act--warn   { color: var(--sg-sev-high); border-color: color-mix(in srgb, var(--sg-sev-high) 38%, transparent); }
.el-act--warn:hover { background: color-mix(in srgb, var(--sg-sev-high) 10%, transparent); border-color: color-mix(in srgb, var(--sg-sev-high) 60%, transparent); }

/* The .vn-chip filter chip is GONE with its only caller. /venues' scan-origin
   filter was a three-button segmented control; it is a <select> now, because
   the filter bar overflowed at desk width and what wrapped to line two was the
   view toggle. Deleted rather than left behind — an unused rule reads as a
   style waiting for a caller, and the next person adds one. */
