:root {
  color-scheme: light dark;
  --bg: #f6f7f9;
  --panel: #ffffff;
  --ink: #16191d;
  --muted: #667085;
  --line: #dfe3e8;
  --accent: #2f6fed;
  --ok: #1a7f4b;
  --warn: #a86400;
  --danger: #b3261e;
  /* The explanation boxes (templates/_help.html): a colour of their own. */
  --help-bg: #26324a;
  --help-ink: #f3f6fc;
  /* One size for a control and the words that go with it: buttons, fields,
     drop-downs, their labels, switches, chips and the headings that open a
     section all use it, so nothing beside a button is bigger or smaller than
     the button. Notes under a control stay a step smaller, as secondary text. */
  --control-font: 14px;
  --control-height: 35px;
  /* A session that needs looking at says so in larger type than the page
     around it, on the desktop and the mobile version alike. */
  --attention-font: 16px;
  /* Log colouring (pre.logview): quoted text and numbers have colours of
     their own; everything else uses the colours above. */
  --log-string: #7a3e9d;
  --log-number: #0b7285;
  /* The stager history: what a command is for, and the program it runs. */
  --log-desc: #b0306a;
  --log-program: #1a7f4b;
  --log-operator: #a86400;
}
@media (prefers-color-scheme: dark) {
  :root {
    --log-string: #d3a6ec;
    --log-number: #5fc9d6;
    --log-desc: #f48fb8;
    --log-program: #6fd39b;
    --log-operator: #e8b04f;
    --help-bg: #dde6f8;
    --help-ink: #10151d;
    --bg: #14171b;
    --panel: #1c2026;
    --ink: #e8eaed;
    --muted: #9aa3ae;
    --line: #2c323a;
    --accent: #6b9bff;
    --ok: #4fc07f;
    --warn: #e0a33a;
    --danger: #f0837c;
  }
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
/* Wide enough that lines and rows only wrap when the window really is too
   narrow for them. */
main { max-width: 1280px; margin: 0 auto; padding: 20px 16px 60px; }
h1 { font-size: 22px; margin: 0 0 6px; }
h2 { font-size: 17px; margin: 0; }
code { font-family: ui-monospace, "Cascadia Mono", Menlo, monospace; font-size: 12px; }
a { color: var(--accent); }

.topbar {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px; background: var(--panel);
  border-bottom: 1px solid var(--line);
}
.brand { font-weight: 650; text-decoration: none; color: var(--ink); }
.spacer { flex: 1; }
.who { font-size: var(--control-font); color: var(--muted); }
.topnav { display: flex; align-items: center; gap: 12px; }
.topnav form { margin: 0; }

.panel {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: 16px; margin-bottom: 16px;
}
.panel.narrow { max-width: 380px; margin-inline: auto; }
/* Settings that read as sentences (the account page): room for a line each. */
.panel.roomy { max-width: 640px; margin-inline: auto; }
.panel.empty { text-align: center; color: var(--muted); }
.muted { color: var(--muted); font-size: 13px; margin: 4px 0; }
.warntext { color: var(--warn); }

.flashes { list-style: none; padding: 0; margin: 0 0 14px; }
.flash {
  padding: 9px 12px; border-radius: 8px; margin-bottom: 8px;
  border: 1px solid var(--line); background: var(--panel); font-size: 14px;
}
.flash.ok { border-color: var(--ok); }
.flash.error { border-color: var(--danger); }
.flash.warn { border-color: var(--warn); }

.sessionhead { display: flex; gap: 16px; align-items: flex-start; }
.sessionhead > div:first-of-type { flex: 1; min-width: 0; }
/* Folding a session down to its header. */
.collapser { order: 3; align-self: flex-start; white-space: nowrap; }
.session.collapsed { padding-bottom: 12px; }
.session.collapsed .fitline { display: none; }
.collapseall { display: flex; gap: 12px; justify-content: flex-end; margin: 0 0 8px; }
.verifynote { display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.verifynote p { margin: 0; }
.linklist input { width: 100%; font-family: monospace; }
/* The admin view of every session. */
/* An explanation: a small ? and the box it opens (templates/_help.html). The
   box never takes up room, so nothing around it moves; app.js fixes it on
   screen just below the ?, inside the window. */
/* Inline, so the word joiner before the ? binds it to the last word; nowrap so
   nothing inside breaks. Middle sits the ? on the centre of the letters, and
   the small lift moves that from the x-height up to the middle of the row. In
   a flex row it is an item of its own, centred on that row. */
.helptip, span.helptip[data-help] {
  position: relative; top: -0.07em; white-space: nowrap; align-self: center;
  display: inline; flex: 0 0 auto; margin: 0; font-size: inherit; color: inherit; }
.helpbutton, button.helpbutton[data-help-button] {
  flex: 0 0 auto; min-width: 0 !important; width: 18px !important; height: 18px !important;
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle;
  width: 18px; height: 18px; min-height: 0 !important; padding: 0 !important;
  /* The negative top and bottom keep the 18px circle from making its line taller. */
  margin: -3px 0 -3px 6px;
  border: 1px solid var(--line); border-radius: 50%; background: transparent;
  color: var(--muted); font: 600 11px/1 system-ui, sans-serif; cursor: help;
}
/* A finger needs more room than the ? shows; the extra is invisible. */
.helpbutton::after { content: ""; position: absolute; inset: -11px; }
.helpbutton:hover, .helptip.open .helpbutton,
button.helpbutton[data-help-button]:hover, .helptip.open button.helpbutton[data-help-button] { color: var(--help-ink); background: var(--help-bg); border-color: var(--help-bg); }
/* Fixed on screen even while closed: a closed box sits at the window's corner,
   invisible, so it never widens a page or gives a scrolling panel a sideways
   scrollbar. app.js moves it under the ? when it opens. */
.helppop, span.helppop[data-help-pop] {
  display: block; margin: 0;
  position: fixed; top: 0; left: 0; z-index: 60;
  width: max-content; max-width: min(440px, calc(100vw - 16px));
  padding: 8px 10px; border-radius: 8px; background: var(--help-bg); color: var(--help-ink);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .22);
  font: 400 13px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  text-align: left; white-space: normal; overflow-wrap: anywhere; letter-spacing: normal;
  text-transform: none; pointer-events: none;
  opacity: 0; visibility: hidden; transition: opacity .16s ease, visibility 0s linear .16s;
}
.helppop.placed { position: fixed; top: 0; left: 0; }
/* Without the script the box sits under the ? in the page, and is not drawn
   at all until it is shown, so a closed one takes no room. */
html:not(.helpjs) .helppop { position: absolute; top: calc(100% + 6px); left: 0; display: none; }
html:not(.helpjs) .helptip:hover .helppop,
html:not(.helpjs) .helptip:focus-within .helppop { display: block; }
.helptip.open .helppop,
html:not(.helpjs) .helptip:hover .helppop,
html:not(.helpjs) .helptip:focus-within .helppop {
  opacity: 1; visibility: visible; pointer-events: auto; transition-delay: 0s;
}
@media (prefers-reduced-motion: reduce) { .helppop { transition: none; } }
/* A button and the ? after it, kept together in a row that wraps. */
.withhelp { display: inline-flex; align-items: center; flex-wrap: nowrap; }
/* A checkbox label's words and their ?, as one piece beside the box. */
.togglelabel { flex: 1 1 0; min-width: 0; }
.statechips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 12px; }
/* Server status: requests to Google a minute. Scales with the panel, so it
   never needs sideways scrolling; the grid and axis text stay recessive. */
.reqchart { display: block; width: 100%; height: auto; margin-top: 8px; }
.reqchart .reqgrid { stroke: var(--chart-grid); stroke-width: 1; }
.reqchart .reqaxis { fill: var(--muted); font-size: 11px; }
.reqchart .reqbar { fill: var(--series-requests); }
.reqchart .reqhit { fill: transparent; }
.reqchart .reqslot:hover .reqhit, .reqchart .reqslot.on .reqhit { fill: var(--chart-empty); }
.reqchart .reqslot { cursor: pointer; outline: none; }
.reqchart .reqslot:focus-visible .reqhit { stroke: var(--accent); stroke-width: 2; }
/* A minute's number, beside its bar, while it is hovered, tapped or focused. */
[data-requests-chart] { position: relative; }
.reqtip { position: absolute; z-index: 5; margin: 0; padding: 4px 8px; max-width: calc(100% - 16px);
  background: var(--help-bg); color: var(--help-ink); border-radius: 6px; font-size: 13px;
  white-space: nowrap; pointer-events: none; box-shadow: 0 2px 8px rgba(0,0,0,.18); }
.reqtable { margin-top: 8px; }
.reqtable table { max-width: 360px; }
/* Every state a session carries, side by side, wrapping in a narrow column. */
.statetags { display: inline-flex; flex-wrap: wrap; gap: 4px 6px; }
/* The search box over an admin list (templates/admin/_search.html). */
.listsearch { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; margin: 8px 0; }
.listsearch input[type="search"] { flex: 1 1 220px; max-width: 360px; }
.listsearch [data-list-search-none] { flex-basis: 100%; margin: 0; }
.searchhidden { display: none !important; }
.statechips a { text-decoration: none; }
.statechips a.on .tag { outline: 2px solid var(--accent); }
.admincontrols { display: flex; flex-wrap: wrap; gap: 6px; }
.admincontrols form { margin: 0; }
.admincontrols button { padding: 3px 9px; font-size: var(--control-font); }
.statusbox { text-align: right; font-size: 13px; flex: 0 1 auto; min-width: 0; max-width: 65%; }
/* A long message, even one unbroken run such as an address or a token, wraps
   inside the box rather than widening the panel. */
.statusbox small { display: block; color: var(--muted); font-size: 12px;
  white-space: normal; overflow-wrap: anywhere; word-break: break-word; }
.wrapanywhere { overflow-wrap: anywhere; word-break: break-word; min-width: 0; }
/* The raw error the admin pages keep for debugging, wrapped the same way. */
code.rawerror { display: block; max-width: 100%; white-space: pre-wrap;
  overflow-wrap: anywhere; word-break: break-word; font-size: 12px; color: var(--muted); }
.dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  background: var(--muted); margin-right: 5px;
}
.dot.live { background: var(--ok); box-shadow: 0 0 0 3px rgba(26,127,75,.18); }
/* Errored: a live run showing an error. Red, never the green of running, on
   the session panels, the dashboard and the admin Sessions page alike. */
.dot.errored { background: var(--danger); box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 22%, transparent); }
.erroredtext { color: var(--danger); font-weight: 600; }

/* --- viewing as someone else -----------------------------------------------
   Sticks to the top so an admin never forgets whose site they are looking at,
   in the warning colour, with the way back always in reach. */
.viewas {
  position: sticky; top: 0; z-index: 20;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px 14px;
  padding: 8px 16px; color: var(--ink); font-size: 14px;
  background: color-mix(in srgb, var(--warn) 16%, var(--panel));
  border-bottom: 2px solid var(--warn);
}
.viewas form { margin: 0; }
.viewasstart form { margin: 0 0 6px; }

/* --- a session locked over the account limit --------------------------------
   Kept, not deleted, so it is set apart in the warning colour with the reason
   and the way out beside it, the same way an account problem is. */
.panel.session[data-locked] { border-color: var(--warn); border-width: 2px; }
.lockednote {
  margin: 10px 0; padding: 10px 12px; border: 1px solid var(--warn); border-radius: 8px;
  background: color-mix(in srgb, var(--warn) 9%, var(--panel));
}
.lockednote strong { color: var(--warn); font-size: 18px; }
.lockednote p { margin: 6px 0 0; font-size: var(--attention-font); }

/* --- dashboard ------------------------------------------------------------
   Every account as a card, side by side, so the running one and how it is
   doing read at a glance. No card is highlighted over the others: the status
   cell and the dot carry which one is live, and every card gets the same
   hover, so the eye finds the active one by reading rather than by a colour
   the layout picked for it. */
.dashboard .dashgrid {
  display: grid; gap: 12px; margin-top: 12px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
.dashcard {
  display: block; padding: 12px 14px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--bg); color: var(--ink); text-decoration: none;
}
.dashcard:hover { border-color: var(--accent); }
.dashname { display: flex; align-items: center; gap: 4px; }
.dashname strong { font-size: 15px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dashacct { display: block; font-size: 12px; margin: 3px 0 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dashstats { display: grid; grid-template-columns: 1fr 1fr; gap: 9px 12px; margin: 0; }
.dashstats dt { font-size: 11px; color: var(--muted); margin-bottom: 1px; }
.dashstats dd { margin: 0; font-size: 14px; font-variant-numeric: tabular-nums; }

.tag {
  display: inline-block; padding: 1px 6px; border-radius: 5px;
  border: 1px solid var(--line); font-size: 11px; color: var(--muted);
}
.tag.ok { color: var(--ok); border-color: var(--ok); }
.tag.warn { color: var(--warn); border-color: var(--warn); }
.tag.bad { color: var(--danger); border-color: var(--danger); font-weight: 600; }

.actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0; }
.actions form { margin: 0; }
button, .button {
  font: inherit; font-size: var(--control-font); padding: 6px 11px; border-radius: 7px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink);
  cursor: pointer; text-decoration: none; display: inline-block;
}
button:hover:not(:disabled), .button:hover { border-color: var(--accent); }
/* Room around buttons. In a form that stacks its pieces, a field, a switch or
   a line of text never touches the field or button under it; the forms laid
   out as rows (.inline, .logfilter, .grid, .actions) space theirs with gap.
   Rows of buttons use gap too, never less than 6px. */
form:not(.inline):not(.logfilter):not(.grid):not(.actions)
  > :is(.field, .toggle, .flash, p) + :is(.field, .toggle, button, .button) { margin-top: 10px; }
button:disabled { opacity: .45; cursor: not-allowed; }
/* Short words that read as one piece never break across lines on the desktop
   site: buttons, tags, chips, menu links. The mobile version lets a button's
   words wrap, since a phone's column is narrow. */
button, .button, .tag, .navlink, .chip, .daychip, .choice, .runbadge { white-space: nowrap; }
button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
button.danger { color: var(--danger); }
button.link {
  background: none; border: none; color: var(--accent); padding: 0; text-decoration: underline;
}

details { border-top: 1px solid var(--line); padding-top: 8px; margin-top: 8px; }
summary { cursor: pointer; font-size: var(--control-font); font-weight: 550; }

.inline { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-end; margin-top: 10px; }
/* A box and the button that acts on it stay side by side. The pair wraps as
   one piece, never apart, and the button lines up with the box rather than
   with the label over it. Rows may grow taller for it; a button is never left
   on a line of its own, away from its box. */
.inputwith { display: flex; flex-wrap: nowrap; align-items: flex-end; gap: 8px; min-width: 0; }
.inputwith > input, .inputwith > select, .inputwith > .field { flex: 1 1 auto; min-width: 0; width: auto; }
.inputwith > button, .inputwith > .button, .inputwith > .withhelp { flex: 0 0 auto; }
.inline input[type=text] { flex: 1; min-width: 220px; }
/* Fields side by side with their button straight after the last one, lined
   up with the boxes. */
.fieldrow { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 12px; margin: 12px 0; }
.fieldrow > .field { flex: 1 1 210px; min-width: 0; }
.fieldrow > button { flex: 0 0 auto; }
/* The time zone picker: a search box over the drop-down it narrows. */
.tzfield input[type=search], .tzfield select {
  width: 100%; font: inherit; font-size: var(--control-font); padding: 6px 8px; border: 1px solid var(--line);
  border-radius: 7px; background: var(--bg); color: var(--ink);
}
.tzfield input[type=search] { margin-bottom: 4px; }
/* Following the computer's zone: shown, sent, but not something to change. */
.tzfield select.locked { pointer-events: none; opacity: .75; }
/* The zone combo box: a button that drops a search field over a list. */
.tzfield select.tzhidden { display: none; }
.tzfield { position: relative; }
.tzbutton {
  width: 100%; text-align: left; font: inherit; font-size: var(--control-font); padding: 6px 8px;
  border: 1px solid var(--line); border-radius: 7px; background: var(--bg); color: var(--ink);
  cursor: pointer;
}
.tzbutton::after { content: ""; float: right; border: 5px solid transparent;
  border-top-color: var(--muted); margin-top: 6px; }
.tzbutton.locked { pointer-events: none; opacity: .75; }
.tzpop {
  position: absolute; z-index: 30; left: 0; right: 0; margin-top: 4px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,.25); padding: 8px;
}
.tzpop input[type=search] { width: 100%; margin: 0 0 6px; }
.tzoptions { list-style: none; margin: 0; padding: 0; max-height: 240px; overflow-y: auto; }
.tzoption { padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: var(--control-font); }
.tzoption:hover { background: var(--bg); }
.tzoption.on { color: var(--accent); font-weight: 600; }

.grid {
  display: grid; gap: 12px; margin: 12px 0;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.field { display: block; }
.field > span:not(.helptip):not(.togglelabel) { display: block; font-size: var(--control-font); margin-bottom: 3px; }
.field em { color: var(--muted); font-style: normal; font-size: inherit; }
input[type=text], input[type=password], input[type=number], input[type=email],
input[type=time], input[type=datetime-local], input[type=search], input[type=url] {
  width: 100%; font: inherit; font-size: var(--control-font); padding: 6px 8px; min-height: var(--control-height);
  border: 1px solid var(--line); border-radius: 7px;
  background: var(--bg); color: var(--ink);
}

fieldset { border: 1px solid var(--line); border-radius: 9px; margin: 14px 0; padding: 10px 12px; }
legend { font-size: var(--control-font); font-weight: 600; padding: 0 4px; }

.modegrid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.mode {
  border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px;
  display: block; cursor: pointer;
}
.mode.on { border-color: var(--accent); }
.mode small { display: block; color: var(--muted); font-size: 12px; }

.days { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.chip {
  border: 1px solid var(--line); border-radius: 999px;
  padding: 3px 10px; font-size: var(--control-font); cursor: pointer;
}
.toggle { display: block; font-size: var(--control-font); margin: 6px 0; cursor: pointer; }

.deadrows { display: grid; gap: 6px; margin: 8px 0; }
.deadrow { display: flex; align-items: center; gap: 8px; }
.deadrow input { max-width: 140px; }

.log { list-style: none; padding: 0; margin: 8px 0 0; font-size: 13px; }
.log li { padding: 3px 0; border-bottom: 1px dotted var(--line); }
.log time { color: var(--muted); font-size: 11px; margin-right: 6px; }
.log li.error { color: var(--danger); }

/* The names column is as wide as its longest name, up to two fifths, so a
   name only wraps when it really cannot fit. */
dl.meta { display: grid; grid-template-columns: fit-content(40%) 1fr; gap: 4px 12px; font-size: 13px; }
dl.meta dt { color: var(--muted); }
dl.meta dd { margin: 0; overflow-wrap: anywhere; }

/* An account's sessions in detail, on the admin account page. Closed until
   asked for; each session is set off from the next by a rule. */
details.sessiondetails > summary {
  cursor: pointer; display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
}
.sessiondetail { border-top: 1px solid var(--line); margin-top: 12px; padding-top: 10px; }
.sessiondetail h3 { display: flex; align-items: center; font-size: 15px; margin: 0 0 8px; }

footer { text-align: center; color: var(--muted); font-size: 12px; padding-bottom: 24px; }

.speedslider {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px;
}
.speedslider input[type=range] { flex: 1; min-width: 160px; max-width: 320px; accent-color: var(--accent); }
.speedslider output { font-variant-numeric: tabular-nums; font-size: var(--control-font); color: var(--muted); }
.speedslider.off { opacity: .45; }
select {
  width: 100%; font: inherit; font-size: var(--control-font); padding: 6px 8px; min-height: var(--control-height);
  border: 1px solid var(--line); border-radius: 7px;
  background: var(--bg); color: var(--ink);
}


/* --- distance chart -------------------------------------------------------
   One stacked column per bucket, of this app's own distance split by whether
   the account confirms it: verified, missing, and not-yet-checked.

   Three solid bands stack, so every adjacent pair was validated, not just the
   outer two. Magenta carries "missing" because red against this blue collapses
   to CVD dE 5.6 - worse than the red/green it would have replaced:
     light  #2563eb / #86198f   worst adjacent CVD dE 14.7
     dark   #4f86e6 / #b5379a   worst adjacent CVD dE 11.9
   Pending is hatched grey rather than a third hue: it is an absence of
   knowledge, not a third category. */
:root {
  --series-verified: #2563eb;
  --series-missing: #86198f;
  --series-pending: #9aa3ae;
  --chart-grid: #e8ebef;
  --chart-empty: #eef0f3;
  --chart-target: #667085;
  /* Server status's requests graph: the validated blue of the distance chart. */
  --series-requests: #2563eb;
}
@media (prefers-color-scheme: dark) {
  :root {
    --series-verified: #4f86e6;
    --series-missing: #b5379a;
    --series-pending: #6b7480;
    --chart-grid: #2c323a;
    --chart-empty: #23282f;
    --chart-target: #9aa3ae;
    --series-requests: #4f86e6;
  }
}

.distance figure.chart { margin: 10px 0 0; }
figure.chart figcaption {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 6px;
}
.hero {
  font-size: 26px; font-weight: 600; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.herounit { font-size: 13px; color: var(--muted); }
figure.chart .asof { margin-left: auto; font-size: 12px; color: var(--muted); }

.legend { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; font-size: 12px; }
.legend .key { display: inline-flex; align-items: center; gap: 5px; color: var(--muted); }
.legend .key b { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 550; }
.legend .key i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.legend .key.verified i { background: var(--series-verified); }
.legend .key.missing i { background: var(--series-missing); }
.legend .key.pending i {
  background: repeating-linear-gradient(45deg,
    var(--series-pending) 0 2px, transparent 2px 4px);
}
.legend .key.target i {
  height: 0; width: 14px; border-radius: 0; border-top: 2px dashed var(--chart-target);
}
.rangepick { margin-left: auto; display: inline-flex; gap: 8px; }
.rangepick .link { text-decoration: none; color: var(--muted); }
.rangepick .link.on { color: var(--ink); text-decoration: underline; }

/* One stacked column per bucket. Debug sits at the bottom, so the dashed
   target line reads directly against the top of the debug segment - exactly
   the quantity the control loop is holding. Zero-height segments are not
   rendered at all, so the 2px surface gap only appears between two real
   segments rather than floating above a single one. */
/* No padding: an absolutely positioned target line resolves its percentage
   against the padding box while the bars size against the content box, so any
   padding here puts the line and the bars on two different scales. */
.plot {
  position: relative; display: flex; align-items: flex-end; gap: 2px;
  height: 96px; margin-top: 14px;
  border-bottom: 1px solid var(--chart-grid);
  overflow-x: auto; overflow-y: hidden;
}
.slot {
  flex: 1; min-width: 4px; height: 100%; position: relative;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 2px;
}
.slot > i.s { display: block; width: 100%; min-height: 2px; }
.slot > i.s.verified { background: var(--series-verified); }
.slot > i.s.missing { background: var(--series-missing); }
/* Not yet checked is neither confirmed nor lost: hatched, so it never
   reads as a solid claim either way. */
.slot > i.s.pending {
  background: repeating-linear-gradient(45deg,
    var(--series-pending) 0 2px, transparent 2px 4px);
}
/* Only the topmost segment carries the rounded data-end. */
.slot > i.s:first-child { border-radius: 4px 4px 0 0; }
.slot:hover > i.s { filter: brightness(1.12); }
/* The direct label is a flex child at the top of its own stack rather than
   absolutely positioned above it: the plot scrolls horizontally, which clips
   anything sitting outside the box. */
.slot > b {
  flex: 0 0 auto; font-size: 11px; font-weight: 500; color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  text-align: center; line-height: 13px;
}
.targetline {
  position: absolute; left: 0; right: 0; height: 0;
  border-top: 2px dashed var(--chart-target); pointer-events: none; z-index: 1;
}
.targetline::after {
  content: attr(data-label); position: absolute; right: 0; top: -14px;
  font-size: 11px; color: var(--chart-target); background: var(--panel); padding: 0 3px;
}
.xaxis {
  display: flex; justify-content: space-between; gap: 8px;
  font-size: 11px; color: var(--muted); margin-top: 4px;
}
figure.chart .note { font-size: 13px; color: var(--muted); margin: 8px 0 0; }
.charttools { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.charttools .muted { margin-left: auto; font-size: 12px; }

table.distdata { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 8px; }
table.distdata caption { text-align: left; color: var(--muted); font-size: 12px; padding-bottom: 4px; }
table.distdata th, table.distdata td {
  text-align: left; padding: 4px 6px; border-bottom: 1px solid var(--line);
}
table.distdata td + td, table.distdata th + th {
  text-align: right; font-variant-numeric: tabular-nums;
}

.charttip {
  position: fixed; z-index: 50; pointer-events: none; opacity: 0;
  transition: opacity .1s; background: var(--panel); color: var(--ink);
  border: 1px solid var(--line); border-radius: 7px; padding: 6px 9px;
  font-size: 12px; box-shadow: 0 3px 12px rgba(0,0,0,.16); white-space: nowrap;
  line-height: 1.45;
}
.charttip.on { opacity: 1; }
.charttip b { font-variant-numeric: tabular-nums; }

/* Number box paired with the speed slider. */
.speedslider .speedbox {
  width: 72px; flex: 0 0 auto; text-align: right;
  font-variant-numeric: tabular-nums;
}
.speedslider .speedunit { font-size: var(--control-font); color: var(--muted); }

/* A shortfall is a fault, so it is called out in words as well as colour. */
figure.chart .note.alarm { color: var(--series-missing); font-weight: 500; }

/* --- header, account allowance ------------------------------------------- */
.who .tag { margin-left: 4px; }
.slots strong { color: var(--ink); font-variant-numeric: tabular-nums; }

/* --- save row and saved configurations ------------------------------------ */
.saverow {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  padding: 10px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.presets { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; }
.presets .inputwith { gap: 6px; }
.presets select { width: auto; min-width: 150px; }
.presets input[type=text] { width: 150px; flex: 0 1 auto; }
.presetnote { flex-basis: 100%; margin: 0; }

/* --- mode panels and schedules -------------------------------------------- */
.modepanel { margin-top: 12px; }
.schedrows { display: grid; gap: 8px; margin: 8px 0; }
.schedrow {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px;
  border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px;
}
.schedrow .days { flex-basis: 100%; }
.schedrow input.schedlabel { width: 180px; }
.schedrow input[type=number] { width: 90px; }
.schedrow input[type=datetime-local] { width: 210px; }
.field.compact > span:not(.helptip):not(.togglelabel) { font-size: var(--control-font); margin-bottom: 3px; }
.toggle.inline-toggle { margin: 0 0 6px; }
.toggle.small { font-size: var(--control-font); margin: 4px 0 0; }
.rowtools { display: flex; gap: 14px; }
.grid.bounds { grid-template-columns: repeat(auto-fit, minmax(140px, 180px)); }

/* --- dead hours ------------------------------------------------------------ */
.deadrow { flex-wrap: wrap; }
.deadrow select { width: auto; }
.deadspeed { display: inline-flex; align-items: center; gap: 6px; }
.deadspeed input { width: 80px; }

/* --- activity type and session name ---------------------------------------- */
.activitybox { display: flex; gap: 8px; flex-wrap: wrap; }
.choice {
  border: 1px solid var(--line); border-radius: 8px; padding: 6px 14px;
  cursor: pointer; font-size: var(--control-font);
}
.choice.on { border-color: var(--accent); }
.tokens { display: flex; flex-wrap: wrap; gap: 5px; margin: 6px 0; }
button.token {
  font-family: ui-monospace, "Cascadia Mono", Menlo, monospace; font-size: 12px;
  padding: 2px 7px; border-radius: 5px;
}

/* --- admin portal ------------------------------------------------------------ */
.navlink { font-size: var(--control-font); font-weight: 550; text-decoration: none; }
.topbar button.link { font-size: var(--control-font); }
.adminwrap, .docswrap { display: grid; grid-template-columns: fit-content(260px) minmax(0, 1fr); gap: 16px; align-items: start; }
.adminwrap { grid-template-columns: fit-content(300px) minmax(0, 1fr); }
.adminnav, .docsnav {
  display: flex; flex-direction: column; gap: 2px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 8px;
}
.adminnav a, .docsnav a {
  padding: 6px 10px; border-radius: 7px; text-decoration: none; color: var(--ink); font-size: var(--control-font);
}
.adminnav a:hover, .docsnav a:hover { background: var(--bg); }
.adminnav a.on, .docsnav a.on { background: var(--bg); color: var(--accent); font-weight: 600; }
/* The admin side list in groups: Information, Management, Config. */
.adminnav .navgroup + .navgroup { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
.adminnav .navgrouptitle { margin: 2px 10px 4px; font-size: 11px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.adminnav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.adminnav ul a { display: block; }
.pagehead .actionhead { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: baseline; }
/* A log's lines, wrapped so a long one never widens the page, in a box that
   scrolls up and down on its own (by wheel, touch or the keyboard once it has
   focus) without scrolling the page behind it. */
pre.logview { min-height: 120px; max-height: 70vh; overflow-x: hidden; overflow-y: auto;
  overscroll-behavior: contain; -webkit-overflow-scrolling: touch; margin: 8px 0 0; padding: 10px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 8px; font-size: 12px;
  white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; }
/* The pieces app.js (logsPage) colours a line by. */
pre.logview .lg-time { color: var(--muted); }
pre.logview .lg-level { font-weight: 700; }
pre.logview .lg-error, pre.logview .lg-bad { color: var(--danger); font-weight: 700; }
pre.logview .lg-warn { color: var(--warn); }
pre.logview .lg-info, pre.logview .lg-ok { color: var(--ok); }
pre.logview .lg-debug, pre.logview .lg-punct { color: var(--muted); }
pre.logview .lg-key, pre.logview .lg-method, pre.logview .lg-flag { color: var(--accent); }
pre.logview .lg-method { font-weight: 700; }
pre.logview .lg-str { color: var(--log-string); }
pre.logview .lg-num { color: var(--log-number); }
pre.logview .lg-url { color: var(--accent); text-decoration: underline; text-decoration-color: var(--line); }
pre.logview .lg-desc { color: var(--log-desc); font-weight: 700; }
pre.logview .lg-sep { color: var(--muted); }
pre.logview .lg-prog { color: var(--log-program); font-weight: 700; }
pre.logview .lg-op { color: var(--log-operator); font-weight: 700; }
pre.logview .lg-host { color: var(--log-number); }
pre.logview .lg-path { color: var(--ink); text-decoration: underline dotted var(--line); }
@media (forced-colors: active) {
  pre.logview [class^="lg-"], pre.logview [class*=" lg-"] { color: CanvasText; }
}
pre.logview:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.logactions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 8px 0 0; }
.logactions:not(:has(> button:not([hidden]))) { display: none; }
.logfilter { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: flex-end; }
.logrefresh, .logsaved { margin-top: 10px; }
.logrefresh input[type=number] { width: 110px; }
/* Server status: the key numbers as tiles, green, amber or red by whether
   they need looking at, and one card per connected service. */
.keytiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; margin-top: 12px; }
.keytile { border: 1px solid var(--line); border-left: 4px solid var(--line); border-radius: 8px;
  padding: 8px 12px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.keytile .keylabel { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.keytile b { font-size: 15px; overflow-wrap: anywhere; }
.keytile small { font-size: 12px; color: var(--muted); }
.keytile.ok { border-left-color: var(--ok); } .keytile.ok b { color: var(--ok); }
.keytile.warn { border-left-color: var(--warn); } .keytile.warn b { color: var(--warn); }
.keytile.bad { border-left-color: var(--danger); } .keytile.bad b { color: var(--danger); }
.servicecards { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px; }
/* Cards in a row are as tall as the tallest, and each one's Check now sits at
   its foot, so the buttons line up however much the cards above them say. */
.servicecard { border: 1px solid var(--line); border-top: 4px solid var(--line); border-radius: 8px;
  padding: 10px 12px; min-width: 0; display: flex; flex-direction: column; }
.servicecard > dl.meta { margin: 0; }
.servicecard > form.servicecheck { margin: auto 0 0; padding-top: 12px; }
.servicecard.ok { border-top-color: var(--ok); }
.servicecard.warn { border-top-color: var(--warn); }
.servicecard.bad { border-top-color: var(--danger); }
.servicehead { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; margin-bottom: 6px; }
.servicecard dl.meta { grid-template-columns: fit-content(50%) 1fr; }
.overviewcards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.overviewcard { border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; min-width: 0; }
.overviewcard h3 { margin: 0 0 4px; font-size: 15px; }
.overviewcard p { margin: 0; font-size: 13px; }
.adminmain, .docsmain { min-width: 0; }
.stats { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; }
.stats > div {
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 14px; min-width: 140px;
}
.stats b { display: block; font-size: 24px; font-variant-numeric: tabular-nums; }
.stats span { color: var(--muted); font-size: 13px; }
table.admintable { width: 100%; border-collapse: collapse; font-size: 14px; }
table.admintable th, table.admintable td {
  text-align: left; padding: 7px 8px; border-bottom: 1px solid var(--line); vertical-align: top;
  overflow-wrap: anywhere;
}
table.admintable th { font-size: 12px; color: var(--muted); font-weight: 600; white-space: nowrap; }
/* A last resort only: everything in the portal is made to fit the window, long
   names and paths wrap, so this scrollbar should never show. */
.adminmain { overflow-x: auto; }
.dangerzone { border-color: var(--danger); }

/* --- the guide -----------------------------------------------------------------
   Reading pages rather than controls, so the measure is narrower than the panel
   and the paragraphs are a little roomier than the rest of the app. */
.docpage { max-width: 80ch; }
.docpage h1 { margin-bottom: 4px; }
.docpage .lead { color: var(--muted); margin-top: 0; }
.docpage h2 { font-size: 16px; margin: 18px 0 6px; }
.docpage p { line-height: 1.55; }
.doccards { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.doccard {
  display: block; padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px;
  text-decoration: none; color: var(--ink); background: var(--bg);
}
.doccard:hover { border-color: var(--accent); }
.doccard strong { display: block; margin-bottom: 2px; }
.doccard span { font-size: 13px; }
.docsfoot { margin-top: 14px; }
.panel.about { margin-top: 12px; }
.panel.about p { line-height: 1.55; }

/* --- account problems ----------------------------------------------------------
   A session whose Google account is not usable says so on the panel itself, in
   words as well as colour, with the way out beside it. */
.panel.session[data-has-issue="error"] { border-color: var(--danger); border-width: 2px; }
.panel.session[data-has-issue="warn"] { border-color: var(--warn); border-width: 2px; }
.issue {
  display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap;
  margin: 12px 0 4px; padding: 10px 12px; border-radius: 8px;
  border: 1px solid var(--danger); border-left-width: 5px;
  background: color-mix(in srgb, var(--danger) 8%, var(--panel));
}
.issue.warn {
  border-color: var(--warn);
  background: color-mix(in srgb, var(--warn) 9%, var(--panel));
}
.issuetext { flex: 1; min-width: 240px; }
.issue strong { display: block; color: var(--danger); font-size: 18px; line-height: 1.35; }
.issue.warn strong { color: var(--warn); }
.issue p { margin: 6px 0 0; font-size: var(--attention-font); line-height: 1.5; }
.issue .issuemeta { color: var(--muted); font-size: 14px; }
.issue details { border: none; padding: 0; margin-top: 4px; }
.issue details summary { font-size: 14px; font-weight: 500; color: var(--muted); }
.issue details code { display: block; white-space: pre-wrap; overflow-wrap: anywhere; margin-top: 4px; font-size: 13px; }
.issue .button.primary {
  font-size: var(--attention-font);
  background: var(--accent); border-color: var(--accent); color: #fff; align-self: center;
}
.healthok { color: var(--ok); font-size: 12px; margin: 8px 0 0; }
.panel.attention { border-color: var(--danger); display: flex; gap: 6px 8px; flex-wrap: wrap; align-items: baseline;
  font-size: var(--attention-font); }
.panel.attention strong { color: var(--danger); font-size: 18px; }
.panel.attention .muted { font-size: var(--attention-font); margin: 0; }
/* A dashboard card's status when its session needs looking at. */
.attentiontext { color: var(--warn); font-weight: 600; font-size: var(--attention-font); }

/* Each name placeholder beside what it stands for, one line apiece. Nothing in
   a row wraps: a row too narrow for its text trims the description with an
   ellipsis, and the row's title carries the whole thing. The token itself and
   the older code beside it never shrink. */
.tokens {
  display: grid; gap: 4px 18px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 380px), 1fr));
}
.tokenrow {
  display: flex; align-items: baseline; gap: 8px; min-width: 0;
  white-space: nowrap;
}
.tokenrow .token { flex: 0 0 auto; white-space: nowrap; }
.tokenrow .tokenmeaning {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  font-size: 12px; color: var(--muted);
}
.tokenrow .tokenalias { flex: 0 0 auto; font-size: 11px; color: var(--muted); opacity: .75; }

/* --- advanced: speed profile, scenarios, replay ---------------------------- */
details.advanced { margin: 14px 0; }
details.advanced > summary { font-size: var(--control-font); }
.stagerows { display: grid; gap: 6px; margin: 8px 0; }
.stagerow { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px; }
.stagerow input[type=number] { width: 100px; }
.scenarios { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr)); }
.scenario {
  display: flex; align-items: center; gap: 10px; justify-content: space-between;
  border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px;
}
.scenariotext { display: flex; flex-direction: column; min-width: 0; }
.scenariotext .muted { margin: 0; }
.scenario button { flex: 0 0 auto; }
.replayname { margin: 4px 0 8px; display: flex; gap: 8px; flex-wrap: wrap; align-items: baseline; }
.replayupload { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 8px; }
button.link.danger { color: var(--danger); }

/* --- the name each run gets in Google Fit ----------------------------------
   Part of the activity session section, set like any other field there - no
   border of its own - with the result in large type underneath. */
fieldset.fitname { border: none; padding: 0; margin: 12px 0 0; }
fieldset.fitname > legend { padding: 0; font-size: var(--control-font); font-weight: 400; margin-bottom: 3px; }
fieldset.fitname > .muted:first-of-type { margin-top: 0; }
fieldset.fitname input[type=text] { font-size: 16px; }
.fitpreview { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin: 8px 0 0; }
.fitpreview span { font-size: 13px; color: var(--muted); }
.fitpreview strong { font-size: 18px; overflow-wrap: anywhere; }
.fitpreview.live strong { color: var(--ok); }
details.placeholders { border-top: none; margin-top: 6px; padding-top: 0; }
details.placeholders > summary { font-size: var(--control-font); font-weight: 500; color: var(--accent); }
.fitline { margin: 2px 0 4px; font-size: 13px; color: var(--muted); }
.fitline strong { color: var(--ink); font-size: 14px; }
.fitline[data-live="1"] strong { color: var(--ok); }

/* --- the free trial and the Patreon pledge --------------------------------- */
.panel.patreon .actions { margin-bottom: 0; }
.panel.patreon.locked { border-color: var(--danger); border-width: 2px; }
.panel.patreon .lockedline strong { color: var(--danger); font-size: 15px; }

/* Phones: the mobile version is static/mobile.css, which base.html loads on
   small screens or when someone picks it. Until then the pieces made for it
   stay hidden. */
.mobileonly { display: none !important; }
.viewswitch { margin: 0 0 8px; }

/* Touch screens get the roomy controls at any width, because a finger is the
   same size on a wide phone held sideways as it is on a narrow one. */
@media (pointer: coarse) {
  button, .button { min-height: 40px; }
  input[type=checkbox], input[type=radio] { width: 20px; height: 20px; flex: 0 0 auto; }
  input[type=range] { height: 32px; }
  input[type=file] { min-height: 40px; padding: 8px 0; }
  .toggle, .chip, .choice { display: flex; align-items: center; gap: 8px; min-height: 40px; }
  summary { padding: 8px 0; }
}

/* --- public pages ---------------------------------------------------------------
   The landing page, the topic pages and the footer links a signed-out visitor
   and a search engine see. Reading pages, so roomier than the panel. */
a.button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.landinghero { text-align: center; padding: 36px 20px; }
.landinghero h1 { font-size: 30px; line-height: 1.2; margin-bottom: 10px; }
.landinghero .lead { max-width: 62ch; margin: 0 auto; color: var(--muted); font-size: 16px; line-height: 1.6; }
.landinghero .actions { justify-content: center; margin-top: 20px; }
.features { display: grid; gap: 12px; margin-top: 12px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.feature { border: 1px solid var(--line); border-radius: 9px; padding: 12px 14px; background: var(--bg); }
.feature h3 { font-size: 15px; margin: 0 0 4px; }
.feature p { margin: 0; line-height: 1.55; font-size: 14px; }
.prose { line-height: 1.6; }
.prose h2 { margin-top: 16px; }
.prose h2:first-child { margin-top: 0; }
.prose p { max-width: 80ch; }
.topicpage .lead { font-size: 16px; }
.steps { padding-left: 20px; max-width: 70ch; }
.steps li { margin: 6px 0; }
.faq h3 { font-size: 15px; margin: 16px 0 4px; }
.faq p { margin: 0; }
.crumbs { display: flex; gap: 6px; margin: 0 0 10px; }
.cta { text-align: center; }
.cta p { max-width: 62ch; margin-inline: auto; }
.cta .actions { justify-content: center; }
.footlinks { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 16px;
  max-width: 1280px; margin: 0 auto 8px; padding: 0 16px; }
.footlinks a { color: var(--muted); }
footer p { max-width: 72ch; margin-inline: auto; padding: 0 16px; }

/* --- the Server page in the admin portal -------------------------------------
   The server actions box, compact, then the settings cards, one per file. */
.serveractions .actionhead { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: baseline;
  justify-content: space-between; }
.runbadge { font-size: 13px; color: var(--muted); }
.runbadge.debug { color: var(--warn); font-weight: 600; }
.actionform { margin-top: 8px; }
.actionrow { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: flex-end; margin: 6px 0; }
.actionrow .field.compact { min-width: 150px; }
/* A confirm switch beside the Go button stands as tall as the button, its
   words on the button's middle line. */
.actionrow .toggle { margin: 0; display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--control-height); }
.actiondays { border: none; padding: 0; margin: 4px 0; display: flex; flex-wrap: wrap; gap: 4px 10px; }
.actiondays legend { font-size: var(--control-font); color: var(--ink); font-weight: 400; padding: 0; margin-bottom: 3px; }
.daychip { font-size: var(--control-font); display: inline-flex; gap: 6px; align-items: center; }
.actionhelp { margin-top: 6px; }
.actionhelp p { font-size: var(--control-font); margin: 6px 0; }
table.compacttable { margin-top: 10px; font-size: 13px; }
table.compacttable td, table.compacttable th { padding: 5px 6px; }
.serverconfig .grid { margin: 12px 0; }
.serverconfig .field small, .serverconfig .widefield small { display: block; margin-top: 3px; }
.serverconfig .widefield { grid-column: 1 / -1; display: block; }
.serverconfig code { font-size: 12px; }
.background { border-top: 1px solid var(--line); margin-top: 12px; padding-top: 10px; }
.background h2 { font-size: 15px; margin-bottom: 4px; }
.serverconfig .fieldgroup { grid-column: 1 / -1; font-size: 14px; margin: 8px 0 0; }

/* --- the settings pages: groups folded to one line --------------------------- */
details.serverconfig { padding: 0; }
details.serverconfig > summary { padding: 12px 16px; display: flex; flex-wrap: wrap; gap: 4px 12px;
  align-items: baseline; list-style-position: inside; }
details.serverconfig > summary .groupsummary { font-weight: 400; font-size: 13px; }
details.serverconfig[open] > summary { border-bottom: 1px solid var(--line); }
details.serverconfig > :not(summary) { margin-left: 16px; margin-right: 16px; }
details.serverconfig > form { margin-bottom: 14px; }
details.serverconfig.needsattention { border-color: var(--warn); }
.serverconfig .filepath { font-size: 12px; margin-top: 0; }
/* A full path has no spaces to wrap at, so let it break anywhere on a phone. */
.serverconfig .filepath code { overflow-wrap: anywhere; }
.compactgrid { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 8px 12px; }
.compactgrid .field > span:not(.helptip):not(.togglelabel) { font-size: var(--control-font); }
/* Hints show while a field is being filled in, and on hover as a title. */
.serverconfig .toggle.field { display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  font-size: var(--control-font); }
details.fittokens { border: none; padding: 0; margin: 8px 16px; }
details.fittokens > summary { font-size: 13px; }
.tokenlist { display: grid; grid-template-columns: max-content 1fr; gap: 2px 12px; margin: 6px 0;
  font-size: 13px; }
.tokenlist dd { margin: 0; color: var(--muted); }
.pagehead { margin-bottom: 12px; }
.startwith select { min-width: 140px; }
.groupsummary { display: inline-flex; flex-wrap: wrap; gap: 2px 0; }
.groupsummary .sumitem + .sumitem::before { content: "/"; margin: 0 8px; color: var(--line); }
