/* Prediction — layout additions on top of the stylesheet copied from opendao-frontend.
   Everything here is additive; nothing in css/style.css was edited. */

/* ── Header ────────────────────────────────────────────────────────────────── */
/* `style.css` sets `justify-content: space-between` on `.header`. With the three flat children
   this page used to have — menu button, logo, wallet button — that centres the *middle* one,
   which put the logo in the middle of the bar. Two things had to be true at once: the logo has
   to sit beside the hamburger, and the two have to be spaced apart.
 *
   The first attempt solved only the first: it left the three children flat and pushed the
   wallet button right with `margin-left: auto`. `space-between` then had nothing left to
   distribute, so the gap between the hamburger and the logo collapsed to zero and the logo sat
   flush against the button. That is a real visual defect and it is what this replaces.
 *
   Now it is the reference interface's shape (`MiningShell.vue`: a flex wrapper with
   `gap: 12px` around the hamburger and the logo): `.header-left` is one child of the header and
   the wallet button is the other, so `space-between` separates the group from the wallet and
   the gap inside the group is explicit. The `margin-left: auto` hack is gone with it. */
.header-left {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 0 0 auto;
}
.header .menu-btn,
.header .brand-logo { flex: 0 0 auto; }

/* ── Page headings ─────────────────────────────────────────────────────────── */
/* The reference interface centres every page's h1 + subtitle (settings.vue does it inline,
   `text-align: center` on the wrapper). Same here, for all four pages at once. */
.page-title,
.page-subtitle { text-align: center; }

.page-title {
    font-size: 28px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 4px;
}
.page-subtitle {
    font-size: 14px;
    color: var(--text-secondary);
    margin-bottom: 24px;
}

/* ── Pool status ───────────────────────────────────────────────────────────── */
/* `[hidden]` has to win over `display: flex`. The user-agent rule is a bare `display: none`,
   which ANY later rule that sets `display` beats — so a hidden `.stat-row` would still draw, and
   it would look like the attribute was ignored. The frontend hides two things by attribute
   (the modal's balance row until there is a balance, the settings wallet's copy button when no
   wallet is connected), so the guard belongs here, once. */
[hidden] { display: none !important; }

.stat-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 8px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.stat-row:last-child { border-bottom: none; }
.stat-label { font-size: 13px; color: var(--text-secondary); }

/* The open modal's second rate row reads from either end — `Risk Rate` or `Remain Rate` — and the
   label is what switches it (task 2026-09-19).

   It is a real `<button>`, for the keyboard and for the screen reader, so this rule's whole job is
   to undo what a button brings with it: a user-agent background, border, padding and font. Modelled
   on `.txid-copy`, the other text-button in this stylesheet, and for the same reason — `font:
   inherit` is the declaration that matters, because a button that keeps the UA font renders at a
   different size from the label above it and the two rows stop lining up.

   The underline is the affordance. There is nothing else on the row to say the word is pressable:
   the value beside it already carries an `[i]`, and a second circle would read as a second note
   rather than as a toggle. `text-underline-offset` keeps it off the descenders, as on `.txid-copy`.

   `text-align: left`, explicitly: a button centres its text and this one sits in a flex row where
   its box can be wider than the word, so without it the two rate labels would not share a left
   edge with the rows above and below them. */
.rate-toggle {
    font: inherit;
    font-size: 13px;
    color: var(--text-secondary);
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    text-align: left;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.rate-toggle:hover, .rate-toggle:focus-visible { color: var(--cyan); }
.stat-value {
    font-family: "JetBrains Mono", monospace;
    font-size: 15px;
    color: var(--text-primary);
}
.stat-value.accent { color: var(--cyan); }

/* The pool banner. It is the LAST thing in `.main` as of the 2026-09-19 task, so it carries no
   bottom margin of its own: the gap above it comes from the preceding card's `margin-bottom`, and
   the gap below it from `.main`'s own bottom padding. A 20px bottom margin here — which is what
   this rule had while the banner sat at the top of the page — would now stack with that padding
   and leave this one page with more trailing whitespace than its three siblings. */
.warn-banner {
    background: rgba(245, 158, 11, 0.12);
    border: 1px solid var(--amber);
    color: #fcd34d;
    border-radius: 12px;
    padding: 12px 16px;
    font-size: 13px;
    line-height: 1.5;
}

/* ── Prize pool metrics ────────────────────────────────────────────────────── */
/* The two headline numbers wear the reference interface's metric card shape
   (`barkx-pool-interface/src/pages/dashboard.vue`: `.grid-2` + `.metric-box` — value on top, a
   small uppercase label under it). This page had them as label/value rows inside one card, which
   gave a per-second-updating balance the same visual weight as a static caption.

   Four boxes in ONE grid as of the 2026-09-15 task (the two global-return figures are the second
   row), which is why the rows are now equalised: `grid-auto-rows: 1fr` gives every row the same
   height as the tallest, so a box whose figure wraps to two lines at 360px does not leave its
   neighbour short. Two separate grids — the obvious way to write "a second row" — would each
   resolve their own column tracks, which is the one thing a grid is for. */
.metric-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-bottom: 20px;
    /* One height for every row: a row whose tallest cell wrapped must not be shorter than its
       neighbour. `align-items: stretch` is the default and is what makes the boxes in a row agree. */
    grid-auto-rows: 1fr;
}
.metric-box {
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid var(--border-dark);
    border-radius: 20px;
    padding: 16px;
    text-align: center;
    min-width: 0;
    /* A box is a fixed-height cell now; without this a wrapped figure would grow it and, at
       `align-items: stretch`, its neighbour with it — which is the correct direction, but the
       content still has to be centred rather than pinned to the top. */
    display: flex;
    flex-direction: column;
    justify-content: center;
    /* Tappable as of the 2026-09-15 task: each box opens the note that explains it. The affordance
       the task asks for is a glow on hover. It is on BOTH `:hover` and `:focus-visible`, and that
       pair is the whole of the accessibility argument for this element: the box is not a
       `<button>`, so its keyboard focus ring is the browser's default — and the same glow has to
       answer the keyboard that answers the pointer, or the only way to find out what the cards do
       is a mouse. `transition` on the two properties the glow moves, not `all`, so the layout
       properties this rule also sets cannot animate into a jitter. */
    cursor: pointer;
    transition: border-color 0.25s ease, box-shadow 0.25s ease;
}
.metric-box:hover,
.metric-box:focus-visible {
    border-color: var(--cyan);
    box-shadow: 0 0 18px var(--cyan-glow);
    outline: none;
}
/* A press has to look like a press: the glow alone leaves a tap with no feedback on a touch
   screen, where there is no hover state to lose. */
.metric-box:active {
    border-color: var(--cyan-bright);
    box-shadow: 0 0 22px var(--cyan-glow);
}
.metric-val {
    font-family: "JetBrains Mono", monospace;
    font-size: 24px;
    font-weight: 700;
    color: var(--text-primary);
    /* BREAK AT THE COMMAS, never inside a digit group (task 2026-09-15, measured at 360px).
       `1,234,567` is 130px of this font at 24px and the cell has 98px of content, so the figure
       must wrap — and left to itself the browser broke it as `1,234,56` / `7`, splitting a group
       and printing a different number. Commas are not break opportunities in CSS, so the break is
       structural: `Fmt.formatBarkxWholeWrappable` puts each group in its own `<span>`.

       `display: inline-block` is what makes that a wrap point, and it is the half that is not
       obvious: adjacent INLINE spans and their commas join into one unbreakable run, so `nowrap`
       on an inline span produced no break at all and the figure simply overflowed the cell. As
       inline-block boxes each group is indivisible on its own, and the only place the figure can
       divide is between two of them. `word-break: normal` states that the width is the limit and
       not a licence to split a group. */
    word-break: normal;
}
/* The comma groups of a wrappable figure. `font-family` is repeated here and it is NOT redundant
   (task 2026-09-20): `style.css` opens with `* { font-family: "Outfit", sans-serif }`, and a
   universal selector matches EVERY element — including these spans — so they took the page font
   while `.metric-val`'s JetBrains Mono reached only the text node the commas left outside them.
   One figure then printed in two typefaces: the pool balance, which is a single text node, in the
   mono the box asks for, and `Prize Pool`'s three span-wrapped neighbours in Outfit at the same
   size and weight. Read as "the first card's number is a different font", which is exactly what
   was reported. `font: inherit` would carry the size and weight too, but it would also carry the
   parent's `font-family` only by virtue of inheriting — the universal selector still loses to this
   rule, so naming the family is both shorter and the thing being asserted. */
.metric-val span {
    display: inline-block;
    white-space: nowrap;
    font-family: "JetBrains Mono", monospace;
}
/* `.accent` is the pool balance's existing colour, unused since the 2026-09-15 task took the
   `accent` class off the balance box. Left in place rather than deleted: it names the page's one
   semantic accent for a metric figure, and the next figure that wants the cyan should find it. */
.metric-val.accent { color: var(--cyan); }
.metric-lbl {
    font-size: 11px;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-top: 4px;
    letter-spacing: 0.5px;
    /* The label may wrap (`FLEXIBLE RETURN RATE` does at 360px) and must not be clipped by the
       cell; it is two short lines there, which the row's equal height already allows for. */
    line-height: 1.3;
}

/* ── Panel titles with an [i] beside them (task 2026-09-15) ────────────────────
   `style.css` draws `.card-title` as a block with a bottom rule. Adding an [i] to that row needs
   the title and the button to be two flex items, or the button lands on the row's next line: the
   title's own text is a text node, and a text node and a button are laid out as inline content,
   which wraps as soon as the title is wide (the Chinese one is). `margin-left: auto` on the button
   is what puts it at the far end — the same right edge the panel's content ends at.

   `align-items: baseline` rather than `center`: the button's own rule carries
   `vertical-align: middle` for the in-row case (see `.info-btn`), and in a flex row that
   declaration has no effect at all — baseline is what keeps the circle sitting on the title's
   own baseline instead of centred in a taller line box. */
.card-title.title-with-info {
    display: flex;
    align-items: baseline;
    gap: 8px;
}
.card-title.title-with-info .info-btn {
    margin-left: auto;
    /* `flex: 0 0 auto`, or the button is a flex item that may be shrunk by the title next to it. */
    flex: 0 0 auto;
}

/* ── Ticker table ────────────────────────────────────────────────────────────
   A real `<table>`, not rows of divs. The previous version laid each row out as its own grid,
   which is not the same thing: a grid resolves *within* a row, so every market's Base Return
   Rate and Mark Price were sized independently and the columns did not line up on any edge
   between rows. A table computes one set of columns for all of its rows — that shared geometry
   is the whole point — which is what "a real table with four well-proportioned columns" asks for.

   The panel is sized for five rows and scrolls beyond that, so a long market list cannot push
   the position list off the page. The height is built from the row height rather than guessed:
   `--ticker-row` is what the rows actually are, `--ticker-head` is the sticky header's own
   height, and the cap adds the two. Changing either figure moves the cap with it — which matters
   because a magic number here silently clips the fifth row or shows a sixth.

   `--ticker-head` is the header's *rendered* height, measured in the browser (2026-09-13), not
   the number of lines it looks like: 9px type at `line-height: 1.3` over two lines is 23.4px,
   plus 8px of vertical padding and the 1px bottom border is 31.9px. It read 30px while the header
   was a single line at 10px, and the two-line header it is now made the real box a hair taller
   than the cap assumed — a fifth row would have been 1.9px short of fully visible. */
#ticker-card {
    --ticker-row: 44px;
    --ticker-head: 32px;
}
.ticker-table {
    /* Five rows plus the header. `overflow-y: auto` is the scrollbar the sixth row brings. */
    max-height: calc((5 * var(--ticker-row)) + var(--ticker-head));
    overflow-y: auto;
    overflow-x: hidden;
}
.ticker-table table {
    width: 100%;
    border-collapse: collapse;
    /* `fixed` is what makes four columns possible at phone width: an automatic layout sizes each
       track to its widest cell and a long market name or a seven-figure mark price then widens
       the table past the panel. With `fixed`, the `col` widths below are the columns, and the
       name column gets `ellipsis`. */
    table-layout: fixed;
}
.ticker-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    /* Opaque, or the rows scroll through it. */
    background: var(--bg-card-solid);
    /* The header sizes itself to its content rather than to `--ticker-head`: the price header
       wraps to two lines and a fixed 30px box let the second line spill out of the sticky band.
       The row cap in `.ticker-table` still uses `--ticker-head` — with the two agreeing at two
       lines, the cap counts the header correctly. */
    min-height: var(--ticker-head);
    padding: 4px 6px;
    /* 9px, not 10px. Measured against the old `Base Return Rate`, which folded to `Base Return`
       over `Rate`: at 9px those two lines are 54.5px and 25px inside a column carrying 64px of
       content at 430px, and at 10px the longer one did not fit. The label is `Profit Rate` now
       and its longer line is far shorter, so the size is kept for the OTHER two-line header
       (`Mark Price`) and because nothing asked for the band to change. */
    font-size: 9px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.2px;
    line-height: 1.3;
    color: var(--text-muted);
    text-align: left;
    /* `normal`, never `anywhere`: the labels break at their spaces. `anywhere` was in an earlier
       draft and broke `RETURN` mid-word into `RETUR`/`N`, which is what turned this header into
       three lines. */
    white-space: normal;
    overflow-wrap: normal;
    border-bottom: 1px solid var(--border-dark);
}
.ticker-table td {
    height: var(--ticker-row);
    padding: 0 6px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
    vertical-align: middle;
}
.ticker-table tbody tr:last-child td { border-bottom: none; }
/* Ticker left, the two figures right-aligned in their own columns, the button pinned right. */
.ticker-table th:nth-child(2), .ticker-table td:nth-child(2),
.ticker-table th:nth-child(3), .ticker-table td:nth-child(3) { text-align: right; }
.ticker-table th:last-child, .ticker-table td:last-child { text-align: right; padding-right: 2px; }
.ticker-col-name {
    font-family: "JetBrains Mono", monospace;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ticker-col-rate,
.ticker-col-price {
    font-family: "JetBrains Mono", monospace;
    white-space: nowrap;
}
.ticker-col-rate { color: var(--text-secondary); }
/* The two profit rates, stacked inside that one cell: up over down, green over red
   (proposal-fork §Market 面板). The colours are `--green` / `--red`, the same pair the direction
   glyph and the price-gap cell use, so a side means one colour everywhere on this page.

   `font-size` is declared here rather than inherited, and it is the reason the row still fits.
   The cell inherits 16px, at which two monospace lines are about 40px inside a 44px row
   (`--ticker-row`) — near enough the cap that a slightly taller line-height would grow the row,
   and the five-row panel height is built from that variable, so the fifth market would drop out
   of view. At 12px/1.35 the pair is 32.4px and the row keeps its margin. It also buys width:
   `100.00%`, the widest figure either line can print, is 50.4px here against 67.2px at 16px. */
.ticker-rate {
    font-size: 12px;
    line-height: 1.35;
    /* Each line is one figure; the cell's `nowrap` keeps it on its own line, and the stacking is
       the two block boxes, not a wrap. */
    white-space: nowrap;
}
.ticker-rate.up { color: var(--green); }
.ticker-rate.down { color: var(--red); }
.ticker-col-price {
    font-size: 14px;
    font-weight: 600;
    color: var(--cyan);
    letter-spacing: -0.2px;
}
/* The button's own box is 98px wide at `Open Position`, more than the column it lives in on a
   430px screen — that is what pushed it past the panel's right edge (reported live 2026-09-13).
   Two things make it fit: the label is `Open`, and the column shares move to give it the room
   (`TICKER_COLS` in `predict.js`). It stays `nowrap` and right-aligned so several rows' buttons
   land on one vertical edge. */
.ticker-btn {
    padding: 7px 10px;
    background: rgba(0, 240, 255, 0.08);
    border: 1px solid var(--cyan);
    color: var(--cyan);
    border-radius: 10px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.2px;
    white-space: nowrap;
    cursor: pointer;
    transition: background 0.2s, box-shadow 0.2s;
}
.ticker-btn:hover { background: rgba(0, 240, 255, 0.16); box-shadow: 0 0 12px var(--cyan-glow); }


.degraded-tag {
    display: inline-block;
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    background: rgba(245, 158, 11, 0.15);
    border: 1px solid var(--amber);
    color: var(--amber);
}

/* ── Position cards ────────────────────────────────────────────────────────── */
.pos-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 12px;
}
.pos-id {
    font-family: "JetBrains Mono", monospace;
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}
.pos-badge {
    font-size: 11px;
    padding: 3px 10px;
    border-radius: 999px;
    border: 1px solid var(--border-dark);
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
/* Three states, not two, and the third is the point (task 2026-09-19).
 *
 * The badge answers one question — is the verdict in, and if so which way — so the palette has to
 * separate "decided" from "still running" BEFORE it separates right from wrong. It did not: a
 * position awaiting its expiry carried the same cyan as a position that had won, so the only two
 * badges a user sees on a healthy card (`Active`, then `Correct`) were the same colour, and the
 * card changed its word without changing its signal.
 *
 * Amber for both undecided states, and `--amber` specifically because this console already spends
 * it on exactly that meaning: `.degraded-tag` above is the same pill in the same colour for a
 * quote whose sources are incomplete, and `toast.js` gives `warning` the same hue. Nothing here
 * says "something is wrong" — `Active` is the normal life of a position. It says "not yet".
 *
 * ── Why these class names ──────────────────────────────────────────────────────────────────────
 *
 * They were `open` and `expired`, which stopped being true when the badge started keying on the
 * verdict: `expired` was being painted on `Incorrect`, and the `Expired` badge now wants the
 * third class instead. A name that states the opposite of the row it paints is a trap for whoever
 * touches this next, so the two renamed with the third's arrival. `history.js` paints the same
 * two and moved with them — that page has only the decided states, which is why it needs no
 * `pending`. */
.pos-badge.win { border-color: var(--cyan); color: var(--cyan); }
.pos-badge.loss { border-color: var(--magenta); color: var(--magenta); }
.pos-badge.pending { border-color: var(--amber); color: var(--amber); }

.pos-grid {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 6px 12px;
    font-size: 13px;
    margin-bottom: 12px;
}
.pos-grid .k { color: var(--text-muted); }
.pos-grid .v {
    font-family: "JetBrains Mono", monospace;
    text-align: right;
    color: var(--text-primary);
}
.pos-grid .v.up { color: var(--green); }
.pos-grid .v.down { color: var(--red); }
.pos-grid .v.dim { color: var(--text-muted); }
.pos-grid .v.hot { color: var(--cyan); }

/* Direction triangle (task 2026-09-12). It carries its own colour rather than inheriting the
   row's: the Direction cell is plain text, and the position's side is fixed at open — unlike the
   gap beneath it, which is green or red depending on how the price has moved since. */
.dir-glyph { margin-left: 5px; vertical-align: -1px; }
.dir-glyph.up { color: var(--green); }
.dir-glyph.down { color: var(--red); }

.txid {
    font-family: "JetBrains Mono", monospace;
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 10px;
    word-break: break-all;
}
.txid a { color: var(--text-secondary); text-decoration: none; }
.txid a:hover { color: var(--cyan); }

/* The txid, tappable to copy (task 2026-09-14). A `<button>` rather than an `<a>` — the tap
   copies the hash, it does not navigate — so everything a button brings with it has to be
   undone here: the UA font, the background, the border, the padding, the box. Underlined on
   purpose: the task asks the user to SEE that it can be clicked, and an underline is the one
   affordance that survives a monospace row of hex where a colour change reads as a link-out. */
.txid-copy {
    font: inherit;
    color: var(--text-secondary);
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.txid-copy:hover, .txid-copy:focus-visible { color: var(--cyan); }

/* The countdown beside the Retry button (task 2026-09-14). Dim and slightly smaller than the
   sentence it sits under: it is a progress note, not a second instruction, and it must not look
   like something to press. `min-width` keeps the row from twitching as the number changes
   width between `(10s)` and `(9s)`. */
.retry-countdown {
    display: inline-block;
    min-width: 42px;
    color: var(--text-muted);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

/* ── Buttons ───────────────────────────────────────────────────────────────── */
.btn-page {
    padding: 10px 18px;
    background: rgba(0, 240, 255, 0.08);
    border: 1px solid var(--cyan);
    color: var(--cyan);
    border-radius: 10px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s, box-shadow 0.2s;
}
.btn-page:hover { background: rgba(0, 240, 255, 0.16); box-shadow: 0 0 12px var(--cyan-glow); }
.btn-page:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    box-shadow: none;
    border-color: var(--text-muted);
    color: var(--text-muted);
    background: transparent;
}
.btn-ghost {
    padding: 10px 18px;
    background: transparent;
    border: 1px solid var(--border-dark);
    color: var(--text-secondary);
    border-radius: 10px;
    font-size: 13px;
    cursor: pointer;
}
.btn-ghost:hover { border-color: var(--text-secondary); color: var(--text-primary); }

.btn-block { display: block; width: 100%; margin-top: 12px; }
.btn-settle {
    width: 100%;
    padding: 14px;
    margin-top: 4px;
    background: var(--green);
    color: #04150f;
    border: none;
    border-radius: 12px;
    font-family: "JetBrains Mono", monospace;
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 0 15px var(--green-glow);
}
.btn-settle:disabled {
    background: transparent;
    border: 1px solid var(--text-muted);
    color: var(--text-muted);
    box-shadow: none;
    cursor: not-allowed;
}

/* ── Principal tiers (2x2, spec §7.2) ──────────────────────────────────────── */
.tier-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
    margin-top: 12px;
}
.tier-btn {
    padding: 12px 10px;
    background: rgba(0, 240, 255, 0.06);
    border: 1px solid var(--border-dark);
    border-radius: 12px;
    color: var(--text-primary);
    cursor: pointer;
    text-align: left;
    transition: border-color 0.2s, background 0.2s;
}
.tier-btn:hover:not(:disabled) { border-color: var(--cyan); background: rgba(0, 240, 255, 0.12); }
.tier-btn:disabled { opacity: 0.35; cursor: not-allowed; }


.tier-amount {
    font-family: "JetBrains Mono", monospace;
    font-size: 15px;
    font-weight: 600;
    display: block;
}
.tier-return {
    display: block;
    margin-top: 4px;
    font-family: "JetBrains Mono", monospace;
    font-size: 11px;
    color: var(--text-secondary);
}

/* ── Direction choice (up/down) ────────────────────────────────────────────────
   Two equal rectangles filling the modal's width. This is the single decision the position rests
   on, and it was two pill-shaped tabs the size of a filter chip — the same affordance as
   "all / correct / wrong" on the history page. Filled colour and the full row make the choice
   read as a choice.

   The colour carries the SELECTION, not the identity (task 2026-09-14). Both buttons were
   previously painted their own colour at all times, the unselected one dimmed to 45% — which
   left the user comparing two coloured rectangles of differing brightness to work out which was
   live, and made "Up" and "Down" look equally chosen. Now only the chosen one is coloured:
   unselected is a neutral grey slab, and green or red appears only on selection. The choice
   reads at a glance, and the two sides cannot be confused with each other.

   `data-dir` carries `'up'` / `'down'` (the lowercase wire vocabulary, U-7). It read `"0"` /
   `"1"` until 3bed8e6 changed the JS to the literals and left these selectors on the numbers —
   neither rule matched anything after that and both buttons fell back to the base rule's white.
   The values here are the contract; `predict.js` writes them and a test pins the pair together. */
.dir-row {
    display: flex;
    gap: 12px;
    /* 4px above read as if the buttons were touching the Return cap row's rule (task
       2026-09-12). The row is the first child of the modal body, so this top margin is the whole
       of the gap between the divider and the buttons. */
    margin: 16px 0;
}
.dir-btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 16px 10px;
    /* A border on every state, transparent until it is earned. Declaring it only on the selected
       rule would make the button grow by the border's width as it was picked, shifting both
       halves of the row mid-tap. */
    border: 1px solid transparent;
    border-radius: 12px;
    font-size: 16px;
    font-weight: 700;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    cursor: pointer;
    background: rgba(255, 255, 255, 0.07);
    color: var(--text-secondary);
    transition: background 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
}
/* `:not(:disabled)` as well as `:not(.active)`: a locked button that still lights up under the
   cursor is answering "yes, tap me" to a question the lock exists to answer no to. */
.dir-btn:hover:not(.active):not(:disabled) {
    background: rgba(255, 255, 255, 0.12);
    color: var(--text-primary);
}
.dir-btn[data-dir="up"].active {
    background: var(--green);
    color: #04150f;
    box-shadow: 0 0 18px var(--green-glow);
}
.dir-btn[data-dir="down"].active {
    background: var(--red);
    color: #1a0505;
    box-shadow: 0 0 18px rgba(239, 68, 68, 0.4);
}
/* Frozen while the wallet is confirming (task 2026-09-14). The choice is settled at that point,
   so both halves go flat and the glow comes off — a highlighted button still casting light reads
   as "still selectable", which is exactly the question the lock exists to answer. The selected
   one keeps its colour (which direction was chosen stays legible) and loses its emphasis. */
.dir-btn:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}
.dir-btn:disabled.active {
    box-shadow: none;
}

/* The four principal buttons' own sizes: `10`, `100`, `1,000`, `10,000` — no decimals (task
   2026-09-12). The amount stays the largest thing on the button; the symbol is set beside it in
   the label's own size so the digits lead. */
.tier-btn .tier-amount {
    font-size: 16px;
}

/* ── Tabs / filters ────────────────────────────────────────────────────────── */
/* Centred (task 2026-09-12). `justify-content` rather than `margin: auto` on the buttons, so a
   group that wraps to two lines at phone width stays centred on both lines. Used by history.html
   and data.html; predict.html's Up/Down pair is `.dir-row`, which fills the width instead. */
.tab-row {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-bottom: 18px;
    flex-wrap: wrap;
}
.tab {
    padding: 8px 16px;
    border-radius: 999px;
    border: 1px solid var(--border-dark);
    background: transparent;
    color: var(--text-secondary);
    font-size: 13px;
    cursor: pointer;
}
.tab.active {
    border-color: var(--cyan);
    color: var(--cyan);
    background: rgba(0, 240, 255, 0.08);
}

/* ── Tables (data page) ────────────────────────────────────────────────────── */
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table th {
    text-align: left;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text-muted);
    padding: 8px 6px;
    border-bottom: 1px solid var(--border-dark);
    font-weight: 500;
}
.data-table td {
    padding: 10px 6px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.05);
    font-family: "JetBrains Mono", monospace;
    color: var(--text-primary);
}
.data-table tr:last-child td { border-bottom: none; }
.data-table td.num, .data-table th.num { text-align: right; }

/* ── Empty / error states ──────────────────────────────────────────────────── */
.state {
    text-align: center;
    padding: 28px 16px;
    color: var(--text-muted);
    font-size: 13px;
}
.state .state-actions { margin-top: 14px; }

/* ── Info button + cap simulator ───────────────────────────────────────────── */
.info-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    margin-left: 6px;
    border-radius: 50%;
    border: 1px solid var(--cyan);
    color: var(--cyan);
    background: transparent;
    font-size: 11px;
    font-weight: 700;
    cursor: pointer;
    /* The alignment correction (task 2026-09-12: "the digits of Flexible return rate and Return
       cap are set in a visibly different font from Base return rate's").

       All three numbers are the same `JetBrains Mono` in the same `.stat-value`, so the
       difference was never the font — it was the box the button put them in. An `inline-flex`
       button is a replaced-ish box on the baseline, so a row *with* one grew a taller line box
       and its text was positioned against that; `baseline` alone does not line the two up. The
       row that carries an [i] laid its number out differently from the row that does not, and at
       this size that reads as a different typeface. `middle` puts the button's own centre on the
       text's centre, which is what makes the three rows agree. */
    vertical-align: middle;
}
.info-btn:hover { background: rgba(0, 240, 255, 0.15); }

/* Two independent ladders (U-9). Each `.cap-ladder` is a self-contained slider + readout pair,
   and nothing in the stylesheet links them — no shared flex context that would make one grow
   with the other, no `:has()` rule that moves its sibling. The head is what says which ladder a
   pair belongs to; without it the two identical slider blocks would be indistinguishable. */
.cap-ladder { margin-bottom: 10px; }
.cap-ladder-head {
    font-family: "JetBrains Mono", monospace;
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-secondary);
    margin-top: 14px;
}
.cap-slider {
    width: 100%;
    margin: 8px 0 6px;
    accent-color: var(--cyan);
}
.cap-readout {
    display: flex;
    justify-content: space-between;
    font-family: "JetBrains Mono", monospace;
    font-size: 13px;
    color: var(--text-secondary);
    margin-bottom: 6px;
}
.cap-readout strong { color: var(--cyan); }

/* ── Modal layer ───────────────────────────────────────────────────────────────
   The backdrop for every modal (open-position, return-cap simulator, settings
   choices), built by `Toast.overlay()`. All of its geometry lives here, once:
   the previous version assigned these same properties inline while wearing
   `custom-modal-overlay`, a class `css/style.css` copied from another project
   where it means "a template's dismissable overlay" — that class sets
   `opacity: 0`, so every modal in the product was transparent (see the note on
   `Toast.overlay()` in js/toast.js). Anything that hides or shows a whole modal
   belongs on this rule, not in an inline style. */
.modal-layer {
    position: fixed;
    inset: 0;
    z-index: 1500;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 20px;
    overflow-y: auto;
    background: rgba(0, 0, 0, 0.85);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}
/* The card, on the layer's own terms: one scroll container, card centred. */
.modal-layer > * {
    margin: auto;
}

/* The wallet-signing mask (task 2026-09-16, spec-frontend-batch-3 §3.2). Built by
   `Toast.walletBusy()` and removed by `Toast.walletIdle()`; this rule is the whole of its
   geometry, which is the §7.2 rule — a full-screen layer owns a rule here, never inline styles.

   `position: fixed; inset: 0` is what makes it block: an element covering the viewport receives
   the pointer events that land on it. There is deliberately NO `pointer-events` declaration —
   `none` would be the definition of the click-through this layer exists to stop, and an explicit
   `auto` would say nothing the geometry does not already say.

   `z-index: 1900` is the load-bearing number. It sits above `.modal-layer` (1500) and the cap
   simulator's inline 1600 — the task is that the WHOLE page becomes untouchable, and the modal is
   part of the page — and below the toast (2000), which must stay readable: a rejected signature,
   a cancelled open and an unconfirmed broadcast all raise their copy while this mask is still up,
   and a toast under the mask is "you signed and nothing was said". */
.wallet-busy {
    position: fixed;
    inset: 0;
    z-index: 1900;
    background: rgba(0, 0, 0, 0.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    cursor: wait;
    /* The mask carries the same ring the open-position modal waits behind (task 2026-09-19). It
       was a bare dark sheet, which says that the page has stopped without saying that anything is
       happening — and what is happening is in another window, the wallet's own. Centring is all
       the ring needs from this rule; the ring itself is `.spinner`, unchanged and shared.
       Still no copy, deliberately (spec §4.2): the wait is already written on the button
       underneath, and two sentences for one wait is one too many. A spinner is not a sentence. */
    display: flex;
    align-items: center;
    justify-content: center;
}

.modal-note {
    font-size: 12px;
    color: var(--text-secondary);
    line-height: 1.6;
    margin-bottom: 12px;
}
.modal-note a { color: var(--cyan); }
.modal-note.dim { color: var(--text-muted); }

/* ── Explainer notes (task 2026-09-15) ─────────────────────────────────────────
   The six note modals are written as short rules rather than as prose, so most of their content is
   a list. A `<ul>` inside `.modal-note` inherits the size and colour (12px, `--text-secondary`) and
   only needs the spacing: the browser's default padding would indent it past the modal's own text,
   and its default margin would break the rhythm of the paragraphs above and below.

   `list-style: disc` with a colour on the marker: the bullets are a scanning aid, so they have to
   be visible, and the default black is invisible on this background. `::marker` is what colours
   them without colouring the text they introduce. */
.modal-note ul {
    margin: 0 0 12px;
    padding-left: 18px;
}
.modal-note li {
    margin-bottom: 4px;
    line-height: 1.5;
}
.modal-note li::marker { color: var(--cyan); }
/* A second level, used by `About Preview` (a formula under each rule) and `About Settlement`
   (what a win does, and what a loss does). `margin-top` rather than the outer list's spacing: a
   nested list belongs to the line above it, and the default `margin: 0 0 12px` would push it as
   far from its own bullet as from the next one. */
.modal-note ul ul {
    margin: 4px 0 0;
    padding-left: 18px;
}
/* The formula lines inside a list. Mono and cyan, the same treatment `.rate-formula` gives the
   standalone formula in the two rate notes — one visual answer to "this is arithmetic, not
   prose", wherever it appears. */
.modal-note li.formula {
    font-family: "JetBrains Mono", monospace;
    color: var(--cyan);
}
/* One note per paragraph, in the same shape every other note modal uses. It exists as a class
   rather than as a bare `<p>` because `.modal-note`'s own `margin-bottom` is the module spacing,
   and a `<p>` would arrive with the browser's margins on top of it. */
.modal-note p { margin: 0 0 12px; }
.modal-note p:last-child, .modal-note ul:last-child { margin-bottom: 0; }

/* ── The info modals' own reading size (task 2026-09-19) ───────────────────────
   Every note modal `openNoteModal` builds carries `.note-modal`, and these are the only places
   the bump applies. `.modal-note` is 12px because it is also the one-line aside above the tier
   buttons and the "connect your wallet" line inside the open-position modal — captions beside a
   control, where 12px is right. A note modal is the opposite case: several hundred words that a
   user opened deliberately in order to read them, and 12px is what the report called hard to
   read.

   Scoped to the wrapper rather than applied to `.modal-note` itself, so raising the notes cannot
   silently re-size those two captions. The line heights come up with the size — at 14px the 1.6
   of the base rule closes the lines up relative to their own height. */
.note-modal .modal-note {
    font-size: 14px;
    line-height: 1.75;
}
.note-modal .modal-note li {
    margin-bottom: 8px;
    line-height: 1.7;
}
.note-modal .modal-note li:last-child { margin-bottom: 0; }
.note-modal .modal-note p { margin-bottom: 14px; }
/* Restated at this specificity on purpose. `.modal-note p:last-child` ties with the rule above it
   and loses on order, so without this line every note modal would carry 14px of dead space under
   its last paragraph — the base rule's `margin-bottom: 0` would be silently outranked. */
.note-modal .modal-note > :last-child { margin-bottom: 0; }

/* ── Modal loading state ───────────────────────────────────────────────────────
   The open-position modal fetches its quote, the allowance, all four tier previews and the
   wallet balance before it paints anything, so it needs a wait state rather than content that
   arrives in pieces. The pieces were the defect: the payment buttons were live for as long as
   the slowest request took, and a tap in that window started an open the wallet could not pay
   for (reported live 2026-09-12).

   The ring is drawn with a border rather than an SVG so it costs nothing to load and animates
   on the compositor. `prefers-reduced-motion` gets a still ring: the state is still legible, and
   a spinner is the one animation a user cannot look away from. */
.modal-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    /* Tall enough that the card does not jump in size when the body arrives. */
    min-height: 148px;
    padding: 18px 0;
}
.spinner {
    width: 26px;
    height: 26px;
    border: 3px solid rgba(0, 240, 255, 0.18);
    border-top-color: var(--cyan);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
}
@keyframes spin {
    to { transform: rotate(360deg); }
}
.modal-loading-text {
    font-size: 12px;
    color: var(--text-muted);
}
@media (prefers-reduced-motion: reduce) {
    .spinner { animation: none; border-top-color: var(--cyan); }
}


/* The standalone formula line in `About Profit Rate` and `About Remain Rate`. It sits INSIDE the
   note's own `.modal-note`, so it inherits the size and colour and only declares the emphasis and
   the breathing room. (It used to carry `.modal-note` itself, as a sibling of the paragraphs; a
   `.modal-note` nested in a `.modal-note` re-declares a font size the note modal has just
   overridden, which is exactly the kind of split that made a modal invisible once already.) */
.rate-formula {
    font-family: "JetBrains Mono", monospace;
    color: var(--cyan);
    text-align: center;
    padding: 8px 0;
    /* The paragraph above it keeps its own bottom margin; this is what stops the paragraph BELOW
       from sitting closer to the formula than the formula sits to its introduction. */
    margin-bottom: 6px;
}

/* ── Node Tier rate table (flexible return modal) ─────────────────────────── */
/* The row for the user's own tier is highlighted with a background tint. `--cyan` at 12% is the
   same weight the tier buttons use for their hover, so "this is the one that applies to you"
   reads the same way in both places. */
.tier-table {
    margin: 4px 0 14px;
    border: 1px solid var(--border-dark);
    border-radius: 12px;
    overflow: hidden;
}
.tier-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 9px 14px;
    font-size: 13px;
    color: var(--text-secondary);
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.tier-row:last-child { border-bottom: none; }
.tier-row .tier-col-rate {
    font-family: "JetBrains Mono", monospace;
    color: var(--text-primary);
}
.tier-row.tier-head {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    color: var(--text-muted);
    background: rgba(255, 255, 255, 0.03);
}
.tier-row.active { background: rgba(0, 240, 255, 0.12); }
.tier-row.active .tier-col-tier,
.tier-row.active .tier-col-rate { color: var(--cyan); }

/* The wallet balance closes the modal, below the four payment buttons and on a line of its own.
   It used to sit with the market rows at the top, which read as another quote figure; the number
   a user checks before choosing a size belongs next to the sizes. `.stat-row` already draws the
   separating hairline, so this only adds the space between it and the buttons above. */
.stat-row.balance-row { margin-top: 18px; }

/* The balance row's own cell: the figure plus the door to the swap (task 2026-09-15). Both are
   right-aligned on the row's edge, so the button lines up with the number it sits under rather
   than with the label opposite it. `gap` is the air between them; the number keeps its own
   `.stat-value` colouring and font, which is what makes this a row of the same table rather than a
   second, smaller thing bolted on. */
.balance-with-swap {
    display: inline-flex;
    align-items: baseline;
    justify-content: flex-end;
    gap: 10px;
    /* The cell has to be able to give width back. An inline-flex box sizes to its content, and a
       `.stat-value` is a 15px monospace figure that can be very long — the balance is whatever the
       wallet holds, which is unbounded. Measured (2026-09-15, in a browser): at a large balance the
       cell grew past the modal's own box and pushed the whole row out of the card, taking the Swap
       button with it. `max-width` + `min-width: 0` on the parts is what lets the number wrap
       instead of the row overflowing. */
    max-width: 100%;
    min-width: 0;
}
.balance-with-swap > [data-role="balance"] {
    /* The number is the part that yields: it may break across lines (a 78-digit figure has no
       better option), while the button beside it is 52px and never shrinks. `text-align: right`
       keeps a wrapped figure aligned with the row's edge rather than centred under it. */
    min-width: 0;
    overflow-wrap: anywhere;
    text-align: right;
}
/* A link wearing a button's clothes, because it is an `.stat-value` cell's neighbour: it has to
   read as tappable at 13px next to a monospace figure, and the cyan-on-transparent treatment is the
   one `.btn-page` and `.txid-copy` already use for exactly that. */
.swap-btn {
    font-family: "Outfit", sans-serif;
    font-size: 12px;
    font-weight: 600;
    color: var(--cyan);
    background: rgba(0, 240, 255, 0.08);
    border: 1px solid var(--cyan);
    border-radius: 8px;
    padding: 4px 10px;
    text-decoration: none;
    white-space: nowrap;
    transition: background 0.2s, box-shadow 0.2s;
}
.swap-btn:hover, .swap-btn:focus-visible {
    background: rgba(0, 240, 255, 0.18);
    box-shadow: 0 0 10px var(--cyan-glow);
    outline: none;
}

/* ── Settings ──────────────────────────────────────────────────────────────── */
.kv {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    padding: 12px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    font-size: 14px;
}
.kv:last-child { border-bottom: none; }
.kv .k { color: var(--text-secondary); }
.kv .v {
    font-family: "JetBrains Mono", monospace;
    font-size: 13px;
    color: var(--text-primary);
    word-break: break-all;
    text-align: right;
}

/* A contract row is an address plus a copy affordance. The address is the part that may
   wrap; the button never shrinks. A plain class rather than `:has()`, which is still uneven
   across the in-app WebViews this page is opened in. */
.kv .v.kv-action {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
}
.kv .v.kv-action > span { min-width: 0; }

/* The address itself (task 2026-09-15).

   TWO defects, and the first one is why this rule has to name `font-family` rather than let
   the monospace come down by inheritance:

     1. `.kv .v` above sets `"JetBrains Mono", monospace`, but the address is a `<span>` INSIDE
        it, and the global `* { font-family: "Outfit", sans-serif }` in `style.css` matches that
        span directly. A declaration that matches an element always beats one the element would
        have inherited, whatever the specificity — so the address has been rendering in the
        proportional face all along, at a computed `Outfit, sans-serif` while its own parent's
        computed family read `"JetBrains Mono", monospace`. Measured with canvas advances at
        13px: `i` 2.91px against `W` 12.75px, i.e. not monospace. The task asks for the address
        in monospace precisely because it is a hex string — a proportional face gives `1` and
        `l`, `0` and `O` different widths, so the wrapped column edges never line up.
     2. No width limit. At 320px the row is 238px and the value cell took 158.5px of it, with
        the address wrapping to three ragged lines of 116.5px each — nothing said how wide the
        address was allowed to be, so it filled whatever the flex row handed it and the last
        line was a two-character stub.

   `max-width: 21ch` is not a round number chosen by eye. `ch` is the advance of `0` in the
   element's own font, and this face's is 7.8px, so 21ch is 163.8px: exactly two characters per
   line more than the 42-character address needs, which is what makes it break `21 + 21`
   instead of `24 + 18`. That even split is the whole point of the limit — the row stops
   reflowing into a lopsided pair at every width, and at 430px both address rows are the same
   163.8px box with the same two lines inside them. `min-width: 0` above is what lets that box
   shrink below its content on a narrow phone rather than pushing the flex row wider, where it
   drops to three lines and then four on the smallest screens; every line there is still full
   width, so the ragged stub is gone at every size.

   `word-break: break-all` is deliberate and already inherited from `.kv .v`: a hex address has
   no break opportunities at all, so without it the single 42-character word overflows instead
   of wrapping. It breaks anywhere, which is exactly right for a string with no words in it. */
.kv .v.kv-action > span[id^="settings-"] {
    font-family: "JetBrains Mono", monospace;
    max-width: 21ch;
}

.copy-btn {
    background: rgba(0, 240, 255, 0.12);
    border: 1px solid rgba(0, 240, 255, 0.3);
    border-radius: 8px;
    width: 32px;
    height: 32px;
    flex: 0 0 auto;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    color: var(--cyan);
    cursor: pointer;
    padding: 0;
    transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease;
}
.copy-btn svg { width: 16px; height: 16px; }
.copy-btn:hover { background: rgba(0, 240, 255, 0.22); }
/* The tick: same geometry, greener, and it holds for 2s (js/settings.js). */
.copy-btn.copied {
    background: rgba(34, 197, 94, 0.2);
    border-color: var(--green);
    color: var(--green);
}

.link-row { display: flex; gap: 12px; margin-top: 8px; }
.link-row .btn-ghost { flex: 1; text-align: center; text-decoration: none; display: block; }

/* ── Language picker modal (js/settings.js) ────────────────────────────────────
   Copied from `barkx-pool-interface`'s settings page — the same column of full-width
   rows, the same cyan border on hover and on the active row — so the two products'
   pickers are one design rather than two that happen to look similar.

   The card itself carries no rule here: everything that makes a modal a modal lives on
   `.modal-layer` (`Toast.overlay()`) and the card's own inline geometry, which is how
   every other modal in this product is built. Only what is specific to this list is
   here. */
.lang-modal-title {
    border: none;
    padding-bottom: 0;
    margin-bottom: 16px;
    padding-right: 24px;
}
.lang-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    /* Ten rows plus a title do not fit a phone screen, and this card is centred rather
       than top-aligned, so without a cap the first and last rows would be unreachable. */
    max-height: 300px;
    overflow-y: auto;
    padding-right: 4px;
}
.lang-list::-webkit-scrollbar { width: 4px; }
.lang-list::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.2);
    border-radius: 4px;
}
.lang-item {
    padding: 12px 16px;
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid var(--border-dark);
    border-radius: 12px;
    color: var(--text-primary);
    cursor: pointer;
    font-size: 14px;
    transition: 0.3s;
    text-align: center;
}
.lang-item:hover:not(:disabled),
.lang-item.active {
    border-color: var(--cyan);
    background: rgba(56, 189, 248, 0.1);
    color: var(--cyan);
}
/* A language with no catalogue yet. `:disabled` alone would only be a cursor change —
   the row still has to READ as unavailable at a glance, or a user taps it, gets nothing,
   and files it as a broken button rather than as a translation that has not landed. The
   `title` attribute carries the sentence; these two declarations carry the look. */
.lang-item:disabled {
    opacity: 0.45;
    cursor: not-allowed;
    color: var(--text-muted);
}

/* ── Position chart (spec-chart §6) ──────────────────────────────────────────────────────────
   The SVG carries no colours of its own: every fill and stroke below is a token, so the chart
   moves with the palette and a colour can never be edited in one of the two places. */
.pos-chart {
    width: 100%;
    height: auto;
    display: block;
}
/* The plot is a pure black square, NOT `--bg-card`: that token is semi-transparent, and a gold
   line over a translucent panel picks up whatever is behind the modal. */
.chart-bg {
    fill: #000;
}
.chart-curve {
    fill: none;
    stroke: var(--gold);
    stroke-width: 1.6;
    stroke-linejoin: round;
    stroke-linecap: round;
}
/* The live end of the curve. The glow is what makes it read as "now" rather than as one more
   vertex, which matters on a line that is otherwise uniform. */
.chart-tip {
    fill: var(--gold);
    filter: drop-shadow(0 0 4px var(--gold-glow));
}
/* The open price. Thin and cyan, per the proposal — it is a reference, not a reading, so it must
   not compete with the curve. */
.chart-mid {
    stroke: var(--cyan);
    stroke-width: 0.8;
    opacity: 0.75;
}
.chart-axis {
    stroke: var(--border-dark);
    stroke-width: 1;
}
.chart-tick {
    stroke: var(--border-dark);
    stroke-width: 1;
}
/* The two dashed leaders from the live dot to the two axes. Their colour is the answer to "is
   this position winning right now", so it is assigned by class from `chart.js` and defined here
   in the same two tokens the card's `Price Gap` uses. */
.chart-guide {
    stroke-width: 0.9;
    stroke-dasharray: 3 3;
}
.chart-guide.right {
    stroke: var(--green);
}
.chart-guide.wrong {
    stroke: var(--red);
}
.chart-countdown {
    fill: var(--text-secondary);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
}
/* The sentence under the chart. It is the accessible description of the drawing as well as copy
   a sighted reader wants, which is why it is real text in the modal rather than a tooltip.

   Monospace, and only here (task 2026-09-20) — the selector exists nowhere else in the product,
   so "only this modal" needs no extra scoping. The stack is the one `style.css` already imports
   and the rest of this file already uses for figures; a second family would mean a second font
   to download for one paragraph. JetBrains Mono carries no CJK, so a Chinese reader gets the
   generic `monospace` for the characters and the metric face for the numbers and the ticker,
   which is the part of this sentence that reads as data.

   `overflow-wrap` is not decoration: a monospace line cannot be condensed, and the longest
   unbreakable run here is a formatted price against a 412px content box on a phone. */
.chart-summary {
    margin-top: 14px;
    color: var(--text-secondary);
    font-family: "JetBrains Mono", monospace;
    font-size: 13px;
    line-height: 1.6;
    overflow-wrap: anywhere;
}
/* The comparison the position turns on — `rises over` / `drops below` — and the only emphasis in
   the sentence. Gold because it is the chart's own accent and carries no verdict: green and red
   are spoken for by the guides and by the card's `Price Gap`, where they mean "winning right now",
   and this phrase states a CONDITION rather than a state. Using either of them here would claim an
   outcome the sentence is careful not to claim. */
.chart-emph {
    color: var(--gold);
    font-weight: 700;
}

/* ── The win poster (task 2026-09-21, spec-poster §5.2/§5.3) ──────────────────────────────────

   No card. Every other modal in this product opens a bordered, titled box with a close button;
   this one is a picture with two buttons under it, and the proposal says so — no title, no
   background, no border, no `✕`. The buttons read as floating because the container draws nothing
   and their own styles (`btn-page` / `btn-ghost`) are the only chrome.

   The canvas is 600 × 800 in its backing store and 300 × 400 here, and both numbers are fixed
   rather than responsive: this element's size is the POSTER's, not the viewport's. */
.poster-wrap {
    margin: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
}
.poster-canvas {
    width: 300px;
    height: 400px;
    display: block;
    border-radius: 0;
}
.poster-actions {
    display: flex;
    gap: 12px;
    margin-top: 16px;
}
