:root {
  --bg: #1b1d23;
  --panel: #23262e;
  --panel-2: #2b2f39;
  --border: #353a46;
  --text: #e8eaf0;
  --muted: #9aa1b1;
  --accent: #4f8cff;
  --danger: #c25a5a;
  --ok: #4caf76;
  --input-bg: #14161b;
  --stage-bg: #14161b;
  --button-hover: #353a46;
}

body.light {
  --bg: #f4f5f8;
  --panel: #ffffff;
  --panel-2: #f0f2f6;
  --border: #d6dae1;
  --text: #1a1d23;
  --muted: #5b6370;
  --accent: #2563eb;
  --danger: #b91c1c;
  --ok: #047857;
  --input-bg: #ffffff;
  --stage-bg: #e9ebef;
  --button-hover: #e3e6ec;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.45 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}

body { display: flex; flex-direction: column; }

button, select, input, textarea {
  font: inherit;
  color: inherit;
}

button {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 6px 10px;
  border-radius: 5px;
  cursor: pointer;
}
button:hover { background: var(--button-hover); }
/* Preview is the one action people reach for constantly; give it weight so
   the topbar reads as one primary action among secondary ones. */
button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
  font-weight: 500;
}
button.primary:hover {
  background: color-mix(in srgb, var(--accent) 85%, #000);
  border-color: color-mix(in srgb, var(--accent) 85%, #000);
}
/* "Preview" and "from here" read as one control with a dominant half. */
.btn-split { display: inline-flex; }
.btn-split button { border-radius: 0; }
.btn-split button:first-child {
  border-top-left-radius: 6px;
  border-bottom-left-radius: 6px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.btn-split button:last-child {
  border-top-right-radius: 6px;
  border-bottom-right-radius: 6px;
  border-left: 1px solid color-mix(in srgb, #000 22%, var(--accent));
  font-weight: 400;
  opacity: .92;
}
.btn-split button:last-child:hover { opacity: 1; }
button.danger { border-color: #5a3030; color: #ffb3b3; }
button.danger:hover { background: #432626; }
body.light button.danger { border-color: #f4c4c4; color: #b91c1c; }
body.light button.danger:hover { background: #fbe1e1; }

.topbar-utility {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}
#btn-theme-toggle,
#btn-settings {
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  line-height: 1;
}

select, input[type=text], textarea {
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 5px 8px;
}

textarea { resize: vertical; }

/* Top bar */
.topbar {
  position: relative;
  display: flex;
  align-items: center;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  gap: 10px 16px;
  flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand strong { font-size: 15px; letter-spacing: .2px; }
#deck-title { width: 280px; }
.topbar-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.field { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: 12px; }
.field[hidden] { display: none; }
.field.grow { flex: 1; }
.field select, .field input, .field textarea { color: var(--text); }

.status { font-size: 12px; color: var(--muted); min-width: 80px; }
.status.saved { color: var(--ok); }

/* Layout */
.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  flex: 1;
  min-height: 0;
}
body.notes-open .layout {
  grid-template-columns: 240px 1fr 380px;
}

/* Sidebar */
.sidebar {
  border-right: 1px solid var(--border);
  background: var(--panel);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.sidebar-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-weight: 600;
  font-size: 13px;
}
.sidebar-head button {
  padding: 2px 10px;
  font-size: 18px;
  line-height: 1;
}
.slide-list {
  list-style: none;
  padding: 8px;
  margin: 0;
  overflow-y: auto;
  flex: 1;
}
.add-slide {
  width: 26px;
  height: 26px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.add-slide .bi { font-size: 14px; }

.slide-list li {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  margin-bottom: 6px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
}
.slide-list li.active {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.slide-list li.drag-over {
  border-top: 2px solid var(--accent);
}
.slide-list li.drag-over-after {
  border-bottom: 2px solid var(--accent);
}
/* Drop-into cue: the target row is outlined as the parent-to-be, and an
   indented bar under it shows where the new sub-slide will land. */
.slide-list li.drag-over-nest {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.slide-list li.drag-over-nest::after {
  content: "";
  position: absolute;
  left: 18px;
  right: 0;
  bottom: -5px;
  height: 2px;
  border-radius: 1px;
  background: var(--accent);
}
.slide-list li.vertical {
  margin-left: 18px;
}
.slide-list li.vertical::before {
  content: "↳";
  position: absolute;
  left: -14px;
  color: var(--muted);
  font-size: 13px;
}
.slide-list .num {
  color: var(--muted);
  font-size: 11px;
  min-width: 18px;
}
.slide-list .label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
}
.slide-list .del {
  background: transparent;
  border: none;
  color: var(--muted);
  padding: 0 4px;
  font-size: 16px;
}
.slide-list .del:hover { color: var(--danger); background: transparent; }

/* Icon sizing in our buttons. The .bi base class sets font-family; we
   control the visual size per location. */
.toolbar button .bi {
  font-size: 15px;
  vertical-align: middle;
}
.toolbar button:disabled {
  opacity: .4;
  cursor: not-allowed;
}
.toolbar button:disabled:hover { background: var(--panel-2); }
/* Sidebar slide-list label icon */
.slide-list .label .label-icon {
  font-size: 13px;
  margin-right: 4px;
  vertical-align: -2px;
  color: var(--muted);
}

/* Editor background badge icon */
.slide-bg-badge .bi {
  font-size: 12px;
  margin-right: 4px;
  vertical-align: -1px;
}

/* Sync pill icon */
.sync-pill .sync-icon {
  font-size: 13px;
  vertical-align: -2px;
  margin-right: 4px;
}
.sync-pill.is-syncing .sync-icon {
  animation: sync-spin 1.1s linear infinite;
}
@keyframes sync-spin { to { transform: rotate(360deg); } }

/* Topbar utility-cluster icon size matches the 32x32 button box. */
#btn-theme-toggle .bi,
#btn-settings .bi {
  font-size: 15px;
}

/* Context menu (right-click on slides in the sidebar) */
.context-menu {
  position: fixed;
  z-index: 200;
  min-width: 200px;
  padding: 4px 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .35);
  font-size: 13px;
}
.context-menu button {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 6px 14px;
  cursor: pointer;
  color: var(--text);
}
.context-menu button:hover { background: var(--button-hover); }
.context-menu button:disabled {
  color: var(--muted);
  cursor: default;
}
.context-menu button:disabled:hover { background: transparent; }
.context-menu button.danger { color: #d97777; }
body.light .context-menu button.danger { color: #b91c1c; }
.context-menu .sep {
  display: block;
  height: 1px;
  background: var(--border);
  margin: 4px 0;
}

.sidebar-foot {
  border-top: 1px solid var(--border);
  padding: 8px 12px;
  display: flex;
}
.sidebar-foot button {
  background: transparent;
  border: none;
  color: var(--muted);
  padding: 4px 6px;
  font-size: 12px;
}
.sidebar-foot button:hover { color: var(--text); background: var(--button-hover); }

/* Editor */
.editor {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
}
/* Clusters wrap as a unit, so a group never splits across two rows the way
   loose buttons separated by <span class="sep"> used to. */
.tb-group {
  display: flex;
  align-items: center;
  gap: 4px;
}
.tb-group + .tb-group {
  border-left: 1px solid var(--border);
  padding-left: 8px;
}
.toolbar button {
  padding: 4px 8px;
  font-size: 12px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
/* Reveal.js-specific tools carry a word as well as a glyph — "fit text",
   "stretch" and "fragment" have no icon convention to lean on. */
.tb-label {
  font-size: 11px;
  letter-spacing: .2px;
}
/* Pressed-in state (e.g. source view toggled on) — theme-aware, unlike a
   hardcoded background color. */
.toolbar button.active,
.toolbar button[aria-pressed="true"] {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 18%, var(--input-bg));
  color: var(--text);
}
/* Split control: the main action and its caret share one outline. */
.tb-split { gap: 0; }
.tb-split button:first-child {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.tb-split button:last-child {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 0;
  padding-left: 5px;
  padding-right: 5px;
}
.tb-caret .bi { font-size: 10px; }
/* Caret goes accent-coloured when the caret sits inside a fragment, so the
   menu shows that fragment's animation rather than the default. */
.tb-caret.bound {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--input-bg));
}
.tb-style { min-width: 108px; }

/* Toolbar popover menus (More, fragment animation) */
.context-menu .menu-tick {
  display: inline-block;
  width: 14px;
  margin-right: 6px;
  font-size: 11px;
  vertical-align: -1px;
  color: var(--accent);
}
.context-menu .menu-hint {
  margin-left: 8px;
  color: var(--muted);
  font-size: 11px;
}
#toolbar-menu button { white-space: nowrap; }

.toolbar-select {
  padding: 0 6px;
  font-size: 12px;
  max-width: 140px;
  height: 26px;
  box-sizing: border-box;
  transition: border-color .15s, background .15s;
}
.toolbar button {
  height: 26px;
  box-sizing: border-box;
}

.stage {
  flex: 1;
  overflow: auto;
  padding: 20px;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  background: var(--stage-bg);
  min-height: 0;
}

.slide-frame {
  width: 100%;
  max-width: 960px;
  aspect-ratio: 16 / 9;
  background: var(--r-background-color, #000);
  color: var(--r-main-color, #fff);
  font-family: var(--r-main-font, system-ui, sans-serif);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(0,0,0,.4);
  position: relative;
  overflow: hidden;
}
.slide-editor, .slide-source {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  padding: 40px 60px;
  outline: none;
  overflow: auto;
  /* The editor renders denser than the real deck: .55 of the theme's base
     size lands close to the 22px this stage was historically hardcoded to
     (themes declare 40-42px), and scales live when a theme — custom or
     built-in — changes --r-main-font-size. */
  font-size: calc(var(--r-main-font-size, 40px) * .55);
  line-height: 1.4;
}

.slide-bg-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.slide-bg-layer iframe,
.slide-bg-layer video {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  background: #000;
}
/* Match reveal.js, which covers the slide with background video unless the
   slide sets data-background-size="contain". The browser default for <video>
   is contain, so cover has to be stated explicitly. */
.slide-bg-layer video { object-fit: cover; }
.slide-bg-layer[data-bg-size="contain"] video { object-fit: contain; }

.slide-bg-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 6;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: calc(100% - 16px);
  padding: 3px 8px;
  background: rgba(0, 0, 0, .72);
  color: #fff;
  font: 11px/1.3 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  border-radius: 4px;
  pointer-events: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.slide-editor h1, .slide-editor h2, .slide-editor h3,
.slide-editor h4, .slide-editor h5, .slide-editor h6 {
  margin: 0 0 .4em;
  font-family: var(--r-heading-font, inherit);
  color: var(--r-heading-color, inherit);
  text-transform: var(--r-heading-text-transform, none);
  font-weight: var(--r-heading-font-weight, 600);
  letter-spacing: var(--r-heading-letter-spacing, normal);
  line-height: var(--r-heading-line-height, 1.1);
  text-shadow: var(--r-heading-text-shadow, none);
}
.slide-editor h1 { font-size: var(--r-heading1-size, 2.4em); }
.slide-editor h2 { font-size: var(--r-heading2-size, 1.9em); }
.slide-editor h3 { font-size: var(--r-heading3-size, 1.5em); }
.slide-editor h4 { font-size: var(--r-heading4-size, 1em); }
.slide-editor p { margin: 0 0 var(--r-block-margin, .6em); }
.slide-editor a { color: var(--r-link-color, inherit); }
.slide-editor pre {
  background: rgba(0,0,0,.55);
  padding: 12px 16px;
  border-radius: 6px;
  font-size: .8em;
  overflow: auto;
}
.slide-editor code, .slide-editor pre code {
  font-family: var(--r-code-font, ui-monospace, Menlo, Consolas, monospace);
}
.slide-editor img { max-width: 100%; max-height: 70vh; }

/* Editor-only marker for click-to-open linked images. Doesn't follow into the
   exported deck or preview — there the link itself is the affordance. */
.slide-editor a.linked-image {
  position: relative;
  display: inline-block;
}
.slide-editor a.linked-image::after {
  content: "↗ " attr(href);
  position: absolute;
  top: 6px;
  right: 6px;
  max-width: calc(100% - 12px);
  background: rgba(0, 0, 0, .65);
  color: #fff;
  font: 11px/1.3 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  padding: 2px 6px;
  border-radius: 3px;
  pointer-events: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.slide-editor blockquote {
  border-left: 3px solid currentColor;
  padding-left: 12px;
  margin: 0 0 var(--r-block-margin, .6em);
  opacity: .85;
}
.slide-editor .fragment {
  outline: 1px dashed rgba(79, 140, 255, .55);
  outline-offset: 2px;
  position: relative;
}
.slide-editor .fragment::after {
  content: attr(data-fragment-label);
  position: absolute;
  top: -8px;
  right: -4px;
  background: var(--accent);
  color: #fff;
  font-size: 10px;
  line-height: 1;
  padding: 2px 4px;
  border-radius: 3px;
  pointer-events: none;
  font-family: system-ui, sans-serif;
  font-weight: 600;
  opacity: .85;
}
.slide-editor .r-fit-text {
  display: block;
  line-height: 1;
  white-space: nowrap;
  margin: 0;
  outline: 1px dotted rgba(76, 175, 118, .5);
  outline-offset: 4px;
}
.slide-editor .r-stretch {
  display: block;
  width: 100%;
  object-fit: contain;
  outline: 1px dotted rgba(76, 175, 118, .5);
  outline-offset: 4px;
}
.slide-source {
  background: #0c0d10;
  color: #d8dee9;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 13px;
  border: none;
}
.drop-overlay {
  position: absolute;
  inset: 0;
  background: rgba(79, 140, 255, .18);
  border: 3px dashed var(--accent);
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
  pointer-events: none;
  z-index: 10;
}

.meta {
  display: flex;
  gap: 16px;
  padding: 10px 14px;
  border-top: 1px solid var(--border);
  background: var(--panel);
  align-items: flex-start;
  flex-wrap: wrap;
}
.meta .field { align-items: flex-start; flex-direction: column; }
.meta .checkbox-inline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text);
  font-size: 13px;
  /* Line up with the sibling fields' inputs, which sit under a 12px label. */
  min-height: 29px;
}
.meta .checkbox-inline input { margin: 0; }
.meta input[type=text], .meta select { min-width: 180px; }
.meta input[type=text]:disabled { opacity: .5; cursor: not-allowed; }
.meta textarea { width: 100%; min-width: 280px; }
.meta .notes-field { display: flex; flex-direction: column; gap: 4px; }
.meta .notes-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  color: var(--muted);
  font-size: 12px;
}
.meta .notes-head button {
  padding: 2px 8px;
  font-size: 11px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

/* Deck markdown mode — replaces the toolbar, stage and meta strip. */
body.markdown-mode .toolbar,
body.markdown-mode .stage,
body.markdown-mode .meta { display: none; }

.markdown-pane {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.markdown-pane[hidden] { display: none; }
.markdown-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  flex-wrap: wrap;
}
.markdown-about { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.markdown-hint { color: var(--muted); font-size: 12px; }
.markdown-hint code {
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 0 4px;
  font-size: 11px;
}
.markdown-actions { display: flex; align-items: center; gap: 8px; }
.markdown-status { font-size: 12px; color: var(--muted); min-width: 120px; text-align: right; }
.markdown-status.bad { color: var(--danger, #d97777); }
#markdown-text {
  flex: 1;
  min-height: 0;
  width: 100%;
  resize: none;
  border: 0;
  border-radius: 0;
  padding: 16px 18px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.6;
  tab-size: 2;
}
#markdown-text:focus { outline: none; }

/* Topbar buttons that pair an icon with a word. */
button.has-icon {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
button.has-icon.active {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 18%, var(--input-bg));
}

/* Modal */
.modal {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.75);
  z-index: 100;
  display: flex;
  flex-direction: column;
}
.modal-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 14px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
}
#preview-frame {
  flex: 1;
  width: 100%;
  border: none;
  background: #000;
}

.notes-panel {
  border-left: 1px solid var(--border);
  background: var(--panel);
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}
.notes-panel-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}
.notes-panel-head button {
  padding: 0 8px;
  font-size: 18px;
  line-height: 1;
}
#notes-panel-text {
  flex: 1;
  width: 100%;
  border: none;
  border-radius: 0;
  resize: none;
  padding: 16px;
  font: 14px/1.55 ui-monospace, Menlo, Consolas, monospace;
  background: var(--panel);
  color: var(--text);
  outline: none;
}

/* Projects modal */
.projects-modal { background: rgba(0,0,0,.55); }
.projects-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 16px 20px;
  background: var(--panel);
  color: var(--text);
  min-height: 0;
}
.projects-toolbar {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
  align-items: center;
  flex-wrap: wrap;
}
.projects-toolbar .projects-import {
  display: inline-flex;
  align-items: center;
  background: var(--panel-2);
  border: 1px solid var(--border);
  padding: 6px 10px;
  border-radius: 5px;
  cursor: pointer;
  font-size: 14px;
}
.projects-toolbar .projects-import:hover { background: var(--button-hover); }
.projects-list {
  list-style: none;
  padding: 0;
  margin: 0;
  overflow-y: auto;
  flex: 1;
}
.projects-list li {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 4px 12px;
  padding: 10px 12px;
  margin-bottom: 6px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  align-items: center;
}
.projects-list li.active {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent) inset;
}
.projects-list .proj-name {
  font-weight: 600;
  font-size: 14px;
  background: transparent;
  border: 1px solid transparent;
  color: inherit;
  padding: 2px 6px;
  margin: -2px -6px;
  border-radius: 4px;
  min-width: 0;
  width: 100%;
}
.projects-list .proj-name:hover { border-color: var(--border); background: var(--input-bg); }
.projects-list .proj-name:focus { border-color: var(--accent); background: var(--input-bg); outline: none; }
.projects-list .proj-meta {
  grid-column: 1;
  font-size: 11px;
  color: var(--muted);
}
.projects-list .proj-actions {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: flex;
  gap: 4px;
  align-items: center;
}
.projects-list .proj-actions button {
  padding: 4px 8px;
  font-size: 12px;
}
.projects-list .proj-actions button.danger { color: #d97777; border-color: transparent; }
.projects-list .proj-actions button.danger:hover { background: #5a2828; }
body.light .projects-list .proj-actions button.danger { color: #b91c1c; }
body.light .projects-list .proj-actions button.danger:hover { background: #fbe1e1; }
.projects-remote {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.projects-remote-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}
.projects-remote-head strong {
  font-size: 13px;
  letter-spacing: .3px;
  text-transform: uppercase;
  color: var(--muted);
}
.projects-remote-head button {
  padding: 3px 8px;
  font-size: 11px;
}
.projects-remote-status {
  font-size: 12px;
  color: var(--muted);
  margin: 0 0 8px;
}
.projects-list .proj-tag {
  display: inline-block;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 600;
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: .4px;
  margin-left: 6px;
}
.projects-list .proj-tag.match { background: rgba(76, 175, 118, .18); color: #6fcc91; border: 1px solid color-mix(in srgb, var(--ok) 50%, transparent); }
.projects-list .proj-tag.orphan { background: rgba(194, 90, 90, .18); color: #ffb3b3; border: 1px solid color-mix(in srgb, var(--danger) 50%, transparent); }
.projects-list .proj-tag.duplicate { background: rgba(255, 180, 60, .18); color: #ffc36f; border: 1px solid #c89a3a; }
body.light .projects-list .proj-tag.match { color: #047857; }
body.light .projects-list .proj-tag.orphan { color: #b91c1c; }
body.light .projects-list .proj-tag.duplicate { color: #b27418; }
.projects-list .proj-id {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 10px;
  color: var(--muted);
}

.projects-empty {
  padding: 20px;
  text-align: center;
  color: var(--muted);
  font-style: italic;
}

/* Sync pill in topbar */
.sync-pill {
  font-size: 11px;
  padding: 4px 8px;
  border-radius: 999px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--muted);
  white-space: nowrap;
}
.sync-pill:hover { background: var(--button-hover); color: var(--text); }
.sync-pill.is-syncing { color: var(--accent); border-color: var(--accent); }
.sync-pill.is-error { color: var(--danger); border-color: var(--danger); }
.sync-pill.is-ok { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 50%, var(--border)); }

/* Sync modal */
.sync-modal {
  background: rgba(0, 0, 0, .55);
  align-items: center;
  justify-content: center;
}
.sync-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  width: min(540px, calc(100% - 32px));
  max-height: calc(100% - 32px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .45);
  overflow: hidden;
}
.sync-card .modal-head { border-radius: 8px 8px 0 0; }
.sync-body {
  padding: 16px 20px;
  overflow-y: auto;
  flex: 1;
  font-size: 13px;
  line-height: 1.55;
}
.sync-body p { margin: 0 0 10px; }
.sync-body a { color: var(--accent); }
.sync-steps { padding-left: 20px; margin: 8px 0 14px; }
.sync-steps li { margin-bottom: 6px; }
.sync-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 10px;
}
.sync-field span { color: var(--muted); font-size: 12px; }
.sync-field em { font-style: normal; color: var(--muted); }
.sync-field input { font-family: ui-monospace, Menlo, Consolas, monospace; }
.sync-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}
.sync-status-line { margin: 4px 0; }
.sync-status-line code {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px;
  background: var(--input-bg);
  border: 1px solid var(--border);
  padding: 1px 6px;
  border-radius: 4px;
}
.sync-mini-btn {
  font-size: 11px;
  padding: 2px 8px;
}
.sync-error {
  color: #ffb3b3;
  background: rgba(194, 90, 90, .15);
  border: 1px solid color-mix(in srgb, var(--danger) 40%, transparent);
  padding: 8px 12px;
  border-radius: 5px;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: 12px;
  white-space: pre-wrap;
  word-break: break-word;
}
body.light .sync-error { color: #b91c1c; }

/* Settings modal */
.settings-modal {
  background: rgba(0, 0, 0, .55);
  align-items: center;
  justify-content: center;
}
.settings-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  width: min(620px, calc(100% - 32px));
  max-height: calc(100% - 32px);
  display: flex;
  flex-direction: column;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .45);
  overflow: hidden;
}
.settings-card .modal-head { border-radius: 8px 8px 0 0; }
.settings-body {
  padding: 16px 20px;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}
.settings-intro {
  margin: 0 0 12px;
  font-size: 12px;
  color: var(--muted);
}
.settings-intro a { color: var(--accent); }
.settings-form { display: flex; flex-direction: column; gap: 4px; }
.settings-form h4 {
  margin: 12px 0 6px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  padding-bottom: 4px;
}
.settings-form h4:first-child { margin-top: 0; }
.settings-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 5px 0;
}
.settings-row label {
  font-size: 13px;
  color: var(--text);
}
.settings-row .help {
  display: block;
  font-size: 11px;
  color: var(--muted);
  margin-top: 1px;
}
.settings-row input[type=number],
.settings-row select {
  min-width: 140px;
}
.settings-row input[type=checkbox] { justify-self: end; }
.settings-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* Custom theme drawer — anchored right with a click-through backdrop so the
   slide stays visible (and editable) while theme changes apply live. */
.theme-modal {
  background: transparent;
  pointer-events: none;
  align-items: stretch;
  justify-content: flex-end;
  flex-direction: row;
}
.theme-card {
  pointer-events: auto;
  background: var(--panel);
  border-left: 1px solid var(--border);
  width: min(400px, calc(100% - 32px));
  display: flex;
  flex-direction: column;
  box-shadow: -12px 0 40px rgba(0, 0, 0, .35);
  overflow: hidden;
}
.color-pair { display: inline-flex; align-items: center; gap: 6px; }
.color-pair input[type=color] {
  width: 28px;
  height: 24px;
  padding: 1px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--input-bg);
  cursor: pointer;
}
.color-pair input[type=text] { width: 150px; min-width: 0; }
#btn-theme-edit { padding: 2px 7px; }

/* About modal */
.about-modal {
  background: rgba(0,0,0,.55);
  align-items: center;
  justify-content: center;
}
.about-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  width: min(440px, calc(100% - 32px));
  max-height: calc(100% - 32px);
  overflow: auto;
  display: flex;
  flex-direction: column;
  box-shadow: 0 12px 40px rgba(0,0,0,.45);
}
.about-card .modal-head { border-radius: 8px 8px 0 0; }
.about-body {
  padding: 16px 18px;
  font-size: 13px;
  line-height: 1.55;
}
.about-body p { margin: 0 0 10px; }
.about-body p:last-child { margin-bottom: 0; }
.about-body a { color: var(--accent); }
.about-version { color: var(--muted); font-size: 12px; }

/* ==========================================================================
   Small screens
   ==========================================================================
   The desktop shell shows everything at once: a slide list, a full topbar, a
   formatting toolbar and a per-slide meta strip. On a phone that leaves the
   slide — the thing being edited — as a letterbox in the middle. So on small
   screens the slide takes the stage and the surrounding chrome collapses into
   three places you pull open: the slide list becomes a left drawer, the meta
   strip becomes a bottom sheet, and the rest of the topbar becomes one "⋯"
   menu. Nothing is duplicated — the mobile controls drive the same elements
   and handlers the desktop chrome does.

   These four are mobile-only, so they start hidden and the media query below
   turns them on. */
#btn-mobile-menu,
.sidebar-close,
.mobile-slidebar,
.mobile-scrim,
.meta-sheet-head,
.slide-list .row-menu { display: none; }

/* Phones, plus phone-sized landscape windows where the vertical space is the
   binding constraint. Kept in sync with MOBILE_QUERY in app.js. */
@media (max-width: 760px), (max-height: 500px) and (max-width: 950px) {
  /* --viewport-height is the visual viewport, set by syncViewportHeight() in
     app.js: when the on-screen keyboard opens it is the only measure that
     shrinks, so without it the bottom strip and the toolbar are pushed under
     the keyboard exactly when they're wanted. It falls back to 100dvh, which
     tracks the browser's collapsing URL bar the way svh/lvh do not. */
  html, body { height: var(--viewport-height, 100dvh); }

  /* iOS Safari zooms the whole page when a control smaller than 16px takes
     focus, which leaves the editor scrolled sideways. */
  select,
  input[type=text],
  input[type=password],
  input[type=number],
  textarea { font-size: 16px; }

  /* ---- Topbar: deck title, Preview, and the overflow menu ---- */
  .topbar {
    padding: 6px 8px;
    padding-top: max(6px, env(safe-area-inset-top));
    gap: 6px;
    flex-wrap: nowrap;
  }
  .brand { flex: 1; min-width: 0; gap: 0; }
  /* The app's own name is the one thing on this bar that never does anything;
     it lives in the About item of the ⋯ menu. */
  .brand strong { display: none; }
  #deck-title {
    width: 100%;
    min-width: 0;
    background: transparent;
    border-color: transparent;
    font-weight: 600;
    padding: 7px 4px;
  }
  #deck-title:focus {
    background: var(--input-bg);
    border-color: var(--border);
  }

  .topbar-actions { flex: 0 0 auto; flex-wrap: nowrap; gap: 6px; }
  /* Theme picker, Markdown, Projects, + New and the save status all reappear
     inside the ⋯ menu. */
  .topbar-actions > .field,
  .topbar-actions > #btn-markdown,
  .topbar-actions > #btn-projects,
  .topbar-actions > #btn-new,
  .topbar-actions > .status { display: none; }
  /* "from here" is a menu item on mobile; Preview keeps only its glyph. */
  #btn-preview-here { display: none; }
  .btn-split button:first-child { border-radius: 6px; }
  #btn-preview {
    width: 40px;
    height: 38px;
    padding: 0;
    justify-content: center;
    gap: 0;
  }
  #btn-preview .bi { font-size: 20px; }
  /* The label is a text node, not an element, so it can't be hidden directly;
     collapse it and let the icon's own font-size stand. */
  #btn-preview { font-size: 0; }

  .topbar-utility { gap: 4px; flex: 0 0 auto; }
  #btn-sync,
  #btn-settings,
  #btn-theme-toggle { display: none; }
  #btn-mobile-menu {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0;
  }
  #btn-mobile-menu .bi { font-size: 18px; }

  /* ---- Slide list as a left drawer ----
     The drawer, the sheet, the notes panel and the backdrop are all anchored
     to .layout rather than the viewport. Fixed positioning is measured
     against the layout viewport, which the on-screen keyboard does not
     shrink — a bottom-anchored sheet would sit behind the keyboard. .layout
     does shrink, because --viewport-height shortens the shell. */
  .layout,
  body.notes-open .layout { grid-template-columns: 1fr; }
  .layout { position: relative; }

  .sidebar {
    position: absolute;
    z-index: 90;
    top: 0;
    bottom: 0;
    left: 0;
    width: min(320px, 86vw);
    padding-bottom: env(safe-area-inset-bottom);
    transform: translateX(-100%);
    transition: transform .22s ease;
    box-shadow: 10px 0 34px rgba(0, 0, 0, .45);
  }
  body.slides-open .sidebar { transform: none; }

  .sidebar-head { gap: 8px; padding: 12px; }
  .sidebar-head > span { flex: 1; font-size: 15px; }
  .sidebar-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
  }
  .add-slide { width: 34px; height: 34px; }
  .add-slide .bi { font-size: 16px; }

  .slide-list { padding: 8px 10px; }
  .slide-list li { padding: 11px 10px; }
  .slide-list .label { font-size: 14px; }
  /* A 16px "×" next to a full-width tap target is an accidental deletion
     waiting to happen; Delete lives in the row menu instead, and reordering
     lives there too since dragging rows is a mouse gesture. */
  .slide-list .del { display: none; }
  .slide-list .row-menu {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin: -6px -4px;
    padding: 0;
    background: transparent;
    border: none;
    color: var(--muted);
    font-size: 16px;
  }
  .sidebar-foot { padding: 10px 12px; }
  .sidebar-foot button { font-size: 14px; padding: 6px 8px; }

  .mobile-scrim {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 80;
    background: rgba(0, 0, 0, .5);
  }

  /* ---- Toolbar: one scrolling row of icon-only buttons ----
     It stays above the stage rather than under it: pinned to the bottom it
     would sit behind the on-screen keyboard exactly when it's wanted. */
  .toolbar {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    gap: 0 6px;
    padding: 6px 8px;
  }
  .toolbar::-webkit-scrollbar { display: none; }
  .tb-group { flex: 0 0 auto; }
  .toolbar button {
    height: 38px;
    min-width: 38px;
    padding: 0 7px;
    justify-content: center;
  }
  .toolbar button .bi { font-size: 17px; }
  .tb-label { display: none; }
  .toolbar-select,
  .tb-style {
    height: 38px;
    min-width: 96px;
    max-width: 116px;
  }
  .tb-split button:last-child { min-width: 26px; padding: 0 4px; }
  /* Whole groups, not single buttons — hiding a lone button would leave the
     group's divider behind as a stray sliver. Both reappear in "More". */
  .tb-group[aria-label="Reveal.js sizing"],
  .tb-group[aria-label="View"] { display: none; }

  /* ---- Stage: the slide, as large as the width allows ---- */
  .stage { padding: 10px; }
  .slide-frame {
    /* Auto margins rather than align-items: center. Both centre the frame in
       the spare height, but a centred flex item in a scroll container has its
       overflowing top cut off and unreachable — which is what happens the
       moment the keyboard shortens the stage. */
    margin-block: auto;
    border-radius: 6px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, .4);
  }
  /* Desktop pads the editor 40/60px inside a 960px frame. As percentages of
     the frame width those are the same insets at any size — and percentage
     padding resolves against width on both axes, which is what keeps a
     third-size frame from looking like it has margins for a poster. */
  .slide-editor,
  .slide-source { padding: 4.2% 6.25%; }
  /* --stage-scale is the frame's width over the 960px it was designed at,
     set by updateStageScale() in app.js. Without it the type keeps its
     desktop size in a frame a third as wide, and a single heading fills the
     slide.

     Note this drops the .55 the desktop stage applies. That factor makes the
     desktop editor deliberately denser than the deck so more text is visible
     while writing; at a third the width it would land near 9px, which is
     neither readable nor honest. Scaling the theme's real base size by the
     frame instead puts a phone at roughly 16px — legible, and a true preview
     of what will overflow. The floor covers the narrowest handsets. */
  .slide-editor {
    font-size: max(13px, calc(var(--r-main-font-size, 40px) * var(--stage-scale, 1)));
  }
  /* Raw HTML is chrome, not slide content, so it doesn't scale with the
     frame — and 16px keeps iOS from zooming when it takes focus. */
  .slide-source { font-size: 16px; }
  /* 70vh is taller than the whole frame here. */
  .slide-editor img { max-height: 100%; }

  /* ---- Slide navigation strip ---- */
  .mobile-slidebar {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    padding: 6px 8px;
    padding-bottom: max(6px, env(safe-area-inset-bottom));
    border-top: 1px solid var(--border);
    background: var(--panel);
  }
  .mobile-slidebar button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-width: 40px;
    height: 40px;
    padding: 0 10px;
  }
  .mobile-slidebar button .bi { font-size: 17px; }
  .mobile-slidebar button:disabled { opacity: .35; }
  .mobile-slidebar button:disabled:hover { background: var(--panel-2); }
  .mobile-slidebar .m-picker {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
  }

  /* ---- Meta strip as a bottom sheet ---- */
  .meta {
    position: absolute;
    z-index: 95;
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 100%;
    overflow-y: auto;
    flex-direction: column;
    align-items: stretch;
    flex-wrap: nowrap;
    gap: 12px;
    padding: 0 14px;
    padding-bottom: max(16px, calc(env(safe-area-inset-bottom) + 16px));
    border-radius: 14px 14px 0 0;
    box-shadow: 0 -8px 34px rgba(0, 0, 0, .45);
    transform: translateY(101%);
    /* Parked below .layout rather than below the viewport, so it would show
       through in the gap whenever the shell is shorter than the window — the
       keyboard normally fills that gap, but nothing guarantees it. Hidden
       until the transform has finished running it out of the way. */
    visibility: hidden;
    transition: transform .24s ease, visibility 0s linear .24s;
  }
  body.meta-open .meta {
    transform: none;
    visibility: visible;
    transition: transform .24s ease, visibility 0s;
  }
  .meta-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: sticky;
    top: 0;
    z-index: 1;
    margin: 0 -14px;
    padding: 12px 14px 10px;
    background: var(--panel);
    border-bottom: 1px solid var(--border);
  }
  .meta .field { width: 100%; }
  .meta select,
  .meta input[type=text],
  .meta textarea {
    width: 100%;
    min-width: 0;
  }
  .meta textarea { min-height: 88px; }
  .meta .notes-head button { padding: 5px 10px; font-size: 13px; }

  /* ---- Overlays that assumed a desktop-sized window ---- */
  .notes-panel {
    position: absolute;
    inset: 0;
    z-index: 96;
    border-left: none;
  }
  .context-menu {
    max-width: calc(100vw - 16px);
    max-height: 70dvh;
    overflow-y: auto;
    font-size: 15px;
  }
  .context-menu button { padding: 10px 14px; }
  .theme-card {
    width: 100%;
    border-left: none;
  }
  .settings-card,
  .sync-card,
  .about-card {
    width: calc(100% - 16px);
    max-height: calc(100% - 16px);
  }
  .settings-row {
    grid-template-columns: 1fr;
    align-items: start;
    gap: 4px;
  }
  .settings-row input[type=checkbox] { justify-self: start; }
  .settings-row input[type=number],
  .settings-row select { width: 100%; }
  .color-pair { width: 100%; }
  .color-pair input[type=text] { width: 100%; min-width: 0; }
  .projects-body { padding: 12px; }
  /* Name, dates and four actions don't fit on one line; let the actions take
     a row of their own under the name. */
  .projects-list li { grid-template-columns: 1fr; }
  .projects-list .proj-actions {
    grid-column: 1;
    grid-row: auto;
    flex-wrap: wrap;
  }
  .projects-list .proj-actions button { padding: 7px 10px; }
  /* Markdown mode already replaces the toolbar, stage and meta strip; the
     slide strip navigates something that isn't on screen. */
  body.markdown-mode .mobile-slidebar { display: none; }

  .markdown-head { gap: 8px; padding: 8px 10px; }
  .markdown-hint { display: none; }
  .markdown-status { min-width: 0; }
  #markdown-text { font-size: 16px; padding: 12px; }
}

/* A short landscape window (a phone on its side) is height-bound, not
   width-bound: a frame sized off the width would be taller than the stage and
   have to be scrolled. Size it off the height instead so the whole slide is
   on screen. */
@media (max-height: 500px) and (max-width: 950px) and (orientation: landscape) {
  .slide-frame {
    width: auto;
    height: 100%;
    max-width: 100%;
  }
}

/* Demo canvas: preserve the presentation coordinate system while editing. */
.slide-editor:has(.lab){width:1280px;height:720px;padding:35px 0;transform:scale(var(--lab-scale,.75));transform-origin:top left;overflow:hidden;box-sizing:border-box}.slide-frame:has(.lab){max-width:960px}

.slide-editor:has(.qslide){width:1280px;height:720px;padding:35px 0;transform:scale(var(--lab-scale,.75));transform-origin:top left;overflow:hidden;box-sizing:border-box;background:#081322}.slide-frame:has(.qslide){max-width:960px}
