/* ============================================================
   kit.css — the component kit's stylesheet, shared by both shells.

   It styles exactly what shared/js/ui.js emits; that class vocabulary is the
   contract (public/js/views/README.md, "Class names"), not this file's taste.
   What it deliberately does NOT style is the shell around the components — the
   rail, the mobile app bar, the working area and the banner live in
   shared/css/shell.css, because admin.neuregami.com and editor.neuregami.com
   wear the same chrome and a second copy of it would drift.

   Load order in a shell's index.html: kit.css, then shell.css, then that
   shell's own file. Nothing here depends on the other two; they depend on the
   tokens declared at the top of this one.

   The palette and the rail colours are copied out of assets/css/site.css rather
   than @imported: the admin wears the site's one palette, ink, so the two read
   as one product, but it does not share the site's marketing-page density or its
   components. There is ONE palette. The picker and the six alternatives are
   gone; do not reintroduce a data-dtheme selector or a localStorage theme key.

   Two colour worlds run through this file. The CANVAS is the mid-tone/dark
   working area; the PANEL is the pale rail colour, and it is where nearly all
   text actually lives. Components are written against --ink / --ink-2 / --line /
   --tint / --field / --fill / --link / --focus, and each panel container rebinds
   that set. Custom properties inherit, so one rule per container corrects every
   descendant without enumerating selectors.

   Overlay geometry (fixed position, z-index, the drawer's edge anchoring, the
   dt-wrap scroller, the drag ghost and the drop indicator) is set inline by
   ui.js and sortable.js so a message is never invisible when this file lags
   behind. Inline wins, so nothing here fights it — the rules below add colour,
   type, spacing and the one thing inline cannot do: the max-height that turns
   .dt-wrap into a real scrollport for its sticky header.

   Views may also reach for the extras near the end, all on the same tokens:
   .stats/.stat/.stat-n/.stat-k, .qcards/.qcard, .pcard/.pcard-img/.pcard-body/
   .pcard-name/.pcard-meta/.pcard-links/.pcard-sec, .gcards/.gcard/.gcard-badge/
   .gcard-dim, .card-actions, .row-actions, .cell-title/.cell-sub/.cell-clamp,
   .thumb/.thumb-empty, .tabs/.tab, .filters, .pager, .kv, .log/.log-row,
   .dropzone, .upload-row, .bar, .badge, .check/.checks, .note, .form-row,
   .mgrid/.mtile (aliases of .mediagrid/.mediatile), .mono/.muted/.trunc/
   .eyebrow/.sr — and the builder's, at the end: .srt-*, .palette*, .ppanel*,
   .items/.item-row, .rich*, .split*, .menu*.

   `.switch` USED TO BE HERE AND IS GONE. It was styled and used by nothing;
   field()'s new 'boolean' type renders `.check`, which users.js already
   hand-rolls. One control, one class — §7.10 asked for exactly this choice.
   ============================================================ */


/* ---- palette. The one pairing, verbatim from site.css. All six custom-property
   names stay: ~40 rules below resolve through them, never through the hexes. ---- */
:root{ --rail-bg:#e8e8e8; --rail-ink:#1a1c20; --canvas:#16181c; --canvas-2:#101216; --canvas-3:#0b0c0f; --on-canvas:#ececec; }

/* ---- token mapping: the CANVAS context, i.e. the document default ---- */
:root{
  --font-display:"Oswald","Helvetica Neue",Arial,sans-serif;
  --font-body:"Inter",ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --font-mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;

  --rail-w:268px;
  --mbar-h:56px;
  --head-h:64px;
  --gut:clamp(16px,2.4vw,28px);
  --r-xs:6px; --r-sm:10px; --r:14px; --r-lg:20px; --r-xl:28px;
  --ease:cubic-bezier(.22,1,.36,1);
  --dur:.24s;
  --shadow:0 1px 2px rgba(0,0,0,.14), 0 10px 30px -14px rgba(0,0,0,.34);
  --shadow-lg:0 2px 6px rgba(0,0,0,.16), 0 34px 70px -28px rgba(0,0,0,.44);

  /* Status colours are fixed, not derived: they must clear 4.5:1 as text on the
     pale panel and carry white at 6:1 as a fill. These were tuned to survive the
     six palettes this file used to carry; ink has the darkest canvas and the
     palest panel of the set, so they still hold with room to spare. */
  --danger:#9b1c14;
  --danger-ink:#ffffff;
  --ok:#1c6b3f;
  --warn:#8a5a00;

  --panel:var(--rail-bg);
  --panel-2:color-mix(in srgb, var(--rail-ink) 5%, var(--rail-bg));

  /* --ink-2 stops at 88% rather than the site's lower alpha, and no text under
     ~20px is ever placed on bare --canvas; small text sits on --canvas-3 (the
     view header band) or on a panel. Both rules were forced by the mid-tone
     palettes this file used to carry, where a muted label collapsed into the
     background. They are kept deliberately: ink is near-black, so they cost
     nothing here and the layout no longer has to be re-checked if a second
     canvas is ever reintroduced. */
  --ink:var(--on-canvas);
  --ink-2:color-mix(in srgb, var(--on-canvas) 88%, transparent);
  --ink-3:color-mix(in srgb, var(--on-canvas) 62%, transparent);
  --line:color-mix(in srgb, var(--on-canvas) 20%, transparent);
  --line-2:color-mix(in srgb, var(--on-canvas) 34%, transparent);
  --tint:color-mix(in srgb, var(--on-canvas) 8%, transparent);
  --tint-2:color-mix(in srgb, var(--on-canvas) 15%, transparent);
  --field:color-mix(in srgb, var(--on-canvas) 8%, transparent);
  --link:var(--on-canvas);
  --focus:var(--on-canvas);
  --fill:var(--rail-bg);
  --fill-ink:var(--canvas-3);

  color-scheme:light;
}

/* ---- the PANEL context. Pale surface, dark ink, and the accent inverts:
   --canvas as a link colour only reaches 3.6:1 on a pale card, so panels reach
   for --canvas-3 instead. --ink-3 is for icon glyphs, rules and disabled state,
   never for text that has to be read. ---- */
.rail,.mbar,
.card,.dt,.empty,.mediatile,
.drawer-panel,.modal,.login-card,.toast,
.stat,.qcard,.pcard,.gcard{
  --ink:var(--rail-ink);
  --ink-2:color-mix(in srgb, var(--rail-ink) 80%, transparent);
  --ink-3:color-mix(in srgb, var(--rail-ink) 62%, transparent);
  --line:color-mix(in srgb, var(--rail-ink) 14%, transparent);
  --line-2:color-mix(in srgb, var(--rail-ink) 26%, transparent);
  --tint:color-mix(in srgb, var(--rail-ink) 6%, transparent);
  --tint-2:color-mix(in srgb, var(--rail-ink) 11%, transparent);
  --field:color-mix(in srgb, var(--rail-bg) 55%, #ffffff);
  --link:var(--canvas-3);
  --focus:var(--canvas-3);
  --fill:var(--canvas-3);
  --fill-ink:var(--on-canvas);
  background:var(--panel);
  color:var(--ink);
}

/* ============================================================
   Base
   ============================================================ */
*,*::before,*::after{ box-sizing:border-box }
/* display:flex/grid on .drawer-backdrop, .modal, .imgpick-bar etc. would
   otherwise beat the UA's [hidden]{display:none} */
[hidden]{ display:none !important }

html{
  -webkit-text-size-adjust:100%;
  color-scheme:light;
  scrollbar-color:var(--line-2) transparent;
}
body{
  margin:0;
  min-height:100dvh;
  background:var(--canvas);
  color:var(--ink);
  font-family:var(--font-body);
  font-size:15px;
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  font-feature-settings:"cv05" 1,"ss01" 1;
  overflow-x:hidden;
  accent-color:var(--canvas-3);
}
img,svg,video{ max-width:100%; display:block }
img{ height:auto }
a{ color:var(--link); text-decoration:none }
a:hover{ text-decoration:underline }
button,input,select,textarea{ font:inherit; color:inherit }
button{ background:none; border:0; padding:0; cursor:pointer }
::selection{ background:var(--canvas-3); color:var(--on-canvas) }
:focus-visible{ outline:2px solid var(--focus); outline-offset:2px }
:focus:not(:focus-visible){ outline:none }

h1,h2,h3,h4,h5{
  font-family:var(--font-display);
  font-weight:700;
  letter-spacing:.02em;
  line-height:1.14;
  margin:0;
  text-wrap:balance;
}
h1{ font-size:1.42rem; letter-spacing:.04em }
h2{ font-size:1.1rem }
h3{ font-size:1rem }
p{ margin:0 0 .85em }
p:last-child{ margin-bottom:0 }
/* <figure> carries a 40px UA side margin, which quietly ruins any grid built
   out of figures — a media tile or a gallery card usually is one */
figure,figcaption,blockquote,dl,dd,ul,ol{ margin:0; padding:0 }
ul,ol{ list-style:none }
fieldset{ margin:0; padding:0; border:0; min-width:0 }
legend{ padding:0 }
table{ border-collapse:collapse }

.mono{ font-family:var(--font-mono); font-size:.82em; letter-spacing:-.01em }
.muted{ color:var(--ink-2) }
.trunc{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0 }
.eyebrow{
  font-family:var(--font-display); font-weight:500; font-size:.62rem;
  letter-spacing:.2em; text-transform:uppercase; color:var(--ink-2);
}
.sr{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

/* icon() returns Material Symbols with viewBox="0 -960 960 960" and 24px stroke
   icons alike; sizing is entirely the box's job */
.ico,.rail-ico,.empty-ico,.banner-ico,.toast-ico,.dt-searchico,.imgpick-ph,.rep-handle,.dt-handle{
  display:inline-grid; place-items:center; flex:none;
}
.ico svg,.rail-ico svg,.empty-ico svg,.banner-ico svg,.toast-ico svg,
.dt-searchico svg,.imgpick-ph svg,.rep-handle svg,.dt-handle svg,.btn svg,
.btn-icon svg,.rail-out svg{
  display:block; fill:currentColor;
}
.ico{ width:18px; height:18px }
.ico svg{ width:100%; height:100% }
.ico-20{ width:20px; height:20px }
.ico-24{ width:24px; height:24px }

/* ============================================================
   Buttons, badges, spinner
   ============================================================ */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  padding:8px 15px; border:1px solid transparent; border-radius:999px;
  font-family:var(--font-display); font-size:.86rem; font-weight:500;
  letter-spacing:.04em; white-space:nowrap; cursor:pointer;
  transition:background var(--dur) var(--ease), border-color var(--dur) var(--ease),
             filter var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.btn:hover{ text-decoration:none }
.btn:active{ transform:translateY(1px) }
.btn:disabled,.btn[aria-disabled="true"],.btn.is-busy{ opacity:.55; cursor:not-allowed; transform:none }
.btn svg{ width:17px; height:17px }

.btn-primary{ background:var(--fill); color:var(--fill-ink) }
.btn-primary:hover{ filter:brightness(1.08) }
.btn-ghost{ background:var(--tint); color:var(--ink); border-color:var(--line) }
.btn-ghost:hover{ background:var(--tint-2); border-color:var(--line-2) }
.btn-danger{ background:var(--danger); color:var(--danger-ink) }
.btn-danger:hover{ filter:brightness(1.12) }
.btn-quiet{ background:transparent; color:var(--ink-2) }
.btn-quiet:hover{ background:var(--tint); color:var(--ink) }
.btn-sm{ padding:5px 11px; font-size:.78rem; gap:6px }
.btn-sm svg{ width:15px; height:15px }
.btn-icon{
  width:34px; height:34px; padding:0; flex:none; gap:0;
  display:inline-grid; place-items:center; border-radius:9px;
  background:transparent; border:0; color:var(--ink-2);
  transition:background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.btn-icon:hover{ background:var(--tint-2); color:var(--ink) }
.btn-icon.on{ background:var(--tint-2); color:var(--ink) }
.btn-icon.btn-sm{ width:28px; height:28px }
.btn-icon.btn-danger,.btn-icon-danger{ background:transparent; color:var(--ink-2) }
.btn-icon.btn-danger:hover,.btn-icon-danger:hover{
  background:color-mix(in srgb, var(--danger) 15%, transparent); color:var(--danger); filter:none;
}

.spinner{ display:inline-grid; place-items:center; padding:18px }
.spinner-ring,.spin{
  display:block; width:20px; height:20px; border-radius:50%;
  border:2px solid color-mix(in srgb, currentColor 28%, transparent);
  border-top-color:currentColor;
  animation:spin .7s linear infinite;
}
.spin{ width:15px; height:15px }
@keyframes spin{ to{ transform:rotate(360deg) } }

.badge{
  display:inline-flex; align-items:center; gap:5px;
  padding:2px 9px; border-radius:999px;
  background:var(--tint-2); border:1px solid var(--line);
  font-family:var(--font-display); font-weight:500; font-size:.68rem;
  letter-spacing:.1em; text-transform:uppercase; color:var(--ink);
  white-space:nowrap;
}
.badge-ok{ color:var(--ok); border-color:color-mix(in srgb, var(--ok) 34%, transparent); background:color-mix(in srgb, var(--ok) 11%, transparent) }
.badge-warn{ color:var(--warn); border-color:color-mix(in srgb, var(--warn) 34%, transparent); background:color-mix(in srgb, var(--warn) 11%, transparent) }
.badge-danger{ color:var(--danger); border-color:color-mix(in srgb, var(--danger) 34%, transparent); background:color-mix(in srgb, var(--danger) 11%, transparent) }

/* ============================================================
   Cards, notes, fact lists
   ============================================================ */
.cards{ display:grid; gap:14px; grid-template-columns:repeat(auto-fill, minmax(min(100%,300px), 1fr)) }
.cards-tight{ grid-template-columns:repeat(auto-fill, minmax(min(100%,232px), 1fr)) }
.card{ border-radius:var(--r); box-shadow:var(--shadow); overflow:hidden; min-width:0 }
.card-pad{ padding:16px 18px 18px }
.card-title{
  font-family:var(--font-display); font-weight:700; font-size:1rem;
  letter-spacing:.03em; margin:0 0 12px;
}
.card-sub{ font-size:.82rem; color:var(--ink-2); margin:-8px 0 12px }
.card-head{
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  padding:13px 16px; border-bottom:1px solid var(--line);
}
.card-head .card-title{ margin:0 }
.card-foot{ padding:12px 16px; border-top:1px solid var(--line); display:flex; align-items:center; gap:10px }

.note{
  padding:10px 12px; border-radius:var(--r-sm); margin:0 0 12px;
  background:var(--tint-2); color:var(--ink); font-size:.86rem;
  border-left:3px solid var(--line-2);
}
.note-danger{
  background:color-mix(in srgb, var(--danger) 12%, transparent);
  border-left-color:var(--danger); color:var(--danger); font-weight:500;
}
.note-warn{ background:color-mix(in srgb, var(--warn) 12%, transparent); border-left-color:var(--warn); color:var(--warn) }
.note-ok{ background:color-mix(in srgb, var(--ok) 12%, transparent); border-left-color:var(--ok); color:var(--ok) }

.facts,.kv{ display:grid; grid-template-columns:auto minmax(0,1fr); gap:8px 18px; align-items:baseline; font-size:.87rem }
.facts dt,.kv dt{
  font-family:var(--font-display); font-weight:500; font-size:.66rem;
  letter-spacing:.14em; text-transform:uppercase; color:var(--ink-2);
}
.facts dd,.kv dd{ margin:0; overflow-wrap:anywhere }
.kv dd{ font-family:var(--font-mono); font-size:.8rem }

/* ============================================================
   dataTable — .dt is the surface, sticky header, zebra-free, row hover
   ============================================================ */
.dt{ border-radius:var(--r); box-shadow:var(--shadow); overflow:hidden; min-width:0 }
.dt-bar{
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  padding:11px 14px; border-bottom:1px solid var(--line);
}
.dt-searchwrap{ position:relative; display:flex; align-items:center; flex:1; min-width:180px; max-width:340px }
.dt-searchico{
  position:absolute; left:11px; width:16px; height:16px;
  color:var(--ink-2); pointer-events:none;
}
.dt-searchico svg{ width:16px; height:16px }
/* qualified past the base input rule further down, which is the same
   specificity and comes later — unqualified, its padding wins and the
   placeholder sits under the magnifier */
.dt-searchwrap .dt-search,
.dt-searchwrap input.input{ padding-left:34px; border-radius:999px }
.dt-count{
  font-family:var(--font-display); font-size:.68rem; letter-spacing:.14em;
  text-transform:uppercase; color:var(--ink-2); white-space:nowrap;
}
.dt-actions{ display:flex; align-items:center; gap:8px; margin-left:auto; flex-wrap:wrap }
.dt-hint{
  margin:0; padding:8px 14px; font-size:.78rem; color:var(--ink);
  background:color-mix(in srgb, var(--warn) 13%, transparent);
  border-bottom:1px solid var(--line);
}

/* ui.js sets overflow-x:auto inline here. A container with overflow-x:auto
   computes overflow-y to auto too, which would silently break position:sticky
   on <th> against the page scroll — so give the wrapper a real height and let
   the rows scroll inside the panel, header pinned. */
.dt-wrap{
  max-height:calc(100dvh - var(--head-h) - 210px);
  min-height:180px;
  overflow-y:auto;
  scrollbar-width:thin;
  overscroll-behavior:contain;
}
.dt-table{ width:100%; border-collapse:separate; border-spacing:0; font-size:.9rem }
.dt-table th,.dt-table td{ text-align:left; vertical-align:middle; padding:9px 12px }
.dt-table thead th{
  position:sticky; top:0; z-index:2;
  background:var(--panel-2);
  border-bottom:1px solid var(--line-2);
  font-family:var(--font-display); font-weight:500; font-size:.66rem;
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink);
  white-space:nowrap;
}
.dt-table thead th.is-sorted{ color:var(--ink); background:var(--tint-2) }
.dt-table tbody td{ border-bottom:1px solid var(--line) }
.dt-table tbody tr:last-child td{ border-bottom:0 }
.dt-row{ transition:background .12s var(--ease) }
.dt-row:hover,.dt-row:focus-within{ background:var(--tint) }
.dt-row[tabindex]{ cursor:pointer }
.dt-row:focus-visible{ outline:2px solid var(--focus); outline-offset:-2px }
.dt-row.is-dragging{ opacity:.45 }
.dt-row.is-over-before td{ box-shadow:inset 0 2px 0 0 var(--fill) }
.dt-row.is-over-after td{ box-shadow:inset 0 -2px 0 0 var(--fill) }
.dt-table td[align="right"],.dt-table th[align="right"]{ text-align:right }
.dt-table .num,.dt-table .col-num{ text-align:right; font-variant-numeric:tabular-nums; font-family:var(--font-mono); font-size:.8rem; color:var(--ink-2) }

.dt-sort{
  display:inline-flex; align-items:center; gap:5px; cursor:pointer;
  font:inherit; letter-spacing:inherit; text-transform:inherit; color:inherit; text-align:left;
}
.dt-sort:hover{ color:var(--ink) }
.dt-arrow{ width:13px; height:13px; display:inline-grid; place-items:center; opacity:0; transition:opacity .15s var(--ease), transform .15s var(--ease) }
.dt-arrow svg{ width:13px; height:13px; fill:currentColor; display:block }
.dt-sort:hover .dt-arrow{ opacity:.5 }
.dt-arrow.on,th.is-sorted .dt-arrow{ opacity:1 }
th[aria-sort="descending"] .dt-arrow{ transform:rotate(180deg) }

.dt-th-handle{ width:36px; padding-left:10px !important; padding-right:0 !important }
.dt-handle{
  width:24px; height:28px; cursor:grab; color:var(--ink-3); opacity:.55;
  transition:opacity .15s var(--ease), color .15s var(--ease); touch-action:none;
}
.dt-handle:active{ cursor:grabbing }
.dt-handle svg{ width:16px; height:16px }
.dt-row:hover .dt-handle,.dt-row:focus-within .dt-handle{ opacity:1; color:var(--ink-2) }
.dt-handle.is-off{ opacity:.25; cursor:not-allowed }
.dt-norows{ padding:26px 16px; text-align:center; color:var(--ink-2); font-size:.88rem }
.dt-emptyhost{ padding:0 }

/* view-supplied cells */
.row-actions{ display:inline-flex; align-items:center; gap:4px; justify-content:flex-end }
@media (hover:hover){
  .row-actions{ opacity:0; transition:opacity .14s var(--ease) }
  tr:hover .row-actions,tr:focus-within .row-actions,
  .pcard:hover .row-actions,.pcard:focus-within .row-actions{ opacity:1 }
}
.cell-title{ font-weight:600; line-height:1.35 }
.cell-sub{ font-size:.79rem; color:var(--ink-2); line-height:1.35; margin-top:1px }
.cell-clamp{ display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden }
.thumb{
  width:44px; height:44px; border-radius:8px; object-fit:cover;
  background:var(--tint-2); border:1px solid var(--line);
}
.thumb-empty{ display:grid; place-items:center; color:var(--ink-3) }
.thumb-empty svg{ width:18px; height:18px; fill:currentColor }

/* ============================================================
   Drawer — ui.js positions the backdrop and the panel inline
   ============================================================ */
.drawer-backdrop{
  background:rgba(0,0,0,.42);
  animation:fade-in .2s var(--ease) both;
}
.drawer-panel{
  box-shadow:var(--shadow-lg);
  animation:drawer-in .3s var(--ease) both;
}
@keyframes fade-in{ from{ opacity:0 } to{ opacity:1 } }
@keyframes drawer-in{ from{ transform:translateX(100%) } to{ transform:none } }
.drawer-head{
  flex:none; display:flex; align-items:center; gap:12px;
  padding:14px 18px; border-bottom:1px solid var(--line);
}
.drawer-titles{ flex:1; min-width:0 }
.drawer-title{ font-family:var(--font-display); font-weight:700; font-size:1.06rem; letter-spacing:.03em }
.drawer-sub{
  margin:2px 0 0; font-family:var(--font-display); font-weight:500; font-size:.62rem;
  letter-spacing:.18em; text-transform:uppercase; color:var(--ink-2);
}
.drawer-body{ padding:18px; scrollbar-width:thin; overscroll-behavior:contain }
.drawer-actions{
  flex:none; display:flex; align-items:center; justify-content:flex-end; gap:10px;
  padding:12px 18px; border-top:1px solid var(--line); background:var(--panel);
}
.drawer-actions > .btn-danger:first-child,
.drawer-actions > .btn-quiet:first-child{ margin-right:auto }

/* ============================================================
   Modal — .modal is the panel, .modal-backdrop the fixed scrim
   ============================================================ */
.modal-backdrop{
  background:rgba(0,0,0,.48); padding:20px;
  animation:fade-in .2s var(--ease) both;
}
.modal{
  position:relative; width:min(560px, 100%); max-height:calc(100dvh - 40px);
  display:flex; flex-direction:column;
  border-radius:var(--r-lg); box-shadow:var(--shadow-lg);
  animation:modal-in .24s var(--ease) both;
}
.modal-sm{ width:min(420px, 100%) }
.modal-md{ width:min(560px, 100%) }
.modal-lg{ width:min(880px, 100%) }
@keyframes modal-in{ from{ opacity:0; transform:translateY(10px) scale(.985) } to{ opacity:1; transform:none } }
.modal-head{ display:flex; align-items:center; gap:12px; padding:16px 20px 12px }
.modal-title{ font-family:var(--font-display); font-weight:700; font-size:1.1rem; letter-spacing:.03em; flex:1; min-width:0 }
.modal-body{ padding:0 20px 4px; overflow-y:auto; scrollbar-width:thin }
.modal-body > p{ color:var(--ink-2) }
.modal-actions{ display:flex; align-items:center; justify-content:flex-end; gap:10px; padding:16px 20px 18px }
.modal-danger .modal-head::before{
  content:""; width:10px; height:10px; border-radius:50%; flex:none;
  background:var(--danger); box-shadow:0 0 0 5px color-mix(in srgb, var(--danger) 18%, transparent);
  margin-right:2px;
}
.modal-danger .modal-title{ color:var(--danger) }

/* ============================================================
   Toasts — ui.js owns the stack's geometry; this is the skin
   ============================================================ */
.toast{
  display:flex; align-items:flex-start; gap:10px;
  padding:11px 14px; border-radius:var(--r-sm);
  border-left:3px solid var(--ink-3);
  box-shadow:var(--shadow-lg);
  font-size:.88rem; line-height:1.45;
  animation:toast-in .26s var(--ease) both;
}
.toast-ico{ width:17px; height:17px; margin-top:1px; flex:none }
.toast-ico svg{ width:17px; height:17px }
.toast-msg{ flex:1; min-width:0; overflow-wrap:anywhere }
.toast-success{ border-left-color:var(--ok) }
.toast-success .toast-ico{ color:var(--ok) }
.toast-error{ border-left-color:var(--danger) }
.toast-error .toast-ico{ color:var(--danger) }
.toast-warn{ border-left-color:var(--warn) }
.toast-warn .toast-ico{ color:var(--warn) }
.toast-info{ border-left-color:var(--fill) }
.toast-info .toast-ico{ color:var(--fill) }
.toast .btn-icon{ width:24px; height:24px; margin:-2px -4px 0 2px }
.toast-out{ animation:toast-out .2s var(--ease) both }
@keyframes toast-in{ from{ opacity:0; transform:translateY(10px) scale(.98) } to{ opacity:1; transform:none } }
@keyframes toast-out{ to{ opacity:0; transform:translateY(6px) scale(.98) } }

/* ============================================================
   Forms
   ============================================================ */
.form{ display:flex; flex-direction:column; gap:15px; min-width:0 }
.f-field{ display:flex; flex-direction:column; gap:6px; min-width:0 }
.f-label{
  display:flex; align-items:center; gap:5px;
  font-family:var(--font-display); font-weight:500; font-size:.68rem;
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-2);
}
.f-req{ color:var(--danger); font-style:normal; font-size:.8rem; line-height:1 }
.f-help{ margin:0; font-size:.78rem; color:var(--ink-2); line-height:1.45 }
.f-error{ margin:0; font-size:.79rem; color:var(--danger); font-weight:500 }
.f-readonly .input,.f-readonly .input:hover{ background:var(--tint); color:var(--ink-2) }
.has-error .input,.has-error .input-area,.has-error .input-select{ border-color:var(--danger) }
.form-row,.row2{ display:grid; gap:15px; grid-template-columns:repeat(2, minmax(0,1fr)) }
.row3{ display:grid; gap:15px; grid-template-columns:repeat(3, minmax(0,1fr)) }

/* :not([type]) is in the list because <input> with no type attribute is not
   matched by input[type="text"], and a hyperscript helper often omits it */
input:not([type]),
input[type="text"],input[type="url"],input[type="number"],input[type="date"],
input[type="email"],input[type="password"],input[type="search"],input[type="tel"],
textarea,select,.input,.input-area,.input-select{
  width:100%; min-width:0;
  padding:9px 12px;
  background:var(--field); color:var(--ink);
  border:1px solid var(--line-2); border-radius:var(--r-sm);
  font-family:var(--font-body); font-size:.92rem; line-height:1.5;
  transition:border-color .15s var(--ease), background .15s var(--ease);
}
textarea,.input-area{ min-height:96px; resize:vertical; line-height:1.6 }
input:hover,textarea:hover,select:hover{ border-color:var(--ink-3) }
input:focus-visible,textarea:focus-visible,select:focus-visible{
  outline:2px solid var(--focus); outline-offset:1px; border-color:transparent;
}
input:disabled,textarea:disabled,select:disabled,.is-busy input,.is-busy textarea,.is-busy select{
  opacity:.55; cursor:not-allowed;
}
input[readonly]{ background:var(--tint); color:var(--ink-2) }
input[aria-invalid="true"]{ border-color:var(--danger) }
/* Firefox dims placeholders on top of the colour; 80% of the ink is as light as
   a placeholder can go and still clear 4.5:1 on the pale panels. */
::placeholder{ color:color-mix(in srgb, var(--ink) 80%, transparent); opacity:1 }
input[type="number"],.input-mono{ font-variant-numeric:tabular-nums }
.input-mono{ font-family:var(--font-mono); font-size:.84rem }
input[type="date"]::-webkit-calendar-picker-indicator{ cursor:pointer; opacity:.6 }
input[type="date"]::-webkit-calendar-picker-indicator:hover{ opacity:1 }
input[type="search"]::-webkit-search-cancel-button{ cursor:pointer }

select,.input-select{
  appearance:none; -webkit-appearance:none;
  padding-right:34px; cursor:pointer;
  /* the field is pale in every theme, so a fixed dark chevron is always legible */
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-opacity='.6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat; background-position:right 11px center; background-size:15px;
}
.view-head select,.view-head .input{ background-color:var(--field); border-color:transparent }
.view-head select{
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-opacity='.8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
/* a combo box carries its own datalist arrow in some engines; keep the box plain */
.input-combo{ padding-right:12px; background-image:none }

.check{ display:flex; align-items:flex-start; gap:9px; cursor:pointer; font-size:.9rem }
.check input[type="checkbox"],.check input[type="radio"]{ width:17px; height:17px; margin:2px 0 0; flex:none; cursor:pointer }
.check span{ line-height:1.45 }
.checks{ display:flex; flex-wrap:wrap; gap:8px 18px }
/* ---- repeatable rows: strings and links ---- */
.rep{ display:flex; flex-direction:column; gap:7px; align-items:flex-start }
.rep-list{ display:flex; flex-direction:column; gap:7px; width:100% }
.rep-row{ display:flex; align-items:center; gap:7px; min-width:0 }
.rep-row.is-dragging{ opacity:.5 }
.rep-row.is-over-before{ box-shadow:inset 0 2px 0 0 var(--fill) }
.rep-row.is-over-after{ box-shadow:inset 0 -2px 0 0 var(--fill) }
.rep-handle{
  width:22px; height:32px; cursor:grab; color:var(--ink-3); touch-action:none;
}
.rep-handle:active{ cursor:grabbing }
.rep-handle svg{ width:15px; height:15px }
.rep-fields{ flex:1; min-width:0; display:flex; gap:7px; align-items:center }
.rep-fields > *{ min-width:0 }
.rep-kind{ flex:0 1 132px }
.rep-href{ flex:2 1 0 }
.rep-text{ flex:1 1 0 }
.rep-del{ flex:none; width:30px; height:30px }
.rep-del svg{ width:16px; height:16px }
.rep > .btn{ align-self:flex-start }

/* ---- image picker ---- */
.imgpick{ display:grid; grid-template-columns:120px minmax(0,1fr); gap:12px; align-items:start }
.imgpick-thumb{
  position:relative; aspect-ratio:1; border-radius:var(--r-sm); overflow:hidden;
  background:var(--tint); border:1px solid var(--line);
  display:grid; place-items:center; color:var(--ink-3);
  transition:border-color .15s var(--ease), background .15s var(--ease);
}
.imgpick-thumb.is-drop,.imgpick.is-drop .imgpick-thumb{
  border-color:var(--fill); border-style:dashed; background:var(--tint-2);
}
.imgpick-img{ width:100%; height:100%; object-fit:cover; grid-area:1/1 }
.imgpick-ph{ grid-area:1/1; width:28px; height:28px }
.imgpick-ph svg{ width:28px; height:28px }
.imgpick-body{ display:flex; flex-direction:column; gap:8px; min-width:0 }
.imgpick-path{ font-family:var(--font-mono); font-size:.78rem }
.imgpick-actions{ display:flex; align-items:center; gap:7px; flex-wrap:wrap }
.imgpick-status{ margin:0; font-size:.78rem; color:var(--ink-2) }
.imgpick-bar,.bar{ height:4px; border-radius:2px; background:var(--tint-2); overflow:hidden }
.imgpick-bar-fill,.bar > i{ display:block; height:100%; width:0; background:var(--fill); transition:width .2s linear }

/* the browse modal's library grid, and any view that wants the same tiles */
.mediagrid,.mgrid{ display:grid; gap:12px; grid-template-columns:repeat(auto-fill, minmax(min(100%,148px), 1fr)) }
.mediatile,.mtile{
  position:relative; border-radius:var(--r-sm); overflow:hidden; text-align:left;
  border:1px solid var(--line); box-shadow:var(--shadow); cursor:pointer; width:100%;
  transition:transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.mediatile:hover,.mtile:hover{ transform:translateY(-2px); box-shadow:var(--shadow-lg) }
.mediatile img,.mtile img{ width:100%; aspect-ratio:1; object-fit:cover; background:var(--tint-2) }
.mediatile-name,.mtile-name{
  display:block; padding:7px 9px 9px; font-size:.76rem; font-weight:500;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.mtile-meta{ padding:0 9px 9px; margin-top:-6px; font-family:var(--font-mono); font-size:.68rem; color:var(--ink-2) }
.mediatile.on,.mtile.on,.mediatile[aria-selected="true"]{ outline:2px solid var(--fill); outline-offset:-2px }
.mtile-flag{
  position:absolute; top:6px; left:6px; padding:1px 7px; border-radius:999px;
  background:var(--panel); color:var(--ink); box-shadow:var(--shadow);
  font-family:var(--font-display); font-size:.6rem; letter-spacing:.1em; text-transform:uppercase;
}

.dropzone{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
  padding:34px 22px; text-align:center; cursor:pointer;
  border:1.5px dashed var(--line-2); border-radius:var(--r);
  background:var(--tint); color:var(--ink-2);
  transition:border-color .15s var(--ease), background .15s var(--ease);
}
.dropzone:hover{ border-color:var(--ink-3) }
.dropzone.is-drop,.dropzone.over{ border-color:var(--fill); background:var(--tint-2) }
.dropzone b{ font-family:var(--font-display); font-weight:500; font-size:1rem; letter-spacing:.03em; color:var(--ink) }
.dropzone span{ font-size:.82rem }
.dropzone svg{ width:30px; height:30px; fill:currentColor; color:var(--ink-3) }
.upload-row{ display:grid; grid-template-columns:1fr auto; gap:4px 10px; align-items:center; font-size:.82rem }
.upload-row .bar{ grid-column:1 / -1 }

/* ============================================================
   Empty state, skeleton
   ============================================================ */
.empty{
  display:flex; flex-direction:column; align-items:center; gap:10px;
  padding:56px 24px; text-align:center; border-radius:var(--r);
}
.empty-ico{
  width:52px; height:52px; border-radius:50%;
  background:var(--tint-2); color:var(--ink-3); margin-bottom:2px;
}
.empty-ico svg{ width:24px; height:24px }
.empty-title{ font-family:var(--font-display); font-weight:700; font-size:1.05rem; letter-spacing:.03em }
.empty-body{ margin:0; max-width:46ch; font-size:.88rem; color:var(--ink-2) }
.empty .btn{ margin-top:6px }
.dt .empty,.dt-emptyhost .empty{ box-shadow:none; border-radius:0 }

.skel{ display:flex; flex-direction:column; gap:10px; padding:16px }
.skel-row{
  height:38px; border-radius:var(--r-sm);
  background:linear-gradient(90deg, var(--tint) 25%, var(--tint-2) 37%, var(--tint) 63%);
  background-size:400% 100%;
  animation:skel 1.4s ease infinite;
}
.skel-row:nth-child(2n){ opacity:.85 }
@keyframes skel{ from{ background-position:100% 0 } to{ background-position:0 0 } }

/* ============================================================
   Login
   ============================================================ */
.login{
  min-height:100dvh; display:grid; place-items:center;
  padding:24px; background:var(--canvas);
  position:relative; isolation:isolate;
}
.login::before{
  content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background-image:linear-gradient(color-mix(in srgb, var(--on-canvas) 6%, transparent) 1px, transparent 1px),
                   linear-gradient(90deg, color-mix(in srgb, var(--on-canvas) 6%, transparent) 1px, transparent 1px);
  background-size:72px 72px;
  mask-image:radial-gradient(ellipse 70% 60% at 50% 40%, #000 10%, transparent 72%);
  -webkit-mask-image:radial-gradient(ellipse 70% 60% at 50% 40%, #000 10%, transparent 72%);
}
.login-card{
  width:min(410px, 100%); padding:28px 28px 26px;
  border-radius:var(--r-lg); box-shadow:var(--shadow-lg);
  display:flex; flex-direction:column;
}
.login-logo{ width:38px; height:auto; margin-bottom:14px }
.login-title{ font-size:1.24rem; letter-spacing:.04em }
.login-sub{ margin:4px 0 20px; font-size:.86rem; color:var(--ink-2) }
.login-form{ display:flex; flex-direction:column; gap:13px }
.login-form .f-label,.login-form label{ margin-bottom:-6px }
.login-form .btn{ width:100%; padding:11px 16px; font-size:.92rem; margin-top:4px }
.login-error,.login-form .f-error{
  display:flex; align-items:flex-start; gap:9px; margin:0;
  padding:10px 12px; border-radius:var(--r-sm);
  background:color-mix(in srgb, var(--danger) 12%, transparent);
  border:1px solid color-mix(in srgb, var(--danger) 40%, transparent);
  color:var(--danger); font-size:.85rem; font-weight:500; line-height:1.45;
}
.login-foot{ margin:16px 0 0; font-size:.76rem; color:var(--ink-2); text-align:center }
.account-form{ max-width:420px }

/* ============================================================
   Extras for the views — same tokens, nothing here is required
   ============================================================ */
.stats{ display:grid; gap:12px; grid-template-columns:repeat(auto-fit, minmax(min(100%,158px), 1fr)) }
.stat{ border-radius:var(--r); padding:15px 16px; box-shadow:var(--shadow); display:flex; flex-direction:column; gap:4px }
.stat-n{ font-family:var(--font-display); font-weight:700; font-size:1.9rem; letter-spacing:.02em; line-height:1 }
.stat-k{ font-family:var(--font-display); font-weight:500; font-size:.64rem; letter-spacing:.16em; text-transform:uppercase; color:var(--ink-2) }
.stat-sub{ font-size:.78rem; color:var(--ink-2) }

.qcards{ display:grid; gap:12px; grid-template-columns:repeat(auto-fill, minmax(min(100%,232px), 1fr)) }
.qcard{
  display:flex; align-items:center; gap:12px; text-align:left; width:100%;
  padding:14px 15px; border-radius:var(--r); border:1px solid var(--line);
  box-shadow:var(--shadow); cursor:pointer; text-decoration:none;
  transition:transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.qcard:hover{ transform:translateY(-2px); box-shadow:var(--shadow-lg); border-color:var(--line-2); text-decoration:none }
.qcard > .ico{ width:36px; height:36px; border-radius:10px; background:var(--tint-2); color:var(--ink) }
.qcard > .ico svg{ width:20px; height:20px }
.qcard b{ display:block; font-family:var(--font-display); font-weight:500; font-size:.92rem; letter-spacing:.03em }
.qcard span span,.qcard small{ display:block; font-size:.78rem; color:var(--ink-2); line-height:1.35 }

.pcard{
  position:relative; display:flex; flex-direction:column;
  border-radius:var(--r); overflow:hidden; box-shadow:var(--shadow);
  transition:transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pcard:hover{ transform:translateY(-3px); box-shadow:var(--shadow-lg) }
.pcard-img{ aspect-ratio:4/5; background:var(--tint-2); position:relative; display:grid; place-items:center }
.pcard-img img{ width:100%; height:100%; object-fit:cover }
.pcard-body{ padding:12px 14px 14px; display:flex; flex-direction:column; gap:6px; flex:1 }
.pcard-name{ font-family:var(--font-display); font-weight:700; font-size:.98rem; letter-spacing:.02em; line-height:1.2 }
.pcard-meta{ font-size:.79rem; color:var(--ink-2); line-height:1.45; display:flex; flex-direction:column; gap:1px }
.pcard-links{ display:flex; flex-wrap:wrap; gap:5px; margin-top:auto; padding-top:8px }
.pcard-links a,.pcard-links span{ width:26px; height:26px; display:grid; place-items:center; border-radius:7px; background:var(--tint-2); color:var(--ink) }
.pcard-links svg{ width:14px; height:14px; fill:currentColor }
.pcard-sec,.gcard-badge{
  position:absolute; padding:2px 8px; border-radius:999px; max-width:calc(100% - 16px);
  background:var(--panel); color:var(--ink); box-shadow:var(--shadow);
  font-family:var(--font-display); font-size:.6rem; letter-spacing:.1em; text-transform:uppercase;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.pcard-sec{ top:8px; left:8px }
.card-actions{ position:absolute; top:8px; right:8px; z-index:2; display:flex; gap:4px }
.card-actions .btn-icon{ background:var(--panel); box-shadow:var(--shadow); width:30px; height:30px }
@media (hover:hover){
  .pcard .card-actions,.mediatile .card-actions,.mtile .card-actions,.gcard .card-actions{ opacity:0; transition:opacity .15s var(--ease) }
  .pcard:hover .card-actions,.pcard:focus-within .card-actions,
  .mediatile:hover .card-actions,.mediatile:focus-within .card-actions,
  .mtile:hover .card-actions,.mtile:focus-within .card-actions,
  .gcard:hover .card-actions,.gcard:focus-within .card-actions{ opacity:1 }
}

.gcards{ display:grid; gap:12px; grid-template-columns:repeat(auto-fill, minmax(min(100%,176px), 1fr)) }
.gcard{ position:relative; border-radius:var(--r-sm); overflow:hidden; box-shadow:var(--shadow); border:1px solid var(--line) }
.gcard img{ width:100%; aspect-ratio:4/3; object-fit:cover; background:var(--tint-2) }
.gcard-badge{ left:7px; bottom:7px }
/* top-left: bottom-right would collide with a long album name and top-right
   belongs to .card-actions */
.gcard-dim{
  position:absolute; left:7px; top:7px;
  font-family:var(--font-mono); font-size:.64rem;
  padding:1px 6px; border-radius:6px; background:var(--panel); color:var(--ink-2);
}
.gcard.is-dragging{ opacity:.45 }
.gcard.is-over-before{ box-shadow:inset 3px 0 0 0 var(--fill) }

/* A segmented control on its own dark band rather than an underlined row: a tab
   strip usually sits on the bare canvas, and nothing at .84rem survives there.
   Fixed colours, so it reads the same inside a card. */
.tabs{
  display:inline-flex; gap:2px; flex-wrap:wrap; align-self:flex-start;
  max-width:100%; padding:4px; border-radius:999px;
  background:var(--canvas-3); color:var(--on-canvas);
}
.tab{
  padding:6px 14px; border-radius:999px; cursor:pointer;
  font-family:var(--font-display); font-size:.84rem; font-weight:500; letter-spacing:.04em;
  color:color-mix(in srgb, var(--on-canvas) 88%, transparent);
  transition:color .15s var(--ease), background .15s var(--ease);
}
.tab:hover{ background:color-mix(in srgb, var(--on-canvas) 13%, transparent); color:var(--on-canvas) }
.tab.on{ background:var(--rail-bg); color:var(--canvas-3) }
.tab:focus-visible{ outline:2px solid var(--on-canvas); outline-offset:-2px }

.filters{ display:flex; align-items:center; gap:8px; flex-wrap:wrap }
.filters select,.filters input{ width:auto; min-width:150px; padding:6px 11px; font-size:.85rem }
.filters select{ padding-right:32px }
.pager{ display:flex; align-items:center; gap:8px; margin-left:auto; font-size:.82rem; color:var(--ink-2) }

.log{ display:flex; flex-direction:column; font-family:var(--font-mono); font-size:.76rem; line-height:1.6 }
.log-row{ display:grid; grid-template-columns:132px 96px 1fr; gap:10px; padding:6px 16px; border-bottom:1px solid var(--line) }
.log-row:last-child{ border-bottom:0 }
.log-row:hover{ background:var(--tint) }
.log-time{ color:var(--ink-2) }
.log-actor{ font-weight:500; overflow:hidden; text-overflow:ellipsis }
.log-what{ overflow-wrap:anywhere }

/* ============================================================
   Reordering — sortable()'s hooks

   .is-dragging / .is-over-before / .is-over-after are styled per component
   above (.dt-row, .rep-row, .gcard) and stay exactly as they were. What is new
   here is the ghost, the drawn insertion line, the keyboard pick-up state, and
   the two properties without which a drag does not work on a touchscreen.
   ============================================================ */

/* touch-action:none is what stops the browser treating a finger on the handle
   as the start of a scroll. Without it the gesture ends in `pointercancel` and
   the drag silently never begins — which reads as broken code rather than as a
   missing declaration, so sortable() also sets it on every handle it finds and
   cancels touchstart on one. Belt and braces, on purpose. */
.dt-handle,.rep-handle,.item-handle,.srt-handle,[data-srt-handle]{
  touch-action:none;
  -webkit-user-select:none;
  user-select:none;
  -webkit-touch-callout:none;
}
/* A drag must not paint a text selection across everything it passes over. */
body.is-sorting,body.is-sorting *{ -webkit-user-select:none; user-select:none }
body.is-sorting{ cursor:grabbing }

/* The floating clone. Geometry is inline (position, size, transform); this is
   the skin. */
.srt-ghost{
  border-radius:var(--r-sm);
  outline:1px solid var(--line-2);
  background:var(--panel);
  color:var(--rail-ink);
}
.srt-ghost .card-actions,.srt-ghost .row-actions,.srt-ghost .btn{ display:none }

/* The drawn indicator, for lists whose rows have no border to shadow. */
.srt-line{
  background:var(--fill);
  border-radius:2px;
  box-shadow:0 0 0 1px color-mix(in srgb, var(--fill) 40%, transparent);
}
.srt-line-y{ height:3px; margin-top:-1.5px }
.srt-line-x{ width:3px; margin-left:-1.5px }

/* Picked up from the keyboard: held, not dropped. Deliberately louder than the
   pointer's .is-dragging, because nothing is following a cursor to say so. */
.is-picked{
  outline:2px dashed var(--fill);
  outline-offset:2px;
  background:var(--tint);
}

/* ============================================================
   Rich text — richField(). It edits SOURCE, so the box is a textarea and the
   preview below it is a separate, non-editable node.
   ============================================================ */
.rich{ display:flex; flex-direction:column; gap:7px; min-width:0 }
.rich-tools{
  display:flex; align-items:center; gap:2px; flex-wrap:wrap;
  padding:3px; border:1px solid var(--line); border-radius:var(--r-xs);
  background:var(--tint);
}
.rich-tools .btn-icon{ width:28px; height:28px }
.rich-tools .btn-icon svg{ width:15px; height:15px }
.rich-count{ margin-left:auto; padding-right:6px; font-family:var(--font-mono); font-size:.7rem; color:var(--ink-2) }
.rich-ta{ font-family:var(--font-mono); font-size:.84rem; line-height:1.55; resize:vertical }
.rich-prevwrap{
  position:relative; border:1px dashed var(--line); border-radius:var(--r-xs);
  padding:16px 12px 10px; min-height:44px;
}
.rich-prevlabel{
  position:absolute; top:-1px; left:10px; transform:translateY(-50%);
  padding:0 6px; background:var(--panel,var(--canvas));
  font-family:var(--font-display); font-weight:500; font-size:.6rem;
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-2);
}
.rich-prev{ font-size:.88rem; line-height:1.55; overflow-wrap:anywhere }
.rich-prev > :first-child{ margin-top:0 }
.rich-prev p{ margin:0 0 .5em }
.rich-prev p:last-child{ margin-bottom:0 }
.rich-prev ul,.rich-prev ol{ margin:0 0 .5em; padding-left:1.3em }
.rich-prev a{ color:var(--link); text-decoration:underline }
.rich-none{ color:var(--ink-3); font-style:italic }
/* What the generator will refuse or leave verbatim, shown before the save says
   so. Never a hard error: a half-typed link is a normal state to be in. */
.rich-bad{
  background:color-mix(in srgb, var(--warn) 16%, transparent);
  box-shadow:0 0 0 1px color-mix(in srgb, var(--warn) 40%, transparent);
  border-radius:3px;
}
.rich-token{ font-family:var(--font-mono); font-size:.92em }
.rich-warn{ margin:6px 0 0; font-size:.76rem; color:var(--warn) }
.rich-help{ font-size:.78rem; color:var(--ink-2) }
.rich-help summary{ cursor:pointer; padding:2px 0 }
.rich-grammar{ display:grid; grid-template-columns:auto minmax(0,1fr); gap:4px 14px; margin:6px 0 0 }
.rich-grammar dt code{ font-family:var(--font-mono); font-size:.76rem; color:var(--ink) }
.rich-grammar dd{ margin:0 }
.rich-note{ margin:8px 0 0 }

/* ============================================================
   Repeatable sub-objects — itemsEditor()
   ============================================================ */
.items{ display:flex; flex-direction:column; gap:8px; align-items:flex-start }
.items-list{ display:flex; flex-direction:column; gap:6px; width:100% }
.item-row{
  display:flex; align-items:flex-start; gap:6px; min-width:0;
  border:1px solid var(--line); border-radius:var(--r-xs); padding:4px;
}
.item-row:focus-visible{ outline:2px solid var(--focus); outline-offset:1px }
.item-row.is-dragging{ opacity:.45 }
.item-row.is-over-before{ box-shadow:inset 0 2px 0 0 var(--fill) }
.item-row.is-over-after{ box-shadow:inset 0 -2px 0 0 var(--fill) }
.item-handle{
  display:grid; place-items:center; width:22px; height:30px; flex:none;
  cursor:grab; color:var(--ink-3);
}
.item-handle:active{ cursor:grabbing }
.item-handle svg{ width:15px; height:15px }
.item-main{ flex:1; min-width:0 }
.item-head{
  display:flex; align-items:center; gap:7px; width:100%; padding:5px 4px;
  background:none; border:0; color:inherit; font:inherit; text-align:left; cursor:pointer;
}
.item-head:hover{ color:var(--ink) }
.item-caret{ display:grid; place-items:center; width:14px; height:14px; flex:none; transition:transform .16s var(--ease) }
.item-caret svg{ width:14px; height:14px }
.item-row.is-open .item-caret{ transform:rotate(90deg) }
.item-n{
  font-family:var(--font-mono); font-size:.7rem; color:var(--ink-3);
  min-width:1.4em; text-align:right; flex:none;
}
.item-name{ font-weight:500; font-size:.85rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0 }
.item-name:empty::before{ content:"Untitled"; color:var(--ink-3); font-weight:400; font-style:italic }
.item-body{ padding:4px 4px 8px }
.item-del{ flex:none; width:28px; height:28px }

/* ============================================================
   Palette — blockPalette(). Reuses .qcards/.qcard; every card is a <button>.
   ============================================================ */
.palette{ display:flex; flex-direction:column; gap:10px; min-width:0 }
.palette-bar{ position:sticky; top:0; z-index:2; padding-bottom:2px; background:inherit }
.palette-grid{ grid-template-columns:repeat(auto-fill, minmax(min(100%,150px), 1fr)) }
.palette-card{ text-align:left; font:inherit; cursor:pointer }
.palette-card:focus-visible{ outline:2px solid var(--focus); outline-offset:2px }
.palette-card > .ico{ width:30px; height:30px }
.palette-card > .ico svg{ width:17px; height:17px }
.palette-none{ padding:12px 2px; font-size:.84rem; color:var(--ink-2) }

/* ============================================================
   Property panel — propertyPanel()
   ============================================================ */
.ppanel{ display:flex; flex-direction:column; gap:12px; min-width:0 }
.ppanel-head{ display:flex; align-items:center; gap:10px }
.ppanel-head > .ico{
  width:32px; height:32px; border-radius:9px; flex:none;
  display:grid; place-items:center; background:var(--tint-2);
}
.ppanel-head > .ico svg{ width:18px; height:18px }
.ppanel-type{ font-family:var(--font-display); font-weight:500; font-size:.98rem; letter-spacing:.03em }
.ppanel-pos{ margin:1px 0 0; font-family:var(--font-mono); font-size:.72rem; color:var(--ink-2) }
.ppanel-desc{ margin:0; font-size:.79rem; line-height:1.45; color:var(--ink-2) }
.ppanel-moves{ display:flex; align-items:center; gap:6px; flex-wrap:wrap }
.ppanel-notes:empty{ display:none }
.ppanel-notes .note + .note{ margin-top:6px }
.ppanel-frame summary{
  cursor:pointer; padding:6px 0; font-family:var(--font-display); font-weight:500;
  font-size:.7rem; letter-spacing:.14em; text-transform:uppercase; color:var(--ink-2);
}
.ppanel-frame[open] summary{ margin-bottom:8px }
.ppanel-foot{ display:flex; align-items:center; gap:8px; padding-top:4px; border-top:1px solid var(--line) }
.ppanel-foot > .btn-danger{ margin-left:auto }
.f-bind .f-label{ align-items:baseline }
.f-bindmark{
  font-family:var(--font-mono); font-style:normal; font-size:.62rem;
  letter-spacing:.06em; text-transform:uppercase; color:var(--ink-3);
  border:1px solid var(--line); border-radius:3px; padding:0 4px;
}
.f-blocks{ display:flex; align-items:center; gap:10px; flex-wrap:wrap }
.f-blocks-sum{ margin:0; font-size:.84rem; color:var(--ink-2) }

/* ============================================================
   splitPane()
   ============================================================ */
.split{ display:flex; min-width:0; min-height:0 }
.split-y{ flex-direction:column }
.split-pane{ min-width:0; min-height:0; overflow:auto }
.split-bar{
  flex:none; position:relative; display:grid; place-items:center;
  background:transparent; border:0; padding:0;
}
.split-x > .split-bar{ width:9px; cursor:col-resize; touch-action:none }
.split-y > .split-bar{ height:9px; cursor:row-resize; touch-action:none }
.split-grip{ display:block; background:var(--line); border-radius:2px }
.split-x > .split-bar .split-grip{ width:1px; height:100% }
.split-y > .split-bar .split-grip{ height:1px; width:100% }
.split-bar:hover .split-grip,.split-bar:focus-visible .split-grip,
.split.is-resizing .split-bar .split-grip{ background:var(--fill) }
.split-x > .split-bar:hover .split-grip,.split.is-resizing .split-x > .split-bar .split-grip{ width:3px }
.split-bar:focus-visible{ outline:2px solid var(--focus); outline-offset:-2px }

/* ============================================================
   menu() — the ⋯ popup. Geometry is inline; this is the skin.
   ============================================================ */
.menu{
  background:var(--panel); color:var(--rail-ink);
  --ink:var(--rail-ink);
  --ink-2:color-mix(in srgb, var(--rail-ink) 80%, transparent);
  --ink-3:color-mix(in srgb, var(--rail-ink) 62%, transparent);
  --line:color-mix(in srgb, var(--rail-ink) 14%, transparent);
  --tint:color-mix(in srgb, var(--rail-ink) 6%, transparent);
  border-radius:var(--r-sm); box-shadow:var(--shadow-lg);
  padding:5px; max-height:min(60vh, 420px); overflow-y:auto; scrollbar-width:thin;
}
.menu-item{
  display:flex; align-items:center; gap:9px; width:100%; padding:7px 10px;
  background:none; border:0; border-radius:var(--r-xs); color:var(--ink);
  font:inherit; font-size:.86rem; text-align:left; cursor:pointer;
}
.menu-item:hover:not(:disabled){ background:var(--tint) }
.menu-item:focus-visible{ outline:2px solid var(--focus); outline-offset:-2px }
.menu-item:disabled{ opacity:.45; cursor:not-allowed }
.menu-item svg{ width:16px; height:16px; flex:none }
.menu-gap{ width:16px; height:16px; flex:none }
.menu-item.is-danger{ color:var(--danger) }
.menu-sep{ border:0; border-top:1px solid var(--line); margin:4px 6px }

/* ============================================================
   Responsive — the components' half. The rail's is in shell.css.
   ============================================================ */
@media (max-width:1080px){
  /* full-width slide-over: 620px of drawer on a phone is the whole screen anyway */
  .drawer-panel{ width:100% !important }
  .dt-wrap{ max-height:calc(100dvh - var(--mbar-h) - var(--head-h) - 150px) }
}
@media (max-width:720px){
  .form-row,.row2,.row3{ grid-template-columns:1fr }
  .imgpick{ grid-template-columns:96px minmax(0,1fr) }
  .rep-fields{ flex-wrap:wrap }
  .rep-kind,.rep-href,.rep-text{ flex:1 1 100% }
  .drawer-body,.drawer-head,.drawer-actions{ padding-left:14px; padding-right:14px }
  .log-row{ grid-template-columns:1fr; gap:2px; padding:8px 14px }
  .cards{ grid-template-columns:repeat(auto-fill, minmax(min(100%,180px), 1fr)) }
  /* on a phone the table is horizontal-first; the header un-sticks, which is the
     honest trade for a scroller that has to move on both axes */
  .dt-wrap{ max-height:none }
  .dt-table{ min-width:560px }
  .dt-searchwrap{ max-width:none }
}
@media (max-width:400px){
  .stats{ grid-template-columns:1fr 1fr }
  .qcards{ grid-template-columns:1fr }
  /* The palette is the exception: at 380px two columns of block cards still
     read, and one column puts the fourteenth type four screens down. */
  .palette-grid{ grid-template-columns:1fr 1fr }
  .mediagrid,.mgrid{ grid-template-columns:repeat(auto-fill, minmax(min(100%,116px), 1fr)) }
  .rich-tools .btn-icon{ width:32px; height:32px }
  .ppanel-moves .btn{ flex:1 1 auto; justify-content:center }
}

/* ============================================================
   Motion
   ============================================================ */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important;
  }
  .skel-row{ background:var(--tint-2) }
}

@media print{
  .toast-stack,.drawer-backdrop,.modal-backdrop,.dt-bar,.menu,.srt-ghost,.srt-line{ display:none !important }
  body{ background:#fff; color:#000 }
  .card,.dt{ box-shadow:none; border:1px solid #999 }
  .dt-wrap{ max-height:none; overflow:visible }
}
