/*
 * The tabletop surface.
 *
 * Desktop-first by decision (see CLAUDE.md §6). The play surface is a fixed-aspect
 * stage that scales to the viewport rather than a reflowing responsive grid — card
 * positions can then be authored in stable stage units, which is what makes physical
 * dealing animations tractable. Phase 3 builds the real board on top of this.
 */

/*
 * What sits outside the board on a screen wider or taller than 16:9.
 *
 * Near-black at the centre falling to true black at the edges — the vignette is worth
 * keeping because it settles the eye onto the board, but it used to fall to the table's
 * own deep blue, which put a second, dimmer play surface around the real one. Black reads
 * as the room the table is in.
 */
body {
    background: radial-gradient(ellipse at 50% 45%, #101014 0%, #000 62%);
    color: var(--colour-text);
    font-family: var(--font-body);
    overflow: hidden;
}

.app {
    display: grid;
    place-items: center;
    height: 100%;
}

/*
 * Fixed-aspect stage. Everything inside is positioned against a stable coordinate
 * space; only this element scales.
 *
 * The aspect is the BOARD ART's aspect, not a chosen one — the painted slots have to
 * land under the dealt cards, and any mismatch between the two is a crop or a letterbox
 * that moves every measured position in `table.css`. `play-board.png` is 1672x941, which
 * is 16:9 to within a pixel, so the stage fills a normal display edge to edge.
 *
 * The board before it was 3:2 and pillarboxed about 150px each side at 1920x1080. Keep
 * this in step with whatever the art is, and never crop to force the two to agree.
 */
.stage {
    --stage-aspect: 16 / 9;

    /*
     * How large a card is LAID OUT, for everything on this stage — the ten in the spread,
     * the Destiny in its panel, and the two the Attunement offers. Cards are rendered at
     * reading size and scaled down into whatever holds them; see "Rendered large, shown
     * small" in card.css for what happens when they are not.
     *
     * Bare counts of `cqh` rather than lengths, because the ratio between a render size
     * and a seat size *is* the scale factor and calc() cannot divide one length by
     * another. **The unit goes back on at the point of use, and it has to.** A container
     * query unit resolves against the nearest ancestor container, and an element is not
     * its own container — so `1cqh` written here would measure the viewport rather than
     * this box, while the identical text one element down measures correctly.
     */
    --held-cqh: 92;
    /* At the 2:3 card aspect, a card 92cqh tall is 61.33cqh wide. */
    --render-cqh: calc(var(--held-cqh) * 2 / 3);

    position: relative;
    width: min(100vw, calc(100vh * (var(--stage-aspect))));
    height: min(100vh, calc(100vw / (var(--stage-aspect))));
    container-type: size;
}

/*
 * The board itself, behind everything.
 *
 * Its aspect matches the stage exactly, so `fill` and `cover` are the same thing here and
 * `fill` says so — a cover-crop would silently eat the painted slots at the top and bottom
 * the moment the two aspects drifted apart, which is precisely the failure this comment
 * exists to make loud.
 */
.stage__board {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

.stage__board img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: fill;
}

/*
 * The board paints its own title and tagline, so the DOM ones would double up.
 *
 * They are hidden rather than deleted: the `<h1>` is what a search engine and a screen
 * reader read, and neither can see painted lettering. Text baked into art is invisible to
 * both, which is a standing cost of this board and not a thing to fix in CSS.
 *
 * Note the painted tagline is the legacy one — "Every story begins with a spark." against
 * the committed "Draw Your Story Destiny." (D6). Regenerating the board is what settles it.
 */
.stage__title,
.stage__tagline {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/*
 * What just happened, bottom-left. Development-only in origin but it is the only running
 * commentary the game has, so it is legible on purpose: over a painted board a line of
 * unbacked text competes with candlelight and ivy and loses. It carries its own ground,
 * and sits clear of the board's bottom edging rather than on it.
 *
 * **It no longer places itself.** It is a row of `.footnote` (see `welcome.css`), which
 * owns that corner and stacks the way back to the welcome above it — two small legible
 * things each positioning itself independently is how they end up on top of each other.
 */
.service-status {
    padding: 0.5cqh 0.9cqh;
    border-radius: 0.35rem;
    border: 1px solid color-mix(in srgb, var(--colour-gold) 22%, transparent);
    background: color-mix(in srgb, var(--colour-table-deep) 88%, black);

    font-size: 1.25cqh;
    line-height: 1.3;
    letter-spacing: 0.04em;
    color: var(--colour-text-muted);
}

.service-status[data-service-status='ok'] {
    color: var(--family-theme);
}

.service-status[data-service-status='degraded'] {
    color: var(--colour-gold);
}

.service-status[data-service-status='error'] {
    color: var(--family-conflict);
}

/* --- The Verse Draft door (D5) ------------------------------------------- */

/*
 * A hit area over the board's painted panel, and nothing else.
 *
 * The panel is finished art: it carries its own title, its own illustration and its own
 * line of copy, so there is nothing for the client to draw into it. Every rule below is
 * therefore about *lighting* it rather than about drawing anything — no border, no
 * ground, no label. A rounded rectangle a few pixels inside a painted one reads as a
 * rendering fault, which is the standing rule for painted furniture on this board.
 *
 * Position and size come from `board-geometry.css`. Nothing here is a measured number.
 */
.verse-door {
    /*
     * The corner cut, measured off the painted frame rather than chosen.
     *
     * These corners are **concave** — the gold line runs down the side, turns through a
     * quarter-arc that bulges *toward* the panel, and picks the top edge up 15px in. A
     * disc subtracted from the corner is exactly that shape, so the outline below is a
     * plain box with four discs taken out of it. A `border-radius` would round the corners
     * the other way and read as a different piece of furniture drawn over this one.
     *
     * 15px of a 1672px board. In `cqw` rather than `cqh` because the mask draws a circle
     * and a circle needs one length — the stage is fixed-aspect, so either unit resolves
     * to the same physical size, and only one of them has to be written twice.
     */
    --verse-notch: 0.9cqw;

    position: absolute;
    inset-block-start: var(--board-verse-y);
    inset-inline-start: var(--board-verse-x);
    width: var(--board-verse-width);
    height: var(--board-verse-height);
    /*
     * Leaning with the paint. Both painted side rules move right going down — 2.45 degrees
     * on the left and 3.30 on the right — because this board is drawn in perspective, and a
     * square-on glow beside them read as a rectangle floating over the panel rather than as
     * the frame catching light.
     *
     * A SKEW rather than a rotation, the same call the deck well and the DESTINY case take:
     * the top and bottom rules are level, and rotating would tilt them against painted
     * horizontals — which is worse than not leaning at all.
     */
    transform: translate(-50%, -50%) skewX(var(--board-verse-skew));
    z-index: 3;

    display: block;
    border-radius: 0.6cqh;
    cursor: pointer;
    /* Warms the painted gold rather than adding light of its own. A glow that reads as a
       separate object would sit *on* the panel; this makes the panel itself catch the
       candlelight, which is what a thing you can touch does on this table. */
    background: radial-gradient(
        70% 60% at 50% 45%,
        color-mix(in srgb, var(--colour-gold) 16%, transparent) 0%,
        transparent 70%
    );
    opacity: 0;
    transition: opacity 260ms var(--ease-deal);

    /*
     * Blurred as a whole, and this is the one line that turns the outline below from a
     * drawn rectangle into light.
     *
     * `filter` is applied to an element *after* its own content — pseudo-elements and their
     * masks included — so the softening lands on top of the finished shape rather than
     * before it. That does two things at once that nothing else here can: it feathers the
     * hard cut where the glow meets the element's edge, which was reading as a translucent
     * white panel laid over the art; and it lets the light bleed *outside* the box, so the
     * painted rule looks like it is catching a lamp instead of having been traced.
     *
     * It cannot go on the `::after` itself. On one element the order is filter, then mask —
     * so the blur would happen first and the mask would cut the softened glow with a fresh
     * hard edge, which is the problem it was added to remove.
     */
    filter: blur(0.13cqw);
}

/*
 * The frame, lit.
 *
 * The gold wash above says *this panel is warm*; it does not say where the panel ENDS, and
 * on a board painted edge to edge in gold filigree that is the thing a pointer needs told.
 * So the outline is white — the one colour on this table that is not already the
 * decoration — and it hugs the painted line rather than being drawn beside it.
 *
 * **Still not a border.** It is a glow with no edge of its own: two inset shadows, a tight
 * bright one on the line and a wide soft one falling inward, so what the eye reads is the
 * painted rule catching light rather than a rectangle the client has stroked over it. The
 * standing rule against drawing on painted furniture is about *lines*, and this has none.
 *
 * The fleurons at the top and bottom centre are deliberately left dark. They stand proud of
 * the frame, so lighting them would need a shape that traces the ornament — and an outline
 * that is a rectangle everywhere except two bumps reads as a mistake either way round.
 */
.verse-door::after {
    content: '';
    position: absolute;
    /*
     * Exactly the measured frame. It used to be nudged inward by a couple of pixels to
     * cover the slack in a box that was only ever measured to be *clicked*; the frame has
     * since been re-measured sub-pixel for this glow, so a correction here would be a
     * second opinion about a number that is now right.
     */
    inset: 0;

    box-shadow:
        inset 0 0 0.26cqw 0 rgb(255 255 255 / 62%),
        inset 0 0 0.95cqw 0 rgb(255 255 255 / 20%);

    /*
     * Four discs taken out of the four corners — the concave cut, drawn as a mask rather
     * than as a shape, because the thing being cut is a pair of box-shadows and a shadow
     * cannot be given an outline of its own.
     *
     * Each layer is one quadrant of the box, opaque except for a circle at its outer
     * corner; the four unioned cover the whole element (the shorthand leaves
     * `mask-composite` at `add`, which is what makes the union). The stops feather the arc
     * over a pixel and a half so it dissolves like the rest of the glow instead of ending
     * in a hard crescent.
     */
    mask:
        radial-gradient(circle var(--verse-notch) at 0 0,
            #0000 calc(100% - 1.5px), #000 100%) 0 0 / 50.5% 50.5% no-repeat,
        radial-gradient(circle var(--verse-notch) at 100% 0,
            #0000 calc(100% - 1.5px), #000 100%) 100% 0 / 50.5% 50.5% no-repeat,
        radial-gradient(circle var(--verse-notch) at 0 100%,
            #0000 calc(100% - 1.5px), #000 100%) 0 100% / 50.5% 50.5% no-repeat,
        radial-gradient(circle var(--verse-notch) at 100% 100%,
            #0000 calc(100% - 1.5px), #000 100%) 100% 100% / 50.5% 50.5% no-repeat;
}

.verse-door:hover,
.verse-door:focus-visible {
    opacity: 1;
}

/*
 * Keyboard users get an outline, and this is the one place the no-border rule yields.
 *
 * The hover treatment is a glow with no edge, which is exactly what a focus ring cannot
 * be: somebody tabbing through the page has to be able to see *where* they are, and a
 * diffuse warm patch on painted candlelight is not an answer to that. Offset outwards so
 * it sits clear of the painted rule rather than a few pixels inside it.
 */
.verse-door:focus-visible {
    outline: 2px solid var(--colour-gold-bright);
    outline-offset: 3px;
}

/* --- The mute (Phase 11) --------------------------------------------------- */

/*
 * Sound off, from anywhere, at any point.
 *
 * The welcome asks the question once and then leaves; this is the answer being
 * changeable afterwards, which matters most in the middle of a reading — so it sits above
 * every act rather than on the board underneath them. Bottom right, because it is the one
 * corner with no painted furniture in it and the one corner of the reading's scrim with
 * nothing laid over it.
 *
 * Held off both edges rather than tucked into the corner: an control flush to the boundary
 * reads as part of the window rather than part of the table, and on a display whose bezel
 * curves it can be genuinely hard to hit.
 */
.mute {
    position: absolute;
    right: 1.6cqw;
    bottom: 2.2cqh;
    z-index: 80;

    display: grid;
    place-items: center;
    width: 4.4cqh;
    height: 4.4cqh;
    padding: 0;

    border: 1px solid color-mix(in srgb, var(--colour-gold) 22%, transparent);
    border-radius: 50%;
    background: color-mix(in srgb, var(--colour-table-deep) 55%, transparent);
    color: var(--colour-gold);
    cursor: pointer;

    /*
     * Present, not advertised. It should be findable by someone looking for it and
     * ignorable by someone who is not, so it sits back until the pointer arrives.
     */
    opacity: 0.7;
    transition:
        opacity 180ms var(--ease-deal),
        border-color 180ms var(--ease-deal),
        background-color 180ms var(--ease-deal);
}

.mute:hover,
.mute:focus-visible {
    opacity: 1;
    border-color: color-mix(in srgb, var(--colour-gold) 55%, transparent);
    background: color-mix(in srgb, var(--colour-table-raised) 75%, transparent);
}

.mute__icon {
    width: 62%;
    height: 62%;
}

/*
 * The picture is driven by `aria-checked` and by nothing else.
 *
 * Not a second class toggled alongside it: two sources for one state is how a control
 * ends up showing "on" to everybody who can see it and "off" to everybody who cannot.
 */
.mute[aria-checked='true'] .mute__bar { display: none; }
.mute[aria-checked='false'] .mute__waves { display: none; }
.mute[aria-checked='false'] { color: var(--colour-text-muted); }
