/* ==========================================================================
   OTP (6-digit code) input group
   ========================================================================== */

.otp-input-group {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
}

.otp-input-box {
    width: 2.75rem;
    height: 3.25rem;
    padding: 0;
    text-align: center;
    font-size: 1.5rem;
}

/* 6 boxes at the desktop size (2.75rem + 0.5rem gap) don't fit inside a narrow
   phone's .auth-card — shrink them so the code entry never overflows/scrolls
   horizontally on Login/VerifyTotp/VerifyEmailCode/EnableTotp. */
@media (max-width: 400px) {
    .otp-input-group {
        gap: 0.3rem;
    }

    .otp-input-box {
        width: 2.15rem;
        height: 2.6rem;
        font-size: 1.15rem;
    }
}

/* --------------------------------------------------------------------------
   Vérification en deux étapes — zones sélectionnables (Profile/TwoFactor)
   -------------------------------------------------------------------------- */

.mfa-zone {
    position: relative;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 1.25rem;
    margin-bottom: 1rem;
}

.mfa-zone:last-child {
    margin-bottom: 0;
}

.mfa-zone--active {
    border-color: var(--accent);
    border-width: 2px;
    background-color: var(--bg-subtle);
}

/* The whole tile is the click target (stretched button below) — hover/focus
   feedback has to live on the tile itself, not on the invisible button. */
.mfa-zone--clickable {
    cursor: pointer;
    transition: background-color var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.mfa-zone--clickable:hover,
.mfa-zone--clickable:focus-within {
    border-color: var(--accent);
    background-color: var(--bg-subtle);
    box-shadow: var(--shadow-sm);
}

.mfa-zone-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 0.75rem;
    margin-bottom: 0.5rem;
}

.mfa-zone-badge {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--accent-contrast);
    background-color: var(--accent);
    border-radius: var(--radius-pill);
    padding: 0.2rem 0.6rem;
    white-space: nowrap;
}

.mfa-zone-badge--muted {
    color: var(--text-muted);
    background-color: var(--bg-subtle);
    border: 1px solid var(--border);
}

.mfa-zone-badge--success {
    color: var(--color-success);
    background-color: var(--color-success-bg);
    border: 1px solid var(--color-success-border);
}

.mfa-zone-badge--warning {
    color: var(--color-warning);
    background-color: var(--color-warning-bg);
    border: 1px solid var(--color-warning-border);
}

.mfa-zone-cta {
    display: inline-block;
    margin-top: 0.75rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--accent);
}

/* Invisible, covers the entire tile so a click anywhere on the zone —
   heading, description, CTA text — submits the form. */
.mfa-zone-stretched-btn {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: var(--radius-md);
    cursor: pointer;
    z-index: 1;
}

/* A secondary, explicit action (e.g. "Réinitialiser") living inside a tile
   that also has a stretched-button click target — needs to sit above it to
   stay clickable on its own. */
.mfa-zone-secondary-action {
    position: relative;
    z-index: 2;
    margin-top: 0.75rem;
}

/* --------------------------------------------------------------------------
   Liste compacte des utilisateurs (Admin/Index)
   -------------------------------------------------------------------------- */

/* Avatar + name + role/2FA badges + action buttons all sit in one flex row.
   On a phone-width viewport they don't fit on one line — wrap instead of
   forcing horizontal scroll of the whole page. flex-basis on identity keeps
   the name from being squeezed to nothing before the row wraps; flex-shrink:0
   on the badges/actions groups makes them wrap as whole blocks rather than
   compressing awkwardly. */
.admin-user-row {
    flex-wrap: wrap;
    row-gap: 0.5rem;
}

.admin-user-row:last-child {
    border-bottom: none !important;
}

.admin-user-identity {
    flex-basis: 10rem;
}

.admin-user-badges,
.admin-user-actions {
    flex-shrink: 0;
}

.admin-user-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    background-color: var(--bg-subtle);
    border: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 0.85rem;
    font-weight: 600;
}

.admin-user-identity {
    min-width: 0;
}

.admin-user-status-dot {
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background-color: var(--color-danger);
    margin-right: 0.35rem;
    vertical-align: middle;
}

.admin-user-icon-btn {
    padding: 0.25rem 0.6rem;
}

/* Admin/Index role filter (Tous/Administrateurs/Utilisateurs) — a Bootstrap
   .btn-group has no wrap by design (its connected look relies on negative
   margins + squared-off middle corners assuming a single row), so 3 buttons
   including "Administrateurs" can overflow a phone-width card. Below 576px,
   drop the connected-group look in favor of separate buttons that wrap. */
@media (max-width: 575.98px) {
    .btn-group {
        display: flex;
        flex-wrap: wrap;
        gap: 0.4rem;
    }

    .btn-group > .btn {
        flex: 1 1 auto;
        margin-left: 0 !important;
        border-radius: var(--radius-sm) !important;
    }
}

.dropdown-item--warning {
    color: var(--color-warning);
}

.dropdown-item--warning:hover,
.dropdown-item--warning:focus {
    background-color: var(--color-warning-bg);
    color: var(--color-warning);
}

.dropdown-item--danger {
    color: var(--color-danger);
}

.dropdown-item--danger:hover,
.dropdown-item--danger:focus {
    background-color: var(--color-danger-bg);
    color: var(--color-danger-hover);
}

/* Without this, the warning/danger modifier color would still show through even when
   the button carries the disabled attribute (self/last-admin guard) — this forces the
   existing greyed-out look to win regardless of the new color modifiers. */
.dropdown-item--warning:disabled,
.dropdown-item--warning.disabled,
.dropdown-item--danger:disabled,
.dropdown-item--danger.disabled {
    color: var(--text-muted);
    background-color: transparent;
}

/* --------------------------------------------------------------------------
   Statut système (Admin/Status)
   -------------------------------------------------------------------------- */

.status-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 1rem;
}

.status-tile {
    background-color: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 1rem;
    text-align: center;
    transition: border-color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
}

.status-tile-value {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--text);
}

.status-tile-label {
    font-size: 0.85rem;
    color: var(--text-muted);
    margin-top: 0.25rem;
}

/* Overview row (uptime/memory/disk/users) — icon badge above the value,
   hover lift to match the homepage dashboard tiles it sits below. */
.status-overview .status-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    padding: 1.25rem 1rem;
}

.status-overview .status-tile:hover {
    border-color: var(--accent);
    transform: translateY(-2px);
    box-shadow: var(--shadow-sm);
}

.status-tile-icon-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--radius-pill);
    background-color: var(--surface);
    color: var(--accent);
}

.status-tile-icon {
    width: 20px;
    height: 20px;
}

/* Per-module breakdown (Wiki/Fichiers/Pastes) — each in its own card instead
   of a plain heading, so the page reads as distinct panels rather than one
   long flat list. */
.status-section {
    background-color: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 1.25rem 1.5rem;
    margin-bottom: 1.25rem;
}

.status-section:last-child {
    margin-bottom: 0;
}

.status-section-header {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    margin-bottom: 1rem;
}

.status-section-icon-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 2rem;
    height: 2rem;
    border-radius: var(--radius-md);
    background-color: var(--bg-subtle);
    color: var(--accent);
}

.status-section-icon {
    width: 17px;
    height: 17px;
}

.status-section-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text);
    margin: 0;
}

/* --------------------------------------------------------------------------
   Icônes SVG partagées (partial _Icon) — tailles par contexte d'usage
   -------------------------------------------------------------------------- */

.admin-nav-icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    margin-right: 0.35rem;
    vertical-align: -0.15em;
}

.kebab-icon {
    width: 18px;
    height: 18px;
}

.btn-icon {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}

.row-icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--text-muted);
}

/* --------------------------------------------------------------------------
   Page d'accueil — tuiles vers les modules
   -------------------------------------------------------------------------- */

/* Two tiles per row (each still an internal icon | text horizontal layout),
   with .dashboard-tile--full (Administration) spanning both columns on its
   own row underneath — not a uniform single-column stack, so the page reads
   as a compact 2x2(+1) block instead of a long scroll of full-width rows. */
.dashboard-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
}

@media (max-width: 576px) {
    .dashboard-grid {
        grid-template-columns: 1fr;
    }
}

.dashboard-tile--full {
    grid-column: 1 / -1;
}

.dashboard-tile {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 1.25rem;
    width: 100%;
    padding: 1.5rem;
    background-color: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: var(--text);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
}

.dashboard-tile:hover,
.dashboard-tile:focus-visible {
    color: var(--text);
    border-color: var(--accent);
    box-shadow: var(--shadow-sm);
    transform: translateY(-2px);
}

.dashboard-tile-icon-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 3.25rem;
    height: 3.25rem;
    border-radius: var(--radius-md);
    background-color: var(--bg-subtle);
    color: var(--accent);
}

.dashboard-tile-icon {
    width: 28px;
    height: 28px;
}

.dashboard-tile-body {
    min-width: 0;
}

.dashboard-tile-title {
    font-size: 1.1rem;
    font-weight: 600;
    margin: 0;
}

.dashboard-tile-desc {
    font-size: 0.9rem;
    color: var(--text-muted);
    margin: 0.15rem 0 0;
}

/* --------------------------------------------------------------------------
   Wiki
   -------------------------------------------------------------------------- */

.wiki-content {
    line-height: 1.6;
}

.wiki-content :is(h1, h2, h3, h4, h5, h6) {
    margin-top: 1.5rem;
    margin-bottom: 0.75rem;
}

/* Explicit scale, capped below .auth-card h1's 1.5rem (base.css) — markdown content headings
   must never outsize the page's own title, which the browser/Bootstrap defaults (h2 ~2rem)
   otherwise do. */
.wiki-content h1 { font-size: 1.35rem; }
.wiki-content h2 { font-size: 1.2rem; }
.wiki-content h3 { font-size: 1.1rem; }
.wiki-content h4 { font-size: 1rem; }
.wiki-content h5,
.wiki-content h6 { font-size: 0.9rem; }

.wiki-content pre {
    padding: 0.75rem 1rem;
    border-radius: var(--radius-md);
    overflow-x: auto;
}

/* Markdown content is author-controlled and unpredictable in size — images
   and tables need their own overflow handling so one wide image or table
   can't push the whole page into horizontal scroll on a phone. */
.wiki-content img {
    max-width: 100%;
    height: auto;
}

.wiki-content table {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
}

.wiki-content code {
    padding: 0.15em 0.35em;
    border-radius: var(--radius-sm);
}

.wiki-content blockquote {
    border-left: 3px solid var(--border);
    margin-left: 0;
    padding-left: 1rem;
    color: var(--text-muted);
}

.wiki-link-missing {
    color: var(--color-danger);
    border-bottom: 1px dashed var(--color-danger);
    text-decoration: none;
}

.wiki-link-missing:hover,
.wiki-link-missing:focus {
    color: var(--color-danger-hover);
    border-bottom-color: var(--color-danger-hover);
}

.wiki-recent-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.wiki-recent-list li {
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--border);
}

.wiki-recent-list li:last-child {
    border-bottom: none;
}

.wiki-search-result {
    padding: 0.75rem 0;
    border-bottom: 1px solid var(--border);
}

.wiki-search-result:last-child {
    border-bottom: none;
}

.wiki-diff {
    white-space: pre-wrap;
    word-break: break-word;
    background-color: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 1rem;
    overflow-x: auto;
    font-size: 0.85rem;
}

.wiki-diff-line {
    display: block;
}

.wiki-diff-line--inserted {
    background-color: var(--color-success-bg);
    color: var(--color-success);
}

.wiki-diff-line--deleted {
    background-color: var(--color-danger-bg);
    color: var(--color-danger);
}

.wiki-tree-list,
.wiki-tree-children {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.85rem;
}

.wiki-tree-root {
    font-weight: 600;
    padding: 0.15rem 0;
}

.wiki-tree-children {
    margin-left: 0.7rem;
    padding-left: 0.7rem;
    border-left: 1px solid var(--border);
}

.wiki-tree-children li {
    padding: 0.15rem 0;
}

/* --------------------------------------------------------------------------
   Recherche — Bootstrap's .list-group-item hardcodes a light background/text
   with no dark-mode variant of its own, so the "Voir tous les résultats" page
   (Search/Index.cshtml) rendered as a white card even in dark mode.
   -------------------------------------------------------------------------- */

.list-group-item {
    background-color: var(--surface);
    border-color: var(--border);
    color: var(--text);
}

.list-group-item-action:hover,
.list-group-item-action:focus {
    background-color: var(--bg-subtle);
    color: var(--text);
}

/* --------------------------------------------------------------------------
   Fichiers / File-Drop — zone de dépôt tus (upload résumable)
   -------------------------------------------------------------------------- */

.file-manager-dropzone {
    border: 2px dashed var(--border);
    border-radius: var(--radius-md);
    padding: 1.25rem;
    text-align: center;
    transition: border-color var(--transition-fast), background-color var(--transition-fast);
}

.file-manager-dropzone--active {
    border-color: var(--accent);
    background-color: var(--bg-subtle);
}

.file-manager-upload-row .progress-bar {
    transition: width var(--transition-base);
}

.file-thumb {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    object-fit: cover;
    background-color: var(--bg-subtle);
}

.file-thumb-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
}

.file-thumb-icon .btn-icon {
    width: 18px;
    height: 18px;
}

.file-thumb-text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.6rem;
    line-height: 1.15;
    padding: 0.15rem 0.3rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background-color: var(--bg-subtle);
    color: var(--text-muted);
    cursor: default;
    white-space: pre-line;
}

[data-preview-trigger] {
    cursor: pointer;
}

/* --------------------------------------------------------------------------
   Listes tabulaires — cartes mobiles (FileManager/Paste/FileDrop Index, <
   576px). These tables have 4-7 columns; forcing that into table-responsive's
   horizontal scroll on a phone leaves a near-illegible sliver of each row —
   and Popper positioning the kebab dropdown's menu against that
   scrolling/clipping ancestor is what caused it to reopen far above the
   button for rows near the bottom (see the "boundary" comment in site.js).
   Below 576px the table itself is hidden (d-none d-sm-block) in favor of
   this stacked one-row-per-item card list instead — same data, same actions,
   no scrolling container involved at all, so the dropdown menu positions
   itself against the real viewport correctly.
   -------------------------------------------------------------------------- */

.row-card-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.row-card {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background-color: var(--bg-subtle);
}

.row-card-main {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    min-width: 0;
    flex: 1 1 auto;
}

.row-card-body {
    min-width: 0;
    flex: 1 1 auto;
}

.row-card-title {
    display: block;
    font-weight: 600;
}

.row-card-meta {
    margin-top: 0.1rem;
}

.row-card-extra {
    margin-top: 0.5rem;
}

.row-card-actions {
    flex-shrink: 0;
}

/* --------------------------------------------------------------------------
   Fichiers — modale d'aperçu (images/vidéo/audio)
   -------------------------------------------------------------------------- */

#filePreviewModal .modal-body {
    text-align: center;
}

#filePreviewImage,
#filePreviewVideo {
    max-width: 100%;
    max-height: 70vh;
}

#filePreviewAudio {
    width: 100%;
}

.file-preview-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 1;
}

.file-preview-nav--prev {
    left: 0.5rem;
}

.file-preview-nav--next {
    right: 0.5rem;
}

/* --------------------------------------------------------------------------
   Paste
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Bouton "copier le lien" — icône seule (site.js's [data-copy-text] handler)
   -------------------------------------------------------------------------- */

.copy-link-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.25rem 0.5rem;
}

.copy-link-icon-check {
    display: none;
}

.copy-link-btn.copied .copy-link-icon-copy {
    display: none;
}

.copy-link-btn.copied .copy-link-icon-check {
    display: inline;
    color: var(--color-success);
}

/* --------------------------------------------------------------------------
   Badge de statut cliquable (Fichiers, Pastes, File-Drop) — un <button>
   stylé comme un .badge Bootstrap plutôt qu'un composant switch séparé :
   la couleur affiche l'état, le clic soumet le formulaire qui le bascule.
   -------------------------------------------------------------------------- */

.badge-toggle {
    display: inline-block;
    min-width: 3.5rem;
    border: none;
    cursor: pointer;
    transition: filter var(--transition-base);
}

.badge-toggle:hover,
.badge-toggle:focus-visible {
    filter: brightness(1.15);
}

/* --------------------------------------------------------------------------
   Fichiers — en-têtes de colonnes triables (sortable-table.js)
   -------------------------------------------------------------------------- */

.sortable-th {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.sortable-th:hover {
    color: var(--accent);
}

.sortable-th-arrow {
    display: inline-block;
    margin-left: 0.25rem;
    font-size: 0.7em;
    color: var(--accent);
}

.paste-content {
    background-color: var(--bg-subtle);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 1rem;
    white-space: pre-wrap;
    word-break: break-word;
    overflow-x: auto;
}

/* highlight.js's own theme (github.min.css/github-dark.min.css, swapped in Pretty.cshtml) sets
   the code block's colors — this just matches paste-content's card framing so the two views
   (raw vs. colorée) feel like the same feature. */
.paste-pretty {
    border-radius: var(--radius-md);
    overflow-x: auto;
}

.paste-pretty code {
    display: block;
    padding: 1rem;
    border-radius: var(--radius-md);
}

/* --- Camera module ------------------------------------------------------------------
   Full-viewport 2x2 grid. The grid's height is set in JS (camera-gallery.js/fitGrid) —
   everything here only has to make the cells share whatever height it was given, which
   is what all the min-height: 0 declarations are for: without them the image imposes its
   natural size and the grid overflows instead of letting itself be compressed. */

.camera-page {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.camera-bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.camera-crumbs {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: wrap;
}

.camera-crumbs a {
    color: var(--link);
    text-decoration: none;
}

.camera-crumbs a:hover {
    text-decoration: underline;
}

.camera-sep {
    color: var(--text-tertiary);
}

.camera-pager {
    display: flex;
    align-items: center;
    gap: 0.35rem;
}

.camera-range,
.camera-count {
    font-variant-numeric: tabular-nums;
}

.camera-count {
    margin-left: auto;
}

.camera-folders {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.camera-chip {
    padding: 0.25rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--text);
    text-decoration: none;
    font-size: 0.875rem;
}

.camera-chip:hover {
    border-color: var(--border-interactive);
    color: var(--text);
}

.camera-grid {
    display: grid;
    gap: 0.5rem;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    min-height: 0;
}

.camera-tile {
    position: relative;
    min-height: 0;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-subtle);
    cursor: pointer;
}

.camera-tile:hover,
.camera-tile:focus-visible {
    border-color: var(--accent);
    outline: none;
}

/* contain, not cover: these are camera frames, nothing may be cropped away. */
.camera-tile img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    opacity: 0;
    transition: opacity var(--transition-fast);
}

.camera-tile img.ready {
    opacity: 1;
}

/* The name sits ON TOP of the image: in a cell worth a quarter of the screen, a line of
   text underneath would take that height away from the image itself. */
.camera-label {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 1rem 0.5rem 0.35rem;
    font-size: 0.75rem;
    color: #f2f3f5;
    background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.72));
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.camera-msg,
.camera-root {
    margin: 0;
}

.camera-root {
    font-size: 0.75rem;
}

.camera-box {
    position: fixed;
    inset: 0;
    z-index: 1080;
    background: rgba(8, 9, 11, 0.96);
    flex-direction: column;
}

/* [hidden] would lose to display: flex, so the display rule is scoped to the open state
   instead of being overridden back. */
.camera-box:not([hidden]) {
    display: flex;
}

.camera-box-bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0.9rem;
    background: #14161a;
    color: #e6e8eb;
    font-size: 0.8125rem;
}

.camera-box-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.camera-box-pos {
    margin-left: auto;
    font-variant-numeric: tabular-nums;
}

.camera-box-stage {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 0;
    padding: 0.5rem;
}

.camera-box-stage img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.camera-box-hint {
    margin: 0;
    padding: 0 0.9rem 0.6rem;
    color: #6d7684;
    font-size: 0.75rem;
}

@media (max-width: 640px) {
    .camera-grid {
        grid-template-columns: 1fr;
        grid-template-rows: repeat(4, 1fr);
    }
}
