/* keel: the AvatarFleet look every app shares, after AvatarOS (/opt/eos STYLEGUIDE),
   first drawn for Dispatch. A warm off-white canvas, white
   cards with one soft shadow, navy for structure and action, gold as the one accent, and
   soft tone pills (ink / wash / edge) for state. Every colour is a token here; templates
   use CoreUI classes and CoreUI's variables point at these. The dark theme (below the
   light tokens) gives the same tokens dark values; <html data-coreui-theme> picks one
   (base_plain.html, js/theme.js).

   An app's own stylesheet (static/css/app.css) loads after this one and uses the same
   tokens; it adds rules for its own pages and never redefines a token. */

:root, [data-coreui-theme="light"] {
  --af-navy: #14243d;
  --af-navy-soft: #1d3358;
  --af-accent: #f2a900;
  --af-canvas: #f8f7f3;
  --af-canvas-deep: #eeece6;
  --af-card: #ffffff;
  --af-cap: #f3f1ec;
  --af-inset: #f6f4ef;
  --af-field: #fbfaf7;
  --af-line: #e3dfd6;
  --af-line-soft: #ebe7df;
  --af-line-strong: #cfc8ba;
  --af-ink: #1f2933;
  --af-ink-soft: #6b6457;
  --af-ink-faint: #8a8378;
  --af-primary: #1d3358;
  --af-primary-soft: #24406c;
  --af-primary-deep: #14243d;
  --af-link: #2f5aa8;
  --af-link-hover: #24477f;
  --af-radius: 14px;
  --af-radius-sm: 10px;
  --af-shadow: 0 1px 2px rgba(31, 41, 51, .03), 0 8px 20px -14px rgba(31, 41, 51, .22);
  /* What floats over the page (dialogs, the sheet in a window, floating windows, menus):
     a clear edge, a deep shadow, a header and footer on the cap tone; a dialog also dims
     (and, where the browser can, slightly blurs) the page behind it. */
  --af-float-shadow: 0 30px 70px -12px rgba(15, 23, 38, .55), 0 12px 28px -10px rgba(15, 23, 38, .35);
  --af-menu-shadow: 0 16px 36px -12px rgba(15, 23, 38, .40), 0 4px 10px -4px rgba(15, 23, 38, .18);
  --af-raised-edge: var(--af-line-strong);  /* the border of anything floating */
  --af-raised-cap: var(--af-cap);           /* a dialog's header and footer */
  --af-backdrop: rgba(15, 23, 38, .55);     /* the dimmed page behind a dialog */
  --af-backdrop-blur: 3px;

  --af-success-ink: #1d7a52; --af-success-wash: #e8f4ee; --af-success-edge: #cfe8db;
  --af-warning-ink: #8a5a00; --af-warning-wash: #fdf3df; --af-warning-edge: #f2dfb3;
  --af-danger-ink: #9b2c2c;  --af-danger-wash: #fbe9e7;  --af-danger-edge: #f3cfc9;
  --af-info-ink: #24477f;    --af-info-wash: #eaf0fa;    --af-info-edge: #cfdbf0;
  --af-neutral-ink: #4d473e; --af-neutral-wash: #efede8; --af-neutral-edge: #ddd8ce;
  --af-warning-mark: #e0a526;  /* the yellow of marks (strips, charts, icons) on white */
  --af-info-mark: #7aa7e8;     /* the blue of running marks (strips, the busy Activity edge) */
  --af-danger-row: #fdf4f2;    /* a failed run's table row, lighter than the danger wash */
  --af-accent-ink: #6b4a00; --af-accent-wash: #fdf0cc; --af-accent-edge: #f2d27a; --af-accent-field: #fffaf0;  /* gold, for Admin */
  --af-slow-mark: #d08a00;     /* a slow run's outline in trend charts */
  --af-chart-mark: #2f5aa8;    /* one-series chart marks (Schedule › Load); 3:1 on cards */
  --af-navy-glow: #23395f;     /* the sign-in page's light spot */
  --af-navy-fill: var(--af-navy);  /* navy behind white text (chips, tabs, your own bubble) */
  --af-navy-ink: #14243d;      /* navy as a text colour */
  --af-code-ink: #24406c;      /* inline code */
  --af-primary-ink: var(--af-primary);  /* primary as a text colour (outline buttons, chips) */
  /* Fills behind white text (solid run outcomes, the state buttons): the ink in light,
     a deeper tone in dark, where the ink is pale. */
  --af-success-solid: #1d7a52; --af-danger-solid: #9b2c2c; --af-neutral-solid: #4d473e;
  --af-warning-on: #1f2933;    /* text on the mark yellow */
  --af-focus-ring: rgba(29, 51, 88, .12);
  --af-raised: var(--af-card);  /* menus and dialogs: white over the dimmed canvas */
  --af-radius-xs: .375rem;     /* small choice tiles */

  --af-log-bg: #0f1726;
  --af-log-ink: #d6e2f5;
  --af-log-err: #ffb4a8;
  --af-log-sys: #8fb3e8;

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

  --cui-body-font-family: var(--af-font);
  --cui-font-sans-serif: var(--af-font);
  --cui-font-monospace: var(--af-mono);
  --cui-body-font-size: .875rem;
  --cui-body-bg: var(--af-canvas);
  --cui-body-color: var(--af-ink);
  --cui-body-color-rgb: 31, 41, 51;
  --cui-secondary-color: var(--af-ink-soft);
  --cui-secondary-color-rgb: 107, 100, 87;
  --cui-tertiary-color: var(--af-ink-faint);
  --cui-tertiary-bg: var(--af-inset);
  --cui-border-color: var(--af-line);
  --cui-border-radius: var(--af-radius-sm);
  --cui-border-radius-sm: 7px;
  --cui-border-radius-lg: var(--af-radius);
  --cui-primary: var(--af-primary);
  --cui-primary-rgb: 29, 51, 88;
  --cui-primary-bg-subtle: #e6ecf5;
  --cui-primary-border-subtle: #b9c7de;
  --cui-link-color: var(--af-link);
  --cui-link-color-rgb: 47, 90, 168;
  --cui-link-hover-color: var(--af-link-hover);
  --cui-link-hover-color-rgb: 36, 71, 127;

  /* CoreUI's state tones point at ours too, so its utilities (text-success, alert-danger,
     the -emphasis / -bg-subtle / -border-subtle variables) give the one green, the one
     red and the one yellow. The plain warning tone is the mark yellow; its text colour
     is the warning ink. */
  --cui-success: var(--af-success-ink);
  --cui-success-rgb: 29, 122, 82;
  --cui-success-text-emphasis: var(--af-success-ink);
  --cui-success-bg-subtle: var(--af-success-wash);
  --cui-success-border-subtle: var(--af-success-edge);
  --cui-danger: var(--af-danger-ink);
  --cui-danger-rgb: 155, 44, 44;
  --cui-danger-text-emphasis: var(--af-danger-ink);
  --cui-danger-bg-subtle: var(--af-danger-wash);
  --cui-danger-border-subtle: var(--af-danger-edge);
  --cui-warning: var(--af-warning-mark);
  --cui-warning-rgb: 224, 165, 38;
  --cui-warning-text-emphasis: var(--af-warning-ink);
  --cui-warning-bg-subtle: var(--af-warning-wash);
  --cui-warning-border-subtle: var(--af-warning-edge);
  --cui-info: var(--af-info-ink);
  --cui-info-rgb: 36, 71, 127;
  --cui-info-text-emphasis: var(--af-info-ink);
  --cui-info-bg-subtle: var(--af-info-wash);
  --cui-info-border-subtle: var(--af-info-edge);
}

/* The dark theme: a deep navy canvas, slightly lighter cards, pale inks. Tone inks keep
   at least 4.5:1 (WCAG AA) on their wash and on a card; white text on the solid fills
   too. CoreUI's own dark variables (its [data-coreui-theme=dark] block) cover what is
   not named here. */
[data-coreui-theme="dark"] {
  --af-navy: #0b1220;
  --af-navy-soft: #2a4572;
  --af-canvas: #0f1622;
  --af-canvas-deep: #0b111b;
  --af-card: #172131;
  --af-cap: #1b2638;
  --af-inset: #131c2a;
  --af-field: #111a27;
  --af-line: #2a3649;
  --af-line-soft: #222e40;
  --af-line-strong: #3a4a62;
  --af-ink: #e4e9f1;
  --af-ink-soft: #a7b1c2;
  --af-ink-faint: #8894a8;
  --af-primary: #3a64ad;
  --af-primary-soft: #4673c0;
  --af-primary-deep: #2f5594;
  --af-link: #8fb3f0;
  --af-link-hover: #b3cbf5;
  --af-shadow: 0 1px 2px rgba(0, 0, 0, .25), 0 8px 20px -14px rgba(0, 0, 0, .6);
  --af-float-shadow: 0 30px 70px -10px rgba(0, 0, 0, .85), 0 12px 28px -10px rgba(0, 0, 0, .6);
  --af-menu-shadow: 0 16px 36px -12px rgba(0, 0, 0, .8), 0 4px 10px -4px rgba(0, 0, 0, .5);
  --af-raised-edge: #4b5e7d;
  --af-raised-cap: #233248;
  --af-backdrop: rgba(2, 5, 10, .72);

  --af-success-ink: #6fd3a0; --af-success-wash: #14301f; --af-success-edge: #23503a;
  --af-warning-ink: #eec062; --af-warning-wash: #33270d; --af-warning-edge: #5a4519;
  --af-danger-ink: #f39b91;  --af-danger-wash: #3a1b1c;  --af-danger-edge: #62302e;
  --af-info-ink: #9dbdf3;    --af-info-wash: #172640;    --af-info-edge: #2c4469;
  --af-neutral-ink: #c6ccd6; --af-neutral-wash: #243040; --af-neutral-edge: #384456;
  --af-danger-row: #261a1f;
  --af-chart-mark: #7fa6e6;
  --af-accent-ink: #f2c75c; --af-accent-wash: #33290c; --af-accent-edge: #6b5313; --af-accent-field: #1d1910;
  --af-navy-fill: #2b4a7e;
  --af-navy-ink: #dbe5f5;
  --af-code-ink: #b9cdf0;
  --af-primary-ink: var(--af-link);
  --af-success-solid: #1d7a52; --af-danger-solid: #bf3a3a; --af-neutral-solid: #56606f;
  --af-focus-ring: rgba(143, 179, 240, .3);
  --af-raised: #1d2a3e;  /* a step lighter than a card: higher is lighter in the dark */

  --cui-body-color-rgb: 228, 233, 241;
  --cui-body-bg-rgb: 15, 22, 34;
  --cui-secondary-color-rgb: 167, 177, 194;
  --cui-secondary-bg: var(--af-cap);
  --cui-border-color-translucent: rgba(167, 177, 194, .16);
  --cui-primary-rgb: 58, 100, 173;
  --cui-primary-bg-subtle: #1a2b47;
  --cui-primary-border-subtle: #34507f;
  --cui-link-color-rgb: 143, 179, 240;
  --cui-link-hover-color-rgb: 179, 203, 245;
  --cui-success-rgb: 111, 211, 160;
  --cui-danger-rgb: 243, 155, 145;
  --cui-info-rgb: 157, 189, 243;
  /* CoreUI's dark primary, secondary and light tones (alert-secondary, alert-light) are
     its own purple and greys; ours instead. */
  --cui-primary-text-emphasis: var(--af-info-ink);
  --cui-secondary-text-emphasis: var(--af-neutral-ink);
  --cui-secondary-bg-subtle: var(--af-neutral-wash);
  --cui-secondary-border-subtle: var(--af-neutral-edge);
  --cui-light-text-emphasis: var(--af-ink);
  --cui-light-bg-subtle: var(--af-inset);
  --cui-light-border-subtle: var(--af-line);
}

body { background: var(--af-canvas); -webkit-font-smoothing: antialiased; }
code, pre, .font-monospace { font-family: var(--af-mono); }
code { color: var(--af-code-ink); }
a { text-underline-offset: 2px; }
.skip-link { position: absolute; left: -999px; top: .5rem; z-index: 3000; background: var(--af-card); padding: .5rem 1rem; border-radius: var(--af-radius-sm); }
.skip-link:focus { left: .5rem; }

/* ------------------------------------------------------------------ sidebar */
.sidebar {
  --cui-sidebar-bg: var(--af-navy);
  --cui-sidebar-nav-link-active-bg: var(--af-navy-soft);
  --cui-sidebar-header-bg: var(--af-navy);
  --cui-sidebar-footer-bg: var(--af-navy);
  --cui-sidebar-nav-link-active-color: #fff;
  --cui-sidebar-nav-link-hover-bg: var(--af-navy-soft);
}
.sidebar-nav {
  --cui-sidebar-nav-link-padding-y: .45rem;
  --cui-sidebar-nav-title-padding-y: .2rem;
  --cui-sidebar-nav-title-margin-top: .9rem;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, .25) transparent;
}
.sidebar-nav-landmark { display: flex; flex: 1; flex-direction: column; min-height: 0; }
.sidebar-nav .nav-link.active { border-left: 3px solid var(--af-accent); }
.sidebar-nav .nav-title { color: rgba(255, 255, 255, .62); font-size: .68rem; letter-spacing: .09em; }
.sidebar-header { display: flex; align-items: center; justify-content: space-between; min-height: 3.5rem; }
/* The sidebar's head, as AvatarOS's: the company wordmark, then the app's name and its
   version on one line under it (never wrapping: the header's height places the menu). */
.sidebar-brand { display: flex; flex-direction: column; align-items: flex-start; gap: .2rem; padding: .3rem 0; color: #fff; }
.brand-logo { height: 1.75rem; width: auto; display: block; }
.brand-logo-mark { height: 1.75rem; width: 1.75rem; display: none; }
.brand-product { font-size: .875em; font-weight: 600; letter-spacing: .02em; color: rgba(255, 255, 255, .92); white-space: nowrap; }
.sidebar-narrow .brand-logo, .sidebar-narrow .brand-product { display: none; }
.sidebar-narrow .brand-logo-mark { display: block; }
.brand-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.9rem; height: 1.9rem; border-radius: 50%;
  background: var(--af-accent); color: var(--af-navy); font-size: 1rem;
  box-shadow: 0 0 0 3px rgba(242, 169, 0, .25);
}
.brand-home { display: flex; align-items: center; gap: .6rem; color: #fff; }
.brand-home:hover { color: #fff; }
.brand-version { margin-left: .4rem; font-size: .85em; font-weight: 400; letter-spacing: 0; color: rgba(255, 255, 255, .78); white-space: nowrap; }
.sidebar-narrow .sidebar-footer { display: none; }
.sidebar-footer { padding: .6rem 1rem; }

/* CoreUI sets --cui-sidebar-occupy-start on the sidebar's siblings; the layout consumes it
   (without this the fixed sidebar covers the page). */
.wrapper { width: 100%; padding-inline-start: var(--cui-sidebar-occupy-start, 0); transition: padding .15s; }
.sidebar-toggler { flex: none; width: 2rem; height: 2rem; border: 0; border-radius: 8px; background: transparent; color: rgba(255, 255, 255, .6); align-items: center; justify-content: center; }
.sidebar-toggler::before { display: none; }
.sidebar-toggler:hover { color: #fff; background: var(--af-navy-soft); }
.sidebar-toggler .toggler-collapsed, .sidebar-narrow .sidebar-toggler .toggler-expanded { display: none; }
.sidebar-narrow .sidebar-toggler .toggler-collapsed { display: inline; }
.sidebar-narrow .sidebar-header { flex-direction: column; gap: .4rem; padding: .5rem 0; }

/* ------------------------------------------------------------------ header */
.header { --cui-header-bg: var(--af-card); --cui-header-border-color: var(--af-line); min-height: 3.5rem; box-shadow: 0 1px 0 var(--af-line); }
.header-toggler { border: 0; background: none; font-size: 1.25rem; color: var(--af-ink); }
.crumbs { display: flex; align-items: center; gap: .4rem; min-width: 0; font-size: .9rem; color: var(--af-ink-soft); white-space: nowrap; overflow: hidden; }
.crumbs > * + *::before { content: "/"; margin-right: .4rem; color: var(--af-line-strong); }
.crumbs a { color: var(--af-ink-soft); text-decoration: none; }
.crumbs a:hover { color: var(--af-link); text-decoration: underline; }
.crumbs span:last-child { color: var(--af-ink); font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.user-btn { border: 0; background: none; color: var(--af-ink); font-weight: 500; }
.avatar-dot { display: inline-flex; width: 1.75rem; height: 1.75rem; border-radius: 50%; align-items: center; justify-content: center; background: var(--af-navy-fill); color: #fff; font-size: .8rem; font-weight: 700; }

/* ------------------------------------------------------------------ page */
.page-header { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 1rem; margin-bottom: 1.5rem; }
.page-header-text { min-width: 0; flex: 1 1 20rem; }
.page-header h1 { font-weight: 650; letter-spacing: -.01em; }
.page-subtitle { color: var(--af-ink-soft); margin: .25rem 0 0; }
.page-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
.eyebrow { font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; color: var(--af-ink-soft); }
/* Notices are toasts: a stack in the top right under the header, message and close
   button side by side (never wrapping under each other). */
.notices { position: fixed; top: 4.25rem; right: 1rem; z-index: 1090; width: min(28rem, calc(100vw - 2rem)); display: flex; flex-direction: column; gap: .5rem; pointer-events: none; }
.notices:empty { display: none; }
.notices .alert { pointer-events: auto; margin: 0; box-shadow: var(--af-shadow); display: flex; align-items: flex-start; gap: .75rem; }
.notices .alert.alert-dismissible { padding-right: .75rem; }
.notices .alert .btn-close { position: static; flex: none; margin-left: auto; padding: .35rem; }
.confirm-modal .modal-body { white-space: pre-line; }
.modal {
  --cui-modal-bg: var(--af-raised);
  --cui-modal-border-color: var(--af-raised-edge);
  --cui-modal-header-border-color: var(--af-line);
  --cui-modal-footer-border-color: var(--af-line);
  --cui-modal-footer-bg: var(--af-raised-cap);
}
.modal-content { box-shadow: var(--af-float-shadow); }
.modal-header { background: var(--af-raised-cap); }
/* The page behind a dialog: dimmed (the colour carries the dimming, so opacity is 1),
   slightly blurred where backdrop-filter works; without it the dimming alone does. */
.modal-backdrop { --cui-backdrop-bg: var(--af-backdrop); --cui-backdrop-opacity: 1; }
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .modal-backdrop, .window-sheet {
    -webkit-backdrop-filter: blur(var(--af-backdrop-blur));
    backdrop-filter: blur(var(--af-backdrop-blur));
  }
}
@media (prefers-reduced-transparency: reduce) {
  .modal-backdrop, .window-sheet { -webkit-backdrop-filter: none; backdrop-filter: none; }
}
.dropdown-menu {
  --cui-dropdown-bg: var(--af-raised);
  --cui-dropdown-header-color: var(--af-ink-soft);
  --cui-dropdown-border-color: var(--af-raised-edge);
  --cui-dropdown-divider-bg: var(--af-line);
  box-shadow: var(--af-menu-shadow);
}

/* ------------------------------------------------------------------ cards and tables */
.card {
  --cui-card-bg: var(--af-card);
  --cui-card-cap-bg: var(--af-cap);
  --cui-card-border-color: var(--af-line);
  --cui-card-border-radius: var(--af-radius);
  --cui-card-inner-border-radius: calc(var(--af-radius) - 1px);
  box-shadow: var(--af-shadow);
}
.card-header { font-weight: 600; font-size: .95rem; border-bottom-color: var(--af-line-soft); padding-top: .7rem; padding-bottom: .7rem; }
/* Card titles are headings (h2) for the page outline; they look like the header text. */
h2.card-header { line-height: inherit; color: inherit; }
.card-heading { font-size: inherit; font-weight: inherit; line-height: inherit; color: inherit; margin: 0; }
.card-footer { background: var(--af-cap); border-top-color: var(--af-line-soft); }
.card-alert { border-color: var(--af-danger-edge); }
/* The pager under a long list (pagination macro): the count left, the page links right. */
.pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem 1rem; }
.pager .pagination { --cui-pagination-bg: var(--af-card); --cui-pagination-border-color: var(--af-line); --cui-pagination-color: var(--af-link); --cui-pagination-hover-bg: var(--af-inset); --cui-pagination-hover-color: var(--af-link-hover); --cui-pagination-active-bg: var(--af-primary); --cui-pagination-active-border-color: var(--af-primary); --cui-pagination-disabled-bg: var(--af-cap); --cui-pagination-disabled-color: var(--af-ink-faint); flex-wrap: wrap; }
.card-alert > .card-header { background: var(--af-danger-wash); color: var(--af-danger-ink); }
/* Not normal but not a failure (a job with no recent success): the yellow of the tokens. */
.card-warn { border-color: var(--af-warning-edge); }
.card-warn > .card-header { background: var(--af-warning-wash); color: var(--af-warning-ink); }
.table { --cui-table-bg: var(--af-card); --cui-table-hover-bg: var(--af-inset); --cui-table-border-color: var(--af-line-soft); margin-bottom: 0; }
.table > thead th { font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--af-ink-soft); background: var(--af-card); white-space: nowrap; }
.table-hover > tbody > tr:hover > * { --cui-table-bg-state: var(--af-inset); }
.table tr.row-paused > td { color: var(--af-ink-soft); }
.cell-note { max-width: 28rem; }
.cell-where { max-width: 14rem; }
.w-check { width: 2rem; }
.list-group { --cui-list-group-bg: var(--af-card); --cui-list-group-border-color: var(--af-line-soft); }
.kv { display: grid; grid-template-columns: minmax(7rem, max-content) 1fr; gap: .45rem 1rem; }
.kv dt { font-weight: 500; color: var(--af-ink-soft); }
.kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.tag { display: inline-block; font-size: .7rem; font-weight: 600; color: var(--af-neutral-ink); background: var(--af-neutral-wash); border: 1px solid var(--af-neutral-edge); border-radius: 6px; padding: 0 .4rem; margin-left: .3rem; }
.empty-state { text-align: center; color: var(--af-ink-soft); padding: 1.25rem .5rem; }
.empty-state > i { font-size: 1.6rem; color: var(--af-ink-faint); display: block; margin-bottom: .35rem; }
.empty-state p { margin: 0; }
.filter-bar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-bottom: 1rem; }
.filter-bar .form-control { max-width: 18rem; }
.btn-check:focus-visible + .btn { outline: 2px solid var(--af-accent); outline-offset: 1px; }
.filter-bar .form-select { width: auto; max-width: 16rem; }

/* ------------------------------------------------------------------ tone pills */
.badge { font-weight: 600; letter-spacing: .01em; border: 1px solid transparent; border-radius: 999px; padding: .32em .62em; }
.text-bg-success   { background: var(--af-success-wash) !important; color: var(--af-success-ink) !important; border-color: var(--af-success-edge); }
.text-bg-warning   { background: var(--af-warning-wash) !important; color: var(--af-warning-ink) !important; border-color: var(--af-warning-edge); }
.text-bg-danger    { background: var(--af-danger-wash) !important;  color: var(--af-danger-ink) !important;  border-color: var(--af-danger-edge); }
.text-bg-info      { background: var(--af-info-wash) !important;    color: var(--af-info-ink) !important;    border-color: var(--af-info-edge); }
.text-bg-secondary { background: var(--af-neutral-wash) !important; color: var(--af-neutral-ink) !important; border-color: var(--af-neutral-edge); }
.text-bg-light     { background: var(--af-inset) !important;        color: var(--af-ink) !important;         border-color: var(--af-line); }
@keyframes keel-pulse { 50% { opacity: .35; } }

/* ------------------------------------------------------------------ alerts, buttons, fields */
.alert { border-radius: var(--af-radius-sm); }
.alert-danger  { --cui-alert-bg: var(--af-danger-wash);  --cui-alert-color: var(--af-danger-ink);  --cui-alert-border-color: var(--af-danger-edge); }
.alert-warning { --cui-alert-bg: var(--af-warning-wash); --cui-alert-color: var(--af-warning-ink); --cui-alert-border-color: var(--af-warning-edge); }
.alert-success { --cui-alert-bg: var(--af-success-wash); --cui-alert-color: var(--af-success-ink); --cui-alert-border-color: var(--af-success-edge); }
.alert-info    { --cui-alert-bg: var(--af-info-wash);    --cui-alert-color: var(--af-info-ink);    --cui-alert-border-color: var(--af-info-edge); }
.btn { font-weight: 500; }
/* Each button rule also names [data-coreui-theme] .btn-…: CoreUI's dark theme restyles
   buttons with that selector, which outranks a bare class, and ours must win in both. */
.btn-primary, [data-coreui-theme] .btn-primary {
  --cui-btn-bg: var(--af-primary); --cui-btn-border-color: var(--af-primary);
  --cui-btn-hover-bg: var(--af-primary-soft); --cui-btn-hover-border-color: var(--af-primary-soft);
  --cui-btn-active-bg: var(--af-primary-deep); --cui-btn-active-border-color: var(--af-primary-deep);
  --cui-btn-disabled-bg: var(--af-primary); --cui-btn-disabled-border-color: var(--af-primary);
}
.btn-outline-primary, [data-coreui-theme] .btn-outline-primary {
  --cui-btn-color: var(--af-primary-ink); --cui-btn-border-color: var(--af-line-strong); --cui-btn-bg: var(--af-card);
  --cui-btn-hover-color: #fff; --cui-btn-hover-bg: var(--af-primary); --cui-btn-hover-border-color: var(--af-primary);
  --cui-btn-active-bg: var(--af-primary-deep); --cui-btn-active-border-color: var(--af-primary-deep);
}
.btn-outline-secondary, [data-coreui-theme] .btn-outline-secondary {
  --cui-btn-color: var(--af-ink); --cui-btn-border-color: var(--af-line-strong); --cui-btn-bg: var(--af-card);
  --cui-btn-hover-color: var(--af-ink); --cui-btn-hover-bg: var(--af-inset); --cui-btn-hover-border-color: var(--af-ink-faint);
  --cui-btn-active-color: #fff; --cui-btn-active-bg: var(--af-primary); --cui-btn-active-border-color: var(--af-primary);
}
.btn-outline-danger, [data-coreui-theme] .btn-outline-danger { --cui-btn-color: var(--af-danger-ink); --cui-btn-border-color: var(--af-danger-edge); --cui-btn-bg: var(--af-card); --cui-btn-hover-color: #fff; --cui-btn-hover-bg: var(--af-danger-solid); --cui-btn-hover-border-color: var(--af-danger-solid); --cui-btn-active-color: #fff; --cui-btn-active-bg: var(--af-danger-solid); --cui-btn-active-border-color: var(--af-danger-solid); }
/* The solid state buttons in the same green, red and yellow as every other mark. */
.btn-success, [data-coreui-theme] .btn-success { --cui-btn-color: #fff; --cui-btn-hover-color: #fff; --cui-btn-active-color: #fff; --cui-btn-bg: var(--af-success-solid); --cui-btn-border-color: var(--af-success-solid); --cui-btn-hover-bg: color-mix(in srgb, var(--af-success-solid) 85%, #000); --cui-btn-hover-border-color: color-mix(in srgb, var(--af-success-solid) 85%, #000); --cui-btn-active-bg: color-mix(in srgb, var(--af-success-solid) 75%, #000); }
.btn-danger, [data-coreui-theme] .btn-danger { --cui-btn-color: #fff; --cui-btn-hover-color: #fff; --cui-btn-active-color: #fff; --cui-btn-bg: var(--af-danger-solid); --cui-btn-border-color: var(--af-danger-solid); --cui-btn-hover-bg: color-mix(in srgb, var(--af-danger-solid) 85%, #000); --cui-btn-hover-border-color: color-mix(in srgb, var(--af-danger-solid) 85%, #000); --cui-btn-active-bg: color-mix(in srgb, var(--af-danger-solid) 75%, #000); }
.btn-warning, [data-coreui-theme] .btn-warning { --cui-btn-bg: var(--af-warning-mark); --cui-btn-border-color: var(--af-warning-mark); --cui-btn-color: var(--af-warning-on); --cui-btn-hover-bg: color-mix(in srgb, var(--af-warning-mark) 88%, #000); --cui-btn-hover-border-color: color-mix(in srgb, var(--af-warning-mark) 88%, #000); --cui-btn-hover-color: var(--af-warning-on); --cui-btn-active-color: var(--af-warning-on); }
.form-control, .form-select { background-color: var(--af-field); border-color: var(--af-line); }
.form-control:hover, .form-select:hover { border-color: var(--af-line-strong); }
.form-control:focus, .form-select:focus { background-color: var(--af-card); border-color: var(--af-primary); box-shadow: 0 0 0 .2rem var(--af-focus-ring); }
.form-label { font-weight: 600; font-size: .9rem; }
.form-check-input:checked { background-color: var(--af-primary); border-color: var(--af-primary); }
.code-input { font-family: var(--af-mono); font-size: .85rem; line-height: 1.5; tab-size: 4; }
.form-actions { display: flex; gap: .5rem; padding: 1rem 0 2rem; }
.narrow-form { max-width: 44rem; }
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { display: inline-block; }

/* ------------------------------------------------------------------ dashboard */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 1rem; }
.stat-tile { display: block; background: var(--af-card); border: 1px solid var(--af-line); border-radius: var(--af-radius); box-shadow: var(--af-shadow); padding: .9rem 1.1rem; color: inherit; text-decoration: none; }
a.stat-tile:hover { border-color: var(--af-line-strong); }
.stat-label { font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .07em; color: var(--af-ink-soft); }
.stat-figure { font-size: 2rem; font-weight: 700; line-height: 1.15; margin: .15rem 0; }
.stat-figure small { font-size: 1rem; color: var(--af-ink-soft); margin-left: .1rem; }
.stat-meta { font-size: .8rem; color: var(--af-ink-soft); }
.stat-tile.is-bad { border-color: var(--af-danger-edge); background: var(--af-danger-wash); }
.stat-tile.is-bad .stat-figure { color: var(--af-danger-ink); }
.stat-tile.is-live .stat-figure { color: var(--af-info-ink); }

/* ------------------------------------------------------------------ commands and logs */
.command-block { background: var(--af-inset); border: 1px solid var(--af-line-soft); border-radius: var(--af-radius-sm); padding: .75rem 1rem; font-size: .82rem; line-height: 1.5; max-height: 28rem; overflow: auto; margin: 0; white-space: pre; }
.command-block.wrap { white-space: pre-wrap; overflow-wrap: anywhere; }

/* Code editor (vendored CodeMirror 5, theme "keel"): the field look, warm syntax
   colours; .is-dark is the dark variant (the run log's navy), .show-hidden marks
   whitespace. Both are the viewer's choice, from the editor's toolbar. */
.cm-s-keel {
  --code-bg: var(--af-field); --code-ink: var(--af-ink); --code-edge: var(--af-line-strong);
  --code-gutter: var(--af-cap); --code-gutter-ink: var(--af-ink-faint); --code-gutter-edge: var(--af-line-soft);
  --code-active: rgba(242, 169, 0, .08); --code-selected: #dbe4f3; --code-cursor: var(--af-ink);
  --code-keyword: #8b3a8f; --code-builtin: #2f5aa8; --code-string: #1d7a52; --code-variable: #9a5b00;
  --code-comment: #6b6457; --code-number: #b5402a; --code-soft: var(--af-ink-soft);
  --code-hidden: #b9b1a3; --code-trailing: var(--af-danger-wash);
}
/* The dark page draws every editor dark too; its own Dark button then has nothing to do. */
.cm-s-keel.is-dark, [data-coreui-theme="dark"] .cm-s-keel {
  --code-bg: var(--af-log-bg); --code-ink: var(--af-log-ink); --code-edge: #26344d;
  --code-gutter: #0b1220; --code-gutter-ink: #5d6c86; --code-gutter-edge: #1d2a40;
  --code-active: rgba(242, 169, 0, .07); --code-selected: #2a3d5e; --code-cursor: #f2a900;
  --code-keyword: #e3a7f0; --code-builtin: #8fb8ff; --code-string: #9fdcb1; --code-variable: #f5c56b;
  --code-comment: #7d8aa3; --code-number: #ffab91; --code-soft: #a9b6cc;
  --code-hidden: #45546f; --code-trailing: rgba(255, 120, 100, .22);
}
.CodeMirror.cm-s-keel { height: auto; font-family: var(--cui-font-monospace, ui-monospace, monospace); font-size: .85rem; line-height: 1.5; color: var(--code-ink); background: var(--code-bg); border: 1px solid var(--code-edge); border-radius: var(--af-radius-sm); }
.CodeMirror.cm-s-keel.CodeMirror-focused { border-color: var(--af-primary-soft); box-shadow: 0 0 0 .2rem var(--af-focus-ring); }
.CodeMirror.cm-s-keel.is-invalid { border-color: var(--af-danger-ink); }
.CodeMirror.cm-s-keel.is-view { max-height: 28rem; }
.CodeMirror.cm-s-keel.is-view:not(.is-dark) { --code-bg: var(--af-inset); --code-edge: var(--af-line-soft); }
.CodeMirror.cm-s-keel.is-view .CodeMirror-scroll { max-height: 28rem; }
.CodeMirror.cm-s-keel.is-view .CodeMirror-cursors { display: none; }
.cm-s-keel .CodeMirror-cursor { border-left-color: var(--code-cursor); }
.cm-s-keel .CodeMirror-gutters { background: var(--code-gutter); border-right: 1px solid var(--code-gutter-edge); border-radius: var(--af-radius-sm) 0 0 var(--af-radius-sm); }
.cm-s-keel .CodeMirror-linenumber { color: var(--code-gutter-ink); padding: 0 .5rem 0 .25rem; }
.cm-s-keel .CodeMirror-lines { padding: .5rem 0; }
.cm-s-keel .CodeMirror-activeline-background { background: var(--code-active); }
.cm-s-keel .CodeMirror-selected, .cm-s-keel.CodeMirror-focused .CodeMirror-selected { background: var(--code-selected); }
.cm-s-keel .CodeMirror-matchingbracket { color: inherit !important; background: rgba(242, 169, 0, .3); }
.cm-s-keel .cm-keyword { color: var(--code-keyword); font-weight: 600; }
.cm-s-keel .cm-builtin, .cm-s-keel .cm-attribute { color: var(--code-builtin); }
.cm-s-keel .cm-string, .cm-s-keel .cm-quote { color: var(--code-string); }
.cm-s-keel .cm-def { color: var(--code-variable); }
.cm-s-keel .cm-comment { color: var(--code-comment); font-style: italic; }
.cm-s-keel .cm-number { color: var(--code-number); }
.cm-s-keel .cm-operator, .cm-s-keel .cm-meta { color: var(--code-soft); }
/* Hidden characters: a dot per space (1ch apart), an arrow per tab, ¬ at each line end. */
.cm-s-keel .cm-ws-space { background-image: radial-gradient(circle, var(--code-hidden) 1px, transparent 1.4px); background-size: 1ch 100%; background-repeat: repeat-x; background-position: 0 50%; }
.cm-s-keel .cm-ws-trailing { background-color: var(--code-trailing); }
.cm-s-keel.show-hidden .cm-tab { position: relative; }
.cm-s-keel.show-hidden .cm-tab::before { content: "\2192"; position: absolute; left: 0; color: var(--code-hidden); }
.cm-s-keel.show-hidden .CodeMirror-line > span::after { content: "\00ac"; color: var(--code-hidden); }
.code-toolbar { display: flex; justify-content: flex-end; gap: .25rem; margin-bottom: .25rem; }
.code-toolbar .btn { padding: .05rem .45rem; font-size: .78rem; color: var(--af-ink-soft); border-color: transparent; }
.code-toolbar .btn:hover { color: var(--af-ink); background: var(--af-inset); }
.code-toolbar .btn.active { color: var(--af-navy-ink); background: var(--af-inset); border-color: var(--af-line); }
[data-coreui-theme="dark"] .code-toolbar [data-code-pref="dark"] { display: none; }
.command-details > summary { cursor: pointer; list-style: none; }
.test-output { white-space: pre-wrap; }

/* ------------------------------------------------------------------ floating windows
   Not modals: no backdrop, the page behind stays usable. Drag the bar to move, the
   corner to resize; position and size are remembered per kind of window; double-click
   the bar to reset. They stay open across page changes (sessionStorage). */
#windows { position: fixed; inset: 0; pointer-events: none; z-index: 1060; }
.float-window {
  position: fixed; pointer-events: auto; display: flex; flex-direction: column;
  width: min(980px, 92vw); height: min(680px, calc(100vh - 6rem));
  min-width: 22rem; min-height: 14rem; max-width: 100vw; max-height: 100vh;
  background: var(--af-card); border: 1px solid var(--af-raised-edge); border-radius: var(--af-radius);
  box-shadow: var(--af-float-shadow); overflow: hidden;
}
/* The resize corner: a pointer handle (app.js makeResizable) rather than CSS `resize`,
   which touch screens ignore. 28 px, above the 24 px minimum target. */
.float-window-grip {
  position: absolute; right: 0; bottom: 0; z-index: 1; width: 28px; height: 28px;
  cursor: nwse-resize; touch-action: none;
  background: linear-gradient(135deg, transparent 55%, var(--af-line-strong) 55%, var(--af-line-strong) 62%,
    transparent 62%, transparent 72%, var(--af-line-strong) 72%, var(--af-line-strong) 79%, transparent 79%);
}
.float-window.is-moving, .float-window.is-resizing { opacity: .93; user-select: none; }
.float-window.is-front .float-window-bar { background: var(--af-navy); }
.float-window-bar {
  display: flex; align-items: center; gap: .6rem; flex: none;
  background: var(--af-navy-soft); color: #fff; padding: .5rem .75rem .5rem 1rem;
  cursor: move; user-select: none; touch-action: none;
}
.float-window-title { font-size: inherit; line-height: inherit; margin: 0; color: inherit; font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.float-window-title:focus { outline: none; }  /* focused for screen readers on open; the window shows where you are */
.float-window-pop { color: rgba(255, 255, 255, .75); }
.float-window-pop:hover { color: #fff; }
.float-window-body { flex: 1; min-height: 0; overflow: auto; padding: .85rem 1rem; overscroll-behavior: contain; display: flex; flex-direction: column; }
/* A confirmation asked from inside a window covers that window only (app.js askInWindow). */
.window-sheet { position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center; padding: 1rem; background: var(--af-backdrop); }
.window-sheet-card { width: min(24rem, 100%); background: var(--af-raised); border: 1px solid var(--af-raised-edge); border-radius: var(--af-radius); box-shadow: var(--af-float-shadow); overflow: hidden; }
.window-sheet-head { margin: 0; padding: .7rem 1.1rem; background: var(--af-raised-cap); border-bottom: 1px solid var(--af-line); }
.window-sheet-body { margin: 0; padding: .9rem 1.1rem; white-space: pre-line; }
.window-sheet-foot { display: flex; justify-content: flex-end; gap: .5rem; padding: .6rem 1.1rem; background: var(--af-raised-cap); border-top: 1px solid var(--af-line); }

/* ------------------------------------------------------------------ sign-in */
.auth-page { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 1rem; background: radial-gradient(circle at 30% 20%, var(--af-navy-glow), var(--af-navy) 60%); }
.auth-card { width: min(28rem, 100%); }
.auth-brand { display: flex; align-items: center; gap: .6rem; font-size: 1.25rem; font-weight: 700; margin-bottom: .5rem; }
.error-page { max-width: 36rem; margin: 3rem auto; }

@media (max-width: 991.98px) {
  .float-window { width: 96vw !important; left: 2vw !important; min-width: 0; }
}
/* A phone: every window is a full-screen sheet (no dragging, no resizing), so its bar and
   close button are always on screen whatever size it was given on a desktop. */
@media (max-width: 767.98px) {
  .float-window {
    left: 0 !important; top: 0 !important; width: 100% !important;
    height: 100% !important; height: 100dvh !important;
    min-width: 0; min-height: 0; max-width: none; max-height: none; border: 0; border-radius: 0;
  }
  .float-window-grip { display: none; }
  .float-window-bar { cursor: default; touch-action: auto; padding-block: .7rem; }
  .float-window-bar .btn-close { width: 1.25rem; height: 1.25rem; padding: .5rem; }
}
.role-select { min-width: 9.5rem; }
/* List views: the edit pencil ends the row (shared/_macros.html edit_link). */
.edit-col { width: 1%; white-space: nowrap; text-align: end; }
.edit-link { color: var(--af-ink-faint); padding: .3rem .5rem; min-width: 24px; min-height: 24px; }
.edit-link:hover, .edit-link:focus-visible { color: var(--af-primary-ink); background: var(--af-inset); }

/* ------------------------------------------------------------------ density
   Lists and the dashboard should show a lot on a normal laptop screen: compact type,
   one line per row, tight cell padding. Forms keep their own breathing room. */
.header { min-height: 3rem; }
.page-header { margin-bottom: 1rem; gap: .75rem; align-items: center; }
.page-header h1 { font-size: 1.35rem; }
.page-subtitle { font-size: .82rem; margin-top: .05rem; }
.eyebrow { font-size: .66rem; }
.card-header { padding: .5rem .85rem; font-size: .88rem; }
.card-body { padding: .85rem; }
.table > :not(caption) > * > * { padding: .32rem .55rem; }
.table > thead th { font-size: .66rem; padding-top: .45rem; padding-bottom: .45rem; }
.table { font-size: .84rem; }
.list-group-item { padding: .38rem .85rem; font-size: .84rem; }
.badge { font-size: .7rem; padding: .25em .55em; }
.btn-sm { --cui-btn-padding-y: .18rem; --cui-btn-padding-x: .55rem; --cui-btn-font-size: .8rem; }
.form-control-sm, .form-select-sm { font-size: .8rem; }
.filter-bar { margin-bottom: .75rem; gap: .4rem; }
.notices .alert { padding: .6rem .75rem .6rem 1rem; }
.stat-grid { gap: .75rem; }
.stat-tile { padding: .55rem .85rem; }
.stat-figure { font-size: 1.5rem; margin: 0; }
.stat-meta, .stat-label { font-size: .72rem; }
.empty-state { padding: .6rem .5rem; }
.empty-state > i { font-size: 1.2rem; }
.row.g-4 { --cui-gutter-x: 1rem; --cui-gutter-y: 1rem; }
.mb-4 { margin-bottom: 1rem !important; }
.mt-4 { margin-top: 1rem !important; }
.row-actions .btn { padding-top: .1rem; padding-bottom: .1rem; }
.tag { font-size: .68rem; }
.table a { text-decoration: none; }
.table a:hover { text-decoration: underline; }
.table .btn-sm, .table .btn-link { --cui-btn-padding-y: .05rem; --cui-btn-padding-x: .4rem; line-height: 1.35; min-height: 24px; }
.table .badge { line-height: 1.15; }
.table td { line-height: 1.35; }
.tag.is-up { border-color: var(--cui-success-border-subtle); background: var(--cui-success-bg-subtle); }
.brand-update { margin-left: .35rem; padding: 0 .35rem; border-radius: 999px; background: var(--cui-primary); color: #fff; font-size: .65rem; text-decoration: none; vertical-align: middle; }

/* Unsaved changes (app.js, after eos): bottom right while a form is dirty and its own
   submit button is off screen; the full width of the bottom on a phone. The amber dot is
   the "needs attention" accent, not a health colour. */
.unsaved-bar { position: fixed; right: 1.25rem; bottom: 1rem; z-index: 1040; display: flex; align-items: center; gap: .6rem; padding: .6rem .75rem .6rem .9rem; background: var(--af-card); border: 1px solid var(--af-navy-soft); border-radius: var(--af-radius-sm); box-shadow: 0 12px 32px -12px rgba(31, 41, 51, .45); font-size: .9rem; }
.unsaved-bar[hidden] { display: none; }
.unsaved-dot { display: inline-block; width: .55rem; height: .55rem; border-radius: 50%; background: var(--af-accent); }
.unsaved-what { font-weight: 600; margin-right: .25rem; }
@media (max-width: 575.98px) { .unsaved-bar { left: .75rem; right: .75rem; bottom: .75rem; flex-wrap: wrap; }
}

/* Combo box (select[data-combo], app.js): a text box over a hidden select, and the
   matching options under it. */
.combo { position: relative; min-width: 12rem; }
.combo-list { position: absolute; z-index: 1050; top: calc(100% + 2px); left: 0; right: 0; max-height: 18rem; overflow-y: auto; margin: 0; padding: .25rem 0; list-style: none; background: var(--af-raised); border: 1px solid var(--af-raised-edge); border-radius: var(--af-radius-sm); box-shadow: var(--af-menu-shadow); font-size: .875rem; }
.combo-list[hidden] { display: none; }
.combo.is-clearable .form-control { padding-right: 2rem; }
.combo-clear { position: absolute; top: 50%; right: .35rem; transform: translateY(-50%); display: inline-flex; padding: .2rem; border: 0; border-radius: var(--af-radius-sm); background: none; color: var(--af-ink-soft); line-height: 1; }
.combo-clear:hover, .combo-clear:focus-visible { color: var(--af-ink); background: var(--af-inset); }
.combo-clear[hidden] { display: none; }
.combo-list [role=option] { padding: .35rem .75rem; cursor: pointer; }
.combo-list [role=option].active { background: var(--af-inset); }
.combo-list [role=option].is-current { font-weight: 600; }
.combo-list mark { padding: 0; background: rgba(242, 169, 0, .3); color: inherit; border-radius: 2px; }
.combo-note { padding: .35rem .75rem; color: var(--af-ink-soft); font-style: italic; }
.combo:has(.is-invalid) ~ .invalid-feedback { display: block; }
.section-panels .section-panel { margin-bottom: 1rem; }
.section-panel .card-header { display: flex; align-items: baseline; gap: .75rem; }
.section-panel-hint { font-size: .8rem; font-weight: 400; color: var(--af-ink-soft); }
.section-panel-nav { display: none; }
.section-panels.is-paneled { display: grid; grid-template-columns: 13rem minmax(0, 1fr); gap: 1.25rem; align-items: start; }
.section-panels.is-paneled .section-panel-nav { display: flex; flex-direction: column; gap: .15rem; position: sticky; top: calc(3.5rem + var(--sticky-bar-h, 8rem) + 1rem); }
.section-panels.is-paneled .section-panel { max-width: 68rem; }
.section-panel-tab { display: flex; align-items: center; gap: .6rem; padding: .5rem .75rem; border-radius: var(--af-radius-sm); color: var(--af-ink-soft); text-decoration: none; font-weight: 500; font-size: .9rem; }
.section-panel-tab:hover { background: var(--af-canvas-deep); color: var(--af-ink); }
.section-panel-tab.active { background: var(--af-navy-fill); color: #fff; }
.section-panel-tab i { font-size: 1rem; opacity: .85; }
.section-panel-errors { margin-left: auto; min-width: 1.3rem; padding: 0 .35rem; border-radius: 999px; background: var(--af-danger-solid); color: #fff; font-size: .72rem; font-weight: 700; text-align: center; }
@media (max-width: 991.98px) {
  .section-panels.is-paneled { grid-template-columns: minmax(0, 1fr); }
  .section-panels.is-paneled .section-panel-nav { flex-direction: row; overflow-x: auto; position: static; padding-bottom: .25rem; }
  .section-panel-tab { white-space: nowrap; }
}

/* Users: who has more than the usual, at a glance. Admins get a gold shield, a gold edge on
   the row and a bold role; broad grants (every job, every SSH profile) a quiet icon each. */
.person-name { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }
.power-badge { display: inline-flex; align-items: center; gap: .25rem; padding: .05rem .4rem; border-radius: 999px; font-size: .72rem; font-weight: 600; color: var(--af-info-ink); background: var(--af-info-wash); border: 1px solid var(--af-info-edge); }
.power-badge.is-admin { color: var(--af-accent-ink); background: var(--af-accent-wash); border-color: var(--af-accent-edge); }
.power-badge i { font-size: .8rem; }
tr.is-admin > td:first-child { box-shadow: inset 3px 0 0 var(--af-accent); }
.role-select.is-no-access { color: var(--af-danger-ink); border-color: var(--af-danger-edge); background-color: var(--af-danger-wash); }
.role-select.is-admin { font-weight: 700; color: var(--af-navy-ink); border-color: var(--af-accent-edge); background-color: var(--af-accent-field); }

/* Sortable headers (sort_th): the whole label is the link; the arrow shows the sort, a
   faint two-way arrow on hover where a column is not sorted. */
th.sortable a { color: inherit; text-decoration: none; display: inline-flex; align-items: center; gap: .3rem; white-space: nowrap; }
th.sortable a:hover { color: var(--af-ink); }
th.sortable .sort-icon { font-size: .7rem; opacity: 0; transition: opacity .1s; }
th.sortable a:hover .sort-icon, th.sortable a:focus-visible .sort-icon { opacity: .45; }
th.sortable.is-sorted { color: var(--af-ink); }
th.sortable.is-sorted .sort-icon { opacity: .9; }
th.text-end.sortable a { flex-direction: row-reverse; }

/* Not production (APP_ENV): a badge in the header so nobody mistakes the instance. */
.env-badge { display: inline-flex; align-items: center; gap: .35rem; margin-left: .75rem; padding: .15rem .6rem; border-radius: 999px; font-size: .78rem; font-weight: 700; letter-spacing: .03em; color: var(--af-warning-ink); background: var(--af-warning-wash); border: 1px solid var(--af-warning-edge); }

/* Quick find in the header (Ctrl/Cmd+K): a search box with a dropdown of results. */
.find { position: relative; align-items: center; gap: .4rem; margin-left: 1rem; padding: 0 .5rem; width: min(26rem, 32vw); height: 2rem; background: var(--af-inset); border: 1px solid var(--af-line); border-radius: var(--af-radius-sm); }
.find:focus-within { border-color: var(--af-navy-soft); background: var(--af-card); box-shadow: 0 0 0 .2rem var(--af-focus-ring); }
.find > i { color: var(--af-ink-faint); }
.find input { flex: 1; min-width: 0; border: 0; background: transparent; outline: none; font-size: .875rem; color: var(--af-ink); }
.find-key { font-size: .65rem; padding: .05rem .3rem; color: var(--af-ink-soft); background: var(--af-card); border: 1px solid var(--af-line); border-radius: 4px; }
.find:focus-within .find-key { display: none; }
.find-results { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 1060; max-height: 70vh; overflow-y: auto; background: var(--af-raised); border: 1px solid var(--af-raised-edge); border-radius: var(--af-radius-sm); box-shadow: var(--af-menu-shadow); padding: .25rem 0; }
.find-results[hidden] { display: none; }
.find-list { list-style: none; margin: 0; padding: 0; }
.find-kind { padding: .4rem .75rem .15rem; font-size: .68rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--af-ink-soft); }
.find-item { display: flex; align-items: baseline; gap: .5rem; padding: .35rem .75rem; color: var(--af-ink); text-decoration: none; font-size: .875rem; }
.find-item i { color: var(--af-ink-faint); align-self: center; }
.find-item.active, .find-item:hover { background: var(--af-inset); }
.find-label { flex: 0 1 auto; max-width: 75%; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.find-detail { flex: 1 1 0; min-width: 0; color: var(--af-ink-soft); font-size: .78rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.find-empty { padding: .6rem .75rem; color: var(--af-ink-soft); font-size: .85rem; }

/* Phones (UX review 2026-10-09): nothing scrolls the page sideways. A table scrolls in
   its own box (its absolutely placed bits stay inside); the job page's long run strip
   shares the width; the jobs list keeps the name readable and drops secondary columns. */
.table-responsive { position: relative; }

/* One green, one red everywhere (badges, pips, charts, timeline), and quiet text that is
   still readable (tertiary grey was under 4.5:1 on the canvas). */
.text-body-tertiary { color: var(--af-ink-soft) !important; }
@media (max-width: 575.98px) {
  .stat-grid { grid-template-columns: 1fr 1fr; }
}
.clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; white-space: normal; max-width: 32rem; }
summary.disclosure { cursor: pointer; list-style: none; }
summary.disclosure::before { content: "\25B8"; display: inline-block; margin-right: .4rem; transition: transform .15s; color: var(--af-ink-faint); }
details[open] > summary.disclosure::before { transform: rotate(90deg); }
details[open] > summary.disclosure .text-body-secondary { display: none; }
/* A switch drawn in a menu item that toggles a setting (the user menu's Daily digest). */
.menu-switch { display: inline-block; position: relative; width: 1.9rem; height: 1.05rem; margin-right: .55rem; vertical-align: -.2rem; border-radius: 999px; background: var(--af-line-strong); transition: background .15s; }
.menu-switch::after { content: ""; position: absolute; top: .15rem; left: .15rem; width: .75rem; height: .75rem; border-radius: 50%; background: #fff; transition: left .15s; }
.menu-switch.is-on { background: var(--af-navy-fill); }
.menu-switch.is-on::after { left: 1rem; }
/* The user menu's theme switch: a label, then one small button per choice. */
.theme-menu { display: flex; align-items: center; justify-content: space-between; gap: .75rem; padding: .25rem 1rem; }
.theme-menu-label { color: var(--cui-dropdown-link-color); white-space: nowrap; }
.theme-menu-choices { display: inline-flex; border: 1px solid var(--af-line-strong); border-radius: 999px; padding: 1px; gap: 1px; }
.theme-menu-choice { border: 0; background: none; color: var(--af-ink-soft); border-radius: 999px; min-width: 24px; min-height: 24px; padding: .1rem .45rem; line-height: 1; }
.theme-menu-choice:hover { background: var(--af-inset); color: var(--af-ink); }
.theme-menu-choice.is-on { background: var(--af-navy-fill); color: #fff; }
.theme-menu-choice:focus-visible { outline: 2px solid var(--af-accent); outline-offset: 1px; }

@media (max-width: 575.98px) {
  .crumbs > :not(:last-child) { display: none; }
  .crumbs > :last-child::before { content: none; }
  .env-badge { margin-left: .25rem; }
}

/* The admin shell, after /opt/eos (admin.css, 2026-09-26): the sections down the left in
   named groups, the one being worked on to the right, alone. Above lg a grid with the
   column pinned under the header; below it the same list is one row that scrolls
   sideways, the group titles hidden. Used by Settings. */
.admin-shell { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.25rem 2rem; align-items: start; }
.admin-nav .nav-link { display: flex; align-items: center; gap: .6rem; color: var(--af-ink); }
.admin-nav-icon { width: 1.1rem; text-align: center; color: var(--af-ink-soft); flex: 0 0 auto; }
.admin-nav-flag { width: .5rem; height: .5rem; margin-left: auto; border-radius: 50%; background: var(--af-accent); flex: 0 0 auto; }
@media (min-width: 992px) {
  .admin-shell { grid-template-columns: 13.5rem minmax(0, 1fr); }
  .admin-nav { position: sticky; top: 4.5rem; max-height: calc(100vh - 6rem); overflow-y: auto; padding-right: .25rem; }
  .admin-nav > .nav { flex-direction: column; gap: 1px; }
  .admin-nav-title { margin-top: 1rem; padding: 0 .75rem .35rem; font-size: .7rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--af-ink-soft); }
  .admin-nav-title:first-child { margin-top: .35rem; }
  .admin-nav .nav-link { padding: .45rem .75rem; border-radius: var(--af-radius-sm); border-left: 3px solid transparent; }
  .admin-nav .nav-link:hover { background: var(--af-inset); }
  .admin-nav .nav-link.active { background: var(--af-card); border-left-color: var(--af-accent); box-shadow: 0 1px 2px rgba(31, 41, 51, .08); font-weight: 600; }
  .admin-nav .nav-link.active .admin-nav-icon { color: var(--af-navy-ink); }
}
@media (max-width: 991.98px) {
  .admin-nav { overflow-x: auto; border-bottom: 1px solid var(--af-line); scroll-snap-type: x proximity; }
  .admin-nav > .nav { width: max-content; min-width: 100%; }
  .admin-nav-title, .admin-nav-icon { display: none; }
  .admin-nav .nav-item { scroll-snap-align: start; }
  .admin-nav .nav-link { white-space: nowrap; padding: .5rem .75rem; border-bottom: 2px solid transparent; }
  .admin-nav .nav-link.active { font-weight: 600; border-bottom-color: var(--af-accent); }
}
.admin-main { max-width: 56rem; }
.admin-head { margin-bottom: 1.25rem; }
.admin-head h1 { font-weight: 650; letter-spacing: -.01em; }
/* Two lines kept for the purpose, so every section's card starts at the same height. */
.admin-subheading { color: var(--af-ink-soft); margin: 0; min-height: 2.6em; max-width: 44rem; }
.af-card { background: var(--af-card); border: 1px solid var(--af-line); border-radius: var(--af-radius); box-shadow: var(--af-shadow); padding: 1.25rem 1.5rem 1.5rem; }
.af-card .form-label { font-weight: 600; font-size: .8125rem; margin-bottom: .3rem; }
.settings-key { font-size: .75rem; color: var(--af-ink-soft); }
/* Save for the whole section, stuck to the bottom of the view while the form is longer
   than it; it lights (navy edge, the chip) when something is unsaved. */
.af-save-bar { position: sticky; bottom: 0; z-index: 5; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; margin-top: 1rem; padding: .75rem 1rem; background: var(--af-card); border: 1px solid var(--af-line); border-radius: var(--af-radius-sm); box-shadow: var(--af-shadow); }
.af-save-bar .unsaved-chip { display: none; align-items: center; gap: .4rem; font-size: .8125rem; font-weight: 600; white-space: nowrap; }
form.is-dirty .af-save-bar { border-color: var(--af-navy-soft); }
form.is-dirty .af-save-bar .unsaved-chip { display: inline-flex; }

/* ------------------------------------------------------------------ what's new
   After /opt/eos's What's new: a timeline, minor releases filled, patches hollow, the
   running one in the accent colour. */
.wn-tools { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .75rem; margin: 0 0 1.25rem; }
.wn-filter { display: flex; flex-wrap: wrap; gap: .4rem; }
.wn-search { display: flex; align-items: center; gap: .5rem; flex: 1 1 14rem; max-width: 20rem; }
.wn-search .form-control { flex: 1 1 auto; }
.wn-clear { font-size: .875rem; white-space: nowrap; }
mark.wn-hit { padding: 0 .1em; background: color-mix(in srgb, var(--af-accent) 35%, transparent); color: inherit; border-radius: .15rem; }
.wn-chip { display: inline-flex; align-items: center; min-height: 2rem; padding: .25rem .8rem; border: 1px solid var(--af-line); border-radius: 999px; background: var(--af-card); color: var(--af-ink-soft); font-size: .85rem; font-weight: 500; text-decoration: none; }
.wn-chip:hover { border-color: var(--af-line-strong); color: var(--af-ink); }
.wn-chip.is-on { border-color: var(--af-navy-fill); background: var(--af-navy-fill); color: #fff; }
.wn-timeline { position: relative; margin: 0; padding: 0; list-style: none; }
.wn-release { position: relative; display: grid; grid-template-columns: 9.5rem 1fr; gap: 0 1.5rem; padding: 0 0 1.5rem; }
.wn-release::before { content: ""; position: absolute; top: .9rem; bottom: -.9rem; left: calc(9.5rem + .75rem - 1px); width: 2px; background: var(--af-line); }
.wn-release:last-child::before { bottom: auto; height: 0; }
.wn-marker { position: absolute; top: .55rem; left: calc(9.5rem + .75rem - .45rem); width: .9rem; height: .9rem; border: 2px solid var(--af-navy-fill); border-radius: 50%; background: var(--af-canvas); z-index: 1; }
.wn-release.is-minor .wn-marker { background: var(--af-navy-fill); }
.wn-release.is-running .wn-marker { border-color: var(--af-accent); background: var(--af-accent); box-shadow: 0 0 0 4px color-mix(in srgb, var(--af-accent) 22%, transparent); }
.wn-when { display: flex; flex-direction: column; align-items: flex-end; gap: .15rem; padding-top: .35rem; text-align: right; }
.wn-version { font-size: 1.05rem; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--af-ink); text-decoration: none; }
.wn-release.is-minor .wn-version { font-size: 1.2rem; }
.wn-version:hover { color: var(--af-link); }
.wn-date { font-size: .82rem; color: var(--af-ink-soft); }
.wn-flag { display: inline-block; margin-top: .25rem; padding: .1rem .55rem; border-radius: 999px; font-size: .7rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.wn-flag-running { background: var(--af-accent); color: var(--af-navy); }
.wn-flag-latest { background: var(--af-navy-fill); color: #fff; }
.wn-flag-ahead { background: var(--af-canvas-deep); color: var(--af-ink-soft); }
.wn-card { margin: 0; }
.wn-release.is-running .wn-card { border-color: var(--af-accent); }
.wn-heading { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; margin: 0 0 .35rem; font-size: 1.15rem; font-weight: 600; color: var(--af-ink); }
.wn-kinds { display: inline-flex; flex-wrap: wrap; gap: .3rem; }
.wn-kind { display: inline-block; padding: .1rem .5rem; border-radius: 999px; font-size: .68rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.wn-kind-new { background: var(--af-success-wash); color: var(--af-success-ink); }
.wn-kind-improved { background: var(--af-info-wash); color: var(--af-info-ink); }
.wn-kind-fixed { background: var(--af-warning-wash); color: var(--af-warning-ink); }
.wn-kind-internal { background: var(--af-canvas-deep); color: var(--af-ink-soft); }
.wn-summary { margin: 0 0 .75rem; font-size: 1.02rem; color: var(--af-ink); line-height: 1.5; }
.wn-group + .wn-group { margin-top: .85rem; }
.wn-group-title { display: flex; align-items: center; gap: .45rem; margin: 0 0 .35rem; font-size: .72rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--af-ink-soft); }
.wn-dot { width: .55rem; height: .55rem; border-radius: 50%; }
.wn-group-new .wn-dot { background: var(--af-success-edge); }
.wn-group-improved .wn-dot { background: var(--af-info-edge); }
.wn-group-fixed .wn-dot { background: var(--af-warning-edge); }
.wn-group-internal .wn-dot { background: var(--af-ink-faint); }
.wn-plan { margin-bottom: 1rem; }
.wn-plan-progress .wn-dot { background: var(--af-info-edge); }
.wn-plan-planned .wn-dot { background: var(--af-ink-faint); }
.wn-plan-body { margin-top: .15rem; color: var(--af-ink-soft); }
.wn-plan-body > :last-child { margin-bottom: 0; }
.wn-changes { margin: 0; padding: 0; list-style: none; }
.wn-change { position: relative; padding: .3rem 0 .3rem 1.1rem; line-height: 1.5; }
.wn-change::before { content: ""; position: absolute; top: .95rem; left: .2rem; width: .35rem; height: .35rem; border-radius: 50%; background: var(--af-line-strong); }
.wn-change + .wn-change { border-top: 1px solid var(--af-line-soft); }
.wn-change p, .wn-change ul { margin: 0; }
.wn-change strong { color: var(--af-ink); }
.wn-change code, .wn-summary code { background: var(--af-inset); color: var(--af-ink); }
.wn-admin { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem 1rem; margin-top: .9rem; padding-top: .6rem; border-top: 1px solid var(--af-line-soft); font-size: .8rem; color: var(--af-ink-soft); }
@media (max-width: 767.98px) {
  .wn-release { grid-template-columns: 1fr; gap: .5rem; padding-left: 1.75rem; }
  .wn-release::before { left: calc(.75rem - 1px); }
  .wn-marker { left: calc(.75rem - .45rem); }
  .wn-when { flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: .5rem .75rem; padding-top: .2rem; text-align: left; }
  .wn-flag { margin-top: 0; }
}
/* The version by the brand opens What's new; an accent dot (as /opt/eos) while the
   newest release is unread. Light on the navy sidebar, white on hover. */
.brand-version-link { position: relative; color: rgba(255, 255, 255, .78); text-decoration: none; font-variant-numeric: tabular-nums; }
.brand-version-link:hover, .brand-version-link:focus-visible { color: #fff; text-decoration: underline; text-underline-offset: .2em; }
.brand-version-link.is-unread { color: #fff; }
.brand-version-link.is-unread::after { content: ""; position: absolute; top: -.15em; right: -.6em; width: .5em; height: .5em; border-radius: 50%; background: var(--af-accent); box-shadow: 0 0 0 2px var(--af-navy); }
.wn-new-badge { background: var(--af-accent); color: var(--af-navy); font-size: .65rem; font-weight: 600; }

/* ------------------------------------------------------------------ ask dispatch
   The chat (templates/ask/_chat.html, static/js/ask.js), after /opt/eos's Ask AvatarOS. */
.sidebar-nav { flex: 1; }
.sidebar-nav .nav-item.nav-foot { margin-top: auto; padding-top: .5rem; border-top: 1px solid rgba(255, 255, 255, .08); }
@keyframes ask-breathe { 0%, 80%, 100% { opacity: .3; transform: scale(.8); } 40% { opacity: 1; transform: scale(1); } }
/* Reduced motion (UI-01): nothing pulses, fades or slides, here or in CoreUI. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Helpers that need script (Test, Check on the host, Refresh, the Ask composer): app.js
   marks <html class="js">; without it they are not drawn rather than doing nothing. */
html:not(.js) .js-only { display: none !important; }
/* Keyboard shortcuts (app.js): the row chosen with j/k, outlined in navy on a light blue
   wash, and the help sheet. */
.table > tbody > tr.is-chosen > * { --cui-table-bg-state: var(--af-info-wash); box-shadow: inset 0 2px 0 var(--af-navy), inset 0 -2px 0 var(--af-navy); }
.table > tbody > tr.is-chosen > :first-child { box-shadow: inset 0 2px 0 var(--af-navy), inset 0 -2px 0 var(--af-navy), inset 2px 0 0 var(--af-navy); }
.table > tbody > tr.is-chosen > :last-child { box-shadow: inset 0 2px 0 var(--af-navy), inset 0 -2px 0 var(--af-navy), inset -2px 0 0 var(--af-navy); }
.shortcuts-heading { font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--af-ink-soft); margin: 1rem 0 .3rem; }
.shortcuts-grid > section:first-child .shortcuts-heading { margin-top: 0; }
.shortcuts-list { margin: 0; }
.shortcuts-list > div { display: flex; align-items: baseline; gap: 1rem; padding: .3rem 0; border-bottom: 1px solid var(--af-line-soft); }
.shortcuts-list dt { order: 2; margin-left: auto; font-weight: normal; white-space: nowrap; color: var(--af-ink-soft); }
.shortcuts-list dd { margin: 0; }
.shortcuts-modal kbd { font-family: var(--af-mono); font-size: .75rem; padding: .05rem .4rem; color: var(--af-ink); background: var(--af-inset); border: 1px solid var(--af-line); border-bottom-width: 2px; border-radius: 4px; box-shadow: none; }

/* Figures line up in columns. */
.table td, .badge, .stat-figure { font-variant-numeric: tabular-nums; }

/* Wide records on a narrow screen (after /opt/eos, STYLEGUIDE "Wide data on a narrow
   screen"): below md a .table-stack-md table becomes a list of cards, one per row, each
   cell a labelled line from its data-label. The identity cell takes .stack-head (the top
   of the card, no label). The card around the table says it hosts one with .stack-host and
   gives up its surface below md, so the page shows between the cards. Use it for record
   lists (people, customers); time series and matrices scroll sideways instead. */
@media (max-width: 767.98px) {
  .stack-host { background-color: transparent; border: 0; box-shadow: none; }
  .stack-host > .card-body, .stack-host > .table-responsive { padding-inline: 0; }
  .stack-host > .card-header { padding-inline: 0; background-color: transparent; border-bottom: 0; }
  .table-stack-md, .table-stack-md > tbody, .table-stack-md > tbody > tr,
  .table-stack-md > tbody > tr > th, .table-stack-md > tbody > tr > td { display: block; width: auto; }
  .table-stack-md { border: 0; }
  .table-stack-md > thead { display: none; }
  .table-stack-md > tbody > tr { margin-bottom: .75rem; background-color: var(--af-card); border: 1px solid var(--af-line); border-radius: var(--af-radius-sm); overflow: hidden; }
  .table-stack-md > tbody > tr:last-child { margin-bottom: 0; }
  .table-stack-md > tbody > tr > th, .table-stack-md > tbody > tr > td { border: 0; padding: .5rem .75rem; }
  .table-stack-md > tbody > tr > .stack-head { background-color: var(--af-cap); border-bottom: 1px solid var(--af-line-soft); }
  .table-stack-md > tbody > tr > td[data-label] { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; text-align: end; }
  .table-stack-md > tbody > tr > td[data-label]::before { content: attr(data-label); flex: 0 0 auto; font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em; color: var(--af-ink-soft); text-align: start; }
  .table-stack-md > tbody > tr > td[data-label] + td[data-label] { border-top: 1px solid var(--af-line-soft); }
  /* A cell holding only the "–" placeholder says nothing on a card. */
  .table-stack-md > tbody > tr > td[data-label]:has(> .text-body-tertiary:only-child) { display: none; }
}

/* A sidebar item the person may not open yet (NavItem.dim_closed): there, dimmed, with
   the reason on hover and on click (keel.js), rather than missing without a word. */
.sidebar-nav .nav-link.is-closed { opacity: .5; cursor: not-allowed; }
.sidebar-nav .nav-link.is-closed:hover { opacity: .7; }

/* A button busy with its form's post (button[data-busy-label], keel.js). */
button[aria-busy="true"] { cursor: progress; }

/* Viewing the app as someone else (keel.viewing): unmistakable, on every page. */
.viewing-banner { background: var(--af-navy); color: #fff; padding: .5rem 1rem; font-size: .9rem; border-bottom: 3px solid var(--af-accent); }
.viewing-banner i { color: var(--af-accent); }

/* Charts ([data-chart], keel.js): the canvas fills its figure. */
.keel-chart { position: relative; margin: 0; }
/* Drag to reorder ([data-sortable], keel.js). */
[data-sortable] [data-sort-grip] { cursor: grab; color: var(--af-ink-faint); }
[data-sortable] .sort-ghost { opacity: .4; background: var(--af-info-wash); }
[data-sortable] .sort-chosen { box-shadow: var(--af-shadow); }
