/*
 * Rubicon's own stylesheet.
 *
 * It shares nothing with css/styles.css, which is deliberate: this is a separate page and
 * borrowing the app's 292KB of styling would tie the two together at exactly the seam we
 * are keeping clean. The consequence is that the app's table and loader conventions do not
 * govern here, because none of the machinery they describe is loaded.
 *
 * The palette is the rubricator's, and the name is where it comes from: ruber, the red
 * ochre a heading was written in down the margin of a law book, and the red clay of the
 * river bed. So red is a third ink here rather than a fill. It carries the headings, the
 * verdict, the one selected row and the moment a rule is written down, and it appears
 * nowhere else. Everything under it is paper, warm grey and near-black.
 *
 * Causal Map's dark blue is gone from this page. The yellow is kept for marked text,
 * where it does a job no colour on the page can take over.
 */

/* The two families are asked for in rubicon.html, where the browser sees them without
   waiting for this file. Do not put an @import back here. */

:root {
    --rb-ink: #17130f;
    --rb-paper: #f7f4ee;
    /* A panel is a leaf of paper on the desk, half a shade lighter than the desk. */
    --rb-panel: #fdfbf6;
    --rb-wash: #f2ece2;
    --rb-line: #e4dcd0;
    --rb-quiet: #8b7f72;
    --rb-faint: #a89a8b;
    --rb-red: #8c1912;
    --rb-red-deep: #5e0f0a;
    /* Red at the weight of a wash rather than of ink: the row you are on, the panel a
       decision was written into. Never as text, which it has nowhere near the contrast for. */
    --rb-red-wash: #f6e8e4;
    --rb-bar: #ecd8d1;
    --rb-mark: #f7ed73;

    /* Run states. Red is spoken for, so failure takes it and the rest are muted enough
       to sit under it: near-neutral hues at low chroma, which is what stops a table of
       chips reading as five brands. */
    --rb-succeeded: #3f6b4a;
    --rb-skipped:   #7d7268;
    --rb-waiting:   #8a6d1f;
    --rb-running:   #445a6b;
    --rb-stale:     #6b5a63;

    /* Two families and no more. The serif reads the prose, which is most of this page;
       the sans is furniture, and is never given a sentence to carry. */
    --rb-serif: "Newsreader", Georgia, "Times New Roman", serif;
    --rb-sans: "Libre Franklin", system-ui, -apple-system, "Segoe UI", sans-serif;
}

/* The furniture: anything that labels, counts or is clicked rather than read. Written
   once, as a class, so a new control joins it by name instead of by repeating the stack. */
.rb-top, .rb-who, .rb-run-meta, .rb-group, .rb-note, .rb-status, .rb-badge,
.rb-key, .rb-legend, .rb-pills, .rb-filter, .rb-toggle, .rb-chosen, .rb-empty,
.rb-caption, .rb-pan-hint, .rb-table th, .rb-fig th, .rb-figure-over,
.rb-kind, .rb-link, .rb-drafts > summary, .rb-yaml > summary,
button, input, select, textarea, .btn {
    font-family: var(--rb-sans);
}

* { box-sizing: border-box; }

/* The page never scrolls; the panes inside it do. Laid out as two rows rather than by
   subtracting a guessed bar height from the viewport, which is what put a five-pixel
   scrollbar down the side of every screen: the bar is as tall as its own contents. */
body {
    margin: 0;
    height: 100vh;
    display: grid;
    grid-template-rows: auto 1fr;
    overflow: hidden;
    /* A serif at 14px is a serif nobody reads. The measure in every column here is short
       and the prose is the point, so the body size goes up rather than the family coming
       down to meet it. */
    font: 15px/1.6 var(--rb-serif);
    color: var(--rb-ink);
    background: var(--rb-paper);
}

/* Headings are set in the serif at a weight below bold, because a serif at 700 in a
   column 300px wide is a shout. What marks a heading here is the red, not the weight. */
h1, h2, h3, h4 { font-family: var(--rb-serif); font-weight: 500; }

/* The one heading level that is furniture rather than prose: the small red label that
   names what follows it, straight off the margin of the manuscript. */
.rb-report h3,
.rb-question-panel h3,
.rb-lightbox h3,
.rb-notes-head h3 {
    font-family: var(--rb-sans);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--rb-red);
}

/* -- the bar ------------------------------------------------------------------ */

/* Paper rather than a dark band, ruled off in red. A dark bar is a piece of software
   announcing itself; a red rule under a name is a page. */
.rb-top {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 9px 18px;
    background: var(--rb-panel);
    color: var(--rb-ink);
    border-bottom: 2px solid var(--rb-red);
}

.rb-mark {
    font-family: var(--rb-serif);
    font-size: 21px;
    font-weight: 500;
    letter-spacing: 0.005em;
    color: var(--rb-red);
}
.rb-project { position: relative; width: auto; min-width: 260px; }
.rb-who { margin-left: auto; color: var(--rb-faint); font-size: 12px; }

/* The project picker: a text box with the matches under it. The behaviour is shared with
   the app (js/combo-picker.js); this is the page's own paper-and-red reading of it, since
   nothing here loads the app's stylesheet. */
.rb-project-list {
    position: absolute;
    top: calc(100% + 3px);
    left: 0;
    right: 0;
    z-index: 40;
    max-height: 340px;
    overflow-y: auto;
    background: var(--rb-panel);
    border: 1px solid var(--rb-line);
    border-radius: 3px;
    box-shadow: 0 6px 20px rgba(23, 19, 15, 0.14);
}
.rb-project-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 5px 10px;
    font-family: var(--rb-sans);
    font-size: 13px;
    color: var(--rb-ink);
    cursor: pointer;
}
.rb-project-option:hover { background: var(--rb-wash); }
/* Red as the wash it is elsewhere on this page: the row you are on. */
.rb-project-option.rb-on { background: var(--rb-red-wash); }
.rb-project-option-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Matching is loose enough that the letters can be scattered through a name, so a hit
   would otherwise look arbitrary. */
.rb-project-match { color: var(--rb-red); font-weight: 600; }
.rb-project-empty {
    padding: 6px 10px;
    font-family: var(--rb-sans);
    font-size: 12px;
    color: var(--rb-quiet);
}

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

/* Bootstrap's dark and outline-dark are what the markup asks for, and this page wants
   them in its own two inks. Overridden here rather than by renaming classes across the
   markup, so the page keeps working while the styling is still being argued about.

   Red is the primary action, which is the one place on this page red means go rather
   than take care. That is the reading the app wants: pressing the button is crossing. */
.rb-top .form-select,
.rb-top .form-control,
.rb-filter .form-control,
.rb-newq-form .form-control,
.rb-ask textarea {
    border-color: var(--rb-line);
    background-color: var(--rb-panel);
    color: var(--rb-ink);
    border-radius: 3px;
}
.rb-top .form-select:focus,
.rb-top .form-control:focus,
.rb-filter .form-control:focus,
.rb-newq-form .form-control:focus,
.rb-ask textarea:focus {
    border-color: var(--rb-red);
    box-shadow: 0 0 0 2px var(--rb-red-wash);
}

.btn-dark {
    background-color: var(--rb-red);
    border-color: var(--rb-red);
    border-radius: 3px;
    font-weight: 500;
    letter-spacing: 0.02em;
}
.btn-dark:hover, .btn-dark:focus, .btn-dark:active {
    background-color: var(--rb-red-deep);
    border-color: var(--rb-red-deep);
}

.btn-outline-dark, .btn-outline-secondary {
    color: var(--rb-quiet);
    border-color: var(--rb-line);
    background-color: var(--rb-panel);
    border-radius: 3px;
}
.btn-outline-dark:hover, .btn-outline-secondary:hover {
    color: var(--rb-red);
    border-color: var(--rb-red);
    background-color: var(--rb-red-wash);
}

/* -- the two halves ----------------------------------------------------------- */

/* Conversation, canvas, runs. The conversation is first because it is the way in. */
/* The two side columns are variables so the dividers can move them, and the middle is
   whatever is left. A person reading a long coding instruction wants the canvas wide; a
   person arguing with Ruby wants her wide; neither wants to be told which. */
/* Questions, then the conversation, then the canvas, which takes what is left.
   Steve, 3 September 2026: "chat should be second column, with wider output canvas moved
   from 2 to 3 position." The canvas is where the work ends up, and a coding instruction,
   a workflow read as a document and a table of quotes all want the room; the conversation
   is a column of short paragraphs and never did. */
.rb-main {
    display: grid;
    grid-template-columns: var(--rb-left, 300px) 5px var(--rb-mid, 400px) 5px minmax(0, 1fr);
    min-height: 0;
}

@media (max-width: 1200px) {
    .rb-main { --rb-left: 260px; --rb-mid: 340px; }
}



.rb-drag {
    cursor: col-resize;
    background: var(--rb-line);
    /* The grab area is wider than the line, because a five-pixel target is a five-pixel
       target however it is drawn. */
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    background-clip: padding-box;
    box-sizing: content-box;
    width: 1px;
    margin: 0 -4px;
    z-index: 2;
}
.rb-drag:hover, .rb-drag.rb-dragging { background-color: var(--rb-ink); }
.rb-drag:focus-visible { outline: 2px solid var(--rb-ink); outline-offset: -2px; }

.rb-side {
    display: grid;
    /* New question, its form, the filter, the chosen bar, Project home, the list. */
    grid-template-rows: auto auto auto auto auto 1fr;
    min-height: 0;
    background: var(--rb-panel);
}

.rb-nav { padding: 10px 12px 0; display: grid; gap: 6px; }

/* Project home carries `.rb-run` in the markup, so its size, padding, rule, hover and
   selected state all come from the question rows and cannot drift from them. What is left
   here is only what makes it the one row that is not a question: the house, and a top rule
   so the list still starts with a line under the filter. "+ New question" stays a plain
   button, since it starts something rather than opening it. */
.rb-home-row { border-top: 1px solid var(--rb-line); }
.rb-home-row .rb-run-name { font-weight: 500; padding-right: 16px; }
.rb-home-row .fa-house { color: var(--rb-red); margin-right: 6px; font-size: 12px; }

.rb-newq-form {
    padding: 10px 12px;
    display: grid;
    gap: 8px;
    border-bottom: 1px solid var(--rb-line);
}

.rb-newq-form[hidden] { display: none; }

/* -- a report section ---------------------------------------------------------- */

/* Read like a document rather than like a screen: one column, generous measure, and the
   finding before the machinery. */
.rb-report {
    max-width: 720px;
    padding: 26px 30px 60px;
    line-height: 1.55;
}

.rb-report h1 { font-size: 30px; font-weight: 400; line-height: 1.22; margin: 0 0 6px; letter-spacing: -0.005em; }
.rb-report h2 { font-size: 20px; margin: 30px 0 8px; }
/* Size and colour come from the one red-label rule at the top of this file. Only the
   spacing is local, because a rule that restates them is a rule that drifts from them. */
.rb-report h3 { margin: 22px 0 6px; }
.rb-report p { margin: 0 0 10px; }
.rb-report ul { margin: 0 0 10px; padding-left: 20px; }
.rb-report footer { margin-top: 34px; padding-top: 12px; border-top: 1px solid var(--rb-line); }

/* The verdict is the thing an evaluation is asked for, so it is set as the answer rather
   than as a field: large, in the serif, in the red the page has kept for it, ruled off
   above the way a heading in a law book was. */
.rb-verdict {
    font-family: var(--rb-serif);
    font-size: 38px;
    font-weight: 400;
    line-height: 1.1;
    color: var(--rb-red);
    border-top: 2px solid var(--rb-red);
    border-bottom: 1px solid var(--rb-line);
    padding: 10px 0 12px;
    margin: 4px 0 18px;
}

.rb-instruction {
    margin: 0 0 10px;
    padding: 8px 12px;
    border-left: 3px solid var(--rb-line);
    color: var(--rb-ink);
    white-space: pre-wrap;
    font-size: 13px;
}

@media print {
    .rb-top, .rb-chat, .rb-side, .rb-caption, .rb-drag { display: none !important; }
    .rb-main { display: block; height: auto; }
    .rb-graph { overflow: visible; height: auto; }
    .rb-report { max-width: none; padding: 0; }

    /* The page never scrolls and the panes inside it do, which is right on a screen and
       is a single-page document on paper: a body one viewport tall with its overflow
       hidden throws away everything below the fold. It matters twice over, because this
       stylesheet is also what the server typesets a report with, and WeasyPrint reads
       print rules. Without this the PDF came out one page long. */
    body { height: auto; display: block; overflow: visible; }
}

/* Choosing rows, and the bar that appears when any are chosen.

   Archiving one thing should cost one click, so the button is on the row and appears on
   hover. Archiving fifteen is a different job, and doing it one hover at a time is what
   makes somebody give up and leave the list cluttered, so the checkboxes are there when
   they are wanted and out of the way when they are not. */
.rb-row { position: relative; display: flex; align-items: stretch; }
.rb-row > .rb-run { flex: 1; min-width: 0; }

.rb-pick {
    flex: 0 0 auto;
    margin: 0 2px 0 8px;
    align-self: center;
    opacity: 0;
    transition: opacity 0.12s;
    accent-color: var(--rb-red);
}
.rb-row:hover .rb-pick,
.rb-pick:checked,
.rb-pick:focus-visible { opacity: 1; }

.rb-arch {
    flex: 0 0 auto;
    border: 0;
    background: none;
    color: var(--rb-quiet);
    padding: 0 10px;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.12s;
}
.rb-row:hover .rb-arch,
.rb-arch:focus-visible { opacity: 1; }
.rb-arch:hover { color: var(--rb-ink); }
.rb-archived { opacity: 0.55; }
.rb-archived .rb-arch { opacity: 1; }

.rb-chosen {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 8px 12px;
    background: var(--rb-ink);
    color: var(--rb-panel);
    font-size: 12px;
}
.rb-chosen .rb-link { color: var(--rb-mark); }

/* Select all or none. It arrives with the bar, so it exists only once something has been
   chosen, which is the point at which choosing the rest becomes a job worth one click.
   Indeterminate while the choice is partial, and the browser draws that itself. */
.rb-pick-all {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin: 0;
    cursor: pointer;
}
.rb-pick-all input { accent-color: var(--rb-red); margin: 0; }

.rb-filter {
    padding: 8px 12px;
    border-bottom: 1px solid var(--rb-line);
    display: flex;
    align-items: center;
    gap: 10px;
}

.rb-filter input[type="search"] { flex: 1; min-width: 0; }

.rb-toggle {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    color: var(--rb-quiet);
    white-space: nowrap;
    margin: 0;
    cursor: pointer;
}

.rb-runs {
    overflow-y: auto;
    min-height: 0;
    background: var(--rb-panel);
}

/* The archive control sits over the row rather than inside it, because a button inside a
   button is not valid HTML and the row itself is the thing you click to open a run. */
.rb-row { position: relative; }
.rb-row:hover .rb-arch, .rb-arch:focus { opacity: 1; }
.rb-archived .rb-run-name { color: var(--rb-quiet); font-weight: 500; }

.rb-arch {
    position: absolute;
    top: 8px;
    right: 8px;
    border: 0;
    background: var(--rb-panel);
    border-radius: 3px;
    padding: 3px 6px;
    color: var(--rb-quiet);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.12s;
}

.rb-arch:hover { color: var(--rb-ink); }
.rb-arch[data-on="true"] { opacity: 1; }
.rb-hidden-note { padding: 10px 16px; }

/* `min-height: 0` on both axes, not just the width. Without it a grid item's automatic
   minimum is its content, so a long question panel in the middle column stretched the
   whole row to its own height, pushed the chat column's Ask button to y=1254 in an 849px
   viewport, and the primary action was simply off the screen with nothing to scroll. */
.rb-canvas { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
/* The pane holds a cytoscape canvas, which sizes itself and never overflows, or a paper
   document, which does. So it scrolls. */
.rb-graph { flex: 1; min-height: 0; overflow: auto; position: relative; }

.rb-pan-hint {
    position: absolute;
    left: 12px;
    bottom: 10px;
    font-size: 11px;
    color: var(--rb-quiet);
    background: rgba(253, 251, 246, 0.9);
    padding: 2px 6px;
    border-radius: 3px;
    pointer-events: none;
}

.rb-caption {
    border-bottom: 1px solid var(--rb-line);
    padding: 10px 18px;
    background: var(--rb-panel);
    display: flex;
    align-items: baseline;
    gap: 14px;
    flex-wrap: wrap;
}

.rb-question { color: var(--rb-quiet); font-style: italic; }
.rb-back { font-size: 13px; margin-right: 4px; }

/* -- runs --------------------------------------------------------------------- */

.rb-run {
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px 16px 10px 24px;
    border: 0;
    border-bottom: 1px solid var(--rb-line);
    background: none;
    cursor: pointer;
    position: relative;
}

.rb-run:hover { background: var(--rb-wash); }
.rb-run-name { display: block; font-weight: 600; }
.rb-run-meta { display: block; color: var(--rb-quiet); font-size: 12px; }

/* -- the one selected-state look, shared by Project Home and every question row --
   Steve, 3 September 2026: the two did not read as the same kind of control, and the
   selected state itself was too faint to spot at a glance. Both get the same darker fill,
   the same left bar, and a chevron that is reserved space rather than added on select, so
   picking a row does not nudge its text sideways.

   Written against BOTH carriers on purpose. `rb-on` lands on the wrapping row for a
   question (from the list markup and from opening one) and on the button itself for a
   loose run (from markSelected), so a rule naming only one of them styles half the list
   and looks, from the outside, exactly like a rule that did not load. */
.rb-run.rb-on,
.rb-row.rb-on > .rb-run {
    background: var(--rb-red-wash);
    box-shadow: inset 4px 0 0 var(--rb-red);
}
.rb-run.rb-on .rb-run-name,
.rb-row.rb-on > .rb-run .rb-run-name { font-weight: 700; }
.rb-run::before {
    content: "\276F";
    position: absolute;
    left: 8px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 11px;
    color: var(--rb-red);
    visibility: hidden;
}
.rb-run.rb-on::before,
.rb-row.rb-on > .rb-run::before { visibility: visible; }

.rb-status {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 2px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--rb-panel);
    margin-right: 4px;
}

/* Named with the chip class as well, so a status beats the fallback by specificity
   rather than by being written above it. It was written above it, and the fallback
   `.rb-status` a few rules down therefore won every one of these: succeeded, failed,
   waiting, running, skipped and stale all painted the same grey, so a table of
   twenty-three attempts said nothing about which had failed. `.rb-status.rb-not-run`
   below was already written this way, which is the tell: the author knew the shape and
   applied it to one case. A default that depends on source order to stay a default is
   the same fault the phone media query had this morning. */
.rb-status.rb-succeeded { background: var(--rb-succeeded); }
.rb-status.rb-skipped   { background: var(--rb-skipped); }
.rb-status.rb-waiting   { background: var(--rb-waiting); }
.rb-status.rb-failed    { background: var(--rb-red); }
.rb-status.rb-running   { background: var(--rb-running); }
/* A run whose process went silent. Not a status the ledger stores: it is derived
   from the heartbeat, so the record still says what it honestly recorded. */
.rb-status.rb-stale     { background: var(--rb-stale); }
/* The same colours where the class is used away from a chip. Tokens rather than repeated
   hexes, because the chip and the legend swatch are the same claim about the same state
   and drifted apart once already. */
.rb-succeeded { background: var(--rb-succeeded); }
.rb-skipped   { background: var(--rb-skipped); }
.rb-waiting   { background: var(--rb-waiting); }
.rb-failed    { background: var(--rb-red); }
.rb-running   { background: var(--rb-running); }
.rb-stale     { background: var(--rb-stale); }

/* -- a theory of change, with what the evidence did to it --------------------- */

/* Tabs rather than a row of small links. Steve, 4 September 2026: "this could be 2 tabs:
   the result canvas, the workflow." They were three underlined links in a caption, which
   reads as somewhere to click for more detail rather than as the choice of what the
   canvas is showing, and the one that matters most was not among them. */
.rb-views { margin-left: auto; display: flex; gap: 2px; align-self: flex-end;
    margin-bottom: -11px; }
.rb-views .rb-link {
    padding: 4px 12px;
    border: 1px solid transparent;
    border-bottom: none;
    border-radius: 4px 4px 0 0;
    text-decoration: none;
    color: var(--rb-quiet);
}
.rb-views .rb-link:hover { color: var(--rb-ink); background: var(--rb-red-wash); }
.rb-views .rb-on {
    color: var(--rb-ink);
    font-weight: 600;
    background: var(--rb-page);
    border-color: var(--rb-line);
}

/* Each result under its own heading, with air between them: a run that produced a table
   and a judgement is two answers, not one long page. */
.rb-result + .rb-result { margin-top: 30px; padding-top: 22px; border-top: 1px solid var(--rb-line); }

/* An asset the Result tab names rather than reads out. A coding pass makes thousands of
   quotes and printing those is not a reading of them. */
.rb-result-aside h2 { font-size: 17px; }

/* The chain, under each result's heading. It is what makes the list readable without the
   diagram beside it: a table above a verdict says nothing until it says the verdict read
   it. */
.rb-came-from { margin: -2px 0 10px; }

/* What is being worked on, above the results, while there is work being done. */
.rb-running-now { border-left: 3px solid var(--rb-red); padding-left: 14px; }

/* The map under the results, which is how they were made. Cytoscape draws into whatever
   box it is given and a box with no height is a box it draws nothing in, so this one says
   how tall it is. It is its own container rather than `#rb-graph` for a second reason:
   cytoscape binds mousedown to its container and prevents the default, so a graph drawn
   into the pane that also holds the prose takes selection away from the words. */
/* Where you are, in the app's own words, at the head of the canvas. The three places a
   person can be are the project hub, a question hub and a workflow, and the strip that
   carries the tabs is where it says which. */
.rb-where {
    font-family: var(--rb-sans);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--rb-red);
}

/* A row that opens one thing is clickable across its width. */
.rb-table tbody tr { cursor: pointer; }
.rb-table tbody tr:hover { background: var(--rb-red-wash); }

.rb-elevate-picks { display: flex; flex-wrap: wrap; gap: 8px; }

/* Its own tab now, so it takes the pane. The pane scrolls and cytoscape does not, so the
   diagram gets a height it can be measured against rather than the pane's own. */
.rb-map-pane { height: calc(100vh - 190px); min-height: 340px; }

/* The diagram's key sits under it rather than over it, because the diagram can be as tall
   as it likes here and there is no corner to tuck into. Named twice to beat `.rb-legend`,
   which is absolute and is declared after this: at equal specificity the later rule wins,
   and the key floated up the page and landed across the Accept button. */
.rb-legend.rb-diagram-key {
    position: static;
    flex-wrap: wrap;
    gap: 6px 16px;
    margin: 10px 22px 24px;
}

/* A status nothing else names. The chip's white text used to land on nothing. */
.rb-status { background: var(--rb-quiet); }
.rb-status.rb-not-run { background: var(--rb-faint); }

/* A workflow's states, which are not a run's. Without these the three that matter most
   were the same grey: candidate and accepted are the whole point of listing workflows,
   and archived has to read as put away rather than as another kind of result. */
.rb-status.rb-candidate { background: var(--rb-succeeded); }
.rb-status.rb-accepted  { background: var(--rb-red); }
.rb-status.rb-archived  { background: var(--rb-faint); }
/* Somebody stopped it. Not a failure, and it must not read as one. */
.rb-status.rb-cancelled { background: var(--rb-skipped); }
/* Somebody stopped it. Not a failure, and it must not read as one. */
.rb-status.rb-cancelled { background: var(--rb-skipped); }

.rb-legend {
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    max-width: calc(100% - 24px);
    background: rgba(253, 251, 246, 0.9);
    padding: 4px 8px;
    border-radius: 3px;
}

.rb-key { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; }
.rb-key i { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }

.rb-theory-supported          { background: var(--rb-succeeded); }
.rb-theory-contradicted       { background: var(--rb-red); }
.rb-theory-contested          { background: var(--rb-waiting); }
.rb-theory-nobody-addressed-it,
.rb-theory-nothing-decisive   { background: var(--rb-skipped); }

/* -- questions ---------------------------------------------------------------- */

/* A question is the unit of work, so its row is taller than a run's and its text is not
   truncated: the whole question is the thing you are choosing between. */
/* Room at the top right for the archive control, which sits over the row rather than in
   it. The title ran under the bin icon, and got worse when the titles went to serif. */
.rb-q .rb-run-name { white-space: normal; line-height: 1.35; padding-right: 26px; }
.rb-starred { color: #b8860b; }

/* The standing conversation is the way in rather than one question among many. */
.rb-general { background: var(--rb-wash); }
.rb-general .rb-run-name { font-style: italic; color: var(--rb-quiet); font-weight: 400; }

.rb-group {
    padding: 12px 16px 4px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--rb-red);
    border-top: 1px solid var(--rb-line);
}

.rb-q-open      { background: var(--rb-running); }
.rb-q-answered  { background: var(--rb-succeeded); }
.rb-q-abandoned { background: var(--rb-skipped); }

.rb-question-panel { max-width: 760px; }
.rb-question-panel h2 { font-size: 24px; font-weight: 400; line-height: 1.28; }
.rb-question-panel h2:focus { outline: 2px solid var(--rb-line); outline-offset: 4px; }
.rb-question-panel h3 { margin: 22px 0 6px; }
.rb-question-panel textarea { max-width: 640px; }

.rb-answer {
    border-left: 3px solid var(--rb-red);
    padding: 4px 0 4px 12px;
}

.rb-status-row { display: flex; gap: 6px; }

/* Elevating sits at the foot of a result, where you are looking at the thing itself. */
.rb-elevate {
    margin-top: 22px;
    padding-top: 14px;
    border-top: 1px solid var(--rb-line);
    display: grid;
    gap: 8px;
    max-width: 560px;
}

.rb-empty, .rb-error, .rb-gate { padding: 22px; color: var(--rb-quiet); }
.rb-error { color: var(--rb-red); }
.rb-gate { font-size: 16px; }

/* -- the lightbox over the graph ---------------------------------------------- */

.rb-lightbox {
    position: fixed;
    inset: 0;
    background: rgba(23, 19, 15, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    z-index: 50;
}

.rb-lightbox[hidden] { display: none; }

.rb-lightbox-inner {
    background: var(--rb-panel);
    border-radius: 6px;
    max-width: 1000px;
    width: 100%;
    max-height: 100%;
    overflow: auto;
    padding: 22px 26px;
    position: relative;
}

/* Sticky rather than absolute, and floated right, because the panel it sits in is the
   scroll container: a quotes table thirteen thousand pixels tall put the close button
   four thousand pixels above the viewport with nothing else to click. */
.rb-close {
    position: sticky;
    top: 6px;
    float: right;
    z-index: 3;
    border: 0;
    background: var(--rb-panel);
    border-radius: 50%;
    width: 32px;
    height: 32px;
    font-size: 24px;
    line-height: 1;
    color: var(--rb-quiet);
    cursor: pointer;
}
.rb-close:hover { color: var(--rb-ink); }

/* A hundred rows down, the header is what tells stance from bearing. */
.rb-lightbox thead th {
    position: sticky;
    top: 0;
    background: var(--rb-panel);
    z-index: 2;
}

.rb-lightbox h2 { font-size: 22px; margin: 0 0 4px; }
.rb-lightbox h3 { margin: 18px 0 6px; }
.rb-kind { color: var(--rb-quiet); font-weight: 400; font-size: 13px; }
.rb-note { color: var(--rb-quiet); font-size: 12px; margin: 2px 0 8px; }

.rb-lightbox pre {
    background: var(--rb-wash);
    border: 1px solid var(--rb-line);
    border-radius: 4px;
    padding: 10px 12px;
    font-size: 12px;
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 340px;
    overflow: auto;
}

/* Wide content scrolls inside itself; the page never scrolls sideways. */
.rb-table { width: 100%; border-collapse: collapse; font-size: 13px; display: block; overflow-x: auto; }
.rb-table th, .rb-table td { border-bottom: 1px solid var(--rb-line); padding: 6px 8px; text-align: left; vertical-align: top; }
.rb-table th { color: var(--rb-quiet); font-weight: 600; font-size: 12px; }
/* Width is decided by what a column holds, not shared out equally. `width: 1%` with
   nowrap is the auto-layout idiom for "shrink to your content": a flag reading True, a
   category and the button column stop claiming a share, and what they hand back goes to
   the prose. The quote keeps a floor so it can never be squeezed into a ribbon again,
   and a ceiling so a single long passage cannot push everything else off the edge.
   Which class a column gets is worked out in page.js from the rows themselves. */
.rb-quote { min-width: 20em; max-width: 460px; }
.rb-col-tight { width: 1%; white-space: nowrap; }
.rb-col-wide { min-width: 14em; }
.rb-col-act { width: 1%; white-space: nowrap; }

.rb-link { border: 0; background: none; padding: 0; color: var(--rb-red); text-decoration: underline; cursor: pointer; font-size: 12px; }
.rb-table .rb-link { text-align: left; }

.rb-source { white-space: pre-wrap; font-size: 13px; line-height: 1.6; }
.rb-source mark { background: var(--rb-mark); padding: 1px 0; }

/* -- an authoring run --------------------------------------------------------- */

.rb-chat {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto;
    min-height: 0;
    background: var(--rb-panel);
}

/* Which chat you are in, and the way to start another. */
.rb-chat-head {
    display: flex;
    gap: 8px;
    align-items: center;
    padding: 8px 12px;
    border-bottom: 1px solid var(--rb-line);
}
.rb-chat-head .form-select { flex: 1; min-width: 0; }
.rb-chat-head[hidden] { display: none; }

/* Ruby's mark, at the head of the row rather than on one control in it: the row is hers,
   the chat and the model both. `flex: none` because the two dropdowns beside it are
   `flex: 1` and must go on splitting what is left evenly; an earlier version wrapped the
   mark and the model picker in a container, and a nested flex container does not take an
   even share, which cost that box 47px. The stone is drawn from the page's own reds so it
   cannot drift from the ink. */
.rb-ruby-mark { display: block; width: 18px; height: 18px; flex: none; }
.rb-ruby-stone { fill: var(--rb-red); }
.rb-ruby-shade { fill: var(--rb-red-deep); }
.rb-ruby-facets {
    fill: none;
    stroke: var(--rb-red-wash);
    stroke-width: 1;
    stroke-linejoin: round;
    opacity: .8;
}

/* -- a question's name, and the brief under it ----------------------------------- */
/* The name is the heading; the brief is what somebody typed and stays whole beneath it.
   A stand-in taken from the opening of the brief is drawn lighter than a name somebody
   chose, because presenting the two the same way would let a derived name pass for a
   decision. */
.rb-q-brief { margin: -4px 0 10px; color: var(--rb-quiet); white-space: pre-wrap; }
#rb-q-title[data-named="false"] { color: var(--rb-quiet); font-style: italic; }
.rb-run-name.rb-unnamed { color: var(--rb-quiet); }
.rb-report-brief { color: var(--rb-quiet); margin: -6px 0 14px; }

.rb-said-list { overflow-y: auto; padding: 14px; min-height: 0; }

.rb-ask {
    border-top: 1px solid var(--rb-line);
    padding: 10px 12px;
    display: grid;
    gap: 8px;
}

.rb-ask-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.rb-ask textarea { resize: vertical; }

.rb-said { border: 1px solid var(--rb-line); border-radius: 6px; padding: 10px 12px; margin-bottom: 10px; background: var(--rb-panel); }
/* Who is speaking, as a red label in the margin rather than as a bold word in the text.
   It is furniture: it names the voice and is never read as part of what was said. */
.rb-said strong {
    display: block;
    font-family: var(--rb-sans);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--rb-red);
    margin-bottom: 4px;
}
/* The other voice is the quieter one, so its label steps back to grey. */
.rb-them strong { color: var(--rb-quiet); }
.rb-said p { margin: 0 0 6px; }
.rb-said p:last-child { margin-bottom: 0; }
.rb-breakdown { margin: 0 0 6px; padding-left: 18px; }
/* Her answer leads and is set as ordinary prose. Her reading of the question sits under
   it, quieter, because it is what to argue with rather than what was asked for. */
.rb-reading { color: var(--rb-quiet); }
.rb-them { background: var(--rb-wash); }
/* Named for what it is, not for a status. `rb-waiting` is already a run status chip,
   and the two collided: the amber chip lost its white text to this rule. */
.rb-thinking {
    color: var(--rb-quiet);
    display: grid;
    grid-template-columns: auto auto;
    gap: 2px 10px;
    align-items: center;
    background: linear-gradient(100deg, var(--rb-panel) 30%, var(--rb-wash) 50%, var(--rb-panel) 70%) 0 0 / 300% 100%;
    animation: rb-wait 2.4s ease-in-out infinite;
}
.rb-thinking [data-elapsed] { font-variant-numeric: tabular-nums; font-size: 12px; }
.rb-thinking [data-thinking-note] { grid-column: 1 / -1; margin-top: 4px; }

/* Three dots that carry the beat, so a minute of silence does not read as a page that
   has stopped. It says something is happening and claims nothing about how far along. */
.rb-dots { display: inline-flex; gap: 4px; }
.rb-dots i {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--rb-quiet);
    animation: rb-dot 1.2s ease-in-out infinite;
}
.rb-dots i:nth-child(2) { animation-delay: 0.15s; }
.rb-dots i:nth-child(3) { animation-delay: 0.3s; }

@keyframes rb-dot {
    0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
    30% { opacity: 1; transform: translateY(-3px); }
}
@keyframes rb-wait {
    0% { background-position: 100% 0; }
    100% { background-position: 0 0; }
}

/* The one bar that says work is happening, wherever it is happening.

   Across the very top, over the dark bar, because that is the one part of the page that is
   on the screen whichever column you are in and whatever you have scrolled to. Fixed rather
   than a grid row, so it can appear and go without moving anything under it. Two pixels: a
   run takes minutes and this has to be noticeable for all of them without being something
   to look at. It reports nothing about progress, which is honest, since a run reports
   nothing until a step lands. */
/* One bar, drawn in two places: across the top of the window, and across the top edge of
   whichever column is waiting. Steve, 4 September 2026, of the window one: "good but
   hardly visible". It was 2px of pale yellow fading into cream over whatever happened to
   be behind it, so on a light page there were stretches of it that were not there at all.
   It now has a track, so the whole width reads as a bar whether or not the highlight is
   passing, and the highlight is the red the rest of the page uses for anything live. */
.rb-busy,
.rb-atwork::before {
    height: 3px;
    background-color: var(--rb-red-wash);
    background-image: linear-gradient(90deg,
        transparent, var(--rb-red), var(--rb-red-deep), var(--rb-red), transparent);
    background-size: 40% 100%;
    background-repeat: no-repeat;
    animation: rb-busy-slide 1.15s linear infinite;
}

.rb-busy {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 50;
}
.rb-busy[hidden] { display: none; }

/* A column says on itself that it is working, so somebody reading the middle of the screen
   does not have to know to look at the top of the window. Only ever put on a column that
   does not scroll: `top: 0` inside a scrolling box is the top of the content, so the bar
   would slide away the moment anybody scrolled. `page.js` names the two that qualify.

   Not `rb-waiting`, which is already the amber chip for a run whose status is waiting.
   Naming this one that put an animated red bar across every one of those chips, which is
   the second time the same collision has been made in this file. */
.rb-atwork { position: relative; }
.rb-atwork::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 5;
    pointer-events: none;
}

@keyframes rb-busy-slide {
    from { background-position: -45% 0; }
    to   { background-position: 145% 0; }
}

/* Movement is a nicety and some people are made ill by it. The count and the words carry
   the whole message without it. */
@media (prefers-reduced-motion: reduce) {
    .rb-thinking, .rb-dots i { animation: none; }
    .rb-dots i { opacity: 0.6; }
    /* Still shown, because its whole message is that something is happening, and a still
       bar says that as well as a moving one. */
    .rb-busy, .rb-atwork::before {
        animation: none; background-position: 0 0; background-size: 100% 100%;
    }
}

/* A proposal has not run, so there are no statuses to draw and it is shown as it is. */
.rb-paper { padding: 18px 22px; max-width: 900px; }

/* -- the hanging margin ------------------------------------------------------- */

/* The thing the rubricator actually did: the label for a section is not above it but
   beside it, out in the margin, in red, while the prose keeps one clean edge all the way
   down the page. It is what makes a page of this scannable without any rules or boxes.

   Done with a float rather than a grid, because the panels are a flat run of h3 and p
   with no wrapper around a section, and grid auto-placement cannot put a label on the
   same row as the content that follows it. The float sits entirely inside the padding the
   container reserves for it, so it never shortens a line box, and the table and the
   pre blocks (which make their own formatting contexts) do not get pushed off it either.

   `clear: left` is the safety valve for a section shorter than its own label: better a
   label pushed down than two labels on top of each other.

   Only where there is room. The margin costs 150px of width, which a phone does not have
   and a canvas column dragged narrow may not either; below that the labels go back to
   sitting above their sections, which is what they did before any of this. */
@media (min-width: 1100px) {
    .rb-question-panel, .rb-report, .rb-draft {
        padding-left: 172px;
        display: flow-root;
    }

    .rb-question-panel > h3,
    .rb-report > h3,
    .rb-draft > h3 {
        float: left;
        clear: left;
        width: 122px;
        margin: 0 0 0 -150px;
        padding-top: 5px;
        text-align: right;
        line-height: 1.4;
    }

    /* The label is out of the flow, so the space above a section has to be carried by the
       first thing in it. Without this every section runs into the one above. */
    .rb-question-panel > h3 + *,
    .rb-report > h3 + *,
    .rb-draft > h3 + * { margin-top: 26px; }

    /* Notes are the one section whose heading is wrapped, because the heading carries the
       edit and cancel buttons beside it. So the head goes out to the margin as a unit and
       takes its buttons with it, rather than the h3 alone floating and leaving them
       stranded in the text column. Any future section built the same way joins this rule.

       Right-aligned and stacked: the label, then what you can do to it under it. */
    .rb-question-panel > .rb-notes { display: flow-root; margin-top: 26px; }
    .rb-question-panel > .rb-notes > .rb-notes-head {
        float: left;
        clear: left;
        width: 122px;
        margin: 0 0 0 -150px;
        padding-top: 5px;
        flex-direction: column;
        align-items: flex-end;
        gap: 3px;
        text-align: right;
    }
    .rb-question-panel > .rb-notes > .rb-notes-head h3 { margin: 0; }
}
/* The first thing on a question nobody has run anything against, because that is the
   moment a person has no idea what the app wants from them. */
.rb-next {
    border-left: 3px solid var(--rb-mark);
    background: #fbf6e2;
    padding: 2px 14px 8px;
    margin: 12px 0;
}
.rb-next h3 { margin-top: 10px; }
.rb-next code { background: var(--rb-wash); padding: 1px 4px; border-radius: 3px; }
.rb-paper h2 { font-size: 21px; margin: 0 0 4px; }
.rb-run-row { display: flex; align-items: center; gap: 10px; margin: 12px 0; }
/* The standard, set apart from the workflow it belongs to, because agreeing to it is a
   separate decision and the person making it should be able to see where it starts. */
.rb-rubric {
    border-left: 3px solid var(--rb-line);
    padding-left: 14px;
    margin: 8px 0 4px;
}
.rb-rubric ul { margin: 0 0 10px; padding-left: 20px; }
.rb-rubric li { margin-bottom: 7px; }
/* The verdict name and its threshold on one line, what earns it under them, so a reader
   scanning for the levels is not reading sentences to find them. */
.rb-rubric li > div { margin-top: 1px; }
.rb-crit { margin: 14px 0 6px; }

/* Notes read as prose with a way in, rather than as a box waiting to be filled. */
.rb-notes-head { display: flex; align-items: baseline; gap: 10px; }
.rb-notes-head h3 { margin-bottom: 4px; }
.rb-notes .rb-prose p { margin: 0 0 6px; }

/* The draft, read as a document. */
.rb-draft { padding: 18px 22px; max-width: 900px; }
.rb-draft h2 { font-size: 21px; margin: 0 0 4px; }
.rb-draft-q { font-size: 15px; color: var(--rb-quiet); margin: 0 0 10px; }
.rb-steps { padding-left: 22px; margin: 14px 0; }
.rb-step { margin-bottom: 18px; }
.rb-step-head { margin: 0 0 4px; }
.rb-step p { margin: 0 0 6px; }
.rb-step ul { margin: 0 0 8px; padding-left: 20px; }
.rb-yaml > summary { cursor: pointer; color: var(--rb-quiet); font-size: 12px; }
.rb-refused {
    border-left: 3px solid var(--rb-red);
    padding-left: 14px;
    margin: 12px 0;
}
.rb-refused ul { margin: 6px 0; padding-left: 20px; }

/* Why a run stopped, printed where the chip saying `failed` is. The chip was the whole
   of it until 6 September 2026: the sentence explaining it sat in the ledger, which the
   page already read, and on no screen. */
.rb-stopped {
    border-left: 3px solid var(--rb-red);
    padding-left: 14px;
    margin: 12px 0;
}
/* Clamped, because a refusal can name every rubric on the project and an unclamped one
   pushes the offer of a fix off the bottom of the screen. Scrolls rather than truncates:
   the whole message is what Ruby is given, so the reader should be able to see it too. */
.rb-stopped ul {
    margin: 6px 0;
    padding-left: 20px;
    list-style: none;
    max-height: 14em;
    overflow-y: auto;
}
.rb-stopped li { margin-bottom: 8px; }
.rb-stopped li div { color: var(--rb-quiet); }
.rb-stopped .rb-run-row { flex-wrap: wrap; }

/* What a revision replaces, and what it costs against what it replaces. It sits directly
   above the Run button, which is the only place the difference can be noticed in time to
   act on it. The wash rather than a border where it is merely context; the red border
   where the revision opens half again as much reading, because that is a decision
   somebody is about to take without meaning to. */
.rb-replaces {
    background: var(--rb-wash);
    border-radius: 4px;
    padding: 10px 14px;
    margin: 12px 0;
    font-size: 13px;
}
.rb-replaces p { margin: 0 0 6px; }
.rb-replaces p:last-child { margin-bottom: 0; }
.rb-replaces-dearer {
    background: var(--rb-red-wash);
    border-left: 3px solid var(--rb-red);
}
.rb-yaml pre {
    background: var(--rb-wash);
    border: 1px solid var(--rb-line);
    border-radius: 4px;
    padding: 10px 12px;
    overflow-x: auto;
    margin-top: 8px;
}
.rb-yaml-key { color: var(--rb-red); font-weight: 600; }
.rb-yaml-comment { color: var(--rb-quiet); font-style: italic; }
.rb-rubric label { display: block; margin: 10px 0 4px; }
.rb-paper pre {
    background: var(--rb-wash);
    border: 1px solid var(--rb-line);
    border-radius: 4px;
    padding: 10px 12px;
    font-size: 12px;
    white-space: pre-wrap;
}
/* The four kinds of thing she says. The badge names the act; the colour only makes the
   badge findable, because three colours with no words were three colours. */
.rb-part {
    margin: 0 0 8px;
    padding-left: 10px;
    border-left: 3px solid var(--rb-hue, currentColor);
}
/* Inside one of these the badge and the rule carry the colour and the sentence stays ink.
   A paragraph set in ochre or in red is a paragraph read as a warning rather than read,
   and all four of these are ordinary things Ruby says. The classes still colour text
   where they are used on their own, which is what an error message wants. */
.rb-part.rb-cannot, .rb-part.rb-asks, .rb-part.rb-sure, .rb-part.rb-cost { color: var(--rb-ink); }
.rb-part .rb-badge { color: var(--rb-hue); border-color: var(--rb-hue); }
.rb-badge {
    display: inline-block;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
    border: 1px solid currentColor;
    border-radius: 3px;
    padding: 0 5px;
    margin-right: 7px;
    vertical-align: 1px;
    white-space: nowrap;
}
.rb-cannot { color: var(--rb-red); --rb-hue: var(--rb-red); }
.rb-asks { color: #3f342c; --rb-hue: #3f342c; }
.rb-sure { color: var(--rb-waiting); --rb-hue: var(--rb-waiting); }
.rb-cost { color: var(--rb-quiet); --rb-hue: var(--rb-quiet); }

/* Folded, because a draft the check threw out is evidence rather than an answer. Shown
   at all, because a workflow that took three goes is a fact about the question. */
.rb-drafts {
    margin: 0 0 10px;
    color: var(--rb-quiet);
}
.rb-drafts > summary { cursor: pointer; font-size: 12px; }
.rb-drafts .rb-said { margin-top: 8px; background: var(--rb-wash); }
.rb-said details summary { cursor: pointer; color: var(--rb-quiet); font-size: 12px; }

/* -- a phone ------------------------------------------------------------------ */

/* Three columns will not fit and none of them is optional, so on a narrow screen the page
   shows one at a time and three pills say which. Steve, 3 September 2026: "mobile view can
   have 3 simple pills across the top to switch between the 3 columns."

   Pills rather than a menu because there are exactly three and they never change: a menu
   would hide two of them behind a tap and tell you nothing about where you are. */
.rb-pills { display: none; }

@media (max-width: 820px) {
    body { grid-template-rows: auto auto 1fr; }

    .rb-pills {
        display: grid;
        grid-template-columns: repeat(3, 1fr);
        gap: 6px;
        padding: 8px 10px;
        background: var(--rb-panel);
        border-bottom: 1px solid var(--rb-line);
    }
    .rb-pills button {
        border: 1px solid var(--rb-line);
        background: var(--rb-panel);
        color: var(--rb-quiet);
        border-radius: 999px;
        padding: 7px 4px;
        font-size: 13px;
        cursor: pointer;
    }
    .rb-pills button.rb-on {
        background: var(--rb-red);
        border-color: var(--rb-red);
        color: var(--rb-panel);
    }

    /* One column, whichever is showing, and the dividers have nothing to divide. */
    .rb-main { display: block; }
    .rb-drag { display: none; }
    .rb-side, .rb-chat, .rb-canvas { display: none; height: 100%; }
    .rb-main[data-showing="questions"] .rb-side,
    .rb-main[data-showing="chat"] .rb-chat { display: grid; }
    .rb-main[data-showing="canvas"] .rb-canvas { display: flex; }

    /* The bar is tall enough on a phone that the project picker needs its own line. */
    .rb-top { flex-wrap: wrap; gap: 8px; padding: 8px 12px; }
    .rb-project { min-width: 0; flex: 1; }
    .rb-who { font-size: 11px; }
}

/* -- figures, one presentation per kind of answer ------------------------------ */

/* A figure is a finding, so it is set as one: its name as a heading, the answer at a size
   somebody can read across a desk, and what it was counted over underneath in small type
   as the caveat it is. They were rows of a five-column table whatever they held. */
.rb-figure { margin: 0 0 26px; }
.rb-figure h4 {
    font-size: 14px;
    margin: 0 0 8px;
    display: flex;
    align-items: baseline;
    gap: 10px;
}
.rb-figure-walk { font-weight: 400; }
.rb-figure-over { margin: 6px 0 0; }

/* One number is the whole answer, so it is allowed to be a number rather than a cell. */
.rb-fig-one {
    font-size: 26px;
    font-weight: 600;
    margin: 0;
    font-variant-numeric: tabular-nums;
}

.rb-fig { border-collapse: collapse; font-size: 14px; }
.rb-fig th, .rb-fig td { padding: 4px 12px; }
.rb-fig tbody th, .rb-fig thead th { font-weight: 600; text-align: left; }
/* Lined up on the digits, so a column of numbers can be compared by eye rather than read
   one at a time. */
.rb-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* A breakdown is a distribution, and a column of numerals hides the shape of one. The bar
   sits behind its number rather than beside it, so the numbers stay in one column and
   still line up. */
.rb-fig-breakdown tbody td { position: relative; min-width: 130px; }
.rb-fig-breakdown .rb-bar {
    position: absolute;
    left: 0;
    top: 3px;
    bottom: 3px;
    background: var(--rb-bar);
    border-radius: 2px;
}
.rb-fig-breakdown .rb-num span { position: relative; }
.rb-fig-breakdown tfoot th, .rb-fig-breakdown tfoot td {
    border-top: 1px solid var(--rb-line);
    color: var(--rb-quiet);
}

/* A cross-tab is read by its margins as much as its cells, so the totals are on both
   edges and marked as not being part of the body. */
.rb-fig-xtab th, .rb-fig-xtab td { border: 1px solid var(--rb-line); }
.rb-fig-xtab thead th { color: var(--rb-quiet); }
.rb-fig-xtab .rb-edge { background: var(--rb-wash); color: var(--rb-quiet); }
.rb-fig-xtab tfoot th, .rb-fig-xtab tfoot td {
    background: var(--rb-wash);
    color: var(--rb-quiet);
}

/* Several single numbers in one asset, as a list rather than as several headings each
   with a number at 26px. Four of those down a page is a lot of room for four numbers and
   hides the only thing worth seeing, which is how they compare. */
.rb-fig-list { width: 100%; margin: 4px 0 10px; }
.rb-fig-list tbody th { font-weight: 600; padding-left: 0; }
.rb-fig-list td.rb-num { font-size: 17px; font-weight: 600; }
.rb-fig-list td:last-child { text-align: right; }

/* Said when a draft is carried from one question to another. Quiet, because it is a
   reassurance rather than a warning: the text is still there, and where it will go if
   sent has changed. */
.rb-kept { margin: 0 0 6px; padding: 0 2px; }

/* A run that ends in a verdict, marked as such. Not decoration: a verdict is a
   stipulation against a standard, and a description is a report of what is there, and the
   scepticism a reader should bring to the two is different. Worked out from whether the
   run produced a judgement, so nothing has to classify anything. */
.rb-verdict-mark {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--rb-mark);
    color: var(--rb-ink);
    font-size: 11px;
    font-weight: 600;
    vertical-align: middle;
}

/* -- rewriting a turn ----------------------------------------------------------- */

/* The pencil sits in the corner of the bubble rather than under the voice label, because
   the label is furniture and the button is not part of what was said. It is there at all
   times on a touch screen, where there is no hover to reveal it with. */
.rb-them { position: relative; }
.rb-edit {
    position: absolute;
    top: 6px;
    right: 8px;
    border: 0;
    background: none;
    padding: 2px 4px;
    line-height: 1;
    font-size: 13px;
    color: var(--rb-quiet);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.12s ease;
}
.rb-them:hover .rb-edit,
.rb-them:focus-within .rb-edit { opacity: 1; }
.rb-edit:hover, .rb-edit:focus { color: var(--rb-red); opacity: 1; }
@media (hover: none) { .rb-edit { opacity: 1; } }

/* Space kept for the pencil, so a one-line question does not run under it. */
.rb-them .rb-words { padding-right: 18px; }

.rb-rewriting { border-color: var(--rb-red); }
.rb-rewriting strong { color: var(--rb-red); }
.rb-rewriting textarea { margin-bottom: 6px; }
.rb-rewrite-row { display: flex; gap: 8px; align-items: center; }

/* Where one chat stops being read and another starts. A rule with a word on it, because
   the turns on either side look identical and were said in different conversations. */
.rb-rewrote {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 2px 0 12px;
    font-family: var(--rb-sans);
    font-size: 10px;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--rb-quiet);
}
.rb-rewrote::before {
    content: '';
    flex: 0 0 18px;
    border-top: 1px solid var(--rb-line);
}
.rb-rewrote::after {
    content: '';
    flex: 1;
    border-top: 1px solid var(--rb-line);
}
.rb-rewrote .rb-link { font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase; }

/* The workflows on a question.
   It takes its lines and spacing from .rb-table and adds only what this table needs. The
   markup carried `.rb-strategies` from the day it was written and nothing matched it, so
   the cost column has been ragged and the dates have been wrapping ever since. */
.rb-workflows { margin: 8px 0 4px; }
.rb-workflows th:nth-child(4),
.rb-workflows td:nth-child(4) {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.rb-workflows td:nth-child(3) { white-space: nowrap; }
.rb-workflows td:last-child { text-align: right; white-space: nowrap; }

/* -- a workflow as one document ------------------------------------------------- */

/* One scrolling document instead of a Result tab and a Workflow tab. Those were the same
   workflow read twice, the steps in one and what they produced in the other, and nothing
   on either said which step made which result. The blocks below are what the margin map
   will scroll to, so each is a real section with a name rather than a run of paragraphs. */
.rb-doc-head { margin-bottom: 18px; }
.rb-doc-head h2 { margin: 0 0 4px; }
.rb-doc-head .rb-run-meta { display: flex; align-items: center; gap: 8px; margin: 0 0 8px; }

/* A step and everything it made, kept together. The rule down the left is what says the
   results below a step belong to it rather than following it by accident. */
.rb-step-block {
    border-left: 2px solid var(--rb-line);
    padding-left: 14px;
    margin: 0 0 20px;
}
.rb-step-block > .rb-fold { margin: 0; }
.rb-step-block:focus-within,
.rb-step-block.rb-found { border-left-color: var(--rb-red); }

/* How the step went, under the step it went. Reads as a caption on the instruction above
   it rather than as a heading over the results below. */
.rb-ran { display: flex; align-items: center; gap: 8px; margin: 6px 0 10px; }

.rb-asset-block { margin: 12px 0 16px; }
.rb-asset-block h3 {
    font-size: 15px;
    margin: 0 0 2px;
    display: flex;
    align-items: baseline;
    gap: 8px;
}

/* One prong of a fan-out, named by the item it ran for, so twelve codings of twelve
   countries can be told apart. */
.rb-prong { margin: 10px 0 0 10px; padding-left: 12px; border-left: 1px dotted var(--rb-line); }
.rb-prong-head {
    font-family: var(--rb-sans);
    font-size: 10px;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--rb-quiet);
    margin: 0 0 4px;
}

/* Assets the workflow as it stands does not account for, which happens where it was
   rewritten between runs. Shown rather than dropped: silently losing a result off the
   page is the one failure this document must not have. */
.rb-orphans { border-top: 1px solid var(--rb-line); margin-top: 24px; padding-top: 12px; }
.rb-orphans > h3 { margin-top: 0; }

/* Where the map has sent the reader. Fades, so it says "here" without staying lit. */
@keyframes rb-found-fade {
    from { background: var(--rb-mark); }
    to { background: transparent; }
}
.rb-found { animation: rb-found-fade 2.4s ease-out; }

/* -- the margin map ------------------------------------------------------------- */

/* The document and its map, side by side inside the scroller. The map is sticky, so it
   stays on the screen the whole way down a workflow that runs to several screens.

   Wider than the 132px it was built at, because the nodes spread across the whole gutter
   now instead of sitting in a column down the middle. The widest rank over the 147 newest
   runs is 8 things across, which at 132px is exactly where a step box starts touching its
   neighbour. It only widens on a screen with the room for it: on a 1280 laptop the
   document beside it is already down to about 360px of prose. */
.rb-doc-with-map {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 140px;
    gap: 16px;
    align-items: start;
}
.rb-minimap {
    position: sticky;
    top: 12px;
    align-self: start;
    padding: 6px 0;
}
.rb-minimap canvas { display: block; width: 100%; }

/* What the two marks are. Steve, 6 September 2026: "the nodes are just the steps not the
   individual components" — of a map that had drawn the results as nodes all along and
   never said which they were. Shape carries it rather than colour, since colour on this
   map already means how a step went and whether a result exists. */
.rb-map-key {
    display: flex;
    align-items: center;
    gap: 5px;
    margin: 6px 0 0;
    font-family: var(--rb-sans);
    font-size: 10px;
    letter-spacing: 0.04em;
    color: var(--rb-quiet);
}
.rb-map-key i { display: inline-block; background: var(--rb-quiet); flex: none; }
.rb-map-key .rb-map-step { width: 14px; height: 8px; border-radius: 3px; }
.rb-map-key .rb-map-asset { width: 9px; height: 9px; border-radius: 50%; margin-left: 5px; }

@media (min-width: 1500px) {
    .rb-doc-with-map { grid-template-columns: minmax(0, 1fr) 176px; }
}

/* Below this the columns are already fighting for width, and a map nobody can read is
   worse than no map. The gutter goes with it rather than leaving a hole. */
@media (max-width: 1100px) {
    .rb-doc-with-map { grid-template-columns: minmax(0, 1fr); gap: 0; }
    .rb-minimap { display: none; }
}

/* -- folding a step, and folding a result --------------------------------------- */

/* Steve, 5 September 2026: "maybe we can make each step and result collapsible too?" A
   workflow with four coding steps is many screens of instruction, and somebody looking for
   one figure should be able to put the rest away. Open by default: a document that opens
   closed is a document nobody reads. */
.rb-fold > summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: baseline;
    gap: 8px;
    padding: 3px 0;
    border-radius: 3px;
}
.rb-fold > summary::-webkit-details-marker { display: none; }
.rb-fold > summary:hover { background: var(--rb-wash); }
/* The triangle, drawn rather than borrowed, so it turns with the fold and sits on the
   baseline of the words beside it. */
.rb-fold > summary::before {
    content: '';
    flex: 0 0 auto;
    width: 0;
    height: 0;
    border-left: 4px solid var(--rb-quiet);
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    transform: translateY(-1px);
    transition: transform 0.12s ease;
}
.rb-fold[open] > summary::before { transform: translateY(1px) rotate(90deg); }
.rb-fold-body { padding-left: 12px; }

/* The step's number, which used to come from an ordered list and now has to be drawn,
   since the line it sits on is the one you press to fold the step. */
.rb-n {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    color: var(--rb-quiet);
    font-size: 12px;
    min-width: 12px;
}
.rb-step-block > .rb-fold > summary > strong { font-size: 15px; }
.rb-asset-block > .rb-fold > summary {
    font-family: var(--rb-sans);
    font-size: 13px;
    font-weight: 600;
}

/* -- ordering the question list -------------------------------------------------- */

/* On hover, because an order is changed rarely and read constantly. The one in force
   stays visible at all times; the others appear when the pointer is over the column, so
   the list is not carrying a permanent row of controls nobody is using. */
.rb-sorts {
    display: flex;
    gap: 2px;
    align-items: center;
    padding: 2px 12px 6px;
    min-height: 22px;
}
.rb-sort {
    border: 0;
    background: none;
    padding: 2px 6px;
    border-radius: 3px;
    font-family: var(--rb-sans);
    font-size: 10px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--rb-quiet);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.12s ease;
}
.rb-sort.rb-on { opacity: 1; color: var(--rb-red); background: var(--rb-red-wash); }
.rb-side:hover .rb-sort,
.rb-sorts:focus-within .rb-sort { opacity: 1; }
.rb-sort:hover { background: var(--rb-wash); color: var(--rb-ink); }
.rb-sort.rb-on:hover { background: var(--rb-red-wash); color: var(--rb-red); }
/* No hover on a touch screen, so the choice has to be there to be made. */
@media (hover: none) { .rb-sort { opacity: 1; } }

/* Which run of a workflow you are reading. Inline in the meta line, because it is one
   more fact about this run rather than a control over the page. */
.rb-run-pick {
    display: inline-block;
    width: auto;
    max-width: 100%;
    font-size: 12px;
    vertical-align: baseline;
}

/* What a run cost the world, beside what it cost in credits. Quieter than the credits,
   because it is an estimate and says so when you hover it. */
.rb-flame { color: var(--rb-quiet); white-space: nowrap; cursor: help; }

/* -- the breadcrumb -------------------------------------------------------------- */

/* Where you are, and the one place above it. Steve, 5 September 2026: "tidy this so they
   look of even weight and make it clear that Workflow is selected."
   They were a 13px underlined red link beside an 11px uppercase red label, which read as
   two unrelated things shouting at each other and said nothing about which one you were
   on. Same size and same family now, so the line reads as one trail; what says which is
   which is colour, weight and the mark between them. */
.rb-caption { gap: 10px; }
.rb-link.rb-back {
    font-size: 12px;
    font-weight: 400;
    color: var(--rb-quiet);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    padding-bottom: 1px;
    margin-right: 0;
}
.rb-link.rb-back:hover,
.rb-link.rb-back:focus { color: var(--rb-red); border-bottom-color: var(--rb-red); }

.rb-where {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: none;
    color: var(--rb-ink);
}
/* The turn in the trail, drawn only where there is something to turn from. */
.rb-link.rb-back + .rb-where::before {
    content: '\203A';
    color: var(--rb-line);
    font-weight: 400;
    margin-right: 10px;
}

/* A question with something running on it, in the list where somebody waits it out.
   The same slow sweep the chat uses while Ruby is thinking, so "working" looks like one
   thing across the page rather than two. A left rule as well as the sweep, because a
   moving background alone is easy to miss on a list somebody is scanning. */
.rb-q-running {
    position: relative;
    background: linear-gradient(100deg, transparent 30%, var(--rb-red-wash) 50%,
                                transparent 70%) 0 0 / 300% 100%;
    animation: rb-wait 2.4s ease-in-out infinite;
}
/* The same bar the top of the window draws, on the row itself. Steve, 6 September 2026,
   of the first attempt: "the running question does have an extra grey bar but it is not
   our recognisable red animation". It swept in `--rb-wash` under a slate rule, which is
   the colour of the *status* running rather than the colour of work happening, so the row
   said something was going on in a register nothing else on the page uses. Work is red
   and it slides, here and in the window bar and on the run row, and it is one thing
   because a signal that looks different in three places is three signals. */
.rb-q-running::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3px;
    background-color: var(--rb-red-wash);
    background-image: linear-gradient(90deg,
        transparent, var(--rb-red), var(--rb-red-deep), var(--rb-red), transparent);
    background-size: 40% 100%;
    background-repeat: no-repeat;
    animation: rb-busy-slide 1.15s linear infinite;
    pointer-events: none;
}
/* Somebody who has asked not to see motion is still told, in the one way that does not
   move. */
@media (prefers-reduced-motion: reduce) {
    .rb-q-running { animation: none; background: var(--rb-red-wash); }
    .rb-q-running::after { animation: none; background-image: none;
                           background-color: var(--rb-red); }
}

/* -- writing it up --------------------------------------------------------------- */

.rb-report-wrap { max-width: 46em; }
.rb-report-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    border-bottom: 1px solid var(--rb-line);
    padding-bottom: 10px;
    margin-bottom: 14px;
}
.rb-report-out { display: flex; gap: 6px; flex-wrap: wrap; }

/* What is in it and in what order, which is an editorial decision rather than a fact
   about the ledger, so it is a control rather than a reading. */
.rb-report-order {
    margin: 0 0 20px;
    padding-left: 20px;
    font-family: var(--rb-sans);
    font-size: 12px;
    color: var(--rb-quiet);
}
.rb-report-order li { margin: 3px 0; }
.rb-report-order .rb-link { font-size: 13px; padding: 0 3px; text-decoration: none; }
.rb-report-order .rb-link[disabled] { opacity: 0.25; cursor: default; }
.rb-report-order span { color: var(--rb-ink); }

/* Named rather than counted: choosing nine questions and getting six, with nothing saying
   which three went missing, is the failure this app exists against. */
.rb-report-left-out { margin-bottom: 16px; font-size: 13px; }
.rb-report-left-out ul { margin: 4px 0 0; padding-left: 20px; }

.rb-report-front { margin-bottom: 24px; }
.rb-report-front h1 { margin-bottom: 4px; }
.rb-report-contents { margin-bottom: 28px; }
.rb-report-contents ol { padding-left: 20px; }

/* The controls are for the person arranging it, not for whoever it is handed to. */
@media print {
    .rb-report-bar, .rb-report-order, .rb-report-left-out,
    .rb-report-wrap > [data-said] { display: none !important; }
    .rb-report-wrap { max-width: none; }
}

/* The leaf html2pdf draws from. It carries the same classes as the report on the screen
   and so gets the same look for nothing; only what a canvas cannot do is said again here.

   Two things about how it is staged, both learned by getting a blank file. It must be IN
   the document, because html2pdf sizes the page it draws onto from the element's own
   measured width and a detached element measures zero. And the parking off the side of the
   window belongs to the wrapper around it, never to this element: html2pdf clones what it
   is given into its own capture box, and a clone that carries `position: fixed; left:
   -10000px` lands ten thousand pixels outside that box, which draws a blank page and
   reports success. The measure stays with .rb-report-wrap, so there is one place it is
   decided. */
.rb-report-pdf { width: 100%; }

/* Two things the screen can do that a page cannot. A wide table scrolls sideways here and
   there is nowhere to scroll to on paper, so it lays out in full instead. And the hanging
   red margin comes and goes with the WINDOW's width, which would make the same report
   come out two different shapes depending on the size of the window it was asked for in;
   the labels sit above their sections in the file, always. */
.rb-report-pdf .rb-table { display: table; overflow: visible; }
.rb-report-pdf .rb-report { padding-left: 0; }
.rb-report-pdf .rb-report > h3 { float: none; width: auto; margin: 22px 0 6px;
                                 text-align: left; }
.rb-report-pdf .rb-report > h3 + * { margin-top: 0; }

/* The page box, said once, for both of the ways a report becomes a PDF.

   The server route sends this whole stylesheet to WeasyPrint, which honours `@page`; the
   browser route is told the same margins in millimetres through html2pdf's own options,
   because it lays a picture onto a page rather than typesetting one and has no page box to
   read. Two mechanisms, one set of numbers, and they are written next to each other here
   so that changing one without the other is hard to do by accident. */
@page {
    size: A4;
    margin: 14mm 12mm 16mm;
}
