/* Die Tafel, herausgelöst aus webui.css (BPU-310). Nur die .monitor-* Regeln, die die Tafel selbst
   zeichnet; nichts aus der Verwaltung, kein MudBlazor. */

@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/inter-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/inter-700.woff2') format('woff2');
}
@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/roboto-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Roboto';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/roboto-700.woff2') format('woff2');
}
@font-face {
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/open-sans-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/open-sans-700.woff2') format('woff2');
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/source-sans-400.woff2') format('woff2');
}
@font-face {
    font-family: 'Source Sans 3';
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url('../fonts/source-sans-700.woff2') format('woff2');
}

* {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
    height: 100%;
    background: #0b0b0d;
    color: #fff;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    -webkit-text-size-adjust: 100%;
}

.monitor-boot {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #9aa0a6;
    font-size: 5vh;
}

#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 0.75rem 1rem;
    background: #ffe082;
    color: #1a1a1a;
    z-index: 100;
}

/* ---------------------------------------------------------------------------
   Monitor board (guest-facing, full screen, no chrome).
   Sizes are in vw/vh so the same markup fills a 24" counter screen and a 65"
   wall panel without a breakpoint.
   --------------------------------------------------------------------------- */

.monitor-root {
    position: fixed;
    inset: 0;
    color: #fff;
    overflow: hidden;
    font-variant-numeric: tabular-nums;

    /* A hand's width off the bezel. Without it the headings and the outer tiles sit flush against
       the edge of the screen, which on a wall-mounted display reads as a cropped picture rather
       than a designed one. box-sizing so the inset:0 above still fills the screen exactly. */
    padding: 10px;
    box-sizing: border-box;
}

.monitor-columns {
    display: grid;
    height: 100%;
    box-sizing: border-box;
}

/* auto-fit + a zero minimum collapses empty tracks: two sections get two equal columns, but when a
   group is hidden (ShowPreparing/ShowReady false) and only one <section> renders, the grid quietly
   becomes one column instead of leaving an empty half. */
.monitor-columns.two-col { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); }

/* One column means the two lists STACK, not that the ready list disappears. Ready goes on top: it is
   the only thing the guest is actually waiting for. */
/* Halves, not auto: a section whose height comes from its content gives .monitor-tiles below nothing
   definite to fit its rows into, and the stacked board goes back to overflowing the screen. */
.monitor-columns.one-col {
    grid-template-columns: 1fr;
    grid-template-rows: minmax(0, 1fr) minmax(0, 1fr);
}

.monitor-columns.one-col .monitor-col-ready { order: 1; }
.monitor-columns.one-col .monitor-col-preparing { order: 2; }

/* 3vh base for the .monitor-box heading below: MonitorStyleCss writes an inline
   font-size:{FontScalePercent}% onto the <h2>, and a percentage resolves against the PARENT, not the
   element itself. This is that base - see the comment on .monitor-box before "fixing" it back there. */
.monitor-col {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
    font-size: 3vh;
}

.monitor-box {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 0.5em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 1vh 1.5vh;
    margin: 0 0 1.5vh 0;
    text-align: center;
    /* font-size deliberately NOT set here: MonitorStyleCss writes an inline font-size:{FontScalePercent}%
       on this element, and a percentage resolves against the PARENT. The responsive base therefore lives
       on .monitor-col above - putting a vh size here too would simply be overridden by the inline style
       and the scale would be dead (this is exactly the bug BPU-19 fixed: font-size:100% resolving
       against nothing but the browser's 16px default). */
}

.monitor-box-lines {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15em;
    min-width: 0;
}

.monitor-box-image {
    flex: 0 0 auto;
    height: 1.6em;
    width: auto;
    max-width: 30%;
    object-fit: contain;
}

.monitor-box-sup {
    font-size: 0.42em;
    font-weight: 500;
    letter-spacing: 0.04em;
    text-transform: none;
    opacity: 0.75;
    line-height: 1.2;
}

.monitor-box-text {
    line-height: 1.1;
}

/* The measuring container, and the reason it is a separate element from the grid it wraps: an element
   with container-type:size cannot answer its OWN cq queries, and a font-size in cqh on the container
   itself is a circular dependency that hangs the renderer outright (it did). flex-basis is 0, not auto,
   for the same family of reason - a basis taken from content would let the tiles size the box that is
   supposed to be sizing them. */
.monitor-tiles-fit {
    flex: 1 1 0;
    min-height: 0;
    container-type: size;
}

.monitor-tiles {
    display: grid;
    height: 100%;
    gap: 1.2vh;
    /* The configured rows ARE the layout, not just the cap on how many tiles get sent. Fixed 1fr tracks
       are what stop the list from growing past the bottom of its .monitor-col, and what stop one tile
       with a larger inline FontScalePercent (the just-ready highlight) from pushing its neighbours. */
    grid-template-rows: repeat(var(--monitor-rows, 1), minmax(0, 1fr));

    /* 8vh base for .monitor-tile below - same reasoning as .monitor-col/.monitor-box above. It is also
       the fallback: a browser without container query units keeps the old fixed size rather than none. */
    font-size: 8vh;
    /* ...but the base a row-built grid needs is the row's own height, so a 100% scale means "fills its
       row" instead of "8vh, whatever the row turned out to be". 1.35 leaves the 1.3 line-height its
       room; the gaps are taken off the top because they are not the rows' to spend. */
    font-size: calc((100cqh - (var(--monitor-rows, 1) - 1) * 1.2vh) / var(--monitor-rows, 1) / 1.35);
}

.monitor-tile {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1.5vw;
    font-weight: 700;
    line-height: 1.3;
    text-align: center;
    /* font-size deliberately NOT set here: MonitorStyleCss writes an inline
       font-size:{FontScalePercent}% on this element, and a percentage resolves against the
       PARENT. The responsive base therefore lives on .monitor-tiles - putting a vh size here
       too would simply be overridden by the inline style and the scale would be dead. */
}

.monitor-tile-ready { color: #4ade80; }

.monitor-columns.lanes .monitor-tile-ready {
    justify-content: space-between;
    padding-left: 1.5vw;
    padding-right: 1.5vw;
}

.monitor-destination {
    font-size: 0.7em;
    font-weight: 600;
    opacity: 0.85;
    white-space: nowrap;
}

.monitor-tile-ready.is-just-ready { animation: monitor-pulse 1.2s ease-in-out 3; }

.monitor-overflow {
    font-size: 2.5vh;
    opacity: 0.55;
    margin-top: 1.5vh;
}

.monitor-notice {
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #0b0b0d;
    text-align: center;
    padding: 4vh 4vw;
}

.monitor-notice-text {
    font-size: 5vh;
    font-weight: 600;
}

/* Colour-blind mode drops the green/grey cue entirely. What carries the status is the column and the
   heading over it - the same thing that carries it in normal mode, which is why dropping the colour
   here costs the guest nothing. */
.monitor-root.cb-safe .monitor-tile-ready { color: #fff; }

@keyframes monitor-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

@media (prefers-reduced-motion: reduce) {
    .monitor-tile-ready.is-just-ready { animation: none; outline: 0.4vh solid currentColor; outline-offset: 0.6vh; }
}

.monitor-root:has(.monitor-banner) {
    display: flex;
    flex-direction: column;
}

.monitor-root:has(.monitor-banner) .monitor-stage {
    flex: 1 1 0;
    min-height: 0;
    height: auto;
}

.monitor-banner {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 3vw;
    padding: 1vh 2vw;
    box-sizing: border-box;
}

.monitor-banner-text {
    margin: 0;
    flex: 1 1 0;
    max-width: 32ch;
    font-size: 1.6vh;
    line-height: 1.35;
}

.monitor-banner:has(.monitor-banner-qr) .monitor-banner-text:first-child {
    text-align: right;
}

.monitor-banner-qr {
    flex: 0 0 auto;
    height: 100%;
    max-height: 22vh;
    width: auto;
    aspect-ratio: 1;
    background: #ffffff;
    padding: 0.6vh;
    border-radius: 0.4vh;
}

/* ── Media on the board (BPU-102) ──────────────────────────────────────────────────────────── */

/* The stage: the board, and beside it the side panel when there is one (BPU-125).

   :has() rather than a class passed down from the component, which is what BPU-90 already does for
   the bottom banner. The layout follows what is actually rendered, so a panel that is not drawn — no
   media, or the switch off — leaves the board exactly as it was. */
.monitor-stage {
    height: 100%;
    min-height: 0;
}

.monitor-stage:has(.monitor-media-panel) {
    display: grid;
    /* The same share the interruption gives its ready column, from the other side: the board is what
       the guest came for and keeps the room. minmax so a narrow screen cannot squeeze the panel into
       a stripe. */
    grid-template-columns: 1fr minmax(18rem, 26%);
    grid-template-rows: minmax(0, 1fr);
    gap: 10px;
}

/* A board with no columns is a screen for the window (BPU-205). The empty .monitor-columns would
   still hold its 1fr track and leave the media in the quarter beside it, so the track goes away and
   the panel takes the stage. */
.monitor-stage.media-only:has(.monitor-media-panel) {
    grid-template-columns: minmax(0, 1fr);
}

.monitor-stage.media-only .monitor-columns {
    display: none;
}

.monitor-media-panel {
    position: relative;
    overflow: hidden;
    width: 100%;
    height: 100%;
}

.monitor-media-frame {
    position: absolute;
    inset: 0;
}

.monitor-media-frame.is-on { z-index: 2; }
.monitor-media-frame.is-off { z-index: 1; }

/* Die drei Arten (BPU-149). Die Dauer und die Farbe kommen als Variablen vom Bauteil, alles andere
   steht hier: zwei Zahlen unterscheiden zwei Läden, der Rest ist überall gleich. */
.monitor-media-panel.transition-none .monitor-media-frame { transition: none; }

/* Beim Wisch tauschen die Rahmen hart, verdeckt von der Fläche darüber. Blendeten sie zusätzlich
   über, wären für einen Moment beide Bilder zu sehen, sobald die Blende länger dauert als die
   Fläche deckt, und genau das soll der Wisch verhindern. */
.monitor-media-panel.transition-wipe .monitor-media-frame { transition: none; }

.monitor-media-wipe {
    position: absolute;
    inset: 0;
    z-index: 3;
    pointer-events: none;
    background: var(--media-wipe-color, #0b0b0d);
    transform: translateX(-100%);
    animation: monitor-media-wipe var(--media-transition-ms, 400ms) linear forwards;
}

/* Eine Richtung, von links. Bei 50 % deckt die Fläche die ganze Seite, und das ist der Moment, in
   dem das Bauteil die Rahmen tauscht. `forwards` lässt sie danach rechts außerhalb stehen. */
@keyframes monitor-media-wipe {
    0% { transform: translateX(-100%); }
    50% { transform: translateX(0); }
    100% { transform: translateX(100%); }
}

.monitor-media-frame img,
.monitor-media-frame video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.monitor-media-panel.transition-fade .monitor-media-frame.is-on img,
.monitor-media-panel.transition-fade .monitor-media-frame.is-on video {
    animation: monitor-media-fadein var(--media-transition-ms, 400ms) cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes monitor-media-fadein {
    from { opacity: 0; }
    to { opacity: 1; }
}

.monitor-ad {
    position: fixed;
    inset: 0;
    z-index: 40;
    display: grid;
    /* Rows, not columns (BPU-126). The row is `auto`, so a board with nothing ready gives the picture
       the whole screen instead of an empty bar. */
    grid-template-rows: 1fr auto;
    background: #000;
}

.monitor-ad-media { position: relative; overflow: hidden; }

.monitor-ad-media img,
.monitor-ad-media video {
    width: 100%;
    height: 100%;
    /* contain, and that is what "centred" means here: the whole picture, in the middle, never cropped
       and never stretched. A shop's own artwork has writing on it. */
    object-fit: contain;
    display: block;
}

/* The strip along the bottom. It never disappears while an advertisement runs — that is the condition
   under which the interruption exists at all: the guest is standing there for a number. */
.monitor-ad-ready {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 1.5rem;
    overflow: hidden;
    padding: 0.75rem 1rem;
    background: rgba(0, 0, 0, 0.85);
}

/* A tile in the strip sizes itself to its number instead of filling a column, and the strip is read
   from across a room. 5vh is the board's 8vh scaled to a strip. */
.monitor-ad-ready {
    font-size: 5vh;
}

/* Das Wort tritt zurück, die Nummern bleiben das Laute: aus zwei Metern trifft die Zahl, das Wort
   erklärt sie. Es schrumpft nicht mit, wenn viele Nummern anstehen — dann weicht es lieber. */
.monitor-ad-ready-label {
    flex: 0 1 auto;
    margin-right: 0.5rem;
    font-size: 0.5em;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.75;
    white-space: nowrap;
}

.monitor-ad-ready .monitor-tile {
    flex: 0 0 auto;
    padding: 0.25rem 1rem;
}

/* Nothing ready means no strip, not an empty black bar across the picture. */
.monitor-ad-ready:empty {
    display: none;
}

.monitor-sound-unlock {
    position: fixed;
    right: 1.5rem;
    bottom: 1.5rem;
    z-index: 60;
    padding: 0.75rem 1.25rem;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.6);
    color: #fff;
    font-size: 1rem;
    cursor: pointer;
}

@media (prefers-reduced-motion: reduce) {
    .monitor-media-panel.transition-fade .monitor-media-frame.is-on img,
    .monitor-media-panel.transition-fade .monitor-media-frame.is-on video {
        animation: none;
    }

    /* Wer Bewegung abbestellt hat, bekommt auch keinen Wisch: die Fläche bleibt links außerhalb,
       das Bauteil tauscht weiter zur halben Zeit, und übrig bleibt ein harter Schnitt. */
    .monitor-media-wipe {
        animation: none;
        transform: translateX(-100%);
    }
}
