/* ===========================================================================
   Intrinsic Value tab.

   A company page that happens to lead with a valuation: price chart, headline
   statistics, the four models, the assumptions behind them, then the reference
   data and the metrics the models were built from.

   Colour discipline, because this page has more numbers than any other:
     · green / amber / red mean ONLY undervalued / fair / overvalued, plus the
       good/bad state of a metric tile. Nothing decorative uses them.
     · cyan belongs to intrinsic value and nothing else.
     · the four model tints (--val-s1..s4) are a validated categorical set for
       this dark surface - worst adjacent pair ΔE 16.0 deutan, 19.7 normal
       vision, all above 3:1 contrast. They avoid green and red on purpose so
       a series colour can never be read as a verdict. Tints come from the
       server (MODEL_TINTS) so the card, the blend segment and the legend
       cannot disagree.

   Tokens are the dashboard's own, from base.css. Rendered by
   static/js/dashboard/valuation-tab.js.
   =========================================================================== */

.val-tab {
    display: flex;
    flex-direction: column;
    /* The page is a stack of separate claims - what it is worth, what it has
       done, what it filed, what the tape says. They need room to read as
       separate rather than as one wall. */
    gap: var(--val-panel-gap);

    /* Derivations, XBRL concept names and figures inside them are identifiers
       and arithmetic to be checked line by line, not prose. Scoped to the tab
       so nothing else inherits it. */
    --val-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;

    /* The space between cards, wherever they are gridded. One value, so
       the model cards, the metric tiles and the reported figures are
       separated by the same amount and read as the same kind of thing. */
    --val-card-gap: 16px;

    /* And between the panels themselves. */
    --val-panel-gap: 26px;
}

/* The panels live in here, not directly in .val-tab - so the tab's own gap
   separates the header from the body and nothing else. Without this every
   panel on the page stacks flush against the one above it, which is what
   made the whole thing read as a single wall of boxes. */
#val-body {
    display: flex;
    flex-direction: column;
    gap: var(--val-panel-gap);
}

/* `hidden` has to win over whatever display a component below sets. An author
   `display: grid` outranks the browser's own [hidden] rule, which is how the
   empty verdict band - dashes, a broken scale, "every weight at zero" - stayed
   on screen underneath the "No fair price" panel that replaced it. */
.val-tab [hidden] { display: none !important; }

/* Shared surfaces. Every panel on this tab is one of these two, so a change
   to the card treatment is one edit rather than nine. */
.val-panel {
    padding: 14px 16px;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius);
}

.val-panel-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.val-panel-head h3 {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

.val-panel-note {
    font-size: 11px;
    color: var(--text-muted);
}

.val-panel-note b {
    color: var(--text-secondary);
    font-weight: 600;
}

/* ---- header: ticker box and provenance ---- */
.val-head {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
}

.val-title {
    margin: 0;
    font-size: 20px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--text-primary);
}

.val-search {
    display: flex;
    gap: 6px;
}

/* One search with an optional second slot under it, rather than three
   controls in a row. The stack sets the width both fields follow, so the
   compare field lines up with the input above it and not with the button. */
.val-search-stack {
    display: flex;
    flex-direction: column;
    gap: 7px;
    width: 232px;
}

.val-search-stack .val-search-box { flex: 1; }
.val-search-stack .val-ticker-input { width: 100%; }

/* ---- ticker autocomplete ----
   Anchored to the input and floated above the page so the tab below does not
   reflow as suggestions appear and disappear on every keystroke. */
.val-search-box { position: relative; }

.val-suggestions {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 40;
    margin: 0;
    padding: 4px;
    list-style: none;
    min-width: 260px;
    max-height: 290px;
    overflow-y: auto;
    background: var(--bg-secondary);
    border: 1px solid var(--border-medium);
    border-radius: var(--radius-xs);
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.55);
}

.val-suggestion {
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 5px 8px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    color: var(--text-secondary);
}

.val-suggestion:hover,
.val-suggestion.is-active {
    background: rgba(16, 185, 129, 0.12);
    color: var(--text-primary);
}

.val-suggestion-ticker {
    flex: 0 0 auto;
    min-width: 46px;
    font-weight: 700;
    font-family: var(--val-mono);
    font-size: 11.5px;
    color: var(--text-primary);
}

.val-suggestion-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 11px;
    color: var(--text-muted);
}

.val-ticker-input {
    width: 150px;
    padding: 6px 10px;
    border: 1px solid var(--border-medium);
    border-radius: var(--radius-xs);
    background: var(--bg-secondary);
    color: var(--text-primary);
    font-family: 'Inter', sans-serif;
    font-size: 12px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.val-ticker-input::placeholder {
    color: var(--text-muted);
    letter-spacing: 0;
    text-transform: none;
}

.val-go {
    padding: 6px 14px;
    border: 1px solid rgba(16, 185, 129, 0.4);
    border-radius: var(--radius-xs);
    background: rgba(16, 185, 129, 0.12);
    color: var(--green);
    font-family: 'Inter', sans-serif;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s ease;
}

.val-go:hover { background: rgba(16, 185, 129, 0.2); }
.val-go[disabled] { opacity: 0.5; cursor: default; }

.val-source {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 11px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.val-source-chip {
    padding: 2px 7px;
    border: 1px solid var(--border-medium);
    border-radius: 4px;
    font-family: var(--val-mono);
    font-size: 10px;
    color: var(--text-secondary);
}

/* ---- status: loading, empty, refused ---- */
.val-status {
    padding: 22px 16px;
    text-align: center;
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--text-muted);
}

.val-status b {
    display: block;
    margin-bottom: 4px;
    color: var(--text-secondary);
    font-size: 13px;
}

/* ---- company line ---- */
.val-company {
    display: flex;
    align-items: flex-end;
    gap: 16px 26px;
    flex-wrap: wrap;
}

.val-co-id {
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.val-co-top {
    display: flex;
    align-items: center;
    gap: 10px;
}

.val-co-sym {
    font-size: 26px;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--text-primary);
}

.val-co-name {
    font-size: 13px;
    color: var(--text-secondary);
}

.val-co-chips {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.val-co-chip {
    padding: 1px 7px;
    border: 1px solid var(--border-subtle);
    border-radius: 3px;
    font-size: 10px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-co-px {
    margin-left: auto;
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.val-co-px-value {
    font-size: 28px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
    color: var(--text-primary);
}

.val-co-px-change { font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Direction colours. Shared by the price change, model upside and tile state
   so "green means better" is one rule on this page. */
.val-up { color: var(--green); }
.val-down { color: var(--red); }
.val-flat { color: var(--text-secondary); }

/* Present only when the price is NOT from the feed - the local store's
   last close, said plainly. A live price needs no label, and the session
   name was noise beside a date the line above already carries. */
.val-co-px-state {
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* ---- chart range ---- */
.val-ranges {
    display: flex;
    gap: 4px;
    justify-content: flex-end;
    margin-bottom: 6px;
    flex-wrap: wrap;
}

.val-range {
    padding: 3px 10px;
    border: 1px solid transparent;
    border-radius: var(--radius-xs);
    background: none;
    font-family: inherit;
    font-size: 11px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    cursor: pointer;
    transition: color 0.12s ease, background 0.12s ease;
}

.val-range:hover { color: var(--text-primary); background: rgba(148, 163, 184, 0.10); }
.val-range:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }

/* The selected range carries the chart's own blue, so the button and the
   line it drew are visibly the same thing. */
.val-range.is-on {
    color: #8fc0f5;
    background: rgba(57, 135, 229, 0.14);
    border-color: rgba(57, 135, 229, 0.38);
}

/* ---- price chart ---- */
.val-chart-wrap { position: relative; }
.val-chart-svg { display: block; width: 100%; }

/* Dims the chart while a different range is in flight rather than clearing
   it: five years takes a moment to arrive, and a blank panel in the middle
   of the page reads as a failure. */
.val-chart-busy {
    position: absolute;
    inset: 0;
    background: rgba(10, 14, 23, 0.45);
    border-radius: var(--radius-xs);
    pointer-events: none;
}

.val-chart-tip {
    position: absolute;
    top: 6px;
    padding: 5px 8px;
    border-radius: var(--radius-xs);
    background: rgba(7, 10, 17, 0.94);
    border: 1px solid var(--border-medium);
    font-size: 11px;
    line-height: 1.35;
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary);
    pointer-events: none;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.1s ease;
}

.val-chart-tip b { color: var(--text-primary); font-weight: 600; }
.val-chart-tip.is-on { opacity: 1; }

/* ---- tiles: statistics and metrics are the same component ---- */
.val-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--val-card-gap);
}

.val-tiles.is-metrics { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

.val-tile {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: end;
    gap: 2px 10px;
    padding: 10px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
}

.val-tiles.is-metrics .val-tile { background: var(--bg-secondary); }

.val-tile-label {
    grid-column: 1 / -1;
    font-size: 10px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-tile-label abbr {
    text-decoration: none;
    border-bottom: 1px dotted var(--border-medium);
    cursor: help;
}

.val-tile-value {
    font-size: 19px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.015em;
    color: var(--text-primary);
}

.val-tiles.is-metrics .val-tile-value { font-size: 18px; }

/* A tile's colour is a claim, so it is only made where the metric has a
   direction - see _state_for. "missing" is muted, never red. */
.val-tile-value.is-good { color: var(--green); }
.val-tile-value.is-bad { color: var(--red); }
.val-tile-value.is-missing { color: var(--text-muted); font-size: 15px; }

.val-spark { display: block; }

/* ---- verdict band ---- */
.val-verdict {
    display: grid;
    grid-template-columns: minmax(170px, 230px) 1fr;
    gap: 20px 28px;
    padding: 16px 18px 16px 20px;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-left: 3px solid var(--val-stripe, var(--text-muted));
    border-radius: var(--radius);
}

.val-verdict.is-undervalued { --val-stripe: var(--green); --val-stripe-bg: rgba(16, 185, 129, 0.12); }
.val-verdict.is-fair { --val-stripe: var(--amber); --val-stripe-bg: rgba(245, 158, 11, 0.12); }
.val-verdict.is-overvalued { --val-stripe: var(--red); --val-stripe-bg: rgba(239, 68, 68, 0.12); }

.val-verdict-top {
    display: flex;
    align-items: center;
    gap: 10px;
}

.val-verdict-h {
    margin: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}

.val-badge {
    padding: 3px 9px;
    border-radius: 999px;
    border: 1px solid var(--val-stripe, var(--border-medium));
    background: var(--val-stripe-bg, transparent);
    color: var(--val-stripe, var(--text-secondary));
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    white-space: nowrap;
}

.val-figures {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));
    gap: 14px 20px;
    align-content: center;
}

.val-fig { display: flex; flex-direction: column; gap: 2px; }

.val-fig-label {
    font-size: 10px;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-fig-label abbr {
    text-decoration: none;
    border-bottom: 1px dotted var(--border-medium);
    cursor: help;
}

.val-fig-value {
    font-size: 22px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.015em;
    color: var(--text-primary);
}

.val-fig-value.is-intrinsic { color: var(--cyan); }
.val-fig-sub { font-size: 10px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* The answer in words, spanning the band. Larger than the figures' captions
   on purpose: it is the sentence most readers will stop at. */
.val-reading {
    grid-column: 1 / -1;
    margin: 0;
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--text-primary);
    max-width: 92ch;
}

.val-reading:empty { display: none; }

/* No model fits the company. Amber rather than red: nothing failed. */
.val-no-valuation { border-color: rgba(245, 158, 11, 0.3); }

.val-badge-plain {
    --val-stripe: var(--amber);
    --val-stripe-bg: rgba(245, 158, 11, 0.12);
}

/* price against value, on one scale */
.val-scale { grid-column: 1 / -1; }

.val-scale-track {
    position: relative;
    height: 30px;
    border-radius: var(--radius-xs);
    background: var(--bg-elevated);
    border: 1px solid var(--border-subtle);
}

.val-scale-fill {
    position: absolute;
    inset: 0 auto 0 0;
    border-radius: var(--radius-xs) 0 0 var(--radius-xs);
    background: linear-gradient(90deg, rgba(6, 182, 212, 0.05), rgba(6, 182, 212, 0.18));
    border-right: 1px solid var(--cyan);
}

.val-mark {
    position: absolute;
    top: -4px;
    bottom: -4px;
    width: 2px;
    transform: translateX(-1px);
}

.val-mark.is-buy { background: repeating-linear-gradient(180deg, var(--green) 0 4px, transparent 4px 8px); }
.val-mark.is-price { background: var(--text-primary); box-shadow: 0 0 0 3px rgba(10, 14, 23, 0.85); }

.val-mark-label {
    position: absolute;
    top: -17px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 9px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--text-muted);
}

.val-mark.is-price .val-mark-label { color: var(--text-primary); font-weight: 600; }
.val-mark.is-buy .val-mark-label { color: var(--green); }

.val-scale-foot {
    display: flex;
    justify-content: space-between;
    margin-top: 16px;
    font-size: 10px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* ---- blend: what the headline number is made of ---- */
.val-blend { grid-column: 1 / -1; }

.val-blend-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin-bottom: 6px;
    font-size: 10px;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-blend-head em {
    font-style: normal;
    letter-spacing: 0;
    text-transform: none;
    font-size: 11px;
    color: var(--text-secondary);
}

/* 2px gaps rather than borders: adjacent fills need a surface-coloured
   separator so two segments never read as one. */
.val-blend-bar {
    display: flex;
    gap: 2px;
    height: 22px;
}

.val-blend-seg {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    border-radius: 3px;
    font-family: var(--val-mono);
    font-size: 10px;
    font-weight: 700;
    color: #0b0f17;
    white-space: nowrap;
    overflow: hidden;
}

.val-blend-keys {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 16px;
    margin-top: 7px;
    font-size: 10.5px;
    color: var(--text-muted);
}

.val-blend-key {
    display: flex;
    align-items: center;
    gap: 5px;
}

.val-blend-dot {
    width: 8px;
    height: 8px;
    border-radius: 2px;
    flex: 0 0 auto;
}

.val-blend-key b {
    color: var(--text-secondary);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* ---- model cards ---- */
.val-models {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(292px, 1fr));
    gap: var(--val-card-gap);
    /* Each card keeps its own height. Stretching left a refused model as a
       tall empty box the height of the tallest card beside it, which read as
       a page that had failed to load rather than one that had answered. */
    align-items: start;
}

.val-model {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-top: 2px solid var(--val-tint, var(--border-subtle));
    border-radius: var(--radius);
}

.val-model.is-muted { opacity: 0.5; }

/* A model that could not run. Dimmed, but its figure stays legible: it is
   the evidence for the refusal. */
.val-model.is-refused { opacity: 0.75; }
.val-model.is-refused .val-rows { opacity: 0.85; }

.val-model-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
}

.val-model-name { font-size: 13px; font-weight: 600; color: var(--text-primary); }

.val-model-kind {
    padding: 1px 6px;
    border: 1px solid var(--border-subtle);
    border-radius: 4px;
    font-size: 9px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-model-value {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.val-model-number {
    font-size: 26px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
    color: var(--val-tint, var(--cyan));
}

.val-model-delta { font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums; }

/* A model that could not run says why, in place of its number. */

.val-rows { display: flex; flex-direction: column; gap: 5px; }

.val-row {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 10px;
    font-size: 11px;
}

.val-row-label { color: var(--text-secondary); }

.val-row-value {
    font-family: var(--val-mono);
    font-size: 11px;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

/* The model's share of the headline, as a readout under its figures. */

/* ---- six months out ---- */
.val-proj-figures {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
    gap: 12px 18px;
    margin-bottom: 18px;
}

.val-proj-fig {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-left: 10px;
    border-left: 2px solid var(--val-tint, var(--border-medium));
}

.val-proj-label {
    font-size: 10px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-proj-value {
    font-size: 20px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.015em;
    color: var(--text-primary);
}

.val-proj-sub { font-size: 10px; color: var(--text-muted); }

.val-lanes { position: relative; }

.val-lane-row {
    display: grid;
    grid-template-columns: 148px 1fr;
    align-items: center;
    gap: 12px;
    padding: 7px 0;
}

.val-lane-label { font-size: 10.5px; line-height: 1.35; color: var(--text-secondary); }

.val-lane-label i {
    font-style: normal;
    display: block;
    color: var(--text-muted);
    font-size: 9.5px;
}

.val-lane {
    position: relative;
    height: 26px;
    border-radius: 4px;
    background: rgba(7, 10, 17, 0.6);
    border: 1px solid var(--border-subtle);
}

.val-band {
    position: absolute;
    top: 5px;
    bottom: 5px;
    border-radius: 3px;
    background: var(--val-band, var(--text-muted));
    opacity: 0.55;
}

.val-band-mid {
    position: absolute;
    top: 2px;
    bottom: 2px;
    width: 2px;
    transform: translateX(-1px);
    background: var(--val-band, var(--text-muted));
}

.val-band-cap {
    position: absolute;
    top: 50%;
    transform: translate(-50%, -50%);
    font-family: var(--val-mono);
    font-size: 9.5px;
    color: var(--text-primary);
    white-space: nowrap;
    text-shadow: 0 0 4px rgba(7, 10, 17, 0.95);
}

/* The price line spans every lane, so it lives in an overlay inset by the
   label column rather than trying to offset itself inside a percentage track. */
.val-lane-overlay {
    position: absolute;
    inset: 0 0 22px 160px;
    pointer-events: none;
}

.val-lane-price {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: rgba(226, 232, 240, 0.55);
}

.val-lane-price span {
    position: absolute;
    top: -2px;
    left: 4px;
    font-size: 9px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-primary);
    white-space: nowrap;
}

.val-lane-axis {
    display: flex;
    justify-content: space-between;
    margin-left: 160px;
    padding-top: 4px;
    border-top: 1px solid var(--border-subtle);
    font-size: 9.5px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

/* How the figures were filed. Amber because nothing is wrong - the numbers
   are sound, they are just older and converted, and reading them as current
   is the mistake this prevents. */
.val-reporting {
    margin: 0;
    padding: 10px 12px;
    border-radius: var(--radius-xs);
    background: rgba(245, 158, 11, 0.08);
    border: 1px solid rgba(245, 158, 11, 0.25);
    font-size: 11.5px;
    line-height: 1.55;
    color: #fcd9a0;
}

.val-reporting b { color: var(--amber); }

/* ---- news ----
   Mirrors the vocabulary the rest of the dashboard already uses for a story:
   category and tag chips, a publisher-tier meter, then the articles. */
.val-story { display: flex; flex-direction: column; gap: 9px; }

.val-story-top { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }

.val-tagchip {
    padding: 1px 7px;
    border-radius: 3px;
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    background: rgba(148, 163, 184, 0.13);
    color: var(--text-secondary);
}

.val-story-age {
    margin-left: auto;
    font-size: 10px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.val-story-head { margin: 0; font-size: 14px; font-weight: 600; line-height: 1.35; color: var(--text-primary); }
.val-story-summary { margin: 0; font-size: 12px; line-height: 1.55; color: var(--text-secondary); max-width: 88ch; }

/* The provenance test the app already runs, drawn. Aggregators alone is the
   weak case and is coloured as such rather than hidden. */
.val-meter { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.val-meter-slots { display: flex; gap: 3px; }

.val-meter-slots i {
    width: 26px;
    height: 5px;
    border-radius: 2px;
    background: rgba(148, 163, 184, 0.16);
}

.val-meter-slots i.is-filled { background: var(--green); }
.val-meter-label { font-size: 10.5px; color: var(--text-secondary); cursor: help; }
.val-meter-label b { font-weight: 600; }
.val-meter-label.is-weak b { color: var(--amber); }
.val-meter-label.is-ok b { color: var(--green); }

.val-articles { display: flex; flex-direction: column; gap: 4px; margin-top: 2px; }

.val-article {
    display: grid;
    grid-template-columns: 18px 1fr auto;
    align-items: baseline;
    gap: 10px;
    padding: 4px 0;
    border-top: 1px solid var(--border-subtle);
    font-size: 11px;
}

/* The publisher's own mark. Small, aligned with the headline's first line,
   and hidden rather than broken when the domain has no icon. */
.val-article-icon {
    width: 16px;
    height: 16px;
    border-radius: 3px;
    align-self: center;
    background: rgba(148, 163, 184, 0.10);
}

.val-article-title { color: var(--text-secondary); line-height: 1.4; text-decoration: none; }
.val-article-title:hover { color: var(--green); }
.val-article-publisher { font-size: 10px; color: var(--text-muted); white-space: nowrap; }

@media (max-width: 620px) {
    .val-article { grid-template-columns: 1fr; gap: 2px; }
}

/* ---- about ---- */
.val-about-desc {
    margin: 0 0 14px;
    font-size: 12.5px;
    line-height: 1.65;
    color: var(--text-secondary);
    max-width: 92ch;
}

.val-about-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 14px 20px;
}

.val-about-key {
    font-size: 10px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-about-value {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

/* ---- reported figures ----
   Separate cards rather than one ruled table. The hairline version - a 1px
   gap over a coloured background - drew every figure into a single block
   where the eye had to find the divisions; these are a dozen independent
   readings off a filing, and they read as such with air between them. */
.val-figure-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(188px, 1fr));
    gap: var(--val-card-gap);
}

.val-cell {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 10px 12px;
    background: var(--bg-secondary);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
}

.val-cell-label {
    font-size: 10px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-cell-value {
    font-size: 15px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text-primary);
}

/* The concept behind the figure. Monospace because it is an identifier to be
   checked against the filing, not prose. */
.val-cell-source {
    font-family: var(--val-mono);
    font-size: 9.5px;
    color: var(--text-muted);
    word-break: break-all;
}

.val-chip {
    align-self: flex-start;
    margin-top: 2px;
    padding: 1px 6px;
    border-radius: 3px;
    font-size: 9px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    background: rgba(245, 158, 11, 0.14);
    color: var(--amber);
    cursor: help;
}

.val-warning {
    display: block;
    margin: 0 0 14px;
    padding: 9px 11px;
    border-radius: var(--radius-xs);
    background: rgba(245, 158, 11, 0.08);
    border: 1px solid rgba(245, 158, 11, 0.25);
    font-size: 11.5px;
    line-height: 1.5;
    color: #fcd9a0;
}

.val-foot {
    margin: 0;
    font-size: 11px;
    line-height: 1.6;
    color: var(--text-muted);
    max-width: 86ch;
}

.val-foot b { color: var(--text-secondary); font-weight: 600; }
.val-foot + .val-foot { margin-top: 8px; }

/* ---- narrow screens ---- */
@media (max-width: 760px) {
    .val-verdict { grid-template-columns: 1fr; }
    .val-lane-row { grid-template-columns: 1fr; gap: 4px; }
    .val-lane-axis { margin-left: 0; }
    .val-lane-overlay { display: none; }
    .val-source { margin-left: 0; width: 100%; }
}

/* ===========================================================================
   The empty state

   A reader arriving without a ticker in mind gets the whole panel, not a
   sentence beside a 150px box. One field still - `.is-empty` enlarges and
   centres the one in the header rather than adding a second, which would
   mean two inputs and two places the value is read from.
   =========================================================================== */

.val-tab.is-empty .val-head {
    flex-direction: column;
    align-items: center;
    gap: 18px;
    padding: 40px 16px 8px;
}

.val-tab.is-empty .val-title { font-size: 26px; }

.val-tab.is-empty .val-search-stack {
    width: 100%;
    max-width: 560px;
}

.val-tab.is-empty .val-ticker-input {
    width: 100%;
    padding: 13px 16px;
    font-size: 15px;
    letter-spacing: 0.06em;
}

.val-tab.is-empty .val-go {
    padding: 13px 22px;
    font-size: 13px;
}

/* The list hangs off a field three times the size, so its rows grow with
   it - suggestions sized for the compact header read as a detached
   fragment under the large one. */
.val-tab.is-empty .val-suggestion { padding: 9px 12px; font-size: 13px; }
.val-tab.is-empty .val-suggestion-ticker { min-width: 62px; font-size: 13px; }
.val-tab.is-empty .val-suggestion-name { font-size: 12px; }
.val-tab.is-empty .val-suggestions { max-height: 340px; }

/* The provenance chips read "-" until there is a company, which is an
   empty row of punctuation above the one thing to do. */
.val-tab.is-empty .val-source { display: none; }

.val-tab.is-empty .val-status {
    max-width: 620px;
    margin: 0 auto;
    border: 0;
    background: none;
    font-size: 13px;
    line-height: 1.7;
}

/* ===========================================================================
   Comparison

   A second company through the same six models. The swatch beside each name
   is the only thing tying a row to a line on the chart, so it is never the
   sole carrier of meaning - the ticker sits next to it in text.
   =========================================================================== */

/* Beside the main field rather than down beside the company name: it is
   the same question asked twice, so it reads as one control with two
   slots. Hidden while the page is empty - there is nothing to compare a
   company against before there is a company. */
.val-compare-search {
    display: flex;
    align-items: center;
    gap: 6px;
}

.val-tab.is-empty .val-compare-search { display: none; }

.val-compare-input {
    font-size: 11.5px;
    padding: 5px 9px;
}

.val-compare-clear {
    width: 24px;
    height: 24px;
    padding: 0;
    border: 1px solid var(--border-medium);
    border-radius: var(--radius-xs);
    background: var(--bg-secondary);
    color: var(--text-muted);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
}

.val-compare-clear:hover { color: var(--red); border-color: rgba(239, 68, 68, 0.4); }

.val-compare-head {
    display: flex;
    gap: 26px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.val-compare-who {
    display: inline-flex;
    align-items: baseline;
    gap: 7px;
    font-size: 12px;
    color: var(--text-primary);
}

.val-compare-swatch {
    width: 9px;
    height: 9px;
    border-radius: 2px;
    align-self: center;
}

.val-compare-name {
    font-size: 11px;
    color: var(--text-muted);
    max-width: 240px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.val-compare-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}

.val-compare-table th {
    width: 1%;
    padding: 7px 14px 7px 0;
    text-align: left;
    white-space: nowrap;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.val-compare-cell {
    padding: 7px 18px 7px 0;
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary);
}

.val-compare-table tr + tr th,
.val-compare-table tr + tr .val-compare-cell {
    border-top: 1px solid var(--border-subtle);
}

/* The verdict words carry the page's own three colours, so "undervalued"
   means here exactly what it means on the badge above. */
.val-compare-cell.is-undervalued { color: var(--green); font-weight: 600; }
.val-compare-cell.is-fair { color: var(--amber, #f59e0b); }
.val-compare-cell.is-overvalued { color: var(--red); }

.val-compare-note {
    margin: 0;
    font-size: 12px;
    color: var(--text-muted);
}

@media (max-width: 900px) {
    .val-compare-search { margin-left: 0; width: 100%; }
    .val-compare-input { flex: 1; width: auto; }
}

/* ===========================================================================
   Reading the next company

   The one on screen stays there, dimmed, until the next is ready. Clearing
   to the status panel instead flashed through "enter a ticker" on every
   search made from a result, which reads as the search having failed.
   =========================================================================== */

.val-busy {
    position: sticky;
    top: 0;
    z-index: 30;
    margin-bottom: -10px;
    padding: 7px 12px;
    border: 1px solid rgba(6, 182, 212, 0.3);
    border-radius: var(--radius-xs);
    background: rgba(6, 182, 212, 0.12);
    font-size: 11.5px;
    font-weight: 600;
    color: #a8e4ef;
    text-align: center;
}

/* The bar carries a moving sheen rather than a spinner: it is already the
   full width of the thing being replaced, so the motion belongs to the
   page rather than to a widget sitting on top of it. */
.val-busy::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: linear-gradient(90deg,
        transparent, rgba(255, 255, 255, 0.14), transparent);
    animation: val-sweep 1.1s ease-in-out infinite;
}

@keyframes val-sweep {
    from { transform: translateX(-100%); }
    to { transform: translateX(100%); }
}

/* Dimmed and inert: the figures underneath are still true, but they are
   about to be replaced, so they should not invite a click meanwhile. */
.val-tab.is-busy #val-body {
    opacity: 0.4;
    pointer-events: none;
    transition: opacity 0.18s ease;
}

@media (prefers-reduced-motion: reduce) {
    .val-busy::after { animation: none; }
}
