/* CLC Resident Calendar — full-screen month view for a wall-mounted TV.

   EVERY COLOUR, SIZE AND WEIGHT BELOW COMES FROM A CUSTOM PROPERTY, which
   index.php writes into a :root block from the saved theme. The fallbacks in
   each var() are the shipped defaults, so this file still renders correctly
   on its own — opened straight from disk, or if the theme fails to load. */

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

html, body {
    height: 100vh; width: 100vw; overflow: hidden;
    background: var(--clc-bg, #0a0e17);
    color: var(--clc-text, #e0e0e0);
    font-family: var(--clc-font, 'Inter', system-ui, sans-serif);
}

/* ── Layout ───────────────────────────────────────────────── */

.clc-wrap {
    position: relative;
    height: 100vh; width: 100vw;
    /* The inset exists because a TV bezel and overscan eat the outer
       centimetre. Written onto the element itself from the saved value. */
    padding: var(--clc-pad, 0px);
}
.clc-inner {
    position: relative;
    width: 100%; height: 100%;
    overflow: hidden;
    /* Its own setting now, rather than something inferred from the inset. */
    border-radius: var(--clc-radius, 0);
    display: flex; flex-direction: column;
}

/* ── Header ───────────────────────────────────────────────── */

/* ── Header ───────────────────────────────────────────────
   OUT OF THE FLOW, so its size is not paid for by the calendar. The only
   thing holding space for it is .clc-header-reserve below, which is a fixed
   height — so enlarging the title, clock or date overlaps DOWN INTO the grid
   rather than squeezing a row out of it.

   The reserve follows the overall size, because that slider is meant to move
   the whole display; it does not follow the three header sliders, which is
   the entire point. */
.clc-header {
    position: absolute; top: 0; left: 0; right: 0;
    /* THREE COLUMNS, THE OUTER TWO EQUAL. Flex with a spacer either side
       only centres the clock when the title and the date happen to be the
       same width, which they never are — the clock ended up starting at the
       centre line rather than sitting on it. Equal outer columns put the
       middle one on the centre of the screen whatever is beside it.

       minmax(0, 1fr), not 1fr: a bare 1fr is minmax(AUTO, 1fr), and that
       auto lets a long title widen its own column and shove the clock off
       centre again — the same trap as the calendar's rows. */
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center; gap: 20px;
    padding: 10px 24px;
    z-index: 10;
    /* Solid for the height it normally occupies, then fading out — so text
       that has grown past the reserve lies over the grid without a hard edge
       cutting across it. */
    background: linear-gradient(to bottom,
                var(--clc-bg, #0a0e17) 0%,
                var(--clc-bg, #0a0e17) 62%,
                transparent 100%);
    /* Anything hanging below the solid part is over grid content, and this
       is what keeps it readable there. */
    text-shadow: 0 calc(2px * var(--clc-scale, 1))
                   calc(6px * var(--clc-scale, 1))
                   var(--clc-bg, #0a0e17);
}

.clc-header-reserve {
    flex-shrink: 0;
    height: calc(62px * var(--clc-scale, 1));
}
/* THE TITLE IS THE ONE THAT GIVES WAY. All three can be scaled
   independently, and between them they can want more width than the header
   has; something has to yield, and the clock and the date are the two a
   corridor actually reads. So the title shrinks and ellipsises while those
   two keep their size. */
/* Left column. min-width: 0 so a long title ellipsises inside its own
   column instead of widening it and pushing the clock off centre. */
.clc-title-group {
    display: flex; align-items: center; gap: 10px;
    min-width: 0;
    justify-self: start;
}
/* The fallback mark and an uploaded one are sized by the same expression, so
   swapping a logo in does not change how big the corner reads. Height only —
   the width follows the image's own proportions, which is what stops a wide
   logo being squashed into a square. */
.clc-cal-icon {
    font-size: calc(32px * var(--clc-scale, 1) * var(--clc-header-scale, 1)
                         * var(--clc-logo-scale, 1));
    line-height: 1; flex-shrink: 0;
}
.clc-logo {
    height: calc(32px * var(--clc-scale, 1) * var(--clc-header-scale, 1)
                      * var(--clc-logo-scale, 1));
    width: auto; max-width: 28vw;
    object-fit: contain;
    display: block; flex-shrink: 0;
}
/* Set to match .clc-date below — same weight, same case, same spacing — so
   the two ends of the header read as one piece of typography rather than a
   masthead with a caption beside it. The title keeps full opacity; the date
   is the one that is dimmed, being the lesser of the two. */
/* ONE SIZE FOR ALL FOUR HEADER TEXTS. The title and the alternate title
   share this slot; the date and the facility share the other. They swap for
   one another when the slide changes, so they are driven from one base and
   one variable — otherwise a swap is also a change of size, and the header
   jumps every time the rotation moves. */
.clc-title {
    font-size: calc(26px * var(--clc-scale, 1) * var(--clc-header-scale, 1));
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
    white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
    /* The clip is at the padding edge, so this is a descender's tail rather
       than empty space — the same reason the event lines carry it. */
    padding-bottom: 0.1em;
}
.clc-spacer { flex: 1; min-width: 8px; }
/* The middle column, sized to its own content and therefore sitting on the
   centre line of the header — which, the header being full width, is the
   centre line of the screen. */
.clc-time {
    font-size: calc(42px * var(--clc-scale, 1) * var(--clc-clock-scale, 1));
    font-weight: 700;
    letter-spacing: 2px; white-space: nowrap;
    font-variant-numeric: tabular-nums;
    justify-self: center;
    /* The letter-spacing adds a gap after the last character, which would
       otherwise shift the whole clock half a space to the left of centre. */
    padding-left: 2px;
}
/* Right column, hard against the edge. Same min-width: 0 and ellipsis as
   the title — a long facility name must not widen its column either. */
.clc-date {
    font-size: calc(26px * var(--clc-scale, 1) * var(--clc-header-scale, 1));
    font-weight: 600;
    white-space: nowrap; opacity: 0.85;
    text-align: right;
    justify-self: end;
    min-width: 0;
    overflow: hidden; text-overflow: ellipsis;
}
.clc-time[hidden], .clc-date[hidden] { display: none; }

/* The date slot holding the facility instead. SAME SIZE — that is the point
   of the lock. A facility name is usually far longer than a date, and it
   gives way by ellipsising inside its column, which .clc-date already
   does. */

/* ── Swapping one line for another ─────────────────────────
   The outgoing text lifts and fades, the incoming rises into its place.
   Applied to the header title and the date slot when the slide changes. */
.clc-title, .clc-date {
    transition: opacity 0.22s ease, transform 0.22s ease;
}
.clc-swap-out { opacity: 0; transform: translateY(-0.35em); }
.clc-swap-in  { opacity: 0; transform: translateY(0.35em); }

@media (prefers-reduced-motion: reduce) {
    .clc-title, .clc-date { transition: none; }
    .clc-swap-out, .clc-swap-in { opacity: 1; transform: none; }
}

/* ── Calendar grid ────────────────────────────────────────── */

/* ── The two views ─────────────────────────────────────────
   Stacked in one box so a crossfade does not move the header or the inset.
   Only one is ever un-hidden; the fade is done by dropping the outgoing one
   to zero, swapping, and bringing the incoming one up, rather than by
   overlapping two opaque panels. */

.clc-views { flex: 1; position: relative; min-height: 0; }
.clc-view {
    position: absolute; inset: 0;
    transition: opacity 0.45s ease;
}
.clc-view[hidden] { display: none; }
.clc-view.fading { opacity: 0; }

.clc-calendar {
    display: flex; flex-direction: column;
    overflow: hidden;
    background: var(--clc-grid-bg, #0d1117);
}
.clc-dow-row {
    display: grid; grid-template-columns: repeat(7, 1fr);
    background: var(--clc-dow-bg, rgba(255, 255, 255, 0.04));
    flex-shrink: 0;
}
/* Carries the theme's accent, and at the heaviest weight the chosen typeface
   has. The 50% opacity it used to sit behind has gone with it — dimming an
   accent colour only produces a washed-out version of it, which is the
   opposite of making the row read. */
.clc-dow {
    padding: 6px 10px; text-align: center;
    font-size: calc(13px * var(--clc-scale, 1));
    font-weight: 900;
    text-transform: var(--clc-dow-case, uppercase);
    letter-spacing: 1px;
    color: var(--clc-accent, #4285F4);
}
.clc-grid {
    flex: 1;
    /* WITHOUT THIS THE LAST WEEK FALLS OFF THE BOTTOM. A flex item's
       automatic minimum size is its min-content height, so a grid full of
       busy days refuses to shrink into the space it was given and simply
       overflows — and .clc-calendar clips what sticks out, taking the last
       row with it. */
    min-height: 0; min-width: 0;
    display: grid;
    /* minmax(0, 1fr), not 1fr: a bare 1fr is minmax(AUTO, 1fr), and that
       auto lets a column be pushed wider by its contents. */
    grid-template-columns: repeat(7, minmax(0, 1fr));
}
.clc-day {
    border: 1px solid var(--clc-line, rgba(255, 255, 255, 0.06));
    padding: 4px 6px; overflow: hidden;
    display: flex; flex-direction: column;
    min-height: 0; min-width: 0;
}
/* Three kinds of "not today", and they are deliberately separate settings.
   A day belonging to the month either side is context; a day of THIS month
   that has already gone is finished business. The two want different
   weights, and calendar.js never puts both classes on one cell. */
.clc-day.other-month { opacity: var(--clc-other, 0.3); }
.clc-day.past        { opacity: var(--clc-past, 0.45); }
.clc-day.today { background: var(--clc-today-bg, rgba(66, 133, 244, 0.12)); }

/* ── The light that runs round today ───────────────────────
 *
 * A CONIC GRADIENT CLIPPED TO A THIN RING. What you see is a bright arc
 * travelling the edge of today's cell and fading back into the grid line
 * behind it. The tint alone said "today" to somebody already looking at the
 * right cell; this is what finds the cell for somebody walking past, and it
 * does it without the cell flashing or moving.
 *
 * HOW IT IS A RING AND NOT A FILLED PANEL: .clc-trace is masked with two
 * stacked boxes — one the size of its content area, one the whole element —
 * composited so they cancel. What is left is the difference between them,
 * which is the ring. There is no other way to put a gradient in a border.
 *
 * THE GRADIENT DOES NOT MOVE. THE ELEMENT UNDER IT TURNS.
 *
 * This started as one pseudo-element whose conic gradient was spun by
 * animating a registered <angle> custom property, which is how most of the web
 * does it and is what tools.automatons.dev does on its menu cards. It hitched.
 * A gradient is built by the rasteriser, not by the compositor, so animating
 * the angle re-generates the whole cell-sized gradient on the main thread
 * EVERY FRAME — and this display has a busy main thread: a clock ticking every
 * second, a grid rebuilt every five minutes, a slide rotation that re-deals 35
 * cells, and fit searches that force ten layouts in a row. The arc stalled
 * whenever any of those ran. On a hover state that lasts a second nobody
 * notices; on a television running all day it is the thing you see.
 *
 * So the gradient is now STATIC and the box holding it is rotated with a
 * transform. A transform is the one property the compositor can animate by
 * itself: the gradient is rasterised once, and each frame after that is the
 * same texture drawn at a different angle. The main thread can be as busy as
 * it likes and the arc keeps turning.
 *
 * WHY A SEPARATE ELEMENT AND NOT A PSEUDO-ELEMENT. The thing that spins and
 * the thing that is masked into a ring cannot be the same box: rotating the
 * mask would swing the ring off the cell's edges. So the ring is the parent
 * and the gradient is a child inside it. .clc-day.today::after is already the
 * TODAY badge, which leaves no second pseudo-element — hence real markup,
 * written by calendar.js into today's cell only.
 *
 * INSIDE THE GRID LINE, NOT INSTEAD OF IT. inset: 0 on an absolutely
 * positioned child is the padding box, so the ring sits immediately within the
 * cell's own border rather than replacing it — and the border is structural
 * here, part of the grid, not decoration on one cell. At a television's
 * viewing distance the two read as one edge. */

/* The containing block for the ring and the badge. On .today only: giving
   every cell a stacking context costs 35 of them for one ring. */
.clc-day.today { position: relative; }

.clc-trace {
    position: absolute; inset: 0;
    /* OFF IS NO ELEMENT AT ALL, not a paused one: a stopped conic gradient is
       a lopsided smear across one corner, which is worse than no ring. The
       same display switch --clc-dot-display uses. */
    display: var(--clc-trace-display, block);
    /* Thickness, and it scales with the display like everything else — a ring
       fixed at 2px would thin away to nothing as the calendar is scaled up.
       It is padding because the mask below is what turns the padding edge into
       the inner edge of the ring. */
    padding: calc(2px * var(--clc-scale, 1));
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    /* The mask already clips the oversized child to the ring. This bounds the
       layer as well, so the compositor is not handed a texture three times the
       size of the cell to hold on to. */
    overflow: hidden;
    pointer-events: none;
}

/* THE PART THAT TURNS. Oversized and centred, because a box that only covered
   the cell would swing its own corners into view as it rotated and the ring
   would break up four times a lap. At 280% the box's inscribed circle is
   comfortably wider than the cell's diagonal at every proportion a month cell
   takes — seven columns against five or six rows, on any screen shape — so
   some part of the gradient is over every edge at every angle.

   The gradient's stops are rays from the centre, so stretching the box to a
   non-square shape does not bend them: they stay straight, and rotating the
   box rotates them. */
.clc-trace span {
    position: absolute; top: 50%; left: 50%;
    width: 280%; height: 280%;
    /* NOT a percentage margin — those resolve against the containing block's
       WIDTH on all four sides, so the vertical centring would be wrong on
       every cell that is not square. */
    transform: translate(-50%, -50%);
    /* The arc: transparent for most of the turn, so what travels is a head and
       a tail rather than a band going all the way round. */
    background: conic-gradient(
                transparent 0deg, transparent 50deg,
                var(--clc-trace-a, #8ab4f8) 95deg,
                var(--clc-trace-b, #4285F4) 130deg,
                transparent 185deg, transparent 360deg);
    animation: clc-day-trace var(--clc-trace-dur, 3s) linear infinite;
    /* Ask for the layer up front. Without it the first lap pays for the
       promotion, which is a stutter exactly where it is most visible — as the
       month slide arrives. */
    will-change: transform;
}

/* The translate is repeated in both keyframes because a transform animation
   replaces the whole property, not just the rotation. Leave it out and the
   element snaps to the centre point rather than being centred ON it. */
@keyframes clc-day-trace {
    from { transform: translate(-50%, -50%) rotate(0deg); }
    to   { transform: translate(-50%, -50%) rotate(360deg); }
}

/* SOMEBODY WHO HAS ASKED FOR LESS MOTION STILL GETS THE MARKER, just not the
   travel: the gradient is dropped for a plain ring in the accent, because a
   conic gradient held still is an arc parked on one side of the cell and reads
   as a rendering fault rather than as a decision. */
@media (prefers-reduced-motion: reduce) {
    .clc-trace span {
        animation: none;
        background: var(--clc-trace-b, #4285F4);
        will-change: auto;
    }
}

/* ── The TODAY badge on the month grid ─────────────────────
 *
 * The word, in the corner opposite the date. The circle and the trace both
 * say "this one" in colour and movement; this is the one that says it in
 * English, which is what a visitor reads rather than decodes.
 *
 * IT IS CONTENT IN CSS ON PURPOSE. The cell is drawn by a loop that runs 35
 * times, and a badge built there would be 35 conditionals and a second piece
 * of markup to keep in step with the .today class that already exists. The
 * class is the condition; ::after is the badge. The month grid is also the one
 * view that never changes its wording — the day view's badge says "Today" or
 * "Tomorrow" depending on the slide, and that one IS built in calendar.js
 * because it has something to decide.
 *
 * ::before IS THE TRACE RING, so this is ::after. One cell, two pseudo
 * elements, both spoken for — anything further would need real markup.
 *
 * CENTRED ON THE DATE CIRCLE, not on the top padding: 4px of cell padding plus
 * half the circle's scaled height puts the anchor on the circle's centre line,
 * and translateY pulls the badge back by half of its own height. So the word
 * sits level with the number at every display scale instead of drifting up as
 * the calendar grows. */
.clc-day.today::after {
    content: 'TODAY';
    position: absolute;
    top: calc(4px + 12px * var(--clc-scale, 1));
    /* Clear of the trace ring, which is 2px of scaled thickness at the edge. */
    right: calc(2px + 2px * var(--clc-scale, 1));
    transform: translateY(-50%);
    display: var(--clc-badge-display, block);
    /* The day view's badge, shrunk to a month cell: same weight, same
       letter-spacing, same accent pill with the page colour as its ink. One
       badge in two sizes rather than two badges. */
    font-size: calc(9.5px * var(--clc-scale, 1));
    font-weight: 800;
    letter-spacing: 0.14em;
    line-height: 1;
    /* A WHITE HALO ON DARK INK ON THE ACCENT. The badge's text is the page
       colour and the pill behind it is the accent, which is a narrow contrast
       at this size — the word was reading as a shape before it read as a word.
       The glow puts a light edge between the two without changing either
       colour. Same no-offset, two-pass build as the date circle's dark glow,
       and the blurs scale with the display for the same reason: so it stays a
       halo instead of hardening into an outline. */
    text-shadow: 0 0 calc(2px * var(--clc-scale, 1)) rgba(255, 255, 255, 0.95),
                 0 0 calc(5px * var(--clc-scale, 1)) rgba(255, 255, 255, 0.65);
    /* Right pad trimmed: letter-spacing adds its gap after the last letter
       too, so equal padding looks heavier on the right than on the left. */
    padding: 0.42em 0.5em 0.42em 0.64em;
    border-radius: 999px;
    color: var(--clc-bg, #0a0e17);
    background: var(--clc-accent, #4285F4);
    white-space: nowrap;
    pointer-events: none;
    animation: clc-day-badge var(--clc-badge-dur, 2s) ease-in-out infinite;
}

/* FADED DOWN, NOT OUT. A badge that leaves completely is a hole in the corner
   half the time and the eye keeps going back to check whether it is still
   there; held well short of gone it stays a word that is pulsing rather than a
   word that keeps vanishing. ease-in-out rather than steps for the same reason
   — a hard square blink on a screen that is on all day is a smoke alarm.

   THE FLOOR IS HIGH ON PURPOSE. At a tenth the badge was legible at the top of
   the pulse and a smudge at the bottom, which made the word itself look like
   it was failing rather than breathing. Just under half keeps every letter
   readable through the whole cycle and leaves the movement to do the work. */
@keyframes clc-day-badge {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
}

/* Less motion: the word stays, the pulse goes. It is the badge that carries
   the meaning — the blink only draws the eye to it. */
@media (prefers-reduced-motion: reduce) {
    .clc-day.today::after { animation: none; }
}
.clc-day-num {
    font-size: calc(13px * var(--clc-scale, 1));
    font-weight: 700;
    margin-bottom: 2px; flex-shrink: 0;
}
.clc-day.today .clc-day-num {
    background: var(--clc-accent, #4285F4); color: #fff;
    width: calc(24px * var(--clc-scale, 1));
    height: calc(24px * var(--clc-scale, 1));
    border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    /* A dark glow around the digits, sitting on the circle rather than behind
       it. White on the accent is the one pairing in the month with no dark
       edge anywhere near it, and on a television across a corridor the number
       softened into its own badge. A shadow separates them without darkening
       the circle or taking the text off white.

       NO OFFSET, and two of them. An offset would read as a drop shadow and
       put the weight on one side of a digit centred in a circle; spread at
       0 0 it stays a halo. The tight pass does the separating and the wide one
       gives it somewhere to fall off to — one alone is either a hard outline
       or a smudge. Both scale with the display, so it stays a glow at every
       size instead of becoming an outline as the calendar is scaled up. */
    text-shadow: 0 0 calc(3px * var(--clc-scale, 1)) rgba(0, 0, 0, 1),
                 0 0 calc(8px * var(--clc-scale, 1)) rgba(0, 0, 0, 0.85);
}
/* min-height: 0 for the same reason as the grid — this is the box whose
   measured height decides how many event lines a day can show.

   The shadow lives here so the event lines AND the "N more" line beneath
   them inherit the one value. Offsets scale with the text, so it stays a
   shadow rather than becoming an outline as the display is scaled up. It is
   worth having because an event line sits on three different backgrounds —
   plain grid, the tint on today, and whatever colour the theme is set to —
   and a shadow is what keeps it legible on all three without tying the text
   colour to any of them. */
.clc-events {
    flex: 1; min-height: 0; overflow: hidden;
    text-shadow: 0 calc(1px * var(--clc-scale, 1))
                   calc(2px * var(--clc-scale, 1))
                   rgba(0, 0, 0, var(--clc-evt-shadow, 0.55));
}
.clc-evt {
    display: flex; align-items: flex-start; gap: 4px;
    font-size: calc(11.5px * var(--clc-scale, 1) * var(--clc-event-scale, 1));
    font-weight: var(--clc-evt-weight, 400);
    /* Room for the descenders. These lines clip their overflow to fit the
       cell, so a line box shorter than the font's descent takes the tail off
       every g, y and p — and the taller the text is scaled, the more of it
       goes. The month still truncates with an ellipsis, because a cell this
       size cannot wrap; the enlarged day view is where long titles get a
       second line. */
    line-height: 1.38;
    padding: 1px 0; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
}
.clc-evt-dot {
    display: var(--clc-dot-display, block);
    width: calc(7px * var(--clc-scale, 1));
    height: calc(7px * var(--clc-scale, 1));
    border-radius: 50%;
    flex-shrink: 0;
    margin-top: calc(3px * var(--clc-scale, 1));
    /* text-shadow does not reach a dot drawn as a background, so it gets the
       same offsets of its own — otherwise the text lifts off the cell and
       the dot stays flat beside it. */
    box-shadow: 0 calc(1px * var(--clc-scale, 1))
                  calc(2px * var(--clc-scale, 1))
                  rgba(0, 0, 0, var(--clc-evt-shadow, 0.55));
}
.clc-evt-text {
    overflow: hidden; text-overflow: ellipsis;
    /* The clip happens at the padding edge, so this is the last few pixels
       of a descender rather than empty space. */
    padding-bottom: 0.12em;
}
.clc-evt-more {
    font-size: calc(10px * var(--clc-scale, 1) * var(--clc-event-scale, 1));
    opacity: 0.5; padding-top: 1px;
}

/* ── The day cells arriving, one after another ─────────────

   Run ONCE, on the first paint. The grid redraws every five minutes and at
   midnight; replaying this each time would make a wall display in a corridor
   twitch all day for no reason. calendar.js adds .clc-animate only on the
   first render and takes it off afterwards.

   The per-cell delay arrives as --d, computed in JS because the dealing order
   is its own setting and nth-child cannot express "diagonal" or "outwards
   from the middle" without 42 hand-written rules.

   ONE CAMERA FOR ALL 35 CARDS. The perspective lives on the grid, not inside
   each cell's transform. That distinction is the whole effect: a cell with
   its own perspective() is viewed head-on from its own centre, so every cell
   turns identically and the result reads as 35 flat flips. Seen from a single
   vanishing point over the middle of the grid, a card in the corner presents
   at a different angle from one in the middle — which is what makes them read
   as separate objects lying in a room. */

.clc-grid {
    perspective: var(--clc-anim-persp, 1200px);
    perspective-origin: center center;
}

@keyframes clc-in-fade  { from { opacity: 0; } }
@keyframes clc-in-rise  { from { opacity: 0; transform: translateY(14px); } }
@keyframes clc-in-scale { from { opacity: 0; transform: scale(0.90); } }
@keyframes clc-in-slide { from { opacity: 0; transform: translateX(-22px); } }

/* ── 3D cards ──────────────────────────────────────────────
   Each pivots on the edge it would really be hinged to — set per style as
   --clc-anim-origin, so one rule below serves all of them.

   The shadow is declared only in the `from` frame and interpolates to the
   cell's own (absent) shadow, so a card carries its shadow down and loses it
   on landing without a second keyframe or anything to clean up. */

@keyframes clc-in-fold {
    from {
        opacity: 0;
        transform: rotateX(-88deg);
        box-shadow: 0 28px 44px rgba(0, 0, 0, 0.55);
    }
}
@keyframes clc-in-card {
    from {
        opacity: 0;
        transform: rotateY(-85deg);
        box-shadow: 22px 20px 44px rgba(0, 0, 0, 0.55);
    }
}
@keyframes clc-in-swing {
    from {
        opacity: 0;
        transform: rotateX(78deg);
        box-shadow: 0 -22px 44px rgba(0, 0, 0, 0.5);
    }
}
@keyframes clc-in-deal {
    from {
        opacity: 0;
        /* Thrown in from above and behind the screen, the way a dealt card
           arrives — depth first, angle second. */
        transform: translate3d(-14%, -34%, -680px) rotateX(46deg) rotateZ(-14deg);
        box-shadow: 0 40px 70px rgba(0, 0, 0, 0.6);
    }
}
@keyframes clc-in-tumble {
    from {
        opacity: 0;
        transform: translateZ(-420px) rotateX(-62deg) rotateY(48deg) scale(0.82);
        box-shadow: 0 30px 60px rgba(0, 0, 0, 0.55);
    }
}

.clc-grid.clc-animate .clc-day {
    animation-name: var(--clc-anim-name, clc-in-rise);
    animation-duration: var(--clc-anim-dur, 420ms);
    animation-timing-function: cubic-bezier(0.22, 0.61, 0.36, 1);
    animation-fill-mode: both;
    animation-delay: var(--d, 0ms);
    transform-origin: var(--clc-anim-origin, center center);
    /* Only while the class is on, so the compositor is not asked to hold 35
       promoted layers for a display that then sits still for five minutes. */
    will-change: transform, opacity;
    backface-visibility: hidden;
}

/* A card turning on its edge sweeps outside the cell it belongs to, and the
   calendar clips its content. Lifted for the length of the animation only;
   .clc-inner still clips at the edge of the display, so cards arrive from
   off-screen rather than from nowhere. */
.clc-calendar:has(.clc-grid.clc-animate) { overflow: visible; }

/* Somebody who has asked their machine for less movement gets the finished
   grid, not a faster version of the same thing. */
@media (prefers-reduced-motion: reduce) {
    .clc-grid.clc-animate .clc-day { animation: none; }
}

/* ── Today, enlarged ───────────────────────────────────────
   The other half of the rotation: one day, read from across a corridor.

   The type SIZES ITSELF to the number of events. --clc-dv-rows is written by
   calendar.js, and each line takes the smaller of a comfortable cap and the
   share of the height going spare. A day with three things on it gets big
   type; a day with twelve gets smaller type and still fits, instead of
   quietly losing the last few off the bottom. */

.clc-dayview {
    display: flex; flex-direction: column;
    overflow: hidden;
    background: var(--clc-grid-bg, #0d1117);
    padding: 3vh 4vw;
}

.clc-dv-head {
    flex-shrink: 0;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 3vw;
    margin-bottom: 2.5vh;
    padding-bottom: 1.8vh;
    border-bottom: 2px solid var(--clc-line, rgba(255, 255, 255, 0.06));
}
.clc-dv-heading { min-width: 0; }

/* ── The TODAY badge ───────────────────────────────────────
   Pinned opposite the day name.

   THE WORD ITSELF NEVER MOVES. Everything that animates is a ring on a
   pseudo-element behind it — a badge whose text pulses or slides is a badge
   that is harder to read than a static one, which is the opposite of what a
   marker on a wall display is for. */
.clc-dv-badge {
    position: relative;
    flex-shrink: 0;
    align-self: center;
    font-size: calc(2.9vh * var(--clc-day-scale, 1));
    font-weight: 800;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    line-height: 1;
    padding: 0.62em 1.05em 0.62em 1.23em;   /* right pad trimmed: letter-spacing adds a gap after the last letter */
    border-radius: 999px;
    color: var(--clc-bg, #0a0e17);
    background: var(--clc-accent, #4285F4);
    white-space: nowrap;
}
.clc-dv-badge[hidden] { display: none; }

/* The ring: expands out of the badge's own edge and fades. Sized off the
   badge so it stays in proportion at any text size. */
.clc-dv-badge::after {
    content: '';
    position: absolute; inset: 0;
    border-radius: inherit;
    border: 0.12em solid var(--clc-accent, #4285F4);
    animation: clc-badge-ring 2.8s cubic-bezier(0.22, 0.61, 0.36, 1) infinite;
    pointer-events: none;
}
@keyframes clc-badge-ring {
    0%        { transform: scale(1);    opacity: 0.85; }
    70%, 100% { transform: scale(1.35); opacity: 0; }
}

/* Arriving with the view, once — same treatment as the cards. */
.clc-dv-badge.entering { animation: clc-badge-in var(--clc-anim-dur, 620ms) cubic-bezier(0.34, 1.56, 0.64, 1) both; }
@keyframes clc-badge-in {
    from { opacity: 0; transform: translateY(-0.5em) scale(0.75); }
}

@media (prefers-reduced-motion: reduce) {
    .clc-dv-badge::after { animation: none; opacity: 0; }
    .clc-dv-badge.entering { animation: none; }
}
.clc-dv-day {
    font-size: calc(9vh * var(--clc-day-scale, 1));
    font-weight: 800; line-height: 1.05;
    letter-spacing: -0.01em;
    color: var(--clc-accent, #4285F4);
    text-transform: uppercase;
}
.clc-dv-date {
    font-size: calc(4vh * var(--clc-day-scale, 1));
    font-weight: 600; opacity: 0.6;
    margin-top: 0.4vh;
}

/* A list that is travelling fades at both ends, so the top and bottom lines
   arrive and leave rather than being sliced off by a hard edge — and it says
   there is more without a scrollbar, which a television has no use for. */
.clc-dv-list.scrolling {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 4%, #000 96%, transparent 100%);
            mask-image: linear-gradient(to bottom, transparent 0, #000 4%, #000 96%, transparent 100%);
}

.clc-dv-list {
    --clc-dv-rows: 6;
    /* Stepped down by calendar.js until everything fits. Starts at 1; the
       row count below is only a first guess, and a wrapped title makes it
       wrong by exactly the amount nobody can predict from the event count. */
    --clc-dv-fit: 1;
    flex: 1; min-height: 0;
    display: flex; flex-direction: column;
    justify-content: flex-start;
    gap: 0.9vh;
    overflow: hidden;
}
.clc-dv-evt {
    display: flex; align-items: baseline; gap: 1.6vw;
    font-size: calc(min(calc(7.5vh * var(--clc-day-scale, 1)),
                        calc(66vh / var(--clc-dv-rows)))
                    * var(--clc-dv-fit, 1));
    /* 1.22 sliced the tail off every g, y and p: the line box was shorter
       than the font's own descent, and the list clips its overflow. */
    line-height: 1.32;
}
.clc-dv-time {
    flex-shrink: 0;
    min-width: 22%;
    text-align: right;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--clc-accent, #4285F4);
    white-space: nowrap;
}
/* NOT truncated. A long title wraps and its continuation hangs in from the
   title's own left edge, so a second line reads as the same entry carrying
   on rather than as another event. No overflow:hidden here either — that is
   what was cutting the descenders off, and with wrapping there is nothing
   left for it to do. */
.clc-dv-title {
    font-weight: 500;
    min-width: 0;
    overflow-wrap: break-word;
    padding-left: 0.9em;
    text-indent: -0.9em;
}
/* The bracketed half, on its own line. text-indent: 0 cancels the hanging
   indent's negative pull, so this line starts at the title's padding edge —
   indented under the first line rather than flush with it. */
.clc-dv-sub {
    display: block;
    text-indent: 0;
    opacity: 0.82;
}
/* A bracket small enough to stay on the line is at least kept whole, so if
   the line does run out the break lands in front of it, never inside it. */
.clc-dv-nb { white-space: nowrap; }

.clc-dv-dot {
    display: var(--clc-dot-display, block);
    width: 0.9em; height: 0.9em; border-radius: 50%;
    flex-shrink: 0;
    /* Sat on the first line rather than centred across a wrapped entry. */
    align-self: flex-start;
    margin-top: 0.21em;
}
.clc-dv-empty {
    flex: 1; display: flex; align-items: center; justify-content: center;
    font-size: calc(5vh * var(--clc-day-scale, 1));
    opacity: 0.4;
}

/* The lines arrive one after another, like the month's cards do. Uses the
   same gap setting, so the two views feel like one display rather than two. */
.clc-dv-list.clc-animate .clc-dv-evt {
    animation: clc-dv-in var(--clc-anim-dur, 620ms) cubic-bezier(0.22, 0.61, 0.36, 1) both;
    animation-delay: var(--d, 0ms);
}
@keyframes clc-dv-in { from { opacity: 0; transform: translateX(-3vw); } }

@media (prefers-reduced-motion: reduce) {
    .clc-dv-list.clc-animate .clc-dv-evt { animation: none; }
    .clc-view { transition: none; }
}

/* ── The week ──────────────────────────────────────────────
   Seven tall cards: denser than the day view, far larger than a month cell.
   Each is a card in its own right rather than a cell in a grid, which is
   what lets today lift out of the row. */

.clc-weekview {
    display: flex; flex-direction: column;
    overflow: hidden;
    background: var(--clc-grid-bg, #0d1117);
    padding: 2.5vh 1.6vw;
}
/* TWO ROWS: five weekdays over two weekend days.
   Seven across a 16:9 screen gives each card a seventh of the width, which
   is too narrow for an event title — "At 1st floor VA Chapel or streamed on
   CH 35:" wrapped five lines deep and the text had to balloon to fill the
   height, which made it worse rather than better. Five across is nearly half
   again as wide.

   Ten columns, every card spanning two: five fill the first row, and the
   weekend starts the second beneath Monday and Tuesday, the same size as
   everything above it. The remaining six columns of that row are left
   empty — seven equal cards do not make a rectangle, and the alternative
   was stretching two of them to two and a half times the rest. */
.clc-wk-grid {
    flex: 1; min-height: 0;
    display: grid;
    grid-template-columns: repeat(10, minmax(0, 1fr));
    /* Equal rows as well as equal columns: with every card the same width,
       a shorter second row would leave them the same width and a different
       size, which is not what "the same" means. Seven cards, all identical. */
    grid-template-rows: 1fr 1fr;
    gap: 0.9vh 0.9vw;
    /* One camera for all seven, as with the month's cards — so the cards at
       the edges turn at a different angle from the ones in the middle. */
    perspective: var(--clc-anim-persp, 1200px);
    perspective-origin: center 40%;
}

.clc-wk-day {
    grid-column: span 2;            /* a weekday: five of these fill the row */
    min-width: 0; min-height: 0;
    display: flex; flex-direction: column;
    border-radius: 1vh;
    padding: 1.4vh 0.7vw;
    background: color-mix(in srgb, var(--clc-text) 4%, transparent);
    border: 1px solid var(--clc-line, rgba(255, 255, 255, 0.06));
    overflow: hidden;
}
/* A weekend day is the SAME WIDTH as a weekday, and placed explicitly on the
   RIGHT of the second row — Saturday under Thursday, Sunday under Friday.
   Left to fall where it would, the weekend started the row and left the
   corner that matters empty at the far end. */
.clc-wk-day.sat { grid-area: 2 / 7 / 3 / 9; }
.clc-wk-day.sun { grid-area: 2 / 9 / 3 / 11; }

/* ── "This week", bottom left ──────────────────────────────
   The six columns the weekend vacated. Not a card: no border, no panel —
   it is the slide's own title, so it sits in the space rather than in a box
   competing with the seven that do. */
.clc-wk-label {
    grid-area: 2 / 1 / 3 / 7;
    min-width: 0; min-height: 0;
    display: flex; flex-direction: column;
    justify-content: center;
    padding: 0 1vw;
}
/* --clc-wk-label-fit is searched for by calendar.js, the same way the day
   cards' text is: this corner is six columns wide and a whole row tall, and
   a fixed size left most of it empty. nowrap is what makes that measurable —
   an unwrapped line's width is exactly proportional to its font size, so one
   measurement gives the answer and there is nothing for a search to find. */
.clc-wk-label-text {
    font-size: calc(9vh * var(--clc-wk-label-fit, 1));
    white-space: nowrap;
    font-weight: 800;
    letter-spacing: -0.015em;
    line-height: 1.02;
    /* THE SHIMMER. A gradient wider than the text, clipped to the glyphs and
       swept across them — so the accent travels through the word rather than
       the word itself moving, which on a wall display would be a distraction
       rather than a flourish. */
    background: linear-gradient(100deg,
                var(--clc-text, #e0e0e0) 0%,
                var(--clc-text, #e0e0e0) 38%,
                var(--clc-accent, #4285F4) 50%,
                var(--clc-text, #e0e0e0) 62%,
                var(--clc-text, #e0e0e0) 100%);
    background-size: 300% 100%;
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    animation: clc-wk-shimmer 7s linear infinite;
}
@keyframes clc-wk-shimmer {
    from { background-position: 150% 0; }
    to   { background-position: -150% 0; }
}
/* Driven from the same multiplier, so the date keeps its proportion to the
   word above it however far that grows. */
.clc-wk-label-range {
    font-size: calc(3.4vh * var(--clc-wk-label-fit, 1));
    white-space: nowrap;
    font-weight: 600;
    opacity: 0.5;
    margin-top: 0.6vh;
}

/* It deals in with the cards, last of all. */
.clc-wk-grid.clc-animate .clc-wk-label {
    animation: clc-wk-in var(--clc-anim-dur, 620ms) cubic-bezier(0.22, 0.61, 0.36, 1) both;
    animation-delay: var(--d, 0ms);
    transform-origin: center bottom;
}

.clc-wk-day.past { opacity: var(--clc-past, 0.45); }

/* TODAY LIFTS OUT OF THE ROW. Scaled a little, tinted, and ringed in the
   accent — the one column somebody crossing a corridor should find first. */
.clc-wk-day.today {
    background: color-mix(in srgb, var(--clc-accent) 16%, transparent);
    border-color: var(--clc-accent, #4285F4);
    box-shadow: 0 1.2vh 3vh color-mix(in srgb, var(--clc-accent) 28%, transparent);
    transform: scale(1.035);
    z-index: 2;
}

.clc-wk-head {
    flex-shrink: 0; text-align: center;
    padding-bottom: 1vh; margin-bottom: 1vh;
    border-bottom: 1px solid var(--clc-line, rgba(255, 255, 255, 0.06));
}
.clc-wk-dow {
    font-size: calc(2.1vh * var(--clc-week-scale, 1));
    font-weight: 900; letter-spacing: 0.1em;
    text-transform: var(--clc-dow-case, uppercase);
    color: var(--clc-accent, #4285F4);
}
.clc-wk-num {
    font-size: calc(5.4vh * var(--clc-week-scale, 1));
    font-weight: 800; line-height: 1.05;
    font-variant-numeric: tabular-nums;
}
.clc-wk-day.today .clc-wk-num { color: var(--clc-accent, #4285F4); }

/* ONE PAIR OF COLUMNS FOR THE WHOLE CARD, not one per row.
   Every row used to be its own flex box, which meant the time column was only
   ever as wide as that row's own time: "2pm" started exactly where "10am"
   did, and the titles beside them began at three different places down the
   card. A grid on the LIST gives all of its rows the same two tracks — the
   times share one, right-aligned so they end on a common edge, and the titles
   share the other, all starting down a single line to the right of it.

   The time track is max-content: the widest time in THAT card and no wider,
   so a quiet day of "2pm"s does not carry the gutter "All day" would need. */
.clc-wk-events {
    flex: 1; min-height: 0; overflow: hidden;
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    align-content: start; align-items: baseline;
    gap: 0.5vh 0.3em;
    /* Here rather than on the row, so the column gap's em is the event text's
       em — and --clc-wk-fit is searched for by calendar.js: the largest size
       the busiest column can hold. The week-scale bias is folded into it
       there rather than multiplied here, so the search and the setting cannot
       fight each other. */
    font-size: calc(1.75vh * var(--clc-wk-fit, 1));
    text-shadow: 0 1px 2px rgba(0, 0, 0, var(--clc-evt-shadow, 0.55));
}
/* A day with nothing on it is a dash in the middle of the card, which wants
   the whole box rather than the top row of a grid. */
.clc-wk-events.empty {
    display: flex; align-items: center; justify-content: center;
}
/* The row is the grid's two items, not a box of its own — its styling still
   reaches the spans by inheritance. */
.clc-wk-evt {
    display: contents;
    line-height: 1.3;
    font-weight: var(--clc-evt-weight, 400);
}
.clc-wk-time {
    font-weight: 700; color: var(--clc-accent, #4285F4);
    grid-column: 1; justify-self: end; text-align: right;
    /* Never wrapped: it is the thing the rest hangs off. Tabular figures so
       the digits stack as cleanly as the right edge does. */
    white-space: nowrap; font-variant-numeric: tabular-nums;
}
/* Narrow columns, so a title wraps rather than truncates — and in the second
   track it wraps under ITSELF rather than running back under the time and
   reading as a second event. The track's own minmax(0, …) is what lets a
   long word break inside the card instead of widening it. */
.clc-wk-title { grid-column: 2; overflow-wrap: break-word; }
/* Each segment of a title is its own line box, so the break in front of a
   bracket is structural rather than a hope about where the text will run out.
   A block inside the grid item, not a second grid item: the title is one
   entry and its lines stay in the one track. */
.clc-wk-line { display: block; }
/* The aside under the activity, set back a shade so the eye takes the name
   first — the same weighting the day view gives its brackets. */
.clc-wk-sub { opacity: 0.82; }
.clc-wk-more {
    font-size: calc(1.5vh * var(--clc-wk-fit, 1));
    opacity: 0.5; margin-top: 0.2vh;
}
.clc-wk-empty {
    flex: 1; display: flex; align-items: center; justify-content: center;
    font-size: calc(1.6vh * var(--clc-week-scale, 1));
    opacity: 0.25;
}

/* ── The week arriving ─────────────────────────────────────
   Columns rise and turn in one after another. Today is dealt LAST whatever
   its position in the week, so the eye finishes on the column it should. */
.clc-wk-grid.clc-animate .clc-wk-day {
    animation: clc-wk-in var(--clc-anim-dur, 620ms) cubic-bezier(0.22, 0.61, 0.36, 1) both;
    animation-delay: var(--d, 0ms);
    transform-origin: center bottom;
    backface-visibility: hidden;
}
@keyframes clc-wk-in {
    from {
        opacity: 0;
        transform: translateY(7vh) rotateX(-24deg) scale(0.94);
    }
}
/* The today card keeps its lift at the end of its own entrance. */
.clc-wk-grid.clc-animate .clc-wk-day.today {
    animation-name: clc-wk-in-today;
}
@keyframes clc-wk-in-today {
    from {
        opacity: 0;
        transform: translateY(7vh) rotateX(-24deg) scale(0.9);
    }
    to {
        opacity: 1;
        transform: scale(1.035);
    }
}

@media (prefers-reduced-motion: reduce) {
    .clc-wk-grid.clc-animate .clc-wk-day,
    .clc-wk-grid.clc-animate .clc-wk-label { animation: none; }
    .clc-wk-day.today { transform: none; }
    /* The sweep stops, but the word keeps the accent rather than losing all
       its colour — the gradient's middle is where the text sits by default. */
    .clc-wk-label-text { animation: none; background-position: 50% 0; }
}

/* ── Nothing configured yet ───────────────────────────────── */

.clc-no-config {
    flex: 1; display: flex; align-items: center; justify-content: center;
    font-size: calc(18px * var(--clc-scale, 1));
    opacity: 0.5; text-align: center; padding: 0 24px;
}

/* ── Admin controls ────────────────────────────────────────
   Deliberately NOT themed. The gear and the settings panel keep their own
   dark palette whatever the display is set to, so a theme in progress — a
   black-on-black one, say — cannot hide the controls needed to fix it. */

.clc-admin-gear {
    position: fixed; top: 8px; right: 8px; z-index: 100;
    width: 32px; height: 32px; border-radius: 50%;
    background: rgba(255, 255, 255, 0.08); border: none;
    color: #fff; font-size: 18px; line-height: 1;
    text-decoration: none;
    opacity: 0.2;
    cursor: pointer; transition: opacity 0.2s, background 0.2s;
    display: flex; align-items: center; justify-content: center;
    font-family: system-ui, sans-serif;
}
.clc-admin-gear:hover,
.clc-admin-gear:focus-visible {
    opacity: 1;
    background: rgba(255, 255, 255, 0.18);
}
.clc-admin-gear:focus-visible { outline: 2px solid #4285F4; outline-offset: 2px; }

/* ── Printing, bottom right ───────────────────────────────
   The gear's opposite number: same 32px circle, same 20%, same full strength
   on hover or focus. Two faint marks in two corners read as a pair; one of
   them a different size would read as a mistake. */

.clc-print-btn {
    position: fixed; bottom: 8px; right: 8px; z-index: 100;
    width: 32px; height: 32px; border-radius: 50%;
    background: rgba(255, 255, 255, 0.08); border: none;
    color: #fff;
    opacity: 0.2;
    cursor: pointer; transition: opacity 0.2s, background 0.2s;
    display: flex; align-items: center; justify-content: center;
    padding: 0;
}
.clc-print-btn svg { width: 18px; height: 18px; display: block; }
.clc-print-btn:hover,
.clc-print-btn:focus-visible {
    opacity: 1;
    background: rgba(255, 255, 255, 0.18);
}
.clc-print-btn:focus-visible { outline: 2px solid #4285F4; outline-offset: 2px; }

/* ── The print window ─────────────────────────────────────
   Its own dark palette, like the settings panel and for the same reason: the
   sheet inside it is white by definition, and a window themed to match the
   display would put a black-on-black frame around a white page. */

.clc-print-bg {
    display: none; position: fixed; inset: 0;
    background: rgba(0, 0, 0, 0.72); z-index: 300;
    align-items: center; justify-content: center;
    font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
    color: #e0e0e0;
}
.clc-print-bg.open { display: flex; }

.clc-print-win {
    background: #1a1f2e; border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 12px;
    width: min(96vw, 1700px); height: min(94vh, 1100px);
    display: flex; flex-direction: column;
    overflow: hidden;
}

/* ── The same window, on a page of its own ────────────────
   public/pdf.php. The window is the page rather than something laid over
   one, so it loses its border and its corners and takes the whole viewport —
   and nothing else about it changes, which is the point of it being the same
   markup. */

body.clc-print-page {
    margin: 0; min-height: 100vh;
    background: #10141d;
    font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
    color: #e0e0e0;
}
body.clc-print-page .clc-print-win {
    width: 100vw; height: 100vh;
    max-width: none;
    border: 0; border-radius: 0;
}

.clc-print-head {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 14px 18px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
}
.clc-print-head h2 { font-size: 17px; font-weight: 700; }
.clc-print-head button {
    padding: 8px 18px; border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    cursor: pointer; font: inherit; font-size: 14px; font-weight: 600;
}

/* What autosave says for itself. Reserved width whether or not it is saying
   anything, so the buttons beside it do not shuffle every time it speaks. */
.clc-print-saved {
    font-size: 12px; font-weight: 600; color: #8ee0a6;
    opacity: 0; transition: opacity 0.18s;
    white-space: nowrap; margin-left: 4px;
}
.clc-print-saved.show { opacity: 1; }
.clc-print-saved.bad  { color: #e8cc84; }

/* The month stepper. A calendar for the month somebody is already standing
   in is the one printing nobody needs. */
.clc-print-month {
    display: flex; align-items: center; gap: 2px;
    margin-left: 6px;
    border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 6px;
    overflow: hidden;
}
.clc-print-month button {
    border: none !important; border-radius: 0 !important;
    background: transparent; color: #9ca3af;
    padding: 7px 11px !important; font-size: 11px !important;
}
.clc-print-month button:hover { background: rgba(255, 255, 255, 0.1); color: #fff; }
.clc-print-month span {
    min-width: 9.5rem; text-align: center;
    font-size: 14px; font-weight: 600; padding: 0 4px;
}

.clc-print-body { flex: 1; min-height: 0; display: flex; }

.clc-print-controls {
    width: 268px; flex-shrink: 0;
    padding: 14px 18px 24px;
    overflow-y: auto;
    border-right: 1px solid rgba(255, 255, 255, 0.08);
}
.clc-print-controls h3 {
    font-size: 11px; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: #7f93a8;
    margin: 18px 0 10px; padding-bottom: 6px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.clc-print-controls h3:first-child { margin-top: 0; }
.clc-print-field { margin-bottom: 12px; }
.clc-print-field label {
    display: block; font-size: 12.5px; opacity: 0.72; margin-bottom: 4px;
}
.clc-print-field select {
    width: 100%; padding: 7px 9px; border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.15); background: #0d1117;
    color: #e0e0e0; font: inherit; font-size: 13px;
}
.clc-print-field .clc-range { margin: 4px 0 2px; }
.clc-print-hint { font-size: 11px; line-height: 1.4; opacity: 0.4; margin-top: 3px; }
.clc-print-note {
    font-size: 11.5px; line-height: 1.5; opacity: 0.55;
    margin-top: 18px; padding-top: 12px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

/* The sheet sits on the darker surround that a page of paper wants, and is
   scaled by the script to whatever room is left beside the controls. */
.clc-print-stage {
    flex: 1; min-width: 0;
    background: #10141d;
    display: flex; align-items: center; justify-content: center;
    overflow: hidden;
    padding: 18px;
}
.clc-print-stage iframe {
    border: 0; background: #fff;
    /* Scaled from its top-left by the script, which also sizes the box that
       holds the space it ends up occupying. */
    transform-origin: top left;
    box-shadow: 0 6px 30px rgba(0, 0, 0, 0.55);
    display: block;
}
.clc-print-sheet-box { position: relative; flex-shrink: 0; }

.clc-print-msg {
    flex-shrink: 0; font-size: 12.5px; min-height: 1.1em;
    padding: 9px 18px; border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.clc-print-msg.warn { color: #e8cc84; }
.clc-print-msg.bad  { color: #ea4335; }
.clc-print-msg.ok   { color: #8ee0a6; }

.clc-modal-bg {
    display: none; position: fixed; inset: 0;
    /* Light enough to keep the calendar readable around the panel — every
       appearance control previews onto it, and a heavy veil would mean
       judging a colour against a colour that is not the one being set. */
    background: rgba(0, 0, 0, 0.35); z-index: 200;
    align-items: center; justify-content: center;
    font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
    color: #e0e0e0;
    transition: opacity 0.18s;
}
.clc-modal-bg.open { display: flex; }
/* Out of the way, and out of the way of the mouse too — a panel at zero
   opacity that still swallowed clicks would be a trap. */
.clc-modal-bg.peeking { opacity: 0; pointer-events: none; }

.clc-modal {
    background: #1a1f2e; border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 12px;
    padding: clamp(18px, 2.5vw, 32px);
    width: min(92vw, 1600px);
    max-height: 88vh; overflow-y: auto;
}
.clc-modal h2 { font-size: 18px; margin-bottom: 4px; }
.clc-modal h3 {
    font-size: 12px; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: #7f93a8;
    margin: 22px 0 12px; padding-bottom: 7px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.clc-modal label { display: block; font-size: 13px; opacity: 0.7; margin-bottom: 4px; }

.clc-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 2px 24px;
    align-items: start;
}
.clc-field { min-width: 0; margin-bottom: 14px; }

.clc-modal input[type="text"],
.clc-modal select {
    width: 100%; padding: 9px 11px; border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.15); background: #0d1117;
    color: #e0e0e0; font-size: 14px; margin-bottom: 6px;
    font-family: inherit;
}
.clc-modal input[type="text"]:focus,
.clc-modal select:focus {
    outline: 2px solid #4285F4; outline-offset: 1px; border-color: #4285F4;
}
.clc-range {
    width: 100%; margin: 6px 0 10px; accent-color: #4285F4;
    display: block;
}

/* A swatch and its hex side by side — the picker for choosing, the text box
   for pasting a brand colour somebody sent you. */
.clc-color-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.clc-color-row input[type="color"] {
    width: 42px; height: 34px; padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 6px;
    background: transparent; cursor: pointer; flex-shrink: 0;
}
.clc-color-row input[type="text"] {
    margin-bottom: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    text-transform: lowercase;
}

/* ── The logo control ─────────────────────────────────────── */

.clc-logo-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.clc-logo-preview {
    width: 42px; height: 42px; flex-shrink: 0;
    object-fit: contain;
    background: #0d1117; border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 6px; padding: 3px;
}
.clc-logo-preview--none {
    display: flex; align-items: center; justify-content: center;
    font-size: 22px; line-height: 1;
}
.clc-logo-row input[type="file"] {
    min-width: 0; flex: 1;
    font-size: 12px; color: #9ca3af;
}
.clc-logo-row input[type="file"]::file-selector-button {
    background: rgba(66, 133, 244, 0.15); border: 1px solid rgba(66, 133, 244, 0.4);
    color: #4285F4; border-radius: 6px; padding: 5px 12px;
    cursor: pointer; font: inherit; margin-right: 8px;
}
.clc-logo-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.clc-btn-add {
    background: rgba(66, 133, 244, 0.15); border: 1px solid rgba(66, 133, 244, 0.4);
    color: #4285F4; border-radius: 6px; padding: 6px 14px;
    cursor: pointer; font: inherit; font-size: 13px; font-weight: 600;
}
.clc-btn-add[disabled] { opacity: 0.5; cursor: progress; }
.clc-btn-sm {
    padding: 6px 14px !important; font-size: 13px !important;
    border-radius: 6px; cursor: pointer;
}
.clc-logo-msg { font-size: 12px; }
.clc-logo-msg.bad { color: #ea4335; }
.clc-logo-msg.ok  { color: #34A853; }
/* .clc-btn-add is a button in the logo group and a link in the kiosk one. */
a.clc-btn-add { text-decoration: none; display: inline-block; }

/* ── The offline and kiosk controls ────────────────────────
   Three downloads in a row. .clc-btn-out could not be borrowed: it takes its
   padding and its border from the .clc-modal-actions rule and outside that
   footer it is only a colour. */

.clc-kiosk-actions {
    display: flex; align-items: center; gap: 8px;
    flex-wrap: wrap; margin-bottom: 8px;
}
.clc-btn-ghost {
    background: transparent; border: 1px solid rgba(255, 255, 255, 0.15);
    color: #9ca3af; border-radius: 6px; padding: 6px 14px;
    cursor: pointer; font: inherit; font-size: 13px; font-weight: 600;
    text-decoration: none; display: inline-block;
}
.clc-btn-ghost:hover { background: rgba(255, 255, 255, 0.06); color: #cfe0f0; }

/* A command somebody is meant to type. Deliberately a SIBLING of the hint
   rather than inside it: .clc-hint is set at 0.45 opacity, and opacity
   applies to a whole subtree — a child cannot be brighter than its parent,
   so a line that has to be read and copied cannot live in there. */
.clc-cmd-row {
    display: flex; align-items: flex-start; gap: 6px; margin-top: 6px;
}

/* ── Emailing the sheet ─────────────────────────────────────
   In the print window's controls column, because a recipient list needs a box
   to type in and the toolbar only carries the verb. Hidden in the markup and
   revealed by assets/print-window.js once mail.php has said this machine can
   send at all. */
.clc-mail {
    margin-top: 18px; padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.clc-mail[hidden], .clc-mail-off[hidden] { display: none; }
.clc-mail-head {
    margin: 0 0 12px; font-size: 12px; font-weight: 700;
    letter-spacing: 0.08em; text-transform: uppercase; opacity: 0.6;
}
.clc-mail textarea {
    width: 100%; box-sizing: border-box; resize: vertical;
    padding: 7px 9px; font-size: 12.5px; line-height: 1.5;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    color: #e7eef7; background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 5px;
}
.clc-mail textarea:focus {
    outline: none; border-color: var(--clc-accent, #4285F4);
}
.clc-mail-row {
    display: flex; align-items: center; gap: 10px; margin: 10px 0;
}
.clc-mail-count { font-size: 12px; opacity: 0.65; }
/* Why the send button is grey, beside the send button. Never the result of
   the last action — that is .clc-mail-result, and the two must not overwrite
   each other: one is what happened, this is what the button can do now. */
.clc-mail-why { font-size: 12px; opacity: 0.65; }
/* A disabled button that still looks pressable is the other half of the same
   problem. */
.clc-mail button[disabled] { opacity: 0.45; cursor: not-allowed; }
.clc-mail-when { display: flex; gap: 10px; }
.clc-mail-when .clc-print-field { flex: 1; }
/* The reply from the last thing that was pressed. min-height so the panel
   does not jump a line taller the first time it says something. */
.clc-mail-result {
    font-size: 12.5px; line-height: 1.45; min-height: 1.4em; margin-top: 4px;
}
.clc-mail-result.ok   { color: #8ee0a6; }
.clc-mail-result.warn { color: #e8cc84; }
.clc-mail-result.bad  { color: #ea4335; }
.clc-mail-log { margin-top: 12px; font-size: 12px; }
.clc-mail-log summary { cursor: pointer; opacity: 0.6; }
.clc-mail-log pre {
    margin: 8px 0 0; padding: 8px; max-height: 190px; overflow: auto;
    font-size: 10.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-all;
    color: #b9c9db; background: rgba(0, 0, 0, 0.3); border-radius: 4px;
}
/* Why there is no panel, when there is no panel. */
.clc-mail-off {
    margin-top: 18px; padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    font-size: 12.5px; line-height: 1.5; opacity: 0.6;
}

/* What to do with the installer once it has downloaded. A strip under the
   print window's toolbar, revealed by the click that downloads it — see
   assets/print-window.js — so it costs the toolbar nothing until it is the
   only thing left to do.

   flex-shrink: 0 because the window is a column and the sheet below it takes
   the rest; without it this strip is what gets squeezed away on a short
   screen, which is the one thing on the page somebody is reading right now. */
.clc-kiosk-cmd {
    flex-shrink: 0;
    padding: 10px 18px 12px;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.03);
    font-size: 12.5px; line-height: 1.5;
}
.clc-kiosk-cmd[hidden] { display: none; }
.clc-kiosk-cmd p { margin: 0; }
.clc-kiosk-cmd-note { margin-top: 7px !important; opacity: 0.72; }
/* Inline code in the prose, which is a different thing from the .clc-cmd
   block below it: a switch named mid-sentence, not a line to be copied. */
.clc-kiosk-cmd p code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.92em; color: #cfe0f0;
    background: rgba(0, 0, 0, 0.3); border-radius: 3px; padding: 1px 4px;
}
/* A switch named inside a sentence — not .clc-cmd, which is a whole command
   on a line of its own. It takes the hint's dimming with it, as a passing
   mention should. */
.clc-hint code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.95em;
}
.clc-cmd {
    display: block; padding: 5px 7px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px; line-height: 1.5; color: #cfe0f0;
    word-break: break-all; user-select: all;
    background: rgba(0, 0, 0, 0.35); border-radius: 4px;
    /* min-width, because a flex item will not shrink below its content and
       an unbroken command would otherwise push the button off the panel. */
    flex: 1; min-width: 0;
}
.clc-copy {
    flex-shrink: 0;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.15);
    color: #9ca3af; border-radius: 4px;
    padding: 5px 10px; font: inherit; font-size: 11px; font-weight: 600;
    line-height: 1.5; cursor: pointer;
    /* Set from the longest word it will ever say, so the row does not jump
       when "Copy" becomes "Copied". */
    min-width: 4.6rem; text-align: center;
}
.clc-copy:hover { background: rgba(255, 255, 255, 0.12); color: #cfe0f0; }
.clc-copy:focus-visible { outline: 2px solid #4285F4; outline-offset: 1px; }
.clc-copy.done {
    background: rgba(52, 168, 83, 0.18);
    border-color: rgba(52, 168, 83, 0.5); color: #8ee0a6;
}
.clc-copy.failed {
    background: rgba(251, 188, 4, 0.12);
    border-color: rgba(251, 188, 4, 0.4); color: #e8cc84;
}

.clc-check {
    display: flex !important; align-items: center; gap: 8px;
    cursor: pointer; opacity: 1 !important; font-size: 14px !important;
    padding: 9px 0;
}
.clc-check input { accent-color: #4285F4; width: 16px; height: 16px; }

.clc-modal-actions {
    display: flex; align-items: center; gap: 8px;
    margin-top: 10px; padding-top: 18px; flex-wrap: wrap;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.clc-modal-actions button, .clc-modal-actions .clc-btn-out {
    padding: 8px 20px; border-radius: 6px; border: 1px solid rgba(255, 255, 255, 0.15);
    cursor: pointer; font-size: 14px; font-weight: 600;
    text-decoration: none; display: inline-block; font-family: inherit;
}
.clc-btn-save { background: #34A853; color: #fff; border-color: #34A853; }
.clc-btn-save[disabled] { opacity: 0.5; cursor: progress; }
.clc-btn-cancel { background: transparent; color: #9ca3af; }
.clc-btn-out { background: transparent; color: #6b7280; }
.clc-hint { font-size: 11.5px; line-height: 1.45; opacity: 0.45; }
.clc-save-msg { font-size: 13px; margin-top: 10px; min-height: 1.2em; text-align: right; }
.clc-save-msg.bad { color: #ea4335; }
.clc-save-msg.ok { color: #34A853; }
