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

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    background: white;
    color: #333;
}

.hidden {
    display: none !important;
}
/* Dev-only markup: hidden everywhere except the dev host, where app.js adds the
   `dev` class to <body>. Use for buttons/panels that must never appear in prod. */
.dev-only { display: none !important; }
body.dev .dev-only { display: revert !important; }

/* Top row, split in two: the date/greeting block on the left, the sign-in or
   signed-in controls on the right. The controls keep their own column stack
   inside #auth-actions, which is why the row itself doesn't set the gap. */
#auth-widget {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
}

#auth-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
    /* The right half owns the leftover width so #signed-in-widget's
       width:100% still right-aligns against the frame edge. */
    flex: 1;
    min-width: 0;
}

#date-block {
    font-size: 14px;
    color: #555;
    flex-shrink: 0;
}

#signin-btn {
    padding: 8px 18px;
    background: #007AFF;
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
}

#signin-btn:hover {
    background: #0056CC;
}

#signin-form {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}

#signin-form-inner {
    background: white;
    padding: 24px;
    border-radius: 12px;
    width: 320px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    box-shadow: 0 8px 32px rgba(0,0,0,0.25);
}

#signin-form-title {
    font-size: 18px;
    font-weight: 600;
    margin-bottom: 4px;
    color: #295A94;
}

#signin-form input {
    padding: 10px 12px;
    border: 1px solid #ccc;
    border-radius: 8px;
    font-size: 14px;
}

#signin-form #login-btn {
    padding: 10px;
    background: #007AFF;
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 14px;
    cursor: pointer;
}

#signin-form #login-btn:hover {
    background: #0056CC;
}

#login-error {
    color: red;
    font-size: 13px;
    margin: 0;
}

#signed-in-widget {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    justify-content: flex-end;
}

#user-name {
    font-size: 14px;
    font-weight: 500;
    color: #333;
}

#user-info {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
}

#user-avatar {
    width: 40px;
    height: 40px;
    background: #34c759;
    color: white;
    font-weight: 700;
    font-size: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    flex-shrink: 0;
}

#user-role {
    font-size: 13px;
    color: #888;
}

#logout-btn {
    padding: 6px 14px;
    background: #ff3b30;
    color: white;
    border: none;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
}

#logout-btn:hover {
    background: #d32f2f;
}

#admin-panel {
    display: flex;
    flex-direction: row;
    height: 100vh;
    padding: 16px;
    gap: 16px;
}

#admin-panel.hidden {
    display: none !important;
}

#sidebar-column {
    width: 220px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    flex-shrink: 0;
}

#brand-frame h1 {
    font-size: 20px;
    color: #295A94;
    font-weight: 700;
}

.sidebar-version {
    font-size: 12px;
    color: #888;
    margin-top: 4px;
}

#nav-frame {
    padding: 8px 0;
}

.sidebar-nav {
    display: flex;
    flex-direction: column;
    flex: 1;
}

.nav-icon {
    display: inline-block;
    width: 16px;
    text-align: center;
    margin-right: 6px;
    filter: grayscale(1);
}

.nav-btn {
    padding: 12px 20px;
    background: none;
    border: none;
    text-align: left;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
    color: #555;
    border-left: 3px solid transparent;
}

.nav-btn:hover {
    color: #295A94;
    background: #f0f5fa;
}

.nav-btn.active {
    color: #295A94;
    border-left-color: #295A94;
    background: #e8f0f8;
}

#content {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 12px;
    overflow-y: auto;
}

.content-frame {
    background: #f5f5f5;
    border-radius: 12px;
    padding: 14px 20px;
    flex-shrink: 0;
}

.content-frame-grow {
    flex: 1;
    overflow-y: auto;
}

/* Activity Logs table (SUPER-only Logs page). */
.logs-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.logs-table th {
    text-align: left; padding: 8px 10px; color: #6e6e73;
    font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em;
    border-bottom: 2px solid #e0e0e0;
}
.logs-table td { padding: 8px 10px; border-bottom: 1px solid #eee; vertical-align: top; }
.logs-when { white-space: nowrap; color: #6e6e73; }
.logs-user { white-space: nowrap; }

.logs-controls { display: flex; gap: 8px; align-items: center; }
/* Scoped to the CONTAINER, not to #logs-search: the Logs page now has two search
   boxes (All activity / Video decisions) and the Videos page has a third. Keying
   on the id styled only the first one and silently left the others bare. */
.logs-controls input[type="text"] {
    padding: 8px 12px; border: 1px solid #d0d0d0; border-radius: 8px;
    font-size: 14px; width: 240px; outline: none;
}
.logs-controls input[type="text"]:focus { border-color: #007AFF; }
/* Matches the text boxes beside it, but sized to its own content — a date field
   is a fixed width by nature, so the 240px of a search box leaves dead space. */
.logs-controls input[type="date"] {
    padding: 7px 12px; border: 1px solid #d0d0d0; border-radius: 8px;
    font-size: 14px; font-family: inherit; color: #333;
    background: #fff; outline: none;
}
.logs-controls input[type="date"]:focus { border-color: #007AFF; }
.logs-controls button {
    padding: 8px 14px; border: none; border-radius: 8px;
    background: #e0e0e0; color: #333; cursor: pointer; font-size: 14px;
}
.logs-controls button:hover { background: #d0d0d0; }

.logs-pagination {
    display: flex; align-items: center; justify-content: center;
    gap: 16px; margin-top: 16px;
}
.logs-pagination button {
    padding: 6px 14px; border: none; border-radius: 8px;
    background: #007AFF; color: #fff; cursor: pointer; font-size: 14px;
}
.logs-pagination button:disabled { background: #ccc; cursor: default; }
.logs-page-info { color: #6e6e73; font-size: 13px; }

/* ---- Monitor (admin video decisions) ---- */
.monitor-outcome {
    display: inline-block;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.02em;
    padding: 2px 8px;
    border-radius: 4px;
    text-transform: uppercase;
}
/* Approve/reject carry the same colours as the moderation buttons, so the
   decision reads the same here as it did when it was made. */
.monitor-outcome.approved { background: #e9f8ee; color: #1c7d3b; }
.monitor-outcome.rejected { background: #fff3f3; color: #8a1c1c; }

.monitor-queue { text-transform: capitalize; color: #6e6e73; white-space: nowrap; }

/* Pager inside a queue column — compact, since the column is only 320px. */
.videos-list-col .logs-pagination { margin-top: 10px; gap: 8px; }
.videos-list-col .logs-pagination button { padding: 4px 10px; font-size: 13px; }
.videos-list-col .logs-page-info { font-size: 12px; }

/* The Rejected page has no actions, so its third column is an empty frame kept
   for layout consistency with Videos. */
#rejected-page .videos-action-col { background: transparent; border-style: dashed; }

/* Video ids are long and uninteresting until you need to copy one — truncate,
   with the full value in a tooltip. */
.monitor-key {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
    color: #6e6e73;
    max-width: 240px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---- Monitor: expandable decision rows ----
   Each decision is a summary <tr> plus a detail <tr> that starts hidden. The
   metadata is captured at decision time (see recordDecision in app.js), so
   expanding is pure DOM work — no fetch, nothing to wait for, hence no loading
   state anywhere below. */
.monitor-row { cursor: pointer; }
.monitor-row:hover { background: #fafafa; }
/* Narrow gutter holding the disclosure. Fixed width so the When column starts at
   the same x whether a row is open or closed. */
.monitor-table .monitor-expand { width: 28px; padding-right: 0; }
.monitor-expand-btn {
    display: block;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: #b0b0b5;
    line-height: 1;
}
.monitor-expand-btn:focus-visible { outline: 2px solid #007AFF; outline-offset: 2px; border-radius: 3px; }
/* Rotated on the span, not the cell: a <td> is transformable per spec, but
   turning the whole cell also turns its padding box, and the inner span keeps
   the rotation to the glyph. */
.monitor-chevron {
    display: inline-block;
    font-size: 15px;
    transition: transform 0.12s ease;
}
.monitor-row.expanded .monitor-chevron { transform: rotate(90deg); }
.monitor-row.expanded .monitor-expand-btn { color: #6e6e73; }

/* Label for the disclosure button, which is a bare glyph on screen. */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
/* No bottom border: the detail row below belongs to this row, and a line between
   them would read as a separate record. */
.monitor-row.expanded td { border-bottom: none; }

.monitor-detail-row > td {
    background: #fafafa;
    padding: 12px 10px 14px 34px;   /* left pad clears the chevron gutter */
}
/* Two columns while there's room, one when there isn't. auto-fit rather than a
   fixed count so a decision with only three fields doesn't leave a dead column. */
.monitor-detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 6px 24px;
}
/* Values here are free text from the moderation record — a long reporter list or
   a profanity match list must wrap inside its cell, not stretch the table. */
.monitor-detail-grid .report-row > span:last-child {
    overflow-wrap: anywhere;
    min-width: 0;
}
.monitor-detail-empty { margin: 0; font-size: 13px; color: #8e8e93; font-style: italic; }
/* Provenance note under a reconstructed record — quiet, but always present, so a
   backfilled row is never mistaken for one captured at decision time. */
.monitor-detail-note {
    margin: 10px 0 0;
    font-size: 12px;
    color: #8e8e93;
    max-width: 70ch;
}

/* Shown only when log entries that look like moderation fail to parse — a format
   drift should be visible, not silent. */
.callout-inline {
    background: #fff8e6;
    border: 1px solid #f5e2a8;
    color: #5c4a12;
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 13px;
    margin-bottom: 12px;
}

/* Signed-out: keep the light-grey segments visible but empty their contents.
   Only the Sign In button (inside #auth-widget) remains. */
body.signed-out #brand-frame > *,
body.signed-out #nav-frame > *,
body.signed-out #ticker-frame > *,
body.signed-out #page-frame > * {
    display: none !important;
}

/* The date/greeting lives in the top row now, alongside the Sign In button, so
   it needs its own signed-out rule — the #auth-widget subtree is deliberately
   left visible by the rule above. */
body.signed-out #date-block {
    display: none !important;
}

#ticker-frame {
    overflow: hidden;
    padding: 10px 0;
    background: #295A94;
}

.ticker-wrapper {
    overflow: hidden;
    width: 100%;
}

.ticker-content {
    display: inline-block;
    white-space: nowrap;
    /* 25s per COPY, not per pass of the whole element — the element holds
       --ticker-copies of the same line and the animation advances by exactly one
       of them. Raise for slower. Unlike the previous versions this speed doesn't
       drift with the window width: the travel is one copy of the text, which is
       a fixed width, so px/second is the same on any screen.

       Was 20s; 25s is the same travel over a quarter more time, i.e. 20% slower. */
    animation: ticker-scroll 25s linear infinite;
    font-size: 13px;
    color: white;
    font-weight: 700;
}

/* Advance by ONE COPY of the line, so the frame at 100% is pixel-identical to
   the frame at 0% — copy 2 has arrived exactly where copy 1 started — and the
   restart is invisible. That is what makes the stream continuous.

   The previous attempts both failed the same way: with a single copy of the text
   the element is narrower than the distance it travels, so there is always a
   stretch of empty banner trailing it, however the keyframes are arranged.
   Repetition is the only fix; the copies are added in app.js, which also sets
   --ticker-copies so the two can't disagree. */
@keyframes ticker-scroll {
    0% { transform: translateX(0); }
    100% { transform: translateX(calc(-100% / var(--ticker-copies, 3))); }
}

/* Second line under the date: "Good afternoon, Sean". Its own line and a shade
   darker than the date — it's addressed to a person, so it reads as the greeting
   rather than as more timestamp. */
.admin-greeting {
    display: block;
    margin-top: 2px;
    font-size: 15px;
    font-weight: 600;
    color: #333;
}

.page {
    display: block;
}

.subtitle {
    color: #888;
    margin-top: 4px;
}

.dashboard-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

/* Its own row under "Reported on {date}", centered. Wraps rather than squeezing:
   four labels this long won't fit one line on a narrow window, and a wrapped row
   of whole buttons beats four truncated ones. */
/* Four equal columns spanning the full row, so the first button's left edge and
   the last button's right edge land exactly on the edges of the .stats-grid
   below, with one identical gap between them.
   A grid rather than flex with space-between: that would flush the ends and
   space the gaps equally too, but the buttons would keep their content widths —
   "QUARTERLY - drive strategy" far wider than "DAILY - direct problems" — and
   nothing would line up with the cards. Equal tracks give equal buttons.
   `gap` matches .stats-grid's 10px so the two rows read as one column system.
   minmax(0, 1fr) rather than 1fr: a plain 1fr floors at the content's min width,
   which on a narrow window would push the row wider than the cards. */
.range-tabs {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 10px;
    margin-top: 12px;
}

.range-btn {
    /* flex, not inline-flex: the grid track sets the width, and the content
       centres inside whatever width it gets. */
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 13px 22px;
    background: white;
    color: #295A94;
    border: 1px solid #ccc;
    border-radius: 10px;
    font-size: 15px;
    font-weight: 500;
    cursor: pointer;
}

.range-btn .range-icon {
    font-size: 14px;
    line-height: 1;
}

/* The cadence carries the weight; what it's for sits alongside it, lighter, so
   the four buttons stay scannable by DAILY / WEEKLY / … at a glance. */
.range-btn-key {
    font-weight: 700;
    letter-spacing: 0.04em;
}

.range-btn-desc {
    font-weight: 400;
    opacity: 0.75;
}

.range-btn.active {
    background: #295A94;
    color: white;
    border-color: #295A94;
}

/* A GRID, not a wrapping flex row, so every metric card is the same size.
   Under flex each card sized to its own content: "Cancellations" came out narrow
   and "Daily Active Users Streaming" wide, and a card whose delta was blank sat
   shorter than its neighbours.
   - auto-fill + minmax gives identical column widths, and fills the row with
     TRACKS rather than dividing it between the cards present — so the two cards
     on Monthly are the same size as the eight on Daily instead of stretching to
     half the page each.
   - grid-auto-rows pins the row height, so a label that wraps to two lines no
     longer makes its card taller than the rest. `auto` is the upper bound: at a
     very narrow window a three-line label grows its row rather than clipping. */
.stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(175px, 1fr));
    grid-auto-rows: minmax(116px, auto);
    gap: 10px;
    margin-top: 12px;
}

.stat-card {
    background: white;
    border-radius: 8px;
    padding: 12px 16px;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Number + delta + label as one block in the middle of a fixed-height card,
       so cards with a blank delta still line up with the rest. */
    justify-content: center;
    text-align: center;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    /* The track decides the width now. A floor of 140px would overflow its track
       on a narrow window, where auto-fill drops to a single column. */
    min-width: 0;
    cursor: pointer;
    transition: transform 0.1s ease, box-shadow 0.1s ease, border 0.1s ease;
    border: 2px solid transparent;
}

.stat-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 3px 8px rgba(0,0,0,0.12);
}

.stat-card.selected {
    border-color: #295A94;
}

.stats-grid .stat-number {
    font-size: 24px;
}

.stat-delta {
    font-size: 12px;
    font-weight: 600;
    margin-top: 2px;
    color: #888;
}

.stat-delta.positive {
    color: #34c759;
}

.stat-delta.negative {
    color: #ff3b30;
}

.chart-panel {
    margin-top: 16px;
    background: white;
    border-radius: 10px;
    padding: 16px 20px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.chart-panel-title {
    font-size: 15px;
    font-weight: 600;
    color: #295A94;
}

.chart-canvas-wrapper {
    position: relative;
    height: 280px;
}

#alt-view {
    margin-top: 12px;
    background: white;
    border-radius: 10px;
    padding: 20px;
    min-height: 320px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.alt-subview.hidden {
    display: none;
}

.stat-number {
    font-size: 36px;
    font-weight: 700;
}

.stat-label {
    font-size: 14px;
    color: #888;
    margin-top: 4px;
}

#conversations-list {
    margin-top: 16px;
}

.user-card {
    background: white;
    border-radius: 10px;
    padding: 16px 20px;
    margin-bottom: 16px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.user-card-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}

.user-card-header h3 {
    font-size: 16px;
}

.user-card-header .user-id {
    font-size: 12px;
    color: #aaa;
    font-family: monospace;
}

.conversation {
    margin-top: 12px;
    background: #f9f9f9;
    border-radius: 8px;
    border-left: 3px solid #007AFF;
    overflow: hidden;
}

.conversation-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    padding: 12px;
    cursor: pointer;
    user-select: none;
}

.conversation-header:hover {
    background: #f0f0f0;
}

.replied-badge {
    font-size: 12px;
    font-weight: 700;
    color: #34c759;
    margin-right: 8px;
}

.conversation-id {
    font-size: 11px;
    color: #aaa;
    font-family: monospace;
}

.conversation-meta {
    font-size: 12px;
    color: #888;
}

.chevron {
    display: inline-block;
    transition: transform 0.2s;
    font-size: 12px;
}

.conversation.expanded .chevron {
    transform: rotate(90deg);
}

.conversation-body {
    display: none;
    padding: 0 12px 12px;
}

.conversation.expanded .conversation-body {
    display: block;
}

.message {
    padding: 6px 0;
    font-size: 14px;
    border-bottom: 1px solid #eee;
}

.message:last-child {
    border-bottom: none;
}

.message-prefix {
    font-weight: 600;
    margin-right: 4px;
}

.message-prefix.reply {
    color: #007AFF;
}

.message-prefix.sent {
    color: #34c759;
}

.message-prefix.admin {
    color: #ff9500;
}

.message-prefix.closed {
    color: #ff3b30;
}

.message-time {
    font-size: 11px;
    color: #aaa;
    margin-left: 8px;
}

.loading-text {
    color: #888;
    font-style: italic;
}

.reply-box {
    display: flex;
    gap: 8px;
    margin-top: 10px;
}

.reply-input {
    flex: 1;
    padding: 8px 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 14px;
}

.reply-btn {
    padding: 8px 16px;
    background: #007AFF;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

.reply-btn:hover {
    background: #0056CC;
}

.reply-btn:disabled {
    background: #999;
    cursor: default;
}

.admin-add-box {
    display: flex;
    gap: 8px;
    margin-top: 16px;
    margin-bottom: 12px;
}

.admin-add-box input {
    flex: 1;
    padding: 10px 12px;
    border: 1px solid #ccc;
    border-radius: 6px;
    font-size: 14px;
    max-width: 400px;
}

#add-admin-btn {
    padding: 10px 20px;
    background: #007AFF;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

#add-admin-btn:hover {
    background: #0056CC;
}

#admin-message {
    font-size: 14px;
    margin-bottom: 12px;
}

.admin-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: white;
    padding: 12px 16px;
    border-radius: 8px;
    margin-bottom: 8px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.admin-info {
    display: flex;
    flex-direction: column;
}

.admin-name {
    font-weight: 600;
}

.admin-email {
    font-size: 13px;
    color: #888;
}

.admin-role {
    font-size: 11px;
    font-weight: 600;
    background: #295A94;
    color: white;
    padding: 2px 8px;
    border-radius: 4px;
    margin-left: 8px;
}

#admin-role-select {
    padding: 10px 12px;
    border: 1px solid #ccc;
    border-radius: 6px;
    font-size: 14px;
}

/* In-row role switcher. Sized to read as the .admin-role pill it replaces, so a
   row where the role can be changed doesn't jump relative to one where it can't. */
.admin-role-switch {
    font-size: 11px;
    font-weight: 600;
    color: #295A94;
    background: #eef2f8;
    border: 1px solid #c3d8ff;
    border-radius: 4px;
    padding: 2px 6px;
    margin-left: 8px;
    cursor: pointer;
}

.admin-role-switch:disabled {
    opacity: 0.6;
    cursor: default;
}

.admin-uid {
    font-size: 11px;
    color: #aaa;
    font-family: monospace;
}

.remove-admin-btn {
    padding: 6px 14px;
    background: #ff3b30;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 13px;
}

.remove-admin-btn:hover {
    background: #d32f2f;
}

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: 12px 0;
}

.page-btn {
    padding: 8px 16px;
    background: #007AFF;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

.page-btn:hover:not(:disabled) {
    background: #0056CC;
}

.page-btn:disabled {
    background: #ccc;
    cursor: default;
}

.page-info {
    font-size: 14px;
    color: #888;
}

.user-card.report-expandable {
    padding: 0;
    overflow: hidden;
}

.report-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 20px;
    cursor: pointer;
    user-select: none;
}

.report-header:hover {
    background: #f9f9f9;
}

.report-summary {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 14px;
}

.report-date {
    color: #888;
    font-size: 13px;
}

.report-body {
    padding: 0 20px 16px;
}

.user-card.report-expandable .report-body {
    display: none;
}

.user-card.report-expandable.expanded .report-body {
    display: block;
}

.user-card.report-expandable .chevron {
    transition: transform 0.2s;
}

.user-card.report-expandable.expanded .chevron {
    transform: rotate(90deg);
}

.report-video {
    width: 30%;
    max-height: 250px;
    border-radius: 8px;
    background: #000;
    margin-bottom: 12px;
}

.report-card {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.report-row {
    display: flex;
    gap: 12px;
    font-size: 14px;
    align-items: baseline;
}

.report-label {
    font-weight: 600;
    color: #888;
    min-width: 110px;
    flex-shrink: 0;
}

/* The vendor website row. A URL has no spaces to break on and the detail column
   is only so wide, so let it wrap mid-string rather than push the pane out.
   min-width:0 is what actually allows a flex item to shrink below its content. */
.report-row a {
    color: #007AFF;
    overflow-wrap: anywhere;
    min-width: 0;
}

.report-category {
    background: #fff3e0;
    color: #e65100;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 13px;
    font-weight: 500;
}

.report-count {
    background: #ff3b30;
    color: white;
    font-size: 12px;
    font-weight: 700;
    min-width: 22px;
    height: 22px;
    border-radius: 11px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 6px;
}

.report-entries {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 12px;
}

.report-entry {
    background: #f9f9f9;
    border-radius: 8px;
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    border-left: 3px solid #e65100;
}

.report-actions {
    display: flex;
    gap: 10px;
    margin-top: 16px;
}

.report-dismiss-btn {
    padding: 8px 24px;
    background: #34c759;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}

.report-dismiss-btn:hover:not(:disabled) {
    background: #2da84e;
}

.report-reject-btn {
    padding: 8px 24px;
    background: #ff3b30;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}

.report-reject-btn:hover:not(:disabled) {
    background: #d32f2f;
}

.report-dismiss-btn:disabled,
.report-reject-btn:disabled {
    background: #999;
    cursor: default;
}

/* ---- Videos page: three-column moderation layout ----
   col 1: the queues (International / Reported / Quarantined), stacked and
          scrolling as one list
   col 2: the selected video + its metadata
   col 3: the moderation actions + the video's owner
   The columns each scroll independently so a long queue never pushes the
   player or the Approve/Reject buttons off-screen. */
.videos-layout {
    display: grid;
    grid-template-columns: 320px minmax(0, 1fr) 280px;
    gap: 16px;
    align-items: start;
    /* Fill the viewport below the header so the columns scroll, not the page. */
    height: calc(100vh - 150px);
}

.videos-col {
    background: white;
    border-radius: 10px;
    border: 1px solid #e5e5e5;
    height: 100%;
    overflow-y: auto;
    padding: 12px;
}

/* One queue entry. Selecting it fills columns 2 and 3. */
.video-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 10px;
    border-radius: 8px;
    cursor: pointer;
    user-select: none;
    border: 1px solid transparent;
}

.video-row:hover {
    background: #f6f6f8;
}

.video-row.active {
    background: #eef4ff;
    border-color: #c3d8ff;
}

.video-row-thumb {
    width: 40px;
    height: 40px;
    object-fit: cover;
    border-radius: 4px;
    background: #eee;
    flex-shrink: 0;
}

.video-row-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.video-row-title {
    font-size: 13.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Second line of a row: queue-specific pills (source country, approval score)
   followed by location · category · date. Truncates rather than wrapping, so
   every row stays one fixed height and the list scans vertically. */
.video-row-meta {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    font-size: 12px;
    color: #888;
}

.video-row-metatext {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.video-row-pill {
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.02em;
    padding: 1px 6px;
    border-radius: 4px;
    background: #eef1f6;
    color: #4a5568;
}

/* Line 3: why this video is in the queue. Colour-coded by source so a scan of
   the list separates user reports from automated quarantines without reading. */
.video-row-reason {
    font-size: 12px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.video-row-reason.reported {
    color: #c0392b;
}

.video-row-reason.quarantined {
    color: #b9770e;
}

/* Rejected with no admin in the loop — one of the backend paths. Cool blue
   against the two warm "a human was involved" colours above. */
.video-row-reason.automatic {
    color: #3f6fa8;
}

.videos-empty,
.videos-col-empty {
    color: #888;
    font-size: 13.5px;
    padding: 4px 6px;
}

.video-detail-player video {
    width: 100%;
    max-height: 60vh;
    border-radius: 8px;
    background: #000;
}

.video-detail-meta {
    margin-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.video-detail-owner {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* The column is ~280px, so the label/value pair that reads fine in column 2
   would leave the value a narrow, wrapping strip. Stack them instead. */
.video-detail-owner .report-row {
    flex-direction: column;
    gap: 2px;
}

.video-detail-owner .report-label {
    min-width: 0;
}

.video-detail-owner .report-row span:last-child {
    overflow-wrap: anywhere;
}

.video-detail-actions {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 16px;
}

/* Full-width in their column, unlike the side-by-side pair on the old card. */
.video-detail-actions button {
    width: 100%;
}

/* Below ~1100px the three columns stop fitting; stack them and let the page
   scroll instead of three separate panes. */
@media (max-width: 1100px) {
    .videos-layout {
        grid-template-columns: 1fr;
        height: auto;
    }
    .videos-col {
        height: auto;
        max-height: 60vh;
    }
}

/* ---- Normalization review ---- */
.norm-subtitle {
    color: #888;
    font-size: 14px;
    margin: 0 0 16px;
    max-width: 680px;
}

.norm-autoapply {
    display: flex;
    align-items: center;
    gap: 12px;
    background: white;
    border-radius: 10px;
    padding: 14px 16px;
    margin-bottom: 16px;
}

.norm-toggle-label {
    font-weight: 600;
    color: #333;
}

.norm-toggle {
    position: relative;
    width: 46px;
    height: 26px;
    border-radius: 13px;
    border: none;
    cursor: pointer;
    transition: background 0.15s;
    padding: 0;
}

.norm-toggle.on { background: #34c759; }
.norm-toggle.off { background: #ccc; }

.norm-toggle-knob {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: white;
    transition: transform 0.15s;
}

.norm-toggle.on .norm-toggle-knob { transform: translateX(20px); }

.norm-toggle-state {
    color: #888;
    font-size: 13px;
}

.norm-card {
    padding: 16px;
}

.norm-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.norm-field-tag {
    background: #e8f0fe;
    color: #1a73e8;
    text-transform: capitalize;
    font-size: 12px;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 11px;
}

.norm-raw {
    font-weight: 600;
    color: #333;
}


.norm-arrow {
    color: #888;
}

.norm-input {
    flex: 1;
    min-width: 160px;
    padding: 8px 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 14px;
}

.norm-meta {
    display: flex;
    gap: 16px;
    color: #888;
    font-size: 13px;
    margin-top: 10px;
}

.norm-actions {
    display: flex;
    gap: 10px;
    margin-top: 16px;
}

/* Dark green, not the bright #34c759 — the same #1c7d3b / #15602d pair as
   .notify-send and .qr-actions button, which is the portal's "approved/active"
   colour (it started as .monitor-outcome.approved). Map is the one button on this
   page that commits: it writes an alias the normalizer applies to every FUTURE
   upload and rewrites every affected video, so it should read as the same weight
   of action as sending a broadcast, not as a light confirm. */
.norm-approve-btn {
    padding: 8px 24px;
    background: #1c7d3b;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}

.norm-approve-btn:hover:not(:disabled) {
    background: #15602d;
}

.norm-reject-btn {
    padding: 8px 24px;
    background: #ff3b30;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}

.norm-reject-btn:hover:not(:disabled) {
    background: #d32f2f;
}

.norm-keep-btn {
    padding: 8px 24px;
    background: #1a73e8;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}

.norm-keep-btn:hover:not(:disabled) {
    background: #1666c9;
}

.norm-approve-btn:disabled,
.norm-reject-btn:disabled,
.norm-keep-btn:disabled {
    background: #999;
    cursor: default;
}

/* Users page — verification-code lookup result card. */
#user-lookup-btn {
    padding: 10px 20px;
    background: #007AFF;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#user-lookup-btn:hover { background: #0056CC; }

/* Classify (manual classifyBySpeech trigger) — the portal's dark green
   (#1c7d3b / #15602d), as used by .notify-send, .qr-actions button and
   .licence-toggle-btn one section below on this same page. Was the brighter
   #34c759; both green buttons on Dev Tools were moved together so the page has
   one green rather than two. */
#classify-run-btn {
    padding: 10px 20px;
    background: #1c7d3b;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#classify-run-btn:hover:not(:disabled) { background: #15602d; }
#classify-run-btn:disabled { background: #c7c7cc; cursor: default; }

/* User management's single Lookup button — the house green (#1c7d3b/#15602d), as
   .notify-send, #internal-lookup-btn, #classify-run-btn and .norm-approve-btn.
   The reset buttons below it stay green/red as the strike actions they mirror.

   REPLACES #rejected-lookup-btn AND #videocounter-lookup-btn, which were two
   separately-styled buttons (one blue, one green) for two segments. Those
   segments merged into one, and both rules styled ids that no longer exist in the
   markup — so the merged button was rendering as a browser default until this
   rule named it. Styling by id is why: renaming the element silently dropped its
   appearance, with nothing to fail. */
#um-lookup-btn {
    padding: 10px 20px;
    background: #1c7d3b;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#um-lookup-btn:hover { background: #15602d; }

/* Same red/yellow and wording as the strike actions in the registered-users
   detail column (.user-detail-reset / .user-detail-prewarn) — same two actions,
   so they should look the same. The labels are too long to sit side by side,
   hence the column. */
#rejected-actions {
    flex-direction: column;
    align-items: stretch;
    max-width: 240px;
}
#rejected-full-reset-btn,
#rejected-prewarning-reset-btn {
    padding: 10px 14px;
    color: #fff;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 600;
}
#rejected-full-reset-btn { background: #d93025; }
#rejected-full-reset-btn:hover:not(:disabled) { background: #b3261e; }
/* Yellow needs dark text to stay legible. */
#rejected-prewarning-reset-btn { background: #f5b400; color: #3d2f00; }
#rejected-prewarning-reset-btn:hover:not(:disabled) { background: #d99e00; }
/* Faded rather than grey, again matching .user-detail-actions. */
#rejected-full-reset-btn:disabled,
#rejected-prewarning-reset-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ---- Users page: Video counter segment ----
   Dark green looks up, blue acts — the same assignment as the Internal accounts
   page below, NOT the rejected-counter segment above where blue looks up and
   red/yellow act. The colours differ by page on purpose; see the note under
   "Internal accounts page". Both pairs here are the portal's existing ones:
   #1c7d3b/#15602d (as .notify-send, #internal-lookup-btn) and #007AFF/#0056CC
   (as #user-lookup-btn, .stats-csv-btn).

   Blue rather than red for Add 5 / Reset because neither is destructive: they
   GIVE a user uploads back. The red/yellow on the segment above is borrowed from
   the strike actions, which take something away. */
#videocounter-add5-btn,
#videocounter-reset-btn {
    padding: 10px 20px;
    background: #007AFF;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#videocounter-add5-btn:hover:not(:disabled),
#videocounter-reset-btn:hover:not(:disabled) { background: #0056CC; }
/* Solid grey, matching every other blue action button that can be disabled
   (#classify-run-btn, #docs-save-btn) rather than the faded treatment used on
   the segment above. These two spend a once-per-cycle grant, so "unavailable"
   should read as a state rather than as a dimmed version of available. */
#videocounter-add5-btn:disabled,
#videocounter-reset-btn:disabled { background: #c7c7cc; cursor: default; }

/* ---- Internal accounts page ---- */
/* Note the colour assignment differs from the rejected-counter page above:
   there, blue looks up and red/yellow act (matching the users detail column).
   Here green looks up and blue acts, as specified — so don't "fix" one to match
   the other by eye. */
#internal-email {
    flex: 1;
    padding: 10px 14px;
    border: 1px solid #d0d0d0;
    border-radius: 8px;
    font-size: 14px;
    max-width: 420px;
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
}
/* Ring matches this page's accent button (#internal-lookup-btn), the same way
   #notify-body:focus matches .notify-send. Moved to dark green with the button —
   leaving it bright would have orphaned the one green element on the page. */
#internal-email:focus {
    border-color: #1c7d3b;
    box-shadow: 0 0 0 3px rgba(28, 125, 59, 0.15);
}
#internal-email::placeholder { color: #aaa; }

/* The portal's dark green (#1c7d3b / #15602d) — same pair as .notify-send,
   .qr-actions button, #classify-run-btn and .norm-approve-btn. Was #34c759. */
#internal-lookup-btn {
    padding: 10px 22px;
    background: #1c7d3b;
    color: #fff;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}
#internal-lookup-btn:hover { background: #15602d; }

#internal-mark-btn,
#internal-unmark-btn {
    padding: 10px 22px;
    background: #007AFF;
    color: #fff;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}
#internal-mark-btn:hover:not(:disabled),
#internal-unmark-btn:hover:not(:disabled) { background: #0056CC; }
/* Disabled is the RESTING state for one of these two — whichever direction the
   account is already in — so it has to read as "not applicable", not "broken". */
#internal-mark-btn:disabled,
#internal-unmark-btn:disabled {
    background: #c7c7cc;
    cursor: default;
}

.user-lookup-card {
    background: white;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    padding: 16px 20px;
    margin-top: 12px;
    max-width: 520px;
}
.user-lookup-card .name { font-weight: 600; font-size: 15px; }
.user-lookup-card .email { color: #666; font-size: 14px; margin-top: 2px; }
.user-lookup-card .uid { color: #999; font-size: 12px; font-family: monospace; margin-top: 2px; }
.user-lookup-card .code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 32px;
    letter-spacing: 8px;
    color: #007AFF;
    margin: 14px 0 8px;
}
.user-lookup-card .status-active  { color: #0a7d2d; font-size: 13px; }
.user-lookup-card .status-expired { color: #b00020; font-size: 13px; }
.user-lookup-card .muted          { color: #888; font-size: 13px; }

/* Video counter eligibility — a user gets ONE reduction per billing cycle, and
   whether this one is still owed a grant is the single thing the admin is there
   to find out. A coloured sentence was not enough: it reads as commentary next to
   the other status lines, and the answer has to be legible before anything is
   read. So it is a filled badge, and BOTH states get one — absence of a badge
   would otherwise be a third, silent state the reader has to interpret.
   The state is derived from `adminAdjustedAt` stored on the document, so it
   survives a logout, another admin, or a different machine. */
.user-lookup-card .eligibility {
    display: inline-block;
    margin-top: 12px;
    padding: 4px 11px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: #fff;
}
.user-lookup-card .eligibility.yes { background: #1c7d3b; }
.user-lookup-card .eligibility.no  { background: #b00020; }
#user-lookup-result .error { color: #b00020; margin-top: 12px; }
#user-lookup-result .loading { color: #666; margin-top: 12px; }

/* Docs — rich-text editor */
.docs-tabs { display: flex; gap: 8px; margin-bottom: 14px; }
.docs-tab {
    padding: 8px 18px;
    border: 1px solid #ddd;
    background: #f5f5f5;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    color: #555;
}
.docs-tab:hover { background: #ececec; }
.docs-tab.active { background: #007AFF; color: #fff; border-color: #007AFF; }

/* Videos page pills: Manage / Rejected, with the Rejected filter box pushed to
   the right of the same row. */
.videos-tabs { align-items: center; }
.videos-filter { margin-left: auto; }
.videos-filter input[type="text"] { width: 260px; }
/* Manage-only source filter, right-aligned on the same row as the mode pills
   and in the same spot the other two modes put their search box — only one of
   the three is ever visible. Its buttons reuse .docs-tab so the active one
   reads the same as an active mode pill. */
.videos-queue-filter { display: flex; gap: 8px; margin-left: auto; }

/* Logs page pills: All activity / Video decisions, with each view's own search
   box pushed to the right of the same row (only one is visible at a time). */
.logs-tabs { align-items: center; }
.logs-filter { margin-left: auto; }
.docs-layout { display: flex; gap: 16px; align-items: flex-start; }
.docs-sidebar { width: 220px; flex-shrink: 0; }
.docs-language-select {
    width: 100%;
    padding: 8px;
    margin-bottom: 8px;
    border: 1px solid #ccc;
    border-radius: 6px;
    font-size: 14px;
    background: #fff;
    color: #333;
    cursor: pointer;
}
#docs-new-btn {
    width: 100%;
    padding: 10px;
    background: #007AFF;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    margin-bottom: 12px;
}
#docs-new-btn:hover { background: #0056CC; }
.docs-list { display: flex; flex-direction: column; gap: 4px; }
.docs-list-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 10px;
    background: #f5f5f5;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 14px;
    color: #333;
}
.docs-list-item:hover { background: #ececec; }
.docs-list-item.active { background: #007AFF; color: #fff; border-color: #007AFF; }
.docs-list-item.active .docs-list-title { color: #fff; }
.docs-list-title {
    flex: 1;
    min-width: 0;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.docs-del-btn {
    flex-shrink: 0;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    padding: 2px 4px;
    opacity: 0.7;
}
.docs-del-btn:hover { opacity: 1; }
.docs-restore-btn {
    flex-shrink: 0;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 15px;
    line-height: 1;
    padding: 2px 4px;
    opacity: 0.7;
    color: #007AFF;
}
.docs-restore-btn:hover { opacity: 1; }
.docs-show-hidden {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: #666;
    margin-bottom: 10px;
    cursor: pointer;
}
.docs-publish { margin-top: 14px; }
/* Dark green (#1c7d3b / #15602d), the portal's publish/commit colour — see
   .notify-send. Publishing rewrites what every app fetches, so it carries the
   same weight as sending a broadcast. Was #34c759; #terms-publish-btn below
   moved with it, since the two do the same thing to different content. */
#docs-publish-btn {
    width: 100%;
    padding: 9px;
    background: #1c7d3b;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#docs-publish-btn:hover:not(:disabled) { background: #15602d; }
#docs-publish-btn:disabled { background: #c7c7cc; cursor: default; }
.docs-publish-status { font-size: 12px; color: #666; margin: 6px 0 0; }
.docs-list-item.docs-deleted { background: #fafafa; border-style: dashed; color: #999; }
.docs-list-item.docs-deleted .docs-list-title { color: #999; font-style: italic; }
.docs-list-divider {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #aaa;
    margin: 10px 0 2px;
}
.docs-empty { color: #888; font-size: 13px; }
.docs-main { flex: 1; min-width: 0; }
.docs-title {
    width: 100%;
    padding: 10px;
    font-size: 18px;
    font-weight: 600;
    border: 1px solid #ddd;
    border-radius: 6px;
    margin-bottom: 12px;
    box-sizing: border-box;
}
.docs-version-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}
.docs-version-label {
    font-size: 13px;
    font-weight: 600;
    color: #666;
}
.docs-version-bar select {
    padding: 6px 10px;
    border: 1px solid #ccc;
    background: #fff;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    color: #333;
}
.docs-showlogin-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    color: #444;
    cursor: pointer;
    margin-left: auto;
}
.docs-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    padding: 8px;
    background: #f5f5f5;
    border: 1px solid #ddd;
    border-bottom: none;
    border-radius: 8px 8px 0 0;
}
.docs-toolbar button,
.docs-toolbar select {
    padding: 6px 10px;
    border: 1px solid #ccc;
    background: #fff;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    color: #333;
}
.docs-toolbar button:hover,
.docs-toolbar select:hover { background: #ececec; }
.docs-color-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: #fff;
    cursor: pointer;
    font-size: 14px;
    color: #333;
}
.docs-color-label input[type="color"] {
    width: 22px;
    height: 22px;
    border: none;
    padding: 0;
    background: none;
    cursor: pointer;
}
.docs-editor {
    min-height: 420px;
    max-height: 65vh;
    overflow-y: auto;
    border: 1px solid #ddd;
    border-radius: 0 0 8px 8px;
    padding: 16px;
    background: #fff;
    font-size: 15px;
    line-height: 1.6;
    color: #222;
}
.docs-editor:focus { outline: none; }
/* Archived (non-current) versions open read-only. */
.docs-editor.docs-readonly {
    background: #f7f7f7;
    color: #555;
}
.docs-actions {
    margin-top: 14px;
    display: flex;
    align-items: center;
    gap: 14px;
}
#docs-save-btn {
    padding: 10px 28px;
    background: #007AFF;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 15px;
}
#docs-save-btn:hover:not(:disabled) { background: #0056CC; }
#docs-save-btn:disabled { background: #c7c7cc; color: #fff; cursor: default; }
#docs-message { color: #666; font-size: 13px; }

/* ---- Global busy overlay (Cloud Function calls in flight) ---- */
.busy-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.55);
    -webkit-backdrop-filter: blur(1px);
    backdrop-filter: blur(1px);
    cursor: progress;
}
.busy-overlay.hidden { display: none; }
.busy-spinner {
    width: 48px;
    height: 48px;
    border: 4px solid rgba(0, 0, 0, 0.12);
    border-top-color: #1a73e8;
    border-radius: 50%;
    animation: busy-spin 0.8s linear infinite;
}
@keyframes busy-spin { to { transform: rotate(360deg); } }

/* ---- Terms page (single-doc editor; reuses .docs-* styles) ---- */
.terms-topbar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}
/* Same dark green as #docs-publish-btn — same action, different content. */
#terms-publish-btn {
    padding: 9px 18px;
    background: #1c7d3b;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#terms-publish-btn:hover:not(:disabled) { background: #15602d; }
#terms-publish-btn:disabled { background: #c7c7cc; cursor: default; }
#terms-save-btn {
    padding: 10px 28px;
    background: #007AFF;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 15px;
}
#terms-save-btn:hover:not(:disabled) { background: #0056CC; }
#terms-save-btn:disabled { background: #c7c7cc; color: #fff; cursor: default; }

/* ---- Merge page (retire a canonical brand/product into another) ---- */
.merge-toolbar {
    display: flex;
    align-items: center;
    gap: 16px;
    margin: 16px 0;
    flex-wrap: wrap;
}

.merge-tabs {
    display: flex;
    gap: 6px;
}

.merge-tab {
    padding: 8px 20px;
    background: #f2f2f7;
    color: #555;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}

.merge-tab:hover:not(.active) { background: #e6e6eb; }

.merge-tab.active {
    background: #007AFF;
    color: #fff;
}

#merge-search {
    flex: 1;
    min-width: 200px;
    padding: 8px 12px;
    border: 1px solid #ddd;
    border-radius: 6px;
    font-size: 14px;
}

.merge-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
}

.merge-name {
    font-weight: 600;
    color: #333;
    flex: 1;
    word-break: break-word;
}

.merge-count {
    color: #888;
    font-size: 13px;
    white-space: nowrap;
}

.merge-pick-btn {
    padding: 6px 18px;
    background: #007AFF;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

.merge-pick-btn:hover { background: #0056CC; }

.merge-panel-box {
    background: #fff8e6;
    border: 1px solid #ffd88a;
    border-radius: 10px;
    padding: 16px;
    margin-bottom: 16px;
}

.merge-panel-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.merge-retire { color: #333; }
.merge-retire strong { word-break: break-word; }

.merge-panel-note { margin: 10px 0 0; }

#merge-apply-btn {
    padding: 8px 24px;
    background: #ff9500;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}

#merge-apply-btn:hover:not(:disabled) { background: #e08600; }
#merge-apply-btn:disabled { background: #c7c7cc; cursor: default; }

.merge-cancel {
    padding: 8px 18px;
    background: #f2f2f7;
    color: #555;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}

.merge-cancel:hover { background: #e6e6eb; }

.merge-message { margin: 10px 0 0; font-size: 14px; }
.merge-message.ok { color: #2da84e; }
.merge-message.err { color: #c00; }

.merge-empty {
    color: #888;
    font-size: 15px;
    padding: 24px 4px;
    line-height: 1.7;
}

.merge-empty-sub {
    color: #aaa;
    font-size: 13px;
}

/* ---- Registered-users cards (Users tab) ---- */
.user-count-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin: 16px 0 8px;
}

.user-count-card {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 190px;
    padding: 18px 22px;
    border-radius: 12px;
    background: #1b5e34;            /* darker green */
    color: #ffffff;
    font-weight: 700;               /* bold throughout */
}

.user-count-card .user-count-country {
    font-size: 12px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #ffffff;
    opacity: 0.85;
}

.user-count-card .user-count-number {
    font-size: 34px;
    line-height: 1.1;
    color: #ffffff;
}

.user-count-card .user-count-label {
    font-size: 14px;
    color: #ffffff;
}

.user-count-card .user-count-asof {
    font-size: 11px;
    font-weight: 600;
    color: #ffffff;
    opacity: 0.75;
    margin-top: 4px;
}

/* Dev Tools — JSON output from the stats run buttons */
/* Dev Tools -> Dashboard Test Data. Its own row class rather than .admin-add-box:
   that one sets `flex: 1` on every input, which stretches a 3-digit day count to
   400px and makes the file picker the widest thing on the page. */
.stats-csv-box {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin: 14px 0 10px;
}
.stats-csv-box label { font-size: 14px; color: #555; }
.stats-csv-box input[type="number"] {
    width: 90px;
    padding: 9px 10px;
    border: 1px solid #ccc;
    border-radius: 6px;
    font-size: 14px;
}
.stats-csv-box input[type="file"] { font-size: 13px; color: #555; max-width: 320px; }
/* A plain gap, so "export / generate" reads as separate from the day count. */
.stats-csv-sep { width: 10px; }
.stats-csv-btn {
    padding: 9px 16px;
    background: #007AFF;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
.stats-csv-btn:hover:not(:disabled) { background: #0056CC; }
.stats-csv-btn:disabled { opacity: 0.5; cursor: default; }
/* The only destructive action on the page — it deletes every adminStats document
   the uploaded file doesn't name. Coloured like the other reject/remove buttons
   so it doesn't read as one more blue utility button. */
.stats-csv-danger { background: #ff3b30; }
.stats-csv-danger:hover:not(:disabled) { background: #d32f27; }

.stats-run-output {
    background: #f0f0f3;
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 12px 14px;
    margin-top: 10px;
    max-height: 320px;
    overflow: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12px;
    line-height: 1.5;
    color: #1d1d1f;
    white-space: pre;
}

/* Daily Stats run buttons — blue, matching #user-lookup-btn. */
#compute-stats-btn,
#get-stats-btn {
    padding: 10px 20px;
    background: #007AFF;
    color: white;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#compute-stats-btn:hover:not(:disabled),
#get-stats-btn:hover:not(:disabled) { background: #0056CC; }
#compute-stats-btn:disabled,
#get-stats-btn:disabled { background: #c7c7cc; cursor: default; }

/* Membership Tier update — same dark green as #classify-run-btn. */
#tier-update-btn {
    padding: 10px 20px;
    background: #1c7d3b;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#tier-update-btn:hover:not(:disabled) { background: #15602d; }
#tier-update-btn:disabled { background: #c7c7cc; cursor: default; }

/* Cancel Subscription — dark red (#b00020, the red already used for expired /
   error states), deliberately distinct from the brighter #ff3b30 reject red so a
   destructive mock isn't mistaken for a moderation action. */
#cancel-sub-btn {
    padding: 10px 20px;
    background: #b00020;
    color: #fff;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    font-size: 14px;
}
#cancel-sub-btn:hover:not(:disabled) { background: #8c0019; }
#cancel-sub-btn:disabled { background: #c7c7cc; cursor: default; }

/* Status breakdown inside the registered-users card. */
.user-status-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid rgba(255, 255, 255, 0.25);
}

.user-status-row {
    display: flex;
    justify-content: space-between;
    gap: 18px;
    font-size: 13px;
    color: #ffffff;
}

.user-status-row .user-status-name { text-transform: capitalize; opacity: 0.85; }
.user-status-row .user-status-count { font-variant-numeric: tabular-nums; }

/* Only the buckets in DRILLDOWN (warning, suspended) are clickable; the
   affordance has to be visible or the drill-down is undiscoverable. Everything
   else — including `new` — renders as the plain row above. */
.user-status-row.user-status-drillable {
    cursor: pointer;
    margin: 0 -6px;
    padding: 0 6px;
    border-radius: 4px;
}

.user-status-row.user-status-drillable:hover,
.user-status-row.user-status-drillable.active {
    background: rgba(255, 255, 255, 0.18);
}

.user-status-row.user-status-drillable .user-status-name { opacity: 1; text-decoration: underline; }

/* ---- Users page: status / list / detail columns ---- */
.users-layout {
    display: grid;
    /* Column 1 is only a frame around the green count card (min-width 190px +
       padding), so it's sized to hug it — every pixel saved there goes to the
       user list, where the email addresses are what actually need the room. */
    grid-template-columns: 240px 420px minmax(0, 1fr);
    gap: 16px;
    align-items: start;
    margin-top: 12px;
}

/* Matches the Videos page: fixed-height columns that scroll independently, so a
   long user list can never push the detail pane off-screen. */
.users-col {
    background: #fff;
    border: 1px solid #e5e5e5;
    border-radius: 10px;
    padding: 12px;
    height: calc(100vh - 380px);
    min-height: 320px;
    overflow-y: auto;
}

/* The status card sits in a transparent frame and shouldn't stretch. */
.users-status-col { height: auto; min-height: 0; overflow: visible; }

/* The list header stays put while the entries scroll under it. */
.users-list-col { position: relative; }
.user-list-head {
    position: sticky;
    top: -12px;               /* cancels the column's own padding */
    background: #fff;
    z-index: 1;
}

/* The status card carries its own dark background, so its column is just a frame. */
.users-status-col { background: transparent; border: none; padding: 0; }


.user-list-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 13px;
    color: #666;
    padding: 0 4px 8px;
    border-bottom: 1px solid #eee;
    margin-bottom: 6px;
}
.user-list-head strong { text-transform: capitalize; color: #1d1d1f; font-size: 14px; }

/* Email filter above the status list. Deliberately NOT sticky: .user-list-head
   already sticks to this column's top edge, and a second sticky element on the
   same edge would park an opaque bar over it. */
.user-list-filter {
    display: flex;
    gap: 6px;
    padding: 0 4px 8px;
}
.user-list-filter input[type="text"] {
    flex: 1;
    min-width: 0;             /* lets the input shrink inside the flex row */
    padding: 7px 10px;
    border: 1px solid #d0d0d0;
    border-radius: 8px;
    font-size: 13px;
    font-family: inherit;
    outline: none;
}
.user-list-filter input[type="text"]:focus { border-color: #007AFF; }
.user-list-filter button {
    padding: 7px 12px; border: none; border-radius: 8px;
    background: #e0e0e0; color: #333; cursor: pointer; font-size: 13px;
}
.user-list-filter button:hover { background: #d0d0d0; }

/* One user in column 2: name / email / (tier + rejections). Boxed like the video
   cards on the Videos page — a visible border, not just a hover tint, so the list
   reads as discrete entries rather than a run of text. */
.user-box {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 9px 11px;
    margin-bottom: 8px;
    border-radius: 8px;
    border: 1px solid #e8e8ec;
    background: #fff;
    cursor: pointer;
}
.user-box:hover { background: #f6f6f8; border-color: #d8d8de; }
.user-box.active { background: #eef4ff; border-color: #c3d8ff; }

.user-box-name { font-size: 13.5px; font-weight: 600; }
.user-box-email {
    font-size: 12px;
    color: #6e6e73;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Tier and rejection count share the third line. */
.user-box-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.user-box-tier { font-size: 12px; text-transform: capitalize; color: #4a5568; }

.strike-count { font-size: 12px; color: #6e6e73; font-variant-numeric: tabular-nums; }
/* Coloured only once it means something: warn at 4, suspend at 5. */
.strike-count.warn { color: #b9770e; font-weight: 600; }
.strike-count.bad  { color: #c0392b; font-weight: 700; }

/* Column 3 — the person, most identifying first. */
.user-detail-card {
    padding: 4px 4px 14px;
    border-bottom: 1px solid #eee;
    margin-bottom: 12px;
}
.user-detail-name {
    font-size: 20px;
    font-weight: 700;
    color: #1d1d1f;
    line-height: 1.25;
}
.user-detail-email { font-size: 14px; margin-top: 4px; }
.user-detail-email a { color: #007AFF; text-decoration: none; }
.user-detail-email a:hover { text-decoration: underline; }
.user-detail-location { font-size: 14px; color: #6e6e73; margin-top: 2px; }
.user-detail-videos { margin-top: 8px; display: flex; align-items: baseline; gap: 6px; }
.user-detail-videos-count { font-size: 17px; font-weight: 600; color: #1d1d1f; font-variant-numeric: tabular-nums; }
.user-detail-videos-label { font-size: 14px; color: #6e6e73; }

.user-detail-asof { color: #999; font-size: 12px; }

/* Column 3 is a profile, not a table — centring it keeps the name as the anchor
   and stops the four short lines from hugging the left edge of a wide column. */
.users-detail-col { display: flex; flex-direction: column; text-align: center; }
.users-detail-col .user-detail-card { padding-left: 0; padding-right: 0; }
.user-detail-videos { justify-content: center; }
.user-detail-meta .report-row { justify-content: center; gap: 8px; }

/* Strike actions pinned to the bottom of the column, stacked. `margin-top: auto`
   holds them there however short the profile above is. */
.user-detail-actions {
    margin-top: auto;
    padding-top: 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.user-detail-actions button {
    width: 100%;
    padding: 10px 14px;
    border: none;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 600;
    color: #fff;
    cursor: pointer;
}
.user-detail-actions button:disabled { opacity: 0.4; cursor: not-allowed; }
.user-detail-reset { background: #d93025; }
.user-detail-reset:hover:not(:disabled) { background: #b3261e; }
/* Yellow needs dark text to stay legible. */
.user-detail-prewarn { background: #f5b400; color: #3d2f00; }
.user-detail-prewarn:hover:not(:disabled) { background: #d99e00; }
.user-detail-action-msg { font-size: 13px; min-height: 16px; }
.user-drill-note { font-size: 12px; color: #888; margin-top: 8px; padding: 0 4px; }

/* ---- Notifications: audience cards ----
   Two mutually exclusive cohorts, side by side. Cards rather than radio inputs
   because each one carries a live count and its own definition; selection reads
   off the border and tint, so the whole card is the control. auto-fit collapses
   them to one column on a narrow window instead of squeezing both. */
.audience-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    max-width: 760px;
    margin: 16px 0 12px;
}
.audience-card {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 6px;
    /* Half as tall as it is wide. min-height is the floor for when the grid
       collapses to one column and the card would otherwise be a thin band too
       short for its own content. */
    aspect-ratio: 2 / 1;
    min-height: 150px;
    /* font/text-align reset: a <button> centres its text and uses the UA font. */
    font: inherit;
    text-align: left;
    padding: 18px 20px;
    background: #fff;
    border: 2px solid #e0e0e0;
    border-radius: 12px;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
}
.audience-card:hover:not(.selected) { border-color: #c7c7cc; }
/* Selected inverts to solid dark green, so the choice is unmistakable across the
   whole card rather than a tint at its edge. Same green as .monitor-outcome.approved,
   which is already the portal's "approved/active" colour. */
.audience-card.selected { background: #1c7d3b; border-color: #1c7d3b; }
/* Ring sits outside the border, over the page background, so one colour reads
   against both the white and the green card. */
.audience-card:focus-visible { outline: 2px solid #1c7d3b; outline-offset: 2px; }
.audience-card-label { font-size: 14px; font-weight: 600; color: #1c1c1e; }
/* tabular-nums so a count ticking up doesn't shift the card's width. */
.audience-card-count {
    font-size: 32px;
    font-weight: 700;
    line-height: 1.1;
    color: #1c1c1e;
    font-variant-numeric: tabular-nums;
}
/* One condition per line. */
/* Prose now, not a stack of one-clause lines, so it's a block with a reading
   line-height rather than a flex column. */
.audience-card-desc {
    display: block;
    font-size: 13px;
    line-height: 1.45;
    color: #6e6e73;
}
.audience-card.selected .audience-card-label,
.audience-card.selected .audience-card-count { color: #fff; }
/* Not full white: the conditions are supporting detail, and at this size pure
   white on the green would compete with the count. */
.audience-card.selected .audience-card-desc { color: rgba(255, 255, 255, 0.82); }

/* ---- Notifications: compose ----
   Same max-width as .audience-cards so the fields line up with the cards above
   rather than running wider than the thing they apply to. */
.notify-compose {
    display: flex;
    flex-direction: column;
    gap: 14px;
    max-width: 760px;
}
.notify-field { display: flex; flex-direction: column; gap: 6px; }
.notify-label { font-size: 13px; font-weight: 600; color: #6e6e73; }
#notify-title,
#notify-body {
    width: 100%;
    padding: 10px 14px;
    border: 1px solid #d0d0d0;
    border-radius: 8px;
    /* font: inherit so the textarea doesn't fall back to the UA monospace. */
    font: inherit;
    font-size: 14px;
    outline: none;
    transition: border-color 0.15s, box-shadow 0.15s;
}
#notify-title:focus,
#notify-body:focus {
    border-color: #1c7d3b;
    box-shadow: 0 0 0 3px rgba(28, 125, 59, 0.15);
}
#notify-title::placeholder,
#notify-body::placeholder { color: #aaa; }
#notify-body { resize: vertical; min-height: 150px; line-height: 1.5; }

/* Same dark green as the selected audience card — one accent for this page. */
.notify-send {
    align-self: flex-start;
    padding: 12px 30px;
    background: #1c7d3b;
    color: #fff;
    border: none;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 0.04em;
    cursor: pointer;
}
.notify-send:hover:not(:disabled) { background: #15602d; }
.notify-send:disabled { opacity: 0.5; cursor: default; }
#notify-message { font-size: 13px; margin: 0; min-height: 18px; }

/* ---- Execute segment: the saved messages ---------------------------------- */
.notify-queue-count { font-size: 13px; color: #6e6e73; margin: 0 0 12px; }
.notify-queue-card {
    max-width: 760px;
    padding: 14px 16px;
    margin-bottom: 12px;
    border: 1px solid #e5e5e5;
    border-radius: 10px;
    background: #fff;
}
.notify-queue-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}
.notify-queue-title { font-size: 15px; font-weight: 600; color: #1d1d1f; }
/* Status is the handoff to the delivery run, so it reads as a state rather than
   another line of text. Only "pending" is written today; anything a later run
   sets falls through to the neutral grey. */
.notify-queue-status {
    flex: none;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 2px 9px;
    border-radius: 20px;
    background: #f0f0f2;
    color: #6e6e73;
    border: 1px solid #e0e0e4;
}
.notify-queue-status.pending { background: #fff6e5; color: #8a5a00; border-color: #f0dcb4; }
.notify-queue-status.sent { background: #eaf7ee; color: #1c7d3b; border-color: #c6e8d1; }
/* pre-wrap: the body was typed in a textarea, so its line breaks are the
   author's and shouldn't be collapsed away. */
.notify-queue-body {
    margin: 8px 0 10px;
    font-size: 13.5px;
    line-height: 1.5;
    color: #3c3c43;
    white-space: pre-wrap;
}
.notify-queue-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
    font-size: 12px;
    color: #8e8e93;
}
.notify-queue-actions { margin-top: 12px; }
.notify-queue-delete {
    padding: 7px 14px;
    border: 1px solid #f0c8c2;
    border-radius: 8px;
    background: #fdf1f0;
    color: #c0392b;
    font-size: 13px;
    cursor: pointer;
}
.notify-queue-delete:hover:not(:disabled) { background: #fae3e0; }
.notify-queue-delete:disabled { opacity: 0.5; cursor: default; }

@media (max-width: 1100px) {
    .users-layout { grid-template-columns: 1fr; }
    .users-col { max-height: none; }
}

/* ---- QR Code Generator ---------------------------------------------------
   Form on the left, the code and its payload on the right, so a field edit and
   the resulting image are visible at once — the point of the page is checking
   one against the other before something gets printed. */
.qr-layout {
    display: grid;
    grid-template-columns: minmax(280px, 380px) 1fr;
    gap: 28px;
    align-items: start;
    margin-top: 18px;
}

.qr-form { display: flex; flex-direction: column; gap: 14px; }

.qr-field { display: flex; flex-direction: column; gap: 6px; }
.qr-field > span { font-size: 13px; font-weight: 600; color: #3a3a3c; }
.qr-field input,
.qr-field select {
    padding: 9px 11px;
    border: 1px solid #d2d2d7;
    border-radius: 8px;
    font-size: 14px;
    background: #fff;
    color: #1d1d1f;
}
.qr-field input:focus,
.qr-field select:focus { outline: 2px solid #007aff33; border-color: #007aff; }

/* The fixed `source` field. Greyed and not interactive, but still legible —
   it is shown so the operator can see the whole payload, so rendering it at a
   disabled control's usual low contrast would defeat the reason it is there. */
.qr-field input:disabled {
    background: #f0f0f3;
    color: #6e6e73;
    cursor: not-allowed;
}

.qr-actions { display: flex; gap: 10px; margin-top: 4px; }

/* Dark green, flat, and the SAME on hover — all four buttons on this page.
   No :hover rule at all, deliberately: a hover colour here was the bug this
   replaced, and the page is a four-field form where nothing needs pointing at.
   Keyboard focus is untouched, so the browser's own focus ring still shows.
   These buttons carry NO shared class (they used to borrow .report-dismiss-btn
   from the moderation queue). That borrowing is exactly what broke them: this
   block's `.qr-actions button` is one class plus one element, which outranks a
   bare `.report-dismiss-btn`, so the fill below won while the moderation page's
   :hover rule — more specific again — won on hover. The result was a white
   button that turned green under the cursor. Styled standalone now, so the two
   pages cannot fight. */
.qr-actions button {
    padding: 8px 18px;
    border: none;
    border-radius: 6px;
    background: #1c7d3b;
    color: #fff;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
}

.qr-canvas-box {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 260px;
    padding: 16px;
    background: #fff;
    border: 1px solid #e3e3e6;
    border-radius: 12px;
}
/* White quiet zone around the code regardless of the page background: a QR
   needs the margin to be scannable, and the library draws right to the edge. */
.qr-canvas-box canvas { display: block; background: #fff; }

.qr-output { display: flex; flex-direction: column; gap: 12px; }

@media (max-width: 1100px) {
    .qr-layout { grid-template-columns: 1fr; }
}

/* ---- Dev Tools: video licensing -----------------------------------------
   Reuses .logs-table for the grid so the row rhythm matches the other admin
   tables; only the two licence-specific cells need anything of their own. */
.licence-table { margin-top: 12px; }
.licence-table td { vertical-align: middle; }

.licence-on {
    font-size: 11px;
    font-weight: 700;
    color: #fff;
    background: #2e75d4;
    border-radius: 3px;
    padding: 2px 6px;
    white-space: nowrap;
}

.licence-toggle-btn {
    padding: 5px 12px;
    border: none;
    border-radius: 6px;
    background: #1c7d3b;
    color: #fff;
    cursor: pointer;
    font-size: 13px;
    white-space: nowrap;
}
/* Removing a licence is the destructive-shaped action of the two, so it reads
   as the reject colour used elsewhere in the portal rather than as another
   green utility button. */
.licence-toggle-btn.licence-off-btn { background: #ff3b30; }
.licence-toggle-btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* Why the button beside it is disabled. A class rather than `.none` (which is a
   shipads class, not one of ours) and rather than inline styles, because the last
   two silent-styling bugs on this page were both an element that LOOKED styled:
   a button styled by #id whose rename dropped its rule, and an <h3> in a
   stylesheet that has no h3. */
.licence-blocked-note {
    font-size: 12px;
    color: #6e6e73;
    margin-top: 4px;
    max-width: 260px;
    line-height: 1.35;
}
