/* ============================================================
   platform.css — The Orrery platform chrome
   Extracted from the design prototype (Platform Chrome.dc.html).
   ONE semantic token set, dark-first; light overrides adjacent.
   Hand-authored vanilla CSS — no build step.
   ============================================================ */

/* ============================================================
   platform.css — campaigns.crepeau.io chrome
   ONE semantic token set. Dark is the base (:root).
   Light overrides live in the adjacent block below and are
   commented so the two never drift.
   ============================================================ */

/* ---- DARK (base) ---- */
:root{
  --surface:#14161a; --surface-elevated:#1c2028; --surface-sunken:#0e1013;
  --surface-hover:#232833; --border:#2a2f3a; --border-strong:#3a414f;
  --text:#e7e9ee; --text-muted:#b6becb; --text-faint:#8a93a2;
  --accent:#5e9bf0; --accent-strong:#7db0ff; --accent-weak:rgba(94,155,240,.14);
  --success:#43b581; --success-weak:rgba(67,181,129,.15);
  --warning:#e0a13c; --warning-weak:rgba(224,161,60,.15);
  --danger:#e5545a;  --danger-weak:rgba(229,84,90,.15);
  --focus-ring:rgba(94,155,240,.55); --overlay:rgba(8,10,14,.66);
  --shadow-1:0 1px 2px rgba(0,0,0,.45);
  --shadow-2:0 8px 24px rgba(0,0,0,.5);
  --shadow-3:0 24px 64px rgba(0,0,0,.62);
  --astral:radial-gradient(120% 90% at 50% -30%, rgba(94,155,240,.16), rgba(94,155,240,.05) 38%, transparent 68%);

  /* type */
  --font-ui:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --font-mono:ui-monospace,"SF Mono",SFMono-Regular,"JetBrains Mono",Menlo,Consolas,monospace;
  --font-display:var(--font-ui);
  --fs-xs:.75rem; --fs-sm:.875rem; --fs-base:1rem; --fs-lg:1.25rem; --fs-xl:1.75rem; --fs-2xl:2.25rem;
  /* spacing */
  --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px; --space-5:20px;
  --space-6:24px; --space-8:32px; --space-10:40px; --space-12:48px; --space-16:64px;
  /* radius + motion */
  --r-1:6px; --r-2:10px; --r-3:14px; --r-full:999px;
  --motion-fast:120ms; --motion:180ms; --ease:cubic-bezier(.2,.6,.2,1);
  --maxw:1120px;
  color-scheme:dark;
}

/* ---- LIGHT (override) — keep every var in the SAME ORDER as dark above ---- */
@media (prefers-color-scheme:light){ :root:not([data-theme]){
  --surface:#f7f8fa; --surface-elevated:#ffffff; --surface-sunken:#eceef2;
  --surface-hover:#eef1f6; --border:#d8dce3; --border-strong:#c2c8d2;
  --text:#1a1d23; --text-muted:#464e5b; --text-faint:#6d7683;
  --accent:#2f6fe0; --accent-strong:#1f57c2; --accent-weak:rgba(47,111,224,.12);
  --success-weak:rgba(67,181,129,.15); --warning-weak:rgba(224,161,60,.15); --danger-weak:rgba(229,84,90,.15);
  --focus-ring:rgba(47,111,224,.5); --overlay:rgba(20,25,35,.42);
  --shadow-1:0 1px 2px rgba(20,25,35,.06),0 1px 3px rgba(20,25,35,.09);
  --shadow-2:0 8px 24px rgba(20,25,35,.12);
  --shadow-3:0 24px 64px rgba(20,25,35,.18);
  --astral:radial-gradient(120% 90% at 50% -30%, rgba(47,111,224,.10), rgba(47,111,224,.03) 40%, transparent 70%);
  color-scheme:light;
}}
[data-theme="light"]{
  --surface:#f7f8fa; --surface-elevated:#ffffff; --surface-sunken:#eceef2;
  --surface-hover:#eef1f6; --border:#d8dce3; --border-strong:#c2c8d2;
  --text:#1a1d23; --text-muted:#464e5b; --text-faint:#6d7683;
  --accent:#2f6fe0; --accent-strong:#1f57c2; --accent-weak:rgba(47,111,224,.12);
  --focus-ring:rgba(47,111,224,.5); --overlay:rgba(20,25,35,.42);
  --shadow-1:0 1px 2px rgba(20,25,35,.06),0 1px 3px rgba(20,25,35,.09);
  --shadow-2:0 8px 24px rgba(20,25,35,.12);
  --shadow-3:0 24px 64px rgba(20,25,35,.18);
  --astral:radial-gradient(120% 90% at 50% -30%, rgba(47,111,224,.10), rgba(47,111,224,.03) 40%, transparent 70%);
  color-scheme:light;
}
[data-theme="dark"]{ color-scheme:dark; } /* forces base dark vars in a light OS */

*{box-sizing:border-box}
html,body{margin:0}
body{background:var(--surface);color:var(--text);font-family:var(--font-ui);font-size:var(--fs-base);line-height:1.5;-webkit-font-smoothing:antialiased;}
h1,h2,h3,h4,p{margin:0}
a{color:var(--accent);text-decoration:none}
a:hover{color:var(--accent-strong)}
button{font:inherit;color:inherit}
:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px;border-radius:var(--r-1)}
::selection{background:var(--accent-weak)}

/* scrollbars */
*::-webkit-scrollbar{width:10px;height:10px}
*::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:var(--r-full);border:2px solid transparent;background-clip:padding-box}

/* ---------- primitives ---------- */
.eyebrow{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);font-weight:500}
.mono{font-family:var(--font-mono)}
.muted{color:var(--text-muted)}
.faint{color:var(--text-faint)}
.rule{height:1px;background:var(--border);border:0;margin:var(--space-6) 0}
.icon{width:1em;height:1em;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;flex:none}
/* Status-glyph tones (hub #37): the sprite draws with stroke:currentColor, so setting color tints it.
   Class-based (never inline style) so the strict CSP holds (ADR 0008). Used via icon(name,{tone}). */
.icon--danger{color:var(--danger)}
.icon--warning{color:var(--warning)}
.icon--success{color:var(--success)}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:var(--space-2);padding:9px 15px;border-radius:var(--r-1);
  font-size:var(--fs-sm);font-weight:600;border:1px solid transparent;cursor:pointer;
  transition:background var(--motion-fast) var(--ease),border-color var(--motion-fast) var(--ease),color var(--motion-fast) var(--ease);white-space:nowrap}
.btn--primary{background:var(--accent);color:#fff;border-color:var(--accent)}
.btn--primary:hover{background:var(--accent-strong);border-color:var(--accent-strong)}
.btn--danger{background:var(--danger);color:#fff;border-color:var(--danger)}
.btn--danger:hover{filter:brightness(1.08)}
.btn--secondary{background:transparent;color:var(--text);border-color:var(--border-strong)}
.btn--secondary:hover{background:var(--surface-hover);border-color:var(--text-faint)}
.btn--ghost{background:transparent;color:var(--text-muted);border-color:transparent;padding:6px 10px}
.btn--ghost:hover{background:var(--surface-hover);color:var(--text)}
.btn--sm{padding:5px 10px;font-size:var(--fs-xs)}
.btn[disabled],.btn:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}

/* ---------- fields ---------- */
.field{display:flex;flex-direction:column;gap:var(--space-2)}
.field > label{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);font-weight:500}
.input{width:100%;background:var(--surface-sunken);color:var(--text);border:1px solid var(--border);
  border-radius:var(--r-1);padding:10px 12px;font:inherit;font-size:var(--fs-sm);
  transition:border-color var(--motion-fast) var(--ease),box-shadow var(--motion-fast) var(--ease)}
.input::placeholder{color:var(--text-faint)}
.input:hover{border-color:var(--border-strong)}
.input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--focus-ring)}
.input[aria-invalid="true"]{border-color:var(--danger);box-shadow:0 0 0 3px var(--danger-weak)}
/* Required/optional marking (DM 2026-07-19): an accent * keys the required fields, an inline tag
   names the optional ones, and .form-legend keys the asterisk at the foot of the form. The tag
   un-does the label's uppercase/tracking so it reads as prose beside the label, not as part of it. */
.field__req{color:var(--accent)}
.field__optional{text-transform:none;letter-spacing:0;color:var(--text-faint);font-weight:400}
.form-legend{border-top:1px solid var(--border);margin-top:var(--space-2);padding-top:var(--space-3);
  font-size:var(--fs-xs);color:var(--text-muted)}
.hint{font-size:var(--fs-xs);color:var(--text-muted)}
.hint--error{color:var(--danger)}
.hint--ok{color:var(--success)}

/* ---------- pills / chips / badges ---------- */
.pill{display:inline-flex;align-items:center;gap:6px;padding:3px 9px;border-radius:var(--r-full);
  font-size:var(--fs-xs);font-weight:600;letter-spacing:.02em;white-space:nowrap}
.pill .dot{width:6px;height:6px;border-radius:var(--r-full);background:currentColor;flex:none}
.pill--success{background:var(--success-weak);color:var(--success)}
.pill--danger{background:var(--danger-weak);color:var(--danger)}
.pill--warning{background:var(--warning-weak);color:var(--warning)}
.pill--muted{background:var(--surface-hover);color:var(--text-muted)}
.pill--accent{background:var(--accent-weak);color:var(--accent)}
/* A .pill can be an interactive toggle (the users-table active/disabled switch):
   render it as a <button class="pill …"> — inherit the pill look, add affordance. */
button.pill{font:inherit;cursor:pointer;border:0;line-height:inherit;
  transition:filter var(--motion-fast),background var(--motion-fast)}
button.pill:hover{filter:brightness(1.08)}
button.pill:focus-visible{outline:2px solid var(--focus-ring);outline-offset:2px}
/* row-actions cell/cluster wraps its controls rather than overflowing the row. */
.row-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);justify-content:flex-end}
.chip{display:inline-flex;align-items:center;gap:6px;padding:3px 9px;border-radius:var(--r-1);
  background:var(--surface-sunken);border:1px solid var(--border);color:var(--text-muted);font-size:var(--fs-xs);font-weight:500}
.badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;
  border-radius:var(--r-full);background:var(--accent);color:#fff;font-size:11px;font-weight:600;line-height:1}

/* ---------- card ---------- */
.card{background:var(--surface-elevated);border:1px solid var(--border);border-radius:var(--r-2);
  box-shadow:var(--shadow-1);transition:border-color var(--motion) var(--ease)}
.card:hover{border-color:var(--border-strong)}
.card__pad{padding:var(--space-5)}

/* ---------- tiles ---------- */
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:var(--space-4)}
.tile{position:relative;display:flex;flex-direction:column;gap:var(--space-3);padding:var(--space-5);
  background:var(--surface-elevated);border:1px solid var(--border);border-radius:var(--r-2);box-shadow:var(--shadow-1);
  cursor:pointer;transition:border-color var(--motion) var(--ease),background var(--motion) var(--ease)}
.tile:hover{border-color:var(--accent);background:var(--surface-hover)}
.tile__icon{width:40px;height:40px;display:grid;place-items:center;border-radius:var(--r-2);
  background:var(--accent-weak);color:var(--accent);font-size:20px}
.tile__title{font-weight:600;font-size:var(--fs-base);color:var(--text)}
.tile__blurb{font-size:var(--fs-sm);color:var(--text-muted);line-height:1.45}
.tile .badge{position:absolute;top:var(--space-4);right:var(--space-4)}

/* ---------- table ---------- */
.table-scroll{overflow-x:auto;border:1px solid var(--border);border-radius:var(--r-2);background:var(--surface-elevated)}
/* On desktop the admin tables fit, so drop the scroll clip → a row's Edit dropdown
   (position:absolute) can extend past the table box instead of being cut off. Narrow
   screens keep overflow-x:auto for horizontal scroll (dropdowns there are enhancement). */
@media(min-width:960px){.table-scroll{overflow:visible}}
table.table{width:100%;border-collapse:collapse;font-size:var(--fs-sm);min-width:640px}
.table th{text-align:left;font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);
  font-weight:500;padding:11px var(--space-4);border-bottom:1px solid var(--border);white-space:nowrap;background:var(--surface-elevated)}
.table td{padding:13px var(--space-4);border-bottom:1px solid var(--border);vertical-align:middle}
.table tbody tr:last-child td{border-bottom:0}
.table tbody tr:nth-child(even){background:var(--surface-sunken)}
.table tbody tr:hover{background:var(--surface-hover)}
.table .row-actions{display:flex;gap:var(--space-1);justify-content:flex-end}
.select{background:var(--surface-sunken);color:var(--text);border:1px solid var(--border);border-radius:var(--r-1);
  padding:6px 10px;font:inherit;font-size:var(--fs-xs)}
.select:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--focus-ring)}

/* ---------- notification item ---------- */
.notif{display:flex;gap:var(--space-3);padding:var(--space-4);border-bottom:1px solid var(--border);position:relative}
.notif:last-child{border-bottom:0}
.notif:hover{background:var(--surface-hover)}
.notif__glyph{width:34px;height:34px;flex:none;border-radius:var(--r-2);display:grid;place-items:center;font-size:16px;
  background:var(--accent-weak);color:var(--accent)}
.notif--service .notif__glyph{background:var(--warning-weak);color:var(--warning)}
.notif__body{flex:1;min-width:0}
.notif__title{font-weight:500;color:var(--text-muted)}
.notif__text{font-size:var(--fs-sm);color:var(--text-faint);margin-top:2px}
.notif__ts{font-size:var(--fs-xs);color:var(--text-faint);white-space:nowrap}
.notif--unread{background:var(--accent-weak)}
.notif--unread::before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--accent)}
.notif--unread .notif__title{color:var(--text);font-weight:600}
.notif--unread .notif__text{color:var(--text-muted)}
.notif__dot{width:8px;height:8px;border-radius:var(--r-full);background:var(--accent);flex:none;margin-top:7px}

/* ---------- connection card ---------- */
.conn{display:flex;align-items:center;gap:var(--space-4);padding:var(--space-4) var(--space-5);
  background:var(--surface-elevated);border:1px solid var(--border);border-radius:var(--r-2);box-shadow:var(--shadow-1)}
.conn__icon{width:42px;height:42px;flex:none;border-radius:var(--r-2);display:grid;place-items:center;
  background:var(--surface-sunken);border:1px solid var(--border);color:var(--text-muted);font-size:20px}
.conn__main{flex:1;min-width:0}
.conn__label{font-weight:600}
.conn__meta{font-size:var(--fs-sm);color:var(--text-muted);margin-top:2px;display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}
.conn__actions{display:flex;gap:var(--space-2)}
/* Connection cards are full-width horizontal rows — a single-column stack, not
   the narrow auto-fill .tiles grid (which would trap each card in a ~240px
   column and force the provider title + status pill to wrap). */
.conn-list{display:flex;flex-direction:column;gap:var(--space-4)}

/* ---------- states ---------- */
.state{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--space-3);
  padding:var(--space-12) var(--space-6);border:1px dashed var(--border);border-radius:var(--r-2)}
.state__disc{width:52px;height:52px;border-radius:var(--r-full);display:grid;place-items:center;font-size:24px;
  background:var(--accent-weak);color:var(--accent)}
.state--error .state__disc{background:var(--danger-weak);color:var(--danger)}
.state__title{font-weight:600;font-size:var(--fs-lg)}
.state__body{color:var(--text-muted);max-width:38ch;font-size:var(--fs-sm)}
.spinner{width:34px;height:34px;border-radius:var(--r-full);border:3px solid var(--border);border-top-color:var(--accent);
  animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.shimmer{position:relative;overflow:hidden;background:var(--surface-sunken);border-radius:var(--r-1)}
.shimmer::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,var(--surface-hover),transparent);animation:sh 1.4s infinite}
@keyframes sh{100%{transform:translateX(100%)}}
@media (prefers-reduced-motion:reduce){
  .spinner{animation:none;border-top-color:var(--accent)}
  .shimmer::after{animation:none}
  *{transition-duration:1ms!important}
}

/* ---------- modal ---------- */
.overlay{position:absolute;inset:0;background:var(--overlay);display:grid;place-items:center;padding:var(--space-6);z-index:40;
  backdrop-filter:blur(1px)}
.modal{width:100%;max-width:440px;background:var(--surface-elevated);border:1px solid var(--border-strong);
  border-radius:var(--r-3);box-shadow:var(--shadow-3);overflow:hidden}
.modal__title{padding:var(--space-5) var(--space-6) 0;font-size:var(--fs-lg);font-weight:600}
.modal__body{padding:var(--space-3) var(--space-6);color:var(--text-muted);font-size:var(--fs-sm)}
.modal__actions{display:flex;justify-content:flex-end;gap:var(--space-2);padding:var(--space-4) var(--space-6) var(--space-6)}

/* ---------- uimodal — JS-injected dialogs from site/js/ui.js (messageDialog /
   confirmDialog / promptNewPassword). Appended to <body>, so position:fixed to
   overlay the viewport regardless of scroll, and z-index above the [DEV] ribbon
   (envbar) + floating theme toggle (z-index:60). ---------- */
.uimodal-overlay{position:fixed;inset:0;z-index:100;background:var(--overlay);backdrop-filter:blur(1px);
  display:grid;place-items:center;padding:var(--space-6)}
/* The dialog NEVER exceeds the overlay's box: it is a flex column capped at the viewport, with the
   CONTENT scrolling while the title + action row stay pinned. Without the cap a modal taller than the
   viewport (the Add-connection form on a ~720px laptop) renders its buttons below the fold, and because
   the overlay is position:fixed there is nothing to scroll — Save/Test become literally unreachable. */
.uimodal{width:100%;max-width:440px;background:var(--surface-elevated);border:1px solid var(--border-strong);
  border-radius:var(--r-3);box-shadow:var(--shadow-3);overflow:hidden;
  max-height:100%;display:flex;flex-direction:column}
.uimodal > form{display:flex;flex-direction:column;min-height:0}   /* form modals: fields scroll, actions pinned */
.uimodal .pw-fields,.uimodal-body{overflow-y:auto;min-height:0}    /* the two body shapes */
.uimodal--wide{max-width:880px}   /* the delivery-health Details modal (roomier send-as grid) */
.uimodal-title{padding:var(--space-5) var(--space-6) 0;font-size:var(--fs-lg);font-weight:600}
.uimodal-msg{padding:var(--space-3) var(--space-6);color:var(--text-muted);font-size:var(--fs-sm)}
.uimodal .pw-fields{padding:var(--space-3) var(--space-6)}
.uimodal-actions{display:flex;justify-content:flex-end;gap:var(--space-2);padding:var(--space-4) var(--space-6) var(--space-6)}
.uimodal-btn{display:inline-flex;align-items:center;gap:var(--space-2);padding:9px 15px;border-radius:var(--r-1);
  font-size:var(--fs-sm);font-weight:600;border:1px solid var(--border-strong);background:transparent;color:var(--text);cursor:pointer;
  transition:background var(--motion-fast) var(--ease),border-color var(--motion-fast) var(--ease),color var(--motion-fast) var(--ease)}
.uimodal-btn:hover{background:var(--surface-hover);border-color:var(--text-faint)}
.uimodal-btn.primary{background:var(--accent);color:#fff;border-color:var(--accent)}
.uimodal-btn.primary:hover{background:var(--accent-strong);border-color:var(--accent-strong)}
.uimodal-btn.danger{background:var(--danger);color:#fff;border-color:var(--danger)}
.uimodal-btn.danger:hover{filter:brightness(1.08)}
.uimodal-btn[disabled],.uimodal-btn:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}

/* ---------- header / nav (in-screen chrome) ---------- */
.screen{position:relative;background:var(--surface);border:1px solid var(--border);border-radius:var(--r-3);overflow:hidden;isolation:isolate}
.screen__glow{position:absolute;inset:0 0 auto 0;height:280px;background:var(--astral);pointer-events:none;z-index:0}
.starfield{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:.55;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 18%, var(--text-faint), transparent),
    radial-gradient(1px 1px at 28% 42%, var(--text-faint), transparent),
    radial-gradient(1.5px 1.5px at 47% 12%, var(--accent), transparent),
    radial-gradient(1px 1px at 63% 34%, var(--text-faint), transparent),
    radial-gradient(1px 1px at 78% 20%, var(--text-faint), transparent),
    radial-gradient(1.5px 1.5px at 88% 46%, var(--text-faint), transparent),
    radial-gradient(1px 1px at 38% 62%, var(--text-faint), transparent),
    radial-gradient(1px 1px at 70% 68%, var(--accent), transparent),
    radial-gradient(1px 1px at 18% 78%, var(--text-faint), transparent),
    radial-gradient(1.5px 1.5px at 55% 84%, var(--text-faint), transparent);
  background-repeat:no-repeat;
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 45%,transparent 80%);
  mask-image:linear-gradient(180deg,#000 0%,#000 45%,transparent 80%)}
@media (prefers-reduced-motion:no-preference){
  .starfield{animation:twinkle 6s var(--ease) infinite alternate}
}
@keyframes twinkle{from{opacity:.4}to{opacity:.7}}
.appbar{position:relative;z-index:2;display:flex;align-items:center;gap:var(--space-4);
  padding:0 var(--space-6);height:60px;background:var(--surface-elevated);border-bottom:1px solid var(--border)}
.brand{display:flex;align-items:center;gap:var(--space-3);font-weight:600;letter-spacing:-.01em}
/* Brand-mark = SUPPLIED raster emblem (identity), placed as-is — never recolored.
   The chrome palette stays neutral; the gold lives in the artwork only. */
/* Sized here, SOURCED from markup (#187): the emblem is an OML asset now, and the server's document
   pass rewrites HTML, not stylesheets — a background:url() here would still point at the bundled
   copy that no longer serves. object-fit mirrors the old center/contain. */
.brand__mark{width:28px;height:28px;border-radius:var(--r-full);flex:none;
  object-fit:contain;display:block}
/* Header lockup = supplied horizontal raster (emblem + wordmark), placed as-is */
.brand__lockup{height:30px;width:auto;display:block;flex:none}
.brand small{color:var(--text-faint);font-weight:400;font-size:var(--fs-xs)}
.nav{display:flex;align-items:center;gap:var(--space-1);margin-left:var(--space-4)}
.nav__item{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border-radius:var(--r-1);
  background:transparent;border:0;color:var(--text-muted);font-size:var(--fs-sm);font-weight:500;cursor:pointer;
  transition:background var(--motion-fast),color var(--motion-fast)}
.nav__item:hover{background:var(--surface-hover);color:var(--text)}
.nav__item--active{background:var(--accent-weak);color:var(--text)}
.appbar__right{margin-left:auto;display:flex;align-items:center;gap:var(--space-2)}
.iconbtn{position:relative;width:36px;height:36px;border-radius:var(--r-1);background:transparent;border:1px solid transparent;
  color:var(--text-muted);display:grid;place-items:center;cursor:pointer;font-size:18px;transition:background var(--motion-fast),color var(--motion-fast)}
.iconbtn:hover{background:var(--surface-hover);color:var(--text)}
.iconbtn .badge{position:absolute;top:2px;right:2px;min-width:15px;height:15px;font-size:10px}
.avatar{width:30px;height:30px;border-radius:var(--r-full);background:var(--surface-sunken);border:1px solid var(--border-strong);
  display:grid;place-items:center;font-size:var(--fs-xs);font-weight:600;color:var(--text-muted)}
.hamburger{display:none}

/* ---------- dropdown menu (account cluster + Admin nav) ---------- */
/* A relatively-positioned wrapper; the panel is absolutely positioned under the
   trigger and hidden until app.js toggles [hidden] (which wins on !important). */
.menu{position:relative;display:inline-flex}
.menu__btn{display:inline-flex;align-items:center;gap:6px;padding:5px 8px;border-radius:var(--r-1);
  background:transparent;border:1px solid transparent;color:var(--text);font:inherit;font-size:var(--fs-sm);cursor:pointer;
  transition:background var(--motion-fast),border-color var(--motion-fast)}
.menu__btn:hover{background:var(--surface-hover)}
.menu__btn[aria-expanded="true"]{background:var(--surface-hover);border-color:var(--border)}
.menu__panel{position:absolute;top:calc(100% + 6px);right:0;z-index:80;min-width:200px;
  background:var(--surface-elevated);border:1px solid var(--border-strong);border-radius:var(--r-2);
  box-shadow:var(--shadow-3);padding:var(--space-1);display:flex;flex-direction:column;gap:2px}
/* Nav dropdown opens left-aligned (trigger sits at the far left of the bar). */
.nav__menu .menu__panel{left:0;right:auto}
.menu__item{display:flex;align-items:center;gap:var(--space-2);width:100%;text-align:left;
  padding:8px 10px;border-radius:var(--r-1);background:transparent;border:0;color:var(--text-muted);
  font-size:var(--fs-sm);font-weight:500;cursor:pointer;text-decoration:none;
  transition:background var(--motion-fast),color var(--motion-fast)}
.menu__item:hover{background:var(--surface-hover);color:var(--text)}
/* A menu item that is itself a form (the Sign-out POST) — flush with the others. */
.menu__form{padding:0}
.menu__form .btn{width:100%;justify-content:flex-start}
/* The Admin nav toggle reuses the .nav__item look. */
.nav__item--menu{border:1px solid transparent}
/* The account trigger content: avatar + identity, wrapped so app.js can target it. */
.acct__trigger{display:inline-flex;align-items:center;gap:var(--space-2)}

/* ---------- breadcrumbs ---------- */
.crumbs{display:flex;align-items:center;flex-wrap:wrap;gap:2px;font-size:var(--fs-sm);color:var(--text-muted);margin-bottom:var(--space-4)}
.crumbs__link{color:var(--text-muted);text-decoration:none;padding:2px 4px;border-radius:var(--r-1);
  transition:color var(--motion-fast),background var(--motion-fast)}
.crumbs__link:hover{color:var(--text);background:var(--surface-hover)}
.crumbs__sep{color:var(--text-faint);padding:0 2px;user-select:none}
.crumbs__here{color:var(--text);font-weight:600;padding:2px 4px}

/* ---------- identity / timestamp inline primitives ---------- */
/* Both inherit the surrounding text; identity gives a subtle "has more on hover" cue. */
.ident{color:inherit;font-weight:500}
.ident[title]{cursor:default;text-decoration:underline dotted var(--border-strong);text-underline-offset:3px}
.ts{color:inherit;white-space:nowrap}
.ts--empty{color:var(--text-faint)}

.screen__body{position:relative;z-index:1;padding:var(--space-8) var(--space-6)}
.wrap{max-width:var(--maxw);margin:0 auto}
.page-title{font-family:var(--font-display);font-size:var(--fs-xl);font-weight:600;letter-spacing:-.01em}

/* generic layout helpers */
.stack{display:flex;flex-direction:column}
.row{display:flex;align-items:center}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-6)}

@media (max-width:760px){
  .nav{display:none}
  .hamburger{display:grid}
  .grid2{grid-template-columns:1fr}
  .tiles{grid-template-columns:1fr}
}

/* ---------- harness ---------- */
.harness{display:grid;grid-template-columns:250px 1fr;min-height:100vh}
.rail{position:sticky;top:0;align-self:start;height:100vh;overflow-y:auto;background:var(--surface-elevated);
  border-right:1px solid var(--border);display:flex;flex-direction:column;padding:var(--space-5) var(--space-4)}
.rail__brand{display:flex;align-items:center;gap:var(--space-3);padding:0 var(--space-2) var(--space-5);font-weight:600}
.rail__group{font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:var(--text-faint);padding:var(--space-4) var(--space-3) var(--space-2)}
.rail__item{display:flex;align-items:center;gap:var(--space-2);width:100%;text-align:left;padding:8px var(--space-3);
  border-radius:var(--r-1);background:transparent;border:0;color:var(--text-muted);font-size:var(--fs-sm);font-weight:500;cursor:pointer;
  transition:background var(--motion-fast),color var(--motion-fast)}
.rail__item:hover{background:var(--surface-hover);color:var(--text)}
.rail__item--active{background:var(--accent-weak);color:var(--text)}
.rail__spacer{flex:1}
.toggle{display:flex;gap:4px;padding:4px;background:var(--surface-sunken);border:1px solid var(--border);border-radius:var(--r-full)}
.toggle button{flex:1;padding:6px;border-radius:var(--r-full);border:0;background:transparent;color:var(--text-muted);
  font-size:var(--fs-xs);font-weight:600;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:5px}
.toggle button[aria-pressed="true"]{background:var(--surface-elevated);color:var(--text);box-shadow:var(--shadow-1)}
.stage{padding:var(--space-8);overflow-x:hidden}
.anno{display:flex;flex-wrap:wrap;gap:var(--space-2);margin-bottom:var(--space-5)}
.anno span{font-size:var(--fs-xs);color:var(--text-muted);background:var(--surface-elevated);border:1px solid var(--border);
  border-radius:var(--r-full);padding:3px 10px}
.anno span b{color:var(--text);font-weight:600}

/* design-system page bits */
.ds-sec{margin-bottom:var(--space-12)}
.ds-sec > h2{font-size:var(--fs-lg);font-weight:600;margin-bottom:var(--space-2)}
.ds-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:var(--space-3)}
.sw{border:1px solid var(--border);border-radius:var(--r-2);overflow:hidden;background:var(--surface-elevated)}
.sw__chip{height:56px}
.sw__meta{padding:8px 10px}
.sw__name{font-size:var(--fs-xs);font-weight:600}
.sw__val{font-size:11px;color:var(--text-muted)}
.panel{border:1px solid var(--border);border-radius:var(--r-2);background:var(--surface);padding:var(--space-5)}
.panel--label{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--text-faint);margin-bottom:var(--space-4)}
.specimen{display:flex;flex-wrap:wrap;gap:var(--space-3);align-items:center}
.scale-row{display:flex;align-items:baseline;gap:var(--space-4);padding:var(--space-2) 0;border-bottom:1px solid var(--border)}
.scale-row .tok{font-size:11px;color:var(--text-faint);width:120px;flex:none}
/* ---- dashboard hero tiles: image on top, container-query icon fallback when narrow ---- */
.tile--hero{padding:0;gap:0;overflow:hidden;container-type:inline-size}
.tile--hero .tile__media{position:relative;width:100%;aspect-ratio:16/10;background:var(--surface-sunken);border-bottom:1px solid var(--border)}
.tile--hero .tile__media image-slot{position:absolute;inset:0}
.tile--hero .tile__meta{display:flex;align-items:flex-start;gap:10px;padding:var(--space-4)}
.tile--hero .tile__meta .tile__icon{display:none;width:32px;height:32px;font-size:16px}
.tile__txt{display:flex;flex-direction:column;gap:3px;min-width:0}
@container (max-width:212px){
  .tile--hero .tile__media{display:none}
  .tile--hero .tile__meta .tile__icon{display:grid}
}
/* compact icon tile — utilities and the small-screen treatment */
.tile--compact{flex-direction:row;align-items:flex-start;gap:12px}
.tile--compact .tile__icon{width:34px;height:34px;font-size:17px}

/* ---- landing: cross-fading campaign panel (atmosphere, campaign art) ---- */
.cyc-frame{position:relative;aspect-ratio:4/3;width:100%;border-radius:var(--r-3);overflow:hidden;
  border:1px solid var(--border-strong);box-shadow:var(--shadow-2);background:var(--surface-sunken)}
.cyc{position:absolute;inset:0;opacity:0;animation:cyc 32s var(--ease) infinite}
.cyc image-slot{position:absolute;inset:0}
.cyc:nth-child(1){animation-delay:0s}
.cyc:nth-child(2){animation-delay:8s}
.cyc:nth-child(3){animation-delay:16s}
.cyc:nth-child(4){animation-delay:24s}
.cyc-cap{position:absolute;left:14px;bottom:14px;z-index:6;pointer-events:none}
@keyframes cyc{0%{opacity:0}3%{opacity:1}22%{opacity:1}25%{opacity:0}100%{opacity:0}}
@media (prefers-reduced-motion:reduce){.cyc{animation:none}.cyc:first-child{opacity:1}}
/* ---- landing full-bleed hero (image spans the block; content sits on top) ---- */
.hero-full{position:relative;overflow:hidden;min-height:460px;display:flex;align-items:center;isolation:isolate}
.hero-full > .cyc,.hero-full > image-slot{position:absolute;inset:0;z-index:0}
.hero-scrim{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(90deg,rgba(8,10,14,.88),rgba(8,10,14,.6) 46%,rgba(8,10,14,.34))}
.hero-content{position:relative;z-index:2;width:100%;pointer-events:none;padding:var(--space-12) var(--space-8)}
.hero-content a,.hero-content button,.hero-content .btn{pointer-events:auto}
.hero-content h1,.hero-content .hero-tag{color:#fff}
.hero-content p{color:rgba(255,255,255,.82)}
.hero-content .eyebrow{color:rgba(255,255,255,.68)}
.btn--onimg{background:rgba(255,255,255,.06);color:#fff;border-color:rgba(255,255,255,.32)}
.btn--onimg:hover{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.6)}
/* ---- landing "inside" cover tiles: full image, text plate over the bottom ---- */
.tile--cover{position:relative;padding:0;overflow:hidden;min-height:230px;display:flex;flex-direction:column;justify-content:flex-end;container-type:inline-size}
.tile--cover image-slot{position:absolute;inset:0;z-index:0}
.tile--cover .tile__plate{position:relative;z-index:2;width:100%;pointer-events:none;
  padding:var(--space-8) var(--space-4) var(--space-4);
  background:linear-gradient(180deg,transparent,rgba(8,10,14,.55) 42%,rgba(8,10,14,.92))}
.tile--cover .tile__plate .tile__title{color:#fff}
.tile--cover .tile__plate .tile__blurb{color:rgba(255,255,255,.82)}
.tile--cover .tile__icon{background:rgba(255,255,255,.16);color:#fff}
@container (max-width:212px){
  .tile--cover{align-items:stretch;min-height:0}
  .tile--cover image-slot{display:none}
  .tile--cover .tile__plate{background:none;padding:var(--space-4)}
  .tile--cover .tile__plate .tile__title{color:var(--text)}
  .tile--cover .tile__plate .tile__blurb{color:var(--text-muted)}
  .tile--cover .tile__icon{background:var(--accent-weak);color:var(--accent)}
}

/* ============================================================
   Production adaptations (mockup port)
   The design shipped platform.css against a review harness that
   rendered each screen as a .screen card and used <image-slot>
   placeholders. In production each screen is a FULL PAGE and the
   slots are real <img>. These few rules are that glue — no tokens
   or component looks are changed. (Harness-only classes above —
   .harness/.rail/.stage — are unused here and prune at build.)
   ============================================================ */

/* Full-page app shell — replaces the prototype's .screen wrapper.
   Same relative/isolate context the glow + starfield need. */
.app{position:relative;min-height:100dvh;isolation:isolate;background:var(--surface);display:flex;flex-direction:column}
.app > .screen__body{flex:1}

/* Overlays/drawers start hidden until opened (app.js toggles [hidden]).
   .overlay sets display:grid, so [hidden] needs to win — it does on
   specificity, no !important. */
.overlay[hidden]{display:none}
[data-drawer][hidden]{display:none}
/* .stack sets display:flex, so a hidden .stack form (the /reset consume form until
   the client reveals it) needs [hidden] to win on specificity — same as above. */
.stack[hidden]{display:none}
/* Server-rendered action modals (admin.js reveals .uimodal-overlay#ID). The overlay
   sets display:grid, so [hidden] must win. */
.uimodal-overlay[hidden]{display:none}
/* Belt-and-braces: any display rule (grid/flex/inline-flex) must yield to [hidden]. */
[hidden]{display:none !important}

/* Real content images fill the slots <image-slot> used to occupy. */
.tile--hero .tile__media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.tile--cover > img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;z-index:0}
.hero-full > img,.hero-full > .cyc > img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.avatar img{width:100%;height:100%;border-radius:inherit;object-fit:cover;display:block}

/* Raster icons: <img class="icon"> replaces the SVG sprite where wired. Same 1em
   sizing as the sprite (scales with the parent font-size), so markup swaps 1:1. */
img.icon{width:1em;height:1em;object-fit:contain;vertical-align:middle;flex:none}
/* Raster icons are fixed-azure (no currentColor). On filled/on-image buttons the icon
   must read white like the label — force it white there. */
.btn--primary img.icon,.btn--danger img.icon,.btn--onimg img.icon{filter:brightness(0) invert(1)}

/* Landing hero: when it stacks to one column (≤760px), show the RIGHT column
   (lockup + tagline) first, then the left column (copy + sign-in). */
@media (max-width:760px){
  .hero-content .grid2 > :first-child{order:2}
  .hero-content .grid2 > :last-child{order:1}
}

/* Site footer (version bar) sits at the bottom of the flex-column .app shell. */
.site-footer{position:relative;z-index:1;margin-top:auto}

/* ============================================================
   W4 shell helpers — class-only re-expressions of the mockups'
   inline layout styles (ADR 0008: no inline style=, style-src 'self').
   ============================================================ */
.svg-sprite{position:absolute;width:0;height:0;overflow:hidden}
.site-footer{border-top:1px solid var(--border);background:var(--surface-elevated);padding:var(--space-5) var(--space-6)}
.footer__row{justify-content:space-between;flex-wrap:wrap;gap:var(--space-3)}
.footer__brand{gap:var(--space-2)}
.footer__status{gap:var(--space-4)}
.appbar__right{gap:var(--space-3)}
.acct__name{font-size:var(--fs-sm);font-weight:500}
.acct__logout{display:inline-flex}
.card__title{font-weight:600;margin-bottom:var(--space-2)}
.card__body{color:var(--text)}
.card__actions{display:flex;gap:var(--space-2);margin-top:var(--space-4);flex-wrap:wrap}
.tile__head{gap:var(--space-2)}
.tile__txt{display:flex;flex-direction:column;gap:2px}
.notif__action{margin-left:auto}
.conn__meta{display:flex;gap:var(--space-2);flex-wrap:wrap;align-items:center;margin-top:4px}
.conn__actions{margin-left:auto}
/* Auth (login) layout — centered brand + form card, stripped chrome. */
.auth{display:grid;place-items:center;min-height:0;padding:var(--space-8) var(--space-6)}
.auth__inner{width:100%;max-width:380px}
.auth__brand{display:flex;flex-direction:column;align-items:center;gap:var(--space-3);margin-bottom:var(--space-6)}
.auth__lockup{width:80%;height:auto}
.auth__note{text-align:center;font-size:var(--fs-xs);margin-top:var(--space-4)}
.auth__error{padding:var(--space-4);border-style:solid;border-color:var(--danger);background:var(--danger-weak);flex-direction:row;text-align:left;align-items:flex-start;gap:var(--space-2)}
.auth__error .state__title{font-size:var(--fs-base)}
/* "Stay signed in on this browser" checkbox on the login form (#154 auth-UX). */
.login__stay{gap:8px;align-items:center;font-size:var(--fs-sm);color:var(--text-muted)}
/* Dashboard scaffolding. */
.dash-head{justify-content:space-between;align-items:flex-end;flex-wrap:wrap;gap:var(--space-2)}
.section-head{gap:var(--space-3);margin:var(--space-6) 0 var(--space-4);align-items:center}
.section-head__rule{flex:1;height:1px;background:var(--border)}
/* live-refresh indicator on the Maintenance System-health header (ops-live.js) */
.ops-live{display:inline-flex;align-items:center;gap:.4rem;font-size:var(--fs-xs);color:var(--text-faint);white-space:nowrap}
.ops-live__dot{width:.5rem;height:.5rem;border-radius:50%;background:currentColor;flex:none}
.ops-live--live{color:var(--success)}
.ops-live--live .ops-live__dot{animation:opspulse 1.6s var(--ease) infinite}
.ops-live--reconnecting{color:var(--warning)}
.ops-live--stale{color:var(--danger)}
@keyframes opspulse{0%,100%{opacity:1}50%{opacity:.28}}
@media (prefers-reduced-motion:reduce){.ops-live--live .ops-live__dot{animation:none}}
.form-actions{display:flex;gap:var(--space-2);flex-wrap:wrap}
.stack--gap{gap:var(--space-4)}
.row--gap{gap:var(--space-3)}

/* ============================================================
   Public landing page (pages.landing → chrome:'landing').
   Class-only re-expression of docs/design/mockups/landing.html's
   inline style= attributes (ADR 0008: no inline style=, style-src
   'self'). No tokens or component looks change — just the one-off
   layout the mockup had inlined (glow height, hero copy widths,
   CTA sizing, brand lockup, "inside" section header + tile grid).
   ============================================================ */
/* Public appbar: transparent over the hero, no bottom rule. */
.appbar--public{background:transparent;border-bottom:0}
.appbar__domain{font-size:var(--fs-xs)}
/* The atmospheric layer is taller on the landing (spans the hero). */
.screen__glow--landing{height:560px}
.starfield--landing{height:560px}
/* Floating theme toggle, bottom-right, above the fold. */
.toggle--floating{position:fixed;right:16px;bottom:74px;z-index:60;width:auto;box-shadow:var(--shadow-2)}
/* The footer variant (#184) — inline in the footer row rather than floating over the page, and
   sized down so it sits with the build stamp instead of competing with it. */
.toggle--footer{width:auto;padding:3px;gap:3px;border-radius:var(--r-full)}
.toggle--footer button{padding:3px 9px;font-size:.7rem;gap:4px}
.toggle--footer .icon{width:.85em;height:.85em}
@media (max-width:640px){.footer__row{flex-wrap:wrap;gap:8px}}
/* Hero layout — two columns, centered, generous gap. */
.hero-grid{align-items:center;gap:var(--space-10)}
.hero-eyebrow{margin-bottom:var(--space-4)}
.hero-title{font-size:var(--fs-2xl);max-width:15ch;margin-bottom:var(--space-4);line-height:1.14}
.hero-lede{font-size:var(--fs-lg);font-weight:400;max-width:46ch;margin-bottom:var(--space-6);line-height:1.5}
.hero-actions{gap:var(--space-3);flex-wrap:wrap}
.hero-actions .btn{padding:13px 22px;font-size:var(--fs-base)}
.hero-note{font-size:var(--fs-xs);margin-top:var(--space-5);text-transform:none;letter-spacing:0}
/* Right column — centered brand lockup + tagline. */
.hero-brand{text-align:center}
.hero-lockup{display:block;width:min(560px,92%);height:auto;margin:0 auto var(--space-4);
  filter:drop-shadow(0 10px 44px rgba(0,0,0,.55))}
.hero-tag{font-size:var(--fs-lg);font-weight:500;opacity:.92}
/* "Inside the workshop" section — extra bottom padding + a titled rule. */
.landing-inside{padding-bottom:var(--space-12)}
.inside-head{gap:var(--space-4);align-items:center;margin-bottom:var(--space-6)}
.tiles--fit{grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}

/* ── DM review round (#72–#79) ─────────────────────────────────────────────── */
/* Header quick sign-out (#72) + Session Log text button (#73) */
.acct__quicklogout{display:inline-flex}
.menu__btn--text{font-size:var(--fs-sm);color:var(--text-muted);white-space:nowrap}
.menu__btn--text:hover{color:var(--text)}

/* Session Log panel (#73) */
.menu__panel--log{min-width:300px;max-width:360px;max-height:60vh;overflow:auto;padding:var(--space-2)}
.menu__empty{padding:var(--space-3);color:var(--text-faint);font-size:var(--fs-sm)}
.session-log{display:flex;flex-direction:column;gap:2px}
.session-log__row{display:grid;grid-template-columns:auto 1fr auto;gap:var(--space-2);align-items:baseline;padding:var(--space-2) var(--space-3);border-radius:var(--r-1)}
.session-log__row:hover{background:var(--surface-hover)}
.session-log__dot{width:7px;height:7px;border-radius:50%;background:var(--ok);align-self:center}
.session-log__row--error .session-log__dot{background:var(--danger)}
.session-log__msg{font-size:var(--fs-sm);color:var(--text)}
.session-log__time{font-size:var(--fs-xs);color:var(--text-faint);white-space:nowrap}

/* Toast messages (#73) — a full-width bar dropping from the top edge */
.toast-stack{position:fixed;top:0;left:0;right:0;z-index:10000;display:flex;flex-direction:column;align-items:stretch;pointer-events:none}
.toast{pointer-events:auto;transform:translateY(-100%);opacity:0;transition:transform .28s ease,opacity .28s ease;border-bottom:1px solid var(--border-strong);box-shadow:0 6px 24px rgba(0,0,0,.28);background:var(--surface-elevated)}
.toast--in{transform:translateY(0);opacity:1}
.toast--ok{border-bottom-color:var(--accent)}
.toast--error{border-bottom-color:var(--danger)}
.toast__inner{max-width:900px;margin:0 auto;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-6)}
.toast__msg{flex:1;font-size:var(--fs-sm);color:var(--text)}
.toast--ok .toast__msg::before{content:"✓ ";color:var(--ok);font-weight:700}
.toast--error .toast__msg::before{content:"! ";color:var(--danger);font-weight:700}
.toast__close{border:0;background:transparent;color:var(--text-muted);font-size:18px;line-height:1;cursor:pointer;padding:2px 6px;border-radius:var(--r-1)}
.toast__close:hover{background:var(--surface-hover);color:var(--text)}
@media(prefers-reduced-motion:reduce){.toast{transition:none}}

/* Users table alignment (#77): Name left, other columns centered (header + cells) */
.table--users th:not(:first-child),.table--users td:not(:first-child){text-align:center}
.table--users td:not(:first-child) .row-actions{justify-content:center}
.table--users td:not(:first-child) form{display:inline-flex;justify-content:center}
.table--users td:not(:first-child) .select{margin:0 auto}

/* View-user (#76): 2-col User/Effective-Access, a lower-right edit link, top actions */
.detail-actions{margin-top:var(--space-4)}
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4);margin-top:var(--space-4);align-items:start}
.card__edit{margin-left:auto;border:0;background:transparent;color:var(--accent);font-size:var(--fs-sm);font-weight:600;cursor:pointer;padding:2px 4px;border-radius:var(--r-1)}
.card__edit:hover{text-decoration:underline}
@media(max-width:720px){.detail-grid{grid-template-columns:1fr}}

/* Maintenance item description (#79) */
.conn__desc{font-size:var(--fs-xs);color:var(--text-faint);margin-top:2px;max-width:56ch}

/* Maintenance monitored-item list (#82): 2 columns + a status-hue box background.
   Reuses the pill tint tokens; grid cells stretch to equal height per row. */
.ops-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--space-3)}
.ops-grid .conn__desc{max-width:none}
.conn--ok{background:var(--success-weak)}
.conn--bad{background:var(--danger-weak)}
.conn--warn{background:var(--warning-weak)}
.conn--muted{background:var(--surface-hover)}
@media(max-width:720px){.ops-grid{grid-template-columns:1fr}}

/* ============================================================
   W15 sender-identity admin pages (PRD 05a §7) — Connections
   (send-as address sub-list + System default sender) and
   Message-requests (per-event assignment). Class-only re-
   expression of the mockups' inline layout (ADR 0008; the CSP
   scan runs over markup, never this file). Reuses existing
   tokens/components — no new design system.
   ============================================================ */
.page-intro{font-size:var(--fs-sm);max-width:76ch;margin-bottom:var(--space-5)}
.check{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--fs-sm);color:var(--text-muted)}

/* System default sender control (the '*' fallback). */
.default-sender{margin-bottom:var(--space-6)}
.default-sender__row{justify-content:space-between;gap:var(--space-4);flex-wrap:wrap;align-items:flex-end}
.default-sender__copy{max-width:56ch}
.default-sender__title{font-weight:600;margin:2px 0 4px}
.default-sender__desc{font-size:var(--fs-sm)}
.default-sender__field{min-width:min(320px,100%)}

/* Connection card + its expandable send-as address sub-list. */
.conn-card__head{justify-content:space-between;gap:var(--space-3);flex-wrap:wrap}
.conn-card__ident{gap:var(--space-3);align-items:flex-start}
.conn-card__hint{margin-top:var(--space-4)}
.conn-card__dcrow{gap:var(--space-3);margin-top:var(--space-3);align-items:center}
.senders{margin-top:var(--space-4)}
.senders__summary{cursor:pointer;font-size:var(--fs-sm);font-weight:500;color:var(--text-muted);list-style:none}
.senders__summary::-webkit-details-marker{display:none}
.senders__summary:hover{color:var(--text)}
.senders__table{margin-top:var(--space-3)}
.senders__add{margin-top:var(--space-3)}
/* Inline single-control forms (Test / Remove) sit in a .row-actions cluster. */
.mr-inline{display:inline-flex}

/* Messaging-connector modals: mailbox split, repeatable stream/domain rows, live test. */
.addr-split{display:flex;gap:var(--space-2);align-items:stretch}
.addr-split .input{flex:1 1 auto;min-width:0}
.addr-split .select{flex:0 0 auto;max-width:52%}
.repeat{margin:var(--space-3) 0;padding:var(--space-3);border:1px solid var(--border);border-radius:var(--radius-2);background:var(--surface-2,rgba(127,127,127,.04))}
.repeat__title{font-size:var(--fs-sm);font-weight:600;color:var(--text)}
.repeat__hint{margin:var(--space-1) 0 var(--space-2)}
.repeat__rows{display:flex;flex-direction:column;gap:var(--space-2)}
.repeat__row{display:flex;gap:var(--space-2);align-items:center}
.repeat__row .input{flex:1 1 auto;min-width:0}
.repeat__rm{flex:0 0 auto;line-height:1}
.repeat__add{margin-top:var(--space-2)}
.test-row{display:flex;gap:var(--space-2);align-items:center;margin-top:var(--space-3);flex-wrap:wrap}
.test-result{font-size:var(--fs-sm)}
.test-result.is-pending{color:var(--text-muted)}
.test-result.is-ok{color:var(--ok,#3fb950)}
.test-result.is-err{color:var(--danger,#f85149)}

/* Delivery-health Details modal (Health on Connectors / Details on Maintenance). */
.cd-body{display:flex;flex-direction:column;gap:var(--space-3);padding:var(--space-4) var(--space-6)}
.cd-summary{display:flex;gap:var(--space-2);align-items:center;flex-wrap:wrap;font-size:var(--fs-sm)}
.cd-grid{display:flex;flex-direction:column;font-size:var(--fs-sm)}
.cd-grid__head{display:grid;grid-template-columns:1.4fr .7fr 1.6fr;gap:var(--space-2);color:var(--text-muted);font-size:.72rem;text-transform:uppercase;letter-spacing:.04em;padding-bottom:var(--space-1);border-bottom:1px solid var(--border)}
.cd-row{display:grid;grid-template-columns:1.4fr .7fr 1.6fr;gap:var(--space-2);align-items:center;padding:var(--space-2) 0;border-bottom:1px solid var(--border)}
.cd-row:last-child{border-bottom:none}
.cd-row__who{word-break:break-all}
.cd-row__events{display:flex;flex-wrap:wrap;gap:var(--space-1)}

/* Message-requests: per-event Sender(s) + Rules cells. */
.mr-senders{display:flex;flex-direction:column;gap:var(--space-2);align-items:flex-start;width:100%}
.mr-sender-row{display:flex;gap:var(--space-2);align-items:center;width:100%}
.mr-sender-label{flex:1 1 auto;min-width:0}
.mr-sender-status{font-size:11px}
.mr-sender-form{display:block;width:100%}
.mr-select{min-width:min(280px,100%);width:100%}
.mr-add-form{width:100%}
.mr-remove{flex:0 0 auto}
.mr-remove-btn{white-space:nowrap}
.mr-none{font-size:var(--fs-sm)}
.mr-drag{flex:0 0 auto;cursor:grab;color:var(--muted);user-select:none;line-height:1}
.mr-sender-row[draggable="true"]{cursor:grab}
.mr-dragging{opacity:.5}
.mr-rules{text-align:right;white-space:nowrap}
.mr-mode{display:inline-block}
.mr-eff{font-size:var(--fs-xs);margin-top:4px}
.mr-event__name{font-weight:500}
.mr-event__key{font-size:11px}
.mr-group__hint{margin:0 0 var(--space-3)}
.mr-foot{margin-top:var(--space-5)}
/* An inline delivery-mode toggle (All / Failover) — sits in the right-aligned cell. */
.toggle--inline{width:auto;display:inline-flex}

/* ============================================================
   W9 storage admin (PRD 08) — Maintenance storage tiles + the
   Connections / Buckets & Bindings / Trash pages. Class-only
   re-expression of the mockups' inline styles (ADR 0008 — the
   CSP scan runs over markup, never this file). Reuses the ops
   tint tokens + .conn / .pill / .table / .chip primitives.
   ============================================================ */

/* A small pill (the mockups' font-size:11px pills — S3 tag, bucket pills, etc.). */
.pill--xs{font-size:11px;padding:2px 8px}

/* The full-width "Storage backends" heading tile inside #ops-health (MF-2). */
.ops-grid__heading{grid-column:1/-1;font-size:var(--fs-lg);font-weight:700;
  border-top:1px solid var(--border);padding-top:var(--space-3);margin-top:var(--space-1)}

/* Storage-backend Details modal internals (metrics row, per-bucket cards, the
   #88 replication + host-probe placeholder lines). */
.metrics{display:flex;flex-wrap:wrap;gap:9px 20px;font-size:var(--fs-sm);color:var(--text-muted);align-items:center}
.metrics b{color:var(--text)}
.meter{display:inline-block;width:110px;height:7px;border-radius:4px;background:var(--border);overflow:hidden;vertical-align:middle}
.meter>i{display:block;height:100%;background:var(--accent)}
.repl{margin-top:12px;font-size:var(--fs-sm);color:var(--text-muted)}
.repl b{color:var(--text)}
.subs{margin-top:14px;display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px}
.bstat{border:1px solid var(--border);border-radius:var(--r-2);padding:10px 12px}
.bstat__top{display:flex;justify-content:space-between;align-items:center;gap:6px}
.bstat__row{display:flex;gap:10px;margin-top:7px;font-size:var(--fs-xs);color:var(--text-muted);align-items:center}
.bstat__row b{color:var(--text)}
.bstat__role{font-size:11px}
.sec-title{font-size:var(--fs-lg);font-weight:700;margin:var(--space-4) 0 var(--space-3)}
.stg-det__subhead{font-size:var(--fs-base);margin:var(--space-4) 0 var(--space-2)}
.stg-det__head{gap:var(--space-2);align-items:center;flex-wrap:wrap;margin-bottom:var(--space-2)}

/* Informational/confirm modal body padding (matches .cd-body so combined use is a no-op). */
.uimodal-body{padding:var(--space-4) var(--space-6)}

/* Two-up field rows inside the storage modals + a read-only static field. */
.field-row{display:flex;gap:var(--space-3)}
.field-row>.field{flex:1 1 0;min-width:0}
.static-field{background:var(--surface-sunken);border:1px solid var(--border);border-radius:var(--r-1);
  padding:9px 12px;color:var(--text-muted)}

/* Storage Connections card: bucket-pill row + the (wrapping) action cluster + Test. */
.conn__buckets{margin-top:9px;display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.conn__buckets-label{font-size:11px}
.conn:has(.conn__buckets) .conn__actions{flex-wrap:wrap;justify-content:flex-end}
.stg-test{display:inline-flex;align-items:center;gap:var(--space-2)}

/* Text-tinted ghost buttons + a plain link-button (mockups' inline colored ghosts). */
.btn--danger-text{color:var(--danger)}
.btn--muted-text{color:var(--text-muted)}
.btn-link{background:none;border:0;color:var(--accent);cursor:pointer;font:inherit;font-size:var(--fs-sm);padding:0}
.btn-link:hover{text-decoration:underline}

/* Buckets & Bindings table + the Manage-bindings modal rows. */
.ta-right{text-align:right}
.store-name{font-weight:500}
.binding-list{gap:var(--space-2)}
.binding-row{display:flex;justify-content:space-between;align-items:center;gap:var(--space-3);
  border:1px solid var(--border);border-radius:var(--r-2);padding:10px 12px}
.binding-row__id{gap:8px;align-items:center;flex-wrap:wrap}
.binding-row__actions{gap:6px;align-items:center}

/* Remove-bucket explainer: the tinted status box + the numbered "what happens" steps. */
.rm-ident{gap:8px;align-items:center;margin-bottom:var(--space-3)}
.rm-ident__name{font-weight:600}
.rmbox{border-radius:var(--r-2);padding:12px 14px;margin-top:var(--space-1)}
.rmbox__head{gap:8px;align-items:center;font-weight:600}
.rmbox__body{margin:6px 0 0;font-size:var(--fs-sm)}
.rmbox--ok{border:1px solid var(--success);background:var(--success-weak)}
.rmbox--ok .rmbox__head{color:var(--success)}
.rmbox--warn{border:1px solid var(--warning);background:var(--warning-weak)}
.rmbox--warn .rmbox__head{color:var(--warning)}
.rmbox--danger{border:1px solid var(--danger);background:var(--danger-weak)}
.rmbox--danger .rmbox__head{color:var(--danger)}
.steps{gap:8px;margin-top:var(--space-3)}
.steps__head{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em}
.step{gap:8px;align-items:flex-start}
.step__num{min-width:20px;justify-content:center}
.step__txt{font-size:var(--fs-sm)}
.rm-purge__lede{margin:0 0 var(--space-3)}

/* Trash object/bucket cell. */
.trash-obj{gap:8px;align-items:center}
.trash-obj__name{font-weight:500}
.trash-obj__hash{font-size:11px}

/* Empty-state call-to-action spacing (storage empty pages). */
.state__action{margin-top:var(--space-5)}

/* ---------- campaign container (#148, PRD 02) — the mockups' page-local styles,
   re-expressed as named classes so the strict CSP holds (ADR 0008). Mockups:
   campaigns.html · campaign-detail.html · campaign-landing.html. ---------- */
.campaign-name{font-weight:500}
.slug{font-size:var(--fs-xs);color:var(--text-muted)}
.row--archived{opacity:.58}
.hintbox{border:1px solid var(--border);border-radius:var(--r-2);padding:var(--space-4);
         background:var(--surface);margin-top:var(--space-4);font-size:var(--fs-sm);color:var(--text-muted)}
.kv{display:grid;grid-template-columns:130px 1fr;gap:9px 14px;font-size:var(--fs-sm);margin:0}
.kv dt{color:var(--text-muted)}
.kv dd{margin:0}
.danger{border:1px solid var(--danger);border-radius:var(--r-2);padding:var(--space-4)}
.danger__row{display:flex;justify-content:space-between;align-items:center;gap:var(--space-3);
             flex-wrap:wrap;padding:10px 0}
.danger__row + .danger__row{border-top:1px solid var(--border)}
.danger__title{font-weight:600;font-size:var(--fs-sm)}
.people{display:flex;flex-wrap:wrap;gap:10px}
.person{display:flex;align-items:center;gap:9px;border:1px solid var(--border);
        border-radius:var(--r-full);padding:5px 14px}
.person__name{font-size:var(--fs-sm);font-weight:500}
.detail-line{justify-content:space-between;gap:var(--space-3);font-size:var(--fs-sm)}
.feature{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3)}
.feature__title{font-weight:500;font-size:var(--fs-sm)}
.feature__blurb{font-size:var(--fs-xs)}

/* campaign subdomain (#154) — the not-provisioned/live sub-line under the Identity kv row. */
.field-hint{font-size:var(--fs-xs);margin-top:4px}

/* ══════════════════════════════════════════════════════════════════════════
   Agent / API access surfaces (S7, PRD 11) — re-expressing the mockups'
   inline styles (agent-tokens.html / agent-activity.html) as named classes
   (ADR 0008: class-only, CSP-clean). Reuses .meter / .ops-grid / .chip / .pill.
   ══════════════════════════════════════════════════════════════════════════ */

/* mint modal — type cards */
.eyebrow--gap{display:block;margin-top:14px;margin-bottom:6px}
.typecard{display:block;position:relative;border:1px solid var(--border);border-radius:var(--r-2);
  padding:12px 14px;cursor:pointer}
.typecard--on{border-color:var(--accent);background:var(--accent-weak)}
.typecard__radio{position:absolute;opacity:0;width:0;height:0;pointer-events:none}
.typecard__t{display:block;font-weight:600}
.typecard__d{display:block;font-size:var(--fs-xs);color:var(--text-muted);margin-top:4px}
.mint-scope{margin-top:12px}
.mint-scope__row{display:flex;gap:var(--space-3);flex-wrap:wrap}
.mint-scope__row .field{flex:1 1 220px}
.check-row{display:flex;gap:8px;align-items:center;margin-top:10px;font-size:var(--fs-sm);cursor:pointer}

/* the envelope preview — the design's central idea made visible */
.envelope{border:1px solid var(--border);border-radius:var(--r-2);padding:12px 14px;margin-top:14px;
  background:var(--surface-elevated)}
.envelope__h{font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--text-muted);
  margin-bottom:8px}
.envelope__h--gap{margin-top:14px}
.envelope__note{margin-top:10px;font-size:var(--fs-xs)}
.caps{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.caps + .caps{margin-top:8px}
.caps__note{font-size:var(--fs-xs)}
.cap{font-size:11px}
.cap--excluded{text-decoration:line-through;opacity:.6}
.chip--accent{border-color:var(--accent);color:var(--accent)}
.chip--muted{opacity:.85}

/* the shown-once secret */
.secret{font-family:var(--font-mono);font-size:var(--fs-sm);background:var(--surface-sunken);
  border:1px solid var(--border);border-radius:var(--r-2);padding:12px 14px;word-break:break-all}
.secret__actions{margin-top:10px;align-items:center}

/* your-tokens table + the campaign-delete affordance */
.tok__name{font-weight:500}
.tok__id{font-size:var(--fs-xs)}
.tok__never{font-style:italic}
.tok__by{font-size:var(--fs-xs)}
.tok__foot{margin-top:var(--space-3)}
.hintbox--warn{border-color:var(--warning);background:var(--warning-weak)}
.tok-affordance{margin:8px 0 0;padding-left:18px}
.tok-affordance li{margin:2px 0}

/* agent activity — budget stat tiles */
.stat__n{font-size:var(--fs-2xl);font-weight:700;line-height:1.1}
.stat__l{font-size:var(--fs-xs);color:var(--text-muted);margin-top:4px}

/* meter accents (the base .meter / .meter>i already exist) + fixed-step widths (CSP: no inline style) */
.meter--warn>i{background:var(--warning)}
.meter--bad>i{background:var(--danger)}
.meter__fill--0{width:0}
.meter__fill--5{width:5%}
.meter__fill--10{width:10%}
.meter__fill--15{width:15%}
.meter__fill--20{width:20%}
.meter__fill--25{width:25%}
.meter__fill--30{width:30%}
.meter__fill--35{width:35%}
.meter__fill--40{width:40%}
.meter__fill--45{width:45%}
.meter__fill--50{width:50%}
.meter__fill--55{width:55%}
.meter__fill--60{width:60%}
.meter__fill--65{width:65%}
.meter__fill--70{width:70%}
.meter__fill--75{width:75%}
.meter__fill--80{width:80%}
.meter__fill--85{width:85%}
.meter__fill--90{width:90%}
.meter__fill--95{width:95%}
.meter__fill--100{width:100%}

/* run detail — request rows + drift diff */
.run__meta{align-items:center;margin-bottom:14px;flex-wrap:wrap;gap:10px}
.req{border:1px solid var(--border);border-radius:var(--r-2);padding:10px 12px;margin-bottom:8px}
.req__top{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap}
.req__meta{font-size:var(--fs-xs);color:var(--text-muted);margin-top:4px}
.req__count{font-size:10px}
.req--done{opacity:.6}
.diff{font-family:var(--font-mono);font-size:var(--fs-xs);border:1px solid var(--border);border-radius:var(--r-1);
  overflow:hidden;margin-top:8px}
.diff>div{padding:7px 10px}
.diff__was{background:var(--danger-weak)}
.diff__now{background:var(--success-weak)}

/* revert result overlay — honest per-target/per-request outcomes */
.outcomes{display:flex;flex-direction:column;gap:10px}
.outcome{font-size:var(--fs-sm);padding:10px 12px;border:1px solid var(--border);border-radius:var(--r-2)}
.outcome--ok{background:var(--success-weak)}
.outcome--warn{background:var(--warning-weak)}
.outcome--bad{background:var(--danger-weak)}
.outcome--muted{color:var(--text-muted)}
.outcome__tgt{font-size:var(--fs-xs)}
.outcome__deps{margin:6px 0 0;padding-left:18px}
.outcome__req{font-weight:600;margin-top:6px}

/* off-screen (copy fallback textarea) */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);
  white-space:nowrap;border:0}

/* ============================================================
   #3 S4 — The Orrery Media Library. Class-only, no inline style
   (ADR 0008: style-src 'self'). The grid reuses the tile rhythm
   but an asset tile is CONTENT (real alt, lazy thumb, type glyph
   fallback) rather than decorative hero art, so it gets its own
   block instead of overloading .tile--cover.
   ============================================================ */
.assets{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px;margin:18px 0}
.asset{position:relative;display:flex;flex-direction:column;background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden;text-decoration:none;color:inherit;transition:border-color .15s,transform .15s}
.asset:hover{border-color:var(--accent);transform:translateY(-2px)}
.asset__face{position:relative;aspect-ratio:4/3;background:var(--panel-2);display:flex;align-items:center;justify-content:center;overflow:hidden}
.asset__thumb{width:100%;height:100%;object-fit:cover;display:block}
.asset__glyph{display:flex;align-items:center;justify-content:center;opacity:.45}
.asset__glyph svg{width:46px;height:46px}
/* Asset flag bubbles (#3) — stacked in the TOP-RIGHT, over the artwork itself.
   z-index because .asset__thumb is a positioned sibling inside .asset__face; without it the image
   paints over the bubbles. pointer-events:none so a flag never eats the click that opens the asset —
   the whole tile is one anchor, and a dead spot in the corner reads as a broken card.
   The ring + drop-shadow are what keep a bright circle legible over bright artwork. */
.asset__flags{position:absolute;top:5px;right:5px;z-index:2;display:flex;flex-direction:row-reverse;gap:3px;pointer-events:none}
/* No letter now — a plain dot. The ring and shadow do ALL the legibility work, because a 11px
   circle over bright artwork has nothing else keeping its edge. */
.flag{display:block;width:11px;height:11px;border-radius:50%;flex:0 0 auto;
  box-shadow:0 0 0 1px rgba(0,0,0,.65),0 1px 3px rgba(0,0,0,.5)}
/* The key. Colour alone means nothing without it, so it is part of the feature, not decoration. */
/* Inline in the header row, LEFT of the controls (the flags are a glance-level cue, so the legend
   belongs where the eye already is rather than at the foot of a scroll). No top border now that it
   is not a footer, and it wraps ahead of the buttons on a narrow viewport. */
.flagkey{display:inline-flex;flex-wrap:wrap;align-items:center;gap:10px;margin-right:6px;
  font-size:.76rem;color:var(--muted)}
.flagkey__title{text-transform:uppercase;letter-spacing:.08em;font-size:.66rem;opacity:.7}
.flagkey__item{display:inline-flex;align-items:center;gap:5px;white-space:nowrap}
.flag--public{background:#22e06a}          /* neon green — verified public */
.flag--shared{background:#4db2ff}          /* light blue — another owner SHARED it with you */
.flag--owned{background:#e0342f}           /* red — another owner, NOT shared: override-only */
.asset__meta{padding:10px 12px;display:flex;flex-direction:column;gap:6px}
.asset__title{font-weight:600;font-size:.94rem;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.asset__tags{display:flex;flex-wrap:wrap;gap:4px}

/* filter bar + tag chips */
.oml-filter{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:14px 0 8px}
.oml-filter .input{max-width:340px}
.oml-tags{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}

/* detail: media beside its metadata, stacking on narrow screens */
.oml-detail{display:grid;grid-template-columns:minmax(0,2fr) minmax(240px,1fr);gap:20px;align-items:start;margin-top:16px}
@media (max-width:820px){.oml-detail{grid-template-columns:1fr}}
.oml-view{display:block;background:var(--panel-2);border:1px solid var(--line);border-radius:12px;overflow:hidden}
.oml-view img{width:100%;height:auto;display:block;cursor:zoom-in}
.oml-view--glyph{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;padding:44px 20px}
.oml-view--glyph svg{width:64px;height:64px;opacity:.45}
.oml-side{display:flex;flex-direction:column;gap:14px}
.oml-meta{display:grid;grid-template-columns:auto 1fr;gap:6px 12px;margin:0}
.oml-meta dt{color:var(--muted);font-size:.82rem}
.oml-meta dd{margin:0;font-size:.9rem}
.oml-desc{margin:0;color:var(--muted);line-height:1.5}
.oml-actions{flex-wrap:wrap;gap:8px}
.oml-form .field{margin-bottom:14px}
.oml-campaigns{border:1px solid var(--line);border-radius:10px;padding:12px}
.oml-campaigns .check{display:block;margin:4px 0;font-size:.9rem}

/* convert preview — original beside candidate, before anything is replaced (ADR 0074) */
.oml-compare{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media (max-width:700px){.oml-compare{grid-template-columns:1fr}}
.oml-compare figure{margin:0}
.oml-compare figcaption{font-size:.8rem;color:var(--muted);margin-bottom:6px}
.oml-compare img{width:100%;height:auto;border:1px solid var(--line);border-radius:8px;display:block}
.oml-revert,.oml-preview{margin-top:4px}
/* the quality ladder — each rung is its own POST form, so without this the forms stack as blocks
   and split the sentence they sit inside. Inline-flex + wrap keeps it reading as one line. */
.oml-quality{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px;margin:10px 0 0;font-size:.85rem}
.oml-quality form{display:inline}

/* click-to-zoom overlay (simple pan; deep-zoom tiling is deferred — #169) */
.oml-zoom{position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.86);display:flex;align-items:center;justify-content:center;overflow:hidden;cursor:zoom-out}
.oml-zoom__img{max-width:96vw;max-height:96vh;object-fit:contain;cursor:grab;transform:translate(var(--oml-pan-x,0),var(--oml-pan-y,0))}
.oml-zoom__img.is-full{max-width:none;max-height:none;cursor:grab}
