:root {
    --ink: #10203a;
    --muted: #4a5b70;
    --faint: #64748b;
    --bg: #f4f7fb;
    --bg-soft: #ffffff;
    --surface: #ffffff;
    --surface-raised: #eef3fa;
    --line: rgba(19, 47, 96, 0.13);
    --line-strong: rgba(19, 47, 96, 0.24);
    --blue: #1f6feb;
    --blue-soft: #e3edfd;
    --amber: #b45309;
    --red: #b91c1c;
    --ok: #15803d;
    /* A tool the reader has bookmarked, wherever one is marked as such. */
    --bookmark-line: #166534;
    --bookmark-fill: #e7f4ea;
    --bookmark-fill-strong: #d7ecdc;
    --shadow: 0 24px 60px rgba(19, 47, 96, 0.14);
    --radius-lg: 0;
    --radius-md: 0;
    --max-width: 1180px;
}

* {
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
}

body {
    margin: 0;
    min-height: 100vh;
    color: var(--ink);
    background:
        radial-gradient(circle at 15% 0%, rgba(31, 111, 235, 0.1), transparent 30rem),
        linear-gradient(180deg, #ffffff 0%, var(--bg) 45%, #e9eff8 100%);
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    line-height: 1.55;
}

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

a {
    color: inherit;
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.skip-link {
    position: fixed;
    z-index: 100;
    top: 0.75rem;
    left: 0.75rem;
    padding: 0.65rem 1rem;
    color: #ffffff;
    background: var(--blue);
    border-radius: 0;
    transform: translateY(-180%);
    transition: transform 160ms ease;
}

.skip-link:focus {
    transform: translateY(0);
}

.site-header {
    position: sticky;
    z-index: 20;
    top: 0;
    border-bottom: 1px solid var(--line);
    background: rgba(255, 255, 255, 0.86);
    backdrop-filter: blur(18px);
}

.nav-shell {
    width: min(calc(100% - 2rem), var(--max-width));
    min-height: 72px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.7rem;
    text-decoration: none;
}

.brand-mark {
    width: 38px;
    height: 38px;
    display: grid;
    place-items: center;
    border: 1px solid rgba(31, 111, 235, 0.42);
    border-radius: 50%;
    color: var(--blue);
    background: linear-gradient(145deg, rgba(31, 111, 235, 0.16), rgba(31, 111, 235, 0.02));
    font-family: Georgia, "Times New Roman", serif;
    font-size: 1.15rem;
}

.brand strong,
.brand small {
    display: block;
    line-height: 1.05;
}

.brand strong {
    font-family: Georgia, "Times New Roman", serif;
    font-size: 1.12rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.brand small {
    margin-top: 0.2rem;
    color: var(--faint);
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.17em;
    text-transform: uppercase;
}

.nav-links {
    display: flex;
    align-items: center;
    gap: 0.3rem;
}

.nav-links > a {
    padding: 0.55rem 0.78rem;
    border-radius: 0;
    color: var(--muted);
    font-size: 0.86rem;
    text-decoration: none;
    cursor: pointer;
    list-style: none;
}

/* No hover state on the nav links: keyboard focus still gets one below. */
.nav-links > a:focus-visible {
    color: var(--ink);
    background: rgba(19, 47, 96, 0.06);
    outline: none;
}

.catalog-section,
.header-section,
.workbench,
.status-section,
.docs-section,
.site-footer {
    width: min(calc(100% - 2rem), var(--max-width));
    margin-inline: auto;
}

.eyebrow,
.panel-kicker {
    color: var(--blue);
    font-size: 0.72rem;
    font-weight: 750;
    letter-spacing: 0.17em;
    text-transform: uppercase;
}

/* The one header every page opens with: the title, the sentence about the page
   beside it, and whatever else that page needs -- an eyebrow, a back link,
   actions -- on its own full-width row, in the order it is written. */
.header-section {
    padding: 4rem 0 6px;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: 4rem;
    border-bottom: 1px solid var(--line);
}

.header-section > * {
    grid-column: 1 / -1;
}

.header-section h1,
.header-section > .header-title-row {
    grid-column: 1;
    max-width: 830px;
    margin: 0;
    font-family: Georgia, "Times New Roman", serif;
    font-size: clamp(1.8rem, 4vw, 2.8rem);
    font-weight: 500;
    letter-spacing: -0.045em;
    line-height: 0.98;
}

/* Only the paragraph the title introduces sits next to it. A page with more to
   say puts the rest in `.hero-copy`, or lets it fall below at full width. */
.header-section > h1 + p,
.header-section > h1 + .hero-copy,
.header-section > .header-title-row + p,
.header-section > .header-title-row + .hero-copy {
    grid-column: 2;
}

/* A header that carries an aside -- the tool page's bookmark button -- opens a
   third column for it, so the button sits beside the title and its copy
   instead of taking a row of its own and pushing the form down the page. */
.header-section:has(> .header-aside) {
    grid-template-columns: auto minmax(0, 1fr) auto;
}

.header-section > .header-aside {
    grid-column: 3;
    margin-top: 0;
}

.header-section > p,
.header-section > .hero-copy {
    max-width: 700px;
    margin-block: 0.4rem;
    color: var(--muted);
    font-size: clamp(1rem, 2vw, 1.18rem);
}

.hero-copy > p + p {
    margin-top: 0.6rem;
}

.hero-actions {
    margin-top: 2rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.7rem;
}

.button {
    min-height: 44px;
    padding: 0.68rem 1.05rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    border-radius: 0;
    font-weight: 720;
    font-size: 0.86rem;
    text-decoration: none;
    cursor: pointer;
    transition: transform 150ms ease, background 150ms ease, border-color 150ms ease;
}

.button:hover:not(:disabled) {
    transform: translateY(-1px);
}

.button:focus-visible,
.copy-button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

.button:disabled {
    cursor: wait;
    opacity: 0.55;
}

/* `display` above wins over the attribute on its own, and a hidden button that
   still takes up a slot in a row of them is worse than no attribute at all. */
.button[hidden] {
    display: none;
}

.button-primary {
    color: #ffffff;
    background: var(--blue);
}

.button-primary:hover:not(:disabled) {
    background: #1a5fd6;
}

.button-secondary,
.button-quiet {
    color: var(--ink);
    border-color: var(--line-strong);
    background: rgba(19, 47, 96, 0.035);
}

.button-secondary:hover:not(:disabled),
.button-quiet:hover:not(:disabled) {
    background: rgba(19, 47, 96, 0.08);
}

.button-danger {
    color: var(--red);
    border-color: rgba(185, 28, 28, 0.4);
    background: rgba(185, 28, 28, 0.07);
}

.button-danger:hover:not(:disabled) {
    background: rgba(185, 28, 28, 0.14);
}

.catalog-filters {
    width: min(calc(100% - 2rem), var(--max-width));
    margin-inline: auto;
    padding-top: 1.25rem;
}

.tool-search-input {
    max-width: 360px;
}

.category-filter-pills {
    margin-top: 0.75rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
}

.category-filter-pill {
    padding: 0.32rem 0.65rem;
    color: var(--blue);
    transition: none;
    font-size: 0.68rem;
    font-weight: 750;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    border: 1px solid rgba(31, 111, 235, 0.25);
    border-radius: 0;
    background: rgba(31, 111, 235, 0.07);
    cursor: pointer;
}

.category-filter-pill-active {
    color: #fff;
    background: var(--blue);
    border-color: var(--blue);
}

.tool-empty-state {
    padding: 2rem 0;
    color: var(--faint);
    font-size: 0.9rem;
    text-align: center;
}

.catalog-section {
    padding: 1rem 0 6rem;
}

.section-heading {
    margin-bottom: 1.5rem;
    padding-top: 2rem;
    display: flex;
    align-items: end;
    justify-content: space-between;
    gap: 2rem;
    border-top: 1px solid var(--line);
}

/* The header already closes with a rule, so a section opening right below it
   does not draw a second one an inch lower. */
.header-section + .docs-section > .section-heading:first-child {
    border-top: none;
}

.section-heading h2,
.panel-heading h2 {
    margin: 0.35rem 0 0;
    font-family: Georgia, "Times New Roman", serif;
    font-weight: 500;
    letter-spacing: -0.02em;
}

.section-heading h2 {
    font-size: clamp(1.8rem, 4vw, 2.8rem);
}

.section-heading > p {
    max-width: 330px;
    margin: 0;
    color: var(--faint);
    font-size: 0.84rem;
    text-align: right;
}

.tool-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.8rem;
}

.tool-card[hidden] {
    display: none;
}

.tool-card {
    padding: 1.0rem;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--line);
    border-radius: 0;
    background:
        linear-gradient(145deg, rgba(31, 111, 235, 0.045), transparent 55%),
        var(--bg-soft);
    text-decoration: none;
    transition: border-color 160ms ease, transform 160ms ease, background 160ms ease;
}

.tool-card:hover {
    border-color: rgba(31, 111, 235, 0.45);
    background-color: #f8fbff;
}

/* A tool the reader has bookmarked. Green rather than the catalog's blue so
   it reads as their own mark on the card, and stated on both properties
   because `.tool-card` paints a gradient over its colour. */
.tool-card-bookmarked {
    border-color: var(--bookmark-line);
    background: var(--bookmark-fill);
}

.tool-card-bookmarked:hover {
    border-color: var(--bookmark-line);
    background-color: var(--bookmark-fill-strong);
}

/* Bookmarking this tool, or dropping it, from its own page. */
.bookmark-button[aria-pressed="true"] {
    color: var(--bookmark-line);
    border-color: var(--bookmark-line);
    background: var(--bookmark-fill);
}

.bookmark-button[aria-pressed="true"]:hover:not(:disabled) {
    background: var(--bookmark-fill-strong);
}

.bookmark-star {
    /* Grey until the tool is bookmarked, so the button's two states differ
       at a glance rather than only in the words beside the star. */
    color: var(--faint);
}

.bookmark-button[aria-pressed="true"] .bookmark-star {
    color: var(--bookmark-line);
}

/* What the dashboard's bookmark checkboxes say instead of a save button. */
.bookmark-status {
    color: var(--faint);
    font-size: 0.72rem;
}

.bookmark-status-failed {
    color: var(--red);
    font-weight: 700;
}

.card-footer,
.panel-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.tool-card h3 {
    margin-top: 0;
    /*margin: 1.5rem 0 0.55rem;*/
    font-family: Georgia, "Times New Roman", serif;
    font-size: 1.55rem;
    font-weight: 500;
}

.tool-card p {
    margin: 0;
    color: var(--muted);
    font-size: 0.88rem;
}

.card-footer {
    margin-top: auto;
    padding-top: 1.25rem;
    align-items: end;
}

.hero-links {
    margin-top: 1.1rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.4rem;
}

.hero-docs-link {
    display: inline-block;
    color: var(--blue);
    font-size: 0.86rem;
    font-weight: 650;
    text-decoration: none;
}

.hero-docs-link:hover {
    text-decoration: underline;
}

.back-link {
    margin-bottom: 2.2rem;
    display: inline-block;
    color: var(--faint);
    font-size: 0.78rem;
    text-decoration: none;
}

.back-link:hover {
    color: var(--blue);
}

.status-section {
    padding: 1rem 0 6rem;
}

/* Which machine served the page, and what the other one is doing. Above the
   search and filters because it qualifies every row below it: on a web node
   most of these tools live on the compute node, and what they can report
   depends on whether that machine is up. */
.node-panel {
    margin: 0 0 1rem;
    padding: 0.8rem 1.1rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 2rem;
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--surface-raised);
}

.node-entry {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.node-chip {
    padding: 0.16rem 0.6rem;
    border-radius: 0;
    border: 1px solid currentColor;
    font-size: 0.76rem;
    font-weight: 700;
}

.node-chip[data-tone="ok"] {
    color: var(--ok);
}

/* Starting and stopping both, since what they share is that the state is
   about to be a different one. */
.node-chip[data-tone="busy"] {
    color: var(--amber);
}

.node-chip[data-tone="off"] {
    color: var(--faint);
}

.node-chip[data-tone="bad"] {
    color: var(--red);
}

.node-facts {
    color: var(--muted);
    font-size: 0.78rem;
}

.node-facts code {
    font-size: 0.72rem;
}

.node-refresh {
    padding: 0.2rem 0.65rem;
    color: var(--muted);
    border: 1px solid var(--line-strong);
    border-radius: 0;
    background: var(--bg-soft);
    font-size: 0.72rem;
    cursor: pointer;
}

.node-refresh:hover:not(:disabled) {
    color: var(--blue);
    border-color: rgba(31, 111, 235, 0.45);
}

.node-refresh:disabled {
    opacity: 0.55;
    cursor: default;
}

/* The instance button is styled with .full-status-button below rather than
   here: both are the deliberate action on their part of this page, as against
   Refresh beside it, which only re-reads what is already true. */

/* Deliberately quieter than the button it sits beside: it saves on its own, so
   it reads as a setting rather than as something else to press. Label, field,
   and unit stay on one line -- this sits in the row of controls above the
   table, and anything that wraps here makes the whole panel taller. */
.node-timeout {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.4rem;
    color: var(--muted);
    font-size: 0.78rem;
    white-space: nowrap;
}

/* Typed as [type="number"] rather than as a bare `input` so it beats the
   form-wide rule further down this file, which is written for the tool forms:
   full width and a 44px touch target are right there and would make this one
   field as wide as the panel and taller than the buttons beside it. */
.node-timeout input[type="number"] {
    /* Room for four digits and the spinner arrows, which is more minutes than
       anyone will set, and no room for the field to grow into. */
    width: 60px;
    flex: 0 0 auto;
    min-height: 0;
    padding: 0.2rem 0.4rem;
    color: var(--ink);
    border: 1px solid var(--line-strong);
    border-radius: 0;
    background: var(--bg-soft);
    font: inherit;
    font-size: 0.78rem;
    text-align: right;
}

.node-timeout input[type="number"]:focus {
    border-color: rgba(31, 111, 235, 0.45);
}

.node-timeout span:last-child {
    color: var(--faint);
    font-size: 0.72rem;
}

/* Only present when something needs explaining, so it takes no room the rest
   of the time -- otherwise a settled node would leave a gap the size of the
   longest message. */
.node-note:empty {
    display: none;
}

.node-note {
    flex-basis: 100%;
    margin: 0;
    color: var(--muted);
    font-size: 0.78rem;
    line-height: 1.5;
}

.status-controls {
    margin: 0 0 0.85rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.status-search {
    flex: 1 1 18rem;
    max-width: 26rem;
}

.status-search input {
    padding: 0.55rem 0.85rem;
    width: 100%;
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--bg-soft);
    font: inherit;
    font-size: 0.85rem;
}

.status-search input:focus-visible {
    border-color: rgba(31, 111, 235, 0.45);
    outline: none;
}

.status-filter {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    color: var(--muted);
    font-size: 0.78rem;
    cursor: pointer;
    user-select: none;
}

.status-filter:hover {
    color: var(--ink);
}

.status-filter input {
    width: 0.95rem;
    height: 0.95rem;
    accent-color: var(--blue);
    cursor: pointer;
}

.status-filter input:focus-visible {
    outline: 2px solid var(--blue);
    outline-offset: 2px;
}

/* The pills themselves are the index page's, laid out under the search row. */
.status-category-filters {
    margin: 0 0 0.9rem;
}

.status-count {
    margin: 0;
    color: var(--faint);
    font-size: 0.75rem;
    letter-spacing: 0.04em;
}

/* Shared with the node panel's start/stop button (.node-action), which is the
   same kind of thing: the one control on its part of the page that does
   something rather than reporting something. */
.full-status-button,
.node-action {
    padding: 0.5rem 0.75rem;
    color: var(--blue);
    border: 1px solid rgba(31, 111, 235, 0.32);
    border-radius: 0;
    background: rgba(31, 111, 235, 0.06);
    font: inherit;
    font-size: 0.76rem;
    font-weight: 700;
    cursor: pointer;
}

.full-status-button:hover:not(:disabled),
.node-action:hover:not(:disabled) {
    background: rgba(31, 111, 235, 0.12);
}

.full-status-button:disabled,
.node-action:disabled {
    color: var(--faint);
    border-color: var(--line);
    background: transparent;
    cursor: progress;
}

.full-status-progress {
    margin: 0;
    flex-basis: 100%;
    min-height: 1.1em;
    color: var(--faint);
    font-size: 0.74rem;
    text-align: right;
}

.status-table {
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--bg-soft);
}

.status-row {
    display: grid;
    grid-template-columns: 1fr 0.85fr 0.5fr 0.9fr 0.8fr 0.85fr 0.75fr 0.55fr 1.25fr 0.95fr;
    gap: 1rem;
    align-items: start;
    padding: 0.85rem 1.1rem;
    border-bottom: 1px solid var(--line);
}

.status-table.has-full-status .status-row {
    grid-template-columns: 1fr 0.85fr 0.5fr 0.9fr 0.8fr 0.85fr 0.75fr 0.55fr 1.25fr 1.35fr 0.95fr;
}

.full-status-column[hidden] {
    display: none !important;
}

/* Beats `display: grid` above, which the plain [hidden] rule cannot. */
.status-row[hidden] {
    display: none;
}

.status-row:last-child {
    border-bottom: none;
}

.status-row-head {
    color: var(--faint);
    font-size: 0.68rem;
    font-weight: 750;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.status-row-empty {
    display: block;
    color: var(--muted);
    font-size: 0.85rem;
}

.status-sort {
    padding: 0;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    color: inherit;
    border: 0;
    background: none;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    cursor: pointer;
}

.status-sort:hover {
    color: var(--blue);
}

.status-sort:focus-visible {
    border-radius: 0;
    outline: 2px solid var(--blue);
    outline-offset: 3px;
}

/* The indicator doubles as the affordance: an idle column shows a dim glyph,
   so a sortable header is distinguishable from Device and Detail. */
.status-sort::after {
    content: "↕";
    opacity: 0.4;
    font-size: 0.85em;
}

[aria-sort="ascending"] .status-sort,
[aria-sort="descending"] .status-sort {
    color: var(--blue);
}

[aria-sort="ascending"] .status-sort::after {
    content: "↑";
    opacity: 1;
}

[aria-sort="descending"] .status-sort::after {
    content: "↓";
    opacity: 1;
}

.expense-chip {
    padding: 0.2rem 0.5rem;
    display: inline-block;
    width: fit-content;
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: 0;
    font-size: 0.7rem;
}

.expense-chip[data-level="2"] {
    color: var(--amber);
    border-color: rgba(180, 83, 9, 0.28);
}

.expense-chip[data-level="3"] {
    color: var(--red);
    border-color: rgba(185, 28, 28, 0.28);
}

.status-tool a {
    color: var(--ink);
    font-weight: 650;
    text-decoration: none;
}

.status-tool a:hover {
    color: var(--blue);
}

.status-category {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    color: var(--muted);
    font-size: 0.8rem;
}

.category-chip {
    padding: 0.2rem 0.5rem;
    display: inline-block;
    width: fit-content;
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: 0;
    font-size: 0.7rem;
}

.status-badge {
    padding: 0.28rem 0.55rem;
    display: inline-block;
    width: fit-content;
    border: 1px solid var(--line-strong);
    border-radius: 0;
    font-size: 0.68rem;
    font-weight: 750;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.status-badge[data-state="pass"] {
    color: var(--ok);
    border-color: rgba(21, 128, 61, 0.32);
    background: rgba(21, 128, 61, 0.08);
}

/* `CheckResult.NOT_INSTALLED`, whose label is "Can't find": these select on
   the enum's member name, which is what both the template and the status
   payloads carry, rather than on the words in the badge. */
.status-badge[data-state="not_installed"] {
    color: var(--amber);
    border-color: rgba(180, 83, 9, 0.3);
    background: rgba(180, 83, 9, 0.08);
}

.status-badge[data-state="error"] {
    color: var(--red);
    border-color: rgba(185, 28, 28, 0.3);
    background: rgba(185, 28, 28, 0.08);
}

/* Nothing was probed -- on a split deployment, the compute node is stopped.
   Deliberately the quietest of the four: it is not a finding about the tool. */
.status-badge[data-state="not_checked"] {
    color: var(--muted);
    border-color: var(--line);
    background: transparent;
}

.launch-chip {
    padding: 0.2rem 0.5rem;
    display: inline-block;
    width: fit-content;
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: 0;
    font-size: 0.7rem;
}

/* Levels follow LicenseCategory's declaration order, so the colour tracks how
   restrictive the terms are: 1 permissive, 2 copyleft, 3 non-commercial,
   4 proprietary. */
.license-chip {
    padding: 0.2rem 0.5rem;
    display: inline-block;
    width: fit-content;
    color: var(--ok);
    border: 1px solid rgba(21, 128, 61, 0.32);
    border-radius: 0;
    font-size: 0.7rem;
    cursor: help;
}

.license-chip[data-level="2"] {
    color: var(--muted);
    border-color: var(--line);
}

.license-chip[data-level="3"] {
    color: var(--amber);
    border-color: rgba(180, 83, 9, 0.28);
}

.license-chip[data-level="4"] {
    color: var(--red);
    border-color: rgba(185, 28, 28, 0.28);
}

.top-chip {
    padding: 0.2rem 0.5rem;
    display: inline-block;
    width: fit-content;
    color: var(--blue);
    border: 1px solid rgba(31, 111, 235, 0.28);
    border-radius: 0;
    font-size: 0.7rem;
    white-space: nowrap;
}

.device-chip {
    padding: 0.2rem 0.5rem;
    display: inline-block;
    width: fit-content;
    color: var(--faint);
    border: 1px solid var(--line);
    border-radius: 0;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.68rem;
}

.status-empty {
    color: var(--faint);
}

.status-detail {
    min-width: 0;
    color: var(--faint);
    font-size: 0.74rem;
    line-height: 1.45;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.full-status-cell {
    min-width: 0;
    color: var(--faint);
    font-size: 0.74rem;
    line-height: 1.45;
    overflow-wrap: anywhere;
}

.full-status-result {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 0.35rem;
}

.full-status-detail {
    display: block;
    white-space: pre-wrap;
}

.status-install {
    min-width: 0;
}

.install-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.install-button {
    padding: 0.28rem 0.45rem;
    color: var(--blue);
    border: 1px solid rgba(31, 111, 235, 0.32);
    border-radius: 0;
    background: transparent;
    font: inherit;
    font-size: 0.72rem;
    cursor: pointer;
}

.install-button:hover:not(:disabled) {
    background: rgba(31, 111, 235, 0.08);
}

.install-button:disabled {
    color: var(--faint);
    border-color: var(--line);
    cursor: progress;
}

.install-button[data-install-action="uninstall"] {
    color: var(--red);
    border-color: rgba(185, 28, 28, 0.28);
}

.install-button[data-install-action="uninstall"]:hover:not(:disabled) {
    background: rgba(185, 28, 28, 0.07);
}

/* A recipe runs for minutes or hours, so the row says whether something is
   happening; the step-by-step detail lives in .install-banner instead, which
   has room for it. */
.install-note:empty {
    display: none;
}

/* Clamped rather than wrapped: a failure's detail can run to any length, and
   one tall row would push the rest of the table off the screen. The full
   text is the element's tooltip. */
.install-note {
    margin: 0.4rem 0 0;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    color: var(--faint);
    font-size: 0.7rem;
    line-height: 1.4;
    overflow-wrap: anywhere;
}

.install-note[data-state="busy"] {
    color: var(--amber);
    font-weight: 700;
}

.install-note[data-state="ok"] {
    color: var(--ok);
}

.install-note[data-state="failed"] {
    color: var(--red);
}

/* Directly above the table, fixed height so a recipe starting or finishing
   never shifts the table below it. Idle and active share the box; only its
   contents and emphasis change. */
.install-banner {
    margin: 0 0 1rem;
    padding: 0.85rem 1.1rem;
    min-height: 5.75rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 0.35rem;
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--surface-raised);
}

.install-banner:not(.is-active) {
    border-style: dashed;
}

.install-banner-title {
    margin: 0;
    color: var(--faint);
    font-size: 0.85rem;
    font-weight: 600;
}

.install-banner.is-active .install-banner-title {
    color: var(--amber);
    font-weight: 700;
}

.install-banner-detail {
    margin: 0;
    color: var(--muted);
    font-size: 0.78rem;
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}

.install-manual {
    color: var(--faint);
    font-size: 0.72rem;
    cursor: help;
}

.workbench {
    padding: 2rem 0 6rem;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 290px;
    gap: 1rem;
    align-items: start;
}

.form-panel,
.sidebar-card {
    border: 1px solid var(--line);
    border-radius: 0;
    background: rgba(255, 255, 255, 0.92);
}

.form-panel {
    min-width: 0;
    padding: clamp(1rem, 3vw, 1.6rem);
}

.panel-heading {
    margin-bottom: 1.35rem;
}

.panel-heading h2 {
    font-size: 1.45rem;
}

.prefill-note {
    margin-bottom: 1.2rem;
    padding: 0.7rem 0.85rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    border: 1px solid rgba(31, 111, 235, 0.35);
    border-radius: 0;
    color: var(--muted);
    background: var(--blue-soft);
    font-size: 0.76rem;
}

.prefill-note[hidden] {
    display: none;
}

.prefill-note a {
    color: var(--blue);
    font-weight: 700;
    text-decoration: none;
}

/* Decisions about the run rather than inputs to the tool: how long it will be
   before it starts, and who hears about it when it ends. Above the form, and
   visibly not part of it. */
.run-options {
    margin-bottom: 1.2rem;
    padding-bottom: 1.1rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem 1.2rem;
    border-bottom: 1px solid var(--line);
}

.queue-note {
    margin: 0;
    color: var(--muted);
    font-size: 0.76rem;
}

.notify-option {
    min-width: 0;
}

.notify-label {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    color: var(--muted);
    font-size: 0.76rem;
    cursor: pointer;
}

.notify-label input[type="checkbox"] {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    accent-color: var(--blue);
}

.notify-label strong {
    color: var(--ink);
    font-weight: 700;
}

.notify-label-anonymous input[type="email"] {
    min-width: 210px;
    min-height: 34px;
    padding: 0.3rem 0.55rem;
    border: 1px solid var(--line-strong);
    border-radius: 0;
    background: #ffffff;
    font-size: 0.76rem;
}

@media (max-width: 720px) {
    .run-options {
        flex-direction: column;
        align-items: flex-start;
    }
}

.run-note {
    margin-top: 1.1rem;
    padding: 0.8rem 0.9rem;
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--surface-raised);
    font-size: 0.78rem;
}

.run-note[hidden] {
    display: none;
}

.run-note[data-state="pending"] {
    border-color: rgba(180, 83, 9, 0.3);
    background: rgba(180, 83, 9, 0.07);
}

.run-note[data-state="error"] {
    border-color: rgba(185, 28, 28, 0.3);
    background: rgba(185, 28, 28, 0.06);
}

.run-note[data-state="success"] {
    border-color: rgba(21, 128, 61, 0.3);
    background: rgba(21, 128, 61, 0.07);
}

.run-note-message {
    margin: 0;
    color: var(--ink);
    white-space: pre-wrap;
    word-break: break-word;
}

.run-note-link {
    margin-top: 0.55rem;
    display: inline-block;
    color: var(--blue);
    font-weight: 700;
    font-size: 0.75rem;
    text-decoration: none;
}

.run-note-link[hidden] {
    display: none;
}


.task-toggle {
    margin-bottom: 1.2rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.task-button {
    min-height: 38px;
    padding: 0.5rem 0.9rem;
    color: var(--muted);
    border: 1px solid var(--line-strong);
    border-radius: 0;
    background: rgba(19, 47, 96, 0.035);
    font-weight: 700;
    font-size: 0.8rem;
    cursor: pointer;
    transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}

/* Scoped off the active button: a plain .task-button:hover outranks
   .task-button-active and painted the selected mode grey under the cursor,
   which left its white label unreadable. */
.task-button:hover:not(.task-button-active) {
    background: rgba(19, 47, 96, 0.08);
}

.task-button-active {
    color: #ffffff;
    border-color: var(--blue);
    background: var(--blue);
}

/* Darkens the way .button-primary does, so the selected mode still answers
   the cursor without losing its contrast. */
.task-button-active:hover {
    border-color: #1a5fd6;
    background: #1a5fd6;
}

.form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
}

.field-group {
    min-width: 0;
}

.field-group-inactive {
    display: none;
}

/* A field section's heading, with its tool's documentation link beside it. */
.field-section-heading {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 1rem;
}

/* The chaining pickers above a tool's primary input field: a previous run's
   output, or a saved sequence, in place of an upload. Set apart from the
   field's own entry so the two are not read as one control. */
.input-sources {
    margin-bottom: 0.55rem;
    padding-bottom: 0.55rem;
    border-bottom: 1px dashed var(--line);
    display: grid;
    gap: 0.3rem;
}

.input-sources > label {
    color: #24384f;
    font-size: 0.72rem;
    font-weight: 700;
}

.input-sources > label + select {
    margin-bottom: 0.35rem;
}

.input-source-failed {
    color: var(--red);
    font-weight: 700;
}

.field-wide,
.field-checkbox {
    grid-column: 1 / -1;
}

.field-group > label:not(.checkbox-label) {
    margin-bottom: 0.4rem;
    display: block;
    color: #24384f;
    font-size: 0.76rem;
    font-weight: 700;
}

input:not([type="checkbox"]),
select,
textarea {
    width: 100%;
    color: var(--ink);
    border: 1px solid var(--line-strong);
    border-radius: 0;
    background: #ffffff;
}

input:not([type="checkbox"]),
select {
    min-height: 44px;
    padding: 0.64rem 0.72rem;
}

textarea {
    min-height: 118px;
    padding: 0.78rem;
    resize: vertical;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.78rem;
    line-height: 1.5;
    tab-size: 4;
}

select {
    color-scheme: light;
}

/* The white background these controls are given otherwise hides the browser's
   own greying of a disabled one, leaving a field that looks ready to use. */
input:disabled,
select:disabled,
textarea:disabled {
    color: var(--faint);
    border-color: var(--line);
    background: var(--surface-raised);
    cursor: not-allowed;
}

/* A whole field that is off: its label and help text fade with its control, so
   the field reads as unavailable before anybody clicks it. */
.field-group[data-disabled] > label:not(.checkbox-label),
.field-group[data-disabled] .field-help {
    color: var(--faint);
    opacity: 0.75;
}

.field-help,
.checkbox-label small {
    margin-top: 0.35rem;
    display: block;
    color: var(--faint);
    font-size: 0.69rem;
    font-weight: 450;
}

.checkbox-label {
    min-height: 58px;
    padding: 0.78rem;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    border: 1px solid var(--line);
    border-radius: 0;
    background: rgba(19, 47, 96, 0.028);
    cursor: pointer;
}

/* A `display` of its own would otherwise beat the attribute, and the tool
   picker hides its boxes by setting it. */
.checkbox-label[hidden] {
    display: none;
}

.checkbox-label input {
    width: 17px;
    height: 17px;
    margin: 0.15rem 0 0;
    accent-color: var(--blue);
}

.checkbox-label strong {
    display: block;
    color: #24384f;
    font-size: 0.78rem;
}

.molecule-list {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    margin-bottom: 0.85rem;
}

.molecule-box {
    padding: 0.9rem;
    border: 1px solid var(--line-strong);
    border-radius: 0;
    background: rgba(19, 47, 96, 0.028);
}

.molecule-box-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.7rem;
}

.molecule-type-select {
    flex: 0 0 auto;
    width: auto;
    min-width: 130px;
}

.molecule-delete {
    margin-left: auto;
}

.molecule-box-body label {
    margin-bottom: 0.4rem;
    display: block;
    color: #24384f;
    font-size: 0.76rem;
    font-weight: 700;
}

.molecule-metadata-grid {
    display: grid;
    grid-template-columns: minmax(180px, 1fr) minmax(100px, 0.35fr);
    gap: 0.65rem;
    margin-bottom: 0.7rem;
}

.molecule-box-body > div:not(.molecule-metadata-grid) {
    margin-top: 0.7rem;
}

.molecule-box-body textarea {
    min-height: 90px;
}

.molecule-box-footer {
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.molecule-box-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
}

.molecule-cyclic-label {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    color: var(--muted);
    font-size: 0.82rem;
    cursor: pointer;
}

.molecule-cyclic-label input {
    width: 16px;
    height: 16px;
    margin: 0;
    accent-color: var(--blue);
}

.molecule-modifications {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.molecule-modification-row {
    display: grid;
    grid-template-columns: 110px 1fr auto;
    gap: 0.5rem;
    align-items: center;
}

@media (max-width: 620px) {
    .molecule-metadata-grid {
        grid-template-columns: 1fr;
    }
}

.molecule-add-button {
    width: 100%;
}

.form-actions {
    margin-top: 1.3rem;
    padding-top: 1.2rem;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    border-top: 1px solid var(--line);
}

/* The preset picker and the badge saying whether the form is still that
   preset, side by side: one answers "which example", the other "still it?",
   and they are read together. The badge takes only the room its word needs. */
.preset-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.preset-row > select {
    min-width: 0;
    flex: 1;
}

.preset-state {
    flex: none;
    padding: 0.2rem 0.5rem;
    border: 1px solid currentColor;
    color: var(--ok);
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}

.preset-state[data-state="altered"] {
    color: var(--amber);
}

.request-status {
    min-width: 0;
    margin-left: auto;
    overflow: hidden;
    color: var(--faint);
    font-size: 0.72rem;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.request-status[data-state="pending"] {
    color: var(--amber);
}

.request-status[data-state="success"] {
    color: var(--ok);
}

.request-status[data-state="error"] {
    color: var(--red);
}

.tool-sidebar {
    display: grid;
    gap: 1rem;
}

.sidebar-card {
    padding: 1rem;
}

.sidebar-card p {
    margin: 0.65rem 0;
    color: var(--muted);
    font-size: 0.78rem;
}

.sidebar-card a {
    color: var(--blue);
    font-size: 0.75rem;
    font-weight: 700;
    text-decoration: none;
}

.endpoint-code {
    margin-top: 0.55rem;
    padding: 0.65rem;
    display: block;
    overflow-x: auto;
    border-radius: 0;
    color: #14498f;
    background: var(--surface-raised);
    font-size: 0.68rem;
    white-space: nowrap;
}

.structure-viewer-panel {
    min-width: 0;
    padding: clamp(1rem, 3vw, 1.35rem);
    grid-column: 1 / -1;
    border: 1px solid var(--line);
    border-radius: 0;
    background: rgba(255, 255, 255, 0.92);
}

.structure-viewer-panel[hidden],
.structure-picker[hidden] {
    display: none;
}

.structure-viewer-heading {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
}

.structure-picker {
    min-width: min(360px, 45vw);
}

.structure-picker label {
    margin-bottom: 0.35rem;
    display: block;
    color: var(--muted);
    font-size: 0.7rem;
    font-weight: 700;
}

.structure-picker select {
    min-height: 38px;
    padding: 0.45rem 0.6rem;
    font-size: 0.75rem;
}

.protein-viewer {
    position: relative;
    width: 100%;
    height: min(68vh, 680px);
    min-height: 480px;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: 0;
    background: #f5f8fd;
}

.protein-viewer .msp-plugin {
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

.structure-viewer-status {
    min-height: 1.2em;
    margin: 0.65rem 0 0;
    color: var(--muted);
    font-size: 0.74rem;
}

.copy-button {
    padding: 0.38rem 0.62rem;
    color: var(--muted);
    border: 1px solid var(--line-strong);
    border-radius: 0;
    background: transparent;
    font-size: 0.7rem;
    cursor: pointer;
}

.copy-button:disabled {
    opacity: 0.4;
    cursor: default;
}

.docs-section {
    padding: 2.6rem 0 0;
}

.docs-section:last-of-type {
    padding-bottom: 5rem;
}

.docs-subheading {
    margin: 2.2rem 0 0.75rem;
    font-size: 1.05rem;
}

/* The dashboard is a landing place rather than a page to read, so its title and
   the sections under it sit closer together than on a page meant to be read. */
.header-section.dashboard-header {
    padding-top: 20px;
}

.docs-section.dashboard-section {
    padding-top: 20px;
}

.dashboard-section > .docs-subheading:first-child {
    margin-top: 0;
}

/* What separates the first of these from the page header is the header's own
   rule; between two sections it is only their content, which wants more room. */
.dashboard-section + .dashboard-section {
    padding-top: 2.1rem;
}

/* Every card is written out and hidden until bookmarked, so a list with nothing
   showing still has to give up its trailing space. */
.bookmark-list:not(:has(> [data-bookmark-card]:not([hidden]))) {
    margin-bottom: 0;
}

.docs-note {
    max-width: 720px;
    margin: 0 0 0.9rem;
    color: var(--muted);
    font-size: 0.85rem;
}

.docs-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 0.9rem;
}

.docs-cards .sidebar-card code {
    color: #14498f;
}

.docs-steps {
    margin: 0;
    padding: 0;
    display: grid;
    gap: 1.4rem;
    list-style: none;
    counter-reset: docs-step;
}

.docs-steps > li {
    padding-left: 2.6rem;
    position: relative;
    counter-increment: docs-step;
}

.docs-steps > li::before {
    content: counter(docs-step);
    position: absolute;
    top: 0.1rem;
    left: 0;
    width: 1.8rem;
    height: 1.8rem;
    display: grid;
    place-items: center;
    color: var(--blue);
    border: 1px solid var(--line-strong);
    border-radius: 50%;
    font-size: 0.78rem;
    font-weight: 750;
}

.docs-steps h3 {
    margin: 0 0 0.35rem;
    font-size: 1rem;
}

.docs-steps p {
    max-width: 720px;
    margin: 0 0 0.7rem;
    color: var(--muted);
    font-size: 0.88rem;
}

.docs-code {
    max-height: 30rem;
    margin: 0 0 0.8rem;
    padding: 0.9rem 1rem;
    overflow: auto;
    border: 1px solid var(--line);
    border-radius: 0;
    color: #1b2c44;
    background: #f5f8fd;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.74rem;
    line-height: 1.55;
    white-space: pre;
}

.docs-table-wrap {
    overflow-x: auto;
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--surface);
}

.docs-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.82rem;
}

.docs-table th,
.docs-table td {
    padding: 0.7rem 0.9rem;
    text-align: left;
    vertical-align: top;
    border-bottom: 1px solid var(--line);
}

.docs-table thead th {
    color: var(--faint);
    background: var(--surface-raised);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    white-space: nowrap;
}

.docs-table tbody tr:last-child td {
    border-bottom: none;
}

.docs-table td {
    color: var(--muted);
}

.docs-table code {
    color: #14498f;
    font-size: 0.78rem;
    word-break: break-word;
}

.docs-table a {
    color: var(--blue);
    text-decoration: none;
}

.docs-table a:hover {
    text-decoration: underline;
}

.docs-table-fields td:first-child,
.docs-table-endpoints td:nth-child(2) {
    min-width: 11rem;
}

.docs-table-fields td:nth-child(5) {
    min-width: 20rem;
}

.docs-default {
    display: inline-block;
    max-width: 16rem;
    overflow-wrap: anywhere;
}

.docs-help {
    margin-top: 0.25rem;
    display: block;
    color: var(--faint);
    font-size: 0.78rem;
}

.docs-muted {
    margin-top: 0.15rem;
    display: block;
    color: var(--faint);
    font-size: 0.68rem;
}

.docs-tag {
    padding: 0.05rem 0.42rem;
    display: inline-block;
    color: var(--blue);
    border: 1px solid var(--line-strong);
    border-radius: 0;
    font-size: 0.64rem;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

.site-footer {
    padding: 2rem 0 3rem;
    display: flex;
    justify-content: space-between;
    gap: 2rem;
    border-top: 1px solid var(--line);
    color: var(--faint);
    font-size: 0.75rem;
}

.site-footer strong {
    color: var(--muted);
}

/* Bookmarks page, and the sign-in form that guards it. */

.nav-signout {
    margin: 0;
}

.nav-signout button {
    padding: 0.55rem 0.78rem;
    border: 0;
    border-radius: 0;
    color: var(--muted);
    background: transparent;
    font-size: 0.86rem;
    cursor: pointer;
}

.nav-signout button:focus-visible {
    color: var(--ink);
    background: rgba(19, 47, 96, 0.06);
    outline: none;
}

.bookmark-messages {
    margin: 1.2rem 0 0;
    padding: 0;
    display: grid;
    gap: 0.5rem;
    list-style: none;
}

.bookmark-message {
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--line);
    border-left-width: 3px;
    border-radius: 0;
    background: var(--surface);
    font-size: 0.82rem;
}

.bookmark-message-success {
    border-left-color: var(--ok);
}

.bookmark-message-info {
    border-left-color: var(--blue);
}

.bookmark-message-error {
    border-left-color: var(--red);
    color: var(--red);
}

.bookmark-list {
    margin-bottom: 1.4rem;
    display: grid;
    gap: 0.9rem;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.bookmark-card p {
    margin: 0.35rem 0 0.6rem;
}

.bookmark-form {
    margin-bottom: 1rem;
    padding: clamp(1rem, 3vw, 1.35rem);
    border: 1px solid var(--line);
    border-radius: 0;
    background: var(--surface);
}

.view-as-form {
    max-width: 520px;
    display: flex;
    align-items: flex-end;
    gap: 0.9rem;
    flex-wrap: wrap;
}

.view-as-form .bookmark-field {
    flex: 1 1 260px;
}

.bookmark-signin {
    max-width: 420px;
    display: grid;
    gap: 0.9rem;
}

.bookmark-tool-grid {
    margin-top: 0.9rem;
    display: grid;
    gap: 0.5rem;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}

.bookmark-row {
    display: flex;
    align-items: flex-end;
    gap: 0.9rem;
    flex-wrap: wrap;
}

.bookmark-field {
    min-width: 0;
    display: grid;
    gap: 0.3rem;
    font-size: 0.78rem;
}

.bookmark-field-wide {
    flex: 1 1 320px;
}

/* Name over type, so the pair sits as one column beside the sequence box.
   `align-content` keeps them against the bottom of the row, which is where
   `.bookmark-row` lines every other field up. */
.bookmark-field-stack {
    display: grid;
    gap: 0.7rem;
    align-content: end;
}

.bookmark-field > span {
    color: var(--faint);
    font-size: 0.7rem;
    font-weight: 750;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.bookmark-field > span small {
    font-weight: 450;
    letter-spacing: 0;
    text-transform: none;
}

.bookmark-field input,
.bookmark-field select,
.bookmark-field textarea {
    padding: 0.55rem 0.68rem;
    border: 1px solid var(--line-strong);
    border-radius: 0;
    color: var(--ink);
    background: var(--bg-soft);
    font-size: 0.82rem;
}

.bookmark-field textarea {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.74rem;
    resize: vertical;
    word-break: break-all;
}

.bookmark-field a {
    color: var(--blue);
    font-size: 0.8rem;
    text-decoration: none;
}

.bookmark-actions {
    display: flex;
    gap: 0.45rem;
}

.bookmark-meta {
    margin: 0.7rem 0 0;
    font-size: 0.72rem;
}

/* A run, in a list of them and on its own page. */

.job-table {
    margin-top: 1rem;
    /* Narrow screens scroll the columns sideways rather than losing any. */
    overflow-x: auto;
    border: 1px solid var(--line);
    border-radius: 0;
    background: rgba(255, 255, 255, 0.92);
}

.job-row {
    padding: 0.7rem 1rem;
    display: grid;
    align-items: center;
    gap: 0.75rem;
    /* Two columns are wider than the rest: the downloads hold two buttons --
       the whole archive and the one file the run is for -- and the status
       holds the button that stops a run beside the badge saying it is open. */
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1.2fr) 140px 90px 140px 150px 96px;
    /* Keeps every column readable once the table starts scrolling. */
    min-width: 975px;
    /* What the row's link is stretched over. */
    position: relative;
    border-bottom: 1px solid var(--line);
    color: inherit;
    font-size: 0.78rem;
    text-decoration: none;
}

.job-row:last-child {
    border-bottom: none;
}

/* The link covers the whole row, so clicking anywhere in it opens the results
   -- but the buttons in the last columns sit above the cover and keep their
   own clicks. A button inside the link could not, which is why the row is a
   div with this in it rather than a link of its own. */
.job-open {
    color: inherit;
    text-decoration: none;
}

.job-open::after {
    content: "";
    position: absolute;
    inset: 0;
}

.job-row:has(.job-open:hover),
.job-row:has(.job-open:focus-visible) {
    background: var(--surface-raised);
}

.job-row-head {
    color: var(--faint);
    background: var(--surface-raised);
    font-size: 0.68rem;
    font-weight: 750;
    letter-spacing: 0.02em;
}

.job-row-empty {
    display: block;
    margin: 0;
    min-width: 0;
}

/* The `display` above would otherwise beat the attribute. The empty state is
   rendered either way, because archiving the last row on screen empties the
   list without a reload. */
.job-row-empty[hidden] {
    display: none;
}

/* Same reason, for a row the filter row above has taken off screen. */
.job-row[hidden] {
    display: none;
}

/* With it hidden, the last row is the one before it. */
.job-table:has(> .job-row-empty[hidden]) > .job-row:nth-last-child(2) {
    border-bottom: none;
}

/* And with rows filtered out, the bottom of the list is the last one still
   shown, which is not necessarily the last one there. */
.job-table > .job-row:nth-last-child(1 of .job-row:not([hidden])) {
    border-bottom: none;
}

.job-row > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Above the link's cover, and free to show a focus ring the shared cell rule
   above would clip. */
.job-row > .job-download,
.job-row > .job-status,
.job-row > .job-archive {
    position: relative;
    overflow: visible;
}

.job-download,
.job-status {
    display: flex;
    gap: 0.35rem;
    align-items: center;
}

/* A control small enough to sit in a table row without setting its height. */
.job-action {
    min-height: 0;
    padding: 0.28rem 0.5rem;
    font-size: 0.68rem;
}

.job-label {
    font-weight: 650;
}

.job-tool,
.job-when,
.job-runtime {
    color: var(--muted);
}

.job-badge {
    padding: 0.2rem 0.5rem;
    display: inline-block;
    border-radius: 0;
    background: var(--surface-raised);
    font-size: 0.68rem;
    font-weight: 700;
}

.job-badge[data-state="succeeded"] {
    color: var(--ok);
    background: rgba(21, 128, 61, 0.12);
}

.job-badge[data-state="failed"] {
    color: var(--red);
    background: rgba(185, 28, 28, 0.1);
}

.job-badge[data-state="running"] {
    color: var(--amber);
    background: rgba(180, 83, 9, 0.12);
}

/* Accepted, but nothing is executing it yet: it is behind the jobs ahead of
   it in its lane. Cooler than "running" so the two read apart at a glance. */
.job-badge[data-state="queued"] {
    color: var(--blue);
    background: var(--blue-soft);
}

/* Neither an outcome nor a fault: a run somebody stopped on purpose. */
.job-badge[data-state="cancelled"] {
    color: var(--faint);
}

/* A run that never reported anything and never will, because whatever was
   executing it is gone. It is a dead end rather than a failure of the tool,
   so it is marked but not coloured like one. */
.job-badge[data-state="interrupted"] {
    color: var(--faint);
    background: var(--surface-raised);
    text-decoration: underline dotted;
    text-underline-offset: 0.2em;
}

/* The filters above a job table: one row, wrapping onto more of them when the
   window is too narrow to hold it. */
.job-filters {
    margin-top: 1rem;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.6rem 0.8rem;
}

.job-filter {
    min-width: 0;
    display: grid;
    gap: 0.3rem;
    flex: 1 1 200px;
    font-size: 0.78rem;
}

/* A date field is a fixed size whatever it is given, so it takes only what it
   needs and leaves the rest to the two search boxes. */
.job-filter-date {
    flex: 0 1 165px;
}

.job-filter-actions {
    flex: 0 0 auto;
    align-content: end;
}

.job-filter > span {
    color: var(--faint);
    font-size: 0.7rem;
    font-weight: 750;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.job-filter input {
    min-height: 0;
    padding: 0.42rem 0.6rem;
    border: 1px solid var(--line-strong);
    border-radius: 0;
    color: var(--ink);
    background: var(--bg-soft);
    font-size: 0.8rem;
}

/* A date field's own picker follows the page rather than the browser. */
.job-filter input[type="date"] {
    color-scheme: light;
}

.job-heading-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem 1rem;
}

.dashboard-section > .job-heading-row:first-child .docs-subheading {
    margin-top: 0;
}

.archived-toggle {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.archived-toggle label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--muted);
    font-size: 0.74rem;
    cursor: pointer;
}

.archived-toggle input {
    width: 15px;
    height: 15px;
    margin: 0;
    accent-color: var(--blue);
}

.job-table-status {
    margin: 0.6rem 0 0;
    color: var(--red);
    font-size: 0.74rem;
}

.job-list-more,
.job-pagination {
    margin-top: 1rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    font-size: 0.78rem;
}

.job-list-more a {
    color: var(--blue);
    font-weight: 700;
    text-decoration: none;
}

.results-hero {
    padding-bottom: 0;
}

.results-backlinks {
    margin-bottom: 1.1rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.9rem;
}

.results-meta {
    margin: 1.2rem 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: 1.6rem;
}

.results-meta dt {
    color: var(--faint);
    font-size: 0.66rem;
    font-weight: 750;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.results-meta dd {
    margin: 0.25rem 0 0;
    font-size: 0.82rem;
}

.results-meta code {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.74rem;
}

.results-actions {
    margin-top: 1.4rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}

/* Bookmarking one of the sequences a run wrote, from the row of buttons at the
   top of its results page. The record picker rides with the button rather than
   above it, so the row stays one line on a wide screen. */
.sequence-bookmark {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.45rem;
}

.sequence-bookmark select {
    width: auto;
    max-width: 22rem;
}

.results-body {
    width: min(var(--max-width), 100% - 3rem);
    margin-inline: auto;
    padding: 1.6rem 0 5rem;
    display: grid;
    gap: 1.1rem;
}

.results-banner {
    padding: 0.8rem 1rem;
    border: 1px solid rgba(31, 111, 235, 0.3);
    border-radius: 0;
    color: var(--muted);
    background: var(--blue-soft);
    font-size: 0.8rem;
}

.results-banner-warn {
    border-color: rgba(180, 83, 9, 0.3);
    background: rgba(180, 83, 9, 0.08);
}

.results-panel {
    min-width: 0;
    padding: clamp(1rem, 3vw, 1.35rem);
    border: 1px solid var(--line);
    border-radius: 0;
    background: rgba(255, 255, 255, 0.92);
}

.results-panel-heading {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
}

.results-panel-error {
    border-color: rgba(185, 28, 28, 0.3);
    background: rgba(185, 28, 28, 0.05);
}

.results-retry {
    margin-top: 1rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
}

.results-retry-status {
    color: var(--red);
    font-size: 0.85rem;
}

.results-error-message,
.results-panel pre {
    max-height: 600px;
    margin: 0;
    padding: 1rem;
    overflow: auto;
    border: 1px solid var(--line);
    border-radius: 0;
    color: #1b2c44;
    background: #f5f8fd;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.74rem;
    line-height: 1.55;
    white-space: pre-wrap;
    word-break: break-word;
}

.backbone-file {
    margin: 0.3rem 0 0;
    color: var(--faint);
    font-size: 0.72rem;
    overflow-wrap: anywhere;
}

.backbone-models {
    margin-top: 0.9rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.backbone-model {
    padding: 0.3rem 0.55rem;
    color: var(--muted);
    border: 1px solid var(--line);
    font-size: 0.7rem;
    text-decoration: none;
}

.backbone-model.is-current {
    color: var(--blue);
    border-color: var(--blue);
    background: var(--blue-soft);
    font-weight: 650;
}

.backbone-summary {
    margin: 0.9rem 0 0.6rem;
    color: var(--muted);
    font-size: 0.78rem;
}

/* Columns of numbers only read as columns while they keep their own line, so
   this listing scrolls sideways where every other panel wraps. */
.results-panel pre.backbone-listing {
    white-space: pre;
    word-break: normal;
    overflow: auto;
    overscroll-behavior-x: contain;
    tab-size: 4;
}

.results-inputs {
    margin: 0;
    display: grid;
    gap: 0.75rem;
}

.results-input {
    padding-bottom: 0.75rem;
    display: grid;
    gap: 0.35rem;
    grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
    border-bottom: 1px solid var(--line);
}

.results-input:last-child {
    padding-bottom: 0;
    border-bottom: none;
}

.results-input dt {
    display: grid;
    gap: 0.2rem;
    font-size: 0.78rem;
    font-weight: 650;
}

.results-input dt code {
    color: var(--faint);
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.68rem;
    font-weight: 450;
}

.results-input dd {
    min-width: 0;
    margin: 0;
    color: var(--muted);
    font-size: 0.78rem;
    overflow-wrap: anywhere;
}

.results-input summary {
    cursor: pointer;
}

.results-input summary small {
    color: var(--faint);
}

.results-input pre {
    max-height: 340px;
    margin-top: 0.5rem;
}

.results-note {
    margin: 0;
    font-size: 0.76rem;
}

/* On its own page the viewer is a panel like any other, rather than something
   that appears under a form once a run has produced one. */
.results-body .structure-viewer-panel {
    grid-column: auto;
}

@media (max-width: 900px) {
    /* No room left for a third column, so the aside goes back to a row of its
       own while the title and its copy stay side by side. */
    .header-section:has(> .header-aside) {
        grid-template-columns: auto minmax(0, 1fr);
    }

    .header-section > .header-aside {
        grid-column: 1 / -1;
        margin-top: 2rem;
    }

    .tool-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .workbench {
        grid-template-columns: minmax(0, 1fr);
    }

    .tool-sidebar {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .results-input {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (max-width: 680px) {
    /* Too narrow to keep the brand and every link on one line, so the links
       drop to their own full-width row and wrap within it. Hiding them
       instead left the phone with a one-item nav. */
    .nav-shell {
        min-height: 64px;
        flex-wrap: wrap;
        gap: 0.25rem 1rem;
        padding-block: 0.55rem;
    }

    .nav-links {
        flex: 1 0 100%;
        flex-wrap: wrap;
        justify-content: flex-start;
        gap: 0.1rem;
    }

    .nav-links > a,
    .nav-signout button {
        padding: 0.4rem 0.55rem;
        font-size: 0.82rem;
    }

    /* Not enough width left for the title and its paragraph to sit side by
       side, so every row of the header becomes a full-width one. */
    .header-section,
    .header-section:has(> .header-aside) {
        grid-template-columns: minmax(0, 1fr);
    }

    .header-section > h1 + p,
    .header-section > h1 + .hero-copy,
    .header-section > .header-title-row + p,
    .header-section > .header-title-row + .hero-copy {
        grid-column: 1;
    }

    .section-heading {
        align-items: flex-start;
        flex-direction: column;
    }

    .section-heading > p {
        text-align: left;
    }

    .tool-grid,
    .form-grid,
    .tool-sidebar {
        grid-template-columns: 1fr;
    }

    /* Single column on mobile, so cards size to their content instead of
       padding out to a shared height meant for a multi-column grid. */
    .tool-card {
        min-height: 0;
        padding: 0.85rem 1rem;
    }

    .tool-card h3 {
        font-size: 1.3rem;
        margin-bottom: 4px;
    }

    .form-actions {
        align-items: stretch;
        flex-direction: column;
    }

    .request-status {
        margin: 0.2rem 0 0;
        text-align: center;
    }

    .status-row {
        grid-template-columns: 1fr;
        gap: 0.35rem;
    }

    .status-table.has-full-status .status-row {
        grid-template-columns: 1fr;
    }

    /* Each row becomes its own card, so the head row stops being column
       labels -- but it still holds the only sort controls, so it turns into a
       toolbar of them rather than disappearing. */
    .status-row-head {
        display: flex;
        flex-wrap: wrap;
        gap: 0.5rem 1.1rem;
    }

    .status-head-plain {
        display: none;
    }

    .status-row > [role="cell"] {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1rem;
    }

    .status-row > [role="cell"]::before {
        content: attr(data-label);
        flex: 0 0 auto;
        color: var(--faint);
        font-size: 0.68rem;
        font-weight: 750;
        letter-spacing: 0.06em;
        text-transform: uppercase;
    }

    .status-detail {
        text-align: right;
    }

    /* The buttons stay beside their label, but a progress line two sentences
       long would crowd them, so it drops to its own line. */
    .status-install {
        flex-wrap: wrap;
    }

    .install-note {
        width: 100%;
        text-align: right;
    }

    .site-footer {
        flex-direction: column;
    }
}

@media (max-width: 680px) {
    /* The narrowest column a phone can do without: the run's own page still
       offers the same download, and dropping it here shortens the sideways
       scroll the rest of the row needs. */
    .job-download {
        display: none;
    }

    .job-row {
        grid-template-columns: minmax(0, 1.6fr) minmax(0, 1.2fr) 140px 90px 150px 96px;
        min-width: 815px;
    }

    .structure-viewer-heading {
        align-items: stretch;
        flex-direction: column;
    }

    .structure-picker {
        min-width: 0;
    }

    .protein-viewer {
        height: 540px;
        min-height: 420px;
    }

    .results-body {
        width: min(var(--max-width), 100% - 2rem);
    }

    .results-meta {
        gap: 1rem 1.4rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
    }
}

/* A job table's names while "Edit names" is on. */
.job-name-input {
    width: 100%;
    min-height: 0;
    padding: 0.28rem 0.45rem;
    font: inherit;
    font-weight: 600;
}

.job-name-input[hidden],
.job-open[hidden] {
    display: none;
}

/* A results page's name, and the control that renames the run, side by side. */
.results-title-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem 1rem;
}

.results-hero .results-title-row h1 {
    font-size: clamp(1.5rem, 3.2vw, 2.2rem);
}

.results-rename {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.results-rename[hidden] {
    display: none;
}

.results-rename input {
    width: min(28rem, 100%);
}

.results-rename-status {
    color: var(--faint);
    font-size: 0.78rem;
}
