/**
Theme Name: Duum Splash
Theme URI: https://duum.io
Author: The Duumvirati
Author URI: https://duum.io
Description: Placeholder front page for the public duum.io until the real site is built. The signed-out splash of the Duum Control console theme (provision/duum-control), as duum.io showed it before the console moved to curia.duum.io on 2026-10-07, without the console's sign-in control or panel. The stylesheet below the header is copied verbatim from duum-control/style.css; retune it there and copy it again rather than letting the two drift.
Version: 1.0.0
Requires at least: 6.0
Requires PHP: 8.0
License: GPL-2.0-or-later
Text Domain: duum-splash
*/
/* ── Nocturne tokens ─────────────────────────────────────────────────────
   Carried from the handoff design system verbatim. Retune here, not inline.
   Inter is self-hosted-by-absence: the design imports it from Google Fonts,
   but this panel is private and signed-in only, so it falls back to the
   system UI stack rather than calling a third party on every load.        */
:root {
  --color-bg:      #161826;
  --color-surface: #232532;
  --color-text:    #e9e9ed;
  --color-accent:  #9184d9;
  --color-divider: color-mix(in srgb, #e9e9ed 16%, transparent);

  --color-neutral-100:#f3f5fe; --color-neutral-200:#e4e7f5; --color-neutral-300:#cfd3e5;
  --color-neutral-400:#b2b6ca; --color-neutral-500:#9397ab; --color-neutral-600:#75798c;
  --color-neutral-700:#595d6c; --color-neutral-800:#3f424d; --color-neutral-900:#292b31;

  --color-accent-100:#f5f4ff; --color-accent-200:#e7e5fe; --color-accent-300:#d2cefd;
  --color-accent-400:#b5abfc; --color-accent-500:#968ae0; --color-accent-600:#796cbf;
  --color-accent-700:#5d5294; --color-accent-800:#423a6a; --color-accent-900:#2b2741;

  --font-heading: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-body:    "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono:    ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 14px;

  --shadow-sm: 0 0 0 1px #3f424d;
  --shadow-md: 0 0 0 1px #595d6c, 0 6px 18px rgba(0,0,0,.55);
  --shadow-lg: 0 0 0 1px #9397ab, 0 16px 40px rgba(0,0,0,.65);

  --ok:   #79c39a;
  --warn: #d9b268;
  --crit: #e0736f;
}

*, *::before, *::after { box-sizing: border-box; }

html, body { margin: 0; height: 100%; }
body.duum-control { overflow: hidden; background: var(--color-bg); color: var(--color-text);
  font-family: var(--font-body); font-size: 13px; line-height: 1.45; }

/* The admin bar would fight a 100vh app shell, so the panel drops it and links
   to wp-admin from the sidebar instead. */
body.duum-control #wpadminbar { display: none; }
html.duum-control-html { margin-top: 0 !important; }

a { color: var(--color-accent); }
a:hover { color: var(--color-accent-300); }

::-webkit-scrollbar { width: 9px; height: 9px; }
::-webkit-scrollbar-thumb { background: var(--color-neutral-800); border-radius: 6px; }
::-webkit-scrollbar-track { background: transparent; }

@keyframes duumPulse { 0%,100% { opacity: 1 } 50% { opacity: .25 } }
@keyframes duumSpin  { to { transform: rotate(360deg) } }

:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }

.sr { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border:0; }

/* ── Buttons, from the design system ─────────────────────────────────── */
.btn { display:inline-flex; align-items:center; justify-content:center; gap:6px;
  cursor:pointer; text-decoration:none; font-family:var(--font-heading); font-weight:500;
  font-size:12px; line-height:1.2; color:var(--color-text); background:transparent;
  border:1px solid transparent; padding:5px 10px; border-radius:var(--radius-md); }
.btn:disabled { opacity:.45; cursor:not-allowed; }
.btn-primary   { color:var(--color-accent); border-color:var(--color-accent); }
.btn-primary:hover  { background: color-mix(in srgb, var(--color-accent) 12%, transparent); }
.btn-secondary { border-color: var(--color-divider); }
.btn-secondary:hover{ background: color-mix(in srgb, var(--color-text) 7%, transparent); }
.btn-ghost     { color: var(--color-accent); padding-inline: 6px; }
.btn-ghost:hover    { background: color-mix(in srgb, var(--color-accent) 10%, transparent); }

/* A remedy button is its own progress indicator: the four states a job can be
   in are four looks on the same control, so the answer arrives where the click
   happened rather than in a toast that outlives its subject by two seconds. */
.btn-ok   { color: var(--ok);   border-color: var(--ok); }
.btn-ok:hover   { background: color-mix(in srgb, var(--ok) 12%, transparent); }
.btn-crit { color: var(--crit); border-color: var(--crit); }
.btn-crit:hover { background: color-mix(in srgb, var(--crit) 12%, transparent); }
.btn.is-working { color: var(--warn); border-color: var(--warn); opacity: 1; cursor: progress; }
.btn.is-working::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: var(--warn); animation: duum-pulse 1.1s ease-in-out infinite;
}
@keyframes duum-pulse { 0%,100% { opacity: .25; transform: scale(.8); } 50% { opacity: 1; transform: scale(1); } }
/* Respect a reader who has asked the system for less motion: the colour and the
   word already carry the state, so the pulse is decoration and can simply stop. */
@media (prefers-reduced-motion: reduce) {
  .btn.is-working::before { animation: none; opacity: .9; }
}
/* A toggle that is on. Privacy mode (1.35.0) is the first: the header button
   reads as pressed, in the accent the segment controls already use, so a
   viewer of a stream can see that the fleet on screen is pseudonymous. The
   hiding itself happens in the text, not the paint, so body.duum-private is
   set alongside for anything that later needs to know and styles nothing. */
.btn[aria-pressed="true"] { background: var(--color-accent-800); color: var(--color-accent-100);
  border-color: var(--color-accent-700); }

.input { width:100%; padding:4px 8px; font:inherit; font-size:12px; color:var(--color-text);
  caret-color:var(--color-accent); background:var(--color-surface);
  border:1px solid var(--color-divider); border-radius:var(--radius-md); }
.input:focus-visible { border-color: var(--color-accent); outline-offset: 0; }
textarea.input { resize: vertical; }

.seg { display:inline-flex; overflow:hidden; border:1px solid var(--color-divider);
  border-radius: var(--radius-md); }
.seg-opt { all:unset; display:inline-flex; align-items:center; cursor:pointer;
  padding:3px 9px; font-size:11.5px; color: var(--color-neutral-400); }
.seg-opt + .seg-opt { border-left: 1px solid var(--color-divider); }
.seg-opt[aria-pressed="true"] { background: var(--color-accent-800); color: var(--color-accent-100); }

.table { width:100%; border-collapse:collapse; font-size:12.5px; }
.table th { text-align:left; font-size:10.5px; letter-spacing:.08em; text-transform:uppercase;
  color: color-mix(in srgb, var(--color-text) 60%, transparent); padding:6px; }
.table thead tr { background: linear-gradient(to right, transparent, var(--color-divider) 48px,
  var(--color-divider) calc(100% - 48px), transparent) no-repeat bottom / 100% 1px; }
.table td { padding:6px; }
.table tbody tr { background: linear-gradient(to right, transparent,
  color-mix(in srgb, var(--color-text) 8%, transparent) 48px,
  color-mix(in srgb, var(--color-text) 8%, transparent) calc(100% - 48px), transparent)
  no-repeat bottom / 100% 1px; }
.table tbody tr:hover { background:
  linear-gradient(color-mix(in srgb, var(--color-text) 4%, transparent),
                  color-mix(in srgb, var(--color-text) 4%, transparent)) no-repeat 0 0 / 100% 100%,
  linear-gradient(to right, transparent, color-mix(in srgb, var(--color-text) 8%, transparent) 48px,
  color-mix(in srgb, var(--color-text) 8%, transparent) calc(100% - 48px), transparent)
  no-repeat bottom / 100% 1px; }

/* ── The domain sheet ────────────────────────────────────────────────────
   A table you work in rather than read, so it gets its own scroll box and a
   header row that stays put. The header cannot be sticky against the page:
   .panel clips its children and .body is the scrollport for everything else,
   so the sheet scrolls itself and the row stays pinned to the sheet.        */
.sheet { max-height: calc(100vh - 330px); min-height: 160px; overflow: auto;
  border-radius: var(--radius-md); background: var(--color-surface); box-shadow: var(--shadow-sm); }
.sheet .table { font-size: 12px; }
.sheet thead th { position: sticky; top: 0; z-index: 1; padding: 0;
  background: #1f2130; box-shadow: 0 1px 0 0 var(--color-divider); }
.sheet td { white-space: nowrap; }
/* One registration can carry several sites: metzae.net alone holds four, and on
   one line it is wider than every other column put together and pushes the whole
   table into horizontal scroll for the sake of a single row. This is the one cell
   allowed to wrap. Truncating it instead would hide the answer to the question
   the column exists to ask. */
.sheet td.cell-sites { white-space: normal; max-width: 250px; }

/* The whole header cell is the sort control, so the click target is the cell
   and not a word inside it. */
.th-sort { all: unset; box-sizing: border-box; display: block; width: 100%; cursor: pointer;
  padding: 7px 6px; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
  color: color-mix(in srgb, var(--color-text) 60%, transparent); }
.th-sort:hover { color: var(--color-text); background: color-mix(in srgb, var(--color-text) 6%, transparent); }
.th-sort--on { color: var(--color-accent-300); }
.sheet th[style*="right"] .th-sort { text-align: right; }

h6 { margin:0; font-size:11px; font-family:var(--font-heading); font-weight:500; }

/* ── Shell ───────────────────────────────────────────────────────────── */
.app { display:flex; height:100vh; height:100svh; }

.side { width:210px; flex:none; display:flex; flex-direction:column;
  border-right:1px solid var(--color-divider);
  background: linear-gradient(180deg,#1a1c2b,#15172433); }
.side__id { padding:14px 14px 10px; display:flex; align-items:center; gap:8px;
  border-bottom:1px solid var(--color-divider); }
/* The mark, 2026-08-23. It was a bordered "D": a box, a letter, and the word
   "duum.io" immediately to its right, which is the name said twice. The annulus
   needs no border because it already has an outline, and no box because it is
   already a shape. */
.side__glyph { width:22px; height:22px; flex:none; display:block; object-fit:contain; }
.side__name { font-family:var(--font-heading); font-size:13px; letter-spacing:-.01em; }
.side__kicker { font-size:10px; letter-spacing:.09em; text-transform:uppercase;
  color: var(--color-neutral-500); }
.side__nav { padding:8px 6px; display:flex; flex-direction:column; gap:1px; overflow:auto; }
.navitem { all:unset; cursor:pointer; display:flex; align-items:center; gap:8px;
  padding:6px 8px 6px 9px; border-radius:var(--radius-sm); font-family:var(--font-heading);
  font-size:12.5px; color: var(--color-neutral-400); box-shadow: inset 2px 0 0 0 transparent; }
.navitem:hover { background: var(--color-neutral-900); }
.navitem[aria-current="page"] { background: var(--color-accent-900); color: var(--color-accent-200);
  box-shadow: inset 2px 0 0 0 var(--color-accent); }
.navitem__label { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.navitem__badge { font-size:10.5px; font-variant-numeric:tabular-nums; padding:1px 5px;
  border-radius:99px; background: var(--color-accent-800); color: var(--color-accent-200); }
.side__foot { margin-top:auto; padding:10px 12px; border-top:1px solid var(--color-divider);
  display:flex; flex-direction:column; gap:6px; }
.side__live { display:flex; align-items:center; gap:6px; font-size:11px; color:var(--color-neutral-400); }
.dot { width:6px; height:6px; border-radius:99px; flex:none; }
.dot--live { animation: duumPulse 1.6s ease-in-out infinite; }
.side__meta { font-size:10.5px; color:var(--color-neutral-600); font-variant-numeric:tabular-nums; }

.main { flex:1; min-width:0; display:flex; flex-direction:column; }
.head { flex:none; display:flex; align-items:center; gap:10px; padding:9px 16px;
  border-bottom:1px solid var(--color-divider); background:#191b2a; }
.head__title { font-family:var(--font-heading); font-size:15px; letter-spacing:-.01em; }
.head__sub { font-size:11px; color: var(--color-neutral-500); }
.head__stats { display:flex; align-items:center; gap:14px; margin-right:6px;
  font-variant-numeric:tabular-nums; }
.head__stat { text-align:right; }
.head__stat b { display:block; font-family:var(--font-heading); font-size:14px; font-weight:500; }
.head__stat span { font-size:10px; letter-spacing:.07em; text-transform:uppercase;
  color: var(--color-neutral-600); }
.spin { display:inline-block; width:9px; height:9px; border-radius:99px;
  border:1.5px solid currentColor; border-top-color:transparent; }
.spin--on { animation: duumSpin .8s linear infinite; }

.body { flex:1; min-height:0; overflow:auto; padding:14px 16px 40px; }

/* The alarm strip sits directly above the view, and .body is block layout with
   no gap, so it touched whatever came next while carrying 14px of the body's
   own padding above it. Metzae, 2026-09-10: "put a gap below this error message
   equal to the space above." The number is the body's top padding, not a guess;
   if that padding changes, change this with it. */
.body > .panel:first-child { margin-bottom:14px; }
.stack { display:flex; flex-direction:column; gap:14px; }
.row { display:flex; align-items:center; gap:10px; }
.grow { flex:1; }

.panel { border-radius:var(--radius-md); background:var(--color-surface);
  box-shadow: var(--shadow-sm); overflow:hidden; }
.panel__head { display:flex; align-items:center; gap:9px; padding:8px 12px;
  border-bottom:1px solid var(--color-divider); }
.panel__pad { padding:10px 12px; }
.muted { color: var(--color-neutral-500); font-size:11px; }

.kpis { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:8px; }
/* The Domains view carries a seventh tile. Without this the row wraps and
   leaves five empty cells under it, which reads as a rendering fault rather
   than as one more number. The narrow-screen rules below name both classes
   so this cannot win over them by being later in the file. */
.kpis--wide { grid-template-columns:repeat(7,minmax(0,1fr)); }
.kpi { padding:9px 11px; border-radius:var(--radius-md); background:var(--color-surface);
  box-shadow: var(--shadow-sm); }
.kpi__label { font-size:10px; letter-spacing:.08em; text-transform:uppercase;
  color: var(--color-neutral-500); }

/* Section headings on the Roster and Modules views. These were .kpi__label, a
   10px caption meant for the small print under a number, which left Instruments
   and Ventures reading as footnotes to their own contents. Metzae, 2026-09-10:
   "enlarge the titles of each section so they're more like subheaders." Big
   enough to structure the page, still uppercase so they read as labels rather
   than competing with the view's own h3. */
.section__title { font-family: var(--font-heading); font-size:15px; font-weight:600;
  letter-spacing:.05em; text-transform:uppercase; color: var(--color-neutral-200); }
.kpi__row { display:flex; flex-wrap:wrap; align-items:baseline; gap:2px 6px; margin-top:3px; }
.kpi__value { font-family:var(--font-heading); font-size:21px; font-variant-numeric:tabular-nums; }
.kpi__note { font-size:10.5px; color: var(--color-neutral-500); overflow-wrap:anywhere; }

.triage { display:grid; grid-template-columns:14px minmax(90px,150px) minmax(0,1fr) minmax(0,110px) 96px;
  align-items:center; gap:10px; padding:7px 12px; border-bottom:1px solid var(--color-divider); }
.triage:hover { background:#262938; }
.triage__site { font-family:var(--font-heading); font-size:12.5px; text-decoration:none;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.triage__msg { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.triage__sys { font-size:11px; color:var(--color-neutral-500); text-transform:uppercase;
  letter-spacing:.06em; }

.cards { display:grid; grid-template-columns:repeat(auto-fill,minmax(268px,1fr)); gap:9px; }
.card { cursor:pointer; padding:10px 11px; border-radius:var(--radius-md);
  background:var(--color-surface); display:flex; flex-direction:column; gap:7px;
  box-shadow: inset 0 0 0 1px var(--color-neutral-800); }
.card:hover { box-shadow: inset 0 0 0 1px var(--color-neutral-600), 0 6px 18px rgba(0,0,0,.5); }
.card--crit { box-shadow: inset 0 0 0 1px rgba(224,115,111,.45); }
.card--warn { box-shadow: inset 0 0 0 1px rgba(217,178,104,.32); }
/* Office portrait on a Roster seat. Full bleed across the card's top, so the
   negative margins undo the card's own padding; the radius matches the card so
   the corners stay clean. The scenes are 11:6 and shown at 11:6, uncropped:
   the roster is where the art is the point, so it gets its whole frame. The
   seat grid runs a little wider than the site cards for the same reason. */
.card--art { overflow:hidden; }
.card__art { display:block; width:calc(100% + 22px); margin:-10px -11px 2px; aspect-ratio:11/6;
  object-fit:cover; object-position:50% 30%; border-radius:var(--radius-md) var(--radius-md) 0 0;
  background:var(--color-neutral-900); }
.cards.seats { grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:10px; }
.seat__name { font-size:14px; }
/* The same space when no portrait exists: the office's mark, and a note. */
.card__art--none { display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:6px; background:linear-gradient(180deg,#1a1c2b,var(--color-neutral-900)); }
.card__mark { display:block; line-height:0; opacity:.9; }
.card__artnote { font-size:10px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--color-neutral-600); }
.card__top { display:flex; align-items:center; gap:7px; }
.card__name { font-family:var(--font-heading); font-size:13px; min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.card__state { font-size:10px; letter-spacing:.07em; text-transform:uppercase; }
.card__metrics { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:4px;
  font-variant-numeric:tabular-nums; }
.card__k { font-size:10px; color:var(--color-neutral-600); letter-spacing:.05em; text-transform:uppercase; }
.card__v { font-size:12px; }
.chips { display:flex; flex-wrap:wrap; gap:4px; min-height:19px; }
.chip { font-size:10.5px; padding:1px 6px; border-radius:99px; }
.chip--crit { background:rgba(224,115,111,.14); color:var(--crit); box-shadow: inset 0 0 0 1px rgba(224,115,111,.35); }
.chip--warn { background:rgba(217,178,104,.12); color:var(--warn); box-shadow: inset 0 0 0 1px rgba(217,178,104,.3); }
.chip--ok   { background:rgba(121,195,154,.12); color:var(--ok); box-shadow: inset 0 0 0 1px rgba(121,195,154,.3); }

.stats4 { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:8px; }
.stat { padding:9px 11px; border-radius:var(--radius-md); background:var(--color-surface);
  box-shadow:var(--shadow-sm); }
.stat__k { font-size:10px; letter-spacing:.08em; text-transform:uppercase; color:var(--color-neutral-500); }
.stat__v { font-family:var(--font-heading); font-size:16px; font-variant-numeric:tabular-nums; }

.split { display:grid; grid-template-columns:1.15fr .85fr; gap:10px; align-items:start; }
.split-even { display:grid; grid-template-columns:1fr 1fr; gap:12px; align-items:start; }

.pluginrow { display:grid; grid-template-columns:minmax(0,1fr) 84px 78px 70px; align-items:center;
  gap:8px; padding:6px 12px; border-bottom:1px solid var(--color-divider);
  font-variant-numeric:tabular-nums; }
.checkrow { display:flex; align-items:center; gap:8px; padding:3px 0;
  border-bottom:1px solid var(--color-divider); }
.constrow { display:flex; align-items:center; gap:8px; padding:3px 0;
  font-family:var(--font-mono); font-size:11.5px; }

.mono { font-family: var(--font-mono); }

/* Empty state. Used wherever the system has no source for a panel yet, which
   is the honest alternative to rendering a number nobody measured. */
.empty { padding:14px; border:1px dashed var(--color-neutral-800); border-radius:var(--radius-md);
  color: var(--color-neutral-500); font-size:12px; line-height:1.6; }
.empty b { color: var(--color-neutral-300); font-weight:500; }

.dialog-backdrop { position:fixed; inset:0; background:rgba(8,9,15,.66);
  display:grid; place-items:center; z-index:40; padding:16px; }
.dialog { width:min(460px,100%); padding:15px 16px; border-radius:var(--radius-lg);
  background:var(--color-surface); box-shadow:var(--shadow-lg);
  display:flex; flex-direction:column; gap:9px; }
.dialog__title { font-family:var(--font-heading); font-size:16px; }
.dialog__body { font-size:12.5px; color:var(--color-neutral-300); }
.dialog__cmd { padding:8px 10px; border-radius:var(--radius-sm); background:var(--color-bg);
  font-family:var(--font-mono); font-size:11.5px; color:var(--color-accent-300);
  white-space:pre-wrap; word-break:break-all; }
.toast { position:fixed; right:16px; bottom:16px; z-index:50; padding:9px 12px;
  border-radius:var(--radius-md); background:var(--color-surface); box-shadow:var(--shadow-md);
  display:flex; align-items:center; gap:8px; font-size:12.5px; }

/* ── Splash, for anyone not signed in ───────────────────────────────────
   A 200, not a redirect to wp-login.php: Sentinel watches duum.io, and a
   permanent redirect would make "is it up?" ambiguous on the one site whose
   job is to answer that about everything else.                            */
.splash { min-height:100vh; min-height:100svh; display:grid; place-items:center;
  padding:28px; position:relative; text-align:center; }
.splash__inner { display:grid; justify-items:center; gap:16px; }
/* The splash title. Lowercase, gothic, absolute grey, sized to sit inside the ring.

   SIZED IN vmax BECAUSE THE BACKDROP IS. `cover` on a SQUARE image renders it at
   max(viewport width, viewport height) in both directions, so every feature of
   the picture is a fixed fraction of 100vmax and stays there at any window
   shape. Measured off the source rather than guessed: the ring's inner hole is
   0.395 of the canvas, so on screen it is always 39.5vmax across, and the type
   below is about 78% of that. The vw cap is for narrow portrait windows, where
   the square scales to the HEIGHT and the ring is wider than the screen.

   #808080 RATHER THAN BLACK, 2026-08-22. Black was tried first and measured
   1.56:1 against the rock behind it, which is what it looked like. Absolute grey
   is 3.42:1 there: above the 3:1 floor for large text, and still reading as a
   tone rather than as a colour. */
.splash__title {
  margin:0;
  font-family:var(--font-display);
  font-weight:700;
  text-transform:lowercase;
  color:#808080;
  font-size:min(5.4vmax, 13vw);
  letter-spacing:.25em;
  /* letter-spacing adds a trailing gap after the last glyph, which shifts the
     word off centre by half of it. Pulled back so the type is optically centred
     in the ring rather than measurably centred in its own box. */
  margin-right:-.25em;
  line-height:1;
}
.splash__line { margin:0; max-width:32ch; color:var(--color-neutral-500); font-size:.95rem; }
.splash__signin { position:absolute; top:28px; right:28px; width:42px; height:42px;
  display:inline-flex; align-items:center; justify-content:center; border-radius:50%;
  border:1px solid var(--color-divider); background:var(--color-surface);
  color:var(--color-neutral-400); text-decoration:none; }
.splash__signin:hover { color:var(--color-text); border-color:var(--color-neutral-600); }

/* The glint, 1.37.1. A faint white highlight that travels once round the ring
   every 72 seconds, like light moving along a glass tube. Decorative only: it
   lives on a pseudo-element, takes no pointer events, and is masked to the
   ring's own band, so it never passes behind the title in the hole.

   SAME GEOMETRY AS THE BACKDROP. The backdrop is a square image under `cover`,
   so the ring is centred on the viewport and sized in vmax (see .splash__title).
   Measured off the source: hole 0.395 of the canvas, outer edge about 0.508. The
   element is 52vmax across and the mask opens from 75% to 98% of its radius,
   which is that band with soft edges.

   `screen` because the ring is already near full saturation: soft-light and
   overlay were tried and vanish on it. Peak white is .4, which reads as a pale
   sheen rather than a white arc. Positioned with `translate` so the animation
   owns `transform` outright. Splash only; the signed-in panel never sees it. */
.duum-splash .splash::before {
  content:""; position:fixed; left:50%; top:50%; z-index:-1;
  width:52vmax; height:52vmax; translate:-50% -50%;
  border-radius:50%; pointer-events:none;
  background:conic-gradient(from 0deg, transparent 0deg 305deg,
    rgba(255,255,255,.4) 357deg, transparent 360deg);
  -webkit-mask:radial-gradient(circle closest-side, transparent 75%, #000 82%, #000 91%, transparent 98%);
          mask:radial-gradient(circle closest-side, transparent 75%, #000 82%, #000 91%, transparent 98%);
  mix-blend-mode:screen;
  animation:duum-splash-orbit 72s linear infinite, duum-splash-glint-in 4s ease-out backwards;
}
@keyframes duum-splash-orbit    { from { transform:rotate(0deg) } to { transform:rotate(360deg) } }
@keyframes duum-splash-glint-in { from { opacity:0 } }
@media (prefers-reduced-motion: reduce) {
  .duum-splash .splash::before { animation:none; display:none; }
}

/* ── Azoth (Miner), 1.23.0 ────────────────────────────────────────────
   One miner per panel, a three-column grid of readings inside it, and the
   buttons underneath. Cells are separated by the divider rather than filled,
   so the veil under .panel reads through them the same as everywhere else. */
.miner__grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); }
.miner__cell { padding:8px 12px; min-width:0;
  border-bottom:1px solid var(--color-divider); border-right:1px solid var(--color-divider); }
.miner__cell:nth-child(3n) { border-right:0; }
.miner__k { font-size:10px; letter-spacing:.08em; text-transform:uppercase;
  color: var(--color-neutral-500); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.miner__v { font-family:var(--font-heading); font-size:15px; font-variant-numeric:tabular-nums;
  margin-top:2px; overflow-wrap:anywhere; }
.miner__v.muted { font-size:12px; }
.miner__note { padding:7px 12px; font-size:11px; color:var(--color-neutral-500);
  border-bottom:1px solid var(--color-divider); }
.miner__acts { display:flex; flex-wrap:wrap; gap:6px; padding:9px 12px; }
.svcrow { display:flex; align-items:center; gap:8px; padding:5px 12px; font-size:12px;
  border-bottom:1px solid var(--color-divider); }
.svcrow:last-child { border-bottom:0; }
.svcrow__name { flex:1; min-width:0; font-family:var(--font-mono); font-size:11.5px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* The reading row wraps rather than squeezing its sentence into a column four
   words wide beside the chip (Covenant Theme rule 2). */
.azoth-top { flex-wrap:wrap; row-gap:6px; }

/* ── Narrow screens ──────────────────────────────────────────────────────
   The panel is a desktop instrument, but it has to stay usable on a phone,
   so the sidebar becomes a top strip and the dense grids collapse.        */
@media (max-width: 900px) {
  .miner__grid { grid-template-columns: repeat(2,minmax(0,1fr)); }
  .miner__cell:nth-child(3n) { border-right:1px solid var(--color-divider); }
  .miner__cell:nth-child(2n) { border-right:0; }
  .kpis, .kpis--wide { grid-template-columns: repeat(3,minmax(0,1fr)); }
  .split, .split-even { grid-template-columns: 1fr; }
  .triage { grid-template-columns: 14px 1fr; row-gap:2px; }
  .triage__sys, .triage .btn { display:none; }
}
@media (max-width: 680px) {
  .azoth-top__why { display:none; }
  .app { flex-direction: column; height:auto; }
  body.duum-control { overflow:auto; }
  .side { width:100%; flex-direction:row; align-items:center; flex-wrap:wrap;
    border-right:0; border-bottom:1px solid var(--color-divider); }
  .side__id { border-bottom:0; }
  .side__nav { flex-direction:row; overflow-x:auto; flex:1; }
  .side__foot { margin-top:0; border-top:0; flex-direction:row; align-items:center; gap:10px; }
  .head { flex-wrap:wrap; }
  .head__stats { order:3; width:100%; justify-content:flex-start; }
  .kpis, .kpis--wide { grid-template-columns: repeat(2,minmax(0,1fr)); }
  .stats4 { grid-template-columns: repeat(2,minmax(0,1fr)); }
  .body { height:auto; overflow:visible; }
  /* On a phone the app shell stops being 100vh, so a sheet with its own vertical
     scroll box would trap the table inside a short window in the middle of an
     already-scrolling page. Drop the height cap and let it run its full length.
     The horizontal scroll stays: eight columns do not fit a phone at any font
     size, and the alternative is the whole page sliding sideways, taking the
     nav and the header with it. With no height cap there is nothing to scroll
     vertically, so the sticky header simply never engages. */
  .sheet { max-height:none; }
}

/* Per-row freshness, added 2026-08-21 with partial refreshes.
   Quiet by default: on a fleet read on cadence this line is true, uninteresting
   and present on every card, so it must not compete with the metrics above it.
   It only raises its voice when nobody has looked in half a day, which is the
   one case where the numbers above are older than the page implies. */
.card__read{margin-top:6px;font-size:10.5px;letter-spacing:.03em;}
.card__read--stale{color:var(--color-warn,#d9b268);}

/* ── Gothic display faces ───────────────────────────────────────────────────
   Self-hosted, not linked. A private control panel should not make a request to
   a third party to render its own name, and linking would also mean the splash
   depends on Google being reachable to look like itself.

   All three are SIL Open Font License, which permits exactly this. The Windows
   blackletter that was already on the machine (Old English Text MT) is Monotype
   licensed and putting it on a web server would be redistribution, so it is not
   here however convenient it looked.

   Three are shipped because "gothic" means two different things: blackletter,
   and the American type sense where Franklin and Century Gothic are sans faces.
   Swapping --font-display below is a one-word change; the files are 9 to 42 KB
   and only the one named is ever fetched. */
@font-face { font-family:"Grenze Gotisch"; font-style:normal; font-weight:400 700;
  font-display:swap; src:url("fonts/GrenzeGotisch.woff2") format("woff2"); }
@font-face { font-family:"UnifrakturMaguntia"; font-style:normal; font-weight:400;
  font-display:swap; src:url("fonts/UnifrakturMaguntia.woff2") format("woff2"); }
@font-face { font-family:"Pirata One"; font-style:normal; font-weight:400;
  font-display:swap; src:url("fonts/PirataOne.woff2") format("woff2"); }

:root {
  /* The splash face. "Grenze Gotisch", "UnifrakturMaguntia" or "Pirata One". */
  --font-display: "Grenze Gotisch", var(--font-mono), serif;
}

/* ── Backdrop ───────────────────────────────────────────────────────────────
   The rainbow annulus on onyx, supplied 2026-08-22, filled to cover the page.

   ONE DIAL, AND IT MEANS WHAT IT SAYS. --backdrop-opacity is the opacity of the
   IMAGE: 1 shows the picture as painted, 0 hides it. It replaced a scrim alpha,
   which expressed the same thing backwards and made "42%" mean 0.58 in the
   file. A number a person will want to change should read the way they said it.

   The login screen sets it to 1 and does so in functions.php, because
   wp-login.php never loads this stylesheet. Two places, one meaning.

   A FIXED PSEUDO-ELEMENT, NOT background-attachment: fixed, which iOS Safari
   ignores outright and which repaints every scroll frame elsewhere.

   THE BASE COLOUR IS ON html AND body IS TRANSPARENT, deliberately. A negative
   z-index child paints above the canvas background but BELOW the background of
   any ancestor that paints one of its own. This worked before only because
   `html` had no background, so `body`'s was propagated to the canvas and body
   was treated as having none. Give `html` a colour without clearing `body` and
   the backdrop vanishes behind an opaque body, with nothing in the CSS looking
   wrong. Both halves below are load-bearing; do not separate them.

   THE IMAGE IS SQUARE AND NO PAGE IS, so `cover` crops. On a 16:9 desktop the
   visible band is roughly the middle 55% of the picture, which is very nearly
   the ring itself: a glow behind the work rather than a photograph of a ring.

   THE SIZES ARE CUT BY HAND. A theme image is invisible to ShortPixel and never
   receives a srcset, because responsive variants come from a media library
   attachment and this is not one (provision/README.md). The source is 2.9 MB.
   These are 85 to 365 KB, fetched once. */
:root {
  /* 1 everywhere since 2026-08-22. The page shows the picture as painted, and
     legibility is bought under the surfaces instead of by dimming the whole
     page. See --surface-dim. */
  --backdrop-opacity: 1;
  --backdrop-base:    #0e0f18;
  /* THE DIMMING THAT USED TO BE GLOBAL, now applied only beneath the panels.
     This is exactly the 58% of base that a 42% backdrop used to leave, so what
     shows through a panel is bit-identical to what showed through before, while
     the exposed background is at full strength. Metzae: "keep the current
     opacity settings on the existing windows and frames so it's still just as
     legible. I don't want it to get brighter in the windows just because the
     background is more obvious now."
     Checked by compositing rather than by eye: over a mid-bright patch of the
     ring the exposed background moves from (71,46,31) to (150,90,40) and the
     panel interior stays at (41,38,47). */
  --surface-dim:      rgba(14, 15, 24, .58);
  /* Panels float ON the backdrop rather than hiding it. Opaque surfaces would
     leave the image visible only in the gutters, which reads as a picture
     leaking round the edges of the UI instead of sitting behind it. */
  --surface-veil:     color-mix(in srgb, #232532 84%, transparent);
  --side-veil:        color-mix(in srgb, #1a1c2b 86%, transparent);
}

html { background-color: var(--backdrop-base); }
body.duum-control { background: transparent; }

/* The signed-out splash shows the picture as painted. There is nothing to read
   over it but a title and one line, so nothing needs the image to yield. The
   panel keeps 42% because it is dense tables all the way down. */
/* The splash no longer needs its own value now that the page default is 1, but
   the class stays: signed out and signed in are genuinely different pages and
   the next thing that differs between them should not have to reintroduce the
   distinction. */

body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: var(--backdrop-opacity);
  background-image:
    image-set(url("images/backdrop-1440.webp") type("image/webp"),
              url("images/backdrop-1440.jpg")  type("image/jpeg"));
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/* Phones take the small cut: 85 KB rather than 182, and at that size behind a
   veiled panel nobody can tell. */
@media (max-width: 900px) {
  body::before {
    background-image:
      image-set(url("images/backdrop-960.webp") type("image/webp"),
                url("images/backdrop-960.jpg")  type("image/jpeg"));
  }
}

/* Large or high-density displays, where the 1440 cut would visibly soften. */
@media (min-width: 1700px), (min-resolution: 1.5dppx) {
  body::before {
    background-image:
      image-set(url("images/backdrop-2048.webp") type("image/webp"),
                url("images/backdrop-2048.jpg")  type("image/jpeg"));
  }
}

/* The surfaces, veiled so the backdrop reads through the whole page.
   backdrop-filter is on the two large containers only, not on every card: it is
   a per-element compositing pass and the module matrix alone is several hundred
   cells. One blurred panel is free; four hundred is a dropped frame. */
/* Two layers each: the dim that restores what the global scrim used to do, and
   the veil on top of it. background-image paints over background-colour, so the
   order reads bottom-up as backdrop, dim, veil. */
.panel {
  background-color: var(--surface-dim);
  background-image: linear-gradient(var(--surface-veil), var(--surface-veil));
  backdrop-filter: blur(9px);
}
.side {
  background-color: var(--surface-dim);
  background-image: linear-gradient(var(--side-veil), var(--side-veil));
  backdrop-filter: blur(9px);
}
.card {
  background-color: var(--surface-dim);
  background-image: linear-gradient(var(--surface-veil), var(--surface-veil));
}

/* Without backdrop-filter, go opaque rather than translucent. A see-through
   panel over a busy image with NO blur is the one combination that is genuinely
   unreadable, so the fallback drops the effect and keeps the page usable. */
@supports not (backdrop-filter: blur(1px)) {
  .panel, .card { background: var(--color-surface); }
  .side { background: linear-gradient(180deg, #1a1c2b, #15172433); }
}
