/*
 * The economy — the purse, the wards, the action rail, and the marks a kept card carries.
 *
 * Everything here is sized in the stage's own `cqh`/`cqw`, like the rest of the table, so
 * the whole play surface scales as one piece. Two exceptions, both deliberate:
 *
 *   `.actions` is placed in screen pixels by CardActions, because it has to line up with
 *   a card centred on the stage at its own layout size, and deriving that in CSS would
 *   mean restating the card-size arithmetic somewhere it could drift.
 *
 *   `.card__seal` is sized in the *card's* cqh, because it is painted on the card and has
 *   to grow with it when the card is picked up.
 */

/* --- The purse ----------------------------------------------------------- */

/*
 * In the board's Fate dish, bottom-right — coins sit in the bowl that is painted for them.
 *
 * No border and no ground of its own: it is *in* a dish, and a panel drawn around the
 * coins would be a second container inside the first. Absolutely positioned out of
 * `.table__controls` for the same reason the deck is.
 */
.tray {
    position: absolute;
    /*
     * The bowl's basin, which is a few pixels below the rim's own centre — you look into
     * this bowl from above and in front, so the hollow you can drop something into is not
     * where the rim's outline is centred. Both are ruled, in `board-geometry.json`.
     *
     * A brightness scan put this 20px lower still, because the brightest things in that
     * window are the specular highlight inside the bowl and the FATE plate under it, not
     * the rim. Round, softly-lit objects are the case where edge detection is worth less
     * than a ruler — see `Design/GeometryOverlays/bowl-ruled.png`.
     */
    inset-block-start: var(--board-dish-y);
    inset-inline-start: var(--board-dish-x);
    transform: translate(-50%, -50%);

    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.3cqh;
    /* Coins are objects you spend, and they were drawn as small change. Twice the size,
       and the bowl is what stops them spreading rather than a row width. */
    --socket-size: 4.8cqh;
}

/* The board paints FATE on a plate under the dish, so the DOM label would be a second
   copy of a word already on the table. Kept for screen readers, which cannot see it. */
.tray__label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/*
 * Coins scattered in the bowl, not lined up in it.
 *
 * A row of coins in a round dish reads as a UI element that happens to be drawn over some
 * artwork. Five resting positions, jittered so no two sit on the same line and none is
 * exactly on centre, read as coins that were dropped in — which is what they are. They
 * touch or very nearly touch, and every one of them stays inside the rim.
 *
 * Fixed positions rather than random ones. The look wanted is *scattered*, not *different
 * every frame*: a coin that moved when its neighbour was spent would draw the eye to the
 * wrong thing, and a random layout would make every screenshot in `Design/TableCaptures`
 * incomparable with the last. `TokenRow` fills these round-robin and then stacks, so a
 * sixth coin piles onto the first rather than needing a sixth hole.
 */
.tray__tokens {
    position: relative;
    width: 10cqw;
    height: 12cqh;
}

/*
 * Coins lying in a bowl that is drawn tilted away from the player.
 *
 * The rim is a circle painted 164 wide and 142 tall, so anything resting in it is
 * foreshortened to 0.87 — and a perfectly round coin was the one object on the board with
 * no point of view at all.
 *
 * `scaleY` rather than `rotateX`: a 3D rotation would need a perspective ancestor, and
 * `.token` carries a `drop-shadow` filter, which flattens `preserve-3d` and would leave
 * the coin square-on anyway. An orthographic squash is what the artwork is doing too.
 *
 * The squash is on the SOCKET and the turn is on the token inside it, because the
 * foreshortening has to happen in screen space. Applied to one element the standalone
 * properties resolve translate, then rotate, then scale — so the coin would be squashed
 * along its own rotated axis and come out as a lozenge leaning the wrong way.
 */
.tray__tokens .token-socket {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: 50%;
    translate: calc(-50% + var(--dish-x)) calc(-50% + var(--dish-y));
    transform: scaleY(var(--board-dish-tilt));
}

.tray__tokens .token-socket .token {
    rotate: var(--dish-turn, 0deg);
}

/*
 * A pentagon, not a grid — and the ORDER matters as much as the positions.
 *
 * The bowl is round, so coins laid out on a rectangle bunch into its lower half and leave
 * the top of it empty: the corners of a square are exactly where a circle has no room. A
 * ring of five with one at twelve o'clock fills the opening evenly and reaches the rim on
 * every side, which is what a handful of coins dropped in a dish actually looks like.
 *
 * The order runs top, lower-left, upper-right, so the common cases — three coins to start
 * with, four after a seal — are already a scatter rather than a line. Filled clockwise
 * they came out as a row with a wobble in it.
 *
 * The small rotations are the rest of it: identical discs at identical angles read as one
 * sprite repeated, however they are arranged.
 */
/* Offsets bounded by the basin: +/-40px across and +/-28px down, for a 45px coin in the
   bowl's 126 x 101 inner ring. */
.tray__tokens .token-socket:nth-child(1) { --dish-x:  0cqw;     --dish-y: -2.976cqh; --dish-turn:  14deg; }
.tray__tokens .token-socket:nth-child(2) { --dish-x: -1.376cqw; --dish-y:  2.657cqh; --dish-turn:  -9deg; }
.tray__tokens .token-socket:nth-child(3) { --dish-x:  2.273cqw; --dish-y: -0.957cqh; --dish-turn:   5deg; }
.tray__tokens .token-socket:nth-child(4) { --dish-x:  1.376cqw; --dish-y:  2.657cqh; --dish-turn: -16deg; }
.tray__tokens .token-socket:nth-child(5) { --dish-x: -2.273cqw; --dish-y: -0.957cqh; --dish-turn:  11deg; }

/*
 * A hole a token sits in — the purse, the wards in hand, and the rail's copy of the purse
 * all use it. An empty supply then reads as "there were three of these and they are gone"
 * rather than as a box that failed to render.
 *
 * Sized from `--socket-size` so the three places can differ without three definitions.
 */
.token-socket {
    position: relative;
    display: grid;
    place-items: center;
    width: var(--socket-size, 3.2cqh);
    height: var(--socket-size, 3.2cqh);
    border-radius: 50%;
}

/*
 * A drawn ring, only where nothing is painted behind the tokens.
 *
 * On the board the container IS the socket — the Fate dish and the ward tray are painted
 * bowls, and dashed circles inside them would be a second set of holes drawn inside the
 * first. The rail and the reading both float on grounds of their own with no board behind
 * them, so there it still has to say *there were three of these and there are none left*.
 * Both are listed here rather than each defining its own, because they are one supply
 * seen from two places and an empty purse that looks different in the two is two bugs.
 */
.actions__purse-tokens .token-socket,
.reading__purse-tokens .token-socket {
    border: 1px dashed color-mix(in srgb, var(--colour-gold) 26%, transparent);
    background: radial-gradient(circle, rgb(0 0 0 / 40%), transparent 72%);
}

.actions__purse-tokens .token-socket:has(.token),
.reading__purse-tokens .token-socket:has(.token) {
    border-color: transparent;
    background: none;
}

/*
 * Tokens stack inside their socket rather than beside it, so a supply that outgrows its
 * painted container piles up in it. Up and to the left, which is how a leaning pile
 * settles and which leaves the bottom token — the one sitting in the painted hollow —
 * fully visible.
 *
 * `translate` rather than `transform`, because the spend and gain animations are written
 * as transforms and the standalone property composes with them instead of replacing them.
 */
.token-socket .token {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    translate: calc(var(--stack, 0) * -13%) calc(var(--stack, 0) * -15%);
}

/*
 * For screen readers only, now that the coins sit in a painted dish.
 *
 * There is nowhere to put it: the bowl fills its corner and the FATE plate is directly
 * under it. That costs nothing, because the note only ever restated what the sockets
 * already show — an empty purse is two empty holes in a dish, which is exactly why
 * supplies are drawn as sockets rather than as bare tokens.
 */
.tray__note {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* An empty purse is stated, not hidden. */
.tray[data-broke='true'] .tray__label,
.tray[data-broke='true'] .tray__note {
    color: color-mix(in srgb, var(--family-conflict) 70%, var(--colour-text-muted));
}

.token {
    display: block;
    width: 3.2cqh;
    height: 3.2cqh;
    filter: drop-shadow(0 0.3cqh 0.5cqh rgb(0 0 0 / 60%));
}

.token img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/*
 * Spending. The coin does not fade — it is lifted, turned, and thrown, because the point
 * is that it went somewhere rather than that it stopped existing.
 */
@keyframes token-spend {
    0% { transform: translateY(0) rotate(0deg) scale(1); opacity: 1; }
    28% { transform: translateY(-1.6cqh) rotate(-14deg) scale(1.18); opacity: 1; }
    100% { transform: translateY(3.4cqh) rotate(38deg) scale(0.5); opacity: 0; }
}

.token--spent {
    animation: token-spend 520ms var(--ease-deal) both;
    pointer-events: none;
}

/* Earning one back is the opposite motion, and deliberately snappier: a refund is a
   reward, and a slow one reads as an apology. */
@keyframes token-gain {
    0% { transform: translateY(-2.6cqh) scale(0.4); opacity: 0; }
    60% { transform: translateY(0.3cqh) scale(1.14); opacity: 1; }
    100% { transform: translateY(0) scale(1); opacity: 1; }
}

.token--gained {
    animation: token-gain 380ms var(--ease-lift) both;
}

/* --- The wards ----------------------------------------------------------- */

/*
 * On the table, permanently, and beside the spread rather than under it.
 *
 * A ward token is *placed*, and a placed thing has to be somewhere — behind a drop-down
 * it vanished at the moment it was spent. Under the cards it was visible but wrong: a
 * full-width band pushed the controls off the bottom of the stage as soon as ten cards
 * were down, and it put the two things furthest apart that are read together.
 */
/*
 * Six sockets carved into the board, and the tokens go in them.
 *
 * The board paints all of this now — an oval tray labelled WARDS for the ones still in
 * hand, six empty circular recesses in a row, and a blank nameplate under each. So the
 * panel is not a panel any more: it has no ground, no border and no box. Every piece is
 * placed on the thing the art already drew for it.
 *
 * `display: contents` because there is nothing left to lay out. The children position
 * themselves against `.table` and this element only groups them in the markup.
 */
.wards {
    display: contents;
}

.wards__header {
    display: contents;
}

/* The board paints the word on the tray's plate. Kept for screen readers. */
.wards__title,
.wards__blurb {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Superseded by the per-socket tooltip below; kept in the markup for screen readers. */
.wards__message {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
}

/*
 * The tokens still in hand, in the board's oval tray to the left of the six sockets, and
 * the exchange to the right of them. Two separate places on the table rather than one
 * strip, because the board drew a home for one of them and not the other.
 */
.wards__supply {
    display: contents;
}

/* In the painted tray. A token moving from here to a socket is visibly the same object
   changing places, which is the entire reason wards are objects and not checkboxes. */
.wards__hand {
    position: absolute;
    /* The centre of the oval's hollow, ruled at x 346-447, y 657-709. The value before it
       was 6px low in a hole only 52px tall, which put both tokens against the bottom rim —
       and it went unnoticed because the box drawn to check it carried the same error. */
    inset-block-start: var(--board-hand-y);
    inset-inline-start: var(--board-hand-x);
    transform: translate(-50%, -50%);

    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.2cqw;
    /* Two of these plus the gap is ~90px against the tray's 104px. They were half this
       and read as loose change dropped in a dish rather than as the pieces you play. */
    --socket-size: 4.6cqh;
}

/*
 * Same hand-dropped angles, and the same foreshortening, as a token in a socket.
 *
 * The two also sit slightly off each other's line. There is only about 6px of clearance
 * in this tray so it is a small effect, but two identical discs at the same height read
 * as a control with two states rather than as two objects someone put down — which is the
 * same reason the coins in the Fate bowl are scattered rather than in a row.
 *
 * `translate` rather than folding it into the `transform`: the standalone properties
 * resolve translate, then rotate, then scale, so this displacement happens in screen space
 * instead of along the squashed axis, and it composes with the foreshortening rather than
 * replacing it.
 */
.wards__hand .token-socket {
    transform: scaleY(var(--board-ward-tilt));
    translate: 0 var(--hand-drop, 0);
}

.wards__hand .token-socket:nth-child(1) { --hand-drop: calc(var(--board-hand-stagger) * -1); }
.wards__hand .token-socket:nth-child(2) { --hand-drop: var(--board-hand-stagger); }

.wards__hand .token-socket:nth-child(1) .token { rotate:  29deg; }
.wards__hand .token-socket:nth-child(2) .token { rotate: -44deg; }

/*
 * The count, for screen readers only.
 *
 * On screen it would be a number beside the thing it counts: the tray is drawn as sockets
 * with tokens in them, so an empty tray is two empty holes and already says "none left" —
 * which is the whole reason supplies are drawn as sockets rather than as bare tokens.
 */
.wards__count {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.wards[data-spent='true'] .wards__supply {
    color: var(--colour-text-muted);
}

/*
 * The exchange, to the right of the six sockets. The board reserves no well for it,
 * because it is a *price* rather than an object — two coins for a token, drawn as the
 * trade itself so it speaks the same language as everything else on the table.
 */
.wards__buy {
    position: absolute;
    inset-block-start: var(--board-ward-y);
    /* Clear of the sixth socket but still on open table — further right it rode up onto
       the painted crystals in the corner. */
    inset-inline-start: var(--board-exchange-x);
    transform: translate(-50%, -50%);

    display: inline-flex;
    align-items: center;
    gap: 0.3cqw;
    font-family: var(--font-body);
    font-size: 1.2cqh;
    padding: 0.3cqh 0.9cqh;
    border-radius: 0.3rem;
    border: 1px solid color-mix(in srgb, var(--colour-gold) 40%, transparent);
    background: none;
    color: var(--colour-gold-bright);
    cursor: pointer;
    transition: background 160ms ease, border-color 160ms ease;
}

.wards__buy:hover:not(:disabled) {
    background: color-mix(in srgb, var(--colour-gold) 18%, transparent);
    border-color: var(--colour-gold);
}

.wards__buy:disabled {
    opacity: 0.35;
    cursor: default;
}

.wards__trade {
    display: inline-flex;
    align-items: center;
    gap: 0.15cqw;
    --token-size: 2.2cqh;
}

.wards__trade .token {
    width: var(--token-size);
    height: var(--token-size);
}

.wards__equals {
    font-family: var(--font-display);
    font-size: 1.5cqh;
    color: color-mix(in srgb, var(--colour-gold) 70%, transparent);
}

/* No list left to lay out: each ward sits on its own painted socket. */
.wards__list {
    display: contents;
    list-style: none;
}

/*
 * One ward, on the socket the board carved for it.
 *
 * Six measured centres rather than a first-position-and-pitch, because **the painted
 * sockets are not evenly spaced**: the five gaps are 129.5, 130.5, 130, 129.5 and 127 px,
 * so a pitch left the last token two and a half pixels off its hole. Small, and the kind
 * of thing that reads as sloppiness rather than as a rounding error.
 *
 * Written against `:nth-child` rather than an index property because these are facts
 * about the art, and the art is what this file is for.
 */
.ward {
    position: absolute;
    inset-block-start: var(--board-ward-y);
    inset-inline-start: var(--ward-x);
    transform: translate(-50%, -50%);
}

/*
 * Each socket's centre and the angle its token lies at are in `board-geometry.css`.
 *
 * The centres are the CENTROID of each painted gold ring, not its edges: edge-finding put
 * four of the six out by 1-3px, because the rings are lit from the upper left and their
 * bright side reads a pixel or two wider than their dark side. A ring's centroid is its
 * centre whatever threshold you pick, which is the property an edge has not got. The last
 * gap really is several pixels shorter than the others; the art is hand-placed and no
 * pitch will ever fit it.
 *
 * The turns are bounded at +/-60 degrees rather than a full circle: a ward token carries a
 * sigil, and one lying face-about reads as a rendering fault rather than as a piece that
 * was pressed into place. Enough to break up six identical discs, not enough to look
 * dropped from a height.
 */

/*
 * The button is the socket. No panel, no border, no ground — the recess is painted, and a
 * rounded rectangle drawn around it would be a second container inside the first.
 */
.ward__button {
    display: block;
    position: relative;
    width: var(--board-ward-size);
    height: var(--board-ward-size);
    padding: 0;
    border: none;
    background: none;
    color: var(--colour-text);
    cursor: pointer;
    transition: opacity 160ms ease;
}

/* `aria-disabled`, not `disabled` — see the tooltip note below. */
.ward__button[aria-disabled='true'] {
    opacity: 0.4;
    cursor: default;
}

/*
 * The socket is the painted recess itself, so this carries no ring of its own — only the
 * token that drops into it, and a faint glow on hover to say the hole can be filled.
 */
.ward__socket {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    border-radius: 50%;
    /* Foreshortened like the coins in the bowl, and for the same reason — see the note on
       `.tray__tokens .token-socket`. The turn lives on the token inside so the squash
       stays in screen space. */
    transform: scaleY(var(--board-ward-tilt));
    transition: box-shadow 160ms ease;
}

.ward__socket .token {
    rotate: var(--ward-turn, 0deg);
}

.ward__button:hover:not(:disabled) .ward__socket {
    box-shadow: 0 0 1.4cqh 0.2cqh color-mix(in srgb, var(--colour-gold) 45%, transparent);
}

.ward__button[data-placed='true']:hover:not(:disabled) .ward__socket {
    box-shadow: 0 0 1.4cqh 0.2cqh color-mix(in srgb, var(--family-conflict) 60%, transparent);
}

/*
 * A ward landing: light rises in the socket, then drains out of it.
 *
 * **The two halves are deliberately unequal — up in a third of the time it takes to go.**
 * A symmetrical flash reads as a blink, which is a notification; something that arrives
 * fast and leaves slowly reads as an effect with a cause, which is what a ward being
 * placed is. 300ms up against 600ms down, and the easing pulls the same way: the rise is
 * `ease-out` so it is almost there immediately, and the fall is `ease-in` so it lingers
 * before it goes.
 *
 * A `box-shadow` rather than a filter, because the socket is a *hole* — the light belongs
 * in and around the recess, not on the token sitting in it. It overrides the hover glow
 * for the length of the animation, which is correct: the pointer is on the socket at that
 * moment, and two glows arguing is worse than one.
 *
 * The animation carries the resting `transform` in every frame. An animation on one
 * property does not replace another, but `scaleY` is what foreshortens this socket into an
 * ellipse and any keyframe that omitted it would pop the token round for 900ms.
 */
@keyframes ward-landing {
    0% {
        box-shadow: 0 0 0 0 color-mix(in srgb, var(--colour-gold-bright) 0%, transparent);
        transform: scaleY(var(--board-ward-tilt));
    }
    33% {
        box-shadow:
            0 0 2.6cqh 0.9cqh color-mix(in srgb, var(--colour-gold-bright) 85%, transparent),
            0 0 0.9cqh 0.2cqh color-mix(in srgb, #fff 70%, transparent);
        transform: scaleY(var(--board-ward-tilt));
    }
    100% {
        box-shadow: 0 0 0.6cqh 0 color-mix(in srgb, var(--colour-gold-bright) 0%, transparent);
        transform: scaleY(var(--board-ward-tilt));
    }
}

.ward__socket--landing {
    /* Two segments of one timeline: `ease-out` to the peak at 33%, then `ease-in` away.
       A single easing across the whole 900ms cannot do fast-in and slow-out. */
    animation: ward-landing 900ms cubic-bezier(0.16, 1, 0.3, 1);
}

.ward__socket .token {
    width: 100%;
    height: 100%;
}

/*
 * Onto the board's nameplate under the socket, centred on it rather than hung below it —
 * the same correction as the card position names.
 *
 * Sized so the longest of the six fits the plate, measured rather than guessed:
 * SUPERNATURAL rendered 77px wide against the ~80px of *flat* plate between the ornate
 * scroll ends, so it was touching them. The plate is 97px overall and only 80px of that
 * is usable — the end caps are decoration, not margin. At this size it comes out ~68px.
 *
 * One size for all six, not a per-word fit. A row of labels at six different sizes reads
 * as a rendering fault, and the longest word is what the row costs.
 */
.ward__label {
    position: absolute;
    inset-block-start: calc(100% + var(--board-ward-plate-drop));
    inset-inline: -22%;
    transform: translateY(-50%);
    text-align: center;
    font-family: var(--font-display);
    letter-spacing: 0.02em;
    text-transform: uppercase;
    font-size: 0.9cqh;
    line-height: 1;
    white-space: nowrap;
    color: var(--colour-gold-bright);
    text-shadow: 0 0.1cqh 0.5cqh rgb(0 0 0 / 90%);
}

.ward__button[data-placed='true'] .ward__label {
    color: color-mix(in srgb, var(--family-conflict) 45%, var(--colour-gold-bright));
}

/*
 * The note, as a tooltip over its own socket.
 *
 * It was one shared line above the row, and that was wrong twice: it printed across the
 * bottom row of cards, and it made the player look somewhere other than the thing they
 * were pointing at. Four states have to stay tellable apart in words — warded, no tokens
 * in hand, cannot afford to lift, and *this would leave THE TURN with nothing to deal* —
 * and only some of them are the player's doing.
 *
 * It carries a ground of its own because the board behind it is painted and busy; text
 * alone at this size disappears into the ivy.
 */
.ward__note {
    position: absolute;
    inset-block-end: calc(100% + 1cqh);
    inset-inline-start: 50%;
    transform: translateX(-50%);
    z-index: 10;

    width: 16cqw;
    padding: 0.6cqh 0.8cqh;
    border-radius: 0.35rem;
    border: 1px solid color-mix(in srgb, var(--colour-gold) 30%, transparent);
    background: color-mix(in srgb, var(--colour-table-deep) 95%, black);
    box-shadow: 0 0.6cqh 1.6cqh rgb(0 0 0 / 70%);

    font-size: 1.15cqh;
    font-style: italic;
    line-height: 1.3;
    text-align: center;
    text-transform: none;
    letter-spacing: 0;
    color: var(--colour-text);

    opacity: 0;
    pointer-events: none;
    transition: opacity 140ms ease;
}

/*
 * Shown on hover or keyboard focus — and note the button is never `disabled`, only
 * `aria-disabled`. A disabled button receives no pointer events at all, so the six
 * sockets that most need to explain themselves would have been the six that could not.
 */
.ward__button:hover .ward__note,
.ward__button:focus-visible .ward__note {
    opacity: 1;
}

/* --- The legend ---------------------------------------------------------- */

/*
 * Permanent, not a hint. The hint rail teaches one verb at the moment it first matters
 * and then leaves; this says what the economy *is* for the whole session, because a
 * player who looks up two minutes in has nowhere else to find out.
 */
/*
 * On the lower half of the shelf, under the buttons it explains.
 *
 * It shares that space with `.table__note`, and the two never need to be read at once:
 * the note only ever says what the armed deal is about to destroy, which outranks a
 * standing explanation of the economy for as long as it is showing. So the legend steps
 * aside rather than the two being given a strip each — the shelf has room for one line
 * of prose, not two.
 */
.table__legend {
    position: absolute;
    /*
     * Centred on the SHELF's own centre (52.06%), not the board's, and in the clear
     * strip between the buttons and the shelf's bottom frame.
     *
     * That frame is at 93.4cqh and it is a hard edge: moved down to make room for a
     * fourth control, this ran its second line straight through the painted gold. The
     * fourth control went into the button row instead, so there was never room to make.
     */
    inset-block-start: 90.7cqh;
    inset-inline-start: 52.06cqw;
    width: 50cqw;
    transform: translate(-50%, -50%);
    text-align: center;
    font-size: 1.1cqh;
    font-style: italic;
    line-height: 1.3;
    color: color-mix(in srgb, var(--colour-text-muted) 85%, transparent);
    transition: opacity 180ms ease;
}

.table:has([data-action='deal'][data-armed='true']) .table__legend,
.table:has([data-action='reattune'][data-armed='true']) .table__legend {
    opacity: 0;
}

/*
 * An armed button says what it is about to destroy, so its price gets out of the way.
 * "Clear the table? — Costs ◉" is two messages competing for one line.
 */
.table__button[data-armed='true'] .table__price {
    display: none;
}

/* --- The action rail ----------------------------------------------------- */

/*
 * Above the scrim, because it belongs to the card the scrim is isolating. `position:
 * fixed` with a JS-set left/top, and the translate pulls it back onto the card's own
 * vertical centre.
 */
.actions {
    position: fixed;
    z-index: 60;
    transform: translateY(-50%);

    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 1.2cqh;
    width: 24cqw;

    /*
     * Its own panel, not a floating group of buttons. The scrim dims the table but does
     * not hide it, so the ward rows behind this were legible straight through the small
     * italic notes and the rail's copy of the purse — two sets of words competing in the
     * same place. A ground of its own makes the rail one object sitting above the table.
     */
    padding: 1.4cqh 1.4cqh 1.6cqh;
    border-radius: 0.5rem;
    border: 1px solid color-mix(in srgb, var(--colour-gold) 26%, transparent);
    background: color-mix(in srgb, var(--colour-table-deep) 94%, black);
    box-shadow: 0 1.5cqh 3cqh rgb(0 0 0 / 70%);
}

.actions[hidden] {
    display: none;
}

/*
 * The purse, repeated where the spending decision is made. The table's copy is behind the
 * card at this point, so a price on a button here was a number with nothing to weigh it
 * against — "1 Fate" means something different when you have three than when you have one.
 */
.actions__purse {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6cqw;
    --socket-size: 2.4cqh;
}

.actions__purse-label {
    font-family: var(--font-display);
    letter-spacing: 0.16em;
    text-transform: uppercase;
    font-size: 1.1cqh;
    color: color-mix(in srgb, var(--colour-gold) 70%, var(--colour-text-muted));
}

.actions__purse-tokens {
    display: flex;
    align-items: center;
    gap: 0.35cqw;
}

.actions[data-broke='true'] .actions__purse-label {
    color: color-mix(in srgb, var(--family-conflict) 70%, var(--colour-text-muted));
}

.actions__item {
    display: flex;
    flex-direction: column;
    gap: 0.4cqh;
}

/* An item whose button is hidden must not leave its explanation behind. */
.actions__item:has(> button[hidden]) {
    display: none;
}

.actions__button {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.6cqw;

    font-family: var(--font-display);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    font-size: 1.5cqh;
    padding: 1.1cqh 1.4cqh;
    border-radius: 0.4rem;
    border: 1px solid color-mix(in srgb, var(--colour-gold) 45%, transparent);
    background: color-mix(in srgb, var(--colour-table-raised) 88%, black);
    color: var(--colour-gold-bright);
    cursor: pointer;
    transition: background 180ms ease, border-color 180ms ease, opacity 180ms ease;
}

.actions__button:hover:not(:disabled) {
    background: color-mix(in srgb, var(--colour-gold) 20%, var(--colour-table-raised));
    border-color: var(--colour-gold);
}

.actions__button:disabled {
    opacity: 0.45;
    cursor: default;
}

.actions__button--sealed {
    background: color-mix(in srgb, var(--colour-gold-deep) 45%, var(--colour-table-raised));
    border-color: var(--colour-gold);
    color: var(--colour-parchment);
}

/* Settling pays rather than costs, so it is the one action that reads as a gain. */
.actions__button--earn {
    border-color: color-mix(in srgb, var(--family-theme) 55%, var(--colour-gold));
}

/*
 * The price, on the button. A signed coin rather than a number, so what changes hands is
 * the same object the purse above is counting.
 */
.actions__price {
    display: inline-flex;
    align-items: center;
    gap: 0.35cqw;
}

/*
 * Set apart from the action rather than subordinate to it. "Tempt Fate" is what you are
 * doing and "Costs" is what it takes — a second clause on the same line, not fine print,
 * so it is the same size as the verb and only the face and colour differ. Shrunk to a
 * footnote it read as a disclaimer, which a price is not.
 */
.actions__dash {
    font-size: 1.4cqh;
    color: color-mix(in srgb, var(--colour-gold) 50%, transparent);
}

.actions__price-word {
    font-family: var(--font-body);
    font-style: italic;
    text-transform: none;
    letter-spacing: 0.02em;
    font-size: 1.5cqh;
    color: color-mix(in srgb, var(--colour-parchment) 85%, transparent);
}

/*
 * What the action acts *on*, at the left of the button that does it — the seal it presses,
 * and the card back it deals. Both larger than the coin on the right, because one is the
 * subject of the sentence and the other is its price.
 *
 * One height for both so the two buttons read as a pair; the card back keeps its own 2:3,
 * because a square card is not a card.
 */
.actions {
    --rail-icon: 3.6cqh;
}

.actions__icon {
    display: block;
    flex: none;
    width: var(--rail-icon);
    height: var(--rail-icon);
}

.actions__icon--back {
    width: calc(var(--rail-icon) * 2 / 3);
    border-radius: 0.2rem;
    overflow: hidden;
    box-shadow: 0 0.2cqh 0.6cqh rgb(0 0 0 / 70%);
}

.actions__icon img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* The back fills its box — it is a whole card, not a badge on a transparent ground. */
.actions__icon--back img {
    object-fit: cover;
}

.actions__price-token {
    display: block;
    width: 2.4cqh;
    height: 2.4cqh;
}

.actions__price-token img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/*
 * A price of more than one coin, drawn as more than one coin.
 *
 * `TokenRow` fills this, so each child is a socket holding a token — the same object the
 * purse under the table is made of. Slightly overlapped rather than spaced, because two
 * coins side by side at this size read as a wide gap in the middle of a button, and
 * because coins on a table sit against each other.
 */
.actions__price-tokens {
    display: flex;
    align-items: center;
    gap: 0;
}

.actions__price-tokens > * {
    width: 2.4cqh;
    height: 2.4cqh;
}

.actions__price-tokens > * + * {
    margin-inline-start: -0.5cqh;
}

.actions__price-tokens img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* A button showing a price it cannot charge is a lie. */
.actions__button[data-refusal='no-coins'] .actions__price {
    display: none;
}

.actions__note {
    font-size: 1.3cqh;
    font-style: italic;
    line-height: 1.35;
    text-align: center;
    color: var(--colour-text-muted);
}

/*
 * The way out. Deliberately quiet — it is always available and never the thing to do, but
 * it has to be *visible*, because clicking the card or the dimmed table also works and
 * nothing says so. On a sealed card the only other button reads "Break the seal", which
 * is a poor thing to be the sole exit from reading something.
 */
.actions__item--quiet {
    margin-block-start: 0.6cqh;
}

.actions__button--quiet {
    font-family: var(--font-body);
    text-transform: none;
    letter-spacing: 0.04em;
    font-size: 1.4cqh;
    font-style: italic;
    padding: 0.7cqh 1.4cqh;
    border-color: color-mix(in srgb, var(--colour-gold) 20%, transparent);
    background: none;
    color: var(--colour-text-muted);
}

.actions__button--quiet:hover:not(:disabled) {
    background: color-mix(in srgb, var(--colour-table-raised) 70%, transparent);
    border-color: color-mix(in srgb, var(--colour-gold) 40%, transparent);
    color: var(--colour-text);
}

/* --- Kept cards ---------------------------------------------------------- */

/*
 * A kept card is marked twice, at two scales, and that is not duplication.
 *
 * `.slot__seal` is sized in stage units and reads at table size, where a card is ~135px
 * wide. `.card__seal` is sized in the card's own units and reads at reading size, where
 * the slot's seal is hidden behind the card anyway. One marker sized for both would be a
 * compromise that worked at neither — and a player who opened a card they had kept used
 * to see only a button saying "Release" with nothing on the card to explain it.
 *
 * Both are CSS placeholders. The shipping mark wants real art, drawn by hand like the
 * coin and block tokens, since anything with transparency is outside the image pipeline
 * (see CLAUDE.md §7).
 */
.slot__seal,
.card__seal {
    display: block;
    /* Square, and not a circle, because the mark is not a token. A Fate Coin and a Ward
       are round things you hold and spend; this is a plaque pressed onto a card. */
    aspect-ratio: 1;
    filter: drop-shadow(0 var(--seal-lift) calc(var(--seal-lift) * 2) rgb(0 0 0 / 70%));
    pointer-events: none;
}

.slot__seal img,
.card__seal img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/*
 * The shimmer — a highlight swept across the mark, and only across the mark.
 *
 * Masked by the seal's own image, so the band runs over the gilding and the wax and stops
 * at their edges instead of crossing a square nobody drew. `--seal-mask` is set from JS
 * (`TableSlot`, `Card`) rather than written here: naming the file in a stylesheet would
 * make this the one place a sweep for the shipping format cannot find (D9).
 *
 * `--shimmer-tint` is registered below so keyframes can *interpolate* it, which is what
 * lets the break run white and then red on one timeline rather than as two animations
 * whose overlap has to be guessed.
 */
@property --shimmer-tint {
    syntax: '<color>';
    inherits: false;
    initial-value: rgb(255 255 255 / 0%);
}

.slot__seal::after,
.card__seal::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;

    --shimmer-tint: rgb(255 252 235 / 88%);
    background: linear-gradient(
        108deg,
        transparent 38%,
        var(--shimmer-tint) 50%,
        transparent 62%
    );
    /* Wider than the mark, so the band can start and finish entirely off it. */
    background-size: 260% 100%;
    background-position: 135% 0;

    mask-image: var(--seal-mask);
    mask-size: contain;
    mask-repeat: no-repeat;
    mask-position: center;

    opacity: 0;
}

@keyframes seal-shimmer {
    0% { opacity: 0; background-position: 135% 0; }
    10% { opacity: 1; }
    88% { opacity: 1; }
    100% { opacity: 0; background-position: -35% 0; }
}

/*
 * Whole and broken, stacked and cross-faded. Hovering "Break the seal" shows what the
 * button would do, at the one size the difference is legible — the icon on the button
 * itself is a few pixels of wax.
 */
.card__seal-face {
    position: absolute;
    inset: 0;
    transition: opacity 180ms ease;
}

.card__seal-face--broken {
    opacity: 0;
}

.card--seal-breaking .card__seal-face {
    opacity: 0;
}

.card--seal-breaking .card__seal-face--broken {
    opacity: 1;
}

/* At table size, on the seat — so it neither turns over with the card nor travels when
   the card is picked up. */
.slot__seal {
    position: absolute;
    inset-block-start: -1.3cqh;
    inset-inline-end: -1.3cqh;
    z-index: 5;

    width: 4.4cqh;
    --seal-lift: 0.4cqh;

    opacity: 0;
    transform: scale(0.4);
    /* The card's hover lift is 200ms on the same easing. Matching it is what makes the two
       read as one object; at 260ms the seal visibly trailed the card it is pressed onto. */
    transition: opacity 200ms ease, transform 200ms var(--ease-lift);
}

.slot--sealed .slot__seal {
    opacity: 1;
    transform: scale(1);
}

/*
 * A seal is pressed ONTO a card, so it goes where the card goes.
 *
 * Hovering a dealt card grows it a quarter and lifts it, and the mark used to stay exactly
 * where it was — which read as a sticker on the screen rather than as something on the
 * card, because it is the one thing on the table that did not move with what it belongs to.
 *
 * The offsets are arithmetic, not styling, and they are the hover transform run backwards.
 * `scale(1.25)` about the seat's centre moves the top-right corner out by an eighth of the
 * card's width and up by an eighth of its height; the lift adds another 16% of the height.
 * The seat is `--card-width` wide and 1.5x that tall (`--card-aspect` is 2/3), so the corner
 * travels `0.125w` right and `(0.125 + 0.16) * 1.5w = 0.4275w` up. Following it exactly is
 * what makes the seal look attached rather than merely nearby.
 *
 * It also grows and straightens to the angle a held card wears (`.card--sealed
 * .card__seal`), because hovering is the half-step toward holding and the mark should be
 * on its way there too.
 *
 * The exclusions mirror the card's own hover rule. Without them the seal would chase a card
 * that is turning over or travelling home, where the card's transform is being driven by
 * something else entirely.
 */
.slot--sealed .slot__seat:has(
    .card:not(.card--inspecting, .card--turning-in-place, .card--putting-down):hover
) .slot__seal {
    transform:
        translate(
            calc(var(--card-width) * 0.125),
            calc(var(--card-width) * -0.4275)
        )
        scale(1.25)
        rotate(-8deg);
}

/* ...and steps aside while its own card is the one being read. */
.slot--inspecting .slot__seal {
    opacity: 0;
}

.slot--sealed .slot__label {
    color: var(--colour-gold-bright);
    text-shadow: 0 0 0.8cqh color-mix(in srgb, var(--colour-gold) 60%, transparent);
}

/* At reading size, on the card itself. Card units, so it grows with the card. */
.card__seal {
    position: absolute;
    /*
     * Fully on the card, not hanging off it. Overhanging the corner read well in theory
     * and was simply clipped in practice — `.card__face` has `overflow: hidden` so the
     * frame's rounded corner does not show square edges behind it. The top corner is also
     * the safest place to cover: the copy is all at the foot of the card and the art
     * window's crown is inset from it.
     */
    inset-block-start: 1.4cqh;
    inset-inline-end: 1.4cqh;
    z-index: 4;

    width: 17cqh;
    --seal-lift: 1cqh;

    opacity: 0;
    transform: scale(0.4) rotate(-14deg);
    transition: opacity 220ms ease, transform 300ms var(--ease-lift);
}

.card--sealed.card--inspecting .card__seal {
    opacity: 1;
    transform: scale(1) rotate(-8deg);
}

/* --- Pressing a seal on, and breaking one off ---------------------------- */

/*
 * Both are ceremonies rather than state changes, and both had to become one.
 *
 * Sealing used to be a mark fading in and breaking used to be a mark fading out, so the
 * two most consequential things a player does to a card — the one that pays them and the
 * one that charges them — looked identical in opposite directions. They cost real Fate;
 * they should be worth watching once.
 *
 * The glow is a `drop-shadow` filter rather than a `box-shadow`, so it follows the *alpha
 * of the mark*: light coming off the gilding and the wax, not off the square they are
 * drawn in. Every keyframe restates the resting shadow underneath it, because `filter` is
 * one property and an animation on it replaces the lot.
 *
 * Timings are duplicated in `TableSlot.SEAL_PRESS_MS` / `SEAL_BREAK_MS`, which is a real
 * seam and a deliberate one: `animationend` fires on an element that a dismissal can take
 * out of the document mid-flight, and a promise that never settles would strand the coin.
 */

/* The mark lands: a flare that fades to the resting drop-shadow. */
@keyframes seal-press {
    0% {
        filter:
            drop-shadow(0 var(--seal-lift) calc(var(--seal-lift) * 2) rgb(0 0 0 / 70%))
            drop-shadow(0 0 calc(var(--seal-lift) * 7) rgb(255 250 225 / 95%));
    }
    45% {
        filter:
            drop-shadow(0 var(--seal-lift) calc(var(--seal-lift) * 2) rgb(0 0 0 / 70%))
            drop-shadow(0 0 calc(var(--seal-lift) * 4) rgb(255 240 200 / 60%));
    }
    100% {
        filter:
            drop-shadow(0 var(--seal-lift) calc(var(--seal-lift) * 2) rgb(0 0 0 / 70%))
            drop-shadow(0 0 0 rgb(255 250 225 / 0%));
    }
}

.slot--sealing .slot__seal,
.slot--sealing .card__seal {
    animation: seal-press 900ms ease-out;
}

.slot--sealing .slot__seal::after,
.slot--sealing .card__seal::after {
    animation: seal-shimmer 760ms 90ms ease-out;
}

/*
 * Breaking it: hold the crack, then turn from white to red, then let it go.
 *
 * One timeline for all three beats rather than three animations chased with timers. The
 * crack is already showing — hovering the button previews it — so the first beat is a
 * *pause* on purpose: what the player has to register is that the wax is broken, and a
 * mark that starts leaving at the same moment it cracks never gets read.
 */
@keyframes seal-break-glow {
    0%, 40% {
        filter:
            drop-shadow(0 var(--seal-lift) calc(var(--seal-lift) * 2) rgb(0 0 0 / 70%))
            drop-shadow(0 0 calc(var(--seal-lift) * 4) rgb(255 250 235 / 70%));
    }
    56%, 78% {
        filter:
            drop-shadow(0 var(--seal-lift) calc(var(--seal-lift) * 2) rgb(0 0 0 / 70%))
            drop-shadow(0 0 calc(var(--seal-lift) * 7) rgb(226 74 52 / 92%));
    }
    100% {
        filter:
            drop-shadow(0 var(--seal-lift) calc(var(--seal-lift) * 2) rgb(0 0 0 / 70%))
            drop-shadow(0 0 calc(var(--seal-lift) * 3) rgb(226 74 52 / 0%));
    }
}

/*
 * ...and the mark itself comes off the card. It tips and grows very slightly as it goes,
 * which reads as something lifting away rather than as something being switched off.
 *
 * The rotation restates the held seal's own `-8deg` for the whole hold, so the shatter is
 * a departure from where it was sitting and not a jump to a second resting angle.
 */
@keyframes seal-break-fall {
    0%, 74% { transform: scale(1) rotate(-8deg); opacity: 1; }
    100% { transform: scale(1.16) rotate(-1deg); opacity: 0; }
}

@keyframes seal-break-shimmer {
    0% { opacity: 0; background-position: 135% 0; --shimmer-tint: rgb(255 252 235 / 88%); }
    8% { opacity: 1; }
    38% { opacity: 1; background-position: -35% 0; --shimmer-tint: rgb(255 252 235 / 88%); }
    /* Snapped back off the far side with nothing showing, so the second sweep starts from
       the same edge as the first instead of running backwards. */
    42% { opacity: 0; background-position: 135% 0; --shimmer-tint: rgb(240 96 72 / 94%); }
    50% { opacity: 1; }
    80% { opacity: 1; background-position: -35% 0; --shimmer-tint: rgb(240 96 72 / 94%); }
    100% { opacity: 0; background-position: -35% 0; }
}

.slot--breaking-seal .card__seal {
    animation:
        seal-break-glow 1200ms ease-in-out forwards,
        seal-break-fall 1200ms ease-in forwards;
}

.slot--breaking-seal .card__seal::after {
    animation: seal-break-shimmer 1200ms linear;
}

/*
 * The table's own mark breaks too, for the case where the card is put down mid-ceremony.
 * It is normally behind the held card and invisible; when it is not, a seal that simply
 * blinked out while its twin was still coming apart would be the same fault the whole
 * ceremony exists to fix.
 */
.slot--breaking-seal .slot__seal {
    animation:
        seal-break-glow 1200ms ease-in-out forwards,
        seal-break-fall 1200ms ease-in forwards;
}

.slot--breaking-seal .slot__seal::after {
    animation: seal-break-shimmer 1200ms linear;
}

/* --- Transmuting --------------------------------------------------------- */

.card--transmuting .card__face {
    box-shadow:
        var(--shadow-card-lifted),
        0 0 6cqh 1cqh color-mix(in srgb, var(--colour-gold) 45%, transparent);
}

/*
 * What a face-down card does instead of turning over.
 *
 * A ward can banish a card nobody has looked at yet, and turning that one over to show
 * the swap would hand the player content the deal deliberately withheld. So it flinches:
 * the slot visibly reacts, and what is in it stays unknown.
 *
 * Written with the standalone `translate` and `scale` properties rather than as a
 * `transform`, because the inner already carries `perspective(...) rotateY(...)` and
 * those compose with it instead of replacing it. A keyframe that restated the transform
 * would have to know which way the card is facing, and would turn it over if it guessed.
 */
@keyframes card-flinch {
    0%, 100% { translate: 0 0; scale: 1; }
    24% { translate: 0 -6%; scale: 1.06; }
    62% { translate: 0 -1%; scale: 1.01; }
}

.card--flinching .card__inner {
    animation: card-flinch var(--duration-flinch) var(--ease-lift) both;
}

/* --- The hint line ------------------------------------------------------- */

/*
 * One line of teaching, on the shelf's prose strip.
 *
 * It used to float on the board's bottom edging with nothing behind it, where a line of
 * italic gold over painted ivy and candlelight is close to unreadable. It now takes the
 * same strip as the legend and the armed-deal warning — three things that are all one
 * sentence about the game, and never all wanted at once, so they share a place and the
 * standing one yields. It keeps a ground of its own because it is transient: a panel that
 * appears and leaves is a different thing from words printed on the shelf.
 */
/*
 * Above the held card, not over it.
 *
 * The hint that matters most fires the first time a card is picked up, and a held card
 * stands 92cqh tall centred on the stage — so it reaches from 4cqh to 96cqh and there is
 * no room *below* it for anything. Sitting on the shelf, this printed across the card's
 * own family banner: a line explaining the rules, over the thing it was explaining.
 *
 * Anchored by its BOTTOM edge rather than its top, so that a line long enough to wrap
 * grows upward into the empty strip instead of downward onto the card. Anchoring the top
 * is what would make this correct at one viewport width and wrong at the next.
 */
.hint {
    position: absolute;
    inset-block-end: 96.6cqh;
    inset-inline-start: 50cqw;
    max-width: 58cqw;
    z-index: 55;

    padding: 0.6cqh 1.2cqh;
    border-radius: 0.35rem;
    border: 1px solid color-mix(in srgb, var(--colour-gold) 30%, transparent);
    background: color-mix(in srgb, var(--colour-table-deep) 92%, black);

    text-align: center;
    font-size: 1.3cqh;
    font-style: italic;
    line-height: 1.35;
    color: color-mix(in srgb, var(--colour-gold-bright) 80%, var(--colour-text));

    opacity: 0;
    translate: -50% 0.8cqh;
    pointer-events: none;
    transition: opacity 400ms ease, translate 400ms var(--ease-deal);
}

.hint[data-shown='true'] {
    opacity: 1;
    translate: -50% 0;
}

/* The standing explanation steps aside for whichever transient line is using the strip. */
.stage:has(.hint[data-shown='true']) .table__legend {
    opacity: 0;
}

/*
 * A control that is not one of the three you use during a reading.
 *
 * Attuning again restarts everything, and it sits on the same shelf as the buttons you
 * press every minute. Smaller and quieter is the whole treatment: it stays reachable and
 * stops competing, which is what you want from the only control that throws away the five
 * answers as well as the table.
 */
.table__button--minor {
    font-size: 1.05cqh;
    padding: 0.5cqh 0.7cqh;
    letter-spacing: 0.08em;
    opacity: 0.78;
}

.table__button--minor:hover:not(:disabled) {
    opacity: 1;
}

/* --- Priced table controls ----------------------------------------------- */

/*
 * The deeper reading and the armed deal share the controls row with two ordinary buttons,
 * so both have to say what they are without a caption. One carries its price in coins; the
 * other changes its own label when it is one press from destroying the reading.
 */
.table__button--priced {
    display: inline-flex;
    align-items: center;
    gap: 0.6cqw;
}

.table__price {
    display: inline-flex;
    align-items: center;
    gap: 0.3cqw;
}

.table__price-tokens {
    display: inline-flex;
    align-items: center;
    gap: 0.15cqw;
}

.table__price-tokens .token-socket {
    /* No socket here: this is a price tag, not a supply. There is no empty state to show. */
    border: none;
    background: none;
    --socket-size: 2cqh;
}

.table__button[data-refusal='no-coins'] .table__price-tokens {
    filter: grayscale(1);
}

/*
 * Armed. Deliberately alarming relative to everything else on the table, because it is the
 * only control here that destroys something and it looks exactly like the two beside it.
 */
.table__button[data-armed='true'] {
    border-color: var(--family-conflict);
    background: color-mix(in srgb, var(--colour-crimson) 55%, transparent);
    color: var(--colour-parchment);
}

/* Same strip as the legend, which yields to it. See the note there. */
.table__note {
    position: absolute;
    inset-block-start: 90.54cqh;
    inset-inline-start: 52.06cqw;
    width: 50cqw;
    transform: translate(-50%, -50%);
    text-align: center;
    font-size: 1.2cqh;
    font-style: italic;
    line-height: 1.3;
    color: color-mix(in srgb, var(--family-conflict) 65%, var(--colour-text));
}
