/*
 * The console at /home.
 *
 * Lifted out of resources/views/v4/home.blade.php, where it was 29KB of inline
 * style on a page every dashboard visit loads. @see that view.
 */
/* ═══════════════════════════════════════════════════════════ the console
   Three layouts were built and put in front of the owner on the real page with
   the real data. This is the one that was chosen. The composer is a card - it
   is a thing you fill in - and the checks are not: a table boxed inside a
   rounded panel reads as two objects rather than one place to work.

   No accent bars on cards anywhere. A coloured stripe over a white box is the
   house style of every admin template ever shipped and it says nothing about
   the number underneath it. */

/* ------------------------------------------------------------------ shell */
.v4-wrap { padding-bottom: 3rem; max-width: 104rem; margin-inline: auto; }

/* 104rem is a sensible page on a laptop and a waste on a wide monitor: at
   1900px it stopped the dashboard about 120px short on each side while the
   checks table underneath was still truncating its own columns. Past 1600px
   the cap lifts and that width goes to the table instead of to the gutter.
   The second ceiling is only there so an ultrawide does not stretch a row
   across three feet of glass.

   Lifting the cap took the margin with it, though, and a page with no margin
   at all reads as content jammed against the chrome rather than as a page. So
   the gutter comes back as padding rather than as a cap - about half what the
   cap used to leave, which is enough for the eye to register an edge and not
   so much that the table goes back to truncating columns to pay for it.

   Padding and not a cap, because the two spend the leftover width differently:
   a cap gives every pixel over 104rem to the gutter, padding takes a fixed
   slice and gives the rest to the table. */
@media (min-width: 1600px) { .v4-wrap { max-width: none; padding-inline: 1.75rem; } }
@media (min-width: 1920px) { .v4-wrap { padding-inline: 2.5rem; } }
@media (min-width: 2160px) { .v4-wrap { max-width: 132rem; } }

/* The layout's own gutter used to be trimmed here as well - the page card's
   20px margin off the window and the container's 30px inside it cut to 12 and
   16 - which is what made the dashboard sit flush against the window on a big
   screen. Those are back at the layout's defaults; the margin above is now the
   only thing setting the page's edge, in one place, on the element that has
   the page in it.

   .v4-dash is still on the body for anything else that needs to know which
   page this is. @see resources/views/v4/home.blade.php */
.v4-head-note {
    margin-inline-start: .55rem;
    font-family: var(--cqn-sans);
    font-size: .8125rem; font-weight: 450; letter-spacing: 0;
    color: var(--secondary-foreground, #71717a);
}
/* On a phone the note wraps into the serif greeting and reads as one broken
   sentence. Its own line, its own size. */
@media (max-width: 640px) {
    /* On a 375px phone the layout spends 88 pixels - a quarter of the screen -
       on side gutters: 20px of card margin and 24px of container padding, twice
       over. That is a margin sized for a monitor being charged to a phone,
       where the table underneath is down to a 104px name column to pay for it.
       Halved on the dashboard only; every other page in this layout is a column
       of text and wants the measure it has. */
    .v4-dash #main_content_container { margin-inline: .625rem; }
    .v4-dash .kt-container-fluid { padding-inline: .75rem; }

    .v4-head-note { display: block; margin: .15rem 0 0; font-size: .78rem; }
    .v4-head { align-items: flex-start; }

    /* The credits strip is one unbreakable line - two balances and two
       buttons - which is wider than the phone it is being drawn on, and it
       pushed the whole PAGE sideways rather than scrolling inside anything.
       Two rows instead: the reading on its own line, the buttons sharing the
       one under it. */
    .v4-head-actions { width: 100%; }
    .v4-wrap .v4-meter { display: flex; width: 100%; flex-wrap: wrap; gap: .4rem; }
    .v4-wrap .v4-meter-counts { flex: 1 0 100%; margin-inline-end: 0; }
    .v4-wrap .v4-meter-ask,
    .v4-wrap .v4-meter-add { flex: 1 1 0; }
    .v4-table-scroll th:nth-child(1), .v4-table-scroll td:nth-child(1) { width: 38%; }
    .v4-table-scroll th:nth-child(2), .v4-table-scroll td:nth-child(2) { width: 20%; }
    .v4-table-scroll th:nth-child(3), .v4-table-scroll td:nth-child(3) { width: 42%; }
    .v4-table-scroll { max-height: none; }
    .cqn-table td { padding-block: .8rem; padding-inline: .5rem; }

    /* All three stay, labels and all. Dropping one would mean choosing which
       of peer, web and AI a phone reader does not need, and there is no answer
       to that; the rule just gets shorter, which costs precision the number
       above it is already carrying. */
    .v4-wrap .v4-bars,
    .v4-wrap .v4-scores-sorts { gap: .25rem; }
    .v4-wrap .v4-bar-label { font-size: .5rem; }
    .v4-wrap .v4-bar-top { height: 1rem; gap: .2rem; }
    .v4-wrap .v4-bar-num { font-size: .8rem; }
    /* The padlock-only chip is not a phone rule - it starts wherever "Unlock"
       stops fitting, which is well up into laptop widths. @see the
       max-width:1279px block near the end of this file. */
    .cqn-meter { display: none; }
    .cqn-score-src { display: none; }
    .cqn-badge { padding: 4px 7px; font-size: 10px; }

    .v4-wrap .v4-scores-title { font-size: 9px; }
}

.v4-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: .9rem; margin-bottom: .9rem; }
.v4-head-actions { display: flex; align-items: center; gap: .5rem; }

/* No stripes on anything. A coloured bar over a white box is the house style
   of every admin template ever shipped and says nothing about what is in it. */
.v4-wrap *::before { }
.v4-wrap .cqn-stat::before, .v4-wrap .cqn-panel::before { content: none !important; }

.v4-shell { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.1rem; align-items: start; }
/* The New check column is the same job as the upload rail on a check's Files
   page, so from 1600 up it is the same width: 30rem. It grows in steps rather
   than at 1100, because the checks table next to it allocates 37rem to its
   four fixed columns — at 1180 the name column is already down to ~50px, and
   every rem the rail takes there comes straight out of it.
   @see .cq-asgn-grid in assignment-submission-1.css */
@media (min-width: 1100px) { .v4-shell { grid-template-columns: 25.5rem minmax(0, 1fr); gap: 1.4rem; } }
@media (min-width: 1400px) { .v4-shell { grid-template-columns: 27rem minmax(0, 1fr); } }
@media (min-width: 1600px) { .v4-shell { grid-template-columns: 30rem minmax(0, 1fr); } }

.v4-rail-inner {
    border: 1px solid var(--border, #e4e4e7);
    border-radius: var(--cqn-r-lg);
    background: var(--background, #fff);
    overflow: hidden;
}

/* --------------------------------------------------------------- the rail */
.v4-rail { min-width: 0; }
.v4-rail-head { padding: .95rem 1rem .8rem; border-bottom: 1px solid var(--input, #e4e4e7); }
.v4-rail-body { padding: .9rem 1rem; display: flex; flex-direction: column; gap: .85rem; flex: 1; }
/* One line: price, then Repeat last, then Clear, then the button. The commit
   action had a full-width row of its own for a while, which bought it weight
   at the cost of a footer two rows tall; the owner wants the footer flat.
   flex-wrap stays so a long label (the guest page's, for one) can still break
   to its own line instead of crushing the rest. */
.v4-rail-foot {
    display: flex; flex-wrap: wrap; align-items: center; gap: .55rem .75rem;
    padding: .75rem 1rem; border-top: 1px solid var(--input, #e4e4e7);
    background: var(--background, #fff);
}

/* The price sits with the button that spends it. */
.v4-cost {
    flex: 1; min-width: 0;
    font-size: .75rem; font-weight: 600;
    color: var(--secondary-foreground, #71717a);
    font-variant-numeric: tabular-nums;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.v4-rail-foot .v4-cost { order: 1; }
.v4-rail-foot .v4-repeat-last { order: 2; }
.v4-rail-foot #v4Clear { order: 3; }
.v4-rail-foot #v4Start {
    order: 4;
    flex: 1 1 auto;
    justify-content: center;
    height: 2.35rem;
    border-radius: .625rem;
    font-size: .8125rem;
}
.v4-rail-foot #v4Start i { font-size: .95rem; }
.v4-repeat-last {
    display: inline-flex; align-items: center; gap: .25rem;
    padding: .34rem .25rem;
    border: 0; background: none;
    color: var(--cqn-accent, #2563eb);
    font-size: .72rem; font-weight: 620;
    white-space: nowrap; cursor: pointer;
}
.v4-repeat-last:hover { text-decoration: underline; }
.v4-repeat-last:focus-visible { outline: 2px solid var(--cqn-accent); outline-offset: 2px; border-radius: 4px; }
.v4-repeat-last i { font-size: .75rem; }
.v4-queue { padding: .8rem 1rem 1rem; border-top: 1px solid var(--input, #e4e4e7); }
.v4-queue .cqn-eyebrow { margin-bottom: .5rem; }

/* The upload panel. One bar and the word "Uploading" is the whole truth for one
   zip and tells you nothing about a hundred, so the head carries the percentage
   and the foot carries the count, the rate and the time left. */
.v4-up-head { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .45rem; }
.v4-up-head .cqn-row-name { flex: 1; min-width: 0; }
.v4-up-pct {
    font-size: .75rem; font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--cqn-accent);
}
.v4-up-note { margin-top: .4rem; font-variant-numeric: tabular-nums; }

/* Fast enough to keep up with the numbers above it, slow enough not to snap.
   The bar's own .25s ease is tuned for a single big jump; a batched upload
   reports many small ones. */
.v4-up-bar > span { transition: width .18s linear; }

/* Long lists scroll instead of pushing the Start button off the rail. Once a
   hundred folders are queued the composer is taller than the window, and the
   button that starts them is the one thing that must stay reachable. */
.cqn-list.is-long { max-height: 15rem; overflow-y: auto; }
.cqn-list.is-long::-webkit-scrollbar { width: 9px; }
.cqn-list.is-long::-webkit-scrollbar-thumb { background: var(--input, #e4e4e7); border-radius: 999px; }

/* -------------------------------------------------- where the code comes from

   Five tiles, icon over label. This was a segmented control - five chips in a
   3px track - and in a 25rem rail that left each one 70px wide with a .74rem
   label and a .8rem icon side by side inside it: nothing you would call a
   target, and the active chip was a white rectangle on a grey rectangle. Stood
   up, each mode gets the same 70px as a square instead of a sliver, the icon
   is big enough to read, and the active tile is tinted rather than merely
   lighter than its neighbours. */
/* ------------------------------------------------------------- mode picker

   One control, not five buttons.

   Five separately bordered boxes with gaps between them is the drawing of five
   things that each DO something - five actions side by side. Nothing in it said
   "these are five names for one slot, and one of them is currently chosen", so
   the row read as a toolbar rather than as the tab strip it is.

   The tile itself was right and is untouched: icon over label, five across. What
   changed is everything around it. The borders and the gaps come off the tiles
   and go onto the GROUP, which becomes a single recessed track; the chosen tile
   then lifts out of that track in white, with the shadow doing the lifting. That
   is the ordinary segmented-control shape, and it reads as one-of-five without
   any label having to say so. */
.v4-modes {
    display: grid; grid-template-columns: repeat(5, 1fr);
    gap: .125rem;
    padding: .25rem;
    border: 1px solid var(--input, #e4e4e7);
    border-radius: var(--cqn-r-lg);
    background: var(--muted, #f4f4f5);
}
.v4-mode {
    display: grid; justify-items: center; gap: .3rem;
    min-width: 0;
    padding: .5rem .2rem .45rem;
    /* Transparent rather than absent, so the tile does not resize by a pixel
       on either axis at the moment it becomes the selected one. */
    border: 1px solid transparent;
    border-radius: var(--cqn-r-md);
    background: transparent;
    color: var(--secondary-foreground, #71717a);
    font-size: .68rem; font-weight: 560;
    cursor: pointer;
    transition: color .15s ease, background-color .15s ease, box-shadow .15s ease;
}
.v4-mode i { font-size: 1.05rem; }
.v4-mode .cqn-src-logo { width: 1.05rem; height: 1.05rem; }
.v4-mode:hover:not(.is-active) { background: rgba(255, 255, 255, .65); color: var(--mono, #18181b); }
.v4-mode:focus-visible { outline: 2px solid var(--cqn-accent); outline-offset: -1px; }
.v4-mode.is-active {
    background: var(--background, #fff);
    border-color: var(--input, #e4e4e7);
    box-shadow: 0 1px 2px rgba(16, 24, 40, .07), 0 1px 1px rgba(16, 24, 40, .04);
    color: var(--cqn-accent);
    font-weight: 640;
}

.dark .v4-modes { background: rgba(255, 255, 255, .04); border-color: rgba(255, 255, 255, .08); }
.dark .v4-mode { background: transparent; border-color: transparent; }
.dark .v4-mode:hover:not(.is-active) { background: rgba(255, 255, 255, .06); color: #e4e4e7; }
.dark .v4-mode.is-active {
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .14);
    box-shadow: none;
}
.v4-mini-code { min-height: 9rem; font-size: .78125rem; }
/* ------------------------------------------------------------- drop target
   The box is the biggest thing in the rail and the one you are meant to throw
   a file at, so it is laid out as a target rather than as a centred label: the
   invitation sits in the optical middle, and the rules that govern the drop sit
   on a footer line at the bottom. Before this the box grew to 13rem and its
   contents did not, which left a 2.75rem pill floating in an empty rectangle.

   Everything inside is pointer-events: none. A drop target with children fires
   dragleave every time the cursor crosses one of them, so the border flickered
   between styles while a file was held over it; with the children transparent
   to the mouse, every event lands on the box, including the click that opens
   the file dialog. */
.v4-wrap .cqn-drop {
    min-height: 13rem;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* White, with the dashes painted rather than bordered. `border-style:
       dashed` draws whatever dash the browser feels like (Chrome: 3px on,
       3px off at this weight) and offers no way to change it, so the outline
       read as a stitched hem. The pattern is an SVG rect in the background
       instead: 1.5px, 8px on, 10px off, which is sparse enough to read as
       "something goes here" without looking sewn. The rect's stroke is
       centred on the box edge, so it is drawn at 3px and the outer half is
       clipped away by the radius; the transparent border keeps 1.5px of room
       for it and holds the geometry still when is-over swaps in a real one.

       The fill went back to the card's own white in the same pass: the grey
       well was there to separate the box from a hairline border, and the
       dashes now do that on their own. */
    border: 1.5px solid transparent;
    border-radius: 12px;
    background-color: var(--background, #fff);
    background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Crect%20width%3D'100%25'%20height%3D'100%25'%20fill%3D'none'%20rx%3D'12'%20ry%3D'12'%20stroke%3D'%23d4d4d8'%20stroke-width%3D'3'%20stroke-dasharray%3D'8%2010'%2F%3E%3C%2Fsvg%3E");
    background-origin: border-box;
    background-clip: border-box;
    background-size: 100% 100%;
    background-repeat: no-repeat;
}

/* The base states in cq-next.css (.cqn-drop:hover, .cqn-drop.is-over) set
   `background` as a shorthand, which would wipe the painted dashes, so both
   are restated here at a weight that wins: blue dashes on a faint wash under
   the pointer, and a solid blue edge once a file is actually held over it.
   The geometry longhands are repeated on :hover because the shorthand below
   them resets those too. */
.v4-wrap .cqn-drop:hover {
    background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Crect%20width%3D'100%25'%20height%3D'100%25'%20fill%3D'none'%20rx%3D'12'%20ry%3D'12'%20stroke%3D'%231379F0'%20stroke-width%3D'3'%20stroke-dasharray%3D'8%2010'%2F%3E%3C%2Fsvg%3E");
    background-color: rgba(19, 121, 240, .04);
    background-origin: border-box;
    background-clip: border-box;
    background-size: 100% 100%;
    background-repeat: no-repeat;
}
.v4-wrap .cqn-drop.is-over {
    background-image: none;
    background-color: var(--cqn-accent-soft);
    border-color: var(--cqn-accent);
    border-style: solid;
}

.v4-drop-in {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .1rem;
    padding: 1.35rem 1.1rem 1.1rem;
    text-align: center;
    pointer-events: none;
}

/* Ink-coloured, on a soft disc. On the grey well the glyph stood on its own
   at 2.75rem; on white it needs a shape behind it or the box is a line of
   text with a picture over it. The disc is the panel's --muted, the one grey
   the page already has, and it takes the accent wash on hover and drag along
   with the glyph, which is where the colour actually carries information. */
.v4-drop-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 4rem;
    height: 4rem;
    margin-bottom: .9rem;
    border-radius: 999px;
    background: var(--muted, #f4f4f5);
    color: var(--mono, #3f3f46);
    font-size: 2rem;
    line-height: 1;
    transition: transform .18s ease, color .18s ease, background-color .18s ease;
}

.v4-drop-title {
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--mono, #18181b);
}

.v4-drop-sub { font-size: .8125rem; color: var(--secondary-foreground, #71717a); }
/* "browse" is the only part of that sentence you can act on, so it is the only
   part that looks like it. Weight, not colour: the box now carries a fill of
   its own and a blue word inside a grey well was the one loud pixel in it. */
.v4-drop-sub b { font-weight: 600; color: var(--mono, #3f3f46); }

.cqn-drop:hover .v4-drop-icon,
.cqn-drop.is-over .v4-drop-icon { color: var(--cqn-accent); background: rgba(19, 121, 240, .10); }

/* The two limits the endpoint enforces, in the place where the file is chosen
   rather than in the error that follows choosing the wrong one. */
.v4-drop-foot {
    flex: none;
    padding: .5rem .85rem;
    /* A solid hairline, not a dashed one: a second, finer dash under the
       painted outline read as a mismatched stitch. No band either; on a
       white box a grey strip is a footer, and this is a caption. */
    border-top: 1px solid var(--border, #ececef);
    background: transparent;
    font-size: .6875rem;
    line-height: 1.45;
    text-align: center;
    text-wrap: balance;
    color: var(--secondary-foreground, #71717a);
    pointer-events: none;
}

.cqn-drop:hover .v4-drop-icon { transform: translateY(-1px); }
.cqn-drop.is-over .v4-drop-icon { transform: translateY(-3px) scale(1.05); }

/* The is-over sweep in cq-next.css paints on ::after over the whole box; the
   footer keeps its hairline under it rather than being washed out by it. */
.cqn-drop.is-over .v4-drop-foot { border-top-color: var(--cqn-accent-line); }

/* The card's own surface on a dark page, the way the rail's other panels
   sit, with the dashes repainted in translucent white; the light SVG's grey
   would vanish against it. The is-over state has no image, so it needs no
   dark restatement. */
.dark .v4-wrap .cqn-drop {
    background-color: transparent;
    background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Crect%20width%3D'100%25'%20height%3D'100%25'%20fill%3D'none'%20rx%3D'12'%20ry%3D'12'%20stroke%3D'rgba(255%2C255%2C255%2C.24)'%20stroke-width%3D'3'%20stroke-dasharray%3D'8%2010'%2F%3E%3C%2Fsvg%3E");
}
.dark .v4-wrap .cqn-drop.is-over { background-image: none; }
.dark .v4-drop-icon { background: rgba(255, 255, 255, .07); }
.dark .v4-drop-title { color: #e4e4e7; }
.dark .v4-drop-icon, .dark .v4-drop-sub b { color: #d4d4d8; }
.dark .v4-drop-sub, .dark .v4-drop-foot { color: #a1a1aa; }
.dark .v4-drop-foot { border-top-color: rgba(255, 255, 255, .10); }

/* An input, not a bare code pane: it had no border, no padding and a resize
   grabber in the corner, which reads as something half-built. */
/* A request, shown as one: verb and path in a header strip, the call itself in
   a terminal, and a copy button that is the whole point of the pane. */
.v4-api { border: 1px solid var(--input, #e4e4e7); border-radius: var(--cqn-r-md); overflow: hidden; }
.v4-api-head {
    display: flex; align-items: center; gap: .45rem;
    padding: .35rem .5rem;
    background: var(--muted, #f8f8f9);
    border-bottom: 1px solid var(--input, #e4e4e7);
}
.v4-api-verb {
    padding: .1rem .35rem; border-radius: 4px;
    background: #16a34a; color: #fff;
    font-size: .55rem; font-weight: 700; letter-spacing: .04em;
}
.v4-api-path { font-family: var(--cqn-mono); font-size: .68rem; color: var(--mono, #18181b); }
.v4-api-copy {
    margin-inline-start: auto;
    padding: .14rem .45rem; border-radius: 4px;
    border: 1px solid var(--input, #e4e4e7); background: var(--background, #fff);
    color: var(--secondary-foreground, #71717a); font-size: .62rem; cursor: pointer;
}
.v4-api-copy:hover { color: var(--mono, #18181b); border-color: var(--ring, #d4d4d8); }
.v4-api-code {
    margin: 0; padding: .6rem .7rem;
    max-height: 10rem;
    background: #0f172a; color: #e2e8f0;
    font-family: var(--cqn-mono); font-size: .64rem; line-height: 1.75;
    white-space: pre; overflow: auto;
}
.v4-api-code::-webkit-scrollbar { height: 6px; width: 6px; }
.v4-api-code::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, .22); border-radius: 999px; }
.v4-api-facts {
    display: grid; gap: .14rem;
    margin: .55rem 0 0; padding: 0; list-style: none;
    font-size: .7rem; color: var(--secondary-foreground, #71717a);
}
.v4-api-facts li { display: flex; align-items: center; gap: .4rem; }
.v4-api-facts li::before {
    content: ""; flex: none;
    width: .3rem; height: .3rem; border-radius: 50%;
    background: var(--cqn-ok, #16a34a);
}
.v4-api-links { display: flex; gap: .4rem; margin-top: .6rem; }
.dark .v4-api { border-color: rgba(255, 255, 255, .1); }
.dark .v4-api-head { background: rgba(255, 255, 255, .04); border-color: rgba(255, 255, 255, .1); }
.dark .v4-api-path { color: #e4e4e7; }
.dark .v4-api-copy { background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .12); color: #e4e4e7; }

.v4-repo-input {
    width: 100%;
    min-height: 4.5rem;
    padding: .5rem .65rem;
    border: 1px solid var(--input, #e4e4e7);
    border-radius: var(--cqn-r-md);
    background: var(--background, #fff);
    color: var(--mono, #18181b);
    font-family: var(--cqn-mono);
    font-size: .73rem;
    line-height: 1.6;
    resize: none;
}
.v4-repo-input:focus { outline: none; border-color: var(--cqn-accent); box-shadow: 0 0 0 3px var(--cqn-accent-soft); }
.v4-repo-input::placeholder { color: var(--secondary-foreground, #a1a1aa); }
.v4-repo-status { display: grid; gap: .2rem; margin-top: .4rem; }
.v4-repo-line { display: flex; align-items: center; gap: .35rem; font-size: .7rem; color: var(--secondary-foreground, #71717a); }
.v4-repo-line.is-ok { color: var(--cqn-ok); }
.v4-repo-line.is-bad { color: var(--cqn-danger); }
.v4-repo-line.is-unknown { color: var(--cqn-warn); }
.v4-repo-line i { font-size: .8rem; }

.v4-repo-foot { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-top: .35rem; }

/* ------------------------------------------------------- detection sources
   Rows, not pills: a mark, a name, a state. Six identical blue capsules made
   every source look like the same decision. */
/* Two columns at tighter metrics. One column per source was unambiguous and
   six rows tall, which pushed the button that starts the check off the screen;
   the labels fit at this size, which was the actual problem. */
/* ── Compare against ──────────────────────────────────────────────────────
   Eight tiles in a two column grid took a third of the rail to say something
   the person setting up a check reads once and changes rarely. One row says
   what it will be compared against; the panel is where it is changed. */
.v4-pick { position: relative; }

.v4-pick-btn {
    display: flex; align-items: center; gap: .6rem; width: 100%;
    padding: .68rem .8rem;
    border: 1px solid var(--border, #e4e4e7); border-radius: 9px;
    background: var(--background, #fff);
    font-size: .875rem; color: var(--mono, #18181b);
    cursor: pointer; text-align: left;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.v4-pick-btn:hover { border-color: var(--cqn-accent); }
.v4-pick-btn:focus-visible { outline: none; border-color: var(--cqn-accent); box-shadow: 0 0 0 3px rgba(37, 99, 235, .14); }
.v4-pick.is-open .v4-pick-btn { border-color: var(--cqn-accent); box-shadow: 0 0 0 3px rgba(37, 99, 235, .12); }

/* The chosen sources' own marks, overlapped, so the row is readable as a
   picture before it is read as a sentence. */
.v4-pick-marks { display: inline-flex; flex: none; align-items: center; }
/* Each mark on a tint of its own brand rather than on white.

   The colours were always full strength; the marks still read as pale, because
   a 15px icon inside a white disc with a hairline grey border is mostly white
   disc. Backing each one in its own colour makes it a token you can pick out
   at a glance, which is the entire reason for showing a logo instead of a dot.
   The globe and the book are outline marks and carried even less weight than
   the filled ones, so their strokes are thickened to match. */
.v4-pick-marks > span {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.55rem; height: 1.55rem; margin-inline-end: -.32rem;
    border-radius: 50%;
    color: var(--brand, currentColor);
    background: var(--background, #fff);
    background: color-mix(in srgb, var(--brand, #71717a) 14%, var(--background, #fff));
    box-shadow: 0 0 0 1px var(--background, #fff),
                inset 0 0 0 1px color-mix(in srgb, var(--brand, #71717a) 26%, transparent);
}
.v4-pick-marks > span:last-child { margin-inline-end: 0; }
.v4-pick-marks svg { width: 16px; height: 16px; opacity: 1; }
.v4-pick-marks svg[stroke] { stroke-width: 1.8; }

/* Same problem in the panel's own rows: a 17px mark on the flat grey wash read
   as decoration rather than as the source it names. */
.v4-pick-mark {
    background: color-mix(in srgb, var(--brand, #71717a) 12%, var(--muted, #f4f4f5));
}
.v4-src .cqn-src-logo,
.v4-pick-mark .cqn-src-logo { opacity: 1; }
.v4-pick-mark svg[stroke] { stroke-width: 1.7; }

.v4-pick-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v4-pick-caret { flex: none; font-size: .6rem; color: var(--secondary-foreground, #71717a); transition: transform .15s ease; }
.v4-pick.is-open .v4-pick-caret { transform: rotate(180deg); }

/* Fixed, not absolute: the composer card clips its own corners with
   overflow:hidden and sits inside the panel's scroll container, so an
   absolutely positioned panel is cut off at the card's edge. Placed by
   script against the trigger's box. */
.v4-pick-menu {
    position: fixed; z-index: 2147480000;
    /* A column so the list is the part that gives, and the peer row and the
       footer stay visible however little room there is. */
    display: flex; flex-direction: column;
    border: 1px solid var(--border, #e4e4e7); border-radius: 11px;
    background: var(--background, #fff);
    box-shadow: 0 18px 40px -16px rgba(16, 24, 40, .3), 0 2px 6px -2px rgba(16, 24, 40, .12);
    overflow: hidden;
    animation: v4pick-in .13s ease both;
}
@keyframes v4pick-in { from { opacity: 0; transform: translateY(-4px); } }
/* Opening upward, it should rise into place rather than drop into it. */
.v4-pick.is-up .v4-pick-menu { animation-name: v4pick-in-up; }
@keyframes v4pick-in-up { from { opacity: 0; transform: translateY(4px); } }
@media (prefers-reduced-motion: reduce) { .v4-pick-menu { animation: none; } }

/* Peer is not a choice, so it is not a checkbox. */
.v4-pick-fixed { flex: none; }
.v4-pick-rule { flex: none; }
.v4-pick-foot { flex: none; }

.v4-pick-fixed,
.v4-src {
    display: flex; align-items: center; gap: .7rem; width: 100%;
    padding: .72rem .85rem;
    border: 0; background: none; text-align: left;
    font-size: .875rem; color: var(--mono, #18181b);
}
.v4-pick-fixed { background: var(--muted, #f4f4f5); }
.v4-src { cursor: pointer; transition: background .1s ease; }
.v4-src:hover:not(:disabled) { background: var(--muted, #f4f4f5); }
.v4-src:focus-visible { outline: none; background: var(--muted, #f4f4f5); box-shadow: inset 2px 0 0 var(--cqn-accent); }

.v4-pick-mark {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 1.85rem; height: 1.85rem; border-radius: 8px;
    background: var(--muted, #f4f4f5); color: var(--brand, currentColor);
}
.v4-pick-fixed .v4-pick-mark { background: var(--background, #fff); }
.v4-pick-mark svg { width: 17px; height: 17px; }

.v4-pick-label { flex: 1; min-width: 0; display: grid; }
.v4-pick-label b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v4-pick-label > span {
    font-size: .78rem; color: var(--secondary-foreground, #71717a);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.v4-pick-always {
    flex: none; font-size: .72rem; font-weight: 600; letter-spacing: .02em;
    padding: .2rem .5rem; border-radius: 6px;
    background: var(--background, #fff); color: var(--cqn-ok, #16a34a);
    border: 1px solid var(--border, #e4e4e7);
}

/* The checkbox: a box that is empty until it is not. */
.v4-src-box {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: 1.3rem; height: 1.3rem; border-radius: 6px;
    border: 1.5px solid var(--border, #d4d4d8);
    color: #fff; background: transparent;
    transition: background .12s ease, border-color .12s ease;
}
.v4-src-box i { font-size: .7rem; opacity: 0; transition: opacity .12s ease; }
/* One accent for every box. This used to paint each checkbox in its own
   source's brand colour, so a column of controls that are all in the same
   state came out blue, black, orange, black: it read as five different kinds
   of thing rather than five things that are all switched on. The logo beside
   it still carries the brand colour, which is where identity belongs; the
   checkbox carries state, and there is only one state to show. */
.v4-src.is-on .v4-src-box { background: var(--cqn-accent); border-color: var(--cqn-accent); }
.v4-src.is-on .v4-src-box i { opacity: 1; }

.v4-src.is-locked { cursor: pointer; opacity: .6; }
.v4-pick-tag {
    flex: none; display: inline-flex; align-items: center; gap: .2rem;
    font-size: .72rem; font-weight: 600;
    padding: .2rem .45rem; border-radius: 6px;
    background: var(--muted, #f4f4f5); color: var(--secondary-foreground, #71717a);
}
.v4-pick-tag i { font-size: .68rem; }

.v4-pick-rule {
    display: flex; align-items: center; gap: .5rem;
    padding: .45rem .65rem .1rem;
}
.v4-pick-rule > span {
    font-size: .7rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
    color: var(--secondary-foreground, #71717a);
}
.v4-pick-rule::after { content: ''; flex: 1; height: 1px; background: var(--border, #e4e4e7); }

.v4-pick-list { flex: 1 1 auto; min-height: 0; max-height: 24rem; overflow-y: auto; overscroll-behavior: contain; padding-bottom: .15rem; }

/* The one row that is not a choice gets to use two lines to say why. */
.v4-pick-fixed .v4-pick-label > span { white-space: normal; overflow: visible; line-height: 1.35; }

.v4-pick-foot {
    display: flex; align-items: center; gap: .6rem;
    padding: .6rem .85rem; border-top: 1px solid var(--border, #e4e4e7);
    font-size: .78rem; color: var(--secondary-foreground, #71717a);
}
.v4-pick-foot > span { flex: 1; }
.v4-pick-link {
    border: 0; background: none; padding: 0; cursor: pointer;
    font-size: .78rem; font-weight: 600; color: var(--cqn-accent);
}
.v4-pick-link:hover { text-decoration: underline; }

.dark .v4-pick-btn,
.dark .v4-pick-menu { background: #18181b; border-color: rgba(255,255,255,.12); }
.dark .v4-pick-fixed { background: rgba(255,255,255,.04); }
.dark .v4-src:hover:not(:disabled) { background: rgba(255,255,255,.05); }

/* The source marks inside the panel: each keeps its own colour, because a
   black octocat and an orange Stack Overflow tray are recognised before the
   label is read. Tinting all seven the same blue threw that away. */
.v4-src .cqn-src-logo { width: 1.15rem; height: 1.15rem; color: var(--brand, currentColor); }
.v4-src.is-locked .cqn-src-logo { filter: grayscale(1); opacity: .6; }

.v4-plan {
    display: flex; align-items: center; gap: .35rem;
    padding: .5rem 1rem; border-top: 1px solid var(--input, #e4e4e7);
    background: var(--muted, #f8f8f9);
    font-size: .73rem; flex-wrap: wrap; row-gap: .15rem;
}
.v4-plan-k { font-size: .5625rem; letter-spacing: .07em; text-transform: uppercase; color: var(--secondary-foreground, #71717a); }
.v4-plan-v { color: var(--mono, #18181b); font-weight: 545; }
.v4-plan-dot { color: var(--secondary-foreground, #71717a); }

/* --------------------------------------------------------------- the main */
.v4-main {
    min-width: 0; display: flex; flex-direction: column;
    border: 1px solid var(--border, #e4e4e7);
    border-radius: var(--cqn-r-lg);
    background: var(--background, #fff);
    box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
    overflow: hidden;
}

/* ================================================================= meter

   Two balances and the two ways to get more of them, in the page header next
   to the greeting.

   This was a tinted, shadowed capsule with four rounded controls packed inside
   it: a pill full of pills. Five surfaces and three fills to say "18 checks, 5
   HyperScans, buy more", which is a reading and two buttons. The chrome was
   doing more work than the numbers were.

   Nothing is boxed now except the two buttons. The balances are set as plain
   text - number first and firm, label after it and quiet - because that is
   what they are: something you glance at, not something you operate. The
   actions follow with exactly one step of rank between them, outlined for the
   ask and solid for the buy, and that step is the only emphasis in the strip.

   Colour only when a balance is the reason something will not run. A healthy
   balance is not news. */

.v4-meter {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
}

/* The reading, as one group: the phone layout gives it a row of its own
   without having to unpick the two buttons that follow it. */
.v4-meter-counts {
    display: inline-flex;
    align-items: center;
    gap: .1rem;
    margin-inline-end: .5rem;
}

.v4-meter-cell {
    display: inline-flex;
    align-items: baseline;
    gap: .3rem;
    padding: .3rem .4rem;
    background: none;
    border: 0;
    border-radius: var(--cqn-r-sm, 8px);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color .12s ease;
}
.v4-meter-cell:hover { background: var(--muted, #f4f4f5); }
.v4-meter-cell:focus-visible { outline: 2px solid var(--cqn-accent, #1379F0); outline-offset: 1px; }

.v4-meter-n {
    font-size: .9375rem;
    font-weight: 660;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: var(--mono, #18181b);
}

.v4-meter-k {
    font-size: .8125rem;
    font-weight: 450;
    line-height: 1;
    color: var(--secondary-foreground, #71717a);
}

/* Empty is a stop, not a warning: nothing runs, so that one keeps its red.
   Low used to go amber, which read as an error on an account that is working
   fine, so a low balance now looks like any other number. */
.v4-meter-cell.is-empty .v4-meter-n { color: #dc2626; }
.v4-meter-cell.is-low   .v4-meter-n { color: var(--mono, #18181b); }

/* No bar left for a rule to divide. A dot is all the separation two numbers
   standing in open space need. */
.v4-meter-rule {
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: var(--input, #d4d4d8);
    flex: none;
}

/* Second in the row, not invisible. Drawn as bare grey text it read as a
   label and nobody pressed it, so it keeps a button's outline and height -
   the same ghost button the rest of the console uses. @see .cqn-btn-ghost */
.v4-meter-ask {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 2rem;
    padding: 0 .75rem;
    font-size: .8125rem;
    font-weight: 560;
    line-height: 1;
    color: var(--secondary-foreground, #71717a);
    background: transparent;
    border: 1px solid var(--input, #e4e4e7);
    border-radius: var(--cqn-r-sm, 8px);
    cursor: pointer;
    white-space: nowrap;
    transition: color .15s ease, background-color .15s ease, border-color .15s ease;
}
.v4-meter-ask:hover {
    color: var(--mono, #18181b);
    background: var(--muted, #f4f4f5);
    border-color: var(--border, #d4d4d8);
}
.v4-meter-ask:focus-visible { outline: 2px solid var(--cqn-accent, #1379F0); outline-offset: 2px; }

/* The one loud thing in the header, and the only reason it is allowed to be
   loud is that everything beside it stopped competing. */
.v4-meter-add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 2rem;
    padding: 0 .85rem;
    font-size: .8125rem;
    font-weight: 600;
    line-height: 1;
    color: #fff;
    background: var(--cqn-accent, #1379F0);
    border: 1px solid transparent;
    border-radius: var(--cqn-r-sm, 8px);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color .15s ease;
}
.v4-meter-add:hover { background: var(--cqn-accent-deep, #0B5FCC); }
.v4-meter-add:focus-visible { outline: 2px solid var(--cqn-accent, #1379F0); outline-offset: 2px; }

/* Only the greys turn over. The blue button is the same button in both
   themes, and there is no surface left to invert. */
.dark .v4-meter-cell:hover { background: rgba(255, 255, 255, .08); }
.dark .v4-meter-n { color: var(--foreground, #fafafa); }
.dark .v4-meter-k { color: var(--secondary-foreground, #a1a1aa); }
.dark .v4-meter-cell.is-empty .v4-meter-n { color: #f87171; }
.dark .v4-meter-cell.is-low   .v4-meter-n { color: var(--foreground, #fafafa); }
.dark .v4-meter-rule { background: rgba(255, 255, 255, .22); }
.dark .v4-meter-ask {
    color: var(--secondary-foreground, #a1a1aa);
    border-color: rgba(255, 255, 255, .16);
}
.dark .v4-meter-ask:hover {
    color: var(--foreground, #fafafa);
    background: rgba(255, 255, 255, .08);
    border-color: rgba(255, 255, 255, .28);
}

/* Says what the rows are. Sits at the start of the filter row rather than on
   a band of its own above it, so the card spends one strip of height on the
   heading and the search instead of two. */
.v4-toolbar-head { min-width: 0; }
.v4-table-title { margin: 0; }

/* Only rendered when a retired folder-page link scoped the table to one
   folder. It explains a shorter table and offers the way back out; there is
   no folder chrome on this page otherwise. */
.v4-folder-scope {
    margin: .1rem 0 0;
    font-size: .8rem;
    color: var(--secondary-foreground, #71717a);
}
.v4-folder-scope strong { color: var(--foreground, #18181b); font-weight: 600; }
.v4-folder-scope a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.v4-folder-scope a:hover { color: var(--foreground, #18181b); }

/* The card's own header row. */
.v4-toolbar {
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    padding: .65rem .85rem; border-bottom: 1px solid var(--border, #e4e4e7);
}
/* Pushed to the end of the heading row. The auto margin also keeps it at
   the end when a folder scope line makes the heading too wide to share a
   line with, so the controls sit under each other rather than jumping to
   the opposite edge. */
.v4-search { position: relative; flex: 0 1 18rem; min-width: 9rem; margin-inline-start: auto; }
.v4-search i { position: absolute; inset-inline-start: .65rem; top: 50%; transform: translateY(-50%); color: var(--secondary-foreground, #71717a); font-size: .8rem; pointer-events: none; }
.v4-search input { padding-inline-start: 2rem; }

/* On a phone the heading and an 18rem field cannot share a line, and letting
   them try pushed the count and the All link onto a third row. Below this the
   heading takes the row to itself and the search shares the next one with
   them, which is the two-row shape the card had before the heading moved up. */
@media (max-width: 640px) {
    .v4-toolbar-head { flex: 1 0 100%; }
    .v4-search { flex: 1 1 8rem; margin-inline-start: 0; }
}

/* Fixed geometry: the columns were sized in rem, overflowed the panel, and
   the console scrolled sideways with Created off the edge.

   The height is whatever is in it. A fixed floor turned an account with one
   check into one row and eleven rows of nothing; the empty state brings its
   own padding, so there is nothing left for a floor to protect. */
.v4-table-scroll { flex: 0 1 auto; min-height: 0; max-height: min(62vh, 46rem); overflow-x: hidden; }
@media (min-width: 1100px) { .v4-table-scroll { max-height: calc(100vh - 17rem); } }
.v4-table-scroll .cqn-table { table-layout: fixed; width: 100%; }
/* Percentages, not rem, and nothing further down the file restating them in
   rem - which is what used to happen, and why the score column sat at a flat
   11rem however wide the table got.

   The name column was taking a third of a 1130px table to say "Benchmark v2:
   MOSS" in 200 pixels of type and 180 of air. It is the column that truncates
   without losing which check it is, so it is the one that pays for the scores:
   three bars are worth more space than a name is, and unlike the name they use
   every pixel they are given. */
.v4-table-scroll th:nth-child(1), .v4-table-scroll td:nth-child(1) { width: 46%; }
.v4-table-scroll th:nth-child(2), .v4-table-scroll td:nth-child(2) { width: 14%; }
/* Three bars, a word and a number each. Below about 10rem the bars are down
   to their own minimum and the cell stops being a comparison. */
/* The cell has a ceiling of its own (see .v4-bars), so past a point a bigger
   share here is reserved space nothing fills. This is roughly where the two
   meet on a 1440px screen. */
.v4-table-scroll th:nth-child(3), .v4-table-scroll td:nth-child(3) { width: 32%; }
/* The When column is gone - the time rides the subtitle under the name - and
   the name is what its 21% buys: it is the column that truncates. */
.v4-table-scroll th:nth-child(4), .v4-table-scroll td:nth-child(4) { width: 8%; }

/* Two lines in the first cell, so the row has a shape rather than a height. */
/* The whole table was sized for a designer's screenshot rather than for
   somebody reading forty rows of it: 13px names over 11px subtitles, 11px
   dates, 9.6px score labels. Everything below is one step up, and the row is
   taller to carry it - a table this dense is read by people who have been
   marking since eight in the morning. */
.cqn-table td { vertical-align: middle; padding-block: .85rem; }
.v4-name { font-size: .875rem; font-weight: 580; color: var(--mono, #18181b); overflow: hidden; text-overflow: ellipsis; }
.v4-sub { margin-top: .15rem; font-size: .75rem; color: var(--secondary-foreground, #71717a); }
.v4-when { font-size: .8125rem; color: var(--secondary-foreground, #71717a); }
.cqn-meter { width: 4.5rem; height: 6px; }
.cqn-score { font-size: .8125rem; }
.cqn-table td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cqn-table td.cqn-table-name { font-weight: 560; color: var(--mono, #18181b); }
.v4-peer-note {
    padding: .45rem 1rem;
    border-top: 1px solid var(--input, #e4e4e7);
    background: rgba(217, 119, 6, .07);
    color: #b45309;
    font-size: .7rem; line-height: 1.45;
}
.dark .v4-peer-note { background: rgba(245, 158, 11, .12); color: #fbbf24; border-color: rgba(255,255,255,.08); }

.v4-plan-cost {
    margin-inline-start: auto; padding-inline-start: .5rem; flex: none;
    font-size: .7rem; font-weight: 620; color: var(--mono, #18181b);
}

/* The upload's own report: one row per thing, saying what is happening to it. */
.v4-pick-row { display: flex; align-items: center; gap: .5rem; margin-top: .5rem; }
.v4-file.is-busy .cqn-row-icon { color: var(--cqn-accent); }
.v4-file.is-done .cqn-row-icon { color: var(--cqn-ok); }
.v4-file.is-failed .cqn-row-icon { color: var(--cqn-danger); }
.v4-file.is-failed .cqn-row-name { color: var(--cqn-danger); }
.v4-file .cqn-row-meta { white-space: nowrap; }

/* The duration sits after the time it belongs to, on the subtitle line.

   There used to be a second set of column widths here, in rem, restating the
   percentages above at the same specificity and later in the file - so the
   percentages never applied and every column was a fixed number of pixels
   whatever the table measured. That is why the score column stayed 11rem on a
   1400px screen. One set of allocations now, and they are the percentages. */
.cqn-table td.v4-when { white-space: nowrap; }
.v4-when-line { white-space: nowrap; }
.v4-runtime { font-size: .75rem; color: var(--secondary-foreground, #71717a); font-variant-numeric: tabular-nums; opacity: .85; }
.cqn-meter { width: 3.4rem; }

/* --------------------------------------------------------- the score bars

   Named .v4-bar and not .v4-meter, which is already the credits pill in the
   page header a few hundred lines up this file - and which this block was
   redefining the border, padding and background of until it was renamed.

   Peer, Web and AI across the cell: label, number, and a hairline rule under
   the number sized to the score.

   Two other shapes were built and looked at on the real page first. Three
   rings abreast spend the column on their own diameters and came out 29px each
   with a two-digit number shrunk to fit inside. Three full-width bars stacked
   read better, but the bar ran the whole column - so the label sat at one edge
   of the cell and its own number 300px away at the other - and cost 68px of
   row height against the 52px every other row wanted.

   This demotes the bar to a rule and promotes the number. The number is what
   is being read and it is now the largest thing in the cell; the rule is there
   to be compared down the column at a glance, which is the one job a bar does
   that a figure cannot. 55px rows, so nearly twice as many checks fit on a
   screen as the stacked bars allowed. */
.v4-bars {
    display: grid; grid-template-columns: repeat(3, 1fr);
    gap: .6rem;
    /* The rule is a comparison aid, not a gauge - past this a column stops
       being read and starts being measured, and the number beside it is
       already the precise answer. The ceiling rose from 16rem when the scores
       became the table's last column and inherited the old actions column's
       room. */
    max-width: 20rem;
}

.v4-bar {
    display: flex; flex-direction: column;
    min-width: 0;
    padding: 0; border: 0; background: none;
    color: var(--secondary-foreground, #71717a);
    font: inherit; text-align: start;
    /* Each bar measures itself, so the caption's fate is decided by the room
       this column actually has - not by a viewport width that guesses at it
       through the sidebar, the rail and the table's percentages. */
    container-type: inline-size;
}
button.v4-bar { cursor: pointer; }

/* Label and number on one line, sharing a baseline. The label used to be a
   row of its own above the figure, which made every cell two lines tall for
   a three-letter word; beside the number it still tells the three apart and
   the cell is the height of the number. A fixed height so the rules under
   the three numbers sit on one line whatever the row holds - a figure, a
   dash, or the lock chip. */
.v4-bar-top {
    display: flex; align-items: baseline; gap: .25rem;
    height: 1.3rem; min-width: 0;
}
/* The figure never gives up width: when the column runs out of room it is
   the caption that clips, not the number it captions. */
.v4-bar-top .v4-bar-num { flex: none; }
.v4-bar-top .v4-bar-label { min-width: 0; }

/* The caption steps down before it clips: full size where "100%" and "PEER"
   both fit, smaller where the column is tight, gone where there is no room -
   the sort links in the header sit on this same three-column grid, so a
   naked number still has its name directly above it. Mid-word clipping
   ("PEI", "WE") is the one state that is never right. Browsers without
   container queries keep the old clipping, which is what they had before. */
@container (max-width: 64px) { .v4-bar-label { font-size: .55rem; } }
@container (max-width: 52px) { .v4-bar-label { display: none; } }

/* 9.6px, not 9. On a 1366 laptop - the width most of them actually are - the
   smaller size put PEER/WEB/AI right at the edge of being read rather than
   recognised, and this label is the only thing telling the three numbers
   apart. */
.v4-bar-label {
    font-size: .66rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
    color: var(--secondary-foreground, #71717a);
    line-height: 1;
    white-space: nowrap; overflow: hidden; text-overflow: clip;
}

.v4-bar-num {
    display: flex; align-items: baseline;
    font-size: 1.075rem; font-weight: 660; font-variant-numeric: tabular-nums;
    letter-spacing: -.025em;
    /* The cell's colour, which the renderer sets from the score. The number
       used to be ink and only went red or amber past a threshold, so a 20%
       and a 4% were the same black as a name in the next column. */
    color: inherit;
}

/* Smaller and quieter than the figure it belongs to. Three numbers in one cell
   is already a lot of characters; a full-size % on each would make it six. */
.v4-bar-pc {
    font-size: .58em; font-weight: 600; font-style: normal;
    margin-inline-start: .06em;
    opacity: .62;
}

.v4-bar-track {
    height: 4px; margin-top: .14rem;
    border-radius: 999px;
    background: var(--input, #e4e4e7);
    overflow: hidden;
}
.v4-bar-track > span {
    display: block; height: 100%; border-radius: 999px;
    background: currentColor;
    transition: width .35s ease;
}

/* Severity, not identity: 91% between two students and 91% off a model are
   both red because both are the reason to open the check. Which of the three
   it is, is the word above it. Colouring by source instead would have meant
   inventing a colour for AI, and the only one going spare is purple.

   How intense that severity is comes from the score itself and is set inline
   by the renderer - a ramp from the brand green through amber to the brand
   red, rather than three steps with nothing at the joins. @see rampColour().
   These three stay as the floor under it: they are what paints the cell if the
   inline colour is ever missing, and they are the hook the band classes give
   anything that wants to ask how bad a row is without reading a hex. */
.v4-bar.cqn-high { color: var(--cqn-danger, #dc2626); }
.v4-bar.cqn-mid  { color: var(--cqn-warn, #d97706); }
.v4-bar.cqn-low  { color: var(--cqn-ok, #16a34a); }

/* Nothing ran. A hollow rule and a dash, which is a different statement from a
   rule sitting at zero - and the row should not have to explain which. */
.v4-bar.is-empty .v4-bar-num { color: #b4b4bb; font-weight: 500; }
.v4-bar.is-empty .v4-bar-track { background: none; box-shadow: inset 0 0 0 1px var(--input, #e4e4e7); opacity: .8; }

/* Mid-run. Not empty and not zero: the engine is on its way to this number
   right now. Indigo on purpose - it is the one colour the severity ramp does
   not use (see above), so "still looking" can never be mistaken for a
   finding. The sheen walking the hollow track is what says RUNNING; the row
   needs no word for it. */
.v4-bar.is-running { color: #2563eb; }
.v4-bar.is-running .v4-bar-num { color: #2563eb; font-weight: 600; letter-spacing: .08em; }
.v4-bar.is-running .v4-bar-track {
    background: none;
    box-shadow: inset 0 0 0 1px #c7d2fe;
}
.v4-bar.is-running .v4-bar-track > span {
    width: 38%;
    background: linear-gradient(90deg, rgba(99,102,241,0), #818cf8 50%, rgba(99,102,241,0));
    animation: v4-bar-running 1.4s ease-in-out infinite;
    transition: none;
}
@keyframes v4-bar-running {
    0%   { transform: translateX(-110%); }
    100% { transform: translateX(290%); }
}
@media (prefers-reduced-motion: reduce) {
    .v4-bar.is-running .v4-bar-track > span { animation: none; width: 30%; opacity: .5; }
}
.dark .v4-bar.is-running { color: #818cf8; }
.dark .v4-bar.is-running .v4-bar-num { color: #818cf8; }
.dark .v4-bar.is-running .v4-bar-track { box-shadow: inset 0 0 0 1px #3730a3; }

/* ------------------------------------------------------------- the upgrade

   The number exists, the check found it, and this plan does not include it.
   A padlock on its own says "you cannot have this" and stops there - it does
   not say what to do about it, and a reader who has never seen the paid tier
   has no reason to read the icon as a door. So the plan's own name sits next
   to it, in the accent colour, on a control that visibly is a control: the
   whole cell is the button, it fills in on hover, and it opens the dialog that
   sells the plan. */
.v4-bar-lock {
    display: inline-flex; align-items: center; justify-content: center;
    /* flex: none, because .v4-bar-num is a flex row and overflow:hidden lets a
       flex item shrink under its own content - which is how a 51px chip ended
       up drawn 42px wide with its word cut in half rather than the column
       honestly being too narrow. It is now whole, or deliberately wordless. */
    flex: none;
    max-width: 100%;
    padding: .14rem .32rem;
    border-radius: .3rem;
    background: var(--cqn-accent-soft, #eff4ff);
    color: var(--cqn-accent, #1379F0);
    font-size: .68rem; font-weight: 700; letter-spacing: .01em; line-height: 1.25;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: background .12s ease, color .12s ease;
}
.v4-bar-lock i { font-size: .72rem; flex: none; }

/* The locked control spans the columns of every score it is holding back, so
   it has room for a word where a single column has none. Centred, because it
   is one object across two columns rather than a third entry in a row of
   left-aligned stacks. */
.v4-bar-unlock { align-items: center; text-align: center; }
/* @see the note in unlockCell(): a padlock with no word still has to be a
   target, and its glyph cannot be relied on to give it one. */
.v4-bar-lock.is-icon-only { min-width: 1.6rem; justify-content: center; gap: 0; padding: .14rem .2rem; }
.v4-bar-unlock .v4-bar-top { justify-content: center; width: 100%; }
.v4-bar-unlock .v4-bar-track { width: 100%; }
.v4-bar.is-locked .v4-bar-track { background: var(--cqn-accent-soft, #eff4ff); }
.v4-bar.is-locked:hover .v4-bar-label { color: var(--cqn-accent, #1379F0); }
.v4-bar.is-locked:hover .v4-bar-lock { background: var(--cqn-accent, #1379F0); color: #fff; }
.v4-bar.is-locked:focus-visible { outline: 2px solid var(--cqn-accent); outline-offset: 2px; border-radius: .3rem; }
/* The whole row is a link to the check. A locked score is not, and the pointer
   should say so before the click does. */
.cqn-table tbody tr:hover .v4-bar.is-locked .v4-bar-lock { box-shadow: 0 0 0 1px var(--cqn-accent-line, #bfdbfe); }

/* The column's own name, on its own line above the three sort links. Without
   it the header read as three columns that had lost their heading - every row
   already says which number is which, so Peer/Web/AI up there is a control,
   not a label, and a control needs to sit under something that names it. */
.v4-scores-title {
    display: block;
    margin-bottom: .18rem;
    /* Whatever the header row is set to. It used to name its own grey, which
       is how it ended up darker than the two headings either side of it. */
    color: inherit;
}
/* The same grid the scores use, so each link lands over the column it sorts.
   Inline, with a margin between them, the three bunched at the left of the
   cell: Peer happened to line up because it is first, Web sat 44px left of the
   web number and AI 86px left of the AI one. Every value here has to stay in
   step with .v4-bars - the columns, the gap and the ceiling. */
.v4-scores-sorts {
    display: grid; grid-template-columns: repeat(3, 1fr);
    gap: .6rem;
    max-width: 20rem;
}

/* Smaller than the heading above them, and not shouting in capitals: these are
   the three things you can sort by, not three more column names. */
.v4-scores-th .v4-sh {
    font-size: 11px;
    text-transform: none;
    letter-spacing: .01em;
    /* Left, like the label under it - the score column reads as three little
       left-aligned stacks, not as three centred ones. */
    display: inline-flex; align-items: center; justify-content: flex-start;
    min-width: 0;
    cursor: pointer;
    white-space: nowrap;
}
.v4-sh .cqn-sort { opacity: .3; font-size: .55rem; margin-inline-start: .12rem; }
.v4-sh:hover { color: var(--mono, #18181b); }
.v4-sh.is-sorted { color: var(--cqn-accent); }
.v4-sh.is-sorted .cqn-sort { opacity: 1; color: var(--cqn-accent); }
.v4-sh:focus-visible { outline: 2px solid var(--cqn-accent); outline-offset: 1px; border-radius: .25rem; }

/* The cell does not clip: every other td in this table ellipsises a long
   string, and three columns of figures are not a string.

   No fixed height any more: it dated from the label-over-number layout and
   kept reserving that layout's headroom above the shorter inline row. The
   name column's two lines set the row height for every row - scored and
   chipped alike - so uniformity survives without a floor here. */
.cqn-table td.v4-scores { overflow: visible; white-space: normal; }

/* On a phone the score cell is 42% of a 314px table, so two merged columns are
   about 60px - enough for the padlock, not for the padlock and a word. The
   label above it still names the scores it covers. */
@media (max-width: 640px) {
    .v4-wrap .v4-bar-unlock .v4-bar-lock {
        font-size: 0; gap: 0; padding: .14rem .2rem; min-width: 1.6rem; justify-content: center;
    }
    .v4-wrap .v4-bar-unlock .v4-bar-lock i { font-size: .74rem; }
}

/* ------------------------------------------------------------ the filters

   Chips rather than two dropdowns. A dropdown hides both the options and the
   answer: you had to open "All statuses" to learn there were three failed
   checks, and once the table came back empty there was nothing on the page to
   say whether that was the filter's doing or the account's.

   One group, and it is the one people use. Signal (peer/web/AI) and Score
   (40%+/75%+) were here too and are gone: nine more chips, three of them
   called "Any", asking a question the column headings now answer in a click. */
.v4-filters {
    display: flex; align-items: center; flex-wrap: wrap; gap: .35rem;
    padding: .5rem .85rem;
    border-bottom: 1px solid var(--border, #e4e4e7);
    background: var(--muted, #fafafa);
}
.dark .v4-filters { background: rgba(255, 255, 255, .02); border-color: rgba(255, 255, 255, .08); }

.v4-fgroup { display: flex; align-items: center; flex-wrap: wrap; gap: .25rem; }

.v4-fchip {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .32rem .62rem;
    border: 1px solid transparent;
    border-radius: 999px;
    background: none;
    color: var(--secondary-foreground, #71717a);
    font-size: .8rem; font-weight: 560; line-height: 1.35;
    white-space: nowrap;
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.v4-fchip:hover { background: var(--background, #fff); color: var(--mono, #18181b); border-color: var(--input, #e4e4e7); }
.v4-fchip:focus-visible { outline: 2px solid var(--cqn-accent); outline-offset: 1px; }
.v4-fchip.is-on {
    border-color: var(--cqn-accent-line, #bfdbfe);
    background: var(--cqn-accent-soft, #eff4ff);
    color: var(--cqn-accent, #1379F0);
}
.dark .v4-fchip:hover { background: rgba(255, 255, 255, .06); }

/* The count is the whole reason the chip beats the dropdown, so it is set in
   figures that line up rather than in the label's own proportional digits. */
.v4-fchip b {
    font-variant-numeric: tabular-nums;
    font-weight: 680;
    font-size: .76rem;
    opacity: .75;
}
.v4-fchip.is-on b { opacity: 1; }

/* A chip that can only ever return nothing. It stays on the row - "0 failed"
   is the answer to a real question - it just stops looking pressable. */
.v4-fchip.is-zero { opacity: .38; }
.v4-fchip.is-zero:hover { background: none; border-color: transparent; color: var(--secondary-foreground, #71717a); }

.v4-freset {
    display: inline-flex; align-items: center; gap: .25rem;
    margin-inline-start: auto;
    padding: .32rem .62rem;
    border: 0; border-radius: 999px; background: none;
    color: var(--cqn-accent, #1379F0);
    font-size: .78rem; font-weight: 600;
    cursor: pointer;
}
.v4-freset:hover { background: var(--cqn-accent-soft, #eff4ff); }
.v4-freset i { font-size: .62rem; }

/* The open affordance sits on the date, not on the actions cell. */
.cqn-table tbody tr .v4-when { position: relative; }
.cqn-table tbody tr .v4-when::after {
    content: "\203A"; position: absolute; inset-inline-end: -.6rem; top: 50%; transform: translateY(-50%);
    font-size: 1rem; line-height: 1; color: var(--cqn-accent); opacity: 0;
    transition: opacity .12s ease, transform .12s ease;
}
.cqn-table tbody tr:hover .v4-when::after { opacity: 1; transform: translateY(-50%) translateX(2px); }

/* --------------------------------------------------------------- live run */
.v4-run {
    position: relative; display: flex; align-items: center; gap: .8rem;
    padding: .7rem .9rem .8rem; overflow: hidden;
    border-bottom: 1px solid var(--border, #e4e4e7);
}
.v4-run-pulse { flex: none; width: .5rem; height: .5rem; border-radius: 50%; background: var(--cqn-accent); box-shadow: 0 0 0 0 rgba(37, 99, 235, .5); animation: v4pulse 1.8s ease-out infinite; }
@keyframes v4pulse { 70% { box-shadow: 0 0 0 .5rem rgba(37, 99, 235, 0); } 100% { box-shadow: 0 0 0 0 rgba(37, 99, 235, 0); } }
.v4-run-text { min-width: 0; flex: 1; }
.v4-run-name { font-size: .8125rem; font-weight: 600; color: var(--mono, #18181b); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.v4-run-stage { font-size: .73rem; color: var(--secondary-foreground, #71717a); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.v4-run-clock { flex: none; text-align: right; line-height: 1.1; }
.v4-run-clock b { font-family: var(--cqn-mono); font-size: .9rem; font-weight: 620; color: var(--mono, #18181b); }
.v4-run-clock span { display: block; font-size: .5625rem; letter-spacing: .06em; text-transform: uppercase; color: var(--secondary-foreground, #71717a); }
.v4-run-track { position: absolute; inset-inline: 0; bottom: 0; height: 2px; overflow: hidden; background: var(--input, #e4e4e7); }
.v4-run-track > span { display: block; height: 100%; width: 35%; background: linear-gradient(90deg, transparent, var(--cqn-accent), transparent); animation: v4sweep 1.6s ease-in-out infinite; }
@keyframes v4sweep { 0% { transform: translateX(-100%); } 100% { transform: translateX(320%); } }

@media (prefers-reduced-motion: reduce) {
    .v4-run-pulse, .v4-run-track > span { animation: none; }
    .v4-run-track > span { width: 100%; }
}

/* ══════════════════════════════════════════ speaking the panel's language
   This page sits inside the same shell as every other v2 panel, so it has to
   be furniture from the same set: 8px corners, the panel's own border token,
   Playfair on headings, and the same pill the submissions table wears. It was
   drifting into a design of its own - rounder, cooler, a different scale - and
   a page that looks imported is a page people do not trust. */
.v4-wrap {
    --cqn-r-lg: 8px;
    --cqn-r-md: 6px;

    /* The console is a stack of full-width bands - the attention strip, the
       filter row, the table head - and at the panel's own --muted each of them
       is a grey stripe the width of the page. Three of those above the first
       row of data and the card reads as grey furniture with a little white in
       it. A band only has to be a shade off the rows to be a band, so this is
       --muted cut with the card's own white, kept as one token so the three
       stay the same shade as each other. */
    --v4-band: #fbfbfc;
    --v4-band: color-mix(in srgb, var(--muted, #f4f4f5) 38%, var(--background, #fff));
}
/* Less white on a dark ground, not more. --muted is near-black in dark mode,
   so the same mix would lighten these bands away from the card instead. */
.dark .v4-wrap { --v4-band: rgba(255, 255, 255, .02); }
.v4-wrap .v4-shell,
.v4-wrap .v4-stats { border-color: var(--border, #e4e4e7); }

.v4-wrap .cqn-h1 {
    font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
    font-weight: 600;
    letter-spacing: -.01em;
}
.v4-wrap .cqn-h2 {
    font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
    font-weight: 600;
    font-size: 1rem;
}

/* The status column wears the panel's pill, not a second dialect of the same
   idea five pixels smaller. */
.v4-wrap .cqn-badge {
    gap: 5px;
    padding: 6px 11px;
    border-radius: 20px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .01em;
}

/* The header is a label for the column, not a row of the table. At the shared
   #71717a it read as dark as the data underneath it and the eye kept stopping
   on it on the way down the list. Two steps lighter, and the contrast it gives
   up is bought back by the states that matter: a heading you are hovering goes
   to full ink, and the one doing the sorting goes brand blue.

   Scoped to the dashboard. .cqn-table is shared with half the panel and the
   other tables are read a column at a time rather than scanned down. */
.v4-wrap .cqn-table th {
    font-size: 12px;
    letter-spacing: .05em;
    background: var(--muted, #f8f8f9);
    color: #a1a1aa;
}
/* Lighter on a dark ground means less white, not more. */
.dark .v4-wrap .cqn-table th { color: #83838d; }

/* A caret at .3 of an already-light grey is not there at all. Restated for
   the sorted one, which the line above outranks: three classes and an element
   beats the three classes that used to turn it up. */
.v4-wrap .cqn-table th .cqn-sort { opacity: .5; }
.v4-wrap .cqn-table th.is-sorted .cqn-sort,
.v4-wrap .cqn-table th .v4-sh.is-sorted .cqn-sort { opacity: 1; color: var(--cqn-accent); }

/* Past the point where a check should have finished, the band stops behaving
   like something is happening. */
.v4-run-slow { font-size: .72rem; margin-top: .15rem; color: var(--cqn-warn, #b45309); }
.v4-run.is-slow .v4-run-pulse,
.v4-run.is-stalled .v4-run-pulse { animation: none; background: var(--cqn-warn, #b45309); }
.v4-run.is-slow .v4-run-track > span,
.v4-run.is-stalled .v4-run-track > span { animation: none; }
.v4-run.is-stalled .v4-run-pulse { background: var(--secondary-foreground, #71717a); }
.v4-run.is-stalled .v4-run-slow { color: var(--secondary-foreground, #71717a); }
.v4-run.is-stalled .v4-run-clock b { color: var(--secondary-foreground, #71717a); }

/* The two settings almost nobody changes, labelled for the few who open them.
   Side by side: the popover each one opens carries the full names and the
   search box, so the trigger only has to say the current choice. */
.v4-tuning { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; margin-top: .5rem; }
/* Only the engine lives in the fold now; one column, full width, or the lone
   select stops at half the card for no reason. */
.v4-tuning.v4-tuning-solo { grid-template-columns: 1fr; }
.v4-tuning-field { display: grid; gap: .2rem; min-width: 0; }
/* The language field promoted out of the fold: same field, breathing room of
   its own between the sources above it and the engine fold below. */
.v4-lang-open { margin-top: .65rem; margin-bottom: .25rem; }
.v4-tuning-field > span {
    font-size: .68rem; font-weight: 600; letter-spacing: .02em;
    color: var(--secondary-foreground, #71717a);
}
@media (max-width: 30rem) { .v4-tuning { grid-template-columns: 1fr; } }

/* ── Language and engine pickers ──────────────────────────────────────────
   A native select styled like the sources trigger, replaced at runtime by
   dash-console.js with a button + popover so the list can carry a search box:
   sixty-five languages is a thing you type at, not scroll. The popover is
   fixed in the viewport for the same reason the sources panel is - the
   composer card clips and scrolls, and would cut an absolute panel off. */
.v4-dd { position: relative; min-width: 0; }
.v4-dd-native { display: none; }
/* display:flex on the menu and the rows would outrank the browser's own
   [hidden] rule, and a closed menu that is merely closed in JS is open. */
.v4-dd [hidden], .v4-dd-menu[hidden] { display: none !important; }

.v4-dd-btn {
    display: flex; align-items: center; gap: .5rem; width: 100%;
    height: 2.1rem; padding: 0 .65rem 0 .75rem;
    border: 1px solid var(--input, #e4e4e7); border-radius: 8px;
    background: var(--background, #fff); color: var(--foreground, #27272a);
    font-size: .78125rem; text-align: left; cursor: pointer;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.v4-dd-btn:hover { border-color: var(--cqn-accent); }
.v4-dd-btn:focus-visible { outline: none; border-color: var(--cqn-accent); box-shadow: 0 0 0 3px rgba(37, 99, 235, .14); }
.v4-dd.is-open .v4-dd-btn { border-color: var(--cqn-accent); box-shadow: 0 0 0 3px rgba(37, 99, 235, .12); }

.v4-dd-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v4-dd-caret { flex: none; font-size: .6rem; color: var(--secondary-foreground, #71717a); transition: transform .15s ease; }
.v4-dd.is-open .v4-dd-caret { transform: rotate(180deg); }

.v4-dd-menu {
    position: fixed; z-index: 2147480000;
    display: flex; flex-direction: column;
    border: 1px solid var(--border, #e4e4e7); border-radius: 11px;
    background: var(--background, #fff);
    box-shadow: 0 18px 40px -16px rgba(16, 24, 40, .3), 0 2px 6px -2px rgba(16, 24, 40, .12);
    overflow: hidden;
    animation: v4pick-in .13s ease both;
}
.v4-dd.is-up .v4-dd-menu { animation-name: v4pick-in-up; }
@media (prefers-reduced-motion: reduce) { .v4-dd-menu { animation: none; } }

.v4-dd-search {
    flex: none; display: flex; align-items: center; gap: .5rem;
    padding: .55rem .75rem; border-bottom: 1px solid var(--border, #e4e4e7);
}
.v4-dd-search i { flex: none; font-size: .8rem; color: var(--secondary-foreground, #71717a); }
.v4-dd-search input {
    flex: 1; min-width: 0; border: 0; background: none; padding: 0;
    font-size: .8125rem; color: var(--foreground, #27272a);
}
.v4-dd-search input:focus { outline: none; }

.v4-dd-list { flex: 1 1 auto; min-height: 0; max-height: 21rem; overflow-y: auto; overscroll-behavior: contain; padding: .25rem 0 .3rem; }

.v4-dd-group {
    padding: .55rem .8rem .25rem;
    font-size: .64rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
    color: var(--secondary-foreground, #71717a);
}

.v4-dd-opt {
    display: flex; align-items: center; gap: .6rem; width: 100%;
    padding: .45rem .8rem; border: 0; background: none; cursor: pointer; text-align: left;
    font-size: .8125rem; color: var(--mono, #18181b);
}
.v4-dd-opt > span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Auto detect and Language Agnostic: the ways of not picking from the list,
   held bold so the list never buries them. */
.v4-dd-opt.v4-dd-meta { font-weight: 650; }
/* The engine list's family headings - Hexagram, MOSS, Zeus - carry the brand
   in a deep blue. The trigger stays ink, like the language beside it: the
   value is a choice, the headings are the product's own names. */
.v4-dd-accent .v4-dd-group { color: #1e40af; }
.dark .v4-dd-accent .v4-dd-group { color: #7ba6f5; }
/* The check travels with the choice; the highlight travels with the pointer
   and the arrow keys. Two different facts, two different marks. */
.v4-dd-opt > i { flex: none; font-size: .7rem; color: var(--cqn-accent); opacity: 0; }
.v4-dd-opt.is-selected { font-weight: 600; }
.v4-dd-opt.is-selected > i { opacity: 1; }
.v4-dd-opt.is-active { background: var(--muted, #f4f4f5); }

.v4-dd-empty { padding: .8rem; font-size: .78rem; color: var(--secondary-foreground, #71717a); text-align: center; }

.dark .v4-dd-btn,
.dark .v4-dd-menu { background: #18181b; border-color: rgba(255,255,255,.12); }
.dark .v4-dd-search { border-color: rgba(255,255,255,.12); }
.dark .v4-dd-opt.is-active { background: rgba(255,255,255,.05); }

/* The HyperScan switch shares the header line with the Language and engine
   summary, but only the line: pinned absolutely rather than given a flex
   column of its own, so the fields underneath open to the card's full width
   instead of stopping where the switch begins. */
.v4-tuning-row { position: relative; }
.v4-tuning-row > details { min-width: 0; }
.v4-tuning-row summary { padding-inline-end: 8rem; }
.v4-hyper {
    position: absolute; inset-block-start: 0; inset-inline-end: 0;
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .25rem 0; cursor: pointer; user-select: none;
}
.v4-hyper > span:first-child {
    font-size: .68rem; font-weight: 600; letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--secondary-foreground, #71717a);
}
.v4-hyper input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.v4-hyper-track {
    position: relative; flex: none; width: 30px; height: 18px;
    border-radius: 999px; background: var(--border, #d4d4d8);
    transition: background .15s ease;
}
.v4-hyper-track::after {
    content: ''; position: absolute; top: 2px; left: 2px;
    width: 14px; height: 14px; border-radius: 50%;
    background: var(--background, #fff);
    box-shadow: 0 1px 2px rgba(16, 24, 40, .25);
    transition: left .15s ease;
}
.v4-hyper input:checked + .v4-hyper-track { background: var(--primary, #2563eb); }
.v4-hyper input:checked + .v4-hyper-track::after { left: 14px; }
.v4-hyper input:focus-visible + .v4-hyper-track {
    outline: 2px solid var(--primary, #2563eb); outline-offset: 2px;
}

/* Out of HyperScans. The switch is not disabled: a disabled control is a dead
   end, and this one has something to sell. It reads as unavailable and it
   still takes the click, which opens the top-up. */
.v4-tuning-row.is-hyper-out summary { padding-inline-end: 11rem; }
.v4-hyper.is-out > span:first-child { color: var(--muted-foreground, #a1a1aa); }
.v4-hyper.is-out .v4-hyper-track { opacity: .55; }
.v4-hyper-left {
    font-size: .62rem; font-weight: 700; letter-spacing: .04em;
    padding: .1rem .3rem; border-radius: 4px;
    color: #b45309; background: rgba(245, 158, 11, .14);
}
.dark .v4-hyper-left { color: #fbbf24; background: rgba(245, 158, 11, .18); }
/* The chip is hidden by attribute, and a flex parent is exactly where a bare
   [hidden] gets overridden by a display rule further up the cascade. */
.v4-hyper-left[hidden] { display: none; }

/* A check takes minutes; nobody watches it land. These say so when it does. */
.v4-toasts {
    position: fixed; z-index: 2147481000;
    inset-block-end: 1rem; inset-inline-end: 1rem;
    display: grid; gap: .5rem; max-width: min(22rem, calc(100vw - 2rem));
}
.v4-toast {
    display: flex; align-items: flex-start; gap: .55rem;
    padding: .65rem .75rem;
    border: 1px solid var(--border, #e4e4e7); border-radius: 10px;
    background: var(--background, #fff);
    box-shadow: 0 12px 32px -12px rgba(16, 24, 40, .35);
    animation: v4toast-in .18s ease both;
}
.v4-toast.is-leaving { animation: v4toast-out .3s ease both; }
@keyframes v4toast-in { from { opacity: 0; transform: translateY(6px); } }
@keyframes v4toast-out { to { opacity: 0; transform: translateY(6px); } }
.v4-toast > i { font-size: .95rem; margin-top: .05rem; }
.v4-toast.is-done > i { color: var(--cqn-ok, #16a34a); }
.v4-toast.is-failed > i { color: var(--cqn-danger, #dc2626); }
.v4-toast-text { min-width: 0; flex: 1; display: grid; }
.v4-toast-text b { font-size: .78rem; font-weight: 600; color: var(--mono, #18181b); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v4-toast-text span { font-size: .7rem; color: var(--secondary-foreground, #71717a); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v4-toast-open { flex: none; align-self: center; font-size: .72rem; font-weight: 600; color: var(--cqn-accent); text-decoration: none; }
.dark .v4-toast { background: #18181b; border-color: rgba(255,255,255,.12); }
.dark .v4-toast-text b { color: #e4e4e7; }
@media (prefers-reduced-motion: reduce) {
    .v4-toast, .v4-toast.is-leaving { animation: none; }
}

/* The page-wide drop target, which only exists while something is over it. */
.v4-veil {
    position: fixed; inset: 0; z-index: 2147482000;
    display: grid; place-items: center;
    background: rgba(15, 23, 42, .45);
    backdrop-filter: blur(2px);
}
.v4-veil-box {
    display: grid; justify-items: center; gap: .3rem;
    padding: 1.6rem 2.4rem;
    border: 2px dashed rgba(255, 255, 255, .55); border-radius: 14px;
    color: #fff; text-align: center;
}
.v4-veil-box i { font-size: 1.6rem; }
.v4-veil-box b { font-size: 1rem; font-weight: 600; }
.v4-veil-box span { font-size: .8rem; opacity: .8; }

/* The clear-filters button only belongs to the filtered empty state. */
#v4ClearFilters { display: none; }
.cqn-empty.is-filtered #v4ClearFilters { display: inline-flex; }

/* Scores close the row now, so past the phone breakpoint the last column is
   theirs: the name takes what is left, the status stays a pill's width, and
   the score grid gets room to breathe instead of the sliver the old actions
   column left it. */
@media (min-width: 761px) {
    .v4-wrap .v4-table-scroll th:nth-child(1), .v4-wrap .v4-table-scroll td:nth-child(1) { width: auto; }
    .v4-wrap .v4-table-scroll th:nth-child(2), .v4-wrap .v4-table-scroll td:nth-child(2) { width: 15%; }
    .v4-wrap .v4-table-scroll th:nth-child(3), .v4-wrap .v4-table-scroll td:nth-child(3) { width: 34%; }
}

.cqn-sr {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* The row's own tooltip: everything the four columns had to leave out. */
.v4-tip {
    position: fixed; z-index: 2147483000;
    max-width: 22rem;
    padding: .3rem .5rem;
    border-radius: var(--cqn-r-md);
    background: #18181b;
    color: #f4f4f5;
    font-size: .68rem; line-height: 1.4;
    white-space: pre-line;
    box-shadow: 0 8px 24px -8px rgba(0, 0, 0, .5);
    pointer-events: none;
}

/* A failure reason, not a hint: more room to say it in, and nothing else.
   The red edge was decoration on a tooltip that is already only ever shown
   over a row wearing a red Failed badge. */
.v4-tip.is-error { max-width: 28rem; }

/* ────────────────────────────────────────────────────────────── dark mode
   The panel has a dark theme and this page had none, so every surface here
   stayed white against it. Same tokens the rest of the panel uses, so the two
   change together. */
.dark .v4-rail-inner,
.dark .v4-main {
    background: rgba(255, 255, 255, .02);
    border-color: rgba(255, 255, 255, .08);
    box-shadow: none;
}
.dark .v4-rail-head,
.dark .v4-rail-foot,
.dark .v4-queue,
.dark .v4-toolbar,
.dark .v4-run,
.dark .v4-plan { border-color: rgba(255, 255, 255, .08); }
.dark .v4-plan { background: rgba(255, 255, 255, .03); }
.dark .v4-src {
    background: rgba(255, 255, 255, .02);
    border-color: rgba(255, 255, 255, .1);
    color: var(--secondary-foreground, #a1a1aa);
}
.dark .v4-src .cqn-src-logo { background: rgba(255, 255, 255, .06); }
.dark .v4-src.is-on { background: rgba(255, 255, 255, .04); border-color: rgba(255, 255, 255, .18); color: #e4e4e7; }
/* The octocat is black, which is invisible on a dark rail. */
.dark .v4-src.is-on[data-source="github"] .cqn-src-logo,
.dark .v4-src.is-on[data-source="gists"] .cqn-src-logo { color: #f0f6fc; }
.dark .v4-src.is-on[data-source="github"],
.dark .v4-src.is-on[data-source="gists"] { color: #f0f6fc; }
.dark .v4-src.is-locked { background: rgba(255, 255, 255, .03); }
.dark .v4-src-box { background: transparent; border-color: rgba(255, 255, 255, .25); }
.dark .cqn-table th { background: rgba(255, 255, 255, .04); }
.dark .v4-name { color: #e4e4e7; }
.dark .v4-repo-input { background: rgba(255, 255, 255, .03); border-color: rgba(255, 255, 255, .12); color: #e4e4e7; }
.dark .v4-head-note { color: var(--secondary-foreground, #a1a1aa); }

/* The composer card carries the same small lift the panel's own cards have. */
.v4-rail-inner { box-shadow: 0 1px 2px rgba(16, 24, 40, .04); }


/* ------------------------------------------------------------ the status cell
   Every cell in this table ellipsizes, because every other cell holds text.
   The status cell holds a pill, and the browser measured the pill as text: run
   a few pixels short and it painted a "…" beside "Completed", which reads as a
   status with more to it. Nothing was hidden. The column simply could not hold
   a shape that will not bend.

   So let it bend. The cell stops ellipsizing, and the pill truncates its own
   label inside its own border, with the full status on the title. That covers
   the long ones for real - "Suspicious submissions" and "Deep Check Accepted"
   are half again as wide as "Completed" and never fit any of these columns. */
.v4-wrap .v4-table-scroll td.v4-status { text-overflow: clip; }
.v4-wrap .v4-table-scroll .cqn-badge { max-width: 100%; min-width: 0; }
.v4-wrap .v4-table-scroll .cqn-badge-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.v4-wrap .v4-table-scroll .cqn-badge > i { flex: none; }

/* Status strings are longer than "Completed" more often than not, and the
   first column is the one with room to give: it is sized `auto` and truncates
   a check name without losing which check it is. */
@media (min-width: 1100px) and (max-width: 1439px) {
    /* Status strings run as long as "Suspicious submissions" and the pill
       truncates in place, so this is the width the longest one reads at, not
       the width it needs. One point off the name, which has more. */
    .v4-wrap .v4-table-scroll th:nth-child(1) { width: 31%; }
    .v4-wrap .v4-table-scroll th:nth-child(2) { width: 15%; }
}

/* Between the point the composer rail appears and the point the table is wide
   enough to carry everything, the subtitle line is the tight fit now: files,
   language, time AND a duration is the string that truncates, and the duration
   is the half already repeated in its tooltip, so it is the half that goes. */
@media (min-width: 641px) and (max-width: 1279px) {
    .v4-wrap .v4-runtime { display: none; }
}

/* The phone plan above (52/22/26) never applied: it is written against
   `.v4-table-scroll th:nth-child(n)`, and the rem widths further down the file
   match on the same specificity and win on order. The result on a 375px phone
   was a name column 31 pixels wide - every check called "E..", "M..", "L..".
   These are the same proportions, stated where they hold. */
@media (max-width: 640px) {
    .v4-wrap .v4-table-scroll th:first-child,
    .v4-wrap .v4-table-scroll th:nth-child(1) { width: 40%; }
    /* Three bars need more of a 375px screen than one number and a word did.
       The status column pays for it: a third of the screen was what the
       longest pill measured, not what it needed. */
    .v4-wrap .v4-table-scroll th:nth-child(1) { width: 38%; }
    .v4-wrap .v4-table-scroll th:nth-child(2) { width: 20%; }
    .v4-wrap .v4-table-scroll th:nth-child(3) { width: 42%; }

    /* Three headings and 343 pixels. Tighter tracking, and Peer/Web/AI are
       three, four and two characters, which is the whole reason they fit. */
    .v4-wrap .cqn-table th { font-size: 10px; letter-spacing: .03em; padding-inline: .5rem; }
}
