/*
+------------------------------------------------------+
|  MODULE:   SEO STACK / PANEL                         |
|  LOADS:    index.html, after base.css and home.css   |
|  CONTAINS: the scan form with its pick row, the      |
|            field rows, the change line, the audit    |
|            list, the meta, the running animation     |
|            and the history frame under the panel     |
|  READS:    tokens from base.css. Defines none.       |
+------------------------------------------------------+
*/

/* A page sheet adds to the components layer. It holds the sections
   of its own page only. No colour is written in place here. */
/* keyframes sit outside the layers, as in base.css */
@keyframes tip-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes ring-travel {
  to {
    stroke-dashoffset: -40;
  }
}

@keyframes pulse-word {
  50% {
    opacity: 0.35;
  }
}

/* the loading build: a bar fills from the left, holds, fades, and starts again; the ring's arc draws
   itself round the same way */
@keyframes bar-build {
  0% {
    transform: scaleX(0);
    opacity: 0.7;
  }

  60% {
    transform: scaleX(1);
    opacity: 0.7;
  }

  100% {
    transform: scaleX(1);
    opacity: 0;
  }
}

/* the loading line in the status box: the cursor blinks, each cell lights in turn and goes out in turn */
@keyframes blink {
  50% {
    opacity: 0;
  }
}

@keyframes cell-fill {
  0%,
  10% {
    opacity: 0.18;
  }

  25%,
  70% {
    opacity: 1;
  }

  85%,
  100% {
    opacity: 0.18;
  }
}

@keyframes arc-build {
  0% {
    stroke-dashoffset: 515.22;
    opacity: 0.5;
  }

  70% {
    stroke-dashoffset: 0;
    opacity: 0.5;
  }

  100% {
    stroke-dashoffset: 0;
    opacity: 0;
  }
}

@layer components {
  /* ---- the scan form ---- */
  /* the head row in the card's top corners: the label at the left edge, the device pills at the
     right edge (on a line of their own too, when the head wraps on a phone) */
  .scan__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    margin: 0 0 1.25rem;
  }

  .scan__label {
    margin: 0;
    padding-left: 0;
  }

  .scan__modes {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-left: auto;
  }

  /* the pick row under the address: which checks run, every pill a toggle (none on dims the button). */
  /* the pick row spreads the three checks across the box's whole width, one column each of the
     same width, with 2rem of room under the ring that the pills' lines (--reach) cross; on a
     narrow screen the row is two wide and the lines go, since a second row has no ring above it */
  .scan__checks {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    justify-items: center; /* each pill at its own width, centred in its column */
    gap: 0.75rem;
    margin-top: 2rem;
  }

  /* each check has a colour (the tokens in base.css); a pressed pill takes it for its word and its border,
     and draws a thin line from its top up to the ring on the address box, across the gap of the row */
  /* the holder of a pill and its bubble: as wide as the pill, so the bubble centres under it */
  .scan__pick {
    display: inline-flex;
  }

  .scan__check {
    --check: var(--text-secondary);
    --reach: 2rem;
    position: relative;
  }

  .scan__check[data-check="speed"] {
    --check: var(--check-speed);
  }

  .scan__check[data-check="security"] {
    --check: var(--check-security);
  }

  .scan__check[data-check="technical"] {
    --check: var(--check-technical);
  }

  .scan__check[aria-pressed="true"] {
    color: var(--check);
    border-color: var(--check);
    background: var(--bg-surface);
  }

  /* the light theme keeps the colour on the border and the line and writes the word in the text
     colour, as the band words do on the board */
  [data-theme="light"] .scan__check[aria-pressed="true"] {
    color: var(--text-accent);
  }

  /* from 38rem the three sit in one row and each pressed pill draws its line from its middle up to
     the ring; the narrow screen's two rows have none, since a second row has no ring above it */
  @media (min-width: 38rem) {
    .scan__checks {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .scan__check::before {
      content: "";
      position: absolute;
      bottom: 100%;
      left: 50%;
      width: 0.0625rem;
      height: var(--reach);
      background: var(--check);
      transform: translateX(-50%) scaleY(0); /* the line grows up from the pill when its check is pressed */
      transform-origin: bottom;
      transition: transform 0.25s var(--ease);
    }

    .scan__check[aria-pressed="true"]::before {
      transform: translateX(-50%) scaleY(1);
    }
  }

  /* the address box and its ring: the pressed checks share one 4 px ring on the box's edge, each
     owning the band above and under its own pill so the pill's line meets its colour, every
     handover an arrow tip cut inside the line so the next band fits it; the page draws it as an
     SVG sized to the box.
     The focus keeps its orange border under the ring and drops the glow, which would sit over the
     colours */
  .scan__box {
    position: relative;
  }

  /* the address reads centred, the placeholder with it */
  .scan__url {
    text-align: center;
  }

  .scan__url:focus-visible {
    box-shadow: none;
    outline: 0.125rem solid var(--accent-strong);
    outline-offset: 0.25rem; /* outside the ring, so a keyboard focus still shows */
  }

  .scan__ring {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
    pointer-events: none;
  }

  /* a pick slides the bands: the page writes every band as two runs, so the dashes transition
     value by value, a band coming on growing out of its pill's point both ways round the ring (a
     tenth of a second after the pill's line sets off) and a band going off folding back to it */
  .scan__seg {
    fill: none;
    stroke-width: 4;
    stroke-linecap: butt;
    transition: stroke-dasharray 0.35s var(--ease);
  }

  .scan__seg--on {
    transition-delay: 0.1s;
  }

  /* the tips are drawn where the handovers end up and fade in once the slide is done */
  .scan__tip {
    stroke: none;
    animation: tip-in 150ms var(--ease) 350ms both;
  }

  /* the run button: a play circle in the frame's bottom corner, where the Early access badge sat;
     on hover, on focus and while a test runs it grows to its words (Run the Test, then the seconds).
     The word stays in the button's name while it is folded away, since it is only narrowed to 0 and
     faded, never display: none or visibility: hidden */
  .scan__button {
    position: absolute;
    right: var(--gutter);
    bottom: 1rem;
    z-index: 1;
    gap: 0;
    min-width: 3rem;
    height: 3rem;
    min-height: 0;
    padding: 0 0.875rem;
    border-radius: var(--r-pill);
  }

  .scan__play {
    flex: none;
    width: 1.25rem;
    height: 1.25rem;
  }

  .scan__word {
    display: inline-block;
    max-width: 0;
    overflow: hidden;
    white-space: nowrap;
    opacity: 0;
    transition: max-width 0.3s var(--ease), opacity 0.2s var(--ease), margin-left 0.3s var(--ease);
  }

  .scan__button:hover .scan__word,
  .scan__button:focus-visible .scan__word,
  .scan__button.is-running .scan__word {
    max-width: 8rem;
    margin-left: 0.5rem;
    opacity: 1;
  }

  .scan__button.is-running .scan__play {
    display: none;
  }

  /* the card keeps the height it had with the button in it: the head sits in its top corners and
     the body (the box, the pick row, the status box) centres in the room under the head, the auto
     margins taking the free height in equal halves */
  .scan {
    display: flex;
    flex-direction: column;
    min-height: 18rem;
  }

  .scan__body {
    margin: auto 0;
  }

  /* ---- the status under the form, drawn like the terminal line under the board: dark in both
     themes, the words centred; a result is the outcome pill and the band pill in its tone ---- */
  .scan__status {
    /* the room between the pick row and the status box, 2rem from 38rem */
    margin-top: 1.5rem;
    font-family: var(--font-mono);
    letter-spacing: 0.04em;
    text-align: center;
    color: var(--cli-text);
    background: var(--cli-bg);
    border-color: var(--cli-border);
    border-radius: var(--r-lg);
  }

  .scan__status.message--info {
    color: var(--cli-muted);
  }

  /* the Security board sits under the speed note with the same room the note has above it, or
     first in the section when Speed did not run; its bars carry no marks, since a category score
     is good from 85 (the grade's A-) and needs work from 70 (its B) */
  .board--security {
    margin-top: 1rem;
  }

  .board[hidden] + .board--security {
    margin-top: 0;
  }

  /* the Technical SEO board follows the same way, and sits first with no room above it when the
     two boards before it did not run; its bars show each kind's share of the checker's messages */
  .board--technical {
    margin-top: 1rem;
  }

  .board[hidden] + .board[hidden] + .board--technical {
    margin-top: 0;
  }

  /* each board folds to its head, ring and meters: the rest (the fixes with their list, the run
     line) sits in a fold that the board's title, its ring or the chevron at its foot opens, so
     the boards read at a glance one under the other. The fold takes the wide area's place across
     both columns; the chevron is a bar across the foot, pointing down when there is more */
  .board__fold {
    grid-column: 1 / -1;
  }

  .board__handle {
    padding: 0;
    text-align: inherit;
    letter-spacing: inherit;
  }

  .board__ring {
    cursor: pointer;
  }

  .board__more {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    margin-top: 0.5rem;
    padding: 0.25rem;
    color: var(--text-secondary);
    border: 0.0625rem solid var(--border-color);
    border-radius: var(--r-pill);
    transition: color 150ms var(--ease), border-color 150ms var(--ease);
  }

  .board__more:hover,
  .board__more:focus-visible,
  .board:has(.board__handle:hover) .board__more,
  .board:has(.board__ring:hover) .board__more {
    color: var(--text-primary);
    border-color: var(--border-hover);
  }

  .board__more-icon {
    width: 1rem;
    height: 1rem;
    transition: transform 0.2s var(--ease);
  }

  .board__more[aria-expanded="true"] .board__more-icon {
    transform: rotate(180deg);
  }

  /* the fix head: the title, the count in the middle, Copy all at the end */
  .audits__count {
    margin-inline: auto;
  }

  /* the runs pill carries the lab note as a hover bubble, centred under the pill */
  .cli__tipbox {
    display: inline-block;
  }

  .cli__tipbox .tip {
    font-family: var(--font-body);
    letter-spacing: 0;
  }

  /* the scan's own line under the security fixes: the sentence on why the scanner takes one scan
     every 30 seconds, centred in an outline of its own under the pills */
  /* two classes: the line rule later in this sheet makes every line a flex row, which pinned the
     words to the start whatever this rule said (seen live on 2026-10-13) */
  .cli__line.cli__note {
    display: block;
    margin: 0;
    padding: 0.5rem 0.75rem;
    text-align: center;
    color: var(--cli-muted);
    border: 0.0625rem solid var(--cli-border);
    border-radius: var(--r-md);
  }

  /* the line in red under every board's run line (asked 2026-10-13): its words and its outline in
     the palette's coral, so the line is read; a second note under it (the scanner's 30 seconds)
     keeps a little room */
  .cli__line.cli__note--important {
    color: var(--color-coral);
    border-color: var(--color-coral);
  }

  .cli__note--important + .cli__note {
    margin-top: 0.375rem;
  }

  .scan__status.message--error {
    color: var(--color-coral);
    border-color: var(--color-coral);
  }

  /* the loading line while a test runs (built by the page): the word with a blinking block cursor,
     then twelve cells that fill from the left and empty from the left, over and over, each one a
     tenth of a second behind the last (--i is its place in the row) */
  .load {
    display: inline-flex;
    align-items: center;
    gap: 0.875rem;
  }

  .load__word::after {
    content: "";
    display: inline-block;
    width: 0.5em;
    height: 1em;
    margin-left: 0.3em;
    vertical-align: -0.15em;
    background: currentColor;
    animation: blink 1.4s steps(2, start) infinite;
  }

  .load__cells {
    display: inline-flex;
    gap: 0.1875rem;
  }

  .load__cell {
    --i: 0;
    width: 0.375rem;
    height: 0.75rem;
    background: var(--cli-text);
    opacity: 0.18;
    animation: cell-fill 4.2s ease-in-out infinite; /* a slow fill reads as work, a fast one as a bounce */
    animation-delay: calc(var(--i) * 0.16s);
  }

  .message--result {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.5rem 0.75rem;
    padding: 0.75rem 1.125rem;
    white-space: normal;
  }

  .message--top {
    border-color: var(--color-lime);
  }

  .message--good {
    border-color: var(--color-leaf);
  }

  .message--mid {
    border-color: var(--color-amber);
  }

  .message--poor {
    border-color: var(--color-coral);
  }

  .message__note {
    flex-basis: 100%;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.04em;
    color: var(--cli-muted); /* the box is dark in both themes, so its note takes the terminal's tone */
  }

  /* the band pill keeps the badge shape and takes its tone on the word as well as the dot; the
     primitives, since the themed text tones go dark in the light theme and this box stays dark.
     The box's own pill rule (.scan__status .badge, later in this sheet) sets the terminal text
     colour with two classes, so these carry three, or the tone never shows on the word */
  .message__band {
    font-weight: 700;
  }

  .scan__status .badge.message__band--top {
    color: var(--color-lime);
  }

  .scan__status .badge.message__band--good {
    color: var(--color-leaf);
  }

  .scan__status .badge.message__band--mid {
    color: var(--color-amber);
  }

  .scan__status .badge.message__band--poor {
    color: var(--color-coral);
  }

  /* ---- while a test runs, the board builds itself over and over: the dashes travel round the
     ring, a dim arc draws itself round and fades, the word breathes, each empty bar fills from
     the left and fades, one row after the other, and the values pulse with the word ---- */
  .board.is-running .ring__track {
    animation: ring-travel 1.4s linear infinite;
  }

  .board.is-running .ring__arc {
    stroke: var(--text-muted);
    animation: arc-build 2.4s ease-in-out infinite;
  }

  .board.is-running .ring__num.is-word,
  .board.is-running .meter__value {
    animation: pulse-word 1.8s ease-in-out infinite;
  }

  .board.is-running .meter__bar::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--shine);
    transform: scaleX(0);
    transform-origin: left;
    animation: bar-build 1.8s ease-in-out infinite;
  }

  .board.is-running .meter:nth-child(2) .meter__bar::after {
    animation-delay: 0.12s;
  }

  .board.is-running .meter:nth-child(3) .meter__bar::after {
    animation-delay: 0.24s;
  }

  .board.is-running .meter:nth-child(4) .meter__bar::after {
    animation-delay: 0.36s;
  }

  .board.is-running .meter:nth-child(5) .meter__bar::after {
    animation-delay: 0.48s;
  }

  .board.is-running .meter:nth-child(6) .meter__bar::after {
    animation-delay: 0.6s;
  }

  /* ---- the board extras on this page ---- */
  /* the band word and the low run take the tone of their band, the quota board's colours: its
     Unlimited lime for a perfect score, its theme green for good, amber needs work, coral poor;
     the high run is always the house green */
  .ring__label-name--top,
  .ring__label-num--top {
    color: var(--text-top);
  }

  .ring__label-name--good,
  .ring__label-num--good {
    color: var(--text-good);
  }

  .ring__label-name--mid,
  .ring__label-num--mid {
    color: var(--text-warn);
  }

  .ring__label-name--poor,
  .ring__label-num--poor {
    color: var(--text-danger);
  }

  .ring__label-num {
    font-weight: 700;
  }

  /* the high run in the house green, the number to hold on to */
  .ring__label-num--high {
    color: var(--color-green);
  }


  /* the value and its band word sit together at the row's right edge */
  .meter__row {
    justify-content: flex-start;
  }

  .meter__name {
    margin-right: auto;
  }

  .meter__band {
    margin-left: 0.5rem;
    font-size: 0.6875rem;
    font-weight: 400;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--text-secondary);
  }

  /* the long metric names only from 38rem: on a phone the row keeps one line */
  .meter__long {
    display: none;
  }

  .meter__band:empty {
    display: none;
  }

  /* the real-visitor rows: value, then the band word in its tone */
  .field__band {
    margin-left: 0.625rem;
    font-size: var(--fs-xs);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-secondary);
  }

  .field__band--good {
    color: var(--text-ok);
  }

  .field__band--mid {
    color: var(--text-warn);
  }

  .field__band--poor {
    color: var(--text-danger);
  }

  /* ---- the failed audits, worst first: each row is a button that slides its step open under it
     (the fold in base.css): what to change, where, and the line to paste into Claude Code ---- */
  /* the head: the title with its bubble, the count pill, Copy all at the far end */
  .audits__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.875rem;
    margin: 1.5rem 0 0.75rem;
  }

  .audits__title {
    font-size: clamp(1.5rem, 2.6vw, 1.875rem);
  }

  .audits__copy {
    margin-left: auto;
  }

  .audits {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    list-style: none;
  }

  /* each audit in its own box; the step opens inside it */
  .audit {
    padding: 0.25rem 0.5rem;
    background: var(--bg-primary);
    border: 0.0625rem solid var(--border-color);
    border-radius: var(--r-lg);
    transition: border-color 150ms var(--ease);
  }

  .audit:hover,
  .audit:focus-within {
    border-color: var(--border-hover);
  }

  /* the row: the dot, the words, the chevron */
  .audit__row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 0.875rem;
    align-items: start;
    width: 100%;
    padding: 0.5rem;
    text-align: left;
    color: inherit;
    background: none;
    border: 0;
    border-radius: 0.5rem;
    cursor: pointer;
    transition: background 150ms var(--ease);
  }

  .audit__row:hover,
  .audit__row:focus-visible {
    background: var(--bg-surface);
  }

  .audit__dot {
    width: 0.5rem;
    height: 0.5rem;
    margin-top: 0.5rem;
    border-radius: 50%;
    background: var(--color-amber);
  }

  .audit--poor .audit__dot {
    background: var(--color-coral);
  }

  /* a note from the HTML checker counts for nothing in the score: its dot is the muted one */
  .audit--note .audit__dot {
    background: var(--text-muted);
  }

  .audit__text {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    min-width: 0;
  }

  .audit__title {
    font-size: var(--fs-md);
    font-weight: 700;
    line-height: 1.4;
    color: var(--text-primary);
  }

  .audit__sub {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1rem;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.04em;
    color: var(--text-secondary);
  }

  .audit__saving {
    color: var(--text-ok);
  }

  /* under a security check: its verdict in the secondary tone, its category in the muted one */
  .audit__display {
    color: var(--text-secondary);
  }

  .audit__category {
    color: var(--text-muted);
  }

  .audit__chevron {
    margin-top: 0.3125rem;
    color: var(--text-secondary);
    transition: transform 0.3s var(--ease);
  }

  .audit__row[aria-expanded="true"] .audit__chevron {
    transform: rotate(180deg);
  }

  /* the step: a head, two labelled lines, the prompt in a box, Copy under it; the words start
     under the title, past the row's padding, the dot and the gap */
  .step {
    display: grid;
    grid-template-columns: minmax(0, 1fr); /* a long word in a scanner's detail may not widen the step */
    gap: 0.625rem;
    padding: 0.25rem 0.5rem 0.75rem 1.875rem;
  }

  .step__head {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-accent);
  }

  /* each label is a small terminal pill with its text under it */
  .step__label {
    display: block;
    width: max-content;
    margin-bottom: 0.375rem;
    padding: 0.1875rem 0.625rem;
    font-family: var(--font-mono);
    font-size: 0.625rem;
    line-height: 1.4;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--cli-text);
    background: var(--cli-bg);
    border: 0.0625rem solid var(--cli-border);
    border-radius: var(--r-pill);
  }

  .step__text {
    font-size: var(--fs-sm);
    line-height: 1.5;
    overflow-wrap: anywhere;
    color: var(--text-primary);
  }

  /* the paste label in the lime of a perfect score; a bracket of it outside the left edge ties the
     label to the box, which takes a softened lime border, full on hover */
  .step__label--paste {
    color: var(--color-lime);
  }

  /* the prompt in the terminal look, lit on hover; the Copy pill floats in its top corner */
  .step__prompt {
    position: relative;
    margin: 0;
    padding: 0.75rem;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    line-height: 1.6;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    background: var(--cli-bg);
    border: 0.0625rem solid var(--lime-line);
    border-radius: var(--r-lg);
    transition: border-color 150ms var(--ease);
  }

  /* the bracket, 0.875rem out, its feet reaching the two left edges: the top one at the label's
     middle (the label is 1.375rem tall: 0.625rem text at 1.4, 0.375rem padding, the border), the
     bottom one 1rem into the box, under the 0.875rem corner, where the border runs straight */
  .step__prompt::before {
    content: "";
    position: absolute;
    top: calc(-0.0625rem - 1rem - 0.6875rem);
    left: -0.875rem;
    width: calc(0.875rem - 0.0625rem);
    height: calc(0.0625rem + 1rem + 0.6875rem + 1rem);
    border: 0.0625rem solid var(--color-lime);
    border-right: 0;
  }

  .step__prompt:hover {
    border-color: var(--color-lime);
  }

  .step__prompt-text {
    color: var(--cli-text);
  }

  .step__muted {
    color: var(--cli-muted);
  }

  .step__address {
    color: var(--color-orange);
  }

  .step__said {
    font-weight: 700;
  }

  .step__file {
    color: var(--color-amber);
  }

  .step__copy {
    float: right;
    min-height: 1.75rem;
    margin: -0.125rem -0.125rem 0.375rem 0.75rem;
    padding: 0.25rem 0.625rem;
    color: var(--cli-muted);
    background: var(--cli-bg);
    border-color: var(--cli-border);
  }

  .step__copy:hover,
  .step__copy:focus-visible {
    color: var(--cli-text);
    border-color: var(--cli-muted);
  }

  .step__copy.is-copied,
  .step__copy.is-copied:hover {
    color: var(--tone-good);
    border-color: var(--tone-good);
  }

  /* the things Google names under the step: the file, its numbers, its reason */
  .step__found {
    display: grid;
    gap: 0.375rem;
    margin: 0;
    padding-left: 1.125rem;
    font-size: var(--fs-sm);
    line-height: 1.5;
    color: var(--text-primary);
  }

  .step__item {
    overflow-wrap: anywhere;
  }

  .step__what {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
  }

  .step__detail {
    margin-left: 0.375rem;
    color: var(--text-secondary);
  }

  .step__note {
    display: block;
    color: var(--text-secondary);
  }

  /* under a mistake the HTML checker found: the places it sits in, each its line and column, then
     the page's own source there in the terminal look (the checker quotes it, so it reads as data) */
  .step__places {
    display: grid;
    gap: 0.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    line-height: 1.5;
  }

  .step__place {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.1875rem;
    overflow-wrap: anywhere;
  }

  .step__line-no {
    letter-spacing: 0.04em;
    color: var(--text-secondary);
  }

  .step__source {
    margin: 0;
    padding: 0.375rem 0.625rem;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: var(--cli-text);
    background: var(--cli-bg);
    border: 0.0625rem solid var(--cli-border);
    border-radius: 0.5rem;
  }

  /* ---- the run line under the board, drawn like a terminal: dark in both themes, the address
     after the prompt sign, the device and Lighthouse pills, the runs as small rings ---- */
  .cli {
    position: relative;
    display: grid;
    gap: 1rem;
    margin-top: 1rem;
    padding: 1.25rem;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.04em;
    color: var(--cli-text);
    background: var(--cli-bg);
    border: 0.0625rem solid var(--cli-border);
    border-radius: var(--r-lg);
  }

  .cli__line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 0.625rem;
    margin: 0;
  }

  /* the foot line: the address button at the start, the pills at the far end; the runs line
     centred. From 38rem the foot sits in the box's bottom corners, out of the flow, so the runs
     pill is centred in the box */
  .cli__foot {
    justify-content: space-between;
  }

  .cli__start {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
  }

  .cli__address {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-width: 0;
    max-width: 100%;
    padding: 0.0625rem 0.4375rem;
    font-size: 0.5rem;
    line-height: 1.4;
    color: var(--cli-text);
    background: none;
    border: 0.0625rem solid var(--cli-border);
    border-radius: var(--r-pill);
    cursor: pointer;
    transition: border-color 150ms var(--ease);
  }

  .cli__address:hover,
  .cli__address:focus-visible {
    border-color: var(--cli-muted);
  }

  .cli__pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-left: auto;
  }

  .cli__runs {
    justify-content: center;
  }

  .cli__prompt {
    font-weight: 700;
    color: var(--color-orange);
  }

  .cli__url {
    overflow-wrap: anywhere;
  }

  .cli__label {
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--cli-muted);
  }

  .cli .badge,
  .scan__status .badge {
    color: var(--cli-text);
    background: none;
    border-color: var(--cli-border);
  }

  /* the run line's pills three steps smaller than the status box's; the plain scan line keeps the
     status box's size, since it has no runs row to crowd */
  .cli:not(.cli--plain) .badge {
    padding: 0.0625rem 0.4375rem;
    font-size: 0.5rem;
  }

  /* the runs and their rings in one pill */
  .cli__group {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    border: 0.0625rem solid var(--cli-border);
    border-radius: var(--r-pill);
  }

  .cli__rings {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.375rem;
  }

  /* five rings fit a phone at 2.5rem; from 38rem they grow to 3rem */
  .cli .hring {
    width: 2.5rem;
    height: 2.5rem;
  }

  .cli .hring__track {
    stroke: var(--cli-border);
  }

  .cli .hring__num {
    color: var(--cli-text);
  }

  /* ---- the history: its own frame under the form's, the list of the member's own rows, newest
     first, with the search box at its head; the whole frame sits in a fold (base.css) that the
     History pill in the form frame's corner slides open and shut, so a fresh page shows the pill
     alone and the frame's top margin folds away with it ---- */
  .app-frame--history {
    margin-top: 1rem;
  }

  /* the note stays in the page as a status region; empty, it takes no room */
  .history__note {
    margin-top: 0.5rem;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    letter-spacing: 0.04em;
    color: var(--text-secondary);
  }

  .history__note:empty {
    margin-top: 0;
  }

  .history__list {
    display: flex;
    flex-direction: column;
    list-style: none;
  }

  .history__item + .history__item {
    border-top: 0.0625rem solid var(--border-color);
  }

  /* each row is a button: a click opens the saved result under it and puts the address and the
     device back in the form; the hover fill bleeds half a rem past the text so both themes show
     it. The address box asks for 10rem of the line and the tail (the pills and the ring) is one
     piece that takes its own line under the box, at the row's end, up to 42rem, where the row
     keeps one line */
  .history__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.75rem;
    width: calc(100% + 1rem);
    margin-inline: -0.5rem;
    padding: 0.625rem 0.5rem;
    text-align: left;
    color: inherit;
    background: none;
    border: 0;
    border-radius: 0.5rem;
    cursor: pointer;
    transition: background 150ms var(--ease);
  }

  .history__row:hover,
  .history__row:focus-visible,
  .history__row[aria-expanded="true"] {
    background: var(--bg-surface);
  }

  /* the saved result under a row, on a line down its left side: a copy of the three boards drawn
     from the row's parts, each with its own fold, the board of a part the row lacks hidden; a part
     that did not load is named in a warning box above them */
  .history__result {
    margin: 0.25rem 0 0.75rem 0.5rem;
    padding: 0.25rem 0 0.25rem 1rem;
    border-left: 0.125rem solid var(--border-color);
  }

  /* the copies draw still: the fold's slide is the one motion under a row, so a redraw of the
     list (Show more, a fresh result, a letter in the search box) does not replay the boards' rise */
  .history__result .board {
    grid-template-columns: minmax(0, 1fr); /* one column until 42rem: the copy is 26 px narrower than the panel */
    opacity: 1;
    transform: none;
    animation: none;
  }

  .history__missing {
    margin: 0 0 1rem;
  }

  /* the tail: the pills and the ring on a line of their own under the box on a phone (every box
     full width, so one size), which wraps inside itself only on a screen too narrow for even the
     tail; there the pills keep the badge's base size with a slimmer side padding, so a grade pill,
     a score pill and the ring fit a 360 px screen on one line in the common case (a long grade
     next to a three-digit score wraps inside the tail, with nothing cut) */
  .history__tail {
    display: flex;
    flex: 0 0 100%;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    max-width: 100%;
  }

  .history__tail .badge {
    flex: none;
    padding: 0.3125rem 0.625rem;
    font-size: 0.6875rem;
  }

  /* the address, then the pills on the same line; the address is cut to its line, and the pills
     sit under it when the box is narrow */
  .history__text {
    display: flex;
    flex: 1 1 10rem;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem 0.75rem;
    min-width: 0;
    padding: 0.5rem 0.75rem;
    background: var(--bg-primary);
    border: 0.0625rem solid var(--border-color);
    border-radius: var(--r-lg);
    transition: border-color 150ms var(--ease);
  }

  /* the box holding the address lights its edge with the row */
  .history__row:hover .history__text,
  .history__row:focus-visible .history__text,
  .history__row[aria-expanded="true"] .history__text {
    border-color: var(--details-hover-border);
  }

  /* the address on one line, cut with an ellipsis when the box is too short for it; the row
     open, it shows whole and the pills go, so the full address reads */
  .history__url {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    font-size: var(--fs-md);
    font-weight: 700;
    white-space: nowrap;
    text-overflow: ellipsis;
    color: var(--text-primary);
    transition: color 150ms var(--ease);
  }

  .history__row[aria-expanded="true"] .history__url {
    white-space: normal;
    overflow-wrap: anywhere;
  }

  .history__row[aria-expanded="true"] .history__pills {
    display: none;
  }

  .history__row:hover .history__url,
  .history__row:focus-visible .history__url {
    color: var(--text-accent);
  }

  /* the pills beside the address: the day and the device */
  .history__pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
  }

  /* the head holds the search box at its start (the base title-row's margin is its gap to the list) */
  .history__head {
    flex-wrap: nowrap;
    justify-content: flex-start;
  }

  /* the pill's holder in the form frame's bottom-left corner, the mirror of the run button in the
     right one (speed.css, .scan__button): 7rem wide on the narrowest phones and 10rem from 22.5rem,
     its centre line on the button's: the button's 1rem from the edge plus half of its 3rem less
     the pill's 1.925rem (0.75rem of padding, the 0.75rem word at a line height of 1.4, two
     0.0625rem borders) */
  .history__handle {
    position: absolute;
    left: var(--gutter);
    bottom: 1.5375rem;
    z-index: 1;
    width: 7rem; /* the word and its chevron fit 6.75rem; 10rem would meet the run button grown to its words on a 320 px screen */
  }

  @media (min-width: 22.5rem) {
    .history__handle {
      width: 10rem; /* the search box's width, from the screen where the grown button clears it */
    }
  }

  /* the pill's bubble opens upward, over the form card: below the pill it would run under the next
     frame, which paints over it (each frame keeps the transform of its spring, so a bubble's z-index
     cannot leave its frame) */
  .history__handle .tip {
    top: auto;
    bottom: calc(100% + 0.5rem);
  }

  /* the heading keeps the section's name for a screen reader and lends the pill none of the
     heading's size */
  .history__heading {
    margin: 0;
    font: inherit;
    letter-spacing: normal;
  }

  /* the search box at the head's start, a mono pill on the page's own background: what is typed
     narrows the list to the addresses that contain it; 10rem, at the base input size on a phone
     (a smaller one makes iOS Safari zoom the page on focus) and the pill size from 38rem */
  .history__search {
    flex: 0 1 10rem;
    min-width: 0;
    min-height: 0; /* the base sheet gives every input 2.75rem; this one sits at pill height */
    padding: 0.25rem 0.75rem; /* an input keeps its normal line height, so the slimmer padding meets the title pill's 31 px on a phone */
    font-family: var(--font-mono);
    font-size: var(--fs-md); /* the base input size on a phone: a smaller one makes iOS Safari zoom the page on focus */
    line-height: 1; /* at the phone's 16 px this keeps the pill's height, with the title pill's */
    letter-spacing: 0.04em;
    text-align: center;
    color: var(--text-primary);
    appearance: none;
    background: var(--bg-primary);
    border: 0.0625rem solid var(--border-color);
    border-radius: var(--r-pill);
  }

  /* the placeholder at the pill size at every width: the zoom rule is about the input's own size,
     and SEARCH at 16 px ran past an 82 px pill on a 320 px screen */
  .history__search::placeholder {
    font-size: var(--fs-xs);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-secondary);
  }

  /* Chrome keeps a search input's own decoration and cancel button in the line, which ate the
     end of the placeholder on a 320 px screen */
  .history__search::-webkit-search-decoration,
  .history__search::-webkit-search-cancel-button {
    display: none;
    appearance: none;
  }

  .history__search:hover {
    border-color: var(--details-hover-border);
  }

  /* the base sheet's focus colour for an input loses to the border shorthand above (components
     comes after base in the layer order), so focus names its colour here, after the hover rule */
  .history__search:focus-visible {
    border-color: var(--accent-strong);
  }

  /* the title as a pill, the one handle of the fold, in the mono voice of the pills: a click
     drops the History frame open under the form or folds it, and the chevron turns from down to
     right while it is shut (the Minimize pill of the first cut merged into it on 2026-10-13) */
  .history__title {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    width: 100%;
    min-height: 0;
    padding: 0.375rem 0.75rem;
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-secondary);
    background: var(--bg-primary);
    border: 0.0625rem solid var(--border-color);
    border-radius: var(--r-pill);
    transition: color 150ms var(--ease), border-color 150ms var(--ease);
  }

  .history__title:focus-visible {
    outline: 0.125rem solid var(--accent-strong);
    outline-offset: 0.125rem;
  }

  .history__title:hover,
  .history__title:focus-visible {
    color: var(--text-primary);
    border-color: var(--details-hover-border);
  }

  .history__title-word {
    white-space: nowrap; /* the pill shrinks on a phone; the word does not break */
  }

  .history__title-icon {
    flex: none;
    width: 1rem;
    height: 1rem;
    transition: transform 0.2s var(--ease);
  }

  .history__title[aria-expanded="false"] .history__title-icon {
    transform: rotate(-90deg);
  }

  /* the score as a small ring in the board's style: the arc in the band's tone, the digits inside */
  .hring {
    position: relative;
    flex: 0 0 auto;
    width: 3.25rem;
    height: 3.25rem;
    border-radius: 50%;
    transition: transform 0.15s var(--ease), box-shadow 0.2s var(--ease);
  }

  /* a hovered or open row lights its ring the way the account button lights up, at a lower level */
  .history__row:hover .hring,
  .history__row:focus-visible .hring,
  .history__row[aria-expanded="true"] .hring {
    transform: scale(1.03);
    box-shadow: 0 0 0 0.0625rem var(--ring-glow-inner), 0 0 0.875rem 0.0625rem var(--ring-glow-outer);
  }

  .hring__svg {
    width: 100%;
    height: 100%;
    transform: rotate(-90deg);
  }

  .hring__track {
    fill: none;
    stroke: var(--bg-elevated);
    stroke-width: 5;
  }

  .hring__arc {
    fill: none;
    stroke: var(--color-teal);
    stroke-width: 5;
    stroke-linecap: butt;
  }

  .hring__arc.tone-lime {
    stroke: var(--tone-top);
  }

  .hring__arc.tone-leaf {
    stroke: var(--tone-good);
  }

  .hring__arc.tone-amber {
    stroke: var(--color-amber);
  }

  .hring__arc.tone-coral {
    stroke: var(--color-coral);
  }

  .hring__num {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-digits);
    font-size: 0.6875rem; /* one size for every ring; 11 px keeps the unhinted bars on single pixel rows at 1x */
    line-height: 1;
    color: var(--text-primary);
  }

  /* Show more and Show less under the list */
  .history__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.75rem;
  }

  /* Narrow, 38rem: the long metric names back, the run line's foot in the box's corners */
  @media (min-width: 38rem) {
    .meter__long {
      display: inline;
    }

    .cli {
      padding: 1.5rem 1.25rem;
    }

    .scan__status {
      margin-top: 2rem;
    }

    .cli__group {
      padding: 0.5rem 1rem;
    }

    .cli .hring {
      width: 3rem;
      height: 3rem;
    }

    /* the search box at the pill font from here (16 px on a phone, the base rule says why) */
    .history__search {
      padding: 0.375rem 0.75rem;
      font-size: var(--fs-xs);
      line-height: 1.4;
    }

    .cli__foot {
      position: absolute;
      inset: auto 0.75rem 0.5rem 0.75rem;
    }

    /* the red line is the box's last row in the flow, so the foot in the corners needs its own
       room under it: its 0.5rem inset, its 0.95rem height and the grid's 1rem gap, to the half
       rem (the foot sat 1 px under the line on 2026-10-13) */
    .cli:not(.cli--plain) {
      padding-bottom: 2.5rem;
    }

    /* the security and Technical SEO lines have no runs row to pin the foot under, so their pills
       stay in the flow, with the sentences under them */
    .cli--plain .cli__foot {
      position: static;
    }
  }

  /* the two-column board (the 15rem ring column beside the meters) starts at 38rem in the panel
     and at 42rem in a copy under a row, where the box is 26 px narrower (its margin, line and
     padding) and the meters' band words ran past the card between the two */
  @media (min-width: 42rem) {
    .history__result .board {
      grid-template-columns: 15rem minmax(0, 1fr);
    }

    /* from 42rem a history row keeps the box, the pills and the ring on one line (narrower, the
       day pill ran out of the box and under the tail), the pills one step up from the badge's base
       size, as they were before the tail */
    .history__row {
      flex-wrap: nowrap;
    }

    /* the tail as three columns, the widest grade pill (security N/A, 146 px), the widest score
       pill (technical 100, 155 px) and the ring, so every row's tail has one width and the boxes
       before them one width too; a part the row lacks leaves its column empty */
    .history__tail {
      display: grid;
      flex: none;
      grid-template-columns: 9.25rem 9.75rem 3.25rem;
      justify-items: start;
      gap: 0.75rem;
    }

    .history__grade {
      grid-column: 1;
    }

    .history__score {
      grid-column: 2;
    }

    /* each pill fills its column with its words centred, so the two columns read as cells */
    .history__grade,
    .history__score {
      justify-self: stretch;
      justify-content: center;
    }

    .history__tail .hring {
      grid-column: 3;
      justify-self: center;
    }

    .history__tail .badge {
      padding: 0.375rem 0.875rem;
      font-size: 0.75rem;
    }

    /* the boxes keep one height while the pills wrap under the address: three lines here, two from
       50rem, none forced from 60rem where the box is three columns */
    .history__text {
      min-height: 6.625rem;
    }
  }

  @media (min-width: 50rem) {
    .history__text {
      min-height: 4.625rem;
    }
  }

  /* from 60rem the box holds its three pieces in columns that line up down the list: the address
     in a 12rem slot (cut with an ellipsis when longer), the day centred in the room left and the
     device at the end; narrower, the pills sit under the address as before. At 60rem the frame is
     920 px (its 58rem cap comes at 60.5rem), so the box is 482 px wide or more with no scrollbar
     and about 465 px beside one (the row's 874 inside its half-rem paddings, less the tail's 380
     and the 12 px gap), which still holds the slot, the day pill and the device pill. The device
     column is fixed too (desktop, the wider pill, is 82 px), so a row with no device pill, or the
     narrower mobile one, keeps its day pill where the others have theirs */
  @media (min-width: 60rem) {
    .history__text {
      display: grid;
      grid-template-columns: 12rem minmax(0, 1fr) 5.25rem;
      column-gap: 0.75rem;
      min-height: 0;
    }

    .history__pills {
      display: contents;
    }

    .history__when {
      grid-column: 2;
      justify-self: center;
    }

    .history__device {
      grid-column: 3;
      justify-self: end;
    }

    .history__row[aria-expanded="true"] .history__text {
      display: block;
    }
  }
}
