/*
 * The wall's table feed (spec-table-feed §4.4, §5): captions over tokens, the side log
 * «События», and the insets that make room for the log. No told words: the narrator and the
 * NPCs are heard, not read (owner rule 2026-09-25, spec 2026-09-25-table-sound-narration §E).
 *
 * Wall only. The page does not load public/css/table.css and carries its own copy of the
 * design tokens (--brass-bright, --bg-iron, --frame-line…), so the outcome and side
 * colours are defined here. Everything is sized for three metres at 1080p and multiplied
 * by --table-scale. Colour always travels with a glyph and a word, never alone. Motion is
 * opacity and transform only: no backdrop-filter, no animated blur or shadow.
 */
:root {
    --outcome-win:  #8fc46a;  /* ✓ успех / попал */
    --outcome-lose: #e2574c;  /* ✗ провал / мимо / ⊘ нельзя (used ≥ 24 px) */
    --outcome-crit: #e8c66a;  /* ✦ КРИТ (the ribbon's crit colour) */
    --outcome-gain: #d9b45e;  /* ✦ item, ◉ gold, ★ XP, ➜ passage */
    --outcome-harm: #ff8a7a;  /* −N damage */
    --outcome-heal: #7fd08a;  /* ✚ +N */
    --side-party:   var(--brass-bright);
    --side-enemy:   #c0463c;
    --side-object:  var(--brass-dim);
    /* Constant while the table is live: the camera never jumps (§5.1). */
    --feed-w: calc(400px * var(--table-scale, 1));
}

/* ── The side log «События» (§5) ─────────────────────────────────────────── */
#tableFeed { display: none; }
body.table-mode #tableFeed.is-on { display: flex; }
/* The console has «Поток»; the lobby, the curtain and the patron own the whole wall. The
   feed is mounted after #tableJoin and #tablePatron, so the sibling selectors reach it. */
body.gm-frame #tableFeed,
#tableJoin.is-open ~ #tableFeed,
#tablePatron.is-on ~ #tableFeed { display: none !important; }
#tableFeed {
    position: fixed; left: 0;
    top: calc(16px * var(--table-scale, 1)); bottom: calc(24px * var(--table-scale, 1));
    width: var(--feed-w); box-sizing: border-box;
    z-index: 895; pointer-events: none;
    flex-direction: column;
    padding: calc(10px * var(--table-scale, 1)) calc(22px * var(--table-scale, 1)) 0 calc(18px * var(--table-scale, 1));
    font-family: var(--font-body, 'EB Garamond', Georgia, serif);
    color: var(--parchment);
}
/* The same coal-to-transparent ground and right-edge mask the wall's log always had — on a
   pseudo-element, so the fade takes the ground and not the words. */
#tableFeed::before {
    content: ''; position: absolute; inset: 0; z-index: -1;
    background: linear-gradient(to top, rgba(10,8,7,.96) 55%, rgba(10,8,7,.55));
    -webkit-mask-image: linear-gradient(to right, #000 72%, transparent);
    mask-image: linear-gradient(to right, #000 72%, transparent);
}
#tableFeed .feed-head {
    font-family: var(--font-display, 'Cinzel', serif);
    font-size: calc(18px * var(--table-scale, 1));
    letter-spacing: .16em; text-transform: uppercase;
    color: var(--brass-dim);
    margin-bottom: calc(8px * var(--table-scale, 1));
}
#tableFeed .feed-state { text-transform: none; letter-spacing: .02em; color: var(--outcome-lose); }
#tableFeed .feed-entries {
    flex: 1; min-height: 0;
    display: flex; flex-direction: column; justify-content: flex-end;
    gap: calc(10px * var(--table-scale, 1));
    overflow: hidden;
    padding-bottom: calc(6px * var(--table-scale, 1));
    /* Lines in the top 35 % fade linearly to .40 — no scrolling, a TV has no input. */
    -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,.4) 0%, #000 35%);
    mask-image: linear-gradient(to bottom, rgba(0,0,0,.4) 0%, #000 35%);
    transition: opacity 1.2s ease;
}
#tableFeed.is-idle .feed-entries { opacity: .55; }
#tableFeed .is-new { animation: tableFeedIn 220ms ease-out both; }
@keyframes tableFeedIn { from { opacity: 0; transform: translateY(calc(10px * var(--table-scale, 1))); } to { opacity: 1; transform: none; } }

#tableFeed .feed-entry {
    --side: var(--parchment-dim);
    display: grid; grid-template-columns: calc(32px * var(--table-scale, 1)) minmax(0, 1fr);
    column-gap: calc(10px * var(--table-scale, 1));
    align-items: start; flex: none;
}
#tableFeed .feed-entry[data-side="party"] { --side: var(--side-party); }
#tableFeed .feed-entry[data-side="enemy"] { --side: var(--side-enemy); }
#tableFeed .feed-entry[data-side="object"] { --side: var(--side-object); }
#tableFeed .feed-entry[data-side="master"],
#tableFeed .feed-entry[data-side="patron"] { --side: var(--brass-bright); }
#tableFeed .feed-avatar {
    width: calc(32px * var(--table-scale, 1)); height: calc(32px * var(--table-scale, 1));
    box-sizing: border-box; border-radius: 50%; overflow: hidden;
    border: calc(2px * var(--table-scale, 1)) solid var(--side);
    background: var(--bg-iron); color: var(--side);
    display: grid; place-items: center;
    font: 600 calc(18px * var(--table-scale, 1))/1 var(--font-display, 'Cinzel', serif);
    margin-top: calc(3px * var(--table-scale, 1));
}
#tableFeed .feed-avatar.is-empty { visibility: hidden; }
#tableFeed .feed-avatar.is-sigil { color: var(--brass-bright); border-color: var(--brass-bright); }
#tableFeed .feed-avatar img { width: 100%; height: 100%; object-fit: cover; }
#tableFeed .feed-body { min-width: 0; display: flex; flex-direction: column; gap: calc(4px * var(--table-scale, 1)); }
#tableFeed .feed-item { border-left: 3px solid transparent; padding-left: calc(6px * var(--table-scale, 1)); }
#tableFeed .feed-item.is-captioned { border-left-color: var(--brass-bright); }
#tableFeed .feed-item.is-captioned[data-glow="party"] { border-left-color: var(--side-party); }
#tableFeed .feed-item.is-captioned[data-glow="enemy"] { border-left-color: var(--side-enemy); }
#tableFeed .feed-item.is-captioned[data-glow="object"] { border-left-color: var(--side-object); }
#tableFeed .feed-line { font-size: calc(23px * var(--table-scale, 1)); line-height: 1.3; overflow-wrap: anywhere; }
#tableFeed .feed-name {
    font-family: var(--font-display, 'Cinzel', serif); font-variant: small-caps;
    font-size: calc(24px * var(--table-scale, 1)); color: var(--side);
}
#tableFeed .feed-closed { color: var(--parchment-dim); font-style: italic; }
#tableFeed .feed-meta {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: calc(10px * var(--table-scale, 1));
    font-size: calc(22px * var(--table-scale, 1));
}
#tableFeed .feed-tag {
    font-family: var(--font-display, 'Cinzel', serif); color: var(--brass-bright);
    border: 1px solid var(--frame-line); padding: 0 calc(6px * var(--table-scale, 1)); border-radius: 2px;
}
#tableFeed .feed-sep {
    flex: none; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center;
    gap: 0 calc(10px * var(--table-scale, 1));
    font-family: var(--font-display, 'Cinzel', serif);
    font-size: calc(22px * var(--table-scale, 1)); color: var(--brass-dim); letter-spacing: .06em;
    text-align: center;
}
#tableFeed .feed-sep .sep-text { color: var(--brass-bright); }
#tableFeed .feed-sep .sep-sub { font-family: var(--font-body, 'EB Garamond', Georgia, serif); color: var(--parchment-dim); letter-spacing: 0; }
/* Dimmer than a fight's separator, still read from three metres (brass-dim was ~2.2:1). */
#tableFeed .feed-sep[data-kind="round"] .sep-text { color: var(--brass); }
#tableFeed .feed-sep[data-kind="round"]::before,
#tableFeed .feed-sep[data-kind="round"]::after {
    content: ''; flex: 1; height: 1px; background: var(--brass-dim); align-self: center; min-width: calc(24px * var(--table-scale, 1));
}
#tableFeed .feed-sep .feed-meta { flex-basis: 100%; justify-content: center; }

/* Outcome and effect tones, shared by the log and the captions. */
.tone-win { color: var(--outcome-win); font-weight: 600; }
.tone-lose { color: var(--outcome-lose); font-weight: 600; }
.tone-crit { color: var(--outcome-crit); font-weight: 700; }
.tone-harm { color: var(--outcome-harm); }
.tone-heal { color: var(--outcome-heal); }
.tone-gain { color: var(--outcome-gain); }
.tone-off .chip-words, .is-struck { text-decoration: line-through; text-decoration-thickness: 2px; }
.feed-chip, .cap-chip { display: inline-flex; align-items: center; gap: calc(6px * var(--table-scale, 1)); white-space: nowrap; }
/* An announced reward (I2) is told from a committed one by its strength alone — dimmed, at
   the row's size, with no word beside it (rc4 B: «объявлено» was a pipeline state). */
.feed-chip.is-unconfirmed > .chip-words,
.feed-chip.is-unconfirmed > .chip-icon { opacity: .55; }
.feed-chip .chip-icon { width: calc(24px * var(--table-scale, 1)); height: calc(24px * var(--table-scale, 1)); object-fit: contain; }

/* ── Captions over tokens (§4) ──────────────────────────────────────────── */
#tableCaptions {
    position: fixed; inset: 0; z-index: 890; pointer-events: none; overflow: hidden;
    will-change: transform;
}
body:not(.table-mode) #tableCaptions { display: none; }
.feed-cap {
    --cap-side: var(--frame-line);
    position: absolute; left: 0; top: 0;
    visibility: hidden;
    font-family: var(--font-body, 'EB Garamond', Georgia, serif);
    font-size: calc(24px * var(--table-scale, 1));
    color: var(--parchment);
    transition: opacity 400ms linear;
}
.feed-cap.is-placed { visibility: visible; }
.feed-cap.is-out { opacity: 0; }
.feed-cap[data-side="party"] { --cap-side: var(--side-party); }
.feed-cap[data-side="enemy"] { --cap-side: var(--side-enemy); }
.feed-cap[data-side="object"] { --cap-side: var(--side-object); }
.feed-cap .cap-plate {
    box-sizing: border-box;
    max-width: calc(380px * var(--table-scale, 1));
    padding: calc(8px * var(--table-scale, 1)) calc(14px * var(--table-scale, 1));
    background: rgba(20, 17, 13, .92);
    border: 2px solid var(--frame-line);
    border-left: 6px solid var(--cap-side);
    border-radius: 2px;
}
.feed-cap.is-placed .cap-plate { animation: tableCaptionIn 180ms ease-out both; }
@keyframes tableCaptionIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.feed-cap[data-role="verb"] .cap-plate { max-width: calc(320px * var(--table-scale, 1)); }
.feed-cap[data-role="pending"] .cap-plate { max-width: calc(300px * var(--table-scale, 1)); }
/* NPCs are people, not sides: no bar, the verb in italics. Objects are square and brass. */
.feed-cap[data-side="npc"] .cap-plate { border-left-width: 2px; }
.feed-cap[data-side="npc"] .cap-what { font-style: italic; }
.cap-row1 {
    display: flex; align-items: baseline; gap: calc(2px * var(--table-scale, 1)) calc(10px * var(--table-scale, 1));
    white-space: nowrap; min-width: 0;
    /* What does not fit moves to a line of its own instead of squeezing «what» (or the
       pending verb) to nothing: the outcome word is loud, but question 4 still reads. */
    flex-wrap: wrap;
}
.cap-row1 > span { flex: none; }
/* Whose caption it is (rc4 D2): small caps in the subject's side colour, before the glyph. */
.cap-name {
    font-family: var(--font-display, 'Cinzel', serif); font-variant: small-caps;
    font-size: calc(22px * var(--table-scale, 1)); color: var(--cap-side); line-height: 1.1;
}
.feed-cap[data-side="npc"] .cap-name, .feed-cap[data-side="neutral"] .cap-name { color: var(--parchment-dim); }
/* On the face of the person a conversation is with, the face's own strip names them (§E). */
.feed-cap.is-on-face .cap-name { display: none; }
.cap-row1 > .cap-what,
.cap-row1 > .cap-num-label { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.cap-row1 > .cap-what { margin-left: auto; }
.cap-glyph, .cap-word {
    font-family: var(--font-display, 'Cinzel', serif); font-weight: 700;
    font-size: calc(34px * var(--table-scale, 1)); letter-spacing: .06em; line-height: 1.1;
}
/* «скрытая проверка», «НЕ РАССУЖЕНО», «без броска»: at 34 px a long word alone fills the plate. */
.cap-word.is-long { font-size: calc(28px * var(--table-scale, 1)); }
.cap-num { font-family: var(--font-display, 'Cinzel', serif); font-weight: 700; font-size: calc(44px * var(--table-scale, 1)); line-height: 1; }
.cap-num-label, .cap-what { font-size: calc(24px * var(--table-scale, 1)); }
.cap-what { color: var(--parchment-dim); }
.cap-row2 {
    display: flex; flex-wrap: wrap; gap: calc(4px * var(--table-scale, 1)) calc(12px * var(--table-scale, 1));
    margin-top: calc(4px * var(--table-scale, 1));
}
.cap-row2:empty { display: none; }
.feed-cap.is-compact .cap-row2 { display: none; }
.cap-chip { font-size: calc(24px * var(--table-scale, 1)); }
.cap-chip .chip-icon { width: calc(28px * var(--table-scale, 1)); height: calc(28px * var(--table-scale, 1)); object-fit: contain; }
.cap-text {
    font-size: calc(24px * var(--table-scale, 1)); line-height: 1.25; white-space: normal;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Outcome colours, always next to their glyph and word. */
.feed-cap[data-outcome="success"] .cap-glyph, .feed-cap[data-outcome="success"] .cap-word { color: var(--outcome-win); }
.feed-cap[data-outcome="failure"] .cap-glyph, .feed-cap[data-outcome="failure"] .cap-word,
.feed-cap[data-outcome="critfail"] .cap-glyph, .feed-cap[data-outcome="critfail"] .cap-word,
.feed-cap[data-outcome="denied"] .cap-glyph, .feed-cap[data-outcome="denied"] .cap-word { color: var(--outcome-lose); }
.feed-cap[data-outcome="crit"] .cap-glyph, .feed-cap[data-outcome="crit"] .cap-word { color: var(--outcome-crit); }
.feed-cap[data-outcome="allowed"] .cap-word { color: var(--parchment); }
.feed-cap[data-outcome="hidden"] .cap-glyph, .feed-cap[data-outcome="hidden"] .cap-word { color: var(--brass-bright); }
.feed-cap[data-outcome="failure"] .cap-what.is-struck,
.feed-cap[data-outcome="critfail"] .cap-what.is-struck { text-decoration-color: rgba(226, 87, 76, .4); }
.feed-cap[data-outcome="crit"] .cap-plate { border-color: var(--outcome-crit); border-left-color: var(--cap-side); }
.feed-cap.is-placed[data-outcome="crit"] .cap-plate { animation: tableCaptionIn 180ms ease-out both, tableCaptionBloom 1s ease-out 180ms; }
@keyframes tableCaptionBloom { 0% { opacity: 1; } 35% { opacity: .72; } 100% { opacity: 1; } }
.feed-cap[data-outcome="denied"] .cap-plate { border-color: var(--outcome-lose); border-left-color: var(--cap-side); }
/* A blow landed on a party member is said from the party's side (evening 5: «Тарра ✓ ПОПАЛ»
   over a hero the ogre had struck read as her hit): «Огр → Тарра ⚔ ПОПАЛ» in the colour of a
   loss. After the outcome rules, which it overrides. */
.feed-cap[data-tone="lose"] .cap-glyph, .feed-cap[data-tone="lose"] .cap-word { color: var(--outcome-lose); }
/* Pending: one quiet line, the ellipsis breathing. */
.feed-cap[data-role="pending"] .cap-plate { padding: calc(6px * var(--table-scale, 1)) calc(12px * var(--table-scale, 1)); }
.feed-cap[data-role="pending"] .cap-row1 > * { font-size: calc(22px * var(--table-scale, 1)); }
.feed-cap[data-role="pending"] .cap-glyph { font-family: var(--font-body, 'EB Garamond', Georgia, serif); font-weight: 400; letter-spacing: 0; }
.feed-cap[data-role="pending"] .cap-row1 { color: var(--parchment-dim); }
.feed-cap[data-role="pending"] .cap-what { margin-left: 0; color: var(--parchment); }
.cap-judge, .cap-clock { color: var(--parchment-dim); font-variant-numeric: tabular-nums; }
.cap-clock:empty { display: none; }
.cap-dots i { font-style: normal; animation: tableCaptionDot 1.4s ease-in-out infinite; }
.cap-dots i:nth-child(2) { animation-delay: .2s; }
.cap-dots i:nth-child(3) { animation-delay: .4s; }
@keyframes tableCaptionDot { 0%, 100% { opacity: .25; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .cap-dots i, .feed-cap.is-placed .cap-plate, #tableFeed .is-new { animation: none; }
}
/* The tail: 10 px, on the anchor's centre. */
.cap-tail {
    position: absolute; width: 0; height: 0; display: none;
    border: calc(10px * var(--table-scale, 1)) solid transparent;
}
.feed-cap[data-tail="down"] .cap-tail {
    display: block; top: 100%; left: calc(var(--tail-x, 50%) - 10px * var(--table-scale, 1));
    border-top-color: var(--frame-line); border-bottom-width: 0;
}
.feed-cap[data-tail="up"] .cap-tail {
    display: block; bottom: 100%; left: calc(var(--tail-x, 50%) - 10px * var(--table-scale, 1));
    border-bottom-color: var(--frame-line); border-top-width: 0;
}
.feed-cap[data-tail="right"] .cap-tail {
    display: block; left: 100%; top: calc(50% - 10px * var(--table-scale, 1));
    border-left-color: var(--frame-line); border-right-width: 0;
}
/* No room over a party member's token: their rail card pulses once in brass (§4.6). */
.feed-pulse { animation: tableFeedPulse 600ms ease-out; }
@keyframes tableFeedPulse { 0% { opacity: 1; } 40% { opacity: .55; } 100% { opacity: 1; } }

/* ── Room for the log (§5.6) ─────────────────────────────────────────────
   The picture, the ribbon and the round banner live in what is left of the wall. The
   interior frame variables (--frame-*) are computed against #tableScene, so people stay on
   their picture. */
body.table-mode.feed-on #tableScene { left: var(--feed-w); }
body.table-mode.feed-on #tableRoll { left: var(--feed-w); }
body.table-mode.feed-on #combatRoundBanner,
body.table-mode.feed-on #combatActiveTurnLabel { left: calc(var(--feed-w) + (100% - var(--feed-w)) / 2); }
/* The line that says the master's trial did not load, centred in the same space. */
body.table-mode.feed-on #tableTrial {
    left: calc(var(--feed-w) + (100% - var(--feed-w)) / 2);
    max-width: min(70vw, calc(1100px * var(--table-scale, 1)), calc(100% - var(--feed-w) - 48px * var(--table-scale, 1)));
}
