/* Campaign journal — list + workspace styles.
   Reuses tokens from tokens.css and button/card primitives from site.css. */

/* --- List page --------------------------------------------------------- */
.campaigns-toolbar {
  display: flex;
  gap: var(--space-3);
  margin-block: var(--space-4);
  flex-wrap: wrap;
}

.builder-button.secondary {
  background-color: var(--color-surface-muted);
  color: var(--color-text);
  box-shadow: none;
  border: 1px solid var(--color-border);
}
.builder-button.secondary:hover:not(:disabled) {
  background-color: var(--color-surface-raised);
  box-shadow: var(--shadow-sm);
}
.builder-button.small {
  padding: 0.45rem 0.75rem;
  min-height: 2rem;
  font-size: var(--font-size-sm);
}

.campaigns-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-4);
}

.campaign-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  background-color: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--motion-fast) var(--motion-ease),
              box-shadow var(--motion-fast) var(--motion-ease);
}
.campaign-card:hover {
  border-color: var(--color-accent-border);
  box-shadow: var(--shadow-md);
}

.campaign-card__header { display: flex; flex-direction: column; gap: var(--space-1); }
.campaign-card__title { margin: 0; font-size: var(--font-size-lg); letter-spacing: var(--letter-spacing-tight); }
.campaign-card__meta { color: var(--color-text-muted); font-size: var(--font-size-sm); }
.campaign-card__description {
  margin: 0;
  color: var(--color-text-muted);
  max-inline-size: var(--measure-prose);
}
.campaign-card__actions {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
  margin-top: auto;
}
.topbar-link.danger { color: var(--color-danger); }

/* --- Inline form (modal) ---------------------------------------------- */
.campaign-form { display: flex; flex-direction: column; gap: var(--space-3); margin-top: var(--space-3); }
.campaign-form__field { display: flex; flex-direction: column; gap: var(--space-1); font-weight: 500; }
.campaign-form__field input,
.campaign-form__field textarea {
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--color-border);
  background-color: var(--color-surface);
  color: var(--color-text);
  border-radius: var(--radius-sm);
  font: inherit;
}
.campaign-form__field input:focus,
.campaign-form__field textarea:focus {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 1px;
}

/* --- Workspace (two-pane + backlinks) --------------------------------- */
.campaign-workspace {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.campaign-workspace__header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.campaign-workspace__status { min-height: 1.75rem; }
.save-indicator {
  display: inline-block;
  padding: 0.25rem 0.6rem;
  border-radius: var(--radius-pill);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  background-color: var(--color-surface-muted);
}
.save-indicator[data-state="saving"] { color: var(--color-info-soft-fg); background-color: var(--color-info-soft); }
.save-indicator[data-state="saved"] { color: var(--color-accent-soft-fg); background-color: var(--color-accent-soft); }
.save-indicator[data-state="error"] { color: var(--color-danger-soft-fg); background-color: var(--color-danger-soft); }

.campaign-workspace__grid {
  display: grid;
  /* Phase 2b: the tree and backlinks widths are CSS custom properties so the
     splitpane JS can override them at runtime. Defaults use clamp() so the
     layout stays sensible even when campaign-splitpane.js fails to load. The
     zero-width splitter tracks sit between panes and receive an ~4px visual
     width via their own box-shadow/background — keeps the grid template
     predictable (5 tracks, 3 content + 2 handles). */
  grid-template-columns:
    var(--tree-width, clamp(15rem, 22vw, 24rem))
    auto
    minmax(0, 1fr)
    auto
    var(--backlinks-width, clamp(14rem, 18vw, 22rem));
  gap: var(--space-4);
  align-items: start;
}

/* Folder tree ----------------------------------------------------------- */
.workspace-tree,
.workspace-backlinks {
  background-color: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  min-height: 400px;
}
.workspace-tree__header {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}
.workspace-tree__header h2,
.workspace-backlinks h2 {
  margin: 0;
  font-size: var(--font-size-base);
  letter-spacing: var(--letter-spacing-tight);
}
.workspace-tree__actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

.note-tree { display: flex; flex-direction: column; gap: var(--space-2); }
.note-tree__folder > summary {
  cursor: pointer;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: var(--font-size-sm);
}
.note-tree__folder[open] > summary { color: var(--color-text); }
.note-tree__list { list-style: none; margin: 0; padding-inline-start: var(--space-4); }
.note-tree__link {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.3rem var(--space-2);
  background: transparent;
  border: none;
  border-radius: var(--radius-xs);
  color: var(--color-text);
  cursor: pointer;
  font: inherit;
}
.note-tree__link:hover { background-color: var(--color-surface-muted); }
.note-tree__link.active {
  background-color: var(--color-accent-soft);
  color: var(--color-accent-soft-fg);
  font-weight: 600;
}

/* Editor pane ----------------------------------------------------------- */
.workspace-editor {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  min-height: 480px;
}
.workspace-editor__toolbar {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
}
.note-title-input {
  flex: 1 1 240px;
  font-size: var(--font-size-lg);
  font-weight: 600;
  padding: 0.4rem 0.6rem;
  border: 1px solid transparent;
  background-color: transparent;
  color: var(--color-text);
  border-radius: var(--radius-sm);
}
.note-title-input:focus {
  border-color: var(--color-border);
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 1px;
}
.workspace-editor__tabs { display: flex; gap: var(--space-1); }
.editor-tab {
  padding: 0.35rem 0.7rem;
  background-color: var(--color-surface-muted);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--font-size-sm);
}
.editor-tab.active {
  background-color: var(--color-accent-soft);
  color: var(--color-accent-soft-fg);
  border-color: var(--color-accent-border);
}
[role="tab"]:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

.workspace-editor__body { display: block; min-height: 400px; }
.workspace-editor__body[data-mode="split"] {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}
.workspace-editor__pane { display: block; min-width: 0; }
.workspace-editor__pane[hidden] { display: none; }
.note-preview {
  padding: var(--space-3);
  background-color: var(--color-surface-sunken);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-md);
  max-inline-size: none;
  overflow: auto;
}
.note-preview [data-unresolved="true"] {
  color: var(--color-danger);
  border-bottom: 1px dotted var(--color-danger);
  cursor: help;
}

/* EasyMDE scoping — keep the editor inside the grid card. */
.EasyMDEContainer .CodeMirror { border-radius: var(--radius-md); }

/* Backlinks pane -------------------------------------------------------- */
.backlinks-list__items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.backlink-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.3rem var(--space-2);
  background: transparent;
  border: none;
  border-radius: var(--radius-xs);
  color: var(--color-text);
  cursor: pointer;
}
.backlink-item:hover { background-color: var(--color-surface-muted); }

/* Small screens — legacy breakpoint replaced by Phase 2b rules below.
   Kept as an empty stub so `diff` hunks stay minimal if the Phase 2b
   block is ever reverted; the 2b media query at 767.98px supersedes this. */

/* --- ARIA tree (role="tree") ----------------------------------------- */
/* Screen-reader only helper (no other rule in the app provides this yet). */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.note-tree--aria {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.note-tree__treeitem {
  display: block;
  min-height: 32px;
  padding: 0.35rem var(--space-2);
  border-radius: var(--radius-xs);
  color: var(--color-text);
  cursor: pointer;
  user-select: none;
  line-height: 1.3;
}
.note-tree__treeitem:hover {
  background-color: var(--color-surface-muted);
}
.note-tree__treeitem:focus { outline: none; }
.note-tree__treeitem:focus-visible {
  /* 3:1 minimum ring on focusable tree items. */
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
  box-shadow: 0 0 0 1px var(--color-surface);
}
.note-tree__treeitem[aria-selected="true"] {
  background-color: var(--color-accent-soft);
  color: var(--color-accent-soft-fg);
  font-weight: 600;
}

.note-tree__treeitem--folder {
  font-weight: 600;
  color: var(--color-text-muted);
}
.note-tree__treeitem--folder[aria-expanded="true"] {
  color: var(--color-text);
}

.note-tree__folder-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.note-tree__twist {
  display: inline-block;
  inline-size: 0.75rem;
  text-align: center;
  color: var(--color-text-muted);
  transition: transform var(--motion-fast) var(--motion-ease);
}
.note-tree__twist::before { content: "\25B6"; font-size: 0.7em; }
.note-tree__treeitem--folder[aria-expanded="true"] > .note-tree__folder-label > .note-tree__twist {
  transform: rotate(90deg);
}
@media (prefers-reduced-motion: reduce) {
  .note-tree__twist { transition: none; }
}

/* Nested groups indent via logical property — RTL-ready even though we ship LTR. */
.note-tree__group {
  padding-inline-start: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-block-start: var(--space-1);
}

/* Touch targets: 44px minimum on coarse pointers (phones, tablets). */
@media (pointer: coarse) {
  .note-tree__treeitem {
    min-height: 44px;
    padding-block: 0.6rem;
  }
}

/* === Phase 2: Skeletons + CLS === */
/*
 * Skeleton shimmer for tree + note body fetch states. Tokens only
 * (no raw colors); gradient animates at --motion-fast cadence. Reduced
 * motion branch collapses to a flat neutral box. Forced-colors collapses
 * to CanvasText outline so skeletons remain perceivable in high-contrast.
 */
@keyframes campaign-skeleton-shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}

.skeleton {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-sm, 6px);
  background-color: var(--color-surface-muted);
  background-image: linear-gradient(
    90deg,
    var(--color-surface-muted) 0%,
    var(--color-surface-raised) 50%,
    var(--color-surface-muted) 100%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: campaign-skeleton-shimmer calc(var(--motion-fast) * 12) linear infinite;
  /* Prevent focus / pointer interaction — purely decorative placeholder. */
  pointer-events: none;
  user-select: none;
}

.skeleton--block {
  min-height: 8rem;
  inline-size: 100%;
}

.skeleton--line {
  block-size: 0.9rem;
  inline-size: 100%;
  margin-block: 0.55rem;
}

.skeleton--line:nth-of-type(3n) {
  inline-size: 82%;
}
.skeleton--line:last-of-type {
  inline-size: 62%;
}

.skeleton--tree-item {
  block-size: 1.6rem;
  inline-size: 100%;
  margin-block: 0.45rem;
}
.skeleton--tree-item:nth-of-type(2n) {
  inline-size: 78%;
}
.skeleton--tree-item:nth-of-type(4n) {
  inline-size: 90%;
}

.skeleton--note-title {
  block-size: 1.6rem;
  inline-size: 52%;
  margin-block: 0.25rem 1rem;
}

/* Skeleton containers reserve space to minimize CLS when markup appears. */
.note-tree__skeleton {
  display: block;
  padding-block: var(--space-1, 0.25rem);
}
.note-body__skeleton {
  display: block;
  padding-block: var(--space-2, 0.5rem);
  /* Reserve roughly one note-body viewport so the editor pane doesn't collapse. */
  min-block-size: 22rem;
}
.note-tree__skeleton[hidden],
.note-body__skeleton[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .skeleton {
    background-image: none;
    background-color: var(--color-surface-muted);
    animation: none;
  }
}

@media (forced-colors: active) {
  .skeleton {
    background-image: none;
    background-color: Canvas;
    border: 1px solid CanvasText;
    animation: none;
  }
}

/*
 * Save indicator reserves a line-height-worth of vertical space so the
 * header doesn't shift when the text toggles between "", "Saving...",
 * "Saved", and error. Value = font-size-sm (~0.875rem) × line-height 1.4
 * + 2× 0.25rem vertical padding = ~1.725rem. Rounded to 1.75rem to
 * match the existing .campaign-workspace__status min-height budget.
 * This additive rule preserves the original .save-indicator layout.
 */
.save-indicator {
  min-height: 1.75rem;
  /* vertical-align so the reserved box aligns with surrounding inline text. */
  vertical-align: middle;
}

/*
 * Preview pane containment — scope layout / paint / style to this box
 * so 150ms-debounced markdown reflows don't ripple into the grid.
 */
.note-preview {
  contain: content;
}

/* === Phase 2b: Responsive + Splitpane ================================= */
/*
 * Desktop (>=768px): three-column grid with draggable splitters between
 *   tree | editor | backlinks. The JS sets `--tree-width` and
 *   `--backlinks-width` as inline custom properties on the grid host; the
 *   grid template consumes them (defined on .campaign-workspace__grid
 *   above). If splitpane JS never loads, the clamp() fallbacks apply.
 *
 * Mobile (<768px): tree slides in from the start edge; backlinks lifts
 *   from the bottom. Both gated by `body[data-drawer-tree|backlinks]`
 *   that campaign-workspace.js flips on toggle-button click / scrim tap /
 *   Escape. A scrim fades over the editor while either drawer is open.
 *
 * Conventions:
 *   - Logical properties only (inset-inline-*, margin-inline-*, ...)
 *   - Transforms for drawer animation (GPU-friendly; falls back to instant
 *     under prefers-reduced-motion).
 *   - Touch targets at >=44px under (pointer: coarse).
 *   - Forced-colors: borders via ButtonBorder, text via CanvasText.
 */

/* --- Mobile toolbar (toggle buttons) --------------------------------- */
.campaign-workspace__mobile-toolbar {
  /* Hidden on desktop; shown inside the mobile media query below. */
  display: none;
  gap: var(--space-2);
  flex-wrap: wrap;
  align-items: center;
}

.workspace-drawer-toggle {
  /* Inherits .builder-button sizing; just ensure enough tap area. */
  min-block-size: 2.25rem;
}

/* --- Splitter (desktop only) ---------------------------------------- */
.workspace-splitter {
  inline-size: 4px;
  align-self: stretch;
  justify-self: center;
  min-block-size: 100%;
  background-color: var(--color-border-subtle, var(--color-border));
  border-radius: var(--radius-pill);
  cursor: col-resize;
  /* Widen the hit target without growing the visible track. */
  position: relative;
  touch-action: none;
  transition: background-color var(--motion-fast) var(--motion-ease);
}
.workspace-splitter::before {
  /* Expands the effective hit area to ~12px without bloating the grid
     track width. Pure decoration; pointer events pass through. */
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline: -4px;
}
.workspace-splitter:hover,
.workspace-splitter:focus-visible,
.workspace-splitter[data-dragging="true"] {
  background-color: var(--color-accent-border, var(--color-accent));
}
.workspace-splitter:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

/* --- Scrim (mobile only, toggled via hidden attribute) --------------- */
.workspace-scrim {
  position: fixed;
  inset-block: 0;
  inset-inline: 0;
  background-color: var(--color-overlay, rgba(0, 0, 0, 0.45));
  opacity: 0;
  pointer-events: none;
  z-index: 40;
  transition: opacity var(--motion-standard) var(--motion-ease);
}
.workspace-scrim[hidden] { display: none; }
@media (max-width: 767.98px) {
  body[data-drawer-tree="open"] .workspace-scrim,
  body[data-drawer-backlinks="open"] .workspace-scrim {
    opacity: 1;
    pointer-events: auto;
  }
}

/* --- Mobile layout (<768px) ----------------------------------------- */
@media (max-width: 767.98px) {
  .campaign-workspace__mobile-toolbar {
    display: flex;
  }

  /* Splitters are inert on mobile — swap to display:none so the grid
     collapses to a single column cleanly. */
  .workspace-splitter {
    display: none;
  }

  .campaign-workspace__grid {
    /* Single-column flow; the editor takes full width. Drawers are
       position:fixed below so they detach from the grid. */
    grid-template-columns: 1fr;
  }

  /* Editor pane occupies the full viewport width of the grid track. */
  .workspace-editor {
    min-block-size: 60svh;
  }

  /* Tree drawer — slide-over from the start edge. */
  .workspace-drawer--tree {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    inline-size: min(88vw, 22rem);
    max-inline-size: 22rem;
    block-size: 100dvh;
    /* Fall back to svh for iOS Safari where 100dvh may resize the
       drawer during URL-bar show/hide. svh is the small viewport. */
    block-size: 100svh;
    z-index: 50;
    overflow: auto;
    border-radius: 0;
    border-block: none;
    border-inline-start: none;
    box-shadow: var(--shadow-lg);
    transform: translateX(-100%);
    transition: transform var(--motion-standard) var(--motion-ease);
  }
  body[data-drawer-tree="open"] .workspace-drawer--tree {
    transform: translateX(0);
  }

  /* Backlinks drawer — slide up from bottom, capped at ~40svh. */
  .workspace-drawer--backlinks {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    block-size: auto;
    max-block-size: 40svh;
    z-index: 50;
    overflow: auto;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-inline: none;
    border-block-end: none;
    box-shadow: var(--shadow-lg);
    transform: translateY(100%);
    transition: transform var(--motion-standard) var(--motion-ease);
  }
  body[data-drawer-backlinks="open"] .workspace-drawer--backlinks {
    transform: translateY(0);
  }

  /* Touch targets on mobile: at least 44px on every tappable control. */
  .workspace-drawer-toggle,
  .note-tree__link,
  .backlink-item,
  .editor-tab {
    min-block-size: 44px;
  }
}

/* Desktop: hide the drawer toggles entirely. The drawers themselves are
   plain inline panes above 768px — no transforms applied, default flow. */
@media (min-width: 768px) {
  .campaign-workspace__mobile-toolbar {
    display: none;
  }
  /* Ensure drawer classes at desktop render as normal panes (no transform
     left over from a mid-resize). */
  .workspace-drawer--tree,
  .workspace-drawer--backlinks {
    position: static;
    transform: none;
    box-shadow: none;
    max-inline-size: none;
    max-block-size: none;
    block-size: auto;
    inline-size: auto;
    inset: auto;
    overflow: visible;
  }
  .workspace-scrim {
    display: none !important;
  }
}

/* --- Pointer-coarse (touch) touch targets --------------------------- */
@media (pointer: coarse) {
  .workspace-drawer-toggle,
  .editor-tab,
  .note-tree__link,
  .backlink-item,
  [data-dialog-action],
  .builder-button.small {
    min-block-size: 44px;
  }
}

/* --- Reduced motion -------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .workspace-drawer--tree,
  .workspace-drawer--backlinks,
  .workspace-scrim,
  .workspace-splitter {
    transition: none;
  }
}

/* --- Forced colors --------------------------------------------------- */
@media (forced-colors: active) {
  .workspace-splitter {
    background-color: ButtonBorder;
    border: 1px solid ButtonBorder;
  }
  .workspace-splitter:hover,
  .workspace-splitter:focus-visible,
  .workspace-splitter[data-dragging="true"] {
    background-color: Highlight;
  }
  .workspace-drawer--tree,
  .workspace-drawer--backlinks {
    border: 1px solid ButtonBorder;
    color: CanvasText;
  }
  .workspace-scrim {
    background-color: Canvas;
    opacity: 0.75;
  }
}

/* === Phase 3: Toasts ============================================= */
/*
 * Stacked notification toasts. Mounted once at the bottom of <body> by
 * campaign-toast.js. The visible queue is capped at 3; a 4th `show()`
 * drops the oldest. We delegate AT announcements to the global
 * #a11y-status / #a11y-alert live regions — this stack is visual only
 * (aria-live="off").
 */
#trpg-toast-stack {
  position: fixed;
  inset-block-end: 1rem;
  inset-inline-end: 1rem;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
  inline-size: min(24rem, calc(100vw - 2rem));
  pointer-events: none;
}

.trpg-toast {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-3);
  align-items: start;
  padding: var(--space-3) var(--space-4);
  padding-inline-start: calc(var(--space-4) + 4px);
  border: 1px solid var(--color-border);
  border-inline-start: 4px solid var(--color-accent);
  border-radius: var(--radius-md);
  background-color: var(--color-surface-raised);
  color: var(--color-text);
  box-shadow: var(--shadow-lg);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-base);
  pointer-events: auto;
  overflow: hidden;
  touch-action: pan-y;
}

.trpg-toast--success { border-inline-start-color: oklch(58% 0.18 155); }
.trpg-toast--info    { border-inline-start-color: var(--color-info); }
.trpg-toast--warning { border-inline-start-color: oklch(65% 0.16 80); }
.trpg-toast--error   { border-inline-start-color: var(--color-danger); }

.trpg-toast__body {
  grid-column: 1;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-inline-size: 0;
}

.trpg-toast__title {
  margin: 0;
  font-weight: 600;
  color: var(--color-text);
}

.trpg-toast__message {
  margin: 0;
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

.trpg-toast__actions {
  grid-column: 1 / -1;
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-block-start: var(--space-1);
}

.trpg-toast__action {
  appearance: none;
  border: 1px solid var(--color-border-strong);
  background-color: transparent;
  color: var(--color-accent-soft-fg);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  font: inherit;
  cursor: pointer;
  min-block-size: 1.75rem;
}
.trpg-toast__action:hover {
  background-color: var(--color-accent-soft);
  border-color: var(--color-accent-border);
}
.trpg-toast__action:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

.trpg-toast__dismiss {
  grid-column: 2;
  grid-row: 1;
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  font-size: 1.25rem;
  line-height: 1;
  padding: 0;
  inline-size: 24px;
  block-size: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-xs);
}
.trpg-toast__dismiss:hover {
  color: var(--color-text);
  background-color: var(--color-surface-muted);
}
.trpg-toast__dismiss:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

@media (pointer: coarse) {
  .trpg-toast__dismiss {
    inline-size: 44px;
    block-size: 44px;
  }
}

/* Slide-in from the inline-end edge. Suppressed under reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  .trpg-toast {
    animation: trpgToastIn var(--motion-standard) var(--motion-ease-out);
    transition: transform var(--motion-standard) var(--motion-ease),
                opacity var(--motion-fast) var(--motion-ease);
  }
  .trpg-toast[data-dismissing="true"] {
    opacity: 0;
    transform: translateX(100%);
  }
  .trpg-toast[data-swiping="true"] {
    transition: none;
  }
  @keyframes trpgToastIn {
    from { opacity: 0; transform: translateX(12%); }
    to   { opacity: 1; transform: translateX(0); }
  }
}

@media (max-width: 767.98px) {
  #trpg-toast-stack {
    inset-block-end: 0.5rem;
    inset-inline-end: 0.5rem;
    inset-inline-start: 0.5rem;
    inline-size: auto;
  }
  .trpg-toast {
    inline-size: 100%;
  }
}

@media (forced-colors: active) {
  .trpg-toast {
    border: 1px solid ButtonBorder;
    background-color: Canvas;
    color: CanvasText;
  }
  .trpg-toast__action,
  .trpg-toast__dismiss {
    border: 1px solid ButtonBorder;
    color: CanvasText;
  }
}

/* === Phase 3: Command Palette === */
/* Full-viewport backdrop that centers the card. The card itself is the ARIA
   dialog; the backdrop is decorative + catches outside-clicks. Backdrop fade
   is suppressed under prefers-reduced-motion and for forced-colors mode. */
.command-palette-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1100;
  display: grid;
  place-items: start center;
  padding-block-start: clamp(4rem, 12vh, 9rem);
  padding-inline: var(--space-4);
  background-color: color-mix(in oklch, black 48%, transparent);
  animation: trpgPaletteFade var(--motion-fast) var(--motion-ease-out);
}

.command-palette-backdrop[hidden] {
  display: none;
}

.command-palette-backdrop[data-reduce-motion="true"] {
  animation: none;
}

@keyframes trpgPaletteFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.command-palette-card {
  inline-size: min(40rem, 100%);
  max-block-size: 70vh;
  display: flex;
  flex-direction: column;
  background-color: var(--color-surface-raised);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

.command-palette-input {
  appearance: none;
  -webkit-appearance: none;
  inline-size: 100%;
  padding: var(--space-4) var(--space-5);
  font-family: var(--font-family-ui);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  color: var(--color-text);
  background-color: transparent;
  border: 0;
  border-block-end: 1px solid var(--color-border);
  outline: none;
}

.command-palette-input::-webkit-search-cancel-button {
  appearance: none;
}

.command-palette-input:focus-visible {
  outline: none;
  box-shadow: inset 0 -2px 0 var(--color-accent);
}

.command-palette-list {
  list-style: none;
  margin: 0;
  padding: var(--space-2) 0;
  overflow-y: auto;
  flex: 1 1 auto;
}

.command-palette-list[hidden] {
  display: none;
}

.command-palette-item {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: var(--space-3) var(--space-5);
  cursor: pointer;
  border-inline-start: 2px solid transparent;
  color: var(--color-text);
}

.command-palette-item__title {
  font-weight: 600;
  font-size: var(--font-size-sm);
  line-height: 1.35;
}

.command-palette-item__subtitle {
  font-size: var(--font-size-xs);
  color: var(--color-text-muted);
  line-height: 1.3;
}

.command-palette-item[aria-selected="true"],
.command-palette-item.is-selected {
  background-color: var(--color-accent-soft);
  color: var(--color-accent-soft-fg);
  border-inline-start-color: var(--color-accent);
}

.command-palette-item[aria-selected="true"] .command-palette-item__subtitle,
.command-palette-item.is-selected .command-palette-item__subtitle {
  color: color-mix(in oklch, var(--color-accent-soft-fg) 80%, var(--color-text-muted));
}

.command-palette-empty {
  margin: 0;
  padding: var(--space-6) var(--space-5);
  text-align: center;
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
}

.command-palette-empty[hidden] {
  display: none;
}

@media (prefers-reduced-motion: reduce) {
  .command-palette-backdrop {
    animation: none;
  }
}

@media (max-width: 767.98px) {
  /* Full-viewport palette on narrow screens: the card takes the whole
     viewport and the list consumes the remaining height under the input. */
  .command-palette-backdrop {
    padding: 0;
    place-items: stretch;
  }
  .command-palette-card {
    inline-size: 100%;
    max-block-size: 100vh;
    block-size: 100vh;
    border-radius: 0;
    border: 0;
  }
}

@media (forced-colors: active) {
  .command-palette-backdrop {
    background-color: Canvas;
  }
  .command-palette-card {
    border: 1px solid CanvasText;
    background-color: Canvas;
    color: CanvasText;
  }
  .command-palette-input {
    border-block-end: 1px solid CanvasText;
    color: CanvasText;
    background-color: Canvas;
  }
  .command-palette-item[aria-selected="true"],
  .command-palette-item.is-selected {
    background-color: Highlight;
    color: HighlightText;
    border-inline-start-color: Highlight;
  }
}

/* === Phase 3: Shortcut Help === */
/* Native <dialog>; ::backdrop dims the page. Layout is a stack of grouped
   <dl>s (combo on the left, description on the right) using a 2-column grid
   so the chips line up across rows. Reuses the same surface tokens as the
   command palette card so theming is consistent. */
.shortcut-help-dialog {
  inline-size: min(560px, calc(100vw - 2rem));
  max-block-size: 80vh;
  overflow-y: auto;
  padding: var(--space-5) var(--space-5) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg, 12px);
  background-color: var(--color-surface, var(--color-bg, white));
  color: var(--color-text);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
}

.shortcut-help-dialog::backdrop {
  background-color: color-mix(in oklch, black 48%, transparent);
  animation: trpgShortcutHelpFade var(--motion-fast, 120ms) var(--motion-ease-out, ease-out);
}

@keyframes trpgShortcutHelpFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.shortcut-help-dialog__title {
  margin: 0 0 var(--space-4);
  font-size: var(--font-size-lg, 1.125rem);
  font-weight: 600;
}

.shortcut-help-group {
  margin-block-end: var(--space-4);
}

.shortcut-help-group:last-of-type {
  margin-block-end: var(--space-3);
}

.shortcut-help-group__heading {
  margin: 0 0 var(--space-2);
  font-size: var(--font-size-sm, 0.875rem);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

.shortcut-help-group__list {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: var(--space-4);
  row-gap: var(--space-2);
  margin: 0;
}

.shortcut-help-row__combo {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1, 0.25rem);
  margin: 0;
}

.shortcut-help-row__desc {
  margin: 0;
  align-self: center;
  color: var(--color-text);
}

.shortcut-help-key {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  min-inline-size: 1.6em;
  text-align: center;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.85em;
  line-height: 1.4;
  color: var(--color-text);
  background-color: var(--color-surface-raised, var(--color-surface, #f3f3f3));
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm, 4px);
  box-shadow: inset 0 -1px 0 var(--color-border);
}

.shortcut-help-key__sep {
  color: var(--color-text-muted);
  font-size: 0.85em;
  padding-inline: 1px;
}

.shortcut-help-dialog__close {
  display: inline-block;
  margin-block-start: var(--space-3);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 8px);
  background-color: transparent;
  color: var(--color-text);
  font: inherit;
  cursor: pointer;
}

.shortcut-help-dialog__close:hover,
.shortcut-help-dialog__close:focus-visible {
  background-color: var(--color-surface-raised, var(--color-surface));
}

.shortcut-help-dialog__close:focus-visible {
  outline: 2px solid var(--color-accent, currentColor);
  outline-offset: 2px;
}

@media (max-width: 767.98px) {
  /* Take the whole viewport on small screens — easier to tap-close, and
     the cheat-sheet rows wrap nicely without horizontal scroll. */
  .shortcut-help-dialog {
    inline-size: 100vw;
    max-block-size: 100vh;
    block-size: 100vh;
    border-radius: 0;
    border: 0;
    margin: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .shortcut-help-dialog::backdrop {
    animation: none;
  }
}

@media (forced-colors: active) {
  .shortcut-help-dialog {
    border: 1px solid CanvasText;
    background-color: Canvas;
    color: CanvasText;
  }
  .shortcut-help-key {
    border: 1px solid CanvasText;
    background-color: Canvas;
    color: CanvasText;
  }
  .shortcut-help-dialog__close {
    border: 1px solid CanvasText;
    color: CanvasText;
  }
}

/* === Phase 5: hotfix — editor toolbar visibility, title affordance, dark CodeMirror === */

/* Belt-and-suspenders @font-face for Font Awesome 4.7. The vendored
   font-awesome.min.css already declares @font-face with relative URLs
   (../fonts/...). This duplicate declaration uses ABSOLUTE paths so the
   font loads correctly regardless of whether the FA CSS itself was
   fetched from a cached / wrong path. Same family name → ::before
   icon rules win on cascade either way. */
@font-face {
  font-family: 'FontAwesome';
  src: url('/lib/font-awesome/fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),
       url('/lib/font-awesome/fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),
       url('/lib/font-awesome/fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

/* (1) EasyMDE toolbar buttons — make them visible against our surface tokens
   in both themes. EasyMDE ships its own light-only styles; without these
   overrides the buttons are near-invisible. */
.EasyMDEContainer .editor-toolbar {
  background-color: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-bottom: none;
  border-top-left-radius: var(--radius-md);
  border-top-right-radius: var(--radius-md);
  padding: 0.25rem 0.35rem;
  opacity: 1;
}
.EasyMDEContainer .editor-toolbar a,
.EasyMDEContainer .editor-toolbar button {
  color: var(--color-text);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  width: 1.85rem;
  height: 1.85rem;
  line-height: 1.85rem;
  text-align: center;
}
.EasyMDEContainer .editor-toolbar a:hover,
.EasyMDEContainer .editor-toolbar button:hover,
.EasyMDEContainer .editor-toolbar a.active,
.EasyMDEContainer .editor-toolbar button.active {
  background-color: var(--color-accent-soft);
  color: var(--color-accent-soft-fg);
  border-color: var(--color-accent-border);
}
.EasyMDEContainer .editor-toolbar a:focus-visible,
.EasyMDEContainer .editor-toolbar button:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}
.EasyMDEContainer .editor-toolbar i.separator {
  border-inline-start: 1px solid var(--color-border);
  margin: 0 0.2rem;
}
@media (pointer: coarse) {
  .EasyMDEContainer .editor-toolbar a,
  .EasyMDEContainer .editor-toolbar button {
    width: 2.5rem;
    height: 2.5rem;
    line-height: 2.5rem;
  }
}

/* EasyMDE status bar — also light-only by default. */
.EasyMDEContainer .editor-statusbar {
  color: var(--color-text-muted);
  background-color: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-top: none;
  border-bottom-left-radius: var(--radius-md);
  border-bottom-right-radius: var(--radius-md);
  padding: 0.25rem 0.6rem;
}

/* (2) Title input — make it look editable. Was invisible (transparent border + bg).
   Now wrapped in a <label class="note-title-label"> so the affordance is unmistakable. */
.note-title-label {
  flex: 1 1 240px;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-inline-size: 0;
}
.note-title-label__text {
  font-size: var(--font-size-xs);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  padding-inline-start: 0.15rem;
}
.note-title-label .note-title-input {
  flex: 1 1 auto;
  inline-size: 100%;
}
.note-title-input {
  background-color: var(--color-surface-muted);
  border: 1px solid var(--color-border);
}
.note-title-input::placeholder {
  color: var(--color-text-muted);
  opacity: 1;
}
.note-title-input:hover:not(:disabled) {
  background-color: var(--color-surface-raised, var(--color-surface));
  border-color: var(--color-border-strong, var(--color-border));
}
.note-title-input:focus {
  background-color: var(--color-surface);
}
.note-title-input:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* (3) Save indicator — always visible (even in idle) so the user knows
   autosave is on. Localized "Auto-save · ⌘S" text rendered by JS. */
.save-indicator[data-state="idle"] {
  color: var(--color-text-muted);
  background-color: var(--color-surface-muted);
  border: 1px solid var(--color-border);
}
.save-indicator[data-state="idle"]::before { content: ""; }

/* (4) CodeMirror dark theme — vendored EasyMDE assumes light. Override the
   surface, text, gutter, cursor, and selection under both the explicit
   data-theme="dark" and the system prefers-color-scheme: dark fallback. */
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror,
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror-scroll {
  background-color: var(--color-surface);
  color: var(--color-text);
}
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror-gutters {
  background-color: var(--color-surface-sunken);
  border-inline-end: 1px solid var(--color-border);
  color: var(--color-text-muted);
}
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror-linenumber {
  color: var(--color-text-muted);
}
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror-cursor {
  border-inline-start-color: var(--color-text);
}
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror-selected,
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror-line::selection,
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror-line ::selection {
  background-color: var(--color-accent-soft);
}
:root[data-theme="dark"] .EasyMDEContainer .CodeMirror-line span.cm-comment {
  color: var(--color-text-muted);
}
:root[data-theme="dark"] .EasyMDEContainer .editor-toolbar {
  background-color: var(--color-surface-sunken);
}
:root[data-theme="dark"] .EasyMDEContainer .editor-statusbar {
  background-color: var(--color-surface-sunken);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .EasyMDEContainer .CodeMirror,
  :root:not([data-theme="light"]) .EasyMDEContainer .CodeMirror-scroll {
    background-color: var(--color-surface);
    color: var(--color-text);
  }
  :root:not([data-theme="light"]) .EasyMDEContainer .CodeMirror-gutters {
    background-color: var(--color-surface-sunken);
    border-inline-end: 1px solid var(--color-border);
    color: var(--color-text-muted);
  }
  :root:not([data-theme="light"]) .EasyMDEContainer .CodeMirror-linenumber {
    color: var(--color-text-muted);
  }
  :root:not([data-theme="light"]) .EasyMDEContainer .CodeMirror-cursor {
    border-inline-start-color: var(--color-text);
  }
  :root:not([data-theme="light"]) .EasyMDEContainer .editor-toolbar,
  :root:not([data-theme="light"]) .EasyMDEContainer .editor-statusbar {
    background-color: var(--color-surface-sunken);
  }

/* --- Attached adversaries (workspace + my-adversaries chips) ----------- */
.campaign-attachments {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: var(--space-4);
}
.campaign-attachments__toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.campaign-attachments__toolbar h2 { margin: 0; }
.campaign-picker-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  max-height: 50vh;
  overflow-y: auto;
  margin: var(--space-3) 0;
}
.campaign-picker-list__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  text-align: start;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.campaign-picker-list__row:hover,
.campaign-picker-list__row:focus-visible {
  background: var(--color-surface-raised);
  border-color: var(--color-accent);
  outline: none;
}
.campaign-picker-list__main { display: inline-flex; align-items: baseline; gap: var(--space-1); }
.campaign-picker-list__main small { color: var(--color-text-muted); }

.adversary-campaigns {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
.adversary-campaigns__heading { margin: 0; font-size: var(--font-size-md); }
.adversary-campaigns__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}
.adversary-campaigns__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px 8px;
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  font-size: var(--font-size-sm);
}
.adversary-campaigns__chip-detach {
  background: none;
  border: none;
  color: var(--color-text-muted);
  cursor: pointer;
  font-size: var(--font-size-md);
  line-height: 1;
  padding: 0;
}
.adversary-campaigns__chip-detach:hover { color: var(--color-danger); }
.adversary-campaigns__empty { color: var(--color-text-muted); margin: 0; }
}
