*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  /* Dark grey palette (replaces deep navy) */
  --dark: #2b2b2b;
  --dark-2: #3a3a3a;
  --dark-3: #4f4f4f;

  /* Text */
  --ink: #1c1c1c;
  --ink-2: #4a4a4a;
  --ink-3: #8a8a8a;

  /* Surfaces — clean whites/light greys, no warm parchment tint */
  --surface: #ffffff;
  --surface-1: #f7f7f5;
  --surface-2: #efefed;
  --border-color: #e2e2de;

  /* Accent — amber/gold, kept from original */
  --gold: #c49a2a;
  --gold-light: #d4ab3a;
  --gold-bg: #fdf8ed;

  /* Danger */
  --crimson: #b33a3a;
  --crimson-bg: #fdf2f2;

  --radius: 8px;
  --shadow: 0 1px 4px rgba(0,0,0,0.07), 0 2px 12px rgba(0,0,0,0.05);
  --shadow-lg: 0 4px 24px rgba(0,0,0,0.10), 0 1px 4px rgba(0,0,0,0.06);
  --border: 1px solid var(--border-color);

  /* Typography — clean sans-serif body, serif only for display names */
  --font-display: 'Palatino Linotype', Palatino, 'Book Antiqua', serif;
  --font-ui: system-ui, -apple-system, sans-serif;
  /* Keep --font-body alias for compat */
  --font-body: var(--font-display);

  /* Parchment aliases — map to surface/border tokens so dark mode is free */
  --parchment: var(--surface-1);
  --parchment-2: rgba(196,154,42,0.3);
  --parchment-3: var(--border-color);
}

body { font-family: var(--font-ui); background: var(--surface-1); color: var(--ink); min-height: 100vh; }

/* Monotone chip — used for neutral status indicators like "private" */
.chip-mono { display: inline-flex; align-items: center; gap: 0.3em; padding: 0.2rem 0.55rem; border-radius: 99px; background: var(--surface-2); border: var(--border); font-family: var(--font-ui); font-size: 0.64rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3); white-space: nowrap; }

/* ── Auth pages ── */
.auth-page { display: flex; align-items: center; justify-content: center; min-height: 100vh; background: var(--dark); }
.auth-card { background: var(--surface); border-radius: 14px; padding: 2.5rem 3rem; width: 360px; box-shadow: var(--shadow-lg); text-align: center; border: var(--border); }
.auth-emblem { font-size: 2.4rem; margin-bottom: 0.5rem; display: block; }
.auth-card h1 { font-family: var(--font-display); font-size: 1.6rem; font-weight: 700; color: var(--ink); margin-bottom: 0.2rem; letter-spacing: 0.01em; }
.auth-card svg { display: block; margin: 0 auto 1.5rem; }
.auth-card > p { font-family: var(--font-ui); font-size: 0.82rem; color: var(--ink-3); margin-bottom: 1.75rem; }
.auth-error { font-family: var(--font-ui); font-size: 0.8rem; color: var(--crimson); margin-top: 0.75rem; min-height: 1.2em; }
.auth-link { font-family: var(--font-ui); font-size: 0.8rem; color: var(--ink-3); margin-top: 1rem; }
.auth-link a { color: var(--gold); text-decoration: none; }

/* ── Header ── */
header { background: var(--dark); color: #fff; padding: 0 2rem; height: 56px; display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; z-index: 100; box-shadow: 0 1px 0 rgba(255,255,255,0.06); }
.header-brand { display: flex; align-items: center; text-decoration: none; color: #fff; line-height: 0; }
.header-brand span { font-size: 1.2rem; }
.header-right { display: flex; align-items: center; gap: 0.4rem; padding-right: 0.25rem; }
.header-user { display: flex; align-items: center; gap: 0.5rem; font-family: var(--font-ui); font-size: 0.82rem; color: rgba(255,255,255,0.7); }
.header-avatar { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; background: var(--gold); display: flex; align-items: center; justify-content: center; font-size: 0.7rem; font-weight: 600; color: #fff; flex-shrink: 0; overflow: hidden; }
.header-avatar img { width: 100%; height: 100%; object-fit: cover; }

/* Nav links */
.nav-link { font-family: var(--font-ui); font-size: 0.78rem; color: rgba(255,255,255,0.5); background: none; border: none; border-radius: 6px; padding: 0.3rem 0.6rem; cursor: pointer; transition: color 0.15s, background 0.15s; text-decoration: none; display: inline-flex; align-items: center; }
.nav-link:hover { color: #fff; background: rgba(255,255,255,0.08); }

/* ── Main layout ── */
main { max-width: 1100px; margin: 0 auto; padding: 2.5rem 1.5rem; }
.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; }
.page-title { font-family: var(--font-display); font-size: 1.3rem; font-weight: 700; color: var(--ink); min-width: 0; word-break: break-word; }
.page-subtitle { font-family: var(--font-ui); font-size: 0.78rem; color: var(--ink-3); margin-top: 0.15rem; }

/* ── Fields ── */
.field { margin-bottom: 1.2rem; text-align: left; }
.field label { display: block; font-family: var(--font-ui); font-size: 0.72rem; font-weight: 600; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 0.4rem; }
.field input[type="text"], .field input[type="password"], .field input[type="email"], .field textarea, .field select {
  width: 100%; padding: 0.6rem 0.85rem; border: 1px solid var(--border-color); border-radius: 6px;
  font-family: var(--font-ui); font-size: 0.92rem; color: var(--ink); background: var(--surface);
  outline: none; transition: border-color 0.15s; resize: vertical;
}
.field input:focus, .field textarea:focus, .field select:focus { border-color: var(--gold); }
.field textarea { min-height: 90px; }

/* ── Buttons ── */
.btn-primary { width: 100%; padding: 0.7rem 1rem; background: var(--dark); color: #fff; border: none; border-radius: 7px; font-family: var(--font-ui); font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: background 0.15s, transform 0.1s; letter-spacing: 0.02em; }
.btn-primary:hover { background: var(--dark-2); }
.btn-primary:active { transform: scale(0.98); }
.btn-primary:disabled { opacity: 0.45; cursor: default; }

.btn-gold { background: var(--gold); color: #fff; border: none; border-radius: 7px; padding: 0.55rem 1.1rem; font-family: var(--font-ui); font-size: 0.85rem; font-weight: 600; cursor: pointer; transition: background 0.15s; display: inline-flex; align-items: center; gap: 0.4rem; letter-spacing: 0.02em; }
.btn-gold:hover { background: var(--gold-light); }
.btn-gold:disabled { opacity: 0.45; cursor: default; }

.btn-outline { background: var(--surface); color: var(--ink-2); border: var(--border); border-radius: 6px; padding: 0.5rem 0.9rem; font-family: var(--font-ui); font-size: 0.83rem; cursor: pointer; transition: background 0.12s, border-color 0.12s; display: inline-flex; align-items: center; gap: 0.35rem; }
.btn-outline:hover { background: var(--surface-2); border-color: #ccc; }

.btn-danger { background: var(--crimson); color: #fff; border: none; border-radius: 6px; padding: 0.55rem 1.1rem; font-family: var(--font-ui); font-size: 0.85rem; font-weight: 500; cursor: pointer; }
.btn-danger:hover { background: #9e2e2e; }

/* ── Cards ── */
.card { background: var(--surface); border-radius: var(--radius); border: var(--border); box-shadow: var(--shadow); }

/* ── Image upload zone ── */
.img-upload-zone { border: 1.5px dashed var(--border-color); border-radius: 7px; background: var(--surface-1); cursor: pointer; transition: border-color 0.15s, background 0.15s; overflow: hidden; }
.img-upload-zone:hover, .img-upload-zone.drag-over { border-color: var(--gold); background: var(--gold-bg); }
.img-upload-zone input { display: none; }
.img-placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 1.5rem; gap: 0.4rem; color: var(--ink-3); }
.img-placeholder svg { width: 26px; height: 26px; }
.img-placeholder span { font-family: var(--font-ui); font-size: 0.78rem; }
.img-preview { width: 100%; aspect-ratio: 3/2; object-fit: cover; display: none; }

/* ── Modal ── */
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.45); display: flex; align-items: flex-start; justify-content: center; z-index: 1000; padding: 1rem; overflow-y: auto; }
.modal { background: var(--surface); border-radius: 12px; width: 100%; max-width: 480px; box-shadow: var(--shadow-lg); overflow: hidden; max-height: 90vh; overflow-y: auto; border: var(--border); margin: auto; }
.modal-header { background: var(--dark); color: #fff; padding: 1rem 1.4rem; display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; }
.modal-header h2 { font-family: var(--font-ui); font-size: 0.92rem; font-weight: 600; letter-spacing: 0.02em; }
.btn-close { background: none; border: none; color: rgba(255,255,255,0.55); cursor: pointer; font-size: 1.3rem; line-height: 1; transition: color 0.12s; }
.btn-close:hover { color: #fff; }
.modal-body { padding: 1.5rem; }
.modal-footer { display: flex; justify-content: flex-end; gap: 0.6rem; padding: 1rem 1.5rem 1.5rem; border-top: var(--border); }

/* ── Avatar ── */
.avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; background: var(--surface-2); display: inline-flex; align-items: center; justify-content: center; font-family: var(--font-ui); font-size: 0.78rem; font-weight: 600; color: var(--ink-3); flex-shrink: 0; overflow: hidden; border: 1.5px solid var(--border-color); }
.avatar img { width: 100%; height: 100%; object-fit: cover; }

/* ── Divider ── */
.divider { border: none; border-top: var(--border); margin: 1.5rem 0; }

/* ── Toast ── */
#toast { position: fixed; bottom: 1.5rem; right: 1.5rem; background: var(--dark); color: #fff; padding: 0.7rem 1.1rem; border-radius: 7px; font-family: var(--font-ui); font-size: 0.82rem; box-shadow: var(--shadow-lg); opacity: 0; transition: opacity 0.2s; pointer-events: none; z-index: 9999; }
#toast.show { opacity: 1; }

@media(max-width:500px) { .auth-card { padding: 2rem 1.5rem; width: calc(100% - 2.5rem); margin: 0 auto; } main { padding: 1.5rem 1rem; } }

/* ── Header create button ── */
.header-create { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.85); border: 1px solid rgba(255,255,255,0.18); border-radius: 6px; padding: 0.3rem 0.8rem; font-family: var(--font-ui); font-size: 0.8rem; font-weight: 500; cursor: pointer; transition: background 0.15s; white-space: nowrap; }
.header-create:hover { background: rgba(255,255,255,0.18); }

/* ── User dropdown ── */
.user-dropdown { position: relative; }
.user-dropdown-trigger { display: flex; align-items: center; gap: 0.45rem; background: none; border: none; border-radius: 6px; padding: 0.3rem 0.4rem; cursor: pointer; color: rgba(255,255,255,0.85); transition: background 0.15s; }
.user-dropdown-trigger:hover { background: rgba(255,255,255,0.08); }
.dropdown-name { font-family: var(--font-ui); font-size: 0.82rem; }
.dropdown-chevron { opacity: 0.5; flex-shrink: 0; transition: transform 0.2s; }
.user-dropdown-trigger[aria-expanded="true"] .dropdown-chevron { transform: rotate(180deg); }

.dropdown-menu { position: absolute; top: calc(100% + 6px); right: 0; background: var(--surface); border: var(--border); border-radius: 10px; box-shadow: var(--shadow-lg); min-width: 180px; overflow: hidden; opacity: 0; transform: translateY(-4px); pointer-events: none; transition: opacity 0.12s, transform 0.12s; z-index: 200; }
.dropdown-menu.open { opacity: 1; transform: translateY(0); pointer-events: all; }

.dropdown-user { display: flex; align-items: center; gap: 0.6rem; padding: 0.85rem 1rem; font-family: var(--font-ui); font-size: 0.85rem; font-weight: 600; color: var(--ink); border-bottom: var(--border); }
.dropdown-divider { border: none; border-top: var(--border); margin: 0; }
.dropdown-item { display: block; width: 100%; padding: 0.6rem 1rem; font-family: var(--font-ui); font-size: 0.84rem; color: var(--ink-2); text-decoration: none; background: none; border: none; text-align: left; cursor: pointer; transition: background 0.1s; }
.dropdown-item:hover { background: var(--surface-1); color: var(--ink); }
.dropdown-signout { color: var(--crimson); }
.dropdown-signout:hover { background: var(--crimson-bg); color: var(--crimson); }

/* ── Dark mode ── */
html.dark {
  --ink: #e8e6e1;
  --ink-2: #b0aca5;
  --ink-3: #706c66;
  --surface: #1e1e1e;
  --surface-1: #161616;
  --surface-2: #272727;
  --border-color: #333330;
  --gold-bg: #2a2410;
  --crimson-bg: #2a1212;
}
html.dark body { background: var(--surface-1); color: var(--ink); }
html.dark header { background: #111111; }
html.dark .modal-header { background: #111111; }
html.dark .field input, html.dark .field textarea, html.dark .field select { background: var(--surface-2); color: var(--ink); }

/* ── Dark mode toggle ── */
.dark-toggle-wrap { display: flex; align-items: center; justify-content: space-between; padding: 0.1rem 0; }
.dark-toggle-label { font-family: var(--font-ui); font-size: 0.85rem; color: var(--ink-2); }
.dark-toggle-track { width: 40px; height: 22px; border-radius: 11px; background: var(--border-color); border: 1px solid var(--border-color); cursor: pointer; position: relative; transition: background 0.2s; flex-shrink: 0; }
.dark-toggle-track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.2); transition: transform 0.2s; }
.dark-toggle-track.on { background: var(--gold); border-color: var(--gold); }
.dark-toggle-track.on::after { transform: translateX(18px); }

/* ── Section heading ── */
.section-heading { font-family:var(--font-ui); font-size:0.7rem; font-weight:600; text-transform:uppercase; letter-spacing:0.1em; color:var(--ink-3); margin-bottom:1rem; }

/* ── Section card ── */
.section-card { background:var(--surface); border:var(--border); border-radius:var(--radius); padding:1.5rem; box-shadow:var(--shadow); margin-bottom:1.5rem; }

/* ── Back link ── */
.back-link { font-family:var(--font-ui); font-size:0.72rem; letter-spacing:0.08em; text-transform:uppercase; color:var(--ink-3); text-decoration:none; display:inline-flex; align-items:center; gap:0.35rem; margin-bottom:1.5rem; }
.back-link:hover { color:var(--ink-2); }

/* ── Entity header (character / place / thing) ── */
.char-top { display:flex; align-items:flex-start; justify-content:space-between; gap:1rem; margin-bottom:0.6rem; }
.char-name-block { flex:1; min-width:0; }
.inline-name { font-size:1.9rem; font-weight:700; font-family:var(--font-body); color:var(--ink); line-height:1.2; width:100%; background:transparent; border:none; outline:none; padding:0.1rem 0.3rem; display:block; border-radius:4px; resize:none; overflow:hidden; }
.inline-subtitle { font-size:0.95rem; color:var(--ink-3); line-height:1; width:100%; background:transparent; border:none; outline:none; font-family:var(--font-ui); padding:0; border-radius:4px; resize:none; overflow:hidden; display:block; margin-top:0.2rem; }
.editable:hover { background:var(--surface-1); }
.editable:focus { background:var(--surface-1); outline:none; }

/* ── Overflow menu ── */
.overflow-wrap { position:relative; flex-shrink:0; }
.overflow-btn { background:none; border:none; cursor:pointer; color:var(--ink-3); padding:0.25rem 0.35rem; border-radius:5px; display:flex; align-items:center; transition:background 0.12s, color 0.12s; }
.overflow-btn:hover { background:var(--surface-2); color:var(--ink); }
.overflow-menu { position:absolute; top:calc(100% + 4px); right:0; background:var(--surface); border:1px solid var(--border-color); border-radius:8px; box-shadow:var(--shadow-lg); min-width:160px; overflow:hidden; opacity:0; transform:translateY(-4px); pointer-events:none; transition:opacity 0.12s, transform 0.12s; z-index:200; }
.overflow-menu.open { opacity:1; transform:translateY(0); pointer-events:all; }
.overflow-item { display:block; width:100%; padding:0.6rem 1rem; font-family:var(--font-ui); font-size:0.85rem; color:var(--ink-2); background:none; border:none; text-align:left; cursor:pointer; transition:background 0.1s; text-decoration:none; }
.overflow-item:hover { background:var(--surface-1); }
.overflow-item.danger { color:var(--crimson); }
.overflow-item.danger:hover { background:var(--crimson-bg); }

/* ── Entity portrait ── */
.char-layout { display:grid; grid-template-columns:1fr 1fr; gap:2.5rem; align-items:start; }
.char-portrait-wrap { position:relative; cursor:pointer; border-radius:var(--radius); overflow:hidden; }
.char-portrait { width:100%; aspect-ratio:1/1; background:var(--surface-2); display:flex; align-items:center; justify-content:center; font-size:5rem; }
.char-portrait img { width:100%; height:100%; object-fit:cover; display:block; }
.portrait-edit-btn { position:absolute; top:0.5rem; right:0.5rem; z-index:10; background:rgba(0,0,0,0.45); color:#fff; border:1px solid rgba(255,255,255,0.25); border-radius:6px; padding:0.35rem 0.4rem; cursor:pointer; display:flex; align-items:center; transition:background 0.15s, opacity 0.2s; backdrop-filter:blur(4px); opacity:0; }
.char-portrait-wrap:hover .portrait-edit-btn { opacity:1; }
.portrait-edit-btn:hover { background:rgba(0,0,0,0.65); }
.char-portrait-wrap.no-image .portrait-edit-btn { opacity:1; }
.below-portrait { margin-top:0; }

/* ── Mention popover ── */
.mention-link { color:var(--gold); text-decoration:none; font-weight:500; }
.mention-link:hover { text-decoration:underline; }
.mention-popover { background:var(--surface); border:var(--border); border-radius:8px; box-shadow:var(--shadow-lg); overflow:hidden; max-height:220px; overflow-y:auto; }
.mention-item { display:flex; align-items:center; gap:0.6rem; width:100%; padding:0.5rem 0.75rem; background:none; border:none; font-family:var(--font-ui); font-size:0.82rem; color:var(--ink-2); text-align:left; cursor:pointer; }
.mention-item.selected, .mention-item:hover { background:var(--surface-1); color:var(--ink); }
.mention-avatar { width:22px; height:22px; border-radius:4px; object-fit:cover; flex-shrink:0; }
.mention-avatar-placeholder { width:22px; height:22px; border-radius:4px; background:var(--surface-2); display:inline-flex; align-items:center; justify-content:center; font-size:0.7rem; flex-shrink:0; }

/* ── Entity body field ── */
.body-display { font-size:0.88rem; color:var(--ink-2); line-height:1.7; font-family:var(--font-ui); min-height:2rem; word-break:break-word; cursor:text; border-radius:4px; padding:0.1rem; transition:background 0.12s; }
.body-display:hover { background:var(--surface-1); }
.body-display p { margin:0 0 0.4em; }
.body-display a { color:var(--gold); text-decoration:none; font-weight:500; }
.body-display a:hover { text-decoration:underline; }
.body-empty { color:var(--ink-3); font-style:italic; }
.body-edit-area { display:none; }
.body-edit-area.open { display:block; }
/* ── Quill global border reset (beats Snow theme defaults) ── */
.ql-container.ql-snow { border:0px solid #ccc !important; }
.ql-toolbar.ql-snow { border:0px solid #ccc !important; }
/* ── Quill overrides — shared between description editor and note editors ── */
.body-edit-area .ql-toolbar.ql-snow,
.note-quill-wrap .ql-toolbar.ql-snow { border-bottom:1px solid var(--border-color); padding:0.4rem 0; font-family:var(--font-ui); }
.body-edit-area .ql-container.ql-snow,
.note-quill-wrap .ql-container.ql-snow { font-family:var(--font-ui); font-size:0.88rem; }
.body-edit-area .ql-editor { padding:0.6rem 0; min-height:80px; line-height:1.7; color:var(--ink); overflow-wrap:break-word; word-break:break-word; }
.note-quill-wrap .ql-editor { padding:0.4rem 0; min-height:60px; line-height:1.6; color:var(--ink); overflow-wrap:break-word; word-break:break-word; }
.body-edit-area .ql-editor.ql-blank::before,
.note-quill-wrap .ql-editor.ql-blank::before { color:var(--ink-3); font-style:italic; left:0; }
.body-edit-area .ql-editor p { margin:0 0 0.5em; }
.note-quill-wrap .ql-editor p { margin:0 0 0.35em; }
.body-edit-area .ql-editor a,
.body-edit-area .ql-editor a:visited,
.note-quill-wrap .ql-editor a,
.note-quill-wrap .ql-editor a:visited { color:var(--gold); text-decoration:none; font-weight:500; }
.body-edit-area .ql-editor a:hover,
.note-quill-wrap .ql-editor a:hover { text-decoration:underline; }
.body-edit-area .ql-snow .ql-stroke,
.note-quill-wrap .ql-snow .ql-stroke { stroke:var(--ink-3); }
.body-edit-area .ql-snow .ql-fill,
.note-quill-wrap .ql-snow .ql-fill { fill:var(--ink-3); }
.body-edit-area .ql-snow.ql-toolbar button:hover .ql-stroke,
.body-edit-area .ql-snow.ql-toolbar button.ql-active .ql-stroke,
.note-quill-wrap .ql-snow.ql-toolbar button:hover .ql-stroke,
.note-quill-wrap .ql-snow.ql-toolbar button.ql-active .ql-stroke { stroke:var(--ink); }
.body-edit-area .ql-snow.ql-toolbar button:hover .ql-fill,
.body-edit-area .ql-snow.ql-toolbar button.ql-active .ql-fill,
.note-quill-wrap .ql-snow.ql-toolbar button:hover .ql-fill,
.note-quill-wrap .ql-snow.ql-toolbar button.ql-active .ql-fill { fill:var(--ink); }
.body-edit-area .ql-snow .ql-picker,
.note-quill-wrap .ql-snow .ql-picker { color:var(--ink-3); }
.body-edit-area .ql-snow .ql-picker-label,
.note-quill-wrap .ql-snow .ql-picker-label { color:var(--ink-3); }
.body-edit-area .ql-snow .ql-picker-options,
.note-quill-wrap .ql-snow .ql-picker-options { background:var(--surface); border:var(--border); border-radius:6px; box-shadow:var(--shadow-lg); }

/* ── Linked entities grid ── */
.linked-section { margin-top:2rem; padding-top:1.5rem; border-top:var(--border); }
.linked-grid { display:flex; flex-wrap:wrap; gap:0.75rem; margin-top:0.75rem; }
.linked-card { display:flex; align-items:center; gap:0.6rem; padding:0.5rem 0.75rem; background:var(--surface); border:var(--border); border-radius:7px; text-decoration:none; color:inherit; transition:background 0.12s; font-family:var(--font-ui); font-size:0.82rem; color:var(--ink-2); }
.linked-card:hover { background:var(--surface-2); color:var(--ink); }
.linked-avatar { width:26px; height:26px; border-radius:4px; object-fit:cover; background:var(--surface-2); flex-shrink:0; display:flex; align-items:center; justify-content:center; font-size:0.7rem; overflow:hidden; }
.linked-avatar img { width:100%; height:100%; object-fit:cover; }

/* ── Entity section label + body textarea ── */
.section-label { font-family:var(--font-ui); font-size:0.68rem; font-weight:600; letter-spacing:0.1em; text-transform:uppercase; color:var(--ink-3); margin-bottom:0.5rem; }
.inline-body { font-size:0.88rem; color:var(--ink-2); line-height:1.7; width:100%; background:transparent; border:1px solid var(--border-color); border-radius:6px; outline:none; font-family:var(--font-ui); padding:0.6rem 0.75rem; resize:none; overflow:hidden; min-height:80px; display:block; transition:border-color 0.15s, background 0.15s; }
.inline-body:hover { background:var(--surface-1); }
.inline-body:focus { border-color:var(--gold); background:var(--surface-1); }

/* ── Note column header ── */
.notes-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:0.5rem; }
.notes-title { font-family:var(--font-ui); font-size:0.68rem; font-weight:600; letter-spacing:0.1em; text-transform:uppercase; color:var(--ink-3); }
.btn-add-note { font-family:var(--font-ui); font-size:0.75rem; font-weight:500; color:var(--ink-2); background:none; border:1px solid var(--border-color); border-radius:5px; padding:0.3rem 0.75rem; cursor:pointer; transition:border-color 0.15s, color 0.15s; }
.btn-add-note:hover { border-color:var(--ink-3); color:var(--ink); }

/* ── Note cards ── */
.note-card { border:1px solid var(--border-color); border-radius:8px; padding:0.9rem 1rem; margin-bottom:0.75rem; background:var(--surface); }
.note-card-private { background:var(--surface-1); }
.note-title-display { font-family:var(--font-ui); font-size:0.78rem; font-weight:700; color:var(--ink); margin-bottom:0.4rem; letter-spacing:0.01em; }
.note-content-display { font-family:var(--font-ui); font-size:0.85rem; line-height:1.6; color:var(--ink-2); overflow-wrap:break-word; word-break:break-word; }
.note-content-display a, .note-content-display a:visited { color:var(--gold); text-decoration:none; font-weight:500; }
.note-content-display a:hover { text-decoration:underline; }
.note-placeholder { font-family:var(--font-ui); font-size:0.82rem; color:var(--ink-3); font-style:italic; }
.note-edit-area { display:none; }
.note-edit-area.open { display:block; }
.note-edit-title { width:100%; background:transparent; border:none; border-bottom:1px solid var(--parchment-3); outline:none; font-family:var(--font-ui); font-size:0.82rem; font-weight:700; color:var(--ink); padding:0 0 0.3rem; margin-bottom:0.5rem; }
.note-edit-title:focus { border-color:var(--gold); }
.note-edit-content { width:100%; background:transparent; border:none; outline:none; font-family:var(--font-ui); font-size:0.85rem; color:var(--ink-2); min-height:60px; resize:none; overflow:hidden; line-height:1.6; }
.note-view-area { }
.note-footer { display:flex; align-items:center; gap:0.5rem; margin-top:0.75rem; padding-top:0.6rem; border-top:1px solid var(--parchment-3); flex-wrap:wrap; }
.note-footer-avatar { flex-shrink:0; }
.note-footer-meta { flex:1; min-width:0; line-height:1.4; }
.note-footer-name { font-family:var(--font-ui); font-size:0.7rem; font-weight:600; color:var(--ink-2); text-transform:uppercase; letter-spacing:0.04em; display:inline; }
.note-footer-time { font-family:var(--font-ui); font-size:0.7rem; color:var(--ink-3); display:inline; }
.note-footer-time::before { content:" · "; }
.note-footer-right { display:flex; align-items:center; gap:0.4rem; margin-left:auto; flex-shrink:0; }
.share-avatars { display:flex; align-items:center; }
.share-avatars .avatar { margin-left:-6px; border:2px solid var(--surface); }
.share-avatars .avatar:first-child { margin-left:0; }
.unshared-label { font-family:var(--font-ui); font-size:0.7rem; color:var(--ink-3); font-style:italic; }
.btn-share-small { font-family:var(--font-ui); font-size:0.7rem; color:var(--ink-3); background:none; border:1px solid var(--border-color); border-radius:4px; padding:0.15rem 0.5rem; cursor:pointer; transition:border-color 0.12s, color 0.12s; white-space:nowrap; }
.btn-share-small:hover { border-color:var(--ink-3); color:var(--ink); }
.note-edit-footer { display:flex; align-items:center; justify-content:space-between; margin-top:0.75rem; flex-wrap:wrap; gap:0.5rem; }
.priv-toggle-wrap { display:inline-flex; align-items:center; gap:0.6rem; cursor:pointer; user-select:none; }
.priv-toggle-wrap input[type=checkbox] { display:none; }
.priv-track { width:44px; height:26px; border-radius:13px; background:#d8d8d8; position:relative; transition:background 0.2s; flex-shrink:0; }
.priv-track::after { content:""; position:absolute; width:20px; height:20px; border-radius:50%; background:#5a5a5a; top:3px; left:3px; transition:left 0.2s, background 0.2s; }
.priv-toggle-wrap input:checked ~ .priv-track { background:#4a4a4a; }
.priv-toggle-wrap input:checked ~ .priv-track::after { left:21px; background:#fff; }
.priv-toggle-label { font-family:var(--font-ui); font-size:0.72rem; font-weight:600; color:var(--ink-2); text-transform:uppercase; letter-spacing:0.06em; }
.note-edit-actions { display:flex; gap:0.4rem; }
.btn-note-save { font-family:var(--font-ui); font-size:0.75rem; background:var(--ink); color:var(--surface); border:none; border-radius:5px; padding:0.3rem 0.65rem; cursor:pointer; }
.btn-note-cancel { font-family:var(--font-ui); font-size:0.75rem; background:none; color:var(--ink-3); border:1px solid var(--border-color); border-radius:5px; padding:0.3rem 0.65rem; cursor:pointer; }
.btn-note-delete { font-family:var(--font-ui); font-size:0.7rem; color:var(--crimson); background:none; border:none; cursor:pointer; padding:0.2rem 0; }
.btn-note-delete:hover { text-decoration:underline; }
.note-overflow-wrap { position:relative; margin-left:auto; flex-shrink:0; }
.note-overflow-btn { background:none; border:none; cursor:pointer; color:var(--ink-3); padding:0.15rem 0.25rem; border-radius:4px; display:flex; align-items:center; transition:background 0.12s, color 0.12s; }
.note-overflow-btn:hover { background:var(--surface-2); color:var(--ink); }
.note-overflow-menu { position:absolute; top:calc(100% + 2px); right:0; background:var(--surface); border:1px solid var(--border-color); border-radius:7px; box-shadow:var(--shadow-lg); min-width:120px; overflow:hidden; opacity:0; transform:translateY(-4px); pointer-events:none; transition:opacity 0.12s, transform 0.12s; z-index:200; }
.note-overflow-menu.open { opacity:1; transform:translateY(0); pointer-events:all; }
.note-overflow-item { display:block; width:100%; padding:0.5rem 0.85rem; font-family:var(--font-ui); font-size:0.82rem; color:var(--ink-2); background:none; border:none; text-align:left; cursor:pointer; transition:background 0.1s; }
.note-overflow-item:hover { background:var(--surface-1); }
.note-overflow-item.danger { color:var(--crimson); }
.note-overflow-item.danger:hover { background:var(--crimson-bg); }
.other-note-section { margin-top:1.25rem; }
.note-vis-tag { font-family:var(--font-ui); font-size:0.68rem; color:var(--ink-3); font-weight:400; letter-spacing:0.01em; }
.other-section-label { font-family:var(--font-ui); font-size:0.68rem; font-weight:700; letter-spacing:0.1em; text-transform:uppercase; color:var(--ink); margin-bottom:0.6rem; }
.share-picker { background:var(--surface); border:1px solid var(--border-color); border-radius:var(--radius); padding:1rem; }
.share-user-row { display:flex; align-items:center; gap:0.6rem; padding:0.4rem 0; }
.share-user-row label { font-family:var(--font-ui); font-size:0.85rem; color:var(--ink-2); cursor:pointer; flex:1; }
.add-note-form { border:1px solid var(--border-color); border-radius:8px; padding:0.9rem 1rem; margin-bottom:0.75rem; background:var(--surface-1); display:none; }
.add-note-form.open { display:block; }
.add-note-form-title { width:100%; background:transparent; border:none; border-bottom:1px solid var(--parchment-3); outline:none; font-family:var(--font-ui); font-size:0.82rem; font-weight:700; color:var(--ink); padding:0 0 0.3rem; margin-bottom:0.5rem; }
.add-note-form-title:focus { border-color:var(--gold); }
.add-note-form textarea { width:100%; background:transparent; border:none; outline:none; font-family:var(--font-ui); font-size:0.85rem; color:var(--ink); min-height:70px; resize:none; line-height:1.6; }
.add-note-controls { display:flex; align-items:center; justify-content:space-between; gap:0.75rem; margin-top:0.6rem; flex-wrap:wrap; }
.note-obscured { text-align:center; padding:0.5rem 0 0.25rem; }
.note-obscured-label { font-family:var(--font-ui); font-size:0.78rem; color:var(--ink-3); font-style:italic; display:block; margin-bottom:0.5rem; }
.btn-ask-share { font-family:var(--font-ui); font-size:0.75rem; font-weight:500; color:var(--ink-2); background:none; border:1px solid var(--border-color); border-radius:5px; padding:0.3rem 0.75rem; cursor:pointer; }

/* ── Entity page responsive ── */
@media(max-width:680px) {
  .char-layout { grid-template-columns:1fr; gap:0; }
  .char-portrait { aspect-ratio:4/3; }
  .below-portrait { margin-top:1.25rem; }
  .right-col { margin-top:1rem; }
  .back-link { margin-bottom:0.75rem; }
}

/* ── Secret entity styles ── */
.secret-card { opacity: 0.5; pointer-events: none; cursor: default; filter: grayscale(0.6); }
.secret-badge { display: inline-flex; align-items: center; gap: 0.25rem; font-family: var(--font-ui); font-size: 0.65rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.07em; color: var(--ink-3); background: var(--surface-2); border: 1px solid var(--border-color); border-radius: 4px; padding: 0.1rem 0.4rem; }
/* ── Deceased character treatment (portrait, campaign cards, dashboard) ── */
/* Low-saturation + a subtle film grain via an SVG turbulence overlay. Apply
   .deceased-media to any positioned image wrapper; it fades and grains the
   image within and hosts the corner tag. .deceased-filter is the image-only
   variant for tiny previews where a tag won't fit. */
.deceased-filter, .deceased-media img { filter: grayscale(0.55) saturate(0.35) contrast(0.97) brightness(0.99); }
.deceased-media { position: relative; }
.deceased-media::after { content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit; z-index: 1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.14; mix-blend-mode: multiply; }
.deceased-tag { position: absolute; top: 0.45rem; left: 0.45rem; z-index: 2; font-family: var(--font-ui); font-size: 0.6rem; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: #fff; background: rgba(24,22,20,0.82); padding: 0.18rem 0.42rem; border-radius: 4px; }

/* ── Character privacy indicators (owner-facing only) ────────────────────── */
/* "Hidden" card tag — same box as .deceased-tag, shown on the campaign roster and
   dashboard when nobody but the owner and campaign owner can see the character.
   Offset to the right so a character that is both deceased and hidden shows both.
   .hidden-media must go on the image wrapper: unlike the deceased treatment there's no
   filter to hang it off, and these wrappers are position:static by default, so without
   it the tag anchors to the page instead of the card. */
.hidden-media { position: relative; }
.hidden-tag { position: absolute; top: 0.45rem; right: 0.45rem; z-index: 2; font-family: var(--font-ui); font-size: 0.6rem; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase; color: #fff; background: rgba(24,22,20,0.82); padding: 0.18rem 0.42rem; border-radius: 4px; }

/* Crosshatch on the rules section when only the owner and campaign owner can read it.
   A reminder for whoever configures sharing — restricted viewers never see this, they
   get no rules section at all. Sits above the card but below any interactive control. */
.rules-exclusive { position: relative; }
.rules-exclusive::after { content: ""; position: absolute; inset: 0; pointer-events: none; border-radius: inherit; z-index: 1;
  background-image: repeating-linear-gradient(45deg, var(--border-color) 0, var(--border-color) 1px, transparent 1px, transparent 9px);
  opacity: 0.3; }
.rules-exclusive > * { position: relative; z-index: 2; }
.rules-exclusive-note { font-family: var(--font-ui); font-size: 0.62rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); white-space: nowrap; }

/* "Hidden from campaign" banner at the top of a character page. */
.hidden-banner { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; padding: 0.55rem 0.8rem; border: var(--border); border-radius: 6px; background: var(--surface-1); font-family: var(--font-ui); font-size: 0.78rem; color: var(--ink-2); }
.hidden-banner strong { font-weight: 600; color: var(--ink); }
.secret-btn { display: inline-flex; align-items: center; gap: 0.35rem; background: transparent; border: 1.5px solid var(--border-color); border-radius: 6px; padding: 0.45rem 0.85rem; font-family: var(--font-ui); font-size: 0.8rem; color: var(--ink-3); cursor: pointer; transition: border-color 0.15s, color 0.15s; }
.secret-btn:hover { border-color: var(--gold); color: var(--gold); }
.secret-btn.is-secret { border-color: var(--gold); color: var(--gold); }

/* ── Gallery ── */
.gallery-section { margin-top: 1.5rem; padding-top: 24px; border-top: 1px solid var(--border-color); }
.gallery-mobile-add { display: none; }
.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 0.5rem; margin-top: 0.75rem; }
.gallery-thumb { border: 0; padding: 0; border-radius: 6px; overflow: hidden; background: var(--surface-2); aspect-ratio: 1/1; cursor: pointer; transition: opacity 0.15s; }
.gallery-thumb:hover { opacity: 0.85; }
.gallery-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery-add-tile { background: transparent; border: 1.5px dashed var(--border-color); display: flex; align-items: center; justify-content: center; color: var(--ink-3); transition: border-color 0.15s, color 0.15s; overflow: visible; }
.gallery-add-tile:hover { border-color: var(--ink-3); color: var(--ink-2); opacity: 1; }
.gallery-thumb.nsfw-blurred { position: relative; }
.gallery-thumb.nsfw-blurred img { filter: blur(12px); }
.nsfw-label { position: absolute; bottom: 6px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.55); color: #fff; font-family: var(--font-ui); font-size: 0.65rem; font-weight: 700; padding: 0.15rem 0.45rem; border-radius: 3px; letter-spacing: 0.07em; pointer-events: none; white-space: nowrap; }
.gallery-empty { font-family: var(--font-ui); font-size: 0.82rem; color: var(--ink-3); padding: 0.5rem 0; }
@media(max-width:680px) { .gallery-mobile-add { display: inline-flex; } .gallery-add-tile { display: none; } }

/* ── Gallery set badge ── */
.gallery-set-badge { position:absolute; top:0; left:0; color:#fff; background:var(--dark,#1a1a1a); border-radius:0 0 6px 0; width:22px; height:22px; display:flex; align-items:center; justify-content:center; pointer-events:none; }
/* Top-RIGHT so it never collides with the set badge on a GM-only set. Gold
   rather than crimson: this marks privacy, not danger. */
.gallery-gm-badge { position:absolute; top:0; right:0; color:#222; background:var(--gold); border-radius:0 0 0 6px; padding:0 0.35rem; height:18px; display:flex; align-items:center; font-family:var(--font-ui); font-size:0.6rem; font-weight:700; letter-spacing:0.06em; pointer-events:none; }
@keyframes gm-spin { to { transform: rotate(360deg); } }
.btn-spinner { animation: gm-spin 0.7s linear infinite; display:block; }

/* Lightbox */
.gallery-lightbox { position: fixed; inset: 0; z-index: 2000; display: none; align-items: center; justify-content: center; }
.lb-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.88); }
.lb-center { position:relative; z-index:1; display:flex; flex-direction:column; align-items:center; max-width:min(90vw,900px); }
.lb-content { display: flex; flex-direction: column; align-items: center; max-height:calc(80vh - 90px); }
.lb-img { max-width: 100%; max-height: calc(75vh - 90px); object-fit: contain; border-radius: 4px; display: block; }
.lb-desc { color: rgba(255,255,255,0.75); font-family: var(--font-ui); font-size: 0.82rem; margin-top: 0.75rem; text-align: center; max-width: 600px; }
.lb-set-caption { color:rgba(255,255,255,0.45); font-family:var(--font-ui); font-size:0.78rem; margin-top:0.5rem; text-align:center; max-width:600px; font-style:italic; }
.lb-filmstrip { display:flex; gap:0.4rem; overflow-x:auto; max-width:min(90vw,900px); padding-bottom:0.25rem; scrollbar-width:thin; position:fixed; bottom:1rem; left:50%; transform:translateX(-50%); z-index:2; }
.lb-strip-item { flex-shrink:0; width:60px; height:60px; border-radius:5px; overflow:hidden; border:2px solid transparent; cursor:pointer; background:rgba(255,255,255,0.08); padding:0; transition:border-color 0.12s; }
.lb-strip-item img { width:100%; height:100%; object-fit:cover; display:block; }
.lb-strip-item.active { border-color:var(--gold); }
.lb-strip-item:hover:not(.active) { border-color:rgba(255,255,255,0.35); }
.lb-nav { position: fixed; top: 50%; transform: translateY(-50%); z-index: 2; background: rgba(0,0,0,0.45); border: none; border-radius: 50%; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; color: #fff; cursor: pointer; transition: background 0.15s; }
.lb-nav:hover { background: rgba(0,0,0,0.72); }
.lb-prev { left: 1rem; }
.lb-next { right: 1rem; }
.lb-top-right { position: fixed; top: 1rem; right: 1rem; z-index: 2; display: flex; align-items: center; gap: 0.4rem; }
.lb-close { background: rgba(0,0,0,0.45); border: none; border-radius: 50%; width: 36px; height: 36px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: #fff; cursor: pointer; transition: background 0.15s; }
.lb-close:hover { background: rgba(0,0,0,0.72); }
.lb-edit { background: rgba(0,0,0,0.45); border: none; border-radius: 50%; width: 36px; height: 36px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; color: #fff; cursor: pointer; transition: background 0.15s; }
.lb-edit:hover { background: rgba(0,0,0,0.72); }
.lb-assocs { display: flex; align-items: center; gap: 0.3rem; flex-wrap: wrap; justify-content: flex-end; }
.lb-assoc-chip { display: inline-flex; align-items: center; gap: 0.3rem; background: rgba(0,0,0,0.5); border: 1px solid rgba(255,255,255,0.2); border-radius: 100px; padding: 0.2rem 0.55rem 0.2rem 0.25rem; font-family: var(--font-ui); font-size: 0.72rem; color: #fff; text-decoration: none; backdrop-filter: blur(4px); transition: background 0.15s; white-space: nowrap; }
.lb-assoc-chip:hover { background: rgba(0,0,0,0.72); }
.lb-assoc-avatar { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; flex-shrink: 0; }
.lb-assoc-initial { width: 20px; height: 20px; border-radius: 50%; background: rgba(255,255,255,0.15); display: inline-flex; align-items: center; justify-content: center; font-size: 0.6rem; flex-shrink: 0; }

/* Gallery modal */
.gallery-modal-box { background: var(--surface); border-radius: 10px; width: min(480px, 96vw); display: flex; flex-direction: column; margin: auto; }
.modal-header { background: var(--dark); color: #fff; padding: 0.9rem 1.2rem; display: flex; align-items: center; justify-content: space-between; border-radius: 10px 10px 0 0; }
.modal-title { font-family: var(--font-ui); font-size: 0.88rem; font-weight: 600; letter-spacing: 0.02em; }
.modal-close { background: none; border: none; color: rgba(255,255,255,0.6); font-size: 1rem; cursor: pointer; line-height: 1; padding: 0.2rem; }
.modal-close:hover { color: #fff; }
.gm-drop-zone { margin: 1rem; border: 2px dashed var(--border-color); border-radius: 8px; min-height: 140px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: border-color 0.15s; overflow: hidden; }
.gm-drop-zone:hover, .gm-drop-zone.drag-over { border-color: var(--gold); }
.gm-placeholder { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; color: var(--ink-3); font-family: var(--font-ui); font-size: 0.82rem; }
.gm-browse-label { color: var(--gold); cursor: pointer; text-decoration: underline; }
.gm-preview { max-width: 100%; max-height: 280px; object-fit: contain; display: block; border-radius: 6px; }
.gm-desc-input { margin: 0 1rem 0.75rem; padding: 0.5rem 0.65rem; border: 1px solid var(--border-color); border-radius: 6px; font-family: var(--font-ui); font-size: 0.82rem; color: var(--ink); background: var(--surface); resize: vertical; outline: none; transition: border-color 0.15s; }
.gm-desc-input:focus { border-color: var(--gold); }
.gm-assoc-section { margin: 0 1rem 0.75rem; }
.gm-assoc-label { font-family: var(--font-ui); font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-3); margin-bottom: 0.45rem; }
.gm-assoc-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.45rem; min-height: 0; }
.gm-tag { display: inline-flex; align-items: center; gap: 0.35rem; border: 1px solid var(--gold); background: var(--gold-bg); border-radius: 100px; padding: 0.22rem 0.5rem 0.22rem 0.3rem; font-family: var(--font-ui); font-size: 0.78rem; color: var(--ink); }
.gm-tag-remove { background: none; border: none; color: var(--ink-3); cursor: pointer; font-size: 0.65rem; line-height: 1; padding: 0; margin-left: 0.1rem; }
.gm-tag-remove:hover { color: var(--crimson); }
.gm-entity-search { width: 100%; padding: 0.4rem 0.6rem; border: 1px solid var(--border-color); border-radius: 6px; font-family: var(--font-ui); font-size: 0.82rem; color: var(--ink); background: var(--surface); outline: none; transition: border-color 0.15s; }
.gm-entity-search:focus { border-color: var(--gold); }
.gm-entity-results { position: absolute; background: var(--surface); border: var(--border); border-radius: 6px; box-shadow: var(--shadow-lg); z-index: 10; overflow: hidden; }
.gm-entity-result { display: flex; align-items: center; gap: 0.5rem; width: 100%; text-align: left; padding: 0.45rem 0.75rem; font-family: var(--font-ui); font-size: 0.82rem; color: var(--ink); background: none; border: none; cursor: pointer; }
.gm-entity-result:hover { background: var(--surface-2); }
.gm-assoc-section { position: relative; }
.gm-quick-assocs { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.45rem; margin-bottom: 0.5rem; }
.gm-quick-assoc-btn { display: inline-flex; align-items: center; gap: 0.35rem; background: var(--surface-1); border: 1px solid var(--border-color); border-radius: 100px; padding: 0.22rem 0.65rem 0.22rem 0.3rem; font-family: var(--font-ui); font-size: 0.78rem; color: var(--ink-2); cursor: pointer; transition: border-color 0.12s, background 0.12s, color 0.12s; }
.gm-quick-assoc-btn:hover { border-color: var(--gold); background: var(--gold-bg); color: var(--ink); }
.gm-quick-assoc-btn.selected { border-color: var(--gold); background: var(--gold-bg); color: var(--ink); }
.gm-qa-avatar { width: 20px; height: 20px; border-radius: 50%; object-fit: cover; flex-shrink: 0; display: inline-block; vertical-align: middle; }
.gm-qa-initial { background: var(--surface-2); display: inline-flex; align-items: center; justify-content: center; font-size: 0.6rem; font-family: var(--font-ui); color: var(--ink-3); }
.gm-actions { display: flex; align-items: center; justify-content: space-between; padding: 0.85rem 1rem; border-top: 1px solid var(--border-color); margin-top: auto; }
.btn-cancel { background: none; border: 1px solid var(--border-color); border-radius: 6px; padding: 0.45rem 0.9rem; font-family: var(--font-ui); font-size: 0.82rem; color: var(--ink-2); cursor: pointer; }
.btn-cancel:hover { border-color: var(--ink-3); }
.btn-danger-sm { background: none; border: 1px solid var(--crimson); border-radius: 6px; padding: 0.45rem 0.9rem; font-family: var(--font-ui); font-size: 0.82rem; color: var(--crimson); cursor: pointer; }
.btn-danger-sm:hover { background: var(--crimson); color: #fff; }
.btn-primary.gm-save { padding: 0.45rem 1rem; width: auto; background: var(--gold); color: #111; }
.btn-primary.gm-save:hover { background: var(--gold-2, #c9971f); }

/* ── Gallery modal: single/set mode toggle ── */
.gm-mode-toggle { display:flex; margin:1rem 1rem 0.75rem; border:1px solid var(--border-color); border-radius:7px; overflow:hidden; }
.gm-mode-btn { flex:1; background:none; border:none; padding:0.5rem 0; font-family:var(--font-ui); font-size:0.82rem; color:var(--ink-3); cursor:pointer; transition:background 0.12s,color 0.12s; }
.gm-mode-btn.active { background:var(--surface-2); color:var(--ink); font-weight:600; }

/* ── Gallery modal: set image grid ── */
.gm-set-grid { display:flex; flex-wrap:wrap; gap:0.5rem; margin:0 1rem 0.75rem; }
.gm-set-grid-item { width:72px; height:72px; border-radius:6px; border:2px solid transparent; overflow:visible; cursor:pointer; flex-shrink:0; padding:0; background:var(--surface-2); position:relative; }
.gm-set-grid-item img { width:100%; height:100%; object-fit:cover; display:block; pointer-events:none; border-radius:4px; }
.gm-set-grid-item.selected { border-color:var(--gold); }
.gm-set-grid-item.drag-target { border-color:var(--gold); opacity:0.7; }
.gm-set-grid-remove { position:absolute; top:-6px; right:-6px; width:20px; height:20px; border-radius:50%; background:rgba(20,20,20,0.85); color:#fff; border:1.5px solid rgba(255,255,255,0.25); cursor:pointer; display:flex; align-items:center; justify-content:center; font-size:10px; line-height:1; z-index:2; padding:0; }
.gm-set-grid-add { width:72px; height:72px; border-radius:6px; border:2px dashed var(--ink-3); display:flex; align-items:center; justify-content:center; cursor:pointer; background:none; color:var(--ink-3); flex-shrink:0; transition:opacity 0.1s; }
.gm-set-grid-add:hover { border-color:var(--gold); color:var(--gold); }
.gm-set-grid.dragging .gm-set-grid-add { display:none; }


/* ── Assoc sections (linked pages: characters / things / locations) ── */
.loc-section { margin-top:1.5rem; padding-top:24px; border-top:1px solid var(--border-color); margin-bottom:1.5rem; }
.loc-modal-item { display:flex; align-items:center; gap:0.75rem; padding:0.6rem 0.75rem; border-radius:7px; cursor:pointer; transition:background 0.1s; }
.loc-modal-item:hover { background:var(--surface-2); }
.loc-modal-item.selected { background:var(--surface-2); }
.loc-modal-check { margin-left:auto; color:var(--gold); font-size:0.9rem; opacity:0; }
.loc-modal-item.selected .loc-modal-check { opacity:1; }
.assoc-search { width:100%; padding:0.45rem 0.75rem; border:1px solid var(--border-color); border-radius:6px; font-family:var(--font-ui); font-size:0.85rem; color:var(--ink); background:var(--surface); outline:none; transition:border-color 0.15s; box-sizing:border-box; margin-bottom:0.5rem; }
.assoc-search:focus { border-color:var(--gold); }
.manage-short { display:none; }
@media(max-width:500px) { .manage-full { display:none; } .manage-short { display:inline; } }

/* ── Entity card grid (campaign page, place locations, etc.) ── */
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:1.5rem; max-width:calc(5 * 220px + 4 * 1.5rem); }
.char-card { background:var(--surface); border-radius:var(--radius); overflow:hidden; box-shadow:var(--shadow); border:var(--border); transition:box-shadow 0.2s,transform 0.2s; display:flex; flex-direction:column; text-decoration:none; color:inherit; }
.char-card:hover { box-shadow:var(--shadow-lg); transform:translateY(-2px); }
.char-img { width:100%; aspect-ratio:1/1; object-fit:cover; background:var(--surface-2); display:flex; align-items:center; justify-content:center; font-size:3.5rem; color:var(--ink-3); flex-shrink:0; overflow:hidden; }
.char-img img { width:100%; height:100%; object-fit:cover; display:block; }
.char-body { padding:0.85rem 1rem 1rem; flex:1; display:flex; flex-direction:column; }
.char-name { font-size:1rem; font-weight:700; color:var(--ink); margin-bottom:0.2rem; font-family:var(--font-display); }
.char-subtitle { font-family:var(--font-ui); font-size:0.78rem; color:var(--ink-3); line-height:1.5; flex:1; overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }
.char-creator { display:flex; align-items:center; gap:0.4rem; margin-top:0.65rem; padding-top:0.65rem; border-top:var(--border); }
.char-creator-name { font-family:var(--font-ui); font-size:0.72rem; color:var(--ink-3); }
.grid.compact { grid-template-columns:repeat(auto-fill,minmax(175px,1fr)); }
.grid.compact .char-name { font-size:0.88rem; }
.grid.compact .char-subtitle { font-size:0.72rem; }
.grid.compact .char-img { font-size:2.8rem; }
@media(max-width:550px) {
  .grid { grid-template-columns:1fr; gap:0.75rem; }
  .char-card { flex-direction:row; }
  .char-img { width:88px; flex-shrink:0; border-radius:0; }
  .char-creator { display:none; }
}

/* ── Skeleton loading ── */
.skel { background: var(--surface-2); border-radius: 4px; display: block; }
#skeleton { animation: none; }
#content { transition: opacity 0.15s ease; }

/* ── Universal header search ── */
/* Shared by the reference <button> and the Encounters <a>, hence the reset of
   link decoration — both render as the same bare icon in the header bar. */
.nav-ref-btn { background: none; border: none; color: rgba(255,255,255,0.75); cursor: pointer; padding: 0.4rem; align-items: center; justify-content: center; border-radius: 6px; transition: background 0.15s, color 0.15s; line-height: 0; text-decoration: none; }
.nav-ref-btn:hover { background: rgba(255,255,255,0.08); color: #fff; }
.header-search { position: relative; display: flex; align-items: center; }
.header-search-btn { display: none; background: none; border: none; color: rgba(255,255,255,0.75); cursor: pointer; padding: 0.4rem; align-items: center; justify-content: center; border-radius: 6px; transition: background 0.15s; }
.header-search-btn:hover { background: rgba(255,255,255,0.08); }
.header-search-bar { display: flex; align-items: center; gap: 0.5rem; width: 200px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.14); border-radius: 8px; padding: 0.35rem 0.6rem; color: rgba(255,255,255,0.5); transition: width 0.15s; }
.header-search-bar:focus-within { border-color: rgba(255,255,255,0.3); }
.header-search-input { flex: 1; min-width: 0; background: none; border: none; outline: none; color: #fff; font-family: var(--font-ui); font-size: 0.82rem; }
.header-search-input::placeholder { color: rgba(255,255,255,0.4); }
.header-search-clear { display: none; align-items: center; justify-content: center; background: none; border: none; color: rgba(255,255,255,0.5); cursor: pointer; font-size: 0.8rem; padding: 0.1rem; flex-shrink: 0; }
.header-search-clear:hover { color: #fff; }
.header-search-bar.has-value .header-search-clear { display: flex; }

.header-search-results { display: none; position: absolute; top: calc(100% + 8px); right: 0; width: 340px; max-width: calc(100vw - 2rem); max-height: 70vh; overflow-y: auto; background: var(--surface); border: var(--border); border-radius: 10px; box-shadow: var(--shadow-lg); padding: 0.4rem 0; z-index: 150; }
.header-search-results.open { display: block; }
.header-search-group + .header-search-group { border-top: var(--border); margin-top: 0.3rem; padding-top: 0.3rem; }
.header-search-group-label { font-family: var(--font-ui); font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--ink-3); padding: 0.3rem 0.9rem 0.2rem; }
.header-search-row { display: flex; align-items: center; gap: 0.6rem; width: 100%; padding: 0.45rem 0.9rem; background: none; border: none; text-align: left; cursor: pointer; font-family: var(--font-ui); font-size: 0.84rem; color: var(--ink); transition: background 0.1s; }
.header-search-row:hover { background: var(--surface-1); }
.header-search-avatar { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; background: var(--surface-2); display: flex; align-items: center; justify-content: center; font-size: 0.7rem; flex-shrink: 0; }
.header-search-row-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.header-search-row-meta { flex-shrink: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.7rem; color: var(--ink-3); }
.header-search-empty { padding: 0.9rem; font-family: var(--font-ui); font-size: 0.82rem; color: var(--ink-3); text-align: center; }

@media(max-width:680px) {
  .header-search-bar { display: none; }
  .header-search-btn { display: flex; }
  .header-search.open { position: absolute; inset: 0; background: var(--dark); z-index: 20; padding: 0 2rem; }
  html.dark .header-search.open { background: #111111; }
  .header-search.open .header-search-btn { display: none; }
  .header-search.open .header-search-bar { display: flex; width: 100%; }
  .header-search.open .header-search-results { left: 2rem; right: 2rem; width: auto; max-width: none; }
}

/* ── Catalog info modal (universal search results) ── */
/* Entry name, at the top of every card, same treatment as a top-level
   Reference menu row (.ref-home-label) — the breadcrumb/modal-header
   already places it in the tree, so this is the visual anchor for the
   card's own content rather than metadata. */
.search-info-title { font-family: var(--font-display); font-size: 1.22rem; font-weight: 600; letter-spacing: 0.01em; color: var(--ink); margin: 0 0 0.9rem; }
/* Psychic Phenomena/Perils: the roll range, inline before the title in the
   same serif treatment but a lighter shade — it's the thing you actually
   look this card up by, so it reads as part of the heading, not a stat. */
.search-info-roll { color: var(--ink-3); margin-right: 0.5em; }
/* Trailing footer: the small grey meta line + edition switcher (when there
   is one), demoted to the bottom now that the title/breadcrumb carry the
   entry's identity. Sits after .search-info-spacer, a flex item that grows
   to soak up any leftover height (pushing the footer flush to the card's
   bottom edge on short content) but never shrinks below its 1rem basis —
   so once content is long enough to need scrolling, the footer still gets
   a guaranteed 1rem of breathing room instead of touching the last line of
   content. (A plain margin-top:auto on the footer itself can't guarantee
   that floor — it collapses to 0 the moment there's no space to distribute.) */
.search-info-spacer { flex: 1 0 1rem; }
.search-info-footer { padding-top: 1rem; border-top: var(--border); }
.search-info-meta { font-family: var(--font-ui); font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.07em; color: var(--ink-3); margin: 0; }
.search-info-stats { margin-bottom: 0.9rem; }
.search-info-stat-row { display: flex; justify-content: space-between; padding: 0.35rem 0; font-family: var(--font-ui); font-size: 0.82rem; border-bottom: var(--border); }
.search-info-stat-row span:first-child { color: var(--ink-3); }
.search-info-stat-row span:last-child { color: var(--ink); font-weight: 600; text-align: right; }
.search-info-desc { font-family: var(--font-ui); font-size: 0.85rem; line-height: 1.6; color: var(--ink-2); }
/* Subsection heading within a consolidated "long page" entry (a `sections`
   array — several book subsections read as one continuous scroll). Later
   sections get a divider + extra top space so a new subsection reads as a
   clear break while scrolling; the one right after the title needs none. */
/* A rules page's own illustrations (see pageArtHTML). Centred and capped rather
   than full-bleed: these are cut-out figures off a book page, and stretching one
   to the width of a 1100px modal magnifies the scan's grain. */
.ref-pageart { margin: 1rem 0 0.4rem; display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center; }
.ref-pageart figure { margin: 0; max-width: min(100%, 420px); }
.ref-pageart img { display: block; width: 100%; height: auto; border-radius: var(--radius); box-shadow: var(--shadow); }
.ref-pageart figcaption { margin-top: 0.4rem; font-family: var(--font-ui); font-size: 0.78rem; font-style: italic; color: var(--ink-3); text-align: center; }
/* An item's `notes` — the line the books print under a weapon's row ("Used
   with: Shotgun, Pump Action Shotgun…", a daemon weapon's attributes). Set
   apart from the description because it is a caption on the profile above it,
   not part of the entry's prose. 146 of the catalogue's items carry one and
   none of them reached the card until now. */
.si-note { font-size: 0.82rem; color: var(--ink-2); margin-top: -0.3rem; }

/* A weapon's special qualities, spelled out under its profile. The kit modal on
   the character sheet has always printed these; the reference card showed only
   the Special row, so the same weapon read thinner in Reference than in a kit.
   Set in the UI face at label size — this is rules text about the profile
   above, not part of the entry's own prose. */
.si-quals-head { margin-top: 1.1rem; }
/* The same label treatment over the prose beneath, so the two blocks read as
   two blocks. Tighter above than the qualities head: this one sits under a list
   that has already put air below itself, and .search-info-desc follows
   immediately. */
.si-desc-head { margin-top: 1rem; margin-bottom: 0.3rem; }
.si-qual { font-family: var(--font-ui); font-size: 0.82rem; line-height: 1.6; color: var(--ink-2); margin-top: 0.45rem; }
.si-qual strong { color: var(--ink); }

.search-info-sectionhead { font-family: var(--font-display); font-size: 1.02rem; font-weight: 700; color: var(--ink); margin-top: 1.6rem; padding-top: 1.2rem; border-top: var(--border); }
.search-info-title + .search-info-sectionhead { margin-top: 0; padding-top: 0; border-top: none; }
/* A page whose first section repeats the page name as its heading (Corruption,
   Mutations, Movement) printed the same words twice, one line apart. Hidden
   rather than dropped from the DOM: the <h5> still carries the section's anchor
   id, so refJumpToSection and a "See DH1e version" cross-link still resolve. */
.search-info-sectionhead-dupe { position: absolute; width: 1px; height: 1px; margin: 0; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border-top: none; }
/* "See DH1e version" cross-link: a Condition/Circumstance/Action whose DH1e
   write-up lives as a section inside a consolidated Gameplay page instead. */
.si-seealso { display: block; margin-top: 1.2rem; padding-top: 0.9rem; border-top: var(--border); background: none; border-left: none; border-right: none; border-bottom: none; font-family: var(--font-ui); font-size: 0.82rem; font-weight: 600; color: var(--gold); cursor: pointer; text-align: left; }
.si-seealso:hover { text-decoration: underline; }

/* ── NPC profile card ── */
/* A book statblock rather than a single rules entry: characteristic tables
   across the top, then one chip per row. Selection uses a high-contrast B&W
   treatment rather than the gold accent — on a card this dense, gold on every
   open chip and panel edge became the loudest thing on screen. */
.npc-titlerow { display: flex; align-items: flex-start; gap: 0.5rem; }
.npc-titlerow .search-info-title { flex: 1; min-width: 0; }

.npc-overflow { position: relative; flex-shrink: 0; }
.npc-overflow-menu { display: none; position: absolute; right: 0; top: 100%; z-index: 20; min-width: 12rem; padding: 0.3rem; border: var(--border); border-radius: 8px; background: var(--surface); box-shadow: var(--shadow); }
.npc-overflow-menu.open { display: block; }
.npc-overflow-menu button { display: block; width: 100%; padding: 0.5rem 0.6rem; border: none; border-radius: 5px; background: none; text-align: left; cursor: pointer; font-family: var(--font-ui); font-size: 0.84rem; color: var(--ink); }
.npc-overflow-menu button:hover { background: var(--surface-1); }

.npc-flavour { margin-bottom: 0.9rem; font-style: italic; }

/* Tables scroll inside their own box on narrow screens so nine columns never
   force the modal body to scroll sideways. */
/* overflow-y: hidden is load-bearing: setting only overflow-x makes the other
   axis compute to `auto`, which reserves a scrollbar gutter even when the table
   fits. The scrollbar is then styled explicitly because the app sets no
   color-scheme, so a default one renders as a bright white bar across the
   bottom of every table in dark mode. Thin + themed works in both modes. */
.npc-profile-wrap { overflow-x: auto; overflow-y: hidden; margin-bottom: 0.7rem; border: var(--border); border-radius: 6px; scrollbar-width: thin; scrollbar-color: var(--ink-3) transparent; }
.npc-profile-wrap::-webkit-scrollbar { height: 6px; }
.npc-profile-wrap::-webkit-scrollbar-track { background: transparent; }
.npc-profile-wrap::-webkit-scrollbar-thumb { background: var(--ink-3); border-radius: 3px; }
.npc-profile { width: 100%; border-collapse: collapse; font-family: var(--font-ui); text-align: center; }
/* No text-transform: the books print mixed-case abbreviations (Ag, Int, Per,
   Fel) and the card reproduces the printed profile as-is. */
.npc-profile th { padding: 0.35rem 0.4rem; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; white-space: nowrap; color: var(--ink-3); background: var(--surface-1); border-bottom: var(--border); }
.npc-profile td { padding: 0.42rem 0.4rem; font-size: 0.95rem; font-weight: 600; white-space: nowrap; color: var(--ink); font-variant-numeric: tabular-nums; }
.npc-profile th + th, .npc-profile td + td { border-left: var(--border); }
/* Unnatural Characteristic Bonus, printed small in the cell's top-left corner.
   The headroom is added only when the table actually carries one, so every
   profile without an Unnatural quality keeps exactly its old row height. */
.npc-profile.has-unnatural td { position: relative; padding-top: 0.75rem; }
.npc-unnatural { position: absolute; top: 1px; left: 3px; font-size: 0.6rem; font-weight: 400; line-height: 1; color: var(--ink-3); pointer-events: none; }

.npc-threat { margin: 0 0 0.8rem; font-family: var(--font-ui); font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-3); }
.npc-threat-link { padding: 0; background: none; border: none; cursor: pointer; font: inherit; letter-spacing: inherit; color: var(--ink-3); text-decoration: underline; text-underline-offset: 2px; }
.npc-threat-link:hover { color: var(--ink); }

.npc-sec { margin-bottom: 0.8rem; }
.npc-sec-label { font-family: var(--font-ui); font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-3); margin-bottom: 0.35rem; }
/* One chip per row: these labels are long ("Speak Language (High Gothic, Low
   Gothic) +10 (Int)") and wrapping them into a shared flex line made the list
   hard to scan. A column also gives each chip's expanded card an unambiguous
   place to open — directly under its own chip. */
.npc-sec-body { display: flex; flex-direction: column; align-items: stretch; gap: 0.25rem; }
.npc-row { display: flex; flex-direction: column; align-items: flex-start; }
.npc-tag { display: inline-block; max-width: 100%; padding: 0.25rem 0.5rem; border-radius: 5px; background: var(--surface-1); border: 1px solid transparent; font-family: var(--font-ui); font-size: 0.78rem; line-height: 1.35; text-align: left; color: var(--ink-2); }
/* Linked entries are visibly interactive; unlinked ones stay plain text so a
   missing catalog entry reads as ordinary profile text, not a broken control. */
.npc-tag-link { cursor: pointer; color: var(--ink); border-color: var(--border-c, rgba(128,128,128,0.35)); }
.npc-tag-link:hover { border-color: var(--ink-2); }
/* Selected: full inversion. Black-on-white in dark mode, white-on-black in
   light — maximum contrast without spending the gold accent on it. */
.npc-tag-link.on { background: var(--ink); border-color: var(--ink); color: var(--surface); font-weight: 600; }

.npc-note, .npc-none { font-family: var(--font-ui); font-size: 0.8rem; font-style: italic; color: var(--ink-3); line-height: 1.5; }

/* Prose blocks on a card: the boxed blurbs printed beside a profile, and the
   printed lines that state a choice across entries. Reads as body text, not as
   another row of chips. */
.npc-prose { font-family: var(--font-ui); font-size: 0.8rem; line-height: 1.55; color: var(--ink-2); }
.npc-prose p { margin: 0 0 0.5rem; }
.npc-prose p:last-child { margin-bottom: 0; }
/* A note block is a callout, so it gets the same left rule as an expanded
   entry — but in a muted weight, since it is source text rather than a
   selection. */
.npc-sec-note { margin-top: 0.9rem; padding-left: 0.7rem; border-left: 2px solid var(--border-c, rgba(128,128,128,0.35)); }
/* Inline link inside prose: underlined text, NOT a chip — a chip mid-sentence
   breaks the line rhythm. Selected state matches the chips' black/white
   inversion so both readings of "open" look like the same idea. */
.npc-inline-link { display: inline; padding: 0; border: 0; background: none; font: inherit; color: var(--ink);
  text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 0.15em; cursor: pointer; }
.npc-inline-link:hover { text-decoration-style: solid; }
.npc-inline-link.on { background: var(--ink); color: var(--surface); text-decoration: none; border-radius: 3px; padding: 0 0.2em; font-weight: 600; }

/* Expanded rules entry, inline under its own chip. The left rule matches the
   selected chip's fill so the two read as one object. */
.npc-detail { align-self: stretch; margin: 0.3rem 0 0.45rem; padding: 0.7rem 0.85rem; border-left: 2px solid var(--ink); border-radius: 0 6px 6px 0; background: var(--surface-1); }
.npc-detail .search-info-title { font-size: 1.02rem; margin-bottom: 0.5rem; }
.npc-detail .search-info-desc { font-size: 0.82rem; }
/* The footer spacer exists to push meta to the bottom of a tall card; inside a
   nested panel it would just add dead space. */
.npc-detail .search-info-spacer { display: none; }
.npc-detail .search-info-footer { margin-top: 0.6rem; }
/* Floating hover peek. Lives on <body> so the reference modal's scroll container
   can't clip it, and sits above both the reference modal (990) and the detail
   popup (1000). pointer-events:none makes it a true peek: it never steals the
   cursor, and moving anywhere at all dismisses it. Height is capped rather than
   scrollable for the same reason — click the chip for the readable version. */
.npc-peek {
  position: fixed; z-index: 1200; pointer-events: none;
  width: min(23rem, calc(100vw - 2rem)); max-height: 55vh; overflow: hidden;
  padding: 0.75rem 0.9rem;
  border: var(--border); border-left: 2px solid var(--ink); border-radius: 8px;
  background: var(--surface); box-shadow: var(--shadow);
}
.npc-peek .search-info-title { font-size: 1rem; margin-bottom: 0.45rem; }
.npc-peek .search-info-desc { font-size: 0.82rem; }
.npc-peek .search-info-spacer { display: none; }
.npc-peek .search-info-footer { margin-top: 0.55rem; }
/* The switcher is inert in a peek — shown for context, not for choosing. */
.npc-peek .si-ed-btn { opacity: 0.55; }

.npc-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem; }
.npc-gen-btn { padding: 0.5rem 0.9rem; border: none; border-radius: 6px; background: var(--gold); color: #1a1408; font-family: var(--font-ui); font-size: 0.82rem; font-weight: 700; cursor: pointer; }
.npc-gen-btn:hover { filter: brightness(1.08); }
.npc-gen-cancel { padding: 0.5rem 0.9rem; border: var(--border); border-radius: 6px; background: none; color: var(--ink-3); font-family: var(--font-ui); font-size: 0.82rem; cursor: pointer; }
.npc-gen-cancel:hover { color: var(--ink); }
.npc-gen-intro { font-family: var(--font-ui); font-size: 0.82rem; line-height: 1.55; color: var(--ink-2); margin-bottom: 0.9rem; }
.npc-gen-field { margin-bottom: 0.7rem; }
.npc-gen-field label { display: block; font-family: var(--font-ui); font-size: 0.72rem; color: var(--ink-3); margin-bottom: 0.25rem; }
.npc-gen-field select, .npc-gen-field input { width: 100%; padding: 0.45rem 0.55rem; border: var(--border); border-radius: 6px; background: var(--surface); color: var(--ink); font-family: var(--font-ui); font-size: 0.84rem; outline: none; }
.npc-gen-field select:focus, .npc-gen-field input:focus { border-color: var(--gold); }
.npc-gen-field .npc-gen-custom { margin-top: 0.35rem; }

/* ── The compact statblock card ── */
/* The card an encounter draws for each NPC, and the same card a lore chapter
   draws for a statblock printed in the book. These rules used to live in
   encounter.html's own <style> block; they are here because the chapter renders
   inside the reference modal, on every page. Encounter-only chrome — the name
   field, the wound tracker, the notes box, the blank-tracker inputs — stays in
   encounter.html, which is the only place it means anything. */
.statcard { background: var(--surface); border: var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: 0.85rem 0.95rem; display: flex; flex-direction: column; gap: 0.6rem; }

/* Compact profile table — the nine characteristics as printed, and the
   Movement/Wounds row below it. */
.statcard-prof-wrap { overflow-x: auto; }
.statcard-prof { width: 100%; border-collapse: collapse; font-family: var(--font-ui); }
.statcard-prof th { font-size: 0.58rem; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-3); font-weight: 600; padding: 0.15rem 0.1rem; border-bottom: var(--border); text-align: center; }
.statcard-prof td { font-size: 0.8rem; color: var(--ink); padding: 0.25rem 0.1rem; text-align: center; font-variant-numeric: tabular-nums; }
/* Unnatural Characteristic Bonus in the cell's top-left, as the books print it.
   Headroom is added only to tables that carry one, so every other profile keeps
   its old row height. `.npc-unnatural` itself is styled above. */
.statcard-prof.has-unnatural td { position: relative; padding-top: 0.6rem; }

.statcard-line { font-family: var(--font-ui); font-size: 0.74rem; color: var(--ink-2); line-height: 1.5; }
.statcard-line b { font-weight: 600; color: var(--ink-3); font-size: 0.62rem; letter-spacing: 0.06em; text-transform: uppercase; display: block; }

/* A boxed blurb printed beside a profile — a special rule, an implant list, the
   footnote explaining a dagger. Same muted left rule the reference card gives
   its notes, so the two surfaces read as one idea. */
.statcard-note { font-family: var(--font-ui); font-size: 0.74rem; line-height: 1.55; color: var(--ink-2); padding-left: 0.7rem; border-left: 2px solid var(--border-c, rgba(128,128,128,0.35)); }
.statcard-note b { display: block; font-weight: 600; color: var(--ink-3); font-size: 0.62rem; letter-spacing: 0.06em; text-transform: uppercase; }
.statcard-note p { margin: 0 0 0.4rem; }
.statcard-note p:last-child { margin-bottom: 0; }

/* A catalogued entry sitting inline in the printed text. Styled to read as the
   prose around it — a row of buttons here would look like a toolbar — and marked
   only by a dotted rule, so an unlinked line beside it looks deliberate rather
   than broken. Gold on hover is the whole affordance.

   A <span role="button">, NOT a <button>: the engine lays a button out as an
   atomic inline-level box whatever `display` says, which let the ", " between two
   entries break onto a line by itself and stopped a long entry name from
   wrapping inside itself. (.npc-inline-link above declares `display:inline` on a
   button for the same reason and is inert for the same reason.) */
.statcard-link {
  cursor: pointer;
  text-decoration: underline; text-decoration-style: dotted; text-decoration-thickness: 1px;
  text-underline-offset: 2px; text-decoration-color: var(--ink-3);
  transition: color 0.12s, text-decoration-color 0.12s;
}
.statcard-link:hover, .statcard-link.on, .statcard-link:focus-visible {
  color: var(--ink); text-decoration-color: var(--gold); text-decoration-style: solid;
}

.statcard-full-btn { font-family: var(--font-ui); font-size: 0.72rem; color: var(--ink-2); background: none; border: var(--border); border-radius: 6px; cursor: pointer; padding: 0.28rem 0.6rem; transition: border-color 0.12s, color 0.12s; }
.statcard-full-btn:hover { border-color: var(--gold); color: var(--ink); }

/* A statblock printed inside a chapter. The card sits in the column of prose it
   belongs to, capped so it reads as an inset panel on a page-width reading view
   rather than a banner — an encounter's cards are ~310px in a grid, and a
   statblock stretched across a full-width chapter loses the shape a reader
   recognises. Margins keep it clear of the paragraphs either side. */
.lore-statcard { max-width: 34rem; margin: 0.9rem 0 1.1rem; }
.lore-statcard-foot { display: flex; justify-content: flex-end; }

/* A section that prints a statblock: the card down one column, the portrait and
   the prose down the other. Stacked — which is what every narrow screen gets —
   a full profile runs Skills through to the boxed special rules before the
   flavour text begins, which puts the description of the creature most of a
   screen below the picture of it.

   The breakpoint is the one that grows the modal to 1100px for the Lore branch
   (see .ref-modal-wide): below it the panel is 560px, and two columns of that
   would leave the profile table scrolling sideways in a 260px gutter. */
.lore-npc-split { display: block; }
.lore-npc-aside { min-width: 0; }
@media (min-width: 900px) {
  /* minmax(0, …) on both tracks rather than a bare 1fr: a grid track's default
     minimum is min-content, and the statblock's profile table plus the long
     unbroken Skills line would push the left column past its share and squeeze
     the portrait. With the floor at 0 the table scrolls inside its own
     .statcard-prof-wrap instead, which is what that wrapper is for. */
  .lore-npc-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 1.4rem;
    align-items: start; }
  /* The card supplies the block's top and bottom margin on its own; matching it
     on the aside keeps the portrait's top edge level with the card's. */
  .lore-npc-aside { margin: 0.9rem 0 1.1rem; }
  /* Free of the reading-width cap: inside half a 1100px panel the card is
     already narrower than the 34rem it is held to on a full-width page. */
  .lore-npc-split .lore-statcard { max-width: none; }
  /* The art is the last thing above the prose here rather than the first thing
     on a full-width section, so it carries the tighter gap of a column. */
  .lore-npc-aside > .lore-art { margin: 0 0 0.8rem; }
}

/* ── Reference modal (Dark Heresy rules browser) ── */
/* Sits below the shared detail popup (.modal-backdrop, z-index 1000) so tapping
   a row opens the info modal cleanly on top of the browser. */
.ref-modal-backdrop { z-index: 990; }
/* Pinned to the top (margin:0 auto overrides .modal's centring) with a stable
   min-height, so the window doesn't jump around as its content resizes. */
.ref-modal { max-width: 560px; margin: 0 auto; min-height: min(600px, 90vh); display: flex; flex-direction: column; overflow: hidden;
  transition: max-width 0.34s cubic-bezier(0.22, 0.61, 0.36, 1),
              min-height 0.34s cubic-bezier(0.22, 0.61, 0.36, 1),
              max-height 0.34s cubic-bezier(0.22, 0.61, 0.36, 1); }
/* Lore takes the whole page (see refApplyLoreWidth). 1100px is `main`'s own
   max-width and the backdrop's 1rem padding is the margin, so the expanded
   panel lands exactly on the page's own edges rather than near them.
   Desktop only: on a phone the modal is already full-bleed, and there is
   nothing to expand into. */
@media (min-width: 900px) {
  .ref-modal.ref-modal-wide { max-width: 1100px; min-height: calc(100vh - 2rem); max-height: calc(100vh - 2rem); }
}
@media (prefers-reduced-motion: reduce) { .ref-modal { transition: none; } }
.ref-nav { padding: 0.6rem 1.2rem; border-bottom: var(--border); background: var(--surface); flex-shrink: 0; }
.ref-nav:empty { display: none; }
.ref-navrow { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.75rem; }
.ref-navrow .ref-prev-toggle { margin-left: auto; }
/* display:flex here (in addition to being a flex ITEM of .ref-modal above)
   makes .ref-body a flex CONTAINER too, so .ref-detail below can reliably
   fill it via flex-grow — a percentage height (min-height:100%) on a child
   of a scrolling flex item is NOT dependable across browsers for this;
   flex-grow is. Harmless for the other things .ref-body renders (menus/
   lists/tables): each is a single non-growing child, sized as before. */
.ref-body { padding: 0; overflow-y: auto; flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* Inline detail view: a leaf entry's card, shown in-place in .ref-body instead
   of a stacked popup. .ref-body itself has no padding (rows manage their own),
   so this wrapper supplies it, matching .modal-body's standalone-popup padding.
   flex:1 grows it to fill .ref-body's visible height on short content (so
   .search-info-spacer has room to push the footer down, see above), while
   content long enough to need scrolling just grows past that and scrolls
   normally — .ref-body already scrolls. */
.ref-detail { padding: 1.2rem 1.4rem 1.6rem; display: flex; flex-direction: column; flex: 1; }
.ref-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0.1rem; font-family: var(--font-ui); font-size: 0.8rem; }
.ref-crumb { background: none; border: none; padding: 0.1rem 0.15rem; cursor: pointer; color: var(--ink-3); font-family: var(--font-ui); font-size: 0.8rem; transition: color 0.12s; }
.ref-crumb:hover { color: var(--gold); }
.ref-crumb-cur { color: var(--ink); font-weight: 700; padding: 0.1rem 0.15rem; }
.ref-crumb-sep { color: var(--ink-3); opacity: 0.6; margin: 0 0.1rem; }

/* Reference head region (banner-home at the root, plain top bar deeper in) */
.ref-head { flex-shrink: 0; }
/* Root screen: no top bar. Banner with the logo, close × in the corner, and a
   floating search box tucked under the banner. */
.ref-home-banner { position: relative; height: 210px; background-color: var(--surface-2); background-image: url("/static/reference-hero.jpg"); background-size: cover; background-position: center 30%; background-repeat: no-repeat; display: flex; align-items: center; justify-content: center; }
.ref-home-logo { max-width: 74%; max-height: 78%; object-fit: contain; filter: drop-shadow(0 3px 10px rgba(0,0,0,0.55)); }
.ref-home-close { position: absolute; top: 0.6rem; right: 0.7rem; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: rgba(0,0,0,0.45); color: #fff; font-size: 1.25rem; backdrop-filter: blur(3px); transition: background 0.12s; }
.ref-home-close:hover { background: rgba(0,0,0,0.7); color: #fff; }
.ref-home-search { display: flex; align-items: center; gap: 0.5rem; width: min(86%, 400px); margin: -22px auto 0.15rem; position: relative; z-index: 2; padding: 0.6rem 0.9rem; background: var(--surface); border: var(--border); border-radius: 10px; box-shadow: var(--shadow-lg); color: var(--ink-3); }
.ref-home-search svg { flex-shrink: 0; width: 15px; height: 15px; }

.ref-home { display: flex; flex-direction: column; }
.ref-home-list { display: flex; flex-direction: column; }
.ref-home-row { display: flex; align-items: baseline; gap: 0.65rem; width: 100%; padding: 0.95rem 1.4rem; background: none; border: none; border-bottom: var(--border); cursor: pointer; text-align: left; transition: background 0.12s; }
.ref-home-row:last-child { border-bottom: none; }
.ref-home-row:hover { background: var(--surface-1); }
.ref-home-label { flex: 1; font-family: var(--font-display); font-size: 1.22rem; font-weight: 600; letter-spacing: 0.01em; color: var(--ink); }
.ref-home-count { font-family: var(--font-ui); font-size: 0.78rem; font-weight: 600; color: var(--ink-3); }
.ref-home-chev { width: 15px; height: 15px; align-self: center; color: var(--ink-3); flex-shrink: 0; transition: color 0.12s, transform 0.12s; }
.ref-home-row:hover .ref-home-chev { color: var(--gold); transform: translateX(2px); }

/* Placeholder rows for the branches whose data is still in flight (see
   refHomeSkeletonHTML). Same box as a real row so nothing shifts when the count
   lands — only the label is dimmed and the count is a shimmering pill. Not a
   button and not hoverable: there is nothing to open yet. */
.ref-home-row-skel { cursor: default; }
.ref-home-row-skel:hover { background: none; }
.ref-home-row-skel .ref-home-label { color: var(--ink-3); }
.ref-home-row-skel .ref-home-chev { opacity: 0.35; }
.ref-skel-count { display: inline-block; width: 22px; height: 0.7rem; border-radius: 4px; background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-1) 50%, var(--surface-2) 75%); background-size: 200% 100%; animation: ref-skel-shimmer 1.3s ease-in-out infinite; }
@keyframes ref-skel-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .ref-skel-count { animation: none; } }

.ref-list { display: flex; flex-direction: column; }
.ref-row { display: flex; align-items: center; gap: 0.6rem; width: 100%; padding: 0.6rem 1.2rem; background: none; border: none; border-bottom: var(--border); text-align: left; cursor: pointer; font-family: var(--font-ui); font-size: 0.86rem; color: var(--ink); transition: background 0.1s; }
.ref-row:hover { background: var(--surface-1); }
.ref-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.12rem; }
.ref-nm { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ref-list.ref-prev-on .ref-nm { white-space: normal; overflow: visible; text-overflow: clip; }
.ref-pv { display: block; color: var(--ink-3); font-size: 0.74rem; line-height: 1.35; white-space: normal; }
.ref-row-meta { flex-shrink: 0; align-self: flex-start; font-size: 0.7rem; color: var(--ink-3); font-weight: 600; padding-top: 0.05rem; }
/* DIRECT children only. This centres a menu row's own two spans against each
   other on the row's cross axis, which on a horizontal flex row means
   vertically. An entry row nests its name inside .ref-row-main, a COLUMN flex
   container, where the very same declaration means horizontally — so every
   entry row that had no epithet to sit beneath its name (an Inquisitor with no
   subtitle, sitting between two who have one) drew its name centred in the
   row. */
.ref-menu-row > .ref-row-meta, .ref-menu-row > .ref-nm { align-self: center; }

/* A category's book preamble above its list (Kit > Income). Collapsed by
   default so the entries still open at the top of the screen; flex-shrink
   guards it against .ref-body's column flex squashing it once expanded. */
.ref-notes { flex-shrink: 0; border-bottom: var(--border); }
.ref-notes-sum { list-style: none; cursor: pointer; padding: 0.6rem 1.2rem; font-family: var(--font-ui); font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-3); transition: color 0.12s; }
.ref-notes-sum::-webkit-details-marker { display: none; }
.ref-notes-sum::after { content: "＋"; float: right; font-weight: 400; }
.ref-notes[open] .ref-notes-sum::after { content: "－"; }
.ref-notes-sum:hover { color: var(--ink); }
.ref-notes-body { padding: 0 1.2rem 1.2rem; }
.ref-notes-body .search-info-desc + .search-info-desc { margin-top: 0.7rem; }
.search-info-tablenote { font-family: var(--font-ui); font-size: 0.72rem; line-height: 1.5; color: var(--ink-3); margin-top: 0.4rem; }

.ref-prev-toggle { display: inline-flex; align-items: center; gap: 0.45rem; background: none; border: none; cursor: pointer; font-family: var(--font-ui); font-size: 0.76rem; color: var(--ink-3); white-space: nowrap; }
.ref-prev-toggle.on { color: var(--ink); }
.ref-prev-track { width: 28px; height: 16px; border-radius: 8px; background: var(--surface-2); position: relative; transition: background 0.15s; flex-shrink: 0; }
.ref-prev-toggle.on .ref-prev-track { background: var(--gold); }
.ref-prev-dot { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: #fff; transition: transform 0.15s; box-shadow: 0 1px 2px rgba(0,0,0,0.3); }
.ref-prev-toggle.on .ref-prev-dot { transform: translateX(12px); }


@media(max-width:680px) { .ref-modal { max-width: 100%; } }

/* Reference "DH1e differs" flag on Phenomena/Perils rows */
.ref-diff { display: inline-block; margin-left: 0.5rem; font-size: 0.6rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--gold); border: 1px solid var(--gold); border-radius: 4px; padding: 0.02rem 0.3rem; vertical-align: middle; }
/* Roll-range column on Phenomena/Perils rows (sorted by roll) */
.ref-roll { flex-shrink: 0; align-self: flex-start; min-width: 3.4rem; font-family: var(--font-ui); font-size: 0.78rem; font-weight: 700; color: var(--ink-3); font-variant-numeric: tabular-nums; padding-top: 0.02rem; }
.search-info-tabletitle { font-family: var(--font-ui); font-size: 0.74rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-2); margin: 1rem 0 0.1rem; }

/* ── Lore archive card ── */
/* Story material, not rules: prose is the content, so it gets a slightly larger
   measure than a rules card's .search-info-desc and paragraph spacing to read
   at length. */
.lore-epithet { font-family: var(--font-display); font-size: 0.92rem; font-style: italic; color: var(--ink-3); margin: 0.3rem 0 0.9rem; }
/* The pull-up is measured against the CARD TITLE's own 0.9rem bottom margin and
   nothing else. An epithet also prints under an EMBED's title (a faction printed
   inside the chapter that prints it) and that heading — like every heading here
   — carries margin-bottom: 0 from the reset, so the same -0.6rem laid the
   epithet straight on top of the words above it. */
.lore-head-title + .lore-epithet { margin-top: -0.6rem; }
/* Art on a lore page spans the FULL WIDTH of the card and stacks, rather than
   sitting in a row of capped thumbnails. These are book plates, maps and page
   renders — the detail in them is the content, and at 260px tall a map of a
   carnival is an illustration of a map rather than one you can use. Several
   pieces stack down the page instead of sharing a row for the same reason. */
.lore-art { display: flex; flex-direction: column; gap: 0.6rem; margin: -0.2rem 0 1rem; }
/* The frame hugs the image (fit-content) and centres itself. A fixed full-width
   frame would stand empty either side of a portrait once the height cap below
   shortens it, which reads as a broken image rather than a tall one. */
.lore-art-item { display: block; width: fit-content; max-width: 100%; margin: 0 auto;
  border: var(--border); border-radius: var(--radius);
  background: var(--surface-1); overflow: hidden; line-height: 0; }
/* Full width, but never taller than 600px. A wide plate or a map hits the width
   limit first and still spans the card; a tall portrait hits the height limit
   and shrinks proportionally rather than running on for a screen and a half.
   Both limits sit on the IMAGE and only one binds at a time, so the aspect ratio
   is never touched — `width: 100%` with a max-height would have stretched it. */
.lore-art-item img { display: block; width: auto; height: auto;
  max-width: 100%; max-height: 600px; }

/* A section that owns its art — a handout beside the document it transcribes.
   It used to sit in a 190px column beside the text; now it spans the section's
   full width above it, like every other plate on a lore page. Art comes FIRST
   in the DOM, which is what puts it above the text it belongs to.
   min-width:0 on the body stops a long unbroken string (a docket reference, a
   transmit code) from forcing the block wider than the card. */
.lore-sec-art { display: block; }
.lore-sec-art > .lore-art { width: 100%; margin: 0 0 0.9rem; }
.lore-sec-art-body { min-width: 0; }
/* Inside a box the art sits tight to the heading rather than the box edge. */
.lore-box .lore-sec-art { margin-top: 0.5rem; }
/* Art gallery leaf. auto-fill rather than a fixed column count so the grid
   reflows from a phone to a wide modal without breakpoints. The plates vary
   wildly in aspect ratio — full-page portraits next to letterbox scenes — so
   tiles are given one shape and the image is cropped to it; an un-cropped grid
   of mixed ratios reads as a mess and makes nothing easier to find. */
.ref-artgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
  gap: 0.5rem; padding: 0.15rem 0 0.4rem; }
.ref-arttile { position: relative; display: block; padding: 0; cursor: pointer;
  border: var(--border); border-radius: var(--radius); background: var(--surface-1);
  overflow: hidden; aspect-ratio: 3 / 4; }
.ref-arttile img { display: block; width: 100%; height: 100%; object-fit: cover; }
.ref-arttile:hover { border-color: var(--ink-3); }
/* The page number rides the tile because most plates are untitled, so it is the
   only thing telling two of them apart at thumbnail size. */
.ref-arttile-cap { position: absolute; left: 0; right: 0; bottom: 0; padding: 0.2rem 0.35rem;
  font-family: var(--font-ui); font-size: 0.62rem; letter-spacing: 0.04em; text-align: right;
  color: #fff; background: linear-gradient(to top, rgba(0,0,0,0.62), rgba(0,0,0,0)); }
/* A tile whose image 404s would otherwise be an empty frame with a page number
   floating in it, which reads as a layout bug rather than a missing file. */
.ref-arttile-broken { display: none; }
/* The full plate on its own card. Capped against the viewport so a tall
   portrait still leaves its provenance and links visible without scrolling. */
.ref-artfull { margin: 0.2rem 0 0.9rem; }
.ref-artfull img { display: block; max-width: 100%; max-height: 62vh; width: auto; height: auto;
  margin: 0 auto; border: var(--border); border-radius: var(--radius); background: var(--surface-1); }
.lore-body { font-size: 0.88rem; line-height: 1.65; }
.lore-body p { margin: 0 0 0.7rem; }
.lore-body p:last-child { margin-bottom: 0; }
/* Inline link between entries: underlined text, deliberately subtle — a page of
   dossiers is dense with names, and chips or gold would turn the prose into a
   list of buttons. Matches .npc-inline-link's dotted treatment so both kinds of
   in-prose link read as the same idea; it has no selected state because
   following one navigates the modal rather than expanding in place. */
.lore-link { color: var(--ink); text-decoration: underline; text-decoration-style: dotted;
  text-underline-offset: 0.15em; cursor: pointer; }
.lore-link:hover { text-decoration-style: solid; color: var(--gold); }
.lore-link:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; border-radius: 2px; }
/* Boxed aside (`box: true` on a section) — the books' sidebars and designer's
   notes. Subtle on purpose: a tinted panel and a hairline, no accent colour and
   no heavy rule, so a page with several of them still reads as one page rather
   than a stack of alerts. The heading is smaller and quieter than a section
   head, since a box interrupts the chapter rather than dividing it. */
.lore-box { display: block; margin: 1.5rem 0; padding: 1.2rem 1rem; border: var(--border);
  border-radius: var(--radius); background: var(--surface-1); }
/* .ref-detail is a column flex container, so adjacent margins do NOT collapse —
   two boxes in a row would otherwise sit 2.6rem apart, twice the gap either has
   with the prose around it. */
.lore-box + .lore-box { margin-top: 0; }
.lore-box-head { font-family: var(--font-ui); font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--ink-3); margin: 0 0 0.45rem; }
/* Tables inside a box sit on the panel, so they lose the outer margin that
   separates them from body copy. */
.lore-box .search-info-tablewrap { margin-top: 0.5rem; }
.lore-box .search-info-tabletitle { margin-top: 0.6rem; }
/* Transcription note — where this transcription had to depart from the printed
   page. It sits at the head of the page, in the position the scenario `facts`
   block used to hold.

   The one voice on the page that is not the book's, so it takes neither of the
   two frames that are: no fill (a box is filled) and no gold rule (a read-aloud
   is). A dashed hairline down the left says "editorial" without competing with
   either — a reader scanning for the passages they act on can skip it by shape,
   which is the point, because on most pages it is not there at all.

   Ink one step back from body copy and set in the UI face rather than the
   chapter's serif, for the same reason. */
/* ── The book's own structure, on screen ─────────────────────────────────────
   A chapter transcribed as one continuous page needs the hierarchy the printed
   page has. Level 2 is the existing .search-info-sectionhead and is unchanged,
   so every entry written before these levels existed renders exactly as it did.
   Level 1 is a major division — the weight and air of a heading you can find by
   scrolling. Level 3 is a sub-heading that must not read as a new section. */
.lore-sec-l1 > .lore-head > .search-info-sectionhead {
  font-size: 1.3rem; letter-spacing: 0.01em; margin-top: 2.4rem; padding-top: 1.5rem;
  border-top: 2px solid var(--gold); }
.lore-sec-l1:first-child > .lore-head > .search-info-sectionhead { margin-top: 0.6rem; }
.lore-sec-l3 > .lore-head > .search-info-sectionhead {
  font-family: var(--font-ui); font-size: 0.82rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--ink-2); margin-top: 1.1rem; padding-top: 0; border-top: none; }

/* A Gameplay page printed inside the chapter it came from. No frame of its own:
   the point is that it reads as part of the chapter, not as a quotation from
   somewhere else. It only needs to not collide with the section heading above
   it, and to keep its own first heading from doubling that heading's rule. */
.lore-embed { display: block; margin: 0.2rem 0 0.4rem; }
.lore-embed > .search-info-sectionhead:first-child { margin-top: 1rem; }
/* The name of a page printed in place. It divides one printed page from the
   next inside a single chapter division — ten alternate ranks in a row are one
   undifferentiated run of "Becoming a…" and "Advances" without it — so it takes
   the weight of a section heading and the gold rule of a division. */
.lore-embed-title { border-top: 1px solid var(--gold); }

/* A menu row that carries a subtitle grows to two lines and stops centring its
   text against the meta column, so the title still sits on the row's first line
   where the eye expects it. The subtitle itself reuses .ref-pv, the same muted
   second line a list row uses, so a chapter reads the same wherever it is met. */
.ref-menu-row-sub { align-items: flex-start; }
.ref-menu-row-sub .ref-pv { display: block; white-space: normal; }

/* An in-world document — a dossier, a warrant, a page from a journal. Framed as
   a leaf of paper laid on the page rather than as a panel of the page: a heavier
   rule, a warm ground off the gold token, and the body in the display serif,
   because the words are a character's and not the book's. Kept in the app's own
   palette rather than tinted like aged parchment, so it still reads as part of
   this interface in both colour modes. */
.lore-doc { display: block; margin: 1.4rem 0; padding: 1rem 1.15rem;
  background: var(--gold-bg); border: 1px solid var(--gold); border-radius: var(--radius); }
.lore-doc + .lore-doc { margin-top: 0.7rem; }
.lore-doc-head { font-family: var(--font-ui); font-size: 0.68rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.09em; color: var(--gold); margin: 0 0 0.55rem; }
.lore-doc .lore-body { font-family: var(--font-display); color: var(--ink); }
.lore-doc .search-info-tablewrap { margin-top: 0.5rem; }

/* How a plate sits against its text, mirroring the book's own placement.
   `stack` is the inherited behaviour and stays the default. */
.lore-art-plate > .lore-art { margin: 0.9rem 0 1.1rem; }
.lore-art-plate .lore-art-item { width: 100%; }
/* Capped: a plate is often a whole page scanned portrait, and at full measure
   that is taller than the screen — the reader loses their place scrolling past
   one picture. The tile stays clickable and opens the full-size image. */
.lore-art-plate .lore-art-item img { width: 100%; max-height: 70vh; object-fit: contain; }
.lore-art-figure > .lore-art { margin: 0.9rem auto 1.1rem; }
.lore-art-figure .lore-art-item { max-width: min(100%, 380px); }
/* A column figure: the prose runs around it the way it runs around a cut-out
   illustration on a printed page. Floated only once there is a measure wide
   enough for the wrap to look deliberate rather than cramped. */
/* A CONTAINER query, not a viewport one: the reference modal is far narrower
   than the window it sits in (560px on a desktop until the lore width kicks in),
   so keying the wrap to screen width floats the figure at exactly the measures
   where there is no room for it. What matters is how wide THIS card is. */
.ref-detail { container-type: inline-size; container-name: lorecard; }
@container lorecard (min-width: 620px) {
  .lore-art-inset { float: right; width: 42%; max-width: 320px; margin: 0.2rem 0 0.9rem 1.2rem; }
  .lore-art-inset > .lore-art { margin: 0; }
  .lore-art-inset .lore-art-item { width: 100%; }
  .lore-art-inset .lore-art-item img { width: 100%; }
  /* The float must not escape its own section and push into the next heading. */
  .lore-sec, .lore-box, .lore-doc, .lore-read { overflow: hidden; }
}

.lore-transcript { display: block; margin: 0 0 1.1rem; padding: 0.1rem 0 0.1rem 0.85rem;
  border-left: 2px dashed var(--border-c, rgba(128,128,128,0.4)); }
.lore-transcript-label { font-family: var(--font-ui); font-size: 0.62rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-3); margin-bottom: 0.3rem; }
.lore-transcript p { font-family: var(--font-ui); font-size: 0.76rem; line-height: 1.6;
  color: var(--ink-2); margin: 0 0 0.45rem; }
.lore-transcript p:last-child { margin-bottom: 0; }

/* Read-aloud passage (`read: true` on a section) — the text a GM reads to the
   table verbatim.

   Deliberately the OPPOSITE treatment to a box, because it has the opposite
   audience: the box is filled and unruled and quiet, this is unfilled and ruled
   and italic. A GM skimming a chapter mid-session has to be able to tell "words
   I say out loud" from "notes to me" by shape alone, at speed, without reading
   either — so the distinction is carried by the frame and the face, not by a
   label they would have to stop and read.

   No background fill on purpose. Two tinted panels of different tints would
   read as the same thing in two moods; an accent rule against open page reads
   as a different thing. */
.lore-read { display: block; margin: 1.3rem 0; padding: 0.15rem 0 0.15rem 1rem;
  border-left: 3px solid var(--gold); }
/* Same non-collapsing-margin case .lore-box + .lore-box handles: .ref-detail is
   a column flex container. A scenario stacks these far more often than it
   stacks boxes — several consecutive read-aloud options for one scene. */
.lore-read + .lore-read { margin-top: 0.65rem; }
.lore-read .lore-body { font-family: var(--font-display); font-style: italic; color: var(--ink-2); }
/* The heading is the cue label the books print above the passage ("If the
   Acolytes open the casket…"), so it is upright and quiet against the italic it
   introduces — the reverse of the usual heading/body contrast. */
.lore-read-head { font-family: var(--font-ui); font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--gold); margin: 0 0 0.4rem; font-style: normal; }
/* Player handouts — their own labelled group at the foot of a scenario card.
   The rule above it separates them from the chapter text: they are an appendix
   to the page, not its last section. */
.lore-handouts { margin-top: 1.6rem; padding-top: 1rem; border-top: var(--border); }
.lore-handouts .search-info-sectionhead { margin-top: 0; }
/* Handouts are documents, not illustrations — they are read, and a thumbnail of
   a page of handwriting says nothing. Full width, one per row, like the plates. */
.lore-handouts .lore-art { gap: 0.75rem; }
/* ── A reader's marks: bookmark + note, on every heading ── */
/* The icons lead the heading rather than trailing it, so they line up in a
   single column down the left edge of a chapter and can be found without
   reading the headings — which is the point of them at speed.
   The rule that separates one section from the last moves from the heading onto
   this wrapper: a border-top on the heading alone would stop short of the
   icons and read as an underlined title rather than a divider. */
/* Two columns: everything the heading is on the left, the ribbon on the right.
   The note icon still sits ABOVE the title rather than beside it — a heading is
   the widest thing on the line and that icon the smallest, and side by side a
   heading long enough to wrap dragged it off centre.
   The ribbon spans both rows and is pinned to the top, so the title wraps to its
   left and the ribbon hangs off the top of the passage the way it would off the
   top of a page. minmax(0,1fr) rather than 1fr: a grid item's automatic minimum
   is its content, and without this a long unbroken heading would push the ribbon
   off the panel instead of wrapping. */
.lore-head { display: grid; grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 0.75rem; align-items: start; margin-bottom: 0.5rem; }
/* The heading takes the whole left column; the ribbon and the note icon stack
   down the right of it, in that order. The note used to sit above the title on
   the left — it belongs under the ribbon, so the two marks a reader can leave
   are one column in one place rather than two tools at opposite corners. */
.lore-head > h3, .lore-head > h5 { grid-column: 1; grid-row: 1 / span 2;
  margin-top: 0; padding-top: 0; border-top: none; min-width: 0; }
/* The ribbon hangs from the rule ABOVE the passage rather than from the top of
   the heading, the way a ribbon sits over the edge of a page rather than inside
   the text block. */
.lore-head > .lore-bmk { grid-column: 2; grid-row: 1; margin-top: -1.5rem; justify-self: center; }
.lore-head > .lore-marks { grid-column: 2; grid-row: 2; justify-self: center; }
/* The rule and the space between one section and the last live on the SECTION,
   not on its heading, so that a bookmarked section's edge spans the whole
   passage rather than the single line the heading occupies. */
.lore-sec { position: relative; margin-top: 1.6rem; padding-top: 1.2rem; border-top: var(--border); }
/* A section head immediately under the page title is the top of the page, not a
   division within it — the same case .search-info-title + .search-info-sectionhead
   handles on a rules card, restated because the note field may sit between them.
   .lore-sec-nodiv is the hidden duplicate-of-the-title section, which would
   otherwise print a rule above nothing. */
.lore-head-title + .lore-sec, .lore-head-title + .lore-note + .lore-sec, .lore-sec-nodiv {
  margin-top: 0; padding-top: 0; border-top: none; }

/* A bookmarked passage carries a gold edge on the CONTAINER's own left edge —
   flush with the panel, outside .ref-detail's padding, so a reader scrolling a
   long chapter sees their marks as a column of ticks down the side rather than
   as decoration on the text. -1.4rem is .ref-detail's left padding exactly.
   Nothing is clipped: the bar stops at the panel's inner edge. */
.lore-bookmarked { position: relative; }
.lore-bookmarked::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: -1.4rem;
  width: 3px; border-radius: 0 2px 2px 0; background: var(--gold); }
/* An absolutely positioned child is placed against its container's PADDING box,
   so a frame with a border of its own needs that border added back to land on
   the same line as a plain section's edge: 1px for a box, 3px for a read-aloud's
   gold rule. */
.lore-box.lore-bookmarked::before { left: calc(-1.4rem - 1px); }
.lore-read.lore-bookmarked::before { left: calc(-1.4rem - 3px); }
/* A section repeating the page's own name keeps its anchor but is not shown
   (see .search-info-sectionhead-dupe) — so its icons must go too, or they would
   float alone above the prose. The page title carries the marks for that text. */
.lore-head-dupe { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.lore-marks { display: inline-flex; align-items: center; gap: 0.1rem; flex-shrink: 0; }
.lore-mark-btn { display: inline-flex; align-items: center; justify-content: center;
  width: 1.55rem; height: 1.55rem; padding: 0; border: none; border-radius: 5px;
  background: none; color: var(--ink-3); cursor: pointer;
  transition: color 0.12s, background 0.12s; }
.lore-mark-btn svg { width: 15px; height: 15px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
/* Under a heading the note icon is drawn to the RIBBON'S width, so the two read
   as one column of marks rather than a big one and a small one. Its own glyph is
   square, so the height follows the width and nothing is stretched; the button
   is the icon plus the room it needs to be hit. */
.lore-head .lore-mark-btn svg { width: var(--bmk-w); height: calc(var(--bmk-w) * 0.892); }
.lore-head .lore-mark-btn { width: var(--bmk-w); height: calc(var(--bmk-w) * 0.892);
  border-radius: 3px; }
.lore-mark-btn:hover { color: var(--ink); background: var(--surface-1); }
.lore-mark-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
/* Saved: the outline fills. Gold is the app's accent and the books' own colour;
   a literal yellow would be the only one of its kind on the page. */
.lore-nb.on { color: var(--gold); }

/* ── The ribbon ── */
/* A bookmark is no longer a toggle that happens to be shaped like one — it is a
   ribbon laid into a slot. The slot is always there: a dotted outline of the
   same silhouette, low enough in contrast to sit beside a heading without
   competing with it, saying "a bookmark goes here" to a reader who has not left
   one yet. Laying one drops the solid ribbon into that slot from above.

   The slot is 6px taller than the ribbon and clips: the overshoot at the end of
   the drop needs somewhere to go, and the same clipping is what hides the ribbon
   above the top edge when there is none. */
/* The ribbon's size lives on the heading BLOCK, not on the ribbon, because the
   note icon under it is sized from the same width — one declaration moves both.
   ONE size everywhere, page title included: a chapter's mark and a passage's
   mark are the same gesture, and printing the first larger made the top of a
   card look like a different control from the rest of it. */
.lore-head { --bmk-w: 18px; --bmk-h: 26px; }
/* The page title's own headroom: its ribbon is lifted the same 1.5rem as every
   other, but there is no rule above the first heading on a card — only
   .ref-detail's padding, which is 4.8px short of the lift. #ref-body is the
   SCROLL container, so anything above its top edge is clipped rather than drawn
   over the breadcrumb, and without this the top of the ribbon is cut off
   whenever the card is scrolled to the top. */
.lore-head-title { margin-top: 0.4rem; }
.lore-bmk { display: block; padding: 0; border: none; background: none;
  cursor: pointer; color: var(--ink-3);
  /* Lifted into the margin, it lies OVER the rule above the passage and over
     whatever else is up there — so it is positioned and raised, and floats on
     top of that content rather than being crossed by it. */
  position: relative; z-index: 1; }
.lore-bmk-slot { position: relative; display: block;
  width: var(--bmk-w); height: calc(var(--bmk-h) + 6px); overflow: hidden; }
.lore-bmk svg { position: absolute; top: 0; left: 0; display: block;
  width: var(--bmk-w); height: var(--bmk-h); }
/* The empty slot is filled with the surface it sits on, not left transparent:
   it hangs over the rule above the passage, and an unfilled outline would have
   that rule drawn straight through the middle of it. */
.lore-bmk-ghost { fill: var(--surface-1); stroke: var(--ink-3); stroke-width: 1;
  stroke-dasharray: 2.5 1; stroke-linejoin: round;
  transition: opacity 0.18s; }
.lore-bmk:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; border-radius: 3px; }
/* Off-screen until it is laid, and it LEAVES the way it came — straight back up
   and out, no bounce, because retracting a ribbon is not the same gesture as
   dropping one. The spring belongs to the .on state, which is the state being
   transitioned INTO when the bookmark is set. */
.lore-bmk-ribbon { fill: var(--gold); transform: translateY(-115%);
  transition: transform 200ms cubic-bezier(0.4, 0, 0.9, 0.35); }
.lore-bmk.on .lore-bmk-ribbon { transform: translateY(0);
  transition: transform 460ms cubic-bezier(0.34, 1.5, 0.64, 1); }
.lore-bmk.on .lore-bmk-ghost { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .lore-bmk-ribbon, .lore-bmk.on .lore-bmk-ribbon { transition: none; }
}

/* The same ribbon on a ROW — a book's row in the menu, a chapter's row in a
   list, a page's row in the round-up. It is a jump link, not a toggle: it says
   where the book is open and takes you there, and it never animates, because
   nothing is being laid. Pushed to the far right so the label and its count stay
   packed together at the left.
   A span rather than a button (every row it rides in is itself a button), so it
   needs the hit area and the pointer spelled out. */
.lore-row-bmk { margin-left: auto; flex-shrink: 0; display: inline-flex;
  align-items: center; padding: 0 0.1rem; cursor: pointer; }
.lore-row-bmk svg { display: block; width: 13px; height: 19px; fill: var(--gold);
  transition: transform 0.12s; }
.lore-row-bmk:hover svg { transform: translateY(1px); }
.lore-row-bmk:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; border-radius: 3px; }

/* The note field itself. Faintly lighter than the card it sits on, with a fine
   static over it — enough to say "this isn't the book, this is mine" at a
   glance, without a heavy frame interrupting a page of prose. The noise is an
   inline SVG turbulence rather than an image file: it costs no request, tiles
   at any size, and blends against whichever surface colour is underneath, so it
   works unchanged in both themes. */
.lore-note { margin: 0.5rem 0 0.9rem; padding: 0.55rem 0.75rem 0.6rem;
  border: var(--border); border-radius: var(--radius);
  background-color: var(--surface-2);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.32'/%3E%3C/svg%3E");
  background-size: 140px 140px; background-blend-mode: soft-light; }
.lore-note-label { font-family: var(--font-ui); font-size: 0.62rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-3); margin-bottom: 0.25rem; }
/* Transparent and unframed: the panel around it is already the field, and a
   second box inside the first would read as a form. Auto-grown in JS
   (loreNoteGrow), hence resize:none and no scrollbar of its own. */
.lore-note-field { display: block; width: 100%; min-height: 2.6rem; padding: 0;
  border: none; background: none; resize: none; overflow: hidden;
  font-family: var(--font-ui); font-size: 0.84rem; line-height: 1.55; color: var(--ink); }
.lore-note-field:focus { outline: none; }
.lore-note-field::placeholder { color: var(--ink-3); opacity: 0.7; }
/* Read-only rendering of the same note in the round-up. `overflow-wrap: anywhere`
   because a note is not prose and cannot be trusted to contain spaces — a pasted
   URL or a row of keyboard mashing is a single 2000px word, and pre-wrap alone
   would send it straight out through the side of the panel. */
.lore-note-read { font-family: var(--font-ui); font-size: 0.84rem; line-height: 1.55;
  color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
.lore-note-field { overflow-wrap: anywhere; }
.lore-noteitem-ctx { overflow-wrap: anywhere; }

/* ── Bookmarks & Notes round-up ── */
/* One <details> per mark. Native <details> rather than a JS accordion — it keeps
   its open state through a re-render and costs nothing.

   The summary is a two-row grid, which is what puts the heading and the page on
   their own lines with the snippet and the controls pinned to the right:

        heading                  first line of the note   →   ×
        page it is on

   Expanded, the passage and then the note follow underneath, so a row reads
   top-down in the order the mark was made: where it is, what it is about, and
   the reader's own words last. */
.lore-notelist { display: flex; flex-direction: column; }
.lore-noteitem { position: relative; border-bottom: var(--border); }
.lore-noteitem-sum { list-style: none; display: grid; grid-template-columns: auto 1fr auto auto;
  align-items: baseline; gap: 0.15rem 0.75rem;
  padding: 0.65rem 1.2rem; cursor: pointer; transition: background 0.12s; }
.lore-noteitem-sum::-webkit-details-marker { display: none; }
.lore-noteitem-sum:hover { background: var(--surface-1); }
.lore-noteitem-head { grid-column: 1; grid-row: 1; font-family: var(--font-ui); font-size: 0.86rem; color: var(--ink); }
/* The snippet is what tells two marks on the same chapter apart, so it takes the
   free width and truncates rather than wrapping the row to three lines. It is
   redundant once the note itself is on screen. */
.lore-noteitem-snip { grid-column: 2; grid-row: 1; min-width: 0; text-align: right;
  font-family: var(--font-ui); font-size: 0.76rem; font-style: italic; color: var(--ink-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lore-noteitem[open] .lore-noteitem-snip { visibility: hidden; }
.lore-noteitem-x { grid-column: 4; grid-row: 1; justify-self: end; padding: 0 0.15rem;
  background: none; border: none; color: var(--ink-3); font-size: 1.05rem; line-height: 1;
  cursor: pointer; transition: color 0.12s; }
.lore-noteitem-x:hover { color: var(--crimson); }
.lore-noteitem-page { grid-column: 1; grid-row: 2; font-family: var(--font-ui); font-size: 0.72rem; color: var(--ink-3); }
/* The way back into the chapter: an arrow on the TITLE's own row, where it
   points at the thing it opens. It lives inside the <summary>, so its handler
   stops the click from also toggling the row. */
.lore-noteitem-go { grid-column: 3; grid-row: 1; justify-self: end; padding: 0 0.15rem;
  background: none; border: none; cursor: pointer; white-space: nowrap; line-height: 1;
  font-family: var(--font-ui); font-size: 0.95rem; font-weight: 600; color: var(--gold); }
.lore-noteitem-go:hover { text-decoration: underline; }
.lore-noteitem-body { padding: 0 1.2rem 0.9rem; }
/* The passage carries NO label. The quoted rule already says this is the book
   talking, and with the note directly beneath it, "MY NOTE" is the only label
   the row needs to keep the two voices apart. */
.lore-noteitem-ctx { margin-top: 0.2rem; padding-left: 0.8rem; border-left: 2px solid var(--border-color);
  font-size: 0.8rem; line-height: 1.6; color: var(--ink-3); }
.lore-noteitem-body .lore-note { margin-bottom: 0; }
/* A bookmarked row wears the same gold edge the passage wears in the chapter,
   flush with the panel — one mark, one shape, in both places it appears. */
.lore-noteitem.lore-bookmarked::before { left: 0; }

/* One page's marks, under the name of the page they are on. The book is set
   quieter beneath it: which page you are looking at is the question this screen
   just answered, and which book it came from is the reminder. */
/* The way back out of a page's marks. It keeps .lore-back's own margin-left:auto
   — the lore back-link sits at the far right everywhere else it appears — and
   only needs the insets that the breadcrumb row would otherwise have given it,
   so it lines up with the rows below rather than with the panel's edge. */
.lore-marks-back { margin: 0.7rem 1.2rem 0 auto; }
.lore-markgroup-head { padding: 0.5rem 1.2rem 0.6rem; border-bottom: var(--border); }
.lore-markgroup-name { display: block; font-family: var(--font-display);
  font-size: 1.05rem; color: var(--ink); }
.lore-markgroup-book { display: block; font-family: var(--font-ui); font-size: 0.72rem;
  color: var(--ink-3); margin-top: 0.1rem; }

/* The star that leads the Bookmarks & Notes row in the Lore menu. */
.ref-row-icon { flex-shrink: 0; width: 13px; height: 13px; fill: var(--gold); color: var(--gold); }

/* The back-link sits at the far end of the breadcrumb row (pushed right the same
   way the preview toggle is), so following a chain of links never loses the way
   out of it. */
.lore-back { margin-left: auto; background: none; border: none; padding: 0.1rem 0.15rem; cursor: pointer;
  font-family: var(--font-ui); font-size: 0.78rem; font-weight: 600; color: var(--gold); }
.lore-back:hover { text-decoration: underline; }

/* Sortable table leaf (Talents, NPCs) */
.ref-table-wrap { overflow-x: auto; }
.ref-thead, .ref-trow { display: grid; align-items: center; min-width: 380px; }
.ref-thead.ref-table-talents, .ref-tbody.ref-table-talents .ref-trow { grid-template-columns: minmax(116px, 1.7fr) 42px minmax(80px, 1.1fr) 68px; }
.ref-thead.ref-table-npcs, .ref-tbody.ref-table-npcs .ref-trow { grid-template-columns: minmax(140px, 2fr) minmax(74px, 1fr) minmax(74px, 1fr); }

/* Column-filter chips above a table leaf. Selected state is the same B&W
   inversion as the edition switcher (.si-ed-btn) — never a literal #fff, which
   goes invisible in dark mode. Groups wrap independently so Class and Rating
   stay visually distinct rows on a narrow screen. */
.ref-chipbar { display: flex; flex-wrap: wrap; gap: 0.3rem 0.9rem; padding: 0 0.15rem 0.6rem; }
.ref-chip-group { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; }
.ref-chip { font-family: var(--font-ui); font-size: 0.72rem; font-weight: 600; padding: 0.2rem 0.55rem; border: 1px solid var(--border-color); border-radius: 999px; background: var(--surface); color: var(--ink-3); cursor: pointer; white-space: nowrap; transition: all 0.12s; }
.ref-chip:hover { border-color: var(--ink); color: var(--ink); }
.ref-chip.on { background: var(--ink); border-color: var(--ink); color: var(--surface); }
.ref-chip-all { text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.64rem; }
.ref-thead { position: sticky; top: 0; background: var(--surface); border-bottom: 2px solid var(--border-color); z-index: 1; }
.ref-th { background: none; border: none; cursor: pointer; text-align: left; font-family: var(--font-ui); font-size: 0.64rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-3); padding: 0.5rem 0.5rem; white-space: nowrap; transition: color 0.12s; }
.ref-th:hover { color: var(--ink); }
.ref-th.on { color: var(--gold); }
.ref-trow { width: 100%; background: none; border: none; border-bottom: var(--border); cursor: pointer; text-align: left; transition: background 0.1s; }
.ref-trow:hover { background: var(--surface-1); }
.ref-td { padding: 0.5rem 0.5rem; font-family: var(--font-ui); font-size: 0.82rem; color: var(--ink-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ref-td.ref-c-name { color: var(--ink); font-weight: 600; }
.ref-td.ref-c-src, .ref-th.ref-c-src { color: var(--ink-3); padding-left: 0.25rem; padding-right: 0.4rem; }
.ref-tbody.ref-prev-on .ref-trow { align-items: start; }
.ref-tbody.ref-prev-on .ref-c-name { white-space: normal; overflow: visible; text-overflow: clip; padding-top: 0.5rem; }
.ref-tbody.ref-prev-on .ref-td:not(.ref-c-name) { padding-top: 0.55rem; }

/* Type-and-filter living in the dark top bar */
.ref-header { gap: 0.85rem; }
.ref-title { flex-shrink: 0; }
.ref-search { flex: 1; min-width: 0; display: flex; align-items: center; gap: 0.45rem; padding: 0.35rem 0.7rem; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.14); border-radius: 7px; color: rgba(255,255,255,0.6); transition: border-color 0.12s, background 0.12s; }
.ref-search:focus-within { border-color: var(--gold); background: rgba(255,255,255,0.14); }
.ref-search svg { flex-shrink: 0; width: 15px; height: 15px; }
.ref-filter-input { flex: 1; min-width: 0; border: none; background: none; outline: none; font-family: var(--font-ui); font-size: 0.84rem; color: #fff; }
.ref-filter-input::placeholder { color: rgba(255,255,255,0.5); }
/* In the light floating home box the text/placeholder need dark ink instead */
.ref-home-search .ref-filter-input { color: var(--ink); }
.ref-home-search .ref-filter-input::placeholder { color: var(--ink-3); }
.ref-home-search:focus-within { border-color: var(--gold); }
/* Clear (×) button: cancels the search without closing the modal. Hidden
   until there's text — toggled both from the template (_refFilter) and live
   via oninput, since typing only re-renders the body, not this header. */
.ref-filter-clear { display: none; flex-shrink: 0; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; border: none; background: none; padding: 0; font-size: 1rem; line-height: 1; cursor: pointer; color: rgba(255,255,255,0.5); transition: color 0.12s, background 0.12s; }
.ref-filter-clear.show { display: inline-flex; }
.ref-filter-clear:hover { color: #fff; background: rgba(255,255,255,0.15); }
.ref-home-search .ref-filter-clear { color: var(--ink-3); }
.ref-home-search .ref-filter-clear:hover { color: var(--ink); background: var(--surface-2); }

/* Catalog info modal: tables (hit locations, crit effects) + edition toggle */
.search-info-tablewrap { overflow-x: auto; margin-top: 0.4rem; }
.search-info-table { width: 100%; border-collapse: collapse; font-family: var(--font-ui); font-size: 0.8rem; }
.search-info-table th { text-align: left; font-weight: 700; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.66rem; padding: 0.35rem 0.5rem; border-bottom: 2px solid var(--border-color); white-space: nowrap; }
.search-info-table td { padding: 0.4rem 0.5rem; border-bottom: var(--border); color: var(--ink-2); vertical-align: top; }
.search-info-table td:first-child { color: var(--ink); font-weight: 600; white-space: nowrap; }
.search-info-editions { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.6rem; }
.si-ed-btn { font-family: var(--font-ui); font-size: 0.74rem; font-weight: 600; padding: 0.25rem 0.6rem; border: 1px solid var(--border-color); border-radius: 6px; background: var(--surface); color: var(--ink-3); cursor: pointer; transition: all 0.12s; }
/* Edition switcher is black/white inversion, NOT gold — same treatment as NPC
   card selection, where gold read as too loud on a dense card. --ink/--surface
   swap between themes, so `on` stays high-contrast either way; the literal
   #fff this replaced went invisible against a light selected chip in dark mode. */
.si-ed-btn:hover { border-color: var(--ink); color: var(--ink); }
.si-ed-btn.on { background: var(--ink); border-color: var(--ink); color: var(--surface); }
.si-differs { margin-left: auto; font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink); }
.si-differs.si-same { color: var(--ink-3); font-weight: 600; }
