/* SPDX-License-Identifier: AGPL-3.0-only */
/* Shieldlist — the night-watch console. Two palettes on one set of
   tokens: dark by default (readable in full sun: bright ink, panels that
   stand off the ground), light when the system asks or the person
   chooses. Data in monospace as the CLI shows it; one accent (the logo's
   blue→violet), one ban colour, one colour for what is dangerous to the
   fleet (exempting). No library, no font download. */
:root {
  --ground: #0d141d;
  --ground-2: #121b26;
  --panel: #1a2634;
  --panel-2: #22303f;
  --panel-3: #2a3a4c;
  --line: rgba(255,255,255,.10);
  --line-2: rgba(255,255,255,.18);
  --ink: #f4f7fa;
  --ink-2: #d5dde6;
  --mute: #aab5c2;
  --act: #5c98ff;
  --act-2: #8b6cff;
  --act-ink: #ffffff;
  --ban: var(--cool); /* THE ban colour, everywhere a ban shows: the accent chosen under Settings → Ban colour (blue by default, red, or a hex) — the theme's --cool */
  --ok: #6fd39c;
  --warn: #f0c14b;
  --danger: #ff6b86;
  --red: #ff5252;
  --test: #c3ccd8; /* what a rule in test WOULD have done: grey, hatched, dashed — never mistaken for a real ban */
  --blue: #6d93d9; /* the optional ban colour, easier on the eye (Settings → Ban colour) */
  --cool: var(--red); /* the ban colour in force: red by default — the simplest to read for a newcomer (Robin, 2026-09-07) — or the blue, or a hex, set on :root by theme.js */
  --field: rgba(0,0,0,.28);
  --overlay: #1e2b3a;
  --shadow: 0 10px 34px rgba(0,0,0,.45);
  --glow: rgba(92,152,255,.28);
  --mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --r: 10px; --r-s: 7px; --r-l: 14px;
  --t: .16s ease;
  color-scheme: dark;
}
:root[data-theme="light"], :root.light-auto {
  --ground: #eef1f5;
  --ground-2: #e6eaf0;
  --panel: #ffffff;
  --panel-2: #f1f4f8;
  --panel-3: #e5eaf1;
  --line: rgba(20,30,45,.10);
  --line-2: rgba(20,30,45,.20);
  --ink: #121a24;
  --ink-2: #2f3b49;
  --mute: #5b6875;
  --act: #2b6fe6;
  --act-2: #6a4ce6;
  --act-ink: #ffffff;
  --ban: var(--cool);
  --ok: #1f9d5b;
  --warn: #b8860b;
  --danger: #d6335a;
  --red: #d61f1f;
  --test: #66727f;
  --blue: #3a67b3;
  --cool: var(--red);
  --field: #ffffff;
  --overlay: #ffffff;
  --shadow: 0 10px 34px rgba(20,30,45,.16);
  --glow: rgba(43,111,230,.2);
  color-scheme: light;
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; min-height: 100%; }
body { background: var(--ground); color: var(--ink); font: 14px/1.5 var(--sans); }
a { color: var(--act); text-decoration: none; transition: color var(--t); }
a:hover { color: var(--act-2); }
code, .mono, input, select, textarea { font-family: var(--mono); font-size: 13px; }
h1, h2, h3 { font-weight: 650; letter-spacing: -0.01em; margin: 0 0 .5rem; color: var(--ink); }
h1 { font-size: 21px; }
h2 { color: var(--ink-2); text-transform: uppercase; letter-spacing: .1em; font-size: 11.5px; margin-top: 1.6rem; font-weight: 700; }
h3 { font-size: 14.5px; }
p { margin: 0 0 .75rem; color: var(--ink-2); }
button, .btn { background: var(--panel-2); color: var(--ink); border: 1px solid var(--line); border-radius: var(--r-s); padding: .42rem .85rem; cursor: pointer; font: inherit; line-height: 1.3; transition: background var(--t), border-color var(--t), transform var(--t), box-shadow var(--t), color var(--t); }
button:hover, .btn:hover { background: var(--panel-3); border-color: var(--line-2); transform: translateY(-1px); color: var(--ink); }
button:active { transform: translateY(0); }
button.primary { background: linear-gradient(135deg, var(--act), var(--act-2)); color: #fff; font-weight: 600; border-color: transparent; box-shadow: 0 4px 14px var(--glow); }
button.primary:hover { filter: brightness(1.08); box-shadow: 0 6px 18px var(--glow); }
button.danger { color: var(--danger); background: color-mix(in srgb, var(--danger) 10%, transparent); border-color: color-mix(in srgb, var(--danger) 25%, transparent); }
button.danger:hover { background: color-mix(in srgb, var(--danger) 18%, transparent); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
button.danger.primary { background: linear-gradient(135deg, #ff5c7a, #ff7a45); color: #1a0710; }
button.small, .btn.small { padding: .24rem .6rem; font-size: 12px; }
button.link { background: none; border: none; color: var(--act); padding: 0; }
button.link:hover { text-decoration: underline; transform: none; background: none; }
button.ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
button.ghost:hover { background: var(--panel-2); color: var(--ink); }
button.ghost.on { background: var(--panel-2); color: var(--ink); border-color: var(--line); }
button:disabled { opacity: .5; cursor: default; transform: none; }
button:focus-visible, input:focus-visible, select:focus-visible, a:focus-visible, .addr:focus-visible { outline: 2px solid var(--act); outline-offset: 2px; }
input, select, textarea { background: var(--field); color: var(--ink); border: 1px solid var(--line-2); border-radius: var(--r-s); padding: .42rem .65rem; transition: border-color var(--t), box-shadow var(--t); }
input::placeholder { color: var(--mute); }
input:hover, select:hover { border-color: var(--mute); }
input:focus, select:focus { border-color: var(--act); box-shadow: 0 0 0 3px var(--glow); outline: none; }
input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--act); vertical-align: -2px; margin: 0 .35rem 0 0; }
select { padding-right: 1.6rem; }
label { color: var(--ink); font-size: 12px; display: block; margin-bottom: .2rem; }
label.check { display: flex; align-items: center; gap: .45rem; font-size: 13px; color: var(--ink); cursor: pointer; }
label.check input[type=checkbox] { flex: none; width: 15px; margin: 0; }
label.check .dim { margin-left: .2rem; }
.hint { color: var(--ink-2); font-size: 12px; margin: .15rem 0 0; }
[data-hint] { position: relative; }
[data-hint]::after { content: attr(data-hint); position: absolute; left: 50%; bottom: calc(100% + 8px); transform: translate(-50%, 0); background: var(--overlay); color: var(--ink); border: 1px solid var(--line-2); border-radius: var(--r-s); padding: .35rem .6rem; font-size: 12px; font-family: var(--sans); white-space: normal; width: max-content; max-width: 320px; line-height: 1.35; text-align: left; pointer-events: none; display: none; z-index: 40; box-shadow: var(--shadow); font-weight: 400; text-transform: none; letter-spacing: 0; animation: fade .12s ease-out; }
[data-hint]:hover::after { display: block; }
/* Inside a modal the list scrolls: a hint above the eye lands under the
   top bar and behind the scrollbar — put it to the LEFT of the eye. */
.modal .rrow .hide[data-hint]::after { left: auto; right: calc(100% + 8px); bottom: auto; top: 50%; transform: translate(0, -50%); z-index: 60; }
body { overflow-x: hidden; }

/* header */
.top { display: grid; grid-template-columns: auto 1fr auto; gap: 1rem; align-items: center; padding: .55rem 1.2rem; border-bottom: 1px solid var(--line); background: var(--panel); position: sticky; top: 0; z-index: 5; }
.brand { color: var(--ink); font-weight: 700; letter-spacing: .01em; white-space: nowrap; display: flex; align-items: center; gap: .55rem; font-size: 15.5px; }
.brand:hover { color: var(--ink); }
.brand img { display: block; transition: transform .3s ease; }
.brand .ver { color: var(--mute); font-family: var(--mono); font-weight: 400; font-size: 11.5px; margin-left: .2rem; }
.who .tag.warn { margin-right: .6rem; }
.livebtn .dot.tick { animation: tick .35s ease-out; }
@keyframes tick { 0% { transform: scale(1.8); box-shadow: 0 0 0 4px rgba(111,211,156,.5); } 100% { transform: scale(1); } }
.chart .foot .chips button { padding: .15rem .55rem; font-size: 11.5px; }
.chips button[role=checkbox] .sw { opacity: .45; }
.chips button[role=checkbox].on .sw { opacity: 1; }
.brand:hover img { transform: rotate(-8deg) scale(1.08); }
.railtab { position: fixed; left: 0; top: 50%; transform: translateY(-50%); z-index: 6; width: 18px; height: 56px; padding: 0; border-radius: 0 var(--r-s) var(--r-s) 0; background: var(--panel-2); border: 1px solid var(--line-2); border-left: none; color: var(--ink); font-size: 12px; display: grid; place-items: center; }
.railtab:hover { transform: translateY(-50%); background: var(--panel-3); }
.railtab[hidden] { display: none; }
.frame.with-rail .railtab { left: 250px; }
@media (max-width: 1100px) {
  .frame.with-rail .railtab { left: 0; }
  .frame.rail-over .railtab { left: min(300px, 85vw); z-index: 31; }
  /* A thumb's target, not a mouse's: wider, taller, a bigger arrow. */
  .railtab { width: 30px; height: 84px; font-size: 20px; border-radius: 0 var(--r) var(--r) 0; box-shadow: var(--shadow); }
  /* The film over the page while the panel is open: it takes the tap. */
  .frame.rail-over::after { content: ""; position: fixed; inset: 0; background: rgba(4, 8, 13, .5); z-index: 29; animation: fade .15s ease-out; }
}
.mark.big { display: block; margin: 0 auto .25rem; }
.mark.logo { width: 200px; height: auto; margin: -.5rem auto 0; }
.nav { display: flex; gap: .2rem; white-space: nowrap; }
.nav a { color: var(--ink-2); padding: .38rem .8rem; border-radius: 999px; transition: background var(--t), color var(--t); font-weight: 500; }
.nav a:hover { color: var(--ink); background: var(--panel-2); }
.nav a.on { color: #fff; background: linear-gradient(135deg, var(--act), var(--act-2)); }
.nav .badge { display: inline-block; min-width: 18px; padding: 0 5px; border-radius: 999px; background: var(--red); color: #fff; font-size: 11px; font-weight: 700; line-height: 18px; text-align: center; margin-left: .2rem; vertical-align: 1px; }
.nav a.on .badge { background: #fff; color: var(--act); }
/* .top scopes the header's .who: a bare .who selector also catches the
   TICKETS' .who cell and lays the address, network and city side by
   side (learned 2026-09-02 — it broke the feed for a night). */
.top .who { display: flex; align-items: center; gap: .55rem; white-space: nowrap; color: var(--ink-2); }
.top .who .pop { display: inline-flex; gap: .3rem; }
#live-btn, #refresh-btn { padding: .18rem .6rem .18rem .5rem; gap: .4rem; cursor: pointer; }
#refresh-btn { padding: .18rem .5rem; }
#refresh-btn .every { color: var(--ink-2); font-variant-numeric: tabular-nums; }
#refresh-btn .carat { color: var(--ink-2); font-size: 10px; }
#refresh-btn:hover .every, #refresh-btn:hover .carat { color: var(--ink); }
/* Hints in the top bar drop BELOW (above is off-screen) and hug the
   right edge so they never leave the viewport. */
.top .who [data-hint]::after { bottom: auto; top: calc(100% + 8px); left: auto; right: 0; transform: none; }
.who a.quiet { color: var(--ink-2); margin-left: .5rem; }
.who a.quiet:hover { color: var(--ink); }
/* The theme toggle, as the website's: one round button, the icon of the
   mode in force (auto = half disc, dark = moon, light = sun). */
.who button.theme { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--line); background: var(--panel-2); color: var(--ink-2); cursor: pointer; padding: 0; margin-left: .8rem; vertical-align: middle; }
.who button.theme:hover { color: var(--ink); border-color: var(--line-2); transform: none; }
.who button.theme svg { width: 17px; height: 17px; display: none; }
.who button.theme[data-mode="system"] .i-auto, .who button.theme[data-mode="dark"] .i-dark, .who button.theme[data-mode="light"] .i-light { display: block; }

/* frame: an optional rail beside the page */
.frame { display: grid; grid-template-columns: minmax(0, 1fr); min-height: calc(100vh - 52px); }
.frame.with-rail { grid-template-columns: 250px minmax(0, 1fr); }
.rail { border-right: 1px solid var(--line); background: var(--ground-2); padding: 1rem .8rem; position: sticky; top: 52px; height: calc(100vh - 52px); overflow-y: auto; }
.rail[hidden] { display: none; }
.rail h2 { margin: 0 .3rem .5rem; font-size: 11px; }
.rail h2:not(:first-child) { margin-top: 1.4rem; }
.rail-list a, .rail-list button { display: grid; grid-template-columns: 12px minmax(0, 1fr) auto auto; gap: .5rem; align-items: center; width: 100%; text-align: left; padding: .4rem .55rem; border-radius: var(--r-s); color: var(--ink); background: none; border: none; font: inherit; cursor: pointer; transition: background var(--t); }
.rail-list a:hover, .rail-list button:hover { background: var(--panel-2); transform: none; color: var(--ink); }
.rail-list .on { background: var(--panel-2); box-shadow: inset 2px 0 0 var(--act); }
.rail-list .name { font-family: var(--mono); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-list .n { font-family: var(--mono); font-size: 12px; color: var(--ink); }
.rail-list .n.ban { color: var(--ban); }
.rail-list .n.h { color: var(--warn); }
.rail .bar { height: 3px; background: var(--panel-3); border-radius: 2px; grid-column: 1 / -1; margin-top: -.2rem; overflow: hidden; display: flex; }
.rail .bar i { display: block; height: 100%; flex: none; }
.rail .bar i.ban { background: var(--ban); }
.rail .bar i.h { background: var(--warn); }
/* The episodes that ended inside the window: grey (yellow is what is
   being counted, green is exempt — Robin). The figure says it on hover. */
.rail .bar i.ended { background: var(--mute); }
.rail .bar i.x { background: var(--ok); }
/* The bright head riding an arc — one per departure, a stream when
   several: a light core, the arc's colour as its ring and glow. */
.world .archead { fill: #fff; stroke: var(--ban); stroke-width: .9; opacity: .95; filter: drop-shadow(0 0 2.5px var(--ban)); }
.world .archead.hit { stroke: var(--warn); filter: drop-shadow(0 0 2.5px var(--warn)); }
.world .archead.exempt { stroke: var(--ok); filter: drop-shadow(0 0 2.5px var(--ok)); }
.rail p.small { margin: .4rem .55rem 0; }
.rail.loading h2:first-child::after { content: ""; display: inline-block; width: 9px; height: 9px; margin-left: .5rem; border: 2px solid var(--line-2); border-top-color: var(--act); border-radius: 50%; animation: spin .8s linear infinite; vertical-align: -1px; }
.credits { color: var(--mute); font-size: 11.5px; margin: 1.5rem 0 0; padding: .8rem 1rem 1.2rem; text-align: center; }
.credits a { color: var(--ink-2); }
.creditlist { margin: 0; padding-left: 1.2rem; line-height: 1.7; }
.card .v.mono { font-family: var(--mono); font-size: 26px; letter-spacing: .02em; }
.ticket.watched .stripe { background: var(--warn); }
/* The meter says it all on two lines: count left, window right, the
   bar under them — the "X of Y hits" badge is gone. */
.ticket .meters { display: inline-flex; flex-wrap: wrap; justify-content: flex-end; gap: .25rem .6rem; align-items: center; }
.ticket .meter { display: inline-flex; flex-direction: row; align-items: center; gap: .35rem; vertical-align: middle; }
.ticket .meter .bar { width: 72px; flex: none; }
/* One line: the count, the bar, then the window at the bar's right —
   "3/20 [bar] 30m", never "3/2030m" glued (Robin, 2026-09-08). */
.ticket .meter .mt { display: contents; }
.ticket .meter b { order: 1; }
.ticket .meter .bar { order: 2; }
.ticket .meter small { order: 3; }
.ticket .meter .bar { display: block; height: 11px; border-radius: 5.5px; background: var(--panel-3); overflow: hidden; }
.ticket .meter .bar i { display: block; height: 100%; border-radius: 5.5px; background: linear-gradient(90deg, var(--warn), var(--ban)); background-size: 86px 100%; background-repeat: no-repeat; }
.ticket .meter b { font-size: 11.5px; font-weight: 600; color: var(--ink); font-family: var(--mono); line-height: 1.1; }
.ticket .meter small { font-size: 10.5px; color: var(--mute); font-family: var(--mono); line-height: 1.1; }

/* page */
.main:focus, .main:focus-visible { outline: none; }
.main { padding: 1.3rem 1.6rem 3rem; max-width: 1760px; margin: 0 auto; min-width: 0; width: 100%; }
/* The home page (it carries the workspace) fills a wide screen — its map,
   chart and feed use the room instead of sitting capped with empty sides.
   Other pages keep the readable cap. */
.main:has(.workspace) { max-width: none; }
.head { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; margin-bottom: .8rem; }
.head h1 { margin: 0; }
.head .grow { flex: 1; }
.crumbs { color: var(--mute); font-size: 13px; margin-bottom: .5rem; }
.crumbs a { color: var(--ink-2); }
.tabs { display: flex; gap: .2rem; margin: .6rem 0 1.1rem; overflow-x: auto; overflow-y: hidden; padding: .25rem; background: var(--panel); border: 1px solid var(--line); border-radius: 999px; width: max-content; max-width: 100%; }
.tabs a { color: var(--ink-2); padding: .38rem .9rem; border-radius: 999px; white-space: nowrap; transition: background var(--t), color var(--t); }
.tabs a.on { color: #fff; background: linear-gradient(135deg, var(--act), var(--act-2)); }
.tabs a:hover { color: var(--ink); }
.tabs .n { color: inherit; opacity: .75; font-family: var(--mono); font-size: 12px; margin-left: .3rem; }

/* blocks */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: .8rem; margin-bottom: 1.2rem; }
/* Compact cards: the home page's top — more figures, less height. */
.cards.compact { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: .5rem; margin-bottom: .8rem; }
.cards.compact .card { padding: .5rem .7rem .45rem; }
.cards.compact .card .k { font-size: 10px; }
.cards.compact .card .v { font-size: 20px; margin-top: .15rem; }
.cards.compact .card .s { font-size: 11px; margin-top: .1rem; line-height: 1.25; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* A card's hint opens BELOW it: the cards sit under the sticky top bar, and a hint above them would slide under it. */
.cards .card[data-hint]::after { bottom: auto; top: calc(100% + 8px); max-width: 260px; }
.cards.compact .card .v .ok { color: var(--ok); } .cards.compact .card .v .warn { color: var(--warn); } .cards.compact .card .v .ban { color: var(--ban); }
.cards.compact .card.warn .v { color: var(--warn); }
/* What the fleet is doing this minute, one line under the cards. */
.live { display: flex; flex-wrap: wrap; gap: .3rem 1.1rem; align-items: baseline; margin: -.5rem 0 .8rem; font-size: 12.5px; color: var(--ink-2); }
.live .k { color: var(--ink-2); font-size: 11px; text-transform: uppercase; letter-spacing: .1em; font-weight: 700; }
.live b { font-family: var(--mono); color: var(--ink); font-weight: 600; }
.live .warn b { color: var(--warn); }
/* The servers, one chip each, under the cards on the home page. */
.rail-list .rrow { display: flex; align-items: stretch; gap: 0; }
.rail-list .rrow > button:first-child { flex: 1; min-width: 0; }
.rail-list button.hide { display: inline-flex; align-items: center; justify-content: center; width: 1.7rem; flex: 0 0 1.7rem; padding: 0; background: none; border: 0; opacity: .45; grid-template-columns: none; }
.rail-list button.hide svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.rail-list button.hide:hover { opacity: 1; background: var(--panel-2); }
.rail-list button.pin { display: inline-flex; align-items: center; justify-content: center; width: 1.1rem; flex: 0 0 1.1rem; padding: 0; margin: 0; background: none; border: 0; opacity: .25; grid-template-columns: none; }
.rail-list button.pin svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.rail-list button.pin:hover { opacity: 1; background: var(--panel-2); }
.rail-list button.pin.on { opacity: 1; color: var(--warn); }
.rail-list button.pin.on svg { fill: currentColor; }
.strip { display: flex; flex-wrap: wrap; gap: .5rem; margin: -.4rem 0 1.2rem; }
.strip .chip { display: inline-flex; align-items: center; gap: .45rem; padding: .35rem .6rem; border: 1px solid var(--line-2); border-radius: var(--r-s); background: var(--panel); color: var(--ink); text-decoration: none; font-size: 12.5px; }
.strip .chip:hover { border-color: var(--ink-2); }
.strip .chip b { font-family: var(--mono); font-weight: 600; }
.strip .chip .n { font-family: var(--mono); color: var(--ink-2); }
.strip .chip .n.ban { color: var(--ban); font-weight: 600; }
.card { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); padding: .8rem 1rem; color: inherit; transition: transform var(--t), border-color var(--t), background var(--t); }
.card .k { color: var(--ink-2); font-size: 11px; text-transform: uppercase; letter-spacing: .1em; font-weight: 700; }
.card .k .date { float: right; text-transform: none; letter-spacing: 0; font-weight: 500; font-family: var(--mono); }
.card .v { font-family: var(--mono); font-size: 24px; margin-top: .25rem; letter-spacing: -.02em; }
.card .v.ban { color: var(--ban); } .card .v.ok { color: var(--ok); } .card .v.warn { color: var(--warn); }
.card .s { color: var(--ink-2); font-size: 12px; margin-top: .15rem; }
a.card:hover { transform: translateY(-2px); border-color: var(--line-2); background: var(--panel-2); color: inherit; box-shadow: var(--shadow); }
table { width: 100%; border-collapse: collapse; }
th { text-align: left; color: var(--ink-2); font-size: 11px; text-transform: uppercase; letter-spacing: .1em; font-weight: 700; padding: .55rem .8rem; border-bottom: 1px solid var(--line-2); white-space: nowrap; }
td { padding: .55rem .8rem; border-bottom: 1px solid var(--line); vertical-align: top; }
tr:last-child td { border-bottom: none; }
tbody tr { transition: background var(--t); }
tbody tr:hover td { background: var(--panel-2); }
td.mono { font-family: var(--mono); font-size: 13px; }
td.num, th.num { text-align: right; font-family: var(--mono); }
td.acts { white-space: nowrap; text-align: right; }
tr.clickable { cursor: pointer; }
.tbl { overflow-x: auto; -webkit-overflow-scrolling: touch; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); }
.tbl table { min-width: 680px; }
.tag { display: inline-block; white-space: nowrap; padding: .1rem .6rem; border-radius: 999px; font-size: 11.5px; font-family: var(--mono); background: var(--panel-3); color: var(--ink); }
/* A ban is red, everywhere (docs/CONSOLE-STYLE.md). */
.tag.ban { color: #fff; background: color-mix(in srgb, var(--ban) 78%, black); }
.tag.ban.dim { color: var(--ink); background: color-mix(in srgb, var(--ban) 22%, transparent); }
.tag.red { color: #fff; background: var(--red); }
.tag.warn { color: var(--warn); }
.tag.ok { color: var(--ok); background: color-mix(in srgb, var(--ok) 16%, transparent); }
.tag.warn { color: var(--warn); background: color-mix(in srgb, var(--warn) 16%, transparent); }
.tag.act { color: var(--act); background: color-mix(in srgb, var(--act) 16%, transparent); }
.tag.danger { color: var(--danger); background: color-mix(in srgb, var(--danger) 14%, transparent); }
a.tag:hover { background: var(--panel-2); color: var(--ink); }
:root[data-theme="light"] .tag.ban, :root.light-auto .tag.ban { background: var(--ban); }
.kv { display: grid; grid-template-columns: max-content 1fr; gap: .3rem 1.4rem; margin: 0 0 1rem; }
.kv dt { color: var(--mute); } .kv dd { margin: 0; font-family: var(--mono); font-size: 13px; }
.row { display: flex; gap: .75rem; align-items: end; flex-wrap: wrap; margin-bottom: 1rem; }
.row > * { min-width: 0; }
.row.tight { margin-bottom: .6rem; }
.field { display: flex; flex-direction: column; }
.field.grow { flex: 1; }
.panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); padding: 1.1rem 1.2rem; margin-bottom: 1rem; }
.panel h3 { margin-bottom: .6rem; }
.empty { color: var(--ink-2); padding: 2rem 1.5rem; text-align: center; border: 1px dashed var(--line-2); border-radius: var(--r); background: var(--panel); }
.err { color: var(--danger); white-space: pre-wrap; font-family: var(--mono); font-size: 12px; }
.small { font-size: 12px; color: var(--ink-2); }
.dim { color: var(--mute); }
.hero { margin: 0 0 1rem; }
.hero .v { font-family: var(--mono); font-size: 22px; }
.hero .v.ban { color: var(--ban); } .hero .v.ok { color: var(--ok); }
.rep b.ban { color: var(--ban); } .rep b.warn { color: var(--warn); } .rep b.ok { color: var(--ok); }
.rep .small { margin: 0 0 .9rem; }
pre.code { background: var(--field); border: 1px solid var(--line); border-radius: var(--r-s); padding: .75rem .9rem; overflow: auto; font-size: 12px; margin: 0 0 .75rem; color: var(--ink); }
pre.ev { background: var(--field); border: 1px solid var(--line); border-radius: var(--r-s); padding: .3rem .55rem; margin: 0; font-size: 11.5px; line-height: 1.4; white-space: pre-wrap; word-break: break-all; color: var(--ink); max-height: 9em; overflow: auto; resize: vertical; }
pre.ev .t { color: var(--ink-2); }
pre.ev.scrolls { overflow-y: scroll; scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
pre.ev.scrolls::-webkit-scrollbar { width: 8px; }
pre.ev.scrolls::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 4px; }
.pulse { display: inline-block; flex: none; justify-self: start; width: 8px; height: 8px; border-radius: 50%; background: var(--mute); vertical-align: middle; }
.pulse.live { background: var(--ok); box-shadow: 0 0 0 0 rgba(111,211,156,.6); animation: pulse 2.4s ease-out infinite; }
.pulse.stale { background: var(--warn); }
.pulse.off { background: var(--danger); }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(111,211,156,.5); } 70% { box-shadow: 0 0 0 7px rgba(111,211,156,0); } 100% { box-shadow: 0 0 0 0 rgba(111,211,156,0); } }

/* the world: countries filled by what they sent, under the current filters */
.world { position: relative; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); padding: .6rem .8rem .4rem; margin-bottom: 1rem; }
.world select { padding: .22rem 1.5rem .22rem .5rem; font-size: 12px; }
.world svg { width: 100%; height: auto; max-height: 400px; display: block; }
.world path { fill: var(--panel-3); stroke: var(--panel); stroke-width: .6; transition: fill var(--t); cursor: pointer; }
.world path.hot { stroke: var(--panel-2); }
.world path:hover { stroke: var(--ink); stroke-width: 1; }
.world path.on { stroke: var(--act); stroke-width: 1.5; }
.world g.tiny { cursor: pointer; }
.world g.tiny circle { stroke: none; }
.chips button.na { opacity: .45; text-decoration: line-through; }
.world .ripple { fill: none; stroke: var(--ban); stroke-width: 1.5; animation: ripple 1.6s ease-out forwards; pointer-events: none; }
.world .ripple.hit { stroke: var(--warn); }
.world .ripple.exempt { stroke: var(--ok); }
@keyframes ripple { 0% { r: 3; opacity: .9; } 100% { r: 30; opacity: 0; } }
/* The attack itself: a spot that stays for the tick (five seconds at
   most) and fades — the duration is set inline by the page. */
.world .spot { fill: var(--ban); stroke: none; opacity: .9; animation: spot 3s linear forwards; pointer-events: none; }
.world .spot.hit { fill: var(--warn); }
.world .spot.exempt { fill: var(--ok); }
@keyframes spot { 0% { opacity: .95; } 75% { opacity: .9; } 100% { opacity: 0; } }
.world .srv circle { fill: var(--act); stroke: #fff; stroke-width: .6; }
.world .srv .halo { fill: var(--act); stroke: none; opacity: .3; animation: halo 2.4s ease-out infinite; }
@keyframes halo { 0% { r: 2.8; opacity: .45; } 100% { r: 9; opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .world .ripple, .world .srv .halo { animation: none; } .world .ripple { display: none; } .world .spot { animation: none; opacity: .8; } }
.world .tip { position: absolute; pointer-events: none; background: var(--overlay); color: var(--ink); border: 1px solid var(--line-2); border-radius: var(--r-s); padding: .3rem .55rem; font-size: 12px; box-shadow: var(--shadow); white-space: nowrap; display: none; z-index: 3; }
.world .foot { display: flex; gap: 1rem; align-items: center; font-size: 11.5px; color: var(--ink-2); margin-top: .3rem; flex-wrap: wrap; }
.world .foot .grow { flex: 1; }
.world .scale { display: inline-flex; align-items: center; gap: .3rem; }
.world .scale i { display: inline-block; width: 28px; height: 8px; border-radius: 2px; }
.world .hide { position: absolute; right: .5rem; top: .4rem; }
.who .me { color: var(--ink); background: none; border: none; padding: 0; font: inherit; cursor: pointer; }
.who .me:hover { color: var(--act); transform: none; }

/* map + chart side by side on the home */
.ovctl { display: inline-flex; align-items: center; gap: .4rem; }
/* Wide-screen arrangements: the picker and the split
   only appear where there is room; a narrow screen keeps the rows. The
   .ovctl scope beats the base .chips display. */
.ovctl .laychips { display: none; margin-left: .1rem; }
@media (min-width: 1400px) { .ovctl .laychips { display: inline-flex; } }
/* The feed keeps its aligned columns while its own column is wide; in a
   narrow column each ticket becomes a dense wrapping row so a long tag
   never spills over the actions. Driven by the feed's width, not the
   viewport, so a wide split column stays aligned and a narrow one wraps. */
#feed { container-type: inline-size; }
/* A feed narrower than the aligned grid needs (the split column, a
   tablet): the COMPACT card, two lines, three with a target (Robin,
   2026-09-07). Line 1: the address, the network (cut short if long),
   then since and the meter at the right. Line 2: the rule, the server,
   the report, the tags. Line 3: the target. Time and flag at the left
   across the lines, the actions at the right; the log lines under. */
@container (min-width: 700px) and (max-width: 1479px) {
  .list .feed { display: block; }
  /* The same card, the meta line flowing instead of aligned: the address
     alone on top, then network, server over target, rule, facts, meter —
     the same order as the wide card, wrapping when it must. Nothing on
     the address line can be pushed; the address is never cut. */
  .list .ticket { display: grid; grid-template-columns: 4px auto auto minmax(0, 1fr) auto; grid-template-areas: "s when f band acts" "s ev ev ev ev"; gap: .1rem .6rem; align-items: center; padding: .3rem .7rem .3rem .8rem; }
  .list .ticket .when { padding-left: 0; min-width: 0; }
  /* The band: the address block first, then the meta in the wide order,
     filling the line beside the block and wrapping under it only when
     the line is full — no reserved line, no void. */
  .list .ticket .band { grid-area: band; display: flex; flex-wrap: wrap; align-items: center; gap: .2rem .7rem; min-width: 0; }
  .list .ticket .l2 { display: contents; }
  .list .ticket .who { flex: none; min-width: 180px; max-width: 100%; }
  .list .ticket .src { flex: 0 1 auto; min-width: 0; max-width: 360px; }
  .list .ticket .rulec { flex: none; max-width: 170px; }
  .list .ticket .what, .list .ticket .what > .facts { display: contents; }
  .list .ticket .prog { flex: none; padding-right: 0; justify-self: auto; }
  .list .ticket .acts { align-self: center; }
  .list .ticket .ev { margin-left: 0; }
}
@container (max-width: 699px) {
  .list .feed { display: block; }
  .list .ticket { display: flex; flex-wrap: wrap; align-items: center; gap: .15rem .55rem; grid-template-columns: none; position: relative; padding: .4rem .7rem .4rem 1rem; }
  .list .ticket .when { padding-left: 0; min-width: 0; }
  .list .ticket .src { flex-direction: row; flex-wrap: wrap; gap: .2rem .4rem; }
  .list .ticket .prog { justify-self: auto; padding-right: 0; }
  .list .ticket .acts { margin-left: auto; flex-wrap: wrap; justify-content: flex-end; row-gap: .25rem; }
  .list .ticket .ev { flex-basis: 100%; order: 10; margin-top: .15rem; }
}
.world { margin-bottom: 0; display: flex; flex-direction: column; }
.world svg { max-height: none; flex: 1; min-height: 0; }
.chart { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); padding: .6rem .8rem .4rem; display: flex; flex-direction: column; height: 100%; }
.chart .chead { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .3rem; }
.chart .plot { flex: 1; min-height: 160px; position: relative; display: flex; flex-direction: column; }
.chart .plot svg { width: 100%; flex: 1; min-height: 0; display: block; }
.chart .plot svg g:hover rect { filter: brightness(1.25); }
.chart .plot svg g { cursor: pointer; }
.chart .scale i.ln { height: 3px; }
.chart .ymax { position: absolute; left: 0; top: 0; z-index: 2; background: var(--overlay); color: var(--ink); border: 1px solid var(--line-2); border-radius: 999px; padding: .05rem .5rem; font-family: var(--mono); font-size: 11px; box-shadow: var(--shadow); }
.chart .xlab { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 11px; color: var(--ink-2); margin-top: .25rem; }
.chart .tip { position: absolute; pointer-events: none; background: var(--overlay); color: var(--ink); border: 1px solid var(--line-2); border-radius: var(--r-s); padding: .3rem .55rem; font-size: 12px; box-shadow: var(--shadow); white-space: nowrap; display: none; z-index: 3; }
.chart .yaxis { position: absolute; left: 4px; top: 0; bottom: 18px; width: 40px; pointer-events: none; z-index: 2; }
.chart .yaxis span { position: absolute; left: 0; transform: translateY(-50%); font-family: var(--mono); font-size: 10px; color: var(--ink-2); background: color-mix(in srgb, var(--panel) 80%, transparent); padding: 0 3px; border-radius: 3px; }
.chart svg line.grid { stroke: var(--line-2); stroke-width: 1; vector-effect: non-scaling-stroke; stroke-dasharray: 3 4; }
.rail-list .rrow.muted > button:first-child { opacity: .45; }
.rail-list .rrow.muted .hide { opacity: .9; }
.chart .foot { display: flex; gap: .6rem 1rem; align-items: center; font-size: 11.5px; color: var(--ink-2); margin-top: .3rem; flex-wrap: wrap; }
.chart .foot .grow { flex: 1; }
.chart .scale { display: inline-flex; align-items: center; gap: .3rem; }
.chart .scale i { display: inline-block; width: 12px; height: 8px; border-radius: 2px; margin-left: .3rem; }
.chart select { padding: .22rem 1.5rem .22rem .5rem; font-size: 12px; }

/* servers as cards */
.servers { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: .9rem; }
.server { display: block; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-l); padding: 1rem 1.1rem; color: inherit; transition: transform var(--t), border-color var(--t), background var(--t), box-shadow var(--t); }
.server:hover { transform: translateY(-2px); border-color: var(--line-2); box-shadow: var(--shadow); color: inherit; }
.server .name { font-family: var(--mono); font-size: 15px; font-weight: 600; display: flex; align-items: center; gap: .5rem; }
.server .meta { color: var(--ink-2); font-size: 12px; margin: .2rem 0 .7rem; }
.server .facts { display: flex; gap: .4rem .5rem; flex-wrap: wrap; align-items: center; }
.server .big { font-family: var(--mono); font-size: 22px; color: var(--ban); margin-left: auto; }
.server .big.zero { color: var(--ok); }

/* the sanction feed: its bar, then tickets */
.fbar { display: flex; flex-direction: column; gap: .55rem; margin-bottom: .9rem; }
.fline { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.fgroup { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }
.fgroup.display { color: var(--ink-2); font-size: 12px; gap: .35rem; flex: none; margin-left: auto; }
.fgroup.display select { padding: .22rem 1.5rem .22rem .5rem; font-size: 12px; }
/* One shape for every on/off toggle — log, arcs, Requests. */
button.tog { padding: .15rem .55rem; font-size: 11.5px; border-radius: var(--r-s); line-height: 1.3; }
button.tog.on { background: var(--act); color: #fff; border-color: var(--act); }
.fdiv { width: 1px; align-self: stretch; background: var(--line-2); margin: 0 .3rem; }
.search { position: relative; flex: 1 1 300px; max-width: 520px; }
.search input { width: 100%; padding: .5rem 2.4rem .5rem 2.2rem; font-size: 14px; border-radius: var(--r); }
.search .ico { position: absolute; left: .7rem; top: 50%; transform: translateY(-50%); color: var(--mute); pointer-events: none; font-size: 14px; }
.search .spin { position: absolute; right: .7rem; top: 50%; width: 14px; height: 14px; margin-top: -7px; border: 2px solid var(--line-2); border-top-color: var(--act); border-radius: 50%; opacity: 0; transition: opacity var(--t); }
.search.busy .spin { opacity: 1; animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.search .open { position: absolute; right: .5rem; top: 50%; transform: translateY(-50%); }
.search .open[data-banaddr] { right: 4.6rem; color: var(--red); border-color: color-mix(in srgb, var(--red) 45%, transparent); background: color-mix(in srgb, var(--red) 10%, transparent); }
.search.busy .open { display: none; }
/* A ✕ to clear a search or filter field (JS: clearable). Inside a .search
   bar it hangs from the bar; a bare field is wrapped in .clrwrap. */
.clrwrap { position: relative; display: inline-flex; align-items: center; }
.clrwrap > input { width: 100%; padding-right: 1.6rem; }
.clrx { position: absolute; top: 50%; right: .4rem; transform: translateY(-50%); display: inline-flex; align-items: center; justify-content: center; width: 1.15rem; height: 1.15rem; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--mute); font-size: 11px; line-height: 1; cursor: pointer; transition: color var(--t), background var(--t); }
.clrx[hidden] { display: none; }
/* Pin the transform through every state: the global button:hover lift
   (translateY(-1px)) would otherwise drop the ✕ out from under the
   cursor, flip hover off, and flicker. */
.clrx:hover, .clrx:active, .clrx:focus { transform: translateY(-50%); }
.clrx:hover { color: var(--fg); background: var(--line-2); }
.search .clrx { right: .55rem; z-index: 1; }
.search.addr .clrx { right: 8.5rem; }
.search.busy .clrx { display: none; }
.dates { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }
.dates[hidden] { display: none; }
.dates label { margin: 0; }
.dates input { padding: .3rem .5rem; font-size: 12.5px; }
.chips { display: inline-flex; gap: .15rem; padding: .18rem; background: var(--panel); border: 1px solid var(--line); border-radius: 999px; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.chips::-webkit-scrollbar { display: none; }
.chips button { border-radius: 999px; padding: .28rem .75rem; font-size: 12.5px; background: none; border-color: transparent; white-space: nowrap; flex: none; color: var(--ink); }
.chips button:hover { background: var(--panel-2); transform: none; color: var(--ink); }
.chips button.on { background: linear-gradient(135deg, var(--act), var(--act-2)); color: #fff; }
/* the states' colours, one signal beside the word: on the chip, on the card's stripe, on the state tag */
.chips button .sw { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: .4rem; vertical-align: 0; background: var(--mute); }
.chips button.c-ban .sw { background: var(--ban); }
.chips button.c-cool .sw { background: var(--cool); }
.chips button.on.c-cool { background: var(--cool); color: #fff; }
.chips button.c-rec .sw { background: repeating-linear-gradient(135deg, var(--test) 0 2px, transparent 2px 4px); }
.chips button.c-test .sw { background: var(--test); }
.chips button.c-exp .sw { background: var(--line-2); }
.chips button.c-act .sw { background: var(--act); }
.chips button.c-warn .sw { background: var(--warn); }
.chips button.c-ok .sw { background: var(--ok); }
.chips button.on.c-ok { background: color-mix(in srgb, var(--ok) 75%, black); }
.chips button.on.c-ban { background: color-mix(in srgb, var(--ban) 80%, black); }
.chips button.on.c-rec, .chips button.on.c-test { background: color-mix(in srgb, var(--test) 16%, var(--panel)); color: var(--ink); border-color: color-mix(in srgb, var(--test) 45%, var(--line)); }
.chips button.on.c-exp { background: var(--panel-3); }
.chips button.on.c-warn { background: color-mix(in srgb, var(--warn) 75%, black); }
.chips button.on .sw { background: #fff; }
.ticket.watched .tag.warn { color: #1a1200; background: var(--warn); }
.livegroup { display: inline-flex; align-items: center; gap: .4rem; margin-left: auto; }
.livegroup select { padding: .3rem .4rem; font-size: 12px; }
.livebtn { display: inline-flex; align-items: center; gap: .5rem; border-radius: 999px; padding: .32rem .9rem .32rem .6rem; background: var(--panel-2); font-size: 12.5px; border: 1px solid var(--line-2); }
.livebtn .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--mute); }
.livebtn.on { border-color: color-mix(in srgb, var(--ok) 50%, transparent); }
.livebtn.on .dot { background: var(--ok); }
.livebtn.on .dot.tick { animation: tick .45s ease-out; }
.livebtn .ic { font-size: 11px; width: 14px; text-align: center; }
.pop { position: relative; }
.pop-menu { position: absolute; right: 0; top: calc(100% + 6px); background: var(--overlay); border: 1px solid var(--line-2); border-radius: var(--r); padding: .6rem .8rem; display: grid; gap: .35rem; z-index: 15; box-shadow: var(--shadow); min-width: 200px; }
.pop-menu[hidden] { display: none; }
.pop-menu label.check { color: var(--ink); font-size: 13px; }
#refresh-btn { font-variant-numeric: tabular-nums; white-space: nowrap; }
.refreshmenu { min-width: 250px; }
.refreshmenu .chips { display: flex; gap: .3rem; flex-wrap: wrap; }
.refreshmenu b.small { color: var(--ink); }
.only-mobile { display: none; }
.more-filters { display: contents; }

/* One grid for the whole feed, each card a subgrid row: the columns line
   up across cards, and the left column takes what the widest network
   name needs (up to a bound) — the name shows when there is room. */
/* Every card shares the columns (subgrid): the when, the flag, the
   address block, WHERE (server + target), the rule, the free facts,
   the meter, the actions — so variable-width IPs, AS names and targets
   stop pushing each other around. */
/* THE GROUPS OF A CARD — the same eight, in the same order, in every
   layout (wide columns or compact flow); each is a unit that moves whole:
     when    time over date            never cut, never re-flowed
     flag
     who     address over network      the address never cut; the network
                                       cut to the address's width
     where   server, target            the target cut short
     rule                              cut short
     state   the badges and the report NEVER cut: a cut badge reads as two
                                       letters. Too long, it wraps inside
                                       its group onto a second line
     meter   the thresholds            several meters wrap, the column
                                       stays within its bound
     acts    the buttons
   Wide layout, the columns aligned across cards: every bounded column
   has a ceiling (fit-content), the state column a FLOOR (230px) — the
   ceilings' sum plus the floor is what a feed must offer to be wide
   (1480px); under that the compact flow takes over (container query
   below), so no column is ever starved. Never give the state column
   minmax(0, 1fr) again: the ceilings ate it whole (2026-09-08). */
.feed { display: grid; gap: .3rem; grid-template-columns: 4px auto 38px fit-content(300px) fit-content(280px) fit-content(170px) minmax(230px, 1fr) fit-content(200px) auto; }
/* A card: the flag on the left, whole height; the moment on top; under
   it the address over its network, then the facts; the actions on the
   right, always in the same place. */
/* content-visibility here BREAKS the subgrid: skipped cards stop
   feeding the fit-content columns and every track collapses (tried
   2026-09-02) — it would need fixed tracks first. */
.ticket { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; grid-template-areas: "s when f who src rulec what prog acts" "s ev ev ev ev ev ev ev ev"; gap: .1rem .7rem; align-items: center; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); padding: .22rem 1rem .25rem 0; overflow: visible; transition: transform var(--t), border-color var(--t), background var(--t), box-shadow var(--t); }
/* The server and, beside it, the target and the fleet notes — ONE line,
   the target cut short if long: a stacked pair made the meta line forty
   pixels tall and left a hole under the address (Robin, 2026-09-07). */
.ticket .src { grid-area: src; display: flex; flex-direction: row; gap: 4px; align-items: center; min-width: 0; }
.ticket .src .tag { max-width: 100%; overflow: hidden; text-overflow: ellipsis; flex: none; }
.ticket .src .srcx { display: flex; flex-wrap: nowrap; gap: 4px; min-width: 0; }
.ticket .src .srcx .tag { max-width: 200px; flex: 0 1 auto; }
.ticket .rulec { grid-area: rulec; min-width: 0; }
.ticket .rulec .tag { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
.ticket .prog { grid-area: prog; justify-self: end; padding-right: .45rem; }
/* A WIDE modal is broad enough for the page's own columns (1260px
   since 2026-09-02
   a NARROW modal folds the card the way the phone does (the overlap
   of 2026-09-02 — the free column collapsed, the meter sat on the
   facts). */
.modal:not(.wide) .feed { grid-template-columns: 4px auto 36px minmax(0, 1fr); }
.modal:not(.wide) .ticket { grid-template-areas: "s when acts acts" "s f who who" "s src src src" "s rulec prog prog" "s what what what" "s ev ev ev"; gap: .1rem .6rem; padding-right: .7rem; }
.modal:not(.wide) .ticket .src { flex-direction: row; flex-wrap: wrap; gap: .3rem; }
.modal:not(.wide) .ticket .prog { justify-self: start; }
/* Exemption is worn by the ADDRESS — a green frame, the reason on
   hover — and the meter counts in green: no badge to break the columns. */
.ticket .addr.exok { border: 1px solid color-mix(in srgb, var(--ok) 55%, transparent); background: color-mix(in srgb, var(--ok) 9%, transparent); border-radius: 6px; padding: 0 .3rem; }
.ticket.exempt .meter b { color: var(--ok); }
.ticket.exempt .meter .bar i { background: linear-gradient(90deg, color-mix(in srgb, var(--ok) 65%, transparent), var(--ok)); }
/* A folded target list unfolds in place on click. */
.ticket .tag.target.unfold { cursor: pointer; }
.ticket .tag.target.open { white-space: normal; overflow: visible; max-width: 320px; overflow-wrap: anywhere; }
.ticket:hover { border-color: var(--line-2); box-shadow: var(--shadow); transform: translateY(-1px); }
/* The coloured stripe: laid over the card's left edge in EVERY layout
   (absolute, out of the grid — in a grid lane it sat 14px inside the
   compact card and measured 0px wide in the aligned one, 2026-09-08).
   Ten pixels wide with the card's own radius so its corner IS the
   card's corner, clipped to four visible. */
.ticket { position: relative; }
.ticket .stripe { position: absolute; left: -1px; top: -1px; bottom: -1px; width: 10px; margin: 0; border-radius: var(--r) 0 0 var(--r); clip-path: inset(0 6px 0 0); background: var(--line-2); }
.ticket .fcol { grid-area: f; justify-self: center; align-self: center; }
/* The date is UNDER the time, always — every width, the split column,
   a phone, a modal. No rule anywhere sets .when to a row; the two are
   pinned here so no later rule can (the fifth report of the same bug). */
.ticket .when { grid-area: when; font-family: var(--mono); font-size: 11.5px; color: var(--ink-2); align-self: center; display: flex !important; flex-direction: column !important; flex-wrap: nowrap !important; align-items: flex-start; line-height: 1.25; padding-left: .9rem; }
.ticket .when .t { color: var(--ink); font-size: 14px; font-weight: 600; }
/* The card: the ADDRESS over its NETWORK, one block, nothing between
   them (the address never wrapped nor cut, the network cut short to the
   address column); beside it, in a fixed order, the server and target,
   the rule, the facts, the meter — aligned in columns when the feed is
   wide, flowing in the same order under the block when it is not. The
   .l2 wrapper is transparent here; the compact card makes it the line. */
.ticket .band, .ticket .l2 { display: contents; }
.ticket .who { grid-area: who; align-self: center; min-width: 180px; }
.ticket .what { grid-area: what; align-self: center; }
.ticket .acts { grid-area: acts; }
.ticket .ev { grid-area: ev; }
/* The card carries its state: a stripe on the left, and a faint tint of
   the same colour over the whole card — banned red, hits yellow, expired
   the ban's orange, detections grey — so a glance cannot mistake one for
   another. */
.ticket.active { background: color-mix(in srgb, var(--ban) 9%, var(--panel)); border-color: color-mix(in srgb, var(--ban) 40%, var(--line)); }
.ticket.watched { background: color-mix(in srgb, var(--warn) 11%, var(--panel)); border-color: color-mix(in srgb, var(--warn) 40%, var(--line)); }
/* An exempt card is GREEN — frame, tint and stripe — where the counted
   ones are yellow: the two read apart at a glance (
   one shared yellow made them hard to tell). */
.ticket.watched.exempt { background: color-mix(in srgb, var(--ok) 8%, var(--panel)); border-color: color-mix(in srgb, var(--ok) 40%, var(--line)); }
/* Banned elsewhere in the fleet, yet still hitting here — flagged, not
   hidden: a red edge so the anomaly stands out. */
.ticket.watched.bannedelse { border-color: color-mix(in srgb, var(--ban) 45%, var(--line)); }
.ticket.watched.exempt .stripe { background: var(--ok); }
/* Ended is grey; lifted keeps the operator's blue on its stripe. */
.ticket.expired, .ticket.lifted { background: color-mix(in srgb, var(--mute) 6%, var(--panel)); }
.ticket.active .stripe { background: var(--ban); }
.ticket.expired .stripe { background: var(--mute); }
.ticket.lifted .stripe { background: var(--act); }
.ticket .who { min-width: 0; }
.ticket .addr { font-family: var(--mono); font-size: 13.5px; font-weight: 600; color: var(--ink); cursor: pointer; transition: color var(--t); user-select: text; white-space: nowrap; }
/* The top bar's .who is nowrap; a ticket's must wrap, or a long IPv6
   pushes the page sideways on a phone. */
.ticket .who { white-space: nowrap; }
.ticket .what .tag { max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: bottom; }
/* The state group never cuts a badge: its facts wrap onto a second line instead (the column has a floor, see .feed). */
.ticket .what > .facts > .fact, .ticket .what > .fact { flex: 0 0 auto; }
.ticket .addr:hover { color: var(--act); }
.ticket .addr.plain { cursor: text; }
.modal-head .rdns { margin-left: .6rem; font-family: var(--mono); font-size: 12px; font-weight: 400; }
.modal-head .copy { background: none; border: none; color: var(--mute); font-size: 14px; padding: 0 .3rem; }
.modal-head .copy:hover { color: var(--act); transform: none; }
.rulebtn { cursor: pointer; }
button.tag.rulebtn { border: none; font-family: var(--mono); }
button.tag.rulebtn:hover { filter: brightness(1.15); transform: none; }
.ticket .copy { background: none; border: none; color: var(--mute); padding: 0 .3rem; font-size: 13px; opacity: 0; transition: opacity var(--t), color var(--t); vertical-align: 1px; }
.ticket:hover .copy { opacity: 1; }
.ticket .copy:hover { color: var(--act); transform: none; }
/* The Ban button reads on every tint — yellow hits, red bans: a
   neutral body like Exempt's, the red on the frame and the word
  . */
.ticket .acts button.ban, .modal-foot button.ban { color: var(--red); border-color: color-mix(in srgb, var(--red) 55%, transparent); background: var(--panel-2); }
.ticket .acts button.ban:hover, .modal-foot button.ban:hover { background: color-mix(in srgb, var(--red) 16%, var(--panel-2)); }
.orders { margin: 0 0 1rem; padding-left: 1.2rem; color: var(--ink-2); font-size: 13px; }
.modal-foot .acts { display: inline-flex; gap: .3rem; }
.modal-foot .acts button.lift, .ticket .acts button.lift { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, transparent); background: color-mix(in srgb, var(--ok) 10%, transparent); }
.modal-foot .acts button.lift:hover, .ticket .acts button.lift:hover { background: color-mix(in srgb, var(--ok) 20%, transparent); }
.modal-foot .acts button.exempt, .ticket .acts button.exempt { color: var(--ink); background: var(--panel-2); border-color: var(--line-2); }
.modal-foot .acts button.exempt:hover, .ticket .acts button.exempt:hover { background: var(--panel-3); }
.modal-foot .acts button:disabled, .ticket .acts button:disabled, .modal-foot .acts button:disabled:hover, .ticket .acts button:disabled:hover { color: var(--mute); background: transparent; border-color: var(--line); opacity: .6; transform: none; cursor: not-allowed; }
.ticket.rq.pending .stripe { background: var(--warn); } .ticket.rq.approved .stripe, .ticket.rq.done .stripe { background: var(--ok); } .ticket.rq.denied .stripe { background: var(--red); }
/* In test — a would-ban (.recorded) or a hit of a rule in test
   (.watched.test): the same sign everywhere, the chart's bars included.
   The card keeps the stage's colour and a SOLID border (a dashed one
   blurred the cards into the log lines), but its ground is finely
   striped in that colour — hatched, like the chart's would-ban bars —
   the side stripe hatched too, and a grey tag says it in words. */
.ticket.recorded { border-color: color-mix(in srgb, var(--ban) 40%, var(--line)); background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--ban) 11%, var(--panel)) 0 5px, color-mix(in srgb, var(--ban) 3%, var(--panel)) 5px 10px); }
.ticket.watched.test { border-color: color-mix(in srgb, var(--warn) 40%, var(--line)); background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--warn) 13%, var(--panel)) 0 5px, color-mix(in srgb, var(--warn) 4%, var(--panel)) 5px 10px); }
.ticket.recorded .stripe { background: repeating-linear-gradient(45deg, var(--ban) 0 4px, var(--test) 4px 8px); }
.ticket.watched.test .stripe { background: repeating-linear-gradient(45deg, var(--warn) 0 4px, var(--test) 4px 8px); }
/* Exempt keeps its green in test too — this rule must FOLLOW .test's
   (specificity order); the hatched stripe stays the test marker. */
.ticket.watched.exempt.test { border-color: color-mix(in srgb, var(--ok) 40%, var(--line)); background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--ok) 12%, var(--panel)) 0 5px, color-mix(in srgb, var(--ok) 3%, var(--panel)) 5px 10px); }
/* The overview line: the title, the regime toggle, the stages, the
   narrowing chips, then the window at the right. */
.ovline { margin-bottom: .45rem; gap: .4rem .6rem; }
.ovline h2 { margin-right: .2rem; }
.what { display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.what .chips button { padding: .22rem .7rem; }
.chips.seg { gap: 0; padding: 0; }
.chips.seg button { border-radius: 999px 0 0 999px; } .chips.seg button + button { border-radius: 0 999px 999px 0; }
.chips.seg button.on { background: var(--ink); color: var(--ground); border-color: var(--ink); }
/* The window: − [a wheel of windows, the middle one chosen] + Custom… */
.period-sel { display: inline-flex; align-items: center; gap: .25rem; }
.period-sel button { padding: .2rem .55rem; font-size: 13px; line-height: 1.2; }
.period-sel button.on { background: var(--act); color: #fff; border-color: var(--act); }
.wheel.dense { width: 150px; padding: .04rem 0; } .wheel.dense span[data-pk] { padding: .08rem .4rem; font-size: 11.5px; }
.wheel span.dis, .wheel span[hidden] { display: none; }
.wheel.drag { scroll-snap-type: none; cursor: grabbing; }
.wheel .pad { cursor: pointer; }
.stepwheel { display: inline-flex; }
.wheel { position: relative; display: flex; align-items: center; gap: .1rem; width: 190px; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; background: var(--panel-3); border: 1px solid var(--line); border-radius: 999px; padding: .12rem 0; mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent); -webkit-mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent); cursor: grab; }
.wheel::-webkit-scrollbar { display: none; }
.wheel .pad { flex: none; width: 50%; align-self: stretch; min-height: 1px; } /* a zero-height box adds no scrollable overflow in Firefox: the last values could not reach the middle */
.wheel span[data-pk] { flex: none; scroll-snap-align: center; padding: .18rem .55rem; font-size: 12px; color: var(--ink-2); white-space: nowrap; border-radius: 999px; user-select: none; }
.wheel span.on { background: #fff; color: #000; }
.what .chips, .chips.seg { overflow: visible; flex-wrap: wrap; }
.chart .foot .chips button.c-test.dashed .sw { background: repeating-linear-gradient(90deg, var(--test) 0 3px, transparent 3px 5px); }
.chart .foot .chips button.c-ok.dashed .sw { background: repeating-linear-gradient(90deg, var(--ok) 0 3px, transparent 3px 5px); }
.world.test path.hot { stroke: var(--line-2); }
/* The regime switches: Prod lit wears the ban accent the person chose
   (blue, red, theirs), Test the test grey — told apart at a glance. */
.chips.seg button.c-prod.on { background: var(--cool); border-color: var(--cool); color: #fff; }
.chips.seg button.c-test.on { background: var(--test); border-color: var(--test); color: var(--ground); }
@media (max-width: 900px) { .what { gap: .3rem .4rem; } .period-sel { width: 100%; justify-content: center; } .wheel { width: 52vw; } }
.tag.test { color: var(--test); background: color-mix(in srgb, var(--test) 14%, transparent); font-weight: 600; }
.ticket.watched.exempt.test .stripe { background: repeating-linear-gradient(45deg, var(--warn) 0 4px, var(--ok) 4px 8px); }
.checks { display: grid; gap: .3rem; }
.checks label.check.dim { color: var(--mute); }
.small.warn { color: var(--warn); }
.chips button[role=radio] { font-weight: 500; }
.ticket .flag { font-size: 22px; background: none; border: none; padding: 0; cursor: pointer; line-height: 1; transition: transform var(--t); }
.ticket .flag:hover { transform: scale(1.12); }
.ticket .flag.none { width: 22px; height: 17px; border-radius: 3px; background: var(--panel-3); cursor: default; }
.ticket .as { background: none; border: none; padding: 0; color: var(--ink-2); font: inherit; cursor: pointer; }
.ticket .as:hover, .ticket .flag:hover { color: var(--act); transform: none; }
.geochips { display: inline-flex; gap: .3rem; }
.chips.period { margin-left: .6rem; }
.chips.period button { padding: .2rem .65rem; font-size: 12px; }
/* The network is UNDER the address, always — every width, the compact
   card, a phone, a modal — as the date is under the time. Pinned. */
.ticket .who { display: block !important; }
/* width 0 + min-width 100%: the network is as wide as the address's
   column and never widens it — so it is what gets cut. */
.ticket .as { display: block !important; width: 0; min-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: left; font-size: 12px; margin-top: .05rem; }
/* The what column FILLS its track (stretch, not start): sized to its
   content it ran over the meter column — a "banned elsewhere" tag over
   the thresholds (Robin, 2026-09-07). Inside, the facts wrap or cut. */
.ticket .what { display: flex; gap: .4rem .75rem; flex-wrap: wrap; align-items: center; min-width: 0; font-size: 12.5px; color: var(--ink); justify-self: stretch; }
.ticket .what > .fact, .ticket .what > .facts > .fact { white-space: nowrap; min-width: 0; max-width: 100%; }
.ticket .what > .fact.dim { color: var(--ink-2); }
.ticket .what > .fact.dim::before, .ticket .what > .facts > .fact.dim::before { content: "·"; color: var(--mute); margin-right: .75rem; }
.ticket .what > .facts { display: contents; }
/* The actions group: the report on top, right-aligned, the three
   buttons under it — one place in every layout. */
.ticket .acts { display: flex; flex-direction: column; align-items: flex-end; gap: .15rem; opacity: .8; transition: opacity var(--t); }
.acts .btns { display: inline-flex; gap: .35rem; align-items: center; }
.ticket .acts .report { font-size: 12px; color: var(--ink-2); white-space: nowrap; }
/* A range is reported once per address that took part, so its report
   offers one link each. Closed it is exactly the label it replaced; open
   it drops the list OVER what is below, never pushing the card's layout
   about (2026-09-09). */
.abuselist { display: inline; position: relative; }
.abuselist > summary { display: inline; cursor: pointer; list-style: none; }
.abuselist > summary::-webkit-details-marker { display: none; }
.abuselist > div {
  position: absolute; right: 0; top: 1.4em; z-index: 30;
  display: flex; flex-direction: column; gap: .1rem;
  padding: .35rem .5rem; text-align: right;
  background: var(--panel-2); border: 1px solid var(--line-2); border-radius: var(--r);
  box-shadow: 0 6px 18px rgb(0 0 0 / .18);
}
.abuselist > div a { font-size: 11px; white-space: nowrap; }
.ticket:hover .acts { opacity: 1; }
.ticket .ev { margin: .15rem 0 0 .9rem; }
/* A new sanction flashes red OVER its own colours (an overlay that fades
   out), never by animating the ticket's background — that painted the
   state's red away and back again. */
/* A row arriving: it slides down from under the row above (the margin
   and the clip both start at its own height), fades in, and its slight
   zoom eases out; the rows below are pushed, not dropped. Timed by the
   feed (--ed, --es) from how many arrived at once. */
.ticket.enter { animation: enter var(--ed, 400ms) cubic-bezier(.22, .68, .2, 1) var(--es, 0ms) both; }
@keyframes enter {
  0% { margin-top: calc(-1 * var(--eh)); clip-path: inset(var(--eh) 0 0 0); opacity: 0; transform: scale(.985); }
  70% { margin-top: 0; clip-path: inset(0); opacity: 1; }
  100% { margin-top: 0; clip-path: inset(0); opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .ticket.enter { animation: none; } }
.ticket.fresh::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: var(--ban); pointer-events: none; animation: fresh 2.5s ease-out forwards; }
@keyframes fresh { 0% { opacity: .28; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .pulse.live, .livebtn.on .dot { animation: none; } .ticket.fresh::after { animation: none; opacity: 0; } * { transition: none !important; } }
.addr { font-family: var(--mono); color: var(--act); background: none; border: none; padding: 0; cursor: pointer; font-size: 13px; }
.addr:hover { text-decoration: underline; }

/* modals */
.backdrop { position: fixed; inset: 0; background: rgba(4,8,13,.66); backdrop-filter: blur(3px); display: grid; place-items: center; z-index: 20; padding: 1rem; overflow-y: auto; animation: fade .15s ease-out; }
@keyframes fade { from { opacity: 0; } }
.modal { background: var(--overlay); border: 1px solid var(--line-2); border-radius: var(--r-l); width: min(760px, 100%); max-height: calc(100vh - 2rem); overflow: auto; box-shadow: 0 30px 80px rgba(0,0,0,.6); animation: rise .18s ease-out; color: var(--ink); }
@keyframes rise { from { transform: translateY(10px) scale(.985); opacity: 0; } }
.modal.narrow { width: min(480px, 100%); }
.modal.wide { width: min(1260px, 100%); }
.modal-head { display: flex; align-items: center; gap: .75rem; padding: .95rem 1.2rem; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--overlay); z-index: 1; }
.modal-head h2 { margin: 0; text-transform: none; letter-spacing: 0; font-size: 16px; color: var(--ink); }
.modal-head .grow { flex: 1; }
.modal-head .x { background: none; border: none; color: var(--mute); font-size: 22px; line-height: 1; padding: 0 .2rem; }
.modal-head .x:hover { color: var(--ink); transform: none; background: none; }
.modal-body { padding: 1.1rem 1.2rem; }
.modal-body p:last-child { margin-bottom: 0; }
.modal-foot { display: flex; gap: .5rem; justify-content: flex-end; align-items: center; padding: .85rem 1.2rem; border-top: 1px solid var(--line); flex-wrap: wrap; }
.modal-foot .grow { flex: 1; }
.modal .danger-note { border-left: 3px solid var(--danger); padding: .55rem .8rem; background: color-mix(in srgb, var(--danger) 9%, transparent); border-radius: var(--r-s); color: var(--ink); margin-bottom: 1rem; }
.fields { display: grid; gap: .85rem; }
.fields input:not([type=checkbox]):not([type=radio]):not([type=search]), .fields select { width: 100%; }
.fields .two { display: grid; grid-template-columns: 1fr 1fr; gap: .85rem; }
.modal .feed { margin-top: .5rem; }
.modal .ticket { background: var(--panel); }

.toast { position: fixed; right: 1rem; bottom: 1rem; background: var(--overlay); color: var(--ink); border: 1px solid var(--line-2); border-left: 3px solid var(--act); padding: .65rem 1rem; border-radius: var(--r-s); opacity: 0; transform: translateY(8px); transition: opacity .2s, transform .2s; max-width: 480px; white-space: pre-wrap; z-index: 30; box-shadow: var(--shadow); }
.toast.show { opacity: 1; transform: none; }
.toast.bad { border-left-color: var(--danger); }
.login[hidden] { display: none; }
.login { position: fixed; inset: 0; background: radial-gradient(900px 500px at 50% 0%, #1a2740 0%, #0d141d 60%); display: grid; place-items: center; z-index: 10; }
.login-card [hidden] { display: none !important; }
.login-card #login-step1, .login-card #login-step2, .login-card #login-token-wrap { display: flex; flex-direction: column; gap: .75rem; }
.login-card { background: rgba(26,38,52,.92); color: #f2f5f8; border: 1px solid rgba(255,255,255,.18); border-radius: var(--r-l); padding: 2.2rem 2rem; width: min(400px, 92vw); display: flex; flex-direction: column; gap: .8rem; text-align: center; box-shadow: 0 10px 34px rgba(0,0,0,.45); }
.login-card input { font-size: 14px; text-align: left; background: rgba(0,0,0,.28); color: #f2f5f8; border-color: rgba(255,255,255,.18); }
.login-card p { text-align: left; color: #c6cfd9; }
.login-card p.err:empty { display: none; }
.login-card .small { text-align: center; }

@media (max-width: 1100px) {
  .frame.with-rail { grid-template-columns: minmax(0, 1fr); }
  .rail { display: none; }
  /* Opened from the tab: over the page, not beside it. */
  .frame.rail-over .rail:not([hidden]) { display: block; position: fixed; left: 0; top: 52px; bottom: 0; height: auto; width: min(300px, 85vw); z-index: 30; box-shadow: var(--shadow); border-right: 1px solid var(--line-2); }
}
@media (max-width: 900px) {
  .top { grid-template-columns: auto 1fr auto; gap: .4rem .6rem; padding: .5rem .75rem; position: static; }
  .railbtn { display: none; }
  .brand { grid-row: 1; grid-column: 1; }
  .who { grid-row: 1; grid-column: 3; }
  .nav { grid-row: 2; grid-column: 1 / -1; overflow-x: auto; gap: .1rem; padding-bottom: .1rem; scrollbar-width: none; }
  .nav::-webkit-scrollbar { display: none; }
  .main { padding: .9rem .75rem 2rem; }
  .cards { grid-template-columns: repeat(2, 1fr); gap: .5rem; }
  .card .v { font-size: 19px; }
  .row { gap: .5rem; }
  .row .field { flex: 1 1 140px; }
  .row input, .row select { width: 100%; }
  .kv { grid-template-columns: 1fr; gap: .1rem; }
  .kv dt { margin-top: .4rem; }
  .hero .v { font-size: 18px; }
  .toast { left: .75rem; right: .75rem; max-width: none; }
  .login-card { padding: 1.4rem; }
  .fields .two { grid-template-columns: 1fr; }
  .backdrop { padding: .5rem; align-items: start; }
  .modal { max-height: calc(100vh - 1rem); }
  /* Dense flow, never a stack: a technical tool must not waste screen.
     The feed drops its shared grid; each ticket is a wrapping flex row that
     fills each line before the next, the log lines alone on a full-width
     line, the actions pushed to the right. */
  .feed { display: block; }
  .ticket { display: flex; flex-wrap: wrap; align-items: center; gap: .15rem .55rem; grid-template-columns: none; position: relative; padding: .4rem .7rem .4rem 1rem; }
  /* The time keeps its date under it (a column), as on a wide screen —
     .list scoped, or the container rule of the narrow feed wins. */
  .ticket .when { gap: 0; padding-left: 0; min-width: 0; }
  .ticket .src { flex-direction: row; flex-wrap: wrap; gap: .2rem .4rem; }
  .ticket .prog { justify-self: auto; padding-right: 0; }
  .ticket .acts { margin-left: auto; flex-wrap: wrap; justify-content: flex-end; row-gap: .25rem; }
  .ticket .flag { font-size: 20px; }
  .ticket .who { min-width: 0; }
  .ticket .addr { font-size: 14px; }
  .ticket .copy { opacity: 1; }
  .ticket .ev { flex-basis: 100%; order: 10; margin-top: .15rem; }
  .search { flex-basis: 100%; max-width: none; }
  /* The display group (views, logs, fields, rows) took its full width
     and ran past the edge: it wraps inside the bar instead. */
  .fgroup.display { flex: 1 1 100%; min-width: 0; max-width: 100%; margin-left: 0; }
  .only-mobile { display: inline-block; }
  .more-filters { display: none; }
  .more-filters.open { display: flex; flex-wrap: wrap; gap: .4rem; width: 100%; }
  .livebtn { margin-left: 0; }
  [data-hint]::after { display: none; }
}
@media (max-width: 480px) { h1 { font-size: 18px; } .cards { grid-template-columns: 1fr 1fr; } }

/* ---- the rule language: editors, packs, previews ---- */
.fields textarea { width: 100%; resize: vertical; min-height: 4.5rem; }
.mrow { border: 1px solid var(--line); border-radius: var(--r-s); padding: .5rem .6rem; margin-bottom: .45rem; background: var(--panel); }
.mrow-head { display: flex; gap: .5rem; align-items: center; }
.mrow-head select { flex: 1; min-width: 0; }
.mrow-head label.w { display: flex; align-items: center; gap: .35rem; font-size: 12px; color: var(--ink-2); white-space: nowrap; }
.mrow-head label.w input { width: 4.2rem; }
.mrow-inline { display: grid; gap: .45rem; margin-top: .5rem; }
.mrow-inline input { width: 100%; }
.mrow-inline .two { display: grid; grid-template-columns: 1fr 1fr; gap: .45rem; }
.mrow-inline .hint { margin: 0; }
.lvls { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.lvls input { width: 100%; }
.chips.packs { margin: 0 0 .9rem; flex-wrap: wrap; }
.chips.packs button .n { opacity: .7; font-family: var(--mono); font-size: 11px; margin-left: .25rem; }
.chips.packs button .srv { display: block; font-size: 10.5px; opacity: .65; margin-top: .05rem; }
.packpanel { display: flex; align-items: center; gap: .75rem; margin-bottom: .9rem; }
.packpanel .grow { flex: 1; }
pre.code.diff { white-space: pre; line-height: 1.45; }
pre.code.diff .dadd { color: var(--ok); }
pre.code.diff .ddel { color: var(--danger); }
pre.code.diff .dsame { color: var(--ink-2); }
.warn-text { color: var(--warn); }
mark { background: color-mix(in srgb, var(--warn) 45%, transparent); color: inherit; border-radius: 2px; padding: 0 1px; }
details > summary { cursor: pointer; color: var(--ink-2); font-size: 13px; }
@media (max-width: 640px) { .lvls, .mrow-inline .two { grid-template-columns: 1fr; } .packpanel { flex-wrap: wrap; } }
.lvl-list { display: grid; grid-template-columns: auto 1fr; gap: .05rem .5rem; align-items: baseline; }
.lvl-list .small { color: var(--ink-2); }
/* ---- the rule language, second pass: five blocks, packs grid, files ---- */
textarea.file { width: 100%; font-family: var(--mono); font-size: 12.5px; line-height: 1.45; resize: vertical; white-space: pre; overflow: auto; }
.ok-text { color: var(--ok); }
.mrow-head input[name=m-parser] { flex: 1; min-width: 0; }
.mrow-or { white-space: nowrap; font-size: 12px; }
.throw { display: flex; gap: .4rem; align-items: center; margin-bottom: .35rem; flex-wrap: wrap; }
.throw input[type=number] { width: 5rem; }
.checks.cols { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: .15rem .8rem; }
.checks.cols .catgroup { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: .15rem .8rem; margin-top: .4rem; }
.checks.cols .catgroup > .small { grid-column: 1 / -1; margin-top: .3rem; text-transform: uppercase; letter-spacing: .04em; font-size: 10.5px; }
#packs th.srvcol, #packs td.srvcol { text-align: center; }
#packs .check.cell { justify-content: center; }
.packrow { display: flex; align-items: center; gap: .6rem; padding: .35rem 0; border-top: 1px solid var(--line); flex-wrap: wrap; }
.packrow:first-child { border-top: none; }
.packrow select { font-size: 12px; padding: .25rem .4rem; }
.logrow { display: flex; gap: .4rem; margin-bottom: .4rem; }
.logrow input[name=l-path] { flex: 1; min-width: 0; }
.logrow.head { color: var(--ink-2); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; font-weight: 700; margin-bottom: .2rem; }
.logrow.head span:first-child { flex: 1; min-width: 0; }
.logrow.head span:nth-child(2), .logrow.head span:nth-child(3) { width: 9rem; flex: 0 0 9rem; }
.logrow.head span:last-child { width: 2rem; flex: 0 0 2rem; }
tr.dim td { opacity: .6; }
@media (max-width: 640px) { .logrow { flex-wrap: wrap; } .packrow select { max-width: 100%; } }
.fields .throw select, .fields .throw input { width: auto; }
.fields .throw select { min-width: 7rem; }

/* A suspect no longer counted: its hits slid out of the window. */
.ticket.gone { opacity: .72; }
.ticket.gone .stripe { background: var(--line-2); }

/* The server and, under it, what the hit was aimed at. */
/* The column never outgrows its row: a long target (a domain, a URL a
   parser named by mistake) is cut with an ellipsis, the hint says it whole. */
.ticket .fact.col { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 2px; max-width: 100%; }
.ticket .fact.col > .tag { max-width: 100%; }
/* A target is a SITE, not a server: no pill — a dotted underline apart
   from the server chip above it. */
.ticket .tag.target { color: var(--ink-2); font-weight: 500; background: none; border: none; padding: 0 .2rem; text-decoration: underline dotted color-mix(in srgb, var(--ink-2) 55%, transparent); text-underline-offset: 3px; }

/* The network view: a window to choose, the prefixes that hit with their
   share of the hits drawn as a bar. */
.winrow { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; margin-bottom: .6rem; }
table.prefixes td.warn { color: var(--warn); font-weight: 600; }
.rail .railsort { margin: 0 0 .3rem; text-align: right; }
/* Bans by hand: a meta line under the rules, never in their ranking. */
.rail-list .rrow.meta { border-top: 1px dashed var(--line); margin-top: .2rem; padding-top: .2rem; }
/* The all… dialog's checkboxes: multi-selection without a keyboard. */
.rail-list .rrow input.ck { flex: 0 0 auto; align-self: center; margin: 0 .1rem 0 .2rem; accent-color: var(--act); width: 15px; height: 15px; }
/* Sortable list columns. */
th[data-sort] { cursor: pointer; user-select: none; white-space: nowrap; }
th[data-sort]:hover { color: var(--ink); }
table.prefixes td.r, table.prefixes th.r { text-align: right; font-variant-numeric: tabular-nums; }
table.prefixes td.share { white-space: nowrap; }
table.prefixes td.share i { display: inline-block; height: 8px; max-width: 60px; border-radius: 4px; background: linear-gradient(90deg, var(--warn), var(--ban)); vertical-align: middle; margin-right: .45rem; }
table.prefixes td.acts { text-align: right; white-space: nowrap; }
table.prefixes td.ban { color: var(--ban); font-weight: 600; }

/* The hour, a touch larger than the other figures. */
.cards.compact .card .v #clock { font-size: 21px; letter-spacing: -.02em; }

/* The lines and their copy button, top right, shown on hover. */
.evwrap { position: relative; }
.evwrap .evcopy { position: absolute; right: .4rem; top: .25rem; font-size: 11px; opacity: 0; padding: .1rem .4rem; border-radius: var(--r-s); background: var(--overlay); border: 1px solid var(--line-2); }
.evwrap:hover .evcopy, .ticket:hover .evwrap .evcopy { opacity: .85; }
.evwrap .evcopy:hover { opacity: 1; }

/* Vitals: a two-column reading, the label narrow, the figure wide. */
.kv-wide table.kv { border-collapse: collapse; margin: .3rem 0 1rem; width: 100%; }
.kv-wide table.kv th { text-align: left; font-weight: 600; color: var(--ink-2); padding: .35rem .9rem .35rem 0; white-space: nowrap; width: 1%; vertical-align: top; font-size: 12.5px; }
.kv-wide table.kv td { padding: .35rem 0; border-bottom: 1px solid var(--line); font-size: 13px; }
.kv-wide table.kv td b { font-size: 15px; }

/* lists: the scope picker (nowhere / the fleet / these servers) */
.hint { color: var(--mute); font-size: 12.5px; margin: -.3rem 0 .35rem; }
.scope { display: flex; flex-direction: column; gap: .3rem; }
.scope label { display: flex; align-items: center; gap: .4rem; line-height: 1.35; }
.scope .servers { margin-left: 1.5rem; }
.scope .servers.dim, .scope .picks.dim { opacity: .45; }
.scope .picks { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: .15rem .8rem; max-height: 12rem; overflow: auto; padding: .3rem 0; }
.scope .filter { width: 14rem; padding: .25rem .5rem; font-size: 12.5px; margin-top: .2rem; }
.scope .every { margin-bottom: .25rem; }
.acts-top { display: flex; align-items: center; gap: .8rem; margin: 0 0 .9rem; padding-bottom: .8rem; border-bottom: 1px solid var(--line); }

/* ⓘ — the page stays quiet; hover, focus or click tells. */
.info { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 50%; border: 1px solid var(--line-2); background: var(--panel); color: var(--ink-2); font: 700 10px/1 var(--sans); padding: 0; margin-left: .3rem; vertical-align: middle; cursor: help; }
.info:hover, .info.open { color: var(--ink); border-color: var(--act); }
.info[data-hint]::after { left: 0; transform: none; bottom: auto; top: calc(100% + 6px); }
.info[data-hint].open::after, [data-hint]:focus-visible::after { display: block; }
h1 .info, h2 .info { position: relative; top: -2px; }
label .info { top: 0; }
.card .v { overflow-wrap: anywhere; min-width: 0; }
.datarow { margin: 1.4rem 0 0; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
/* The pickers: a network or a country from what is typed. */
.pick { position: relative; }
.pick input:not([type=hidden]) { width: 100%; }
.suggest { position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 30; background: var(--overlay); border: 1px solid var(--line-2); border-radius: var(--r); box-shadow: var(--shadow); max-height: 16rem; overflow: auto; display: grid; }
.suggest[hidden] { display: none; }
.suggest button { text-align: left; background: none; border: 0; border-radius: 0; padding: .4rem .7rem; font: inherit; color: var(--ink); display: flex; gap: .5rem; align-items: baseline; width: 100%; }
.suggest button:hover, .suggest button:focus { background: var(--panel-3); outline: none; }
.suggest button .grow { flex: 1; }
.fields .row.tight > select[name=kind] { width: auto; flex: 0 0 auto; }
.fields .row.tight > .pick { flex: 1 1 320px; min-width: 260px; }
.suggest { width: max-content; min-width: 100%; max-width: min(640px, 90vw); }
.suggest button { white-space: nowrap; }
.suggest button .grow { overflow: hidden; text-overflow: ellipsis; }
.scope .packs { margin-left: 1.5rem; }
.scope .packs.dim { opacity: .45; }
.suggest .none { padding: .5rem .7rem; color: var(--ink-2); font-size: 12.5px; }
.pop-menu.views { min-width: 260px; }
.pop-menu .vrow { display: flex; align-items: center; gap: .3rem; }
.pop-menu .vrow .link { flex: 1; text-align: left; }
.pop-menu .vrow .x { padding: 0 .4rem; }
.feedsrc input[name=url] { width: 100%; }
.feedsrc .row.tight label.small { display: inline-flex; align-items: center; gap: .4rem; }
.toast.sticky { bottom: 4.2rem; opacity: 1; transform: none; display: flex; align-items: center; gap: .7rem; border-left-color: var(--warn); }
/* The scope picker's server filter is one line, above the grid. */
.scope .servers { display: block; }
.scope .servers .filter { display: block; width: 100%; max-width: 360px; height: auto; margin: 0 0 .35rem; padding: .3rem .55rem; font-size: 13px; }
/* The top bar: one line of small cards, the meaning on hover. */
.cards.compact { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: .7rem; }
.cards.compact .card { flex: 0 1 auto; min-width: 0; padding: .35rem .6rem .3rem; }
.cards.compact .card .k { font-size: 9.5px; letter-spacing: .08em; white-space: nowrap; }
.cards.compact .card .v { font-size: 17px; margin-top: .1rem; white-space: nowrap; }
.cards.compact .card .v #clock { font-size: 17px; }
.cards.compact .card[data-hint]::after { left: 0; transform: none; top: calc(100% + 6px); bottom: auto; }
/* The top bar's tips: every card the same — no hover lift (a transformed
   card is its own stacking context and its tip went under the next
   card), the tip below, above everything. */
.cards.compact .card, .cards.compact .card:hover, .cards.compact a.card:hover { transform: none; }
.cards.compact .card:hover { z-index: 50; }
.cards.compact .card[data-hint]::after { left: 0; right: auto; transform: none; top: calc(100% + 6px); bottom: auto; max-width: 320px; z-index: 60; }
.chart .tip { white-space: nowrap; text-align: left; line-height: 1.45; }
.chart .tip .c-test { color: var(--test); font-weight: 600; } .chart .tip .c-ok { color: var(--ok); font-weight: 600; } .chart .tip .c-ban { color: var(--ban); font-weight: 600; } .chart .tip .c-det { color: var(--ink-2); font-weight: 600; } .chart .tip .c-hit { color: var(--warn); font-weight: 600; }
/* Arcs: attack → server, drawn along their length then gone. */
/* The trail draws LINEARLY over the same 1.4 s as the head's run (31% of
   4.5 s), so the head rides the drawing tip — eased, the tip sprinted
   ahead and the head trailed it. */
/* The rays: a BAN is a true red, a HIT is yellow. */
.world .arc { fill: none; stroke: var(--ban); stroke-width: 1.8; stroke-linecap: round; opacity: .85; stroke-dasharray: 1; stroke-dashoffset: 1; animation: arc 4.5s linear forwards; pointer-events: none; filter: drop-shadow(0 0 1.5px color-mix(in srgb, var(--ban) 60%, transparent)); }
.world .arc.hit { stroke: var(--warn); filter: drop-shadow(0 0 1.5px color-mix(in srgb, var(--warn) 60%, transparent)); }
.world .arc.exempt { stroke: var(--ok); filter: drop-shadow(0 0 1.5px color-mix(in srgb, var(--ok) 60%, transparent)); }
@keyframes arc { 0% { stroke-dashoffset: 1; opacity: .95; } 31% { stroke-dashoffset: 0; opacity: .95; } 65% { stroke-dashoffset: 0; opacity: .8; } 100% { stroke-dashoffset: 0; opacity: 0; } }
/* Full screen: the map fills the screen, the svg scales, the chrome stays. */
.world:fullscreen { background: var(--ground); padding: 1.2rem 1.6rem; display: flex; flex-direction: column; }
.world:fullscreen svg { flex: 1; width: 100%; height: auto; max-height: calc(100vh - 120px); }
.world .fs { margin-right: .5rem; }
.world .foot .arcs { margin-left: 1rem; white-space: nowrap; display: inline-flex; align-items: center; gap: .3rem; }
@media (prefers-reduced-motion: reduce) { .world .arc { display: none; } }
.modal-head .origin { margin-left: .6rem; font-size: 12px; font-weight: 400; color: var(--ink-2); font-family: var(--sans); }
/* The overview: the map and the chart, no titles, the same foot row.
   Rows (the default): ONE band of height --ov-h under the cards. The map
   keeps its aspect ratio, so its width follows the band's height; the
   chart takes every pixel left — on an ultrawide screen a very wide
   timeline, never a capped box with a void beside it. "Large view" is a
   taller band: the map grows with it, the chart too. Under 1100px the
   two stack, each full width.
   The band is bounded by the overview's own WIDTH as well (container
   units — the rail takes its share of the viewport), so the map never
   crowds the chart on a screen that is tall but narrow: the map takes at
   most ~46 % of the width, ~65 % in the large view. */
.overview { --ov-h: min(clamp(240px, 30vh, 400px), 24cqw); container-type: inline-size; display: flex; gap: .6rem; margin-bottom: .6rem; align-items: stretch; }
.workspace[data-h="l"] .overview { --ov-h: min(clamp(360px, 55vh, 760px), 34cqw); }
.overview[hidden], .overview #world[hidden], .overview #chart[hidden] { display: none; }
.overview #world { flex: 0 0 auto; min-width: 0; }
.overview #chart { flex: 1 1 0; min-width: 0; }
.overview .world { height: var(--ov-h); width: auto; max-width: 100%; aspect-ratio: 1000 / 520; padding: .45rem .7rem .35rem; }
.overview .world svg { flex: 1 1 0; min-height: 0; width: 100%; height: 100%; max-height: none; }
.overview .chart { height: var(--ov-h); padding: .45rem .7rem .35rem; }
@media (max-width: 1100px) {
  .overview { flex-direction: column; }
  .overview .world { height: auto; width: 100%; }
  .overview .chart { height: clamp(220px, 30vh, 360px); }
  /* Stacked, the map is as wide as the page and "Large view" would
     change nothing: gone. The foot stays ONE line, the full-screen
     button at the map's bottom right (the legend shrinks first). */
  .world .foot [data-bigger] { display: none; }
  .world .foot { flex-wrap: nowrap; gap: .3rem .5rem; }
  .world .foot .scale { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; }
  .world .foot .scale i { width: 18px; }
  .world .foot .fs { margin-right: 0; }
}
@media (max-width: 1400px) { .ovline #livebox { margin-left: auto; } .ovline .livegroup { margin-left: 0; } }
/* Split (a wide screen): the map and the chart pinned in a left column
   that fills the viewport height, the feed beside. The map takes the
   column's width, the chart the height left. The column is bounded by the
   viewport's height (the map's share of it, through its ratio) so the
   chart always keeps a readable band; "Large view" is a wider column. */
.workspace[data-wide="split"] { --ov-col: min(40%, calc((100vh - 84px) * .55 * 1000 / 520)); display: grid; grid-template-columns: var(--ov-col) minmax(0, 1fr); gap: 1rem; align-items: start; }
.workspace[data-wide="split"][data-h="l"] { --ov-col: min(56%, calc((100vh - 84px) * .66 * 1000 / 520)); }
.workspace[data-wide="split"] .overview { position: sticky; top: 64px; flex-direction: column; height: calc(100vh - 84px); margin-bottom: 0; }
.workspace[data-wide="split"] .overview .world { height: auto; width: 100%; }
.workspace[data-wide="split"] .overview #chart { min-height: 0; }
.workspace[data-wide="split"] .overview .chart { height: 100%; }
.workspace[data-wide="split"] #feed { min-width: 0; }
/* The top row: the cards, and the page's clock at the right. */
.toprow { display: flex; gap: .8rem; align-items: flex-start; margin-bottom: .5rem; }
.toprow #top { flex: 1; min-width: 0; }
.toprow .cards.compact { margin-bottom: 0; }
.toprow #livebox { flex: none; padding-top: .3rem; }
@media (max-width: 900px) { .toprow { flex-wrap: wrap; } .toprow #livebox { width: 100%; padding-top: 0; } .toprow .livegroup { width: 100%; justify-content: flex-end; } }
.world .foot, .chart .foot { margin-top: .2rem; min-height: 26px; gap: .4rem .7rem; }
.world .foot .credit, .world .foot .fs { padding: .15rem .55rem; font-size: 11.5px; }
.world .foot .credit[data-hint]::after, .world .foot .fs[data-hint]::after, .world .foot .tog[data-hint]::after { left: auto; right: 0; transform: none; }
.cards.compact { margin-bottom: .5rem; }
/* Stats */
.statgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: .7rem; }
.statgrid.loading { opacity: .6; }
.panel.stat .tools { display: flex; align-items: center; gap: .4rem; margin-bottom: .3rem; } .panel.stat .tools h3 { margin: 0; }
.panel.stat input.filter { max-width: 150px; padding: .2rem .5rem; font-size: 12px; }
.panel.stat .tbl { max-height: 60vh; overflow: auto; overflow-x: hidden; border: none; border-radius: 0; } .panel.stat .tbl table { min-width: 0; }
.panel.stat td.bar i { background: linear-gradient(90deg, var(--act), var(--act-2)); }
.panel.stat table { width: 100%; border-collapse: collapse; font-size: 12.5px; table-layout: fixed; }
.panel.stat td { padding: .18rem .35rem; border-top: 1px solid var(--line); white-space: nowrap; }
.panel.stat td.rank { color: var(--mute); font-family: var(--mono); text-align: right; }
.panel.stat td.name { overflow: hidden; text-overflow: ellipsis; }
/* The bar: bans (the ban accent) then hits (yellow), end to end — the
   same colours as the rail and the chart. */
.panel.stat td.bar .stk { display: flex; height: 8px; border-radius: 4px; overflow: hidden; background: var(--panel-3); }
.panel.stat td.bar .stk i { display: block; height: 100%; flex: none; }
.panel.stat td.bar .stk i.b { background: var(--cool); }
.panel.stat td.bar .stk i.h { background: var(--warn); }
.panel.stat td.num.h { color: var(--warn); }
.stat-legend { margin: -.2rem 0 .6rem; }
.stat-legend .sw { display: inline-block; width: 14px; height: 8px; border-radius: 4px; vertical-align: middle; margin: 0 .2rem 0 .4rem; }
.stat-legend .sw.b { background: var(--cool); } .stat-legend .sw.h { background: var(--warn); }
.panel.stat td.num { text-align: right; font-family: var(--mono); }
.panel.stat td.sp { width: 90px; } .spark { width: 84px; height: 18px; display: block; } .spark polyline { fill: none; stroke: var(--ink-2); stroke-width: 1.2; vector-effect: non-scaling-stroke; }
.head .chips a { color: var(--ink-2); padding: .3rem .8rem; border-radius: 999px; } .head .chips a.on { color: #fff; background: linear-gradient(135deg, var(--act), var(--act-2)); }
/* Custom dates: a date and a time side by side (after .fields' 100 % rule). */
.dt { display: flex; gap: .3rem; min-width: 0; }
.modal .fields .dt input[type=date] { flex: 1 1 0; min-width: 0; width: auto; }
.modal .fields .dt input[type=time] { flex: none; width: 7.5em; }

/* The map and the chart say when they are loading: a dim and a small
   spinner in the corner — they are the slowest to come. */
.world.loading, .chart.loading { position: relative; }
/* The map never dims while it recounts — the corner wheel says it
  ; the chart keeps its veil. */
.chart.loading .plot { opacity: .55; transition: opacity .2s; }
.world.loading::after, .chart.loading::after { content: ""; position: absolute; top: .6rem; right: .8rem; width: 14px; height: 14px; border: 2px solid var(--line-2); border-top-color: var(--act); border-radius: 50%; animation: spin .8s linear infinite; z-index: 4; }
/* Narrow screens, once more and properly (Firefox mobile, 2026-08-23):
   the Overview line is a grid — title, View, live on the first row; the
   regime and stages on the second; the wheel on the third. Every popup
   menu becomes a sheet in the middle of the screen (a menu anchored to a
   button near an edge opened off-screen). Dialogs never wider than the
   screen, and nothing inside them pushes sideways. */
@media (max-width: 900px) {
  .ovline { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: .45rem .5rem; align-items: center; }
  .ovline h2 { grid-row: 1; grid-column: 1; }
  .ovline > .pop { grid-row: 1; grid-column: 2; }
  .ovline #livebox { grid-row: 1; grid-column: 3; margin: 0; }
  .ovline #what { grid-row: 2; grid-column: 1 / -1; }
  .ovline > .grow { display: none; }
  .ovline .period-sel { grid-row: 3; grid-column: 1 / -1; width: 100%; justify-content: center; }
  /* The Stats line is not the home's: its controls wrap as a plain row
     (the grid above placed them in three columns and broke it). The
     wheel and the range each take a full line; one table per row. */
  .ovline.stline { display: flex; flex-wrap: wrap; }
  .ovline.stline #st-wheel { flex-basis: 100%; display: flex; justify-content: center; }
  .ovline.stline #st-range { flex-basis: 100%; }
  .statgrid { grid-template-columns: minmax(0, 1fr); }
  /* A phone's table: a shorter bar, the name keeps the room. */
  .panel.stat colgroup col:nth-child(3) { width: 56px !important; }
  .panel.stat colgroup col:nth-child(1) { width: 2em !important; }
  .pop-menu { position: fixed; left: .75rem; right: .75rem; top: 50%; transform: translateY(-50%); width: auto; min-width: 0; max-height: 80vh; overflow: auto; z-index: 25; }
  .modal { width: 100%; max-width: 100%; box-sizing: border-box; }
  .modal-head { padding: .8rem .9rem; } .modal-body { padding: .9rem; overflow-x: hidden; } .modal-foot { padding: .7rem .9rem; }
  .modal-head h2 { min-width: 0; overflow-wrap: anywhere; word-break: break-all; }
  .modal-head .rdns { display: block; margin-left: 0; }
  .modal .tbl table { min-width: 0; }
  .modal .cards { grid-template-columns: 1fr 1fr; }
  .modal .two { grid-template-columns: 1fr; }
  .modal img, .modal svg, .modal pre { max-width: 100%; }
  .toast { z-index: 60; }
  /* A dialog's foot: the note on its own row, the buttons under it. */
  .modal-foot > .small, .modal-foot > p, .modal-foot > .note { flex-basis: 100%; margin: 0 0 .2rem; }
}
.asfind { width: 100%; }
.asfound { display: flex; flex-direction: column; align-items: flex-start; gap: .25rem; margin-top: .35rem; }
.asfound .asopen { text-align: left; }
/* A window narrower than the wide modal's ideal: the wide modal folds
   too — nine columns in ~1000px was the overlap all over again. */
@media (max-width: 1280px) {
  .modal.wide .feed { grid-template-columns: 4px auto 36px minmax(0, 1fr); }
  .modal.wide .ticket { grid-template-areas: "s when acts acts" "s f who who" "s src src src" "s rulec prog prog" "s what what what" "s ev ev ev"; gap: .1rem .6rem; padding-right: .7rem; }
  .modal.wide .ticket .src { flex-direction: row; flex-wrap: wrap; gap: .3rem; }
  .modal.wide .ticket .prog { justify-self: start; }
}
/* Each rail section's own search, and the dialogs': small, quiet. */
.rail h2 .rfind { width: 74px; margin-left: auto; padding: .1rem .4rem; font-size: 11.5px; border-radius: var(--r-s); }
/* Every rail section folds behind its own chevron (never a multi-zone
   header); the servers' group sub-headers mirror the Servers page. */
.rail h2 .fold { background: none; border: none; color: var(--mute); cursor: pointer; padding: 0 .3rem 0 0; font-size: 11px; line-height: 1; }
.rail h2 .fold:hover { color: var(--ink); }
.rail .railgrp { margin: .4rem 0 .1rem; font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--mute); }
/* The Users table: rows unfold in place; the server picker is a filter
   over checkboxes — a fleet of thousands is typed, never scrolled. */
#usrs .fold { background: none; border: none; color: var(--mute); cursor: pointer; padding: 0 .45rem 0 0; font-size: 11px; line-height: 1; }
#usrs .fold:hover { color: var(--ink); }
#usrs tr.unfold > td { background: color-mix(in srgb, var(--panel-2) 55%, var(--panel)); }
.srvpick .filter { width: 100%; max-width: 24rem; margin-bottom: .25rem; }
.srvpick .picks { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: .15rem .8rem; max-height: 11rem; overflow: auto; padding: .3rem 0; }
.rail h2 { display: flex; align-items: center; gap: .3rem; }
.dlgfind { width: 100%; }
