/* Optional palettes; the unmodified palette remains the default. */
:root[data-colour-theme="ocean"]{
  --bg:#07121b;--bg2:#0c1b28;--panel:#122636;--panel2:#1b3548;--panel3:#294b60;
  --border:#294556;--border2:#52778e;--text:#effaff;--dim:#c0dce9;--faint:#8eafc1;
  --accent:#67e8f9;--accent2:#22d3ee;--accent-dark:#103b43;--accent-soft:rgba(34,211,238,.12);
  --violet:#7dd3fc;--violet2:#bae6fd;--violet-soft:rgba(125,211,252,.14);
}
:root[data-colour-theme="rose"]{
  --bg:#190c13;--bg2:#24121c;--panel:#321b28;--panel2:#442738;--panel3:#60394e;
  --border:#563247;--border2:#956780;--text:#fff1f5;--dim:#ebc6d6;--faint:#c59aae;
  --accent:#fda4af;--accent2:#fb7185;--accent-dark:#4c1d2b;--accent-soft:rgba(251,113,133,.12);
  --violet:#f9a8d4;--violet2:#fbcfe8;--violet-soft:rgba(249,168,212,.14);
}
:root[data-colour-theme="forest"]{
  --bg:#07140c;--bg2:#0c1c12;--panel:#13281b;--panel2:#1c3a27;--panel3:#2b5540;
  --border:#2a4a38;--border2:#4f8a68;--text:#effaf3;--dim:#c4e0d0;--faint:#8fb8a0;
  --accent:#86efac;--accent2:#4ade80;--accent-dark:#14532d;--accent-soft:rgba(74,222,128,.12);
  --violet:#6ee7b7;--violet2:#a7f3d0;--violet-soft:rgba(110,231,183,.14);
}
:root[data-colour-theme="amber"]{
  --bg:#140e06;--bg2:#1c1409;--panel:#2a1c0d;--panel2:#3a2812;--panel3:#543c1c;
  --border:#4a3618;--border2:#8a6a32;--text:#fff8eb;--dim:#ead9b4;--faint:#c4ab7a;
  --accent:#fbbf24;--accent2:#f59e0b;--accent-dark:#451a03;--accent-soft:rgba(245,158,11,.14);
  --violet:#fcd34d;--violet2:#fde68a;--violet-soft:rgba(252,211,77,.14);
}
:root[data-colour-theme="violet"]{
  --bg:#0e0818;--bg2:#160f24;--panel:#221833;--panel2:#2f2148;--panel3:#46336a;
  --border:#3a2a55;--border2:#7c63b0;--text:#f6f0ff;--dim:#d4c6ef;--faint:#ab96d0;
  --accent:#c4b5fd;--accent2:#a78bfa;--accent-dark:#2e1065;--accent-soft:rgba(167,139,250,.16);
  --violet:#ddd6fe;--violet2:#ede9fe;--violet-soft:rgba(221,214,254,.16);
}
:root[data-colour-theme="slate"]{
  --bg:#0b1016;--bg2:#121820;--panel:#1a222c;--panel2:#24303c;--panel3:#354656;
  --border:#2c3a48;--border2:#5b7388;--text:#f1f5f9;--dim:#c5d0db;--faint:#93a4b5;
  --accent:#94a3b8;--accent2:#64748b;--accent-dark:#1e293b;--accent-soft:rgba(148,163,184,.14);
  --violet:#cbd5e1;--violet2:#e2e8f0;--violet-soft:rgba(203,213,225,.14);
}
:root[data-colour-theme="ember"]{
  --bg:#160906;--bg2:#1f0e09;--panel:#2c1610;--panel2:#3d2016;--panel3:#5a3022;
  --border:#4a281c;--border2:#8a5640;--text:#fff4ef;--dim:#ebc9bb;--faint:#c49a88;
  --accent:#fb923c;--accent2:#f97316;--accent-dark:#431407;--accent-soft:rgba(249,115,22,.14);
  --violet:#fdba74;--violet2:#fed7aa;--violet-soft:rgba(253,186,116,.14);
}
:root[data-colour-theme="mint"]{
  --bg:#061412;--bg2:#0b1c1a;--panel:#122926;--panel2:#1a3834;--panel3:#27524c;
  --border:#264440;--border2:#4d8078;--text:#eefaf7;--dim:#c3e0db;--faint:#8fb8b2;
  --accent:#5eead4;--accent2:#2dd4bf;--accent-dark:#134e4a;--accent-soft:rgba(45,212,191,.12);
  --violet:#99f6e4;--violet2:#ccfbf1;--violet-soft:rgba(153,246,228,.14);
}
:root[data-colour-theme="midnight"]{
  --bg:#05060d;--bg2:#0a0c16;--panel:#121528;--panel2:#1b2040;--panel3:#2a3260;
  --border:#23284a;--border2:#4c5890;--text:#eef0ff;--dim:#c4c9e8;--faint:#8e95bd;
  --accent:#818cf8;--accent2:#6366f1;--accent-dark:#1e1b4b;--accent-soft:rgba(99,102,241,.16);
  --violet:#a5b4fc;--violet2:#c7d2fe;--violet-soft:rgba(165,180,252,.16);
}
:root[data-colour-theme="sand"]{
  --bg:#16120c;--bg2:#1e1912;--panel:#2a241a;--panel2:#3a3224;--panel3:#544832;
  --border:#433a2a;--border2:#7a6a4c;--text:#faf6ee;--dim:#ddd2bb;--faint:#b3a488;
  --accent:#d6b07a;--accent2:#c19654;--accent-dark:#3f2e14;--accent-soft:rgba(193,150,84,.14);
  --violet:#e7c9a0;--violet2:#f3e0c4;--violet-soft:rgba(231,201,160,.14);
}
:root[data-colour-theme="neon"]{
  --bg:#07060f;--bg2:#0d0b18;--panel:#171428;--panel2:#231e3c;--panel3:#382e5c;
  --border:#2e2750;--border2:#6b5aa0;--text:#f5f3ff;--dim:#d0c8ee;--faint:#9d93c4;
  --accent:#22d3ee;--accent2:#e879f9;--accent-dark:#1e1b4b;--accent-soft:rgba(34,211,238,.14);
  --violet:#c084fc;--violet2:#f0abfc;--violet-soft:rgba(192,132,252,.16);
}
:root:is([data-colour-theme="ocean"],[data-colour-theme="rose"],[data-colour-theme="forest"],[data-colour-theme="amber"],[data-colour-theme="violet"],[data-colour-theme="slate"],[data-colour-theme="ember"],[data-colour-theme="mint"],[data-colour-theme="midnight"],[data-colour-theme="sand"],[data-colour-theme="neon"]){
  --surface:linear-gradient(135deg,var(--panel2),var(--panel));
  --surface-edge:var(--border2);--tool-surface:var(--surface);--tool-inset:var(--bg2);
  --tool-ring:0 0 0 3px var(--accent-soft);
}
html:is([data-colour-theme="ocean"],[data-colour-theme="rose"],[data-colour-theme="forest"],[data-colour-theme="amber"],[data-colour-theme="violet"],[data-colour-theme="slate"],[data-colour-theme="ember"],[data-colour-theme="mint"],[data-colour-theme="midnight"],[data-colour-theme="sand"],[data-colour-theme="neon"]) body{background:var(--bg);color:var(--text)}
html:is([data-colour-theme="ocean"],[data-colour-theme="rose"],[data-colour-theme="forest"],[data-colour-theme="amber"],[data-colour-theme="violet"],[data-colour-theme="slate"],[data-colour-theme="ember"],[data-colour-theme="mint"],[data-colour-theme="midnight"],[data-colour-theme="sand"],[data-colour-theme="neon"]) body :is(#side,main,#topbar,#sideuser,.tabs,.uisrail,.uisside){background:var(--bg2)}
html:is([data-colour-theme="ocean"],[data-colour-theme="rose"],[data-colour-theme="forest"],[data-colour-theme="amber"],[data-colour-theme="violet"],[data-colour-theme="slate"],[data-colour-theme="ember"],[data-colour-theme="mint"],[data-colour-theme="midnight"],[data-colour-theme="sand"],[data-colour-theme="neon"]) body :is(.panel,.modal){background:var(--surface)}
html:is([data-colour-theme="ocean"],[data-colour-theme="rose"],[data-colour-theme="forest"],[data-colour-theme="amber"],[data-colour-theme="violet"],[data-colour-theme="slate"],[data-colour-theme="ember"],[data-colour-theme="mint"],[data-colour-theme="midnight"],[data-colour-theme="sand"],[data-colour-theme="neon"]) body .btn:not(.ghost):not(.danger){background:linear-gradient(120deg,var(--accent),var(--accent2));color:var(--bg)}
html:is([data-colour-theme="ocean"],[data-colour-theme="rose"],[data-colour-theme="forest"],[data-colour-theme="amber"],[data-colour-theme="violet"],[data-colour-theme="slate"],[data-colour-theme="ember"],[data-colour-theme="mint"],[data-colour-theme="midnight"],[data-colour-theme="sand"],[data-colour-theme="neon"]) body :is(#nav a.on,.tabs button.on){background:var(--accent-soft);color:var(--text);border-color:var(--border2)}
.themecontrol{display:flex;align-items:center;gap:10px;margin-top:16px;color:var(--dim);font-size:12px}
.themecontrol select{flex:1;min-width:0;padding:7px;border:1px solid var(--border);border-radius:8px;background:var(--bg2);color:var(--text)}
.fxcontrol{justify-content:space-between}
.fxcontrol input{appearance:none;width:42px;height:24px;border-radius:99px;border:1px solid var(--border2);background:var(--bg);position:relative;cursor:pointer;flex:0 0 auto}
.fxcontrol input::after{content:'';position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:var(--dim);transition:transform .2s ease,background .2s ease}
.fxcontrol input:checked{background:var(--accent-soft);border-color:var(--accent)}
.fxcontrol input:checked::after{transform:translateX(18px);background:var(--accent)}
html[data-fx="future"] #shell{position:relative;isolation:isolate;overflow:hidden}
html[data-fx="future"] #shell::before{content:'';position:absolute;inset:0;z-index:0;pointer-events:none;border-radius:inherit;background:
  linear-gradient(rgba(163,230,53,.22) 1px,transparent 1px),
  linear-gradient(90deg,rgba(167,139,250,.2) 1px,transparent 1px);
  background-size:32px 32px;animation:ppgrid 28s linear infinite!important;opacity:.7}
html[data-fx="future"] #shell::after{content:none}
html[data-fx="future"] #side,html[data-fx="future"] main{position:relative;z-index:1;box-shadow:none}
html[data-fx="future"] #view{background-image:linear-gradient(rgba(163,230,53,.16) 1px,transparent 1px),linear-gradient(90deg,rgba(167,139,250,.14) 1px,transparent 1px);background-size:32px 32px;animation:ppgrid 28s linear infinite!important}
html[data-fx="future"] .panel:not(.buddypanel),html[data-fx="future"] .modal{box-shadow:none}
html[data-fx="future"] .panel:not(.buddypanel)::after,html[data-fx="future"] .modal::after{content:none}
html[data-fx="future"] .btn:not(.ghost):not(.danger){background:linear-gradient(120deg,var(--accent),var(--accent2),var(--violet),var(--accent))!important;background-size:300% 100%;color:var(--bg)!important;animation:ppbtn 10s linear infinite!important}
html[data-fx="future"] .sidecallout .signal{animation:pppulse 2.4s ease-in-out infinite}
@keyframes ppgrid{to{background-position:32px 32px,32px 32px}}
@keyframes ppbtn{to{background-position:300% 0}}
@keyframes pppulse{0%,100%{opacity:1}50%{opacity:.65}}
:root{
  color-scheme:dark;
  --bg:#0c0814; --bg2:#130d20; --panel:#1c142c; --panel2:#291d3e; --panel3:#3e2c59;
  --border:#38284d; --border2:#70558e; --text:#f6f1ff; --dim:#d3c5e5; --faint:#ad99c5;
  --accent:#a3e635; --accent2:#84cc16; --accent-dark:#263b16; --accent-soft:rgba(163,230,53,.12);
  --violet:#a78bfa; --violet2:#c4b5fd; --violet-soft:rgba(167,139,250,.14);
  --blue:#80bdff; --amber:#f5cc85; --red:#ff8c9c; --red-soft:rgba(255,140,156,.11);
  --muted:var(--dim);
  --shadow:0 24px 70px rgba(0,0,0,.4); --mono:Consolas,'Cascadia Mono',Menlo,monospace;
}
/* Floating command-center shell; existing routes and controls stay unchanged. */
@media(min-width:901px){
  body #shell{padding:8px;gap:8px;grid-template-columns:220px minmax(0,1fr)}
  body #side{border:1px solid var(--border);border-radius:14px;background:linear-gradient(165deg,#281a3d,#130d20 65%);box-shadow:inset 0 1px 0 #ffffff08,0 16px 48px #0003}
  body main{border:1px solid var(--border);border-radius:14px;overflow:hidden;background:rgba(17,11,28,.8)}
  body #topbar{height:48px;background:rgba(30,20,45,.92);padding-inline:14px}
  body #view{padding:14px 14px 72px}
}
/* Responsive shell: keep navigation usable on tablets without shrinking content. */
@media(min-width:601px) and (max-width:900px){
  body #shell{padding:10px;gap:10px;grid-template-columns:200px minmax(0,1fr)}
  body #side{border:1px solid var(--border);border-radius:16px;background:linear-gradient(165deg,#281a3d,#130d20 65%)}
  body main{min-width:0;border:1px solid var(--border);border-radius:16px;overflow:hidden}
  body #view{padding:12px 12px 72px}
  body .panel{padding:10px 12px}
}
body #side{padding:8px 8px 6px;overflow:hidden;min-height:0;display:flex;flex-direction:column}
body #side .brand{padding:4px 6px 8px;gap:8px}
body #side .brandmark{width:56px;height:33.6px;flex-shrink:0;background:transparent;filter:none}
body #side .brandcopy b{font-size:18px}
body #side .brandcopy small{margin-top:3px}
body #side .navlabel{font-size:9px;letter-spacing:.14em;color:var(--faint);padding:4px 10px 4px}
body #side .navlabel-spaced{margin-top:6px}
body #topbar .crumb{font-size:17px;font-weight:750}
body #sideuser{border:1px solid var(--border);border-radius:10px;background:rgba(16,10,26,.5);padding:8px 10px;margin-top:8px}
body #sideuser b{font-size:12px}
body #nav{gap:1px;flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin}
body #nav a{min-height:32px;padding:5px 10px;border-radius:8px;gap:8px}
body #nav a::before{display:grid;place-items:center;width:22px;height:22px;border-radius:6px;background:rgba(167,139,250,.07);font-size:16px}
body #nav a.on::before{background:rgba(167,139,250,.13)}
body #nav a.on{background:linear-gradient(100deg,rgba(167,139,250,.18),rgba(139,92,246,.1));box-shadow:inset 3px 0 var(--accent2),0 5px 20px #0002}
body #side .sidesearch{position:relative;margin-bottom:4px}
body #side .sidesearch input{width:100%;min-height:32px;padding:6px 8px}
body .sidecallout{padding:7px 8px;margin:6px 0 0;border-radius:8px}
.themecontrol{margin-top:8px}
body #topbar .crumb{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
body .panel{background:linear-gradient(145deg,rgba(37,25,54,.97),rgba(22,15,34,.98))}
body .btn:not(.ghost):not(.danger){background:linear-gradient(120deg,#bef264,#84cc16);box-shadow:0 4px 18px #a78bfa18,inset 0 1px 0 #ffffff55}
body .btn{min-height:32px;padding:6px 12px}
body .tabs{display:flex;flex-wrap:nowrap;overflow-x:auto;max-width:100%;border:1px solid var(--border);border-radius:14px;background:rgba(16,10,26,.8);scroll-padding-inline:8px}
body .tabs button{flex:0 0 auto;white-space:nowrap;color:var(--dim);border-radius:9px}
body .tabs button.on{font-weight:750;position:relative}
body .tabs button.on::after{content:'';position:absolute;bottom:3px;left:30%;right:30%;height:2px;border-radius:2px;background:var(--accent);box-shadow:0 0 10px #a78bfa66;pointer-events:none}
body .controls{gap:10px}
body .panel>h3{padding-bottom:14px;border-bottom:1px solid var(--border)}
body .tabs button.on{background:linear-gradient(115deg,rgba(167,139,250,.18),rgba(139,92,246,.15));border-color:#7958a6;color:#e0d2ff}
body :is(.cfgbox,.netblk,.renewbox)>summary{display:flex;align-items:center;gap:10px;list-style:none}
body :is(.cfgbox,.netblk,.renewbox)>summary::before{content:'+';display:grid;place-items:center;width:24px;height:24px;border-radius:7px;background:var(--accent-soft);color:var(--accent);font-family:var(--mono);flex-shrink:0}
body :is(.cfgbox,.netblk,.renewbox)[open]>summary::before{content:'−'}
body :is(.cfgbox,.netblk,.renewbox)>summary::-webkit-details-marker{display:none}
body .fsbar{padding:12px;background:var(--tool-inset);border:1px solid var(--border);border-radius:12px}
body :is(.uisrail,.uisside){padding:12px;background:rgba(16,10,26,.5);border:1px solid var(--border);border-radius:14px}
body .uisrailhead{color:var(--accent)}
body .modal{background:linear-gradient(145deg,#2a1b3e,#150e23);border:1px solid rgba(167,139,250,.25);border-radius:22px;border-top-color:var(--accent2);box-shadow:0 32px 100px #0009,inset 0 1px 0 #ffffff0a}
body .authcard{box-shadow:0 24px 100px #0008,0 0 80px #a78bfa09}
/* Submenu rails, search and backup workflows share the command-center language. */
body :is(.wxside,.ppcats){background:rgba(16,10,26,.65);border:1px solid var(--border);border-radius:14px;padding:10px}
body :is(.wxitem,.ppcat){min-height:32px;border-radius:8px;transition:background .15s,border-color .15s;color:var(--dim)}
body :is(.wxitem,.ppcat):hover{background:rgba(167,139,250,.08);color:var(--text)}
body :is(.wxitem,.ppcat):focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
body .wxside{gap:6px}
body .wxitem.on{color:var(--accent);font-weight:700}
body :is(.wxitem,.ppcat).on{background:linear-gradient(110deg,rgba(167,139,250,.16),rgba(139,92,246,.1));border-color:var(--accent2);box-shadow:inset 3px 0 var(--accent2)}
body :is(.planbox,.copybox){background:linear-gradient(135deg,#251a38,#160f24);border-radius:16px;padding:18px}
body :is(.bkcat,.copytarget,.gitem){border-radius:12px;background:rgba(19,12,31,.65)}
body :is(.bkcat,.copytarget).on{background:var(--accent-soft);border-color:var(--accent2)}
body :is(.bkcat,.copytarget,.gitem):hover{border-color:var(--border2)}
body .spanel{background:linear-gradient(145deg,#2a1b3e,#150e23);border-color:var(--border2);border-radius:18px;box-shadow:var(--shadow)}
body .shead{padding:16px 18px}
body .sglabel{color:var(--accent);letter-spacing:.12em}
body .shit{margin:3px 8px;padding:12px;border-radius:9px}
body .shit:hover{background:var(--accent-soft)}
body .sidesearch input{padding-right:52px}
body :is(.mailfacts>div,.readyitem){border-radius:12px;background:rgba(23,15,36,.8)}
body :is(button,a,input,select,textarea,summary):focus-visible{outline:2px solid var(--accent);outline-offset:3px}
@media(prefers-reduced-motion:reduce){
  html:not([data-fx="future"]) body *,html:not([data-fx="future"]) body *::before,html:not([data-fx="future"]) body *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
}
@media(max-width:560px){
  body :is(.planbox,.copybox){padding:12px}
  body .ppcats{padding:8px;scrollbar-width:thin}
}
/* Unified command-center surfaces, shared by the panel and its submenus. */
body #view{
  background-image:radial-gradient(ellipse at 90% 0,rgba(167,139,250,.09),transparent 50%),linear-gradient(rgba(167,139,250,.025) 1px,transparent 1px),linear-gradient(90deg,rgba(167,139,250,.025) 1px,transparent 1px);
  background-size:auto,40px 40px,40px 40px;
}
body .page>h1,body .pagebar>h1{font-size:clamp(18px,2vw,22px);letter-spacing:-.03em;font-weight:750;line-height:1.2;text-wrap:balance;margin:0}
/* Shared form treatment also reaches dialogs and every settings submenu. */
body :is(input,select,textarea):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]){
  background:#140e21;border:1px solid var(--border2);border-radius:10px;
  color:var(--text);padding:6px 10px;box-shadow:inset 0 2px 5px #0002;
  transition:border-color .15s,box-shadow .15s;
}
body :is(input,select,textarea):not([type=checkbox]):not([type=radio]):not([type=range]):focus{
  border-color:var(--accent2);box-shadow:0 0 0 3px #a78bfa20;
}
body :is(input,select,textarea):disabled{opacity:.55;cursor:not-allowed}
body button:disabled,body .btn:disabled{opacity:.45;cursor:not-allowed;box-shadow:none;filter:saturate(.35)}
/* An editor button whose module is off, or whose server has not reported yet. It is *not* `disabled`:
   a disabled button cannot be clicked, and the whole point of drawing it is that pressing it says why
   it cannot open. Same look, a dashed edge to say it is a promise rather than a control, and a
   `help` cursor so it reads as "ask me" instead of "broken". */
body .btn.off{opacity:.5;box-shadow:none;filter:saturate(.35);border-style:dashed;cursor:help}
body .btn.off:hover{opacity:.62;box-shadow:none}
body :is(input,select,textarea)[aria-invalid=true]{border-color:var(--red);box-shadow:0 0 0 3px var(--red-soft)}
body :is(input,textarea):not(:placeholder-shown):user-invalid{border-color:var(--red)}
body :is(.wxitem,.ppcat,.tabs button,#nav a):focus-visible{outline:2px solid var(--accent);outline-offset:-3px}
body :is(.consbox,textarea.editor){scrollbar-width:thin;scrollbar-color:var(--border2) #140e21}
body ::selection{background:#5b3485;color:#faf5ff}
body :is(.cfgbox,.netblk,.renewbox)>summary{cursor:pointer}
body :is(.controls,.modalfoot){row-gap:12px}
body .panel table{font-variant-numeric:tabular-nums}
@media(forced-colors:active){
  body :is(#nav a.on,.tabs button.on,.chip.on,.ppcat.on){outline:2px solid Highlight;outline-offset:-2px}
}
/* Clear interaction states across every workspace, including nested editors. */
body :is(input,select,textarea){accent-color:var(--accent2)}
body :is(button,a,summary){-webkit-tap-highlight-color:transparent}
body :is(.panel,.cfgbox,.netblk,.renewbox){scroll-margin-top:24px}
body :is(.tabs,.ppcats){scrollbar-width:thin;scrollbar-color:var(--border2) transparent}
@media(hover:hover){
  body #nav a:hover{background:rgba(167,139,250,.09);color:var(--text)}
  body .tabs button:not(.on):hover{background:rgba(167,139,250,.09);color:var(--text)}
  body :is(.cfgbox,.netblk,.renewbox)>summary:hover{color:var(--accent)}
}
body .page>.pagedesc{display:none}
body .panel{background:linear-gradient(145deg,rgba(37,25,54,.97),rgba(22,15,34,.98));border:1px solid rgba(167,139,250,.17);border-radius:12px;border-top-color:rgba(167,139,250,.28);padding:12px 14px;box-shadow:inset 0 1px 0 rgba(167,139,250,.06),0 8px 20px #0003;min-width:0}
.pagebar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 10px}
.pagebar h1{flex:1 1 auto}
details.pop{position:relative;flex:0 0 auto}
details.pop>summary{list-style:none;cursor:pointer;display:inline-flex;align-items:center;gap:6px;padding:5px 10px;border:1px solid var(--border);border-radius:8px;background:transparent;color:var(--text);font-size:12px;font-weight:600;user-select:none}
details.pop>summary::-webkit-details-marker{display:none}
details.pop>summary:after{content:'▾';opacity:.7;font-size:10px}
details.pop[open]>summary{border-color:var(--border2);background:rgba(167,139,250,.1)}
.popbox{position:absolute;top:calc(100% + 6px);left:0;z-index:50;min-width:240px;max-width:min(560px,88vw);padding:6px;border:1px solid var(--border2);border-radius:12px;background:var(--panel);box-shadow:0 16px 40px #0007}
/* The About chip sits at the right edge of the page bar, and a popover that opens to the right from
   there runs off the window: measured at 1278px on a 1389px page, it ended at 1498 and the page
   description - the thing it exists to show - was half outside it. The health popover already
   answered this by opening leftwards; this is the same answer for the page bar. */
.pagebar details.pop>.popbox{right:0;left:auto}
.popbox .pagedesc,.popbox .hint{margin:0;padding:8px 10px;color:var(--dim);font-size:12.5px;line-height:1.45}
.popbox button,.popbox a.btn{display:block;width:100%;text-align:left;margin:0;border:0;border-radius:8px;background:transparent;color:var(--text);padding:8px 10px;font-size:13px}
.popbox button.on{background:var(--accent-soft)}
.popbox button:hover,.popbox a.btn:hover{background:rgba(167,139,250,.12);text-decoration:none}
.terse{cursor:help}
.tabs.tabpop{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:0;margin:0 0 10px;background:transparent;border:0;box-shadow:none;overflow:visible;position:relative;z-index:20}
/* Purple surfaces keep the green navigation accents and primary actions easy to find. */
body .page{min-width:0}
body .panel :is(h3,h4){color:var(--text);line-height:1.4}
body .panel :is(.sub,.note){line-height:1.4}
body :is(.cfgbox,.netblk,.renewbox)>summary{min-height:32px}
body :is(.uisrail,.uisside,.ppcats){scrollbar-width:thin;scrollbar-color:var(--border2) transparent}
body :is(.consbox,textarea.editor){font-variant-ligatures:none;tab-size:2}
body .modal{overscroll-behavior:contain}
@media(hover:hover){
  body .btn:not(:disabled):hover{box-shadow:0 0 0 1px rgba(167,139,250,.25),0 6px 22px #0003}
  body :is(.chip,.days button,.ppcat):not(.on):hover{background:rgba(167,139,250,.1);color:var(--text)}
}
@media(max-width:600px){
  body .tabs{border-radius:12px;scroll-snap-type:x proximity}
  body .tabs button{scroll-snap-align:start}
  body .modalfoot .btn{flex:1 1 auto}
}
body .panel h3{font-size:15px;letter-spacing:-.015em}
body .cards .card{position:relative;overflow:hidden;padding:10px 12px}
body .cards .card::before{content:"";position:absolute;inset:0 20px auto;width:auto;height:2px;background:linear-gradient(90deg,var(--accent2),var(--violet),transparent);pointer-events:none}
body .cards .card::after{content:"";position:absolute;inset:0 0 auto auto;width:100px;height:100px;background:radial-gradient(circle at top right,rgba(167,139,250,.12),transparent 70%);pointer-events:none}
body .card .k{font-size:11px;letter-spacing:.11em}
body .card .v small{line-height:1.6;margin-top:8px}
body .card .v{font-variant-numeric:tabular-nums;font-size:22px}
body .tabs{display:flex;flex-wrap:nowrap;padding:6px;gap:6px;background:rgba(5,13,25,.75);border:1px solid rgba(167,139,250,.16);border-radius:14px;box-shadow:inset 0 1px 10px #0003}
body .tabs button{min-height:32px;padding:6px 12px}
body .tabs button{border:1px solid transparent;border-radius:10px;color:var(--dim);background:transparent;font-weight:650;transition:background .15s,border-color .15s,color .15s}
body .tabs button.on{color:#b6f6ff;background:linear-gradient(120deg,rgba(167,139,250,.2),rgba(139,92,246,.13));border-color:rgba(167,139,250,.35);box-shadow:inset 0 1px 0 #ffffff12,0 3px 12px #0003}
body th{padding:7px 10px;background:rgba(5,12,25,.65)}
body td{padding:8px 10px;border-bottom-color:var(--border)}
body tr:hover td{background:rgba(167,139,250,.045)}
body .pill{padding:5px 10px;font-size:11px}
body .overlay{background:rgba(3,8,17,.78);backdrop-filter:blur(8px)}
body .modal h3{font-size:23px;letter-spacing:-.025em}
body .modal :is(input,select,textarea){border-color:var(--border2)}
body .modal label{font-size:12px;margin-top:16px}
body :is(input,textarea)::placeholder{color:var(--faint);opacity:1}
body .modal>.sub{font-size:13px;line-height:1.7}
body .modalfoot{gap:10px;flex-wrap:wrap}
body .empty{padding:56px 24px;border:1px dashed var(--border2);border-radius:16px;background:radial-gradient(ellipse at top,rgba(167,139,250,.06),transparent 70%),rgba(5,13,25,.45);color:var(--dim);line-height:1.8;text-wrap:pretty}
body .empty .btn{margin-top:16px}
body .panel table :is(th,td):first-child{padding-left:18px}
body .panel table :is(th,td):last-child{padding-right:18px}
body .panel tr:focus-within td{background:rgba(167,139,250,.06)}
body .ppcats{width:170px}
body .ppcat{padding:11px 13px}
body .ppgrid{gap:12px}
body .alertbanner{border-radius:14px;padding:16px 18px}
@media(max-width:600px){
  body #topbar{padding-inline:12px;gap:6px;height:auto;min-height:64px;flex-wrap:wrap;padding-block:10px}
  body #view{padding:20px 12px 104px}
  body .panel,body .cards .card{padding:16px}
  body .grid.cols{grid-template-columns:minmax(0,1fr)}
  body .ppbody{flex-direction:column}
  body .ppcats{width:100%;flex-direction:row;overflow-x:auto;border-right:0;border-bottom:1px solid var(--border);padding:0 0 10px}
  body .ppcat{flex-shrink:0;white-space:nowrap}
  body .ppsearchrow{flex-wrap:wrap}
  body .ppsearchrow input{min-width:0}
  body .netgame{flex-wrap:wrap}
  body .netgame input,body .netgame select{min-width:100px}
  body .modal{padding:20px 16px;max-height:90dvh}
  body .toast{min-width:0;max-width:calc(100vw - 40px)}
}
:root{
  --surface:linear-gradient(135deg,rgba(27,47,72,.97),rgba(12,23,40,.99));
  --surface-edge:rgba(128,210,255,.3);
  --tool-surface:linear-gradient(145deg,rgba(24,40,62,.92),rgba(10,20,35,.96));
  --tool-inset:rgba(5,13,25,.65);
  --tool-ring:0 0 0 3px rgba(167,139,250,.14);
}
/* Nested workspaces share the same visual language as the main navigation. */
body :is(.cfgbox,.netblk,.renewbox,.ppsel,.npclist,.uishelp){
  background:var(--tool-surface);border:1px solid var(--border);border-radius:14px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.04);
}
body :is(.gamecard,.pp-tile){
  background:var(--tool-surface);border-radius:14px;padding:16px;
}
body :is(.gamecard.sel,.ppcat.on,.chip.on,.days button.on){
  background:linear-gradient(115deg,rgba(167,139,250,.16),rgba(139,92,246,.1));
  border-color:var(--accent2);color:var(--accent);
}
body :is(.consbox,textarea.editor){
  background:var(--tool-inset);border:1px solid var(--border);border-radius:14px;
  padding:18px;line-height:1.7;box-shadow:inset 0 2px 16px rgba(0,0,0,.2);
}
body :is(.ppcat,.chip,.days button){min-height:40px;transition:background .15s,color .15s,border-color .15s}
/* Clear, unified controls throughout nested settings and tool workspaces. */
body :is(.cfgbox,.netblk,.renewbox){padding:18px}
body :is(.cfgbox,.netblk,.renewbox) label{color:var(--dim);line-height:1.5}
body :is(.cfgbox,.netblk,.renewbox) input:not([type=checkbox]):not([type=radio]):not([type=range]),
body :is(.cfgbox,.netblk,.renewbox) select{min-height:42px}
body :is(.cfgbox,.netblk,.renewbox) summary{padding:8px 0;color:var(--text);font-weight:650}
body :is(.chip,.days button){border-radius:9px}
body .btn.sm{min-height:34px;padding:6px 12px;border-radius:9px}
body .btn.ghost{background:rgba(17,32,49,.65)}
body .btn.ghost:hover{background:var(--accent-soft)}
body .btn:disabled{transform:none;filter:none;box-shadow:none;opacity:.45}
body .btn:not(:disabled):active{transform:translateY(1px)}
body .btn.danger{background:var(--red-soft);border:1px solid rgba(255,140,156,.45);color:var(--red)}
body .btn.danger:not(:disabled):hover{background:rgba(255,140,156,.2);border-color:var(--red)}
body :is(.gamecard,.pp-tile):hover{box-shadow:0 8px 24px #0003,inset 0 0 0 1px rgba(167,139,250,.12)}
body :is(.cfgbox,.netblk,.renewbox) :is(input,select,textarea):focus{
  border-color:var(--accent2);box-shadow:var(--tool-ring);
}
body #authwrap{min-height:100vh;min-height:100dvh;height:auto;background:radial-gradient(ellipse at 20% 15%,#a78bfa17,transparent 50%),radial-gradient(ellipse at 85% 80%,#8b5cf620,transparent 50%),var(--bg)}
body .authcard{background:var(--tool-surface);border-radius:24px;border-top-color:var(--accent2);width:440px;padding:40px}
body .authlogo .brandmark{width:160px;height:96px;margin-bottom:20px;background:transparent;filter:none}
body .authword{font-size:36px}
body .authcard h2{font-size:24px;letter-spacing:-.035em}
body .authcard .sub{font-size:13px;line-height:1.7}
body .authcard .field{min-height:46px}
body .authfoot{line-height:1.7}
body .toast{background:var(--tool-surface);border-radius:14px;padding:14px 18px}
body .bc{gap:5px}
body .bc a{padding:6px 9px;border:1px solid transparent;border-radius:8px}
body .bc a:hover{border-color:var(--border)}
body .statbar{gap:10px;padding:12px 14px;border:1px solid var(--border);border-radius:12px;background:var(--tool-inset);flex-wrap:wrap}
body .consrow{gap:10px}
body .consrow input{font-family:var(--mono);min-height:44px}
body .bc{flex-wrap:wrap;overflow-wrap:anywhere}
body .uishelp{padding:16px;color:var(--dim);line-height:1.7}
body .pp-tile{border:1px solid var(--border);transition:border-color .15s,background .15s}
body .pp-tile:hover{border-color:var(--accent2)}
body :is(.cfgbox,.netblk,.renewbox)[open]{border-color:var(--border2);box-shadow:inset 3px 0 0 var(--accent2),0 8px 24px #0002}
body :is(.cfgbox,.netblk,.renewbox)[open]>summary{border-bottom:1px solid var(--border);padding-bottom:14px;margin-bottom:14px}
body :is(.cfgbox,.netblk,.renewbox)>summary:focus-visible{border-radius:6px}
body select{cursor:pointer}
body select option{background:var(--bg2);color:var(--text)}
body .ppcats{gap:6px}
body .ppcat{border:1px solid transparent}
body .ppcat.on{border-color:rgba(167,139,250,.3)}
/* Persistent wayfinding across the main panel and nested workspaces. */
body #nav a{border:1px solid transparent;border-radius:11px;min-height:44px;margin-bottom:5px;transition:background .15s,border-color .15s,color .15s}
body #nav a.on{color:#b6f6ff;background:linear-gradient(100deg,rgba(167,139,250,.16),rgba(139,92,246,.08));border-color:rgba(167,139,250,.24);box-shadow:inset 3px 0 0 var(--accent2)}
body #topbar{background:rgba(10,19,33,.94);border-bottom:1px solid rgba(167,139,250,.17);backdrop-filter:blur(16px)}
body .gamecard.sel,body .pp-tile.sel{border-color:var(--accent2);box-shadow:0 0 0 2px rgba(167,139,250,.12)}
body .modalfoot{border-top:1px solid var(--border);padding-top:18px}
body .sidecallout{background:linear-gradient(120deg,rgba(167,139,250,.1),rgba(139,92,246,.1));border-color:var(--border);border-radius:14px}
@media(max-width:600px){
  body .consrow{flex-wrap:wrap}
  body .consrow input{flex-basis:100%}
  body .formrow{flex-direction:column;gap:0}
  body .fsbar{flex-wrap:wrap}
  body .authcard{padding:26px 20px}
  body .cards{grid-template-columns:repeat(2,minmax(0,1fr))}
  body .card .v{font-size:28px}
  body .ppsel{flex-wrap:wrap}
  body .tabs button{min-height:42px;padding:9px 12px}
  body .statbar{font-size:12px}
}
@media(forced-colors:active){
  body #nav a.on,body .tabs button.on,body .ppcat.on,body .chip.on{outline:2px solid Highlight;outline-offset:-3px}
}
html{scrollbar-color:var(--border2) var(--bg);scrollbar-width:thin}
button,a,input,select,textarea{touch-action:manipulation}
input,select,textarea{max-width:100%}
.formrow>div,.inputrow .field,.consrow input{min-width:0}
/* Shared interaction defaults apply to every submenu and editor. */
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
::selection{background:var(--accent-dark);color:var(--text)}
input[type=checkbox],input[type=radio],input[type=range]{accent-color:var(--accent2)}
button:disabled{cursor:not-allowed}
summary{cursor:pointer}
@media(prefers-reduced-motion:reduce){
  html:not([data-fx="future"]) *,html:not([data-fx="future"]) *::before,html:not([data-fx="future"]) *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
}
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{background:radial-gradient(ellipse at 90% 0,rgba(167,139,250,.13),transparent 45%),radial-gradient(ellipse at 10% 90%,rgba(139,92,246,.12),transparent 50%),var(--bg);background-attachment:fixed;color:var(--text);font:14px/1.6 Inter,ui-sans-serif,system-ui,-apple-system,'Segoe UI',sans-serif;letter-spacing:.01em}
body:before{content:"";position:fixed;inset:0;pointer-events:none;background:radial-gradient(900px 420px at 72% -12%,rgba(131,48,252,.13),transparent 68%),linear-gradient(115deg,rgba(255,255,255,.015),transparent 35%);z-index:-1}
a{color:var(--violet2);text-decoration:none} a:hover{text-decoration:underline}
button{font:inherit;cursor:pointer} input,select,textarea{font:inherit;background:#091321;color:var(--text);border:1px solid var(--border);border-radius:11px;padding:11px 13px;outline:none;transition:border-color .15s,box-shadow .15s,background .15s}
input:hover,select:hover,textarea:hover{border-color:var(--border2)} input:focus,select:focus,textarea:focus{border-color:var(--accent2);box-shadow:0 0 0 3px var(--accent-soft);background:#0e151d}
label{color:var(--dim);font-size:12px;display:block;margin:13px 0 5px;font-weight:600;letter-spacing:.02em} label .req{color:var(--red)}
.boot{display:flex;height:100vh;align-items:center;justify-content:center;gap:12px;color:var(--dim);font-size:13px}/* The mark is an SVG symbol (index.html) so every copy is the same drawing. */
/* The mark is 81x33, so roughly 2.45:1. Every size below keeps that ratio: a
   mismatched height letterboxes the drawing inside its own box. */
.bootmark{width:56px;height:28px;display:inline-block;vertical-align:middle}
/* auth */
#authwrap{display:flex;align-items:center;justify-content:center;height:100vh;background:radial-gradient(680px 480px at 50% 0%,rgba(131,48,252,.22),transparent 70%),var(--bg);padding:20px}
.authcard{width:390px;max-width:100%;background:rgba(17,23,32,.94);border:1px solid var(--border);border-radius:18px;padding:34px;box-shadow:var(--shadow)}
.authlogo{text-align:center;margin-bottom:25px}.brandmark{display:inline-block;vertical-align:middle;width:56px;height:28px}.authlogo .brandmark{display:block;width:224px;height:112px;margin:0 auto 14px}.authword{display:block;font-size:30px;line-height:1;font-weight:800;letter-spacing:-.045em}.authword i{font-style:normal;color:var(--accent)}.authword b{color:var(--violet)}.authtag{display:block;color:var(--faint);font-size:9.5px;letter-spacing:.2em;font-weight:700;margin-top:11px}.authcard h2{font-size:18px;margin:0 0 3px}.authcard .sub{color:var(--dim);font-size:12px;margin-bottom:12px}.authfoot{border-top:1px solid var(--border);margin-top:25px;padding-top:15px;color:var(--faint);font-size:11px;text-align:center}.signal{width:7px;height:7px;border-radius:50%;display:inline-block;background:var(--violet);box-shadow:0 0 10px rgba(131,48,252,.75);margin-right:6px}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;border:1px solid transparent;background:var(--accent2);color:#04212a;font-weight:750;padding:10px 16px;border-radius:10px;transition:filter .15s,transform .1s,background .15s,border-color .15s;box-shadow:0 2px 8px rgba(0,0,0,.16)}.btn:hover{filter:brightness(1.08);text-decoration:none}.btn:active{transform:translateY(1px)}.btn:disabled{opacity:.45;cursor:not-allowed}.btn.ghost{background:transparent;border-color:var(--border);color:var(--text);font-weight:600;box-shadow:none}.btn.ghost:hover{border-color:var(--accent2);color:var(--accent)}.btn.danger{background:var(--red);color:#21090b}.btn.sm{padding:5px 10px;font-size:12px;border-radius:7px}.btn.block{width:100%;margin-top:17px}.formrow{display:flex;gap:12px}.formrow>div{flex:1}.field{width:100%;margin:0}.err{color:var(--red);font-size:12px;min-height:16px;margin-top:7px}.note{color:var(--dim);font-size:12px}.note.warn{color:var(--amber)}.note.err{color:var(--red)}
/* shell */
#shell{display:grid;grid-template-columns:248px minmax(0,1fr);height:100vh;height:100dvh}#side{background:linear-gradient(180deg,#101c2d,#0a1220);border-right:1px solid var(--border);display:flex;flex-direction:column;padding:26px 14px 18px;min-height:0;overflow-y:auto}.brand{display:flex;align-items:center;gap:12px;padding:4px 12px 28px}.brandcopy{display:flex;flex-direction:column;line-height:1;min-width:0}.brandcopy b{font-size:19px;letter-spacing:-.045em}.brandcopy .word i{font-style:normal;color:var(--accent)}.brandcopy .word b{color:var(--violet)}.brandcopy small{font-size:8px;letter-spacing:.18em;color:var(--faint);font-weight:700;margin-top:6px}
/* The tagline under the wordmark. "Play" takes the green half of the mark and the
   rest stays quiet, so the sidebar reads "PlayPort | Play your way" without the
   pipe having to be drawn. */
.brandcopy .tag{letter-spacing:.14em;color:#7d8798;white-space:nowrap}.brandcopy .tag i{font-style:normal;color:var(--accent)}.brandver{font-style:normal;color:var(--faint);font-size:10px;margin-left:auto;align-self:flex-start;margin-top:2px}.navlabel{color:var(--faint);font-size:9px;font-weight:800;letter-spacing:.17em;padding:0 13px 8px}.navlabel-spaced{margin-top:26px}#nav{display:flex;flex-direction:column;gap:4px;flex:1}#nav a{color:var(--dim);padding:12px 13px;border:1px solid transparent;border-radius:12px;font-weight:600;display:flex;align-items:center;gap:11px;position:relative;transition:background .18s,border-color .18s,color .18s}#nav a:hover{color:var(--text);background:rgba(255,255,255,.045);text-decoration:none}#nav a.on{color:var(--accent);background:linear-gradient(110deg,rgba(167,139,250,.16),rgba(139,92,246,.08));border-color:rgba(167,139,250,.24);box-shadow:inset 3px 0 0 var(--accent2),0 4px 18px #0002}#nav a::before{content:attr(data-ic);font-size:17px;width:20px;text-align:center;opacity:.9;font-weight:400}.sidecallout{border:1px solid rgba(131,48,252,.3);background:linear-gradient(120deg,rgba(131,48,252,.09),rgba(156,232,11,.05));border-radius:10px;padding:11px 12px;display:flex;align-items:center;gap:9px;margin:0 3px 13px;color:var(--dim)}.sidecallout b{display:block;color:var(--text);font-size:11px}.sidecallout small{font-size:10px;color:var(--faint)}#sideuser{border-top:1px solid var(--border);padding:14px 10px 2px;color:var(--dim);font-size:11px}#sideuser b{color:var(--text);display:block;font-size:12px;margin-bottom:2px}#sideuser button{margin-top:9px}
/* The page reserves a gutter where the assistant's bubble floats. Without it the last row of a
   table - a Copy button, a port address - sat underneath the bubble and could not be read or
   clicked until you scrolled, which is what "it keeps bugging me on the other tabs" meant. */
main{display:flex;flex-direction:column;min-width:0;min-height:0}#topbar{height:56px;flex-shrink:0;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;padding:0 20px;background:rgba(11,19,33,.88);backdrop-filter:blur(16px);position:sticky;top:0;z-index:10}#topbar .crumb{font-weight:700;font-size:14px;letter-spacing:-.02em;margin:0;line-height:1.15;min-width:0}#topbar .crumb a.btn{font-size:12px;letter-spacing:0;margin-left:10px}.spacer{flex:1}#view{flex:1;overflow:auto;padding:16px 20px 88px;scrollbar-color:#2b394a transparent}
/* generic */
.page{max-width:1500px;margin:0 auto}.page h1{font-size:22px;line-height:1.2;letter-spacing:-.03em;margin:0}.page .pagedesc{color:var(--dim);font-size:12.5px;margin:0}.grid{display:grid;gap:10px}.grid.cols{grid-template-columns:repeat(auto-fit,minmax(270px,1fr))}.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-bottom:10px}.card{background:radial-gradient(ellipse at top right,rgba(167,139,250,.09),transparent 70%),linear-gradient(145deg,#142338,#0f1a2a);border:1px solid var(--border);border-radius:12px;padding:12px 14px;box-shadow:inset 0 1px 0 #ffffff06,0 8px 24px #0002;min-width:0;transition:transform .15s,border-color .15s,box-shadow .15s}.card:hover{border-color:var(--border2);box-shadow:0 8px 24px rgba(0,0,0,.17)}.card .k{color:var(--dim);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.09em}.card .v{font-size:22px;font-weight:750;letter-spacing:-.04em;margin-top:4px}.card .v small{display:block;font-size:11px;color:var(--dim);font-weight:400;letter-spacing:0;margin-top:2px}.panel{background:linear-gradient(145deg,rgba(19,32,50,.97),rgba(12,22,37,.97));border:1px solid var(--border);border-radius:12px;padding:14px;margin-bottom:10px;box-shadow:inset 0 1px 0 #ffffff05,0 8px 28px #0002}.panel h3{margin:0 0 8px;font-size:13px;letter-spacing:-.01em}.row{display:flex;gap:8px;align-items:center}.controls{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px;align-items:center}.btnrow{display:flex;gap:8px;flex-wrap:wrap}
.pill{display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:20px;font-size:10px;font-weight:800;text-transform:capitalize;letter-spacing:.04em;white-space:nowrap}.pill:before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor}.pill.running{background:rgba(74,222,128,.12);color:#86efac}.pill.stopped{background:rgba(145,160,179,.12);color:var(--dim)}.pill.error,.pill.crashed{background:var(--red-soft);color:var(--red)}.pill.starting,.pill.stopping,.pill.installing,.pill.restarting{background:rgba(241,198,117,.13);color:var(--amber)}.st{width:8px;height:8px;border-radius:50%;background:var(--faint);display:inline-block;margin-right:6px}.st.on{background:var(--accent);box-shadow:0 0 7px var(--accent)}.st.bad{background:var(--red)}
table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px;overflow:hidden}th{background:rgba(5,13,24,.4);color:var(--dim);text-align:left;font-weight:750;padding:10px 12px;border-bottom:1px solid var(--border);font-size:10px;text-transform:uppercase;letter-spacing:.09em}td{padding:12px;border-bottom:1px solid rgba(38,50,66,.72);vertical-align:middle}tr:last-child td{border-bottom:0}tr:hover td{background:rgba(255,255,255,.025)}.actions{white-space:nowrap}.actions button{margin-right:5px}.empty{color:var(--faint);text-align:center;padding:22px 8px;font-size:13px}.empty .eic{font-size:22px;display:block;margin-bottom:6px;opacity:.65}
/* modal/toasts */
#toasts{position:fixed;top:18px;right:20px;z-index:300;display:flex;flex-direction:column;gap:9px}
/* **Down, out of the way, while a dialog is open.** A toast column lives in the top right, and so
   does an editor's Properties/Layers pane - so every "Painted slot N" from a run of clicks landed
   on top of the very panel the clicks were being read against. When the overlay is up the toasts
   move to the bottom right, where the dialog leaves room. */
body:has(#modalroot .overlay) #toasts{top:auto;bottom:20px}.toast{background:#1b2531;border:1px solid var(--border2);border-left:3px solid var(--accent2);padding:11px 15px;border-radius:9px;min-width:240px;max-width:380px;box-shadow:var(--shadow);font-size:13px}.toast.err{border-left-color:var(--red)}#modalroot .overlay{position:fixed;inset:0;background:rgba(2,5,8,.78);display:flex;align-items:center;justify-content:center;z-index:200;padding:20px;backdrop-filter:blur(5px)}/* Width scale, one step per job. Every number here was raised once, together, because a modal that
   is one character narrower than its neighbour is a dialog that looks broken beside it: a plain
   form, a form with a list rail (`wide`), a table editor (`xwide`), and the editors that need the
   real window (`uis`, `picker`). */
.modal{background:linear-gradient(145deg,#15253b,#0d1727);border:1px solid var(--border2);border-radius:22px;padding:26px;width:700px;max-width:96vw;max-height:92vh;overflow:auto;box-shadow:var(--shadow)}.modal.wide{width:960px}
.modal.xwide{width:1240px}
/* The picker is the one dialog whose content is unbounded: step 1 is a catalogue and step 2 is a form
   with the starter-pack list inside it, and together they run well past the dialog's own 92vh cap.
   The dialog is what scrolls, so Cancel/Back/Create used to sit at the very bottom of the content -
   off screen until the whole thing had been scrolled through, which is how a dialog ends up with no
   visible way out. Pinning the footer keeps those three buttons on screen at every scroll position.
   The backdrop is translucent plus a blur rather than a fixed colour, so the band picks up whichever
   colour theme is on instead of needing one background per theme; the top bar uses the same trick. */
.modal.picker .modalfoot{position:sticky;bottom:0;z-index:4;margin-top:16px;padding-top:12px;
  border-top:1px solid var(--border);background:rgba(8,6,14,.8);backdrop-filter:blur(14px)}
/* The UI editor is a **workspace**, not a dialog: it opens at nearly the size of the window, the page
   rail stays put on the left, and the canvas gets the rest of the width instead of whatever is left
   after a two-column form. Everything inside keeps its own scroll, so the canvas never scrolls out
   from under the thing being clicked. */
.modal.uis{width:min(1720px,97vw);height:min(93vh,1000px);max-height:93vh;display:flex;flex-direction:column;overflow:hidden}
.modal.uis .uisws{display:grid;grid-template-columns:214px minmax(0,1fr);gap:16px;flex:1 1 auto;min-height:0;margin-top:10px}
.modal.uis .uisrail{display:flex;flex-direction:column;gap:8px;min-height:0}
.modal.uis .uisrailhead{font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);padding-left:2px}
.modal.uis .npclist{flex:1 1 auto;height:auto;max-height:none}
.modal.uis .npcpane{height:100%;overflow:auto;padding-right:5px}
.modal.uis .ubnewrow{padding:8px;border-top:1px solid var(--border)}
.modal.uis .uishelp{margin:0;font-size:11.5px}
.modal.uis .uishelp[open]{max-height:34vh;overflow:auto}
@media(max-width:900px){.modal.uis{height:auto}.modal.uis .uisws{grid-template-columns:1fr}.modal.uis .npcpane{height:auto;overflow:visible}}
/* Inside a pane: tools and canvas on the left, the things that describe what is selected on the
   right. The canvas column is `minmax(0,1fr)` so it can shrink without pushing the right rail away. */
.uiscols{display:grid;grid-template-columns:minmax(0,1fr) 330px;gap:14px;align-items:start}
.uismain{min-width:0}
.uisside{min-width:0;display:flex;flex-direction:column;gap:10px}
.uisside .cfgbox{margin-bottom:0}
.uisside .uifields{grid-template-columns:1fr}
.uisside .upal{flex-direction:column;gap:6px}
.uisside .upalchips{max-height:none}
.uisside .cfgfield{margin-bottom:9px}
.uisside .note{font-size:11.5px}
@media(max-width:1180px){.uiscols{grid-template-columns:minmax(0,1fr)}}.modal h3{margin:0 0 5px;font-size:19px;letter-spacing:-.035em}.modal .sub{color:var(--dim);font-size:12px;margin-bottom:13px}.modalfoot{display:flex;gap:8px;justify-content:flex-end;margin-top:21px}
/* game picker */
.chiprow{display:flex;gap:6px;flex-wrap:wrap;margin:9px 0}.chip{background:transparent;border:1px solid var(--border);color:var(--dim);border-radius:20px;padding:4px 12px;font-size:11px}.chip:hover{color:var(--text);border-color:var(--accent2)}.chip.on{color:var(--accent);border-color:var(--accent2);background:var(--accent-soft)}.gamegrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(205px,1fr));gap:9px;max-height:270px;overflow:auto;margin:10px 0 5px}.gamecard{background:rgba(11,16,23,.72);border:1px solid var(--border);border-radius:9px;padding:12px;cursor:pointer;transition:transform .12s,border-color .12s,background .12s}.gamecard:hover{border-color:var(--accent2);transform:translateY(-1px)}.gamecard.sel{border-color:var(--accent2);background:var(--accent-soft)}.gamecard b{display:block;font-size:13px;line-height:1.25;margin-bottom:4px}.gamecard .gid{color:var(--faint);font-size:10px;margin-bottom:8px}
/* console/files */
.consbox{background:#070a0e;border:1px solid #020305;border-radius:9px;font-family:var(--mono);font-size:12px;height:52vh;overflow-y:auto;padding:12px;white-space:pre-wrap;word-break:break-all;line-height:1.55;box-shadow:inset 0 1px 12px #0007}.consbox .in{color:#ffc;background:rgba(255,255,255,.03)}.consline{color:#c7d0dc}.consline.err{color:#f07982}.consrow{display:flex;gap:8px;margin-top:10px}.consrow input{flex:1;font-family:var(--mono)}.wsdot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px;background:var(--faint)}.wsdot.live{background:var(--accent);box-shadow:0 0 7px var(--accent)}.wsdot.dead{background:var(--red)}.statbar{display:flex;gap:16px;font-size:11px;color:var(--dim);flex-wrap:wrap;margin:4px 0 14px}.statbar b{color:var(--text)}canvas.chart{width:100%;height:110px;display:block}.chartwrap{margin-bottom:15px}.chartwrap .lbl{font-size:11px;color:var(--dim);font-weight:700;text-transform:uppercase;letter-spacing:.08em;margin-bottom:7px}
.fsbar{display:flex;gap:8px;align-items:center;margin-bottom:13px;flex-wrap:wrap}.bc{display:flex;gap:2px;align-items:center;font-size:12px;flex-wrap:wrap}.bc a{padding:3px 5px;border-radius:4px;color:var(--dim)}.bc a:hover{color:var(--accent);text-decoration:none;background:var(--accent-soft)}.bc .sep{color:var(--faint)}.filetd{display:flex;align-items:center;gap:9px}.fname{cursor:pointer}.fname.d{font-weight:700}.ic{font-size:14px;opacity:.8}textarea.editor{width:100%;height:56vh;font-family:var(--mono);font-size:12.5px;white-space:pre;resize:none}.tabs{display:flex;gap:5px;border:1px solid var(--border);border-radius:14px;padding:5px;background:rgba(5,13,24,.65);margin-bottom:20px;align-items:center;overflow-x:auto;scrollbar-width:thin}.tabs button{background:transparent;border:1px solid transparent;border-radius:9px;color:var(--dim);padding:10px 15px;font-weight:650;white-space:nowrap;transition:background .15s,color .15s}.tabs button:hover{color:var(--text);background:var(--panel2)}.tabs button.on{color:var(--accent);border-color:rgba(167,139,250,.25);background:linear-gradient(120deg,var(--accent-soft),var(--violet-soft));box-shadow:0 2px 8px #0002}.mini{display:inline-block;width:34px;text-align:right;margin-right:6px}.chkrow{display:flex;gap:8px;align-items:center;margin-top:11px}.chkrow input{width:auto}.chkrow label{margin:0}.days{display:flex;gap:4px;margin-top:5px;flex-wrap:wrap}.days button{background:var(--panel);border:1px solid var(--border);border-radius:5px;padding:4px 9px;color:var(--dim);font-size:11px}.days button.on{background:var(--accent-soft);border-color:var(--accent2);color:var(--accent)}.mono{font-family:var(--mono);font-size:12px}.hint{font-size:11px;color:var(--faint);margin-top:5px}kbd{background:var(--panel2);border:1px solid var(--border);border-radius:4px;padding:0 5px;font-size:11px}/* Wrap rather than squeeze. Three controls in a half-width card left 78px per field, which cut both
   placeholders ("Label, e.g. Save the world") off at the first word, so the row said what it wanted
   only if you had already used it. A basis wide enough for the placeholder lets the row break into
   two lines when the card is narrow instead of shrinking its fields into ellipses. */
.inputrow{display:flex;gap:8px;flex-wrap:wrap}.inputrow .field{flex:1 1 150px}
/* alerts/helper */
.alertbanner{display:flex;gap:11px;align-items:flex-start;padding:13px 15px;border-radius:10px;border:1px solid;margin-bottom:15px;font-size:13px}.alertbanner .abic{font-size:16px;line-height:1.3}.alertbanner b{display:block;margin-bottom:3px}.alertbanner .abmsg{flex:1;min-width:0}.alertbanner .abmsg div{color:var(--dim);font-size:12px;margin-top:2px;word-break:break-word}.alertbanner.error{background:var(--red-soft);border-color:#63343b;color:#ffabb1}.alertbanner.warn{background:rgba(241,198,117,.1);border-color:#65542d;color:#f4d595}.alertbanner.info{background:rgba(116,183,255,.08);border-color:#2d4a69;color:#acd1f8}.alertbanner.ok{background:rgba(156,232,11,.08);border-color:#3d5a1c;color:#d4ff9d}
.renewbox{margin-top:14px;padding:13px;border:1px solid var(--border);border-radius:9px;background:rgba(255,255,255,.025)}.renewbox>b,.renewbox>div>b{font-size:12px}.switchrow{display:flex;align-items:center;gap:8px;margin-top:12px}.switchrow input{width:auto}.renewbox label:not(.switchrow){margin-top:10px}
.sitetable td a.url{font-family:var(--mono);font-size:12px}.pill.preview{background:rgba(116,183,255,.15);color:var(--blue)}.hidden{display:none!important}
/* catalog picker (AMP-style) */
.ppsearchrow{display:flex;gap:10px;align-items:center;margin:14px 0 12px}.ppsearchrow input{flex:1}.ppload{color:var(--faint);font-size:11px;white-space:nowrap}
.ppbody{display:flex;gap:14px;min-height:300px}
.ppcats{width:148px;flex:0 0 auto;display:flex;flex-direction:column;gap:3px;border-right:1px solid var(--border);padding-right:12px}
.ppcat{background:transparent;border:0;color:var(--dim);text-align:left;padding:9px 11px;border-radius:8px;font-weight:650;font-size:13px;cursor:pointer}
.ppcat:hover{color:var(--text);background:rgba(255,255,255,.045)}
.ppcat.on{color:var(--accent);background:var(--accent-soft);box-shadow:inset 2px 0 0 var(--accent2)}
.ppgrid{flex:1;display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:9px;max-height:340px;overflow:auto;align-content:start}
.pp-tile{background:rgba(11,16,23,.72);border:1px solid var(--border);border-radius:10px;padding:11px;cursor:pointer;text-align:center;transition:transform .12s,border-color .12s,background .12s;min-width:0}
.pp-tile:hover{border-color:var(--accent2);transform:translateY(-2px);background:rgba(17,23,32,.9)}
.pp-tile b{display:block;font-size:12px;line-height:1.25;margin-top:8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pp-tile small{display:block;color:var(--faint);font-size:10px;margin-top:3px;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pp-tile-ic{width:52px;height:52px;border-radius:11px;margin:0 auto;display:flex;align-items:center;justify-content:center;overflow:hidden;background:var(--icbg,#2b3445);box-shadow:0 4px 12px rgba(0,0,0,.3);font-size:24px}
.pp-tile-ic img{width:100%;height:100%;object-fit:cover}
.pp-tile.steam .pp-tile-ic{width:100%;height:58px;border-radius:8px}
.pp-tile.steam .pp-tile-ic img{object-fit:cover;object-position:center}
.ppsel{display:flex;gap:12px;align-items:center;background:rgba(255,255,255,.03);border:1px solid var(--border);border-radius:10px;padding:12px;margin-bottom:6px}
.ppsel .pp-tile-ic{margin:0;width:56px;height:56px;flex:0 0 auto}
.ppsel .pp-tile-ic.steamwide{width:132px;height:56px}
.ppsel b{display:block;font-size:14px}
.ppsel small{color:var(--faint);font-size:11px;display:block;margin-top:2px}
.ppconfig .modalfoot{margin-top:20px}
.netblk{background:rgba(255,255,255,.03);border:1px solid var(--border);border-radius:10px;padding:12px;margin-top:12px}
.netblkhd{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:10px;font-weight:700;font-size:12.5px}
.netchk{display:flex;align-items:center;gap:6px;font-weight:600;color:var(--dim);font-size:12px;margin:0;white-space:nowrap}
.netchk input{width:auto}
.netgame{display:flex;gap:8px;align-items:center;margin-bottom:7px}
.netgame .gname{flex:2}
.netgame .gtype{flex:1.1}
.netgame .gram{flex:.8}
/* The core-template picker on each game row: wider than the port/memory field, because
   "PlayPort Core: Creative / building" has to be readable at a glance. */
.netgame .gcore{flex:1.5}
.netgame .gdel{flex:0 0 auto}
.surls{display:flex;gap:8px;flex-wrap:wrap;margin-top:2px}
.surls .url{color:var(--blue);font-family:var(--mono);font-size:12px}
.inl{font-family:var(--mono);font-size:11.5px;background:var(--panel2);border:1px solid var(--border);border-radius:5px;padding:1px 5px;color:var(--text)}
@media(max-width:850px){#shell{grid-template-columns:76px 1fr}#side{padding:18px 9px}.brand{padding:4px 10px 25px;justify-content:center}.brandcopy,.brandver,.navlabel,.sidecallout,#sideuser span,#sideuser b{display:none}#nav a{justify-content:center;padding:12px}#nav a::before{font-size:19px}#sideuser{text-align:center;padding:12px 0}#sideuser button{font-size:0;padding:8px;width:34px}#sideuser button:after{content:'↪';font-size:15px}#topbar{padding:0 20px}#view{padding:24px 20px 96px}.page h1{font-size:23px}.formrow{flex-direction:column;gap:0}.actions{white-space:normal}.actions .btn{margin-bottom:4px}}


/* Floating workspace shell and clear navigation across every panel page. */
@media(min-width:1100px){
  #shell{padding:16px;gap:16px;grid-template-columns:240px minmax(0,1fr)}
  #side{border:1px solid var(--border);border-radius:22px;box-shadow:0 16px 48px #0003}
  main{border:1px solid var(--border);border-radius:22px;overflow:hidden;background:rgba(6,14,26,.58)}
  #topbar{height:76px;padding-inline:30px}
  #view{padding:30px 30px 104px}
  .brand{padding-bottom:8px}
}
#nav a{min-height:32px}
#side{background:radial-gradient(ellipse at 0 0,rgba(167,139,250,.08),transparent 55%),linear-gradient(180deg,#101c2d,#0a1220)}
#topbar{box-shadow:0 1px 0 rgba(255,255,255,.025),0 8px 28px #0002}
#nav a:hover{border-color:var(--border)}
.cards .card{position:relative;overflow:hidden}
.cards .card::before{content:"";position:absolute;inset:18px auto 18px 0;width:3px;background:linear-gradient(var(--accent2),var(--violet));border-radius:0 3px 3px 0}
.cards .card .v{font-variant-numeric:tabular-nums}
.panel>table{border:1px solid var(--border);border-radius:12px}
.panel td{border-bottom-color:var(--border)}
.panel tr:hover td{background:rgba(167,139,250,.035)}
.empty{border:1px dashed var(--border2);border-radius:14px;background:rgba(5,13,24,.25);padding:42px 18px}
.modal{box-shadow:0 32px 100px #0009,inset 0 1px 0 rgba(167,139,250,.12)}
#nav a.on::after{content:"";width:6px;height:6px;margin-left:auto;flex-shrink:0;border-radius:50%;background:var(--accent);box-shadow:0 0 12px var(--accent2)}
.page h1{color:#f2f7ff;text-wrap:balance}
.page .pagedesc{font-size:14px}
.panel{position:relative}
.panel h3{display:flex;align-items:center;gap:9px}
.panel h3::before{content:"";width:3px;height:16px;border-radius:3px;background:linear-gradient(var(--accent),var(--violet));flex-shrink:0}
.tabs button{display:inline-flex;align-items:center;justify-content:center;gap:7px}
.tabs button:focus-visible{outline-offset:-3px}
.ppcat:focus-visible,.chip:focus-visible{outline-offset:-3px}
.modalfoot{flex-wrap:wrap}
.consrow .btn{flex-shrink:0}
@media(max-width:600px){
  #topbar{height:auto;min-height:64px;padding:12px;flex-wrap:wrap}
  #view{padding:20px 12px 100px}
  .panel,.card{padding:16px;border-radius:14px}
  .grid.cols{grid-template-columns:minmax(0,1fr)}
  .cards{grid-template-columns:repeat(2,minmax(0,1fr))}
  #modalroot .overlay{padding:10px}
  .modal{padding:18px;max-width:100%;max-height:92dvh;border-radius:16px}
  .ppbody{flex-direction:column;min-height:0}
  .ppcats{width:100%;flex-direction:row;overflow-x:auto;flex-shrink:0}
  .ppcat{white-space:nowrap;flex-shrink:0}
  .ppgrid{grid-template-columns:repeat(auto-fill,minmax(120px,1fr))}
  .netgame{flex-wrap:wrap}
  .netgame .gname{flex-basis:100%}
  .ppsearchrow{flex-wrap:wrap}
  .ppsearchrow input{min-width:0}
  .modalfoot .btn{flex:1;min-width:100px}
  #toasts{left:12px;right:12px}
  .toast{min-width:0;max-width:100%}
}

/* Command-center finish: nested tools use the same surfaces as the dashboard. */
#side{background:radial-gradient(ellipse at 0 0,rgba(167,139,250,.09),transparent 55%),linear-gradient(180deg,#101b2c,#080f1b)}
#topbar{box-shadow:0 8px 32px #0002;border-bottom-color:rgba(128,210,255,.18)}
.page>.pagedesc{max-width:78ch;line-height:1.75}
.cards .card{position:relative;overflow:hidden}
.cards .card::after{content:"";position:absolute;inset:auto 22px 0;height:2px;background:linear-gradient(90deg,var(--accent2),var(--violet),transparent);opacity:.6;pointer-events:none}
.card .v{font-variant-numeric:tabular-nums}
.tabs{box-shadow:inset 0 1px 0 #ffffff05,0 4px 18px #0002}
.tabs button.on{box-shadow:inset 0 -2px 0 var(--accent2),0 2px 10px #0003}
:where(.panel,.modal) table{border:1px solid var(--border);border-radius:12px}
th{padding:13px 14px;background:rgba(4,12,24,.65)}
td{padding:14px;border-bottom-color:rgba(128,170,210,.12)}
tr:hover td{background:rgba(167,139,250,.035)}
:where(.modal,.panel) summary{transition:color .15s}
:where(.modal,.panel) summary:hover{color:var(--accent)}
.btn.sm{min-height:32px;padding:6px 11px}
.empty{border:1px dashed var(--border);border-radius:14px;background:rgba(5,13,24,.25);padding:44px 16px}
.modal{box-shadow:0 32px 100px #0009,0 0 0 1px #67e8f90a}
:where(button,a,input,select,textarea,summary){scroll-margin-block:16px}

.panel,.modal,.card{border-top-color:var(--surface-edge)}
.page h1{font-weight:800}
.panel h3{font-size:15px}
.btn:not(.ghost):not(.danger){background:linear-gradient(120deg,#bef264,#84cc16);box-shadow:0 4px 16px rgba(167,139,250,.12)}
.btn.ghost{background:rgba(17,32,51,.65)}
/* Consistent interactive surfaces throughout the panel and nested tools. */
:where(button,a,input,select,textarea,summary):focus-visible{outline:2px solid var(--accent);outline-offset:3px}
::selection{background:rgba(167,139,250,.28);color:#f0fdff}
:where(.panel,.modal) :where(input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]),select,textarea){background:#091525;border:1px solid var(--border2);border-radius:10px;color:var(--text);padding:10px 12px;transition:border-color .15s,box-shadow .15s}
:where(.panel,.modal) :where(input,select,textarea):focus{border-color:var(--accent2);box-shadow:0 0 0 3px rgba(167,139,250,.1)}
:where(input,textarea)::placeholder{color:var(--dim);opacity:.8}
:where(input[type=checkbox],input[type=radio]){accent-color:var(--accent2)}
:where(.panel,.modal) details{border:1px solid var(--border);border-radius:12px;background:rgba(5,13,24,.3);padding:12px 14px;margin-block:10px}
:where(.panel,.modal) summary{cursor:pointer;color:var(--text);font-weight:650;line-height:1.6}
:where(.panel,.modal) details[open]{border-color:var(--border2)}
:where(.panel,.modal) details[open]>summary{margin-bottom:12px;color:var(--accent)}
/* The why-pop is a chip, not a slab. A `details` inside a panel is a bordered block because a real
   disclosure holds a form - but the "why" affordance inherited that and drew four words in a 519px
   box, one of them per slot, which is what "way too wide" looks like. `fit-content` also stops it
   stretching when the container is a flex column, where a stretched cross-axis would win. */
:where(.panel,.modal) details.whypop{width:fit-content;max-width:100%;padding:0;border:0;background:none;margin:6px 0 0}
:where(.panel,.modal) details.whypop[open]{border:0}
:where(.panel,.modal) details.whypop>summary{margin-bottom:0}
.modalfoot{border-top:1px solid var(--border);padding-top:18px}
.chip.on,.days button.on{box-shadow:inset 0 1px 0 #ffffff12}
@media(prefers-reduced-motion:reduce){html:not([data-fx="future"]) *,html:not([data-fx="future"]) *::before,html:not([data-fx="future"]) *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}}
.btn:disabled{filter:none;transform:none;box-shadow:none}
.tabs{scroll-padding-inline:8px}
.tabs button{min-height:40px}
.chip,.days button{min-height:32px}
.ppcats{background:rgba(5,13,24,.35);border:1px solid var(--border);border-radius:14px;padding:8px;gap:5px}
.ppcat{min-height:40px;border:1px solid transparent;border-radius:10px}
.ppcat.on{border-color:rgba(167,139,250,.24);background:linear-gradient(110deg,var(--accent-soft),var(--violet-soft))}
.pp-tile,.gamecard{background:var(--surface);border-radius:14px;padding:15px}
.pp-tile:hover,.gamecard:hover{box-shadow:0 8px 22px #0003}
.ppsel,.netblk,.renewbox,.dbcred{background:var(--surface);border-radius:14px}
.consbox{background:linear-gradient(145deg,#07111e,#080f1b);border-color:var(--border);border-radius:14px;padding:16px;line-height:1.7}
textarea.editor{background:#08121f;border-radius:12px;line-height:1.65;padding:16px;tab-size:2}
.statbar{padding:10px 12px;border:1px solid var(--border);border-radius:10px;background:rgba(5,13,24,.4)}
.toast{background:var(--surface);border-radius:12px}
.authcard{background:var(--surface);border-top-color:var(--accent);box-shadow:0 24px 80px #0006,0 0 60px rgba(167,139,250,.05)}
.sidecallout{background:linear-gradient(120deg,var(--violet-soft),var(--accent-soft));border-color:var(--surface-edge)}
.alertbanner.ok{background:rgba(74,222,128,.08);border-color:rgba(74,222,128,.3);color:#a7f3c0}
@media(max-width:600px){
  #shell{grid-template-columns:minmax(0,1fr);grid-template-rows:auto minmax(0,1fr)}
  #side{padding:8px 10px;border-right:0;border-bottom:1px solid var(--border);overflow:visible;flex-direction:row;align-items:center;gap:8px}
  #side .brand,#sideuser{display:none}
  #nav{flex-direction:row;overflow-x:auto;gap:4px;scrollbar-width:thin;padding-bottom:3px;min-width:0}
  #nav a{flex:0 0 auto;padding:9px 12px;font-size:12px;gap:6px;border-radius:9px}
  #nav a::before{font-size:15px;width:18px}
  #topbar{height:auto;min-height:60px;padding:12px 16px;flex-wrap:wrap}
  #topbar .crumb{font-size:14px}
  #view{padding:20px 14px 104px}
  .panel,.card{padding:16px;border-radius:14px}
  .page h1{font-size:26px}
  .grid.cols{grid-template-columns:minmax(0,1fr)}
  .cards{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}
  .card .v{font-size:26px}
  .tabs button{padding:9px 12px}
  .modal{padding:18px;max-width:100%;border-radius:16px}
  #modalroot .overlay{padding:10px}
  .modalfoot{flex-wrap:wrap}
  .ppbody{flex-direction:column}
  .ppcats{width:100%;flex-direction:row;overflow-x:auto}
  .ppcat{flex:0 0 auto;white-space:nowrap}
  .ppgrid{grid-template-columns:repeat(auto-fill,minmax(125px,1fr))}
  .netgame{flex-wrap:wrap}
  .netgame input,.netgame select{flex:1 1 40%!important;min-width:0}
  #toasts{left:12px;right:12px}
  .toast{min-width:0;max-width:100%;overflow-wrap:anywhere}
}

/* database credential cards (site/instance detail) */
.dbcred{border:1px solid var(--border);border-radius:14px;padding:14px 16px;margin:12px 0;background:var(--surface)}

/* ================= activity / task list ================= */
.tlist{display:flex;flex-direction:column;gap:8px;margin-top:14px}
.trow{display:flex;gap:12px;align-items:flex-start;border:1px solid var(--border);border-radius:14px;padding:15px 17px;background:var(--surface);box-shadow:inset 0 1px 0 #ffffff05}
.trow.running{border-left:3px solid var(--accent)}
.trow.queued{border-left:3px solid var(--accent)}
.trow.done{border-left:3px solid var(--ok,#3fb96f)}
.trow.failed{border-left:3px solid var(--err,#e5534b);background:color-mix(in srgb,var(--err,#e5534b) 6%,var(--panel))}
.tic{font-size:16px;line-height:1.2;opacity:.9;min-width:20px;text-align:center}
.tbody{flex:1;min-width:0}
.ttop{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.ttop b{font-size:13.5px}
.ttime{font-size:11px}
.tdet{font-size:12px;color:var(--dim);margin-top:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tprog{height:4px;border-radius:3px;background:var(--panel2);margin-top:8px;overflow:hidden}
.tprog i{display:block;height:100%;background:var(--accent);border-radius:3px;transition:width .3s}
.tprog i.indet{width:40%;animation:indet 1.2s ease-in-out infinite alternate}
@keyframes indet{from{margin-left:0}to{margin-left:60%}}
.tactions{display:flex;gap:6px;flex-shrink:0;align-items:center}
.navbadge{display:inline-block;min-width:17px;padding:1px 5px;border-radius:9px;background:var(--accent);color:#04212a;font-size:10.5px;font-weight:700;text-align:center;margin-left:6px}
.navbadge.bad{background:var(--err,#e5534b)}

/* ================= minecraft network cards ================= */
.netsect{margin:6px 0 22px}
.netsect h3{margin:14px 0 8px;font-size:14px}
.netcard{border:1px solid var(--border);border-radius:16px;padding:18px;margin-bottom:12px;background:var(--surface);box-shadow:0 6px 20px #0002}
.nethd{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;flex-wrap:wrap}
.netactions{display:flex;gap:6px;flex-shrink:0;align-items:center}
.netmems{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
.netmem{display:inline-flex;gap:6px;align-items:center;border:1px solid var(--border);border-radius:7px;padding:3px 9px;font-size:11.5px;background:var(--panel2)}
.netmem em{font-style:normal;color:var(--faint);font-size:10.5px}
.netmem .dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--faint)}
.netmem .dot.ok{background:var(--green,#3fb96f);box-shadow:0 0 5px var(--green,#3fb96f)}
.netmem .dot.warn{background:#e8a33d;box-shadow:0 0 5px #e8a33d}
.netmem .dot.bad{background:var(--red,#e5534b);box-shadow:0 0 5px var(--red,#e5534b)}
.netmem .dot.idle{background:var(--faint)}
.netmem .pls{color:var(--accent);font-weight:700;font-size:11px}
.netplayers{display:inline-flex;gap:5px;align-items:center;font-size:12px;color:var(--dim)}
.netplayers b{color:var(--text);font-size:13px}

/* ================= plain-English diagnosis panel ================= */
.diagbox{border:1px solid var(--border);border-left:3px solid var(--accent);border-radius:14px;padding:16px 18px;margin:0 0 16px;background:var(--surface)}
.dghead{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin-bottom:6px}
.dghead b{font-size:13.5px}
.dghead .spacer{flex:1}
.dgitem{border-top:1px solid var(--border);padding:10px 0 4px;margin-top:6px}
.dgitem:first-of-type{border-top:0}
.dgtop{display:flex;gap:8px;align-items:center}
.dgtop b{font-size:13px}
.dgsev{font-size:9.5px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;padding:2px 6px;border-radius:5px;background:var(--panel2);color:var(--dim)}
.dgsev.fatal{background:var(--red,#e5534b);color:#fff}
.dgsev.error{background:rgba(229,83,75,.18);color:#f07982}
.dgsev.warn{background:rgba(232,163,61,.18);color:#e8a33d}
.dgwhat{font-size:12.5px;color:var(--dim);margin:5px 0 7px;line-height:1.5}
.dgsteps{margin:0 0 7px 20px;padding:0;font-size:12.5px;line-height:1.65}
.dgsteps li{margin-bottom:2px}
.dgsnip{font-size:11px;color:var(--faint);background:var(--panel2);border-radius:6px;padding:5px 8px}
.dgsnip code{font-family:var(--mono);font-size:10.5px;word-break:break-all}
/* an answer the model wrote in markdown, and the lines it was actually given */
.mdh{font-weight:700;font-size:12.5px;color:var(--text);margin:9px 0 3px}
.dgwhat p{margin:0 0 7px}
.dgwhat ul,.dgwhat ol{margin:0 0 7px 20px;padding:0}
.dgwhat li{margin-bottom:2px}
.dgwhat code{font-family:var(--mono);font-size:11px;background:var(--panel2);border-radius:4px;padding:1px 4px;word-break:break-all}
.dgfeed{margin-top:8px}
.dgfeed summary{font-size:11.5px;color:var(--dim);cursor:pointer;margin-top:4px}
.dgpre{font-family:var(--mono);font-size:10.5px;line-height:1.5;color:var(--faint);background:var(--panel2);border-radius:6px;padding:7px 9px;margin:5px 0 0;max-height:220px;overflow:auto;white-space:pre-wrap}
.dgworld{border-top:1px solid var(--border);padding:10px 0 4px;margin-top:6px}
.dgactions{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:4px}
.dgactions .note{flex:1;min-width:0}

/* detach control on network member chips */
.netmem .netx{background:transparent;border:0;color:var(--faint);font-size:13px;line-height:1;padding:0 0 0 3px;cursor:pointer}
.netmem .netx:hover{color:var(--red,#e5534b)}

/* ================= plugins tab ================= */
.pghd{display:flex;gap:10px;align-items:flex-start;margin:4px 0 12px}
.pghd .spacer{flex:1}
.pgsearch{display:flex;gap:8px;margin-bottom:14px}
.pgsearch input{flex:1}
.pglist{display:flex;flex-direction:column;gap:8px}
/* A row wraps. The core card's toolbar is eighteen module buttons wide - wider than a
   laptop - and with `flex-shrink:0` on it the description beside it was squeezed to its
   minimum content width, one word per line. Now the text has a floor and the toolbar
   drops to its own line when it does not fit, right-aligned under it. */
.pgitem{display:flex;gap:11px;align-items:flex-start;flex-wrap:wrap;border:1px solid var(--border);border-radius:9px;padding:10px 12px;background:var(--panel)}
.pgico{width:36px;height:36px;border-radius:7px;object-fit:cover;background:var(--panel2);flex-shrink:0}
.pgico.ph{display:flex;align-items:center;justify-content:center;font-size:16px}
.pgbody{flex:1 1 360px;min-width:0}
.pgname{font-size:13.5px;font-weight:650;display:flex;gap:7px;align-items:center;flex-wrap:wrap}
.pgname .note{font-weight:400}
.pgdesc{font-size:12px;color:var(--dim);margin-top:2px;line-height:1.45}
.pgmeta{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:6px}
.tag{font-size:10px;text-transform:uppercase;letter-spacing:.05em;padding:2px 6px;border-radius:5px;background:var(--panel2);color:var(--dim)}
.tag.ok{background:rgba(156,232,11,.15);color:#9ce80b}
.tag.warn{background:rgba(224,168,71,.18);color:#e0a847}
.pgactions{display:flex;gap:6px;align-items:center;flex-wrap:wrap;justify-content:flex-end;flex:0 1 auto;min-width:0;margin-left:auto}
/* The template-profile picker on the core plugin card: narrower than the global
   select so the row stays one line on a laptop, and full width on a phone where
   the whole card stacks anyway. */
#plcoreprofile{min-width:170px;padding:6px 9px;font-size:12px}
.pgsep{height:1px;background:var(--border);margin:20px 0 14px}
.pgsearch{flex-wrap:wrap}
.pgchk{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--dim);white-space:nowrap;cursor:pointer}
.pgchk input{margin:0}
/* Starter packs: a working server in one click, in the picker and on the tab. */
.packpick{margin:14px 0 4px;border:1px solid var(--border);border-radius:10px;padding:12px 14px;background:var(--panel2)}
/* The template-profile choice inside the create form: a quiet row under the pack
   list, not a second decision competing with it. */
.corerow{border-top:1px solid var(--border);margin-top:10px;padding-top:10px}
.corerow-in{display:flex;gap:12px;align-items:center;justify-content:space-between;flex-wrap:wrap}
.corerow-in>span{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
.corerow-in small{color:var(--dim);font-size:11.5px;line-height:1.45}
.corerow-in select{min-width:210px;width:auto}
/* the worlds a server is made of, asked for before it exists */
.worldbox{border-top:1px solid var(--border);margin-top:10px;padding-top:10px}
.worldhd{display:flex;flex-direction:column;gap:2px;margin-bottom:7px}
.worldhd b{font-size:12.5px}
.worldhd .note{font-size:11.5px;color:var(--dim);line-height:1.5}
.worldrow{display:flex;gap:7px;align-items:center;margin-bottom:6px;flex-wrap:wrap}
.worldrow .field{flex:1 1 120px;min-width:0}
.worldrow .wname{flex:2 1 160px}
.worldrow .wpreset{flex:1 1 96px;min-width:0}
.worldrow .wseed{flex:1 1 120px;min-width:0}
.worldrow .wdel{flex:0 0 auto;padding:4px 9px}
.packhd{margin-bottom:8px}
.packhd b{display:block;margin-bottom:2px}
.packrow{display:flex;gap:9px;align-items:flex-start;padding:7px 4px;border-radius:8px;cursor:pointer}
.packrow:hover{background:rgba(255,255,255,.03)}
.packrow input{margin-top:3px}
.packrow span{display:flex;flex-direction:column;gap:2px}
.packrow small{color:var(--dim);font-size:12px;line-height:1.5}
.pgpacks .pgitem{align-items:flex-start}

/* =====================================================================
   Mobile and tablet rework.
   The old rules turned the sidebar into a top block with icon-only links
   and left the page tabs as a second, separate scroller, which meant two
   stacked scrollers eating vertical space and tiny tap targets. These
   rules make one sticky nav bar, one sticky tab row under it, and stack
   every action row so nothing is squeezed on a phone.
   ===================================================================== */

/* comfortable tap targets everywhere on touch screens */
@media(max-width:850px){
  .btn{min-height:38px;padding:9px 14px}
  .btn.sm{min-height:34px;padding:7px 11px}
  .tabs{scrollbar-width:none;-ms-overflow-style:none}
  .tabs::-webkit-scrollbar{display:none}
  .tabs button{min-height:44px;padding:12px 15px;font-size:13px}
  /* Action rows would otherwise crush the text beside them. They must also be
     allowed to shrink: flex-shrink:0 plus 3 buttons meant the row kept its
     max-content width and pushed the whole page wider than the screen. */
  .pgitem,.trow,.netcard{flex-direction:column;align-items:stretch}
  .pgactions,.tactions,.netactions{
    flex-wrap:wrap;margin-top:9px;flex-shrink:1;min-width:0;width:100%;justify-content:flex-start;
  }
  /* A button is as wide as its own label, and the row wraps instead. These used to be shrinkable
     (`flex:0 1 auto;min-width:0`), which let a long row squeeze every button below the width of
     its text - the card stayed narrow and the labels became illegible, which is a worse trade
     than a taller card. The row itself is what must not push the page wide, and it is
     `flex-shrink:1;width:100%` above. */
  .pgactions>.btn,.tactions>.btn,.netactions>.btn{flex:0 0 auto;min-width:0;white-space:nowrap}
  .netplayers{margin-top:4px}
  .pgsearch{flex-wrap:wrap}
}

/* phone: one sticky app bar, one sticky tab row, stacked forms */
@media(max-width:560px){
  /* minmax(0,1fr) plus explicit min-width:0 is essential: a grid/flex item
     defaults to min-width:auto, so the horizontally scrolling nav row would
     otherwise stretch the whole page wider than the phone screen. */
  #shell{grid-template-columns:minmax(0,1fr)}
  #side{
    position:sticky;top:0;z-index:30;height:auto;display:block;min-width:0;
    padding:8px 10px 0;border-right:0;border-bottom:1px solid var(--border);
    background:rgba(13,17,24,.96);backdrop-filter:blur(14px);
  }
  #nav,main,#view,.page{min-width:0}
  .brand{padding:6px 2px 10px;justify-content:flex-start}
  .brandcopy b{font-size:17px}
  .brandcopy small{font-size:7px;margin-top:5px}
  .brandver{display:block;margin-left:auto;align-self:center;font-size:9.5px;margin-top:0}
  .sidecallout,#sideuser{display:none}
  .navlabel{display:none}

  /* A horizontally scrolling pill row hid the last destinations: Activity,
     Users and Settings sat off the right edge behind a hidden scrollbar, so the
     bar looked like it simply stopped at Email. Wrap it instead, so every
     destination is visible at a glance, and drop the icon glyphs because the
     labels read better at this size and the icons cost a third of the width. */
  #nav{
    flex-direction:row;flex-wrap:wrap;gap:6px;padding:0 0 10px;overflow:visible;
  }
  #nav a{
    flex:1 1 auto;justify-content:center;padding:8px 11px;font-size:12.5px;gap:0;
    border:1px solid var(--border);border-radius:9px;background:var(--panel);
  }
  #nav a::before{display:none}
  #nav a.on{box-shadow:none;border-color:var(--accent2);background:var(--accent-soft)}
  .navbadge{margin-left:3px}
  #nav .navlabel-spaced{display:none}

  /* let the top bar wrap instead of hiding its action button: "+ New server"
     and "+ Host a website" are primary actions, not decoration. */
  #topbar{height:auto;min-height:48px;padding:8px 12px;flex-wrap:wrap;gap:8px}
  #topbar .crumb{font-size:14px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
  #topbar .crumb a.btn{margin-left:0;flex:0 0 auto}

  /* tabs stick directly under the app bar so the section is always visible */
  .tabs{
    position:sticky;top:0;z-index:20;margin:0 -14px 14px;padding:0 6px;
    background:rgba(9,12,17,.97);backdrop-filter:blur(10px);
  }
  .tabs button{min-height:46px;padding:12px 12px;font-size:12.5px}
  .tabs .spacer{display:none}
  .tabs .pill{position:sticky;right:0;margin-left:auto;align-self:center}

  #view{padding:16px 14px 92px}
  .page h1{font-size:20px;margin-bottom:6px}
  .page .pagedesc{font-size:12.5px}

  /* everything stacks: no side-by-side squeeze on a 360px screen */
  .formrow,.inputrow,.chkrow{flex-direction:column;align-items:stretch;gap:8px}
  .inputrow .btn,.inputrow a.btn{width:100%;justify-content:center}
  .statbar{display:grid;grid-template-columns:1fr 1fr;gap:6px 12px}
  .cards{grid-template-columns:1fr}
  .panel{padding:14px}
  .gamegrid{grid-template-columns:1fr}
  .netmems{gap:5px}
  .netmem{font-size:11px;padding:5px 9px;width:100%}
  .pgico{width:30px;height:30px}
  .pgname{font-size:12.5px}
  .pgactions{gap:6px}
  /* Chips, not columns. `flex:1` gave every action an equal slice of the row, which reads well
     for the three buttons this card started with and turns fourteen into a smear: at a 400px
     window each got ~24px, so "Enchants (15)" broke one letter per line and overlapped the
     button beside it - the row looked like three buttons and a pile of letters. */
  .pgactions .btn{flex:0 0 auto;justify-content:center;white-space:nowrap}
  .dghead .spacer{display:none}

  /* modals become sheets: usable in landscape and portrait */
  .overlay{align-items:flex-start;padding:8px}
  .modal{width:100%;max-width:100%;margin:8px 0;padding:16px;max-height:92vh;overflow:auto;border-radius:12px}
  .modalfoot{position:sticky;bottom:0;background:var(--panel);padding-top:12px}
  .consrow input{font-size:16px}   /* 16px stops iOS zooming the page in */
  .consbox{height:42vh}
  .days button{min-height:34px;padding:6px 11px}
}

/* ================= table containment ================= */
.tscroll{overflow-x:auto;-webkit-overflow-scrolling:touch;max-width:100%;
  /* a soft edge hints that there is more to the right */
  background:linear-gradient(to right,transparent,transparent) right/18px 100% no-repeat,
             linear-gradient(to right,var(--border),transparent) right/1px 100% no-repeat}
@media(max-width:560px){
  #view{overflow-x:hidden}
  .tscroll{border:1px solid var(--border);border-radius:9px;padding:0 2px}
  .tscroll table{min-width:620px;margin:0}
  table{min-width:0}
}
/* The player table is three columns, one of which is a row of buttons, so the
   620px floor above pushed every action off the side of a phone. Letting the
   buttons wrap inside their cell keeps the whole row reachable, and the wider
   breakpoint covers the tablet-ish widths where it was equally awkward. */
@media(max-width:760px){
  .tscroll table.ptable{min-width:0}
  .ptable .actions{white-space:normal}
  .ptable .actions button{margin:0 4px 6px 0}
  .ptable .puuid{max-width:170px}
}

/* ================= unrecognised crash review ================= */
.sechead{font-size:12px;text-transform:uppercase;letter-spacing:.09em;color:var(--dim);margin:26px 0 0}
.upanel{border:1px solid #57432a;border-left:3px solid var(--warn,#f1c675);border-radius:10px;
  background:color-mix(in srgb,var(--warn,#f1c675) 5%,var(--panel));padding:14px 16px;margin:4px 0 6px}
.uhead{display:flex;gap:11px;align-items:flex-start}
.uic{font-size:17px;line-height:1.25}
.ubody{flex:1;min-width:0}
.ubody b{display:block;font-size:14px;margin-bottom:3px}
.ubody .note{display:block;color:var(--dim);font-size:12px;line-height:1.5}
.uactions{display:flex;gap:6px;flex-shrink:1;flex-wrap:wrap;min-width:0;justify-content:flex-end}
.ulist2{display:flex;flex-direction:column;gap:8px;margin-top:12px}
.urow{border:1px solid var(--border);border-radius:9px;padding:10px 12px;background:var(--panel)}
.urow.ok{border-left:3px solid var(--ok,#9ce80b)}
.utop{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.utop b{font-size:13px}
.utime{white-space:nowrap}
.upill{background:rgba(241,198,117,.15);border:1px solid #65542d;color:#f4d595;border-radius:20px;
  padding:1px 8px;font-size:11px;font-weight:700;flex-shrink:0}
.upill.hot{background:rgba(229,83,75,.16);border-color:#63343b;color:#ffabb1}
.uhint{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;color:var(--dim);margin-top:6px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.uunder{font-size:12px;color:#d4ff9d;margin-top:7px}
/* The panel's own reading of the queue. Green-ish, but deliberately plainer than a rule's
   verdict: a model's classification is a good hint, not a tested answer, and it should
   not look like one. */
.uai{display:flex;gap:9px;align-items:center;flex-wrap:wrap;font-size:12px;color:var(--dim);
  margin-top:11px;padding-top:10px;border-top:1px dashed var(--border)}
.uai.off{color:var(--dim)}
.uaitag{background:rgba(131,48,252,.16);border:1px solid #46306b;color:#cbb2ff;border-radius:20px;
  padding:1px 8px;font-size:11px;font-weight:700;text-transform:lowercase}
.uaiv{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap;margin-top:8px;padding:8px 10px;
  border:1px solid #46306b;border-left:3px solid var(--accent2,#8330fc);border-radius:8px;
  background:rgba(131,48,252,.07)}
.uaiclass{border-radius:5px;padding:1px 6px;font-size:10.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.4px;background:rgba(131,48,252,.2);color:#cbb2ff}
.uaiclass.harmless{background:rgba(156,232,11,.14);color:#d4ff9d}
.uaiclass.investigate{background:rgba(229,83,75,.16);color:#ffabb1}
.uaiclass.config{background:rgba(241,198,117,.16);color:#f4d595}
.uaiwhy{font-size:12.5px;color:#dbe6f2;flex:1;min-width:0}
/* The offer to read a log the rules could not: the one place the model is asked from the
   server's own console, because that is where somebody who just watched it crash is. */
.dgask{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:9px;padding-top:9px;
  border-top:1px dashed var(--border)}
.dgask .note{flex:1;min-width:0}
/* The lookups card: what left the machine, in the words that left it. */
.wlist{display:flex;flex-direction:column;gap:5px;max-height:220px;overflow:auto;margin-top:6px}
.wrow{display:flex;gap:9px;align-items:baseline;flex-wrap:wrap;font-size:12px;
  border-bottom:1px solid var(--border);padding:4px 0}
.wq{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;color:#c8d3e0;flex:1;min-width:0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wres{border:1px solid var(--border);border-radius:8px;padding:8px 10px;margin-top:7px;background:var(--panel)}
.wres b{font-size:12.5px}
/* Help. One box, wherever the person is, grounded in the panel's own map and its own
   notes - so an answer can say "Game servers, then Settings" and be right. */
/* ================= the assistant in the corner ================= */
/* One conversation, on every page. The bubble is deliberately small and quiet: it opens
   when it is asked to and never otherwise, because a help panel that volunteers itself on
   every tab is a help panel people learn to dismiss. */
#buddy{position:fixed;right:22px;bottom:22px;z-index:60;display:flex;flex-direction:column;align-items:flex-end;gap:10px}
/* `display:flex` beats the `hidden` attribute in the UA stylesheet, so the states that are
   meant to be invisible have to say so themselves. It hides the bubble behind the login card
   and keeps the closed panel closed - a chat panel that opens itself over the sign-in form is
   exactly the "bugging me on other tabs" the bubble replaced. */
#buddy[hidden],.buddypanel[hidden],.buddydot[hidden]{display:none}
/* Small, and receding: at rest it is 46px of gradient at 88% so it reads as a launcher rather
   than as part of the page, and it comes fully forward on hover or keyboard focus. */
.buddybtn{width:46px;height:46px;border-radius:50%;border:1px solid var(--border);cursor:pointer;position:relative;opacity:.88;
  transition:opacity .15s ease,transform .15s ease;
  background:linear-gradient(140deg,var(--accent),var(--violet));box-shadow:0 10px 26px rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center}
.buddybtn:hover{filter:brightness(1.08)}
#buddy:hover .buddybtn,#buddy:focus-within .buddybtn{opacity:1;transform:translateY(-1px)}
.buddybtn svg{width:22px;height:22px;fill:#0b0f16}
.buddydot{position:absolute;top:3px;right:3px;width:11px;height:11px;border-radius:50%;background:#fff;border:2px solid var(--accent)}
.buddypanel{width:min(420px,calc(100vw - 32px));height:min(620px,calc(100vh - 130px));display:flex;flex-direction:column;
  background:var(--panel);border:1px solid var(--border);border-radius:16px;box-shadow:0 24px 60px rgba(0,0,0,.55);overflow:hidden}
.bubhead{display:flex;align-items:center;gap:10px;padding:12px 13px;border-bottom:1px solid var(--border);background:var(--panel2)}
.bubmk{width:26px;height:26px;border-radius:50%;background:linear-gradient(140deg,var(--accent),var(--violet));display:flex;align-items:center;justify-content:center;flex:0 0 auto}
.bubmk svg{width:15px;height:15px;fill:#0b0f16}
.bubtitle{display:flex;flex-direction:column;line-height:1.25;min-width:0}
.bubtitle b{font-size:13.5px}
.bubtitle small{font-size:11px;color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bubicon{background:transparent;border:0;color:var(--faint);font-size:11.5px;cursor:pointer;padding:4px 6px;border-radius:7px}
.bubicon:hover{color:var(--text);background:rgba(255,255,255,.06)}
.bubbody{flex:1;overflow:auto;padding:13px;display:flex;flex-direction:column;gap:9px}
.bubrow{display:flex;flex-direction:column;align-items:flex-start;gap:6px;max-width:100%}
.bubrow.mine{align-items:flex-end}
.bubmsg{font-size:12.5px;line-height:1.55;padding:9px 11px;border-radius:13px;background:var(--panel2);border:1px solid var(--border);max-width:100%;word-break:break-word}
.bubrow.mine .bubmsg{background:color-mix(in srgb,var(--accent) 16%,transparent);border-color:color-mix(in srgb,var(--accent) 40%,transparent)}
.bubmsg p{margin:0 0 7px}.bubmsg p:last-child{margin-bottom:0}
.bubmsg .mdh{font-size:12.5px;margin:7px 0 3px}
.bubmsg ul,.bubmsg ol{margin:0 0 7px 18px;padding:0}
.bubmsg code{font-family:var(--mono);font-size:11px;background:rgba(0,0,0,.35);border-radius:4px;padding:1px 4px}
.bubask{border-color:color-mix(in srgb,var(--violet) 50%,transparent)}
.buberr{background:rgba(229,83,75,.12);border-color:rgba(229,83,75,.4)}
.bubthink{display:flex;align-items:center;gap:8px;color:var(--dim)}
.bdots{display:inline-flex;gap:3px}.bdots i{width:5px;height:5px;border-radius:50%;background:var(--accent);opacity:.45;animation:bubp 1.15s infinite}
.bdots i:nth-child(2){animation-delay:.18s}.bdots i:nth-child(3){animation-delay:.36s}
@keyframes bubp{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-3px)}}
.bubsteps{margin:0 0 0 17px;padding:0;font-size:12px;line-height:1.6;color:var(--dim)}
.bubmeta{display:flex;gap:7px;align-items:center;flex-wrap:wrap}
.bubnote{font-size:11px;color:var(--faint)}
.bubchips{display:flex;gap:6px;flex-wrap:wrap;padding:0 13px 10px}
.bubchip{background:var(--panel2);border:1px solid var(--border);color:var(--dim);font-size:11.5px;padding:5px 9px;border-radius:999px;cursor:pointer;text-align:left}
.bubchip:hover{color:var(--text);border-color:var(--accent)}
.bubfoot{display:flex;gap:8px;align-items:flex-end;padding:10px 12px 12px;border-top:1px solid var(--border);background:var(--panel2)}
.bubinput{flex:1;resize:none;font-family:var(--font);font-size:12.5px;line-height:1.5;color:var(--text);background:var(--input,var(--panel));
  border:1px solid var(--border);border-radius:10px;padding:8px 10px;max-height:84px}
.bubinput:focus{outline:none;border-color:var(--accent)}
@media(max-width:560px){
  #buddy{right:14px;bottom:14px}
  .buddypanel{width:calc(100vw - 24px);height:min(70vh,520px)}
}

.modal.wide{max-width:960px;width:min(960px,94vw)}
.wres .note{font-size:11.5px;word-break:break-all}
.ulog{font-family:ui-monospace,Consolas,monospace;font-size:11.5px;line-height:1.5;color:#c8d3e0;
  background:#0b1017;border:1px solid var(--border);border-radius:8px;padding:11px 13px;margin:10px 0 2px;
  max-height:320px;overflow:auto;white-space:pre-wrap;word-break:break-word}
@media(max-width:560px){
  .uhead{flex-direction:column}
  .uactions{width:100%;justify-content:flex-start}
  .uactions .btn{flex:1;justify-content:center}
  .utop .spacer{display:none}
  .utop .btn{flex:1 1 auto;justify-content:center}
  .ulog{max-height:240px}
}

/* ================= wide game/template picker (desktop only) ================= */
/* Hundreds of AMP templates in a 3-wide strip inside a 740px modal meant endless
   scrolling and unreadable names. On a real desktop window the picker now uses
   the width that is actually available; phones are untouched (they stay full
   width via the .modal rule in the max-width:560px block). */
@media(min-width:561px){
  /* The picker is the one dialog whose content is a catalogue rather than a form, so it takes the
     window: tiles wide enough for a whole template name, and a name that wraps to a second line
     instead of being cut to `Sol…`. */
  .modal.picker{width:min(1560px,98vw)}
  .modal.picker .ppcats{width:210px;padding-right:14px}
  .ppgrid{grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:12px;
    /* The picker's footer holds Cancel (and Create on step 2), so it is the one part of the
       dialog that must not be reachable only by scrolling. A grid capped at 70vh let the
       header + grid + footer add up past the dialog's own 92vh cap, which pushed the buttons
       below the scroll edge - on a short window the only way out was Escape. Subtract the
       chrome the header and footer need first, then cap: Cancel is on screen when it opens. */
    max-height:min(70vh,780px,calc(92vh - 260px))}
  .modal.picker .pp-tile{padding:13px}
  .modal.picker .pp-tile b{font-size:13.5px;white-space:normal;line-height:1.3;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .modal.picker .pp-tile small{font-size:11px;white-space:normal;line-height:1.35;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .modal.picker .pp-tile-ic{width:64px;height:64px;font-size:28px}
  .modal.picker .pp-tile.steam .pp-tile-ic{height:78px}
  .modal.picker .ppsel .pp-tile-ic.steamwide{width:190px;height:72px}
  /* the config step is a form, not a rail: it widens with the picker but keeps a readable measure */
  .modal.picker .ppconfig{max-width:1040px}
}
@media(min-width:561px) and (max-width:900px){
  /* a narrow desktop window still benefits, just less aggressively */
  .modal.picker{width:min(1000px,96vw)}
  .ppgrid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}
}

/* On a phone the category rail took 148px of a ~450px modal, leaving room for a
   single column of templates. Turn the rail into a horizontal chip row so the
   tiles get the full width instead. */
@media(max-width:560px){
  .ppbody{flex-direction:column;gap:10px}
  .ppcats{flex-direction:row;width:100%;flex:0 0 auto;overflow-x:auto;gap:6px;
    border-right:0;border-bottom:1px solid var(--border);padding:0 0 9px;
    scrollbar-width:none;-ms-overflow-style:none}
  .ppcats::-webkit-scrollbar{display:none}
  .ppcat{flex:0 0 auto;padding:7px 12px;border:1px solid var(--border);border-radius:8px;font-size:12.5px}
  .ppcat.on{box-shadow:none;border-color:var(--accent2)}
  .ppgrid{max-height:52vh}
}

/* email hosting */
.sharedtag{display:inline-block;padding:2px 7px;border-radius:20px;font-size:9.5px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;background:rgba(116,183,255,.13);color:var(--blue)}
.pill.good{background:rgba(156,232,11,.13);color:var(--accent)}
.pill.bad{background:var(--red-soft);color:var(--red)}
.pill.warn{background:rgba(241,198,117,.13);color:var(--amber)}
.pill.info{background:rgba(116,183,255,.13);color:var(--blue)}
/* game settings (server.properties, labelled) */
.gbar{display:flex;gap:10px;align-items:center;margin-bottom:14px;flex-wrap:wrap}
.gbar .field{flex:1;min-width:230px;margin:0}
.ggroup h3{display:flex;align-items:center;gap:8px}
.gsetgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:12px}
.gitem{border:1px solid var(--border);border-radius:10px;padding:11px 13px;background:rgba(11,16,23,.55);transition:border-color .15s,background .15s;min-width:0}
.gitem:hover{border-color:var(--border2)}
.gitem.dirty{border-color:var(--accent2);background:var(--accent-soft)}
.gitem .glab{font-weight:700;font-size:13px;margin-bottom:7px;display:flex;align-items:center;gap:7px}
.gitem select,.gitem>.field,.gitem .inputrow .field{width:100%}
.gitem .ghelp{font-size:11.5px;color:var(--dim);margin-top:7px;line-height:1.5}
/* Not `.gitem .gkey`: the server Overview prints the same class for the folder a server lives in,
   and there the rule never applied - the path came out in the body font and painted straight past
   the panel's right edge (283px box, 422px of text). A path is monospace and it wraps wherever it
   has to, in both places. */
.gkey{font-family:var(--mono);font-size:10.5px;color:var(--faint);margin-top:6px;word-break:break-all}
.gcheck{display:flex;align-items:center;gap:9px;margin:0;color:var(--text);font-size:13px;font-weight:600;cursor:pointer}
/* A 36-character UUID in a narrow table cell wrapped onto three lines; one
   clipped line with the whole value in the tooltip reads far better. */
.puuid{font-family:var(--mono);font-size:10.5px;color:var(--faint);margin-top:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:230px}
/* storage bars: a size is only meaningful next to the other sizes */
.dbar{display:grid;grid-template-columns:minmax(90px,1.1fr) 2fr auto;gap:10px;align-items:center;padding:4px 0;font-size:12.5px}
.dname{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dmeter{background:rgba(255,255,255,.06);height:7px;border-radius:6px;overflow:hidden}
.dmeter i{display:block;height:100%;background:linear-gradient(90deg,var(--accent2),var(--accent));border-radius:6px}
.dsize{font-family:var(--mono);font-size:11.5px;color:var(--dim);text-align:right;white-space:nowrap}
table.ops{margin-top:4px}
table.ops td{font-size:12.5px}
.snipbar{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:12px}
.snipbar .chip{cursor:pointer}
@media(max-width:560px){
  .dbar{grid-template-columns:1fr auto;grid-template-areas:"n s" "m m";row-gap:4px}
  .dname{grid-area:n}.dsize{grid-area:s}.dmeter{grid-area:m}
}
.gcheck input{width:auto}
@media(max-width:560px){.gsetgrid{grid-template-columns:1fr}}

/* per-user resource permissions */
.permtable{margin:6px 0 2px}
.permtable td{vertical-align:top}
.permtable td.pcell{white-space:normal;min-width:190px}
.pbox{display:inline-flex;align-items:center;gap:4px;margin:0 10px 5px 0;font-size:12px;color:var(--dim);cursor:pointer;white-space:nowrap}
.pbox input{width:auto;margin:0}
@media(max-width:560px){
  .permtable td.pcell{min-width:0}
  .pbox{margin-right:8px}
}

/* server readiness preflight */
/* the .pill base style capitalizes every word, which turns a whole sentence
   into title case; the headline is prose, so it opts out of that */
.readyhead .pill{text-transform:none;font-size:11.5px;padding:4px 12px;letter-spacing:.01em}
.readyhead{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:10px 0 2px}
/* Readiness is a list of lines, not a stack of cards: the coloured rule is the status cue and
   the box around each one was four pixels of border and a second of padding per item, which put
   ten lines of text on six hundred pixels of page. The detail and the fix share a paragraph too -
   they were two paragraphs saying one thing. */
.readyarea{margin-top:10px}
.readyarea .sechead{margin:0 0 5px}
/* The readiness panel is the whole rack and the list is twelve short lines, so on a desktop it
   flows into two columns - each area kept whole - instead of one 1100-pixel line of state/fix
   pairs. `columns` rather than grid, because the items are different heights and grid would
   either stretch them or leave a hole under every short one. */
@media(min-width:1000px){
  #readybox{columns:2;column-gap:26px}
  #readybox .readyarea{break-inside:avoid}
  #readybox .readyhead{column-span:all}
}
.readyitem{padding:4px 0 4px 10px;border:0;border-left:2px solid transparent;border-radius:0;margin-bottom:3px;background:none}
.readyitem.fail{border-left-color:var(--red)}
.readyitem.warn{border-left-color:var(--amber)}
.readyitem.pass{border-left-color:var(--accent)}
.readyitem.info{border-left-color:var(--blue)}
.readyitem .rt{font-size:12.5px;font-weight:650;display:flex;align-items:center;gap:7px;flex-wrap:wrap}
.readyitem .rt .pill{font-size:10.5px;padding:2px 7px}
.readyitem .note{margin:1px 0 0;font-size:12px;line-height:1.45}
.rdyfix{margin-top:6px;font-size:12.5px;color:var(--accent);line-height:1.5}
.rdyfix b{color:var(--accent2)}
.rdyfix.inline{margin:0;font-size:12px}
.rdyfix.inline b{color:var(--accent2)}
.switchline{display:flex;align-items:center;gap:8px;text-transform:none;letter-spacing:0;font-size:13px;color:var(--text);margin:8px 0}
.switchline input{width:auto;margin:0}
.mailfacts{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:12px}
.mailfacts>div{display:flex;flex-direction:column;gap:2px;padding:9px 11px;border:1px solid var(--border);border-radius:10px;background:var(--panel2,rgba(255,255,255,.02))}
.mailfacts b{font-size:13px}
.mailbody{margin:10px 0 4px;padding:12px;max-height:52vh;overflow:auto;white-space:pre-wrap;word-break:break-word;font-size:12.5px;line-height:1.55;border:1px solid var(--border);border-radius:10px;background:var(--bg2,rgba(0,0,0,.25))}
#msettings select,#msettings input,#msend input{width:100%}
@media(max-width:560px){
  .mailfacts{grid-template-columns:repeat(auto-fit,minmax(120px,1fr))}
  .mailbody{max-height:46vh;font-size:12px}
  /* A row whose action buttons sit off the right edge is a row nobody can act on,
     and a horizontally scrolling table hides that. Drop the least useful column
     on a phone instead of making the operator scroll to reach the buttons. */
  .hide-sm{display:none}
}

/* ---- selective backup plan ----
   Sizes are the point of this panel, so each category is a full-width card with
   the number in its own monospace line rather than a cramped checkbox row. */
.planbox{background:rgba(17,23,32,.55);border:1px solid var(--border);border-radius:11px;padding:15px;margin-bottom:16px}
.plantop{display:flex;gap:14px;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;margin-bottom:13px}
.planh{font-size:14px;font-weight:750;margin-bottom:3px}
.presets{display:flex;gap:6px;flex-wrap:wrap}
.presets .prenum{color:var(--dim);font-weight:600;font-size:11px}
.bkgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:9px}
.bkcat{display:flex;gap:10px;align-items:flex-start;background:rgba(11,16,23,.6);border:1px solid var(--border);border-radius:9px;padding:10px 12px;cursor:pointer;transition:border-color .15s,background .15s}
.bkcat:hover{border-color:var(--border2)}
.bkcat.on{border-color:var(--accent2);background:rgba(156,232,11,.07)}
.bkcat.mute{opacity:.45;cursor:not-allowed}
.bkcat input{width:auto;margin-top:2px;flex:none}
.bkcat .bktext{display:block;min-width:0}
.bkcat .bktext b{display:block;font-size:13px}
.bkcat .bktext i{display:block;font-style:normal;color:var(--dim);font-size:11.5px;margin-top:2px;line-height:1.45}
.bkcat .bktext em{display:block;font-family:var(--mono);font-size:11.5px;color:var(--accent);margin-top:5px}
.bkcat:not(.on) .bktext em{color:var(--faint)}
.bkfoot{margin-top:13px;display:flex;flex-direction:column;gap:8px}
.bkadd{display:flex;gap:7px;max-width:430px}
.bksums{font-size:12.5px;color:var(--dim)}
.bksums b{color:var(--text)}
.bksums .warnline{color:var(--amber)}
.bkrm{font-family:var(--mono);font-size:11px;padding:3px 8px}
.bkcat+.bkcat{margin:0}

/* ---- backup destinations ----
   "Also copy each backup to" sits above the button that makes one, because a
   copy is part of what a backup is, not a detail buried in settings. */
.copybox{background:rgba(17,23,32,.55);border:1px solid var(--border);border-radius:11px;padding:14px;margin-bottom:14px}
.copybox.none{border-style:dashed}
.copyhead{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;margin-bottom:10px}
.copyhead b{font-size:13.5px}
.copylist{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:8px}
.copytarget{display:flex;gap:9px;align-items:flex-start;background:rgba(11,16,23,.6);border:1px solid var(--border);border-radius:8px;padding:9px 11px;cursor:pointer}
.copytarget.on{border-color:var(--accent2);background:rgba(156,232,11,.07)}
.copytarget input{width:auto;margin-top:2px;flex:none}
.copytarget b{display:block;font-size:12.5px}
.copytarget i{display:block;font-style:normal;color:var(--dim);font-size:11px;margin-top:2px;word-break:break-word}
.copies .copyok,.copies .copybad{display:inline-block;font-size:11px;margin-right:6px;white-space:nowrap}
.copies .copyok{color:var(--accent);font-weight:650}
.copies .copybad{color:var(--red);font-weight:650}
.copies .copyok:before{content:"\2713 ";font-weight:800}
.copies .copybad:before{content:"\2715 ";font-weight:800}
a.linkish{color:var(--accent);text-decoration:none;font-weight:650}
a.linkish:hover{text-decoration:underline}
.remotehead{display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin:24px 0 10px}
.remotehead h3{margin:0;font-size:15px}
.remotehead select{width:auto;min-width:190px}
.destrow{display:flex;gap:12px;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;padding:11px 0;border-top:1px solid var(--border)}
.destrow.off{opacity:.6}
.destrow .dmain{min-width:0;flex:1}
.dline{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:3px}
.dactions{display:flex;gap:5px;flex-wrap:wrap}

/* ---- dashboard: system health ---- */
/* Compact status bar + one-line parts. The old alertbanner read as a billboard. */
#healthpanel{padding:14px 16px}
#healthpanel .healthhead{margin:0 0 8px;align-items:center}
#healthpanel .healthhead h3{font-size:13px;font-weight:650;letter-spacing:.02em;text-transform:uppercase;color:var(--dim)}
.hbar{display:flex;align-items:center;gap:8px;padding:5px 8px;margin:0 0 6px;border-radius:8px;border:1px solid var(--border);font-size:12px;line-height:1.3;background:transparent}
.hbar .abic{font-size:12px;line-height:1;width:14px;text-align:center;flex:0 0 auto}
.hbarmsg{color:var(--text);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hbarcounts{margin-left:auto;color:var(--dim);font-size:11px;white-space:nowrap;flex:0 0 auto}
.hbar.ok{border-color:rgba(74,222,128,.28);background:rgba(74,222,128,.06)}
.hbar.warn{border-color:rgba(244,213,149,.3);background:rgba(244,213,149,.06)}
.hbar.error{border-color:rgba(255,154,162,.32);background:rgba(255,154,162,.06)}
.hlist{display:flex;flex-direction:column;margin-top:0;border-top:1px solid var(--border)}
.hlist .hitem{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;padding:4px 2px;
  border:0;border-bottom:1px solid var(--border);border-radius:0;background:transparent}
.hlist .hitem:last-child{border-bottom:0}
.hgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px;margin-top:4px}
.hitem{border:1px solid var(--line);border-radius:10px;padding:11px 12px;background:var(--panel2);border-left:3px solid var(--line)}
.hitem.h-ok{border-left-color:#5c8a2a}
.hitem.h-warn{border-left-color:#8a7134}
.hitem.h-fail{border-left-color:#8d3b43}
.hitem.h-off{border-left-color:#3a4450}
.hrow{display:flex;align-items:center;gap:7px;font-size:13px;margin-bottom:5px}
.hrow .hic{font-size:12px;width:14px;text-align:center;color:var(--dim)}
.hitem.h-ok .hic{color:#9ce80b}
.hitem.h-warn .hic{color:#f4d595}
.hitem.h-fail .hic{color:#ff9aa2}
.hline{font-size:12.5px;color:var(--text);line-height:1.45}
.hfix{font-size:11.5px;color:var(--dim);margin-top:4px;line-height:1.45;word-break:break-word}
/* ...and the list form of the same pieces: name, sentence, button, all on one line. */
.hlist .hrow{margin:0;flex:0 0 auto;min-width:118px}
.hlist .hline{flex:1 1 260px;min-width:0;color:var(--dim);overflow:hidden;text-overflow:ellipsis}
.hlist .hitem.h-ok .hline{color:var(--dim)}
.hlist .hitem.h-fail .hline,.hlist .hitem.h-warn .hline{color:var(--text)}
.hlist .hact{flex:0 0 auto}
.hlist .hact .btn{padding:3px 10px;font-size:12px}
.hlist .hfix{flex:1 1 100%;margin:2px 0 0 21px;padding-left:0}
@media(max-width:560px){.hgrid{grid-template-columns:1fr}}
.abmsg .hsub{display:block;font-size:11.5px;color:var(--dim);margin-top:3px}

/* ---- sidebar search + results overlay ---- */
.sidesearch{position:relative;margin:2px 12px 10px}
.sidesearch input{width:100%;padding-right:52px}
.sidesearch kbd{position:absolute;right:8px;top:50%;transform:translateY(-50%);font:600 9.5px/1 var(--mono);color:var(--faint);border:1px solid var(--line);border-radius:5px;padding:3px 4px;background:var(--panel2);pointer-events:none}
.sover{position:fixed;inset:0;z-index:80;background:rgba(6,8,12,.62);display:flex;align-items:flex-start;justify-content:center;padding:60px 16px 16px}
.spanel{width:min(900px,100%);max-height:min(70vh,640px);display:flex;flex-direction:column;background:var(--panel);border:1px solid var(--line);border-radius:14px;box-shadow:0 22px 60px rgba(0,0,0,.5);overflow:hidden}
.shead{display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--line);font-size:13px;font-weight:600}
.shead .spacer{flex:1}
.shead .skey{font-size:11px;color:var(--faint);font-weight:400}
.sres{overflow:auto;padding:6px 0 10px}
.sgroup{padding:4px 0 2px}
.sglabel{font-size:10.5px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint);padding:8px 14px 5px}
.sglabel i{font-style:normal;color:var(--dim)}
.shit{padding:8px 14px;cursor:pointer;border-left:2px solid transparent}
.shit:hover{background:var(--panel2);border-left-color:var(--accent)}
.sht{font-size:13px;color:var(--text);word-break:break-word}
.shs{font-size:11.5px;color:var(--dim);margin-top:2px;word-break:break-word}
.sempty{padding:18px 16px;color:var(--dim);font-size:12.5px}
.sempty.err{color:#ff9aa2}
@media(max-width:560px){.sover{padding:10px}.spanel{max-height:82vh}.sidesearch{margin:2px 10px 8px}}

/* The core plugin's settings form, the older `.cfgbox` layout: still used by the
   editors that are a list of records (a kit, a page, a crop), where a box per
   group is the right shape. The settings page itself moved to `.wx*` below. */
.cfgbox{border:1px solid var(--border);border-radius:11px;padding:12px 14px;margin-bottom:11px;background:var(--panel)}
/* A pre-generation job's progress, in the world view. Deliberately the same shape as a health bar:
   a track the width of the row with a filled part, so how much is left reads at a glance where a
   `43/289` has to be worked out. */
.wgbar{height:6px;border-radius:4px;background:var(--panel3);border:1px solid var(--border);overflow:hidden;margin:6px 0 4px}
.wgbar>i{display:block;height:100%;background:var(--accent);transition:width .4s ease}
.cfghead{font-size:12px;letter-spacing:.04em;text-transform:uppercase;color:var(--dim);margin-bottom:9px}
.cfgfield{display:flex;flex-direction:column;gap:4px;margin-bottom:11px}
.cfgfield .cfgname{font-size:12.5px;color:var(--text)}
.cfgfield .note{font-size:11px;color:var(--warn)}
@media(min-width:620px){.cfgbox .cfgfield{align-items:center;display:grid;grid-template-columns:1fr 220px;gap:10px;margin-bottom:8px}.cfgbox .cfgfield .note{grid-column:2}}
/* The core settings page: a rail of groups, a pane of rows, a sticky footer. It
   replaced one long scroll of every setting, which is a page where finding a key
   means reading 5,000 pixels of them - the rail is navigation, the search box is
   the way in when the name is known, and the footer is where the save lives so it
   is never further away than the bottom of the window. */
.wxsub{color:var(--dim);font-size:12px;line-height:1.55;margin:2px 0 12px;max-width:78ch}
.wxsub code{font-family:var(--mono);font-size:11.5px;background:#0b1017;border:1px solid var(--border);border-radius:5px;padding:1px 5px}
.wxbar{display:flex;gap:9px;align-items:center;margin-bottom:12px;flex-wrap:wrap}
.wxbar .field{flex:1;min-width:220px;margin:0}
.wxbar .note{margin-left:auto;text-align:right}
.wxwrap{display:grid;grid-template-columns:206px 1fr;gap:14px;align-items:start}
.wxside{position:sticky;top:0;max-height:62vh;overflow:auto;padding-right:2px;border-right:1px solid var(--border)}
.wxitem{display:block;width:100%;text-align:left;background:transparent;border:1px solid transparent;border-radius:9px;padding:7px 10px;color:var(--text);cursor:pointer;position:relative;margin-bottom:2px}
.wxitem:hover{border-color:var(--border);background:rgba(31,42,58,.5)}
.wxitem.on{border-color:var(--accent2);background:var(--accent-soft)}
.wxitem b{display:block;font-size:12.5px;font-weight:650;line-height:1.3}
.wxitem span{display:block;color:var(--faint);font-size:10.5px;margin-top:2px}
.wxitem.off{cursor:default;color:var(--dim)}
.wxdot{position:absolute;right:9px;top:11px;width:6px;height:6px;border-radius:50%;background:var(--amber);box-shadow:0 0 8px rgba(232,163,61,.7)}
.wxsidefoot{padding:9px 10px 2px;border-top:1px solid var(--border);margin-top:7px}
.wxpane{min-width:0}
/* The pane's title names whichever thing is driving the list - the rail's group, or the search. */
.wxpanehead{display:flex;gap:9px;align-items:baseline;font-size:12.5px;color:var(--text);padding:1px 9px 8px;border-bottom:1px solid var(--border);margin-bottom:6px}
.wxpanehead b{font-weight:650}
.wxpanehead .note{margin-left:auto;color:var(--faint);font-size:11px}
.wxmain{max-height:62vh;overflow:auto;padding-right:4px}
.wxgroup{margin-bottom:15px}
.wxgrouphead{font-size:11.5px;letter-spacing:.05em;text-transform:uppercase;color:var(--dim);margin:2px 0 7px;display:flex;gap:8px;align-items:baseline}
.wxgrouphead .note{margin-left:auto;color:var(--faint)}
.wxrow{display:grid;grid-template-columns:1fr 264px 150px;gap:10px;align-items:center;padding:7px 9px;border:1px solid transparent;border-radius:9px}
.wxrow+.wxrow{border-top:1px solid rgba(38,50,66,.5);border-radius:0}
.wxrow:hover{background:rgba(24,33,46,.6)}
.wxrow.changed{background:rgba(29,52,38,.5);border-color:#2f5a3c;border-radius:9px}
.wxrow.pinned .wxid b{color:var(--accent)}
.wxid{min-width:0}
.wxid b{display:block;font-size:12.5px;font-weight:600}
.wxid code{display:block;font-family:var(--mono);font-size:10.5px;color:var(--faint);margin-top:2px}
.wxgrp{display:inline-block;font-size:10px;text-transform:uppercase;letter-spacing:.05em;color:var(--dim);margin-bottom:2px}
.wxchip{display:inline-block;font-size:10px;border:1px solid var(--border);border-radius:20px;padding:1px 7px;color:var(--amber);margin-left:7px;vertical-align:1px}
.wxctl{display:flex;gap:6px;align-items:center}
.wxctl .field{margin:0}
.wxseg{display:flex;border:1px solid var(--border);border-radius:8px;overflow:hidden;flex:1}
.wxseg button{flex:1;background:transparent;border:0;color:var(--dim);font-size:11.5px;padding:7px 6px;cursor:pointer;font-weight:600}
.wxseg button:hover{color:var(--text);background:rgba(31,42,58,.7)}
.wxseg button.on{background:var(--accent-soft);color:var(--accent)}
.wxreset{background:transparent;border:1px solid var(--border);border-radius:7px;color:var(--dim);width:28px;height:28px;cursor:pointer;font-size:13px;line-height:1}
.wxreset:hover:not(:disabled){border-color:var(--accent2);color:var(--accent)}
.wxreset:disabled{opacity:.3;cursor:default}
.wxcfg{color:var(--faint);font-size:11px;text-align:right;display:flex;flex-direction:column;gap:3px;align-items:flex-end}
.wxcfg b{color:var(--dim);font-weight:650;font-family:var(--mono);font-size:11px}
.wxuse{background:transparent;border:1px dashed var(--border);border-radius:20px;color:var(--dim);font-size:10.5px;padding:2px 8px;cursor:pointer;white-space:nowrap}
.wxuse:hover{border-color:var(--accent2);color:var(--accent)}
.wxempty{padding:26px 10px;color:var(--dim);font-size:13px;text-align:center}
.wxfoot{position:sticky;bottom:-24px;background:var(--panel);border-top:1px solid var(--border);padding-top:12px;align-items:center}
.wxfoot .note{font-size:11.5px}
@media(max-width:820px){
  .wxwrap{grid-template-columns:1fr}
  .wxpanehead{padding:4px 6px 8px}
  .wxside{position:static;max-height:none;border-right:0;border-bottom:1px solid var(--border);padding:0 0 8px;display:flex;gap:6px;overflow-x:auto}
  .wxitem{min-width:150px;margin-bottom:0}
  .wxsidefoot{display:none}
  .wxrow{grid-template-columns:1fr;gap:6px;padding:9px 6px}
  .wxmain{max-height:none}
  .wxcfg{text-align:left;flex-direction:row;align-items:center}
}
/* The NPC editor: a list on the left, the selected character on the right. Below
   700px it stacks, because a two-column form on a phone is two unusable columns. */
.npcwrap{display:flex;gap:14px;align-items:flex-start;min-height:320px}
.npclist{flex:0 0 250px;border:1px solid var(--border);border-radius:11px;background:var(--panel);overflow:auto;max-height:60vh}
.npcitem{position:relative;padding:9px 11px;border-bottom:1px solid var(--border);cursor:pointer}
/* The page rail's delete. Shown on hover, and always on the page that is selected, so it is found
   without competing with the page's own name the rest of the time. */
.npcitem .uisdel{position:absolute;right:6px;top:6px;padding:1px 7px;line-height:1.2;font-size:14px;opacity:0}
.npcitem:hover .uisdel,.npcitem.on .uisdel{opacity:.9}
.npcitem .uisdel:hover{opacity:1;border-color:var(--red,#e05a5a);color:var(--red,#e05a5a)}
.npcitem:hover{background:#1b2531}
.npcitem.on{background:#1d2b22;box-shadow:inset 3px 0 0 var(--accent)}
.npcn{font-size:13px;color:var(--text);font-weight:600}
.npcitem .note{font-size:11px}
.npcpane{flex:1 1 auto;min-width:0}
.npcpane .cfgfield{margin-bottom:8px}
.npcact{display:flex;gap:6px;align-items:center;margin-bottom:7px}
.npcact .field{flex:0 0 200px}
.npcact input.field{flex:1 1 auto}
.npcadd{display:flex;gap:7px;margin-top:9px;flex-wrap:wrap}
@media(max-width:700px){.npcwrap{flex-direction:column}.npclist{flex:1 1 auto;width:100%;max-height:180px}.npcact{flex-wrap:wrap}.npcact .field{flex:1 1 100%}}
/* The menu editor's canvas: the page as a chest grid. It is a layout rather than a
   picture - the panel ships no item textures - so a cell carries the button's count,
   its name in the colour the player will read it in, and its material underneath.
   The tooltip preview below it is the game's colour, which is what makes a wrong
   `&c` on a name visible before the page is saved. */
.uigrid{display:grid;grid-template-columns:repeat(9,var(--uicw,1fr));gap:4px;background:var(--bg2);border:1px solid var(--border);border-radius:11px;padding:8px;overflow:auto;justify-content:start}
.uicell{position:relative;aspect-ratio:1;border:1px solid var(--border);border-radius:7px;background:#0b1017;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:4px;cursor:pointer;overflow:hidden;text-align:center}
.uicell:hover{border-color:var(--border2);background:#0e151d}
.uicell.has{border-color:var(--border2);background:var(--mat,var(--panel2))}
.ubzoom{display:inline-flex;gap:2px;align-items:center;margin-left:6px}
.uicell.on{border-color:var(--accent2);box-shadow:0 0 0 2px var(--accent-soft)}
.uicell .uinm{font-size:10px;line-height:1.15;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;word-break:break-word;white-space:pre-wrap}
.uicell .uimat{font-size:8.5px;line-height:1;color:var(--faint);letter-spacing:.02em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
.uicell .uidim{font-size:9px;color:var(--faint)}
.uicell .uiamt{position:absolute;right:3px;top:2px;font-size:9px;font-weight:700;color:var(--accent)}
/* A slot's index, in the corner of the cell. A page is saved as `slot: 11`, so the canvas has to be
   readable in the same numbers the file uses or the two are different documents. */
.uicell .uino{position:absolute;left:3px;bottom:1px;font-size:8.5px;line-height:1.1;color:var(--faint);font-variant-numeric:tabular-nums}
.uicell.has .uino{color:var(--dim)}
.uicell:hover .uino{color:var(--fg2)}
.uicell.on .uino,.uicell.in .uino{color:var(--accent2)}
/* An empty slot that the page fills: a wash rather than the material's name repeated in twenty
   cells. The name is said once, in `#ulegend` under the grid. */
.uicell.fill{background-image:repeating-linear-gradient(45deg,rgba(255,255,255,.03) 0 5px,transparent 5px 10px)}
.ulegend{font-size:11.5px;color:var(--faint);margin:5px 2px 0}
/* The menu editor's material palette: the brush's material list, searchable. A strip of chips under
   the tool bar rather than a side column, because the modal is already two columns wide and a
   palette that scrolls sideways is one you stop reading. */
.ubrush{color:var(--accent);font-weight:650}
.upal{display:flex;gap:9px;align-items:flex-start;margin:0 0 9px;padding:7px 8px;background:var(--bg2);border:1px solid var(--border);border-radius:10px}
.upalq{flex:0 0 190px;margin:0}
.upalchips{flex:1 1 auto;display:flex;flex-wrap:wrap;gap:4px;max-height:66px;overflow:auto;padding:2px 0}
.upalchip{font:inherit;font-size:11.5px;line-height:1.3;padding:3px 8px;border-radius:20px;border:1px solid var(--border);background:#0b1017;color:var(--dim);cursor:pointer}
.upalchip:hover{border-color:var(--border2);color:var(--text)}
.upalchip.on{border-color:var(--accent2);background:var(--accent-soft);color:var(--accent);font-weight:600}
@media(max-width:820px){.upal{flex-direction:column}.upalq{flex:1 1 auto;width:100%}}
.ulegend b{color:var(--dim);font-weight:600}
/* The editor's own explanation, folded away. */
.kuilead{margin-bottom:10px}
.kuitell{margin-top:4px}
.kuitell summary{cursor:pointer;color:var(--fg2);font-size:12px}
.kuitell summary:hover{color:var(--accent)}
.kuitell[open] summary{margin-bottom:5px}
.uicell .uilock{position:absolute;left:3px;top:2px;font-size:9px;line-height:1}
.uifields{display:grid;grid-template-columns:1fr 1fr;gap:2px 14px;align-items:start}
.uiac{display:flex;gap:6px;align-items:center;margin-bottom:6px}
.uiac .uiverb{flex:0 0 118px}
.uiac .uiarg{flex:1 1 auto}
.uitip{background:#17001b;border:1px solid #2f0a35;border-radius:8px;padding:8px 11px;font-size:12.5px;line-height:1.5;max-width:330px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.04)}
.uitipname{font-weight:700}
.uitipline{color:#aaa}
@media(max-width:700px){.uifields{grid-template-columns:1fr}.uigrid{gap:2px;padding:5px}.uicell{padding:2px}.uicell .uimat{display:none}.uicell .uinm{font-size:8.5px}.uiac{flex-wrap:wrap}.uiac .uiverb{flex:1 1 100%}}

/* ---- boards: the printed canvas ---------------------------------------------------------- */
/* The canvas is monospace and nothing else is: a board is a grid of characters, so every cell has
   to advance by exactly one character or the whole point of the ruler is lost. `white-space:pre` on
   a cell keeps a space a space (the holes in a frame are the frame), and the font size is what sets
   the column width everywhere at once. */
.ubkind{font-size:8.5px;letter-spacing:.06em;text-transform:uppercase;border:1px solid var(--border);border-radius:5px;padding:1px 4px;margin-left:6px;color:var(--faint)}
.ubkind.board{color:#8ad;border-color:#2b3f55}
.ubkind.strip{color:#c8a0e8;border-color:#40304f}
.ubnewrow{display:flex;gap:6px;flex-wrap:wrap;padding:0 8px 8px}
.ubtplgrid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.ubtplhead{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin:0 0 4px 2px}
.ubbar{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:2px 0 8px;padding:7px 8px;background:var(--bg2);border:1px solid var(--border);border-radius:10px}
.ubpenlbl{font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.ubbar .spacer{flex:1 1 auto}
.ubpen{font:inherit;font-size:11.5px;padding:3px 9px;border-radius:7px;border:1px solid var(--border);background:#0b1017;color:var(--fg);cursor:pointer}
.ubpen:hover{border-color:var(--border2)}
.ubpen.on{border-color:var(--accent2);box-shadow:0 0 0 2px var(--accent-soft);color:var(--accent)}
.ubcanvas{position:relative;overflow:auto;background:#080c12;border:1px solid var(--border);border-radius:11px;padding:8px 10px 10px;font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace;font-size:12.5px;line-height:1.35;outline:none}
.ubcanvas:focus{border-color:var(--border2)}
.ubrrow{display:flex;align-items:stretch;white-space:pre}
.ubgut{flex:0 0 26px;text-align:right;padding-right:8px;color:var(--faint);font-size:10.5px;user-select:none}
.ubruler .ubrc{color:var(--faint)}
.ubrc{position:relative;flex:0 0 auto;width:1ch;text-align:center;cursor:text;user-select:none}
.ubrc.owned{cursor:pointer}
.ubrc.sel{background:rgba(120,180,255,.16)}
.ubrc.sel.sl{box-shadow:inset 2px 0 0 var(--accent2)}
.ubrc.sel.sr{box-shadow:inset -2px 0 0 var(--accent2)}
.ubrc.sel.st{box-shadow:inset 0 2px 0 var(--accent2)}
.ubrc.sel.sb{box-shadow:inset 0 -2px 0 var(--accent2)}
.ubrc.sel.st.sl{box-shadow:inset 2px 0 0 var(--accent2),inset 0 2px 0 var(--accent2)}
.ubrc.sel.st.sr{box-shadow:inset -2px 0 0 var(--accent2),inset 0 2px 0 var(--accent2)}
.ubrc.sel.sb.sl{box-shadow:inset 2px 0 0 var(--accent2),inset 0 -2px 0 var(--accent2)}
.ubrc.sel.sb.sr{box-shadow:inset -2px 0 0 var(--accent2),inset 0 -2px 0 var(--accent2)}
.uhw,.uhh{position:absolute;width:9px;height:9px;border-radius:2px;background:var(--accent2);cursor:ew-resize;z-index:2}
.uhw{right:-4px;top:-4px}
.uhh{left:-4px;bottom:-4px;cursor:ns-resize}
/* A block in a multiple selection: a flat wash, so it is legible at a glance which block the
   inspector is showing (that one has the outline and the handles) and which are only along for the
   drag. */
.ubrc.inset{background:rgba(120,180,255,.07)}
/* A picture frame's cells: the nine-slice the server draws, shown as *which piece* rather than as
   dashes. The editor cannot draw the pack's own pixels, so it draws the map - a corner is a corner,
   an edge runs along its side, the middle is the field of tiles the content sits on top of. */
.ubrc.ubart{background:rgba(150,200,255,.10)}
.ubrc.ubart-t{box-shadow:inset 0 1px 0 rgba(150,200,255,.45)}
.ubrc.ubart-b{box-shadow:inset 0 -1px 0 rgba(150,200,255,.45)}
.ubrc.ubart-l{box-shadow:inset 1px 0 0 rgba(150,200,255,.45)}
.ubrc.ubart-r{box-shadow:inset -1px 0 0 rgba(150,200,255,.45)}
.ubrc.ubart-tl{background:rgba(150,200,255,.22)}
.ubrc.ubart-tr{background:rgba(150,200,255,.22)}
.ubrc.ubart-bl{background:rgba(150,200,255,.22)}
.ubrc.ubart-br{background:rgba(150,200,255,.22)}
.ubrc.ubart-c{background:rgba(150,200,255,.05)}
/* The alignment guides a drag has lined up. Above the cells, never in the way of a click. */
.ubguides{position:absolute;inset:0;pointer-events:none;z-index:1}
.ubguide{position:absolute;background:var(--accent2);opacity:.55}
.ubguide.v{width:1px}
.ubguide.h{height:1px}
/* The rubber band over the cells it will pick, drawn from the two corner cells so it always covers
   exactly what a click will select. */
.ubmarq{position:absolute;border:1px solid var(--accent2);background:rgba(120,180,255,.12);border-radius:3px;pointer-events:none;z-index:1}
.ubcount{margin-left:8px;padding:1px 7px;border-radius:999px;background:var(--accent-soft);color:var(--accent2);font-size:11px}
.ubalgn{display:inline-flex;gap:2px;margin-right:6px}
.ubalgn .btn.sm{padding:2px 6px;font-size:11px}
.ubfoottools{display:inline-flex;gap:4px;margin-left:10px}
/* A page in the list that the server's own check would complain about: a count, with the first few
   named in the tooltip. The list is a list, so it stays one line - but nobody has to open four pages
   to find which one is broken. */
.ubpbadge{margin-left:6px;padding:0 6px;border-radius:999px;background:rgba(230,180,80,.18);border:1px solid rgba(230,180,80,.45);color:#e0b060;font-size:10.5px;line-height:16px}
.ublayer.in{border-color:var(--accent2);opacity:.85}
.uicell.in{border-color:var(--accent2);box-shadow:0 0 0 1px var(--accent-soft)}
.ubprob[data-ubgoto]{cursor:pointer}
.ubprob[data-ubgoto]:hover{background:rgba(230,180,80,.10);border-left-color:#e0b060}
/* The review dialog: the file the panel is about to write, with the changed lines marked. */
.ubdiff{max-height:52vh;overflow:auto;background:#080c12;border:1px solid var(--border);border-radius:10px;padding:8px 0;font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace;font-size:11.5px;line-height:1.45}
.ubdline{display:flex;gap:8px;padding:0 10px;white-space:pre-wrap;word-break:break-word}
.ubdline.add{background:rgba(90,200,120,.12)}
.ubdline.del{background:rgba(220,90,90,.12);opacity:.85}
.ubdline.same{color:var(--faint)}
.ubdsign{width:8px;flex:0 0 8px;color:var(--fg2)}
.ubfilehead{margin:8px 2px;font-size:11.5px;color:var(--faint)}
/* The command palette: every command the dialog has, filtered by typing. */
/* A sheet over the editor's own dialog: the palette, the template picker and the save review. Inside
   the box rather than a second modal, because app.js's `modal()` keeps one dialog at a time and a
   second one would close the editor behind it. */
.ubsheet{position:absolute;inset:0;z-index:5;background:rgba(5,9,14,.74);display:flex;align-items:flex-start;justify-content:center;padding:22px;overflow:auto;border-radius:inherit}
.ubsheetbox{background:var(--bg2);border:1px solid var(--border2);border-radius:12px;padding:16px;width:100%;max-width:980px;box-shadow:0 22px 60px rgba(0,0,0,.55)}
.ubpal{margin-bottom:8px}
.ubpallist{max-height:50vh;overflow:auto;border:1px solid var(--border);border-radius:10px;background:var(--bg2)}
.ubpalrow{display:grid;grid-template-columns:1fr auto;gap:2px 10px;align-items:center;padding:6px 10px;cursor:pointer;border-bottom:1px solid var(--border)}
.ubpalrow:last-child{border-bottom:0}
.ubpalrow.on{background:var(--accent-soft)}
.ubpalname{font-size:12.5px}
.ubpalkey{font-size:10.5px;color:var(--accent);font-family:ui-monospace,Menlo,Consolas,monospace}
.ubpalnote{grid-column:1 / -1;font-size:11px;color:var(--faint)}
.ubedit{position:absolute;z-index:3;font:inherit;font-family:inherit;background:#101722;color:var(--fg);border:1px solid var(--accent2);border-radius:5px;padding:1px 5px;min-width:220px}
.ubhint{font-size:11.5px;color:var(--fg2);margin:6px 2px}
/* The page's own fields, under the canvas rather than over it: a board is drawn, so the drawn thing
   comes first and the settings that shape it are the paper underneath. */
.ubbelow{margin-top:12px;border-top:1px solid var(--border);padding-top:10px}
.ubok{margin:6px 0;padding:6px 9px;border-radius:8px;background:rgba(90,200,120,.10);border:1px solid rgba(90,200,120,.35);font-size:12px}
.ubwarn{margin:6px 0;padding:6px 9px;border-radius:8px;background:rgba(230,180,80,.10);border:1px solid rgba(230,180,80,.35);font-size:12px}
.ubprobs{margin:4px 0 0}
.ubprob{font-size:11.5px;color:var(--fg2);padding:2px 0 2px 2px;border-left:2px solid rgba(230,180,80,.5);margin-bottom:3px;padding-left:8px;word-break:break-word}
.ubpath{color:var(--accent);margin-right:5px}
.ubinsp{margin-top:10px;padding:9px 10px;background:var(--bg2);border:1px solid var(--border);border-radius:10px}
.ubinsphead{font-size:12px;margin-bottom:6px}
.ubplaceholders{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin:6px 0 2px;font-size:11.5px}
.ubplaceholders .btn.sm{padding:2px 7px;font-size:11px}
.ubsample{display:flex;gap:8px;flex-wrap:wrap}
.ubsample label{display:flex;align-items:center;gap:4px;font-size:11px;color:var(--faint)}
.ubsample input{width:74px}
.ubmiss{color:#e0b060}
.ublayers{margin-top:4px}
.ublayer{display:flex;align-items:center;gap:8px;padding:4px 6px;border:1px solid transparent;border-radius:8px;cursor:pointer}
.ublayer:hover{background:var(--panel2)}
.ublayer.on{border-color:var(--border2);background:var(--panel2)}
.ublnum{font-size:11px;color:var(--faint);flex:0 0 30px}
.ubltype{font-size:10px;letter-spacing:.06em;text-transform:uppercase;flex:0 0 62px;color:#8ad}
.ublbits{font-size:11.5px;color:var(--fg2);flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ublines{margin-top:4px;background:#080c12;border:1px solid var(--border);border-radius:10px;padding:7px 9px;font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace;font-size:12px;overflow:auto}
.ubline{display:flex;gap:10px;white-space:pre}
.ubline.bad{background:rgba(230,120,90,.12)}
.ublno{flex:0 0 20px;text-align:right;color:var(--faint);font-size:10px}
.ublmine{flex:0 0 auto}
.ublsep{color:var(--faint)}
.ublserver{color:#9fb8a0}
/* The element list is the board's draw-order list, so it is **drawn** with the board's classes and
   only the two states a strip has but a board does not are added: the primary element (`.on`) and one
   the renderer refuses (`.bad`). Same rows, same columns, same buttons. */
.ublayer.bad{border-color:rgba(230,180,80,.45);background:rgba(230,180,80,.07)}
.ubltype.art{color:#9dc8ff}
.ubltype.text{color:var(--fg2)}
.ubltype.empty{color:#e0b060}

/* ---- strips: the pixel canvas ------------------------------------------------------------ */
/* The third canvas is not a grid of characters - it is **pixels**, so nothing here is monospace and
   the size of everything is derived from one variable: `--uhz` is how many screen pixels one HUD
   pixel occupies, written on the wrapper by the pane and read by every box, label and ruler tick
   below. That is the whole zoom: one number, and a strip that reads at 1x on a laptop reads at 4x
   when somebody is placing a picture against a frame's edge. */
/* The canvas is the biggest thing in the modal on purpose: a strip is 256 pixels wide and every
   decision on it is a decision about a few of them, so it gets the height and the width it needs
   rather than being sized to fit the window it happens to be in. */
/* The left padding is 30 px, and the lanes' id column is 8 + 14 + 8 = 30: both coordinate spaces
   start at the same place, so a picture at x 40 in the canvas is directly above the block at x 40 in
   the lane underneath it. The equality is asserted in the editor's suite - if one padding moves, the
   two views stop lining up and the suite says so rather than leaving it to the eye. */
.uhcanvas{position:relative;overflow:auto;background:#080c12;background-image:repeating-linear-gradient(0deg,rgba(255,255,255,.022) 0 1px,transparent 1px 10px),repeating-linear-gradient(90deg,rgba(255,255,255,.022) 0 1px,transparent 1px 10px);border:1px solid var(--border);border-radius:11px;padding:14px 16px 22px 30px;outline:none;min-height:min(46vh,460px);cursor:crosshair}
.uhcanvas:focus{border-color:var(--border2)}
/* `--uhz` is screen pixels per HUD pixel, written here by the pane: 4 by default, which is the
   smallest zoom a 12-pixel row can be read at. */
.uhwrap{--uhz:4px;position:relative;min-width:100%}
/* The ruler: ticked every 10 HUD pixels and labelled every 50, in the *page's* numbers, so `x: 40` is
   read off the canvas instead of counted. The label sits to the right of its own tick mark. */
.uhruler{position:relative;height:26px;border-bottom:1px solid var(--border);margin-bottom:8px}
.uhtick{position:absolute;bottom:0;width:1px;height:6px;background:var(--border2)}
.uhtick.big{width:auto;height:auto;background:none;border-left:1px solid var(--faint);padding-left:6px;font-size:12.5px;line-height:15px;font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace;color:var(--fg2)}
/* One band per line of the strip. A row is visible even when it is empty - an owner placing a second
   line needs to see where the second line is before anything is on it. */
.uhrow{position:relative;background:rgba(255,255,255,.016);border-bottom:1px solid rgba(255,255,255,.05)}
.uhrow:first-of-type{border-top:1px solid rgba(255,255,255,.05)}
.uhrow:hover{background:rgba(255,255,255,.03)}
/* An element: a picture is drawn at its own size (so its box *is* its size), a text element is a
   guessed box and says so by being dashed. */
/* A label is read, not measured, so it never shrinks below 12 px however far the zoom is pulled
   out - at 2x a name has to stay a name. */
.uhelm{position:absolute;top:50%;transform:translateY(-50%);box-sizing:border-box;display:flex;align-items:center;justify-content:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 4px;border-radius:6px;font-size:clamp(12px,calc(var(--uhz) * 4.2),19px);font-weight:500;line-height:1.2;letter-spacing:.01em;user-select:none;cursor:grab;color:var(--fg2)}
.uhelm:active{cursor:grabbing}
/* A picture is drawn at exactly its own size times the zoom, so nothing may take room *inside*
   its box: the visible frame is an `outline`, which takes no space in layout, and the padding is
   gone. With the ordinary 1px border and 4px padding the picture was scaled by 246/64 - a fraction
   - so `pixelated` had to make some source pixels 4 screen pixels wide and others 3. */
.uhelm.art{background:linear-gradient(180deg,rgba(120,180,255,.20),rgba(120,180,255,.08));padding:0;border:none;outline:1px solid rgba(120,180,255,.5);outline-offset:-1px;color:#cfe4ff}
.uhelm.text{background:rgba(255,255,255,.05);border:1px dashed rgba(255,255,255,.24);justify-content:flex-start;font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace}
.uhelm.in{border-color:var(--accent2)}
.uhelm.on{border-color:var(--accent2);box-shadow:0 0 0 2px var(--accent-soft)}
.uhelm.art.in{outline-color:var(--accent2)}
.uhelm.art.on{outline:2px solid var(--accent2);outline-offset:-2px}
/* An element that cannot be drawn sits at the cursor that refused it, hollow and labelled, rather than
   being listed somewhere off the canvas: the reason an element cannot be placed *is* where it lands. */
.uhelm.bad{background:rgba(230,180,80,.10);border:1px dashed rgba(230,180,80,.6);color:#e0b060;cursor:not-allowed;padding:1px 5px;width:auto;height:auto}
/* The picture itself, at the size it is drawn at and blown up whole-numbered, because that is the
   only way to see that a 20 px frame is 20 px. Pointer events are off: the element under it is the
   thing being dragged, and a drag that starts on the picture has to start the drag. */
.uhelm img{width:100%;height:100%;display:block;object-fit:fill;image-rendering:pixelated;pointer-events:none}
.uhelm.hid{opacity:.45;border-style:dashed}
.uhelm.hid img{opacity:.7}

/* ---- strips: the workspace ------------------------------------------------------------------ */
/* Tools down the left, canvas in the middle with its readouts, properties on the right behind tabs,
   and the state of the strip along the bottom. The point of the shape is that the picture, the
   numbers and the tools are on screen at the same time - which is what makes it an editor rather
   than a form with a preview in it. */
.uhapp{display:grid;grid-template-columns:46px minmax(0,1fr) 330px;gap:10px;align-items:start;margin-top:8px}
.uhrail{display:flex;flex-direction:column;gap:3px;padding:6px 5px;background:#0d1219;border:1px solid var(--border);border-radius:11px}
.uhrailsep{height:1px;background:var(--border);margin:4px 2px}
.uhrail .spacer{flex:1 1 auto}
.uhtool{display:flex;align-items:center;justify-content:center;width:34px;height:30px;font:inherit;font-size:14px;line-height:1;color:var(--fg2);background:transparent;border:1px solid transparent;border-radius:7px;cursor:pointer}
.uhtool:hover{background:#1a2432;color:var(--text);border-color:var(--border2)}
.uhtool.on{background:var(--accent-soft);border-color:var(--accent2);color:var(--accent)}
.uhmain{min-width:0;display:flex;flex-direction:column;gap:8px}
/* The head row: the zoom, what the cursor is over, and the strip's size. All three are numbers the
   file itself uses. */
.uhchead{display:flex;align-items:center;gap:10px;padding:3px 8px;background:#0b1017;border:1px solid var(--border);border-radius:9px;font-size:11.5px;color:var(--faint)}
.uhcc{display:inline-flex;gap:2px;align-items:center}
.uhcursor{font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace;font-variant-numeric:tabular-nums;color:var(--fg2);min-width:132px}
.uhsiz{font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace}
/* The lanes: one track per row, at the canvas's own scale, so a picture at x 40 sits over the lane at
   40 and the gap between two elements is a distance rather than a subtraction. */
.uhlanes{padding:8px 10px 10px 8px;background:#0a0e14;border:1px solid var(--border);border-radius:11px;overflow:auto}
.uhlaneshead{display:flex;justify-content:space-between;font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin-bottom:6px}
.uhlanescale{letter-spacing:.02em;text-transform:none}
.uhlane{display:flex;align-items:center;gap:8px;margin-top:4px}
/* `flex:0 0` on both, and that is not tidiness: a lane's track is as wide as the strip, which is
   usually wider than the panel, and a flex row shrinks its children to fit - the id column shrank
   from 14 px to 8 and the whole lane moved left by 5 px, out of line with the canvas above it. The
   track keeps its width and the panel scrolls instead. */
.uhlaneid{flex:0 0 14px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px;color:var(--faint);text-align:right}
/* An inset shadow rather than a border, for the same 1-pixel reason: a block is positioned from the
   track's padding box, so a 1 px border would put every lane block 1 px right of the element it
   belongs to. `box-shadow: inset` draws the same line and takes no room in the layout. */
.uhltrack{flex:0 0 auto;position:relative;height:26px;border-radius:6px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.05);background-image:repeating-linear-gradient(45deg,rgba(255,255,255,.018) 0 6px,transparent 6px 12px)}
.uhlblk{position:absolute;top:3px;bottom:3px;display:flex;align-items:center;padding:0 6px;border-radius:5px;font-size:11px;line-height:1.1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;box-sizing:border-box}
.uhlblk.art{background:rgba(120,180,255,.22);border:1px solid rgba(120,180,255,.5);color:#cfe4ff}
.uhlblk.text{background:rgba(255,255,255,.05);border:1px dashed rgba(255,255,255,.24);color:var(--fg2);font-family:ui-monospace,Menlo,Consolas,monospace}
.uhlblk.on{border-color:var(--accent2);box-shadow:0 0 0 2px var(--accent-soft)}
.uhlblk.in{border-color:var(--accent2)}
/* The right column's tabs. Three panes, one column: the inspector, the page's own numbers, and the
   palette plus the element list. */
.uhside{min-width:0;display:flex;flex-direction:column;gap:8px}
.uhtabs{display:flex;gap:2px;border-bottom:1px solid var(--border)}
.uhtab{font:inherit;font-size:12px;padding:6px 10px;background:none;border:none;border-bottom:2px solid transparent;color:var(--faint);cursor:pointer}
.uhtab:hover{color:var(--fg2)}
.uhtab.on{color:var(--accent);border-bottom-color:var(--accent2);font-weight:600}
.uhtabpane{display:none}
.uhtabpane.on{display:block}
.uhside .cfgbox{margin-bottom:0}
.uhside .uifields{grid-template-columns:1fr}
.uhside .upal{flex-direction:column;gap:6px}
.uhside .upalchips{max-height:none}
.uhside .cfgfield{margin-bottom:9px}
.uhside .note{font-size:11.5px}
/* The status bar: what the strip is, in the file's own numbers. */
.uhstatus{display:flex;align-items:center;gap:8px;margin-top:10px;padding:6px 10px;background:#0b1017;border:1px solid var(--border);border-radius:9px;font-size:11.5px;color:var(--fg2)}
.uhstatus .spacer{flex:1 1 auto}
.uhsep{margin:0 8px;color:#2b3846}
.uhst{font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
/* The menu pane's rail carries words rather than glyphs - select, paint, erase, pick are four
   different jobs and four unlabelled squares is a guessing game - so the column is wide enough for
   the longest of them and the grid is told the same number. */
.uhapp.withpens{grid-template-columns:88px minmax(0,1fr) 300px}
.uhrail.wide{padding:6px 7px}
.uhrailhead{font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);padding:2px 4px 5px}
.uhrail .uhtool.wide{width:100%;height:28px;justify-content:flex-start;padding:0 9px;font-size:12.5px}
.uhlayhead{display:flex;justify-content:space-between;align-items:center;font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);padding:2px 2px 6px}
/* One-shot actions laid out as a wrapping row: the label column is wide enough for a word, so the
   glyph buttons would otherwise each sit alone on their own line. */
.uhrow{display:flex;flex-wrap:wrap;gap:3px;padding:2px 0}
.uhrow .uhtool{width:30px;height:26px;flex:0 0 auto}

/* ---- the sidebar pane: a list of lines, so the list *is* the editor ---- */
.usidelines{display:flex;flex-direction:column;gap:3px;background:#0b1017;border:1px solid var(--border);border-radius:11px;padding:6px;max-height:46vh;overflow:auto}
.usideline{display:flex;align-items:center;gap:5px;flex-wrap:wrap;padding:4px 6px;border-radius:8px;background:rgba(255,255,255,.016);border:1px solid transparent}
.usideline:hover{background:rgba(255,255,255,.03)}
.usideline.on{border-color:var(--accent2);background:var(--accent-soft)}
.usideline.bad{border-color:rgba(230,120,90,.5)}
.usideline input.field{height:26px;font-size:12px}
.usidetext{flex:1 1 260px;min-width:150px;font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace}
.usidenode{flex:0 0 138px;font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace;font-size:11px}
.uhbit{width:26px;height:26px;flex:0 0 auto;font-size:12px;color:var(--faint)}
.uhbit:hover{color:var(--text)}
.usidebad{color:#e88a72}
/* The sidebar as a client stacks it: right-aligned lines over a dark plate, with the title above.
   It is deliberately not the ``.ublines`` monospace box - the whole point of this preview is that a
   holder of a sidebar reads right-aligned text, and a column of left-aligned monospace would not
   show a line that runs past the client's cut. */
.usidep{background:#0a0f16;border:1px solid var(--border);border-radius:10px;padding:10px 12px;font-size:13px;line-height:1.5;text-align:right;overflow:hidden}
.usideptitle{color:#fff;font-weight:700;text-align:center;border-bottom:1px solid rgba(255,255,255,.14);padding-bottom:4px;margin-bottom:5px;white-space:pre}
.usideprow{white-space:pre}
.usideprow.usideempty{color:var(--faint);font-style:italic}
.usidecheck{display:flex;flex-direction:column;gap:6px}

/* ---- the reference enchant library picker: hundreds of rows, so it scrolls and stays dense ---- */
.celib{max-height:52vh;overflow:auto;border:1px solid var(--border);border-radius:10px;background:#0b1017;padding:4px}
.celib .npcitem{margin:0 0 2px}
.celibrow{cursor:pointer}
.celibrow .npcn .ubkind{margin-left:8px}
.celibneed{color:var(--faint)}
.celibrow:hover .celibneed{color:#e0b462}
/* A `ready` row that this server applies *differently* from the reference it was read from: the same
   dim tone as a need, because it is the same kind of sentence - what the owner will not get - and one
   notch brighter, because the row does work. */
.celibnear{color:var(--faint);font-style:italic}
.celibrow:hover .celibnear{color:#8fb8e0}

@media(max-width:1180px){.uhapp.withpens{grid-template-columns:88px minmax(0,1fr)}}
@media(max-width:1180px){.uhapp{grid-template-columns:46px minmax(0,1fr)}.uhside{grid-column:1 / -1}}
/* The picture palette: one chip per PNG in the pack, with the size it will be drawn at - because that
   size *is* the element's width, and choosing between two frames is a decision about pixels. */
.uhchip{display:inline-flex;flex-direction:column;align-items:flex-start;gap:2px;font:inherit;font-size:12.5px;line-height:1.35;padding:5px 11px;border-radius:9px;border:1px solid var(--border);background:#0b1017;color:var(--dim);cursor:pointer;text-align:left;max-width:100%}
/* The picture in the palette, at its own size up to 28 px tall: choosing between two frames is a
   decision about pixels, and the name alone does not make it. */
.uhchip img{max-height:28px;max-width:100%;image-rendering:pixelated;display:block;opacity:.95}
.uhchip b{font-weight:600;color:var(--fg2)}
.uhchip span{font-size:10px;color:var(--faint);font-family:ui-monospace,Menlo,Consolas,monospace}
.uhchip:hover{border-color:var(--border2);color:var(--text)}
.uhchip.art b{color:#9dc8ff}
.uhchip.on{border-color:var(--accent2);background:var(--accent-soft);box-shadow:0 0 0 2px var(--accent-soft)}
.uhchip.on b{color:var(--accent)}
.uhchip.bad{opacity:.55;cursor:not-allowed}
.uhchip.bad span{color:#e0b060}
.uhchip.bad:hover{border-color:var(--border);color:var(--dim)}
/* The element inspector: the same box a block's is, with room on the header row for the reason an
   element refuses to draw. */
.uhinsp{margin-top:10px;padding:10px 11px;background:var(--bg2);border:1px solid var(--border);border-radius:10px}
.uhinsp .uifields{margin-top:2px}
.uhtitle{display:flex;align-items:baseline;gap:8px;margin:0 0 7px;font-size:12.5px;color:var(--text)}
.uhtitle b{color:var(--accent)}
.uhtitle .uhwarn{margin-left:auto;font-size:11.5px;text-align:right}
.uhwarn{color:#e0b060;font-weight:600}
/* The comparison. When this side and the server disagree the two layouts are printed side by side,
   because a preview that is merely self-consistent is the bug this pane exists to catch. */
.uhcmp{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:6px}
.uhcmp>div>b{display:block;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin:0 0 3px 2px}
.uhcmp pre{margin:0;padding:7px 9px;border-radius:9px;background:#080c12;border:1px solid var(--border);font-family:ui-monospace,Menlo,Consolas,"DejaVu Sans Mono",monospace;font-size:11px;line-height:1.45;white-space:pre-wrap;word-break:break-all;max-height:170px;overflow:auto}
@media(max-width:700px){.uhcmp{grid-template-columns:1fr}}
/* What the server's own check says about this strip, in its words. */
.uprobs{margin:8px 0;padding:8px 11px;border-radius:9px;background:rgba(230,180,80,.08);border:1px solid rgba(230,180,80,.35);font-size:12px}
.uprobs b{color:#e0b060}
.uprobs ul{margin:5px 0 0 16px;padding:0}
.uprobs li{margin:3px 0;color:var(--fg2)}
@media(max-width:1000px){.ubtplgrid{grid-template-columns:1fr}}
/* Cheat alerts: one line on the card, a compact list in the modal. The row is a grid
   so the columns line up without a table, and it wraps on a narrow screen rather
   than pushing a column off the edge. */
.galerts{display:flex;align-items:center;gap:8px;margin-top:9px;font-size:12.5px;color:var(--text);flex-wrap:wrap}
.galerts b{color:var(--amber);font-weight:600}
.gdot{width:7px;height:7px;border-radius:50%;flex:0 0 auto;box-shadow:0 0 0 3px rgba(0,0,0,.25)}
.gdot.warn{background:var(--amber)}
.gdot.ok{background:var(--accent)}
/* The database card, under the core plugin it belongs to.
   It is a sub-card rather than one more row of settings because the choice it holds is a different
   kind of thing: it has state (the plugin's own `db-link.txt`), a driver on disk and a database
   somewhere else, and an operator should see all three at once rather than infer them. The green
   edge is the one bit of colour, so "this server is on a database" reads at a glance from the
   plugin list without opening anything. */
.dbbox{margin-top:10px;padding:11px 13px;border:1px solid var(--border);border-radius:10px;background:var(--bg2)}
.dbbox.on{border-color:rgba(163,230,53,.3)}
.dbhd{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--text)}
.dbhd b{font-weight:600}
.dbbox code{font-family:var(--mono);font-size:11.5px;color:var(--accent2);background:var(--panel2);padding:1px 5px;border-radius:4px}
.dbbox .pgactions{margin-top:10px}
/* **The card's collapsed shape.** One line under the header and one press to open the rest: an
   owner who does not want a database should not have to read a driver path and a table prefix to
   find that out, and a server that *has* one still only needs its name on the way past. The
   details block holds the two group switches (the seven record groups and every group of data a
   module declared) and the plugin's own sentences about what each answer means. */
.dbbox .dbnote{margin-top:6px;color:var(--dim);font-size:12px;line-height:1.5}
.dbbox .dbnote code{background:transparent;padding:0}
.dbbox .dbmore{margin-top:8px}
.dbbox .dbmore>summary{cursor:pointer;list-style:none;display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;color:var(--dim);user-select:none}
.dbbox .dbmore>summary::-webkit-details-marker{display:none}
.dbbox .dbmore>summary::before{content:'▸';font-size:10px;opacity:.8}
.dbbox .dbmore[open]>summary::before{content:'▾'}
.dbbox .dbmore[open]>summary{margin-bottom:9px;color:var(--accent)}
.dbbox .dbsect{display:flex;align-items:baseline;gap:9px;margin:11px 0 6px;flex-wrap:wrap}
.dbbox .dbsect b{font-size:12.5px;font-weight:650;color:var(--text)}
.dbbox .dbsect .note{font-size:11.5px;color:var(--faint);line-height:1.45;flex:1 1 260px}
.dbbox .dbgroups{margin-top:0}
/* The per-group switch: one row per group of player records, each either this server's own database
   or the network's. The name and the columns are on every row because "playtime" is not the name of
   a column anywhere and the reader is deciding about a column set, not a chapter title. */
.dbgroups{margin-top:11px;border:1px solid var(--border);border-radius:9px;background:var(--bg2);overflow:hidden}
.dbghead{display:flex;align-items:center;gap:9px;padding:8px 11px;background:var(--panel2);border-bottom:1px solid var(--border);flex-wrap:wrap}
.dbghead label{margin:0}
.dbgrow{display:flex;align-items:center;gap:9px;padding:7px 11px;margin:0;border-top:1px solid rgba(255,255,255,.04);cursor:pointer;flex-wrap:wrap}
.dbgrow:first-of-type{border-top:0}
.dbgrow:hover{background:var(--panel2)}
.dbgrow input{width:auto;margin:0}
.dbgrow.on .dbgname{color:var(--accent)}
.dbgname{min-width:74px;font-weight:650;color:var(--text)}
.dbgcols{font-family:var(--mono);font-size:11px;color:var(--dim);background:var(--panel2);border-radius:4px;padding:1px 5px}
.dbgrow .note{margin-left:auto}
.dbgfoot{padding:8px 11px;border-top:1px solid var(--border);color:var(--faint);line-height:1.45}
@media(max-width:760px){.dbgname{min-width:0}.dbgrow .note{margin-left:0;width:100%}}
.glist{max-height:340px;overflow:auto;border:1px solid var(--border);border-radius:10px;background:var(--bg2)}
.grow{display:grid;grid-template-columns:52px minmax(90px,1fr) minmax(150px,1.4fr) 78px 74px;gap:10px;align-items:center;padding:8px 12px;border-bottom:1px solid var(--border);font-size:12.5px}
.grow:last-child{border-bottom:0}
.grow:hover{background:var(--panel2)}
.gtime{color:var(--faint);font-family:var(--mono)}
.gwho{color:var(--text);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gck{color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.gck.gcombat{color:var(--red)}
.gck.gmovement{color:var(--amber)}
.gck.gplayer{color:var(--violet2)}
.gvl{color:var(--dim);font-family:var(--mono);font-size:12px}
.gvl.over{color:var(--red)}
.gago{color:var(--faint);text-align:right}
@media(max-width:700px){.grow{grid-template-columns:52px 1fr 74px}.gck{grid-column:2/3}.gvl{display:none}}

/* The fishing view: the catalog and the leaderboard share one row shape, so the two
   tables read as the same list rather than two different screens. Rarity is carried by
   colour alone, in the module's own ascending order, so a glance down the left edge
   tells an owner where the rare end of the table is. */
.fhead{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin:14px 0 6px;font-size:12.5px;color:var(--text);font-weight:600}
.fhead .note{font-weight:400;text-align:right}
.fsep{display:flex;align-items:baseline;gap:8px;padding:7px 12px;background:var(--panel2);border-bottom:1px solid var(--border);font-size:11.5px}
.frow{display:grid;grid-template-columns:minmax(110px,1fr) 74px 70px 86px;gap:10px;align-items:center;padding:7px 12px;border-bottom:1px solid var(--border);font-size:12.5px}
.frow:last-child{border-bottom:0}
.frow:hover{background:var(--panel2)}
.frow.arow{grid-template-columns:30px minmax(110px,1fr) 74px 96px 96px}
.fname{color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.flvl{color:var(--dim)}
.frank{color:var(--faint);font-family:var(--mono);text-align:right}
.fnum{color:var(--dim);font-family:var(--mono);font-size:12px;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.frar{font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:.04em}
.frar.fr-common{color:var(--dim)}
.frar.fr-uncommon{color:var(--accent)}
.frar.fr-rare{color:var(--blue)}
.frar.fr-epic{color:var(--violet2)}
.frar.fr-legendary{color:var(--amber)}
.frar.fr-mythic{color:var(--red)}
/* Narrow screens drop one column per table and re-declare the grid to match. The two
   rules must stay in step: hiding a cell without removing its column leaves the row one
   cell short, and the last value wraps onto a line of its own - which reads as a broken
   row, not a compact one. */
.flist{max-height:min(54vh,460px)}
@media(max-width:700px){
  .frow{grid-template-columns:minmax(90px,1fr) 68px 86px}
  .frow>*:nth-child(3){display:none}
  .frow.arow{grid-template-columns:26px minmax(90px,1fr) 86px 92px}
}

/* A14: the dungeons page. A row here is a handful of inputs that belong to one entry, so they are
   laid out as a row that wraps rather than as one field per line - a loot table is read in columns
   (item, amount, weight, name) and a list of them is the point of the page. The moment tabs reuse
   the same shape as the module tabs above them, one rung smaller. */
.cfgtabs{display:flex;flex-wrap:wrap;gap:6px;margin:2px 0 10px}
.cfgtabs .tab{background:transparent;border:1px solid var(--border);border-radius:9px;color:var(--muted);
  cursor:pointer;font-size:12px;padding:4px 10px}
.cfgtabs .tab.on{border-color:var(--accent);color:var(--text)}
/* The dungeons page's preview: the picture the running server drew of a theme at a seed. The SVG is
   drawn by the plugin into a file and read back whole, so it scales with the pane rather than with a
   viewport - `max-width:100%` is what keeps a 40-cell map inside a form. */
.dunpreview{margin:2px 0 10px}
.dunprevhead{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-bottom:6px}
.dunpicwrap{border:1px solid var(--border);border-radius:12px;background:var(--bg2);padding:8px;
  overflow:auto;max-height:420px}
.dunpicwrap img{display:block;max-width:100%;height:auto;image-rendering:pixelated}
.dunmapcap{font-family:ui-monospace,monospace;font-size:11px;margin-top:6px;word-break:break-word}
/* A picture of a dungeon the form no longer describes. Amber, not red: nothing is broken, the page
   is just refusing to imply that the drawing answers a question it was not asked. */
.dunstale{border-left:2px solid #d8a657;padding:4px 8px;color:#e8c47a;margin-top:6px;font-size:12px}
.dunshipped{color:var(--accent)}
.dunrows{display:flex;flex-direction:column;gap:6px}
.dunrow{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.dunrow .field{flex:1 1 150px;min-width:110px}
.dunwave{border:1px solid var(--border);border-radius:10px;padding:8px;display:flex;flex-direction:column;gap:6px}
.dunwave .note{font-size:11px;color:var(--muted);display:flex;align-items:center;gap:5px}

/* The cooking page. Its template picker is the one list on the panel where a row is a *recipe*
   rather than a name: an owner choosing one has to see the ingredients and the payout, because two
   dishes with the same label differ in exactly what they take and pay. So a row is two columns - the
   description on the left, an Add button pinned right - and it scrolls at the same height as every
   other picker on the page. */
.ckbox{max-height:min(60vh,520px);overflow:auto;display:flex;flex-direction:column;gap:0}
.ckrow{display:flex;gap:10px;align-items:flex-start;padding:8px 10px;border-bottom:1px solid var(--border)}
.ckrow:last-child{border-bottom:0}
.ckrow:hover{background:var(--panel2)}
.ckl{flex:1 1 auto;min-width:0}
.ckl .npcn{font-size:12.5px}
.ckl .note{margin-top:2px}

/* ==========================================================================
   The Buddy page: the panel's own chat, as a page.
   The bubble (see above) is a strip of conversation in the corner of somebody
   else's page; this is the whole page, so it can afford the two things the
   bubble cannot: a column wide enough for a real answer, and a rail that says
   which model is answering and which ones are free. Nothing here is copied from
   the bubble's classes because the two are drawn at different sizes - a shared
   class would mean every bubble tweak silently resizing the page.
   ========================================================================== */
.budpage{display:flex;flex-direction:column;gap:8px;height:calc(100vh - 96px)}
.budhead{display:flex;align-items:flex-start;gap:14px;flex-wrap:wrap}
.budtitle h1{margin:0 0 2px}
.budstate{margin-left:auto;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.budcols{display:grid;grid-template-columns:minmax(0,1fr) 280px;gap:8px;min-height:0;flex:1}
.budcol-chat{display:flex;flex-direction:column;min-height:0;overflow:auto;background:var(--panel);
  border:1px solid var(--border);border-radius:12px;padding:10px}
.budlog{display:flex;flex-direction:column;gap:11px;flex:1}
.budempty{display:flex;flex-direction:column;align-items:flex-start;gap:6px;color:var(--dim);padding:18px 2px}
.budempty h2{margin:0;color:var(--text);font-size:17px}
.budmark{font-size:26px;color:var(--accent);line-height:1}
.budrow{display:flex;flex-direction:column;align-items:flex-start;gap:6px;max-width:100%}
.budrow.mine{align-items:flex-end}
.budmsg{font-size:13.5px;line-height:1.6;padding:11px 13px;border-radius:14px;background:var(--panel2);
  border:1px solid var(--border);max-width:min(760px,100%);word-break:break-word}
.budrow.mine .budmsg{background:color-mix(in srgb,var(--accent) 15%,transparent);
  border-color:color-mix(in srgb,var(--accent) 38%,transparent)}
.budmsg p{margin:0 0 8px}.budmsg p:last-child{margin-bottom:0}
.budmsg ul,.budmsg ol{margin:0 0 8px 19px;padding:0}
.budmsg code{font-family:var(--mono);font-size:12px;background:rgba(0,0,0,.35);border-radius:4px;padding:1px 4px}
.budmsg.budwork{color:var(--dim);display:flex;align-items:center;gap:8px}
.budmsg.buderr{background:var(--red-soft);border-color:color-mix(in srgb,var(--red) 40%,transparent);color:#ffd9dc}
.budmsg.budask{border-style:dashed}
/* The model's own reasoning: visible, because it is the interesting part, and
   closed by default, because an answer somebody is reading should not have to
   compete with the notes the model made while writing it. */
.budthink{margin:0 0 9px;border:1px solid var(--border);border-radius:9px;background:rgba(0,0,0,.22)}
.budthink summary{cursor:pointer;padding:6px 9px;font-size:11.5px;color:var(--dim);font-weight:600}
.budthink[open] summary{border-bottom:1px solid var(--border)}
.budthink pre{margin:0;padding:9px;max-height:300px;overflow:auto;white-space:pre-wrap;font-family:var(--mono);
  font-size:11px;line-height:1.5;color:var(--dim)}
.budsteps{margin:8px 0 0 18px;padding:0;font-size:12.5px;line-height:1.65;color:var(--dim)}
.budmeta{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:7px}
.budmeta .conf{font-size:10.5px;text-transform:uppercase;letter-spacing:.04em;color:var(--blue);
  background:rgba(116,183,255,.13);border-radius:5px;padding:2px 6px}
.budchips{display:flex;gap:6px;flex-wrap:wrap;padding:12px 0 0}
.budchip{background:var(--panel2);border:1px solid var(--border);color:var(--dim);font-size:12px;
  padding:6px 10px;border-radius:999px;cursor:pointer;text-align:left}
.budchip:hover{color:var(--text);border-color:var(--accent)}
.budchip.on{color:var(--text);border-color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent)}
.budchip.billed{color:var(--amber);border-color:color-mix(in srgb,var(--amber) 40%,transparent)}
.budchip[disabled]{opacity:.5;cursor:not-allowed}
.budchip .buddot{display:inline-block;width:5px;height:5px;border-radius:50%;background:var(--violet2);margin-left:6px;vertical-align:middle}
.budcomposer{display:flex;flex-direction:column;gap:7px;padding-top:13px;margin-top:13px;border-top:1px solid var(--border)}
.budtext{width:100%;resize:none;min-height:46px;max-height:200px;line-height:1.55}
.budsend{display:flex;align-items:center;gap:9px}
.budsend .note{margin-right:auto}
.budcol-rail{display:flex;flex-direction:column;gap:6px;min-height:0;overflow:auto}
.budcol-rail .panel{margin:0}
.budbig{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;margin-top:5px}
.budbig b{font-size:15px}
.budbig small{color:var(--dim);font-family:var(--mono);font-size:11px}
.budchips2{display:flex;gap:6px;flex-wrap:wrap;margin-top:9px}
.budtiers{display:flex;flex-direction:column;gap:6px;margin-top:9px}
.budtier{display:flex;flex-direction:column;gap:1px;text-align:left;background:var(--panel2);
  border:1px solid var(--border);border-radius:9px;padding:8px 10px;color:var(--dim)}
.budtier b{color:var(--text);font-size:12.5px}
.budtier small{font-size:11px}
.budtier:hover{border-color:var(--border2)}
.budtier.on{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 10%,transparent)}
.budtier[disabled]{opacity:.55;cursor:not-allowed}
.budthink summary span.note{font-weight:400}
@media (max-width:1080px){
  .budcols{grid-template-columns:minmax(0,1fr)}
  .budpage{height:auto}
  .budcol-chat{max-height:none}
}
/* A model the router has just rate limited: still clickable (pinning it is a decision
   somebody may make), but marked, so "why is a different model answering" is visible
   in the list rather than only in the answer's own note. */
.budchip.waiting{color:var(--amber);border-color:color-mix(in srgb,var(--amber) 35%,transparent)}
.budchip .budwait{font-style:normal;font-size:10px;margin-left:5px;opacity:.9}
.budswitch{font-size:11px;color:var(--amber);background:rgba(241,198,117,.12);border-radius:6px;padding:2px 7px}

/* ---- Buddy's hands (the assistant that can read and fix, see Hands.cs) -------------- */
.budsel { background:#0f1219; color:#cfd6e4; border:1px solid #2a3140; border-radius:7px;
  padding:6px 8px; font-size:12px; font-family:inherit; max-width:190px; }
.budsel:hover { border-color:#3a4356; }
.budbook { margin-top:6px; display:flex; flex-direction:column; gap:9px; }
.budbookrow { border-top:1px solid #232833; padding-top:7px; }
.budbookrow:first-child { border-top:0; padding-top:0; }
.budbookrow b { font-size:12px; }
.budbooklook { padding-left:9px; }
.budbookfix { padding-left:9px; color:#8fd6a4; }
.budfix { margin-top:10px; border:1px solid #2f3a4d; background:#111722; border-radius:9px; padding:10px 11px; }
.budfixtitle { font-weight:600; margin-bottom:4px; }
.budfixwhere { margin-top:6px; font-size:12px; }
.budfixwhere code { background:#0c1017; border:1px solid #242b38; border-radius:5px; padding:1px 5px; }
.budfixwhere s { color:#8b93a5; }
.budfixwhere b { color:#bfe6c9; }
.budworklog { margin-top:10px; border:1px solid #2a3140; border-radius:9px; padding:8px 10px; background:#0f1320; }
.budworklog summary { cursor:pointer; font-size:12px; color:#9aa4b8; }
.budstep { margin-top:7px; font-size:12px; display:flex; gap:7px; }
.budstepkind { min-width:88px; color:#9aa4b8; }
.budstep.ok .budstepkind { color:#8fd6a4; }
.budstep.bad .budstepkind { color:#e0a3a3; }
.budsteptext { white-space:pre-wrap; word-break:break-word; color:#cfd6e4; }
.note.ok { color:#8fd6a4; }

/* Unified secondary navigation and editor surfaces. */
body .cfgtabs{gap:8px;padding:7px;border:1px solid var(--border);border-radius:14px;background:var(--bg2)}
body .cfgtabs .tab{min-height:40px;padding:8px 14px;border-color:transparent;border-radius:9px;color:var(--dim);font-weight:600}
body .cfgtabs .tab:hover{background:var(--panel2);color:var(--text)}
body .cfgtabs .tab.on{background:var(--accent-soft);border-color:var(--accent2);color:var(--accent)}
body .dunwave,body .budfix,body .budworklog{border:1px solid var(--border);border-radius:14px;background:var(--bg2);padding:14px}
body .dunrows{gap:12px}
body .dunrow{gap:10px}
body .ckrow{padding:14px;gap:14px;transition:background .16s ease}
body .budsel{min-height:40px;background:var(--bg2);color:var(--text);border:1px solid var(--border);border-radius:10px;padding:8px 12px}
body .budcol-chat{background:linear-gradient(150deg,var(--panel),var(--bg2));border-radius:18px}
body .budchip{min-height:36px;padding:8px 12px}
body .budtier{padding:12px;border-radius:12px}
body .budworklog summary,body .budstepkind{color:var(--dim)}
body .budsteptext{color:var(--text)}
body :is(button,a,input,select,textarea,summary):focus-visible{outline:2px solid var(--accent);outline-offset:3px}
body :is(.tabs button,.cfgtabs .tab,.budchip,.budtier):disabled{opacity:.45;cursor:not-allowed}
@media(min-width:1081px){body .budpage{height:calc(100dvh - 190px);min-height:400px}}
@media(max-width:600px){
  body .cfgtabs{flex-wrap:nowrap;overflow-x:auto;max-width:100%;scroll-padding-inline:7px}
  body .cfgtabs .tab{flex:0 0 auto;white-space:nowrap}
  body .budstate{margin-left:0;width:100%}
  body .budsend{flex-wrap:wrap}
  body .budsel{max-width:100%;width:100%}
  body .dunrow .field{min-width:0;flex-basis:100%}
}
/* Final shared workspace treatment: dashboard, nested settings and editors. */
body{background:radial-gradient(ellipse at 0 0,#123044 0,transparent 45%),radial-gradient(ellipse at 100% 100%,#201635 0,transparent 45%),var(--bg)}
body #topbar{border-bottom:1px solid var(--border);box-shadow:0 4px 24px #0002}
body #side .brandcopy b{letter-spacing:-.05em;color:var(--text)}
body #nav a{border:1px solid transparent;transition:background .16s,border-color .16s,color .16s}
body #nav a.on{border-color:#386074;color:var(--accent)}
body :is(.tabs,.cfgtabs){margin-bottom:10px}
body :is(.tabs button,.cfgtabs .tab){display:inline-flex;align-items:center;justify-content:center;gap:8px}
body :is(.cfgbox,.netblk,.renewbox){margin-block:14px}
body :is(.cfgbox,.netblk,.renewbox)>summary{letter-spacing:-.015em}
body :is(.cfgbox,.netblk,.renewbox)>summary::before{border:1px solid #386074;box-shadow:0 0 14px #a78bfa0a}
body :is(.cfgbox,.netblk,.renewbox)[open]{background:linear-gradient(135deg,#14283a,#0c1626);border-color:#386074}
body .cfgtabs .tab.on{background:linear-gradient(115deg,#153a48,#262541);box-shadow:inset 0 1px 0 #ffffff12,0 3px 12px #0003;color:#b6f6ff}
body .fsbar{gap:10px;box-shadow:inset 0 1px 0 #ffffff06}
body :is(.consbox,textarea.editor){border-top:2px solid #386074;background:#070f1b;color:#d7e7fa}
body .statbar{font-variant-numeric:tabular-nums;color:var(--dim)}
body .statbar b{color:var(--text)}
body .panel table th{font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--faint);border-bottom:1px solid var(--border)}
body .panel table td{vertical-align:middle}
body .modal{border-top-width:2px}
body .modalfoot{padding-top:18px;border-top:1px solid var(--border);margin-top:22px}
body .empty{color:var(--dim);line-height:1.7}
body :is(.uisrail,.uisside,.budcol-chat){box-shadow:inset 0 1px 0 #ffffff08}
body :is(.budfix,.budworklog,.dunwave){background:var(--tool-surface)}
body .budchip{border:1px solid var(--border);border-radius:10px;background:var(--bg2);color:var(--dim)}
@media(hover:hover){
  body #nav a:hover{border-color:var(--border)}
  body :is(.budchip,.cfgtabs .tab):not(:disabled):hover{border-color:#386074;background:#173044;color:var(--text)}
}
@media(max-width:600px){
  body :is(.tabs,.cfgtabs){margin-bottom:18px}
  body :is(.cfgbox,.netblk,.renewbox){padding:14px}
  body .fsbar{flex-wrap:wrap}
}
@media(prefers-reduced-motion:reduce){body .ckrow{transition:none}}


/* Compact health pop-out and keep every sidebar link reachable. */
.healthpop .popbox{right:0;left:auto;min-width:min(520px,92vw);max-height:min(70vh,640px);overflow:auto}
.healthboxwrap .healthhead{margin:0 0 6px;gap:8px}
.hlist{display:flex;flex-direction:column;gap:2px;margin-top:6px}
.hitem{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:2px 8px;padding:5px 6px;border-radius:8px;border:1px solid transparent}
.hitem .hrow{display:flex;align-items:center;gap:6px;font-size:12px}
.hitem .hline{grid-column:1/-1;color:var(--dim);font-size:11px;line-height:1.3}
.hitem .hfix{grid-column:1/-1;color:var(--faint);font-size:11px;line-height:1.35}
.hitem .hact{justify-self:end}
.hbar{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:8px;background:rgba(16,10,26,.45);font-size:12px}
.hbarcounts{margin-left:auto;color:var(--faint)}
body .sidecallout{padding:6px 8px;margin:4px 0 0}
body #side .navlabel-spaced{margin-top:4px}
body #side .brand{padding:2px 4px 4px}
body #view{padding-bottom:56px}

.mailgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:8px;align-items:start}
.mailgrid>.panel{margin:0}
.mailgrid>.mailspan{grid-column:1/-1}
.mailgrid>details.mailcard>summary{cursor:pointer;font-weight:700;font-size:13px;list-style:none;display:flex;align-items:center;gap:8px}
.mailgrid>details.mailcard>summary::-webkit-details-marker{display:none}
.mailgrid>details.mailcard>summary::before{content:'▸';color:var(--accent);width:12px}
.mailgrid>details.mailcard[open]>summary::before{content:'▾'}
.mailgrid>details.mailcard[open]{grid-column:span 2}
@media(max-width:700px){.mailgrid>details.mailcard[open]{grid-column:1/-1}}

.modgrid>#modCatalog,.modgrid>#modChat,.modgrid>#modEvents,.agtgrid>#agFeed,.agtgrid>#agEndpoint{grid-column:1/-1}

/* ============================ This installation ============================
   The Settings card about the panel itself, and a REPORT rather than a set of jobs: installing,
   updating and removing the program all happen in its own Windows wizard, so the only control
   here is the one that opens that window. It wears the brand - the mark in its tile, the
   wordmark, the state in a corner pill - because it is the one card that talks about the program
   rather than about the servers it runs, and below that it is two path lines and two buttons.
   Every colour is a palette variable, so all of the themes carry it. */
.insthero{position:relative;display:flex;align-items:center;gap:13px;flex-wrap:wrap;margin:0 0 10px;padding:11px 14px;border:1px solid var(--border);border-radius:14px;overflow:hidden;background:radial-gradient(125% 190% at 0% 0%,var(--accent-soft),transparent 58%),radial-gradient(125% 190% at 100% 100%,var(--violet-soft),transparent 58%),linear-gradient(145deg,rgba(19,32,50,.9),rgba(9,17,28,.9))}
.insthero::before{content:'';position:absolute;inset:0 28px auto;height:2px;background:linear-gradient(90deg,transparent,var(--accent2),var(--violet),transparent)}
.instlogo{position:relative;display:grid;place-items:center;width:84px;height:50px;flex-shrink:0;border:1px solid var(--border);border-radius:11px;background:linear-gradient(160deg,rgba(5,11,19,.75),rgba(5,11,19,.25));box-shadow:inset 0 1px 0 #ffffff0d}
.instlogo::before{content:'';position:absolute;inset:0 12px auto;height:2px;border-radius:2px;background:linear-gradient(90deg,var(--accent2),var(--violet))}
.instlogo svg{width:62px;height:37px}
.instwho{display:flex;flex-direction:column;gap:3px;min-width:0}
.instword{font-size:21px;line-height:1;font-weight:800;letter-spacing:-.045em}
.instword i{font-style:normal;color:var(--accent)}
.instword b{color:var(--violet)}
.insttag{color:var(--faint);font-size:9px;letter-spacing:.17em;font-weight:700}
.instside{display:flex;flex-direction:column;align-items:flex-end;gap:4px;margin-left:auto;text-align:right;min-width:0}
.instbuild{color:var(--faint);font-size:10px;letter-spacing:.04em;font-family:var(--mono)}
/* Where the program and its data live: the question the old card answered with a table of every
   folder that could hold data, answered with two lines and the folder's own numbers on them. */
.instpath{display:grid;grid-template-columns:74px minmax(0,1fr);align-items:baseline;gap:2px 10px;margin:0 0 6px;padding:9px 12px;border:1px solid var(--border);border-radius:11px;background:linear-gradient(145deg,rgba(11,20,33,.55),rgba(8,15,26,.35))}
.instpath .k{color:var(--faint);font-size:9.5px;font-weight:750;letter-spacing:.13em;text-transform:uppercase}
.instpath .mono{font-family:var(--mono);font-size:11.5px;color:var(--text);word-break:break-all;min-width:0}
/* The caption lives under its path in the same column: an `auto` third track sizes to the
   caption's max-content during track sizing and starves the path to nothing (measured at 25px). */
.instpath .sub{grid-column:2;color:var(--dim);font-size:11px;line-height:1.4}
.instsay{color:var(--dim);font-size:12px}
.instsay .mono{color:var(--text)}
.instsay.warn{color:var(--amber)}
.instsay.warn .mono{color:var(--amber)}
