/* FST design for the portal (Portal 1.49.0): one look for the admin panel, the public site and the sign-in pages, the same
   as the FST client's (FST-DESIGN.md): dark only, layered greys, square borderless tiles with a faint shadow, controls with a
   6 px radius and a 1 px border, Geist for words and Geist Mono for numbers (400 and 700 only), UPPERCASE button captions, no
   hover transitions, thin scroll bars. The tokens below are FST's :root block (its names and values) without the ones the
   portal does not read; the rest are the components the portal uses, built from them. site.css holds the portal's own
   layout (the shell, the sidebar, the pages).
   Portal 1.50.33: the loader of a grid that turns in place (.grid-busy, .grid-leaving, .is-busy; loaders section below).
   Portal 1.50.35: the same loader on a Mail pane that is no list (a message, a form page: .mail-view, .mail-form).
   Portal 1.50.36: the same loader on the whole content area of a page that the shell swaps in (.page-busy, .page-leaving on .admin-main).
   Strict CSP: no inline styles anywhere; the data: images in this file are the only images it draws. */
@font-face { font-family: "Geist"; src: url("../fonts/Geist-Variable.woff2") format("woff2"); font-weight: 100 900; font-display: swap; }
@font-face { font-family: "Geist Mono"; src: url("../fonts/GeistMono-Variable.woff2") format("woff2"); font-weight: 100 900; font-display: swap; }

:root {
  color-scheme: dark;

  /* ---- 1.1 page and surfaces: the layer stack (UiTheme.cs:30-37) ---- */
  --fst-bg: #0C1015;            /* page behind every tile; title band; content dialogs */
  --fst-sidebar: #0D1117;       /* sidebar; grid header band; journal rows; splash */
  --fst-surface: #11161D;       /* tiles, cards, message boxes, grid cells, popup bodies */
  --fst-raised: #171D26;        /* buttons, inputs, switch track, tooltip, toast, small cells on a card */
  --fst-hover: #1D2530;         /* hover plate; loader track */
  --fst-selected: #1D2B42;      /* selected row, pressed button, active sidebar item */
  --fst-disabled-bg: #12171E;   /* disabled control fill */
  --fst-log: #080A0E;           /* log / output boxes, account history tree ground */
  --fst-day-row: #10151C;       /* history tree: day row */
  --fst-trade-area: #0A0D12;    /* history tree: trade rows */
  --fst-trade-header: #0F141A;  /* history tree: trade block header */

  /* ---- 1.2 lines ---- */
  --fst-border: #242C37;          /* hairline between tile rows, sidebar dividers */
  --fst-border-strong: #2E3845;   /* control border at rest, tooltip frame, scroll thumb */
  --fst-border-hover: #3E4A5A;    /* hover border of Neutral button and input; scroll thumb hover */
  --fst-border-disabled: #202832; /* disabled control border */
  --fst-grid-line: #1C232C;       /* hairline under grid rows, under a card header */
  --fst-check-hover-outline: #4A5665;

  /* ---- 1.3 text ---- */
  --fst-text: #E8EDF3;            /* primary */
  --fst-text-2: #B7C0CC;          /* secondary */
  --fst-text-muted: #8A94A3;      /* captions, hints */
  --fst-text-disabled: #5B6573;   /* disabled, placeholder, footer e-mail */
  --fst-on-accent: #07101F;       /* ink on an accent fill; tick in a checked box; badge digits */
  --fst-disabled-glyph: #46505E;

  /* ---- 1.4 accent (blue) ---- */
  --fst-accent: #4F8CFF;
  --fst-accent-hover: #6A9DFF;
  --fst-accent-pressed: #3E76E0;
  --fst-accent-soft: #1D2F52;     /* selected segment fill, LIVE tag fill */
  --fst-accent-border: #345696;   /* switch thumb border, LIVE tag border, trade block bar */
  --fst-link: #6EA2FF;
  --fst-link-hover: #96BCFF;

  /* ---- 1.5 meaning colours ---- */
  --fst-positive: #2FD28A;        /* profit, long, running, Primary button ink */
  --fst-negative: #FF6B6B;        /* loss, short, error */
  --fst-warning: #F5B54A;         /* warning, Stop button ink */
  --fst-danger: #FF8A8A;          /* Danger button ink */

  /* ---- 1.12 derived (computed from the code's blends; opaque) ---- */
  /* Portal 1.50.54: the green, amber and red buttons keep their role colour on hover and press. Rest: raised fill + faint border + dimmed caption
     (raised + .18 / .85 x (ink - raised)). Hover: the same fill, border at raised + .45 x (ink - raised), full ink caption. Pressed: --fst-surface fill, full ink border and caption. */
  --fst-btn-primary-edge-rest: #1B3E38;  --fst-btn-primary-ink-rest: #2BB77B;  --fst-btn-primary-edge-hover: #226E53;
  --fst-logo-dot: var(--fst-btn-primary-ink-rest);   /* the logo dot = the Primary button's ink at rest (Yan, 5 Nov); not --fst-positive */
  --fst-btn-stop-edge-rest: #3F382C;     --fst-btn-stop-ink-rest: #D49E45;     --fst-btn-stop-edge-hover: #7B6136;
  --fst-btn-danger-edge-rest: #413138;   --fst-btn-danger-ink-rest: #DC7A7B;   --fst-btn-danger-edge-hover: #7F4E53;
  --fst-sidebar-glyph-rest: var(--fst-text-muted);   /* portal 1.50.51: the sidebar's rest glyph is the muted grey */
  --fst-bell-glyph: #688FD8;      /* the notification preview's bell (FST's own colour) */
  --fst-sidebar-glyph-hover: #6B9DFD;
  --fst-chip-rest: #111926;       /* sidebar icon chip: accent at alpha 16 over the plate */
  --fst-chip-hover: #223047;      /* alpha 28 */
  --fst-chip-active: #253B61;     /* alpha 42 */
  --fst-active-line: #2B4676;     /* active item's bottom line: accent at alpha 70 */
  --fst-glow-line: #90B6FF;       /* the line under the wordmark */

  /* ---- 1.11 alphas ---- */
  --fst-veil: rgba(0, 0, 0, .35);              /* behind a modal window */
  --fst-scroll-wash: rgba(255, 255, 255, .055);/* scroll track while the pointer is on the bar */
  --fst-sh-near: rgba(0, 0, 0, .298);          /* tile shadow strips (7.5) */
  --fst-sh-far: rgba(0, 0, 0, .125);
  --fst-sh-side: rgba(0, 0, 0, .173);

  /* ---- 1.8-1.10 badges, banners, chrome ---- */
  --fst-tag-session-ink: #8DB3FF;                                    /* line: --fst-accent-border */
  --fst-banner-red-bg: #2E1517; --fst-banner-red-ink: #F7C3C3;
  --fst-amber-bg: #2A2214;  --fst-amber-line: #5A4520;
  --fst-amber-text: #D9CBB3;
  --fst-avatar-from: #6FA3FF;   --fst-avatar-to: #2B5FD0;  --fst-avatar-ink: #EAF1FF;

  /* ---- 2 typography (pt x 4/3; Regular 400 and Bold 700 only) ---- */
  --fst-font: "Geist", "Segoe UI", system-ui, -apple-system, sans-serif;
  --fst-mono: "Geist Mono", Consolas, ui-monospace, "SFMono-Regular", monospace;
  --fs-badge: 8.67px;     /* 6.5 pt  bell badge digits */
  --fs-tag: 9px;          /* 6.75 pt tags, pills, ticker tag */
  --fs-tiny: 10px;        /* 7.5 pt  card small caption, update chip text, splash version */
  --fs-caption: 11px;     /* 8.25 pt grid header, tile caption, sidebar version/e-mail */
  --fs-switch: 11.33px;   /* 8.5 pt  switch caption (Mono Bold), grid cell text */
  --fs-small: 11.67px;    /* 8.75 pt tooltip, grid numbers, card body */
  --fs-body: 12px;        /* 9 pt    button caption, check box, list item, toast */
  --fs-nav: 12.33px;      /* 9.25 pt sidebar caption */
  --fs-input: 12.67px;    /* 9.5 pt  input text, message-box text */
  --fs-value: 13px;       /* 9.75 pt card title, metric value */
  --fs-dialog-title: 17.33px; /* 13 pt */

  /* ---- 3 shape ---- */
  --fst-r-btn: 6px;       /* button, input, combo, refresh button */
  --fst-r-track: 7px;     /* switch track */
  --fst-r-pill: 5px;      /* switch pill, hover plate, status pill (min(5, h/2)), pager tile */
  --fst-r-seg: 4px;       /* segment button, check box, chips, icon button, basket toggle */
  --fst-r-tag: 3px;       /* journal tag, basket lit half */

  /* ---- 4 spacing and sizes ---- */
  --fst-h-btn: 32px;  --fst-h-btn-dialog: 28px;  --fst-h-input: 30px;  --fst-h-switch: 28px;  --fst-h-pill: 22px;  --fst-h-small: 26px;
  --fst-h-th: 32px;   --fst-h-row: 28px;    --fst-h-row-trades: 30px;
  --fst-w-sidebar: 179px;         /* computed from the widest caption (4.6); today's value */
  --fst-w-sidebar-collapsed: 84px;
  --fst-min-w-btn: 82px;  --fst-min-w-btn-role: 84px;  --fst-min-w-btn-dialog: 88px;

  /* ---- 6 motion (curves are exact: x linear, y the FST polynomial; section 0.4) ---- */
  --fst-ease-out-cubic: cubic-bezier(.333, 1, .667, 1);
  --fst-ease-out-quad: cubic-bezier(.333, .667, .667, 1);
  --fst-ease-in-quad: cubic-bezier(.333, 0, .667, .333);
  --fst-ease-smooth: cubic-bezier(.333, 0, .667, 1);
  --fst-ease-inout-cubic: cubic-bezier(.645, .045, .355, 1);   /* approximation */
  --fst-ease-inout-quad: cubic-bezier(.455, .03, .515, .955);  /* approximation */
  --fst-dur-switch: 140ms;   --fst-dur-sidebar: 240ms;
  --fst-dur-handle-hover: 120ms;  --fst-dur-handle-turn: 160ms;
  --fst-dur-open: 160ms;     --fst-dur-close: 280ms;
  --fst-dur-toast-in: 140ms;  --fst-dur-toast-out: 150ms;
  --fst-dur-cover-delay: 150ms; --fst-dur-cover-in: 120ms; --fst-dur-cover-out: 150ms;
  --fst-dur-sweep: 1200ms;

  /* ---- PROPOSAL: shadows for web-only floating things (FST has the OS draw them) ---- */
  --fst-shadow-dialog: 0 8px 24px rgba(0, 0, 0, .45), 0 0 0 1px rgba(0, 0, 0, .2);
  --fst-shadow-menu: 0 4px 12px rgba(0, 0, 0, .4);
}

/* ======================================================================== base (7.2) */
*, *::before, *::after { box-sizing: border-box; }
html { background: var(--fst-bg); -webkit-text-size-adjust: 100%; }
body { margin: 0; background: var(--fst-bg); color: var(--fst-text); font: 400 var(--fs-body)/1.3 var(--fst-font); font-kerning: none; font-variant-ligatures: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: var(--fst-link); text-decoration: none; }
a:hover { color: var(--fst-link-hover); }
img { display: block; max-width: 100%; }
h1, h2, h3, h4 { margin: 0; font-weight: 700; }
p { margin: 0 0 .8em; }
hr { border: 0; height: 1px; margin: 0; background: var(--fst-border); }
code { font-family: var(--fst-mono); font-size: 11.67px; padding: 0 5px; background: var(--fst-raised); border-radius: var(--fst-r-tag); }
:focus-visible { outline: 2px solid var(--fst-accent); outline-offset: 2px; }
::selection { background: var(--fst-accent-soft); color: var(--fst-text); }
[hidden] { display: none !important; }
.muted { color: var(--fst-text-muted); }
.mono { font-family: var(--fst-mono); }
.pos { color: var(--fst-positive); }
.neg { color: var(--fst-negative); }
.nowrap { white-space: nowrap; }
.sr { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden; }
.hp { position: absolute; left: -5000px; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: .01ms !important; transition-delay: 0s !important; animation-duration: .01ms !important;
    animation-delay: 0s !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
}

/* ======================================================================== thin scroll bars (7.10) */
::-webkit-scrollbar { width: 14px; height: 14px; background: transparent; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-track:hover { background: var(--fst-scroll-wash); }
::-webkit-scrollbar-thumb { min-height: 24px; min-width: 24px; background: var(--fst-border-strong); background-clip: padding-box; border-radius: 999px; border: solid transparent; border-width: 0 2px 0 6px; }
::-webkit-scrollbar-thumb:horizontal { border-width: 6px 0 2px 0; }
::-webkit-scrollbar-thumb:hover { background: var(--fst-border-hover); border-width: 0 2px; }
::-webkit-scrollbar-thumb:horizontal:hover { border-width: 2px 0; }
::-webkit-scrollbar-thumb:active { background: var(--fst-accent); }
::-webkit-scrollbar-corner { background: transparent; }
@supports not selector(::-webkit-scrollbar) { * { scrollbar-width: thin; scrollbar-color: var(--fst-border-strong) transparent; } }

/* ======================================================================== buttons by role (7.4)
   Neutral is the plain look (.btn.ghost); the plain .btn is Primary (a dark tile with a green caption: go, save, add - and the main action
   of a dialog, of a sign-in card and of the public form); .warn is Stop, .danger Danger. No button is filled with the accent colour.
   A caption is UPPERCASE. No transition. */
.btn {
  --ink: var(--fst-text); --fill: var(--fst-raised); --edge: var(--fst-border-strong); --fill-hover: var(--fst-hover); --edge-hover: var(--fst-border-hover); --fill-press: var(--fst-selected); --edge-press: var(--fst-border-hover); --ink-rest: var(--ink);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; flex: none;
  height: var(--fst-h-btn); min-width: var(--fst-min-w-btn); margin: 0; padding: 0 12px;
  background: var(--fill); border: 1px solid var(--edge); border-radius: var(--fst-r-btn);
  color: var(--ink-rest); font: 700 var(--fs-switch)/1 var(--fst-mono); text-transform: uppercase; white-space: nowrap; text-decoration: none; cursor: pointer;
}
.btn:hover { background: var(--fill-hover); border-color: var(--edge-hover); color: var(--ink); }
.btn:active { background: var(--fill-press); border-color: var(--edge-press); color: var(--ink); }
.btn:focus-visible { border-color: var(--fst-accent); outline: none; }
.btn:not(.ghost):not(.danger):not(.warn) { --ink: var(--fst-positive); --ink-rest: var(--fst-btn-primary-ink-rest); --fill: var(--fst-raised); --edge: var(--fst-btn-primary-edge-rest); --fill-hover: var(--fst-raised); --edge-hover: var(--fst-btn-primary-edge-hover); --fill-press: var(--fst-surface); --edge-press: var(--ink); min-width: var(--fst-min-w-btn-role); }
.btn.warn { --ink: var(--fst-warning); --ink-rest: var(--fst-btn-stop-ink-rest); --fill: var(--fst-raised); --edge: var(--fst-btn-stop-edge-rest); --fill-hover: var(--fst-raised); --edge-hover: var(--fst-btn-stop-edge-hover); --fill-press: var(--fst-surface); --edge-press: var(--ink); min-width: var(--fst-min-w-btn-role); }
.btn.danger { --ink: var(--fst-danger); --ink-rest: var(--fst-btn-danger-ink-rest); --fill: var(--fst-raised); --edge: var(--fst-btn-danger-edge-rest); --fill-hover: var(--fst-raised); --edge-hover: var(--fst-btn-danger-edge-hover); --fill-press: var(--fst-surface); --edge-press: var(--ink); min-width: var(--fst-min-w-btn-role); }
.btn.small { height: var(--fst-h-small); min-width: 0; padding: 0 10px; border-radius: var(--fst-r-pill); }
.btn.block { width: 100%; }
.dialog :is(.modal-actions, .gf-actions, .msg-btns) .btn:is(.btn) { height: var(--fst-h-btn-dialog); min-width: var(--fst-min-w-btn); padding: 0 12px; }
/* Clear next to a filter switch, and a ghost that deletes: the red tile of Danger (portal 1.50.52; was a red outline on no fill) */
.grid-bar > .btn.ghost, form[data-confirm] .btn.ghost {
  --ink: var(--fst-danger); 
  --ink-rest: var(--fst-btn-danger-ink-rest); --fill: var(--fst-raised); --edge: var(--fst-btn-danger-edge-rest); 
  --fill-hover: var(--fst-raised); --edge-hover: var(--fst-btn-danger-edge-hover); --fill-press: var(--fst-surface); --edge-press: var(--ink);
}
.grid-bar > .btn.ghost { height: var(--fst-h-small); min-width: 0; padding: 0 10px; border-radius: var(--fst-r-pill); font: 700 var(--fs-switch)/1 var(--fst-mono); }
.btn:disabled, .btn[aria-disabled="true"] { background: var(--fst-disabled-bg); border-color: var(--fst-border-disabled); color: var(--fst-text-disabled); cursor: default; }
form.inline { display: inline; }

/* ======================================================================== inputs, drop-downs, check boxes (7.7) */
label { display: block; margin: 0 0 4px; color: var(--fst-text-2); font: 400 var(--fs-body)/1.3 var(--fst-font); }
:where(input[type=text], input[type=email], input[type=password], input[type=tel], input[type=date], input[type=number], input[type=search], input[type=url], input:not([type])), :where(select), :where(textarea) {
  width: 100%; height: var(--fst-h-input); margin: 0; padding: 0 4px 0 9px;
  background: var(--fst-raised); border: 1px solid var(--fst-border-strong); border-radius: var(--fst-r-btn);
  color: var(--fst-text); font: 400 var(--fs-input)/1 var(--fst-font); outline: none;
}
:where(input, select, textarea):hover { border-color: var(--fst-border-hover); }
:where(input, select, textarea):focus { border-color: var(--fst-accent); }
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: none; }
:where(input, textarea)::placeholder { color: var(--fst-text-disabled); }
:where(input, select, textarea):disabled { background: var(--fst-disabled-bg); border-color: var(--fst-border-disabled); color: var(--fst-text-disabled); }
:where(textarea) { height: auto; min-height: 96px; padding: 7px 9px; line-height: 1.4; resize: vertical; }
:where(input[type=date]) { color-scheme: dark; padding-right: 8px; }
:where(select) {
  appearance: none; -webkit-appearance: none; padding-right: 22px; cursor: pointer; color-scheme: dark;
  background: var(--fst-raised) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='7' height='4.5' viewBox='0 0 7 4.5'><path d='M0 0h7L3.5 4.5z' fill='%238A94A3'/></svg>") no-repeat right 7.5px center;
}
:where(select):hover { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='7' height='4.5' viewBox='0 0 7 4.5'><path d='M0 0h7L3.5 4.5z' fill='%23E8EDF3'/></svg>"); }
:where(select):disabled { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='7' height='4.5' viewBox='0 0 7 4.5'><path d='M0 0h7L3.5 4.5z' fill='%235B6573'/></svg>"); }
option { background: var(--fst-raised); color: var(--fst-text); }
input[type=file] { width: auto; height: var(--fst-h-input); padding: 3px 6px; color: var(--fst-text-2); font: 400 var(--fs-switch)/1 var(--fst-font); }
input[type=file]::file-selector-button { margin-right: 10px; height: 22px; padding: 0 10px; border: 1px solid var(--fst-border-strong); border-radius: var(--fst-r-pill);
  background: var(--fst-hover); color: var(--fst-text); font: 700 var(--fs-switch)/1 var(--fst-mono); text-transform: uppercase; cursor: pointer; }
input[type=checkbox] {
  appearance: none; -webkit-appearance: none; flex: none; width: 16px; height: 16px; margin: 0; vertical-align: middle; cursor: pointer;
  background: var(--fst-raised); border: 1px solid var(--fst-border-strong); border-radius: var(--fst-r-seg);
}
input[type=checkbox]:hover, input[type=checkbox]:focus-visible { border-color: var(--fst-check-hover-outline); outline: none; }
input[type=checkbox]:checked {
  background: var(--fst-accent) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M3.84 8.32 6.88 11.2 12.16 5.12' fill='none' stroke='%2307101F' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>") center / 16px 16px no-repeat;
  border-color: var(--fst-accent);
}
input[type=checkbox]:disabled { background-color: var(--fst-disabled-bg); border-color: var(--fst-text-disabled); cursor: default; }
.check { display: flex; align-items: center; gap: 8px; min-height: 22px; margin: 0; color: var(--fst-text-2); font: 400 var(--fs-body)/1.3 var(--fst-font); cursor: pointer; }
.check:has(> input:disabled) { color: var(--fst-text-disabled); cursor: default; }
.field { margin-bottom: 12px; }
.field .err, .form-err { margin-top: 4px; color: var(--fst-negative); font: 400 var(--fs-caption)/1.3 var(--fst-font); }
.form-err { margin: 0 0 12px; }
.field:has(.err) :where(input, select, textarea) { border-color: var(--fst-negative); }
.field:has(.err) :where(input, select, textarea):focus { border-color: var(--fst-accent); }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }

/* ======================================================================== segment switch: filters, tabs, two-way toggles (7.6)
   One rounded track, a pill that slides to the picked segment (140 ms, ease-out cubic), the picked caption in the accent. Portal 1.50.19: the captions are in capitals, as FST's (Geist Mono Bold). */
.seg, .tabs, .dtabs, .dseg {
  --n: 2; --i: 0;
  position: relative; display: inline-grid; grid-auto-flow: column; grid-auto-columns: minmax(52px, 1fr); align-items: stretch; flex: none;
  height: var(--fst-h-switch); margin: 0; padding: 2px;
  background: var(--fst-raised); border: 1px solid var(--fst-border-strong); border-radius: var(--fst-r-track);
}
.seg::before, .tabs::before, .dtabs::before, .dseg::before {
  content: ""; position: absolute; top: 2px; bottom: 2px; left: 2px; width: calc((100% - 4px) / var(--n));
  background: var(--fst-accent-soft); border: 1px solid var(--fst-accent-border); border-radius: var(--fst-r-pill);
  transform: translateX(calc(var(--i) * 100%)); transition: transform var(--fst-dur-switch) var(--fst-ease-out-cubic); pointer-events: none;
}
.seg:has(> :nth-child(3):last-child), .tabs:has(> :nth-child(3):last-child), .dtabs:has(> :nth-child(3):last-child), .dseg:has(> :nth-child(3):last-child) { --n: 3; }
.seg:has(> :nth-child(4):last-child), .tabs:has(> :nth-child(4):last-child) { --n: 4; }
.seg:has(> :nth-child(5):last-child), .tabs:has(> :nth-child(5):last-child) { --n: 5; }
.seg:has(> :nth-child(6):last-child), .tabs:has(> :nth-child(6):last-child) { --n: 6; }
.tabs:has(> :nth-child(7):last-child) { --n: 7; }
.tabs:has(> :nth-child(8):last-child) { --n: 8; }
.seg:has(> :nth-child(2) input:checked), .dseg:has(> :nth-child(2) input:checked), .tabs:has(> :nth-child(2)[aria-selected="true"]) { --i: 1; }
.seg:has(> :nth-child(3) input:checked), .dseg:has(> :nth-child(3) input:checked), .tabs:has(> :nth-child(3)[aria-selected="true"]) { --i: 2; }
.seg:has(> :nth-child(4) input:checked), .tabs:has(> :nth-child(4)[aria-selected="true"]) { --i: 3; }
.seg:has(> :nth-child(5) input:checked), .tabs:has(> :nth-child(5)[aria-selected="true"]) { --i: 4; }
.seg:has(> :nth-child(6) input:checked), .tabs:has(> :nth-child(6)[aria-selected="true"]) { --i: 5; }
.tabs:has(> :nth-child(7)[aria-selected="true"]) { --i: 6; }
.tabs:has(> :nth-child(8)[aria-selected="true"]) { --i: 7; }
.seg:has(:focus-visible), .tabs:has(:focus-visible), .dseg:has(:focus-visible) { border-color: var(--fst-accent); }
.seg-o, .tabs > a, .tabs > button, .dtab, .dseg-o {
  position: relative; display: flex; align-items: center; justify-content: center; gap: 6px; margin: 0; padding: 0 12px;
  border: 0; border-radius: var(--fst-r-pill); background: none; color: var(--fst-text-2);
  font: 700 var(--fs-switch)/1 var(--fst-mono); text-transform: uppercase; white-space: nowrap; text-decoration: none; cursor: pointer; user-select: none;
}
.seg-o input, .dseg-o input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; pointer-events: none; }
.seg-o:hover, .tabs > a:hover, .tabs > button:hover, .dtab:hover, .dseg-o:hover { background: var(--fst-hover); color: var(--fst-text); }
.seg-o:has(input:checked), .dseg-o:has(input:checked), .tabs > [aria-selected="true"] { background: none; color: var(--fst-accent); }
.seg-o:has(input:checked):hover, .dseg-o:has(input:checked):hover, .tabs > [aria-selected="true"]:hover { background: none; color: var(--fst-accent); }
.seg-o > span, .dseg-o > span { display: inline-flex; align-items: center; gap: 6px; }
.seg-o .n, .tabs > a .n { color: var(--fst-text-muted); font-weight: 400; }
.seg-o:has(input:checked) .n, .tabs > [aria-selected="true"] .n { color: var(--fst-accent); }
/* the tabs of a page: a narrow window scrolls the strip, never the page */
.tabs-scroll { align-self: flex-start; max-width: 100%; overflow-x: auto; scrollbar-width: none; }
.tabs-scroll > .tabs { min-width: max-content; }
.tabs-scroll::-webkit-scrollbar { display: none; }

/* ======================================================================== tiles, cards, metric tiles (7.5)
   A tile is square and has no border: a fill one step above the page and a faint shadow of three 1 px strips on the bottom and
   right edges. It needs 2 px of free space at its right and below (the 8 px gap is enough). */
.panel, .card, .metric, .tile { position: relative; background: var(--fst-surface); border: 0; border-radius: 0; min-width: 0; }
.panel::after, .card::after, .metric::after, .tile::after {
  content: ""; position: absolute; inset: 0 -1px -2px 0; pointer-events: none;
  background:
    linear-gradient(var(--fst-sh-near), var(--fst-sh-near)) 1px calc(100% - 1px) / calc(100% - 1px) 1px no-repeat,
    linear-gradient(var(--fst-sh-far), var(--fst-sh-far)) 2px 100% / calc(100% - 2px) 1px no-repeat,
    linear-gradient(var(--fst-sh-side), var(--fst-sh-side)) 100% 2px / 1px calc(100% - 4px) no-repeat;
}
.card { padding: 16px 18px; }
.panel-head { position: relative; display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; min-height: 48px; padding: 8px 16px; }
.panel-head::after { content: ""; position: absolute; left: 1px; right: 2px; bottom: 0; height: 1px; background: var(--fst-grid-line); }
.panel-head h2 { font: 700 var(--fs-value)/1.3 var(--fst-font); text-transform: uppercase; color: var(--fst-text); white-space: nowrap; margin: 0 4px 0 0; }
.panel-head .meta { color: var(--fst-warning); font-size: var(--fs-switch); }   /* a status line: amber, as the desktop app's warning colour (portal 1.50.42) */
.panel-head .end, .panel-foot .end { margin-left: auto; display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; font-size: var(--fs-switch); }
.panel-body { padding: 12px 16px; }
.panel-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 14px; min-height: 40px; padding: 6px 16px; border-top: 1px solid var(--fst-grid-line); }
.empty { padding: 28px 16px; color: var(--fst-text-muted); font: 700 var(--fs-small)/1.3 var(--fst-font); text-align: center; }
.hint { margin: 8px 0 0; color: var(--fst-text-muted); font-size: var(--fs-caption); }
.hint.status { color: var(--fst-warning); }   /* a status line of the Parameters page (portal 1.50.42); a plain .hint stays muted */
.cap { margin: 0 0 6px; color: var(--fst-text-muted); font: 700 var(--fs-caption)/1.3 var(--fst-font); text-transform: uppercase; }
.metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(168px, 100%), 1fr)); gap: 8px; }
.metric { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; min-width: 120px; padding: 9px 15px 10px; text-align: center; }
.metric .k { color: var(--fst-text-muted); font: 700 var(--fs-tiny)/1.3 var(--fst-font); text-transform: uppercase; white-space: nowrap; }
.metric .v { color: var(--fst-text-2); font: 700 17.33px/1.25 var(--fst-mono); white-space: nowrap; }
.metric .v.amber { color: var(--fst-warning); }
.metric .v.pos { color: var(--fst-positive); }
.metric .v.neg { color: var(--fst-negative); }
.metric .s { color: var(--fst-text-muted); font: 400 var(--fs-tiny)/1.3 var(--fst-font); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.metric .v .s { display: inline; margin-left: 4px; }
.metric .s a { color: var(--fst-link); }

/* ======================================================================== grids: tables (7.8)
   Centred columns, a header band, one hairline under every row, no hover highlight. The header and a totals foot stay put while
   the rows scroll (sticky). */
.tbl-wrap { overflow: auto; }
table.tbl { width: 100%; border-collapse: separate; border-spacing: 0; }
.tbl th, .tbl td { height: var(--fst-h-row); padding: 0 8px; border-bottom: 1px solid var(--fst-grid-line); text-align: center; vertical-align: middle; white-space: nowrap;
  font: 400 var(--fs-switch)/1.25 var(--fst-font); color: var(--fst-text-2); }
.tbl thead th { position: sticky; top: 0; z-index: 2; height: var(--fst-h-th); background: var(--fst-sidebar); border-bottom: 0; color: var(--fst-text-muted); font: 700 var(--fs-caption)/1.2 var(--fst-font); }
.tbl tbody tr:last-child td { border-bottom: 0; }
.tbl th.l, .tbl td.l { text-align: left; }
.tbl thead th.sort.l > a { justify-content: flex-start; }
.tbl .num, .tbl td.mono { font-family: var(--fst-mono); font-size: var(--fs-small); }
.tbl td.wrap-text { white-space: normal; min-width: 220px; max-width: 460px; }
.tbl .sub { display: block; color: var(--fst-text-muted); font-size: var(--fs-caption); line-height: 1.3; }
/* Portal 1.50.38: a cell with a second line (.sub right in the cell) has 4 px above and below, as .user-cell has (a cell has none otherwise, so a
   two-line cell was as high as its text and touched the header and the row line). The user cell keeps its own 4 px: its .sub is inside it. */
.tbl tbody td:has(> .sub) { padding-top: 4px; padding-bottom: 4px; }
.tbl strong { font-weight: 700; color: var(--fst-text); }
.tbl td a strong { color: inherit; }
.tbl .actions, .row-actions { display: flex; justify-content: center; align-items: center; gap: 8px; }
.tbl td.act { width: 1%; }
.tbl tr.dim td { opacity: .55; }
.tbl .amber { color: var(--fst-warning); }
.tbl .pos, .tbl .pos strong { color: var(--fst-positive); }
.tbl .neg, .tbl .neg strong { color: var(--fst-negative); }
.tbl tfoot { position: sticky; bottom: 0; z-index: 2; }
.tbl tfoot td { height: 40px; background: var(--fst-surface); border-top: 1px solid var(--fst-grid-line); border-bottom: 0; color: var(--fst-text); font-weight: 700; }
.tbl tfoot tr + tr td { height: 34px; border-top-color: var(--fst-grid-line); }
.clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.35; }
/* a head that sorts: the arrow of the sorted column is an accent triangle */
.tbl thead th.sort, .xt-head .sort { padding: 0; }
.tbl thead th.sort > a, .xt-head .sort > a { display: flex; align-items: center; justify-content: center; gap: 6px; height: 100%; min-height: var(--fst-h-th); padding: 0 8px; color: inherit; text-decoration: none; }
.tbl thead th.sort > a:hover, .xt-head .sort > a:hover, .tbl thead th.sort.on > a, .xt-head .sort.on > a { color: var(--fst-text); }
.sort.on > a::after { content: ""; flex: none; width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; }
.sort.on.asc > a::after { border-bottom: 5px solid var(--fst-accent); }
.sort.on.desc > a::after { border-top: 5px solid var(--fst-accent); }

/* ======================================================================== pills, tags, dots (7.16) */
.pill {
  --c: var(--fst-text-2); display: inline-flex; align-items: center; justify-content: center; gap: 5px; height: 17px; padding: 0 6px; vertical-align: middle;
  border: 1px solid var(--fst-border-strong); border-radius: 5px; background: var(--fst-raised); color: var(--c);
  font: 700 var(--fs-tag)/1 var(--fst-mono); text-transform: uppercase; white-space: nowrap;
}
.pill.green, .pill.red, .pill.amber { background: color-mix(in srgb, var(--c) 8.63%, transparent); border-color: color-mix(in srgb, var(--c) 35.3%, transparent); }
.pill.green { --c: var(--fst-positive); }
.pill.red { --c: var(--fst-negative); }
.pill.amber { --c: var(--fst-warning); }
.pill.blue { --c: var(--fst-accent); background: var(--fst-accent-soft); border-color: var(--fst-accent-border); }
.tk { display: inline-block; color: var(--fst-text); font: 700 var(--fs-small)/1.2 var(--fst-mono); vertical-align: middle; }
.tks { display: inline-flex; flex-wrap: wrap; gap: 4px 8px; justify-content: center; }
.chips { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.chip { display: inline-flex; align-items: center; height: 17px; padding: 0 6px; border: 1px solid var(--fst-accent-border); border-radius: var(--fst-r-seg);
  background: var(--fst-accent-soft); color: var(--fst-tag-session-ink); font: 700 var(--fs-tag)/1 var(--fst-mono); white-space: nowrap; }
.chip.plain { border-color: var(--fst-border-strong); background: var(--fst-raised); color: var(--fst-text-2); }
.chip s, .prow s { color: var(--fst-text-muted); margin-left: 5px; }
.presence { display: inline-flex; align-items: center; gap: 8px; color: var(--fst-text-muted); font: 700 var(--fs-tag)/1 var(--fst-mono); text-transform: uppercase; vertical-align: middle; }
.presence.on { color: var(--fst-positive); }
.presence .dot { --c: var(--fst-positive); flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--c); box-shadow: 0 0 0 3px color-mix(in srgb, var(--c) 15.7%, transparent); }
/* Portal 1.50.18: a status badge right after text on the same line (no space between them in the markup) stands 8 px off it; the ONLINE dot's
   3 px halo counts as the badge, so its dot sits 11 px off. Flex rows (page heads, panel heads) keep their own gap. */
.gap-before { margin-left: 8px; }
.presence.on.gap-before { margin-left: 11px; }
.avatar { display: inline-grid; place-items: center; flex: none; border-radius: 50%; object-fit: cover; color: var(--fst-avatar-ink); font-weight: 700;
  background: linear-gradient(145deg, var(--fst-avatar-from), var(--fst-avatar-to)); box-shadow: 0 0 0 1px var(--fst-accent-border); }
.avatar.sm { width: 28px; height: 28px; font-size: 11px; }
.avatar.lg { width: 44px; height: 44px; font-size: 16px; }
/* Portal 1.50.23: 4 px above and below the cell, so the 28 px avatars of two rows no longer touch (the row was as high as its avatar). */
.user-cell { display: flex; align-items: center; justify-content: flex-start; gap: 10px; min-width: 0; padding: 4px 0; text-align: left; }

/* ======================================================================== pager and page size (7.17, 5.16) */
.grid-foot { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 16px; min-height: 40px; padding: 4px 16px; border-top: 1px solid var(--fst-grid-line); background: var(--fst-surface); color: var(--fst-text-muted); font-size: var(--fs-caption); }
.grid-foot .total { color: var(--fst-text-2); font: 700 var(--fs-switch)/1 var(--fst-mono); white-space: nowrap; }
.pager { display: inline-flex; align-items: center; gap: 2px; height: 32px; padding: 2px; background: var(--fst-raised); border: 1px solid var(--fst-border-strong); border-radius: 0; }
.per { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.per > .muted { color: var(--fst-text-muted); font-size: var(--fs-caption); }
.per > .pg, .per > a.pg { min-width: 30px; }
.pg { display: inline-flex; align-items: center; justify-content: center; min-width: 28px; height: 26px; padding: 0 6px; border: 1px solid transparent; border-radius: var(--fst-r-pill);
  color: var(--fst-text-2); font: 700 var(--fs-switch)/1 var(--fst-mono); text-decoration: none; }
a.pg:hover { background: var(--fst-hover); color: var(--fst-text); }
a.pg:active { background: var(--fst-selected); }
.pg.cur { background: var(--fst-accent-soft); border-color: var(--fst-accent-border); color: var(--fst-accent); }
.pg.gap { min-width: 16px; padding: 0; color: var(--fst-text-muted); }
.pg.off { opacity: 1; }
/* the four arrows are the first two and the last two children: FST's tile, 28 x 26, accent outline while enabled */
.pager > .pg:nth-child(-n+2), .pager > .pg:nth-last-child(-n+2) { width: 28px; min-width: 28px; padding: 0; font-size: 0; border-color: var(--fst-accent); background: var(--fst-raised); color: var(--fst-text); }
.pager > a.pg:nth-child(-n+2):hover, .pager > a.pg:nth-last-child(-n+2):hover { background: var(--fst-hover); }
.pager > span.pg.off:nth-child(-n+2), .pager > span.pg.off:nth-last-child(-n+2) { background: var(--fst-disabled-bg); border-color: var(--fst-border-strong); color: var(--fst-disabled-glyph); }
.pager > .pg:nth-child(-n+2)::before, .pager > .pg:nth-last-child(-n+2)::before { content: ""; width: 7px; height: 11px; background: currentColor;
  -webkit-mask: var(--arrow) center / contain no-repeat; mask: var(--arrow) center / contain no-repeat; }
.pager > .pg:nth-child(2)::before { --arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 7 11'><path d='M7 0 0 5.5 7 11z'/></svg>"); }
.pager > .pg:nth-last-child(2)::before { --arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 7 11'><path d='M0 0 7 5.5 0 11z'/></svg>"); }
.pager > .pg:nth-child(1)::before { width: 12px; --arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 11'><path d='M6 0 0 5.5 6 11zM12 0 6 5.5 12 11z'/></svg>"); }
.pager > .pg:nth-last-child(1)::before { width: 12px; --arrow: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 11'><path d='M0 0 6 5.5 0 11zM6 0 12 5.5 6 11z'/></svg>"); }

/* ======================================================================== tooltips (5.10), dialogs (7.14), toast (7.15) */
[title] { cursor: help; }
a[title], button[title], summary[title] { cursor: pointer; }
.dialog { padding: 0; border: 0; border-radius: 0; background: var(--fst-surface); color: var(--fst-text); width: min(640px, calc(100vw - 24px)); max-height: calc(100vh - 24px); box-shadow: var(--fst-shadow-dialog); overflow: auto; }
.dialog[open] { animation: fst-open var(--fst-dur-open) var(--fst-ease-out-cubic); }
.dialog[open]::backdrop { background: var(--fst-veil); animation: fst-fade-in-0 var(--fst-dur-open) var(--fst-ease-out-cubic); }
.dialog.is-closing { animation: fst-close var(--fst-dur-close) var(--fst-ease-in-quad) forwards; }
.dialog.is-closing::backdrop { animation: fst-fade-out var(--fst-dur-close) var(--fst-ease-in-quad) forwards; }
@keyframes fst-open { from { opacity: 0; transform: translateY(8px); } }
@keyframes fst-close { to { opacity: 0; transform: translateY(4px); } }
@keyframes fst-fade-in-0 { from { opacity: 0; } }
@keyframes fst-fade-out { to { opacity: 0; } }
@keyframes fst-fade-in { to { opacity: 1; } }
/* a message box: the question of a form that asks before it does (static/js/ui.js) */
.msgbox { width: min(520px, calc(100vw - 24px)); padding: 16px; }
.msg { display: grid; grid-template-columns: 30px minmax(0, 1fr); column-gap: 14px; }
.msg-icon { --tone: var(--fst-accent); width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; color: var(--tone); font: 700 16px/1 var(--fst-font);
  background: color-mix(in srgb, var(--tone) 14.9%, transparent); box-shadow: inset 0 0 0 1.2px color-mix(in srgb, var(--tone) 58.8%, transparent); }
.msg-icon.error { --tone: var(--fst-negative); }
.msg-icon.warning { --tone: var(--fst-warning); }
.msg-text { font: 400 var(--fs-input)/1.4 var(--fst-font); color: var(--fst-text); overflow-wrap: anywhere; }
.msg-btns { grid-column: 1 / -1; display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.toasts { position: fixed; z-index: 1100; left: 50%; top: 14px; translate: -50% 0; display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none; max-width: calc(100vw - 24px); }
.toast { display: flex; align-items: center; gap: 7px; min-height: 30px; padding: 6px 14px 6px 12px; background: var(--fst-raised); border-radius: 8px; color: var(--fst-text);
  font: 700 var(--fs-body)/1.3 var(--fst-font); opacity: 0; transition: opacity var(--fst-dur-toast-in) var(--fst-ease-out-cubic); max-width: min(560px, calc(100vw - 24px)); }
.toast.is-in { opacity: 1; }
.toast.is-out { opacity: 0; transition: opacity var(--fst-dur-toast-out) var(--fst-ease-in-quad); }
.toast-ok { --tone: var(--fst-positive); flex: none; width: 15px; height: 15px; border-radius: 50%; background: color-mix(in srgb, var(--tone) 18%, var(--fst-raised)); display: grid; place-items: center; }
.toast-ok svg { width: 15px; height: 15px; fill: none; stroke: var(--tone); stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.toast.warn .toast-ok { --tone: var(--fst-warning); }
.toast.error .toast-ok { --tone: var(--fst-negative); }

/* ======================================================================== loaders (7.11, 7.12): a slim line, a cover, a skeleton */
.loadline {
  --h: 4px; --clip: 2px; --s1: 1.2px; --s2: 2.4px; --s3: 3.6px; --a1: .165; --a2: .118; --a3: .071;
  container-type: inline-size; position: relative; height: var(--h); border-radius: 999px; background: var(--fst-hover); clip-path: inset(calc(var(--clip) * -1) 0);
}
.loadline > i { position: absolute; left: 0; top: 0; height: 100%; width: min(50%, 50px); border-radius: 999px; background: var(--fst-accent);
  box-shadow: 0 0 0 var(--s1) rgb(79 140 255 / var(--a1)), 0 0 0 var(--s2) rgb(79 140 255 / var(--a2)), 0 0 0 var(--s3) rgb(79 140 255 / var(--a3));
  animation: fst-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite; }
@keyframes fst-sweep { from { transform: translateX(-100%); } to { transform: translateX(100cqw); } }
.loadline.cover-line { --clip: 6px; --h: 3px; --s1: 1.6px; --s2: 3.2px; --s3: 4.8px; --a1: .196; --a2: .141; --a3: .086; width: 150px; }
@media (prefers-reduced-motion: reduce) { .loadline > i { animation: none; left: 50%; translate: -50% 0; } }
.skel { position: relative; overflow: hidden; min-height: 12px; background: var(--fst-raised); opacity: 0; animation: fst-fade-in var(--fst-dur-cover-in) var(--fst-ease-out-quad) var(--fst-dur-cover-delay) forwards; }
.skel::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, rgb(79 140 255 / .08) 50%, transparent);
  animation: fst-skel-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite; }
@keyframes fst-skel-sweep { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skel::after { animation: none; } }
/* Portal 1.50.33: the loader of a grid that turns in place (static/js/grid.js). The box of the grid (or a group's slot, or the list pane of a swap region
   such as Mail's) carries .grid-busy from the click, and the control that was clicked carries .is-busy. The rows dim to .45 (the sticky totals stay solid:
   they hide the rows that pass under them) and a soft shimmer (the skeleton's) sweeps behind them, the accent line of .loadline sweeps along the top of
   the table just under its head, the table and the pager take no clicks, and the control shows a ring in place of its label. Nothing changes size: the
   ring and the line are drawn over what is there. grid.js holds it at least 300 ms after the click, then swaps .grid-busy for .grid-leaving, which fades
   it (--fst-dur-cover-out). */
.grid-busy { cursor: progress; }
.grid-busy .tbl-wrap, .grid-busy .xt, .grid-busy .grid-foot { pointer-events: none; }
.grid-busy .tbl-wrap, .grid-busy .xt {
  background: linear-gradient(90deg, transparent 33.3%, rgb(79 140 255 / .08) 50%, transparent 66.7%) 100% 0 / 300% 100% no-repeat;
  animation: fst-grid-shimmer var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite;
}
.grid-busy tbody, .grid-busy .xt-row {
  opacity: .45; animation: fst-grid-dim var(--fst-dur-cover-in) var(--fst-ease-out-quad);
}
.grid-leaving tbody, .grid-leaving .xt-row {
  animation: fst-grid-undim var(--fst-dur-cover-out) var(--fst-ease-in-quad);
}
.grid-busy .tbl-wrap::before, .grid-busy .xt::before, .grid-leaving .tbl-wrap::before, .grid-leaving .xt::before {
  content: ""; display: block; position: sticky; z-index: 3; left: 0; top: var(--grid-line-top, var(--fst-h-th)); width: 100%; height: 2px; margin-bottom: -2px; pointer-events: none;
  background: linear-gradient(var(--fst-accent), var(--fst-accent)) 0 0 / min(50%, 50px) 100% no-repeat, var(--fst-hover);
  animation: fst-grid-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite, fst-fade-in-0 var(--fst-dur-cover-in) var(--fst-ease-out-quad);
}
.grid-leaving .tbl-wrap::before, .grid-leaving .xt::before {
  animation: fst-grid-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite, fst-fade-out var(--fst-dur-cover-out) var(--fst-ease-in-quad) forwards;
}
/* a list with nothing in it has no rows to dim: its words dim and the line runs along its top */
.grid-busy .empty { position: relative; color: color-mix(in srgb, var(--fst-text-muted) 45%, transparent); }
.grid-busy .empty::before, .grid-leaving .empty::before {
  content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 2px; pointer-events: none;
  background: linear-gradient(var(--fst-accent), var(--fst-accent)) 0 0 / min(50%, 50px) 100% no-repeat, var(--fst-hover);
  animation: fst-grid-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite, fst-fade-in-0 var(--fst-dur-cover-in) var(--fst-ease-out-quad);
}
.grid-leaving .empty::before { animation: fst-grid-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite, fst-fade-out var(--fst-dur-cover-out) var(--fst-ease-in-quad) forwards; }
/* Portal 1.50.35: a pane of the Mail layout that has no rows to dim (a message, compose, the settings, a template): its whole content dims, the shimmer sweeps
   behind it and the accent line runs along the top of the pane - the same loader, over what is there, nothing changes size. */
.mail-view.grid-busy, .mail-form.grid-busy { cursor: progress;
  background-image: linear-gradient(90deg, transparent 33.3%, rgb(79 140 255 / .08) 50%, transparent 66.7%); background-size: 300% 100%; background-repeat: no-repeat;
  background-position: 100% 0;
  animation: fst-grid-shimmer var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite; }
.mail-view.grid-busy > *, .mail-form.grid-busy > * { opacity: .45; pointer-events: none; animation: fst-grid-dim var(--fst-dur-cover-in) var(--fst-ease-out-quad); }
.mail-view.grid-leaving > *, .mail-form.grid-leaving > * { animation: fst-grid-undim var(--fst-dur-cover-out) var(--fst-ease-in-quad); }
.mail-view.grid-busy::before, .mail-view.grid-leaving::before, .mail-form.grid-busy::before, .mail-form.grid-leaving::before {
  content: ""; display: block; flex: none; position: sticky; z-index: 3; left: 0; top: 0; width: 100%; height: 2px; margin-bottom: -2px; pointer-events: none;
  background: linear-gradient(var(--fst-accent), var(--fst-accent)) 0 0 / min(50%, 50px) 100% no-repeat, var(--fst-hover);
  animation: fst-grid-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite, fst-fade-in-0 var(--fst-dur-cover-in) var(--fst-ease-out-quad);
}
.mail-view.grid-leaving::before, .mail-form.grid-leaving::before {
  animation: fst-grid-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite, fst-fade-out var(--fst-dur-cover-out) var(--fst-ease-in-quad) forwards;
}
.tbl-wrap:has(> table.mx) { --grid-line-top: 0px; }   /* the matrix has two head rows: its line lies on the top edge */
/* the rows of an opened group inside a busy box are dimmed with the group: no second dimming, no second line */
.grid-busy .xt-body .tbl-wrap { background: none; animation: none; }
.grid-busy .xt-body .tbl-wrap::before { content: none; }
.grid-busy .xt-body tbody { opacity: 1; animation: none; }
.is-busy { position: relative; color: transparent !important; }
.sort.on > a.is-busy::after { border-bottom-color: transparent !important; border-top-color: transparent !important; }
.pg.is-busy::after, .sort > a.is-busy::before, .btn.is-busy::after, button.is-busy::after {
  content: ""; position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%;
  border: 2px solid rgb(79 140 255 / .25); border-top-color: var(--fst-accent); animation: fst-spin 700ms linear infinite;
}
@keyframes fst-grid-shimmer { from { background-position-x: 100%; } to { background-position-x: 0%; } }
@keyframes fst-grid-sweep { from { background-position: -50px 0, 0 0; } to { background-position: calc(100% + 50px) 0, 0 0; } }
@keyframes fst-grid-dim { from { opacity: 1; } }
@keyframes fst-grid-undim { from { opacity: .45; } }
@keyframes fst-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .grid-busy .tbl-wrap, .grid-busy .xt { animation: none; }
  .grid-busy .tbl-wrap::before, .grid-busy .xt::before, .grid-leaving .tbl-wrap::before, .grid-leaving .xt::before { animation: none; background-position: 50% 0, 0 0; }
  .grid-busy .empty::before, .grid-leaving .empty::before { animation: none; background-position: 50% 0, 0 0; }
  .mail-view.grid-busy, .mail-form.grid-busy { animation: none; }
  .mail-view.grid-busy::before, .mail-view.grid-leaving::before, .mail-form.grid-busy::before, .mail-form.grid-leaving::before { animation: none; background-position: 50% 0, 0 0; }
  .pg.is-busy::after, .sort > a.is-busy::before, .btn.is-busy::after, button.is-busy::after { animation: none; }
}
/* Portal 1.50.36: the loader of a page that the shell swaps in (static/js/shell.js), the grid's look on the whole content area. .admin-main carries .page-busy
   from the click: everything in it dims to .45 (a transition, so a page that is swapped in meanwhile appears dimmed at once and not bright), a soft shimmer
   sweeps behind it, the accent line of .loadline runs along the top edge of the area, and nothing in it takes a click. shell.js holds it at least 300 ms after
   the click, then swaps .page-busy for .page-leaving, which fades it (--fst-dur-cover-out). A module switch has the cover instead (below). */
.admin-main > * { transition: opacity var(--fst-dur-cover-out) var(--fst-ease-in-quad); }
.admin-main.page-busy { cursor: progress;
  background-image: linear-gradient(90deg, transparent 33.3%, rgb(79 140 255 / .08) 50%, transparent 66.7%); background-size: 300% 100%; background-repeat: no-repeat;
  background-position: 100% 0;
  animation: fst-grid-shimmer var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite; }
.admin-main.page-busy > * { opacity: .45; pointer-events: none; transition-duration: var(--fst-dur-cover-in); transition-timing-function: var(--fst-ease-out-quad); }
.admin-main.page-busy::before, .admin-main.page-leaving::before {
  content: ""; position: fixed; z-index: 3; top: 0; left: var(--side-w, 0px); right: 0; height: 2px; pointer-events: none;
  background: linear-gradient(var(--fst-accent), var(--fst-accent)) 0 0 / min(50%, 50px) 100% no-repeat, var(--fst-hover);
  animation: fst-grid-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite, fst-fade-in-0 var(--fst-dur-cover-in) var(--fst-ease-out-quad);
}
.admin-main.page-leaving::before {
  animation: fst-grid-sweep var(--fst-dur-sweep) var(--fst-ease-inout-quad) infinite, fst-fade-out var(--fst-dur-cover-out) var(--fst-ease-in-quad) forwards;
}
@media (max-width: 899.98px) { .admin-main.page-busy::before, .admin-main.page-leaving::before { top: 48px; } }   /* under the top bar */
@media (prefers-reduced-motion: reduce) {
  .admin-main.page-busy { animation: none; }
  .admin-main.page-busy::before, .admin-main.page-leaving::before { animation: none; background-position: 50% 0, 0 0; }
}
/* the page's progress line and cover: static/js/ui.js shows them 150 ms after something starts to load - and a click on a module of the sidebar
   shows the cover at once and alone (Portal 1.50.4) */
.loadhost { position: fixed; z-index: 1200; top: 0; left: var(--side-w, 0px); right: 0; opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--fst-dur-cover-out) var(--fst-ease-in-quad), visibility 0s linear var(--fst-dur-cover-out); }
.loadhost.on { opacity: 1; visibility: visible; transition: opacity var(--fst-dur-cover-in) var(--fst-ease-out-quad); }
.loadhost .loadline { border-radius: 0; }
.loadhost .loadline > i { border-radius: 0; }
.cover { position: fixed; z-index: 1150; inset: 0 0 0 var(--side-w, 0px); display: grid; place-items: center; background: rgb(12 16 21 / .25);
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--fst-dur-cover-out) var(--fst-ease-in-quad), visibility 0s linear var(--fst-dur-cover-out); }
.cover.on { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity var(--fst-dur-cover-in) var(--fst-ease-out-quad); }
.cover-box { display: grid; justify-items: center; gap: 8px; }
.cover-caption { color: var(--fst-text-2); font: 400 var(--fs-input)/1.3 var(--fst-font); }
.cover-caption:empty { display: none; }
