/* Base shared by every interface alternative. Each alternative adds its
   own layout on top; nothing here decides how a slice is arranged. */

:root {
  /* The real height of .topbar (disclaimer + app-bar), kept current by
     JS — see `syncTopbarHeight`. Everything that docks under it reads
     this instead of a guessed constant, so a taller disclaimer (a
     narrow screen wrapping its text to two lines) never leaves a gap
     or an overlap. */
  --topbar-h: 86px;
  --command: #2563eb;
  --command-soft: #eff6ff;
  --event: #f97316;
  --event-soft: #fff7ed;
  --entity: #16a34a;
  --entity-soft: #f0fdf4;
  /* Projections share the entity green: a standalone projection and an
     entity property are the same thing — folded state — and a second
     colour would suggest a distinction the model does not make. */
  --projection: var(--entity);
  --projection-soft: var(--entity-soft);
  /* Constraints. Fuchsia rather than a shade of the entity green: a
     rule is not a kind of thing in the model, it is the gate in front
     of one, and it should not read as belonging to whatever it guards. */
  --rule: #c026d3;
  --rule-soft: #fdf4ff;
  --type: #6b7280;
  --type-soft: #f9fafb;
  /* The interface's own colour — buttons, focus, hover, the selected
     tab — and deliberately not one of the kinds above: chrome painted
     command blue reads as "this is about a command". dcb.events teal
     (Material's teal, a step darker for contrast on white), so the
     playground and the site it lives on read as one place. */
  --accent: #007a6c;
  --accent-strong: #00594f;
  --accent-soft: #e6f4f2;
  --bg: #f8fafc;
  --surface: #ffffff;
  --border: #e2e8f0;
  --border-strong: #cbd5e1;
  --text: #0f172a;
  --muted: #64748b;
  --danger: #dc2626;
  --danger-soft: #fef2f2;
  --radius: 8px;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

/* The same palette after dark. Every colour above is a token and every
   rule below this line reads one, so the theme is this block and
   nothing else — except the handful of places where a chip needed a
   border and a text colour of its own, which are re-stated here. */
body.f-dark {
  color-scheme: dark;
  --command: #60a5fa;
  --command-soft: #16243d;
  --event: #fb923c;
  --event-soft: #2d1c0d;
  --entity: #4ade80;
  --entity-soft: #0f2a1c;
  --rule: #e879f9;
  --rule-soft: #2a1130;
  --type: #94a3b8;
  --type-soft: #172030;
  --accent: #00ccb8;
  --accent-strong: #5eead4;
  --accent-soft: #0b2a2a;
  --bg: #0b1220;
  --surface: #131c2e;
  --border: #22304a;
  --border-strong: #3a4b69;
  --text: #e2e8f0;
  --muted: #93a3b8;
  --danger: #f87171;
  --danger-soft: #2d1414;
}
body.f-dark .chip.command { border-color: #1e40af; color: #bfdbfe; }
body.f-dark .chip.event { border-color: #9a3412; color: #fed7aa; }
body.f-dark .chip.entity, body.f-dark .chip.projection { border-color: #166534; color: #bbf7d0; }
body.f-dark .chip.rule { border-color: #86198f; color: #f5d0fe; }
body.f-dark .btn.primary { color: #06121f; }
body.f-dark .btn.primary:hover { color: #06121f; }
body.f-dark .advanced { background: #101827; }
body.f-dark #toast { background: #1e293b; color: var(--text); }

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--text);
  background: var(--bg);
}

h1, h2, h3, h4 { margin: 0; font-weight: 600; }
h1 { font-size: 16px; }
h2 { font-size: 15px; }
h3 { font-size: 13px; }
h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); }
p { margin: 0 0 10px; }
code, .mono { font-family: var(--mono); font-size: 12px; }

/* ---------- app chrome ---------- */

/* The disclaimer sits above the bar rather than inside it, so it
   scrolls with nothing and stays put — the two stick together as one
   unit rather than each claiming top:0 for itself. */
.topbar { position: sticky; top: 0; z-index: 20; }

.disclaimer {
  text-align: center; font-size: 11.5px; font-weight: 700; letter-spacing: .01em;
  padding: 5px 12px;
  background: #fef3c7; color: #92400e; border-bottom: 1px solid #fde68a;
}
body.f-dark .disclaimer { background: #2d2408; color: #fcd34d; border-bottom-color: #4a3b0f; }

.app-bar {
  display: flex; align-items: center; gap: 16px;
  padding: 10px 18px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.app-bar .spacer { flex: 1; }
/* The way back to the site the playground lives on. Same tab: the
   playground is a place you went to, not a popup over the docs. */
.app-bar .home { color: var(--muted); text-decoration: none; font-size: 12px; white-space: nowrap; }
.app-bar .home:hover { color: var(--accent); }

/* The mark and title double as the way back to Models, for switching
   or managing what's loaded — see `renderSplash` for the full-page
   equivalent shown, unrequested, while nothing is loaded yet. */
.brand-link {
  display: flex; align-items: center; gap: 16px;
  border: 0; background: none; padding: 0; margin: 0; font: inherit; color: inherit;
  cursor: pointer; border-radius: 6px;
}
.brand-link:hover h1 { color: var(--accent); }
.brand-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.brand-mark { flex: none; width: 22px; height: 22px; display: block; }

.app-bar #model-switch, .app-bar #tools, .app-bar #jump, .app-bar #actions {
  display: flex; align-items: center; gap: 4px;
}
/* Most of the time there is no loose end to report — an empty #tools
   should not still cost the bar two gaps either side of nothing. */
.app-bar #tools:empty { display: none; }
/* display:inline-block so max-width actually clips it — a plain
   inline box ignores max-width entirely, which desktop had enough
   room to never expose. */
.app-bar .model-name {
  display: inline-block; max-width: 220px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}

/* Import/export and Settings — icon only, same footprint as the
   undo/redo buttons they sit next to. */
.app-bar .icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: none; padding: 0; font-size: 15px;
  border: 1px solid transparent; border-radius: 6px;
  background: transparent; color: var(--muted); cursor: pointer;
}
.app-bar .icon-btn:hover { border-color: var(--accent); color: var(--accent); }
.app-bar .icon-btn svg { width: 16px; height: 16px; }
/* The rail's own toggle — only meaningful once the rail is off-canvas,
   so it stays out of the bar entirely above that breakpoint. */
.app-bar .menu-btn { display: none; }

/* The trigger that opens quick open — a button that looks like a
   search field but only ever holds a placeholder; the field itself
   lives in the palette that pops over the page. Fixed height, rather
   than one padding happens to produce, so it lines up exactly with the
   icon buttons it sits beside. */
.app-bar .trigger {
  display: flex; align-items: center; gap: 7px; max-width: 260px;
  height: 30px; padding: 0 9px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--muted); font: inherit; font-size: 12.5px;
  cursor: pointer;
}
.app-bar .trigger:hover { border-color: var(--border-strong); color: var(--text); background: var(--surface); }
.app-bar .trigger svg { flex: none; width: 14px; height: 14px; }
.app-bar .trigger .label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-bar .trigger .kbd {
  margin-left: auto; flex: none; font-family: var(--mono); font-size: 10px; color: var(--muted);
  border: 1px solid var(--border); border-radius: 4px; padding: 0 4px;
}

/* ---------- narrow screens ----------
   One breakpoint, everything the bar and the rail need to keep working
   at a phone width: the rail becomes a drawer (see .rail below), its
   toggle appears, the bar's own controls compact enough that nothing
   is pushed off the edge, and touch targets grow to the ~40px a finger
   actually needs — the desktop 30px stays for a mouse. */
@media (max-width: 760px) {
  .app-bar .menu-btn { display: inline-flex; }
  .app-bar { gap: 6px; padding: 8px 10px; }
  /* No room left in the bar's budget below; the footer links back too. */
  .app-bar .home { display: none; }
  .brand-link { gap: 8px; }
  .brand-link h1 { display: none; }
  /* Everything else in the bar is fixed-width (~266px of icons, gaps
     and padding at this breakpoint's sizes) — the name gets whatever
     is left of the screen, not a fraction of it. A vw-based cap falls
     short right at the narrow end (a fixed share of not much is still
     too much); this keeps the same margin at every width instead of
     needing a second breakpoint to patch the narrowest phones. */
  .app-bar .model-name { max-width: clamp(40px, calc(100vw - 266px), 220px); }
  .app-bar .icon-btn, .app-bar .trigger { width: 40px; height: 40px; }
  .app-bar .icon-btn svg { width: 18px; height: 18px; }
  .app-bar .trigger { padding: 0; justify-content: center; }
  .app-bar .trigger svg { width: 17px; height: 17px; }
  .app-bar .trigger .label, .app-bar .trigger .kbd { display: none; }
}

/* ---------- generic bits ---------- */

.btn {
  font: inherit; font-size: 13px;
  padding: 7px 14px; border-radius: 6px;
  border: 1px solid var(--border-strong); background: var(--surface);
  color: var(--text); cursor: pointer;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; }
.btn.ghost { border-color: transparent; color: var(--muted); }
.btn.ghost:hover { border-color: var(--border-strong); }
.btn.tiny { padding: 4px 10px; font-size: 12px; }
.btn.danger:hover { border-color: var(--danger); color: var(--danger); }
/* A button with nothing left to do stays where it is rather than
   disappearing: where undo lives should not move as you use it. */
.btn:disabled { opacity: .35; cursor: default; }
.btn:disabled:hover { border-color: transparent; color: var(--muted); }

.chip {
  display: inline-block; padding: 3px 10px; border-radius: 999px;
  font-size: 12px; font-family: var(--mono);
  background: var(--type-soft); border: 1px solid var(--border);
}
.chip.command { background: var(--command-soft); border-color: #bfdbfe; color: #1e40af; }
.chip.event { background: var(--event-soft); border-color: #fed7aa; color: #9a3412; }
.chip.entity, .chip.projection { background: var(--entity-soft); border-color: #bbf7d0; color: #166534; }
.chip.rule { background: var(--rule-soft); border-color: #f5d0fe; color: #86198f; }
/* No value yet. Drawn as an absence — dashed, unfilled, italic — so it
   never reads as a value someone typed. `""` and `[]` are values and
   are drawn as values; this is the one that is not, and the whole
   point of the distinction is that you can see it. */
.chip.unset {
  background: transparent; border-style: dashed; color: var(--muted);
  font-style: italic; font-family: inherit;
}

.muted { color: var(--muted); }
.small { font-size: 12px; }
.tiny { font-size: 11px; }
.empty { color: var(--muted); font-size: 12px; font-style: italic; }

input[type=text], select, textarea {
  font: inherit; font-size: 14px;
  padding: 8px 11px; border: 1px solid var(--border-strong);
  border-radius: 5px; background: var(--surface); color: var(--text);
}
input[type=text]:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent);
}
/* Every dropdown is a customizable <select> where the browser has one
   (`appearance: base-select`) — all of them, not just the ones with
   something to show, because a styled picker beside a native one in
   the same row reads as two different widgets. What it buys: options
   that carry markup (a mark re-fronted, a token in monospace, a
   context span shown only once picked — see `pick`), group headings
   that read as headings, and a popover in the page's palette. A
   browser without it gets the native control and the options' plain
   text, which every caller keeps readable on its own. */
@supports (appearance: base-select) {
  select, ::picker(select) { appearance: base-select; }
  select { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
  select::picker-icon { color: var(--muted); font-size: 10px; }
  ::picker(select) {
    border: 1px solid var(--border-strong); border-radius: var(--radius);
    background: var(--surface); color: var(--text); font-size: 13px;
    padding: 4px; margin-top: 3px; box-shadow: 0 10px 28px -12px rgba(15, 23, 42, .45);
  }
  option { display: flex; align-items: center; gap: 4px; padding: 4px 8px; border-radius: 4px; }
  option:hover, option:focus-visible { background: var(--accent-soft); outline: none; }
  option:checked { font-weight: 600; }
  option::checkmark { color: var(--accent); }
  option:disabled { color: var(--muted); }
  optgroup {
    font-size: 10px; font-weight: 400; text-transform: uppercase;
    letter-spacing: .06em; color: var(--muted); padding-top: 4px;
  }
  optgroup + optgroup, option + optgroup, optgroup + option {
    border-top: 1px dashed var(--border); margin-top: 4px;
  }
  optgroup option { font-size: 13px; text-transform: none; letter-spacing: normal; color: var(--text); }
  selectedcontent { display: flex; align-items: center; gap: 4px; }
  option .icon, selectedcontent .icon { order: -1; }
  /* A token option (`pick`'s fourth element): monospace, short in the
     open list where its group heading carries the context, whole in
     the closed face where nothing else does. */
  .token { font-family: var(--mono); font-size: 12px; }
  option .ctx { display: none; }
  selectedcontent .ctx { display: inline; }
}
input.inline {
  border: 0; border-bottom: 1px dashed var(--border-strong);
  border-radius: 0; padding: 3px 4px; background: transparent;
  font-family: var(--mono);
}
input.inline:focus { border-bottom-color: var(--accent); }

/* ---------- advanced disclosure ---------- */

.advanced {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  background: #fbfcfe;
  margin-top: 10px;
}
.advanced > summary {
  cursor: pointer; padding: 7px 12px;
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); list-style: none;
}
.advanced > summary::-webkit-details-marker { display: none; }
.advanced > summary::before { content: "▸ "; }
.advanced[open] > summary::before { content: "▾ "; }
.advanced > div { padding: 0 12px 12px; }

/* ---------- toast ---------- */

#toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  background: var(--text); color: #fff; padding: 9px 16px;
  border-radius: 6px; font-size: 12px; z-index: 100;
  box-shadow: 0 6px 20px rgba(15,23,42,.25);
}
/* A refusal wears the same diagnostic dress as the problems — tinted
   panel, hairline red border, ⚠ — not a solid red shout. The colours
   are the advisory/issue hardcodes (see index.html), restated for dark
   below like the chips'; the dark rule must outrank `body.f-dark
   #toast` above, which is what flattened error toasts to grey. */
#toast.err {
  background: var(--danger-soft); color: #991b1b; border: 1px solid #fca5a5;
  display: flex; gap: 8px; align-items: baseline;
}
#toast.err::before { content: '⚠'; }
body.f-dark #toast.err { background: #2d1414; color: #fecaca; border-color: #7f1d1d; }

/* ---------- the scripted-handler editor ---------- */

/* The Monaco host. The frame matches the textareas around it; the
   height is set by the widget, tracking the code's own height. */
.script-editor {
  border: 1px solid var(--border); border-radius: 5px;
  overflow: hidden; background: var(--bg);
}
.script-editor:focus-within { border-color: var(--accent); }

/* ---------- chooser ---------- */

.chooser { max-width: 860px; margin: 0 auto; padding: 34px 20px 60px; }
.chooser .lead { color: var(--muted); margin-bottom: 26px; }
.alt {
  display: block; text-decoration: none; color: inherit;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 16px 18px; margin-bottom: 12px;
}
.alt:hover { border-color: var(--accent); }
.alt h2 { margin-bottom: 4px; }
.alt .answer { font-size: 12px; color: var(--muted); margin-top: 8px; }
.alt .answer b { color: var(--text); font-weight: 600; }
