/* Industrial Precision — admin chrome (issue #80).
   ═══════════════════════════════════════════════
   The single home for the styling of every admin/maintenance screen: the page hero, the panels
   and tables, the accent button, status badges, and the corrections Bootstrap utilities need on
   the dark chrome.

   THE RULE THIS FILE INSTALLS: navy is chrome, white is content.

     page ................ --surface-2   --surface-ink   --surface-rule
     hero band ........... --chrome-fill --chrome-ink    --chrome-rule
     panel header strip .. --chrome-fill --chrome-ink    --chrome-rule
     panel BODY .......... --surface-1   --surface-ink   --surface-rule
     article toolbar ..... --chrome-fill --chrome-muted  (stays dark: sticky chrome)
     modal ............... navy header, light body

   Colours come from the semantic names in theme-surfaces.css, never from --navy/--white/--muted
   directly, so dark mode is a token flip rather than a rewrite of this file. Read that file's
   header for why the panel body is light at all.

   ── THE STALE-BUNDLE HAZARD. READ BEFORE EDITING. ────────────────────────────────────────────
   wwwroot/css/customerportal.min.css is a committed compiled artifact. Its SCSS source no longer
   defines the admin chrome, but the compiled copy still carries the PRE-ISSUE-#80 NAVY VERSIONS,
   because CustomerPortal.csproj compiles SCSS with `npx sass` under ContinueOnError="true" and
   Node is not installed — so the build reports success and never regenerates it. This file loads
   after that bundle and therefore wins ties, BUT ONLY ON PROPERTIES IT ACTUALLY DECLARES.

   Exactly eight rules are ghosted there:

       .admin-panel                     .admin-table tbody tr
       .admin-panel-header              .admin-table tbody tr:last-child
       .admin-table                     .admin-table tbody tr:hover
       .admin-table thead th            .admin-table tbody td

   Inside those eight: CHANGE VALUES, NEVER REMOVE A DECLARATION. A declaration deleted from one
   of them silently reverts to the old navy value. Nothing else here has a ghost — no badge,
   toolbar, modal, DataTables, hero or .btn-accent rule — so everywhere else, delete freely.
   AdminThemeTests derives the required property list from the bundle itself and fails if one
   goes missing, so this stays true even if the bundle is regenerated later.

   Plain CSS on purpose: like admin-nav.css it must not depend on the Dart Sass build step.
   Loaded by _Layout and _LayoutArticle after theme-surfaces.css. */

/* ── Page hero ───────────────────────────────────────────────────────────────
   One band, two class names. .cp-inner-hero is the customer-facing name (21 views, declared in
   Styles/modules/_industrial.scss and present in the compiled bundle); .inner-page-hero is the
   admin name (84 views, declared here). They were authored separately and drifted — different
   title scale, different subtitle colour — which is most of why admin and customer screens looked
   like different products. Every shared declaration lists both names.

   Deliberately NOT shared: padding, and .cp-inner-hero-sub's max-width. Re-declaring
   `.cp-inner-hero { padding }` here would kill the bundle's `@media (max-width:576px)` override,
   because a later stylesheet beats an earlier media query at equal specificity. Padding is layout,
   not visual language — leave it where it is. */

.inner-page-hero,
.cp-inner-hero {
    position: relative;
    background: var(--chrome-fill);
    border-bottom: 2px solid rgba(61, 184, 245, 0.15);
    overflow: hidden;
    /* Declares the band a dark surface so anything dropped into it inherits a dark-surface
       colour instead of body's --surface-ink. .admin-badge--muted relies on this. */
    color: var(--chrome-muted);
}

.inner-page-hero {
    padding: 30px 0 34px;
}

/* The precision grid, as on the public cp-inner-hero, without extra markup. Not applied to
   .cp-inner-hero: that band paints the same grid via its own child element, and adding it here
   would double it on 21 customer views. */
.inner-page-hero::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image:
        linear-gradient(rgba(61, 184, 245, 0.04) 1px, transparent 1px),
        linear-gradient(90deg, rgba(61, 184, 245, 0.04) 1px, transparent 1px);
    background-size: 40px 40px;
    pointer-events: none;
}

.inner-page-hero > .container {
    position: relative;
    z-index: 1;
}

.inner-page-hero-eyebrow,
.cp-inner-hero-badge {
    color: var(--chrome-link);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 2px;
    text-transform: uppercase;
}

.inner-page-hero-eyebrow {
    margin: 0 0 6px;
}

.inner-page-hero-title,
.cp-inner-hero-title {
    font-family: 'Barlow Condensed', sans-serif;
    /* Was 26/3vw/38 on admin and 28/4vw/44 on customer; one scale for both. */
    font-size: clamp(28px, 3.4vw, 42px);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: -0.5px;
    color: var(--chrome-ink);
    line-height: 1.05;
    margin: 0;
}

.inner-page-hero-sub,
.cp-inner-hero-sub {
    /* The customer band used rgba(255,255,255,.55) (~4.5:1); --chrome-muted is 11.3:1. */
    color: var(--chrome-muted);
    font-weight: 300;
    line-height: 1.7;
}

.inner-page-hero-sub {
    font-size: .9rem;
    margin: 6px 0 0;
}

.inner-page-hero-sub a,
.cp-inner-hero-sub a {
    color: var(--chrome-link);
}

/* ── Accent button ─────────────────────────────────────────────────────────── */
/* Accent fill with navy text is 8.3:1, so it reads on a light body and on navy chrome alike. */

.btn-accent {
    background: var(--accent);
    border: 1px solid var(--accent);
    color: var(--navy);
    font-weight: 600;
}

.btn-accent:hover,
.btn-accent:focus {
    background: var(--accent2);
    border-color: var(--accent2);
    color: var(--navy);
    box-shadow: 0 4px 14px rgba(61, 184, 245, 0.3);
}

.btn-accent:disabled,
.btn-accent.disabled {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--navy);
    opacity: .55;
}

/* ── Panels ────────────────────────────────────────────────────────────────── */
/* A light card with a navy header strip — the same object as .card / .card-header in
   Styles/modules/_industrial.scss, which HubspotImport, PublishNewErpRelease and
   PublishNewSfdRelease already use. This makes the other 45 views agree with those three.

   GHOSTED RULE: every declaration below is also set by the stale bundle. Change values only. */

.admin-panel {
    background: var(--surface-1);
    border: 1px solid var(--surface-rule);
    border-radius: 4px;             /* was 8px; 4px matches .card */
    overflow: hidden;               /* clips the header strip's top corners */
    padding: 0;                     /* the legacy pink class set 5px; keep explicit */
    color: var(--surface-ink);
    box-shadow: 0 1px 6px rgba(0, 20, 55, 0.06);   /* as .card */
}

/* GHOSTED RULE. */
.admin-panel-header {
    background: var(--chrome-fill);
    /* The bundle says var(--rule), a navy-tinted hairline that is invisible on navy. An accent
       hairline echoes the hero's own accent underline. */
    border-bottom: 1px solid var(--chrome-rule);
    padding: 12px 20px;
    font-weight: 600;
    font-size: .9rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--chrome-ink);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.admin-panel a:not(.btn) {
    color: var(--surface-link);
}

.admin-panel a:not(.btn):hover {
    color: var(--surface-link-hover);
}

/* …but the header strip and the article-page toolbar are still dark surfaces. */
.admin-panel-header a:not(.btn),
.admin-toolbar a:not(.btn) {
    color: var(--chrome-link);
}

/* Inline code, app-wide. Bootstrap's CDN sets code { color: #d63384 } — Bootstrap pink, which is
   not in this palette — and the compiled bundle overrides only its background (#eff0f1), never
   its colour. On the old navy panel that inherited --white and rendered white-on-near-white at
   ~1.07:1, which is why every key in the permission catalogue was invisible.
   .article-body code and .visu-article-body code are more specific and already correct, so this
   only reaches the ones nothing ever styled — including the help paragraphs that sit outside a
   panel on the light page. */
code {
    color: var(--code-ink);
}

/* Inside a panel, give it a chip. */
.admin-panel code,
.admin-modal code {
    color: var(--code-ink);
    background: var(--surface-2);
    border: 1px solid var(--surface-rule);
    border-radius: 3px;
    padding: 1px 5px;
}

/* ── Tables ────────────────────────────────────────────────────────────────── */
/* GHOSTED RULE. */

/* .cp-table is the customer-facing twin of .admin-table (issue #164). ONE declaration block serves
   both, exactly as the two hero families are styled together so they cannot drift — the ticket lists
   are customer-facing and should not be wearing a class called "admin".

   .admin-table comes LAST in each list on purpose: AdminThemeTests derives the required property
   list from the stale bundle and looks for `.admin-table ... {`, so the ghosted selector has to be
   the one immediately before the brace. */
.cp-table,
.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .9rem;
    margin: 0;
    color: var(--surface-ink);
}

/* GHOSTED RULE. A tinted band, because a transparent header on white has nothing separating it
   from the first row. --surface-muted is 5.31:1 on --surface-2. */
.cp-table thead th,
.admin-table thead th {
    background: var(--surface-2);
    color: var(--surface-muted);
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    padding: 10px 20px;
    border-bottom: 1px solid var(--surface-rule);
    white-space: nowrap;
}

/* GHOSTED RULE. */
.cp-table tbody tr,
.admin-table tbody tr {
    border-bottom: 1px solid var(--surface-rule);
    transition: background .15s;
}

/* GHOSTED RULE. */
.cp-table tbody tr:last-child,
.admin-table tbody tr:last-child {
    border-bottom: none;
}

/* GHOSTED RULE. */
.cp-table tbody tr:hover,
.admin-table tbody tr:hover {
    background: var(--surface-hover);
}

/* GHOSTED RULE. */
.cp-table tbody td,
.admin-table tbody td {
    padding: 12px 20px;
    color: var(--surface-ink);
    vertical-align: middle;
    background: transparent;
}

/* Opt-in banding for the dense catalogues. NOT the default: Revisions.cshtml filters rows with
   `row.hidden = true`, which does not remove them from :nth-child, so banding would stagger as
   a filter is typed. */
.admin-table--zebra tbody tr:nth-child(even) {
    background: var(--surface-zebra);
}

.admin-table--zebra tbody tr:nth-child(even):hover {
    background: var(--surface-hover);
}

/* Denser rows for catalogue screens, replacing per-view inline font-size. */
.admin-table--dense thead th {
    padding: 8px 16px;
}

.admin-table--dense tbody td {
    padding: 8px 16px;
    font-size: .85rem;
}

/* Bootstrap's .table drives itself off CSS variables. Keep the Industrial hairline and hover
   rather than Bootstrap's #dee2e6 where the two are mixed (Users/Index uses a bare .table;
   Activity used table-dark). Same shape as before, light values.
   .admin-panel .table is (0,2,0) and so also beats .table-dark (0,1,0). */
.admin-table.table,
.admin-panel .table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--surface-ink);
    --bs-table-border-color: var(--surface-rule);
    --bs-table-striped-bg: var(--surface-zebra);
    --bs-table-striped-color: var(--surface-ink);
    --bs-table-hover-bg: var(--surface-hover);
    --bs-table-hover-color: var(--surface-ink);
    color: var(--surface-ink);
}

/* ── Status badges ─────────────────────────────────────────────────────────── */
/* These appear on THREE surfaces on a single page — Revisions.cshtml puts one in the hero, one
   in a navy header strip and one in a light table body — so no per-surface override can work.
   Every variant has to read on navy and on white. */

.admin-badge {
    display: inline-block;
    padding: .3em .65em;
    font-size: .74rem;
    font-weight: 600;
    letter-spacing: .03em;
    border-radius: 3px;
    line-height: 1;
}

/* Accent fill, navy text: 8.3:1 on either surface. */
.admin-badge--live {
    background: var(--accent);
    color: var(--navy);
}

/* An outline chip that takes its colour from whatever surface it lands on: --surface-ink inside
   a panel body, --chrome-ink inside a header strip, --chrome-muted inside a hero. */
.admin-badge--muted {
    background: transparent;
    color: inherit;
    border: 1px solid currentColor;
}

/* Solid, not outline: the #ff8095 outline this replaced was 2.39:1 on a light body. */
.admin-badge--danger {
    background: var(--status-danger-fill);
    border: 1px solid var(--status-danger-fill);
    color: var(--status-danger-ink);
}

/* Count pills on panel headers ("Active Pages [12]"). */
.admin-badge--count {
    background: var(--accent);
    color: var(--navy);
    font-weight: 700;
    border-radius: 999px;
    padding: .3em .7em;
}

/* ── Bootstrap corrections on the dark chrome ─────────────────────────────────
   The panel BODY is a light surface now, so text-muted, form-label, form-text, badges and
   btn-outline-* are correct exactly as Bootstrap authors them, and the .admin-panel overrides
   that used to force them to --steel are gone.

   What is still dark is the chrome: the hero band, the header strip, the article toolbar and the
   modal header. Nine views put Bootstrap utilities inside a header strip (ConferenceResources,
   PartnerSubmissions, PageSections, VisUManagement, Capabilities, ChatBoards, ChatModeration,
   Pages, Revisions), so these rules moved rather than died. */

.inner-page-hero .text-muted,
.cp-inner-hero .text-muted,
.admin-panel-header .text-muted,
.admin-toolbar .text-muted,
.admin-modal .modal-header .text-muted {
    color: var(--chrome-muted) !important;
}

.admin-panel-header .badge.bg-secondary,
.inner-page-hero .badge.bg-secondary {
    background: rgba(255, 255, 255, 0.14) !important;
    color: var(--chrome-muted);
}

.inner-page-hero .btn-outline-secondary,
.cp-inner-hero .btn-outline-secondary,
.admin-panel-header .btn-outline-secondary {
    color: var(--chrome-control-ink);
    border-color: var(--chrome-control-border);
}

.inner-page-hero .btn-outline-secondary:hover,
.inner-page-hero .btn-outline-secondary:focus,
.cp-inner-hero .btn-outline-secondary:hover,
.cp-inner-hero .btn-outline-secondary:focus,
.admin-panel-header .btn-outline-secondary:hover,
.admin-panel-header .btn-outline-secondary:focus {
    background: var(--chrome-control-hover-bg);
    color: var(--chrome-ink);
    border-color: var(--chrome-control-hover-border);
}

/* VisUManagement is the one view with a delete button inside a header strip. */
.admin-panel-header .btn-outline-danger {
    color: var(--chrome-danger-ink);
    border-color: var(--chrome-danger-ink);
}

.admin-panel-header .btn-outline-danger:hover,
.admin-panel-header .btn-outline-danger:focus {
    background: rgba(220, 53, 69, 0.18);
    color: #ffb3c0;
    border-color: #ffb3c0;
}

/* Revisions.cshtml puts its filter box in the header strip; match the navbar's own search field
   rather than dropping a white block onto the navy. */
.admin-panel-header .form-control,
.admin-panel-header .form-control:focus {
    background: var(--chrome-control-hover-bg);
    border-color: var(--chrome-control-border);
    color: var(--chrome-ink);
}

.admin-panel-header .form-control::placeholder {
    color: var(--chrome-muted);
}

.admin-panel hr {
    border-color: var(--surface-rule);
    opacity: 1;
}

/* ── Admin toolbar strips on article pages ───────────────────────────────────
   Docs, KnowledgeBase and both ShowArticle views wrap their admin-only edit links in
   `admin-panel admin-toolbar`. Those are customer-facing article pages on the light canvas, and
   ShowArticle's strip is position:sticky over the article — so this one STAYS DARK on purpose.
   It is chrome, and a white sticky bar on an off-white article page would read as part of the
   article rather than as an admin control.

   (0,2,0), so it beats both .admin-panel above and the ghost in the bundle. */

.admin-panel.admin-toolbar {
    background: var(--chrome-fill);
    border: 1px solid var(--rule-dark);
    color: var(--chrome-muted);
}

.admin-toolbar {
    padding: 10px 16px;
    display: flex;
    flex-wrap: wrap;
    gap: 8px 20px;
    align-items: center;
    font-size: .9rem;
    margin-bottom: 14px;
}

/* Destructive actions on the toolbar read as warnings, not the darkred-on-navy the old inline
   styles produced. */
.admin-panel a.admin-toolbar-danger {
    color: var(--chrome-danger-ink);
}

/* ── DataTables inside panels ────────────────────────────────────────────────
   Images, File Upload, Users and Customers run DataTables, whose CDN chrome assumes a white
   page — which the panel body now is, so most of the old dark skin is gone. What remains only
   aligns the CDN's typography and spacing with .admin-table. */

.admin-panel .dataTables_wrapper {
    padding: 14px 20px;
    color: var(--surface-muted);
}

.admin-panel .dataTables_wrapper .dataTables_length,
.admin-panel .dataTables_wrapper .dataTables_filter,
.admin-panel .dataTables_wrapper .dataTables_info,
.admin-panel .dataTables_wrapper .dataTables_processing,
.admin-panel .dataTables_wrapper .dataTables_paginate {
    color: var(--surface-muted);
}

.admin-panel table.dataTable thead th,
.admin-panel table.dataTable thead td {
    background: var(--surface-2);
    color: var(--surface-muted);
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    border-bottom: 1px solid var(--surface-rule);
    padding: 10px 12px;
}

.admin-panel table.dataTable tbody td {
    padding: 10px 12px;
    color: var(--surface-ink);
}

.admin-panel table.dataTable tbody tr {
    border-bottom: 1px solid var(--surface-rule);
}

.admin-panel table.dataTable tbody tr:hover {
    background: var(--surface-hover);
}

/* DataTables paints row selection as a box-shadow, which no background can override. */
.admin-panel table.dataTable tbody tr.selected,
.admin-panel table.dataTable tbody tr.selected > * {
    background: rgba(61, 184, 245, 0.16);
    color: var(--surface-ink);
    box-shadow: none !important;
}

.admin-panel .dataTables_wrapper .dataTables_paginate .paginate_button.current,
.admin-panel .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
    color: var(--navy) !important;
    background: var(--accent);
    border-color: var(--accent);
}

/* ── Modals ──────────────────────────────────────────────────────────────────
   Confirmation dialogs on admin screens. Bootstrap's .modal-content is already a light surface,
   so only the navy header needs stating — and stating it explicitly is what keeps
   `btn-close-white` correct in Pages.cshtml without touching the view. */

.admin-modal .modal-header {
    background: var(--chrome-fill);
    color: var(--chrome-ink);
    border-bottom: 1px solid var(--chrome-rule);
}

.admin-modal .modal-title {
    color: var(--chrome-ink);
}

.admin-modal .modal-footer {
    border-color: var(--surface-rule);
}

/* --surface-1, not --surface-2: .modal-content computes to --offwhite, so a --surface-2 callout
   would be the same tone as the body it sits in and only its hairline would show. */
.admin-modal .callout {
    background: var(--surface-1);
    border: 1px solid var(--surface-rule);
    border-radius: 6px;
    padding: 12px 16px;
}

/* ── Image picker (issue #93) ────────────────────────────────────────────────
   The drag-and-drop image field on the admin editors (Views/Shared/_ImagePicker.cshtml). Its
   values were authored for a dark panel, but ConferenceEdit renders the picker directly on the
   light page — so those were already wrong before this change, not made wrong by it. */

.img-picker-row {
    display: flex;
    align-items: flex-start;
    gap: 14px;
}

.img-picker-fields {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* Fixed box so the row does not jump as previews of different sizes load. */
.img-picker-preview {
    flex: 0 0 auto;
    width: 108px;
    height: 108px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--surface-1);
    border: 1px solid var(--surface-rule);
    border-radius: 4px;
    overflow: hidden;
}

.img-picker-preview img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* Shared with the file picker below and with the universal drop zone (issue #260): these are
   the same control, and looking alike is the point — an admin should not have to learn a
   second one. One rule, so the three cannot drift apart. */
.updrop-zone {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 14px 16px;
    border: 1px dashed rgba(0, 55, 120, 0.28);
    border-radius: 5px;
    background: rgba(0, 55, 120, 0.03);
    color: var(--surface-muted);
    font-size: .85rem;
    cursor: pointer;
    transition: border-color .15s, background .15s, color .15s;
}

.updrop-zone i {
    font-size: 1.15rem;
}

.updrop-zone:hover,
.updrop-zone:focus-visible {
    border-color: var(--accent);
    background: rgba(61, 184, 245, 0.08);
    color: var(--navy2);
    outline: none;
}

/* While a file is over the target. */
/* .drag-over is the class TicketFormModule and CxFilesListModule toggle on .pasteImageBox —
   they own their own attachment queues and were here long before this zone. Both names, one
   rule, so the ticket form and the CX modal highlight exactly like an admin drop target. */
.updrop-zone--over,
.updrop-zone.drag-over,
.pasteImageBox.drag-over {
    border-color: var(--accent);
    border-style: solid;
    background: rgba(61, 184, 245, 0.14);
    color: var(--navy2);
}

.img-picker-status,
.file-picker-status,
.updrop-status {
    font-size: .78rem;
    color: var(--surface-link);
    min-height: 1.1em;
}

/* One error colour for all three, and a TOKEN rather than the #b02a37 that was hardcoded
   here: a literal cannot follow the theme, so the picker's error text kept a light-mode red
   in dark mode. --surface-danger is the danger ink for a content surface; --status-danger-ink
   is the ink that goes ON a danger fill and is var(--white), which here would be white on
   white (#221). */
.img-picker-status--error,
.file-picker-status--error,
.updrop-status--error {
    color: var(--surface-danger);
}

@media (max-width: 575px) {
    .img-picker-row {
        flex-direction: column;
    }
}

/* ── File picker ─────────────────────────────────────────────────────────────
   The drag-and-drop "Uploaded file Id" field (Views/Shared/_FilePicker.cshtml). It shares
   the drop zone above with the image picker and adds a line naming the file the id points
   at, because an id on its own tells an admin nothing. */

.file-picker-fields {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.file-picker-current {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: .82rem;
    min-width: 0;
}

.file-picker-current a {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The variant that lives in a table cell: no label, no name box, and a zone slim enough that a
   row does not become twice as tall.

   Targets .updrop-zone now. The two rules that styled .file-picker-drop here went with the
   element: this picker renders a plain input the shared control upgrades (issue #260), so nothing
   produces that class any more and the rules were styling something that no longer exists. */
.file-picker--compact .updrop-zone {
    padding: 6px 8px;
    gap: 6px;
    font-size: .75rem;
}

.file-picker--compact .updrop-zone i {
    font-size: .95rem;
}

.file-picker--compact .file-picker-current,
.file-picker--compact .file-picker-status {
    font-size: .72rem;
}

/* ── Universal upload drop zone ───────────────────────────────────────────────
   Issue #260. wwwroot/js/upload-drop.js upgrades any plain <input type="file"> into a
   click / drag-and-drop / paste target. It is THE drop zone now — the three pickers' own
   zones were removed once each of them moved onto this control, so there is one definition of
   what a drop target looks like rather than four that happened to agree.

   Everything is written against the --surface-* semantic tokens, so it is correct on the
   white admin panel and on navy chrome with no second rule, and needs nothing in
   theme-dark.css — which is also why the pickers above have no dark overrides. */

.updrop {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

/* Applied BY THE SCRIPT once the zone exists, never in the markup. With the script absent
   the ordinary file input is still there and still visible, which is the whole of issue
   #136's rule: never hide a working baseline behind a script that may not load. */
.updrop-input.updrop-input {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.updrop-prompt {
    text-align: center;
}

/* Reads as the action it is, without being a <button> — a real button inside these forms
   would submit them. */
.updrop-browse {
    color: var(--surface-link);
    text-decoration: underline;
}

.updrop-zone--over .updrop-browse,
.updrop-zone:hover .updrop-browse {
    color: inherit;
}

/* The ticket and CX attachment boxes stack a prompt over a preview strip, where every other
   zone is a single centred line. Same zone, one extra modifier — rather than the separate
   .pasteImageBox look they had, which is why "drop a file here" was drawn two different ways
   depending on whether you were on an admin screen or the support form (issue #260). */
.updrop-zone--stack {
    flex-direction: column;
    gap: 8px;
    padding: 18px 16px;
}

/* ── The ticket and CX attachment boxes ───────────────────────────────────────
   Issue #260. These are .form-control fields inside a .form-floating, and they have to stay
   that way: the wrapper positions its caption absolutely over the control and only lays out
   correctly against Bootstrap's own form-control padding. Taking the class off to use the
   shared zone markup printed "Please attach supporting files…" straight across "Drag files
   here…" on the customer-facing support form.

   So the SHAPE stays and the LOOK is unified here instead — same dashed target, same accent
   highlight while dragging, over the top of .form-control. This is why .pasteImageBox is
   styled rather than replaced. */

.pasteImageBox {
    border: 1px dashed rgba(0, 55, 120, 0.28);
    border-radius: 5px;
    background: rgba(0, 55, 120, 0.03);
    color: var(--surface-muted);
    font-size: .85rem;
    text-align: center;
    cursor: pointer;
    transition: border-color .15s, background .15s, color .15s;

    /* ROOM FOR THE FLOATING CAPTION, and the reason this box needs stating explicitly.

       .form-floating puts its <label> at top:0 with 1rem of padding, expecting a control that is
       3.5rem tall with matching padding so the caption occupies the top strip and the value sits
       below it. This control is a <div> carrying an inline height:min-content, so when it holds
       NOTHING it collapses to a single centred line — and the caption lands on exactly that line.
       "Please attach supporting files…" then prints straight through "Drag files here…".

       It only shows in the EMPTY state, which is why it survived a screenshot review: one
       attachment makes the box tall enough to hide it entirely.

       min-height beats the inline height because a minimum wins over a smaller declared height,
       so this is fixable here without touching the three views that set it. */
    min-height: calc(3.5rem + 2px);
    padding: 1.625rem .75rem .625rem;   /* 1.625rem is Bootstrap's own floating-textarea top */
}

.pasteImageBox:hover {
    border-color: var(--accent);
    background: rgba(61, 184, 245, 0.08);
    color: var(--navy2);
}

/* ── Attachment tiles ─────────────────────────────────────────────────────────
   Issue #268. The strip inside a drop zone, rendered by UploadDrop.renderAttachments.

   What this replaces: a 200x175 box holding a 150px image, a red glyph nudged into place with
   top:-10px right:20px, and the filename in a grey pill — no token, card, radius or hairline
   anywhere in it, so a ticket with three attachments looked like a different product from the
   form around it. Non-images were a 150px black paperclip illustration, the same picture for a
   spreadsheet, a PDF and a log file.

   Tokens only, so this is right on the white support form, on an admin panel, and in dark mode
   without a second rule. */

.attachmentsPreviewContainer {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(116px, 1fr));
    gap: 12px;
    align-items: start;
    justify-content: start;
    width: 100%;
    margin-top: 4px;
}

/* Empty on a fresh form: collapse rather than reserving a gap under the prompt. */
.attachmentsPreviewContainer:empty {
    display: none;
}

.updrop-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0;
    padding: 0;
    text-align: left;
    min-width: 0;
}

/* A FIXED RATIO that crops. The old strip capped both axes without cropping, so a wide screenshot
   and a tall one took visibly different footprints and no two rows lined up. */
.updrop-tile-thumb {
    position: relative;
    aspect-ratio: 4 / 3;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border: 1px solid var(--surface-rule);
    border-radius: 4px;
    background: var(--surface-2);
    color: var(--surface-muted);
}

.updrop-tile-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    opacity: 0;
    transition: opacity .18s ease-in;
}

.updrop-tile--ready .updrop-tile-thumb img {
    opacity: 1;
}

/* File-type icon, at a size that reads as a label rather than as artwork. */
.updrop-tile-thumb i {
    font-size: 1.9rem;
    line-height: 1;
}

/* The busy state, which is the whole point: the tile is in the DOM the instant the file is
   queued, so the answer to "did it take it?" does not wait on decoding the image. */
.updrop-tile--busy .updrop-tile-thumb::after {
    content: "";
    position: absolute;
    width: 18px;
    height: 18px;
    border: 2px solid var(--surface-rule);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: updropSpin .7s linear infinite;
}

@keyframes updropSpin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .updrop-tile--busy .updrop-tile-thumb::after { animation-duration: 2.4s; }
    .updrop-tile-thumb img { transition: none; }
}

/* A real button, sitting on the thumbnail's corner rather than shoved there with offsets. */
.updrop-tile-remove {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--surface-rule);
    border-radius: 3px;
    background: var(--surface-1);
    color: var(--surface-muted);
    font-size: .7rem;
    line-height: 1;
    cursor: pointer;
    opacity: .85;
    transition: background .15s, color .15s, border-color .15s, opacity .15s;
}

.updrop-tile-remove:hover,
.updrop-tile-remove:focus-visible {
    background: var(--status-danger-fill);
    border-color: var(--status-danger-fill);
    /* The ink that goes ON a danger fill — the one place --status-danger-ink is correct (#221). */
    color: var(--status-danger-ink);
    opacity: 1;
    outline: none;
}

.updrop-tile-name {
    margin: 4px 0 0;
    font-size: .74rem;
    line-height: 1.25;
    color: var(--surface-ink);
    /* One line, ellipsised; the full name is on the title attribute. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.updrop-tile-size {
    font-size: .68rem;
    color: var(--surface-muted);
}

/* ── Attachment detail modal ──────────────────────────────────────────────────
   Issue #269. Click a tile's thumbnail or its name and the file opens larger, with its name
   editable in place. This restores the click-to-preview showOverlay used to give — which #268
   dropped when the strip was rewritten — and is also why the upload screens no longer carry a
   separate "Name" box: one box can only name one file. */

.updrop-tile--opens .updrop-tile-thumb,
.updrop-tile--opens .updrop-tile-name {
    cursor: pointer;
}

.updrop-tile--opens .updrop-tile-thumb:hover {
    border-color: var(--accent);
}

.updrop-tile--opens .updrop-tile-name:hover,
.updrop-tile--opens .updrop-tile-name:focus-visible {
    color: var(--surface-link);
    text-decoration: underline;
    outline: none;
}

.updrop-modal-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 180px;
    max-height: 60vh;
    padding: 12px;
    border: 1px solid var(--surface-rule);
    border-radius: 4px;
    background: var(--surface-2);
}

.updrop-modal-image {
    max-width: 100%;
    max-height: 56vh;
    object-fit: contain;
}

/* A file with no preview still gets something to look at, at a size that suits a dialog rather
   than the 1.9rem the tile uses. */
.updrop-modal-icon i {
    font-size: 4rem;
    color: var(--surface-muted);
}

.updrop-modal-caption {
    margin: 0;
    color: var(--surface-muted);
    font-size: .85rem;
    text-align: center;
    word-break: break-all;
}
