/* ---------- css/fonts.css ---------- */
/* Every font URL below carries the first characters of that file's SHA-256, so
   the year-long immutable cache the asset handler sends is correct for it.
   TestTheStylesheetNamesEachFontsOwnHash holds each hash equal to the file it
   names, which is what turns a re-vendored font into a red test rather than a
   browser that keeps the old one. */

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("ibm-plex-sans-latin-400-normal.woff2?v=db71f8a28ad85015") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("ibm-plex-sans-latin-500-normal.woff2?v=5ef914e59b0047a2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("ibm-plex-sans-latin-600-normal.woff2?v=31535a91ce3f6b8e") format("woff2");
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("ibm-plex-sans-latin-700-normal.woff2?v=fff71a83690454ee") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("ibm-plex-mono-latin-400-normal.woff2?v=3c5a451f9ec27a35") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("ibm-plex-mono-latin-500-normal.woff2?v=756026ff72eb76fd") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("ibm-plex-mono-latin-600-normal.woff2?v=c4d3deb734a27e6d") format("woff2");
}

/* ---------- css/tokens.css ---------- */
/* Console tokens: colours (light-dark pairs), the type, space and radius scales,
   control sizes, motion and layers. Everything else reads these. */
:root{
  --hue:265;
  --font-ui:"IBM Plex Sans","Segoe UI",system-ui,sans-serif;
  --font-data:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
  /* Every size is in rem. The root is 140% (base.css), so 1rem is 22.4px. Canvas-only
     values (chart line, point and grid widths) stay in px, because uPlot draws in CSS px. */
  --fs-xs:0.5938rem;--fs-sm:0.6562rem;--fs-md:0.75rem;--fs-lg:0.8438rem;--fs-xl:1rem;--fs-metric:1.1875rem;--fs-headline:1.875rem;
  --target-list-height:32rem;--navw:13rem;--card-min:30rem;--icon:0.8125rem;--control-height:1.75rem;--control-height-sm:1.5625rem;--target-min:1.125rem;--target-touch:2rem;--topbar-height:calc(var(--control-height) + 2 * var(--sp-2));--chart-height:12.5rem;
  /* --toolbar-top is where the page toolbar band sticks: the viewport top, and under
     the top bar where the top bar sticks (the small breakpoint, responsive.css). */
  --toolbar-height:3.25rem;--toolbar-top:0rem;--knob:0.8125rem;--dot:0.375rem;--mark-size:0.3125rem;--key-size:0.4375rem;
  --chart-line-width:2px;--chart-point-size:5px;--chart-grid-width:1px;--chart-tip-gap:var(--sp-3);
  --chart-dash:6 4;--chart-dot:2 3;--chart-dashdot:8 3 2 3;--chart-long:12 4;--chart-grid-dash:3 3;
  --rule:0.125rem;--hairline:1px;--prose-max:78ch;
  /* the switch track, then a descriptor form row (forms.css): the label column and the help line beside it */
  --switch-width:2.25rem;--field-col:16rem;--help-max:60ch;
  --lh-flat:1;--lh-snug:1.3;--lh-body:1.4;--lh-loose:1.6;
  --ls-caps:.06em;--ls-tight:-.005em;--ls-display:.02em;
  --r-mark:0.0625rem;--r-ctrl:0.25rem;--r-panel:0.43rem;--r-chip:999px;
  --sp-hair:1px;--sp-half:0.125rem;--sp-1:0.1875rem;--sp-2:0.375rem;--sp-3:0.5625rem;--sp-4:0.75rem;--sp-5:0.875rem;
  --sp-6:1.125rem;--sp-7:1.5rem;--sp-8:1.875rem;
  /* the room the page toolbar keeps free at its end edge for the window controls */
  --sp-tools:34rem;
  --dur-fast:.15s;--dur-slow:.2s;
  --z-raised:1;--z-sticky:2;--z-tools:3;--z-toolbar:4;--z-topbar:5;--z-skip:10;
  --focus-ring:0.125rem solid var(--accent);--focus-offset:0.0625rem;
  --bg:light-dark(oklch(0.970 0.004 var(--hue)),oklch(0.185 0.012 var(--hue)));
  --surface:light-dark(oklch(1 0 0),oklch(0.223 0.016 var(--hue)));
  --subtle:light-dark(oklch(0.979 0.003 var(--hue)),oklch(0.244 0.018 var(--hue)));
  --topbar-operator:light-dark(oklch(0.981 0.009 256),oklch(0.235 0.018 var(--hue)));
  --sidebar:light-dark(oklch(0.403 0.015 276),oklch(0.183 0.013 var(--hue)));
  --nav-active:light-dark(oklch(0.421 0.013 275),oklch(0.252 0.020 var(--hue)));
  --nav-hover:light-dark(oklch(0.421 0.013 275),oklch(0.252 0.020 var(--hue)));
  /* The sidebar is the dark slate chrome in both themes, so its text, hairline and
     accent are their own tokens; .nav scopes --ink, --line and --accent to them. */
  --nav-ink:light-dark(oklch(0.828 0.016 274),oklch(0.662 0.030 var(--hue)));
  --nav-ink-on:light-dark(oklch(1 0 0),oklch(0.931 0.008 var(--hue)));
  --nav-line:light-dark(oklch(0.458 0.013 275),oklch(0.286 0.021 var(--hue)));
  --nav-accent:light-dark(oklch(0.80 0.10 251),oklch(0.716 0.136 251));
  --sun:light-dark(oklch(0.826 0.148 82),oklch(0.826 0.148 82));
  --on-accent:light-dark(oklch(1 0 0),oklch(0.187 0.015 var(--hue)));
  --backdrop:light-dark(oklch(.20 .03 var(--hue) / .38),oklch(0 0 0 / .6));
  --shadow:0 1px 2px light-dark(oklch(.30 .02 var(--hue) / .06),oklch(0 0 0 / .3)),
    0 2px 8px light-dark(oklch(.30 .02 var(--hue) / .10),oklch(0 0 0 / .35));
  --ink:light-dark(oklch(0.346 0.008 var(--hue)),oklch(0.894 0.010 var(--hue)));
  --ink2:light-dark(oklch(0.444 0.018 var(--hue)),oklch(0.786 0.019 var(--hue)));
  --ink3:light-dark(oklch(0.544 0.027 var(--hue)),oklch(0.662 0.030 var(--hue)));
  --line:light-dark(oklch(0.916 0.008 var(--hue)),oklch(0.286 0.021 var(--hue)));
  /* --line-strong frames inputs, selects and buttons: 3:1 on --bg, --surface and
     --subtle in both themes (TestTheControlBorderClearsTheNonTextContrastFloor). */
  --line-strong:light-dark(oklch(0.64 0.020 var(--hue)),oklch(0.53 0.026 var(--hue)));
  --accent:light-dark(oklch(0.533 0.168 256),oklch(0.716 0.136 251));
  --accent-soft:light-dark(oklch(0.533 0.168 256 / .12),oklch(0.716 0.136 251 / .18));
  --good:light-dark(oklch(0.55 0.13 150),oklch(0.72 0.13 150));
  --good-ink:light-dark(oklch(.42 .10 150),oklch(.86 .11 150));
  --good-bg:light-dark(oklch(.97 .03 150),oklch(.28 .045 150));
  --warn:light-dark(oklch(0.68 0.14 75),oklch(0.78 0.13 80));
  --warn-ink:light-dark(oklch(.43 .09 75),oklch(.85 .10 75));
  --warn-bg:light-dark(oklch(.97 .025 80),oklch(.29 .035 75));
  --error:light-dark(oklch(0.51 0.17 27),oklch(0.68 0.15 25));
  --error-ink:light-dark(oklch(.44 .13 25),oklch(.85 .07 25));
  --error-bg:light-dark(oklch(.975 .01 25),oklch(.28 .025 25));
  --seg-0:light-dark(oklch(.52 .17 255),oklch(.68 .15 255));
  --seg-1:light-dark(oklch(.56 .15 150),oklch(.72 .14 150));
  --seg-2:light-dark(oklch(.62 .15 70),oklch(.78 .14 70));
  --seg-3:light-dark(oklch(.52 .18 20),oklch(.68 .16 20));
  --seg-4:light-dark(oklch(.54 .14 320),oklch(.72 .13 320));
  --seg-5:light-dark(oklch(.58 .10 200),oklch(.76 .10 200));
  /* Lines clear 3:1 over --bg and every overlapping fill composite, including
     the operator request chart's four-band overlay.
     Fill colors stay separate so a stroke adjustment cannot change its ground.
     The 8% peak fill opacity in chart.js faint() is part of this contrast
     contract. TestEveryBandClearsTheSurfaceContrastFloor checks both themes. */
  --band-allowed:light-dark(oklch(0.49 0.09 158),oklch(0.8 0.085 158));
  --band-challenged:light-dark(oklch(0.525 0.085 75),oklch(0.72 0.08 75));
  --band-blocked:light-dark(oklch(0.51 0.18 27),oklch(0.78 0.125 25));
  /* Bandwidth uses violet and teal to distinguish direction from security status. */
  --band-in:light-dark(oklch(0.54 0.09 290),oklch(0.8 0.07 290));
  --band-out:light-dark(oklch(0.51 0.075 210),oklch(0.82 0.065 210));
  --band-unknown-outcome:light-dark(oklch(0.5 0.02 250),oklch(0.78 0.02 250));
  /* The TLS engine match chart is its own three-band overlay on the overview. */
  --band-tls-coherent:light-dark(oklch(0.54 0.11 200),oklch(0.74 0.11 200));
  --band-tls-contradicted:light-dark(oklch(0.51 0.18 27),oklch(0.7 0.16 25));
  --band-tls-unknown:light-dark(oklch(0.525 0.14 80),oklch(0.72 0.13 80));
  --band-nic-0:light-dark(oklch(0.44 0.15 260),oklch(0.56 0.14 260));
  --band-nic-1:light-dark(oklch(0.62 0.15 60),oklch(0.665 0.14 60));
  --band-nic-2:light-dark(oklch(0.6 0.11 195),oklch(0.665 0.11 195));
  --band-nic-3:light-dark(oklch(0.5 0.15 345),oklch(0.56 0.15 345));
  --band-nic-other:light-dark(oklch(0.32 0.02 250),oklch(0.82 0.02 250));
  --fill-allowed:light-dark(oklch(0.55 0.075 155),oklch(0.72 0.07 155));
  --fill-challenged:light-dark(oklch(0.645 0.085 75),oklch(0.72 0.08 75));
  --fill-blocked:light-dark(oklch(0.51 0.18 27),oklch(0.53 0.16 25));
  --fill-in:light-dark(oklch(0.56 0.09 290),oklch(0.72 0.08 290));
  --fill-out:light-dark(oklch(0.58 0.065 210),oklch(0.74 0.06 210));
  /* Protocol hues, one per protocol on every chart, validated as one categorical
     set (all pairs, protan, deutan and tritan, both themes). The outcome is the
     line pattern: allowed solid, challenged dotted, dropped dashed, unknown
     dash-dot. A chart without a protocol draws its outcomes in --band-outcome. */
  --band-tcp:light-dark(oklch(0.44 0.14 260),oklch(0.56 0.14 260));
  --band-udp:light-dark(oklch(0.6 0.15 65),oklch(0.64 0.15 65));
  --band-icmp:light-dark(oklch(0.56 0.11 185),oklch(0.64 0.12 185));
  --band-proto-other:light-dark(oklch(0.44 0.17 10),oklch(0.56 0.17 10));
  --band-outcome:light-dark(oklch(0.34 0.01 265),oklch(0.8 0.01 265));
  --fill-tcp:light-dark(oklch(0.9 0.05 260),oklch(0.3 0.06 260));
  --fill-udp:light-dark(oklch(0.9 0.05 65),oklch(0.3 0.06 65));
  --fill-icmp:light-dark(oklch(0.9 0.05 185),oklch(0.3 0.06 185));
  --fill-proto-other:light-dark(oklch(0.9 0.05 10),oklch(0.3 0.06 10));
  --fill-outcome:light-dark(oklch(0.9 0.01 265),oklch(0.3 0.01 265));
  --fill-unknown-outcome:light-dark(oklch(0.9 0.01 250),oklch(0.3 0.01 250));
  --fill-tls-coherent:light-dark(oklch(0.58 0.11 200),oklch(0.74 0.11 200));
  --fill-tls-contradicted:light-dark(oklch(0.51 0.18 27),oklch(0.53 0.16 25));
  --fill-tls-unknown:light-dark(oklch(0.645 0.14 80),oklch(0.72 0.13 80));
  --fill-nic-0:light-dark(oklch(0.9 0.05 260),oklch(0.3 0.06 260));
  --fill-nic-1:light-dark(oklch(0.9 0.05 60),oklch(0.3 0.06 60));
  --fill-nic-2:light-dark(oklch(0.9 0.05 195),oklch(0.3 0.06 195));
  --fill-nic-3:light-dark(oklch(0.9 0.05 345),oklch(0.3 0.06 345));
  --fill-nic-other:light-dark(oklch(0.9 0.01 250),oklch(0.3 0.01 250));
  --gridline:light-dark(oklch(0.916 0.008 var(--hue)),oklch(0.286 0.021 var(--hue)));
  /* --axis paints the uPlot tick labels and axis line, which are text and must
     read on the chart ground; .3 alpha left the dark labels near 2:1. The grid
     above stays faint on purpose - only the labels move. */
  --axis:light-dark(oklch(0.544 0.027 var(--hue)),oklch(0.662 0.030 var(--hue)));
}

/* ---------- css/base.css ---------- */
/* Reset, root size and the document defaults. */
*,*::before,*::after{box-sizing:border-box}
:root{color-scheme:light dark;interpolate-size:allow-keywords}
:root[data-theme="light"]{color-scheme:light}
:root[data-theme="dark"]{color-scheme:dark}
/* UI scale: every size in the console is in rem, so this one value scales the
   whole console (type, spacing, sidebar, controls, charts). 140% matches the old
   px layout at 140% browser zoom. The container breakpoints in responsive.css are
   in rem too, so they scale with it. */
html{font-size:140%}
html,body{margin:0}
body{
  background:var(--bg);color:var(--ink);
  font-family:var(--font-ui);font-size:var(--fs-md);line-height:var(--lh-body);
  -webkit-font-smoothing:antialiased;
}
.sr-only{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* The global :focus-visible outline is drawn around the clipped 1x1 box and
   clipped with it, which leaves the first tabbable element of every page
   focusable and invisible - WCAG 2.2 SC 2.4.7 Focus Visible, level AA. */
.sr-only:focus-visible{position:fixed;inset-block-start:var(--sp-2);inset-inline-start:var(--sp-2);
  inline-size:auto;block-size:auto;overflow:visible;clip-path:none;z-index:var(--z-skip);
  background:var(--surface);color:var(--ink);
  border:var(--hairline) solid var(--line-strong);border-radius:var(--r-ctrl);padding:var(--sp-1) var(--sp-3);
  font-family:var(--font-ui);font-size:var(--fs-sm);text-decoration:none;white-space:normal}
a{color:inherit}
button,select,input{font:inherit}
button{cursor:pointer}
:focus-visible{outline:var(--focus-ring);outline-offset:var(--focus-offset)}
.spacer{flex:1}
.mono{font-family:var(--font-data);font-variant-numeric:tabular-nums}
.eyebrow{margin:0;color:var(--ink3);font-size:var(--fs-xs);font-weight:700;letter-spacing:var(--ls-caps);
  text-transform:uppercase}
.form-field>.eyebrow,.field-label .eyebrow,.field .eyebrow,.kv>.eyebrow,.errfacts>.eyebrow{font-size:var(--fs-md);font-weight:600;
  letter-spacing:normal;text-transform:none}
.request-detail-group>.eyebrow{font-size:var(--fs-md)}
.icon{inline-size:var(--icon);block-size:var(--icon);flex:none;fill:none;
  stroke:currentColor;stroke-width:1.65;stroke-linecap:round;stroke-linejoin:round}
.detail{margin:0;color:var(--ink2);text-wrap:pretty}

/* ---------- css/shell.css ---------- */
/* Page chrome: the shell grid, the slim top bar (status, zone, theme, sign out),
   the slate sidebar navigation, the operator zone tint and the legal footer. The
   page toolbar (title + window controls) is toolbar.css. */

/* .screen is the query container and .s-grid is a descendant of it, never the
   same element: a container never matches its own container query, so a rule on
   .screen inside @container screen is dead. */
.screen{container-type:inline-size;container-name:screen;background:var(--bg);color:var(--ink);
  font-family:var(--font-ui);font-size:var(--fs-md);line-height:var(--lh-body);min-block-size:100svh;display:grid}
.s-grid{display:grid;grid-template-columns:var(--navw) minmax(0,1fr);min-block-size:100svh}
.main-column{min-inline-size:0;display:flex;flex-direction:column}

/* ---------- top bar ---------- */
.topbar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-3);
  min-block-size:var(--topbar-height);padding:var(--sp-2) var(--sp-5);
  background:var(--surface);border-block-end:var(--hairline) solid var(--line)}
.top-actions{display:flex;align-items:center;flex-wrap:wrap;gap:var(--sp-3)}
.top-actions form{margin:0}
.top-actions .btn{min-block-size:var(--control-height-sm);padding-inline:var(--sp-3);font-size:var(--fs-sm)}
.top-status:empty{display:none}
.brand{font-family:var(--font-ui);font-size:var(--fs-lg);font-weight:700;
  letter-spacing:var(--ls-tight);line-height:var(--lh-snug);overflow-wrap:anywhere}

/* The theme toggle is a pill: sun on the start edge, moon on the end edge and a
   thumb that slides under the glyph of the theme in force. The label stays in the
   button for assistive technology and is clipped at every width. The thumb sits a hairline
   from the edge so its centre lands on the glyph's centre at the --sp-1 padding. */
.top-actions .theme{position:relative;inline-size:3.125rem;min-inline-size:3.125rem;padding-inline:var(--sp-1);justify-content:space-between;
  background:var(--subtle);border-color:var(--line);border-radius:var(--r-chip)}
.theme .icon{position:relative;z-index:var(--z-raised);inline-size:0.75rem;block-size:0.75rem;transition:color var(--dur-slow),opacity var(--dur-slow)}
.theme .sun{order:-1;color:var(--sun);fill:currentColor}
.theme .moon{color:var(--ink3);fill:currentColor;opacity:.55}
.theme::before{content:"";position:absolute;inset-block-start:50%;inset-inline-start:var(--sp-hair);inline-size:1.05rem;block-size:1.05rem;
  translate:0 -50%;border-radius:var(--r-chip);background:var(--surface);
  box-shadow:0 0 var(--sp-2) var(--sp-hair) color-mix(in oklab,var(--sun) 55%,transparent),var(--shadow);
  transition:inset-inline-start var(--dur-slow),box-shadow var(--dur-slow)}
.theme .btn-label{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .theme .sun{color:var(--ink3);opacity:.55}
  :root:not([data-theme="light"]) .theme .moon{color:var(--accent);opacity:1}
  :root:not([data-theme="light"]) .theme::before{inset-inline-start:calc(100% - 1.05rem - var(--sp-hair));box-shadow:0 0 var(--sp-2) var(--sp-hair) color-mix(in oklab,var(--accent) 60%,transparent),var(--shadow)}
}
:root[data-theme="dark"] .theme .sun{color:var(--ink3);opacity:.55}
:root[data-theme="dark"] .theme .moon{color:var(--accent);opacity:1}
:root[data-theme="dark"] .theme::before{inset-inline-start:calc(100% - 1.05rem - var(--sp-hair));box-shadow:0 0 var(--sp-2) var(--sp-hair) color-mix(in oklab,var(--accent) 60%,transparent),var(--shadow)}
.nav-open{display:none}

/* ---------- navigation ---------- */
/* The sidebar is slate chrome in both themes. It scopes the shell's text, line and
   accent tokens to its own, so every nav rule below reads the slate palette. */
.nav{--ink:var(--nav-ink-on);--ink3:var(--nav-ink);--line:var(--nav-line);--accent:var(--nav-accent);
  background:var(--sidebar);color:var(--ink3);padding:0 0 var(--sp-4);
  border-inline-end:var(--hairline) solid var(--line);min-inline-size:0;position:sticky;
  inset-block-start:0;block-size:100svh;overflow-y:auto}
.nav .brand{display:flex;align-items:center;gap:var(--sp-3);margin-block-end:var(--sp-2);padding:var(--sp-4) var(--sp-5) var(--sp-3);
  border-block-end:var(--hairline) solid var(--line);color:var(--ink);font-size:var(--fs-md);font-weight:700;letter-spacing:var(--ls-display)}
.brand-mark{inline-size:1.5rem;block-size:1.5rem;flex:none;fill:color-mix(in oklab,var(--accent) 16%,transparent);stroke:var(--accent);
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.nav ul{list-style:none;margin:0;padding:0}
.nav>ul{padding-inline:var(--sp-2)}
.nav-section+.nav-section{margin-block-start:var(--sp-2);border-block-start:var(--hairline) solid var(--line)}
.nav-section>ul{margin-inline-start:var(--sp-2);padding-inline-start:var(--sp-2);border-inline-start:var(--hairline) solid var(--line)}
.nav a{display:flex;align-items:center;gap:var(--sp-3);min-block-size:var(--control-height-sm);
  padding:var(--sp-1) var(--sp-3);border-radius:var(--r-ctrl);color:var(--ink3);
  font-size:var(--fs-md);font-weight:500;text-decoration:none;transition:color var(--dur-fast),background-color var(--dur-fast)}
.nav-title{min-inline-size:0;overflow-wrap:anywhere}
.nav .nav-subsection{display:grid;gap:var(--sp-1);padding-block-start:var(--sp-1);padding-inline-start:var(--sp-3)}
.nav a:active{background:var(--nav-hover)}
.nav a[aria-current="page"]{background:var(--nav-active);color:var(--ink);font-weight:600;box-shadow:inset var(--rule) 0 0 var(--accent)}
.nav-label{display:flex;align-items:center;gap:var(--sp-2);
  min-block-size:var(--control-height-sm);padding:var(--sp-3) var(--sp-2) var(--sp-1);user-select:none}
.nav .nav-label{color:var(--ink3)}
.nav-section:has([aria-current="page"])>.nav-label{color:var(--accent)}
/* A layer group (L4 PacketShield, L7 web edge) carries a short layer tag before
   its label, so the two layers read as separate blocks. */
.nav-layer{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:end;column-gap:var(--sp-2);padding-inline-start:var(--sp-2)}
.nav-layer>.nav-label{padding-inline-start:0}
.nav-layer>ul{grid-column:1/-1;margin-inline-start:0}
.nav-tag{margin-block-end:var(--sp-1);padding:var(--sp-half) var(--sp-1);border:var(--hairline) solid var(--line);border-radius:var(--r-ctrl);
  background:var(--nav-hover);color:var(--ink3);font-size:var(--fs-xs);font-weight:700;line-height:var(--lh-flat);letter-spacing:var(--ls-caps)}
.nav-section:has([aria-current="page"])>.nav-tag{border-color:var(--accent);color:var(--accent)}
.nav a.leaf{font-weight:500}
.nav .nav-close{display:none}

.breadcrumbs ol{display:flex;flex-wrap:wrap;gap:var(--sp-2);list-style:none;margin:0;padding:0;color:var(--ink3);font-size:var(--fs-sm)}
.breadcrumbs li+li::before{content:"/";margin-inline-end:var(--sp-2);opacity:.6}
.breadcrumbs li{overflow-wrap:anywhere;min-inline-size:0}
.breadcrumbs a{text-decoration:none}

/* ---------- operator zone ---------- */
.zonetag{padding:var(--sp-half) var(--sp-2);border:var(--hairline) solid var(--accent);border-radius:var(--r-ctrl);
  font-size:var(--fs-xs);font-weight:700;letter-spacing:var(--ls-caps);text-transform:uppercase;color:var(--accent)}
.zonenote{color:var(--ink2);font-size:var(--fs-sm)}
.opzone .mainarea{background:var(--bg)}
.opzone .topbar{background:var(--topbar-operator)}

.legal-footer{padding:var(--sp-4) var(--sp-6);font-size:var(--fs-sm);color:var(--ink2)}
.legal-links{display:flex;flex-wrap:wrap;gap:var(--sp-4)}
.legal-links a{color:inherit}

/* ---------- css/toolbar.css ---------- */
/* The page toolbar and the main area grid.

   The toolbar is one band under the top bar: breadcrumb and page title at the
   start edge (.pagehead, rendered by the layout), the window controls at the end
   edge (.time-bar, rendered by the page inside its live region so a poll swap
   carries it). The main area is position:relative and nothing between it and the
   time bar is positioned, so the bar sits in the toolbar row whatever its depth
   in the page; the title keeps --sp-tools free for it. Under the small breakpoint
   the bar drops back into the flow under the head (responsive.css).

   On a page with a time bar the whole band sticks at --toolbar-top: the head and
   the controls row take that one offset, so they read and move as one bar. The
   breadcrumbs slide away above the title: timepicker.js writes the head height
   (--toolbar-band) and the breadcrumb lead above the title (--toolbar-lead) on the
   main area, and the head sticks that lead above the offset. --toolbar-stuck is
   what stays in view, and what sticks below the band starts there. Without the
   script the defaults keep the whole head in view. */
.mainarea{--toolbar-band:var(--toolbar-height);--toolbar-lead:0rem;--toolbar-stuck:calc(var(--toolbar-band) - var(--toolbar-lead));
  position:relative;display:grid;grid-template-columns:var(--sp-6) minmax(0,1fr) var(--sp-6);
  row-gap:var(--sp-6);align-content:start;padding-block-end:var(--sp-8);min-inline-size:0;inline-size:100%;flex:1 1 auto}
.mainarea>*{grid-column:2;min-inline-size:0}
.mainarea>.pagehead{grid-column:1/-1}
.mainarea .detail,.chart-cap{max-inline-size:var(--prose-max)}

.pagehead{display:flex;flex-wrap:wrap;align-items:center;align-content:center;gap:var(--sp-1) var(--sp-3);
  min-block-size:var(--toolbar-height);padding:var(--sp-3) var(--sp-6);min-inline-size:0;
  background:var(--surface);border-block-end:var(--hairline) solid var(--line)}
.pagehead .breadcrumbs{flex-basis:100%}
.pagehead h1{margin:0;min-inline-size:0;font-size:var(--fs-xl);font-weight:700;letter-spacing:var(--ls-tight);
  line-height:var(--lh-snug);overflow-wrap:anywhere}
.pagehead .object-name{font-family:var(--font-data)}
.mainarea:has(.time-bar)>.pagehead{position:sticky;inset-block-start:calc(var(--toolbar-top) - var(--toolbar-lead));z-index:var(--z-toolbar);
  padding-inline-end:var(--sp-tools)}

/* ---------- window controls ---------- */
/* One right-aligned row: the range picker group, then Refresh now and the
   refresh interval, then the paused note. */
/* The bar spans the main area height from the title line down and lets clicks
   through, so its row can stick with the head on a page of any depth. The row is
   as tall as the part of the head that stays in view and centres the controls on
   the title, at rest and stuck alike. The bar sets no layer: the frozen note under
   the row scrolls beneath the head, and only the row stacks above it. */
.time-bar{position:absolute;inset-block:var(--toolbar-lead) 0;inset-inline-end:var(--sp-6);pointer-events:none;
  display:flex;flex-direction:column;align-items:flex-end;gap:var(--sp-1) var(--sp-3);max-inline-size:var(--sp-tools)}
.time-bar .input{font-size:var(--fs-sm);min-block-size:var(--control-height-sm)}
.time-row{position:sticky;inset-block-start:var(--toolbar-top);z-index:var(--z-toolbar);
  display:flex;flex-wrap:wrap;gap:var(--sp-2);align-items:center;justify-content:flex-end;min-block-size:var(--toolbar-stuck)}
.time-row:has(+.detail){inset-block-start:calc(var(--toolbar-top) + var(--sp-3));min-block-size:var(--control-height-sm);margin-block-start:var(--sp-3)}
.time-row>*{pointer-events:auto}
.tp-group,.time-actions{border-radius:var(--r-ctrl)}
.time-bar>.detail{text-align:end;color:var(--ink3);font-size:var(--fs-sm)}

/* The range picker: two joined button groups (shift, range, shift, zoom out;
   refresh now, refresh interval). The range button opens a two-column panel:
   the absolute From/To form and the quick range list. */
.tp-group,.time-actions{display:inline-flex;align-items:stretch}
.tp-btn{display:inline-flex;align-items:center;gap:var(--sp-1);min-block-size:var(--control-height-sm);padding-inline:var(--sp-2);
  border:var(--hairline) solid var(--line-strong);background:var(--surface);color:var(--ink2);font-family:var(--font-ui);
  font-size:var(--fs-sm);font-weight:500;line-height:var(--lh-flat);text-decoration:none;white-space:nowrap;cursor:pointer;
  transition:background-color var(--dur-fast),color var(--dur-fast)}
.tp-group>.tp-btn+.tp-btn,.tp-group>.tp-btn+.tp-range>.tp-btn,.tp-group>.tp-range+.tp-btn,.time-actions>.tp-btn+.tp-refresh>.tp-btn{border-inline-start:0}
.tp-group>.tp-btn:first-child,.time-actions>.tp-btn:first-child,.tp-group>.tp-range:first-child>.tp-btn,.time-actions>.tp-refresh:first-child>.tp-btn{border-start-start-radius:var(--r-ctrl);border-end-start-radius:var(--r-ctrl)}
.tp-group>.tp-btn:last-child,.time-actions>.tp-btn:last-child,.tp-group>.tp-range:last-child>.tp-btn,.time-actions>.tp-refresh:last-child>.tp-btn{border-start-end-radius:var(--r-ctrl);border-end-end-radius:var(--r-ctrl)}
.tp-btn .icon{inline-size:var(--icon);block-size:var(--icon);fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.tp-range{position:relative;display:flex}
.tp-range>summary{list-style:none;color:var(--ink);font-variant-numeric:tabular-nums}
.tp-range>summary::-webkit-details-marker{display:none}
.tp-range[open]>summary{background:var(--subtle)}
.tp-caret{color:var(--ink3)}
.tp-range-label{max-inline-size:18rem;overflow:hidden;text-overflow:ellipsis}
.tp-select{appearance:none;padding-inline:var(--sp-2)}
.tp-refresh{display:flex}
.tp-panel{position:absolute;inset-block-start:100%;inset-inline-end:0;z-index:var(--z-tools);inline-size:min(36rem,92vw);
  margin-block-start:var(--sp-1);background:var(--surface);border:var(--hairline) solid var(--line);border-radius:var(--r-panel);box-shadow:var(--shadow);
  font-size:var(--fs-sm);color:var(--ink)}
.tp-columns{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.tp-columns--quick{grid-template-columns:minmax(0,1fr)}
.tp-panel:has(.tp-columns--quick){inline-size:min(16rem,92vw)}
.tp-absolute,.tp-quick{display:grid;align-content:start;gap:var(--sp-3);padding:var(--sp-4)}
.tp-absolute{border-inline-end:var(--hairline) solid var(--line)}
.tp-heading{margin:0;font-size:var(--fs-sm);font-weight:600;color:var(--ink)}
.tp-form{display:grid;gap:var(--sp-3);justify-items:start}
.tp-field{display:grid;gap:var(--sp-1);inline-size:100%}
.tp-field>span:first-child{color:var(--ink3);font-size:var(--fs-xs)}
.tp-input{display:flex}
.tp-input .input{flex:1 1 auto;min-inline-size:0;font-variant-numeric:tabular-nums}
.tp-cal{display:inline-flex;align-items:center;padding-inline:var(--sp-2);border:var(--hairline) solid var(--line-strong);border-inline-start:0;
  border-start-end-radius:var(--r-ctrl);border-end-end-radius:var(--r-ctrl);background:var(--subtle);color:var(--ink2);cursor:pointer}
.tp-cal .icon{inline-size:var(--icon);block-size:var(--icon);fill:none;stroke:currentColor;stroke-width:2}
.tp-help{margin:0;color:var(--ink3);font-size:var(--fs-xs)}
.tp-list{display:grid;margin:0;padding:0;list-style:none;max-block-size:18rem;overflow:auto}
.tp-option{display:block;min-block-size:var(--target-min);padding:var(--sp-1) var(--sp-2);border-radius:var(--r-ctrl);color:var(--ink2);text-decoration:none}
.tp-option.current{background:var(--subtle);color:var(--ink);font-weight:600}
.tp-zone{margin:0;padding:var(--sp-2) var(--sp-4);border-block-start:var(--hairline) solid var(--line);color:var(--ink3);font-size:var(--fs-xs)}

/* The live status (the paused note) closes the row at its end edge, after the
   refresh controls. The countdown stays in the document for console.js and is
   clipped, so the toolbar shows no seconds. */
.live-status{display:grid;gap:var(--sp-half);justify-items:center;order:1}
.live-status .countdown{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.countdown{font-size:var(--fs-md);color:var(--ink3);font-variant-numeric:tabular-nums}
.live-paused{display:block;visibility:hidden;padding-inline:var(--sp-1);background:var(--surface);border-radius:var(--r-ctrl);font-size:var(--fs-xs);color:var(--ink3)}
.livewrap.is-poll-paused .live-paused{visibility:visible}

/* ---------- css/controls.css ---------- */
/* Buttons, inputs, chips, badges and small links. Hover rules are gated to real
   pointers and live in responsive.css with the other conditional rules. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--sp-2);
  min-block-size:var(--control-height);padding:var(--sp-half) var(--sp-4);white-space:nowrap;
  color:var(--ink);background:var(--surface);border:var(--hairline) solid var(--line-strong);
  border-radius:var(--r-ctrl);font-family:var(--font-ui);font-size:var(--fs-md);font-weight:600;line-height:var(--lh-snug);text-decoration:none;
  transition:background-color var(--dur-fast),border-color var(--dur-fast),transform var(--dur-fast)}
/* The compact button of a card head band, a pager or the toolbar. */
.btn--sm{min-block-size:var(--control-height-sm);padding-inline:var(--sp-3);font-size:var(--fs-sm)}
.btn--primary{background:var(--accent);border-color:var(--accent);color:var(--on-accent);font-weight:600}
.btn--danger{background:var(--error);border-color:var(--error);color:var(--on-accent);font-weight:600}
.btn--ghost{background:none;color:var(--ink2)}
.btn--ghost.current{background:var(--accent-soft);color:var(--accent);border-color:transparent;font-weight:600}
.btn--link{min-inline-size:var(--target-min);min-block-size:var(--target-min);padding:var(--sp-half) var(--sp-1);border:0;background:none;
  color:var(--accent)}
.btn--icon{min-inline-size:var(--target-min);min-block-size:var(--target-min);padding:0;border:0;border-radius:var(--r-chip);
  background:var(--line-strong);color:var(--ink);line-height:var(--lh-flat)}
.btn:disabled{cursor:not-allowed;opacity:.55}
/* ---------- hover, gated to real pointers ---------- */
@media (hover:hover) and (pointer:fine){
  .btn:hover:not(:disabled){background:var(--subtle);border-color:var(--accent)}
  .btn.btn--primary:hover{background:var(--accent);color:var(--on-accent);box-shadow:var(--shadow);filter:brightness(1.08)}
  .btn.btn--danger:hover{background:var(--error);color:var(--on-accent);box-shadow:var(--shadow)}
  .btn.btn--primary:disabled,.btn.btn--danger:disabled{box-shadow:none}
  .btn--link:hover:not(:disabled){background:none;text-decoration:underline}
  .btn--icon:hover:not(:disabled){background:color-mix(in oklab,var(--line-strong) 80%,var(--ink))}
  .chart-info>summary:hover{background:var(--subtle);color:var(--ink)}
  .btn--ghost:hover:not(:disabled){background:none;border-color:var(--accent);color:var(--ink)}
  .btn--ghost.current:hover:not(:disabled){background:var(--accent-soft);color:var(--accent)}
  .workspace-nav a:hover{color:var(--ink);background:var(--subtle)}
  .tp-btn:hover,.tp-option:hover,.tp-cal:hover{color:var(--ink);background:var(--subtle)}
  .seg-control-option:hover{color:var(--ink);background:var(--subtle)}
  .nav a:hover{color:var(--ink);background:var(--nav-hover)}
  .nav a[aria-current="page"]:hover{color:var(--ink);background:var(--nav-active)}
  .dtable tbody tr:hover{background:var(--subtle)}
  .golink:hover{color:var(--accent)}
  .golink:hover::after{translate:var(--sp-half) 0}
  .row-inspect:hover .row-toggle{color:var(--accent)}
  .detail-disclosure:hover>.detail-popover{display:block}
  .breadcrumbs a:hover{color:var(--accent)}
}
.btn:active:not(:disabled){background:var(--subtle);transform:translateY(1px)}
.btn.btn--primary:active{background:var(--accent);color:var(--on-accent)}
.btn.btn--danger:active{background:var(--error);color:var(--on-accent)}
.btn--ghost:active:not(:disabled){background:var(--subtle);border-color:var(--accent)}
.btn--ghost.current:active:not(:disabled){background:var(--accent-soft)}
.btn--link:active:not(:disabled){background:none;text-decoration:underline}
.btn--icon:active:not(:disabled){background:color-mix(in oklab,var(--line-strong) 70%,var(--ink))}

.input{inline-size:100%;min-inline-size:0;min-block-size:var(--control-height);
  padding:var(--sp-1) var(--sp-3);font:inherit;font-size:var(--fs-md);color:var(--ink);
  background:var(--surface);border:var(--hairline) solid var(--line-strong);border-radius:var(--r-ctrl)}
textarea.input{resize:vertical;padding-block:var(--sp-2)}
.input:focus-visible{border-color:var(--accent)}

.chip{display:inline-flex;align-items:center;gap:var(--sp-1);border-radius:var(--r-chip);
  font-size:var(--fs-sm);line-height:var(--lh-loose);padding:0 var(--sp-2);white-space:nowrap}
.chipset{display:inline-flex;gap:var(--sp-1);align-items:center}
.cell-list{margin:0;padding-inline-start:var(--sp-4);display:grid;gap:var(--sp-1);min-inline-size:min(100%,16rem);overflow-wrap:anywhere}

/* Single-select domain picker: a link group where the current scope is filled
   with the accent. htmx swaps the live region on a click; the href is the same
   URL so the control still works without the swap. */
.picker{display:flex;flex-wrap:wrap;gap:var(--sp-2)}
/* The segmented protocol control: joined link buttons, the current one on a muted
   fill with strong ink and an underline, so it reads without an accent colour. */
.seg-control{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2)}
.seg-control-label{color:var(--ink3);font-size:var(--fs-sm)}
.seg-control-options{display:inline-flex;flex-wrap:wrap}
.seg-control-option{display:inline-flex;align-items:center;min-block-size:var(--control-height-sm);padding-inline:var(--sp-3);
  border:var(--hairline) solid var(--line-strong);color:var(--ink2);background:var(--surface);
  font-size:var(--fs-sm);font-weight:500;text-decoration:none;transition:background-color var(--dur-fast),color var(--dur-fast)}
.seg-control-option+.seg-control-option{border-inline-start:0}
.seg-control-option:first-child{border-start-start-radius:var(--r-ctrl);border-end-start-radius:var(--r-ctrl)}
.seg-control-option:last-child{border-start-end-radius:var(--r-ctrl);border-end-end-radius:var(--r-ctrl)}
.seg-control-option.current{background:var(--subtle);color:var(--ink);font-weight:600;box-shadow:inset 0 calc(-2 * var(--hairline)) 0 var(--ink3)}
.picker .btn{white-space:normal;overflow-wrap:anywhere;text-align:start}

/* Status and decision badges: square-cornered, upper-case tags with a status
   square, on a faint tint of their colour (the severity chips of the design). */
.status-badge{display:inline-flex;align-items:center;gap:var(--sp-1);justify-self:start;padding:var(--sp-half) var(--sp-2);
  border:var(--hairline) solid var(--line);border-radius:var(--r-ctrl);font-size:var(--fs-xs);font-weight:700;line-height:var(--lh-body);
  text-transform:uppercase;letter-spacing:var(--ls-caps);color:var(--ink2)}
.status-badge::before{content:"";inline-size:var(--mark-size);block-size:var(--mark-size);border-radius:var(--r-mark);background:currentColor;flex:none}
.status-badge.status-warn{border-color:var(--warn);background:var(--warn-bg);color:var(--warn-ink)}
.status-badge.status-bad{border-color:var(--error);background:var(--error-bg);color:var(--error-ink)}
.action-badge::before{content:"";inline-size:var(--mark-size);block-size:var(--mark-size);border-radius:var(--r-mark);background:currentColor;flex:none}
.action-badge{display:inline-flex;align-items:center;gap:var(--sp-1);padding:0 var(--sp-2);border:var(--hairline) solid var(--line);border-radius:var(--r-ctrl);
  text-transform:uppercase;letter-spacing:var(--ls-caps);font-size:var(--fs-xs);font-weight:700;line-height:var(--lh-loose);white-space:nowrap;color:var(--ink2)}
.action-badge--allowed{border-color:var(--good);background:var(--good-bg);color:var(--good-ink)}
.action-badge--challenged{border-color:var(--warn);background:var(--warn-bg);color:var(--warn-ink)}
.action-badge--blocked{border-color:var(--error);background:var(--error-bg);color:var(--error-ink)}
.action-badge--error{border-style:dashed;border-color:var(--error);color:var(--error-ink)}
.action-badge--unrecorded{border-style:dashed;color:var(--ink3)}

.golink{margin-block-start:auto;color:var(--accent);font-weight:600;text-decoration:none;font-size:var(--fs-sm);
  align-self:start;padding:var(--sp-half) 0;transition:color var(--dur-fast)}
.golink::after{content:" \2192";transition:translate var(--dur-fast);display:inline-block}
.golink:active{translate:1px 0}

/* ---------- css/cards.css ---------- */
/* Cards and page layout: page families, the entity workspace, the section card
   with its head band, half-width pairs, the overview blocks.

   A section (.content-section) is a card: a head band (.section-heading) holding
   the title, the subtitle (.section-note, the coverage text) and the end-edge
   actions (.section-actions), then the body. Body parts sit inside the card's
   padding; a table or a pager directly in the card runs flush to its edges. */
.page-family{display:grid;gap:var(--sp-6);align-content:start;min-inline-size:0;inline-size:100%}
/* Page families keep the shell consistent without forcing every workflow into
   the same card grid. Analytics prioritizes plots, collections prioritize row
   scanning, and settings keep related controls in one continuous document. */
.page-analytics .chart{min-inline-size:0}
.page-collection .tablebox,.page-system .tablebox{inline-size:100%}
.page-settings>.editable-page,.page-workspace>.workspace-page{inline-size:100%}
.collection-page{min-inline-size:0}

.workspace-page{display:grid;grid-template-columns:10rem minmax(0,1fr);column-gap:var(--sp-6);min-inline-size:0}
.entity-workspace{display:contents}
.entity-header{grid-column:1/-1;display:flex;align-items:end;justify-content:space-between;gap:var(--sp-5);min-inline-size:0;border-block-end:var(--hairline) solid var(--line);padding-block-end:var(--sp-3)}
.workspace-meta{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:var(--sp-2);min-inline-size:0;color:var(--ink2)}
.workspace-context{display:inline-flex;align-items:center;min-block-size:var(--control-height-sm);padding-inline:var(--sp-3);border:var(--hairline) solid var(--line);border-radius:var(--r-ctrl);font-size:var(--fs-sm)}
.owner-tag{margin-inline-start:var(--sp-1);color:var(--ink3);font-size:var(--fs-xs);font-weight:400;vertical-align:middle;white-space:nowrap}
.workspace-nav{grid-column:1;grid-row:2;min-inline-size:0;padding-block-start:var(--sp-5);border-inline-end:var(--hairline) solid var(--line)}
.workspace-nav ul{display:flex;flex-direction:column;gap:var(--sp-half);list-style:none;margin:0;padding:0 var(--sp-4) 0 0}
.workspace-nav a{display:flex;align-items:center;min-block-size:var(--control-height-sm);padding-inline:var(--sp-3);border-inline-start:var(--rule) solid transparent;
  border-radius:0 var(--r-ctrl) var(--r-ctrl) 0;color:var(--ink2);font-size:var(--fs-md);text-decoration:none}
.workspace-nav a[aria-current="page"]{border-color:var(--accent);color:var(--accent);background:var(--surface);font-weight:600}
.workspace-nav a[aria-current="true"]{color:var(--ink);font-weight:600}
.workspace-nav ul ul{margin-block-start:var(--sp-1);padding:0 0 0 var(--sp-4)}
.workspace-page>.editable-page,.workspace-page>.workspace-body{grid-column:2;grid-row:2;padding-block-start:var(--sp-5);min-inline-size:0}
.workspace-nav a:active{background:var(--accent-soft)}

/* ---------- section card ---------- */
.content-section{display:grid;gap:0;min-inline-size:0;align-content:start;padding-block-end:var(--sp-4);
  background:var(--surface);border:var(--hairline) solid var(--line);border-radius:var(--r-panel);box-shadow:var(--shadow)}
.content-section>:not(.section-heading){margin:var(--sp-4) var(--sp-4) 0}
.section-heading{position:relative;display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-1) var(--sp-3);min-inline-size:0;
  min-block-size:calc(var(--target-min) + 2 * var(--sp-2));padding:var(--sp-2) var(--sp-4);background:var(--subtle);border-block-end:var(--hairline) solid var(--line);
  border-radius:var(--r-panel) var(--r-panel) 0 0}
.section-heading h2,.section-heading h3,.chart-head h3,.setting-intro>h2,.fact-row>h2{margin:0;max-inline-size:100%;
  color:var(--ink);font-size:var(--fs-md);line-height:var(--lh-snug);letter-spacing:var(--ls-caps);font-weight:700;
  text-transform:uppercase;overflow-wrap:anywhere}
/* The subtitle: the section's coverage text, beside the title in the band. */
.section-note{flex:1 1 auto;margin:0;min-inline-size:0;color:var(--ink3);font-size:var(--fs-sm);overflow-wrap:anywhere}
.section-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2);margin-inline-start:auto}
.section-state-note,.chart-empty{margin:0;color:var(--ink3);font-size:var(--fs-md)}
.section-heading>.section-state-note{font-size:var(--fs-sm)}
.chart-empty strong{color:var(--ink2);font-weight:600}
/* A section with no body is its head band alone. */
.content-section:has(>.section-heading:last-child){padding-block-end:0}
.content-section>.section-heading:last-child{border-block-end:0;border-radius:var(--r-panel)}
/* A folded section (.section-fold, a native details) is a card that opens: its head
   band is the summary, which keeps the native marker, with the latest state at the
   end edge. Closed, the card is its head band alone. */
.section-fold>summary{display:list-item;cursor:pointer}
.section-fold>summary>h2{display:inline}
.section-fold-state{float:inline-end;margin-inline-start:var(--sp-3);color:var(--ink2);font-size:var(--fs-sm);font-variant-numeric:tabular-nums}
.section-fold:not([open]){padding-block-end:0}
.section-fold:not([open])>summary{border-block-end:0;border-radius:var(--r-panel)}
/* A table or pager directly in the card runs to its edges. */
.content-section>.tablebox,.content-section>.request-pagination,.content-section>.table-pager{margin-inline:0;border-inline:0;border-radius:0}
.content-section>.section-heading+.tablebox{margin-block-start:0;border-block-start:0}
.content-section>.tablebox+.request-pagination,.content-section>.tablebox+.table-pager{margin-block-start:0;border-block-start:0}
.content-section>.tablebox:last-child,.content-section>.request-pagination:last-child,.content-section>.table-pager:last-child{border-block-end:0;border-radius:0 0 var(--r-panel) var(--r-panel)}
.content-section:has(>.tablebox:last-child),.content-section:has(>.request-pagination:last-child),.content-section:has(>.table-pager:last-child){padding-block-end:0}
.scope-section{padding-block-end:var(--sp-4)}
.plot-section{min-inline-size:0;padding:var(--sp-4);background:var(--surface);border:var(--hairline) solid var(--line);
  border-radius:var(--r-panel);box-shadow:var(--shadow)}

/* Shared editable pages present read state before full-width grouped actions. */
.editable-page{display:grid;gap:var(--sp-6);min-inline-size:0}
.page-intro{max-inline-size:var(--prose-max);margin:0;color:var(--ink2);font-size:var(--fs-md);text-wrap:pretty}
.read-sections{display:grid;gap:var(--sp-6);min-inline-size:0}
/* A page marks related charts Half to pair them in two columns: two cards side by
   side with the grid gap between them. The :not(.section-half) below keeps the
   full-width rule from outranking the half rule by specificity. */
.read-sections:has(.section-half){grid-template-columns:repeat(2,minmax(0,1fr));column-gap:var(--sp-6);row-gap:var(--sp-6)}
.read-sections:has(.section-half)>.content-section:not(.section-half){grid-column:1/-1}
.read-sections>.section-half{grid-column:auto;align-content:start}
.read-sections>.section-half:not(.section-half+.section-half):not(:has(+.section-half)){grid-column:1/-1}
.editable-overview .read-sections{grid-template-columns:repeat(2,minmax(0,1fr));
  column-gap:var(--sp-6);row-gap:var(--sp-6);align-items:stretch}
.editable-overview .content-section{align-content:start}
.editable-overview .section-full{grid-column:1/-1;--chart-height:9rem}

/* ---------- overview blocks ---------- */
/* A block heading sits above its cards with whitespace, not in a card. */
.overview-block{display:grid;gap:var(--sp-4);min-inline-size:0}
.overview-block-title{margin:0;color:var(--ink);font-size:var(--fs-md);font-weight:700;letter-spacing:var(--ls-caps);text-transform:uppercase;
  line-height:var(--lh-snug);overflow-wrap:anywhere}

/* ---------- css/kpi.css ---------- */
/* KPI tiles, the overview tile grid, meters and key-value lists.

   A KPI row (.metric-strip, from the metric-strip partial) is a grid of separate
   tiles: small upper-case key, monospace value with a muted unit, an optional
   detail line and sparkline, and the accent edge on the start side. */
.metric-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,9.375rem),1fr));gap:var(--sp-3);
  align-items:stretch;min-inline-size:0}
.metric{display:grid;row-gap:var(--sp-1);padding:var(--sp-3) var(--sp-4);align-content:start;min-inline-size:0;position:relative;
  background:var(--surface);border:var(--hairline) solid var(--line);border-radius:var(--r-panel);box-shadow:var(--shadow);overflow:clip}
.metric::before{content:"";position:absolute;inset-block:0;inset-inline-start:0;inline-size:var(--rule);background:var(--accent);opacity:.55}
/* Inside a card the tiles are flat, so a card does not carry a second shadow. */
.content-section .metric,.content-section .overview-summary{box-shadow:none;background:var(--subtle)}
.metric .label{font-size:var(--fs-xs);font-weight:700;color:var(--ink3);text-transform:uppercase;letter-spacing:var(--ls-caps);
  overflow-wrap:anywhere}
.metric .v{font-family:var(--font-data);font-weight:600;font-size:var(--fs-metric);line-height:var(--lh-snug);
  font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.metric .detail{font-size:var(--fs-sm);color:var(--ink3)}
.bigv .unit,.metric .v .unit{font-family:var(--font-ui);font-size:var(--fs-sm);color:var(--ink3);margin-inline-start:var(--sp-1);font-weight:500}
.sparkline{display:block;inline-size:100%;max-inline-size:7.5rem;block-size:var(--sp-7);margin-block-start:var(--sp-1)}
.editable-overview .metric-strip{grid-template-columns:repeat(2,minmax(0,1fr))}
.editable-overview .section-full .metric-strip{grid-template-columns:repeat(auto-fit,minmax(min(100%,11rem),1fr))}

/* The overview tile grid (tile-grid partial): the same tiles, each with a link. */
.overview-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:var(--sp-3);min-inline-size:0}
.overview-summary{display:flex;flex-direction:column;gap:var(--sp-2);min-inline-size:0;position:relative;
  min-block-size:8rem;padding:var(--sp-4);background:var(--surface);border:var(--hairline) solid var(--line);
  border-radius:var(--r-panel);box-shadow:var(--shadow)}
.overview-summary>h2,.overview-summary>h3{margin:0;color:var(--ink3);font-size:var(--fs-xs);text-transform:uppercase;
  letter-spacing:var(--ls-caps);line-height:var(--lh-body);font-weight:700}
.bigv{font-family:var(--font-data);font-weight:600;font-size:var(--fs-metric);line-height:var(--lh-flat);margin:var(--sp-half) 0 0;
  font-variant-numeric:tabular-nums}

/* A meter is drawn, not sized by a style attribute: the width of the filled bar
   is data and belongs in an SVG attribute the server writes. */
.meter{block-size:var(--dot);border-radius:var(--r-mark);background:var(--line);
  position:relative;overflow:clip;margin-block:var(--sp-1)}
.meter svg{position:absolute;inset:0;inline-size:100%;block-size:100%;display:block}
.meter .fill{fill:var(--accent)}
.meter .fill-allowed{fill:var(--band-allowed)}
.meter .fill-blocked{fill:var(--band-blocked)}
.meter .fill-challenged{fill:var(--band-challenged)}
.meter .fill-unknown{fill:var(--band-unknown-outcome)}

/* key-value list */
.kvlist{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,14rem),1fr));
  gap:var(--sp-3) var(--sp-6);margin:0;padding-block:var(--sp-1);min-inline-size:0}
.kvlist+.kvlist{margin-block-start:var(--sp-4);padding-block-start:var(--sp-4);border-block-start:var(--hairline) solid var(--line)}
.kv{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp-1) var(--sp-3);align-content:start;min-inline-size:0}
.kv dd{margin:0;min-inline-size:0;overflow-wrap:anywhere}

/* ---------- css/forms.css ---------- */
/* Settings groups, edit forms, write notices, the confirm dialog, the inline
   edit list and the list (chips) widget. */
/* The settings list is one card; its groups are ruled rows inside it, title and
   description on the start side, controls beside them. */
.settings-list{display:grid;min-inline-size:0;padding-inline:var(--sp-5);background:var(--surface);
  border:var(--hairline) solid var(--line);border-radius:var(--r-panel);box-shadow:var(--shadow)}
.settings-list>:last-child>.setting-group,.settings-list>.setting-group:last-child{border-block-end:0}
.setting-group{display:grid;grid-template-columns:minmax(min(12rem,30%),.42fr) minmax(0,1fr);
  gap:var(--sp-7);min-inline-size:0;padding-block:var(--sp-5);
  border-block-end:var(--hairline) solid var(--line)}
.setting-wide>.setting-group{grid-template-columns:minmax(0,1fr);gap:var(--sp-5)}
.setting-wide>.setting-group>.setting-intro>p{max-inline-size:72ch}
.setting-intro{align-self:start;min-inline-size:0}
.setting-intro>.status-badge{margin-block-start:var(--sp-2)}
.setting-intro>p{margin:var(--sp-2) 0 0;max-inline-size:var(--prose-max);color:var(--ink2);text-wrap:pretty;overflow-wrap:anywhere;min-inline-size:0}
/* The closed disclosure under a section description holds the long help; it stays as
   wide as the description. */
.setting-details{margin-block-start:var(--sp-3);max-inline-size:var(--prose-max)}
.setting-details>.edit-disclosure-body>.detail{margin:0;text-wrap:pretty}
.setting-controls{display:grid;gap:var(--sp-5);align-content:start;min-inline-size:0}
.fact-row{display:grid;grid-template-columns:minmax(10rem,.35fr) minmax(0,1fr);gap:var(--sp-5);
  padding-block:var(--sp-4);border-block-end:var(--hairline) solid var(--line);min-inline-size:0}
.fact-row>p{margin:0;overflow-wrap:anywhere}
.fact-info{padding-inline-start:var(--sp-4);border-inline-start:var(--rule) solid var(--accent)}
.fact-danger{padding-inline-start:var(--sp-4);border-inline-start:var(--rule) solid var(--error);background:var(--error-bg)}
.fact-danger>h2,.fact-danger>p{color:var(--error-ink)}
.edit-notice,.edit-group-error,.authnote{margin:0;padding:var(--sp-3) var(--sp-4);background:var(--error-bg);
  border:var(--hairline) solid var(--error-ink);border-radius:var(--r-ctrl);color:var(--error-ink);text-wrap:pretty}
.edit-notice{background:var(--warn-bg);border-color:var(--warn-ink);color:var(--warn-ink)}
.edit-notice[data-write-outcome="ok"]{background:var(--good-bg);border-color:var(--good-ink);color:var(--good-ink)}
.edit-notice[data-write-outcome="error"]{background:var(--error-bg);border-color:var(--error-ink);color:var(--error-ink)}
.edit-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,18rem),1fr));gap:var(--sp-4)}
.edit-form-inline{display:flex;flex-wrap:wrap;align-items:flex-end}
.edit-form-inline>.form-field{flex:1 1 12rem;min-inline-size:0}
.edit-form-inline>.form-actions{flex:1 0 100%}
/* A form of checkboxes only stacks them in one column with its button under them,
   so two switches do not spread across a grid made for labelled inputs. */
.edit-form:has(>.form-checkbox):not(:has(>.form-field:not(.form-checkbox))){grid-template-columns:minmax(0,1fr);row-gap:var(--sp-2)}
.edit-form:has(>.form-checkbox):not(:has(>.form-field:not(.form-checkbox)))>.form-checkbox{grid-row:auto;grid-template-rows:none}
.edit-form:has(>.form-checkbox):not(:has(>.form-field:not(.form-checkbox)))>.form-checkbox>*{grid-row:auto}
.edit-form:has(>.form-checkbox):not(:has(>.form-field:not(.form-checkbox)))>.form-actions{justify-content:flex-start;padding-block-start:var(--sp-2)}
.edit-fieldset{display:grid;gap:var(--sp-4);min-inline-size:0;margin:0;padding:var(--sp-5) 0 0;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));
  border:0;border-block-start:var(--hairline) solid var(--line)}
.edit-fieldset>legend{padding-inline-end:var(--sp-3);color:var(--ink2);font-size:var(--fs-lg);font-weight:600;max-inline-size:100%;overflow-wrap:anywhere}
.edit-fieldset,.edit-disclosure,.form-actions,.form-field:has(textarea){grid-column:1/-1}
.edit-fieldset>legend,.edit-fieldset .form-actions,.edit-fieldset>.edit-form{grid-column:1/-1}
.form-field,.field{display:grid;gap:var(--sp-2);min-inline-size:0;align-content:start}
.edit-form>.form-field:not(:has(textarea)):not(.list-widget),.edit-fieldset>.form-field:not(:has(textarea)):not(.list-widget){grid-row:span 3;grid-template-rows:subgrid;row-gap:var(--sp-2)}
.edit-form>.form-checkbox>.input,.edit-form>.form-checkbox>span,.edit-fieldset>.form-checkbox>.input,.edit-fieldset>.form-checkbox>span{grid-row:2}
.edit-form>.form-checkbox>small,.edit-fieldset>.form-checkbox>small{grid-row:3}
.form-field>small{color:var(--ink3);font-size:var(--fs-md);text-wrap:pretty}
/* One help line for each option of a select, such as the SYN shield modes. */
.help-lines{display:grid;gap:var(--sp-1)}
.field-label{display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--sp-3);min-inline-size:0}
.field-label>small{color:var(--ink3);font-size:var(--fs-md);line-height:var(--lh-flat)}
.form-checkbox{grid-template-columns:auto 1fr;align-items:center;min-block-size:var(--control-height);
  column-gap:var(--sp-3)}
.form-checkbox>small{grid-column:2}
.form-checkbox>span{font-size:var(--fs-md);font-weight:400}
.form-checkbox .input{inline-size:0.875rem;min-block-size:0.875rem;padding:0;accent-color:var(--accent)}
.form-switch .input{appearance:none;-webkit-appearance:none;inline-size:var(--switch-width);block-size:var(--target-min);
  min-block-size:var(--target-min);padding:0;border:0;border-radius:var(--r-chip);background:var(--line-strong);
  position:relative;cursor:pointer;transition:background var(--dur-fast) ease}
.form-switch .input::before{content:"";position:absolute;inset-block-start:calc((var(--target-min) - var(--knob)) / 2);inset-inline-start:var(--sp-1);
  inline-size:var(--knob);block-size:var(--knob);border-radius:var(--r-chip);background:var(--on-accent);transition:translate var(--dur-fast) ease}
.form-switch .input:checked{background:var(--accent)}
.form-switch .input:checked::before{translate:calc(var(--switch-width) - var(--knob) - 2 * var(--sp-1)) 0}
/* A segmented choice (EditField.Segmented): one row of joined radio buttons, the
   chosen one on the muted fill with the accent underline; an option that needs
   another control on stays visible and greyed (form.js enables it). */
.seg-choice{display:inline-flex;flex-wrap:wrap;justify-self:start;max-inline-size:100%}
.seg-choice-option{position:relative;display:inline-flex;align-items:center;min-block-size:var(--control-height);padding-inline:var(--sp-3);
  border:var(--hairline) solid var(--line-strong);color:var(--ink2);cursor:pointer}
.seg-choice-option+.seg-choice-option{border-inline-start:0}
.seg-choice-option:first-child{border-start-start-radius:var(--r-ctrl);border-end-start-radius:var(--r-ctrl)}
.seg-choice-option:last-child{border-start-end-radius:var(--r-ctrl);border-end-end-radius:var(--r-ctrl)}
.seg-choice-option>input{position:absolute;inset:0;inline-size:100%;block-size:100%;margin:0;appearance:none;-webkit-appearance:none;
  background:none;border:0;border-radius:inherit;cursor:inherit}
.seg-choice-option:hover{color:var(--ink);background:var(--subtle)}
.seg-choice-option:has(>input:checked){background:var(--subtle);color:var(--ink);font-weight:600;box-shadow:inset 0 calc(-2 * var(--hairline)) 0 var(--accent)}
.seg-choice-option:has(>input:disabled){color:var(--ink3);background:none;cursor:not-allowed}
/* A number with its unit (EditField.Unit): the unit stands muted after the input. */
.input-unit{display:flex;align-items:center;gap:var(--sp-2);min-inline-size:0}
.input-unit>.input{flex:1 1 auto;min-inline-size:0}
.input-unit-text{color:var(--ink3);font-size:var(--fs-sm);white-space:nowrap}
.form-field[hidden]{display:none}
.edit-fieldset[hidden]{display:none}
/* Toggle chips: one wrapping row of checkbox pills, the checked ones in the accent. */
.edit-chips{display:flex;flex-wrap:wrap;gap:var(--sp-2)}
.edit-chips>legend{flex:1 0 100%;margin-block-end:var(--sp-2)}
.edit-chips>.form-checkbox{display:inline-flex;align-items:center;gap:var(--sp-2);min-block-size:var(--target-min);
  padding-inline:var(--sp-3);border:var(--hairline) solid var(--line-strong);border-radius:var(--r-chip);cursor:pointer}
.edit-chips>.form-checkbox:has(.input:checked){border-color:var(--accent);background:var(--accent-soft)}
.confirm-pop{position:fixed;inset:auto;top:50%;left:50%;translate:-50% -50%;margin:0;
  inline-size:min(28rem,calc(100vw - 2 * var(--sp-4)));max-block-size:calc(100dvh - 2 * var(--sp-4));overflow:auto;
  padding:var(--sp-6);border:var(--hairline) solid var(--line);border-radius:var(--r-panel);
  background:var(--surface);color:var(--ink);box-shadow:var(--shadow);
  white-space:normal;text-align:start;font-size:var(--fs-md);
  opacity:1;scale:1;transition:opacity var(--dur-fast) ease,scale var(--dur-fast) ease}
.confirm-pop[open]{display:grid;gap:var(--sp-4);align-content:start;border-color:var(--accent);
  box-shadow:0 0 0 var(--sp-1) var(--accent-soft),var(--shadow)}
.confirm-pop::backdrop{background:var(--backdrop)}
.confirm-title{margin:0;color:var(--ink);font-size:var(--fs-lg);font-weight:600;overflow-wrap:anywhere}
.confirm-text{margin:0;color:var(--ink2);text-wrap:pretty}
.confirm-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--sp-2);padding-block-start:var(--sp-3);border-block-start:var(--hairline) solid var(--line)}
@starting-style{
  .confirm-pop[open]{opacity:0;scale:.96}
}
.edit-disclosure{border:var(--hairline) solid var(--line-strong);border-radius:var(--r-ctrl)}
.edit-disclosure>summary{display:list-item;min-block-size:var(--control-height);
  padding:var(--sp-2) var(--sp-3);cursor:pointer;font-weight:600}
.edit-disclosure-body{display:grid;gap:var(--sp-2);padding:var(--sp-3)}
.edit-group-disclosure+.edit-group-disclosure{margin-block-start:var(--sp-3)}
.edit-group-disclosure>.edit-disclosure-body{gap:var(--sp-4)}
.edit-group-error{padding:var(--sp-2) var(--sp-3)}
.form-actions{display:flex;align-items:flex-start;justify-content:flex-end;gap:var(--sp-2);flex-wrap:wrap}
.form-actions .btn--primary{order:1}
.setting-controls>.edit-form>.form-actions>.btn{max-inline-size:100%;white-space:normal}
.edit-form[data-unsaved]>.form-actions{position:sticky;inset-block-end:0;z-index:var(--z-sticky);align-items:center;
  padding-block:var(--sp-3);background:var(--surface);border-block-start:var(--hairline) solid var(--warn-ink)}
.form-unsaved-note:empty{display:none}
.form-actions .form-unsaved-note{order:2}
.form-unsaved-note{color:var(--warn-ink);font-size:var(--fs-md);font-weight:600}
/* The inline edit list: the URL column takes the width, the number, state and actions
   columns shrink to their content, and each row keeps its actions on one line. The
   actions cell sticks to the end edge, which only shows when the table scrolls. */
.edit-list td{vertical-align:middle}
.edit-list th:not(:first-child),.edit-list td:not(:first-child){inline-size:1%;white-space:nowrap}
.edit-list .table-actions{position:sticky;inset-inline-end:0;background:var(--surface)}
.edit-list thead .table-actions{box-shadow:inset 0 calc(-1 * var(--hairline)) 0 var(--line-strong)}
/* The start edge of the pinned actions shows only while the table scrolls: the scroll
   timeline of the table box is inactive when nothing overflows, so the rule stays at
   opacity 0, and it fades out once the table is scrolled to its end. */
.edit-list .table-actions::before{content:"";position:absolute;inset-block:0;inset-inline-start:0;
  border-inline-start:var(--hairline) solid var(--line-strong);opacity:0;pointer-events:none;
  animation:edit-list-edge linear both;animation-timeline:scroll(nearest inline)}
@keyframes edit-list-edge{0%,99%{opacity:1}100%{opacity:0}}
.edit-list-actions{display:flex;align-items:center;justify-content:flex-end;gap:var(--sp-2)}
.edit-list .table-actions .edit-form{display:flex;justify-content:flex-end}
.edit-list .table-actions .form-actions{flex-wrap:nowrap;justify-content:flex-end}
.edit-list [data-edit-open]{min-block-size:var(--control-height-sm);padding-inline:var(--sp-2);font-size:var(--fs-sm)}
.edit-list tr[data-edit-row]:not([data-editing]) .edit-row-input,.edit-list tr[data-edit-row]:not([data-editing]) .edit-row-form{display:none}
.edit-list tr[data-editing] .edit-row-text,.edit-list tr[data-editing] .edit-row-view{display:none}
.edit-list tr[data-editing]>td{background:linear-gradient(var(--accent-soft),var(--accent-soft)) var(--surface)}
.edit-list tr[data-editing]>td:first-child{box-shadow:inset var(--rule) 0 0 var(--accent)}
.edit-list .edit-row-new>td{border-block-start:var(--hairline) dashed var(--line-strong)}
.edit-list .edit-row-input{display:grid;gap:var(--sp-1);justify-items:start}
.edit-list td:first-child .edit-row-input{justify-items:stretch}
.edit-list td select.input{inline-size:auto}
.edit-list td .input[type="url"]{inline-size:100%;min-inline-size:20ch;max-inline-size:none}
.edit-list td .input[type="number"]{inline-size:calc(8ch + 2 * var(--sp-3));font-variant-numeric:tabular-nums}
.edit-list td.n .edit-row-input{justify-items:end}
.edit-row-check{display:inline-flex;align-items:center;gap:var(--sp-2);min-block-size:var(--control-height);white-space:nowrap}
.edit-row-check>.input{inline-size:0.875rem;min-block-size:0.875rem;padding:0;accent-color:var(--accent)}
.edit-row-help{display:block;max-inline-size:30ch;white-space:normal;color:var(--ink3);font-size:var(--fs-sm)}
.edit-list .edit-row-form[data-unsaved]>.form-actions{position:static;padding-block:0;border-block-start:0;background:none}
.edit-list .form-unsaved-note{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.edit-list .table-actions .form-actions>.btn{inline-size:auto;min-block-size:var(--control-height-sm);padding-inline:var(--sp-2);font-size:var(--fs-sm);white-space:nowrap}

.list-widget{grid-column:1/-1;display:grid;align-content:start;gap:var(--sp-2);min-inline-size:0}
.list-widget-box{display:flex;align-items:center;inline-size:100%;max-inline-size:32rem;min-block-size:var(--control-height);padding-inline:var(--sp-1);background:var(--surface);border:var(--hairline) solid var(--line-strong);border-radius:var(--r-ctrl)}
.list-widget-box:focus-within{border-color:var(--accent)}
.list-widget-box[data-invalid]{border-color:var(--error)}
.list-widget-input{flex:1 1 auto;min-inline-size:0;min-block-size:var(--control-height-sm);border:0;background:transparent;color:var(--ink);font:inherit;font-size:var(--fs-md);padding:0 var(--sp-2)}
.list-widget-foot{display:flex;justify-content:space-between;align-items:start;gap:var(--sp-2);max-inline-size:32rem;min-block-size:1.4em;font-size:var(--fs-md);line-height:var(--lh-body);color:var(--ink3)}
.list-widget-error{color:var(--error-ink)}
.list-widget-count{margin-inline-start:auto;font-variant-numeric:tabular-nums}
.list-widget-chips{display:flex;flex-wrap:wrap;align-content:flex-start;gap:var(--sp-1);list-style:none;margin:0;padding:0;max-block-size:12rem;overflow-y:auto}
.list-widget-chips:empty{display:none}
.list-chip{display:inline-flex;gap:var(--sp-1);color:var(--ink);border:var(--hairline) solid var(--line)}
.list-chip[aria-invalid="true"]{border-color:var(--error);background:var(--error-bg);color:var(--error-ink)}
.list-flag{font-size:var(--fs-lg);line-height:var(--lh-flat)}
.list-chip-remove{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--target-min);block-size:var(--target-min);min-block-size:var(--target-min)}
.table-actions{white-space:nowrap}
.inline-action{position:relative;display:flex;flex-wrap:nowrap;align-items:center;gap:var(--sp-2)}
/* A switch with its own numbers (edit-switch): the switch, a summary of its stored
   overrides and the opener, then its overrides dialog. Without JavaScript the
   dialog stands open inline under the switch; console.js turns it into a modal. */
.switch-row{grid-column:1/-1;display:grid;gap:var(--sp-2);min-inline-size:0}
.override-control{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp-2);margin:0}
.override-summary{color:var(--ink3);font-size:var(--fs-sm)}
.override-dialog{position:static;inline-size:auto;max-inline-size:none;margin:0;padding:0;border:0;background:none;color:inherit}
.override-dialog[open]{display:grid;gap:var(--sp-4);grid-template-columns:minmax(0,1fr)}
.override-dialog-head,.override-dialog .form-actions,.override-dialog .form-field:has(textarea){grid-column:1/-1}
.override-dialog-head{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-4)}
.override-dialog-title{margin:0;color:var(--ink2);font-size:var(--fs-md);font-weight:600;overflow-wrap:anywhere}
.override-dialog:modal{position:fixed;inset:auto;top:50%;left:50%;translate:-50% -50%;
  inline-size:min(40rem,calc(100vw - 2 * var(--sp-4)));max-block-size:calc(100dvh - 2 * var(--sp-4));overflow:auto;
  padding:var(--sp-6);border:var(--hairline) solid var(--accent);border-radius:var(--r-panel);
  background:var(--bg);color:var(--ink);box-shadow:var(--shadow);white-space:normal;text-align:start}
.override-dialog::backdrop{background:var(--backdrop)}
/* A descriptor form row pair (edit-row): a rate and its burst, or a low and a high
   port, side by side above the small breakpoint (responsive.css), stacked under it.
   A pair whose fields are both hidden takes no row. */
.field-pair{grid-column:1/-1;display:grid;gap:var(--sp-4);min-inline-size:0}
.field-pair:not(:has(>.form-field:not([hidden]))){display:none}

/* ---------- css/charts.css ---------- */
/* Charts: the uPlot mount and its drag-zoom brush, the hover tip, the chart head
   with info caption and legend, the donut, and the band colour classes. The
   canvas axes (monospace ticks, faint dashed grid, "now" at the end) are drawn by
   chart.js from the --axis, --gridline, --chart-* and --font-data tokens. */
.chart{display:grid;gap:var(--sp-2);min-inline-size:0;position:relative}
.chart-probe{position:absolute;inline-size:0;block-size:0;visibility:hidden}
/* CSS owns the bounded desktop/mobile plot height and chart.js mirrors this
   token into uPlot. */
.uplot-mount{inline-size:100%;min-block-size:var(--chart-height);min-inline-size:0}
/* uPlot's own stylesheet sets .uplot{width:min-content}; cap it to the mount so
   the column, not the content, sets the width. */
.uplot-mount .uplot{inline-size:100%;max-inline-size:100%}
/* uPlot's default drag-select overlay is invisible on a dark ground, so the accent
   fill paints the zoom brush in both themes. No border: uPlot keeps this element at
   0x0 at the origin when idle, where a border paints a 1px dot. */
.uplot-mount .u-select{background:var(--accent-soft);border:none;
  box-shadow:inset var(--hairline) 0 0 var(--accent),inset calc(-1 * var(--hairline)) 0 0 var(--accent)}
.chart-tip{position:absolute;inset-block-start:0;inset-inline-start:0;z-index:var(--z-raised);pointer-events:none;
  inline-size:max-content;max-inline-size:100%;overflow-wrap:anywhere;color:var(--ink);background:var(--surface);
  border:var(--hairline) solid var(--line-strong);border-radius:var(--r-ctrl);box-shadow:var(--shadow);
  padding:var(--sp-2) var(--sp-3);font-family:var(--font-ui);font-size:var(--fs-sm)}
.chart-tip[hidden]{display:none}
.chart-tip-time{color:var(--ink2);font-weight:600;margin-block-end:var(--sp-1)}
.chart-tip-row,.donut-legend li{display:flex;align-items:center;gap:var(--sp-2)}
.chart-tip-key{inline-size:var(--key-size);block-size:var(--key-size);border-radius:var(--r-mark);flex:none}
.chart-tip-value{margin-inline-start:auto;padding-inline-start:var(--sp-3);color:var(--ink);font-family:var(--font-data);font-variant-numeric:tabular-nums}
.chart-head{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;margin-block:0 var(--sp-2)}
.chart+.chart{margin-block-start:var(--sp-5)}
/* Two charts side by side (SectionBody.PairCharts), in equal tracks; they stack
   under the medium breakpoint, as a Half pair does. */
.chart-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-5) var(--sp-6);align-items:start}
.chart-pair-item{min-inline-size:0}
.chart-cap{color:var(--ink3);font-size:var(--fs-md);margin:0}
.chart-info>summary{display:inline-flex;align-items:center;justify-content:center;inline-size:var(--target-min);block-size:var(--target-min);
  border:var(--hairline) solid var(--line-strong);border-radius:var(--r-chip);color:var(--ink3);font-size:var(--fs-sm);
  font-weight:600;line-height:var(--lh-flat);cursor:pointer;list-style:none;user-select:none}
.chart-info>summary::-webkit-details-marker{display:none}
.chart-info>summary:active{background:var(--subtle)}
.chart-info[open]>.chart-cap{position:absolute;inset-inline-end:0;inline-size:min(28rem,100%);z-index:var(--z-tools);
  padding:var(--sp-3);background:var(--surface);border:var(--hairline) solid var(--line-strong);border-radius:var(--r-ctrl)}
.section-heading .chart-info[open]>.chart-cap{inset-block-start:100%;inset-inline:var(--sp-4) auto;inline-size:min(28rem,calc(100% - 2 * var(--sp-4)))}
.legend{display:flex;flex-wrap:wrap;gap:var(--sp-2) var(--sp-4);font-size:var(--fs-sm);color:var(--ink3);align-items:center}
/* An interactive legend entry is a plain text button: a click shows only its
   series, a right-click hides it. A hidden entry fades and its square goes hollow. */
.legend-entry{display:inline-flex;align-items:center;min-block-size:var(--target-min);padding:0;border:0;background:none;
  color:inherit;font:inherit;cursor:pointer}
.legend-entry[aria-pressed="false"]{opacity:.55;text-decoration:line-through}
.legend-entry[aria-pressed="false"] .key{background:none;box-shadow:inset 0 0 0 var(--hairline) var(--ink3)}
.key{inline-size:var(--key-size);block-size:var(--key-size);border-radius:var(--r-mark);display:inline-block;margin-inline-end:var(--sp-1);vertical-align:baseline}
/* Donut share chart. Each segment carries a seg-N class holding its colour in
   --seg, used for both the ring path fill and the legend swatch; the surface
   stroke between segments is the shape edge SC 1.4.11 is satisfied by. */
.donut-chart{margin-block-start:var(--sp-2)}
.donut-wrap{display:flex;gap:var(--sp-6);align-items:center;flex-wrap:wrap}
.donut-svg{inline-size:7.5rem;block-size:7.5rem;flex:none}
.donut-seg{fill:var(--seg);stroke:var(--surface);stroke-width:.6}
.donut-legend{list-style:none;margin:0;padding:0;display:grid;gap:var(--sp-2);font-size:var(--fs-md);min-inline-size:12rem}
.donut-legend .donut-label{font-family:var(--font-data);overflow-wrap:anywhere}
.donut-legend .donut-share{color:var(--ink3);font-variant-numeric:tabular-nums;margin-inline-start:auto}
.donut-legend .key{background:var(--seg)}
.seg-0{--seg:var(--seg-0)}
.seg-1{--seg:var(--seg-1)}
.seg-2{--seg:var(--seg-2)}
.seg-3{--seg:var(--seg-3)}
.seg-4{--seg:var(--seg-4)}
.seg-5{--seg:var(--seg-5)}
.k-alw{background:var(--band-allowed)}
.k-chl{background:var(--band-challenged)}
.k-blk{background:var(--band-blocked)}
.k-in{background:var(--band-in)}
.k-out{background:var(--band-out)}
.k-unko{background:var(--band-unknown-outcome)}
.k-tcp{background:var(--band-tcp)}
.k-udp{background:var(--band-udp)}
.k-icmp{background:var(--band-icmp)}
.k-poth{background:var(--band-proto-other)}
.k-otc{background:var(--band-outcome)}
.k-coh{background:var(--band-tls-coherent)}
.k-con{background:var(--band-tls-contradicted)}
.k-unk{background:var(--band-tls-unknown)}
.k-nic0{background:var(--band-nic-0)}
.k-nic1{background:var(--band-nic-1)}
.k-nic2{background:var(--band-nic-2)}
.k-nic3{background:var(--band-nic-3)}
.k-nico{background:var(--band-nic-other)}
.f-blk{stop-color:var(--fill-blocked)}
.f-chl{stop-color:var(--fill-challenged)}
.f-alw{stop-color:var(--fill-allowed)}
.f-in{stop-color:var(--fill-in)}
.f-out{stop-color:var(--fill-out)}
.f-unko{stop-color:var(--fill-unknown-outcome)}
.f-tcp{stop-color:var(--fill-tcp)}
.f-udp{stop-color:var(--fill-udp)}
.f-icmp{stop-color:var(--fill-icmp)}
.f-poth{stop-color:var(--fill-proto-other)}
.f-otc{stop-color:var(--fill-outcome)}
.f-coh{stop-color:var(--fill-tls-coherent)}
.f-con{stop-color:var(--fill-tls-contradicted)}
.f-unk{stop-color:var(--fill-tls-unknown)}
.f-nic0{stop-color:var(--fill-nic-0)}
.f-nic1{stop-color:var(--fill-nic-1)}
.f-nic2{stop-color:var(--fill-nic-2)}
.f-nic3{stop-color:var(--fill-nic-3)}
.f-nico{stop-color:var(--fill-nic-other)}
/* Draw the lines after the fills and keep their interiors unpainted. */
.series-line{fill:none;stroke-width:2;stroke-linejoin:round;vector-effect:non-scaling-stroke}
.series-line.f-alw{stroke:var(--band-allowed)}
.series-line.f-chl{stroke:var(--band-challenged)}
.series-line.f-blk{stroke:var(--band-blocked)}
.series-line.f-in{stroke:var(--band-in)}
.series-line.f-out{stroke:var(--band-out)}
.series-line.f-unko{stroke:var(--band-unknown-outcome)}
.series-line.f-tcp{stroke:var(--band-tcp)}
.series-line.f-udp{stroke:var(--band-udp)}
.series-line.f-icmp{stroke:var(--band-icmp)}
.series-line.f-poth{stroke:var(--band-proto-other)}
.series-line.f-otc{stroke:var(--band-outcome)}
/* A sparkline line carries its outcome pattern as a class, as the canvas charts do. */
.series-line.line-dash{stroke-dasharray:var(--chart-dash)}
.series-line.line-dot{stroke-dasharray:var(--chart-dot)}
.series-line.line-dashdot{stroke-dasharray:var(--chart-dashdot)}
.series-line.f-coh{stroke:var(--band-tls-coherent)}
.series-line.f-con{stroke:var(--band-tls-contradicted)}
.series-line.f-unk{stroke:var(--band-tls-unknown)}
.series-line.f-nic0{stroke:var(--band-nic-0)}
.series-line.f-nic1{stroke:var(--band-nic-1)}
.series-line.f-nic2{stroke:var(--band-nic-2)}
.series-line.f-nic3{stroke:var(--band-nic-3)}
.series-line.f-nico{stroke:var(--band-nic-other)}
.zoom-chip,.list-chip{align-items:center;font-size:var(--fs-sm);background:var(--surface);
  border-radius:var(--r-chip);padding:var(--sp-half) var(--sp-1) var(--sp-half) var(--sp-2)}
.zoom-chip{position:absolute;inset-block-start:var(--sp-1);inset-inline-start:var(--sp-2);display:flex;gap:var(--sp-2);
  border:var(--hairline) solid var(--line-strong)}
.zoom-chip[hidden]{display:none}


/* The legend square of a dashed series carries a pattern fill in its colour, so
   the outcomes of one protocol stay apart in the legend: a solid square for
   allowed, dots for challenged, stripes for dropped, upright stripes for unknown. */
.k-tcp{color:var(--band-tcp)}
.k-udp{color:var(--band-udp)}
.k-icmp{color:var(--band-icmp)}
.k-poth{color:var(--band-proto-other)}
.k-otc{color:var(--band-outcome)}
.key.key-dash{background:repeating-linear-gradient(135deg,currentColor 0 var(--sp-half),transparent var(--sp-half) var(--sp-1))}
.key.key-dot{background:radial-gradient(circle,currentColor 45%,transparent 50%) 0 0/var(--sp-1) var(--sp-1)}
.key.key-dashdot{background:repeating-linear-gradient(90deg,currentColor 0 var(--sp-half),transparent var(--sp-half) var(--sp-1))}

/* ---------- css/tables.css ---------- */
/* Tables and pagers.

   A table (table partial) is framed: small upper-case heads on a head band,
   hairline rows, a hover tint. A Paged table carries data-pager and console.js
   adds the .table-pager band under it: "x-y of z", the page size (10, 25, 50,
   100) and the previous/next controls. A server-paged list renders the
   pagination partial, the same band with links. */
.tablebox{overflow-x:auto;min-inline-size:0;background:var(--surface);border:var(--hairline) solid var(--line);border-radius:var(--r-panel)}
.dtable{border-collapse:collapse;inline-size:100%;min-inline-size:0;font-size:var(--fs-md)}
.dtable caption{text-align:start}
.tablebox>.dtable>caption:not(.sr-only){padding:var(--sp-2) var(--sp-3);font-size:var(--fs-sm);font-weight:600;color:var(--ink2);border-block-end:var(--hairline) solid var(--line)}
.dtable th{text-align:start;font-weight:700;color:var(--ink3);font-size:var(--fs-xs);letter-spacing:var(--ls-caps);text-transform:uppercase;
  padding:var(--sp-2) var(--sp-3);border-block-end:var(--hairline) solid var(--line);white-space:nowrap;
  background:var(--subtle)}
.dtable td{padding:var(--sp-2) var(--sp-3);border-block-start:var(--hairline) solid var(--line);vertical-align:baseline}
.dtable tbody tr{transition:background-color var(--dur-fast)}
.dtable tbody tr:active{background:color-mix(in oklab,var(--ink) 7%,transparent)}
.dtable tbody tr:first-child td{border-block-start:0}
.dtable .n{text-align:end;font-family:var(--font-data);font-variant-numeric:tabular-nums;white-space:nowrap}
.dtable th.n{font-family:var(--font-ui)}
.dtable .mono{white-space:nowrap;font-size:var(--fs-md)}
.clip{max-inline-size:40ch;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom}
.shortcell .clip{max-inline-size:24ch}
.badcell{color:var(--error);font-weight:600}
.badcell::before{content:"\25B2\00a0";font-size:var(--fs-sm)}
.warncell{color:var(--warn-ink);font-weight:600}
.endcell{text-align:end;white-space:nowrap}
.mutedcell{color:var(--ink3)}
.linescell{white-space:pre-line}
.dtable td.childcell{padding-inline-start:var(--sp-6)}
.dtable tbody tr[data-collapsed]{display:none}
.dtable .row-group th{padding:0;border-block-end:0;border-block-start:var(--hairline) solid var(--line);background:var(--subtle);color:var(--ink)}
.row-group-toggle{display:flex;align-items:center;gap:var(--sp-3);inline-size:100%;min-block-size:var(--control-height);padding:var(--sp-1) var(--sp-2);border:0;background:none;color:inherit;font-weight:600;text-align:start}
.row-group-toggle::before,.row-toggle{flex:none;inline-size:0.5rem;block-size:0.5rem;background:currentColor;clip-path:polygon(30% 0,30% 100%,80% 50%)}
.row-group-toggle::before{content:"";rotate:90deg;transition:rotate var(--dur-fast)}
.row-group-toggle[aria-expanded="false"]::before{rotate:0deg}
.row-group-detail{font-weight:400;color:var(--ink3);font-size:var(--fs-md)}
.edit-grid-block{grid-column:1/-1;min-inline-size:0}
.edit-grid-block>.tablebox{position:relative}
.edit-grid td{padding:var(--sp-1) var(--sp-2);vertical-align:middle}
.edit-grid .edit-grid-ipv4{inline-size:1%;white-space:nowrap}
.edit-grid .edit-grid-ipv4 .input{inline-size:calc(15ch + 2 * var(--sp-3) + 2 * var(--hairline));font-variant-numeric:tabular-nums}
.edit-grid .edit-grid-note{inline-size:100%}
.edit-grid .edit-grid-note .input{min-inline-size:10ch}
.edit-grid th[scope=row]{width:var(--sp-7);vertical-align:middle}
.edit-grid tr[hidden]{display:none}
.edit-grid-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-3);padding-block-start:var(--sp-2)}
.edit-grid-count{margin:0;color:var(--ink3);font-size:var(--fs-md);font-variant-numeric:tabular-nums}
tr:has(.badcell)>td{background:color-mix(in oklab,var(--error) 6%,transparent)}

/* A row the pager puts off the current page. The filter hides rows with the
   hidden attribute; the pager keeps its own mark so the two never undo each other. */
.dtable tbody tr[data-pager-off]{display:none}

/* ---------- pagers ---------- */
.request-pagination,.table-pager{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2) var(--sp-3);
  padding:var(--sp-2) var(--sp-3);background:var(--subtle);border:var(--hairline) solid var(--line);border-radius:var(--r-panel);
  color:var(--ink3);font-size:var(--fs-sm)}
.request-pagination>span,.table-pager>output{font-variant-numeric:tabular-nums}
.request-pagination .pager-range,.table-pager>output{margin-inline-end:auto}
.request-pagination .btn,.table-pager .btn{min-block-size:var(--control-height-sm);padding-inline:var(--sp-3);font-size:var(--fs-sm)}
.table-pager label{display:inline-flex;align-items:center;gap:var(--sp-2)}
.table-pager select.input{inline-size:auto;min-block-size:var(--control-height-sm);font-size:var(--fs-sm)}
.table-pager[hidden]{display:none}
/* Client-side row filter above a long table. console.js hides non-matching rows. */
.table-filter{display:block;max-inline-size:26rem}
/* Two tables side by side (SectionBody.PairTables): equal tracks, equal frame
   heights, and the same column proportions in both, so a row reads across. */
.table-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--sp-4);align-items:stretch}
.table-pair-item{display:grid;min-inline-size:0}
.table-pair .dtable th:first-child{inline-size:40%}
.table-pair .dtable th.n{inline-size:20%}
/* An external link in a cell (an AS number to bgp.tools): muted, underlined on hover. */
.ext-link{color:var(--ink2);text-decoration:none}
.ext-link:hover{color:var(--accent);text-decoration:underline}
/* Entry list editor (partials/entry_list.gohtml): the IP blocklist and the filter
   bypasses of one target. The table runs flush in its card; the count leads the
   head band. */
.entry-list .entry-count{font-size:var(--fs-lg);font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}
.entry-list-tools{display:grid;gap:var(--sp-3);min-inline-size:0}
.entry-list-intro,.entry-match{margin:0;color:var(--ink2)}
.entry-search{display:flex;flex-wrap:wrap;align-items:end;gap:var(--sp-3);min-inline-size:0}
.entry-search>.form-field{flex:1 1 16rem;min-inline-size:0}
.entry-search-actions,.entry-row-actions,.entry-confirm-form{display:flex;flex-wrap:wrap;align-items:end;gap:var(--sp-2)}
.entry-row-actions{justify-content:flex-end;flex-wrap:nowrap}
.content-section>.entry-list-form{margin-inline:0}
.entry-list-form>.tablebox{border-inline:0;border-radius:0}
.entry-table .entry-check{inline-size:var(--sp-7)}
.entry-table .entry-empty{color:var(--ink3)}
.entry-table .entry-editing>td{background:var(--accent-soft)}
.entry-list-foot{display:flex;padding:var(--sp-3) var(--sp-4) 0}
.entry-add-body{display:grid;gap:var(--sp-4);min-inline-size:0}
.entry-rejected{display:grid;gap:var(--sp-2);padding:var(--sp-3) var(--sp-4);background:var(--warn-bg);color:var(--warn-ink);
  border-inline-start:var(--rule) solid var(--warn)}
.entry-rejected>.eyebrow{margin:0;color:var(--warn-ink)}
.entry-rejected-list{margin:0;padding-inline-start:var(--sp-5);max-block-size:12rem;overflow:auto}
.entry-confirm-text{display:grid;gap:var(--sp-2);padding-block:var(--sp-3)}
.entry-confirm-text>h2,.entry-confirm-text>p{margin:0}
.entry-confirm-form>.edit-form{flex:1 1 16rem;min-inline-size:0}
/* The Minecraft tab: the server icon beside the status facts, and a kick and a
   ban form side by side at the end of each player row. */
.mc-status-body{display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--sp-5)}
.mc-favicon{flex:none;image-rendering:pixelated}
.mc-status-body>.kvlist{flex:1 1 20rem;min-inline-size:0}
.mc-row-actions{display:flex;flex-wrap:nowrap;justify-content:flex-start;align-items:center;gap:var(--sp-2)}
/* The ban duration hangs on the Ban button as one control. */
.inline-action.mc-ban-row{gap:0}
.mc-ban-row>.btn{border-start-end-radius:0;border-end-end-radius:0}
.mc-ban-row>.mc-ban-duration{inline-size:auto;min-block-size:var(--control-height-sm);padding-block:0;font-size:var(--fs-sm);border-inline-start:0;border-start-start-radius:0;border-end-start-radius:0}

/* ---------- css/states.css ---------- */
/* Live regions, the refresh indicator, the stale bar, the refresh-failed mark,
   the filter bar and the error-state facts. */
/* filter bar */
.filterbar{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap}

/* The three designed error states. The two error pages carry no navigation, so
   .screen holds the header and the main area directly rather than through
   .s-grid, and the facts a retention refusal names are a two-column list. */
.errfacts{display:grid;grid-template-columns:auto 1fr;gap:var(--sp-1) var(--sp-3);
  margin:var(--sp-3) 0 var(--sp-3)}
.errfacts dt{align-self:baseline}
.errfacts dd{margin:0;font-variant-numeric:tabular-nums}

/* The live region and the refresh indicator.

   htmx writes its own .htmx-indicator rules into a <style> element at load, and
   this panel's policy names no style-src, so default-src 'self' drops them. The
   rule it drops is the one that HIDES an indicator, so an indicator would be
   permanently on screen, idle included. The layout therefore sets
   includeIndicatorStyles:false and the two rules live here, where the policy
   admits them; the reveal redeclares every property the hide sets. */
.livewrap{display:grid;gap:var(--sp-5);min-inline-size:0}
.htmx-indicator{opacity:0;visibility:hidden}
.htmx-request .htmx-indicator,.htmx-request.htmx-indicator{opacity:1;visibility:visible;
  transition:opacity var(--dur-slow) ease-in}

/* stale */
.stalebar{display:flex;align-items:center;gap:var(--sp-3);flex-wrap:wrap;padding:var(--sp-2) var(--sp-4);
  margin-block-start:var(--sp-3);border:var(--hairline) solid color-mix(in oklab,var(--warn) 45%,transparent);
  border-radius:var(--r-ctrl);background:color-mix(in oklab,var(--warn) 10%,transparent)}
.stalebar strong{color:var(--warn-ink)}
.stalebar span{color:var(--ink2)}

.refresh-failed{display:block;margin:0;padding:var(--sp-2) var(--sp-3);font-size:var(--fs-md);font-weight:600;color:var(--warn-ink);background:var(--warn-bg);border:var(--hairline) solid var(--warn-ink);border-radius:var(--r-ctrl);overflow-wrap:anywhere}

/* ---------- css/auth.css ---------- */
/* The auth pages (login, code, message). These pages carry no shell and no
   script; they follow the OS theme through the light-dark() tokens. The card's
   top rule is the accent. */
.authbody{--control-height:2rem;min-block-size:100svh;display:grid;place-items:center;padding:var(--sp-6);
  background:var(--bg);color:var(--ink);font-family:var(--font-ui);line-height:var(--lh-body)}
.authwrap{inline-size:min(100%,18rem)}
.authcard{background:var(--surface);border:var(--hairline) solid var(--line);border-radius:var(--r-panel);
  box-shadow:var(--shadow);padding:var(--sp-8) var(--sp-7);
  border-block-start:var(--rule) solid var(--accent)}
.authbrand{margin:0;font-size:var(--fs-lg);font-weight:700;letter-spacing:var(--ls-tight);
  color:var(--accent);overflow-wrap:anywhere}
.authtitle{margin:var(--sp-2) 0 var(--sp-6);font-size:var(--fs-xl);font-weight:600;
  letter-spacing:var(--ls-tight);text-wrap:balance}
.authmsg{margin:var(--sp-4) 0 0;color:var(--ink2);font-size:var(--fs-lg);font-weight:500;line-height:var(--lh-body);text-wrap:pretty}
.authback{margin-block-start:var(--sp-5);align-self:start}
.authnote{margin:0 0 var(--sp-5);font-size:var(--fs-md)}
.authform{display:grid;gap:var(--sp-5)}
.authwrap .legal-footer{padding:var(--sp-4) 0 0;text-align:center}
.authwrap .legal-links{justify-content:center}

/* ---------- css/forensics.css ---------- */
/* Operator tables under a height cap and the request forensics page: the
   frozen investigation tools, the log region, the row disclosure and its detail. */
/* Operator tables scroll within a stable region without changing customer tables. */
.operator-nodes{--operator-table-height:min(36rem,65dvh);display:grid;gap:var(--sp-5);min-inline-size:0}
/* contain:paint isolates the sticky thead's scroll overflow to this box: without
   it a wide table's sticky header leaks its width to the document and scrolls the
   whole page horizontally. */
.operator-nodes .tablebox{max-block-size:var(--operator-table-height);overflow:auto;contain:paint}
.operator-nodes .dtable{white-space:nowrap}
.operator-nodes .dtable thead{position:sticky;inset-block-start:0;background:var(--subtle);z-index:var(--z-raised)}
.operator-nodes .chart+.chart{margin-block-start:var(--sp-5)}
.operator-node-detail .tablebox{max-block-size:none}
.operator-node-detail > .detail{min-inline-size:0;overflow-wrap:anywhere}
/* A single-section operator page (one JA4 dictionary, one user list) has nothing
   below its table, so the shared cap wastes the viewport. Size such a table to its
   content and let it grow to fill the screen, scrolling inside past that. A page
   with several sections keeps the cap so the sections share the height. */
.operator-nodes:has(> section.content-section:only-of-type) .tablebox{max-block-size:calc(100dvh - 18rem)}

/* Frozen request investigations keep navigation outside the row scroll. */
.request-forensics{--request-log-height:36rem;display:grid;gap:var(--sp-5);min-inline-size:0}
.request-tools{position:sticky;inset-block-start:calc(var(--toolbar-top) + var(--toolbar-stuck));z-index:var(--z-tools);background:var(--bg)}
.request-filters{background:var(--bg);padding-block:var(--sp-3);max-block-size:35svh;overflow:auto}
.request-predicate,.request-controls,.request-day-nav,.request-day-form{display:flex;flex-wrap:wrap;align-items:end;gap:var(--sp-3)}
.request-filters label,.request-day-form label{display:grid;gap:var(--sp-1);min-inline-size:0;flex:1 1 12rem;font-size:var(--fs-md)}
.request-predicate,.request-controls{margin-block-start:var(--sp-3)}
.request-results{min-inline-size:0}
.request-day-nav{background:var(--bg);padding-block:var(--sp-3);justify-content:space-between}
.target-list-region{max-block-size:var(--target-list-height);overflow-y:auto;overscroll-behavior:contain;scrollbar-gutter:stable;min-inline-size:0}
.request-log-region{block-size:var(--request-log-height);overflow:auto;overscroll-behavior:contain;scrollbar-gutter:stable;min-inline-size:0;container-type:inline-size}
.request-log-region .tablebox{overflow:visible}
.request-log-region .dtable{white-space:nowrap;border-collapse:separate;border-spacing:0}
.request-log-region .dtable thead{position:sticky;inset-block-start:0;z-index:var(--z-raised);background:var(--subtle)}
.entity-value{font-family:var(--font-data);color:var(--accent)}
.value-disclosure{inline-size:28ch;white-space:normal}
.value-disclosure summary,.detail-disclosure summary{cursor:pointer;overflow-wrap:anywhere}
.full-value{white-space:pre-wrap;overflow-wrap:anywhere;padding-block:var(--sp-2);font-family:var(--font-data)}


.row-inspect{position:relative;display:flex;align-items:center;gap:var(--sp-2);cursor:pointer}
/* The disclosure control is a caret, not a raw checkbox: the input stays for the
   :has(:checked) mechanism and keyboard semantics, but its box is drawn as a
   triangle that turns to point down when the row is open. */
.row-toggle{appearance:none;-webkit-appearance:none;margin:0;cursor:pointer;color:var(--ink3);
  transition:rotate var(--dur-fast),color var(--dur-fast)}
.row-toggle:checked{rotate:90deg;color:var(--accent)}
.row-detail{display:none}
tr:has(.row-toggle:checked)+.row-detail{display:table-row}
.row-detail>td{padding:0}
/* The detail sits in a colspan cell of the horizontally scrollable log table, so
   its own width would follow the widest log row and never the viewport. Bind it to
   the region's visible width (100cqi) and pin it to the left, so it reflows on
   resize and stays in view while the log rows scroll sideways. */
.tablebox:has(.row-detail){container-type:inline-size}
.request-detail{position:sticky;inset-inline-start:0;inline-size:100cqi;box-sizing:border-box;display:grid;gap:var(--sp-2);padding:var(--sp-3);white-space:normal;min-inline-size:0}
.request-detail-group{display:grid;gap:var(--sp-1);min-inline-size:0}
.request-detail-group+.request-detail-group{border-block-start:var(--hairline) solid var(--line);padding-block-start:var(--sp-2)}
.signal-legend{display:grid;gap:var(--sp-2);min-inline-size:0}
.request-detail-note{display:block;margin:0;color:var(--ink3);font-size:var(--fs-md);text-wrap:pretty}
.request-detail-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,10.5rem),1fr));gap:var(--sp-1) var(--sp-4);margin:0;min-inline-size:0}
.request-detail-field{min-inline-size:0}
/* A field carrying a decoded-signal list needs the whole row: a long description in
   a single narrow track wraps letter by letter. A match disclosure spans the whole
   row so the match facts have room. */
.request-detail-field:has(.detail-explanations),.request-detail-field:has(.detail-disclosure){grid-column:1/-1}
.request-detail-field dt{color:var(--ink3);font-size:var(--fs-md);line-height:var(--lh-snug)}
.request-detail-field dd{margin:0;min-inline-size:0;font-family:var(--font-data);font-size:var(--fs-md);line-height:var(--lh-snug);white-space:pre-wrap;overflow-wrap:anywhere}
.detail-value{overflow-wrap:anywhere}
.detail-explanations{display:grid;gap:var(--sp-half);margin:var(--sp-1) 0 0;padding:0;list-style:none;font-family:var(--font-ui)}
.detail-explanations li{display:grid;grid-template-columns:minmax(8rem,12rem) minmax(0,1fr);gap:var(--sp-3);align-items:start}
.detail-explanations li:not(:has(span)){grid-template-columns:minmax(0,1fr)}
.detail-explanations code{color:var(--accent);font-family:var(--font-data);overflow-wrap:anywhere}
.detail-explanations span{color:var(--ink2);font-size:var(--fs-md);text-wrap:pretty}
/* The disclosure expands in flow rather than as an overlay: an absolute popover is
   clipped by the log region's own scroll. */
.detail-match-state{margin-inline-start:var(--sp-2);color:var(--accent);font-family:var(--font-ui);font-size:var(--fs-md);white-space:nowrap}
.detail-popover{display:none;margin-block-start:var(--sp-2);max-inline-size:32rem;padding:var(--sp-2) var(--sp-3);color:var(--ink);background:var(--surface);border:var(--hairline) solid var(--line);border-radius:var(--r-ctrl);white-space:normal}
.detail-disclosure[open]>.detail-popover,.detail-disclosure:focus-within>.detail-popover{display:block}
.detail-popover-fields{display:grid;grid-template-columns:minmax(6rem,auto) minmax(0,1fr);gap:var(--sp-half) var(--sp-4);margin:0;font-size:var(--fs-md)}
.detail-popover-fields dt{color:var(--ink3);font-family:var(--font-ui)}
.detail-popover-fields dd{margin:0;overflow-wrap:anywhere;white-space:pre-wrap;font-family:var(--font-data)}
/* The domain Logs filter: one grid that wraps to a single column at phone width, the
   choice groups, the hint and the action row on their own full rows. */
.request-log-filter{display:grid;gap:var(--sp-3) var(--sp-4);grid-template-columns:repeat(auto-fit,minmax(min(100%,14rem),1fr));align-items:end;margin-block-end:var(--sp-4)}
.request-log-filter>fieldset,.request-log-filter>[role="alert"],.request-log-filter>.request-log-filter-hint,.request-log-filter>.form-actions{grid-column:1/-1}
.request-log-filter>fieldset{margin:0;padding:0;border:0;min-inline-size:0}
.request-log-filter>.form-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2)}
.request-log-filter-hint{margin:0}
.request-controls>.page-size{flex:0 1 10rem}

/* ---------- css/flag.css ---------- */
/* Country flags (httpx/ui_flag.go, partials/flag.gohtml), shared by every page.
   A thin inset ring keeps a mostly white flag visible on the light panel. */
.flag{display:inline-block; width:1rem; height:0.75rem; border-radius:var(--r-mark); vertical-align:-0.125rem; object-fit:cover;
  box-shadow:0 0 0 1px color-mix(in srgb,currentColor 22%,transparent); flex:none}
.flag-none{background:var(--line)}
.flaglbl{display:inline-flex; align-items:center; gap:var(--sp-2); white-space:nowrap}
.flagcc{font-size:var(--fs-xs); color:var(--ink3)}

/* ---------- css/operator-overview.css ---------- */
/* Operator overview (/admin): the Open incidents tiles. Two tiles share the row in
   the same two columns and gap as the traffic cards below them, so their edges line
   up; one tile alone takes the full width. */
.op-attention{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);column-gap:var(--sp-6);row-gap:var(--sp-4);min-inline-size:0}
.op-attn{display:grid;gap:var(--sp-2);min-inline-size:0;padding:var(--sp-4) var(--sp-5);color:inherit;text-decoration:none;
  background:var(--surface);border:var(--hairline) solid var(--line);border-inline-start:var(--rule) solid var(--line-strong);
  border-radius:var(--r-panel);box-shadow:var(--shadow)}
.op-attn:hover{border-color:var(--accent)}
.op-attn-key{color:var(--ink3);font-size:var(--fs-xs);font-weight:700;letter-spacing:var(--ls-caps);text-transform:uppercase}
.op-attn-value{display:flex;align-items:baseline;gap:var(--sp-3);line-height:var(--lh-flat)}
.op-attn-count{font-family:var(--font-data);font-size:var(--fs-metric);font-weight:600}
.op-attn-state{font-size:var(--fs-sm);font-weight:600;letter-spacing:var(--ls-caps);text-transform:uppercase}
.op-attn-detail{color:var(--ink3);font-size:var(--fs-sm)}
.op-attn--ok{border-inline-start-color:var(--good)}
.op-attn--ok .op-attn-state{color:var(--good-ink)}
.op-attn--ok .op-attn-count{color:var(--ink3)}
.op-attn--crit{border-inline-start-color:var(--error);background:var(--error-bg)}
.op-attn--crit .op-attn-count,.op-attn--crit .op-attn-state{color:var(--error-ink)}
.op-attn--unknown .op-attn-state{color:var(--ink3)}

@container screen (max-width:53.75rem){
  .op-attention{grid-auto-flow:row;grid-template-columns:minmax(0,1fr)}
}

/* ---------- css/operator-statistics.css ---------- */
/* Layer 4 statistics: the attribution card stacks the top-5 chart over the table,
   both full width. */
.op-attribution-grid{display:grid;grid-template-columns:minmax(0,1fr);row-gap:var(--sp-4);min-inline-size:0}
.content-section>.op-attribution-grid{margin-inline:0}
.op-attribution-chart{min-inline-size:0;padding-inline:var(--sp-4)}
.op-attribution-table{min-inline-size:0;padding-inline:var(--sp-4)}
.op-attribution-table .flaglbl{white-space:normal}

/* ---------- css/operator-targets.css ---------- */
/* IP targets: each form and each address family list of a card is a disclosure
   row (operator-fold), ruled from the row above and not framed, so no card sits
   in the card. The summary keeps its native marker and puts the count at the end
   edge; a form inside drops the frame of its settings list. */
.content-section>.op-fold,.content-section>.target-list-region{margin-inline:0}
.content-section>.op-fold+.op-fold,.content-section>.op-fold+.target-list-region{margin-block-start:0}
.op-fold{border-block-start:var(--hairline) solid var(--line)}
.op-fold>summary{display:list-item;min-block-size:var(--control-height);
  padding:var(--sp-2) var(--sp-4);cursor:pointer;font-weight:600;overflow-wrap:anywhere}
.op-fold-count{float:inline-end;margin-inline-start:var(--sp-3);color:var(--ink3);font-weight:400;font-variant-numeric:tabular-nums}
.op-fold-body{display:grid;gap:var(--sp-3);min-inline-size:0;padding:0 var(--sp-4) var(--sp-4)}
.op-fold .settings-list{padding-inline:0;border:0;box-shadow:none;background:none}
.op-list>.edit-form-inline{margin-block-end:var(--sp-2)}
/* A list row ends in its action (Remove, Reset target): a small button in a column
   that shrinks to it at the end edge, pinned there while a narrow table scrolls, so
   the action stays in reach on a phone. A bypass row starts with its select box,
   and Remove selected sits under the list with its cap hint. */
.op-fold .dtable .table-actions{position:sticky;inset-inline-end:0;inline-size:1%;text-align:end;background:var(--surface)}
.op-fold .table-actions .inline-action{justify-content:flex-end}
.op-fold .table-actions .form-actions{flex-wrap:nowrap;justify-content:flex-end}
.op-fold .table-actions .form-actions>.btn{inline-size:auto;min-block-size:var(--control-height-sm);padding-inline:var(--sp-2);font-size:var(--fs-sm);white-space:nowrap}
.op-fold .dtable .table-check{inline-size:1%;padding-inline-end:0}
.op-select-form{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2) var(--sp-3)}
.op-select-form .form-actions>.btn{inline-size:auto;min-block-size:var(--control-height-sm);padding-inline:var(--sp-3);font-size:var(--fs-sm)}
.op-select-hint{margin:0;color:var(--ink3);font-size:var(--fs-sm)}

/* Drops by filter: the trend of each drop reason in its own row. */
.cell-spark{display:block;inline-size:100%;min-inline-size:4rem;max-inline-size:8rem;block-size:var(--sp-6)}
.cell-spark path{fill:none;stroke:var(--band-blocked);stroke-width:var(--chart-line-width);vector-effect:non-scaling-stroke}

/* Target overview: the rate summary over the drops by filter rows is four
   unframed columns (label, value with its unit, trend line), two by two on a
   narrow card. */
.metric-strip[data-layout=inline],.editable-overview .metric-strip[data-layout=inline]{
  grid-template-columns:repeat(auto-fit,minmax(min(100%,4.5rem),1fr));gap:var(--sp-3)}
.content-section .metric-strip[data-layout=inline] .metric{padding:0;row-gap:0;background:none;border:0;box-shadow:none}
.metric-strip[data-layout=inline] .metric::before{content:none}
.metric-strip[data-layout=inline] .metric .label{font-size:var(--fs-xs)}
.metric-strip[data-layout=inline] .metric .v{font-size:var(--fs-lg);white-space:nowrap}
.metric-strip[data-layout=inline] .metric .v .unit{font-size:var(--fs-md);color:var(--ink2);font-weight:500}
/* A figure with no trend (the drop rate) is the strip's headline: set large so it
   fills the height its neighbours give their value and trend, centred in it
   under the label. */
.metric-strip[data-layout=inline] .metric:not(:has(.metric-trend)){grid-template-rows:auto 1fr}
.metric-strip[data-layout=inline] .metric:not(:has(.metric-trend)) .v{font-size:var(--fs-headline);line-height:var(--lh-flat);align-self:center}
.metric-trend{display:block;inline-size:100%;block-size:var(--sp-6);margin-block-start:var(--sp-1);--trend:var(--accent)}
.metric[data-tone=allowed] .metric-trend{--trend:var(--band-allowed)}
.metric[data-tone=blocked] .metric-trend{--trend:var(--band-blocked)}
.metric-trend .line{fill:none;stroke:var(--trend);stroke-width:var(--chart-line-width);vector-effect:non-scaling-stroke}
.metric-trend .area{fill:color-mix(in oklab,var(--trend) 20%,transparent);stroke:none}

/* The drops rows of the side card are dense, one line each (the share bar sits
   after its percentage, not under it), so more reasons fit beside the chart. */
.section-aside .dtable td{padding-block:var(--sp-1)}
.section-aside .dtable td .meter{display:inline-block;vertical-align:middle;inline-size:var(--sp-8);
  margin-block:0;margin-inline-start:var(--sp-2)}
.section-aside .cell-spark{block-size:var(--sp-4)}

/* Target overview: the drops by filter card is an Aside, the narrow side column
   of its pair, so the traffic chart beside it keeps the wider share: seven of
   twelve tracks, and two thirds on a wide screen. Twelve tracks keep the other
   pairs at half each. Below the medium breakpoint every card takes its own
   row (responsive.css). */
@container screen (min-width:53.75rem){
  .read-sections:has(>.section-half+.section-aside){grid-template-columns:repeat(12,minmax(0,1fr))}
  .read-sections:has(>.section-aside)>.section-half{grid-column:span 6}
  .read-sections:has(>.section-aside)>.section-half:has(+.section-aside){grid-column:span 7}
  .read-sections:has(>.section-aside)>.section-aside{grid-column:span 5}
}
@container screen (min-width:100rem){
  .read-sections:has(>.section-aside)>.section-half:has(+.section-aside){grid-column:span 8}
  .read-sections:has(>.section-aside)>.section-aside{grid-column:span 4}
}

/* Target overview: the class and split switches of the traffic chart sit in its
   head band after the title and wrap under it on a narrow card. */
.section-switches{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-2) var(--sp-4);margin-inline-start:auto}

/* ---------- css/operator-incidents.css ---------- */
/* Incident lists: a running incident (its status cell carries the warn tone) is
   tinted so it stands out above the ended ones. */
.op-incidents .dtable tbody tr:has(>td.warncell:last-child)>td{background:var(--error-bg);font-weight:600}
.op-incidents .dtable tbody tr:has(>td.warncell:last-child)>td.warncell{color:var(--error-ink)}

/* ---------- css/responsive.css ---------- */
/* Conditional rules, kept last so no later top-level rule overrides them:
   reduced motion, the four container breakpoints and forced colours.

   The breakpoints are in rem, so they scale with the 140% root: 40rem, 53.75rem,
   100rem and 140rem are 896px, 1204px, 2240px and 3136px at that root. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition-duration:.01ms!important;animation-duration:.01ms!important}
}

/* ---------- medium: entity navigation, one column of cards ---------- */
@container screen (max-width:53.75rem){
  .main-column{--chart-height:11rem}
  .page-analytics{--chart-height:11rem}
  .editable-overview .read-sections{grid-template-columns:1fr}
  .read-sections:has(.section-half){grid-template-columns:1fr}
  .chart-pair{grid-template-columns:1fr}
  .workspace-page{grid-template-columns:minmax(0,1fr);column-gap:0}
  .entity-header{grid-column:1;grid-row:1}
  .workspace-nav{grid-column:1;grid-row:2;border-inline-end:0;border-block-end:var(--hairline) solid var(--line);padding-block:var(--sp-2)}
  .workspace-nav ul{flex-direction:row;flex-wrap:wrap;min-inline-size:0;padding:0}
  .workspace-nav a{border-inline-start:0;border-block-end:var(--rule) solid transparent;border-radius:0;white-space:nowrap}
  .workspace-nav li:has(>ul){display:flex;flex-wrap:wrap;gap:var(--sp-1)}
  .workspace-nav ul ul{margin-block-start:0;padding:0}
  .workspace-page>.editable-page,.workspace-page>.workspace-body{grid-column:1;grid-row:3;padding-block-start:var(--sp-5)}
}

/* ---------- small: no sidebar, touch-sized controls, the window controls in the flow ---------- */
@container screen (max-width:40rem){
  .entry-row-actions{flex-wrap:wrap}
  .entry-search>.form-field{flex-basis:100%}
  .edit-grid tr{display:grid;grid-template-columns:var(--sp-8) minmax(0,1fr) 8ch;align-items:center}
  .edit-grid tr>.edit-grid-note{grid-column:2;grid-row:2}
  .edit-grid tr>:last-child:not(.edit-grid-note){grid-column:3;grid-row:1}
  .edit-grid th,.edit-grid td{padding-inline:var(--sp-1);border-block:0}
  .edit-grid thead tr{border-block-end:var(--hairline) solid var(--line-strong)}
  .edit-grid tbody tr+tr{border-block-start:var(--hairline) solid var(--line)}
  .edit-grid .edit-grid-ipv4,.edit-grid .edit-grid-note{inline-size:auto}
  .edit-grid .edit-grid-ipv4 .input{inline-size:min(100%,calc(15ch + 2 * var(--sp-3) + 2 * var(--hairline)))}
  .edit-grid .edit-grid-note .input{min-inline-size:0}
  .s-grid{grid-template-columns:1fr}
  .table-pair{grid-template-columns:1fr}
  .nav{display:none}
  .nav:target{display:block;border-inline-end:0;border-block-end:var(--hairline) solid var(--line);
    min-block-size:0;block-size:auto;position:static;overflow:visible}
  .nav a{min-block-size:var(--control-height)}
  .time-bar .input{font-size:var(--fs-lg)}
  .tp-columns{grid-template-columns:minmax(0,1fr)}
  .tp-absolute{border-inline-end:0;border-block-end:var(--hairline) solid var(--line)}
  .tp-btn-text{display:none}
  .s-grid{--control-height:2rem;--topbar-height:calc(var(--control-height) + 2 * var(--sp-2));--toolbar-top:var(--topbar-height)}
  .nav .nav-close{display:flex;margin-block-end:var(--sp-3)}
  .nav-open{display:inline-flex;justify-content:center;min-inline-size:var(--control-height);padding-inline:0}
  .nav-open .btn-label{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
  .overview-grid{grid-template-columns:1fr}
  .metric-strip{grid-template-columns:repeat(2,minmax(0,1fr))}
  .metric{padding-inline:var(--sp-3)}
  .mainarea{grid-template-columns:var(--sp-3) minmax(0,1fr) var(--sp-3);row-gap:var(--sp-4)}
  .pagehead{padding:var(--sp-3)}
  .mainarea:has(.time-bar)>.pagehead{min-block-size:0;padding:var(--sp-2) var(--sp-3)}
  .topbar{position:sticky;inset-block-start:0;z-index:var(--z-topbar);flex-wrap:nowrap;gap:var(--sp-3);
    block-size:var(--topbar-height);padding:var(--sp-2) var(--sp-4)}
  .time-bar{position:sticky;inset-block-start:calc(var(--toolbar-top) + var(--toolbar-stuck));inset-block-end:auto;inset-inline-end:auto;
    z-index:var(--z-toolbar);pointer-events:auto;flex-flow:row wrap;align-items:center;justify-content:space-between;
    min-inline-size:0;max-inline-size:none;padding:var(--sp-2) var(--sp-3);background:var(--surface);
    border:var(--hairline) solid var(--line);border-radius:var(--r-panel)}
  .time-row{position:static;flex:0 1 auto;flex-wrap:nowrap;min-inline-size:0;min-block-size:0}
  .tp-group,.tp-range,.tp-range-btn{min-inline-size:0}
  .time-row:has(+.detail){margin-block-start:0}
  .time-bar>.detail{flex-basis:100%}
  .tp-range-label{max-inline-size:8rem}
  .livewrap:not(.is-poll-paused) .live-paused{display:none}
  .request-forensics .request-tools{position:static}
  .top-status{min-inline-size:0}
  .top-status .stalebar{flex-wrap:nowrap;min-inline-size:0;overflow:hidden;white-space:nowrap;margin-block-start:0}
  .top-status .stalebar span{min-inline-size:0;overflow:hidden;text-overflow:ellipsis}
  .top-actions{flex-wrap:nowrap;margin-inline-start:auto}
  .top-actions .btn{position:relative;min-block-size:var(--control-height);min-inline-size:var(--control-height);padding-inline:0}
  .top-actions .btn-label{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
  .chart-head{gap:var(--sp-2)}
  .filterbar .spacer{flex-basis:100%}
  .zonenote{display:none}
  .setting-group{grid-template-columns:1fr;gap:var(--sp-4);padding-block:var(--sp-4)}
  .editable-overview .read-sections{grid-template-columns:1fr;column-gap:0}
  .editable-overview .content-section:nth-child(n+3){grid-column:auto}
  .edit-form,.edit-fieldset{grid-template-columns:1fr}
  .entity-header{align-items:start;flex-direction:column;gap:var(--sp-3)}
  .workspace-meta{justify-content:flex-start}
  .editable-page .uplot{max-inline-size:100%}
  .input{font-size:var(--fs-lg)}
  .form-actions .btn{inline-size:100%;white-space:normal}
  .chart-info>summary{inline-size:var(--target-touch);block-size:var(--target-touch)}
  .section-fold-state{display:block;float:none;margin-inline-start:0}
  .detail-explanations li,.detail-popover-fields{grid-template-columns:minmax(0,1fr)}
  .request-pagination .pager-range,.table-pager>output{flex-basis:100%}
}

/* ---------- above small: a group whose only control is one button gives its text the width;
   a descriptor form (.edit-fieldset-aside) puts each control and its label on the start side
   and the help beside it, in three shared tracks: the switch, the label, the help line. A
   number takes the first two tracks, and a rate with its burst the whole row. ---------- */
@container screen (min-width:40rem){
  .setting-group:has(>.setting-controls>.edit-form:only-child>.form-actions:not(.form-field~*,.edit-fieldset~*,.edit-disclosure~*)){grid-template-columns:minmax(0,1fr) auto}
  .edit-fieldset-aside{grid-template-columns:var(--switch-width) minmax(0,var(--field-col)) minmax(0,var(--help-max));column-gap:var(--sp-3);row-gap:var(--sp-4)}
  .edit-fieldset-aside>.form-field:not(:has(textarea)):not(.list-widget),.edit-fieldset-aside>.switch-row,.edit-fieldset-aside>.switch-row>.form-checkbox{grid-column:1/-1;grid-row:auto;grid-template-columns:subgrid;grid-template-rows:none}
  .edit-fieldset-aside>.form-checkbox>.input,.edit-fieldset-aside>.form-checkbox>span,.edit-fieldset-aside>.form-checkbox>small,
  .edit-fieldset-aside>.switch-row>.form-checkbox>.input,.edit-fieldset-aside>.switch-row>.form-checkbox>span,.edit-fieldset-aside>.switch-row>.form-checkbox>small{grid-row:1}
  .edit-fieldset-aside>.form-field:not(.form-checkbox):not(:has(textarea)):not(.list-widget)>:not(small){grid-column:1/span 2}
  .edit-fieldset-aside>.form-field:not(.form-checkbox):not(:has(textarea)):not(.list-widget)>small{grid-column:3;grid-row:1/span 2;align-self:center}
  .edit-fieldset-aside>.form-field:not(.form-checkbox):not(:has(textarea)):not(.list-widget)>small,.edit-fieldset-aside>.form-checkbox>small,.edit-fieldset-aside>.switch-row>.form-checkbox>small{max-inline-size:var(--help-max);padding-inline-start:var(--sp-5);font-size:var(--fs-sm)}
  .edit-fieldset-aside>.form-checkbox>small,.edit-fieldset-aside>.switch-row>.form-checkbox>small{grid-column:3}
  .edit-fieldset-aside>.switch-row>.override-control{grid-column:2/-1}
  .edit-fieldset-aside>.switch-row>.override-dialog{grid-column:1/-1}
  .field-pair,.override-dialog[open]{grid-template-columns:repeat(2,minmax(0,1fr))}
  .override-dialog>.form-field:not(.form-checkbox):not(:has(textarea)){grid-column:1}
  .override-dialog>.form-checkbox,.override-dialog>.field-pair{grid-column:1/-1}
  .override-dialog>.field-pair{grid-template-columns:subgrid}
}

/* ---------- wide ---------- */
@container screen (min-width:100rem){
  .overview-grid{grid-template-columns:repeat(auto-fit,minmax(15rem,1fr))}
  .setting-group{grid-template-columns:minmax(12rem,20rem) minmax(0,1fr)}
  .input:not(textarea){max-inline-size:var(--card-min)}
  .form-actions{justify-content:flex-start}
}

/* ---------- ultrawide: chart cards flow into as many columns as fit ---------- */
@container screen (min-width:140rem){
  .read-sections,.operator-nodes{display:flex;flex-wrap:wrap;align-items:stretch}
  .read-sections>*,.operator-nodes>*{flex:1 1 100%}
  .read-sections>.section-half,.editable-overview .read-sections>.content-section:not(.section-full),
  .operator-nodes>.content-section:has(>.chart):not(.section-full){flex:1 1 var(--card-min)}
  .read-sections>.content-section:has(>.chart+.chart):not(.section-full),
  .operator-nodes>.content-section:has(>.chart+.chart):not(.section-full){flex-basis:calc(2 * var(--card-min) + var(--sp-6))}
  .read-sections>.section-half:has(+.section-aside){flex-grow:2}
  .content-section:has(>.chart+.chart){grid-template-columns:repeat(2,minmax(0,1fr))}
  .content-section:has(>.chart+.chart)>:not(.chart){grid-column:1/-1}
  .content-section>.chart+.chart{margin-block-start:var(--sp-4)}
  .op-attribution-grid{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:var(--sp-6)}
  .op-attribution-chart{--chart-height:19rem}
  .setting-controls:has(>.edit-fieldset+.edit-fieldset){grid-template-columns:repeat(auto-fill,minmax(var(--card-min),1fr))}
  .setting-controls>.edit-fieldset{grid-column:auto}
}

@media (forced-colors:active){
  .nav a{border:var(--hairline) solid transparent}
  .nav a[aria-current="page"]{border-color:Highlight}
  .form-switch .input{border:var(--hairline) solid ButtonText}
  .form-switch .input::before{background:ButtonText}
  .chart-info[open]>.chart-cap{border-color:CanvasText}
  .btn--ghost.current{border-color:Highlight}
  .seg-control-option.current{border-color:Highlight;box-shadow:none}
}

