/* ============================================================
   Inkara Admin Dashboard — Shared Styles
   Matches Widget Dashboard brand: Inter + DM Serif Display,
   warm cream/paper palette, rose/mauve accent
   ============================================================ */

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

:root {
    /* Brand tokens */
    --ink: #1a1a2e;
    --paper: #faf9f6;
    --accent: #af2860;
    --accent-dark: #8e1f4d;
    --accent-light: #f4e2e8;
    --border: #e2ddd5;
    --muted: #8a8477;
    --success: #2d6a4f;
    --danger: #c0392b;
    --warning: #f39c12;
    --radius: 6px;
    --sidebar-bg: #1a1a2e;
    --sidebar-hover: #2c2c44;
    --card-bg: #fff;

    /* Aliases for backward compat (used in views.js inline styles) */
    --bg: var(--paper);
    --primary: var(--accent);
    --primary-dark: var(--accent-dark);
    --text: var(--ink);
    --text-light: var(--muted);
}

body {
    font-family: 'Inter', system-ui, -apple-system, sans-serif;
    background: var(--paper);
    color: var(--ink);
    line-height: 1.5;
    min-height: 100vh;
}

.hidden { display: none !important; }

/* --- Login --- */
#login-screen {
    background: #faf9f6;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    padding-top: 60px;
}
.login-box {
    text-align: center;
    padding: 2.5rem 3rem;
    background: var(--card-bg);
    border-radius: var(--radius);
    box-shadow: 0 8px 24px rgba(0,0,0,0.08);
    width: 100%;
    max-width: 350px;
    height: fit-content;
}
/* The Set-Password (invite) form carries the Terms-of-Use box + agree check,
   so it needs more room than the slim sign-in card. Widen the login card only
   while that form is the visible one. */
.login-box:has(#change-pw-form:not(.hidden)) {
    max-width: 450px;
}
.login-logo {
    height: 80px;
    width: auto;
    margin-bottom: 1.25rem;
}
.login-heading {
    font-family: 'DM Serif Display', serif;
    font-size: 1.25rem;
    color: var(--ink);
    line-height: 1.4;
    margin-bottom: 1.5rem;
    letter-spacing: -0.3px;
}
.login-subtitle {
    font-size: 0.85rem;
    color: var(--muted);
    margin-bottom: 1.25rem;
    line-height: 1.5;
}
.login-field {
    text-align: left;
    margin-bottom: 1rem;
}
.login-field label {
    display: block;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 0.3rem;
}
.login-field input {
    width: 100%;
    padding: 0.6rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: 0.875rem;
    font-family: 'Inter', sans-serif;
    background: #fff;
    color: var(--ink);
    transition: border-color 0.15s;
}
.login-field input:focus {
    outline: none;
    border-color: var(--accent);
}
.btn-block {
    display: block;
    width: 100%;
    padding: 0.65rem 1rem;
    margin-top: 0.5rem;
}
.login-link {
    display: inline-block;
    margin-top: 1rem;
    font-size: 0.8rem;
    color: var(--muted);
    text-decoration: none;
    transition: color 0.15s;
}
.login-link:hover {
    color: var(--accent);
}
.login-error {
    background: #fdf0f0;
    border: 1px solid #e8c4c4;
    color: #8b2020;
    font-size: 0.8rem;
    padding: 0.6rem 0.75rem;
    border-radius: var(--radius);
    margin-bottom: 1rem;
    text-align: left;
}

/* --- Buttons --- */
.btn {
    display: inline-block;
    padding: 0.5rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card-bg);
    color: var(--ink);
    cursor: pointer;
    font-size: 0.875rem;
    font-family: 'Inter', sans-serif;
    font-weight: 500;
    text-decoration: none;
    transition: all 0.15s;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn-primary { background: var(--accent); color: #fff; border-color: var(--accent); }
.btn-primary:hover { background: var(--accent-dark); border-color: var(--accent-dark); color: #fff; }
.btn-success { background: var(--success); color: #fff; border-color: var(--success); }
.btn-success:hover { opacity: 0.9; }
.btn-danger { background: var(--danger); color: #fff; border-color: var(--danger); }
/* Explicit color/bg/border to win against the generic .btn:hover rule
   above (same specificity), which would otherwise turn the text accent-pink
   on the red bg — unreadable. Darker red maintains contrast. */
.btn-danger:hover { background: #a02822; border-color: #a02822; color: #fff; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-sm { padding: 0.25rem 0.75rem; font-size: 0.8rem; }
.btn-lg { padding: 0.75rem 2rem; font-size: 1rem; }
.btn-spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid rgba(255,255,255,0.3); border-top-color: #fff; border-radius: 50%; animation: spin 0.6s linear infinite; vertical-align: middle; margin-right: 4px; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Outline accent button — used by the Suggest buttons next to the
   BISAC/Thema hint fields on the event-edit modals. Visually it's an
   accent-coloured outline with no fill, going to a soft accent fill on
   hover. */
.btn-outline-accent { background: transparent; border: 1px solid var(--accent); color: var(--accent); }
.btn-outline-accent:hover { background: var(--accent-light); border-color: var(--accent); color: var(--accent); }
/* Editor header "Template" indicator — Inkara accent outline by default,
   flips to neutral muted on hover. Sits in the editor's header slot
   (#ie-template-slot) whenever an Inklet template is being edited. */
.me-template-badge {
    background: transparent;
    border: 1px solid var(--accent);
    color: var(--accent);
    border-radius: 6px;
    padding: 4px 10px;
    cursor: pointer;
    font-size: 0.82rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
    transition: border-color 0.15s ease, color 0.15s ease;
}
.me-template-badge:hover { border-color: var(--muted); color: var(--muted); }

/* In-form helper text under labelled inputs (BISAC/Thema hints,
   description fields, etc.). Sits muted + small so it reads as a
   sub-hint rather than competing with the input value. */
.field-help { font-size: 0.8rem; color: var(--muted); margin-top: 4px; line-height: 1.4; }

/* Modal form-field labels — one notch larger + semibold compared to the
   base `.field label` (12px / 500) defined in widget-shared.css. Applied
   to every form modal that uses .modal-user (user, channel, channel-group,
   event, settings-event, upload, etc.). */
.modal-user .field label { font-size: 13px; font-weight: 600; }

/* --- Action menu (3-dot dropdown) — still used by views-users.js;
 *     the Streams page has migrated to the unified Stream Edit modal
 *     below but the Users page hasn't yet. Keep both. --- */
.action-menu-btn { font-size: 1.2rem; line-height: 1; padding: 0.2rem 0.5rem; letter-spacing: 1px; background: var(--paper); border: 1px solid var(--accent); color: var(--accent); cursor: pointer; border-radius: 4px; transition: all 0.15s; font-weight: 900; }
.action-menu-btn:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
.action-menu { position: absolute; right: 0; top: 100%; background: #fff; border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 8px 24px rgba(0,0,0,0.08); z-index: 100; min-width: 150px; padding: 4px 0; }
.action-menu-item { display: block; width: 100%; padding: 8px 16px; border: none; background: none; text-align: left; cursor: pointer; font-size: 0.85rem; font-family: 'Inter', sans-serif; color: var(--ink); transition: background 0.15s, color 0.15s; }
.action-menu-item:hover { background: var(--accent-light); color: var(--accent); }

/* --- Stream Edit modal (Streams page row → unified four-tab dialog) --- */
/* Sized 60vw × 80vh per the approved plan. Header (title + close-X)
 * + tab bar + scrollable body are the three vertical sections. */
.modal.stream-edit-modal {
    width: 80vw;
    max-width: 80vw;
    height: 80vh;
    padding: 0;
    display: flex;
    flex-direction: column;
}
.stream-edit-header {
    padding: 16px 20px 12px;
    border-bottom: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
    flex-shrink: 0;
}
/* Tab bar inside the stream-edit modal — kill the default
 * margin-bottom so the body's own padding handles spacing. */
.modal.stream-edit-modal .tab-bar {
    padding: 0 20px;
    margin: 0;
    flex-shrink: 0;
}
/* Body has its own unique class (no clash with .modal) so the simple
 * selector works — earlier I chained .modal.stream-edit-modal-body by
 * accident which matched nothing, leaving the body padding-less. */
.modal.stream-edit-modal .stream-edit-modal-body {
    flex: 1;
    overflow-y: auto;
    padding: 24px 32px;
}

/* --- Layout --- */
#main-screen { display: flex; min-height: 100vh; }

.sidebar {
    width: 240px;
    background: var(--paper);
    color: var(--ink);
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0; bottom: 0;
    border-right: none;
    transition: width 0.2s ease;
    overflow: hidden;
    z-index: 50;
}
.sidebar-header { padding: 1.5rem; }
.sidebar-header-row { display: flex; align-items: center; justify-content: space-between; }
.sidebar-header img.sidebar-logo { height: 63px; width: auto; }
.sidebar-collapse-btn { background: none; border: none; cursor: pointer; padding: 4px; color: var(--muted); transition: color 0.15s; }
.sidebar-collapse-btn:hover { color: var(--ink); }
.sidebar-collapse-btn svg { width: 18px; height: 18px; }
.nav-links a.nav-disabled { color: var(--muted); opacity: 0.5; cursor: default; pointer-events: none; }
#nav-logout { color: var(--muted); }
#nav-logout:hover { color: var(--accent); background: rgba(175,40,96,0.04); }
.user-email { font-size: 0.75rem; color: var(--muted); word-break: break-all; display: block; margin-top: 0.35rem; }

.nav-links { list-style: none; padding: 0.5rem 0; flex: 1; }
.nav-links a {
    display: block;
    padding: 0.5rem 1.5rem;
    color: var(--ink);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 500;
    transition: all 0.15s;
    border-left: 3px solid transparent;
}
.nav-links a:hover, .nav-links a.active {
    background: rgba(175,40,96,0.04);
    color: var(--accent);
}
.nav-links a.active { border-left-color: var(--accent); }

/* Nav icons */
.nav-links a svg, .nav-accordion-toggle svg { width: 16px; height: 16px; vertical-align: -2px; margin-right: 8px; stroke-width: 2; }

/* Nav accordions */
.nav-accordion-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    border-left: none !important;
}
.nav-accordion-toggle::after {
    content: '\203A';
    font-size: 1.2rem;
    line-height: 1;
    color: var(--muted);
    transition: transform 0.2s ease;
    flex-shrink: 0;
    margin-left: 0.5rem;
    margin-right: 0.5rem;
    display: inline-block;
    vertical-align: -3px;
    transform: rotate(90deg);
}
.nav-accordion.open > .nav-accordion-toggle::after {
    transform: rotate(-90deg);
}
.nav-sub {
    list-style: none;
    display: none;
    padding: 0;
}
.nav-accordion.open > .nav-sub {
    display: block;
}
.nav-sub a {
    padding-left: 2.5rem !important;
    padding-top: 0.2rem !important;
    padding-bottom: 0.2rem !important;
    font-size: 0.85rem !important;
}
/* Second-level group inside an accordion's .nav-sub: a static non-clickable
   label with its child links indented one step deeper (Super Admin >
   Streams & Ingestion). */
.nav-sub-group-label {
    display: block;
    padding: 0.45rem 1.5rem 0.15rem 2.5rem;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
    cursor: default;
}
.nav-sub-group-label svg { width: 13px; height: 13px; vertical-align: -2px; margin-right: 6px; stroke-width: 2; }
.nav-sub a.nav-sub-child { padding-left: 3.25rem !important; }

.sidebar-user-card {
    margin: 0.75rem 0.75rem 0.75rem 1rem; padding: 0.75rem 1rem;
    border-radius: 10px; text-align: center;
    font-size: 0.8rem; line-height: 1.5;
}
.suc-info:hover { opacity: 0.85; }
#suc-account-filter-slot { padding: 0 1rem; }
.suc-account-select {
    width: 100%; padding: 5px 8px; margin-bottom: 10px;
    border: 1px solid var(--border); border-radius: 8px;
    background: #fff; color: var(--ink);
    font-size: 0.78rem; font-family: 'Inter', sans-serif;
    cursor: pointer; outline: none; display: block;
}
.sidebar-collapsed #suc-account-filter-slot { display: none; }
.suc-account-select:focus { border-color: var(--accent); }
.sidebar-user-card .suc-name { font-weight: 700; color: var(--ink); }
.sidebar-user-card .suc-role { color: var(--ink); opacity: 0.7; font-size: 0.75rem; }
.sidebar-user-card .suc-account { color: var(--ink); opacity: 0.7; font-size: 0.75rem; }
.sidebar-user-card .suc-email { color: var(--ink); opacity: 0.6; font-size: 0.7rem; word-break: break-all; }
.sidebar-collapsed .sidebar-user-card { display: none; }
.sidebar-user-avatar {
    display: none; /* hidden when expanded */
}
.sidebar-collapsed .sidebar-user-avatar {
    display: flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 50%;
    margin: 0 auto 12px; cursor: pointer;
    font-size: 0.85rem; font-weight: 700; color: var(--ink);
    transition: opacity 0.15s;
}
.sidebar-collapsed .sidebar-user-avatar:hover { opacity: 0.75; }

.sidebar-account { padding: 1rem 1.5rem; }
.sidebar-account label { display: block; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin-bottom: 0.4rem; }
.sidebar-account select { width: 100%; padding: 0.4rem 0.5rem; border: 1px solid var(--border); border-radius: var(--radius); background: #fff; color: var(--ink); font-size: 0.8rem; font-family: 'Inter', sans-serif; cursor: pointer; }
.sidebar-account select:focus { outline: none; border-color: var(--accent); }


.content { margin-left: 240px; padding: 2rem; flex: 1; min-width: 0; transition: margin-left 0.2s ease; }

/* --- Sidebar expand button (visible only when collapsed) --- */
.sidebar-expand { display: none; position: fixed; top: 14px; left: 14px; cursor: pointer; z-index: 100; }
.sidebar-expand-icon { width: 28px; height: 28px; border-radius: 4px; }
.sidebar-expand-icon:hover { opacity: 0.7; }

/* --- Sidebar collapsed state --- */
.sidebar-collapsed .sidebar { width: 56px; background: #f3efe8; }
.sidebar-collapsed .sidebar-header { padding: 0.75rem 0; border-bottom: 1px solid var(--border); }
.sidebar-collapsed .sidebar-header-row { justify-content: center; }
.sidebar-collapsed .sidebar-logo { display: none; }
.sidebar-collapsed .sidebar-collapse-btn { padding: 6px; }
.sidebar-collapsed .sidebar-collapse-btn svg { transform: rotate(180deg); }
.sidebar-collapsed .user-email { display: none; }
.sidebar-collapsed .nav-links a span,
.sidebar-collapsed .nav-accordion-toggle span { display: none; }
.sidebar-collapsed .nav-links a { padding: 0.75rem 0; text-align: center; border-left: none; }
.sidebar-collapsed .nav-links a svg { margin-right: 0; }
.sidebar-collapsed .nav-sub { display: none !important; }
.sidebar-collapsed .nav-accordion-toggle { justify-content: center; padding: 0.75rem 0; }
.sidebar-collapsed .nav-accordion-toggle::after { display: none; }

/* --- Flyout: sub-menu as a floating panel when sidebar is collapsed --- */
/* JS sets .flyout-open on the <li.nav-accordion> plus inline `top` on the
   .nav-sub so the popover aligns with the clicked icon. */
.sidebar-collapsed .nav-accordion.flyout-open .nav-sub {
    display: block !important;
    position: fixed;
    left: 56px;                /* matches .sidebar-collapsed .sidebar width */
    min-width: 200px;
    padding: 4px 0;
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
    z-index: 200;
    animation: flyoutFade 0.12s ease-out;
}
/* In flyout mode, items use regular horizontal padding (not the 2.5rem
   indent used by the in-sidebar expanded accordion). Show the text labels
   that are hidden in the collapsed sidebar itself. */
.sidebar-collapsed .nav-accordion.flyout-open .nav-sub a {
    padding: 0.5rem 1rem !important;
    font-size: 0.9rem !important;
    display: flex;
    align-items: center;
    gap: 8px;
    text-align: left;
}
.sidebar-collapsed .nav-accordion.flyout-open .nav-sub a span {
    display: inline;
}
.sidebar-collapsed .nav-accordion.flyout-open .nav-sub a svg {
    width: 14px;
    height: 14px;
    margin-right: 0;
}
/* Group label + its children inside a flyout panel: flatter padding to
   match the flyout's compact layout. */
.sidebar-collapsed .nav-accordion.flyout-open .nav-sub .nav-sub-group-label {
    padding: 0.5rem 1rem 0.2rem;
}
.sidebar-collapsed .nav-accordion.flyout-open .nav-sub a.nav-sub-child {
    padding-left: 1.75rem !important;
}
@keyframes flyoutFade {
    from { opacity: 0; transform: translateX(-6px); }
    to   { opacity: 1; transform: translateX(0); }
}
.sidebar-collapsed .nav-links a.active { border-left: none; color: var(--accent); }
/* sidebar-account removed — filter now in suc-account-filter-slot */
.sidebar-collapsed .content { margin-left: 56px; }
.sidebar-collapsed .sidebar-expand { display: none; }

/* --- Cards --- */
.card {
    background: var(--card-bg);
    border: 1px solid rgba(0,0,0,0.04);
    border-radius: 14px;
    box-shadow: 0 2px 12px rgba(0,0,0,0.08);
    padding: 1.5rem;
    margin-bottom: 1.5rem;
}
.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1rem;
}
.card-header h3 { font-family: 'DM Serif Display', serif; font-size: 1.1rem; letter-spacing: -0.3px; }

/* --- Stats row --- */
.stats-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 1rem;
    margin-bottom: 1.5rem;
}
.stat-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.25rem;
    box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}
.stat-card .label { font-size: 0.75rem; font-weight: 500; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.stat-card .value { font-size: 1.75rem; font-weight: 700; margin-top: 0.25rem; color: var(--ink); }
.stat-card .sub { font-size: 0.8rem; color: var(--muted); margin-top: 0.25rem; }

/* --- Overview mid-row -----------------------------------------------
 * 2-column row: Marketing Campaigns (70%) on the left, Events stacked
 * over Trending (30% column) on the right. The right column's height
 * matches the left exactly via flex:1 on each child card, so the gap
 * is consumed inside the column instead of overflowing it.
 * Marketing card uses flex-direction:column so its tab-bar pins to the
 * top and the empty body region fills the remaining space.
 */
.overview-mid-row {
    display: grid;
    grid-template-columns: 4fr 1fr;   /* 80% / 20% */
    gap: 1.5rem;
    margin-bottom: 1.5rem;
}
.overview-mid-row .card { margin-bottom: 0; }
/* Marketing Campaigns card grows to fit its content — initially 2
 * rows of book cards, more after the user clicks Load more. The
 * right column's height tracks the left automatically because both
 * are children of the same grid row (default align-items:stretch). */
.overview-mid-marketing-card {
    display: flex;
    flex-direction: column;
}
/* Bookmark tab is an icon-only button pinned to the right of the tab bar
   (margin-left:auto inline). Trim the icon down from Feather's default 24px
   and centre it within the tab-btn's padding box. */
.overview-mc-bookmarks-tab {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.overview-mc-bookmarks-tab svg {
    width: 17px;
    height: 17px;
}
.overview-mc-body {
    flex: 1;
    min-height: 0;
    margin-top: 1rem;
}
.overview-mid-right-col {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}
/* Right-column cards split available height evenly via flex:1, but
 * each is capped at 500px so a tall Marketing Campaigns panel
 * doesn't drag Events / Trending into the 600+ range. When the
 * marketing card grows past ~1000px the right column simply leaves
 * whitespace at its bottom rather than scaling its children further. */
.overview-mid-right-col .card {
    flex: 1;
    min-height: 0;
    max-height: 500px;
    overflow-y: auto;
}

/* ─── Overview > Events panel ──────────────────────────────────────
   Vertical stack of lozenges populated by /stream-events
   ?upcoming_only=true&limit=5. Three stacked rows per lozenge —
       row 1: icon (left) + recurrence badge (right)
       row 2: event name
       row 3: date window
*/
.overview-events-empty {
    padding: 1rem 0;
    color: var(--muted);
    font-size: 0.85rem;
}
/* Events panel header — title left, neutral-outline edit icon right (links to
   Settings > Events). */
.overview-events-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
.overview-events-edit-btn {
    color: var(--muted);
    border: 1px solid var(--border);
    border-radius: 6px;
    background: transparent;
    cursor: pointer;
    text-decoration: none;
    transition: color 0.15s, border-color 0.15s;
}
.overview-events-edit-btn:hover {
    color: var(--accent);
    border-color: var(--accent);
}
/* Trending placeholder — feature not built yet. */
.overview-trending-soon {
    padding: 1rem 0;
    color: var(--muted);
    font-size: 0.85rem;
}

/* ISBN editor modal (Phase 6) — 90vw×90vh overlay hosting Views.inkletEditor.
   No left nav (the overlay covers it); the mini hero sits 10px below the modal
   top; closes only via a save option or the hero ×. */
.ie-modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 100002;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
}
/* While the 95% editor modal (z 100002) is open, lift its CHILD dialogs (book
   picker, asset library, font browser, rewriting-text, Export Options — opened
   from inside the editor) above it. Scoped to body.ie-modal-open so the shared
   .preset-overlay / .modal-overlay / .series-info-save-overlay z-index is
   untouched everywhere else. The PARENT overlay the editor was launched over
   (e.g. the Book modal) is tagged .ie-behind on open so it stays UNDER the
   editor rather than being lifted with the child dialogs. */
body.ie-modal-open .preset-overlay:not(.ie-behind),
body.ie-modal-open .modal-overlay:not(.ie-behind),
body.ie-modal-open .series-info-save-overlay:not(.ie-behind) {
    z-index: 100010;
}
.ie-modal {
    width: 95vw;
    height: 95vh;
    background: var(--paper, #fff);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 12px 48px rgba(0, 0, 0, 0.30);
    display: flex;
    flex-direction: column;
}
.ie-modal-body {
    flex: 1;
    min-height: 0;
    overflow: auto;
    display: flex;
    flex-direction: column;
}
/* Plain header row (replaces the image hero in modal mode): black title left,
   neutral-outline controls right, 12px top/bottom padding, flush to the editor
   controls below (no other whitespace). */
.ie-modal-hero {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
/* Save dropdown → neutral outline (the base styling is white-on-translucent
   for the dark image hero, invisible on the light modal row). */
.ie-modal-hero .ie-save-dropdown-btn { border-color: var(--border); }
.ie-modal-hero .ie-save-main { background: #fff; color: var(--muted); }
.ie-modal-hero .ie-save-main:hover { background: var(--cream); color: var(--accent); }
.ie-modal-hero .ie-save-caret { background: #fff; color: var(--muted); border-left-color: var(--border); }
.ie-modal-hero .ie-save-caret:hover { background: var(--cream); color: var(--accent); }
/* Editor controls sit flush under the row. */
.ie-modal-body #ie-editor-body { flex: 1; min-height: 0; margin: 0; }
.ie-modal-body #ie-editor-body .widget-editor-layout { margin: 0; }
.overview-event-lozenge {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 0.75rem 0;
    border-top: 1px solid var(--border);
}
.overview-event-lozenge:first-child { border-top: none; }
.overview-event-header {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 0.75rem;
    margin-bottom: 2px;
}
.overview-event-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
    position: relative;     /* anchor for the .is-loading ::after spinner */
}
.overview-event-icon svg {
    width: 100%;
    height: 100%;
    display: block;
    transition: opacity 0.15s;
}
/* Icon becomes a clickable trigger when wired to load matching books. */
.overview-event-icon[data-trigger="load-books"] {
    cursor: pointer;
    border-radius: 4px;
    padding: 4px;
    margin: -4px;            /* keep the 20×20 visual size despite padding */
    box-sizing: content-box;
    transition: background 0.1s;
}
.overview-event-icon[data-trigger="load-books"]:hover {
    background: var(--accent-light);
}
/* While the books request is in flight, dim the icon SVG and overlay a
   small spinner. Spinner uses the .btn-spinner @keyframes already in style.css. */
.overview-event-icon.is-loading svg { opacity: 0.2; }
.overview-event-icon.is-loading::after {
    content: '';
    position: absolute;
    width: 14px; height: 14px;
    border: 2px solid var(--accent);
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}
.overview-event-name {
    font-weight: 600;
    font-size: 0.95rem;
    line-height: 1.25;
    min-width: 0;
}
.overview-event-window {
    color: var(--muted);
    font-size: 0.8rem;
}
.overview-event-badge {
    flex-shrink: 0;
    font-size: 0.7rem;
}

/* ─── Book Match modal (Overview > Events calendar-icon click) ──────
   Three-step flow: pick books → pick tagline → preview + Generate.
   Sizing mirrors the multi-editor's ISBN book picker so curators get
   a consistent "picker" feel across the dashboard. Layout class lives
   here; positioning + backdrop are inline-styled in
   views-overview.js::_overviewOpenBookMatchModal because they reuse
   the lib-modal pattern (z-index 99999, fixed inset). */
.bm-modal-inner {
    display: flex;
    flex-direction: column;
    padding: 0;
}
.bm-header {
    padding: 16px 20px 12px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}
.bm-header h2 {
    font-family: 'DM Serif Display', serif;
    margin: 0 0 4px;
    font-size: 1.25rem;
    color: var(--ink);
}
.bm-header .bm-subtitle {
    color: var(--muted);
    font-size: 0.9rem;
}
.bm-back {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--accent);
    font-size: 0.85rem;
    text-decoration: none;
    cursor: pointer;
    padding: 0;
    border: none;
    background: none;
    margin-top: 8px;
}
.bm-back:hover { text-decoration: underline; }
.bm-body {
    flex: 1;
    overflow-y: auto;
    padding: 16px 20px;
}
/* Footer is two-sided so the Back button can live bottom-LEFT and the
   step's primary action sits bottom-RIGHT. When the footer holds only
   one child (Step 1's solo [Add (n)] or Step 3's solo [Back]) the
   :has() rule keeps it right-anchored vs anchored to the start so the
   layout doesn't drift between steps. */
.bm-footer {
    padding: 12px 20px;
    border-top: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}
.bm-footer:has(> :only-child:not(.btn-outline-accent)) { justify-content: flex-end; }

/* Step 1 — 2-col book grid. Each lozenge is a flex row: thumb left,
   four-line meta block right. Selected state outlines + tints with
   the Inkara accent. */
.bm-books-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}
.bm-book-lozenge {
    position: relative;     /* anchor for the book-open icon top-right */
    display: flex;
    gap: 12px;
    padding: 10px;
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    background: #fff;
    transition: border-color 120ms, background 120ms;
}
.bm-book-lozenge:hover { border-color: var(--accent); }
.bm-book-lozenge.bm-selected {
    border-color: var(--accent);
    background: var(--accent-light);
}
.bm-book-lozenge img {
    width: 64px;
    height: 96px;
    object-fit: cover;
    flex-shrink: 0;
    border: 1px solid var(--border);
}
.bm-book-lozenge .bm-meta {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    font-size: 0.85rem;
}
.bm-book-lozenge .bm-title {
    font-weight: 600;
    color: var(--ink);
    line-height: 1.25;
}
.bm-book-lozenge .bm-subtitle {
    color: var(--muted);
    font-style: italic;
    font-size: 0.8rem;
    line-height: 1.3;
}
.bm-book-lozenge .bm-isbn-format,
.bm-book-lozenge .bm-pubdate {
    color: var(--muted);
    font-size: 0.78rem;
}
/* Top-right book-open icon — opens the book detail modal without
   toggling the lozenge's selection state (click handler calls
   stopPropagation). Padding around the icon keeps it well clear of
   the rounded lozenge border. */
.bm-book-lozenge .bm-book-open-icon {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 28px;
    height: 28px;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 4px;
    color: var(--muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: color 120ms, background 120ms;
}
.bm-book-lozenge .bm-book-open-icon:hover {
    color: var(--accent);
    background: var(--accent-light);
}
/* Push the meta column slightly so the title row never collides with
   the absolutely-positioned icon on very narrow viewports. */
.bm-book-lozenge .bm-meta { padding-right: 28px; }

/* Step 2 — full-width single-select tagline lozenges. */
.bm-tagline-lozenge {
    padding: 14px 18px;
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    background: #fff;
    margin-bottom: 10px;
    font-size: 0.95rem;
    color: var(--ink);
    transition: border-color 120ms, background 120ms;
}
.bm-tagline-lozenge:hover { border-color: var(--accent); }
.bm-tagline-lozenge.bm-selected {
    border-color: var(--accent);
    background: var(--accent-light);
    font-weight: 600;
}
/* Custom tagline (the 5th option) — the lozenge wraps a borderless input; a
   hint + live character count sit underneath. */
.bm-custom-tagline { padding: 0; cursor: text; margin-bottom: 4px; }
.bm-custom-tagline-input {
    width: 100%;
    box-sizing: border-box;
    border: none;
    outline: none;
    background: transparent;
    padding: 14px 18px;
    border-radius: 6px;
    font-size: 0.95rem;
    color: var(--ink);
}
.bm-custom-tagline-input::placeholder { color: var(--muted); }
.bm-custom-tagline-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin: 0 2px 10px;
    font-size: 0.78rem;
    color: var(--muted);
}

/* Step 3 — centred tagline above either a horizontal thumb row
   (initial state) OR the generated campaign image (post-generate);
   Generate / Regenerate button sits below. */
.bm-step3-tagline {
    text-align: center;
    font-family: 'DM Serif Display', serif;
    font-size: 1.3rem;
    margin: 4px 0 6px;
    color: var(--ink);
}
.bm-step3-thumbs {
    display: flex;
    gap: 12px;
    justify-content: center;
    margin-bottom: 24px;
}
.bm-step3-thumbs img {
    height: 120px;
    object-fit: cover;
    border: 1px solid var(--border);
}
/* Generated-campaign image replaces the thumb row once the OpenAI
   call completes. Clickable to open at full size in a new tab. */
.bm-step3-image {
    display: flex;
    justify-content: center;
    margin-bottom: 16px;
}
.bm-step3-image a { display: block; }
.bm-step3-image img {
    max-width: 100%;
    max-height: 45vh;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: #f4f4f6;
}
/* Amber stale-prompt notice — fires when the curator edited the
   prompt after this campaign was generated. Mirrors the wording of
   the Marketing tab's stale hint so curators see a consistent
   pattern. */
.bm-step3-stale {
    text-align: center;
    margin: 4px 0 16px;
    padding: 8px 14px;
    background: #fff3cd;
    color: #856404;
    border-radius: 4px;
    font-size: 0.85rem;
}
.bm-step3-actions {
    display: flex;
    justify-content: center;
}

/* ── Event Match modal polish pass (Step 2 refresh + Step 3 label /
   format line / greyscale placeholder / selected-thumb row) ─────── */

/* Step 2 — small icon-only refresh-cw button under the tagline
   lozenges. Circular outline, muted by default, accent on hover. */
.bm-refresh-taglines-row { text-align: center; margin-top: 12px; }
.bm-refresh-taglines {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    padding: 0;
    transition: color 120ms, border-color 120ms;
}
.bm-refresh-taglines:hover {
    color: var(--accent);
    border-color: var(--accent);
}
.bm-refresh-taglines:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Step 3 — small all-caps "MARKETING IDEA" label above the tagline.
   Matches the attached design (Inkara accent-pink, bold, letter-
   spaced). The format line under it is the muted format descriptor. */
.bm-step3-section-label {
    text-align: center;
    color: #b73b73;
    font-weight: 800;
    font-size: 0.95rem;
    letter-spacing: 1px;
    text-transform: uppercase;
    margin: 8px 0 4px;
}
.bm-step3-format-line {
    text-align: center;
    color: var(--muted);
    font-size: 0.82rem;
    margin-bottom: 16px;
}

/* Step 3 — greyscale sample-asset placeholder shown when no campaign
   has been generated for this selection but the prompt has been used
   on a different event/book combination. Visual language matches the
   Marketing tab's `.marketing-prompt-card-thumb.placeholder` pattern. */
.bm-step3-placeholder {
    display: flex;
    justify-content: center;
    margin-bottom: 12px;
}
/* Anchor wrapper — `position:relative` makes the eye icon + Sample label
   anchor against the image (not the row). `inline-block` so the wrapper
   sizes exactly to the image's natural width (since we set
   `width:auto` on the img to preserve aspect ratio). */
.bm-step3-placeholder a {
    display: inline-block;
    position: relative;
    border-radius: 4px;
    overflow: hidden;
    transition: transform 160ms ease, box-shadow 160ms ease;
}
.bm-step3-placeholder img {
    max-width: 100%;
    /* Smaller than the full Step-3 image (which uses max-height: 40vh)
       so the placeholder reads as "preview / not your asset yet" — the
       monochrome treatment + the smaller height combine to de-emphasise. */
    height: 225px;
    width: auto;
    border: 1px solid var(--border);
    border-radius: 4px;
    filter: grayscale(100%);
    opacity: 0.5;
    display: block;
    transition: filter 160ms ease, opacity 160ms ease;
}
/* "SAMPLE" watermark — centred over the image (both axes). Light,
   semi-transparent type with wide letter-spacing reads as a watermark
   rather than a UI control. Text-shadow gives readability on any
   underlying cover without needing a backdrop band. */
.bm-step3-placeholder-label {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: rgba(255, 255, 255, 0.82);
    font-size: 1.5rem;
    font-weight: 400;
    letter-spacing: 0.25em;
    text-transform: uppercase;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.65);
    pointer-events: none;
    transition: color 160ms ease;
    white-space: nowrap;
}
/* Rollover — stays monochrome (no colour bleed). Lift opacity, raise
   the tile, brighten the watermark. */
.bm-step3-placeholder a:hover img {
    opacity: 0.85;
}
.bm-step3-placeholder a:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
}
.bm-step3-placeholder a:hover .bm-step3-placeholder-label {
    color: rgba(255, 255, 255, 1);
}

/* Step 3 — small selected-book thumb row. Only visible when no image
   has been generated yet for the current selection. */
.bm-step3-selected-thumbs {
    display: flex;
    gap: 10px;
    justify-content: center;
    margin-bottom: 20px;
    flex-wrap: wrap;
}
.bm-step3-selected-thumbs img {
    height: 90px;
    width: 60px;
    object-fit: cover;
    border: 1px solid var(--border);
    border-radius: 2px;
}

/* ── Multi-Book asset modal (migrations 071+072) ────────────────────
   openMultiBookAssetModal in views-books.js.
   - .bm-step-subtitle — generic muted subtitle used on Step 1 + 2.
   - .bm-book-lozenge-grid + .bm-book-lozenge — Step 1's book picker
     (current book pre-selected; click toggles; range enforced).
   - .bm-empty — Step 1 / Step 2 empty-state filler.
   - .mba-customtext-row — Step 2 input rows (one per
     {customTextLabel-N:::…} parsed from the prompt body).
   - .mba-step3-ct-echo — Step 3 stack of "Label: value" rows showing
     what the curator typed in Step 2.
*/
.bm-step-subtitle {
    text-align: center;
    color: var(--muted);
    font-size: 0.95rem;
    margin: 0 0 14px;
}
.bm-empty {
    text-align: center;
    color: var(--muted);
    font-size: 0.9rem;
    padding: 24px;
    background: #fafafa;
    border-radius: 6px;
}
.bm-book-lozenge-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    margin: 0 0 18px;
}
.bm-book-lozenge {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    font-size: 0.9rem;
    text-align: left;
    transition: border-color 0.12s, background 0.12s;
}
.bm-book-lozenge:hover { border-color: var(--accent); }
.bm-book-lozenge.selected {
    border-color: var(--accent);
    background: rgba(67, 56, 202, 0.06);
}
.bm-book-lozenge img {
    width: 30px;
    height: 45px;
    object-fit: cover;
    border-radius: 2px;
    flex-shrink: 0;
}
.bm-book-lozenge-title {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.mba-customtext-row { margin-bottom: 16px; }
.mba-customtext-row label {
    display: block;
    font-size: 0.85rem;
    color: var(--muted);
    margin-bottom: 6px;
}
.mba-customtext-row input,
.mba-customtext-row textarea {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-size: 0.95rem;
    box-sizing: border-box;
    font-family: inherit;
}
.mba-customtext-row textarea {
    /* 3-line height; let users resize taller if they want more room. */
    min-height: calc(1.45em * 3 + 22px);
    resize: vertical;
    line-height: 1.45;
}
.mba-customtext-row input:focus,
.mba-customtext-row textarea:focus { outline: none; border-color: var(--accent); }
/* All-caps per-slot toggle under the textarea. Small inline checkbox +
   label. When the row gets `.mba-caps`, both chips and the textarea
   render uppercased via `text-transform`. The underlying value stays
   canonical — the transform applies to display + (at serialization
   time) the payload sent downstream. */
.mba-caps-toggle-label {
    display: inline-flex !important;
    align-items: center;
    gap: 6px;
    margin: 8px 0 0 !important;
    font-size: 0.8rem;
    color: var(--muted);
    cursor: pointer;
    user-select: none;
}
.mba-caps-toggle-label input[type="checkbox"] {
    width: auto;
    margin: 0;
    cursor: pointer;
}
.mba-customtext-row.mba-caps textarea,
.mba-customtext-row.mba-caps .mba-preset-chip,
.mba-customtext-row.mba-caps .mba-preset-generate {
    text-transform: uppercase;
}
/* Per-slot preset chips above each input. Curator-managed list of pre-coded
   one-line strings (stored at
   `ai_prompts.parameters.custom_text_presets[slotKey]`). Clicking a chip
   fills the input below; typing into the input clears the highlight. The
   row scrolls horizontally if the curator added more chips than fit. */
/* Amazon A+ 3-panel form — per-panel "Review / Marketing copy" segmented toggle. */
.aap-mode {
    font-size: 0.72rem;
    padding: 3px 11px;
    border: 1px solid var(--border);
    background: #fff;
    color: var(--muted);
    border-radius: 14px;
    cursor: pointer;
    font-weight: 500;
}
.aap-mode.active {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
}

/* Amazon A+ composed form — tabbed steps (2026-07-09). One numbered tab per
   step (ISBN + one per panel); tabs + description + textarea are pinned in
   the modal frame and only .aap-pane-scroll (chips / book picker) scrolls. */
.aap-tabs {
    display: flex;
    gap: 0;
    padding: 0 20px;
    border-bottom: 2px solid var(--border);
    flex-shrink: 0;
}
.aap-tab-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 0.55rem 0.9rem;
    border: none;
    background: none;
    cursor: pointer;
    font-family: 'Inter', sans-serif;
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--muted);
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    transition: color 0.15s, border-color 0.15s;
    white-space: nowrap;
}
.aap-tab-btn:hover { color: var(--ink); }
.aap-tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
.aap-step-circle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 0.78rem;
    font-weight: 600;
    flex-shrink: 0;
}
/* Overrides .bm-body's own overflow-y:auto for THIS modal only: the body is
   a fixed flex frame; the active pane owns the scroll region. */
.aap-body {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.aap-pane { display: none; }
.aap-pane.active {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}
.aap-tab-desc {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--muted);
    font-size: 0.85rem;
    margin: 2px 0 8px;
    flex-shrink: 0;
}
.aap-pane-scroll {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}
/* Panel-pane 50/50 split: textarea + chips on the left (chips scroll under
   the pinned textarea), live preview on the right, generous gutter. Same
   layout for landscape, square and portrait formats. */
.aap-cols {
    display: flex;
    gap: 48px;
    flex: 1;
    min-height: 0;
}
.aap-col-left {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
}
.aap-col-left .mba-ct-input {
    width: 100%;
    flex-shrink: 0;
    margin-bottom: 6px;
}
.aap-col-right {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
}
.aap-preview-hint {
    color: var(--muted);
    font-size: 0.78rem;
    margin-top: 6px;
}
/* Missing-assets "Generate now": translucent overlay over the whole form
   card while the Branding jobs run (quill + copy + Minimise button). */
.aap-genwait {
    position: absolute;
    inset: 0;
    z-index: 5;
    background: rgba(255, 255, 255, 0.96);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    text-align: center;
    padding: 24px;
}
/* Minimised form: pill docked to the right edge of the screen. Sits ABOVE
   every modal overlay (form 99999, quill dialogs 100001) so the generating /
   ready state stays visible wherever the curator navigates — including into
   the Book modal to watch the Branding tab. */
.aap-dock {
    position: fixed;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 100002;
    background: var(--accent);
    color: #fff;
    border: none;
    border-radius: 8px 0 0 8px;
    padding: 14px 14px;
    font-family: 'Inter', sans-serif;
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.35;
    text-align: left;
    max-width: 190px;
    cursor: pointer;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25);
}
.aap-dock:hover { background: var(--accent-dark); }
.aap-dock--ready { animation: aapDockPulse 1.6s ease-in-out infinite; }
@keyframes aapDockPulse {
    0%, 100% { box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25); transform: translateY(-50%) scale(1); }
    50%      { box-shadow: 0 2px 18px rgba(175, 40, 96, 0.6); transform: translateY(-50%) scale(1.05); }
}
.aap-dock--failed { background: #a12734; animation: none; }
.aap-panel-preview {
    position: relative;
    width: 100%;
    max-height: 100%;
    background-size: cover;
    background-position: center;
    border: 1px solid var(--border);
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-shrink: 0;
}
.aap-panel-preview[hidden] { display: none; }
/* Middle panel with the 3D cover showing on its right half: centre the
   text within the LEFT half (matches the real render's layout). */
.aap-panel-preview--cover {
    justify-content: flex-start;
}
.aap-panel-preview--cover .aap-panel-preview-text {
    width: 50%;
}
/* Middle panel only: the 3D cover the real render composites on the right. */
.aap-panel-preview-cover {
    position: absolute;
    right: 3%;
    top: 50%;
    transform: translateY(-50%);
    height: 84%;
    width: auto;
    pointer-events: none;
}
.aap-panel-preview-cover[hidden] { display: none; }
.aap-edit-zone--side .aap-panel-preview {
    width: 200px;
}
.aap-panel-preview-text {
    text-align: center;
    color: #fff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6), 0 0 12px rgba(0, 0, 0, 0.35);
    padding: 4% 6%;
    max-height: 100%;
    overflow: hidden;
    line-height: 1.3;
    word-break: break-word;
}

.mba-preset-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    /* Chips sit BELOW the textarea now (text-field → chips → caps toggle),
       so the margin shape is top-only — separates chips from the input
       above and lets the caps-toggle's own top margin handle the gap
       below. */
    margin: 8px 0 0;
    /* No horizontal scroll — wrap is more readable when the chip count is
       modest. If a future prompt has 20+ chips per slot we can revisit. */
}
.mba-preset-chip {
    background: #f4f4f6;
    border: 1px solid var(--border);
    /* Was `border-radius: 999px` (pill). Long, wrapped chips need
       softer-but-still-square corners — pure pills look strange when
       the chip spans 2-3 lines. */
    border-radius: 8px;
    padding: 6px 12px;
    font-size: 0.8rem;
    font-family: inherit;
    color: var(--ink);
    cursor: pointer;
    /* Allow long preset strings (e.g. resolved `{review1-20}` quotes) to
       wrap inside the chip instead of overflowing the row. */
    white-space: normal;
    text-align: left;
    /* Keep individual chips from growing wider than the row's content
       column on wide modals — gives the chip row a "tile" feel rather
       than one giant chip spanning the modal. */
    max-width: 100%;
    line-height: 1.35;
    transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}
.mba-preset-chip:hover { background: #ebebef; border-color: #bbb; }
.mba-preset-chip.active {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
    font-weight: 600;
}
/* AI-generate sibling of the preset chip: same dimensions, outline-accent
   treatment so it visually invites a click but reads as "different action"
   from the value-fill chips next to it. Each click hits
   /books/{id}/generate-tagline with the slot's configured prompt_code
   and replaces the input value with the model's suggestion. */
.mba-preset-generate {
    background: transparent;
    border: 1px solid var(--accent);
    color: var(--accent);
    border-radius: 999px;
    padding: 4px 12px;
    font-size: 0.8rem;
    font-family: inherit;
    font-weight: 600;
    line-height: 1.3;
    cursor: pointer;
    white-space: nowrap;
    transition: background 120ms ease, color 120ms ease;
}
.mba-preset-generate:hover { background: var(--accent); color: #fff; }
.mba-preset-generate:disabled,
.mba-preset-generate.mba-preset-generate-loading {
    opacity: 0.65;
    cursor: progress;
}
.mba-step3-ct-echo {
    margin: 12px 0 16px;
    padding: 12px 14px;
    background: #fafafa;
    border-radius: 6px;
    font-size: 0.9rem;
}
.mba-step3-ct-row { padding: 2px 0; }
.mba-step3-ct-label {
    color: var(--muted);
    font-weight: 500;
}
.mba-step3-ct-value { color: var(--text); }

/* Scaled-down (~75%) variant of .campaign-grid for the Overview's
 * Marketing Campaigns panel. Reuses the .campaign-book-card markup
 * unchanged — the descendant selectors below trim paddings + font
 * sizes so the cards display ~3/4 the size of the Campaigns page.
 * Grid keeps 5 columns; the narrower panel + scaled-down internals
 * compound to deliver the visual 75%. */
/* Grey per-tab subtitle under the Marketing-Campaigns tab bar — reuses
   .section-title-pair .section-subtitle for the grey colour, but centred
   and sized to match the .tab-btn font (0.85rem) above it. */
.overview-mc-subtitle-wrap { margin: 4px 0 14px; justify-content: center; }
/* id selector (1,0,0) so it beats the later, equal-specificity shared
   rule .section-title-pair .section-subtitle (0,2,0) regardless of order. */
#overview-mc-subtitle { font-size: 0.85rem; font-style: italic; }
.overview-mc-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    /* 50% wider gutters than the original 0.75rem so the cards
       breathe better against each other and against the panel edge. */
    gap: 1.125rem;
}
@media (max-width: 1200px) { .overview-mc-grid { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 900px)  { .overview-mc-grid { grid-template-columns: repeat(3, 1fr); } }
.overview-mc-grid .campaign-book-card { border-radius: 8px; }
.overview-mc-grid .campaign-book-meta { padding: 9px 11px; }
.overview-mc-grid .campaign-book-format { font-size: 0.75rem; color: var(--muted); }
.overview-mc-grid .campaign-book-date   { font-size: 0.75rem; color: var(--muted); margin-top: 3px; }
.overview-mc-grid .campaign-book-view-btn { font-size: 0.7rem; padding: 4px 10px; }

/* When there's no cover image the placeholder shows the title +
 * author centred + vertically aligned in the cover box (instead of
 * the generic "No cover" string the campaigns page uses). Scoped to
 * the overview surfaces so the campaigns page's existing empty
 * state stays unchanged. */
.overview-mc-grid .campaign-book-cover-empty,
.overview-mc-stack-modal-grid .campaign-book-cover-empty {
    flex-direction: column;
    padding: 12px;
    text-align: center;
    box-sizing: border-box;
    color: #475569;
    font-size: 0.85rem;
}
.overview-mc-grid .campaign-book-cover-empty-title,
.overview-mc-stack-modal-grid .campaign-book-cover-empty-title {
    font-weight: 600;
    line-height: 1.25;
}
.overview-mc-grid .campaign-book-cover-empty-author,
.overview-mc-stack-modal-grid .campaign-book-cover-empty-author {
    margin-top: 6px;
    font-size: 0.75rem;
    color: #64748b;
    line-height: 1.25;
}

/* Card wrapper — every card is wrapped (singleton or stack) so that
 * single→stack upgrades on Load-more can be done by outerHTML-
 * replacing the wrapper in place. The multi-format indicator is now
 * a Feather `layers` icon inside the card body (see .campaign-book-
 * stack-icon below) — no outside-the-card badge any more. */
/* Stack wrapper styling applied to BOTH the panel grid AND the
 * formats modal — so cards inside the modal match the overview
 * cards 1:1 (hover behaviour, cursor, structure). */
.overview-mc-grid .campaign-book-stack,
.overview-mc-stack-modal-grid .campaign-book-stack {
    position: relative;
    cursor: pointer;
}
.overview-mc-grid .campaign-book-card,
.overview-mc-stack-modal-grid .campaign-book-card {
    transition: filter 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
/* Hover darken — sits on top of the existing .campaign-book-card
 * shadow + translate-Y effects (defined further down). brightness()
 * dims every pixel uniformly so the cover, the format / layers icon,
 * and the inline date all darken together. */
.overview-mc-grid .campaign-book-stack:hover .campaign-book-card,
.overview-mc-stack-modal-grid .campaign-book-stack:hover .campaign-book-card {
    filter: brightness(0.93);
}

/* Format row: holds either the format label OR (for multi-format
 * stack cards) a left-aligned Feather `layers` icon in its place.
 * Single column, left-aligned — applied to both the panel grid and
 * the formats modal grid (modal cards always show the format text). */
.overview-mc-grid .campaign-book-format-row,
.overview-mc-stack-modal-grid .campaign-book-format-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.overview-mc-grid .campaign-book-stack-icon,
.overview-mc-stack-modal-grid .campaign-book-stack-icon {
    width: 14px;
    height: 14px;
    color: var(--muted);
    flex-shrink: 0;
}
.overview-mc-grid .campaign-book-stack-icon svg,
.overview-mc-stack-modal-grid .campaign-book-stack-icon svg {
    width: 14px;
    height: 14px;
    stroke: var(--muted);
}

/* Stack modal — lists every edition in the clicked group as full-
 * size .campaign-book-card (no .overview-mc-grid descendant scale-
 * downs apply here). 3-col grid sized to fit ~5 editions max
 * comfortably. */
.overview-mc-stack-modal {
    position: fixed; inset: 0; z-index: 99999;
    background: rgba(26,26,46,0.45);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center;
    animation: fadeIn 0.2s ease;
}
.overview-mc-stack-modal-inner {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    width: 760px; max-width: 92vw;
    max-height: 88vh;
    padding: 0;
    display: flex; flex-direction: column;
    box-shadow: 0 8px 32px rgba(0,0,0,0.25);
    overflow: hidden;
}
.overview-mc-stack-modal-shell { display: flex; flex-direction: column; height: 100%; }
.overview-mc-stack-modal-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    padding: 1.25rem 1.5rem 1rem;
    border-bottom: 1px solid var(--border);
}
.overview-mc-stack-modal-title {
    font-family: 'DM Serif Display', serif;
    font-size: 1.1rem;
    letter-spacing: -0.3px;
    color: var(--ink);
}
.overview-mc-stack-modal-sub {
    font-size: 0.8rem;
    color: var(--muted);
    margin-top: 4px;
}
.overview-mc-stack-modal-close {
    background: none; border: none;
    font-size: 1.5rem; line-height: 1;
    cursor: pointer; color: var(--muted);
    padding: 0 0.25rem;
}
.overview-mc-stack-modal-close:hover { color: var(--ink); }
.overview-mc-stack-modal-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 0.75rem;
    padding: 1.25rem 1.5rem 1.5rem;
    overflow-y: auto;
}
/* Mirror the .overview-mc-grid scale-downs so cards in the modal
 * look identical to the cards in the panel — same paddings + font
 * sizes, same rounded-corner radius, same per-line meta lines. */
.overview-mc-stack-modal-grid .campaign-book-card { border-radius: 8px; }
.overview-mc-stack-modal-grid .campaign-book-meta { padding: 9px 11px; }
.overview-mc-stack-modal-grid .campaign-book-format { font-size: 0.75rem; color: var(--muted); }
.overview-mc-stack-modal-grid .campaign-book-date   { font-size: 0.75rem; color: var(--muted); margin-top: 3px; }
.overview-mc-stack-modal-grid .campaign-book-view-btn { font-size: 0.7rem; padding: 4px 10px; }

/* --- Tables --- */
.table-wrap { overflow: visible; }
table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
th { text-align: left; padding: 0.75rem; border-bottom: 2px solid var(--border); color: var(--muted); font-weight: 600; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap; }
td { padding: 0.75rem; border-bottom: 1px solid var(--border); }
tr:hover td { background: #f8f6f2; }

/* --- Badges --- */
.badge {
    display: inline-block;
    padding: 0.2rem 0.5rem;
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 600;
    white-space: nowrap;
}
.badge-active { background: #d4edda; color: #155724; }
.badge-inactive { background: #f8d7da; color: #721c24; }
.badge-pending { background: #fff3cd; color: #856404; }
.badge-hidden { background: #fff3cd; color: #856404; }
.badge-primary { background: var(--accent-light); color: var(--accent); }
.badge-unmatched { background: #ffe0b2; color: #e65100; }
.badge-failed { background: #f8d7da; color: #721c24; }
.badge-succeeded { background: #d4edda; color: #155724; }
.badge-processing { background: #d1ecf1; color: #0c5460; }

/* Feedback status badges */
.badge-new { background: #dbeafe; color: #1d4ed8; }
.badge-open { background: #fef3c7; color: #92400e; }
.badge-awaiting-user { background: #fef3c7; color: #92400e; }
.badge-awaiting-inkara { background: #ede9fe; color: #5b21b6; }
.badge-closed { background: #d1fae5; color: #065f46; }

/* Feedback table + modal */
.feedback-table tr { cursor: pointer; }
.feedback-clip { vertical-align: middle; }
th[data-sort-key] { cursor: pointer; user-select: none; }
th[data-sort-key]:hover { color: var(--accent); }
th .sort-arrow { font-size: 0.65rem; margin-left: 4px; }
/* Higher specificity than the base `.modal` rule (which is declared LATER in
   this file with max-height:80vh) so the feedback modal's taller cap wins. */
.modal.modal-feedback { max-width: 900px; max-height: 95vh; overflow-y: auto; }
.feedback-detail-grid {
    display: grid; grid-template-columns: auto 1fr; gap: 4px 12px;
    font-size: 0.85rem; margin-bottom: 16px;
}
.feedback-detail-label { color: var(--muted); font-weight: 500; }
.feedback-detail-comment {
    background: var(--cream, #faf9f5); border: 1px solid var(--border);
    border-radius: 6px; padding: 12px; margin: 0 0 16px;
    white-space: pre-wrap; font-size: 0.85rem; line-height: 1.5;
}
.feedback-detail-screenshot img {
    width: 100%; border-radius: 6px; border: 1px solid var(--border); margin-top: 8px;
    cursor: pointer; transition: opacity 0.15s;
}
.feedback-detail-screenshot img:hover { opacity: 0.85; }

/* Feedback thread — message bubbles inside the detail modal */
.fb-thread {
    max-height: 360px;
    overflow-y: auto;
    margin-bottom: 16px;
    padding: 8px 0;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}
.fb-msg {
    margin-bottom: 12px;
    padding: 10px 12px;
    border-radius: 8px;
    font-size: 0.85rem;
    line-height: 1.5;
}
.fb-msg-user {
    background: var(--cream, #faf9f5);
    border: 1px solid var(--border);
    margin-right: 24px;
}
.fb-msg-admin {
    background: #eef2ff;
    border: 1px solid #c7d2fe;
    margin-left: 24px;
}
.fb-msg-header {
    font-size: 0.75rem;
    color: var(--muted);
    margin-bottom: 4px;
}
.fb-msg-email { margin-left: 4px; }
.fb-msg-time { margin-left: 8px; }
.fb-msg-body { white-space: pre-wrap; word-break: break-word; }
.fb-msg-screenshot img {
    max-width: 100%;
    border-radius: 4px;
    border: 1px solid var(--border);
    margin-top: 6px;
    cursor: pointer;
    max-height: 200px;
    object-fit: cover;
}
.fb-msg-screenshot img:hover { opacity: 0.85; }

/* Reply form */
.fb-reply-form {
    margin-top: 8px;
}
.fb-reply-textarea {
    width: 100%;
    box-sizing: border-box;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 10px;
    font-size: 0.85rem;
    font-family: 'Inter', sans-serif;
    resize: vertical;
    min-height: 80px;
    background: var(--paper, #fff);
    color: var(--ink);
}
.fb-reply-textarea:focus { outline: none; border-color: var(--accent); }

/* Feedback screenshot lightbox — full-screen overlay with a dark top bar */
.feedback-lightbox {
    position: fixed; inset: 0; z-index: 100000;
    background: rgba(0,0,0,0.92);
    display: flex; flex-direction: column;
    cursor: pointer;
}
.feedback-lightbox-bar {
    flex-shrink: 0; height: 40px;
    background: rgba(0,0,0,0.6);
    display: flex; align-items: center; justify-content: flex-end;
    padding: 0 12px;
}
.feedback-lightbox-close {
    background: none; border: none; color: #fff; cursor: pointer;
    font-size: 1.2rem; line-height: 1; padding: 4px 8px; opacity: 0.8;
    transition: opacity 0.15s;
}
.feedback-lightbox-close:hover { opacity: 1; }
.feedback-lightbox-body {
    flex: 1; display: flex; align-items: center; justify-content: center;
    padding: 16px; overflow: auto;
}
.feedback-lightbox-body img {
    max-width: 100%; max-height: 100%; object-fit: contain;
    border-radius: 4px; pointer-events: none;
}
.badge-received { background: #e2e3e5; color: #383d41; }
.badge-format { background: #e8e6e1; color: #383d41; }
.badge-full { background: #d1ecf1; color: #0c5460; }
.badge-partial { background: #fff3cd; color: #856404; }
/* Role badge — User. Inkara lilac, distinct from the yellow Pending status. */
.badge-role-user { background: #ece9f7; color: #5b3a8e; }

/* --- Page header --- */
.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 1.5rem;
}
.page-header h2 {
    font-family: 'DM Serif Display', serif;
    font-size: 1.5rem;
    letter-spacing: -0.3px;
}

/* --- Page Hero Header --- */
.page-hero {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 2rem 2.5rem;
    margin-bottom: 1.5rem;
    border-radius: 14px;
    min-height: 120px;
    background-size: cover;
    background-position: center;
    position: relative;
}
.page-hero.has-dropdown { overflow: visible; }
.page-hero:not(.has-dropdown) { overflow: hidden; }
.page-hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(135deg, rgba(0,0,0,0.45), rgba(0,0,0,0.25));
    border-radius: 14px;
}
.page-hero > * { position: relative; z-index: 2; }
.page-hero h2 {
    font-family: 'DM Serif Display', serif;
    font-size: 1.8rem;
    letter-spacing: -0.3px;
    color: #fff;
    margin: 0;
}
.page-hero .btn {
    background: rgba(255,255,255,0.15);
    color: #fff;
    border: 1.5px solid rgba(255,255,255,0.6);
    backdrop-filter: blur(4px);
}
.page-hero .btn:hover {
    background: rgba(255,255,255,0.25);
    border-color: #fff;
}

/* Hero stream switcher (pill-style dropdown in hero banner) */
.hero-stream-switcher {
    display: flex; align-items: center; gap: 6px;
    background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.4);
    border-radius: 20px; padding: 6px 14px;
    color: #fff; cursor: pointer; font-size: 0.85rem; font-weight: 500;
    user-select: none; white-space: nowrap;
}
.hero-stream-switcher:hover { background: rgba(255,255,255,0.25); }
.hero-stream-dropdown {
    display: none; position: absolute; top: 100%; right: 0; margin-top: 6px;
    background: var(--card-bg); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.15); min-width: 220px; z-index: 1000;
    max-height: 300px; overflow-y: auto;
}
.hero-stream-dropdown.open { display: block; }
.hero-stream-option {
    padding: 10px 14px; cursor: pointer; color: var(--ink); font-size: 0.85rem;
    border-bottom: 1px solid var(--border);
}
.hero-stream-option:last-child { border-bottom: none; }
.hero-stream-option:hover { background: var(--cream); }
.hero-stream-option.active { color: var(--accent); font-weight: 600; }

/* --- Compact Hero (Inklet Editor) --- */
.page-hero-sm {
    min-height: 50px !important;
    padding: 0.8rem 2rem !important;
    overflow: visible !important;
}
.page-hero-sm h2 { font-size: 1.2rem; }

/* --- Inline Name Editor --- */
.ie-editor-name {
    background: none; border: none; border-bottom: 1px solid transparent;
    color: #fff; font-family: 'DM Serif Display', serif;
    font-size: 1.1rem; outline: none; padding: 2px 4px;
    flex: 1; min-width: 150px; max-width: 500px;
}
.ie-editor-name:focus {
    border-bottom-color: rgba(255,255,255,0.6);
}
.ie-editor-name::placeholder { color: rgba(255,255,255,0.4); }

/* --- Channel Switcher --- */
.ie-channel-switcher {
    display: flex; align-items: center; gap: 8px;
    background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.4);
    border-radius: 20px; padding: 4px 12px 4px 4px;
    color: #fff; cursor: pointer; position: relative;
    font-size: 0.85rem; white-space: nowrap;
    transition: background 0.15s;
}
.ie-channel-switcher:hover { background: rgba(255,255,255,0.25); }
.ie-channel-switcher > img {
    width: 28px; height: 28px; border-radius: 50%; object-fit: cover;
}
.ie-channel-dropdown {
    display: none; position: absolute; top: 100%; left: 0;
    margin-top: 6px; background: var(--card-bg);
    border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.12); padding: 6px 0;
    min-width: 200px; z-index: 1000;
}
.ie-channel-dropdown.open { display: block; }
.ie-channel-option {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 14px; cursor: pointer; color: var(--ink);
    font-size: 0.85rem; transition: background 0.1s;
}
.ie-channel-option:hover { background: var(--cream); }
.ie-channel-option.active { color: var(--accent); font-weight: 600; }
.ie-channel-option img {
    width: 24px; height: 24px; border-radius: 4px; object-fit: cover;
}

/* --- Forms --- */
.form-row { display: flex; gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap; }
.form-group { display: flex; flex-direction: column; flex: 1; min-width: 200px; }
.form-group label { font-size: 0.75rem; font-weight: 500; color: var(--muted); margin-bottom: 0.25rem; text-transform: uppercase; letter-spacing: 0.5px; }
/* User Settings account/stream pickers: per-item labels inherit the uppercase
   from `.form-group label` above — revert to standard capitalisation, and
   neutralise the bold Stream-Access account headers (were near-black). */
.modal-user .user-accounts-list label,
.modal-user #m-streams-list label,
.modal-user #ie-streams-list label,
.modal-user #m-streams-list .ust-acct-head,
.modal-user #ie-streams-list .ust-acct-head { text-transform: none; letter-spacing: normal; }
.modal-user #m-streams-list .ust-acct-head,
.modal-user #ie-streams-list .ust-acct-head { color: var(--muted); font-size: 0.75rem; font-weight: 500; }
/* AI Prompt form Streams field reuses the shared account-grouped picker
   (renderStreamAccessCheckboxes) but isn't inside .modal-user, so style its
   account headings here. */
#ap-streams-list .ust-acct-head { color: var(--muted); font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }
#ap-streams-list > div + div .ust-acct-head { margin-top: 0.35rem; }
/* Inkara-template stream-access checklist (Inklet Settings modal) — mirrors
   the user modal's #m-streams-list box. */
.modal-user #ie-streams-list { max-height: 200px; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius); padding: 0.5rem; }
/* Users table: keep all action icons on one line even with 4+ icons. */
.user-actions-cell { white-space: nowrap; }
input, select, textarea {
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    font-size: 0.875rem;
    font-family: 'Inter', sans-serif;
    background: var(--paper);
    color: var(--ink);
    transition: border-color 0.15s;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); }

/* --- Filter bar --- */
.filter-bar {
    display: flex;
    gap: 0.75rem;
    align-items: flex-end;
    margin-bottom: 1rem;
    flex-wrap: wrap;
}
.filter-bar .form-group { min-width: 150px; flex: 0 0 auto; }
.filter-bar input[type="text"] { width: 250px; }

/* --- Tabbed book search helper (views-book-search.js) ---
   Search bar + Recent/Released/Coming/Category tabs + BISAC dropdowns
   + Load-more. Reuses .tab-bar / .tab-btn from the global tab vocabulary;
   the .bs-* scope only adds spacing + the search-mode mute. */
.bs-host { margin-bottom: 1rem; }
.bs-bar {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    margin-bottom: 0.75rem;
}
.bs-bar .bs-input {
    flex: 1;
    padding: 0.55rem 0.85rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: #fff;
    font-size: 0.9rem;
}
.bs-bar .bs-search-btn { flex-shrink: 0; }
.bs-tab-bar.tab-bar { margin-bottom: 0.75rem; }
.bs-tab-bar.bs-tabs-search-mode .tab-btn {
    /* When a search query is live, mute the tabs so users see they're
       in search mode but can still click a tab to clear and reload. */
    color: var(--border);
}
.bs-tab-bar.bs-tabs-search-mode .tab-btn.active { border-bottom-color: transparent; }
.bs-cat-row {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
    flex-wrap: wrap;
}
.bs-cat-row select {
    flex: 1;
    min-width: 220px;
    max-width: 360px;
    padding: 0.5rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: #fff;
    font-size: 0.85rem;
}
.bs-no-stream {
    padding: 0.85rem 1rem;
    background: var(--paper);
    border: 1px dashed var(--border);
    border-radius: var(--radius);
    color: var(--muted);
    font-size: 0.85rem;
    margin-bottom: 0.75rem;
}
.bs-load-more-wrap {
    margin: 0.75rem 0;
    text-align: center;
}

/* --- Modal --- */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(26,26,46,0.45);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 99999;
    animation: fadeIn 0.2s ease;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

/* Template bulk-create modal (widgets/template-bulk-modal.js).
   Fixed-size shell (90vw × 90vh) — top bar, controls row (channel-icon
   picker + dropdowns + refresh), and a scaled preview grid. Backdrop
   matches .modal-overlay. */
.tbm-modal {
    position: fixed; inset: 0; z-index: 99999;
    background: rgba(26,26,46,0.45);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center;
    animation: fadeIn 0.2s ease;
}
.tbm-modal-inner {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    width: 90vw; height: 90vh;
    padding: 0; overflow: hidden;
    display: flex; flex-direction: column;
}
.tbm-modal .modal-close-x {
    background: none; border: none; font-size: 1.5rem; cursor: pointer;
    color: var(--muted); padding: 0 4px; line-height: 1;
}
.tbm-modal .modal-close-x:hover { color: var(--ink); }

/* Expanded-state top bar: mirrors inklet-preview-shell.js style. */
.tbm-top-bar {
    background: #d4d4d4;
    padding: 10px 20px;
    display: flex; justify-content: space-between; align-items: center;
    flex-shrink: 0;
}
.tbm-top-bar .section-title-pair { display: flex; align-items: baseline; gap: 12px; }
.tbm-top-bar .section-title { font-family: 'DM Serif Display', serif; font-size: 1.2rem; color: #1a1a2e; }
.tbm-top-bar .section-subtitle { font-size: 0.9rem; color: #64748b; font-weight: 500; }

/* (Assets (AI) fullscreen modal CSS retired in Phase 5 of the
 * Marketing Assets refactor — the surface is now an inline Book modal
 * tab, so the .assets-ai-modal-inner / .assets-ai-top-row / .assets-ai-body
 * / #assets-marketing-host overrides that lived here are dead code.
 * Branding tab sub-panels use the base .book-asset-panel rules below;
 * Marketing Assets uses the .campaigns-filter-row + .campaign-asset-grid
 * styling shared with the Campaigns page.) */

.tbm-controls {
    display: flex; justify-content: center; align-items: center;
    gap: 12px; padding: 16px; flex-shrink: 0;
    border-bottom: 1px solid var(--border);
}
/* Unified 36px height across all controls (selects, channel-icon pills,
   refresh button) so the row reads as a single horizontal strip. */
.tbm-controls select {
    height: 36px;
    padding: 0 10px; border: 1px solid var(--border); border-radius: 6px;
    background: #fff; font-size: 0.9rem; cursor: pointer;
}
.tbm-refresh-btn {
    height: 36px; width: 36px;
    background: none; border: 1px solid var(--border); border-radius: 6px;
    padding: 0; cursor: pointer; color: var(--ink);
    display: inline-flex; align-items: center; justify-content: center;
    line-height: 1;
}
.tbm-refresh-btn svg { width: 16px; height: 16px; }
.tbm-refresh-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.tbm-refresh-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.tbm-refresh-btn.tbm-spinning svg { animation: spin 0.8s linear infinite; }

/* Channel-icon picker inside the controls row. Reuses .me-channel-bar
   pill styling from widget-shared.css; this just nudges spacing so it sits
   flush with the dropdowns to its right. */
.tbm-channel-bar {
    margin: 0;
    margin-right: 4px;
}

/* Stacked controls: a search-bar row on top, the tab bar beneath. Replaces
   the old single filter-dropdown + refresh row. */
.tbm-controls-stack { flex-direction: column; align-items: stretch; gap: 10px; }
.tbm-controls-row { display: flex; align-items: center; gap: 10px; }
.tbm-search-input {
    flex: 1; height: 36px; padding: 0 12px;
    border: 1px solid var(--border); border-radius: 6px;
    background: #fff; font-size: 0.9rem;
}
.tbm-controls-row .tbm-search-btn,
.tbm-controls-row .tbm-clear-btn { height: 36px; flex-shrink: 0; }
.tbm-tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.tbm-tab {
    height: 34px; padding: 0 14px;
    border: 1px solid var(--border); border-radius: 6px;
    background: #fff; color: var(--ink);
    font-size: 0.9rem; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.tbm-tab:hover { border-color: var(--accent); color: var(--accent); }
.tbm-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.tbm-tab svg { width: 16px; height: 16px; }

.tbm-grid {
    flex: 1; overflow-y: auto;
    padding: 16px;
    display: flex; flex-wrap: wrap; gap: 16px;
    align-content: flex-start;
}
.tbm-load-more-wrap {
    display: flex; justify-content: center;
    padding: 12px 16px;
    flex-shrink: 0;
    border-top: 1px solid var(--border);
}
.tbm-load-more-btn {
    height: 36px;
    padding: 0 20px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: #fff; color: var(--ink);
    font-size: 0.9rem; cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease;
}
.tbm-load-more-btn:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--accent);
}
.tbm-load-more-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.tbm-tile {
    position: relative;
    overflow: hidden;
    cursor: pointer;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 6px;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.tbm-tile:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(0,0,0,0.12);
}
.tbm-tile-scaler { pointer-events: none; }

.modal {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 2rem;
    width: 90%;
    max-width: 560px;
    max-height: 80vh;
    overflow-y: auto;
    box-shadow: 0 20px 60px rgba(0,0,0,0.18);
    animation: slideUp 0.25s ease;
}
@keyframes slideUp { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } }

.modal h3 { font-family: 'DM Serif Display', serif; margin-bottom: 1.25rem; letter-spacing: -0.3px; }

/* Run detail modal — large, near-fullscreen */
.modal-run-detail {
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    width: 90vw;
    height: 90vh;
    display: flex;
    flex-direction: column;
    box-shadow: 0 20px 60px rgba(0,0,0,0.18);
    animation: slideUp 0.25s ease;
    overflow: hidden;
}
.modal-run-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}
.modal-run-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid var(--border);
    background: var(--card-bg);
    flex-shrink: 0;
}
.modal-run-header h3 {
    font-family: 'DM Serif Display', serif;
    font-size: 1.25rem;
    letter-spacing: -0.3px;
    margin: 0;
}
.modal-run-close {
    background: none;
    border: none;
    font-size: 1.5rem;
    color: var(--muted);
    cursor: pointer;
    padding: 0 4px;
    line-height: 1;
    transition: color 0.15s;
}
.modal-run-close:hover { color: var(--ink); }
.modal-run-body {
    padding: 1.5rem;
    overflow-y: auto;
    flex: 1;
}

/* --- Book detail modal — top zone (cover thumb + title stack + 3-col grid) ---
   The modal-run-detail shell (90vw × 90vh, flex column, overflow:hidden) is
   shared with the run detail modal. Book detail uses a different inner
   layout: a non-scrolling top zone + non-scrolling tab bar + scrolling
   tab-content panel. This avoids the previous "everything scrolls" feel
   where the tab bar disappeared on long descriptions. */
.book-detail-close {
    position: absolute;
    top: 12px;
    right: 16px;
    z-index: 2;
    background: none;
    border: none;
    font-size: 1.5rem;
    color: var(--muted);
    cursor: pointer;
    padding: 0 4px;
    line-height: 1;
}
.book-detail-close:hover { color: var(--ink); }

.book-detail-top {
    flex-shrink: 0;
    padding: 1.5rem 1.5rem 1rem;
    border-bottom: 1px solid var(--border);
}
/* 4-column top panel:
   col 1 = bookmark star (auto width — narrow, ~32px),
   col 2 = cover thumbnail with stream row stacked below it,
   col 3 = title stack (status chips + title/subtitle + ISBN/Pub date/Format),
   col 4 = bibliographic stack (Authors / Publisher / Imprint / Series). */
/* Book detail header: 5-column grid.
   star (auto) | cover (auto) | col-1 (1fr) | col-2 (1fr) | col-3 (1fr)
   - The three 1fr columns are equal-width by design so the content
     redistributes evenly across the header strip.
   - Stream now lives in col-3 (was previously stacked under the cover
     thumb in the col-cover column). */
/* Header layout: [star] [cover] [text-left 2/3] [text-right 1/3].
 * Left text col carries badges + title + subtitle. Right text col
 * carries the two key fields (ISBN + Pub Date) — everything else
 * lives in the Key Info tab now. */
.book-detail-grid-4col {
    display: grid;
    grid-template-columns: auto auto 2fr 1fr;
    gap: 1.5rem;
    align-items: start;
}
.book-detail-col-star {
    display: flex;
    justify-content: center;
}
.book-detail-col-star .library-star { padding: 4px; border-radius: 4px; transition: background 0.15s; }
.book-detail-col-star .library-star:hover { background: rgba(212, 168, 67, 0.12); }
.book-detail-col-cover {
    /* Pre-allocate placeholder space at the cover's 2:3 ratio so the
       column doesn't snap wider when the image finishes loading.
       Matches the cover-thumb height (130px) × 2/3 ≈ 87px min-width. */
    min-width: 87px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.book-detail-col-left,
.book-detail-col-right {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;       /* allow long titles / publisher strings to wrap */
}
.book-cover-thumb {
    height: 130px;
    width: auto;
    /* No aspect-ratio override — let the image show at its natural
       proportions. Without this rule a square cover got squished to
       87 × 130 to honour 2:3. The right-column content can be pushed
       wider for non-portrait covers; the column's min-width: 87px
       still reserves a typical paperback's worth of space so a
       portrait cover doesn't cause its neighbours to reflow. */
    border-radius: 4px;
    cursor: pointer;
    border: 1px solid var(--border);
    background: linear-gradient(110deg, #f0ede8 30%, #e8e4de 50%, #f0ede8 70%);
    background-size: 200% 100%;
    animation: img-skeleton 1.5s ease-in-out infinite;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    display: block;
}
.book-cover-thumb:hover {
    transform: scale(1.03);
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.book-cover-thumb-empty {
    width: 87px;
    background: #cccccc;
    align-items: center;
    justify-content: center;
    color: #666;
    font-size: 0.75rem;
    cursor: default;
    animation: none;
    display: flex;
}
.book-cover-thumb-empty:hover {
    transform: none;
    box-shadow: none;
}
.book-detail-status-row {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    align-items: center;
    margin-bottom: 2px;
}
.book-detail-title {
    margin: 0;
    font-family: 'DM Serif Display', serif;
    font-size: 1.25rem;
    line-height: 1.2;
}
.book-detail-subtitle {
    font-size: 0.9rem;
    color: var(--muted);
    margin-bottom: 4px;
}

/* Tab bar — non-scrolling band */
.book-detail-tabs {
    flex-shrink: 0;
    padding: 0 1.5rem;
    border-bottom: 2px solid var(--border);
}
.book-detail-tabs .tab-bar {
    border-bottom: none;
    margin-bottom: 0;
}

/* Tab content — only this scrolls */
.book-detail-tab-content {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 1rem 1.5rem 1.5rem;
}

/* The book detail modal-run-detail needs position:relative so the absolute
   close button anchors to it. (.modal-run-detail's existing rule doesn't
   set position; flex shells default to static.) */
.modal-run-detail { position: relative; }

/* --- Admin → Suggestions page (Phase B) ---
   Two-column layout: main suggestion stream + sidebar showing recent runs. */
.suggestions-layout {
    display: grid;
    grid-template-columns: 1fr 280px;
    gap: 1rem;
    margin-top: 1rem;
}
.suggestions-sidebar { min-width: 0; }
.suggestions-main { min-width: 0; }
.sg-card {
    margin-bottom: 0.75rem;
    transition: box-shadow 0.15s ease;
}
.sg-card:hover { box-shadow: 0 4px 14px rgba(0,0,0,0.07); }
.sg-cover {
    width: 90px;
    height: 135px;
    border-radius: 4px;
    object-fit: cover;
    flex-shrink: 0;
    background: linear-gradient(110deg, #f0ede8 30%, #e8e4de 50%, #f0ede8 70%);
    background-size: 200% 100%;
    animation: img-skeleton 1.5s ease-in-out infinite;
    border: 1px solid var(--border);
}
.sg-cover-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    background: #cccccc;
    color: #666;
    font-size: 0.7rem;
    animation: none;
}
.sg-headline-line {
    margin-top: 6px;
    font-size: 0.85rem;
    line-height: 1.4;
}
.sg-headline-link {
    color: var(--ink);
    text-decoration: none;
    border-bottom: 1px dotted var(--muted);
}
.sg-headline-link:hover { border-bottom-color: var(--accent); color: var(--accent); }
.sg-reasoning {
    margin-top: 6px;
    font-size: 0.85rem;
    line-height: 1.5;
    color: var(--ink);
    font-style: italic;
}
.sg-actions {
    display: flex;
    gap: 6px;
    margin-top: 10px;
    flex-wrap: wrap;
}
.sg-run-row {
    padding: 6px 0;
    border-bottom: 1px solid var(--border);
}
.sg-run-row:last-child { border-bottom: none; }

/* Now Trending — grouped card (one per book, with stacked headlines) */
.nt-card { margin-bottom: 0.75rem; transition: box-shadow 0.15s ease; }
.nt-card:hover { box-shadow: 0 4px 14px rgba(0,0,0,0.07); }
.nt-cover {
    width: 100px; height: 150px;
    border-radius: 4px; object-fit: cover; flex-shrink: 0;
    background: linear-gradient(110deg, #f0ede8 30%, #e8e4de 50%, #f0ede8 70%);
    background-size: 200% 100%;
    animation: img-skeleton 1.5s ease-in-out infinite;
    border: 1px solid var(--border);
}
.nt-cover-empty {
    display: flex; align-items: center; justify-content: center;
    background: #cccccc; color: #666; font-size: 0.7rem; animation: none;
}
.nt-card-body { flex: 1; min-width: 0; }
.nt-card-head { padding-bottom: 8px; border-bottom: 1px solid var(--border); margin-bottom: 8px; }
.nt-title {
    margin: 0; font-family: 'DM Serif Display', serif;
    font-size: 1.1rem; line-height: 1.25;
}
.nt-subtitle {
    font-size: 0.85rem; color: var(--muted); margin-top: 2px;
}
.nt-meta-row {
    display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
    margin-top: 6px; font-size: 0.8rem; color: var(--muted);
}
.nt-meta-sep { color: var(--border); }
.nt-isbn { color: var(--ink); background: var(--paper); padding: 1px 5px; border-radius: 3px; }
.nt-pub-date { color: var(--ink); }
.nt-stream { font-style: italic; }
.nt-match-count {
    background: var(--accent-light, #f8d7e6);
    color: var(--accent, #1a1a2e);
    padding: 1px 7px; border-radius: 10px;
    font-size: 0.7rem; font-weight: 600; font-style: normal;
}
.nt-card-actions {
    display: flex; gap: 6px; margin-top: 8px;
}

/* Headline rows under each card. */
.nt-headline-list { display: flex; flex-direction: column; gap: 6px; }
.nt-headline-row {
    display: flex; gap: 10px; align-items: flex-start;
    padding: 6px 8px; border-radius: 4px;
    transition: background 0.15s;
}
.nt-headline-row:hover { background: var(--paper); }
.nt-hr-main { flex: 1; min-width: 0; }
.nt-hr-head {
    display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
}
.nt-hr-title {
    color: var(--ink); text-decoration: none;
    border-bottom: 1px dotted var(--muted);
    font-size: 0.85rem; line-height: 1.35;
}
.nt-hr-title:hover { color: var(--accent); border-bottom-color: var(--accent); }
.nt-hr-source { font-size: 0.7rem; color: var(--muted); }
.nt-hr-reason {
    margin-top: 4px; font-size: 0.8rem; line-height: 1.4;
    color: var(--muted); font-style: italic;
}
.nt-hr-actions {
    flex-shrink: 0; display: flex; align-items: center;
}

@media (max-width: 1100px) {
    .suggestions-layout { grid-template-columns: 1fr; }
    .suggestions-sidebar { order: -1; }
}
@media (max-width: 700px) {
    .nt-card { flex-direction: column !important; }
    .nt-cover { width: 80px; height: 120px; }
}

/* Now Trending — tab bar (In the News | Events). Reuses the global
   .tab-bar / .tab-btn vocabulary; this scope just nudges spacing so
   the tab bar sits flush above the filters card. */
.nt-tab-bar {
    margin-bottom: 1rem;
    margin-top: -0.25rem;
}

/* Events-tab card variant — same chrome as the news card, but the
   row title is the event name (not a clickable link) and the source
   slot carries the date window. */
.nt-card-event { /* nothing distinct yet — variant hook for future tweaks */ }
.nt-event-name {
    color: var(--ink);
    font-weight: 500;
    font-size: 0.85rem;
    line-height: 1.35;
    border-bottom: none;
}

/* News-feeds modal — per-stream Used by checkbox list */
.nf-stream-help { font-size: 0.8rem; color: var(--muted); margin-bottom: 6px; }
.nf-stream-list {
    max-height: 220px; overflow-y: auto;
    border: 1px solid var(--border); border-radius: 6px;
    padding: 6px 8px;
}
.nf-stream-row {
    display: flex; align-items: center; gap: 8px;
    padding: 4px 0; font-size: 0.85rem;
}
.nf-stream-row + .nf-stream-row { border-top: 1px solid var(--paper); }
.nf-stream-name { flex: 1; }
.nf-stream-account { color: var(--muted); font-size: 0.75rem; }

/* Now Trending — Collections section (one card per headline cluster) */
.nt-collection-card {
    margin-bottom: 0.75rem;
    padding: 0.9rem 1.25rem 1rem;
    border-left: 3px solid var(--accent, #c2185b);
}
.nt-coll-head {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    font-size: 0.85rem;
    margin-bottom: 6px;
    padding-bottom: 6px;
    border-bottom: 1px solid var(--border);
}
.nt-coll-star { color: var(--accent, #c2185b); }
.nt-coll-title { font-weight: 600; color: var(--ink); }
.nt-coll-trait { color: var(--muted); font-style: italic; margin-left: auto; font-size: 0.78rem; }
.nt-coll-headline {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    font-size: 0.9rem; margin-bottom: 8px;
}
.nt-coll-headline-link {
    color: var(--ink); text-decoration: none;
    border-bottom: 1px dotted var(--muted);
}
.nt-coll-headline-link:hover { color: var(--accent); border-bottom-color: var(--accent); }
.nt-coll-source { font-size: 0.72rem; color: var(--muted); }
.nt-coll-rows { display: flex; flex-direction: column; gap: 8px; }
.nt-coll-row {
    display: flex; align-items: center; gap: 10px;
    padding: 6px;
    border-radius: 4px;
    background: var(--paper);
}
.nt-coll-row:hover { background: var(--card-bg, #fff); box-shadow: 0 1px 4px rgba(0,0,0,0.04); }
.nt-coll-cover {
    width: 60px; height: 90px; object-fit: cover; flex-shrink: 0;
    border-radius: 3px; border: 1px solid var(--border);
    background: linear-gradient(110deg, #f0ede8 30%, #e8e4de 50%, #f0ede8 70%);
    background-size: 200% 100%;
    animation: img-skeleton 1.5s ease-in-out infinite;
}
.nt-coll-cover-empty {
    display: flex; align-items: center; justify-content: center;
    background: #cccccc; color: #666; font-size: 0.7rem; animation: none;
}
.nt-coll-row-body { flex: 1; min-width: 0; }
.nt-coll-row-title { font-size: 0.9rem; font-weight: 500; color: var(--ink); line-height: 1.3; }
.nt-coll-row-author { font-size: 0.75rem; color: var(--muted); margin-top: 1px; }
.nt-coll-row-reason { font-size: 0.78rem; color: var(--muted); font-style: italic; margin-top: 3px; line-height: 1.4; }
.nt-coll-row-actions {
    display: flex; align-items: center; gap: 6px; flex-shrink: 0;
}

/* Now Trending — Re-run feed button + modal */
.nt-rerun-btn {
    background: none; border: 1px solid var(--border); border-radius: 6px;
    padding: 5px 8px; cursor: pointer;
    color: var(--ink);
    display: inline-flex; align-items: center; justify-content: center;
    transition: border-color 0.15s, color 0.15s;
}
.nt-rerun-btn svg { width: 14px; height: 14px; }
.nt-rerun-btn:hover { border-color: var(--accent); color: var(--accent); }

.nt-rerun-overlay {
    position: fixed; inset: 0; z-index: 100000;
    background: rgba(26, 26, 46, 0.55);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    display: flex; align-items: center; justify-content: center;
    animation: fadeIn 0.15s ease;
}
.nt-rerun-modal {
    background: var(--card-bg, #fff);
    border-radius: 10px;
    padding: 32px 28px 26px;
    width: 380px;
    max-width: calc(100vw - 36px);
    box-shadow: 0 20px 60px rgba(0,0,0,0.25);
    text-align: center;
    position: relative;
}
.nt-rerun-close {
    position: absolute; top: 8px; right: 12px;
    background: none; border: none; font-size: 1.4rem;
    color: var(--muted); cursor: pointer; line-height: 1;
}
.nt-rerun-close:hover { color: var(--ink); }
.nt-rerun-quill {
    display: flex; justify-content: center;
    margin-bottom: 14px;
    height: 80px;
}
.nt-rerun-quill-body {
    transform-origin: 32px 64px;
    animation: nt-rerun-quill-rock 1.6s ease-in-out infinite;
}
@keyframes nt-rerun-quill-rock {
    0%, 100% { transform: rotate(-8deg); }
    50%      { transform: rotate(8deg); }
}
.nt-rerun-ink {
    animation: nt-rerun-ink-drop 2s ease-out infinite;
    transform-origin: 20px 72px;
    opacity: 0;
}
@keyframes nt-rerun-ink-drop {
    0%   { transform: translateY(0); opacity: 0.95; }
    35%  { transform: translateY(8px); opacity: 0.6; }
    60%  { transform: translateY(14px); opacity: 0; }
    100% { opacity: 0; }
}
.nt-rerun-headline {
    font-family: 'DM Serif Display', serif;
    font-size: 1.05rem;
    color: var(--ink);
    margin-bottom: 14px;
    min-height: 1.4em;
    transition: color 0.2s;
}
.nt-rerun-progress-track {
    background: var(--paper);
    border-radius: 12px;
    height: 8px;
    overflow: hidden;
    margin-bottom: 4px;
}
.nt-rerun-progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--accent, #c2185b), var(--accent-light, #f8d7e6));
    transition: width 0.4s ease;
    width: 5%;
}
.nt-rerun-progress-pct {
    font-size: 0.75rem; color: var(--muted);
    margin-bottom: 14px;
}
.nt-rerun-phases {
    list-style: none;
    margin: 0; padding: 0;
    text-align: left;
    font-size: 0.85rem;
    color: var(--ink);
}
.nt-rerun-phases li {
    padding: 4px 0;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}
.nt-rerun-phases li:not([data-phase="done"]) { /* default styling */ }

.modal-user { max-width: 425px; }
/* Inklet Settings modal is wider — it carries the Inklet Type dropdown + Active
   + Stream access. Scoped to this modal so the shared .modal-user stays 425px. */
.modal.modal-user.modal-inklet-settings { max-width: 575px; }

/* "Understanding Inklets & Templates" info icon — neutral by default, Inkara
   accent on the outline (and icon) on rollover. Sits left of the Emporium
   user/scope dropdown. */
.ie-info-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--paper);
    color: var(--muted);
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease;
}
.ie-info-btn:hover {
    border-color: var(--accent);
    color: var(--accent);
}
.modal-apikeys { max-width: 650px; }
.form-row-name { flex-wrap: nowrap; }
.form-row-name .form-group:first-child { flex: 1; min-width: 0; }
.form-row-name .form-group:last-child { flex: 2; min-width: 0; }
.modal-actions, .form-actions { display: flex; gap: 0.5rem; justify-content: flex-end; margin-top: 3rem; }

/* --- Colour Picker --- */
.colour-picker { display: flex; gap: 10px; margin-top: 4px; flex-wrap: wrap; }
.colour-swatch {
    width: 26px; height: 26px; border-radius: 50%; border: 3px solid transparent;
    cursor: pointer; display: flex; align-items: center; justify-content: center;
    transition: border-color 0.15s, transform 0.15s; padding: 0;
    /* Don't let the flex row compress width below 26px (was making the swatches
       oval); wrap instead so they stay perfect circles. */
    flex: 0 0 26px; box-sizing: border-box;
}
.colour-swatch:hover { transform: scale(1.12); }
.colour-swatch.selected { border-color: var(--ink); }

/* --- Loading --- */
.loading { text-align: center; padding: 3rem; color: var(--muted); }
.loading::after { content: ''; display: inline-block; width: 20px; height: 20px; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.6s linear infinite; margin-left: 0.5rem; vertical-align: middle; }

/* --- Clickable rows --- */
.clickable { cursor: pointer; }
.clickable:hover td { background: #f5efe8; }

/* --- Pagination --- */
.pagination { display: flex; gap: 0.5rem; justify-content: center; margin-top: 1rem; }

/* --- Toast ---
   Bottom-right but indented past the floating feedback button (44px wide
   at right:24px, bottom:24px). Match the button's bottom edge + min-height
   so the two sit on the same horizontal line. 5.5rem (88px) right clears
   the button + leaves a ~20px gap. */
.toast {
    position: fixed;
    bottom: 24px;
    right: 5.5rem;
    min-height: 44px;
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 1.25rem;
    background: var(--ink);
    color: #fff;
    border-radius: var(--radius);
    font-size: 0.875rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.2);
    z-index: 2147483647;   /* always on top — above modals (99999) and lightboxes (100000) */
    animation: fadeInUp 0.3s ease-out;
}
.toast.error { background: var(--danger); }
.toast.success { background: var(--success); }
/* Error toasts carry a dismiss X (Toast.show in api.js stops auto-dismissing
   for type='error'). Sits just to the right of the message with a subtle
   hover lift. Non-error toasts have no children, just textContent. */
.toast .toast-msg { margin-right: 0.75rem; }
.toast .toast-close {
    background: transparent;
    border: none;
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
    padding: 2px 4px;
    margin-left: 0.25rem;
    transition: color 0.15s ease;
}
.toast .toast-close:hover { color: #fff; }
@keyframes fadeInUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }

/* --- Detail view --- */
.detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 1rem;
}
.detail-item .label { font-size: 0.75rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
.detail-item .value { font-size: 1rem; margin-top: 0.15rem; }

/* --- Tabs --- */
.tab-bar { display: flex; gap: 0; border-bottom: 2px solid var(--border); margin-bottom: 1rem; }
.tab-btn { padding: 0.6rem 1.2rem; border: none; background: none; cursor: pointer; font-family: 'Inter', sans-serif; font-size: 0.85rem; font-weight: 500; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -2px; transition: color 0.15s, border-color 0.15s; }
.tab-btn:hover { color: var(--ink); }
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* --- Breadcrumb --- */
.breadcrumb { font-size: 0.85rem; color: var(--muted); margin-bottom: 1rem; }
.breadcrumb a { color: var(--accent); text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }

/* --- ISBN Emporium --- */

/* CTA button */
.ie-cta-wrap { text-align: center; margin: 3rem 0 2rem; }
.ie-cta-btn {
    display: inline-block; padding: 18px 48px;
    background: var(--accent); color: #fff;
    border: none; border-radius: 30px;
    font-size: 1.3rem; font-weight: 600;
    font-family: 'Inter', sans-serif;
    cursor: pointer; transition: background 0.2s, transform 0.15s;
}
.ie-cta-btn:hover { background: var(--accent-dark); transform: translateY(-1px); }
.ie-cta-sub {
    margin-top: 12px; font-size: 0.85rem;
    color: var(--ink); max-width: 500px;
    margin-left: auto; margin-right: auto;
    line-height: 1.5;
}

/* Create modal */
.ie-create-modal { align-items: flex-start; justify-content: center; padding-top: 50px; }
.ie-create-panel {
    background: var(--card-bg); border-radius: 16px;
    padding: 2.5rem; position: relative;
    box-shadow: 0 8px 30px rgba(0,0,0,0.15);
}
.ie-create-close {
    position: absolute; top: 12px; right: 16px;
    background: none; border: none; font-size: 1.5rem;
    color: var(--muted); cursor: pointer;
}
.ie-create-close:hover { color: var(--ink); }
.ie-create-options { display: flex; gap: 40px; justify-content: center; padding: 1rem 0; }
.ie-create-option-wrap { text-align: center; }
.ie-create-option {
    width: 190px; height: 190px;
    background: #f0f0f0; border: none;
    border-radius: 16px; cursor: pointer;
    display: block; position: relative;
    padding: 0; overflow: hidden; transition: all 0.2s;
}
.ie-create-option:hover {
    box-shadow: 0 4px 16px rgba(0,0,0,0.08);
    transform: translateY(-2px);
}
.ie-create-option.disabled {
    opacity: 0.4; cursor: default; pointer-events: none;
}
.ie-create-option-img {
    position: absolute; top: 0; left: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    border-radius: 16px;
}
.ie-create-option-label {
    margin-top: 12px; font-size: 0.85rem; color: var(--ink);
    font-weight: 500; line-height: 1.4;
}
.ie-create-option-label.ie-label-disabled { opacity: 0.4; }
.ie-label-isbn { font-weight: 500; }

/* Wizard modal */
.ie-wizard-panel { width: 820px; max-width: 90vw; }
.ie-wiz-step { margin-bottom: 8px; }
.ie-wiz-header {
    font-size: 0.82rem; font-weight: 600; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.3px; margin-bottom: 10px;
}
.ie-wiz-summary {
    padding: 10px 14px; background: var(--cream); border-radius: 8px;
    font-size: 0.85rem; color: var(--ink); display: flex; align-items: center; gap: 8px;
    flex-wrap: wrap;
}
.ie-wiz-change {
    color: var(--accent); font-size: 0.78rem; text-decoration: none;
    margin-left: auto; white-space: nowrap;
}
.ie-wiz-change:hover { text-decoration: underline; }

/* Search inside wizard */
.ie-modal-search-title { font-family: 'DM Serif Display', serif; font-size: 1.2rem; color: var(--ink); margin-bottom: 1rem; letter-spacing: -0.3px; }
.ie-wiz-step .ie-search-container { max-width: 100%; width: 100%; }
.ie-wiz-step .ie-search-box { border-radius: 12px; }
.ie-wiz-step .ie-results { border-radius: 0 0 12px 12px; max-height: 350px; }
.ie-modal-back:hover { color: var(--ink); }

/* Browse tabs + book cards */
.ie-new-books-section { margin-top: 1.5rem; }
.ie-browse-tabs {
    display: flex; gap: 4px; margin-bottom: 1rem;
    border-bottom: 1px solid var(--border); padding-bottom: 0;
}
.ie-browse-tab {
    background: none; border: none; border-bottom: 2px solid transparent;
    padding: 8px 14px; font-size: 0.82rem; font-weight: 500;
    color: var(--muted); cursor: pointer; transition: all 0.2s;
    margin-bottom: -1px;
}
.ie-browse-tab:hover:not(.disabled) { color: var(--ink); }
.ie-browse-tab.active {
    color: var(--accent); border-bottom-color: var(--accent); font-weight: 600;
}
.ie-browse-tab.disabled {
    opacity: 0.35; cursor: default; pointer-events: none;
}
.ie-new-books-scroll { max-height: 420px; overflow-y: auto; }
.ie-new-books-grid {
    display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px;
    align-items: start;
}
.ie-book-card {
    cursor: pointer; text-align: center; transition: transform 0.15s;
    display: flex; flex-direction: column;
}
.ie-book-card:hover { transform: translateY(-2px); }
.ie-book-card-cover-wrap {
    width: 100%; height: 190px;
    border-radius: 6px; overflow: hidden;
    background: #ccc;
}
.ie-book-card-cover-wrap img {
    width: 100%; height: 100%;
    object-fit: cover; display: block;
}
.ie-book-card-placeholder {
    background: #ccc;
}
.ie-book-card-title {
    font-size: 0.78rem; font-weight: 600; color: var(--ink);
    margin-top: 6px; line-height: 1.3;
    overflow: hidden; text-overflow: ellipsis;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.ie-book-card-author {
    font-size: 0.72rem; color: var(--muted); margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ie-book-card-stream {
    font-size: 0.65rem; color: var(--text-light); margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-style: italic;
}
.ie-view-more-btn {
    background: var(--card-bg); color: var(--ink);
    border: 1px solid var(--border); padding: 8px 24px;
    border-radius: 20px; font-size: 0.85rem; cursor: pointer;
    transition: all 0.2s;
}
.ie-view-more-btn:hover {
    background: var(--accent); color: #fff; border-color: var(--accent);
}

.ie-channel-grid {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px;
    margin-top: 16px;
}

/* ── Settings > Events — master-list lozenge picker ──
 * 3-column grid of clickable lozenges. Each shows the event name (bold),
 * its window dates (muted), and a recurrence badge in the corner. Single
 * click toggles the .selected state — a soft accent fill + border. Used
 * exclusively by the picker modal in views-settings-events.js. Sized
 * for the 980 px-wide picker modal. */
.se-events-picker-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
    padding: 4px;
}
.se-event-lozenge {
    position: relative;
    padding: 14px 14px 18px;
    border: 2px solid var(--border);
    border-radius: 10px;
    background: var(--cream);
    cursor: pointer;
    transition: border-color 0.12s, background 0.12s, transform 0.12s, box-shadow 0.12s;
}
.se-event-lozenge:hover {
    border-color: var(--accent);
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
.se-event-lozenge.selected {
    border-color: var(--accent);
    background: var(--accent-light);
}
.se-event-lozenge-name {
    font-weight: 700;
    font-size: 0.92rem;
    color: var(--ink);
    line-height: 1.25;
    margin-bottom: 6px;
    /* Cap to ~2 lines so the grid stays uniform */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.se-event-lozenge-window {
    font-size: 0.78rem;
    color: var(--muted);
}
.se-event-lozenge-badge {
    position: absolute;
    top: 10px;
    right: 10px;
    font-size: 0.68rem;
}

.ie-channel-card {
    display: flex; flex-direction: column; align-items: center; text-align: center;
    padding: 12px 10px; border-radius: 10px; border: 2px solid var(--border);
    background: var(--cream); cursor: pointer; transition: all 0.15s;
}
.ie-channel-card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
.ie-channel-card.selected { border-color: var(--accent); background: var(--accent-light); }
.ie-channel-card-icon { margin-bottom: 6px; opacity: 0.6; line-height: 1; }
.ie-channel-card-icon svg { width: 28px; height: 28px; }
.ie-channel-card-name { font-weight: 700; font-size: 0.9rem; color: var(--ink); margin-bottom: 4px; }
.ie-channel-card-dims { font-size: 0.78rem; color: var(--muted); margin-bottom: 4px; }
.ie-channel-card-desc { font-size: 0.72rem; color: var(--text-light); line-height: 1.3; }

/* Column accordions in Display tab (two-column mode) */
.col-accordion-header {
    display: flex; justify-content: space-between; align-items: center;
    padding: 10px 12px; background: var(--cream); border: 1px solid var(--border);
    border-radius: 6px; cursor: pointer; font-weight: 600; font-size: 0.85rem;
    color: var(--ink); user-select: none;
}
.col-accordion-header:hover { background: #f0ede8; }
.col-accordion-header.active { border-color: var(--accent); color: var(--accent); }
.col-accordion-arrow { font-size: 0.7rem; transition: transform 0.2s; }
.col-accordion-header.active .col-accordion-arrow { transform: rotate(180deg); }
.col-accordion-body { padding: 8px 0; }

/* Display sub-tab panes */
.display-tab-pane { display: none; padding-top: 12px; }
.display-tab-pane.active { display: block; }
.ie-channel-card-img {
    width: 150px; height: 150px; border-radius: 12px; overflow: hidden;
    background: #f0ede8; box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.ie-channel-card-img img {
    width: 100%; height: 100%; object-fit: cover; display: block;
}
.ie-channel-card-name {
    font-size: 0.82rem; font-weight: 600; color: var(--ink);
    margin-top: 8px; text-align: center;
}
.ie-channel-card-summary {
    font-size: 0.7rem; color: var(--muted); text-align: center;
    margin-top: 2px;
}
.ie-channel-card-dims {
    font-size: 0.65rem; color: var(--text-light); text-align: center;
    margin-top: 1px;
}

.ie-search-wrap { display: flex; justify-content: center; margin: 2rem 0; }
.ie-search-container { position: relative; width: 100%; max-width: 650px; }
.ie-search-box {
    display: flex; align-items: stretch;
    border: 2px solid var(--border); border-radius: 30px;
    background: var(--card-bg); transition: border-color 0.2s;
    overflow: visible;
}
.ie-search-box:focus-within { border-color: var(--accent); }

/* Custom stream picker */
.ie-stream-picker {
    position: relative; flex-shrink: 0;
    border-right: 1px solid var(--border);
    cursor: pointer; user-select: none;
}
.ie-stream-display {
    display: flex; flex-direction: column; justify-content: center;
    padding: 10px 28px 10px 20px; min-width: 120px; max-width: 220px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238a8477' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 10px center;
}
.ie-stream-label {
    font-size: 0.7rem; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.05em; color: var(--ink);
}
.ie-stream-name {
    font-size: 0.8rem; color: var(--muted); margin-top: 2px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ie-stream-dropdown {
    display: none; position: absolute; top: calc(100% + 6px); left: 0;
    min-width: 220px; max-height: 280px; overflow-y: auto;
    background: var(--card-bg); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: 0 4px 12px rgba(0,0,0,0.12);
    z-index: 200;
}
.ie-stream-dropdown.open { display: block; }
.ie-stream-option {
    padding: 10px 16px; font-size: 0.85rem; cursor: pointer;
    border-bottom: 1px solid var(--border); transition: background 0.15s;
}
.ie-stream-option:last-child { border-bottom: none; }
.ie-stream-option:hover { background: var(--paper); }
.ie-stream-option.active { background: var(--accent-light); color: var(--accent); font-weight: 500; }

.ie-search {
    flex: 1; padding: 18px 20px 18px 16px; font-size: 1.2rem;
    border: none; outline: none; background: transparent;
    font-family: 'Inter', sans-serif; box-sizing: border-box;
    min-width: 0;
}
.ie-subtitle { font-family: 'DM Serif Display', serif; font-size: 1.2rem; color: var(--ink); margin: 2rem 0 1rem; }
.ie-categories { display: flex; gap: 12px; flex-wrap: wrap; }
.ie-category {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 10px 20px; background: var(--card-bg);
    border: 1px solid var(--border); border-radius: 24px;
    font-size: 0.9rem; font-weight: 500; color: var(--ink);
    cursor: pointer; transition: background 0.2s, border-color 0.2s;
}
.ie-category:hover { background: var(--accent-light); border-color: var(--accent); }
.ie-category svg { width: 16px; height: 16px; }

/* Search results dropdown */
.ie-results {
    position: absolute; top: calc(100% + 4px); left: 0; right: 0;
    background: var(--card-bg); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: 0 4px 12px rgba(0,0,0,0.1);
    max-height: 420px; overflow-y: auto; z-index: 100;
}
.ie-result {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 16px; border-bottom: 1px solid var(--border); cursor: default;
}
.ie-result:last-child { border-bottom: none; }
.ie-result:hover { background: var(--paper); }
.ie-result-img {
    width: 40px; height: 40px; object-fit: cover;
    border-radius: 4px; background: #eee; flex-shrink: 0;
}
.ie-result-img.author { border-radius: 50%; }
.ie-result-info { flex: 1; min-width: 0; }
.ie-result-title {
    font-weight: 500; font-size: 0.9rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ie-result-sub { font-size: 0.8rem; color: var(--muted); }

/* --- Widget Grid (ISBN Emporium) --- */
.ie-widget-grid {
    display: flex; flex-wrap: wrap; gap: 16px; justify-content: flex-start;
}
.ie-widget-card {
    width: 120px; height: 110px; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 8px;
    background: var(--cream); border: 1px solid var(--border); border-radius: 10px;
    cursor: pointer; transition: all 0.15s ease; text-decoration: none; color: var(--ink);
}
.ie-widget-card:hover {
    border-color: var(--accent); box-shadow: 0 2px 8px rgba(175,40,96,0.12);
    transform: translateY(-2px);
}
.ie-widget-icon { width: 32px; height: 32px; color: var(--ink); }
.ie-widget-icon svg { width: 100%; height: 100%; }
.ie-widget-label { font-size: 0.75rem; font-weight: 500; text-align: center; line-height: 1.2; }

/* --- Widget Editor (Inklet Editor) --- */
.ie-editor-widget-switcher {
    display: flex; align-items: center; gap: 8px;
    background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.4);
    border-radius: 20px; padding: 6px 14px 6px 8px;
    color: #fff; cursor: pointer; font-size: 0.85rem; font-weight: 500;
    position: relative; user-select: none;
}
.ie-editor-widget-switcher:hover { background: rgba(255,255,255,0.25); }
.ie-editor-widget-icon-sm { width: 20px; height: 20px; }
.ie-editor-widget-icon-sm svg { width: 100%; height: 100%; }
.ie-editor-widget-dropdown {
    display: none; position: absolute; top: 60px; left: 16px;
    background: var(--card-bg); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.15); min-width: 220px; z-index: 1000;
    max-height: 400px; overflow-y: auto;
}
.ie-editor-widget-dropdown.open { display: block; }
.ie-editor-widget-option {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 14px; cursor: pointer; color: var(--ink); font-size: 0.85rem;
    border-bottom: 1px solid var(--border);
}
.ie-editor-widget-option:last-child { border-bottom: none; }
.ie-editor-widget-option:hover { background: var(--cream); }
.ie-editor-widget-option.active { color: var(--accent); font-weight: 600; }
.ie-editor-widget-option .ie-editor-widget-icon { width: 22px; height: 22px; flex-shrink: 0; }
.ie-editor-widget-option .ie-editor-widget-icon svg { width: 100%; height: 100%; }

/* --- Inklet Gallery --- */
.inklet-gallery-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px;
}
/* Single-row variant used by ISBN Emporium > Inklets (Create + Generate
   sections). Same minmax breakpoint as the grid above; layout flexes to
   fit available width. The frontend caps card count to ROW_LIMIT so
   overflow only matters at very small viewports. */
.inklet-gallery-row {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px;
}
/* Footer strip below each Inklet row: optional left-aligned controls
   (e.g. show-archived toggle), right-aligned "View all". No divider —
   the row above is treated as the visual section boundary. */
.inklet-row-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; margin-top: 12px; flex-wrap: wrap;
}
/* "View all" link, bottom-right of each row. Placeholder behaviour for
   now (no href); see views-isbn.js. */
.inklet-gallery-view-all {
    font-size: 13px; color: var(--accent); text-decoration: none;
}
.inklet-gallery-view-all:hover { text-decoration: underline; }
/* Muted variant — same text styling as `.inklet-gallery-view-all`, used
   for the "Archived →" link that sits to the right of "View all →". */
.inklet-gallery-view-all--muted {
    color: var(--muted);
}
.inklet-gallery-view-all--muted:hover { color: var(--muted); text-decoration: underline; }
/* Wrap the View all + Archived links so they sit side-by-side at the
   start of the row footer. Without this wrapper the `.inklet-row-foot`
   `space-between` rule would push them to opposite ends. */
.inklet-row-foot-links {
    display: flex; align-items: center; gap: 1rem;
}
/* Two-tone section title used by ISBN Emporium > Inklets section headers.
   Mirrors the book detail modal's contrasting title/subtitle font pairing
   (DM Serif Display + Inter); see views-books.js:194 for the modal source. */
.section-title-pair {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin: 0;
}
.section-title-pair .section-title {
    font-family: 'DM Serif Display', serif;
    font-size: 1.5rem;
    font-weight: 400;
    letter-spacing: -0.3px;
    color: var(--ink);
    line-height: 1.2;
}
.section-title-pair .section-subtitle {
    font-family: 'Inter', sans-serif;
    font-size: 1.2rem;
    font-weight: 400;
    color: #94a3b8;
    line-height: 1.2;
}

.inklet-gallery-card {
    position: relative; border: 1px solid var(--border); border-radius: 10px;
    overflow: hidden; cursor: pointer; background: var(--paper);
    transition: box-shadow 0.15s, transform 0.15s;
}
.inklet-gallery-card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.1); transform: translateY(-2px); }
.inklet-gallery-thumb {
    width: 100%; aspect-ratio: 1; overflow: hidden;
    background: linear-gradient(110deg, #f0ede8 30%, #e8e4de 50%, #f0ede8 70%);
    background-size: 200% 100%;
    animation: img-skeleton 1.5s ease-in-out infinite;
}
.inklet-gallery-thumb img {
    width: 100%; height: 100%; object-fit: cover; display: block;
    opacity: 0; transition: opacity 0.3s ease;
}
.inklet-gallery-thumb img.loaded { opacity: 1; }
@keyframes img-skeleton {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}
.inklet-gallery-meta { padding: 10px 12px; }
.inklet-gallery-name {
    font-size: 0.82rem; font-weight: 600; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.inklet-gallery-badge {
    display: inline-block; font-size: 0.65rem; padding: 2px 6px; border-radius: 4px;
    background: var(--sand); color: var(--muted); margin-top: 4px;
}
/* Channel line on inklet cards: "{Group} {Channel}" — flush-left plain
   text (no pill bg), group rendered heavier than the channel name. */
.inklet-gallery-channel {
    font-size: 0.68rem; color: var(--muted); margin-top: 4px;
}
.inklet-gallery-channel-group { font-weight: 600; display: block; }
.inklet-gallery-channel-name  { font-weight: 400; display: block; }
.inklet-gallery-channel-desc  { font-weight: 400; display: block; color: var(--text-light); }
.inklet-gallery-date { font-size: 0.68rem; color: var(--muted); margin-top: 4px; }
/* Corner-action buttons (Duplicate + Delete) shown on rollover of any
   gallery card. Same SVG icons + 24×24 dark-pill treatment used by the
   multi-editor canvas tools row, so the visual language is consistent. */
.inklet-gallery-corner-btn {
    position: absolute;
    top: 6px; width: 24px; height: 24px;
    border-radius: 50%; border: none;
    background: rgba(0,0,0,0.5);
    color: #fff;
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    opacity: 0; transition: opacity 0.15s, background 0.15s;
    padding: 0;
    z-index: 3;
}
.inklet-gallery-card:hover .inklet-gallery-corner-btn { opacity: 1; }
.inklet-gallery-corner-btn:hover { background: var(--accent); }
/* Right-most button = Delete/Archive; sits flush right. Duplicate sits to
   its left. 2026-06-29 — Download/export (Feather download) sits to the
   left of Duplicate at 66px, between it and the cog. Inklet-Settings cog
   sits on the far left of the row. Phase 26 toggle-template (Feather
   log-out) is retired but its offset rule is kept harmless. Order from
   right to left:
     active : [ archive @ 6 ] [ dup @ 36 ] [ download @ 66 ] [ cog @ 96 ]. */
.inklet-gallery-delete           { right: 6px; }
.inklet-gallery-duplicate        { right: 36px; }
.inklet-gallery-download         { right: 66px; }
.inklet-gallery-toggle-template  { right: 66px; }
.inklet-gallery-cog              { right: 66px; }
/* When the download/export button is present (active all-action cards) the
   cog shifts one slot left so the four-button row stays gap-free. */
.inklet-gallery-card:has(.inklet-gallery-download) .inklet-gallery-cog,
.inklet-gallery-card:has(.inklet-gallery-toggle-template) .inklet-gallery-cog {
    right: 96px;
}
/* Archive button (active cards) uses the default accent rollover from
   .inklet-gallery-corner-btn:hover — no red override (it archives, not deletes). */
/* Archived cards: right-aligned like active cards. Order (left→right):
   restore (upload) then delete-forever (trash) flush at the corner.
   Delete-forever is always red — white icon on danger bg — matching the
   destructive trash treatment used elsewhere (.btn-danger). */
.inklet-gallery-restore          { right: 36px; }
.inklet-gallery-delete-forever   { right: 6px; background: var(--danger); }
.inklet-gallery-delete-forever:hover { background: #a02822; }

/* Generate-row hover overlay: mutes the card and surfaces explicit Edit + Use
   buttons for live (non-archived) template cards. Mirrors the
   .inklet-gallery-delete opacity pattern so visual cohesion stays consistent
   across hover affordances. */
.inklet-gallery-hover-actions {
    position: absolute; inset: 0;
    background: rgba(20, 20, 30, 0.62);
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    gap: 10px;
    opacity: 0; pointer-events: none;
    transition: opacity 0.15s ease;
    z-index: 1;
}
.inklet-gallery-card:hover .inklet-gallery-hover-actions {
    opacity: 1; pointer-events: auto;
}
/* Match the hero-stream-switcher pill: translucent white on a dark backdrop,
   rounded, white text. Overrides .btn / .btn-primary background + border so
   the buttons read as "hero chrome" rather than primary CTAs. */
.inklet-gallery-hover-actions .btn {
    width: 150px;
    padding: 6px 14px;
    font-size: 0.85rem;
    font-weight: 500;
    background: rgba(255, 255, 255, 0.15);
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 20px;
    color: #fff;
}
.inklet-gallery-hover-actions .btn:hover {
    background: rgba(255, 255, 255, 0.25);
    border-color: rgba(255, 255, 255, 0.5);
    color: #fff;
}

/* Lift the corner archive / restore / delete-forever buttons above the new
   overlay so they remain clickable when the overlay is visible. */
.inklet-gallery-delete,
.inklet-gallery-restore,
.inklet-gallery-delete-forever {
    z-index: 2;
}

/* --- Feedback Widget --- */
.ie-feedback-btn {
    position: fixed;
    bottom: 24px;
    right: 24px;
    /* Above the 95% editor modal (100002), layers palette (100005), and the
       editor's child dialogs (100010) so a ticket can be raised from anywhere.
       Below the action-modal blocker (2147483647) which is the "I'm working,
       don't touch me" overlay. */
    z-index: 100020;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: none;
    background: var(--accent);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 12px rgba(0,0,0,0.2);
    transition: background 0.15s, transform 0.15s;
}
.ie-feedback-btn:hover { background: #8a1f3c; transform: scale(1.05); }
/* Red dot indicator for unread replies */
.ie-feedback-btn.has-unread::after {
    content: '';
    position: absolute;
    top: 2px;
    right: 2px;
    width: 12px;
    height: 12px;
    background: #ef4444;
    border-radius: 50%;
    border: 2px solid var(--paper, #fff);
    pointer-events: none;
}
.ie-feedback-panel {
    position: fixed;
    bottom: 80px;
    right: 24px;
    z-index: 100020;
    width: 360px;
    background: var(--card-bg, #fff);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 16px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.15);
    font-family: 'Inter', sans-serif;
}
.ie-feedback-header {
    font-size: 0.95rem;
    font-weight: 600;
    margin-bottom: 4px;
}
.ie-feedback-email {
    font-size: 0.75rem;
    color: var(--muted);
    margin-bottom: 12px;
}
.ie-feedback-textarea {
    width: 100%;
    box-sizing: border-box;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 10px;
    font-size: 0.85rem;
    font-family: 'Inter', sans-serif;
    resize: vertical;
    min-height: 80px;
    margin-bottom: 8px;
    background: var(--paper, #fff);
    color: var(--ink);
}
.ie-feedback-textarea:focus {
    outline: none;
    border-color: var(--accent);
}
.ie-feedback-checkbox {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.8rem;
    color: var(--muted);
    margin-bottom: 12px;
    cursor: pointer;
}
.ie-feedback-status {
    font-size: 0.75rem;
    color: var(--muted);
    text-align: center;
    margin-top: 8px;
    min-height: 1em;
}

/* Ticket list in the feedback panel */
.ie-fb-ticket-list {
    max-height: 340px;
    overflow-y: auto;
}
.ie-fb-ticket-row {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px;
    border-bottom: 1px solid var(--border);
    cursor: pointer;
    transition: background 0.1s;
    font-size: 0.8rem;
}
.ie-fb-ticket-row:hover { background: var(--cream, #faf9f5); }
.ie-fb-ticket-row:last-child { border-bottom: none; }
.ie-fb-ticket-info { flex: 1; min-width: 0; }
.ie-fb-ticket-comment {
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: var(--ink);
    margin-bottom: 2px;
}
.ie-fb-ticket-meta { font-size: 0.7rem; color: var(--muted); display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.ie-fb-unread-dot {
    width: 8px; height: 8px;
    background: #ef4444;
    border-radius: 50%;
    flex-shrink: 0;
    margin-top: 6px;
}

/* Thread messages in the feedback panel */
.ie-fb-thread-messages {
    max-height: 300px;
    overflow-y: auto;
    margin-bottom: 8px;
    padding: 4px 0;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}

/* Reply form in the feedback panel */
.ie-fb-reply-form { margin-top: 4px; }

/* New Inklet card variant — reuses .inklet-gallery-card for dimensions/hover.
   Overrides the skeleton gradient/animation on .inklet-gallery-thumb so a
   random hero background image can show through, darkened slightly, with a
   white Feather plus-circle centred on top. */
.inklet-gallery-card.inklet-gallery-new .inklet-gallery-thumb {
    background: none;
    animation: none;
    position: relative;
}
.inklet-gallery-card.inklet-gallery-new .inklet-gallery-bg {
    position: absolute;
    inset: 0;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}
.inklet-gallery-card.inklet-gallery-new .inklet-gallery-bg::after {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.28);
    pointer-events: none;
}
.inklet-gallery-card.inklet-gallery-new .inklet-gallery-plus {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 64px;
    height: 64px;
    color: #fff;
    stroke-width: 1.2;
    pointer-events: none;
    z-index: 1;
    transition: color 0.15s ease;
}
.inklet-gallery-card.inklet-gallery-new:hover .inklet-gallery-plus {
    /* Subtle neutral contrast vs the default white — keeps the lozenge
     * visually different on hover without competing with the accent
     * colour used for primary CTAs elsewhere. */
    color: #d4d4d8;
}

/* --- Save Split Button --- */
.ie-save-dropdown-btn {
    display: flex; align-items: stretch; border-radius: 6px; overflow: hidden;
    border: 1px solid rgba(255,255,255,0.3);
}
.ie-save-main {
    background: rgba(255,255,255,0.2); color: #fff; border: none;
    padding: 4px 14px; font-size: 0.85rem; cursor: pointer; font-family: 'Inter', sans-serif;
}
.ie-save-main:hover { background: rgba(255,255,255,0.3); }
.ie-save-caret {
    background: rgba(255,255,255,0.15); color: #fff; border: none; border-left: 1px solid rgba(255,255,255,0.3);
    padding: 4px 8px; font-size: 0.6rem; cursor: pointer;
}
.ie-save-caret:hover { background: rgba(255,255,255,0.3); }
.ie-save-dropdown-menu {
    position: absolute; top: 100%; right: 0; margin-top: 4px;
    background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px;
    box-shadow: 0 4px 16px rgba(0,0,0,0.15); min-width: 140px; z-index: 1000;
}
.ie-save-option {
    display: block; width: 100%; text-align: left; border: none; background: transparent;
    padding: 10px 14px; font-size: 0.85rem; color: var(--ink); cursor: pointer;
    font-family: 'Inter', sans-serif;
}
.ie-save-option:hover { background: var(--cream); }

/* --- Save Status --- */
.save-status-saving { color: var(--muted); animation: pulse-fade 1s infinite; }
.save-status-saved { color: var(--success); }
.save-status-failed { color: var(--danger); }
.save-status-dirty { color: var(--muted); }
@keyframes pulse-fade { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }

/* --- Widget Editor Layout (inline mode) --- */
.widget-editor-layout {
    display: grid; grid-template-columns: 370px 1fr; gap: 25px;
    min-height: calc(100vh - 120px);
}
.widget-panel {
    overflow-y: auto; max-height: calc(100vh - 120px);
    background: var(--paper);
    padding: 0;
    position: relative;
    display: flex; flex-direction: column;
}
.panel-main-content { flex: 1; overflow-y: auto; }

/* Column tabs */
.col-tab-bar { border-bottom: 1px solid var(--border); }
.col-tab {
    padding: 8px 14px; border: none; background: transparent;
    font-size: 12px; font-weight: 500; font-family: 'Inter', sans-serif;
    color: var(--muted); cursor: pointer; border-bottom: 2px solid transparent;
    text-transform: uppercase; letter-spacing: 0.5px; transition: all 0.15s;
}
.col-tab:hover { color: var(--ink); }
.col-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.col-tab.dragging { opacity: 0.4; }
.col-tab.drag-over { border-bottom-color: var(--accent); }
.col-tab-add {
    padding: 6px 10px; border: 1px dashed var(--border); border-radius: 4px;
    background: transparent; font-size: 11px; font-family: 'Inter', sans-serif;
    color: var(--muted); cursor: pointer; transition: all 0.15s;
}
.col-tab-add:hover { border-color: var(--accent); color: var(--accent); }
.col-tab-cog {
    padding: 6px 8px; border: none; background: transparent;
    font-size: 16px; color: var(--muted); cursor: pointer; transition: all 0.15s;
}
.col-tab-cog:hover { color: var(--accent); }
.col-tab-cog.active { color: var(--accent); }

/* Bottom action bar */
.panel-action-bar {
    display: flex; gap: 8px; padding: 12px 20px;
    border-top: 1px solid var(--border); background: var(--paper);
    flex-shrink: 0;
}
.panel-action-btn {
    flex: 1; padding: 8px 4px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--paper); color: var(--ink); font-size: 0.78rem; font-weight: 500;
    cursor: pointer; text-align: center; transition: all 0.15s ease;
    font-family: 'Inter', sans-serif;
}
.panel-action-btn:hover { border-color: var(--accent); color: var(--accent); }
.panel-action-reset { color: var(--muted); font-weight: 400; }
.panel-action-reset:hover { color: var(--accent); }
#pc-col-delete-btn:hover { color: #dc2626; }
/* Fixed Book slot (not draggable, not deletable) */
.slot-item.slot-item-fixed {
    background: var(--sand);
}
.slot-grab.slot-grab-disabled {
    opacity: 0 !important;
    pointer-events: none;
    cursor: default;
}
.widget-output {
    overflow-y: auto; max-height: calc(100vh - 120px);
    padding: 0;
}

/* Slot edit button + text preview */
.slot-edit {
    border: none; background: transparent; cursor: pointer;
    color: var(--muted); font-size: 13px; padding: 2px 4px;
    flex-shrink: 0;
}
.slot-edit:hover { color: var(--accent); }
.slot-text-preview {
    flex: 1; font-size: 10px; color: var(--muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    margin: 0 2px; min-width: 0;
}

/* Slot editor — overlays the full editing panel */
.slot-editor-inline {
    position: absolute; top: 0; left: 0; right: 0; bottom: 0;
    background: var(--card-bg); z-index: 100;
    overflow-y: auto;
}
/* Per-slot alignment control */
.se-align-row { margin-bottom: 4px; }
.se-align-label {
    display: block; font-size: 12px; font-weight: 500; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 5px;
}
.se-align-icons { display: flex; gap: 4px; }
.se-align-btn {
    width: 32px; height: 28px; display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--border); border-radius: 4px; background: var(--paper);
    color: var(--muted); cursor: pointer; transition: all 0.15s;
}
.se-align-btn:hover { border-color: var(--accent); color: var(--accent); }
.se-align-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); }
.slot-editor-close-btn {
    border: 1px solid var(--border) !important; background: transparent !important;
    color: var(--ink) !important; font-size: 0.82rem !important; padding: 5px 16px !important;
    border-radius: 6px !important; cursor: pointer; transition: all 0.15s;
}
.slot-editor-close-btn:hover {
    background: var(--accent) !important; color: #fff !important; border-color: var(--accent) !important;
}
/* Inline typography section in slot editors */
.se-typo-section {
    margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border);
}
.se-typo-header {
    display: block;
    font-size: 12px; font-weight: 500; color: var(--muted);
    text-transform: uppercase; letter-spacing: 0.5px;
    padding: 4px 0 8px;
}
.se-typo-body .field { margin-bottom: 10px; }
.se-typo-body .field-row { display: flex; gap: 10px; margin-bottom: 10px; }
.se-typo-body .field-row .field { flex: 1; margin-bottom: 0; }
.se-typo-body .toggle-row { display: flex; align-items: center; gap: 8px; }
.slot-editor-header {
    display: flex; justify-content: space-between; align-items: center;
    padding: 16px 20px 12px; border-bottom: 1px solid var(--border);
}
.slot-editor-header h3 { margin: 0; font-size: 1rem; }
.slot-editor-close {
    border: none; background: transparent; font-size: 1.3rem;
    cursor: pointer; color: var(--muted); padding: 0; line-height: 1;
}
.slot-editor-close:hover { color: var(--ink); }
.slot-editor-body {
    padding: 16px 20px;
}
/* Quill rich text editor overrides */
.slot-editor-inline .ql-toolbar {
    border-color: var(--border); border-radius: 6px 6px 0 0;
    background: var(--sand); padding: 6px 8px;
}
.slot-editor-inline .ql-container {
    border-color: var(--border); border-radius: 0 0 6px 6px;
    font-family: 'Inter', sans-serif; font-size: 13px;
}
.slot-editor-inline .ql-editor {
    min-height: 120px; max-height: 400px; overflow-y: auto;
}
.slot-editor-inline .ql-editor.ql-blank::before {
    font-style: normal; color: var(--muted);
}
.slot-editor-body .field { margin-bottom: 12px; }
/* Labels inherit from base .field label in widget-shared.css (12px, 500, uppercase) */
.slot-editor-body input[type="text"],
.slot-editor-body input[type="number"],
.slot-editor-body textarea,
.slot-editor-body select {
    width: 100%; padding: 8px; border: 1px solid var(--border);
    border-radius: var(--radius); font-size: 13px;
    font-family: 'Inter', sans-serif; background: var(--paper);
    color: var(--ink); box-sizing: border-box;
}
.slot-editor-body textarea { resize: vertical; }
.slot-editor-footer {
    padding: 12px 20px 16px; border-top: 1px solid var(--border);
    display: flex; justify-content: flex-end;
}
.se-section { }
.slot-editor-body .isbn-text-row { margin-bottom: 0; }
.slot-editor-body .isbn-text-header {
    display: flex; align-items: center; justify-content: space-between;
    margin-bottom: 4px;
}
.slot-editor-body .isbn-text-header label {
    margin: 0;
}
.slot-editor-body .revert-btn {
    border: none; background: transparent; cursor: pointer;
    color: var(--muted); padding: 2px; width: 20px; height: 20px;
}
.slot-editor-body .revert-btn:hover { color: var(--accent); }
.slot-editor-body .toggle-row {
    display: flex; align-items: center; gap: 8px;
}

/* --- Admin → Status panel --- */
.status-controls {
    display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
    margin-bottom: 1rem;
}
.status-overview {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 0.75rem;
}
.status-pill {
    border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px;
    background: var(--paper);
}
.status-pill-label {
    font-size: 0.75rem; color: var(--muted); text-transform: uppercase;
    letter-spacing: 0.04em; margin-bottom: 4px;
}
.status-pill-value { font-size: 0.9rem; font-weight: 600; color: var(--ink); }
.status-pill-green { border-left: 3px solid #16a34a; }
.status-pill-amber { border-left: 3px solid #d4a843; background: #fef9e7; }
.status-pill-red   { border-left: 3px solid #c44;    background: #fdecec; }

.status-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 0.75rem;
}
.status-metric {
    border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px;
    background: var(--paper);
}
.status-metric-label {
    font-size: 0.72rem; color: var(--muted); text-transform: uppercase;
    letter-spacing: 0.04em; margin-bottom: 6px;
}
.status-metric-value {
    font-size: 1.05rem; font-weight: 600; color: var(--ink); margin-bottom: 6px;
}
.status-metric-sub {
    font-size: 0.75rem; font-weight: 400; color: var(--muted); margin-left: 4px;
}

.status-state {
    display: inline-block; padding: 2px 8px; border-radius: 10px;
    font-size: 0.7rem; font-weight: 600; letter-spacing: 0.02em;
}
.status-state-alarm                 { background: #fdecec; color: #a02822; }
.status-state-insufficient_data     { background: #fef9e7; color: #8a6d1d; }
.status-state-ok                    { background: #e7fbe9; color: #16713a; }
.status-state-dlq                   { background: #fdecec; color: #a02822; margin-left: 6px; }

.status-bad  { color: #c44; font-weight: 600; }
.status-warn { color: #8a6d1d; font-weight: 600; }

/* ── Now Trending — Clusters sub-tab (views-admin-now-trending.js) ── */

.ntc-cluster-card {
    margin-bottom: 0.75rem; padding: 1rem 1.25rem;
    transition: box-shadow 0.15s ease, transform 0.05s ease;
    cursor: pointer;
}
.ntc-cluster-card:hover { box-shadow: 0 4px 14px rgba(0,0,0,0.08); }
.ntc-cluster-card:active { transform: translateY(1px); }

.ntc-cluster-head {
    display: flex; gap: 1rem; justify-content: space-between; align-items: flex-start;
}
.ntc-cluster-titleblock { flex: 1; min-width: 0; }
.ntc-cluster-title {
    margin: 0 0 6px; font-family: 'DM Serif Display', Georgia, serif;
    font-size: 1.15rem; line-height: 1.2;
}
.ntc-cluster-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ntc-chip {
    display: inline-block; padding: 2px 8px; border-radius: 10px;
    background: #eee; color: #444; font-size: 0.7rem; font-weight: 500;
}
.ntc-chip-anchor {
    background: #e6f0ff; color: #2a4d8c;
    font-family: 'SFMono-Regular', Menlo, monospace; font-size: 0.65rem;
}
.ntc-cluster-scoreblock {
    display: flex; align-items: center; gap: 10px; flex-shrink: 0;
}
.ntc-cluster-score {
    font-size: 1.5rem !important; font-weight: 700 !important;
    padding: 4px 12px !important; min-width: 56px; text-align: center;
}
.ntc-cluster-desc {
    margin: 8px 0 4px; color: #444; font-size: 0.9rem; line-height: 1.4;
}
.ntc-cluster-meta {
    color: var(--muted); font-size: 0.78rem; margin-top: 6px;
    display: flex; gap: 4px; align-items: center; flex-wrap: wrap;
}
.ntc-meta-sep { color: #ccc; }
.ntc-cluster-actions {
    display: flex; gap: 8px; margin-top: 10px; padding-top: 8px;
    border-top: 1px solid var(--border);
}

/* Components mini-bar — five vertical segments coloured per component */
.ntc-cbar {
    display: flex; gap: 2px; align-items: flex-end;
    height: 36px; width: 70px;
}
.ntc-cbar-seg {
    flex: 1; height: 100%; position: relative;
    background: #f0f0f0; border-radius: 2px;
    display: flex; flex-direction: column; justify-content: flex-end;
    overflow: hidden;
}
.ntc-cbar-fill {
    width: 100%; transition: height 0.3s ease;
}
.ntc-cbar-lbl {
    position: absolute; bottom: -14px; left: 0; right: 0;
    text-align: center; font-size: 0.55rem; color: var(--muted);
    font-family: 'SFMono-Regular', Menlo, monospace;
}

/* Cluster detail modal */
.ntc-detail-overlay { z-index: 9000; }
.ntc-detail-modal {
    background: #fff; max-width: 880px; width: 92%; max-height: 90vh;
    overflow-y: auto; border-radius: 10px; padding: 1.5rem 1.75rem;
    position: relative; box-shadow: 0 10px 40px rgba(0,0,0,0.18);
}
.ntc-detail-close {
    position: absolute; top: 10px; right: 12px; background: none; border: none;
    font-size: 1.6rem; line-height: 1; cursor: pointer; color: #888;
}
.ntc-detail-close:hover { color: #222; }
.ntc-detail-head {
    display: flex; gap: 1rem; justify-content: space-between; align-items: flex-start;
    padding-bottom: 12px; border-bottom: 1px solid var(--border); margin-bottom: 16px;
}
.ntc-detail-title {
    margin: 0 0 6px; font-family: 'DM Serif Display', Georgia, serif; font-size: 1.4rem;
}
.ntc-detail-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.ntc-detail-score-badge {
    font-size: 2.2rem; font-weight: 700; color: #0a5;
    padding: 4px 14px; background: #e7fbe9; border-radius: 10px;
}
.ntc-detail-desc { color: #333; font-size: 0.95rem; line-height: 1.5; margin: 0 0 16px; }
.ntc-detail-h {
    margin: 18px 0 8px; font-size: 0.85rem; font-weight: 600;
    color: #555; text-transform: uppercase; letter-spacing: 0.04em;
}
.ntc-detail-empty { color: var(--muted); font-size: 0.85rem; padding: 12px 0; }

.ntc-detail-campaign {
    background: #f6f6f3; border-radius: 8px; padding: 14px 16px;
    margin-bottom: 8px;
}
.ntc-detail-pitch { font-weight: 600; font-size: 1.05rem; margin-bottom: 4px; }
.ntc-detail-rationale { color: #555; font-size: 0.88rem; margin-bottom: 12px; }
.ntc-detail-asset-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 10px;
}
.ntc-detail-asset {
    background: #fff; border: 1px solid var(--border); border-radius: 6px;
    padding: 10px 12px;
}
.ntc-detail-asset-h {
    font-size: 0.7rem; font-weight: 600; color: #888; text-transform: uppercase;
    letter-spacing: 0.04em; margin-bottom: 6px;
}
.ntc-detail-asset-sub { font-size: 0.8rem; font-weight: 500; margin-bottom: 4px; }
.ntc-detail-asset-body { font-size: 0.8rem; color: #444; line-height: 1.4; }
.ntc-detail-asset-empty { color: var(--muted); font-size: 0.8rem; }
.ntc-detail-ig { padding-left: 18px; margin: 0; font-size: 0.8rem; color: #444; }
.ntc-detail-ig li { margin-bottom: 3px; }

.ntc-detail-match {
    display: flex; gap: 12px; align-items: flex-start;
    padding: 10px 0; border-bottom: 1px solid #f0f0f0;
}
.ntc-detail-match:last-child { border-bottom: none; }
.ntc-detail-cover {
    width: 50px; height: 75px; object-fit: cover; border: 1px solid var(--border);
    border-radius: 3px; flex-shrink: 0;
}
.ntc-detail-cover-empty {
    background: #f5f5f5; display: flex; align-items: center; justify-content: center;
    color: #ccc; font-size: 0.7rem;
}
.ntc-detail-match-body { flex: 1; min-width: 0; }
.ntc-detail-match-title { font-weight: 600; font-size: 0.92rem; }
.ntc-detail-match-author { color: var(--muted); font-size: 0.8rem; margin-top: 2px; }
.ntc-detail-match-reason { font-size: 0.82rem; color: #444; margin-top: 4px; line-height: 1.4; }
.ntc-detail-match-actions { display: flex; flex-direction: column; gap: 4px; align-items: flex-end; flex-shrink: 0; }

.ntc-detail-signal {
    display: grid; grid-template-columns: 90px 110px 1fr 70px;
    gap: 8px; padding: 6px 0; font-size: 0.78rem; align-items: baseline;
    border-bottom: 1px solid #f7f7f7;
}
.ntc-detail-signal:last-child { border-bottom: none; }
.ntc-detail-signal-kind {
    font-family: 'SFMono-Regular', Menlo, monospace; font-size: 0.7rem;
    color: #2a4d8c; background: #e6f0ff; padding: 1px 6px; border-radius: 3px;
    text-align: center;
}
.ntc-detail-signal-source { color: var(--muted); font-size: 0.75rem; }
.ntc-detail-signal-title { color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ntc-detail-signal-when { color: var(--muted); font-size: 0.72rem; text-align: right; }

@media (max-width: 720px) {
    .ntc-cluster-head { flex-direction: column; }
    .ntc-cluster-scoreblock { align-self: flex-start; }
    .ntc-detail-signal { grid-template-columns: 1fr; gap: 2px; }
}

/* ── Font Browser modal (widgets/font-browser.js) ──────────────────── */
.fbm-modal { width: 80vw; max-width: 1100px; height: 80vh; display: flex; flex-direction: column; padding: 1.25rem; }
.fbm-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem; }
.fbm-toolbar { display: flex; flex-direction: column; gap: 0.55rem; margin-bottom: 0.5rem; }
.fbm-toolbar input[type="search"] { padding: 0.5rem 0.75rem; border: 1px solid var(--border); border-radius: 6px; font-size: 0.9rem; font-family: 'Inter', sans-serif; }
.fbm-categories { display: flex; gap: 1rem; flex-wrap: wrap; font-size: 0.85rem; color: var(--muted); }
.fbm-categories label { display: inline-flex; align-items: center; gap: 0.3rem; cursor: pointer; }
.fbm-themes { display: flex; flex-wrap: wrap; gap: 6px; }
.fbm-theme {
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-family: 'Inter', sans-serif;
  cursor: pointer;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.fbm-theme:hover { border-color: var(--accent); color: var(--accent); }
.fbm-theme.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.fbm-status { font-size: 0.8rem; color: var(--muted); margin-bottom: 0.5rem; }
.fbm-grid { flex: 1; overflow-y: auto; display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); grid-auto-rows: min-content; align-content: start; gap: 0.75rem; padding-right: 0.25rem; }
.fbm-card { border: 1px solid var(--border); border-radius: 8px; padding: 0.9rem 1rem; background: var(--paper); display: flex; flex-direction: column; gap: 0.4rem; min-height: 130px; }
.fbm-card-head { font-size: 1.5rem; line-height: 1.2; color: var(--ink); }
.fbm-card-sample { font-size: 1rem; line-height: 1.35; color: var(--ink); flex: 1; overflow: hidden; }
.fbm-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-top: auto; }
.fbm-card-cat { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.fbm-card-add { font-size: 0.75rem; padding: 0.25rem 0.6rem; }
.fbm-card-add.fbm-card-added { background: var(--accent-light); color: var(--accent); border-color: var(--accent); }

/* ── Settings → Fonts page (views-fonts.js) ────────────────────────── */
.vf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 0.75rem; }
.vf-card { border: 1px solid var(--border); border-radius: 8px; padding: 1rem 1.1rem; background: var(--paper); display: flex; flex-direction: column; gap: 0.4rem; min-height: 140px; }
.vf-card-head { font-size: 1.6rem; line-height: 1.2; color: var(--ink); }
.vf-card-sample { font-size: 1rem; line-height: 1.35; color: var(--ink); flex: 1; overflow: hidden; }
.vf-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin-top: auto; }
.vf-card-cat { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.vf-card-del { padding: 0.3rem 0.5rem; line-height: 0; }

/* ── Book Cover → Text accordion → AI Font Pairing (multi-editor-engine.js) ── */
.bcfp-intro { font-size: 0.8rem; color: var(--muted); margin-bottom: 0.65rem; line-height: 1.4; }
.bcfp-run { width: 100%; }
.bcfp-run:disabled { opacity: 0.5; cursor: not-allowed; }
.bcfp-hint { font-size: 0.72rem; color: var(--muted); margin-top: 0.5rem; font-style: italic; }
.bcfp-results { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.85rem; }
.bcfp-results:empty { margin-top: 0; }
.bcfp-empty { font-size: 0.8rem; color: var(--muted); padding: 0.5rem 0; font-style: italic; }
.bcfp-card { border: 1px solid var(--border); border-radius: 8px; padding: 0.7rem 0.85rem; background: var(--paper); display: flex; flex-direction: column; gap: 0.3rem; max-width: 400px; }
.bcfp-card-head { font-size: 1.15rem; line-height: 1.2; color: var(--ink); }
.bcfp-card-sample { font-size: 0.85rem; line-height: 1.35; color: var(--ink); }
/* Stacked + left-aligned: reason on top, +Add button on its own row below. */
.bcfp-card-foot { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; margin-top: 0.2rem; }
.bcfp-card-reason { font-size: 0.7rem; font-style: italic; color: var(--muted); line-height: 1.35; }
.bcfp-card-add { font-size: 0.72rem; padding: 0.25rem 0.55rem; flex-shrink: 0; align-self: flex-start; }
.bcfp-card-add.bcfp-card-added { background: var(--accent-light); color: var(--accent); border-color: var(--accent); cursor: default; }

/* ─────────────────────────────────────────────────────────────
 * Campaigns page
 * Hero gains a centre slot (search input). Lozenge cards use a
 * book-cover layout with a hover overlay mirroring the inklet-gallery-card
 * idiom — dark mute + centred "View" button + bottom-aligned title/sub.
 * ───────────────────────────────────────────────────────────── */
.page-hero-center {
    flex: 1 1 auto;
    display: flex;
    justify-content: center;
    align-items: center;
    min-width: 0;       /* allow the flex item to shrink below input min-content */
    padding: 0 1rem;
}
.hero-search {
    width: 100%; max-width: 380px;
    border-radius: 20px;
    background: rgba(255,255,255,0.15);
    border: 1px solid rgba(255,255,255,0.4);
    color: #fff;
    padding: 6px 14px;
    font-family: inherit; font-size: 0.85rem;
    -webkit-appearance: none;
    appearance: none;
}
.hero-search::placeholder { color: rgba(255,255,255,0.7); }
.hero-search:hover  { background: rgba(255,255,255,0.22); }
.hero-search:focus  { outline: none; background: rgba(255,255,255,0.28); border-color: rgba(255,255,255,0.7); }
/* Hide the WebKit clear-X (we leave the user to backspace; matches our other
 * search inputs) */
.hero-search::-webkit-search-cancel-button { display: none; }

.campaign-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 1rem;
}
@media (max-width: 1200px) { .campaign-grid { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 900px)  { .campaign-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 600px)  { .campaign-grid { grid-template-columns: repeat(2, 1fr); } }

.campaign-book-card {
    position: relative;
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow: hidden;
    background: var(--paper);
    cursor: pointer;
    transition: box-shadow 0.15s, transform 0.15s;
}
.campaign-book-card:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.1); transform: translateY(-2px); }

.campaign-book-cover {
    aspect-ratio: 2 / 3;
    background: #f1f5f9;
    position: relative;
    overflow: hidden;
}
.campaign-book-cover img {
    width: 100%; height: 100%;
    object-fit: cover; object-position: center;
    display: block;
    opacity: 0;
    transition: opacity 0.2s;
}
.campaign-book-cover img.loaded { opacity: 1; }
.campaign-book-cover-empty {
    width: 100%; height: 100%;
    display: flex; align-items: center; justify-content: center;
    color: #94a3b8; font-size: 0.75rem;
}

.campaign-book-meta { padding: 10px 12px; }
.campaign-book-title {
    font-weight: 600;
    font-size: 0.82rem;
    line-height: 1.25;
    color: var(--ink);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
    min-height: calc(0.82rem * 1.25 * 2);   /* reserve 2 lines so cards align */
}
.campaign-book-sub  { font-size: 0.7rem;  color: var(--muted); margin-top: 4px; }
.campaign-book-date { font-size: 0.7rem;  color: var(--muted); margin-top: 2px; }

/* Hover overlay sits INSIDE .campaign-book-cover, so inset:0 confines it
 * to the cover box — the meta footer below remains readable on hover. */
.campaign-book-hover {
    position: absolute; inset: 0;
    background: rgba(20,20,30,0.62);
    opacity: 0;
    transition: opacity 0.15s;
    pointer-events: none;
}
.campaign-book-card:hover .campaign-book-hover { opacity: 1; pointer-events: auto; }

.campaign-book-view-btn {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    border-radius: 20px;
    background: rgba(255,255,255,0.15);
    border: 1px solid rgba(255,255,255,0.55);
    color: #fff;
    padding: 6px 22px;
    font-family: inherit; font-size: 0.85rem; font-weight: 500;
    cursor: pointer;
    backdrop-filter: blur(2px);
    transition: background 0.15s, border-color 0.15s;
}
.campaign-book-view-btn:hover { background: rgba(255,255,255,0.28); border-color: #fff; }

/* ─────────────────────────────────────────────────────────────
 * Campaigns per-book modal — channel-group navigator
 * Clone of .tbm-modal at 95vw × 95vh, with a left sidebar listing
 * channel groups and a right placeholder pane (template inklets land
 * here next iteration). lib-modal.js owns lifecycle (Esc + backdrop).
 * ───────────────────────────────────────────────────────────── */
.campaign-modal {
    position: fixed; inset: 0; z-index: 99999;
    background: rgba(26,26,46,0.45);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center;
    animation: fadeIn 0.2s ease;
}
.campaign-modal-inner {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    width: 95vw; height: 95vh;
    overflow: hidden;
    display: flex; flex-direction: column;
}

/* Top bar — 1.5-line darker-grey strip. Title-pair sits left, X right. */
.campaign-modal-top-bar {
    background: #d4d4d4;
    padding: 6px 20px;
    display: flex; justify-content: space-between; align-items: center;
    flex-shrink: 0;
    min-height: calc(2.25rem + 5px);   /* +5px taller than the original 2.25rem */
}
.campaign-modal-top-bar .section-title-pair { display: flex; align-items: baseline; gap: 12px; }
.campaign-modal-top-bar .section-title    { font-family:'DM Serif Display', serif; font-size: 1.4rem; color: var(--ink); line-height: 1.2; }
.campaign-modal-top-bar .section-subtitle { font-family:'Inter', sans-serif;       font-size: 0.9rem; color: #64748b; font-weight: 400; line-height: 1.2; }
.campaign-modal-top-bar .modal-close-x    { background: none; border: none; font-size: 1.5rem; cursor: pointer; color: var(--muted); padding: 0 4px; line-height: 1; }
.campaign-modal-top-bar .modal-close-x:hover { color: var(--ink); }

/* Body — flex row: fixed-width sidebar + flexible right pane. */
.campaign-modal-body { display: flex; flex: 1; min-height: 0; }
.campaign-modal-sidebar {
    width: 240px; flex-shrink: 0;
    border-right: 1px solid var(--border);
    background: var(--paper);
    overflow-y: auto;
    padding: 12px 8px;
    display: flex; flex-direction: column; gap: 4px;
}
.campaign-modal-sidebar-loading,
.campaign-modal-sidebar-empty,
.campaign-modal-sidebar-error { padding: 12px; font-size: 0.85rem; color: var(--muted); }
.campaign-modal-pane { flex: 1; overflow: auto; }

/* Sidebar items — icon + name, with three modifier states:
 *   .is-active    → full colour (default; regular weight)
 *   .is-inactive  → muted text, dimmed icon
 *   .is-selected  → accent outline + bold weight
 * Only the selected group is bold; non-selected active groups are regular.
 */
.campaign-modal-group {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 12px;
    border: 2px solid transparent;
    border-radius: 8px;
    background: none;
    text-align: left;
    cursor: pointer;
    color: var(--ink);
    font-family: 'Inter', sans-serif; font-size: 0.95rem; font-weight: 400;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.campaign-modal-group:hover { background: rgba(175,40,96,0.04); }
.campaign-modal-group.is-inactive { color: var(--muted); }
.campaign-modal-group.is-inactive .campaign-modal-group-icon { opacity: 0.45; }
.campaign-modal-group.is-selected { border-color: var(--accent); font-weight: 600; }
.campaign-modal-group-icon {
    width: 24px; height: 24px; flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--ink);
    font-size: 22px; line-height: 1;
}
.campaign-modal-group-icon svg { width: 24px; height: 24px; display: block; }
.campaign-modal-group.is-inactive .campaign-modal-group-icon svg { color: var(--muted); }
.campaign-modal-group-name { line-height: 1.2; }

/* ─────────────────────────────────────────────────────────────
 * Campaigns modal — right pane (template grid)
 * Populated when a channel group is selected. 3–4 cards per row
 * via auto-fill; each card is a square thumb with channel name +
 * summary below, hover surfaces the accent on the border.
 * ───────────────────────────────────────────────────────────── */
.campaign-pane-loading,
.campaign-pane-empty,
.campaign-pane-error { padding: 24px; color: var(--muted); font-size: 0.9rem; }
.campaign-pane-error { color: #c0392b; }

.campaign-pane-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1rem;
    padding: 20px;
}

.campaign-pane-card {
    display: flex; flex-direction: column;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    text-align: center;
    font-family: inherit;
    color: var(--ink);
}
/* Suppress the default <button> outline so we don't get a blue ring around
 * the whole card when focus returns after closing the zoom modal via Esc
 * (X click moves focus to the X button — which then disappears with the
 * modal — so the ring only appears on the Esc path). Keep keyboard a11y by
 * mirroring the hover state on :focus-visible. */
.campaign-pane-card:focus { outline: none; }
.campaign-pane-card:focus-visible .campaign-pane-thumb {
    border-color: var(--accent);
}
.campaign-pane-thumb {
    aspect-ratio: 1 / 1;
    border: 2px solid var(--border);
    border-radius: 12px;
    background: var(--paper);
    position: relative;
    overflow: hidden;
    box-sizing: border-box;
    transition: border-color 0.12s;
}
.campaign-pane-card:hover .campaign-pane-thumb {
    border-color: var(--accent);
}
/* Loading + empty placeholders fill the whole thumb box. */
.campaign-pane-thumb-loading,
.campaign-pane-thumb-empty {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    color: var(--muted);
    font-size: 0.85rem;
}
/* Scaler: positioned absolutely by applyRenderedHtml — width/height set to
 * the channel's natural canvas size; transform/left/top set inline. */
.campaign-pane-thumb-scaler {
    position: absolute;
    transform-origin: 0 0;
    pointer-events: none;
    /* width, height set inline; transform, left, top set after measure. */
}
/* Fallback <img> path (when /inklets/batch fails or no book context). */
.campaign-pane-thumb img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: contain;
    padding: 16px;
    box-sizing: border-box;
    display: block;
}
.campaign-pane-name {
    margin-top: 10px;
    font-family: 'Inter', sans-serif;
    font-size: 0.95rem;
    font-weight: 500;
    line-height: 1.2;
}
.campaign-pane-sub {
    margin-top: 4px;
    font-family: 'Inter', sans-serif;
    font-size: 0.8rem;
    color: var(--muted);
    line-height: 1.3;
}

/* ─────────────────────────────────────────────────────────────
 * Zoom modal body — reuses the .campaign-modal* outer shell so
 * the chrome (95vw × 95vh, grey top bar, X) is identical to the
 * parent navigator. Only the body differs: centred canvas wrap
 * with a light scrim so the inklet's white canvas stands out.
 * ───────────────────────────────────────────────────────────── */
.campaign-zoom-body {
    flex: 1;
    overflow: auto;
    background: #f5f5f5;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 24px;
}
.campaign-zoom-canvas-wrap {
    background: #fff;
    box-shadow: 0 4px 24px rgba(0,0,0,0.12);
    max-width: 100%;
}
.campaign-zoom-canvas-wrap > * { display: block; }

/* (AI Inklet modal + Schedule modal styles deleted Phase 33 alongside the
 * ISBN Emporium "Automate" section. The .ai-inklet-modal-* + the lone
 * .schedule-frequency-detail rule lived here until the widget files were
 * removed; git history preserves them.) */

/* ─────────────────────────────────────────────────────────────
 * AI Controls — Phase 31
 * Tabbed admin page for editing AI prompts stored in ai_prompts table.
 * Replaces the old "AI Assets" search/grid layout (deleted with that
 * feature in the same phase).
 * ───────────────────────────────────────────────────────────── */
.aic-tab-bar { margin-bottom: 1rem; margin-top: -0.25rem; }

.ai-prompts-table { width: 100%; }
.ai-prompts-table th,
.ai-prompts-table td { padding: 0.6rem 0.75rem; vertical-align: top; }
.ai-prompts-table tbody tr { border-top: 1px solid var(--border); }
.ai-prompts-table .btn-sm { margin-left: 4px; }

/* Icon-only action buttons in the AI Prompts table. Square hit-targets,
   tight padding, feather SVGs constrained to 14px. */
.aic-icon-btn {
    padding: 4px 6px;
    line-height: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.aic-icon-btn svg {
    width: 14px;
    height: 14px;
    display: block;
}

/* Inline code badge under the name in the table — shows the immutable
   lookup identifier wired into Lambda code. */
.ai-prompt-code-badge {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 6px;
    background: #f1f5f9;
    border: 1px solid var(--border);
    border-radius: 4px;
    font-family: 'SF Mono', Menlo, 'Courier New', monospace;
    font-size: 0.72rem;
    color: var(--muted);
    vertical-align: middle;
}

/* Placeholder token preview under the name in the AI Placeholders table
   — visually flags the curator-typed token they can paste into AI prompt
   bodies inside `{` `}` braces. */
.aic-placeholder-token {
    display: inline-block;
    margin-top: 2px;
    font-family: 'SF Mono', Menlo, 'Courier New', monospace;
    font-size: 0.8rem;
    color: var(--accent);
}

/* Small pill flagging a built-in placeholder row in the Placeholders
   tab. Built-in rows can be edited (metadata) or archived but never
   deleted — the pill signals that visually. */
.aic-builtin-pill {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 6px;
    background: var(--accent-light);
    color: var(--accent);
    border-radius: 4px;
    font-size: 0.7rem;
    font-weight: 600;
    vertical-align: middle;
}

/* Read-only Code field in Edit modal — visually distinct from editable
   fields so users instinctively know not to try changing it. */
.ai-prompt-input[readonly],
.ai-prompt-input:disabled {
    background: #f8fafc;
    color: var(--muted);
    cursor: not-allowed;
}

/* Form fields inside the Add/Edit modal */
.ai-prompt-field { margin-bottom: 1.25rem; }
/* Paired-fields container — Channel Group + Channel, Model + Env Var */
.ai-prompt-field-row {
    display: flex;
    gap: 1rem;
    margin-bottom: 1.25rem;
}
.ai-prompt-field-row .ai-prompt-field { margin-bottom: 0; }
.ai-prompt-field label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 0.35rem;
}
.ai-prompt-field .req { color: #c0392b; margin-left: 2px; }
.ai-prompt-field .ai-prompt-optional {
    font-size: 0.7rem;
    font-weight: normal;
    color: var(--muted);
    margin-left: 4px;
    text-transform: lowercase;
}
/* Greyed-out state for the System Prompt field when the API URL is an OpenAI
   image endpoint (/v1/images/…) — the field is saved but not sent there, so
   we visually de-emphasise it and surface an inline "ignored" tag + hint. The
   textarea stays editable (so switching to a chat endpoint keeps the value). */
.ai-prompt-field-muted label { color: var(--muted); }
.ai-prompt-field-muted .ai-prompt-ignored-tag { color: #c0392b; text-transform: none; }
.ai-prompt-input-muted {
    opacity: 0.55;
    background: #f6f6f8;
}
.ai-prompt-input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.55rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    font-family: 'Inter', sans-serif;
    font-size: 0.92rem;
    background: #fff;
}
.ai-prompt-input:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px rgba(99,102,241,0.12);
}
.ai-prompt-mono {
    font-family: 'SF Mono', Menlo, 'Courier New', monospace;
    font-size: 0.82rem;
    line-height: 1.45;
}
.ai-prompt-hint {
    font-size: 0.75rem;
    color: var(--muted);
    margin-top: 4px;
}
.ai-prompt-readonly {
    background: #f8fafc;
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.6rem 0.75rem;
    font-family: 'SF Mono', Menlo, 'Courier New', monospace;
    font-size: 0.78rem;
    line-height: 1.45;
    white-space: pre-wrap;
    word-break: break-word;
    margin: 0;
    max-height: 280px;
    overflow-y: auto;
}

/* History modal — one row per revision */
.aic-revision-row {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--border);
}
.aic-revision-row:last-child { border-bottom: none; }

/* Overlay shell + inner card for the AI Controls modals. Mirrors the
   .modal-overlay / .tbm-modal pattern used elsewhere — fixed full-screen
   backdrop + centred card with comfortable max-width for textareas. */
.aic-modal {
    position: fixed; inset: 0; z-index: 99999;
    background: rgba(26,26,46,0.45);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    display: flex; align-items: flex-start; justify-content: center;
    padding: 4vh 1rem 6vh;
    overflow-y: auto;
    animation: fadeIn 0.2s ease;
}
.aic-modal-inner {
    width: 100%;
    max-width: 760px;
    background: var(--card-bg);
    border-radius: 12px;
    box-shadow: 0 12px 48px rgba(0,0,0,0.18);
    overflow: hidden;
}

/* ── Phase 17 — Floating Layers panel (multi-editor canvas mode) ── */
.me-layers-panel {
    position: fixed;
    top: 96px; right: 24px;
    width: 260px;
    max-height: calc(100vh - 140px);
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.16);
    display: flex; flex-direction: column;
    /* Above the 95% editor modal (.ie-modal-overlay, 100002) so the floating
       Layers tool is usable while editing; below the editor's child dialogs
       (100010) so a modal dialog still covers it. */
    z-index: 100005;
    font-family: 'Inter', sans-serif;
}
.me-layers-titlebar {
    display: flex; align-items: center;
    justify-content: space-between;
    padding: 8px 12px;
    background: #f4f4f6;
    border-bottom: 1px solid var(--border);
    border-radius: 8px 8px 0 0;
    cursor: move;
    user-select: none;
}
.me-layers-title { font-size: 13px; font-weight: 600; color: var(--ink); }
.me-layers-close {
    background: none; border: none;
    color: var(--muted);
    font-size: 1.5rem; line-height: 1;
    cursor: pointer; padding: 0 4px;
}
.me-layers-close:hover { color: var(--ink); }

.me-layers-list {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 4px 0;
}
.me-layers-empty {
    padding: 16px; color: var(--muted);
    font-size: 12px; text-align: center;
}

.me-layers-row {
    display: flex; align-items: center; gap: 8px;
    padding: 4px 10px;
    cursor: pointer;
    border-top: 1px solid transparent;
    border-bottom: 1px solid transparent;
}
.me-layers-row:hover { background: rgba(0,0,0,0.03); }
.me-layers-row.is-selected { background: rgba(175,40,96,0.08); }
.me-layers-row.is-hidden .me-layers-label,
.me-layers-row.is-hidden .me-layers-thumb { opacity: 0.45; }
.me-layers-row.is-dragging { opacity: 0.5; }
.me-layers-row.me-layers-drag-over { border-top-color: var(--accent); }

.me-layers-eye {
    background: none; border: none; padding: 2px;
    color: var(--ink); cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.me-layers-eye:hover { color: var(--accent); }
.me-layers-row.is-hidden .me-layers-eye { color: var(--muted); }

.me-layers-thumb {
    width: 36px; height: 36px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--paper);
    flex-shrink: 0;
    overflow: hidden;
    position: relative;
    display: flex; align-items: center; justify-content: center;
}
.me-layers-thumb-t {
    font-family: 'Libre Baskerville', Georgia, serif;
    font-weight: 700;
    color: var(--ink);
    font-size: 18px;
}
.me-layers-thumb-glyph {
    font-family: 'Inter', sans-serif;
    font-weight: 600;
    color: var(--muted);
    font-size: 14px;
    text-transform: uppercase;
}
.me-layers-thumb-img {
    /* Phase 17: image thumb — centre-crop the underlying cover/photo/image
     * into the 36×36 box. Filling the box keeps every row visually balanced
     * regardless of the source aspect ratio. */
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
.me-layers-thumb-inner {
    transform: scale(0.18);
    transform-origin: top left;
    width: 200px; height: 200px;
    pointer-events: none;
}

.me-layers-label {
    font-size: 12px; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    flex: 1 1 auto;
}

/* ── Channel name + summary in the in-canvas switcher bar ── */
/* Both name + summary use the same muted colour for visual harmony;
 * the bold weight on the name still gives it visual priority. */
.me-channel-name {
    font-weight: 400;
    color: var(--muted);
}
.me-channel-name strong {
    font-weight: 600;
    color: var(--muted);
}
.me-channel-summary {
    color: var(--muted);
    margin-left: 6px;
    font-weight: 400;
}

/* ── Image-block: Flip row + crop icon + icon buttons ── */
.me-image-flip-row {
    display: flex; flex-direction: column; align-items: flex-start; gap: 6px;
    margin-top: 10px;
}
.me-image-flip-row > label {
    margin: 0; font-size: 12px;
    color: var(--muted);
}
.me-image-flip-buttons {
    display: flex; gap: 6px;
}
.me-btn-icon {
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 6px;
    cursor: pointer;
    color: var(--ink);
    display: inline-flex; align-items: center; justify-content: center;
    line-height: 0;
}
.me-btn-icon:hover { border-color: var(--accent); color: var(--accent); }
.me-btn-icon.is-active {
    background: rgba(175,40,96,0.08);
    border-color: var(--accent);
    color: var(--accent);
}

/* (Image crop icon moved into .me-canvas-block-tools — no standalone CSS) */

/* ── Crop modal shell + preview + sliders ── */
.me-image-crop-overlay {
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.5);
    z-index: 9500;
    display: flex; align-items: center; justify-content: center;
}
.me-image-crop-modal {
    background: var(--paper);
    border-radius: 8px;
    box-shadow: 0 12px 40px rgba(0,0,0,0.24);
    width: 480px; max-width: 90vw;
    max-height: 90vh;
    display: flex; flex-direction: column;
}
.me-image-crop-top-bar {
    display: flex; align-items: center; justify-content: space-between;
    padding: 12px 20px;
    border-bottom: 1px solid var(--border);
    font-family: 'DM Serif Display', serif;
    font-size: 1rem;
    color: var(--ink);
}
.me-image-crop-top-bar .modal-close-x {
    background: none; border: none;
    color: var(--muted);
    font-size: 1.5rem; line-height: 1;
    cursor: pointer; padding: 0 4px;
}
.me-image-crop-top-bar .modal-close-x:hover { color: var(--ink); }
.me-image-crop-body {
    padding: 16px 20px;
    overflow-y: auto;
    flex: 1 1 auto;
}
.me-image-crop-preview {
    width: 100%;
    aspect-ratio: 1 / 1;
    border: 1px solid var(--border);
    border-radius: 6px;
    overflow: hidden;
    background: #f4f4f6;
    margin-bottom: 16px;
}
.me-image-crop-preview img {
    width: 100%; height: 100%;
    display: block;
}
.me-image-crop-sliders .slider-row {
    margin-bottom: 10px;
}
.me-image-crop-sliders label {
    display: block;
    font-size: 12px;
    color: var(--muted);
    margin-bottom: 4px;
}
.me-image-crop-sliders input[type="range"] {
    width: 100%;
}
.me-image-crop-foot {
    display: flex; justify-content: flex-end; gap: 12px;
    padding: 12px 20px;
    border-top: 1px solid var(--border);
}

/* ── Phase 21 — Shared Position accordion (rotation + flip + alignment) ── */
.me-position-row {
    display: flex; align-items: flex-end; gap: 16px;
    margin-bottom: 12px;
}
.me-position-row > label {
    font-size: 12px;
    color: var(--muted);
    flex-shrink: 0;
    margin: 0;
}
/* Row variant: label stacks above its content (Flip / H Align / V Align rows). */
.me-position-row-stack {
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}
.me-position-rotation-row {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 12px;
    align-items: end;
    margin-bottom: 8px;
}
/* Number input + Rotate-90° quick button sit side-by-side under the
 * "Rotation (deg)" label. The input takes the remaining width; the
 * button sizes to its text (white-space:nowrap on the button itself). */
.me-position-rotation-input-group {
    display: flex;
    align-items: stretch;
    gap: 6px;
}
.me-position-rotation-input-group input[type="number"] {
    flex: 1 1 auto;
    min-width: 0;
}
/* Rotate 90° button sits next to the CW/CCW icons in the Direction
 * column. Match .me-btn-icon's intrinsic height (18px SVG + 6px×2
 * padding = 32px box including the 1px border on each side) so the
 * three controls line up perfectly on the same horizontal row. */
.me-position-button-inline {
    background: var(--paper);
    border: 1px solid var(--border);
    border-radius: 4px;
    cursor: pointer;
    font-size: 12px;
    font-family: 'Inter', sans-serif;
    color: var(--ink);
    padding: 0 10px;
    height: 32px;
    line-height: 1;
    white-space: nowrap;
    transition: border-color 0.12s, color 0.12s;
}
.me-position-button-inline:hover {
    border-color: var(--accent);
    color: var(--accent);
}
.me-position-icon-group {
    display: inline-flex; gap: 6px;
}
.me-position-button-fullwidth {
    width: 100%; height: 32px;
    background: #f5f1ea;
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    font-size: 13px;
    font-family: 'Inter', sans-serif;
    color: var(--ink);
    margin-bottom: 12px;
    transition: border-color 0.12s, color 0.12s;
}
.me-position-button-fullwidth:hover {
    border-color: var(--accent);
    color: var(--accent);
}
/* Alignment buttons inherit .me-btn-icon styling (outline accent on hover /
 * active) from earlier phases — see the Flip-icon rules. */
.me-position-align-btn { /* nothing extra — pure marker class for the wireup selector */ }

/* Ingestion Runs — super-admin upload toolbar (sits below the hero) */
.runs-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin: 0.75rem 0 1rem;
}
.runs-toolbar-left,
.runs-toolbar-right {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.runs-covers-status {
    font-size: 0.85rem;
    color: var(--muted);
    margin-right: 0.25rem;
}
.runs-toolbar-hint {
    font-size: 0.8rem;
    color: var(--muted);
}

/* Ingestion Runs — Pending CSV conversions card (super-admin) */
.csv-conversions-card { margin-bottom: 1.5rem; }
.csv-conversions-title {
    font-family: 'DM Serif Display', serif;
    margin: 0 0 0.75rem;
    font-size: 1.1rem;
}
.csv-conversion-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--border);
}
.csv-conversion-row:last-child { border-bottom: none; }
.csv-conversion-meta {
    font-size: 0.85rem;
    color: var(--muted);
    margin-top: 0.15rem;
}
.csv-conversion-actions {
    display: flex;
    gap: 0.5rem;
    flex-shrink: 0;
}
.csv-conversion-status-pending  { color: #c98a06; font-weight: 600; margin-left: 0.5rem; }
.csv-conversion-status-failed   { color: #c0392b; font-weight: 600; margin-left: 0.5rem; }
.csv-conversion-status-rejected { color: var(--muted); font-weight: 600; margin-left: 0.5rem; }
.csv-conversion-status-approved { color: #2e7d32; font-weight: 600; margin-left: 0.5rem; }

/* Skipped-pages details toggle on conversion rows */
.conv-skip-details { margin-top: 0.35rem; font-size: 0.82rem; }
.conv-skip-details summary { cursor: pointer; color: #c98a06; }
.conv-skip-details ul { margin: 0.35rem 0 0; padding-left: 1.25rem; color: var(--muted); }
.conv-skip-details li { margin-bottom: 0.2rem; }

/* Conversion Review modal (readable pre-approve summary) */
.conv-review-modal {
    max-width: 760px;
    width: min(760px, 94vw);
    display: flex;
    flex-direction: column;
    max-height: 86vh;
}
.conv-review-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}
.conv-review-head h3 { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conv-review-body { overflow-y: auto; flex: 1; min-height: 0; padding-right: 0.25rem; }
.conv-review-note { font-size: 0.85rem; color: var(--muted); margin-bottom: 0.75rem; }
.conv-review-card {
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.9rem 1rem;
    margin-bottom: 0.9rem;
}
.conv-review-title { font-weight: 600; font-size: 1.02rem; margin-bottom: 0.5rem; }
.conv-review-subtitle { font-weight: 400; color: var(--muted); }
.conv-review-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: 0.25rem 1rem;
    margin-bottom: 0.5rem;
}
.conv-review-kv { font-size: 0.85rem; }
.conv-review-k {
    text-transform: uppercase;
    font-size: 0.7rem;
    letter-spacing: 0.5px;
    color: var(--muted);
    margin-right: 0.4rem;
}
.conv-review-contrib { font-size: 0.88rem; margin-bottom: 0.25rem; }
.conv-review-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.4rem 0; }
.conv-review-chip {
    font-size: 0.75rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.1rem 0.6rem;
}
.conv-review-desc, .conv-review-expand p {
    font-size: 0.88rem;
    line-height: 1.45;
    margin: 0.4rem 0;
    white-space: pre-wrap;
}
.conv-review-expand { margin: 0.35rem 0; font-size: 0.85rem; }
.conv-review-expand summary { cursor: pointer; color: var(--accent); }
.conv-review-quote {
    margin: 0.5rem 0;
    padding: 0.5rem 0.85rem;
    border-left: 3px solid var(--border);
    font-size: 0.88rem;
    font-style: italic;
    color: var(--text);
}
.conv-review-quote footer { font-style: normal; color: var(--muted); margin-top: 0.3rem; font-size: 0.82rem; }
.conv-review-points { margin: 0.25rem 0 0.4rem; padding-left: 1.25rem; font-size: 0.88rem; }
.conv-review-actions { margin-top: 1rem; flex-shrink: 0; }

/* ── Rights Catalogs ─────────────────────────────────────────── */
.rc-list-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1rem;
}
.rc-list-head h2 { margin: 0; font-family: 'DM Serif Display', serif; }
.rc-list-head h2 a { color: inherit; text-decoration: none; }
.rc-list-head h2 a:hover { text-decoration: underline; }
.rc-status { font-weight: 600; font-size: 0.85rem; text-transform: capitalize; }
.rc-status-ready { color: #2e7d32; }
.rc-status-extracting { color: #c98a06; }
.rc-status-failed { color: #c0392b; }
.rc-status-draft { color: var(--muted); }
.rc-progress {
    height: 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 999px;
    overflow: hidden;
}
.rc-progress-bar { height: 100%; background: var(--accent); transition: width 0.4s; }

/* Editor workspace */
.rc-editor { display: flex; flex-direction: column; height: calc(100vh - 90px); }
.rc-editor-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding-bottom: 0.75rem;
    flex-wrap: wrap;
}
.rc-back { color: var(--muted); text-decoration: none; white-space: nowrap; }
.rc-back:hover { color: var(--text); }
.rc-name-input {
    font-size: 1.05rem;
    font-weight: 600;
    border: 1px solid transparent;
    background: transparent;
    padding: 0.25rem 0.4rem;
    min-width: 220px;
}
.rc-name-input:hover, .rc-name-input:focus { border-color: var(--border); background: #fff; }
.rc-save-status { font-size: 0.8rem; color: var(--muted); white-space: nowrap; }
.rc-pagecount { font-size: 0.85rem; color: var(--muted); white-space: nowrap; }
.rc-editor-main { flex: 1; min-height: 0; display: flex; gap: 0.75rem; }
.rc-canvas-outer {
    flex: 1;
    min-width: 0;
    overflow-y: auto;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    gap: 0.4rem;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 1rem 0.25rem;
}
.rc-canvas-scale {
    transform-origin: top left;
    flex: 0 0 auto;
    box-shadow: 0 2px 14px rgba(0,0,0,0.18);
}
.rc-nav-arrow {
    position: sticky;
    top: 45%;
    border: 1px solid var(--border);
    background: #fff;
    border-radius: 50%;
    width: 38px;
    height: 38px;
    font-size: 1.3rem;
    cursor: pointer;
    flex: 0 0 auto;
}
.rc-nav-arrow:hover { background: var(--bg); }
.rc-field:focus {
    outline: 2px solid rgba(74,144,226,0.55);
    outline-offset: 1px;
    border-radius: 2px;
}
.rc-slot.rc-overflow { outline: 2px solid rgba(192,57,43,0.65); outline-offset: -2px; }
.rc-overflow-badge {
    position: absolute;
    right: 4px;
    bottom: 4px;
    background: #c0392b;
    color: #fff;
    font-size: 8pt;
    font-family: 'Inter', sans-serif;
    padding: 1px 7px;
    border-radius: 3px;
    pointer-events: none;
}
.rc-chrome {
    position: absolute;
    top: 2px;
    right: 2px;
    display: none;
    gap: 4px;
    font-family: 'Inter', sans-serif;
}
.rc-slot:hover .rc-chrome { display: flex; }
.rc-chrome-btn, .rc-chrome-sel {
    font-size: 8.5pt;
    border: 1px solid var(--border);
    background: rgba(255,255,255,0.95);
    border-radius: 4px;
    padding: 2px 7px;
    cursor: pointer;
}
.rc-chrome-btn:hover { background: var(--bg); }
.rc-chrome-sel { max-width: 130px; }
/* Sourceless review attribution — invisible in the editor until the slot is
   hovered (and fully visible while typing); never rendered in print. */
.rc-review-src-empty { opacity: 0; }
.rc-slot:hover .rc-review-src-empty { opacity: 0.35; }
.rc-review-src-empty:focus-within { opacity: 1; }

/* Editor-only marker for coverless books — zero layout impact (the layout
   itself reserves no cover space; print shows nothing). */
.rc-cover-missing {
    position: absolute;
    top: 32px;
    right: 4px;
    border: 1px dashed #b9b9b9;
    background: rgba(255,255,255,0.92);
    color: #9a9a9a;
    font-family: 'Inter', sans-serif;
    font-size: 8.5pt;
    padding: 2px 8px;
    border-radius: 4px;
    pointer-events: none;
}

/* Floating text toolbar */
.rc-toolbar {
    position: fixed;
    z-index: 3000;
    display: flex;
    align-items: center;
    gap: 3px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 6px;
    box-shadow: 0 3px 12px rgba(0,0,0,0.18);
    padding: 3px;
}
.rc-toolbar button {
    width: 26px;
    height: 24px;
    border: 1px solid transparent;
    background: transparent;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.85rem;
    line-height: 1;
    padding: 0;
}
.rc-toolbar button:hover { background: var(--bg); border-color: var(--border); }
/* Compact size select — the global select styling is far too tall here and
   the toolbar ends up covering the very line being edited. */
.rc-toolbar select {
    height: 24px;
    padding: 0 2px 0 6px;
    font-size: 0.75rem;
    line-height: 1;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: #fff;
    width: auto;
}

/* Thumbs strip */
.rc-thumbs {
    flex: 0 0 auto;
    display: flex;
    gap: 0.5rem;
    overflow-x: auto;
    padding: 0.6rem 0.2rem 0.3rem;
}
.rc-thumb {
    flex: 0 0 auto;
    width: 74px;
    height: 96px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 4px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    padding: 4px;
    font-size: 7px;
    line-height: 1.3;
}
.rc-thumb.active { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.rc-thumb { cursor: grab; }
.rc-thumb.dragging { opacity: 0.4; cursor: grabbing; }
.rc-thumb.rc-sec-drag { outline: 2px dashed var(--accent); outline-offset: 1px; }
.rc-thumb.drop-before { box-shadow: -3px 0 0 0 var(--accent); }
.rc-thumb.drop-after { box-shadow: 3px 0 0 0 var(--accent); }
.rc-thumb-banner {
    color: #fff;
    text-align: center;
    font-size: 6.5px;
    padding: 2px 1px;
    margin: -4px -4px 3px;
    white-space: nowrap;
    overflow: hidden;
}
.rc-thumb-line {
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    margin-bottom: 2px;
}
.rc-thumb-rule { border-top: 1px solid #999; margin: 3px 6px; }
.rc-thumb-num {
    position: absolute;
    bottom: 2px;
    right: 4px;
    color: var(--muted);
    font-size: 7px;
}

/* Settings drawer */
.rc-settings-drawer {
    flex: 0 0 300px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 1rem;
    background: #fff;
}
.rc-settings-drawer h3 { margin: 0 0 0.6rem; font-size: 1rem; }
.rc-set-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.55rem;
    font-size: 0.85rem;
}
.rc-set-row label { flex: 1; }
.rc-color-hex { font-size: 0.75rem; color: var(--muted); }

/* Cover picker modal */
.rc-cover-result {
    display: flex;
    gap: 0.6rem;
    align-items: center;
    padding: 0.4rem;
    border-radius: 6px;
    cursor: pointer;
}
.rc-cover-result:hover { background: var(--bg); }
.rc-cover-result img { width: 40px; display: block; }
.rc-cover-result-none {
    width: 40px;
    height: 56px;
    background: var(--bg);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--muted);
}

/* Ingestion Runs — Pending Covers card (super-admin) */
.pending-covers-card { margin-bottom: 1.5rem; }
.pending-covers-card .stats-row { margin-bottom: 0; }
.pc-action { margin-top: 0.6rem; min-height: 32px; font-size: 0.85rem; color: var(--muted); }
.pc-action .btn { font-size: 0.85rem; }
/* Base .btn-spinner is white-on-white outside a filled button — recolor. */
.pc-action .pc-spinner { border-color: rgba(0,0,0,0.15); border-top-color: var(--accent); }
.pc-note { font-size: 0.8rem; margin-bottom: 0.35rem; }
.pc-note-ok  { color: #2e7d32; }
.pc-note-err { color: #c0392b; }

/* ── Books modal → Assets tab → sub-panels ───────────────────── */
.book-asset-panel { margin-top: 1rem; }

.book-asset-grid {
    display: grid;
    grid-template-columns: 35% 65%;
    gap: 1rem;
    align-items: start;
}
.book-asset-col-desc {
    font-size: 0.9rem;
    color: var(--muted);
    line-height: 1.45;
    text-align: left;
}
.book-asset-col-cover { text-align: left; }

/* Empty-state cover renders via the same .book-asset-cover-img as the
   populated state, so the picture is the same size + same place before
   and after the no-text variant is generated. */

/* Empty-state actions live under the description in column 1, left-aligned. */
.book-asset-desc-actions {
    margin-top: 12px;
    display: flex;
    align-items: center;
    gap: 8px;
    justify-content: flex-start;
}
.book-asset-generate-btn {
    font-size: 12px;
    padding: 4px 10px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--paper);
    color: var(--ink);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.book-asset-generate-btn:hover { border-color: var(--accent); color: var(--accent); }
/* Usage Tips (migration 165) — same shape as Generate, outline-accent CTA. */
.book-asset-usage-tips-btn {
    background: transparent;
    border-color: var(--accent);
    color: var(--accent);
}
.book-asset-usage-tips-btn:hover { background: var(--accent-light); }
.book-asset-ai-glyph { font-size: 13px; }
/* Usage Tips info icon on generated-asset cards (migration 165) — accent
   colour so it reads as the help affordance, first in the action row. */
.aic-icon-btn.usage-tips-icon { color: var(--accent); }
.aic-icon-btn.usage-tips-icon:hover { color: var(--accent-dark); }
.book-asset-info-btn {
    border: none;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    padding: 0;
    display: inline-flex;
    align-items: center;
}
.book-asset-info-btn:hover { color: var(--accent); }

/* Populated cover: image at 150px tall with corner trash button */
.book-asset-cover-wrap {
    position: relative;
    display: inline-block;
}
.book-asset-cover-img {
    display: block;
    height: 150px;
    width: auto;
    border-radius: 4px;
    border: 1px solid var(--border);
}
/* Placeholder treatment for the Background tab's empty state — shows
   the original cover greyed out so it's visibly NOT the final asset
   yet. Same filter as .marketing-prompt-card-thumb.placeholder. */
.book-asset-cover-img-placeholder {
    filter: grayscale(100%);
    opacity: 0.5;
}
.book-asset-cover-wrap a { cursor: zoom-in; }

/* Centred eye overlay — appears on hover, signals "click to open". */
.book-asset-cover-eye {
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 48px; height: 48px;
    border-radius: 50%;
    background: rgba(0,0,0,0.55);
    color: #fff;
    display: flex; align-items: center; justify-content: center;
    opacity: 0;
    transition: opacity 0.15s;
    pointer-events: none;   /* the <a> ancestor handles the click */
    z-index: 3;
}
.book-asset-cover-wrap:hover .book-asset-cover-eye { opacity: 1; }

/* Bigger trash — lives in column 1 under the description (~2× the
   old on-image variant). Outline-style so it sits comfortably in
   a text context; danger-red on hover. */
.book-asset-delete-big {
    width: 33px; height: 33px;
    border-radius: 50%;
    border: 1.5px solid var(--border);
    background: var(--paper);
    color: var(--muted);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.book-asset-delete-big:hover {
    border-color: var(--danger);
    color: var(--danger);
}

.book-asset-coming-soon {
    padding: 2rem;
    color: var(--muted);
    font-style: italic;
    text-align: center;
}

/* ── Marketing sub-tab (Books modal → Assets (AI) → Marketing) ──
   Horizontal lozenge row of channel groups (one per inklet_channel_groups
   row) above a grid of prompt cards.
   Visual mirrors the existing .badge palette so these chips line up
   stylistically with the Active / Primary status badges sitting on the
   same modal — same sizing, same soft-pastel + dark-text scheme. Each
   lozenge gets its colours via two inline CSS custom properties:
     --lz-bg  soft pastel fill (used when selected, plus on hover)
     --lz-fg  dark text + outline colour (used always when not greyed) */
.marketing-lozenge-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 4px 0 16px;
}
.marketing-lozenge {
    display: inline-block;
    padding: 0.2rem 0.5rem;
    border-radius: 4px;
    font-size: 0.75rem;
    font-weight: 600;
    background: transparent;
    color: var(--lz-fg);
    border: 1px solid var(--lz-fg);
    cursor: pointer;
    transition: background 0.12s;
    font-family: inherit;
    line-height: 1.3;
}
.marketing-lozenge:not(.inactive):hover {
    background: var(--lz-bg);
}
.marketing-lozenge.active {
    background: var(--lz-bg);
    color: var(--lz-fg);
    border-color: transparent;
}
.marketing-lozenge.inactive {
    color: var(--muted);
    border-color: var(--border);
    background: transparent;
    cursor: not-allowed;
    opacity: 0.6;
}
/* Card grid — three equal columns at every row. (The channel-group's
   ai_summary text is no longer surfaced on the Marketing tab; it
   remains stored on inklet_channel_groups + editable via the Edit
   Group modal, but the Marketing surface is now purely the asset
   launchpad. Removed Phase 32d.) */
.marketing-prompt-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}
.marketing-prompt-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 18px 16px 16px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--paper);
    text-align: center;
    box-sizing: border-box;
    min-width: 0;
}
.marketing-prompt-card-name {
    font-weight: 600;
    font-size: 0.95rem;
    line-height: 1.25;
}
/* Marketing-asset card type badge: one-off Inklet / (stream) Template /
   System Template — each in a distinct Inkara-palette colour. */
.mkt-type-badge {
    display: inline-block;
    margin: 3px 0 2px;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    line-height: 1.5;
}
/* All badge types share the neutral grey treatment. */
.mkt-type-badge--inklet,
.mkt-type-badge--template,
.mkt-type-badge--system,
.mkt-type-badge--generated { background: #ece9e3; color: #6b6457; }
.marketing-prompt-card-channel {
    font-size: 0.8rem;
    color: var(--muted);
}
.marketing-prompt-card .book-asset-generate-btn {
    margin-top: auto;
}

/* Cached-asset thumbnail (rendered when book_ai_assets has a row for
   this (book, prompt) pair). Click opens the full-resolution PNG in a
   new tab. Letterboxed at 192×96 inside the card; img is object-fit
   contain so non-16:9 outputs aren't cropped. */
.marketing-prompt-card-thumb {
    display: block;
    width: 100%;
    max-width: 200px;
    aspect-ratio: 16 / 9;
    border-radius: 6px;
    overflow: hidden;
    background: #f1f5f9;
    border: 1px solid var(--border);
    cursor: pointer;
    transition: transform 0.15s, box-shadow 0.15s;
}
.marketing-prompt-card-thumb:hover {
    transform: scale(1.03);
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
}
.marketing-prompt-card-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #f8fafc;
}

/* Multi-panel composite (Amazon A+ 3-panel, tagline carousel) — a wide
   horizontal strip OR a tall vertical stack. Drop the fixed 16:9 box + 200px
   cap and centre the image at its natural aspect (no letterbox bars / squash),
   capping height so a tall vertical composite stays a reasonable card size. */
.marketing-prompt-card-thumb.mkt-thumb-auto {
    aspect-ratio: auto;
    max-width: 100%;
    max-height: 360px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.marketing-prompt-card-thumb.mkt-thumb-auto img {
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 360px;
    object-fit: contain;
}

/* Placeholder thumbnail — when there's no asset for THIS (book, prompt)
   yet, we surface the most recent asset from any OTHER book in muted
   monochrome so the user gets a feel for what the prompt produces.
   Non-interactive (no anchor wrapper) to keep it visually distinct
   from a real cached asset. */
.marketing-prompt-card-thumb.placeholder {
    cursor: pointer;
}
/* Hover lift + shadow already declared on the base
   .marketing-prompt-card-thumb:hover rule above; placeholder inherits
   them now that it's clickable. */
.marketing-prompt-card-thumb.placeholder img {
    filter: grayscale(100%);
    opacity: 0.5;
}
/* "+ New" campaign-asset wizard: show the sample preview in FULL COLOUR (not
   greyscaled) with a centred "SAMPLE" watermark — mirrors the Step-3
   .bm-step3-placeholder so a curator sees the true colours while still
   reading it as an example, not their own asset. Higher specificity
   (.placeholder.campaign-asset-sample) overrides the greyscale rule above. */
.marketing-prompt-card-thumb.campaign-asset-sample {
    position: relative;
}
.marketing-prompt-card-thumb.placeholder.campaign-asset-sample img {
    filter: none;
    opacity: 1;
}
.campaign-asset-sample-label {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: rgba(255, 255, 255, 0.85);
    font-size: 1.1rem;
    font-weight: 600;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
    pointer-events: none;
    white-space: nowrap;
}

/* Shimmer loader — while the resized thumbnail downloads from the assets
   CDN (assetThumb → assets.inkara.io), the .shimmer anchor shows the shared
   img-skeleton gradient and the <img> fades in once it fires onload. Reuses
   the exact idiom from .inklet-gallery-thumb / .campaign-book-cover. Placed
   AFTER the .placeholder img rule so .shimmer img{opacity:0} wins during load
   (equal specificity, later in source) and the .loaded rules restore each
   state. Pending/cover-pending variants opt out (no .shimmer class) so their
   campaign-spinner overlay is untouched. */
.marketing-prompt-card-thumb.shimmer {
    background: linear-gradient(110deg, #f0ede8 30%, #e8e4de 50%, #f0ede8 70%);
    background-size: 200% 100%;
    animation: img-skeleton 1.5s ease-in-out infinite;
}
.marketing-prompt-card-thumb.shimmer img {
    opacity: 0;
    transition: opacity 0.3s ease;
}
.marketing-prompt-card-thumb.shimmer img.loaded {
    opacity: 1;
}
/* keep the grayscale sample muted after it loads */
.marketing-prompt-card-thumb.shimmer.placeholder img.loaded {
    opacity: 0.5;
}

/* Stale flag — shown when the prompt has been edited since this asset
   was generated. Muted amber to match the existing .badge-pending /
   .badge-hidden palette. */
.marketing-prompt-card-stale {
    font-size: 0.72rem;
    font-style: italic;
    color: #856404;
}

/* ── Campaigns page — generated-asset cards ──
 * The Campaigns page (views-campaigns.js) reuses the Assets (AI) card classes
 * (.marketing-prompt-card / -thumb / -name / -channel) and adds an ISBN•Format
 * sub-line plus a 4-icon action bar (Download / Bookmark / Refresh / Trash).
 * Full-width responsive grid (the page isn't a modal, so fit more per row). */
.campaign-asset-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 1rem;
}
.campaign-asset-sub {
    font-size: 0.78rem;
    color: var(--muted);
    word-break: break-word;
}
/* Custom-text echo (book_multi cards) — the joined chip / freeform strings
   the curator chose. Sits between the prompt name and the ISBN • format
   line on both pending and finished cards. Same muted size as .sub but
   uppercase-friendly weight so multi-line headlines read cleanly. */
.campaign-asset-custom-text {
    font-size: 0.78rem;
    color: var(--ink);
    font-weight: 500;
    word-break: break-word;
    line-height: 1.35;
}
/* Italic date line — publication date (book assets) or event date-window
   (event assets). Sits just under the channel. */
.campaign-asset-when {
    font-size: 0.78rem;
    font-style: italic;
    color: var(--muted);
}
/* Push the action bar to the card's bottom edge regardless of title length. */
.campaign-asset-actions {
    margin-top: auto;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding-top: 6px;
}
/* Bookmark toggle — styled like the select buttons (outline pill): neutral by
   default, accent (icon + outline) on hover. Equal padding makes it square so
   the outline matches the select-button height; align-items:center on the row
   keeps it vertically aligned, and it stays pushed to the right. */
.campaigns-bookmark-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    padding: 0.45rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--ink);
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, box-shadow 0.15s;
}
.campaigns-bookmark-btn svg {
    width: 16px;
    height: 16px;
    display: block;
}
.campaigns-bookmark-btn:hover,
.campaigns-bookmark-btn.active {
    border-color: var(--accent);
    color: var(--accent);
}
/* Active (bookmarks view on) also fills the glyph so the mode reads at a glance. */
.campaigns-bookmark-btn.active svg { fill: var(--accent); }
/* Filter row — channel + date-range buttons and their chips on the left (all
   on one line), bookmark toggle pushed to the right. */
.campaigns-filter-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}
/* Spacing lives on the (only-in-bookmarks) heading so the default view, where
   the heading is hidden, sits flush at the card padding. */
#campaigns-title-pair { margin-bottom: 1rem; }
.campaigns-filter-left {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}
/* "+ New" create-asset modal — step-1 book picker: the same .bm-book-lozenge
   cards as the ISBN-Emporium picker, 3 per row, but COMPACT (small cover +
   tight text) to match the ISBN-Emporium search-result lozenge size. */
.camp-new-books-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    align-items: start;
}
.camp-new-books-grid .bm-book-lozenge { padding: 8px; gap: 10px; }
.camp-new-books-grid .bm-book-lozenge img { width: 44px; height: 62px; }
.camp-new-books-grid .bm-book-lozenge .bm-meta { font-size: 0.78rem; padding-right: 0; gap: 1px; }
.camp-new-books-grid .bm-book-lozenge .bm-title { font-size: 0.8rem; line-height: 1.2; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.camp-new-books-grid .bm-book-lozenge .bm-isbn-format { font-size: 0.72rem; }

/* Multi-Book Asset modal step-1 picker — mirrors the Campaigns "+ New"
   picker (search box + Coming / Recently Released / Backlist tabs +
   3-col card grid), but with multi-select instead of single-select.
   Reuses the same .bm-book-lozenge cards in the same 3-col layout for
   visual consistency. */
.mba-picker-host { display: block; }
.mba-picker-subhead {
    margin: 0 0 10px;
    padding: 8px 12px;
    background: #f7f5fa;
    border-left: 3px solid var(--accent);
    border-radius: 4px;
    font-size: 0.82rem;
    color: var(--ink);
}
.mba-picker-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
    align-items: start;
}
.mba-picker-grid .bm-book-lozenge { padding: 8px; gap: 10px; cursor: pointer; }
.mba-picker-grid .bm-book-lozenge img { width: 44px; height: 62px; }
.mba-picker-grid .bm-book-lozenge .bm-meta { font-size: 0.78rem; padding-right: 0; gap: 1px; }
.mba-picker-grid .bm-book-lozenge .bm-title { font-size: 0.8rem; line-height: 1.2; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.mba-picker-grid .bm-book-lozenge .bm-isbn-format { font-size: 0.72rem; }

/* Book modal Author tab — "Other titles by this author" section
   heading. Serif + black to read as a content header above the table
   below, distinguishing it from the author-info card above. The table
   itself reuses the standard .card / .table-wrap chrome so it matches
   the Formats / Series tabs visually. */
.author-other-titles-heading {
    font-family: Georgia, 'Times New Roman', 'Cormorant Garamond', serif;
    color: #000;
    font-weight: 700;
    font-size: 1.15rem;
    margin: 1.5rem 0 0.75rem;
}

/* Series Info save-confirm modal (migration 100 + dashboard wiring).
   Asks the curator whether to persist the series info for THIS book
   only or for every book in the series. Reuses the existing
   .action-modal-quill PNG + meQuillMove animation defined in
   widget-shared.css so the modal feels visually consistent with the
   other long-running-write modals already in the dashboard. */
.series-info-save-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    /* Must sit above the Book modal (99999) and lightboxes (100000)
       since this confirm modal is opened FROM inside the Book modal.
       Toast (2147483647) still wins so error/success feedback shows
       on top of this dialog. */
    z-index: 100001;
}
.series-info-save-modal {
    position: relative;
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.22);
    padding: 28px 32px 24px;
    width: 90%;
    max-width: 420px;
    text-align: center;
    animation: sisave-pop-in 200ms ease-out;
}
@keyframes sisave-pop-in {
    from { opacity: 0; transform: translateY(8px) scale(0.97); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
.series-info-save-close {
    position: absolute;
    top: 8px;
    right: 12px;
    background: none;
    border: none;
    font-size: 1.5rem;
    line-height: 1;
    color: var(--muted);
    cursor: pointer;
    padding: 4px 8px;
}
.series-info-save-close:hover { color: var(--ink); }
.series-info-save-quill-wrap {
    display: flex;
    justify-content: center;
    margin-bottom: 8px;
    overflow: visible;
}
.series-info-save-title {
    font-family: Georgia, 'Times New Roman', 'Cormorant Garamond', serif;
    font-weight: 700;
    font-size: 1.35rem;
    color: var(--ink);
    margin: 4px 0 8px;
    text-align: center;
}
.series-info-save-sub {
    color: var(--muted);
    font-size: 0.92rem;
    line-height: 1.4;
    margin: 0 0 22px;
    text-align: center;
}
.series-info-save-actions {
    display: flex;
    justify-content: center;
    gap: 12px;
    flex-wrap: wrap;
}
.series-info-save-actions .btn { min-width: 150px; }

/* Export Options modal — clones the .series-info-save-* chrome but widens it to
   fit the 3-card chooser (reuses the .me-bg-picker-* cards from widget-shared.css).
   Compound selectors keep these overrides specificity-safe regardless of which
   stylesheet loads last. */
.export-options-modal { max-width: 600px; }
.me-bg-picker-grid.eo-grid-3 { grid-template-columns: repeat(3, 1fr); }
.me-bg-picker-grid.eo-grid-2 { grid-template-columns: repeat(2, 1fr); }
.me-bg-picker-item.selected {
    border-color: var(--accent);
    background: var(--accent-light);
}
.me-bg-picker-item.selected svg { color: var(--accent); }

.channel-filter-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.9rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--ink);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, box-shadow 0.15s;
    white-space: nowrap;
}
.channel-filter-btn:hover {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
}
.channel-filter-btn svg { width: 16px; height: 16px; }
/* Category button — a category is always selected, so it reads as active
   (accent outline + text); hover drops the outline to neutral to signal you're
   just switching the current selection. */
.group-filter-btn {
    border-color: var(--accent);
    color: var(--accent);
}
.group-filter-btn:hover {
    border-color: var(--border);
    box-shadow: none;
}
.channel-chip-row {
    display: flex;
    /* Centre the chips horizontally — applies to both the Campaigns page and
       the Book modal > Marketing Assets tab. */
    justify-content: center;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}
/* Chips sit on their own line below the button row, so wrapping never pushes
   the right-aligned "+ New" button. A solid top gap so chips don't collide
   with the filter buttons. No top gap when there are no chips.
   Selector matches both the legacy #campaigns-chips id (now retired in the
   mountAssetSurface refactor) and the new shared [data-role="chips"] host
   that the factory paints — works in Campaigns AND the Book modal. */
[data-role="chips"].channel-chip-row { margin-top: 1rem; }
[data-role="chips"].channel-chip-row:empty { margin-top: 0; }
/* Inline variant — chips sit on the SAME row as the filter buttons (left-
   aligned, just after the date-range button). Override the centred /
   wrapping defaults so the row reads "Social Content, Filter channels,
   Square (1:1)" left-to-right. The wrapping parent (.campaigns-filter-left)
   handles overflow via its own flex-wrap. */
.channel-chip-row.channel-chip-row-inline {
    justify-content: flex-start;
    margin-top: 0;
    margin-left: 0.4rem;
}
[data-role="chips"].channel-chip-row.channel-chip-row-inline { margin-top: 0; }
.channel-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.35rem 0.3rem 0.7rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--ink);
    white-space: nowrap;
}
.channel-chip-x, .date-chip-x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.12);
    color: var(--ink);
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
    padding: 0;
}
.channel-chip-x:hover, .date-chip-x:hover { background: rgba(0, 0, 0, 0.25); }
/* Date-range option cards carry only a label — dead-center it and keep them
   compact. line-height:1 trims the empty descender space so the descenderless
   labels read as vertically centred rather than sitting high. */
.date-option-card { justify-content: center; align-items: center; min-height: 64px; }
.date-option-card .ie-channel-card-name { margin: 0; line-height: 1; }

/* Channel-filter modal — the card grid scrolls inside a flex column so the
   "Add (n)" footer stays pinned and visible regardless of channel count. */
.channel-filter-modal { display: flex; flex-direction: column; overflow: hidden; }
.channel-filter-modal .ie-channel-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    margin-top: 1rem;
    /* Inner padding so the hover lift (translateY) + box-shadow + the selected
       2px border aren't clipped by the scroll container's overflow. */
    padding: 6px;
}
.channel-filter-modal .ie-channel-scroll .ie-channel-grid { margin-top: 0; }
.channel-filter-footer {
    flex: 0 0 auto;
    display: flex;
    justify-content: flex-end;
    margin-top: 1.5rem;
}
/* Bookmarked state — fill the Feather outline so saved assets read at a glance. */
.campaign-bookmark-btn.active svg {
    fill: var(--accent);
    color: var(--accent);
}
/* Trash goes red on hover (no destructive chrome until intent is shown). */
.campaign-trash-btn:hover svg {
    color: #c0392b;
}
/* Spin the refresh glyph while a regeneration is in flight. */
.campaign-refreshing svg {
    animation: campaign-spin 0.8s linear infinite;
}
@keyframes campaign-spin {
    to { transform: rotate(360deg); }
}

/* ── "Generating…" pending placeholder card ──
 * Background-generation jobs (asset-jobs.js, migration 076) surface a
 * placeholder tile while the renderer works, so navigating away and coming
 * back still shows progress that resolves into the finished asset. Reuses the
 * .marketing-prompt-card chrome; adds a spinner overlay on the thumb + a
 * dimmed "Generating…" label in place of the action bar. */
.campaign-asset-pending {
    opacity: 0.95;
}
.campaign-asset-pending-thumb {
    position: relative;
    cursor: default;
}
/* Suppress the lift/zoom hover (inherited from .marketing-prompt-card-thumb)
   — a placeholder isn't interactive. */
.campaign-asset-pending-thumb:hover {
    transform: none;
    box-shadow: none;
}
/* Cover-shaped in-flight placeholder — shows the BOUND book's cover
   (or first picked book's for multi-book) greyscaled + centre-cropped
   to the channel's aspect ratio. The OUTER container inherits the
   standard `.marketing-prompt-card-thumb` 16:9 ≈ 200×110 rectangle so
   the chrome / outline / size match a finished asset exactly. The
   INNER frame is sized to the channel's aspect ratio (inline) and
   centred inside the rectangle — a Square (1:1) cover shows as a
   ~110×110 centred square inside the 200×110 rectangle with light
   letterbox strips, a Story (9:16) cover as a tall narrow strip, etc.
   Used on the Campaigns "Generating…" tile + the Book modal Marketing
   tab pending state. */
.campaign-asset-pending-cover,
.marketing-prompt-card-cover-pending {
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: default;
}
.marketing-prompt-card-cover-pending:hover {
    transform: none;
    box-shadow: none;
}
/* Inner frame — height fills the outer rectangle; width derives from
   the inline `aspect-ratio`. `max-width: 100%` caps super-wide channels
   (e.g. 3:1 banners) so they stay within the rectangle. */
.campaign-asset-pending-cover-frame,
.marketing-prompt-card-cover-pending-frame {
    height: 100%;
    max-width: 100%;
    overflow: hidden;
    /* aspect-ratio set inline (per channel). */
}
.campaign-asset-pending-cover-frame img,
.marketing-prompt-card-cover-pending-frame img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
    filter: grayscale(100%);
    opacity: 0.6;
}
.campaign-asset-pending-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(241, 245, 249, 0.55);
}
.campaign-spinner {
    display: inline-block;
    width: 26px;
    height: 26px;
    border: 3px solid rgba(99, 102, 241, 0.25);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: campaign-spin 0.8s linear infinite;
}
.campaign-asset-pending-label {
    margin-top: auto;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--accent);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding-top: 6px;
}

/* ── Marketing modal (channel previews as cards) ──
 * Replaces the older per-channel sub-tab pattern: one card per
 * (template × channel) pair, three-column grid, each card lazy-
 * loads its widget-renderer preview into a fixed-size thumbnail
 * via the transform:scale pattern. Click a card to open the full-
 * size preview through the shared inklet-preview-shell. The grid
 * lives inside .tbm-modal-inner alongside .tbm-top-bar — flex:1 so
 * it fills the remaining vertical space, overflow:auto so long
 * lists scroll within the modal rather than the page. */
.mkt-cards-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
    padding: 20px;
    flex: 1;
    overflow: auto;
    align-content: start;
}
/* Inherits .marketing-prompt-card's column flex + padding + border;
 * .mkt-card just adds the click affordance + hover lift. */
.mkt-cards-grid .mkt-card {
    cursor: pointer;
    transition: box-shadow 0.15s, transform 0.15s;
}
.mkt-cards-grid .mkt-card:hover {
    box-shadow: 0 4px 12px rgba(0,0,0,0.10);
    transform: translateY(-2px);
}
/* Thumbnail box — uses the same 16:9 / max-200px sizing as the
 * Assets (AI) card thumbnails (inherited from .marketing-prompt-card-
 * thumb on the same element). We only override display/centring so
 * the scaled-down preview wrap can flex-centre inside the box.
 * Applies in the dedicated `.mkt-cards-grid` AND in the campaign-asset
 * grid (Book modal Marketing Assets tab, where inklet cards are
 * interleaved with AI prompt cards). */
.mkt-cards-grid .mkt-card-thumb,
.campaign-asset-card .mkt-card-thumb {
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    /* width / max-width / aspect-ratio / border / background all
       come from .marketing-prompt-card-thumb. */
}
.mkt-thumb-scaler-wrap {
    overflow: hidden;
    /* width/height set inline by fitScalerToThumb so the scaled
       footprint flex-centres inside .mkt-card-thumb. */
}
.mkt-thumb-scaler-inner {
    transform-origin: top left;
    /* width/height/transform set inline by fitScalerToThumb. */
}
.mkt-card-loading,
.mkt-card-error {
    font-size: 0.85rem;
    color: var(--muted);
    padding: 16px;
    text-align: center;
}
.mkt-card-error { color: #b91c1c; }

/* ── Matching Fonts shared panel (Books modal + Book Cover Text accordion) ── */
.font-pair-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 1rem;
    align-items: start;
}
.font-pair-col-desc {
    font-size: 0.9rem;
    color: var(--muted);
    line-height: 1.45;
}
/* PRIMARY + SECONDARY columns — let the inner lozenge fill the column
   width rather than capping at .bcfp-card's default max-width:400px. */
.font-pair-col-primary .bcfp-card,
.font-pair-col-secondary .bcfp-card {
    max-width: 100%;
}
.font-pair-section { display: flex; flex-direction: column; gap: 4px; }
.font-pair-section .me-label {
    /* Same as canvas-panel labels (e.g. "Cover Height (px)"). */
    display: block;
    font-size: 12px;
    font-weight: 500;
    color: var(--muted);
    margin-bottom: 4px;
    letter-spacing: 0;
    text-transform: none;
    font-family: 'Inter', sans-serif;
}

/* ─── BISAC/Thema "Suggest" pick modal ──────────────────────────────
   Each suggestion is rendered as a clickable row with a checkbox, the
   canonical code + description (accent rose), and a short LLM-supplied
   reason underneath. Confirming the modal appends the checked codes
   to the BISAC/Thema text input on the event-edit form.
*/
.suggest-row {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.1s;
    margin-bottom: 0.5rem;
}
.suggest-row:hover { background: #faf7f9; }
.suggest-row input[type=checkbox] { margin-top: 3px; flex-shrink: 0; }
.suggest-row-code {
    font-weight: 600;
    color: var(--accent);
    font-size: 0.9rem;
    margin-bottom: 0.25rem;
}
.suggest-row-reason { color: var(--muted); font-size: 0.85rem; line-height: 1.4; }

/* ─── Shared book-grid helper (views-book-grid.js) ──────────────────
   The helper emits a tab bar with a right-pinned "View all >" link
   (Overview only). Same accent rose as the rest of the dashboard;
   subtle hover underline so it reads as a link without competing with
   the tab buttons. */
.book-grid-view-all {
    color: var(--accent);
    font-size: 0.85rem;
    text-decoration: none;
    font-weight: 500;
    white-space: nowrap;
}
.book-grid-view-all:hover { text-decoration: underline; color: var(--accent-dark); }
/* Optional section title above the tab bar (Overview passes title:'Books').
   Black DM Serif, in line with .card-header h3 / page hero conventions. */
.book-grid-title {
    font-family: 'DM Serif Display', serif;
    font-size: 1.5rem;
    font-weight: 400;
    color: var(--ink);
    margin: 0 0 0.75rem 0;
    letter-spacing: -0.3px;
}
/* Match the Overview right-rail card headers (Events, Trending) to the
   book-grid-title above. Scoped to .overview-mid-right-col so other
   .card-header h3 instances elsewhere keep the global 1.1rem sizing. */
.overview-mid-right-col .card-header h3 {
    font-size: 1.5rem;
    font-weight: 400;
}
/* Bookmark tab button — matches the Campaigns page's bookmark toggle
   (.campaigns-bookmark-btn): circle outline, neutral by default,
   accent on hover/active, glyph filled when this tab is the live view. */
.book-grid-bookmarks-tab {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    align-self: center;       /* don't stretch to .tab-bar's tallest child */
    flex: 0 0 auto;
    width: 32px;              /* explicit square so border-radius:999px = circle */
    height: 32px;
    padding: 0;               /* size driven by width/height, not padding */
    box-sizing: border-box;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card-bg);
    color: var(--ink);
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, box-shadow 0.15s;
}
.book-grid-bookmarks-tab svg {
    width: 16px;
    height: 16px;
    display: block;
}
.book-grid-bookmarks-tab:hover,
.book-grid-bookmarks-tab.active {
    border-color: var(--accent);
    color: var(--accent);
}
.book-grid-bookmarks-tab.active svg { fill: var(--accent); }
/* 20px gap between the bookmark and the first text tab — the helper
   uses gap:0 on .tab-bar, so a margin-right here is what produces the
   indent. */
.book-grid-tab-bar .book-grid-bookmarks-tab { margin-right: 20px; }
/* The standard .tab-bar paints a 2px grey baseline that .tab-btn.active
   overrides with accent. On the book-grid we want that baseline to start
   AFTER the bookmark button — drop it from the tab-bar itself and apply
   it to each .tab-btn instead. With gap:0 the per-button borders abut
   into a continuous line; active still flips to accent. Bookmark is NOT
   a .tab-btn, so no baseline under it. */
.tab-bar.book-grid-tab-bar { border-bottom: none; }
.tab-bar.book-grid-tab-bar .tab-btn {
    border-bottom: 2px solid var(--border);
    margin-bottom: 0;
}
.tab-bar.book-grid-tab-bar .tab-btn.active { border-bottom-color: var(--accent); }

/* ── Asset Library upload progress rows ─────────────────────────────
   Per-file status row rendered in the upload modal while files are
   uploading. One row per file, stacked vertically. States:
     • In flight  → fill bar at N% + percentage text
     • Done       → .al-upload-row-done shows ✓
     • Error/skip → .al-upload-row-error shows ✗ + reason
   Inherits theme tokens (accent/border) so it tracks the rest of the
   admin UI. */
.al-upload-row { display: flex; gap: 8px; align-items: center; margin: 4px 0; font-size: 13px; }
.al-upload-row-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.al-upload-row-bar { width: 160px; height: 8px; background: var(--border); border-radius: 4px; overflow: hidden; }
.al-upload-row-bar-fill { height: 100%; background: var(--accent); transition: width 200ms ease; width: 0%; }
.al-upload-row-pct { flex: 0 0 auto; color: var(--muted); min-width: 36px; text-align: right; }
.al-upload-row-done { flex: 0 0 auto; color: #16a34a; font-weight: 600; }
.al-upload-row-error { flex: 0 0 auto; color: #dc2626; font-weight: 600; }

/* --- Content Pages (CMS) --- */
/* Listing-table preview snippet under the page title (2-3 lines, muted). */
.cp-preview {
    font-size: 0.8rem;
    color: var(--muted);
    margin-top: 4px;
    max-width: 540px;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.cp-public-badge {
    display: inline-block;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.5px;
    color: #15803d;
    background: #dcfce7;
    border: 1px solid #bbf7d0;
    border-radius: 4px;
    padding: 1px 5px;
    vertical-align: middle;
    margin-left: 6px;
}
/* Edit modal: is_public inline checkbox + multi-stream checklist + Quill. */
.cp-inline-check {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-weight: 400;
    cursor: pointer;
}
.cp-stream-list {
    max-height: 160px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 8px 10px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.cp-stream-opt {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.875rem;
    cursor: pointer;
}
.cp-editor {
    background: #fff;
}
.cp-editor .ql-container {
    min-height: 220px;
    font-size: 0.95rem;
    font-family: 'Inter', sans-serif;
}
.cp-editor .ql-editor {
    min-height: 220px;
}
/* Revision-preview rendered HTML box. */
.cp-preview-html {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px 14px;
    background: #fafafa;
    max-height: 50vh;
    overflow-y: auto;
}
.cp-preview-html h1, .cp-preview-html h2, .cp-preview-html h3,
.cp-preview-html h4, .cp-preview-html h5 { margin: 0.6em 0 0.3em; line-height: 1.25; }
.cp-preview-html p { margin: 0 0 0.6em; }
.cp-preview-html ul, .cp-preview-html ol { margin: 0 0 0.6em 1.2em; }

/* Content Pages add/edit modal — fills ~95% of the viewport (overrides the
   760px / auto-height aic-modal-inner) so the rich-text editor has real room.
   Flex column: fixed header + footer, scrollable body in between. */
.cp-edit-modal-inner {
    width: 95vw;
    max-width: 95vw;
    height: 95vh;
    max-height: 95vh;
    display: flex;
    flex-direction: column;
}
.cp-edit-modal-inner .cp-edit-body {
    flex: 1 1 auto;
    min-height: 0;
}
.cp-edit-modal-inner .cp-editor .ql-container,
.cp-edit-modal-inner .cp-editor .ql-editor {
    min-height: 48vh;
}

/* --- Terms of Use on the new-password (invite) form --- */
/* ~4-line scrollable box rendering the published Terms HTML pre-login. */
.login-terms-box {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.5rem 0.75rem;
    max-height: 6.5em;          /* ≈ 4 lines */
    overflow-y: auto;
    font-size: 0.8rem;
    line-height: 1.4;
    color: var(--ink);
    background: #fafafa;
    text-align: left;
}
.login-terms-box h1, .login-terms-box h2, .login-terms-box h3,
.login-terms-box h4, .login-terms-box h5 { font-size: 0.85rem; margin: 0.4em 0 0.2em; }
.login-terms-box p { margin: 0 0 0.4em; }
.login-terms-box ul, .login-terms-box ol { margin: 0 0 0.4em 1.1em; padding: 0; }
.login-agree {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.8rem;
    color: var(--ink);
    margin: 0 0 1rem;
    cursor: pointer;
    text-align: left;
}
.login-agree input { flex: 0 0 auto; }

/* ─── Training Sessions ─────────────────────────────────────────────
   Floating "Book a Training Session" FAB (calendar), stacked ABOVE the
   feedback bubble but at a LOWER z-index (900 < the 99999+ modal overlays)
   so it hides under the Books modal / Canvas editor. */
.ie-training-btn {
    position: fixed;
    bottom: 76px;              /* above the 44px feedback bubble at bottom:24px */
    right: 24px;
    z-index: 900;              /* below modal overlays (>=99999) → hidden under modals */
    width: 44px; height: 44px;
    border-radius: 50%;
    border: none;
    background: var(--accent);
    color: #fff;
    cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 2px 12px rgba(0,0,0,0.2);
    transition: background 0.15s, transform 0.15s;
}
.ie-training-btn:hover { background: #8a1f3c; transform: scale(1.05); }

/* Booking / manage modal — reuses .series-info-save-overlay/modal, wider. */
.training-modal { max-width: 560px; text-align: left; }
.training-modal .series-info-save-title,
.training-modal .series-info-save-sub { text-align: center; }
.training-manage { max-width: 720px; display: flex; flex-direction: column; max-height: 88vh; }
.training-manage-assign { margin: 0 0 0.6rem; }
.training-warn { background:#fdecea; color:#a12734; border:1px solid #f5c6cb; border-radius:6px; padding:8px 12px; font-size:0.82rem; margin:0 0 0.6rem; }
.training-manage-footer { padding-top: 0.9rem; margin-top: 0.5rem; border-top: 1px solid var(--border); text-align: right; }

.training-grid-scroll { max-height: 55vh; overflow-y: auto; padding: 2px; }
.training-grid-tall { max-height: 60vh; }
.training-week { margin-bottom: 0.85rem; }
.training-week + .training-week { border-top: 1px dashed var(--border); padding-top: 0.75rem; }
.training-grid { display: grid; gap: 6px; }
.training-col-head { font-size: 0.72rem; font-weight: 600; text-align: center; color: var(--muted); padding: 2px 0 4px; }

.training-cell {
    border: 1px solid var(--border); border-radius: 6px;
    padding: 5px 3px; min-height: 34px; font-size: 0.78rem; line-height: 1.15;
    text-align: center; background: #f4f4f5; color: #9aa0a6;
}
.training-cell-time { display: block; }
.training-cell-sa { display: block; font-size: 0.62rem; font-weight: 400; opacity: 0.9; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.training-cell.is-available { background: #fff; border-color: var(--accent); color: var(--accent); cursor: pointer; font-weight: 600; }
.training-cell.is-available:hover { background: var(--accent); color: #fff; }
.training-cell.is-unavailable { opacity: 0.5; }
.training-cell-manage { cursor: pointer; }
.training-cell-manage.is-selected { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
.training-cell-manage.is-booked.is-selected { background: #8a1f3c; border-color: #8a1f3c; }
.training-cell-manage.is-booked:not(.is-selected) { background: #fff; border: 1px dashed #c0392b; color: #c0392b; text-decoration: line-through; }
.training-cell.is-past { opacity: 0.38; cursor: not-allowed; }

/* Booked state (booking modal) */
.training-booked { text-align: center; padding: 0.4rem 0 0.2rem; }
.training-booked-line { margin: 0 0 0.75rem; }
.training-join { margin: 0.25rem 0; }
.training-hint { font-size: 0.75rem; color: var(--muted); margin: 0.6rem auto; max-width: 380px; }
.training-cal-links { font-size: 0.82rem; color: var(--muted); margin: 0.85rem 0 0.25rem; }
.training-cal-links a { color: var(--accent); }
.training-change { margin-top: 0.9rem; }
.training-change a { color: var(--muted); font-size: 0.85rem; }

/* List status badges */
.ts-badge { display: inline-block; padding: 2px 9px; border-radius: 10px; font-size: 0.75rem; font-weight: 600; }
.ts-badge-available { background: #e8f5e9; color: #1a7f37; }
.ts-badge-booked { background: #fdecef; color: #af2860; }

/* ─── Staged Amazon A+ — Branding sub-tab cards (migration 171) ───── */
.branding-aplus-thumbs { display: flex; flex-direction: column; gap: 6px; max-width: 280px; }
.branding-aplus-thumb {
    display: block; width: 100%; height: auto; border: 1px solid var(--border);
    border-radius: 4px; aspect-ratio: 1940 / 600; object-fit: cover;
}
/* Checkerboard behind the transparent 3D-cover PNG so alpha is visible. */
.branding-3d-checker {
    background-image:
        linear-gradient(45deg, #e8e8e8 25%, transparent 25%, transparent 75%, #e8e8e8 75%),
        linear-gradient(45deg, #e8e8e8 25%, transparent 25%, transparent 75%, #e8e8e8 75%);
    background-size: 16px 16px;
    background-position: 0 0, 8px 8px;
    border-radius: 6px;
    padding: 6px;
}

/* ── Feedback attachment control (paperclip → Capture/Upload, max 5) ──
   Shared by the widget's composers + the admin Feedback modal
   (markup/wiring exported from widgets/feedback-widget.js). */
.fb-attach { position: relative; display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.fb-attach-btn {
    background: none; border: 1px solid var(--border); border-radius: 6px;
    padding: 4px 7px; cursor: pointer; color: var(--muted);
    display: inline-flex; align-items: center; flex: none;
}
.fb-attach-btn:hover { color: var(--accent); border-color: var(--accent); }
.fb-attach-btn svg { width: 15px; height: 15px; }
.fb-attach--has-files .fb-attach-btn { color: var(--accent); border-color: var(--accent); }
.fb-attach-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.fb-attach--busy .fb-attach-btn svg { animation: spin 0.9s linear infinite; }
.fb-attach-menu {
    position: absolute; bottom: calc(100% + 6px); left: 0; z-index: 30;
    background: var(--paper, #fff); border: 1px solid var(--border);
    border-radius: 8px; box-shadow: 0 8px 24px rgba(0,0,0,0.14);
    padding: 4px; min-width: 215px;
}
.fb-attach-menu button {
    display: flex; align-items: center; gap: 8px; width: 100%;
    background: none; border: none; padding: 7px 10px; font-size: 0.82rem;
    color: var(--ink); cursor: pointer; border-radius: 6px; text-align: left;
    font-family: inherit;
}
.fb-attach-menu button:hover { background: rgba(0,0,0,0.05); }
.fb-attach-menu button svg { width: 14px; height: 14px; color: var(--muted); flex: none; }
.fb-attach-chips { display: inline-flex; gap: 4px; flex-wrap: wrap; }
.fb-attach-chip {
    position: relative; width: 34px; height: 34px; border-radius: 6px;
    overflow: hidden; border: 1px solid var(--border); flex: none;
}
.fb-attach-chip img { width: 100%; height: 100%; object-fit: cover; display: block; }
.fb-attach-remove {
    position: absolute; top: 0; right: 0; width: 14px; height: 14px;
    line-height: 12px; font-size: 11px; padding: 0; border: none;
    border-radius: 0 0 0 6px; background: rgba(0,0,0,0.55); color: #fff; cursor: pointer;
}
/* Multi-image rows in thread bubbles (widget + admin modal). */
.fb-msg-screenshots { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.fb-msg-screenshots img {
    max-height: 110px; max-width: 48%; border-radius: 4px;
    cursor: pointer; object-fit: cover;
}
