/* Portfolio Competition module styles (Epic AM: AM3/AM4). Layout only —
   colour, spacing and card/badge/subnav/field/btn primitives all come from
   the already-linked terminal.css / design-system.css / utilities.css; this
   file exists for the handful of competition-specific layout rules those
   sheets have no equivalent for. Breakpoints are the declared scale ONLY
   (scripts/check-breakpoints.js): max-width 480/640/820/1024,
   min-width 641/1440.

   THE RULE THIS SHEET FOLLOWS, after the pass that rebuilt these screens to
   match the rest of the app: every panel on a competition screen is the
   app's own `.table-wrap` + `.table-header` + `h3` (the primitive every
   other table and panel in this app already uses), every stat band is the
   app's own `.cards` + `.card-label`/`.card-value`/`.card-sub`, and every
   table is a plain terminal.css table with no competition-specific override
   of its type scale or its alignment. What was here before re-declared
   those — a bare `.card` with no border for a panel, `font-size:12px` and a
   blanket `td:last-child{text-align:right}` for tables — which is precisely
   how these screens came to look like a different product. Numeric columns
   now carry terminal.css's own `.num` class instead.

   The table-in-a-wrap part is not only cosmetic: terminal.css floors every
   table at `min-width:480px` and only `.table-wrap` scrolls, so a table
   outside one silently loses its right-hand columns on a phone with nothing
   to scroll them into view. The leaderboard's Value column did exactly that
   at 390px. */

/* ── Shared scaffolding ─────────────────────────────────────────────────── */

#competition-root .subnav,
#competition-admin-host .subnav { margin-bottom: 12px; }

/* Vertical rhythm between stacked panels, matching .cards' own 18px. */
.comp-block { margin-bottom: 14px; }
/* `.cards` paints its grid lines by letting its own `--line` background show
   through a 1px gap. That is right when the tiles fill the grid and wrong
   when they do not: five tiles in the two-column phone layout leave one cell
   painted line-grey, which reads as a sixth, broken tile. Same lines, drawn
   per tile instead, so an empty cell is simply page background. */
.comp-kpis { margin-bottom: 8px; background: transparent; border: 0; }
.comp-kpis .card { box-shadow: 0 0 0 1px var(--line); }
/* The band's own provenance line — what the figures above are and are not.
   It reads under the band rather than inside a tile so no single `.card-sub`
   has to carry a sentence and wrap the whole row taller. */
.comp-kpis-note { font-size: 10px; color: var(--dim); line-height: 1.6; margin: 0 0 14px; max-width: 86ch; }

/* The right-hand caption slot in a `.table-header` — a count, an "as of"
   stamp, a scope note. Prose, so it is not upper-cased like the `h3`. */
.comp-head-note { font-size: 10px; color: var(--dim); letter-spacing: .3px; text-align: right; }

/* A titled divider above a group of tiles (the picker, the admin list), in
   the shape of terminal.css's own `.section-title` plus a caption slot. */
.comp-sec-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding-bottom: 4px; margin-bottom: 12px; border-bottom: 1px solid var(--line); }
.comp-sec-title { font-size: 12px; font-weight: 700; color: var(--fg); letter-spacing: .4px; margin: 0; }

/* Loading placeholder for a whole panel. The app's own `.skel` shimmer —
   these screens used to print the word "Loading…" inside `.empty`, i.e. the
   primitive that means "there is nothing here", which says something else. */
.comp-skel { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--line); background: var(--bg); }
.comp-skel .skel { width: 100%; }

/* Symbol cells: the data font, the way every other ticker in the app reads. */
.comp-sym { font-family: var(--font-num); font-weight: 700; letter-spacing: .4px; }

/* A colspan "nothing here yet" row inside a real table. */
.comp-td-empty { color: var(--dim); text-align: center; padding: 18px 10px; }

/* A closing note under a panel's content (footnote, rule reminder). */
.comp-panel-foot { padding: 8px 12px; border-top: 1px solid var(--line); font-size: 10px; color: var(--dim); line-height: 1.5; }

/* Inline form-level error/status text. Was bare `u-fs-11px` with no colour,
   so a refusal reason read like a caption. */
.comp-form-err { font-size: 11px; color: var(--neg); line-height: 1.5; }
.comp-form-err:empty { display: none; }

/* ── Demonstration-data marker ──────────────────────────────────────────── */
/* Drawn from `is_sample` (migration 115), never from the name. It sits beside
   the status badge rather than replacing it: a sample competition still has a
   real lifecycle status, and the reader wants both facts. */
.comp-sample-badge { letter-spacing: .8px; }
/* The tile, header and admin card all carry more than one chip now, so the
   chips get their own row box rather than each being positioned by hand. */
.comp-pick-tags { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; flex: none; }

/* ── Dashboard teaser card ──────────────────────────────────────────────── */
/* The competition's one presence on the screen people land on. Built from the
   same `.table-wrap` + `.table-header` panel and the same `.comp-fact-k` /
   `.comp-fact-v` label-value pairing as the competition screens themselves,
   so it reads as a window onto that page rather than a different component
   that happens to mention it. */
.comp-dash-body { display: flex; align-items: center; justify-content: space-between; gap: 18px;
  flex-wrap: wrap; padding: 14px 16px; }
.comp-dash-lead { min-width: 0; }
.comp-dash-title { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.comp-dash-name { font-size: 14px; font-weight: 700; color: var(--fg); }
.comp-dash-phase { display: flex; align-items: center; gap: 7px; font-size: 11px; font-weight: 700; }
.comp-dash-sub { font-weight: 400; color: var(--dim); }
.comp-dash-stats { display: flex; gap: 22px; flex-wrap: wrap; }
.comp-dash-stat { min-width: 0; }
.comp-dash-go { flex: none; }

@media (max-width: 640px) {
  /* Stacked, and the action goes full width — at phone size the row would
     otherwise put a 2-line phase line beside a squeezed stat group. */
  .comp-dash-body { align-items: stretch; gap: 14px; }
  .comp-dash-stats { gap: 16px; }
  .comp-dash-go .btn { width: 100%; justify-content: center; }
}

/* ── Selected-competition header band ───────────────────────────────────── */
/* Replaces `<strong>Name</strong>` beside two raw ET calendar strings. Facts
   use the same label/value pairing as terminal.css's `.card-label` /
   `.card-value`, one size down, so the band reads as part of the same
   system as the stat cards below it. */
.comp-head-back { margin-bottom: 10px; }
.comp-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px;
  flex-wrap: wrap; padding-bottom: 12px; margin-bottom: 14px; border-bottom: 1px solid var(--line); }
.comp-head-id { min-width: 0; }
.comp-head-line { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.comp-head-name { font-size: 16px; font-weight: 700; color: var(--fg); letter-spacing: .2px; margin: 0; }
.comp-head-desc { font-size: 11px; color: var(--dim); line-height: 1.55; margin: 5px 0 0; max-width: 78ch; }
.comp-head-facts { display: flex; gap: 22px; flex-wrap: wrap; }
.comp-fact { min-width: 0; }
.comp-fact-k { font-size: 9px; text-transform: uppercase; letter-spacing: 1.4px; color: var(--dim); margin-bottom: 3px; }
.comp-fact-v { font-size: 12px; font-weight: 700; color: var(--fg); font-family: var(--font-num); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ── Competition picker ─────────────────────────────────────────────────── */
/* One box per competition (was one dense full-width row): a stat grid over a
   bottom action band tinted to the competition's phase. The box itself is a
   real <button data-call="competitionSelect(...)"> so the whole tile stays
   one click target — see js/competition.js's renderCompetitionPicker.
   auto-fill/minmax collapses to one column on phones on its own; no extra
   breakpoint is needed for that. */

.comp-pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; margin-bottom: 18px; }
.comp-pick-card { display: flex; flex-direction: column; text-align: left; background: var(--bg); border: 1px solid var(--line); padding: 0; cursor: pointer; font-family: inherit; transition: border-color .15s var(--ease), transform .15s var(--ease); }
.comp-pick-card:hover { border-color: var(--fg); transform: translateY(-1px); }
/* The body grows to fill the tile, and the DESCRIPTION takes up the slack
   (below), so the stat grid lands at the same height across a grid row
   whatever the title costs — without reserving a blank second title line on
   every card to pay for the occasional one that wraps. */
.comp-pick-body { padding: 14px 14px 12px; display: flex; flex-direction: column; flex: 1; }
/* The chips sit on their own row ABOVE the title rather than beside it. With
   two of them (a status and, on demo rows, EXAMPLE) a shared row stole enough
   width to wrap "Sample · Autumn 2026 Challenge" onto three lines and pushed
   that tile's stat grid out of line with its neighbours'. */
.comp-pick-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
/* Up to two lines; a longer name ellipsises rather than pushing everything
   below it down. The full name is on the header once the competition is
   opened, the same trade the description below makes. */
.comp-pick-name { font-size: 14px; font-weight: 700; color: var(--fg); line-height: 1.3; margin-bottom: 6px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* Clamped to two lines, with the height reserved either way: the tiles sit in
   one grid row, so a one-line description in one card and a three-line one in
   the next used to leave their stat grids and action bands on different
   baselines across the row. The full text is on the header of the
   competition once it is opened, so nothing is lost by clamping here. */
.comp-pick-desc { font-size: 11px; color: var(--dim); line-height: 1.5; margin-bottom: 12px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 33px; flex: 1 1 auto; }
.comp-pick-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px 16px; padding-top: 10px; border-top: 1px solid var(--line); }
.comp-pick-stat-label { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 1.2px; color: var(--dim); margin-bottom: 3px; }
.comp-pick-stat-value { font-size: 12px; font-weight: 700; color: var(--fg); font-variant-numeric: tabular-nums; }
.comp-pick-action { margin-top: auto; min-height: 46px; padding: 10px 14px; display: flex; align-items: center; justify-content: space-between; gap: 10px; border-top: 1px solid var(--line); }
.comp-pick-action-left { display: flex; align-items: center; gap: 9px; min-width: 0; }
.comp-pick-action-icon { width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex: none; }
.comp-pick-action-main { font-size: 11px; font-weight: 700; line-height: 1.3; }
.comp-pick-action-sub { font-size: 10px; color: var(--dim); margin-top: 1px; }
.comp-pick-btn { display: inline-flex; align-items: center; padding: 5px 12px; font-size: 10px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; white-space: nowrap; flex: none; }

/* Per-phase colour, as a CLOSED class family (accent/pos/neutral/neg) —
   never a `style` attribute. server.ts's CSP ships styleSrcAttr 'self' with
   no 'unsafe-inline' (Epic Z / Z5): an inline style setting color/background
   is not a cosmetic risk here, the browser silently drops it. This is the
   same "generated class family" pattern Z5 already used for every other
   runtime-computed style in this app (css/mod-scales.css's heat-ramp
   classes, utilities.css's u-w-<n>pct scale) — js/competition.js's
   renderCompetitionPicker only ever picks a tone name, never a color value.
   --dim2 (band wash + icon fill) is a hairline/border token, not AA-text-safe
   on its own — the neutral text rules below use --dim / --fg instead, same
   contrast reasoning as the accent/pos/neg tones (each already used as text
   elsewhere in this app: .badge.blue, .card.green etc.). */
.comp-pick-action--accent  { background: color-mix(in srgb, var(--accent) 12%, var(--panel)); }
.comp-pick-action--pos     { background: color-mix(in srgb, var(--pos) 12%, var(--panel)); }
.comp-pick-action--neutral { background: color-mix(in srgb, var(--dim2) 12%, var(--panel)); }
.comp-pick-action--neg     { background: color-mix(in srgb, var(--neg) 12%, var(--panel)); }

.comp-pick-action-icon--accent  { background: color-mix(in srgb, var(--accent) 22%, var(--panel)); color: var(--accent); }
.comp-pick-action-icon--pos     { background: color-mix(in srgb, var(--pos) 22%, var(--panel)); color: var(--pos); }
.comp-pick-action-icon--neutral { background: color-mix(in srgb, var(--dim2) 22%, var(--panel)); color: var(--dim); }
.comp-pick-action-icon--neg     { background: color-mix(in srgb, var(--neg) 22%, var(--panel)); color: var(--neg); }

.comp-pick-action-main--accent  { color: var(--accent); }
.comp-pick-action-main--pos     { color: var(--pos); }
.comp-pick-action-main--neutral { color: var(--dim); }
.comp-pick-action-main--neg     { color: var(--neg); }

.comp-pick-btn--accent  { background: var(--accent); color: var(--bg); }
.comp-pick-btn--pos     { background: var(--pos); color: var(--bg); }
.comp-pick-btn--neutral { background: var(--fg); color: var(--bg); }
.comp-pick-btn--neg     { background: var(--neg); color: var(--bg); }

/* ── Enter flow (pre-enrollment) ────────────────────────────────────────── */
/* A reading column, not the full page width: the terms ran edge to edge at
   1280px, which is a ~200-character measure for the one block of text on
   these screens that a reader is actually agreeing to. */
.comp-enter { max-width: 760px; }
.comp-enter-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 12px 18px;
  padding: 14px 16px; border-bottom: 1px solid var(--line); background: var(--panel); }
.comp-terms { padding: 14px 16px 4px; }
.comp-terms-p { font-size: 12px; color: var(--dim); line-height: 1.7; margin: 0 0 12px; }
.comp-enter-form { padding: 4px 16px 16px; }
.comp-enter-form .field { margin-bottom: 12px; }
/* The activation area is the LABEL (clicking the sentence toggles the box),
   so the 24px floor belongs on it — the same reasoning design-system.css
   records for every other wrapping checkbox label in the app. */
.comp-agree { display: flex; align-items: flex-start; gap: 10px; font-size: 11px; color: var(--fg);
  line-height: 1.6; cursor: pointer; padding: 6px 0; min-height: 24px; }
.comp-agree input { margin-top: 2px; flex: none; }
.comp-enter-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 12px; }
.comp-enter-hint { font-size: 10px; color: var(--dim); line-height: 1.5; }

/* ── Portfolio tab ──────────────────────────────────────────────────────── */
.comp-portfolio-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
@media (min-width: 641px) {
  .comp-portfolio-grid { grid-template-columns: minmax(0, 1fr) 264px; align-items: start; }
}
.comp-holdings-card, .comp-allowance-card { min-width: 0; }
.comp-allowance-body { padding: 14px 12px 0; text-align: center; }
.comp-allowance-body svg { display: block; margin: 0 auto; max-width: 100%; }
.comp-allowance-cap { font-size: 11px; font-weight: 700; color: var(--fg); margin: 2px 0 12px;
  font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.comp-allowance-body .comp-panel-foot { margin: 0 -12px; text-align: left; }

/* ── Leaderboard tab ────────────────────────────────────────────────────── */
.comp-lb-rank { width: 62px; font-family: var(--font-num); font-variant-numeric: tabular-nums; }
/* The viewer's own row. Was a bold font-weight plus the word "(you)" glued to
   a display name; it is now a tinted row with the app's own `.badge`, and it
   deliberately out-ranks terminal.css's `tr:hover td` (same specificity,
   later sheet) so the highlight survives the pointer passing over it. */
.comp-lb-you td { background: color-mix(in srgb, var(--accent) 12%, transparent); font-weight: 700; }
.comp-lb-you td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
.comp-lb-pending { color: var(--dim); font-size: 10px; letter-spacing: .4px; }

/* ── Results tab ────────────────────────────────────────────────────────── */
.comp-overlay { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.comp-chart { padding: 12px 14px 14px; }
/* Same stat line js/equity-chart.js puts above the identical chart on the
   Paper Portfolio page, so the two readings of one component match. */
.comp-stats { display: flex; flex-wrap: wrap; gap: 6px 20px; font-size: 11px; color: var(--fg);
  padding-bottom: 10px; margin-bottom: 8px; border-bottom: 1px dashed var(--line); }
.comp-stats:empty { display: none; }
.comp-stat b { font-family: var(--font-num); font-variant-numeric: tabular-nums; }
#comp-results-chart svg { width: 100%; height: auto; display: block; }
/* Legend by LINE STYLE, not by colour: the equity line is drawn green or red
   depending on which way the period went, so a coloured swatch for it would
   be wrong half the time. The other three marks are fixed by the chart. */
.comp-chart-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-top: 12px; font-size: 10px; color: var(--dim); }
.comp-legend { display: inline-flex; align-items: center; gap: 6px; }
.comp-legend::before { content: ''; width: 16px; height: 0; flex: none; border-top: 2px solid var(--line); }
.comp-legend--own::before { border-top: 2px solid var(--fg); }
.comp-legend--bench::before { border-top: 2px dashed var(--accent); }
.comp-legend--peak::before { border-top: 2px dashed var(--dim); }
.comp-legend--dd::before { height: 10px; border-top: 0; background: color-mix(in srgb, var(--neg) 22%, transparent); }

/* ── Admin: lifecycle timeline ──────────────────────────────────────────── */
/* One full-width card per competition with a 4-stage progress bar (Draft /
   Published / Running / Ended, or a Cancelled stop) instead of a single
   status sentence, so an admin can see how far along a competition is at a
   glance. Card/badge language matches the member-side picker above
   (js/competition.js's renderAdminCompetitionCard) for visual consistency
   between the two surfaces, per the same competition. */
.comp-admin-list { display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px; }
.comp-admin-card { background: var(--bg); border: 1px solid var(--line); padding: 14px 16px; }
.comp-admin-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; margin-bottom: 3px; }
.comp-admin-name { font-size: 14px; font-weight: 700; color: var(--fg); }
.comp-admin-desc { font-size: 11px; color: var(--dim); margin-bottom: 16px; }
.comp-admin-badge { display: inline-flex; align-items: center; padding: 1px 7px; font-size: 9px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; border: 1px solid; white-space: nowrap; flex: none; }

/* Capped: with `flex:1` segments and no cap, a four-stage progress bar on a
   1600px page is four dots separated by 300px of hairline, which reads as
   four unrelated markers rather than one track. */
.comp-admin-tl { display: flex; align-items: center; margin: 0 4px 14px; max-width: 760px; }
/* 74px wide with wrapping labels put "Created Aug 19" and "Enrolls Aug 29"
   on two ragged lines under every dot. The node is wider and both lines are
   nowrap, so a stage label reads as one label. */
.comp-admin-tl-node { display: flex; flex-direction: column; align-items: center; flex: none; width: 100px; }
.comp-admin-tl-dot { width: 11px; height: 11px; border-radius: 50%; border: 2px solid var(--dim2); background: var(--bg); }
.comp-admin-tl-dot.is-now { width: 15px; height: 15px; }
.comp-admin-tl-label { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: var(--dim); margin-top: 6px; text-align: center; white-space: nowrap; }
.comp-admin-tl-label.is-on { color: var(--fg); }
.comp-admin-tl-label.is-on.comp-admin-tl-label--stop { color: var(--neg); }
/* --dim, not --dim2: this is 9px TEXT, and --dim2 is the hairline/border
   token this repo's own contrast notes keep off text (it measures ~4.1:1). */
.comp-admin-tl-date { font-size: 9px; color: var(--dim); margin-top: 2px; font-family: var(--font-num); font-variant-numeric: tabular-nums; text-align: center; white-space: nowrap; }
.comp-admin-tl-seg { flex: 1; height: 2px; background: var(--dim2); margin: 0 -2px; position: relative; top: -13px; }
.comp-admin-tl-seg.is-stop { background: var(--neg); }

.comp-admin-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; padding-top: 12px; border-top: 1px solid var(--line); }
.comp-admin-stats-inline { display: flex; gap: 18px; flex-wrap: wrap; font-size: 10px; color: var(--dim); }
.comp-admin-stats-inline b { color: var(--fg); font-family: var(--font-num); font-variant-numeric: tabular-nums; }
.comp-admin-actions { display: flex; gap: 6px; flex: none; }
.comp-admin-result { font-size: 11px; color: var(--fg); line-height: 1.5; margin-top: 10px; }
.comp-admin-result:empty { display: none; }

/* Per-tone colour, as a CLOSED class family (accent/pos/neutral/neg) — see
   the comp-pick-action--<tone> rules above for why this is never a `style`
   attribute. `muted` is a 5th, admin-only badge tone (draft's badge only —
   ADMIN_STATUS in js/competition.js), reusing --dim2/--dim rather than one
   of the 4 tones so a draft's badge stays visually quiet even though its
   timeline glow is deliberately `accent` (it is the one status asking the
   admin to act). */
.comp-admin-badge--muted   { border-color: var(--dim2); color: var(--dim); }
.comp-admin-badge--accent  { border-color: var(--accent); color: var(--accent); }
.comp-admin-badge--pos     { border-color: var(--pos); color: var(--pos); }
.comp-admin-badge--neutral { border-color: var(--dim2); color: var(--dim); }
.comp-admin-badge--neg     { border-color: var(--neg); color: var(--neg); }

.comp-admin-tl-dot--accent.is-done  { background: var(--accent); border-color: var(--accent); }
.comp-admin-tl-dot--pos.is-done     { background: var(--pos); border-color: var(--pos); }
.comp-admin-tl-dot--neutral.is-done { background: var(--dim2); border-color: var(--dim2); }
.comp-admin-tl-dot--neg.is-done     { background: var(--neg); border-color: var(--neg); }
.comp-admin-tl-dot--accent.is-now  { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent); }
.comp-admin-tl-dot--pos.is-now     { border-color: var(--pos); box-shadow: 0 0 0 3px color-mix(in srgb, var(--pos) 25%, transparent); }
.comp-admin-tl-dot--neutral.is-now { border-color: var(--dim2); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dim2) 25%, transparent); }
.comp-admin-tl-dot--neg.is-now     { border-color: var(--neg); box-shadow: 0 0 0 3px color-mix(in srgb, var(--neg) 25%, transparent); }
.comp-admin-tl-dot.is-stop { width: 15px; height: 15px; background: var(--neg); border-color: var(--neg); }

.comp-admin-tl-seg--accent.is-done  { background: var(--accent); }
.comp-admin-tl-seg--pos.is-done     { background: var(--pos); }
.comp-admin-tl-seg--neutral.is-done { background: var(--dim2); }
.comp-admin-tl-seg--neg.is-done     { background: var(--neg); }

/* ── Forms (admin create) ───────────────────────────────────────────────── */
.comp-form { padding: 14px 16px; }
.comp-form-row { display: flex; gap: 12px; flex-wrap: wrap; }
.comp-form-row .field { flex: 1 1 170px; min-width: 0; max-width: 300px; }
.comp-field-hint { display: block; font-size: 10px; color: var(--dim); margin-top: 4px; line-height: 1.4; }
/* `.field label` for something that is not a <label>: the Buy/Sell control is
   a group of two buttons named through aria-labelledby, and an orphan <label>
   with no `for` and no wrapped control names nothing. */
.comp-field-label { display: block; font-size: 9px; color: var(--dim); margin-bottom: 4px; letter-spacing: 1.5px; text-transform: uppercase; }
.comp-label-opt { text-transform: none; letter-spacing: normal; font-weight: 400; color: var(--dim); }
.comp-form-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 4px; }

/* ── Trade tab — Split Panel ────────────────────────────────────────────── */
/* A standing, filterable browse of the curated universe (js/competition.js's
   loadUniversePage — GET /:id/instruments in browse mode when the filter is
   empty, ranked search otherwise) on the left feeds the ticket on the right,
   replacing the old "search box + wall of result buttons" and the free-text
   symbol field. Both panels are `.table-wrap`, so the borders, the header
   band and the scroll behaviour are the app's, not this sheet's; what is
   left here is the column split and the ticket's own controls.

   Filter/quantity inputs use the real `.field` class (terminal.css) rather
   than a bare `<input>` — the old bare inputs here fell back to the
   browser's own default white fill under this app's dark theme, since
   nothing styled them; `.field input` is the SAME primitive the admin
   create-form already uses correctly, not a new one invented here. */
.comp-trade-grid { display: flex; flex-direction: column; gap: 14px; }
@media (min-width: 641px) {
  .comp-trade-grid { display: grid; grid-template-columns: 300px minmax(0, 1fr); align-items: start; }
}
.comp-trade-universe, .comp-trade-ticket { min-width: 0; }

.comp-trade-filter { padding: 12px 12px 0; }
.comp-trade-filter .field { margin-bottom: 12px; }
.comp-trade-list { border-top: 1px solid var(--line); max-height: 420px; overflow-y: auto; }
.comp-trade-list .comp-skel { border: 0; }
/* A real <button>, so it is keyboard-operable (js/dispatch.js delegates click
   only) — these rules are the button reset that keeps it looking like a row. */
.comp-trade-item { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 10px;
  padding: 8px 12px; border: 0; border-bottom: 1px solid var(--line); background: transparent;
  color: var(--fg); font-family: inherit; text-align: left; cursor: pointer; }
.comp-trade-list .comp-trade-item:last-child { border-bottom: none; }
.comp-trade-item:hover { background: var(--panel); }
.comp-trade-item.sel { background: var(--panel); box-shadow: inset 2px 0 0 var(--accent); }
.comp-trade-item-text { min-width: 0; }
.comp-trade-item-sym { display: block; font-family: var(--font-num); font-weight: 700; font-size: 12px; }
.comp-trade-item-name { display: block; font-size: 10px; color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.comp-trade-item-mark { flex: none; font-size: 11px; color: var(--dim); }
.comp-trade-item.sel .comp-trade-item-mark { color: var(--accent); }
.comp-trade-list .empty { border: none; padding: 20px 12px; }

/* The ticket's controls are capped at a form's width rather than stretched to
   the panel: at 1280px a two-button action row was two 450px buttons. */
.comp-trade-ticket-body { padding: 14px 16px; max-width: 560px; }
#comp-trade-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.comp-trade-head-sym { font-size: 18px; font-weight: 700; font-family: var(--font-num); }
.comp-trade-head-name { font-size: 11px; color: var(--dim); }
.comp-trade-head-empty { font-size: 12px; color: var(--fg); }
.comp-trade-head-empty span { display: block; font-size: 10px; color: var(--dim); margin-top: 3px; }

.comp-trade-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; align-items: start; }
.comp-trade-row2 .field { margin-bottom: 0; }
/* 31px matches a `.field input` exactly (12px text + 7px padding + 1px
   border, both sides), so the side toggle and the quantity box sit on one
   line rather than nearly on one. Phones raise both to 44px through
   design-system.css's own touch-target floor, which reaches these because
   they are real <button>s. */
.comp-trade-toggle { display: grid; grid-template-columns: 1fr 1fr; border: 1px solid var(--line); min-height: 31px; }
.comp-trade-toggle button { font-size: 10px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; background: var(--bg); color: var(--dim); border: none; cursor: pointer; font-family: inherit; }
.comp-trade-toggle button:hover { color: var(--fg); }
.comp-trade-toggle button.on.buy { background: var(--pos); color: var(--bg); }
.comp-trade-toggle button.on.sell { background: var(--neg); color: var(--bg); }

.comp-trade-actions { display: flex; gap: 8px; margin-bottom: 14px; }
.comp-trade-actions .btn { flex: 1; justify-content: center; }

.comp-trade-preview { min-height: 44px; }
.comp-trade-idle { font-size: 11px; color: var(--dim); line-height: 1.5; padding-top: 10px; border-top: 1px dashed var(--line); }
.comp-trade-preview-rows { border-top: 1px solid var(--line); padding-top: 10px; margin-top: 6px; }
.comp-trade-preview-head { font-size: 9px; text-transform: uppercase; letter-spacing: 1.4px; color: var(--dim); margin-bottom: 6px; }
.comp-trade-preview-row { display: flex; justify-content: space-between; gap: 12px; font-size: 11px; padding: 3px 0; }
.comp-trade-preview-row .k { color: var(--dim); }
.comp-trade-preview-row b { font-family: var(--font-num); font-variant-numeric: tabular-nums; }

/* The AL6 stale/indicative-price case — never plain dim text, since it is
   the one moment Confirm is disabled even though a price is shown, and
   that needs to read as deliberate rather than broken. Its own class,
   not the real `.banner` (terminal.css) — that primitive is upper-cased
   and centred for a one-line status tag, not this multi-line explanation. */
.comp-trade-warn { padding: 8px 12px; margin-top: 14px; display: flex; gap: 10px; font-size: 11px; border: 1px solid var(--line); background: var(--panel); color: var(--fg); line-height: 1.5; }
.comp-trade-warn::before { content: '[!]'; font-weight: 700; color: var(--warn); flex: none; }
.comp-trade-warn-body div + div { margin-top: 6px; color: var(--dim); }
.comp-trade-warn-body .lede { color: var(--fg); }
.comp-trade-warn-body b { color: var(--fg); }

/* ── Narrow viewports ───────────────────────────────────────────────────── */
@media (max-width: 820px) {
  /* The timeline is four labelled stages; below tablet the segments have no
     room left, so the stages wrap into a grid instead of being squeezed. */
  .comp-admin-tl { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px 8px; margin: 0 0 14px; }
  .comp-admin-tl-node { width: auto; }
  .comp-admin-tl-seg { display: none; }
}
@media (max-width: 640px) {
  .comp-head { align-items: flex-start; }
  .comp-head-facts { gap: 16px; }
  .comp-trade-ticket-body { max-width: none; }
  /* `.table-header` is one row of title + caption, which is right until the
     caption is a button group or a sentence: at phone width the Results panel
     squeezed "Your performance" into a third of the bar and wrapped three
     buttons beside it. Scoped to these two hosts — the primitive itself is
     shared with every other page and is not this sheet's to change. */
  #competition-root .table-header,
  #competition-admin-host .table-header { flex-wrap: wrap; row-gap: 8px; }
  #competition-root .comp-head-note,
  #competition-admin-host .comp-head-note { text-align: left; }
  .comp-overlay { width: 100%; justify-content: flex-start; }
}
@media (max-width: 480px) {
  .comp-trade-row2 { grid-template-columns: 1fr; }
  .comp-trade-actions { flex-direction: column; }
  .comp-enter-actions, .comp-form-actions { align-items: stretch; }
}
