/* bump.rs: one stylesheet, no build step and no framework. The page is four
   files S3 serves as they are. */

/* Oxide: rust as the metal rather than the mascot. Warm iron neutrals, a
   burnt orange-red running the top of the severity scale, moss at the bottom.

   The accent is deliberately a deeper, browner oxide than `--high` rather than
   the same value. They share a hue -- that is the point of the palette -- but
   on a finding row the source link sits directly beside the gauge, and if the
   two matched exactly then every link on a high finding would read as part of
   its severity. Two steps apart in lightness keeps the family and keeps the
   signal.

   Not Rust's own #CE422B: that is the project's mark, and bump.rs sells a
   paid tier. Borrowing it would read as endorsement.

   The accent is the one cool value in the palette, and that is the point: with
   the ramp, the links, the tags and the buttons all in oxide, the page read as
   a single brown wash and nothing stood out because everything stood out. Warm
   now means severity and cool means "you can act on this" -- so a link never
   competes with a gauge, and the two never have to be told apart by shade.
   Patina, in other words: the same oxidation, further along.

   Every value here clears 4.5:1 on this ground and sits more than 40 RGB apart
   from the accent. That is not decoration: the severity word under the gauge
   is 0.62rem, and since the restyle it is the only place severity is written
   down. */
:root {
  --bg: #f4f2ef;
  --panel: #fdfcfa;
  --sunk: #eeebe6;
  --ink: #1c1917;
  --ink-2: #4a4340;
  --muted: #6f665f;
  --line: #e2ddd7;
  --line-2: #cdc5bd;
  --accent: #1f6f74;
  --accent-2: #2a8a90;
  --accent-ink: #ffffff;

  --critical: #a3230f;
  --high: #b84f1b;
  --medium: #8a6524;
  --low: #4f7355;
  --info: #6f665f;

  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --shadow: 0 1px 2px rgba(20, 18, 14, .05), 0 8px 24px -12px rgba(20, 18, 14, .18);
}

@media (prefers-color-scheme: dark) {
  :root {
    /* The same palette with the roles inverted: neutrals stay warm rather
       than going blue, so the ground reads as iron and not as slate. The
       accent lifts to a lighter oxide here -- on a dark ground the deeper
       burnt tone the light theme uses would not carry as something to click. */
    --bg: #141210;
    --panel: #1c1917;
    --sunk: #191614;
    --ink: #ece7e1;
    --ink-2: #b8afa7;
    --muted: #8b817a;
    --line: #2b2621;
    --line-2: #3d362f;
    --accent: #63c6cf;
    --accent-2: #83dbe2;
    --accent-ink: #0d1a1c;

    --critical: #f2705f;
    --high: #e5744a;
    --medium: #d4a75c;
    --low: #8fc09a;
    --info: #8b817a;
    --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 12px 32px -14px rgba(0, 0, 0, .7);
  }
}

* { box-sizing: border-box; }

/* Beats any author `display` rule. Without it an element with the `hidden`
   attribute stays visible whenever its class sets a display of its own. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* A soft wash behind everything, so the card has something to sit on. */
.backdrop {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(60rem 40rem at 50% -12rem, color-mix(in srgb, var(--accent) 12%, transparent), transparent 70%),
    radial-gradient(40rem 30rem at 88% 8%, color-mix(in srgb, var(--medium) 7%, transparent), transparent 70%);
}

.screen { max-width: 60rem; margin: 0 auto;
          padding: 3.5rem clamp(1.25rem, 4vw, 2.5rem) 5rem; }
#results { max-width: 64rem; padding-top: 0; }

h1, h2, h3 { margin: 0; }
p { margin: 0 0 .6rem; }
p:last-child { margin-bottom: 0; }
code { font-family: var(--mono); font-size: .88em; }
a { color: var(--accent); }
.muted, .hint { color: var(--muted); font-size: .87rem; }

/* ---- brand -------------------------------------------------------------- */

.brand { text-align: center; margin-bottom: 2.25rem; position: relative; }

/* Small and off to one side: it is a joke, not a feature. */
.lang { position: absolute; top: 0; right: 0; background: transparent;
        border: 1px solid var(--line); border-radius: 999px; cursor: pointer;
        color: var(--muted); font-size: .7rem; padding: .25rem .7rem;
        letter-spacing: .04em; transition: color .15s, border-color .15s; }
.lang:hover { color: var(--ink); border-color: var(--line-2); }
.lang.is-on { color: var(--accent); border-color: var(--accent); }
.topbar .lang { position: static; margin-left: auto; }
.topbar .subject { margin-left: .9rem; }
.wordmark { font-size: 2.9rem; letter-spacing: -.045em; font-weight: 700; line-height: 1; }
.wordmark span { color: var(--accent); }
.wordmark.small { font-size: 1rem; letter-spacing: -.03em; }
.tagline { margin: .9rem 0 .3rem; font-size: 1.06rem; color: var(--ink-2); }

/* ---- cards -------------------------------------------------------------- */

.card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 1.4rem;
  box-shadow: var(--shadow);
  margin-bottom: 1rem;
}

/* ---- controls ----------------------------------------------------------- */

input, select, textarea, button { font: inherit; color: inherit; }

.field { display: flex; flex-direction: column; gap: .35rem; flex: 1 1 8rem; }
.field.wide { flex: 1 1 100%; }
.field > span { font-size: .7rem; text-transform: uppercase; letter-spacing: .1em;
                color: var(--muted); font-weight: 600; }

.field input, .field select, .field textarea, .more input, .more select {
  background: var(--sunk);
  border: 1px solid var(--line-2);
  border-radius: 9px;
  padding: .6rem .7rem;
  font-family: var(--mono);
  font-size: .92rem;
  width: 100%;
  transition: border-color .15s, background .15s;
}
.field input:hover, .field textarea:hover { border-color: var(--muted); }
textarea { resize: vertical; line-height: 1.45; font-size: .84rem; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A placeholder is an example, never content. The browser's default sits close
   enough to the text colour in dark mode that an empty form reads as a filled
   one -- which is how the first audit run from this page failed, with the API
   correctly reporting that nothing had been sent. */
::placeholder { color: var(--muted); opacity: .55; font-style: italic; }

.modes { display: flex; gap: .3rem; margin-bottom: 1.4rem; padding: .25rem;
         background: var(--sunk); border-radius: 10px; }
.mode { flex: 1; background: transparent; border: 0; border-radius: 8px;
        color: var(--muted); cursor: pointer; padding: .45rem .5rem;
        font-size: .9rem; transition: background .18s, color .18s; }
.mode:hover { color: var(--ink); }
.mode.is-on { background: var(--panel); color: var(--ink); font-weight: 600;
              box-shadow: 0 1px 3px rgba(0,0,0,.1); }

.versions { display: flex; align-items: flex-end; gap: .8rem; flex-wrap: wrap; }
.versions .arrow { padding-bottom: .65rem; color: var(--muted); font-size: 1.1rem; }
.mode-body > .field.wide { margin-bottom: .9rem; }
.pr-row { display: flex; gap: .5rem; }
.pr-row input { flex: 1; }
#fetchpr { background: var(--sunk); border: 1px solid var(--line-2); border-radius: 9px;
           padding: .6rem 1.1rem; cursor: pointer; color: var(--ink-2); white-space: nowrap;
           transition: border-color .15s, color .15s; }
#fetchpr:hover:not([disabled]) { border-color: var(--accent); color: var(--accent); }
#fetchpr[disabled] { opacity: .55; cursor: progress; }
.prnote { font-size: .8rem; font-family: var(--mono); margin: .5rem 0 0; color: var(--muted); }
.prnote.bad { color: var(--critical); }

.locks { display: flex; gap: .8rem; flex-wrap: wrap; margin-top: 1rem; }
.locks .field { flex: 1 1 16rem; }

.tuck { margin-top: 1rem; }
.tuck summary, .more summary { cursor: pointer; font-size: .85rem; color: var(--muted); }
.tuck summary:hover, .more summary:hover { color: var(--ink); }
.tuck[open] summary, .more[open] summary { margin-bottom: .7rem; }


#run {
  width: 100%;
  margin-top: 1.75rem;
  background: var(--accent); border: 1px solid var(--accent); color: var(--accent-ink);
  border-radius: 10px; padding: .75rem 1rem; font-weight: 650; font-size: 1rem;
  letter-spacing: .01em; cursor: pointer; position: relative; overflow: hidden;
  transition: filter .15s, transform .06s;
}
#run:hover:not([disabled]) { filter: brightness(1.07); }
#run:active:not([disabled]) { transform: translateY(1px); }
#run[disabled] { cursor: progress; }
#run[disabled] .run-label { opacity: .75; }
/* A bar sweeping the button is the only honest progress we have: the API
   reports nothing until the report itself arrives. */
#run[disabled]::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.28), transparent);
  animation: sweep 1.15s linear infinite;
}
@keyframes sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.run-timer { position: absolute; right: 1rem; font-family: var(--mono);
             font-size: .82rem; opacity: .8; font-variant-numeric: tabular-nums; }

.keyrow { margin-top: 1.1rem; }
.keyrow .keyset { font-size: .82rem; color: var(--muted); margin: 0; }
.linkish { background: none; border: 0; padding: 0; color: var(--accent);
           cursor: pointer; font-size: inherit; text-decoration: underline; }


/* Not monospaced: a sentence for a person, not a line from a log. */
.error { max-width: 60rem; margin: 1rem auto 0; padding: 1rem 1.15rem;
         border: 1px solid var(--critical); border-radius: 10px;
         background: var(--panel); }
.error-msg { margin: 0; color: var(--critical); font-size: .95rem; font-weight: 600; }
.error-detail { margin: .4rem 0 0; color: var(--muted); font-size: .84rem;
                font-family: var(--mono); overflow-wrap: anywhere; }


/* ---- results ------------------------------------------------------------ */

/* Padded to match a card, so the bar's contents line up with the contents of
   everything below it rather than with the cards' outer edges. The rule under
   it still spans the full width, which is what a header rule should do. */
.topbar { position: sticky; top: 0; z-index: 5; display: flex; align-items: center;
          gap: 1rem; padding: .9rem 1.4rem; margin-bottom: 1.2rem;
          background: color-mix(in srgb, var(--bg) 88%, transparent);
          backdrop-filter: blur(10px); border-bottom: 1px solid var(--line); }
.topbar .back { text-decoration: none; font-size: .9rem; white-space: nowrap; }
/* Shrinks and ellipsises rather than shoving the wordmark off the left: a
   long crate name with two versions is easily wider than a phone. */
.topbar .subject { margin-left: auto; font-family: var(--mono); font-size: .85rem;
                   color: var(--muted); text-align: right; min-width: 0;
                   overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.verdict-head { display: flex; align-items: center; gap: 1.1rem; flex-wrap: wrap; }
.verdict-head h2 { flex: 1 1 16rem; font-size: 1.08rem; line-height: 1.4; font-weight: 600; }

.badge { padding: .38rem .85rem; border-radius: 999px; font-size: .72rem; font-weight: 700;
         text-transform: uppercase; letter-spacing: .07em; white-space: nowrap; }
.badge.block { background: var(--critical); color: var(--bg); }
.badge.review { background: var(--high); color: var(--bg); }
.badge.likely_safe { background: var(--low); color: var(--bg); }

.score { text-align: right; font-variant-numeric: tabular-nums; line-height: 1; }
.score b { font-size: 2.3rem; font-weight: 700; letter-spacing: -.04em; }
.score small { display: block; margin-top: .3rem; font-size: .64rem; color: var(--muted);
               text-transform: uppercase; letter-spacing: .1em; font-weight: 650; }
.score small.critical { color: var(--critical); }
.score small.high { color: var(--high); }
.score small.medium { color: var(--medium); }
.score small.low { color: var(--low); }

.breakdown { display: flex; gap: 2px; margin-top: 1.2rem; height: 8px;
             border-radius: 999px; overflow: hidden; background: var(--sunk); }
.breakdown span { display: block; width: 0; transition: width .85s cubic-bezier(.2,.7,.3,1); }
.breakdown-key { display: flex; flex-wrap: wrap; gap: .3rem 1rem; margin-top: .6rem;
                 font-size: .73rem; color: var(--muted); font-family: var(--mono); }
.breakdown-key i { display: inline-block; width: .5rem; height: .5rem;
                   border-radius: 2px; margin-right: .35rem; font-style: normal; }

.reasons { margin: 1.1rem 0 0; padding-left: 1.15rem; font-size: .89rem; color: var(--ink-2); }
.reasons li { margin-bottom: .35rem; }
.reasons li::marker { color: var(--muted); }
.run-errors { margin-top: 1.1rem; padding-top: .9rem; border-top: 1px solid var(--line); }
.run-errors h3 { color: var(--high); margin-bottom: .4rem; }

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr)); gap: 1rem; }
.card.stat { padding: 1rem 1.1rem; }
h3 { font-size: .7rem; text-transform: uppercase; letter-spacing: .1em;
     color: var(--muted); font-weight: 650; margin-bottom: .7rem; }
.card dl { margin: 0; display: grid; grid-template-columns: 1fr auto; gap: .24rem .8rem; font-size: .85rem; }
.card dt { color: var(--muted); }
.card dd { margin: 0; font-family: var(--mono); font-variant-numeric: tabular-nums;
           text-align: right; color: var(--ink-2); }

.sev-row { display: flex; flex-wrap: wrap; gap: .2rem 1rem; }
.sev-tally { font-size: .66rem; font-family: var(--mono); text-transform: uppercase; letter-spacing: .06em; }
.sev-tally b { display: block; font-size: 1.2rem; letter-spacing: -.03em; }
.sev-tally.zero { opacity: .32; }
.critical { color: var(--critical); }
.high { color: var(--high); }
.medium { color: var(--medium); }
.low { color: var(--low); }
.info { color: var(--info); }

/* ---- findings ----------------------------------------------------------- */

.findings-title { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; }
.findings-head { display: flex; justify-content: space-between; align-items: center;
                 gap: 1rem; flex-wrap: wrap; margin-bottom: 1rem; }
.findings-head h3 { margin: 0; font-size: .78rem; }
.filters { display: flex; gap: .4rem; flex-wrap: wrap; flex: 1 1 auto; }
.filters select, .filters input { background: var(--sunk); border: 1px solid var(--line-2);
                                  border-radius: 8px; padding: .38rem .55rem; font-size: .84rem; }
.filters input { flex: 1 1 9rem; min-width: 0; }





/* When there is nothing to act on the panel steps back to a plain note: a
   full callout for "all clear" teaches people to skim it. */

.deep-wrap { margin-top: .5rem; }
/* The same pill as the main action, one step quieter: there is one of these
   per finding, and a page of solid accent buttons would shout over the
   findings they belong to. */
.deep-open { display: inline-flex; align-items: center; gap: .35rem;
             font-family: var(--mono); font-size: .7rem; font-weight: 650;
             color: var(--accent); cursor: pointer; list-style: none;
             padding: .26rem .68rem; border-radius: 999px;
             border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
             background: color-mix(in srgb, var(--accent) 10%, transparent);
             transition: background .15s, border-color .15s; }
.deep-open::-webkit-details-marker { display: none; }
.deep-open::marker { content: ""; }
.deep-open:hover { background: color-mix(in srgb, var(--accent) 18%, transparent);
                   border-color: var(--accent); }
.deep-wrap[open] .deep-open { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.deep-open .ask-icon { fill: currentColor; flex: none; }

/* Already answered: it reads as a record rather than an invitation. */

.deep-wrap.done .deep-open { color: var(--muted); border-color: var(--line-2);
                             background: transparent; }
.deep-wrap.done .deep-open:hover { color: var(--ink); border-color: var(--muted); }

.deep { margin-top: .55rem; padding: .6rem .75rem; border-radius: 8px;
        background: var(--panel); border-left: 2px solid var(--line-2);
        font-size: .86rem; color: var(--ink-2); }
.deep.benign { border-left-color: var(--low); }
.deep.worth_a_look { border-left-color: var(--medium); }
.deep.concerning { border-left-color: var(--critical); }
.deep-verdict { margin: 0 0 .35rem; font-family: var(--mono); font-size: .68rem;
                text-transform: uppercase; letter-spacing: .07em; font-weight: 700; }
.deep.benign .deep-verdict { color: var(--low); }
.deep.worth_a_look .deep-verdict { color: var(--medium); }
.deep.concerning .deep-verdict { color: var(--critical); }
.deep-text { margin: 0 0 .4rem; line-height: 1.55; }


/* Where the link lands, so the eye finds it after the scroll. */
@keyframes flash {
  from { background: color-mix(in srgb, var(--accent) 26%, transparent); }
  to   { background: transparent; }
}
.finding.flash { animation: flash 1.6s ease-out; }




/* Settled by a second look: kept in place so the reader can see what happened
   to it, but visibly done with. */
.finding.cleared { opacity: .45; }
.finding.cleared .crate { text-decoration: line-through; }
.finding.cleared .msg { text-decoration: line-through; text-decoration-color: var(--line-2); }
.finding.cleared:hover { opacity: .8; }

.muffled { font-size: .68rem; color: var(--muted); font-family: var(--mono); }


.findings { margin: 0; padding: 0; }
.findings-list { list-style: none; margin: 0; padding: 0; }

/* The report sorts by status before severity, so severity restarts partway
   down the list. That reads as a bug unless the structure is on the page:
   whether this upgrade *introduces* a finding matters more than how severe it
   is, which is the whole distinction the tool exists to draw. */
.group { margin-bottom: 1.7rem; }
.group:last-child { margin-bottom: 0; }
.group-head { display: flex; align-items: baseline; gap: .5rem; margin: 0 0 .15rem; font-size: .78rem; }
.group-name { text-transform: uppercase; letter-spacing: .1em; font-weight: 700; }
.group-name.introduced { color: var(--high); }
.group-name.pre_existing { color: var(--muted); }
.group-name.removed { color: var(--low); }
.group-count { font-family: var(--mono); color: var(--muted); font-weight: 400; }
.group-blurb { font-size: .79rem; color: var(--muted); margin: 0 0 .75rem; max-width: 42rem; }

/* One finding is one row of a sheet, not a card.
   
   Border, radius, fill and shadow each say "separate object", and every
   finding wore all four -- so an info and a high looked equally important and
   severity had to shout over its own container to be seen. What is left is a
   hairline between rows and a gauge in the margin.

   `color` is set here deliberately. The element carries its severity as a
   class, and the global `.high`/`.medium` colour rules would otherwise be
   inherited by every child that does not set its own -- which is why the crate
   name used to come out tinted. Severity belongs to the gauge now, so the row
   states its own ink and the tint has exactly one place to live. */
.finding {
  display: grid;
  grid-template-columns: 4.4rem minmax(0, 1fr);
  column-gap: 1.1rem;
  padding: .85rem 0;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
}
.finding > * { grid-column: 2; min-width: 0; }
.finding:last-child { border-bottom: none; }

/* Four segments, filled to the level. The bar is readable before the word is,
   which is what makes this scannable at six hundred rows, and it still says
   something when the colour cannot be seen at all. */
.gauge { grid-column: 1; grid-row: 1 / -1; }
.gauge .bars { display: flex; gap: 2px; }
.gauge .bars i { display: block; width: .5rem; height: .72rem; background: var(--line-2); }
.gauge .bars i.on { background: var(--muted); }
.gauge em {
  display: block; font-style: normal; font-family: var(--mono);
  font-size: .62rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); margin-top: .3rem; overflow-wrap: anywhere;
}

.gauge.critical .bars i.on { background: var(--critical); }
.gauge.critical em      { color: var(--critical); }
.gauge.high .bars i.on  { background: var(--high); }
.gauge.high em          { color: var(--high); }
.gauge.medium .bars i.on { background: var(--medium); }
.gauge.medium em         { color: var(--medium); }
.gauge.low .bars i.on   { background: var(--low); }
.gauge.low em           { color: var(--low); }

.finding-top { display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap; }
.chip { font-family: var(--mono); font-size: .66rem; font-weight: 700;
        text-transform: uppercase; letter-spacing: .08em; }
.crate { font-family: var(--mono); font-size: .83rem; }
.finding-top .spacer { flex: 1 1 2rem; }

.where { font-family: var(--mono); font-size: .78rem; white-space: nowrap; }
a.where { color: var(--accent); text-decoration: none; border-bottom: 1px solid transparent; }
a.where:hover { border-bottom-color: currentColor; }
a.where::after { content: " ↗"; font-size: .8em; opacity: .7; }
span.where { color: var(--muted); border-bottom: 1px dotted var(--line-2); cursor: help; }

a.difflink { font-family: var(--mono); font-size: .76rem; color: var(--accent);
             text-decoration: none; padding: .05rem .4rem; border-radius: 5px;
             border: 1px solid currentColor; white-space: nowrap; }
a.difflink:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }

.msg { margin: .4rem 0 .45rem; font-size: .89rem; color: var(--ink-2); overflow-wrap: anywhere; }
.finding-meta { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
                font-size: .72rem; color: var(--muted); font-family: var(--mono); }
.tag { display: inline-block; font-size: .66rem; padding: .08rem .38rem; border-radius: 5px;
       border: 1px solid var(--line-2); color: var(--muted); white-space: nowrap; }
/* The depth tag answers a question, so it should look answerable. */
button.tag.chain { cursor: help; font: inherit; font-size: .66rem; font-family: var(--mono);
                   border-style: dashed; }
button.tag.chain:hover { color: var(--ink); border-color: var(--muted); }
button.tag.chain.open { border-style: solid; color: var(--accent); border-color: var(--accent); }

/* Drops to its own line inside the wrapping header row. */
.chain-row { flex: 1 0 100%; margin-top: .4rem; display: flex; flex-wrap: wrap;
             align-items: center; gap: .3rem; font-family: var(--mono); font-size: .72rem; }
.chain-step { color: var(--muted); }
.chain-step.here { color: var(--ink); font-weight: 650; }
.chain-arrow { color: var(--line-2); }

/* Whether this upgrade brought the finding is the most useful thing on the
   row, and it was rendered in the same grey outline as "platform conditional".
   Weight rather than another hue: the palette already has two jobs for colour
   and does not need a third. */
.tag.st-introduced { color: var(--ink); border-color: var(--muted); font-weight: 600; }
.tag.st-pre-existing { border-style: dashed; }
.tag.st-removed { color: var(--low); border-color: currentColor; }

.tag.build { color: var(--high); border-color: currentColor; }
.tag.new { color: var(--accent); border-color: currentColor; }

.snippet { margin-top: .55rem; }
.snippet summary { font-size: .75rem; font-family: var(--mono); color: var(--muted); cursor: pointer; }
.snippet pre { background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
               padding: .6rem 0; margin: .45rem 0 0; }
.snippet .ln { display: block; padding: 0 .7rem; white-space: pre; }
.snippet .ln.hit { background: color-mix(in srgb, var(--high) 22%, transparent); }
.snippet .num { display: inline-block; width: 3.2rem; text-align: right;
                margin-right: .9rem; color: var(--muted); user-select: none; }

pre { overflow-x: auto; font-family: var(--mono); font-size: .78rem; line-height: 1.5; margin: 0; }
#raw { max-height: 28rem; overflow-y: auto; }
.empty { text-align: center; color: var(--muted); font-size: .88rem; padding: 1.5rem 0; }

/* ---- motion ------------------------------------------------------------- */

@keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@keyframes sink { to { opacity: 0; transform: translateY(-10px); } }

.screen.entering { animation: rise .42s cubic-bezier(.2,.7,.3,1) both; }
.screen.leaving { animation: sink .2s ease-in both; }

/* Results arrive in sequence rather than all at once: the verdict first,
   because it is the answer, then the evidence behind it. */
.screen.entering > * { animation: rise .45s cubic-bezier(.2,.7,.3,1) both; }
.screen.entering > *:nth-child(1) { animation-delay: 0s; }
.screen.entering > *:nth-child(2) { animation-delay: .06s; }
.screen.entering > *:nth-child(3) { animation-delay: .13s; }
.screen.entering > *:nth-child(4) { animation-delay: .2s; }
.screen.entering > *:nth-child(5) { animation-delay: .26s; }

.finding { animation: rise .38s cubic-bezier(.2,.7,.3,1) both;
           animation-delay: calc(var(--i, 0) * 22ms); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-delay: 0s !important;
                           transition-duration: .001ms !important; }
}

/* A phone is not a narrow desktop: the same padding that frames a card at
   1200px eats a third of the width at 390. */
@media (max-width: 30rem) {
  .screen { padding-top: 2rem; }
  .card { padding: 1rem; border-radius: 12px; }
  .card.stat { padding: .85rem .9rem; }
  .finding { padding: .65rem .7rem; }
  .wordmark { font-size: 2.2rem; }
  .findings-head { gap: .6rem; }
  .cards { gap: .7rem; }
}
