/*
 * Knucklebones: page styles (SPEC.md sections 6 and 7.2).
 *
 * All colours come from the site design tokens in assets/styles/app.css :root
 * (loaded on every page via the app bundle); the game-local accents are
 * declared in .kn-wrap, mirroring how botfight.css defines its own family in
 * a scoped block. Typography: DM Serif Display (headings) + DM Sans (UI) +
 * monospace (dice and hashes), all already loaded by base.html.twig.
 *
 * Animation classes (spec 7.2): .kn-die--rolling (450ms tumble),
 * .kn-die--dropping (180ms drop-in), .kn-die--destroyed (220ms fade out),
 * .kn-col--pulse (300ms coral pulse); score count-up is JS-driven and the
 * whole motion layer is disabled under prefers-reduced-motion.
 */

.kn-wrap {
    /* Game-local accents (spec 6.1) */
    --kn-opponent: #e0785f;
    --kn-opponent-dark: #c05e46;
    --kn-opponent-tint: #fdf0ec;
    --kn-player-tint: #f0fbf9;
    --kn-match-tint: #d9f2e4;
    --kn-score-up: #14663f;
    --kn-score-up-bg: #d9f2e4;
    --kn-socket: #cfdde8;
    --kn-socket-text: #b6c6d4;
    /* Muted text that sits ON the mint page background. The stock muted
       (#64748B) is 4.31:1 on mint, which fails AA below large sizes; the
       spec's contrast table names #52616b (5.80:1) for exactly this case. */
    --kn-muted-on-mint: #52616b;

    /* Board cell geometry. One variable family drives the dice, the sockets,
       the column height and the score row, so the board can be scaled up on
       touch screens without the four drifting apart. 34px is the spec size
       (SPEC.md 5.3 / 6.3) and stays the desktop default. */
    --kn-cell: 34px;
    --kn-cell-fs: 1rem;
    --kn-cell-radius: 9px;
    --kn-cell-gap: 6px;
    --kn-col-pad: 8px;
    --kn-board-pad: 14px;
    /* The die outline. Mobile raises it: at 52px a die is a large white block
       on a near-white column and #E2E8F0 no longer carries its shape. */
    --kn-die-border: var(--color-border);
    /* The rolled-but-unplaced die, shown in the controls row. */
    --kn-tile: 52px;
    --kn-tile-fs: 1.5rem;

    max-width: 1100px;
    margin: 0 auto;
    padding: 1.5rem 1.15rem 3rem;
    color: var(--color-text);
    font-family: 'DM Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    font-variant-numeric: tabular-nums;
}

.kn-wrap *, .kn-wrap *::before, .kn-wrap *::after { box-sizing: border-box; }

/* ---------------- header ---------------- */
.kn-top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap; }
.kn-title { margin: 0; font-family: 'DM Serif Display', Georgia, serif; font-weight: 400; font-size: clamp(1.9rem, 4vw, 2.75rem); line-height: 1; color: var(--color-navy); letter-spacing: 0.01em; }
.kn-title span { color: var(--kn-opponent); }
.kn-back { font-size: 0.9rem; color: var(--kn-muted-on-mint); text-decoration: none; border-bottom: 1px solid var(--color-border); }
.kn-back:hover { color: var(--color-turquoise-dark); border-color: var(--color-turquoise-dark); }
.kn-tagline { margin: 0.45rem 0 1.3rem; color: var(--kn-muted-on-mint); max-width: 48rem; line-height: 1.55; }

/* ---------------- layout ---------------- */
.kn-layout { display: grid; grid-template-columns: minmax(300px, 360px) 1fr; gap: 14px; align-items: start; }
@media (max-width: 900px) {
    .kn-layout { grid-template-columns: 1fr; }
    /* Spec 5.3: board first, the opponent card collapses beneath it. */
    .kn-board-card { order: 1; }
    .kn-side-card-play { order: 2; }
}

.kn-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-card); overflow: hidden; }
.kn-card-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--color-border); background: linear-gradient(180deg, #fbfdfc, #f4faf6); }
.kn-card-head h2 { margin: 0; font-family: 'DM Sans', sans-serif; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--color-turquoise-dark); }
.kn-card-body { padding: 14px; }

/* ---------------- opponent list ---------------- */
.kn-opponents { display: grid; gap: 8px; }

/* Name field on the setup card. Only used for online play, but always shown so
   a player can set it before choosing a mode. */
.kn-nick { margin-top: 12px; display: grid; gap: 5px; }
.kn-nick-label { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-text-muted); }
.kn-nick-input {
    width: 100%; border: 1px solid var(--color-border); border-radius: 10px;
    padding: 8px 11px; font: inherit; color: var(--color-text); background: var(--color-surface);
}
.kn-nick-input:focus-visible { outline: 2px solid var(--color-turquoise); outline-offset: 2px; }
.kn-nick-input[aria-invalid="true"] { border-color: #c0392b; }
.kn-opp {
    display: flex; align-items: center; gap: 10px; text-align: left; width: 100%;
    border: 1px solid var(--color-border); border-radius: 12px; padding: 9px 11px;
    background: var(--color-surface); cursor: pointer; font: inherit; color: var(--color-text);
}
.kn-opp:hover { border-color: var(--color-turquoise); }
.kn-opp:focus-visible { outline: 2px solid var(--color-turquoise); outline-offset: 2px; }
.kn-opp[aria-pressed="true"] { border-color: var(--color-turquoise); background: var(--kn-player-tint); }
.kn-opp-glyph { width: 34px; height: 34px; border-radius: 10px; display: grid; place-items: center; font-size: 18px; background: var(--color-sky); flex: none; }
.kn-opp-meta { display: flex; flex-direction: column; min-width: 0; }
.kn-opp-name { font-weight: 700; font-size: 0.92rem; color: var(--color-navy); }
.kn-opp-tag { font-size: 0.74rem; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 600; }
.kn-opp-diff { margin-left: auto; font-size: 0.72rem; font-weight: 700; color: var(--color-turquoise-dark); white-space: nowrap; }
.kn-opp-hash { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.68rem; color: var(--color-text-muted); white-space: nowrap; }
.kn-opp--compact { padding: 7px 9px; }
.kn-opp--compact .kn-opp-glyph { width: 28px; height: 28px; font-size: 15px; }
.kn-opp--compact .kn-opp-hash { display: none; }

/* ---------------- hash block ---------------- */
.kn-hash { margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--color-border); font-size: 0.76rem; color: var(--color-text-muted); line-height: 1.7; }
.kn-hash code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.74rem; background: var(--color-surface-2); border: 1px solid var(--color-border); border-radius: 6px; padding: 1px 5px; color: var(--color-text); }

/* ---------------- board ---------------- */
.kn-board-wrap { padding: var(--kn-board-pad); }
.kn-side { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.kn-who { font-weight: 700; display: flex; align-items: center; gap: 8px; color: var(--color-navy); }
.kn-chip { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.kn-chip--p { background: var(--color-turquoise); }
.kn-chip--a { background: var(--kn-opponent); }

.kn-pill { font-size: 0.72rem; font-weight: 700; border-radius: 999px; padding: 3px 10px; background: var(--color-navy); color: #fff; }
.kn-pill--idle { background: var(--color-surface-2); color: var(--color-text-muted); border: 1px solid var(--color-border); }
.kn-pill--thinking { background: var(--kn-opponent); color: var(--color-ink); }
.kn-pill--swap { animation: kn-pill-swap 240ms ease-out; }
@keyframes kn-pill-swap {
    0% { opacity: 0; transform: translateY(-3px); }
    100% { opacity: 1; transform: translateY(0); }
}

.kn-grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
/* The figures above the columns belong to the facing pose only: off it, each
   column carries its value inside its own box, as on the desktop. */
.kn-col-heads { display: none; }
.kn-col { position: relative; background: var(--color-surface-2); border: 1px solid var(--color-border); border-radius: 12px; padding: var(--kn-col-pad); display: grid; gap: var(--kn-cell-gap); align-content: start; min-height: 132px; transition: outline-color 150ms ease-out; }
.kn-col-dice { display: grid; gap: var(--kn-cell-gap); }

/* The column is the choice, not the cell: the cell the die lands in is the one
   control, and its hit area is stretched over the whole column it belongs to, so
   placing a die is a tap anywhere in the column rather than a precise hit on one
   53px box. Absolutely positioned against the column, it adds no layout. */
button.kn-socket--here::before { content: ""; position: absolute; inset: 0; }

.kn-socket { height: var(--kn-cell); border-radius: var(--kn-cell-radius); border: 1px dashed var(--kn-socket); display: grid; place-items: center; color: var(--kn-socket-text); font-size: 0.72rem; background: transparent; font-family: inherit; width: 100%; }
button.kn-socket { cursor: pointer; padding: 0; }
button.kn-socket:hover, button.kn-socket:focus-visible { border-color: var(--color-turquoise); border-style: solid; color: var(--color-turquoise-dark); outline: none; }
button.kn-socket:focus-visible { outline: 2px solid var(--color-turquoise); outline-offset: 2px; }

.kn-die {
    height: var(--kn-cell); border-radius: var(--kn-cell-radius); display: grid; place-items: center;
    font-family: ui-monospace, Menlo, Consolas, monospace; font-weight: 800; font-size: var(--kn-cell-fs);
    background: #fff; border: 1px solid var(--kn-die-border); color: var(--color-ink);
    box-shadow: inset 0 -2px 0 rgba(31, 42, 56, 0.08);
}
.kn-die--ai { background: var(--kn-opponent-tint); border-color: #f0cfc6; }
.kn-die--match { background: var(--kn-match-tint); border-color: #9fd9bd; }

.kn-col-score { text-align: center; font-size: 0.74rem; font-weight: 700; color: var(--color-turquoise-dark); }
.kn-col--target { outline: 2px solid var(--color-turquoise); outline-offset: 1px; }
.kn-col--hot { outline: 2px solid var(--kn-opponent); outline-offset: 1px; }
.kn-col--pulse { animation: kn-col-pulse 300ms ease-out; }
.kn-col--flash { animation: kn-col-flash 180ms ease-out; }

@keyframes kn-col-flash {
    0% { border-color: var(--color-turquoise); box-shadow: 0 0 0 2px rgba(64, 224, 208, 0.45); }
    100% { border-color: var(--color-border); box-shadow: 0 0 0 0 rgba(64, 224, 208, 0); }
}

@keyframes kn-col-pulse {
    0% { background-color: var(--kn-opponent-tint); }
    100% { background-color: var(--color-surface-2); }
}

.kn-divider { display: flex; align-items: center; gap: 10px; margin: 12px 0; color: var(--color-text-muted); font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.12em; font-weight: 700; }
.kn-divider::before, .kn-divider::after { content: ""; flex: 1; height: 1px; background: var(--color-border); }

/* ---------------- totals ---------------- */
.kn-totals { display: flex; gap: 12px; justify-content: center; margin-top: 12px; flex-wrap: wrap; }
.kn-total { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 12px; padding: 8px 18px; text-align: center; min-width: 130px; }
.kn-total .kn-total-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-text-muted); font-weight: 700; }
.kn-total .kn-total-value { font-family: 'DM Serif Display', Georgia, serif; font-size: 1.9rem; color: var(--color-navy); line-height: 1.1; font-variant-numeric: tabular-nums; }
.kn-total--lead .kn-total-value { color: var(--color-turquoise-dark); }

/* ---------------- controls ---------------- */
.kn-rollzone { display: flex; align-items: center; gap: 14px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--color-border); flex-wrap: wrap; }
.kn-btn { cursor: pointer; border-radius: var(--radius-btn); font: inherit; font-weight: 700; font-size: 0.95rem; padding: 0.6rem 1rem; border: 1px solid var(--color-turquoise); background: var(--color-turquoise); color: var(--color-ink); min-height: 44px; }
.kn-btn:hover:not([disabled]) { filter: brightness(0.97); }
.kn-btn:focus-visible { outline: 2px solid var(--color-turquoise-dark); outline-offset: 2px; }
.kn-btn--ghost { background: transparent; color: var(--color-turquoise-dark); border-color: var(--color-border); }
.kn-btn--ghost:hover:not([disabled]) { border-color: var(--color-turquoise-dark); }
.kn-btn[disabled] { opacity: 0.45; cursor: not-allowed; }

.kn-pending-tile {
    width: var(--kn-tile); height: var(--kn-tile); border-radius: 12px; display: grid; place-items: center;
    font-family: ui-monospace, Menlo, Consolas, monospace; font-weight: 800; font-size: var(--kn-tile-fs);
    background: var(--color-sky); border: 1px solid #9ccfe6; color: var(--color-ink);
}
.kn-pending-tile[hidden] { display: none; }
.kn-ai-pending { width: var(--kn-cell); height: var(--kn-cell); border-radius: var(--kn-cell-radius); display: grid; place-items: center; font-family: ui-monospace, Menlo, Consolas, monospace; font-weight: 800; font-size: var(--kn-cell-fs); background: var(--kn-opponent-tint); border: 1px solid #f0cfc6; color: var(--color-ink); }
.kn-ai-pending[hidden] { display: none; }

.kn-hint { font-size: 0.84rem; color: var(--color-text-muted); min-height: 1.3em; }
/* The hint is two fragments (the die in hand, then the advice). Without a
   separator they render as one run-on line: "the 3 Column 2 would make it 3." */
.kn-pending-ref:not([hidden])::after { content: "\00A0\00B7\00A0"; }

/* ---------------- animations (spec 7.2) ---------------- */
.kn-die--rolling { animation: kn-roll 450ms cubic-bezier(0.2, 0.8, 0.2, 1); }
@keyframes kn-roll {
    0% { transform: rotate(0deg) translateY(-14px); }
    100% { transform: rotate(720deg) translateY(0); }
}
.kn-die--dropping { animation: kn-drop 180ms ease-out; }
@keyframes kn-drop {
    0% { transform: translateY(-18px) scale(0.94); }
    100% { transform: translateY(0) scale(1); }
}
.kn-die--destroyed { animation: kn-destroy 220ms ease-in forwards; }
@keyframes kn-destroy {
    0% { transform: scale(1); opacity: 1; }
    100% { transform: scale(0.4); opacity: 0; }
}

/* ---------------- game over ---------------- */
.kn-over { margin-top: 14px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-card); box-shadow: var(--shadow-card); padding: 18px; text-align: center; animation: kn-over-in 220ms ease-out; }
.kn-over[hidden] { display: none; }
@keyframes kn-over-in {
    0% { transform: translateY(12px); opacity: 0; }
    100% { transform: translateY(0); opacity: 1; }
}
.kn-over h3 { font-family: 'DM Serif Display', Georgia, serif; color: var(--color-navy); font-weight: 400; font-size: 1.6rem; margin: 0 0 0.35rem; }
.kn-over p { color: var(--color-text-muted); margin: 0 0 6px; font-size: 0.88rem; }
.kn-over .kn-over-record { font-size: 0.8rem; color: var(--color-turquoise-dark); font-weight: 600; margin-bottom: 12px; }
.kn-over .kn-over-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 12px; }
.kn-over code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 0.78rem; background: var(--color-surface-2); border: 1px solid var(--color-border); border-radius: 6px; padding: 1px 5px; }

/* ---------------- how to play ---------------- */
.kn-how { margin-top: 14px; }
.kn-how details { padding: 12px 14px; }
.kn-how summary { cursor: pointer; font-weight: 700; font-size: 0.85rem; color: var(--color-turquoise-dark); text-transform: uppercase; letter-spacing: 0.1em; }
.kn-how .kn-how-body { padding-top: 10px; color: var(--color-text-muted); font-size: 0.9rem; line-height: 1.6; }

/* ---------------- states ---------------- */
/* Setup card shows while loading (its noscript note is the no-JS fallback)
   and in setup; the two-column game layout replaces it while playing. */
.kn-wrap[data-state="loading"] .kn-layout,
.kn-wrap[data-state="setup"] .kn-layout { display: none; }

.kn-wrap[data-state="playing"] .kn-setup-card,
.kn-wrap[data-state="over"] .kn-setup-card { display: none; }

/* The game-over card replaces the controls row (spec 5.4). */
.kn-wrap[data-state="over"] .kn-rollzone { display: none; }

.kn-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.kn-error { color: var(--kn-opponent-dark); font-size: 0.88rem; }

/* ---------------- two-sided scaffolding (pose: facing) ----------------
   Inert in the ordinary view: the half foot is an empty block, the middle line
   is not displayed, and the pips field only exists once the pose asks for dots.
   The pose itself is laid out in the mobile block below. */
.kn-half-foot { display: block; }
.kn-mid { display: none; }
.kn-half-tag { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
.kn-half-tag[hidden] { display: none; }
.kn-mid-exit {
    width: 44px; height: 44px; flex: none; border-radius: 50%; cursor: pointer;
    border: 1px solid var(--color-border); background: var(--color-surface);
    color: var(--color-text-muted); font: inherit; font-size: 17px; line-height: 1; padding: 0;
}
.kn-mid-exit:hover { border-color: var(--color-turquoise); color: var(--color-turquoise-dark); }
.kn-mid-exit:focus-visible { outline: 2px solid var(--color-turquoise); outline-offset: 2px; }

/* A die face drawn as dots: nine cells, filled per face value. Every face of a
   die is symmetric under a 180 degree turn, which is the whole reason to use
   them here: a numeral read from the far side of the table is a lie (a 6 is a
   9), a pattern of dots reads the same from either end. */
.kn-pips { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; width: calc(var(--kn-cell) * 0.64); height: calc(var(--kn-cell) * 0.64); }
.kn-pips i { border-radius: 50%; background: transparent; }
.kn-pips[data-face="1"] i:nth-child(5),
.kn-pips[data-face="2"] i:nth-child(1), .kn-pips[data-face="2"] i:nth-child(9),
.kn-pips[data-face="3"] i:nth-child(1), .kn-pips[data-face="3"] i:nth-child(5), .kn-pips[data-face="3"] i:nth-child(9),
.kn-pips[data-face="4"] i:nth-child(1), .kn-pips[data-face="4"] i:nth-child(3), .kn-pips[data-face="4"] i:nth-child(7), .kn-pips[data-face="4"] i:nth-child(9),
.kn-pips[data-face="5"] i:nth-child(1), .kn-pips[data-face="5"] i:nth-child(3), .kn-pips[data-face="5"] i:nth-child(5), .kn-pips[data-face="5"] i:nth-child(7), .kn-pips[data-face="5"] i:nth-child(9),
.kn-pips[data-face="6"] i:nth-child(1), .kn-pips[data-face="6"] i:nth-child(3), .kn-pips[data-face="6"] i:nth-child(4), .kn-pips[data-face="6"] i:nth-child(6), .kn-pips[data-face="6"] i:nth-child(7), .kn-pips[data-face="6"] i:nth-child(9) {
    background: currentColor;
}

/* ---------------- mobile / touch (SPEC 5.3) ----------------
   The spec's 34px dice are a desktop size. On a 390px phone they leave the
   board reading as a table of dashes, the tap targets break the spec's own
   44px rule, and the sticky control ended up sitting on top of the board it
   belongs to. Everything here scales the cell off the viewport (one clamp
   curve for phones and portrait tablets), turns empty sockets into quiet
   dots, lifts the game above the copy, and rebuilds the controls as a docked
   action bar instead of a button floating over the dice. */
@media (max-width: 900px) {
    .kn-wrap {
        /* 13.5vw is a ~53px die on a 390px phone and 72px on a tablet. */
        --kn-cell: clamp(46px, 13.5vw, 72px);
        --kn-cell-fs: calc(var(--kn-cell) * 0.42);
        --kn-cell-radius: calc(var(--kn-cell) * 0.22);
        --kn-cell-gap: 8px;
        --kn-col-pad: 10px;
        --kn-tile: calc(var(--kn-cell) * 1.15);
        --kn-tile-fs: calc(var(--kn-cell) * 0.52);
        /* At this size a die is a large white block on a near-white column:
           the hairline border needs to carry the shape. */
        --kn-die-border: #cfdae6;

        display: flex;
        flex-direction: column;
        padding: 1rem 0.9rem 2.25rem;
    }

    /* The game comes first: the tagline reads after it instead of pushing the
       board past the fold. Same DOM, order is presentation. */
    .kn-top { order: 1; align-items: center; }
    .kn-layout, .kn-setup-card { order: 2; }
    .kn-over { order: 3; }
    .kn-tagline { order: 4; margin: 0.35rem 0 0; font-size: 0.92rem; }

    /* Board: the reason the page exists, so it gets the space. */
    .kn-layout { gap: 12px; }
    .kn-grid3 { gap: var(--kn-cell-gap); }
    .kn-col { min-height: calc(var(--kn-cell) * 3 + var(--kn-cell-gap) * 2 + var(--kn-col-pad) * 2 + 1.5rem); }
    .kn-col-score { font-size: 0.9rem; }
    .kn-side { margin-bottom: 10px; }
    .kn-who { font-size: 1rem; }
    .kn-divider { margin: 14px 0; font-size: 0.78rem; }

    /* An empty cell is the absence of something: a dashed pit says that faster
       than nine repetitions of "empty", and without a pip-like dot there is no
       chance of reading it as a die showing 1. The word stays in the DOM, and
       so in the accessibility tree, for screen readers. */
    .kn-socket:not(.kn-socket--here) {
        font-size: 0;
        border-color: #bccfdf;
        background: rgba(207, 221, 232, 0.16);
    }
    /* Where you can actually play, say so, legibly. */
    .kn-socket--here {
        font-size: 0.82rem; font-weight: 700; color: var(--color-turquoise-dark);
        border-color: var(--color-turquoise);
        background: rgba(64, 224, 208, 0.08);
    }

    /* Totals: two cards that use the width they are given. */
    .kn-totals { display: grid; grid-template-columns: 1fr 1fr; gap: var(--kn-cell-gap); margin-top: 14px; }
    .kn-total { min-width: 0; padding: 10px 12px; }
    .kn-total .kn-total-label { font-size: 0.76rem; }
    .kn-total .kn-total-value { font-size: 2.15rem; }

    /* Controls: docked, full-bleed to the card, solid enough to read as
       furniture rather than as a button dropped on the dice. */
    .kn-rollzone {
        position: sticky;
        bottom: 0;
        z-index: 3;
        gap: 10px;
        margin: 14px calc(var(--kn-board-pad) * -1) calc(var(--kn-board-pad) * -1);
        padding: 10px var(--kn-board-pad) calc(10px + env(safe-area-inset-bottom, 0px));
        background: var(--color-surface);
        border-top: 1px solid var(--color-border);
        box-shadow: 0 -8px 18px -12px rgba(31, 42, 56, 0.35);
        border-radius: 0 0 var(--radius-card) var(--radius-card);
    }
    #kn-action { flex: 1 1 auto; min-height: 52px; font-size: 1.05rem; border-radius: 12px; }
    .kn-hint { flex: 1 0 100%; font-size: 0.92rem; line-height: 1.4; }
    /* No hint on this turn means no second row: the bar stays one line tall. */
    .kn-rollzone:has(#kn-hint:empty) .kn-hint { display: none; }

    /* 44px minimum on every tap target the page owns (SPEC 10). */
    .kn-back { display: inline-flex; align-items: center; min-height: 44px; }
    .kn-nick-input { min-height: 46px; }
    .kn-opp { padding: 12px; }
    .kn-opp-glyph { width: 40px; height: 40px; font-size: 21px; border-radius: 12px; }
    .kn-opp-name { font-size: 1rem; }
    .kn-btn--ghost { min-height: 46px; }

    /* Game over: three full-width actions beat three squeezed ones. */
    .kn-over { padding: 20px 16px; }
    .kn-over h3 { font-size: 1.75rem; }
    .kn-over-actions { flex-direction: column; }
    .kn-over-actions .kn-btn { width: 100%; min-height: 50px; }

    .kn-hash { font-size: 0.82rem; }
    .kn-how summary { font-size: 0.9rem; }

    /* ---------------- two players facing each other (pose: facing) ----------
       The phone lies flat between them. The far half is rotated, so whoever sits
       there reads their own board, their own total and their own controls
       upright; the dice become pips, because a pattern of dots reads the same
       from either side of the table; and the whole match is trimmed to one
       screen, because anything scrolled out of view is out of view for whoever
       is sitting on the far side. */
    .kn-wrap[data-pose="facing"] {
        padding: 0.35rem 0.5rem 0.5rem;
        /* Two half-cockpits have to share one screen, so the cell is whatever is
           left once the fixed furniture is paid for: about 500px of totals, name
           rows, scores, the middle line and the controls. It grows to 46px on a
           tall phone and shrinks on a short one, instead of the far player having
           to scroll for their own board. 34px is the floor: below that the two
           seats stop being readable and the phone should be handed over instead. */
        --kn-cell: max(34px, min(46px, calc((100vh - 500px) / 6)));
        --kn-cell-fs: calc(var(--kn-cell) * 0.42);
        --kn-cell-radius: calc(var(--kn-cell) * 0.22);
        --kn-cell-gap: 5px;
        --kn-col-pad: 6px;
        --kn-board-pad: 8px;
        --kn-tile: calc(var(--kn-cell) * 1.05);
        --kn-tile-fs: calc(var(--kn-cell) * 0.5);
    }
    /* Copy, the opponent card and the between-boards divider step aside: each
       half carries its own name row, so the divider has nothing left to say. */
    .kn-wrap[data-pose="facing"] .kn-tagline,
    .kn-wrap[data-pose="facing"] .kn-side-card-play,
    .kn-wrap[data-pose="facing"] .kn-divider,
    .kn-wrap[data-pose="facing"] .kn-totals,
    .kn-wrap[data-pose="facing"] .kn-back { display: none; }
    /* Anything drawn once at the top can only face one of the two seats, so the
       title bar steps aside. It stays in the document, and in the accessibility
       tree, where a heading belongs. */
    .kn-wrap[data-pose="facing"] .kn-top { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    /* The card header keeps nothing but the live region for screen readers. */
    .kn-wrap[data-pose="facing"] .kn-card-head { min-height: 0; padding: 0; border: 0; background: none; }
    .kn-wrap[data-pose="facing"] .kn-card-head h2,
    .kn-wrap[data-pose="facing"] .kn-card-head .kn-pill { display: none; }
    .kn-wrap[data-pose="facing"] .kn-board-card { border-radius: 12px; }

    /* The far seat. */
    .kn-wrap[data-pose="facing"] .kn-half--north { transform: rotate(180deg); }
    .kn-wrap[data-pose="facing"] .kn-half { padding: 4px; border: 1px solid transparent; border-radius: 12px; }
    /* The live seat glows in its own colour, not in the other one's: the second
       player's dice are coral, so their turn is coral too. */
    .kn-wrap[data-pose="facing"] .kn-half--live { background: rgba(64, 224, 208, 0.1); border-color: rgba(64, 224, 208, 0.5); }
    .kn-wrap[data-pose="facing"] .kn-half--north.kn-half--live { background: rgba(224, 120, 95, 0.1); border-color: rgba(224, 120, 95, 0.55); }
    .kn-wrap[data-pose="facing"] .kn-side { margin-bottom: 4px; }
    .kn-wrap[data-pose="facing"] .kn-half-tag { display: inline-block; color: var(--color-text-muted); }
    .kn-wrap[data-pose="facing"] .kn-half--live .kn-half-tag { color: var(--color-turquoise-dark); }
    .kn-wrap[data-pose="facing"] .kn-half--north.kn-half--live .kn-half-tag { color: var(--kn-opponent-dark); }

    /* The figures for the seat that reads this half, above the columns and outside
       their outline: the value of that player's column, and the value of the
       opponent's column of the same name, which is the one this column meets. The
       numbers are the same ones drawn on the other board, turned onto this side of
       the table so that whoever sits here reads them upright: duplication that
       exists so neither player has to read their own decisions upside down. */
    .kn-wrap[data-pose="facing"] .kn-col-heads {
        display: grid; grid-template-columns: repeat(3, 1fr);
        gap: var(--kn-cell-gap); margin-bottom: 3px;
    }
    .kn-wrap[data-pose="facing"] .kn-col-head {
        display: flex; flex-direction: column; align-items: center; line-height: 1.1;
    }
    .kn-wrap[data-pose="facing"] .kn-col-head-vs,
    .kn-wrap[data-pose="facing"] .kn-col-head-own { font-variant-numeric: tabular-nums; }
    .kn-wrap[data-pose="facing"] .kn-col-head-vs { font-size: 0.82rem; font-weight: 700; color: var(--kn-opponent-dark); }
    .kn-wrap[data-pose="facing"] .kn-col-head-own { font-size: 1.05rem; font-weight: 800; color: var(--color-turquoise-dark); }
    /* The far seat belongs to the second player: their own figures are the coral
       ones, and the mirrored figures they read are the first player's. */
    .kn-wrap[data-pose="facing"] .kn-half--north .kn-col-head-vs { color: var(--color-turquoise-dark); }
    .kn-wrap[data-pose="facing"] .kn-half--north .kn-col-head-own { color: var(--kn-opponent-dark); }
    /* The value inside the box keeps its place in the accessibility tree and steps
       out of the way visually: the figures are above the outline now, and each
       column's value should be announced once, not twice. */
    .kn-wrap[data-pose="facing"] .kn-col-score {
        position: absolute; width: 1px; height: 1px; overflow: hidden;
        clip: rect(0 0 0 0); white-space: nowrap;
    }

    /* Dots on the dice, and no digit: a digit drawn on a die is honest for one
       reader and a lie for the other, because a 6 read from the far end of the
       table is a 9. The dots are symmetric under that turn, so they are the one
       face that can be read from both ends; the figures each player needs are
       above their columns instead. Nothing here changes the tiles' display, so
       the tiles keep their own hidden state. */

    /* Each seat's own furniture: that player's total, and the controls of
       whoever is playing, on their side of the phone. Kept tight: every pixel
       here is a pixel the boards do not get. */
    .kn-wrap[data-pose="facing"] .kn-side { margin-bottom: 4px; }
    .kn-wrap[data-pose="facing"] .kn-half-foot { display: flex; flex-direction: column; gap: 5px; margin-top: 5px; }
    .kn-wrap[data-pose="facing"] .kn-half-foot .kn-total { width: 100%; min-width: 0; padding: 5px 9px; }
    /* The half is already named at its own end, so the total is just that
       player's number: no name said twice inside one half. */
    .kn-wrap[data-pose="facing"] .kn-half-foot .kn-total-label { display: none; }
    .kn-wrap[data-pose="facing"] .kn-half-foot .kn-total-value { font-size: 1.6rem; line-height: 1.1; }
    .kn-wrap[data-pose="facing"] .kn-rollzone {
        position: static; margin: 0; padding: 6px 8px; border-radius: 10px;
        border-top: 0; box-shadow: none; background: var(--color-surface-2);
    }
    .kn-wrap[data-pose="facing"] #kn-action { min-height: 44px; font-size: 0.92rem; }
    /* On a screen short enough to shrink the cell, the cell is no longer a 44px
       target: the one placeable cell per column keeps the minimum even when the
       dice around it shrink. */
    .kn-wrap[data-pose="facing"] .kn-socket--here { min-height: 44px; }
    .kn-wrap[data-pose="facing"] .kn-hint { font-size: 0.76rem; line-height: 1.3; }

    /* The line between the seats: shared chrome, so it carries only the exit, and
       the exit sits at the end of the line rather than in the middle, where both
       players reach for the centre columns. */
    .kn-wrap[data-pose="facing"] .kn-mid { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin: 5px 0; }
    .kn-wrap[data-pose="facing"] .kn-mid::before { content: ""; flex: 1; height: 1px; background: var(--color-border); }
}

@media (prefers-reduced-motion: reduce) {
    .kn-die--rolling, .kn-die--dropping, .kn-die--destroyed, .kn-col--pulse, .kn-col--flash, .kn-pill--swap, .kn-over { animation: none; }
    .kn-die--destroyed { opacity: 0.35; }
}

/* Two half-cockpits have to share one screen, and the cell is sized off the
   available height above. Where the visible height moves with the browser's own
   furniture (a phone toolbar that comes and goes), the dynamic unit is the honest
   one: 100vh there is the height the toolbar is hiding. */
@supports (height: 100dvh) {
    @media (max-width: 900px) {
        .kn-wrap[data-pose="facing"] {
            --kn-cell: max(34px, min(46px, calc((100dvh - 500px) / 6)));
        }
    }
}

/* ---------------- first-visit cookie banner (integration) ----------------
   The site-wide consent banner (templates/base.html.twig) is fixed over the
   viewport bottom. Until the visitor answers it, it must never cover the
   game's controls: the page gets room to scroll the game-over actions clear
   of the banner, and on mobile the sticky roll/place control is lifted
   above it. --kn-banner-h is measured by app.js (ResizeObserver, so font
   load and resizing are covered); the fallback only matters for the
   instant before app.js boots, in which state there are no controls yet. */
body:has(#cookie-consent:not([hidden])) .kn-wrap {
    padding-bottom: calc(var(--kn-banner-h, 240px) + 2rem);
}

@media (max-width: 900px) {
    body:has(#cookie-consent:not([hidden])) .kn-wrap .kn-rollzone {
        bottom: var(--kn-banner-h, 240px);
    }
}
