Files
llmgw/internal/web/static/base.css
T
Ilker ManapandClaude Opus 5.5 fffed716be Make people, limits and usage one page, with a panel per branch
A team's members, quota and spend were on three pages that linked to each
other. They are three tabs of one page now, each at its old address, and
choosing anything in the tree opens its limits, capacity and use beside it,
editable in place.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 19:10:48 +02:00

905 lines
58 KiB
CSS

/* Deliberately one small stylesheet with no build step. The single-binary goal
rules out a JS toolchain, and nothing here needs one. */
:root {
color-scheme: light dark;
--bg: #fbfbfa;
--fg: #1c1c1a;
--muted: #6b6b66;
--line: #e3e3df;
--card: #ffffff;
--accent: #2f5d50;
--ok: #2f7a4f;
--warn: #9a6b1f;
--bad: #a33a32;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #17181a; --fg: #e8e8e4; --muted: #9a9a93; --line: #2c2e31;
--card: #1e2022; --accent: #7fb3a2; --ok: #6fbd8c; --warn: #d6a44e; --bad: #e07b72;
}
}
* { box-sizing: border-box; }
body {
margin: 0; background: var(--bg); color: var(--fg);
font: 15px/1.55 ui-sans-serif, -apple-system, "Segoe UI", Roboto, sans-serif;
}
.wrap { max-width: 1100px; margin: 0 auto; padding: 2.5rem 1.25rem 4rem; }
header { border-bottom: 1px solid var(--line); margin-bottom: 2rem; padding-bottom: 1.25rem; }
h1 { font-size: 1.5rem; margin: 0 0 .25rem; letter-spacing: -.01em; }
h2 { font-size: 1.05rem; margin: 2.25rem 0 .75rem; letter-spacing: -.005em; }
.sub { color: var(--muted); margin: 0; }
nav { margin-top: .75rem; display: flex; gap: 1rem; flex-wrap: wrap; }
nav a { color: var(--accent); text-decoration: none; font-size: .9rem; }
nav a:hover { text-decoration: underline; }
table { width: 100%; border-collapse: collapse; font-size: .875rem; background: var(--card);
border: 1px solid var(--line); border-radius: 6px; overflow: hidden; }
th, td { text-align: left; padding: .5rem .7rem; border-bottom: 1px solid var(--line); }
th { font-weight: 600; color: var(--muted); font-size: .78rem; text-transform: uppercase;
letter-spacing: .04em; }
tr:last-child td { border-bottom: none; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85em; }
pre { background: var(--card); border: 1px solid var(--line); border-radius: 6px;
padding: .9rem 1rem; overflow-x: auto; }
.state { font-weight: 600; font-size: .8rem; }
.state-ready, .state-active { color: var(--ok); }
.state-pending, .state-starting, .state-draining, .state-cordoned { color: var(--warn); }
.state-failed, .state-lost, .state-unreachable, .state-revoked { color: var(--bad); }
.empty { color: var(--muted); font-style: italic; padding: .9rem 0; }
.note { color: var(--muted); font-size: .85rem; margin: .4rem 0 0; }
footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
color: var(--muted); font-size: .8rem; display: flex; justify-content: space-between; gap: 1rem; }
button {
font: inherit; font-weight: 600; cursor: pointer;
background: var(--accent); color: var(--bg); border: none;
border-radius: 6px; padding: .55rem 1.1rem;
}
button:hover { filter: brightness(1.1); }
ul { padding-left: 1.2rem; }
li { margin: .25rem 0; }
a { color: var(--accent); }
.wrap.narrow { max-width: 30rem; }
label { display: block; margin: .9rem 0; font-size: .9rem; font-weight: 600; }
input[type=text], input[type=email], input[type=password], input[type=search], input[type=number] {
display: block; width: 100%; margin-top: .3rem; font: inherit; font-weight: 400;
padding: .5rem .6rem; border: 1px solid var(--line); border-radius: 6px;
background: var(--card); color: var(--fg);
}
input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.error { color: var(--bad); border: 1px solid var(--bad); border-radius: 6px;
padding: .6rem .8rem; background: var(--card); }
form.inline { display: inline; }
button.link { background: none; color: var(--accent); padding: 0;
text-decoration: underline; font-weight: 400; font-size: .9rem; }
button.link.danger { color: var(--bad); }
.ok { color: var(--ok); border: 1px solid var(--ok); border-radius: 6px;
padding: .6rem .8rem; background: var(--card); }
select { font: inherit; padding: .35rem .4rem; border: 1px solid var(--line);
border-radius: 6px; background: var(--card); color: var(--fg); }
.chart-wrap { position: relative; margin: .5rem 0 1.5rem; }
.chart { width: 100%; height: 120px; display: block;
background: var(--card); border: 1px solid var(--line); border-radius: 6px; }
.chart rect { transition: opacity .1s; }
.chart rect:hover { opacity: .65; }
.chart .axis { stroke: var(--line); stroke-width: 1; }
.chart-peak { position: absolute; top: .4rem; right: .6rem;
font-size: .72rem; color: var(--muted); }
.chart-row { display: grid; gap: 1rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 720px) { .chart-row { grid-template-columns: 1fr; } }
.chat-bar { display: flex; align-items: center; gap: 1rem; margin-bottom: .75rem; }
.inline-label { font-size: .85rem; font-weight: 600; margin: 0; }
.inline-label select { margin-left: .4rem; font-weight: 400; }
.chat-log { min-height: 16rem; max-height: 55vh; overflow-y: auto; padding: .5rem;
background: var(--card); border: 1px solid var(--line); border-radius: 6px; }
.bubble { white-space: pre-wrap; word-wrap: break-word; padding: .55rem .75rem;
margin: .4rem 0; border-radius: 8px; max-width: 46rem; }
.bubble.you { background: var(--accent); color: var(--bg); margin-left: auto; }
.bubble.assistant { background: var(--bg); border: 1px solid var(--line); }
.bubble.failed { color: var(--bad); border-color: var(--bad); }
.bubble .loading { min-width: 16rem; }
.bubble .loading .stage { font-size: .85rem; color: var(--muted); margin-bottom: .35rem; }
.bubble .loading progress { width: 100%; height: .5rem; accent-color: var(--accent); }
.chat-composer { display: flex; gap: .6rem; margin-top: .75rem; align-items: flex-end; }
.chat-composer textarea { flex: 1; font: inherit; padding: .5rem .6rem; resize: vertical;
border: 1px solid var(--line); border-radius: 6px; background: var(--card); color: var(--fg); }
/* KaTeX sits inside a bubble that sets white-space: pre-wrap for plain text;
display maths needs to escape that or every fraction gets the newlines in
its own source. */
.bubble .katex-display { white-space: normal; margin: .6rem 0; overflow-x: auto; }
.bubble .katex { white-space: normal; }
.bubble.you .katex { color: inherit; }
/* A reply that stopped because it ran out of room, not because it was done. */
.note.truncated { color: var(--warn, #a15c00); margin: .2rem 0 .8rem; }
/* Offered under a reply that contains files worth keeping. */
.link.download { display: inline-block; margin: .3rem 0 .8rem; }
/* A usage bar inside a table cell. Drawn with two elements rather than a
gradient so the number stays selectable and readable by a screen reader. */
.meter { position: relative; min-width: 9rem; }
.meter > span {
display: block; height: .75rem; border-radius: 2px;
background: var(--accent); opacity: .55;
}
.meter > em { font-style: normal; font-size: .8rem; color: var(--muted); }
.bad { color: var(--bad); }
/* Chart palette.
*
* Roles rather than raw colours, so light and dark swap in one place and the
* charts are written against meaning. The values are a validated categorical
* set: every adjacent pair clears the colour-vision-deficiency separation
* threshold, and the dark column is the same hues re-stepped for the dark
* surface rather than an automatic flip of the light one.
*
* Checked with a validator rather than by eye, which is the only way anyone
* can honestly claim a palette is colourblind-safe. */
:root {
--viz-1: #2a78d6; /* blue */
--viz-2: #eb6834; /* orange */
--viz-3: #1baf7a; /* aqua */
--viz-4: #eda100; /* yellow */
--viz-grid: #e6e5e1;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--viz-1: #3987e5;
--viz-2: #d95926;
--viz-3: #199e70;
--viz-4: #c98500;
--viz-grid: #2e2e2c;
}
}
:root[data-theme="dark"] {
--viz-1: #3987e5;
--viz-2: #d95926;
--viz-3: #199e70;
--viz-4: #c98500;
--viz-grid: #2e2e2c;
}
/* Stat tiles: the headline figures, which are numbers rather than charts. */
.tiles { display: flex; flex-wrap: wrap; gap: .75rem; margin: .5rem 0 1.25rem; }
.tile {
flex: 1 1 8.5rem; min-width: 8.5rem; padding: .6rem .75rem;
border: 1px solid var(--viz-grid); border-radius: 6px; background: var(--card);
}
.tile .v { font-size: 1.5rem; line-height: 1.1; font-variant-numeric: tabular-nums; }
.tile .k { font-size: .8rem; color: var(--muted); }
.tile svg { display: block; margin-top: .35rem; }
/* Ranked horizontal bars. A wrapper, not the SVG itself: .chart above is the
* fixed-height time-series box, and sharing the name gave these a 120px cage
* their taller content overflowed, over the table beneath them. Capped at the
* design width so scaling up never inflates the 12px labels. */
.ranked { margin: .25rem 0 1rem; }
.ranked svg { display: block; width: 100%; max-width: 720px; height: auto; }
/* The landing page.
*
* It is the only page here a stranger sees, and for most readers it is the whole
* product until they have a key. Everything below is layout and surface: the
* page still says exactly what it said before, and still names no model, no node
* and no count. */
.banner { border-radius: 6px; padding: .7rem .9rem; margin: 0 0 1.5rem;
border: 1px solid var(--line); background: var(--card); font-size: .875rem; }
.warn-banner { border-color: var(--warn); color: var(--warn); }
.hero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px);
gap: 2rem; align-items: center; border-bottom: 1px solid var(--line);
margin-bottom: 2.25rem; padding-bottom: 2rem; }
.hero h1 { font-size: 2.6rem; line-height: 1; margin: 0 0 .5rem; letter-spacing: -.03em; }
.hero .sub { font-size: 1.15rem; color: var(--fg); font-weight: 500; }
.hero .lede { color: var(--muted); max-width: 34rem; margin: .75rem 0 0; }
.cta { display: flex; flex-wrap: wrap; gap: .6rem; margin: 1.35rem 0 0; }
.btn { display: inline-block; font-size: .9rem; font-weight: 600; text-decoration: none;
background: var(--accent); color: var(--bg); border: 1px solid var(--accent);
border-radius: 6px; padding: .5rem 1.1rem; }
.btn:hover { filter: brightness(1.1); }
.btn.ghost { background: none; color: var(--accent); border-color: var(--line); }
.btn.ghost:hover { border-color: var(--accent); filter: none; }
.schematic { margin: 0; }
.schematic svg { display: block; width: 100%; height: auto; }
.schematic figcaption { color: var(--muted); font-size: .8rem; margin-top: .6rem; }
@media (max-width: 760px) {
.hero { grid-template-columns: 1fr; gap: 1.5rem; }
.hero h1 { font-size: 2rem; }
}
/* The base URL is what a reader came for, so it is a headline rather than a
* line of prose with a colon in it. */
.url-card { display: flex; flex-wrap: wrap; align-items: baseline; gap: .35rem 1rem;
background: var(--card); border: 1px solid var(--line);
border-left: 3px solid var(--accent); border-radius: 6px;
padding: .85rem 1.1rem; }
.url-card .k { font-size: .72rem; font-weight: 600; text-transform: uppercase;
letter-spacing: .06em; color: var(--muted); }
.url-card code { font-size: 1.05rem; word-break: break-all; }
.steps { list-style: none; padding: 0; margin: .5rem 0 0;
display: grid; gap: 1rem; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.steps > li { margin: 0; background: var(--card); border: 1px solid var(--line);
border-radius: 8px; padding: 1.1rem 1.1rem 1.25rem; }
.steps .n { display: inline-grid; place-items: center; width: 1.6rem; height: 1.6rem;
border-radius: 50%; background: var(--accent); color: var(--bg);
font-size: .8rem; font-weight: 700; }
.steps h3 { font-size: .98rem; margin: .6rem 0 .4rem; }
.steps p { margin: .4rem 0 0; font-size: .9rem; }
/* A card is narrower than a line of shell, and a block that scrolls sideways
* inside a card is one nobody discovers they can scroll. */
.steps pre { margin: .7rem 0 0; font-size: .78em;
white-space: pre-wrap; overflow-wrap: anywhere; }
@media (max-width: 860px) { .steps { grid-template-columns: 1fr; } }
/* Tabs with no JavaScript: the radios carry the state, so the panel a reader
* chose survives everything including the script never loading. */
.tabs { display: flex; flex-wrap: wrap; gap: .35rem; margin: .5rem 0 .75rem; }
.tabs > input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.tabs > label { display: inline-block; margin: 0; cursor: pointer; font-size: .85rem;
font-weight: 600; color: var(--muted); padding: .35rem .85rem;
border: 1px solid transparent; border-radius: 999px; }
.tabs > label:hover { color: var(--fg); }
.tabs > input:checked + label { color: var(--fg); background: var(--card);
border-color: var(--line); }
.tabs > input:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }
.tabs > .panels { flex: 1 0 100%; }
.tabs .panel { display: none; margin: 0; }
.tabs > input:nth-of-type(1):checked ~ .panels > .panel:nth-child(1),
.tabs > input:nth-of-type(2):checked ~ .panels > .panel:nth-child(2),
.tabs > input:nth-of-type(3):checked ~ .panels > .panel:nth-child(3) { display: block; }
/* Copy buttons are injected by script, so they are positioned against the block
* they copy rather than reserving space that stays empty without one. */
pre { position: relative; }
pre .copy { position: absolute; top: .4rem; right: .4rem; padding: .15rem .5rem;
font-size: .72rem; font-weight: 600; background: var(--bg);
color: var(--muted); border: 1px solid var(--line); border-radius: 4px;
opacity: 0; transition: opacity .12s; }
pre:hover .copy, pre .copy:focus-visible { opacity: 1; }
pre .copy:hover { color: var(--fg); filter: none; }
.endpoints { list-style: none; padding: 0; margin: .5rem 0 0; }
.endpoints > li { display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem .8rem;
padding: .55rem 0; border-bottom: 1px solid var(--line); margin: 0; }
.endpoints > li:last-child { border-bottom: none; }
.endpoints > li span { color: var(--muted); font-size: .85rem; }
/* A button revealed by hover is a button a phone never has. */
@media (hover: none) { pre .copy { opacity: 1; } }
/* The mirror page. A model version spills onto a second line under its row:
the catalog URL is long, and a column wide enough for it would squeeze every
other column on the table into uselessness. */
.url-row code, .error-row { font-size: .78rem; padding-top: 0; }
.url-row { color: var(--muted); }
.url-row code { color: var(--muted); }
.error-row { color: var(--bad); }
tr:has(+ .url-row) td, tr:has(+ .error-row) td { border-bottom: none; }
/* The fetch form's three short fields sit on one line on a wide screen and
stack on a narrow one, because two of them are a word long and a full-width
input for "2026-09" reads as though more is wanted. */
.field-row { display: flex; gap: 1rem; flex-wrap: wrap; }
.field-row label { flex: 1 1 12rem; }
.opt { font-weight: 400; color: var(--muted); font-size: .8rem; }
/* The row whose quota form is open. Tinted rather than outlined: a border
inside a table cell breaks the ruling of every column beside it. */
tr.editing > td { background: color-mix(in srgb, var(--accent) 8%, var(--card)); }
tr.editing form { margin: .4rem 0; }
/* Multi-line time series. Thin marks, recessive grid: the data is the darkest
thing on the chart, which is the only rule that makes a chart readable. */
.chart-lines { height: 150px; }
.chart-lines .line { stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart-lines .axis { stroke: var(--viz-grid); stroke-width: 1; }
.chart-lines .tick { fill: var(--muted); font-size: 9px; text-anchor: end; }
/* A legend is always present for two lines or more: colour alone is not an
encoding a reader can rely on. */
.legend { display: flex; flex-wrap: wrap; gap: .1rem 1rem; margin: .2rem 0 1rem; font-size: .8rem; }
.legend span { display: inline-flex; align-items: center; gap: .35rem; color: var(--muted); }
.legend i { width: 10px; height: 2px; border-radius: 1px; display: inline-block; }
.legend b { font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
.chart-title { font-size: .9rem; font-weight: 600; margin: 0 0 .3rem; }
/* The filter row above a report's charts.
One row, above the charts rather than beside them: the reader chooses what
they are looking at before they look, and a control column on the right is
found after the question has already been answered wrongly. Links rather
than a form, so the view is in the URL and can be shared. */
.filters { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
margin: 0 0 1rem; }
.filter-label { color: var(--muted); font-size: .78rem; text-transform: uppercase;
letter-spacing: .04em; margin-right: .2rem; }
.filter-label + .chip { margin-right: 0; }
.filters .filter-label:not(:first-child) { margin-left: 1rem; }
/* The same pill as .pills a: one look for every choice that is a link. */
.chip { font-size: .85rem; font-weight: 600; text-decoration: none; color: var(--muted);
padding: .3rem .8rem; border: 1px solid transparent; border-radius: 999px; }
.chip:hover { color: var(--fg); }
.chip.on { color: var(--fg); background: var(--card); border-color: var(--line); }
/* People: the tenancy tree with its people and keys. Indented by level and
ruled on the left, so which team a project is in reads at a glance without
a table per level. */
.org { background: var(--card); border: 1px solid var(--line); border-radius: 8px;
padding: .9rem 1rem; margin: 0 0 1rem; }
.team { margin: .8rem 0 0 .4rem; padding-left: .9rem; border-left: 2px solid var(--line); }
.node-head { display: flex; align-items: center; gap: .4rem .9rem; flex-wrap: wrap; }
.node-name { font-weight: 600; }
.org > .node-head .node-name { font-size: 1.05rem; }
.node-meta { color: var(--muted); font-size: .82rem; font-weight: 400; }
details.edit { display: inline-block; font-size: .85rem; }
details.edit > summary { cursor: pointer; color: var(--accent); list-style: none;
text-decoration: underline; }
details.edit > summary::-webkit-details-marker { display: none; }
details.edit[open] > summary { color: var(--muted); }
details.edit form { display: flex; gap: .4rem; align-items: center; margin: .35rem 0; }
details.edit input[type=text] { width: 14rem; }
details.project { margin: .45rem 0; border: 1px solid var(--line); border-radius: 6px;
background: var(--bg); }
details.project > summary { cursor: pointer; padding: .45rem .7rem; display: flex;
gap: .8rem; align-items: baseline; }
details.project[open] > summary { border-bottom: 1px solid var(--line); }
.project-body { padding: .6rem .7rem .8rem; display: grid; gap: .7rem; }
table.compact { font-size: .83rem; }
table.compact th, table.compact td { padding: .35rem .55rem; }
form.add { display: flex; gap: .4rem; margin: .5rem 0 0; }
form.add input[type=text], details.edit input[type=text] { margin: 0; padding: .35rem .55rem; }
form.add input[type=text] { max-width: 18rem; }
form.add button, button.small { padding: .3rem .7rem; font-size: .82rem; }
form.org-add { margin: 0 0 1rem; }
td.actions { white-space: nowrap; }
td.actions form + form { margin-left: .6rem; }
.tag { font-size: .7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
color: var(--accent); border: 1px solid var(--accent); border-radius: 4px;
padding: 0 .3rem; margin-left: .3rem; }
input.search { width: 100%; max-width: 26rem; margin: 0 0 .7rem; padding: .45rem .6rem;
font: inherit; border: 1px solid var(--line); border-radius: 6px;
background: var(--card); color: var(--fg); }
tr[hidden] { display: none; }
/* The mirror's list of what it holds: one line per version. */
table.held td.file { max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
table.held td.actions > * + * { margin-left: .7rem; }
table.held .tag { margin: 0 .3rem 0 0; text-transform: none; letter-spacing: 0; }
/* The shared top menu. The page you are on is marked by weight and an
underline, not by colour alone. */
nav.menu { align-items: center; gap: .35rem 1.1rem; }
nav.menu a { font-size: .9rem; }
nav.menu a[aria-current="page"] { color: var(--fg); font-weight: 600;
text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: .35rem; }
nav.menu .menu-gap { flex: 1 1 auto; min-width: .5rem; }
nav.menu form.inline button.link { font-size: .9rem; text-decoration: none; color: var(--muted); }
nav.menu form.inline button.link:hover { color: var(--fg); text-decoration: underline; }
/* Ranked bars: label, bar, value, each taking the room it needs. */
.ranked { display: grid; grid-template-columns: minmax(7rem, max-content) minmax(6rem, 1fr) max-content;
gap: .35rem .8rem; align-items: center; max-width: 100%; }
.ranked .rk { display: contents; }
.ranked .rk-label { font-size: .82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
max-width: 18rem; }
.ranked .rk-track { height: .7rem; }
.ranked .rk-bar { display: block; height: 100%; border-radius: 0 4px 4px 0; min-width: 2px; }
.ranked .rk-value { font-size: .8rem; color: var(--muted); font-variant-numeric: tabular-nums;
white-space: nowrap; }
/* A warning about the page as a whole, set apart from the notes around it. */
.banner-warn { border: 1px solid var(--warn); border-left-width: 4px; border-radius: 6px;
background: var(--card); padding: .6rem .8rem; margin: 0 0 1rem;
font-size: .88rem; color: var(--fg); }
/* Single-column forms read as a form, not as a row of the page. */
form.narrow, .narrow-form form { max-width: 30rem; }
/* CPU-only capacity: one line of figures in place of a GPU table. */
.capacity-cpu { display: flex; flex-wrap: wrap; gap: .4rem 1.6rem; margin: .2rem 0 1rem; }
.capacity-cpu b { font-size: 1.15rem; font-variant-numeric: tabular-nums; }
.capacity-cpu span { color: var(--muted); font-size: .85rem; }
/* Quotas: the tenancy tree as a table. Depth is indentation and a guide line,
organizations are tinted, and a limit held from above is muted and arrowed. */
table.quota-tree td { vertical-align: top; }
table.quota-tree .q-where { padding-left: .8rem; }
table.quota-tree tr.q-depth-1 .q-where { padding-left: 2rem; }
table.quota-tree tr.q-depth-2 .q-where { padding-left: 3.2rem; }
table.quota-tree tr.q-depth-0 > td { background: color-mix(in srgb, var(--accent) 5%, var(--card)); }
table.quota-tree tr.q-depth-0 .q-name { font-weight: 600; }
.q-kind { font-size: .72rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin-left: .3rem; }
.q-sub { font-size: .78rem; color: var(--muted); margin-top: .15rem; }
.q-warn { color: var(--warn); }
.q-up { color: var(--muted); font-style: italic; cursor: help; }
.q-none { color: var(--line); }
.q-per { color: var(--muted); font-size: .8em; }
.q-spent { white-space: nowrap; }
.q-spend { font-variant-numeric: tabular-nums; display: inline-block; min-width: 3.5rem; text-align: right; }
.q-meter { display: inline-block; width: 5rem; height: .45rem; background: var(--line); border-radius: 3px;
margin: 0 .4rem 0 .6rem; vertical-align: middle; overflow: hidden; }
.q-meter i { display: block; height: 100%; background: var(--accent); }
.q-meter.hot i { background: var(--bad); }
.q-pct { font-size: .78rem; color: var(--muted); }
.q-act { text-align: right; white-space: nowrap; }
.q-form { max-width: 46rem; }
.q-form .field-row { align-items: flex-end; }
.q-form .field-row label { flex: 1 1 8rem; }
.q-form .field-row label.q-period { flex: 0 0 auto; }
.q-form-title { margin: .2rem 0 .4rem; font-size: .88rem; }
.q-form-actions { display: flex; gap: 1rem; align-items: center; margin-top: .6rem; }
.q-form-actions .danger { margin-left: auto; }
.q-legend { font-size: .8rem; color: var(--muted); margin: .5rem 0 0; }
details.explain { margin-top: 1.5rem; font-size: .88rem; color: var(--muted); }
details.explain > summary { cursor: pointer; color: var(--accent); }
details.explain p { max-width: 48rem; }
/* Memberships as removable chips. */
.chip-m { display: inline-flex; align-items: center; gap: .2rem; font-size: .78rem;
border: 1px solid var(--line); border-radius: 999px; padding: .05rem .15rem .05rem .55rem;
margin: .1rem .25rem .1rem 0; background: var(--card); white-space: nowrap; }
.chip-m form.inline { display: inline; }
.chip-x { background: none; color: var(--muted); padding: 0 .35rem; font-weight: 400; line-height: 1.2; }
.chip-x:hover { color: var(--bad); filter: none; }
td.where { max-width: 26rem; }
.scope-members { background: var(--card); border: 1px solid var(--line); border-radius: 6px;
padding: .5rem .6rem; margin: .4rem 0; min-width: 24rem; }
h3.limits-for { font-size: .92rem; margin: 1rem 0 .4rem; }
table.narrow-table { max-width: 30rem; }
.q-act a + a { margin-left: .8rem; }
.chip-role { font-size: .68rem; font-weight: 600; color: var(--accent); text-transform: uppercase; letter-spacing: .04em; }
/* Runs: one line per model — a catalog name that differs from the mirror's,
how it runs, and where — with the selector at a fixed width so the
replica counts line up down the column. */
table.held .use { display: flex; align-items: center; gap: .5rem; margin: .1rem 0; white-space: nowrap; }
table.held .use .as { font-size: .72rem; color: var(--muted); max-width: 10rem;
overflow: hidden; text-overflow: ellipsis; }
table.held .use form.inline { margin: 0; }
table.held .use .mode-fixed { width: 7.5rem; font-size: .8rem; color: var(--muted); }
table.held td.num { white-space: nowrap; }
table.held select.mode { width: 7.5rem; margin: 0; padding: .1rem .3rem; font-size: .8rem; }
/* Link pills: the same look as .tabs, for choices that are addresses rather
than panels on one page — a filtered listing should survive a reload and be
worth sending to somebody. */
/* A page's own sections, one address each. Underlined rather than pills so
* they do not read as one more filter beside the ones inside a section. */
.page-tabs { display: flex; flex-wrap: wrap; gap: 0 1.5rem; margin: -1rem 0 1.5rem;
border-bottom: 1px solid var(--line); }
.page-tabs a { padding: .55rem 0; margin-bottom: -1px; font-weight: 600; color: var(--muted);
text-decoration: none; border-bottom: 2px solid transparent; }
.page-tabs a:hover { color: var(--fg); }
.page-tabs a.on { color: var(--fg); border-bottom-color: var(--accent); }
.page-tabs .count { font-weight: 500; font-size: .8rem; color: var(--muted); margin-left: .25rem; }
.pills { display: flex; flex-wrap: wrap; gap: .35rem; margin: .5rem 0; }
.pills a { font-size: .85rem; font-weight: 600; color: var(--muted); padding: .35rem .85rem;
border: 1px solid transparent; border-radius: 999px; text-decoration: none; }
.pills a:hover { color: var(--fg); }
.pills a.on { color: var(--fg); background: var(--card); border-color: var(--line); }
.pills.small a { font-size: .78rem; font-weight: 500; padding: .2rem .65rem; }
.explore-filters { display: flex; flex-wrap: wrap; gap: .25rem 1.25rem; }
.explore-search { display: flex; flex-wrap: wrap; gap: .5rem .75rem; align-items: center; margin: .5rem 0 .75rem; }
.explore-search input[type=search] { flex: 1 1 18rem; width: auto; margin: 0; }
.explore-search label.check { font-size: .85rem; color: var(--muted); display: inline-flex; gap: .35rem; align-items: center; margin: 0; }
table.explore td:first-child { max-width: 28rem; overflow-wrap: anywhere; }
table.explore td.num { white-space: nowrap; }
table.explore .cannot { color: var(--bad); }
.tag { display: inline-block; font-size: .7rem; font-weight: 600; color: var(--muted);
border: 1px solid var(--line); border-radius: 999px; padding: 0 .4rem; margin-left: .3rem; vertical-align: 1px; }
.tag.new { color: var(--accent); border-color: var(--accent); }
table.explore .from { font-size: .75rem; color: var(--muted); margin-top: .15rem; overflow-wrap: anywhere; }
.repo-meta { display: flex; flex-wrap: wrap; gap: .25rem 1rem; font-size: .85rem; color: var(--muted); margin: .25rem 0 .75rem; }
/* A README is written for its own site. Held to a box so the file list, which
is what this page is for, stays within reach below it. */
details.model-card { border: 1px solid var(--line); border-radius: 8px; background: var(--card); margin: 0 0 1rem; }
details.model-card > summary { cursor: pointer; padding: .5rem .8rem; font-weight: 600; font-size: .9rem; }
details.model-card .card-body { max-height: 28rem; overflow: auto; padding: 0 1rem 1rem; font-size: .88rem; line-height: 1.55; }
.card-body h1 { font-size: 1.25rem; } .card-body h2 { font-size: 1.1rem; } .card-body h3, .card-body h4 { font-size: .95rem; }
.card-body pre { overflow-x: auto; padding: .6rem .75rem; background: var(--bg); border-radius: 6px; font-size: .8rem; }
.card-body table { width: auto; max-width: 100%; display: block; overflow-x: auto; font-size: .8rem; }
.card-body td, .card-body th { padding: .25rem .5rem; }
.card-body a.img { font-size: .8rem; }
/* A repository opened beneath its row in the listing. */
table.explore tr.open > td { background: var(--bg); }
table.explore a.open-repo::before { content: "▸ "; color: var(--muted); text-decoration: none; display: inline-block; width: 1em; }
table.explore a.open-repo[aria-expanded="true"]::before { content: "▾ "; }
table.explore tr.repo-detail > td { background: var(--bg); padding: .75rem 1rem 1rem; border-top: 0; }
table.explore tr.repo-detail table { background: var(--card); }
table.explore tr.repo-detail td:first-child { max-width: none; }
/* A repository's files: name, version and button on one line, so a repository
of a dozen quantisations is a dozen lines rather than a dozen boxes. */
table.repo-files td { vertical-align: middle; }
table.repo-files td:first-child { overflow-wrap: anywhere; }
table.repo-files form.inline { display: flex; flex-wrap: nowrap; gap: .35rem; align-items: center; margin: 0; }
table.repo-files form.inline input[type=text] { display: inline-block; width: auto; margin: 0; padding: .3rem .45rem; font-size: .8rem; }
table.repo-files form.inline input[name=model] { width: 12rem; min-width: 0; flex: 1 1 12rem; }
table.repo-files form.inline input[name=version] { width: 6.5rem; }
table.repo-files form.inline button { padding: .3rem .7rem; font-size: .8rem; }
table.repo-files td:last-child { width: 1%; white-space: nowrap; }
table.repo-files td:last-child .opt { display: block; margin-bottom: .2rem; }
/* A finished reply, rendered from markdown. Wrapping goes back to normal:
pre-wrap, right for text as it streams, doubles every gap between blocks. */
.bubble.md { white-space: normal; }
.bubble.md > :first-child { margin-top: 0; }
.bubble.md > :last-child { margin-bottom: 0; }
.bubble.md p, .bubble.md ul, .bubble.md ol, .bubble.md pre, .bubble.md table, .bubble.md blockquote { margin: .5rem 0; }
.bubble.md ul, .bubble.md ol { padding-left: 1.4rem; }
.bubble.md h1, .bubble.md h2, .bubble.md h3, .bubble.md h4 { margin: .8rem 0 .35rem; font-size: 1rem; }
.bubble.md h1 { font-size: 1.15rem; }
.bubble.md code { font-size: .85em; background: var(--card); padding: .05rem .3rem; border-radius: 4px; }
.bubble.md pre { white-space: pre; overflow-x: auto; background: var(--card); border: 1px solid var(--line);
border-radius: 6px; padding: .6rem .75rem; }
.bubble.md pre code { background: none; padding: 0; font-size: .82rem; }
.bubble.md table { width: auto; max-width: 100%; display: block; overflow-x: auto; font-size: .85rem; }
.bubble.md th, .bubble.md td { padding: .25rem .6rem; border-bottom: 1px solid var(--line); }
.bubble.md blockquote { border-left: 3px solid var(--line); padding-left: .75rem; color: var(--muted); }
/* How much of a budget is gone, on a person's own page. */
.budget { max-width: 34rem; margin: .4rem 0 .6rem; }
.budget-head { display: flex; justify-content: space-between; gap: 1rem; font-size: .85rem; color: var(--muted); }
.budget-head b { color: var(--fg); font-variant-numeric: tabular-nums; }
.budget-bar { height: .5rem; border-radius: 999px; background: var(--card); border: 1px solid var(--line); overflow: hidden; margin-top: .25rem; }
.budget-bar > span { display: block; height: 100%; background: var(--accent); }
.budget.near .budget-bar > span { background: var(--warn); }
.budget.over .budget-bar > span { background: var(--bad); }
.budget.over .budget-head b { color: var(--bad); }
.own-use .tile { min-width: 9rem; }
/* Between what the cluster is and how it is being used, on the cluster page. */
hr.part { border: 0; border-top: 1px solid var(--line); margin: 2.25rem 0 .5rem; }
/* How full a node is, in the cluster page's node table and on its own page. */
td.load { white-space: nowrap; min-width: 8.5rem; }
.minibar { display: inline-block; width: 3.2rem; height: .45rem; border-radius: 999px; background: var(--bg);
border: 1px solid var(--line); overflow: hidden; vertical-align: middle; margin-right: .4rem; }
.minibar > span { display: block; height: 100%; background: var(--accent); }
.minibar.warm > span { background: var(--warn); }
.minibar.hot > span { background: var(--bad); }
.load-v { font-variant-numeric: tabular-nums; font-size: .85rem; }
table.nodes td:first-child code { white-space: nowrap; }
/* The mirror's table, grouped by model: a heading row, then its versions. */
table.held tr.model-group > td { background: var(--bg); padding-top: .7rem; border-bottom: 1px solid var(--line); }
table.held tr.model-group strong { font-size: .95rem; }
table.held tr.model-version > td:first-child { padding-left: 1.4rem; }
.tag.bad { color: var(--bad); border-color: var(--bad); }
ul.fabric-problems { list-style: none; padding: 0; margin: .25rem 0 .75rem; font-size: .88rem; }
ul.fabric-problems li { margin: .25rem 0; }
ul.fabric-problems li.info { color: var(--muted); }
/* ---------------------------------------------------------------------
The fabric map. A dark surface in both themes: it is a picture of light
moving through cables, and glows and flows read on dark the way a
network operations wall does.
--------------------------------------------------------------------- */
.wrap.wide { max-width: 1400px; }
.fabric-head h2 { margin: .75rem 0 .1rem; }
.fabric-map {
--fm-bg: #0b1020; --fm-bg2: #111a33; --fm-fg: #dbe4ff; --fm-dim: #7c89ad; --fm-line: #1e2a4a;
--fm-xdr: #f472b6; --fm-ndr: #a78bfa; --fm-hdr: #38bdf8; --fm-edr: #34d399; --fm-slow: #fbbf24; --fm-bad: #fb7185;
position: relative; margin: .75rem 0 1.5rem; border-radius: 14px; overflow: hidden;
background: radial-gradient(1200px 500px at 50% -10%, #1b2a55 0%, var(--fm-bg) 55%) , var(--fm-bg);
border: 1px solid #1c2747; box-shadow: 0 20px 60px -30px rgba(40, 80, 200, .45), inset 0 1px 0 rgba(255,255,255,.04);
color: var(--fm-fg);
}
.fm-bar { display: flex; flex-wrap: wrap; gap: .75rem 1.25rem; justify-content: space-between; align-items: center;
padding: .8rem 1rem; border-bottom: 1px solid var(--fm-line); background: rgba(10, 16, 36, .55); backdrop-filter: blur(6px); }
.fm-stats { display: flex; flex-wrap: wrap; gap: .4rem 1.4rem; }
.fm-stat { display: flex; flex-direction: column; line-height: 1.1; }
.fm-stat b { font-size: 1.15rem; font-variant-numeric: tabular-nums; color: #fff; text-shadow: 0 0 18px rgba(120, 160, 255, .35); }
.fm-stat span { font-size: .68rem; letter-spacing: .08em; text-transform: uppercase; color: var(--fm-dim); }
.fm-stat.bad b { color: var(--fm-bad); text-shadow: 0 0 16px rgba(251, 113, 133, .55); }
.fm-stat.ok b { color: var(--fm-edr); }
.fm-tools { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.fm-tools .pills { margin: 0; }
.fm-tools button, .fm-tools .pills button {
font: inherit; font-size: .78rem; font-weight: 600; color: var(--fm-dim); background: transparent; cursor: pointer;
border: 1px solid var(--fm-line); border-radius: 999px; padding: .28rem .75rem; }
.fm-tools button:hover { color: var(--fm-fg); border-color: #2f3f6e; }
.fm-tools button.on { color: #fff; background: linear-gradient(180deg, #2a3b73, #1d2a55); border-color: #3b4f8f;
box-shadow: 0 0 14px rgba(100, 140, 255, .35); }
.fabric-map input.fm-search { display: inline-block; width: 11rem; font: inherit; font-size: .8rem; padding: .3rem .7rem; border-radius: 999px; margin: 0;
color: var(--fm-fg); background: rgba(255,255,255,.04); border: 1px solid var(--fm-line); }
.fm-stage { position: relative; }
.fm-canvas { min-height: 22rem; }
.fm-empty { padding: 4rem 2rem; text-align: center; color: var(--fm-dim); }
.fm-svg { display: block; width: 100%; cursor: grab; user-select: none; }
.fm-svg.dragging { cursor: grabbing; }
.fm-gridline { stroke: rgba(120, 150, 255, .06); stroke-width: 1; }
.fm-tier { fill: var(--fm-dim); font-size: 10px; letter-spacing: .18em; text-transform: uppercase; opacity: .75; text-anchor: middle; }
/* Cables, coloured by what they trained at. */
.fm-link { fill: none; stroke-linecap: round; opacity: .72; transition: opacity .2s, stroke-width .2s; }
.fm-link.sp-xdr { stroke: var(--fm-xdr); } .fm-link.sp-ndr { stroke: var(--fm-ndr); }
.fm-link.sp-hdr { stroke: var(--fm-hdr); } .fm-link.sp-edr { stroke: var(--fm-edr); } .fm-link.sp-slow { stroke: var(--fm-slow); }
.fm-link.host { opacity: .55; }
.mode-rails .fm-link.host { stroke: var(--rail); opacity: .8; }
.mode-rails .fm-link:not(.host) { stroke: #3a4670; opacity: .5; }
.fm-link.degraded { stroke: var(--fm-bad) !important; stroke-dasharray: 5 5; opacity: 1; filter: url(#fm-glow);
animation: fm-march 1s linear infinite, fm-pulse 1.6s ease-in-out infinite; }
@keyframes fm-march { to { stroke-dashoffset: -20; } }
@keyframes fm-pulse { 50% { stroke-opacity: .35; } }
.fm-warn circle { fill: #3b0d1a; stroke: var(--fm-bad); stroke-width: 1.5; filter: url(#fm-glow); }
.fm-warn text { fill: var(--fm-bad); font-size: 10px; font-weight: 800; text-anchor: middle; }
.fm-warn { cursor: help; }
/* Traffic: particles running the cables, up for sent, down for received. */
.fm-particle { filter: url(#fm-glow); pointer-events: none; }
.fm-particle.up { fill: #7dd3fc; } .fm-particle.down { fill: #c4b5fd; }
/* Switches. */
.fm-switch { cursor: pointer; }
.fm-switch rect { stroke-width: 1.2; transition: filter .2s; }
.fm-switch.t-leaf rect { stroke: #2dd4bf; } .fm-switch.t-spine rect { stroke: #a78bfa; } .fm-switch.t-core rect { stroke: #fbbf24; }
.fm-switch:hover rect, .fm-switch.hot rect { filter: url(#fm-glow); }
.fm-switch.has-bad rect { stroke: var(--fm-bad); }
.fm-name { fill: #fff; font-size: 11px; font-weight: 700; text-anchor: middle; }
.fm-sub { fill: var(--fm-dim); font-size: 8.5px; text-anchor: middle; }
.fm-use { stroke: rgba(255,255,255,.55); stroke-width: 2; stroke-linecap: round; }
.fm-badge circle { fill: var(--fm-bad); filter: url(#fm-glow); }
.fm-badge text { fill: #fff; font-size: 9px; font-weight: 800; text-anchor: middle; }
/* Adapters. */
.fm-host { cursor: pointer; }
.fm-host circle { fill: #334066; stroke: #4b5a88; stroke-width: 1; }
.fm-host .fm-halo { fill: transparent; stroke: transparent; }
.fm-host.known circle:not(.fm-halo) { fill: #2dd4bf; stroke: #99f6e4; }
.mode-rails .fm-host.known circle:not(.fm-halo) { fill: var(--rail); stroke: #fff; }
.fm-host.known .fm-halo { stroke: rgba(45, 212, 191, .35); animation: fm-breathe 3s ease-in-out infinite; }
.fm-host.errs circle:not(.fm-halo) { stroke: var(--fm-slow); stroke-width: 2; }
.fm-host.down circle:not(.fm-halo) { fill: var(--fm-bad); stroke: #fecdd3; }
.fm-host.down .fm-halo { stroke: var(--fm-bad); animation: fm-breathe 1s ease-in-out infinite; }
.fm-host.found .fm-halo { stroke: #fff; stroke-width: 2; }
@keyframes fm-breathe { 50% { stroke-opacity: .1; r: 11; } }
.fm-hlabel { fill: var(--fm-dim); font-size: 8.5px; }
.fm-host:hover .fm-hlabel, .fm-host.hot .fm-hlabel { fill: #fff; }
/* Focus: hovering or choosing something fades what it does not touch. */
.fm-svg.focus .fm-link:not(.hot) { opacity: .08; }
.fm-svg.focus .fm-nodes > g:not(.hot) { opacity: .28; }
.fm-svg.focus .fm-link.hot { opacity: 1; stroke-width: 2.6; filter: url(#fm-glow); }
.fm-svg.focus .fm-particle { opacity: .35; }
.only-problems .fm-link:not(.degraded) { opacity: .06; }
.only-problems .fm-nodes > g:not(.has-bad):not(.down):not(.errs) { opacity: .2; }
.fm-tip { position: absolute; z-index: 3; max-width: 260px; pointer-events: none; font-size: .78rem; line-height: 1.45;
padding: .5rem .65rem; border-radius: 8px; background: rgba(12, 18, 40, .94); border: 1px solid #2c3b6b;
box-shadow: 0 10px 30px rgba(0,0,0,.5); color: var(--fm-fg); }
.fm-panel { position: absolute; top: .75rem; right: .75rem; z-index: 2; width: 17rem; max-height: calc(100% - 1.5rem); overflow: auto;
padding: .85rem 1rem; border-radius: 12px; background: rgba(12, 18, 40, .92); border: 1px solid #2c3b6b;
box-shadow: 0 16px 40px rgba(0,0,0,.55); backdrop-filter: blur(8px); font-size: .82rem; }
.fm-panel h4 { margin: 0 0 .5rem; font-size: 1rem; color: #fff; }
.fm-panel dl { display: grid; grid-template-columns: auto 1fr; gap: .2rem .75rem; margin: 0 0 .6rem; }
.fm-panel dt { color: var(--fm-dim); } .fm-panel dd { margin: 0; overflow-wrap: anywhere; }
.fm-panel code { background: none; color: var(--fm-fg); font-size: .75rem; }
.fm-panel a { color: #7dd3fc; }
.fm-panel ul { margin: .25rem 0; padding-left: 1rem; }
.fm-close { position: absolute; top: .4rem; right: .5rem; background: none; border: 0; color: var(--fm-dim); font-size: 1.2rem; cursor: pointer; }
.fm-bad { color: var(--fm-bad); } .fm-dim { color: var(--fm-dim); }
.fm-legend { display: flex; flex-wrap: wrap; gap: .4rem 1.1rem; padding: .6rem 1rem .8rem; font-size: .74rem; color: var(--fm-dim);
border-top: 1px solid var(--fm-line); }
.fm-legend span { display: inline-flex; align-items: center; gap: .35rem; }
.fm-legend .sw { width: 18px; height: 3px; border-radius: 2px; display: inline-block; }
.fm-legend .sw.sp-xdr { background: var(--fm-xdr); } .fm-legend .sw.sp-ndr { background: var(--fm-ndr); }
.fm-legend .sw.sp-hdr { background: var(--fm-hdr); } .fm-legend .sw.sp-edr { background: var(--fm-edr); }
.fm-legend .sw.sp-slow { background: var(--fm-slow); }
.fm-legend .sw.degraded { background: repeating-linear-gradient(90deg, var(--fm-bad) 0 4px, transparent 4px 7px); }
.fm-legend .dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; background: #334066; border: 1px solid #4b5a88; }
.fm-legend .dot.known { background: #2dd4bf; border-color: #99f6e4; } .fm-legend .dot.down { background: var(--fm-bad); }
.fm-legend .note { color: var(--fm-dim); opacity: .8; }
@media (prefers-reduced-motion: reduce) {
.fm-link.degraded, .fm-host .fm-halo { animation: none; }
}
.mode-rails .fm-host.down circle:not(.fm-halo) { fill: var(--fm-bad); stroke: #fecdd3; }
.fm-svg.focus .fm-warn { opacity: .25; }
/* A node's GPUs: which pairs NVLink joins. */
.gpu-wiring { display: flex; flex-wrap: wrap; gap: 1.25rem; align-items: flex-start; }
table.nvl-matrix { width: auto; border-collapse: separate; border-spacing: 3px; background: none; font-size: .75rem; }
table.nvl-matrix th { font-weight: 600; color: var(--muted); padding: 0 .3rem; text-align: center; border: 0; }
table.nvl-matrix td { width: 2rem; height: 2rem; text-align: center; border-radius: 6px; padding: 0; border: 0;
font-weight: 700; font-size: .7rem; }
table.nvl-matrix td.self { background: var(--line); }
table.nvl-matrix td.nv { color: #04211c; background: linear-gradient(135deg, #5eead4, #2dd4bf);
box-shadow: 0 0 10px rgba(45, 212, 191, .45); }
table.nvl-matrix td.pcie { color: var(--muted); background: var(--card); border: 1px solid var(--line); }
table.gpu-list { width: auto; flex: 1 1 22rem; }
/* ---------------------------------------------------------------------
NVLink domains, drawn as racks: the switch band on top, a backplane
carrying light down the side, trays of GPU chips that glow when joined.
--------------------------------------------------------------------- */
.racks { display: grid; grid-template-columns: repeat(auto-fill, minmax(26rem, 1fr)); gap: 1.25rem; margin: 1rem 0 1.5rem; }
.rack { border-radius: 16px; overflow: hidden; color: #dbe4ff;
background: radial-gradient(700px 260px at 50% -20%, #1a3b3b 0%, #0b1020 60%), #0b1020;
border: 1px solid #1c2f3f; box-shadow: 0 20px 50px -28px rgba(45, 212, 191, .45); }
.rack.troubled { border-color: #4a1d2c; box-shadow: 0 20px 50px -28px rgba(251, 113, 133, .45); }
.rack-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; padding: .9rem 1rem .6rem; }
.rack-head h2 { margin: 0; color: #fff; font-size: 1.15rem; letter-spacing: .02em; }
.rack-head .note, .rack-head code { color: #7c89ad; background: none; font-size: .72rem; }
.rack-stats { display: flex; gap: 1rem; text-align: right; }
.rack-stats b { display: block; font-size: 1.1rem; color: #fff; font-variant-numeric: tabular-nums; }
.rack-stats span { font-size: .62rem; letter-spacing: .1em; text-transform: uppercase; color: #7c89ad; }
.rack-body { padding: .25rem 1rem 1rem; }
.nvswitch { position: relative; height: 1.9rem; border-radius: 8px; overflow: hidden; display: flex; align-items: center;
justify-content: center; font-size: .68rem; letter-spacing: .22em; text-transform: uppercase; color: #ccfbf1;
background: linear-gradient(90deg, #0f3b3a, #115e59 50%, #0f3b3a); border: 1px solid #2dd4bf55;
box-shadow: 0 0 22px rgba(45, 212, 191, .35), inset 0 0 12px rgba(45, 212, 191, .25); }
.nvswitch span { position: relative; z-index: 1; text-shadow: 0 0 8px rgba(94, 234, 212, .8); }
.nvswitch i { position: absolute; inset: 0; background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,.28) 50%, transparent 70%);
transform: translateX(-100%); animation: nv-shimmer 3.2s ease-in-out infinite; }
@keyframes nv-shimmer { to { transform: translateX(100%); } }
.rack-trays { position: relative; margin-top: .6rem; padding-left: 1.1rem; display: flex; flex-direction: column; gap: .45rem; }
.backplane { position: absolute; left: .3rem; top: -.6rem; bottom: .3rem; width: 4px; border-radius: 4px;
background: linear-gradient(180deg, #2dd4bf, #0f766e 40%, #2dd4bf 60%, #0f766e); background-size: 100% 300%;
box-shadow: 0 0 12px rgba(45, 212, 191, .6); animation: nv-flow 2.4s linear infinite; }
@keyframes nv-flow { to { background-position: 0 300%; } }
.tray { position: relative; display: flex; align-items: center; gap: .75rem; padding: .45rem .6rem; border-radius: 10px;
background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.015)); border: 1px solid #1f2d4a; }
.tray::before { content: ""; position: absolute; left: -.8rem; top: 50%; width: .8rem; height: 2px;
background: linear-gradient(90deg, #2dd4bf, transparent); box-shadow: 0 0 6px #2dd4bf; }
.tray.out { border-color: #5b2333; background: linear-gradient(180deg, rgba(251,113,133,.08), rgba(251,113,133,.02)); }
.tray.out::before { background: linear-gradient(90deg, #fb7185, transparent); box-shadow: 0 0 6px #fb7185; }
.tray-name { min-width: 6.5rem; font-size: .8rem; font-weight: 700; color: #e0f2fe; text-decoration: none; }
.tray-name:hover { color: #5eead4; }
.chips { display: flex; flex-wrap: wrap; gap: .35rem; flex: 1; }
.gpu-chip { width: 1.7rem; height: 1.7rem; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center;
font-size: .62rem; font-weight: 800; color: #04211c; cursor: default; }
.gpu-chip.joined { background: linear-gradient(135deg, #5eead4, #14b8a6); box-shadow: 0 0 12px rgba(45, 212, 191, .55);
animation: nv-breathe 3.6s ease-in-out infinite; }
.gpu-chip.joined.slow { background: linear-gradient(135deg, #fde68a, #f59e0b); box-shadow: 0 0 12px rgba(245, 158, 11, .6); }
.gpu-chip.lost { color: #fff; background: linear-gradient(135deg, #fb7185, #be123c); box-shadow: 0 0 14px rgba(251, 113, 133, .7);
animation: nv-alarm 1.2s ease-in-out infinite; }
@keyframes nv-breathe { 50% { box-shadow: 0 0 4px rgba(45, 212, 191, .25); } }
@keyframes nv-alarm { 50% { opacity: .55; } }
.imex { font-size: .6rem; font-weight: 800; letter-spacing: .08em; padding: .15rem .4rem; border-radius: 999px;
color: #fecdd3; border: 1px solid #fb7185; }
.imex.up { color: #99f6e4; border-color: #2dd4bf; box-shadow: 0 0 8px rgba(45, 212, 191, .35); }
.rack-problems { padding: 0 1rem .9rem; margin: 0; }
.rack-problems li { color: #dbe4ff; }
@media (prefers-reduced-motion: reduce) {
.nvswitch i, .backplane, .gpu-chip.joined, .gpu-chip.lost { animation: none; }
}
/* People: an account row on one line, its rarer actions in a menu. */
form.add-to { display: flex; gap: .35rem; align-items: center; flex-wrap: nowrap; }
form.add-to select { width: auto; max-width: 13rem; margin: 0; padding: .25rem .4rem; font-size: .8rem; }
form.add-to button { padding: .25rem .6rem; font-size: .78rem; }
details.row-menu { position: relative; display: inline-block; }
details.row-menu > summary { list-style: none; cursor: pointer; padding: .1rem .55rem; border-radius: 6px;
color: var(--muted); font-weight: 700; letter-spacing: .05em; user-select: none; }
details.row-menu > summary::-webkit-details-marker { display: none; }
details.row-menu > summary:hover, details.row-menu[open] > summary { color: var(--fg); background: var(--card); }
details.row-menu .menu { position: absolute; right: 0; top: calc(100% + .25rem); z-index: 5; min-width: 8rem;
display: flex; flex-direction: column; gap: .15rem; padding: .35rem; border-radius: 8px;
background: var(--card); border: 1px solid var(--line); box-shadow: 0 10px 24px rgba(0,0,0,.18); }
details.row-menu .menu form { display: block; }
details.row-menu .menu button { width: 100%; text-align: left; padding: .3rem .5rem; border-radius: 5px; }
details.row-menu .menu button:hover { background: var(--bg); }
details.add-inline { margin: .25rem 0 .4rem; }
details.add-inline > summary { list-style: none; cursor: pointer; display: inline-block; font-size: .8rem;
color: var(--accent); padding: .1rem .2rem; }
details.add-inline > summary::-webkit-details-marker { display: none; }
details.add-inline[open] > summary { color: var(--muted); }
details.add-inline form.add { margin-top: .3rem; }
.fetch-note { margin-top: .3rem; font-size: .78rem; white-space: normal; } .fetch-note.bad { color: var(--bad); }
/* ---------------------------------------------------------------------
A phone. A wide table scrolls sideways inside its own box rather than
widening the page, which made a phone zoom the whole page out until
nothing on it could be read.
--------------------------------------------------------------------- */
@media (max-width: 760px) {
.wrap { padding: 1.25rem .9rem 3rem; }
table { display: block; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
pre { max-width: 100%; overflow-x: auto; }
header nav, .nav { flex-wrap: wrap; row-gap: .35rem; }
.tiles .tile { flex: 1 1 45%; min-width: 0; }
.chart-row { grid-template-columns: 1fr !important; }
.field-row { flex-direction: column; }
.racks { grid-template-columns: 1fr; }
.rack-stats { gap: .6rem; }
.fm-bar { flex-direction: column; align-items: flex-start; }
.explore-search input[type=search] { flex-basis: 100%; }
form.add-to select { max-width: 9rem; }
}
/* Chat: the settings, the stop button, and a reply's timing. */
details.chat-settings { margin: .5rem 0 .25rem; }
details.chat-settings > summary { cursor: pointer; font-size: .85rem; color: var(--muted); }
details.chat-settings label { display: block; margin: .5rem 0; font-size: .85rem; }
details.chat-settings textarea { width: 100%; font: inherit; font-size: .88rem; padding: .45rem .55rem; margin-top: .3rem;
border: 1px solid var(--line); border-radius: 6px; background: var(--card); color: var(--fg); resize: vertical; }
details.chat-settings .temp input[type=range] { width: 14rem; vertical-align: middle; margin: 0 .5rem; accent-color: var(--accent); }
details.chat-settings output { font-variant-numeric: tabular-nums; font-weight: 700; margin-left: .3rem; }
#send.stop { background: var(--bad); border-color: var(--bad); }
.chat-stats { margin: -.2rem 0 .6rem; font-size: .72rem; font-variant-numeric: tabular-nums; }
/* Connecting an editor. */
.connect-result { border: 1px solid var(--accent); border-radius: 12px; padding: .25rem 1.1rem 1rem; margin: 1rem 0 1.5rem;
background: var(--card); box-shadow: 0 10px 30px -18px var(--accent); }
.copyable { position: relative; }
.copyable pre { margin-bottom: .25rem; overflow-x: auto; }
.copyable button { margin-right: .75rem; font-size: .8rem; }
.connect-form .field-row label { flex: 1 1 12rem; }
table.connect-fields th { white-space: nowrap; }
/* The card opened beneath a held version on the mirror page. */
tr.held-detail > td { background: var(--bg); padding: .75rem 1rem 1rem; }
.held-card .held-model { margin-top: .75rem; padding-top: .75rem; border-top: 1px solid var(--line); }
.held-card .held-model:first-of-type { border-top: 0; padding-top: 0; }
.held-card h3 { font-size: 1rem; margin: 0 0 .5rem; display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; }
.held-card table.replicas { margin: .25rem 0 .5rem; }
.held-card .headroom { margin: .5rem 0 .25rem; font-weight: 600; }
.held-card details.blocked { font-size: .85rem; color: var(--muted); margin-bottom: .5rem; }
.held-card details.blocked ul { margin: .35rem 0 0 1.1rem; padding: 0; }
.held-card .replicas-form .field-row { align-items: flex-end; }
.held-card .replicas-form .field-row label { flex: 0 0 auto; }
.held-card .replicas-form input[type=number] { width: 8rem; margin: .25rem 0 0; }
.held-card .replicas-form button { margin: 0; }
a.open-held { text-decoration: none; color: inherit; }
a.open-held:hover strong { text-decoration: underline; }
a.open-held[aria-expanded=true]::before { content: "▾ "; color: var(--muted); }
a.open-held[aria-expanded=false]::before { content: "▸ "; color: var(--muted); }
.use .runs-now { font-size: .78rem; color: var(--muted); }
.use .runs-now.short { color: var(--warn); }
.use .runs-now.bad { color: var(--bad); }
.use .runs-now .node { white-space: nowrap; }
/* One line per record, in every table. A header never wraps; a cell of free
* text is cut to its column with the whole of it on hover, instead of a row of
* its own or a line break that makes one record look like two. */
th { white-space: nowrap; }
table.rows td { white-space: nowrap; }
td.clip { max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
table.rows td.clip { color: var(--muted); font-size: .85rem; }
/* A row across the whole table is a note or an open form, not a record: it
* wraps as prose does. */
table.rows td[colspan] { white-space: normal; }
table.rows td.where { max-width: none; }
table.rows .more { cursor: help; margin-left: .2rem; }
table.explore td:first-child { max-width: 34rem; overflow: hidden; text-overflow: ellipsis; }
table.explore .from { margin-left: .5rem; font-size: .78rem; color: var(--muted); }
.clip-inline { display: inline-block; max-width: 24rem; overflow: hidden; text-overflow: ellipsis;
vertical-align: bottom; font-size: .8rem; color: var(--muted); margin-left: .4rem; }
table.rows td.warn { color: var(--warn); cursor: help; }
.held-card .replicas-form select, .capacity-form select { display: block; margin-top: .25rem; }
.held-card .replicas-form .field-row { flex-wrap: wrap; gap: .75rem 1rem; }
.quota-tree td.q-act form.inline { margin-left: .75rem; }
.held-card .adapters { margin: .75rem 0; }
.held-card .adapters h4 { font-size: .9rem; margin: 0 0 .35rem; display: flex; gap: .75rem; align-items: baseline; }
.held-card .adapter-form .field-row { align-items: flex-end; flex-wrap: wrap; gap: .5rem 1rem; }
.held-card .adapter-form select { display: block; margin-top: .25rem; max-width: 28rem; }
/* The GPU map: a square per card, a row per node. */
.gpu-map { display: flex; flex-direction: column; gap: 3px; margin: 6px 0; overflow-x: auto; }
.gpu-row { display: flex; align-items: center; gap: 3px; }
.gpu-node { width: 7em; flex: none; font-size: 12px; color: var(--muted, #666); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gpu-cell { width: 26px; height: 26px; flex: none; border: 1px solid rgba(0,0,0,.18); border-radius: 4px;
font-size: 10px; line-height: 26px; text-align: center; color: rgba(0,0,0,.7); cursor: default; }
/* The people page: one page in tabs, and a panel for the branch or person
chosen in the tree, beside it on a wide screen and above it on a narrow one. */
.page-tabs { display: flex; flex-wrap: wrap; gap: .25rem; border-bottom: 1px solid var(--line); margin: .25rem 0 1rem; }
.page-tabs a { padding: .45rem .8rem; color: var(--muted); text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.page-tabs a:hover { color: var(--fg); }
.page-tabs a[aria-current="page"] { color: var(--fg); border-bottom-color: var(--accent); font-weight: 600; }
.people-grid.with-detail { display: grid; gap: 1.25rem; grid-template-columns: minmax(0, 1fr) minmax(0, 24rem); align-items: start; }
.people-grid.with-detail .people-detail { grid-column: 2; grid-row: 1; position: sticky; top: .75rem; max-height: calc(100vh - 1.5rem); overflow-y: auto; }
.people-grid.with-detail .people-main { grid-column: 1; grid-row: 1; min-width: 0; }
@media (max-width: 960px) {
.people-grid.with-detail { grid-template-columns: minmax(0, 1fr); }
.people-grid.with-detail .people-detail { grid-column: 1; grid-row: 1; position: static; max-height: none; }
.people-grid.with-detail .people-main { grid-row: 2; }
}
.people-detail { background: var(--card); border: 1px solid var(--line); border-radius: 8px; padding: .9rem 1rem; }
.people-detail h2 { margin: .1rem 0 0; font-size: 1.15rem; }
.people-detail h3 { margin: 1rem 0 .4rem; font-size: .9rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
.people-detail .chart-wrap { margin-bottom: .75rem; }
.people-detail .q-form .field-row { flex-wrap: wrap; }
.pd-head { position: relative; padding-right: 1.5rem; }
.pd-path, .pd-sub { margin: .2rem 0 0; font-size: .85rem; color: var(--muted); }
.pd-close { position: absolute; top: 0; right: 0; font-size: 1.3rem; line-height: 1; color: var(--muted); text-decoration: none; }
.pd-limits { display: grid; grid-template-columns: auto 1fr; gap: .25rem .9rem; margin: 0; font-size: .88rem; }
.pd-limits dt { color: var(--muted); }
.pd-limits dd { margin: 0; font-variant-numeric: tabular-nums; }
a.sel-link { color: inherit; text-decoration: none; border-bottom: 1px dotted var(--line); }
a.sel-link:hover { color: var(--accent); border-bottom-color: var(--accent); }
a.sel-link.on { color: var(--accent); border-bottom: 2px solid var(--accent); }
a.sel-link.small { font-size: .78rem; color: var(--muted); margin-left: .4rem; }
.people-detail h3.chart-title { text-transform: none; letter-spacing: 0; color: var(--fg); font-size: .85rem; margin: .2rem 0; }
.people-detail details.edit { display: inline-block; margin-right: 1rem; vertical-align: top; }
.people-detail details.edit[open] { display: block; margin: .5rem 0; }