/* ---------------------------------------------------------------------------
   report.css — styling for documents produced by renderReport() (lib/report.tsx).

   The page is two halves and they share one vocabulary:

     <section class="poster">   the card — renderReport() owns this markup
                                outright. Sized to be screenshotted into a
                                LinkedIn post: masthead, then the opportunity
                                score as its own hero band, then a breakdown
                                row of the three supporting metrics.
     <article class="teardown"> the prose, from renderDoc() — the Report
                                fields (abstract, background, strengths,
                                market_gap, build_complexity, expected_revenue,
                                biggest_challenges_for_solo_builder, ai_angle,
                                conclusion) concatenated into one Doc so the
                                section counter runs over the whole page.

   renderDoc emits a *wrapper div* and inside it a very small set of tags and
   class names:

       <h1> <h2> <p> <ol> <li> <span> <a> <b>   <figure class="quote">
       <table> <tr> <td>       <div class="row">       <div class="card">
       <div class="chart"> <div class="chart-group"> <span class="chart-bar">

   Hence `.teardown > div > h2` rather than `body > h2`: the wrapper is the
   page, and apart from `a`, `figure.quote` and `.card` there are no hooks, so
   every other visual distinction in that half is derived from structure —
   position in the tree, sibling order.

     .teardown > div > h1        page title      .card > h1      stat figure
     .teardown > div > h1 + p    lead paragraph  .card > h1 + p  stat caption
     .teardown > div > h2        numbered section .card > h2     card label
     .teardown > div > ol        prose list      .card table     aligned data

   Prose lists and data tables are different tags, so there's no clash: `ol`
   is always prose, `table` is always data — unlike the old workaround this
   replaced, an unclassed `ol` pretending to be an aligned readout.

   A `.row` is a horizontal band. A `.card` is a labeled (or, for a stat,
   unlabeled) box — nest a few cards inside a row to lay them out side by
   side, or drop a row inside a card to lay two things (say a `table` and a
   `chart`) out horizontally within it. `Doc` has no dedicated stat element —
   a "stat" is just a `card` with no label, holding an `h1` figure and a `p`
   caption. `chart` carries numbers only, no labels, so it always needs a
   caption or a neighboring `table` to say what the bars are.
--------------------------------------------------------------------------- */

:root {
    color-scheme: light dark;

    --paper: #fbfaf7;
    --surface: #ffffff;
    --ink: #14140f;
    --ink-2: #56564c;
    --ink-3: #6e6d63;
    --rule: rgba(20, 20, 15, 0.11);
    --rule-soft: rgba(20, 20, 15, 0.06);
    --accent: #1f6b4a;
    --accent-soft: rgba(31, 107, 74, 0.09);
    --track: rgba(20, 20, 15, 0.16);
    --good: #1f6b4a;
    --good-soft: rgba(31, 107, 74, 0.10);
    --critical: #a6321f;
    --critical-soft: rgba(166, 50, 31, 0.10);

    /* Noto ships with the system on Linux and Android; the rest are fallbacks,
       so an icon costs no network request and degrades to the platform's own. */
    --emoji: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", "Twemoji Mozilla", sans-serif;
    --serif: ui-serif, Georgia, "Iowan Old Style", "Palatino Linotype", serif;
    --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

@media (prefers-color-scheme: dark) {
    :root:where(:not([data-theme="light"])) {
        --paper: #100f0d;
        --surface: #1a1917;
        --ink: #f4f2ec;
        --ink-2: #b0aea3;
        --ink-3: #858177;
        --rule: rgba(244, 242, 236, 0.13);
        --rule-soft: rgba(244, 242, 236, 0.07);
        --accent: #5fbc8c;
        --accent-soft: rgba(95, 188, 140, 0.12);
        --track: rgba(244, 242, 236, 0.18);
        --good: #5fbc8c;
        --good-soft: rgba(95, 188, 140, 0.14);
        --critical: #e8785f;
        --critical-soft: rgba(232, 120, 95, 0.14);
    }
}

:root[data-theme="dark"] {
    --paper: #100f0d;
    --surface: #1a1917;
    --ink: #f4f2ec;
    --ink-2: #b0aea3;
    --ink-3: #858177;
    --rule: rgba(244, 242, 236, 0.13);
    --rule-soft: rgba(244, 242, 236, 0.07);
    --accent: #5fbc8c;
    --accent-soft: rgba(95, 188, 140, 0.12);
    --track: rgba(244, 242, 236, 0.18);
    --good: #5fbc8c;
    --good-soft: rgba(95, 188, 140, 0.14);
    --critical: #e8785f;
    --critical-soft: rgba(232, 120, 95, 0.14);
}

* {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

/* Flex column filling the viewport so the footer sits at the bottom of the
   screen on short pages (an empty feed, /about) instead of right under the
   content — `main` is the only child that grows, so on tall pages (a long
   report, a full feed) it pushes the footer below the fold exactly as far as
   the content requires. `100dvh` (falls back to `100vh` where unsupported)
   accounts for mobile browser chrome so the footer isn't hidden behind it. */
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
    max-width: 1060px;
    margin: 0 auto;
    padding: 44px 24px 112px;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--sans);
    font-size: 18px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

main {
    flex: 1 0 auto;
}

/* ===========================================================================
   THE POSTER
   One framed block, cropped straight out of the page as the social image. It
   is deliberately denser than the teardown: at thumbnail size the type scale
   is doing the work, not the prose.
=========================================================================== */

.poster {
    padding: 26px 26px 24px;
    border: 1px solid var(--rule);
    background:
        radial-gradient(120% 90% at 100% 0%, var(--accent-soft), transparent 55%),
        linear-gradient(180deg, var(--surface), var(--paper) 160%);
}

/* --- foot: rating trend + installs, categories + report date ----------------
   <div class="poster-head">
     <div class="rating-trend" data-tone>          — see below, installs
       tucked into .rating-trend-figures alongside lifetime/now since it's
       the same kind of trailing-history figure
     <div class="poster-head-meta">
       <ul class="categories"><li class="category">…</li></ul>
       <time class="report-date">
     </div>
   </div>
   A row spanning the card's full width, closing it out below the score
   breakdown — the rating trend (with installs) sits on the left, categories
   stacked above the date sit on the right, `space-between` leaving the
   middle deliberately empty rather than centering a third element there.
*/

.poster-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    column-gap: 20px;
    row-gap: 14px;
    margin-top: 22px;
    padding-top: 18px;
    border-top: 1px solid var(--rule);
}

.poster-head-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
}

.categories {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 6px;
    list-style: none;
    margin: 0;
    padding: 0;
}

.category {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    background: var(--accent-soft);
    padding: 4px 10px;
}

.category a {
    position: relative;
    z-index: 2;
    color: var(--ink-2);
    text-decoration: none;
    transition: none;
}

.category a:hover {
    background: none;
    text-decoration: underline;
}

.installs {
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    color: var(--ink-3);
}

.report-date {
    font-size: 15px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}

/* --- masthead --------------------------------------------------------------
   <div class="poster-headline"><a class="poster-icon-link"><img class="poster-icon">
                                 (or a bare <img>, on the feed) <h1>
   `--headline` is the one place the title's type scale is set; the icon reads
   its size off it (a multiple of the headline font-size) so the two stay in
   lockstep instead of two numbers that can drift — `.feed-card .poster-headline`
   overrides just the variable to shrink both together at feed scale.
*/

.poster-headline {
    display: flex;
    align-items: center;
    gap: 18px;
    margin-bottom: 18px;
    --headline: clamp(32px, 4.8vw, 50px);
}

.poster-icon {
    display: block;
    width: calc(var(--headline) * 2.2);
    height: calc(var(--headline) * 2.2);
    background: var(--surface);
    object-fit: cover;
}

.poster-icon-link {
    flex: 0 0 auto;
    line-height: 0;
}

.poster-headline h1 {
    font-family: var(--serif);
    font-size: var(--headline);
    line-height: 1.05;
    letter-spacing: -0.025em;
    font-weight: 500;
    margin: 0;
    max-width: 21ch;
    text-wrap: balance;
}

/* the offer — one sentence, and the loudest line after the title. `tagline`
   wraps a single-element renderDoc() output, so the div's own font settings
   cascade into the `p` it contains once the browser margin on `p` is zeroed. */
.poster .tagline {
    font-family: var(--serif);
    font-size: clamp(18px, 2.3vw, 23px);
    line-height: 1.4;
    font-weight: 500;
    color: var(--accent);
    text-wrap: pretty;
    margin: 0 0 30px;
    max-width: 46ch;
}

.poster .tagline p {
    margin: 0;
}

.poster .tagline b {
    font-weight: 700;
}

/* --- hero score: opportunity_score pulled out above its own breakdown, on
   the schema's fixed 1-5 scale — the single number a scroller decides on
   before reading the three supporting cards underneath it. */

.hero-score {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 22px;
    padding: 20px 28px;
    margin-bottom: 18px;
    background: var(--accent-soft);
    border: 1px solid var(--rule);
}

.hero-score[data-tone="good"] {
    background: var(--good-soft);
}

.hero-score[data-tone="bad"] {
    background: var(--critical-soft);
}

.hero-score-ring {
    position: relative;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 104px;
    aspect-ratio: 1;
    border-radius: 50%;
    background: conic-gradient(var(--ink-3) calc(var(--v) * 360deg), var(--track) 0);
}

.hero-score[data-tone="good"] .hero-score-ring {
    background: conic-gradient(var(--good) calc(var(--v) * 360deg), var(--track) 0);
}

.hero-score[data-tone="bad"] .hero-score-ring {
    background: conic-gradient(var(--critical) calc(var(--v) * 360deg), var(--track) 0);
}

.hero-score-ring::before {
    content: "";
    position: absolute;
    inset: 9px;
    border-radius: 50%;
    background: var(--surface);
}

/* the number and its /5 as one baseline-aligned group, so they read as a
   fraction instead of two independently-centered pieces */
.hero-score-ring>span {
    position: relative;
    display: inline-flex;
    align-items: baseline;
    gap: 2px;
}

.hero-score-ring strong {
    font-size: 38px;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.hero-score[data-tone="good"] .hero-score-ring strong {
    color: var(--good);
}

.hero-score[data-tone="bad"] .hero-score-ring strong {
    color: var(--critical);
}

.hero-score-ring small {
    font-size: 14px;
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
}

.hero-score-label {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.hero-score-label span {
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink);
}

.hero-score-label em {
    font-style: normal;
    font-size: 12.5px;
    color: var(--ink-3);
}

/* --- scores: the breakdown — three cards below the hero score. Expected MRR
   is the punchline of the whole poster, so its column runs wider than the
   other two rather than splitting the row evenly. */

.scores {
    display: grid;
    grid-template-columns: 1fr 1fr 1.4fr;
    gap: 16px;
}

/* --- rating trend: leftmost item in the card foot, installs riding along ----
   <div class="rating-trend" data-tone>
     <svg class="rating-trend-chart"><polyline class="rating-trend-line"></svg>
     <div class="rating-trend-figures">
       <span class="rating-trend-lifetime">4.4★ lifetime</span>
       <span class="rating-trend-current">2.9★ now</span>
       <span class="installs">1.2M+ installs</span>
     </div>
   </div>
   A fixed 0-5 domain line (see RatingTrend in lib/report.tsx) — the first
   point is the app's lifetime score, the last is the current trailing
   quarterly mean. `data-tone` compares those two endpoints, not either
   figure's own scale, matching how `.hero-score` and `.score` set tone.
*/

.rating-trend {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
}

.rating-trend-chart {
    display: block;
    width: 108px;
    height: 30px;
    overflow: visible;
}

.rating-trend-line {
    fill: none;
    stroke: var(--ink-3);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
}

.rating-trend[data-tone="good"] .rating-trend-line {
    stroke: var(--good);
}

.rating-trend[data-tone="bad"] .rating-trend-line {
    stroke: var(--critical);
}

.rating-trend-figures {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 7px;
    font-variant-numeric: tabular-nums;
}

.rating-trend-lifetime {
    font-size: 11px;
    color: var(--ink-3);
}

.rating-trend-current {
    font-size: 13px;
    font-weight: 600;
    color: var(--ink);
}

.rating-trend[data-tone="good"] .rating-trend-current {
    color: var(--good);
}

.rating-trend[data-tone="bad"] .rating-trend-current {
    color: var(--critical);
}

/* ===========================================================================
   THE TEARDOWN
=========================================================================== */

.teardown {
    display: block;
    margin-top: 58px;
}

.teardown>div {
    counter-reset: section;
}

/* --- sections ------------------------------------------------------------ */

.teardown>div>h2 {
    counter-increment: section;
    position: relative;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--ink);
    margin: 56px 0 18px;
    padding-top: 18px;
    border-top: 1px solid var(--rule);
}

.teardown>div>h2:first-child {
    margin-top: 0;
}

.teardown>div>h2::before {
    content: counter(section, decimal-leading-zero);
    position: absolute;
    top: 18px;
    right: 0;
    font-family: var(--mono);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--ink-3);
}

.teardown>div>p {
    max-width: 68ch;
    margin: 0 0 16px;
    color: var(--ink-2);
    text-wrap: pretty;
}

/* the lede: full-ink contrast is what marks it as the lead, so it must read
   at least as large as the muted body it introduces, not smaller */
.teardown>div>h2+p {
    color: var(--ink);
    font-size: 19px;
    line-height: 1.5;
}

/* the last paragraph of the document is the colophon */
.teardown>div>p:last-child {
    margin-top: 64px;
    padding-top: 18px;
    border-top: 1px solid var(--rule);
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--ink-3);
    max-width: 62ch;
}

/* --- inline runs --------------------------------------------------------- */
/*
   `p` holds `span`, `b` and `a` kids. `span` carries no variant of any kind, so
   there is nothing to key emphasis off and it stays transparent — splitting a
   paragraph changes the DOM and not the page.
*/

a {
    color: inherit;
    text-decoration: underline;
    text-decoration-color: var(--accent);
    text-decoration-thickness: 1.5px;
    text-underline-offset: 3px;
    border-radius: 2px;
    transition: background-color .12s ease;
}

a:hover {
    background: var(--accent-soft);
    text-decoration-color: currentColor;
}

.teardown b {
    font-weight: 600;
    color: var(--ink);
}

/* in the colophon, links and emphasis recede with everything else */
.teardown>div>p:last-child a {
    text-decoration-thickness: 1px;
    text-decoration-color: var(--ink-3);
}

.teardown>div>p:last-child b {
    color: inherit;
}

/* --- prose lists --------------------------------------------------------- */

.teardown>div>ol:not([class]) {
    list-style: none;
    counter-reset: item;
    padding: 0;
    margin: 24px 0 8px;
    max-width: 74ch;
}

.teardown>div>ol:not([class])>li {
    counter-increment: item;
    position: relative;
    padding: 14px 0 14px 46px;
    border-top: 1px solid var(--rule-soft);
    color: var(--ink-2);
    text-wrap: pretty;
}

.teardown>div>ol:not([class])>li:last-child {
    border-bottom: 1px solid var(--rule-soft);
}

.teardown>div>ol:not([class])>li::before {
    content: counter(item, decimal-leading-zero);
    position: absolute;
    left: 0;
    top: 16px;
    font-family: var(--mono);
    font-size: 11.5px;
    font-weight: 600;
    color: var(--accent);
}

/* ===========================================================================
   SHARED COMPONENTS — used by both halves
=========================================================================== */

/* --- score rings -----------------------------------------------------------
   <div class="score" data-tone --v><i>icon</i><strong>value</strong><small>/5</small>
                                    <span>label</span><em>hint</em>
   The ring is a conic-gradient masked into a donut; --v is always value / 5,
   the schema's fixed scale, never the value's own maximum. `data-tone` is set
   by renderReport() per card. Only opportunity_score and market_gap_score draw
   this way — build_time_weeks and expected_mrr_usd have no fixed 1-5 domain,
   so they render as `.metric` instead, below.
*/

.score {
    display: grid;
    grid-template-columns: auto auto;
    justify-content: center;
    justify-items: center;
    align-content: start;
    gap: 10px 5px;
    padding: 18px 16px 16px;
    background: var(--surface);
    border: 1px solid var(--rule);
    text-align: center;
}

.score::before {
    content: "";
    grid-area: 1 / 1 / 2 / 3;
    width: 96px;
    aspect-ratio: 1;
    border-radius: 50%;
    background: conic-gradient(var(--ink-3) calc(var(--v) * 360deg), var(--track) 0);
    -webkit-mask: radial-gradient(closest-side, #0000 70%, #000 71%);
    mask: radial-gradient(closest-side, #0000 70%, #000 71%);
}

.score[data-tone="good"]::before {
    background: conic-gradient(var(--good) calc(var(--v) * 360deg), var(--track) 0);
}

.score[data-tone="bad"]::before {
    background: conic-gradient(var(--critical) calc(var(--v) * 360deg), var(--track) 0);
}

.score i {
    grid-area: 1 / 1 / 2 / 3;
    align-self: center;
    font-style: normal;
    font-family: var(--emoji);
    font-size: 32px;
    line-height: 1;
}

.score strong {
    grid-area: 2 / 1;
    align-self: baseline;
    font-size: 22px;
    line-height: 1;
    letter-spacing: -0.02em;
    font-weight: 550;
    font-variant-numeric: tabular-nums;
}

.score small {
    grid-area: 2 / 2;
    align-self: baseline;
    font-size: 12px;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    color: var(--ink-3);
}

.score[data-tone="good"] strong {
    color: var(--good);
}

.score[data-tone="bad"] strong {
    color: var(--critical);
}

.score span {
    grid-area: 3 / 1 / 4 / 3;
    margin-top: 2px;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-2);
}

.score em {
    grid-area: 4 / 1 / 5 / 3;
    font-style: normal;
    font-size: 11px;
    color: var(--ink-3);
}

/* --- metric cards ----------------------------------------------------------
   <div class="metric" data-tone><i>icon</i><strong>figure</strong><small>unit</small>
                                  <span>label</span><em>hint</em>
   Same box and grid as `.score`, minus the ring: build_time_weeks and
   expected_mrr_usd carry their own units instead of a fraction of 5.
*/

.metric {
    display: grid;
    grid-template-columns: auto auto;
    justify-content: center;
    justify-items: center;
    align-content: start;
    gap: 10px 5px;
    padding: 18px 16px 16px;
    background: var(--surface);
    border: 1px solid var(--rule);
    text-align: center;
}

/* same 96px footprint as .score::before, so a metric card without a ring
   still lines up with the ring cards beside it in the .scores row */
.metric i {
    grid-area: 1 / 1 / 2 / 3;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 96px;
    height: 96px;
    font-style: normal;
    font-family: var(--emoji);
    font-size: 32px;
    line-height: 1;
}

.metric strong {
    grid-area: 2 / 1;
    align-self: baseline;
    font-size: 22px;
    line-height: 1;
    letter-spacing: -0.02em;
    font-weight: 550;
    font-variant-numeric: tabular-nums;
}

.metric small {
    grid-area: 2 / 2;
    align-self: baseline;
    font-size: 12px;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    color: var(--ink-3);
}

.metric[data-tone="good"] strong {
    color: var(--good);
}

.metric[data-tone="bad"] strong {
    color: var(--critical);
}

.metric span {
    grid-area: 3 / 1 / 4 / 3;
    margin-top: 2px;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-2);
}

.metric em {
    grid-area: 4 / 1 / 5 / 3;
    font-style: normal;
    font-size: 11px;
    color: var(--ink-3);
}

@media (max-width: 560px) {

    /* the poster is sized to be screenshotted at desktop scale; on a phone
       screen the same paddings and fixed-px elements (104px ring, 96px score
       circles) eat most of the viewport before any content shows, so shrink
       them in lockstep rather than just reflowing the grid below. This block
       must come after the .score/.metric/.rating-trend-chart base rules
       above — equal-specificity selectors resolve by source order, so an
       unconditional base rule appearing later would otherwise win over this
       media query at every viewport, not just wide ones. */
    .poster {
        padding: 18px 16px 16px;
    }

    .poster-headline {
        gap: 12px;
        margin-bottom: 12px;
        --headline: clamp(22px, 6vw, 28px);
    }

    .poster .tagline {
        margin-bottom: 18px;
    }

    .hero-score {
        gap: 14px;
        padding: 14px 16px;
        margin-bottom: 12px;
    }

    .hero-score-ring {
        width: 76px;
    }

    .hero-score-ring strong {
        font-size: 28px;
    }

    .hero-score-label span {
        font-size: 12px;
    }

    .scores {
        grid-template-columns: repeat(1, minmax(0, 1fr));
        gap: 10px;
    }

    .score,
    .metric {
        padding: 14px 12px 12px;
    }

    .score::before,
    .metric i {
        width: 72px;
        height: 72px;
    }

    .score i,
    .metric i {
        font-size: 26px;
    }

    .score strong,
    .metric strong {
        font-size: 19px;
    }

    .poster-head {
        margin-top: 14px;
        padding-top: 12px;
    }

    .rating-trend-chart {
        width: 90px;
        height: 25px;
    }
}

/* --- quotes -------------------------------------------------------------- */
/*
   <figure class="quote" data-rating><blockquote>…</blockquote>
     <figcaption><em>★★☆☆☆</em><cite>source</cite></figcaption></figure>
   The stars turn critical at one and two, which is derived from the number
   rather than chosen by whoever wrote the document.
*/

.quote {
    margin: 34px 0;
    padding: 2px 0 2px 28px;
    border-left: 2px solid var(--accent);
    max-width: 60ch;
}

.quote blockquote {
    margin: 0;
    font-family: var(--serif);
    font-size: clamp(19px, 2.4vw, 24px);
    line-height: 1.42;
    letter-spacing: -0.012em;
    color: var(--ink);
    text-wrap: pretty;
}

.quote figcaption {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 14px;
    margin-top: 15px;
}

.quote em {
    font-style: normal;
    font-size: 13px;
    letter-spacing: 0.14em;
    color: var(--ink-3);
}

.quote[data-rating="1"],
.quote[data-rating="2"] {
    border-left-color: var(--critical);
}

.quote[data-rating="1"] em,
.quote[data-rating="2"] em {
    color: var(--critical);
}

.quote[data-rating="4"] em,
.quote[data-rating="5"] em {
    color: var(--good);
}

.quote cite {
    font-style: normal;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-3);
}

/* --- rows: a horizontal band --------------------------------------------- */

.row {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    align-items: stretch;
    margin: 28px 0;
}

.row>* {
    flex: 1 1 220px;
    min-width: 0;
    margin: 0;
}

/* a row nested inside a card is laying two things (say a `table` and a
   `chart`) out side by side within it, not spacing bands of the page, so it
   doesn't want the band's own vertical rhythm */
.card .row {
    margin: 0;
}

/* --- cards: a labeled (or, for a stat, unlabeled) box ---------------------
   <div class="card"><h2>label</h2>…</div> — nest a few inside a `row` to lay
   them out side by side; `.row>*`'s `flex: 1 1 220px` already applies since a
   card is just another row child, so no extra rule is needed for that.
*/

.card {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    gap: 10px;
    padding: 22px 24px 24px;
    background: var(--surface);
    border: 1px solid var(--rule);
}

/* an h1 in a card is a stat figure — a stat is just a card with no label,
   holding an `h1` + `p`, and this is the entire rule that makes it read as
   one. */
.card h1 {
    font-size: clamp(30px, 4.4vw, 42px);
    line-height: 1;
    letter-spacing: -0.03em;
    font-weight: 550;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
    margin: 2px 0 0;
    max-width: none;
}

/* the paragraph after a stat figure is its caption */
.card h1+p {
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-3);
    margin: 0;
    max-width: none;
}

/* an h2 in a card is the card label */
.card h2 {
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--accent);
    margin: 0 0 2px;
    padding: 0;
    border: 0;
}

.card p {
    font-size: 15px;
    line-height: 1.55;
    color: var(--ink-2);
    margin: 0;
    max-width: none;
    text-wrap: pretty;
}

/* an icon inside a label comes off the emoji stack, wherever the label is */
h2 i {
    font-style: normal;
    font-family: var(--emoji);
    font-size: 1.05em;
    margin-right: 8px;
}

/* the first card in a band carries the accent edge */
.row>.card:first-child {
    border-left: 2px solid var(--accent);
}

/* --- tables ---------------------------------------------------------------
   <table><thead><tr><th>…</thead><tbody><tr><td>…</table> — the first row is
   a real header row (`<th scope="col">`), styled bold and ruled off from the
   body. There is no per-row metadata otherwise, so a total/answer row is
   marked by the document itself wrapping that row's cells in `b`, not by CSS
   guessing which row is last.

   Each body row's first cell is a `<th scope="row">` too (for screen readers
   — a row label like "Installs" or "Retained" is a header for that row's data,
   same as a column head), but it's styled identically to `td` below: this is
   an accessibility annotation, not a visual distinction.
*/

table {
    width: 100%;
    border-collapse: collapse;
    margin: 24px 0 8px;
    font-size: 14px;
}

th,
td {
    padding: 9px 16px 9px 0;
    border-bottom: 1px solid var(--rule-soft);
    font-variant-numeric: tabular-nums;
    text-align: left;
}

td,
tbody th {
    color: var(--ink-2);
    font-weight: 400;
}

tbody tr:last-child th,
tbody tr:last-child td {
    border-bottom: 0;
}

thead th {
    padding-top: 0;
    font-weight: 600;
    color: var(--ink);
    border-bottom: 1px solid var(--rule);
}

th:last-child,
td:last-child {
    padding-right: 0;
}

th:not(:first-child),
td:not(:first-child) {
    text-align: right;
}

.card table {
    margin: 6px 0 2px;
    font-size: 13px;
}

.card th,
.card td {
    padding: 7px 12px 7px 0;
}

/* --- charts -----------------------------------------------------------------
   <div class="chart"><div class="chart-group"><span class="chart-bar" data-last --v>
   Grouped bars, one axis: --v is value divided by the largest value anywhere
   in the chart (lib/doc.tsx computes it), never a group's own maximum, so
   groups stay comparable to each other. There are no labels in the schema, so
   a chart always needs a caption, a heading, or a table beside it to say what
   the bars are. Multiple bars in one group read as a trailing run at that
   category, not distinct series — they share one hue, and only the most
   recent bar per group carries full weight.
*/

.chart {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    height: 108px;
    margin: 24px 0 8px;
    padding-bottom: 1px;
    border-bottom: 1px solid var(--rule);
}

.chart-group {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 3px;
    flex: 1 1 0;
    height: 100%;
    min-width: 0;
}

.chart-bar {
    display: block;
    flex: 1 1 0;
    max-width: 28px;
    min-width: 4px;
    height: max(calc(var(--v, 0) * 100%), 3px);
    background: var(--accent);
    opacity: .35;
}

.chart-bar[data-last="true"] {
    opacity: 1;
}

.card .chart {
    height: auto;
    margin: 4px 0 2px;
}

/* --- print --------------------------------------------------------------- */

@media print {
    :root {
        --paper: #fff;
        --surface: #fff;
    }

    body {
        padding: 0;
        max-width: none;
    }

    /* the poster is page one on its own */
    .poster {
        break-after: page;
        border: 0;
        padding: 0;
        background: none;
    }

    .card {
        break-inside: avoid;
    }

    .teardown>div>h2 {
        break-after: avoid;
        margin-top: 34px;
    }
}