/* =========================================================================
   LazyBlog — Admin-only styles
   Loaded by layout.php only when the request path starts with /admin.
   Inherits theme tokens (--bg, --primary, etc.) from site.css.
   ========================================================================= */

/* ---------- Admin canvas — wider than public pages ---------- */
body.is-admin main { max-width: 1180px; }
@media (min-width: 1400px) {
    body.is-admin main { max-width: 1320px; }
}
@media (min-width: 1600px) {
    body.is-admin main { max-width: 1480px; }
}

/* ---------- Admin shell ---------- */
.admin-card {
    max-width: 440px;
    margin: 0 auto;
    padding: 28px 24px;
    background: var(--panel);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 3px;
}
.admin-header-row {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}
.admin-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}
/* Forms wrapping buttons (logout, delete) must not add their own margin —
   browsers vary, and any stray gap distorts button sizing visually. */
.admin-actions form,
.admin-header-row form {
    margin: 0;
    padding: 0;
    display: inline-flex;
}

/* ---------- Admin tabs ----------
   Section switcher between POSTS and PLUGINS. Each tab carries the H2
   weight when active so it reads as the page heading; inactive tabs sit at
   the same size but dimmed, so the user can scan-switch between sections
   without losing the "where am I" anchor. */
.admin-tabs {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 22px;
    margin: 28px 0 16px;
}
.admin-tab {
    font-family: 'VT323', 'Share Tech Mono', monospace;
    font-size: clamp(28px, 5vw, 36px);
    line-height: 1;
    letter-spacing: 1px;
    color: var(--text-dim);
    text-decoration: none;
    padding: 0 0 4px;
    border-bottom: 2px solid transparent;
    transition: color 0.18s ease, border-color 0.18s ease, text-shadow 0.18s ease;
}
.admin-tab:hover {
    color: var(--primary);
}
.admin-tab[aria-current="page"] {
    color: var(--primary);
    border-bottom-style: dotted;
    border-bottom-color: var(--primary);
    text-shadow: 0 0 8px var(--primary-glow);
}
/* `<button class="admin-tab">` (logout) needs the user-agent button reset
   so its visual matches the `<a>` siblings. NOTE: don't set `font: inherit`
   — `button.admin-tab` selector beats `.admin-tab` on specificity, and
   `inherit` would pull from the wrapping `<form>` (which has no font
   set), collapsing the button back to the browser default ~13px. Leaving
   font/color unset here lets the `.admin-tab` class win cleanly. */
button.admin-tab {
    background: transparent;
    border: 0;
    cursor: pointer;
    padding: 0 0 4px;
    border-bottom: 2px solid transparent;
}
.admin-tab-form {
    margin: 0;
    padding: 0;
    display: inline-flex;
    align-items: flex-end;
}
@media (max-width: 768px) {
    /* On narrow viewports the desktop `clamp(28px, 5vw, 36px)` + gap 22px
       lands [ POSTS (N) ] on one row and pushes [ PLUGINS ] / [ SECURITY ]
       onto a second wrap row, which reads as a different group of links.
       Shrink the font + drop into a single horizontally-scrollable row so
       all tabs stay visually paired with each other. Threshold matches
       the admin-table mobile rules below so a single "is this mobile?"
       answer applies across the page. */
    .admin-tabs {
        gap: 14px;
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }
    .admin-tabs::-webkit-scrollbar { display: none; }
    .admin-tab {
        font-size: 20px;
        white-space: nowrap;
        flex-shrink: 0;
    }
}
@media (max-width: 380px) {
    .admin-tab { font-size: 18px; }
    .admin-tabs { gap: 10px; }
}

/* ---------- Edit form ---------- */
/* Compact, write-focused form. Meta fields cluster on flex rows so the
   body textarea gets most of the vertical space. */
.admin-form { display: flex; flex-direction: column; gap: 10px; margin-top: 18px; }

.admin-form-row {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    align-items: flex-end;
}
.admin-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;        /* allow ellipsis / shrink inside flex */
}
.admin-field-grow { flex: 1 1 240px; }

@media (max-width: 600px) {
    .admin-form-row { gap: 8px; }
    .admin-form-row .admin-field { flex: 1 1 100% !important; }
}

.admin-label {
    font-family: 'Share Tech Mono', monospace;
    font-size: 10px;
    letter-spacing: 2px;
    color: var(--primary-dim);
    text-transform: uppercase;
    display: block;
    margin: 0;
    line-height: 1;
}
.admin-label-hint {
    color: var(--text-dim);
    text-transform: none;
    letter-spacing: 1px;
    font-size: 10px;
}
.admin-input {
    width: 100%;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--text);
    font-family: 'Play', system-ui, sans-serif;
    font-size: 15px;
    padding: 7px 10px;
    border-radius: 2px;
    box-sizing: border-box;
}
.admin-input:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 8px var(--primary-glow);
}
.admin-mono { font-family: 'Share Tech Mono', monospace; font-size: 13px; }

/* iOS Safari renders native <input type="date"> and <input type="time">
   with their value text centred (and a system font), which throws the
   Date / Time fields off the left-aligned column the rest of the form
   uses. `-webkit-appearance: none` strips the native widget chrome so
   `text-align: left` and the .admin-mono font actually stick. The
   browser-supplied calendar / clock picker still opens on focus. */
.admin-input[type="date"],
.admin-input[type="time"] {
    -webkit-appearance: none;
    appearance: none;
    text-align: left;
    /* Match the height of plain text inputs — iOS otherwise sizes these
       slightly taller because of the (now-hidden) native indicator. */
    min-height: 34px;
}
.admin-textarea-body {
    /* Compact default — drag handle (resize:vertical) is always there if
       the writer wants more. */
    min-height: clamp(280px, 40vh, 440px);
    font-family: 'Share Tech Mono', monospace;
    font-size: 14px;
    line-height: 1.6;
    resize: vertical;
}

/* ---------- Tag chip input (edit form) ---------- */
/* Visually identical to .admin-input: same border, padding, height, and
   focus glow. The wrap holds rendered chips + the entry input. */
.tag-chip-hidden { display: none; }
.tag-chip-wrap {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    padding: 5px 8px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 2px;
    min-height: 35px;
    box-sizing: border-box;
}
.tag-chip-wrap:focus-within {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 8px var(--primary-glow);
}
.tag-chip-list { display: contents; }
.tag-chip-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 6px 2px 10px;
    background: var(--primary-glow);
    border: 1px solid var(--primary-dim);
    border-radius: 2px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 12px;
    color: var(--primary);
    letter-spacing: 1px;
}
.tag-chip-x {
    background: transparent;
    border: none;
    color: var(--primary);
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
    padding: 0 2px;
    opacity: 0.7;
}
.tag-chip-x:hover { opacity: 1; }
/* Entry input lives inside the wrap; visually it's just the cursor + text.
   No own border/background so it inherits the wrap's frame. */
.tag-chip-entry {
    flex: 1 1 120px;
    min-width: 120px;
    background: transparent !important;
    border: none !important;
    padding: 2px 0 !important;
    margin: 0 !important;
    height: 22px !important;
    line-height: 1.4 !important;
    box-shadow: none !important;
    font-size: 14px !important;
    color: var(--text) !important;
}
.tag-chip-entry:focus { outline: none; }
.tag-chip-entry::placeholder { color: var(--text-dim); }

/* Default Chrome / Safari paint input placeholders at ~42% opacity of
   the inherited color. On the CRT themes the partial alpha kills the
   phosphor cue. Pin the placeholder colour to the theme's --text-dim
   token so every theme owns its placeholder hue explicitly. */
.admin-input::placeholder { color: var(--text-dim); opacity: 1; }

/* ---------- EasyMDE theme integration ---------- */
.EasyMDEContainer .editor-toolbar {
    background: var(--panel);
    border: 1px solid var(--border);
    border-bottom: none;
    border-radius: 2px 2px 0 0;
    opacity: 1;
    /* Stick the toolbar to the top of the viewport while scrolling so
       the formatting buttons stay reachable inside long posts. */
    position: sticky;
    top: 0;
    z-index: 50;
}
.EasyMDEContainer .editor-toolbar button {
    color: var(--text) !important;
    border-radius: 2px;
}
.EasyMDEContainer .editor-toolbar button:hover,
.EasyMDEContainer .editor-toolbar button.active {
    background: var(--primary-glow) !important;
    border-color: var(--primary-dim) !important;
    color: var(--primary) !important;
}
.EasyMDEContainer .editor-toolbar i.separator {
    border-left-color: var(--border);
    border-right-color: var(--border);
}
.EasyMDEContainer .CodeMirror {
    background: var(--bg);
    color: var(--text);
    border: 1px solid var(--border);
    font-family: 'Share Tech Mono', monospace;
    font-size: 14px;
    line-height: 1.55;
    /* Same envelope as the plain textarea fallback. */
    min-height: clamp(280px, 40vh, 440px);
}
.EasyMDEContainer .CodeMirror-cursor { border-left-color: var(--primary); }
.EasyMDEContainer .CodeMirror-selected { background: var(--primary-glow) !important; }
.EasyMDEContainer .editor-statusbar {
    background: var(--panel);
    color: var(--text-dim);
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    border: 1px solid var(--border);
    border-top: none;
}
.EasyMDEContainer .editor-preview, .EasyMDEContainer .editor-preview-side {
    background: var(--bg);
    color: var(--text);
    border-color: var(--border);
}

/* Fullscreen + side-by-side: EasyMDE applies its own background:white via
   .CodeMirror-fullscreen / .editor-toolbar.fullscreen with high specificity,
   so we override with !important to keep the CRT theme. */
.EasyMDEContainer .editor-toolbar.fullscreen,
.editor-toolbar.fullscreen {
    background: var(--panel) !important;
    border: 1px solid var(--border) !important;
    opacity: 1 !important;
}
.EasyMDEContainer .CodeMirror-fullscreen,
.CodeMirror.CodeMirror-fullscreen {
    background: var(--bg) !important;
    color: var(--text) !important;
    border: none !important;
}
.CodeMirror-fullscreen .CodeMirror-scroll,
.EasyMDEContainer .CodeMirror-fullscreen .CodeMirror-scroll {
    background: var(--bg) !important;
}
.editor-preview-side,
.editor-preview-active-side,
.EasyMDEContainer .editor-preview-side {
    background: var(--bg) !important;
    color: var(--text) !important;
    border-left: 1px solid var(--border) !important;
}
.editor-preview-active,
.EasyMDEContainer .editor-preview-active {
    background: var(--bg) !important;
    color: var(--text) !important;
}
/* Preview pane typography matches the post body style */
.editor-preview h1, .editor-preview h2, .editor-preview h3,
.editor-preview-side h1, .editor-preview-side h2, .editor-preview-side h3 {
    color: var(--primary);
    font-family: 'VT323', monospace;
}
.editor-preview a, .editor-preview-side a { color: var(--accent); }
.editor-preview code, .editor-preview-side code,
.editor-preview pre, .editor-preview-side pre {
    background: var(--panel);
    color: var(--text);
    border: 1px solid var(--border);
}
/* Hide scanline overlay + flicker on fullscreen so the editor isn't covered */
body.editor-fullscreen-active::before { display: none; }
body.editor-fullscreen-active.is-home { animation: none; }

/* ---------- Draft pill ---------- */
.admin-checkbox-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    color: var(--primary);
    border: 1px solid var(--primary-dim);
    border-radius: 2px;
    padding: 0 12px;
    height: 34px;            /* match admin-input visual height (7+15+7+2 border) */
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    letter-spacing: 2px;
    text-transform: uppercase;
    user-select: none;
}
.admin-checkbox-pill input { accent-color: var(--primary); margin: 0; }
.admin-checkbox-pill:hover { background: var(--primary-glow); border-color: var(--primary); }

/* ---------- META collapse (post editor) ----------
   Secondary fields (date, slug, tags, author, icon, social image, series,
   password) live inside a native <details> block so the writing surface
   stays focused. Closed by default; the <summary> reads as a small terminal
   header chip. The `[ + ]` glyph flips to `[ - ]` while the body is open. */
.admin-meta-collapse {
    margin: 4px 0 6px;
}
.admin-meta-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    color: var(--primary-dim);
    border: 1px dashed var(--border);
    border-radius: 2px;
    padding: 6px 12px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 10px;
    letter-spacing: 2px;
    text-transform: uppercase;
    user-select: none;
    /* Strip the default disclosure triangle — we render our own glyph. */
    list-style: none;
}
.admin-meta-toggle::-webkit-details-marker { display: none; }
.admin-meta-toggle:hover {
    color: var(--primary);
    background: var(--primary-glow);
    border-color: var(--primary-dim);
}
.admin-meta-toggle:focus-visible {
    outline: 1px solid var(--primary);
    outline-offset: 2px;
}
.admin-meta-toggle-glyph {
    color: var(--accent);
    /* Keep `[ + ]` on a single visual line — without nowrap the three
       characters wrap onto separate rows on narrow viewports because the
       parent flex container shrinks the span below its content width. */
    white-space: nowrap;
    flex-shrink: 0;
}
.admin-meta-toggle-glyph::before { content: '[ + ]'; }
.admin-meta-collapse[open] > .admin-meta-toggle .admin-meta-toggle-glyph::before { content: '[ - ]'; }
.admin-meta-toggle-label-fields { color: var(--text-dim); }
.admin-meta-collapse-body {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 10px;
    padding: 12px 14px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent-dim);
    border-radius: 2px;
    background: var(--panel);
}
@media (max-width: 600px) {
    /* On phones the long dot-separated field list (`· DATE · SLUG · …`)
       makes the toggle balloon into a multi-line block. Hide the field
       list, keep just `[ + ] META`, and stretch the chip across the row
       so it reads as an obvious tap target. */
    .admin-meta-toggle { display: flex; width: 100%; justify-content: flex-start; }
    .admin-meta-toggle-label-fields { display: none; }
    .admin-meta-collapse-body { padding: 10px 10px; }
}

/* ---------- Admin buttons ---------- */
/* Unified to the dominant theme color (--primary). Reset native <button>
   user-agent styles so <a class="admin-btn"> and <button class="admin-btn">
   render identically. Fixed height + box-sizing prevents row-divider artifacts. */
.admin-btn {
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 32px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    font-weight: 400;
    color: var(--primary);
    background: transparent;
    border: 1px solid var(--primary-dim);
    padding: 0 14px;
    margin: 0;
    letter-spacing: 2px;
    text-decoration: none;
    border-radius: 2px;
    cursor: pointer;
    text-transform: uppercase;
    line-height: 1;
    vertical-align: middle;
    -webkit-appearance: none;
    appearance: none;
    transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease;
}
/* Native <button> needs every font property re-asserted because some
   browsers don't inherit font on <button>. Do NOT use `font: inherit` —
   that shorthand resets font-size+line-height to body values (17-18px). */
button.admin-btn,
button.admin-btn-sm {
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    font-weight: 400;
    line-height: 1;
    letter-spacing: 2px;
    overflow: visible;
    width: auto;
    min-width: 0;
}
button.admin-btn-sm { font-size: 10px; }
.admin-btn:hover {
    background: var(--primary-glow);
    border-color: var(--primary);
}
.admin-btn-primary {
    background: var(--primary-glow);
    border-color: var(--primary);
}
.admin-btn-primary:hover {
    background: var(--primary);
    color: var(--bg);
}
.admin-btn-danger {
    color: #ff7676;
    border-color: #663030;
}
.admin-btn-danger:hover {
    background: rgba(255, 80, 80, 0.14);
    border-color: #ff5555;
    color: #ff9090;
}
.admin-btn-sm {
    height: 26px;
    font-size: 10px;
    padding: 0 10px;
}

/* ---------- Inline status messages ---------- */
.admin-error {
    color: #ff8a8a;
    border-left: 3px solid #ff5555;
    padding: 8px 12px;
    background: rgba(255, 80, 80, 0.08);
    font-family: 'Share Tech Mono', monospace;
    font-size: 13px;
}
.admin-flash {
    color: var(--primary);
    border-left: 3px solid var(--primary);
    padding: 8px 12px;
    background: var(--primary-glow);
    font-family: 'Share Tech Mono', monospace;
    font-size: 13px;
    margin-bottom: 14px;
}

/* ---------- Admin list table ---------- */
.admin-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 14px;
    font-size: 14px;
    /* Auto layout: browser distributes width by content. Combined with
       white-space: nowrap on tight columns + wrap-friendly word-wrap on
       text columns, the table reflows smoothly across viewport sizes. */
    table-layout: auto;
}
.admin-table th, .admin-table td {
    text-align: left;
    padding: 10px 8px;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
    word-wrap: break-word;
}
/* Fluid column behavior — no fixed px widths. Tight columns (DATE,
   STATUS, ACTIONS) get nowrap so they shrink to content. TITLE, SERIES,
   TAGS wrap naturally as the viewport narrows. table-layout: auto
   (default) handles the rest. */
.admin-table th:nth-child(1), .admin-table td:nth-child(1) { white-space: nowrap; }
.admin-table th:nth-child(5), .admin-table td:nth-child(5) { white-space: nowrap; text-align: center; }
.admin-table th:nth-child(6), .admin-table td:nth-child(6) { white-space: nowrap; }
.admin-table .admin-col-series { max-width: 220px; }
.admin-table th {
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    letter-spacing: 2px;
    color: var(--primary-dim);
    text-transform: uppercase;
}
.admin-table tbody tr:hover { background: var(--panel); }

/* Title cell: stretches to fill the remaining space; long titles ellipsis-
   truncate; full title shown on hover via title="" attr. */
.admin-table .admin-title-cell {
    overflow: hidden;
}
.admin-table .admin-title-cell a {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
    /* Default global a { text-decoration: underline } is noisy on a tight
       table — show underline only on hover/focus as an affordance. */
    text-decoration: none;
}
.admin-table .admin-title-cell a:hover,
.admin-table .admin-title-cell a:focus-visible {
    text-decoration: underline;
    text-decoration-style: dashed;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}
/* Tags wrap inside their fixed-width column instead of squeezing the
   title column. */
.admin-table .admin-mono { line-height: 1.45; }
/* Action column: flex container, no special padding — fixed-height buttons
   line up naturally with vertical-align:top across all rows. */
.admin-table td.admin-row-actions { white-space: nowrap; }
.admin-row-actions {
    display: flex;
    gap: 6px;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-start;
}
.admin-row-actions .admin-btn { flex: 0 0 auto; }
.admin-row-actions form { margin: 0; padding: 0; display: inline-flex; }

/* Input + "[ UPLOAD ]" trigger sitting on one row. Keeps the file
 * picker as a small adjunct to the URL field so externally-hosted
 * social images still work. */
.admin-input-with-upload {
    display: flex;
    gap: 8px;
    align-items: stretch;
}
.admin-input-with-upload .admin-input { flex: 1 1 auto; min-width: 0; }
.admin-input-with-upload .admin-btn { flex: 0 0 auto; align-self: center; }

/* Status dot — replaces bigger LIVE/DRAFT/SCHEDULED text badges.
   Live: green dot with glow. Draft: muted gray. Scheduled: amber. */
.admin-status {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    vertical-align: middle;
    font-size: 0;             /* hide any visible text we keep for a11y */
    line-height: 0;
    margin-top: 6px;          /* nudge onto title baseline */
}
.admin-status-live {
    background: #39ff14;
    box-shadow: 0 0 6px rgba(57, 255, 20, 0.6);
}
.admin-status-draft {
    background: #5a5a5a;
}
.admin-status-scheduled {
    background: #ffb700;
    box-shadow: 0 0 6px rgba(255, 183, 0, 0.5);
}

@media (max-width: 768px) {
    /* Mobile layout shared by every .admin-table (post list, plugins,
       security keys). Each <tr> becomes a 2-row flex card:
         row 1 — `.admin-title-cell` (post title / plugin name / key name)
         row 2 — every `.admin-mono` meta cell inline + status dot + actions
       Cells reorder via `order` so the DOM keeps serving desktop column
       order while mobile sees title-first regardless of which column
       (varies per table) carries it. Breakpoint at 768 (not 600) covers
       landscape phones + small tablets where the desktop table still
       produces awkward column wraps. */
    .admin-table thead { display: none; }
    .admin-table, .admin-table tbody { display: block; width: 100% !important; }
    .admin-table tr {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        column-gap: 10px;
        row-gap: 4px;
        padding: 8px 0;
        border-bottom: 1px solid var(--border);
        width: 100% !important;
    }
    .admin-table td {
        display: block;
        padding: 0;
        border: none;
        text-align: left !important;
        width: auto !important;
    }

    /* Hide noisy columns on mobile — desktop still shows them.
       `.admin-col-tags` is added to the post-list TAGS td so we can hide
       it by class rather than nth-child, since different tables have
       different column orders. */
    .admin-table .admin-col-series,
    .admin-table .admin-col-tags,
    .admin-table .admin-col-mobile-hide { display: none; }

    /* Title cell takes the full first row. Negative order so it always
       wins against any default-ordered (=0) meta cell. */
    .admin-table .admin-title-cell {
        order: -1;
        flex: 1 0 100%;
    }

    /* All other .admin-mono cells (date, slug, version, type, etc) collapse
       into a single inline meta line, styled like the date used to be:
       dim monospace small caps. */
    .admin-table tbody td.admin-mono {
        order: 2;
        color: var(--primary-dim);
        font-family: 'Share Tech Mono', monospace;
        font-size: 11px;
        letter-spacing: 1.5px;
    }

    /* Status dot — sits between meta and actions. `:has(> .admin-status)`
       wins over `td.admin-mono` (same specificity) because it's declared
       later. */
    .admin-table tbody td:has(> .admin-status) {
        order: 3;
    }
    .admin-table .admin-status { margin-top: 0; }

    /* Actions push to the right edge. */
    .admin-table td.admin-row-actions {
        order: 4;
        margin-left: auto;
    }

    /* Strip the slug chip's dashed border + padding on mobile so it sits
       inline as plain mono text alongside the date instead of dominating
       the meta row with a 180 px wide chip. Desktop keeps the visual
       chip — there's plenty of room there. */
    .admin-table .admin-series-chip {
        border: 0;
        padding: 0;
        background: transparent;
    }

    /* Series-list-specific: collapse to a single line per series. The
       generic .admin-table mobile rules above produce a 2-row card with
       title + meta inline; for the series list every meta field (slug
       chip, posts count, manifest flag, cover thumb, last activity) is
       reachable on the per-slug edit page, so the listing only needs
       to show "what is it" + "open it". `.admin-table.admin-series-
       table .admin-title-cell` wins over the generic `.admin-table
       .admin-title-cell` (specificity 0,0,3,0 vs 0,0,2,0). */
    .admin-table.admin-series-table tr {
        flex-wrap: nowrap;
        column-gap: 8px;
    }
    .admin-table.admin-series-table .admin-title-cell {
        flex: 1 1 0;
        order: 0;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .admin-table.admin-series-table .admin-mono { display: none; }
    /* Action column keeps EDIT but hides DEL — destructive ops should
       live on the edit screen where the operator has full context. */
    .admin-table.admin-series-table .admin-row-actions form { display: none; }
}

/* ---------- Series chip in admin posts list (under the title) ---------- */
.admin-series-chip {
    display: inline-block;
    margin-top: 4px;
    padding: 1px 8px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 10px;
    letter-spacing: 1px;
    color: var(--accent);
    background: transparent;
    border: 1px dashed var(--accent-dim);
    border-radius: 2px;
    text-decoration: none;
    text-transform: uppercase;
    transition: background 0.15s, border-color 0.15s;
}
.admin-series-chip:hover {
    background: var(--primary-glow);
    border-color: var(--accent);
    color: var(--primary);
}

/* ---------- Upload status pill (top-right floating notification) ---------- */
#upload-status {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 1200;
    background: var(--panel);
    border: 1px solid var(--accent);
    color: var(--accent);
    font-family: 'Share Tech Mono', monospace;
    font-size: 12px;
    letter-spacing: 1px;
    padding: 10px 16px;
    border-radius: 2px;
    box-shadow: 0 0 12px var(--primary-glow-strong);
    opacity: 0;
    transform: translateY(-8px);
    pointer-events: none;
    transition: opacity 0.15s ease, transform 0.15s ease;
}
#upload-status.visible {
    opacity: 1;
    transform: translateY(0);
}
#upload-status.visible::before {
    content: '◉ ';
    animation: upload-pulse 1s ease-in-out infinite;
}
@keyframes upload-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
}

/* ---------- Admin: Series management ---------- */
.admin-flash-warn {
    color: #ffce5b;
    border-left-color: #ffce5b;
    background: rgba(255, 200, 80, 0.08);
}
/* Row content middle-aligned so the cover thumbnail, date, and action
   buttons share a single horizontal baseline. The base `.admin-table td`
   rule sets `vertical-align: top`; we override with !important because
   media-query specificity ties with the base selector and a partial
   override leaves the action cell snapped to the top. Desktop-only
   because the mobile rules below collapse cells into block stacks. */
@media (min-width: 601px) {
    .admin-series-table td {
        vertical-align: middle !important;
        padding-top: 10px;
        padding-bottom: 10px;
    }
    .admin-series-table th { vertical-align: middle !important; }
    /* The shared `.admin-row-actions` rule sets `display: flex` on the TD,
       which breaks table-cell vertical alignment — flex items center
       within the flex container's intrinsic height, not the row's table-
       cell height, so EDIT / DEL MANIFEST end up at a different baseline
       than the cover thumb and date in the same row. Force this table
       back to `display: table-cell` and lay the buttons out as a small
       inline-block cluster that honours the cell's `vertical-align`. */
    .admin-series-table td.admin-row-actions {
        display: table-cell !important;
        white-space: nowrap;
        text-align: left;
        line-height: 1;
    }
    /* Keep `.admin-btn` at its native `inline-flex` so the text inside
       EDIT (an <a>) stays vertically centered like DEL MANIFEST (a
       <button> with native text-centering). Only the wrapping <form>
       needs the inline-block + middle align shim. */
    .admin-series-table td.admin-row-actions .admin-btn {
        vertical-align: middle;
        margin-right: 6px;
    }
    .admin-series-table td.admin-row-actions form {
        display: inline-block;
        vertical-align: middle;
        margin-right: 6px;
    }
    .admin-series-table td.admin-row-actions form:last-child,
    .admin-series-table td.admin-row-actions .admin-btn:last-child {
        margin-right: 0;
    }
}
.admin-series-table .admin-series-cover-thumb {
    display: inline-block;
    width: 32px;
    height: 32px;
    background-color: currentColor;
    -webkit-mask-image: var(--dot-mask);
    mask-image: var(--dot-mask);
    -webkit-mask-size: cover;
    mask-size: cover;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    color: var(--primary);
    vertical-align: middle;
}
/* Slim YES/NO dot indicator for the MANIFEST column — replaces the
   chunky status badge which was visually overwhelming the row. */
.admin-series-flag {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--primary);
    box-shadow: 0 0 6px var(--primary-glow-strong);
    vertical-align: middle;
}
.admin-series-table .admin-row-actions {
    white-space: nowrap;
}
/* Column widths — narrow columns get explicit ch widths so the layout
   stays stable when rows have or don't have a cover. Desktop only;
   the mobile rules at the bottom of this file collapse cells to block. */
@media (min-width: 601px) {
    .admin-series-table th:nth-child(1),
    .admin-series-table td:nth-child(1) { width: 22ch; }
    .admin-series-table th:nth-child(3),
    .admin-series-table td:nth-child(3),
    .admin-series-table th:nth-child(4),
    .admin-series-table td:nth-child(4) { width: 8ch; text-align: center; }
    .admin-series-table th:nth-child(5),
    .admin-series-table td:nth-child(5) { width: 56px; text-align: center; }
    .admin-series-table th:nth-child(6),
    .admin-series-table td:nth-child(6) { width: 14ch; white-space: nowrap; }
}

/* Legacy `.admin-series-table` mobile card was here. Removed: it set
   `display: block !important` on every td which overrode the new generic
   `.admin-table` mobile rules + the series-specific single-line override
   higher in this file. The current behaviour (single row per series,
   title + EDIT only) lives at the top of the same admin-table mobile
   block. */
/* Cancel the global h2 top margin (base.css `h2 { margin: 28px 0 16px }`)
   inside admin header rows — the page already has main padding above and
   actions on the right, so the title doesn't need its own breathing room. */
.admin-header-row h2 {
    margin: 0;
    line-height: 1.05;
}
.admin-series-rename-block {
    margin: 16px 0 4px;
}
.admin-series-edit-left {
    min-width: 0;
    display: flex;
    flex-direction: column;
}
.admin-series-rename-row {
    display: flex;
    gap: 10px;
    align-items: center;
    margin-top: 6px;
    flex-wrap: wrap;
}
.admin-series-rename-row input[type="text"] {
    flex: 1 1 240px;
    background: var(--bg);
    color: var(--primary);
    border: 1px solid var(--border);
    padding: 8px 10px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 14px;
    border-radius: 2px;
    letter-spacing: 1px;
}
.admin-series-rename-row .admin-btn {
    flex: 0 0 auto;
}
.admin-series-edit-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 280px;
    gap: 24px;
    margin-top: 16px;
}
@media (max-width: 900px) {
    .admin-series-edit-grid { grid-template-columns: 1fr; }
}
.admin-series-edit-form .admin-label { margin-top: 14px; }
.admin-series-edit-form input[type="text"],
.admin-series-edit-form textarea {
    width: 100%;
    background: var(--panel);
    color: var(--text);
    border: 1px solid var(--border);
    padding: 8px 10px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 14px;
    border-radius: 2px;
}
.admin-series-edit-form textarea {
    resize: vertical;
    min-height: 96px;
}
.admin-series-cover-block {
    border: 1px solid var(--border);
    padding: 12px 14px;
    margin-top: 18px;
    border-radius: 2px;
}
.admin-series-cover-block[disabled] { opacity: 0.55; }
.admin-series-cover-block legend {
    padding: 0 6px;
    color: var(--primary);
    font-family: 'Share Tech Mono', monospace;
    font-size: 12px;
    letter-spacing: 1px;
}
.admin-series-cover-preview-row {
    display: flex;
    gap: 18px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.admin-series-cover-preview {
    flex: 0 0 auto;
}
.admin-series-cover-thumb-big {
    display: block;
    width: 200px;
    aspect-ratio: 1 / 1;
    background-color: currentColor;
    -webkit-mask-image: var(--dot-mask);
    mask-image: var(--dot-mask);
    -webkit-mask-size: cover;
    mask-size: cover;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    color: var(--primary);
    border: 1px dashed var(--border);
}
.admin-series-cover-preview-fresh .admin-series-cover-thumb-big {
    border-style: solid;
    border-color: var(--primary);
}
.admin-series-cover-caption {
    margin: 6px 0 0;
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    letter-spacing: 1px;
    color: var(--text-dim);
}
.admin-series-cover-actions {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 10px;
    flex-wrap: wrap;
}
.admin-series-promote-toggle {
    color: var(--text-dim);
    font-size: 13px;
    font-family: 'Share Tech Mono', monospace;
    letter-spacing: 1px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
}
.admin-series-promote-toggle input[type="checkbox"] {
    accent-color: var(--primary);
    width: 14px;
    height: 14px;
    margin: 0;
    cursor: pointer;
}
/* Native <input type="file"> styled to match the terminal/CRT aesthetic.
   ::file-selector-button targets the trigger button across modern browsers
   (Chrome, Firefox, Safari 15+). The wrapper input gets monospace text +
   border so the "No file chosen" label reads as a console line, not a
   default-OS Arial fragment. */
input[type="file"].admin-file-input,
.admin-series-cover-block input[type="file"] {
    font-family: 'Share Tech Mono', monospace;
    font-size: 12px;
    letter-spacing: 1px;
    color: var(--text-dim);
    background: var(--panel);
    border: 1px solid var(--border);
    padding: 6px 8px;
    border-radius: 2px;
    max-width: 100%;
}
input[type="file"].admin-file-input::file-selector-button,
.admin-series-cover-block input[type="file"]::file-selector-button {
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    letter-spacing: 1px;
    color: var(--accent);
    background: transparent;
    border: 1px solid var(--accent-dim);
    border-radius: 2px;
    padding: 4px 10px;
    margin-right: 10px;
    cursor: pointer;
    text-transform: uppercase;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}
input[type="file"].admin-file-input::file-selector-button:hover,
.admin-series-cover-block input[type="file"]::file-selector-button:hover {
    background: var(--primary-glow);
    border-color: var(--accent);
    color: var(--primary);
}
.admin-form-actions {
    margin-top: 22px;
    display: flex;
    gap: 8px;
}
.admin-series-edit-aside {
    border-left: 1px solid var(--border);
    padding-left: 18px;
}
@media (max-width: 900px) {
    .admin-series-edit-aside {
        border-left: 0;
        border-top: 1px solid var(--border);
        padding-left: 0;
        padding-top: 14px;
    }
}
.admin-aside-heading {
    font-family: 'Share Tech Mono', monospace;
    font-size: 12px;
    letter-spacing: 1px;
    color: var(--primary);
    margin: 0 0 10px;
}
.admin-series-posts-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    gap: 8px;
    font-size: 13px;
}
.admin-series-posts-list li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 8px;
    align-items: baseline;
}
/* Title link (middle 1fr column) needs `min-width: 0` (via `minmax(0, 1fr)`
   above) so a long slug doesn't push the grid wider than the container —
   without it the date column gets squeezed and wraps "2026-06-23" into
   "2026-06-/23". Ellipsis-truncate over-long titles inside the column. */
.admin-series-posts-list li > a {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
/* Date never breaks. */
.admin-series-posts-list li > .admin-mono:last-child {
    white-space: nowrap;
}

/* ---------- Mobile mini-toolbar ----------
   Only renders on touch + viewport <= 768px, where EasyMDE is skipped
   to keep iOS IME (Vietnamese telex etc.) working. Sits directly above
   the raw <textarea>, sticky-scrolls with it. Each button is a square
   tap target on the terminal-chrome aesthetic. */
.mobile-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 6px 0;
    margin-bottom: 6px;
    /* Sticky to the top of the body field so the toolbar stays
       reachable while scrolling a long post. */
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--bg);
}
.mobile-toolbar-btn {
    flex: 0 0 auto;
    min-width: 38px;
    height: 38px;
    padding: 0 8px;
    background: var(--bg);
    color: var(--primary);
    border: 1px solid var(--border);
    border-radius: 2px;
    font-family: 'Share Tech Mono', monospace;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    /* Strip iOS button defaults (rounded blue ring, gradient bg). */
    -webkit-appearance: none;
    appearance: none;
    -webkit-tap-highlight-color: transparent;
}
.mobile-toolbar-btn:active {
    background: var(--primary-glow);
    border-color: var(--primary);
}

/* ---------- Mobile preview modal ----------
   Triggered by the 👁 toolbar button. Full-viewport overlay with an
   iframe that re-mounts the same stylesheet bundle the public post
   page uses, so the preview reads identical to the shipped post. */
.mobile-preview-modal {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: var(--bg);
    display: flex;
    flex-direction: column;
}
.mobile-preview-header {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    font-family: 'Share Tech Mono', monospace;
    font-size: 12px;
    letter-spacing: 2px;
    color: var(--accent);
}
.mobile-preview-close {
    background: transparent;
    border: 1px solid var(--accent-dim);
    color: var(--accent);
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    letter-spacing: 2px;
    padding: 6px 12px;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}
.mobile-preview-close:active { background: var(--primary-glow); }
.mobile-preview-iframe {
    flex: 1 1 auto;
    width: 100%;
    border: 0;
    background: var(--bg);
}

/* ---------- Admin confirm modal ----------
   Shared dialog rendered once via _confirm-modal.php and driven by
   `data-confirm="message"` on any form. Drops the native browser confirm
   in favour of a styled, theme-aware prompt matching the rest of the
   admin chrome. */
.admin-confirm-modal {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: center;
    animation: admin-confirm-fade 0.18s ease;
}
.admin-confirm-modal[hidden] { display: none; }

@keyframes admin-confirm-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.admin-confirm-modal-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.72);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.admin-confirm-modal-panel {
    position: relative;
    background: var(--panel);
    border: 1px solid var(--border);
    padding: 26px 28px 22px;
    width: min(480px, 92vw);
    box-shadow: 0 0 0 1px var(--primary-glow), 0 18px 50px rgba(0, 0, 0, 0.55);
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.admin-confirm-modal-tag {
    font-family: 'Share Tech Mono', monospace;
    font-size: 11px;
    letter-spacing: 0.12em;
    color: var(--primary);
    text-transform: uppercase;
}

.admin-confirm-modal-message {
    margin: 0;
    color: var(--text);
    font-size: 14px;
    line-height: 1.55;
}

.admin-confirm-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 4px;
}

/* ── /admin/login : tap-key button (admin-btn-primary variant) ──────────
 * Sits inline alongside [ BACK ] in `.admin-actions` — matches the
 * `[ LOG IN ]` / `[ BACK ]` shape of the password login form so the two
 * variants of /admin/login feel like the same screen.
 * `data-state="waiting"` swaps onto the phosphor-pulse animation while
 * the browser is waiting for the authenticator. JS toggles the attribute
 * around `navigator.credentials.get()`.
 */
.admin-btn-tap {
    white-space: nowrap;
}
.admin-btn-tap[data-state="waiting"] {
    animation: phosphor-pulse 1.4s ease-in-out infinite;
    pointer-events: none;
    opacity: 0.85;
}
@keyframes phosphor-pulse {
    50% { box-shadow: 0 0 24px var(--primary-glow); }
}

/* ── /admin/security : 1-line status summary (WEBAUTHN env state) ─── */
.admin-mode-summary {
    color: var(--text-dim);
    font-family: 'Share Tech Mono', monospace;
    font-size: 13px;
    margin: 12px 0 18px;
}
