/* Preview station kiosk styling.
   These screens are unattended and full-bleed: the event's background fills the display,
   content sits centred on top, and there is no app chrome to navigate. Matches the
   Preview Flow screen sketches in docs/PreviewFlowDiagrams.

   Colours come from MudBlazor's palette variables, never hardcoded. Hardcoding a light
   panel paints light-on-light the moment the machine is in dark mode — which is exactly
   how the staff keypad ended up invisible. */

.preview-shell {
    /* The station never scrolls as a document: the event background stays fixed
       and long content scrolls INSIDE the floating card below — its rounded
       bottom corners stay on screen at all times. */
    height: 100vh;
    overflow: hidden;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    display: flex;
    flex-direction: column;
}

/* ── Scan screen ──────────────────────────────────────────────────────────── */

.preview-room-root {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
    padding: 3vh 4vw;
    /* Depth, so a flat brand colour does not read as an empty screen. */
    background: radial-gradient(ellipse at 50% 35%, rgba(255, 255, 255, 0.07), transparent 62%);
}

.scan-card {
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border-radius: 18px;
    padding: clamp(2rem, 5vh, 3.5rem) clamp(2rem, 5vw, 4rem);
    width: min(620px, 92vw);
    text-align: center;
    box-shadow: 0 24px 64px rgba(0, 0, 0, 0.4);
}

.scan-logo {
    display: block;
    margin: 0 auto 2rem;
    max-width: 320px;
    max-height: 170px;
    object-fit: contain;
}

/* Until an event uploads a logo, keep a deliberate title rather than a broken-image box.
   Height hugs the text — a fixed box left a dead gap underneath. */
.scan-logo--placeholder {
    height: auto;
    max-width: none;
    margin-bottom: 2.2rem;
    padding-bottom: 1.4rem;
    border-bottom: 1px solid var(--mud-palette-divider);
    color: var(--mud-palette-text-primary);
    font-size: clamp(1.35rem, 2.6vw, 1.85rem);
    font-weight: 300;
    letter-spacing: 0.15em;
    line-height: 1.3;
    text-transform: uppercase;
}

.scan-label {
    font-size: 1.2rem;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 1rem;
}

/* A badge scanner types fast and ends with Enter — this field must always look ready,
   and be large enough to read from standing height. */
.badge-scan-input {
    width: 100%;
    box-sizing: border-box;
    font-size: 1.5rem;
    text-align: center;
    padding: 0.7em 0.6em;
    border: 2px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    background: var(--mud-palette-background);
    color: var(--mud-palette-text-primary);
    outline: none;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}

.badge-scan-input:focus {
    border-color: var(--mud-palette-primary);
    box-shadow: 0 0 0 4px rgba(25, 118, 210, 0.18);
}

.badge-scan-input::placeholder {
    color: var(--mud-palette-text-secondary);
}

.scan-error {
    margin-top: 1.1rem;
    padding: 0.7rem 1rem;
    border-radius: 8px;
    background: rgba(211, 47, 47, 0.14);
    color: var(--mud-palette-error);
    font-size: 1rem;
}

/* Staff door: a PIN-pad icon, reachable but never competing with the speaker's
   content for attention. Sits on the event background, so it is light against
   that rather than themed; the tooltip carries the words. */
.staff-login-link,
.staff-login-link.mud-icon-button {
    position: absolute;
    bottom: 2.5vh;
    right: 2.5vw;
    background: none;
    border: none;
    color: rgba(255, 255, 255, 0.62);
    cursor: pointer;
    padding: 0.55rem;
    border-radius: 10px;
}

.staff-login-link:hover,
.staff-login-link:focus-visible,
.staff-login-link.mud-icon-button:hover,
.staff-login-link.mud-icon-button:focus-visible {
    color: #fff;
    background: rgba(255, 255, 255, 0.14);
    outline: none;
}

/* Staff door on card pages (speaker page): the card scrolls internally, so the
   button pins to the viewport corner instead of the page flow. */
.staff-login-link--fixed,
.staff-login-link--fixed.mud-icon-button {
    position: fixed;
    z-index: 950; /* above the card and scroll thumb, below the PIN overlay */
}

/* ── Staff PIN entry ──────────────────────────────────────────────────────────
   Sits over the whole station, not below the scan card. Without these rules the
   component renders in the page flow, which is what made it look bolted on. The panel
   itself is a MudPaper, so it themes correctly on its own — do not restyle it. */

.staff-pin-overlay {
    position: fixed;
    inset: 0;
    z-index: 1400;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 4vh 4vw;
    background: rgba(10, 12, 24, 0.72);
    backdrop-filter: blur(3px);
}

.staff-pin-overlay .mud-paper {
    width: min(400px, 92vw);
    border-radius: 14px !important;
    box-shadow: 0 22px 60px rgba(0, 0, 0, 0.45) !important;
}

.staff-pin-overlay .pin-display {
    font-size: 2rem;
    letter-spacing: 0.5rem;
    color: var(--mud-palette-text-primary);
    /* The dots are fixed-width glyphs; centring needs the trailing space trimmed. */
    text-indent: 0.5rem;
    min-height: 2.4rem;
}

.staff-pin-overlay .mud-button-root {
    min-height: 54px;
    font-size: 1.25rem;
}

/* ── Operator finder: stat strip + sectioned surfaces ─────────────────────────
   Dark-UI elevation done properly: layered surfaces (page < section < card <
   hover), 1px low-alpha borders instead of hard lines, and the operator's four
   most actionable numbers in the prime top strip. Touch targets ≥44px — these
   run on kiosk touchscreens. */

.op-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 12px;
    margin-bottom: 22px;
}

.op-stat {
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
    border: 1px solid rgba(255, 255, 255, 0.07);
    border-radius: 14px;
    padding: 14px 18px;
}

.op-stat__value {
    font-size: 1.7rem;
    font-weight: 600;
    line-height: 1.15;
    color: var(--mud-palette-text-primary);
}

.op-stat__value--good { color: var(--mud-palette-success); }
.op-stat__value--warn { color: var(--mud-palette-warning); }

.op-stat__label {
    font-size: 0.72rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    margin-top: 2px;
}

.op-section {
    background: rgba(255, 255, 255, 0.02);
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 14px;
    padding: 16px 18px;
    margin-bottom: 18px;
}

.op-section__header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

.op-section__count {
    background: rgba(255, 255, 255, 0.08);
    border-radius: 999px;
    padding: 1px 10px;
    font-size: 0.72rem;
    color: var(--mud-palette-text-primary);
}

/* Filter inputs: fill the row by RATIO, never by content. Verified against the
   rendered DOM: MudSelect/MudAutocomplete wrap their input control in an OUTER
   `<div class="mud-select">` / `<div class="mud-autocomplete">` that receives
   neither Style nor Class — THAT wrapper is the row's flex child, so it must
   carry the flex rules (matched via :has on the class we control). flex-basis 0
   makes the shares the only thing that sizes them; min-width 0 lets a long
   selected title clip instead of grow. */
.op-filter-row > .mud-select,
.op-filter-row > .mud-autocomplete {
    min-width: 0;
}

.op-filter-row > .mud-select:has(.op-filter--room),
.op-filter-row > .mud-autocomplete:has(.op-filter--room) {
    flex: 1 1 0;
}

.op-filter-row > .mud-select:has(.op-filter--session),
.op-filter-row > .mud-autocomplete:has(.op-filter--session) {
    flex: 2 1 0;
}

.op-filter {
    width: 100%;
    min-width: 0;
}

.op-filter .mud-input-slot,
.op-filter input {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Section pager: compact page buttons + a type-in page box ("[6] / 31"). */
.op-pager {
    display: flex;
    align-items: center;
    gap: 8px;
}

.op-pager__input {
    width: 56px;
    text-align: center;
    padding: 4px 2px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 6px;
    background: transparent;
    color: var(--mud-palette-text-primary);
    font-size: 0.85rem;
    appearance: textfield;
    -moz-appearance: textfield;
}

.op-pager__input::-webkit-outer-spin-button,
.op-pager__input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.op-pager__input:focus {
    outline: none;
    border-color: var(--mud-palette-primary);
}

.op-pager__total {
    color: var(--mud-palette-text-secondary);
    font-size: 0.8rem;
    white-space: nowrap;
}

/* Participant / session rows: touch-sized, hover lifts a surface level. */
.op-row {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 48px;
    padding: 6px 12px;
    margin-bottom: 6px;
    border: 1px solid rgba(255, 255, 255, 0.05);
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.015);
    transition: background 100ms ease, border-color 100ms ease;
}

.op-row:hover {
    background: rgba(255, 255, 255, 0.05);
    border-color: rgba(255, 255, 255, 0.12);
}

/* ── Presentation card (speaker page, finder, session view) ───────────────────
   One shared shape for every presentation row, matching the screen sketches:
   thumbnail with the speaker's photo overlapping its corner, session line with
   the loaded dot, two-line clamped title, actions bottom-right. */

.presentation-card {
    display: flex;
    gap: 14px;
    align-items: stretch;
    border: 1px solid rgba(255, 255, 255, 0.06);
    border-radius: 12px;
    padding: 10px 14px 10px 10px;
    margin-bottom: 10px;
    background: rgba(255, 255, 255, 0.025);
    transition: background 100ms ease, border-color 100ms ease;
}

.presentation-card:hover {
    background: rgba(255, 255, 255, 0.055);
    border-color: rgba(255, 255, 255, 0.13);
}

.presentation-card__media {
    position: relative;
    flex: 0 0 auto;
    /* Room for the avatar that hangs off the thumbnail's lower-left corner. */
    padding: 0 0 14px 14px;
}

.presentation-card__thumb {
    display: block;
    width: 108px;
    height: 68px;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-background);
}

.presentation-card__avatar {
    position: absolute;
    left: 0;
    bottom: 0;
}

.presentation-card__body {
    flex: 1 1 auto;
    min-width: 0; /* lets the session title actually truncate */
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.presentation-card__session-line {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.presentation-card__room {
    font-weight: 600;
    white-space: nowrap;
}

.presentation-card__time {
    white-space: nowrap;
    color: var(--mud-palette-text-secondary);
}

.presentation-card__session-title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
}

.presentation-card__dot {
    flex: 0 0 auto;
    width: 14px;
    height: 14px;
    border-radius: 50%;
}

.presentation-card__dot--loaded {
    background: var(--mud-palette-success);
}

.presentation-card__dot--missing {
    background: var(--mud-palette-error);
}

/* Two lines, then ellipsis — presentation titles run long by design. */
.presentation-card__title {
    color: var(--mud-palette-text-secondary);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-clamp: 2;
    overflow: hidden;
}

.presentation-card__footer {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: auto;
}

.presentation-card__spacer {
    flex: 1 1 auto;
}

/* ── Screens that carry content (finder, speaker page, manage, session) ───── */

.operator-page,
.speaker-page-root,
.manage-page,
.session-view-page,
.file-manager-page {
    flex: 1;
    /* Fill the shell to 2.5vh short of each edge and scroll internally — the
       card floats at a constant height just under the screen's. */
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    margin: 2.5vh auto;
    padding: 2.5vh 2.5vw;
    width: min(1600px, 96vw);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border-radius: 14px;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.3);
}

/* ── File manager panels ─────────────────────────────────────────────────────
   Flex row instead of a 5/2/5 grid: the arrow keeps only its own width and the
   two work panels split the rest. Each panel wears a colored top accent so the
   page reads "pick here → lands there" instead of two grey slabs. */

.fm-panels {
    display: flex;
    align-items: stretch;
    gap: 12px;
}

.fm-panel {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.fm-arrow {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 4px;
}

@media (max-width: 959px) {
    .fm-panels { flex-direction: column; }
    .fm-arrow { display: none; }
}

.fm-panel--local.mud-paper,
.fm-panel--server.mud-paper {
    border-radius: 10px;
    border: 1px solid var(--mud-palette-lines-default);
    border-top-width: 3px;
}

.fm-panel--local.mud-paper {
    border-top-color: var(--mud-palette-primary);
    background: linear-gradient(180deg, rgba(var(--mud-palette-primary-rgb), 0.07), transparent 140px),
                var(--mud-palette-surface);
}

.fm-panel--server.mud-paper {
    border-top-color: var(--mud-palette-success);
    /* No --mud-palette-success-rgb variable exists — fixed rgba of the success green. */
    background: linear-gradient(180deg, rgba(0, 200, 83, 0.05), transparent 140px),
                var(--mud-palette-surface);
}

/* Drag handle between the Name and Modified columns. */
.fm-col-resize {
    flex: 0 0 auto;
    width: 7px;
    align-self: stretch;
    cursor: col-resize;
    touch-action: none;
    border-left: 1px solid var(--mud-palette-lines-default);
}

.fm-col-resize:hover {
    border-left-color: var(--mud-palette-primary);
}

/* ── File manager dropzone (browser upload path) ─────────────────────────────
   The invisible InputFile fills the whole zone, so both click-to-choose and a
   native drag-drop land on the real <input type=file> — no JS involved. */

.fm-dropzone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 180px;
    padding: 24px;
    border: 2px dashed var(--mud-palette-lines-default);
    border-radius: 10px;
    cursor: pointer;
    text-align: center;
    transition: border-color 120ms ease, background-color 120ms ease;
}

.fm-dropzone:hover,
.fm-dropzone--active {
    border-color: var(--mud-palette-primary);
    background: rgba(var(--mud-palette-primary-rgb), 0.06);
}

.fm-dropzone__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

/* ── Connection state: reconnect overlay + hub badge ─────────────────────────
   Driven by the inline recovery loop in _Host.cshtml. Both live outside the
   Blazor root so they keep rendering while the circuit is down. */

#st-reconnect-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 2000; /* above everything, including the PIN overlay */
    align-items: center;
    justify-content: center;
    background: rgba(10, 10, 20, 0.82);
    backdrop-filter: blur(3px);
}

#st-reconnect-overlay.st-reconnect-overlay--visible {
    display: flex;
}

.st-reconnect-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    padding: 36px 48px;
    border-radius: 14px;
    background: var(--mud-palette-surface, #25253a);
    color: var(--mud-palette-text-primary, #e4e4f0);
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.5);
    text-align: center;
    max-width: 420px;
}

.st-reconnect-title { font-size: 1.25rem; font-weight: 600; }
.st-reconnect-message { font-size: 0.95rem; opacity: 0.8; }

.st-reconnect-spinner {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    border: 4px solid rgba(255, 255, 255, 0.15);
    border-top-color: var(--mud-palette-primary, #796eff);
    animation: st-reconnect-spin 0.9s linear infinite;
}

@keyframes st-reconnect-spin {
    to { transform: rotate(360deg); }
}

#st-hub-status {
    display: none;
    position: fixed;
    bottom: 6px;
    left: 8px;
    z-index: 940; /* under the staff PIN button and overlays */
    align-items: center;
    gap: 6px;
    padding: 2px 8px;
    border-radius: 10px;
    background: rgba(0, 0, 0, 0.35);
    color: rgba(255, 255, 255, 0.55);
    font-size: 0.7rem;
    pointer-events: none;
}

#st-hub-status.st-hub-status--visible { display: flex; }

.st-hub-status__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #4caf50;
}

#st-hub-status.st-hub-status--down .st-hub-status__dot {
    background: #ef5350;
    animation: st-hub-blink 1s ease-in-out infinite;
}

@keyframes st-hub-blink {
    50% { opacity: 0.3; }
}

/* Scrollbars: the native bar is hidden outright and station-scrollbar.js draws
   a plain 6px thumb instead. This engine generation (Chromium 15x / WebView2 151)
   ignores ::-webkit-scrollbar styling, and Edge's fluent bar paints stepper
   arrows in every mode — an owned thumb is the only arrow-less, corner-nestled
   bar we can guarantee. */

.operator-page,
.speaker-page-root,
.manage-page,
.session-view-page,
.file-manager-page,
.preview-shell .kiosk-form > .mud-grid-item {
    scrollbar-width: none;
}

.st-scroll-thumb {
    position: fixed;
    width: 6px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.28);
    opacity: 0;
    z-index: 900; /* above the cards, below the PIN overlay (1400) and dialogs */
    transition: opacity 160ms ease, background 120ms ease;
    cursor: default;
    touch-action: none;
}

.st-scroll-thumb:hover,
.st-scroll-thumb:active {
    background: rgba(255, 255, 255, 0.45);
}

/* ── Manage page: header → centred actions → exits pinned bottom-right ─────── */

.manage-page {
    display: flex;
    flex-direction: column;
}

/* The four big actions own the middle of whatever height the card has. */
.manage-actions {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.manage-actions .mud-grid {
    width: 100%;
}

/* The disabled Edit tile is wrapped in a MudTooltip whose inline root otherwise
   collapses the tile — make the wrapper (and its tile) fill the grid cell. */
.manage-actions .mud-tooltip-root {
    display: block;
    width: 100%;
    height: 100%;
}

.manage-actions .mud-tooltip-root > .manage-tile {
    height: 100%;
}

/* ── Manage action tiles ──────────────────────────────────────────────────────
   Each action wears its own colour in a tinted icon disc. State is the design:
   with no file, Upload glows as the starting point and the others sit switched
   off (still tappable — the page explains why). */

.manage-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    min-height: 160px;
    padding: 24px 16px;
    border-radius: 16px;
    border: 1px solid rgba(255, 255, 255, 0.07);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.015));
    cursor: pointer;
    transition: transform 120ms ease, border-color 120ms ease, background 120ms ease, box-shadow 120ms ease;
}

.manage-tile:hover:not(.manage-tile--off) {
    transform: translateY(-3px);
    border-color: var(--tile-colour, var(--mud-palette-primary));
    box-shadow: 0 14px 34px rgba(0, 0, 0, 0.35);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));
}

.manage-tile:active:not(.manage-tile--off) {
    transform: translateY(-1px);
}

.manage-tile__icon {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--tile-colour, var(--mud-palette-primary));
    background: color-mix(in srgb, var(--tile-colour, var(--mud-palette-primary)) 16%, transparent);
    transition: background 120ms ease;
}

.manage-tile:hover:not(.manage-tile--off) .manage-tile__icon {
    background: color-mix(in srgb, var(--tile-colour, var(--mud-palette-primary)) 26%, transparent);
}

.manage-tile--upload { --tile-colour: var(--mud-palette-primary); }
.manage-tile--view   { --tile-colour: var(--mud-palette-info); }
.manage-tile--edit   { --tile-colour: var(--mud-palette-secondary); }
.manage-tile--done   { --tile-colour: var(--mud-palette-success); }

/* No file yet: these actions have nothing to act on. */
.manage-tile--off {
    opacity: 0.35;
    filter: saturate(0.4);
    cursor: not-allowed;
}

/* No file yet: Upload is the one thing to do — let it breathe. */
.manage-tile--start {
    border-color: color-mix(in srgb, var(--mud-palette-primary) 55%, transparent);
    animation: manage-tile-beckon 2.6s ease-in-out infinite;
}

@keyframes manage-tile-beckon {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--mud-palette-primary) 34%, transparent); }
    50% { box-shadow: 0 0 24px 3px color-mix(in srgb, var(--mud-palette-primary) 22%, transparent); }
}

.manage-exits {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 16px;
}

/* ── Admin forms shown INSIDE the kiosk shell (ContextualFormLayout) ──────────
   The KioskForm cascade already removed the admin sidebar; these rules give the
   remaining single column the kiosk card look — centred, capped, floating on the
   event background — instead of a full-bleed admin webpage. */

.preview-shell .kiosk-form {
    width: min(1000px, 94vw);
    margin: 0.5vh auto 2.5vh;
    /* Same floating-card contract as the preview pages: the shell never scrolls,
       so the form column takes the remaining height and scrolls inside. */
    flex: 1 1 auto;
    min-height: 0;
}

/* The COLUMN is the card: one rounded scrolling surface, however many MudCards
   (form, station media, …) stack inside it. */
.preview-shell .kiosk-form > .mud-grid-item {
    max-height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    background: var(--mud-palette-surface);
    border-radius: 14px;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.3);
    padding: 1.5rem 2rem;
}

.preview-shell .kiosk-form .mud-card {
    border-radius: 0;
    box-shadow: none;
    background: transparent;
}

/* The admin PageHeader above a kiosk form: align it with the card and let the
   event background show through rather than painting an app-bar strip. */
.preview-shell > .mud-paper:has([data-testid='page-header-title']) {
    width: min(1000px, 94vw);
    margin: 2.5vh auto 0.5vh;
    background: transparent;
    box-shadow: none;
}

/* ── Fluid scale across kiosk display sizes ───────────────────────────────────
   1920×1080 is the baseline. Bigger glass scales type up — :has() scopes the
   root scale to pages inside the kiosk shell, so admin browsing on a 4K desktop
   keeps its density. Small tablets tighten the frame instead of shrinking text. */

/* QHD / ultrawide (2560×1440, 3440×1440) */
@media (min-width: 2200px) {
    :root:has(.preview-shell) {
        font-size: 18px;
    }

    .operator-page,
    .speaker-page-root,
    .manage-page,
    .session-view-page,
    .file-manager-page {
        width: min(1900px, 90vw);
    }
}

/* 4K (3840×2160) — 3440-wide ultrawides stay on the QHD tier above: their pixel
   density is 1440p-class, only their aspect is wider. */
@media (min-width: 3600px) {
    :root:has(.preview-shell) {
        font-size: 22px;
    }

    .operator-page,
    .speaker-page-root,
    .manage-page,
    .session-view-page,
    .file-manager-page {
        width: min(2400px, 86vw);
    }
}

/* Tablets (iPad landscape and below): full-bleed frame, wrapping toolbars. */
@media (max-width: 1100px) {
    .operator-page,
    .speaker-page-root,
    .manage-page,
    .session-view-page,
    .file-manager-page {
        width: 100%;
        margin: 0;
        border-radius: 0;
        padding: 2vh 3vw;
        box-shadow: none;
    }

    .op-toolbar,
    .op-filter-row {
        flex-wrap: wrap;
    }

    /* Wrapped filters take the full row each rather than fighting for slivers. */
    .op-filter-row > .mud-select,
    .op-filter-row > .mud-autocomplete {
        flex: 1 1 100%;
    }

    .preview-shell .kiosk-form,
    .preview-shell > .mud-paper:has([data-testid='page-header-title']) {
        width: 100%;
    }
}
